Display thread id instead of %fs in relay trace.
[wine.git] / dlls / msvcrt / dir.c
blob2493e36cb2860dc60f3b327defa5fe8d98f31c10
1 /*
2 * msvcrt.dll drive/directory functions
4 * Copyright 1996,1998 Marcus Meissner
5 * Copyright 1996 Jukka Iivonen
6 * Copyright 1997,2000 Uwe Bonnes
7 * Copyright 2000 Jon Griffiths
8 */
10 #include <time.h>
11 #include "ntddk.h"
12 #include "wine/unicode.h"
13 #include "msvcrt.h"
14 #include "ms_errno.h"
16 DEFAULT_DEBUG_CHANNEL(msvcrt);
18 typedef struct MSVCRT_finddata_t
20 unsigned attrib;
21 time_t time_create; /* -1 when N/A */
22 time_t time_access; /* -1 when N/A */
23 time_t time_write;
24 unsigned long size; /* FIXME: 64 bit ??*/
25 char name[MAX_PATH];
26 } MSVCRT_finddata_t;
28 typedef struct MSVCRT_wfinddata_t
30 unsigned attrib;
31 time_t time_create; /* -1 when N/A */
32 time_t time_access; /* -1 when N/A */
33 time_t time_write;
34 unsigned long size; /* FIXME: 64 bit ??*/
35 WCHAR name[MAX_PATH];
36 } MSVCRT_wfinddata_t;
38 typedef struct __MSVCRT_diskfree_t {
39 unsigned num_clusters;
40 unsigned available;
41 unsigned cluster_sectors;
42 unsigned sector_bytes;
43 } MSVCRT_diskfree_t;
45 /* INTERNAL: Translate finddata_t to PWIN32_FIND_DATAA */
46 static void MSVCRT__fttofd(LPWIN32_FIND_DATAA fd, MSVCRT_finddata_t* ft)
48 DWORD dw;
50 if (fd->dwFileAttributes == FILE_ATTRIBUTE_NORMAL)
51 ft->attrib = 0;
52 else
53 ft->attrib = fd->dwFileAttributes;
55 RtlTimeToSecondsSince1970( &fd->ftCreationTime, &dw );
56 ft->time_create = dw;
57 RtlTimeToSecondsSince1970( &fd->ftLastAccessTime, &dw );
58 ft->time_access = dw;
59 RtlTimeToSecondsSince1970( &fd->ftLastWriteTime, &dw );
60 ft->time_write = dw;
61 ft->size = fd->nFileSizeLow;
62 strcpy(ft->name, fd->cFileName);
65 /* INTERNAL: Translate wfinddata_t to PWIN32_FIND_DATAA */
66 static void MSVCRT__wfttofd(LPWIN32_FIND_DATAW fd, MSVCRT_wfinddata_t* ft)
68 DWORD dw;
70 if (fd->dwFileAttributes == FILE_ATTRIBUTE_NORMAL)
71 ft->attrib = 0;
72 else
73 ft->attrib = fd->dwFileAttributes;
75 RtlTimeToSecondsSince1970( &fd->ftCreationTime, &dw );
76 ft->time_create = dw;
77 RtlTimeToSecondsSince1970( &fd->ftLastAccessTime, &dw );
78 ft->time_access = dw;
79 RtlTimeToSecondsSince1970( &fd->ftLastWriteTime, &dw );
80 ft->time_write = dw;
81 ft->size = fd->nFileSizeLow;
82 strcpyW(ft->name, fd->cFileName);
85 char * MSVCRT__strndup(const char *, unsigned int);
86 LPWSTR __cdecl MSVCRT__wcsdup( LPCWSTR );
87 LPWSTR __cdecl MSVCRT__wstrndup( LPCWSTR , unsigned int );
88 char *__cdecl MSVCRT_getenv(const char *);
89 WCHAR *__cdecl wcscpy(WCHAR *,const WCHAR *);
90 WCHAR *__cdecl wcsncpy(WCHAR *,const WCHAR *,unsigned int);
91 WCHAR *__cdecl wcscat(WCHAR *,const WCHAR *);
92 WCHAR *__cdecl wcschr(WCHAR *,WCHAR);
93 WCHAR *__cdecl wcsrchr(WCHAR *,WCHAR);
94 void __cdecl _splitpath(const char *,char *, char *,char *,char *);
96 /*********************************************************************
97 * _chdir (MSVCRT.@)
99 int __cdecl MSVCRT__chdir(const char * newdir)
101 if (!SetCurrentDirectoryA(newdir))
103 MSVCRT__set_errno(newdir?GetLastError():0);
104 return -1;
106 return 0;
109 /*********************************************************************
110 * _wchdir (MSVCRT.@)
112 int __cdecl MSVCRT__wchdir(const WCHAR * newdir)
114 if (!SetCurrentDirectoryW(newdir))
116 MSVCRT__set_errno(newdir?GetLastError():0);
117 return -1;
119 return 0;
122 /*********************************************************************
123 * _chdrive (MSVCRT.@)
125 int __cdecl MSVCRT__chdrive(int newdrive)
127 char buffer[3] = "A:";
128 buffer[0] += newdrive - 1;
129 if (!SetCurrentDirectoryA( buffer ))
131 MSVCRT__set_errno(GetLastError());
132 if (newdrive <= 0)
133 SET_THREAD_VAR(errno,MSVCRT_EACCES);
134 return -1;
136 return 0;
139 /*********************************************************************
140 * _findclose (MSVCRT.@)
142 int __cdecl MSVCRT__findclose(DWORD hand)
144 TRACE(":handle %ld\n",hand);
145 if (!FindClose((HANDLE)hand))
147 MSVCRT__set_errno(GetLastError());
148 return -1;
150 return 0;
153 /*********************************************************************
154 * _findfirst (MSVCRT.@)
156 DWORD __cdecl MSVCRT__findfirst(const char * fspec, MSVCRT_finddata_t* ft)
158 WIN32_FIND_DATAA find_data;
159 HANDLE hfind;
161 hfind = FindFirstFileA(fspec, &find_data);
162 if (hfind == INVALID_HANDLE_VALUE)
164 MSVCRT__set_errno(GetLastError());
165 return -1;
167 MSVCRT__fttofd(&find_data,ft);
168 TRACE(":got handle %d\n",hfind);
169 return hfind;
172 /*********************************************************************
173 * _wfindfirst (MSVCRT.@)
175 DWORD __cdecl MSVCRT__wfindfirst(const WCHAR * fspec, MSVCRT_wfinddata_t* ft)
177 WIN32_FIND_DATAW find_data;
178 HANDLE hfind;
180 hfind = FindFirstFileW(fspec, &find_data);
181 if (hfind == INVALID_HANDLE_VALUE)
183 MSVCRT__set_errno(GetLastError());
184 return -1;
186 MSVCRT__wfttofd(&find_data,ft);
187 TRACE(":got handle %d\n",hfind);
188 return hfind;
191 /*********************************************************************
192 * _findnext (MSVCRT.@)
194 int __cdecl MSVCRT__findnext(DWORD hand, MSVCRT_finddata_t * ft)
196 WIN32_FIND_DATAA find_data;
198 if (!FindNextFileA(hand, &find_data))
200 SET_THREAD_VAR(errno,MSVCRT_ENOENT);
201 return -1;
204 MSVCRT__fttofd(&find_data,ft);
205 return 0;
208 /*********************************************************************
209 * _wfindnext (MSVCRT.@)
211 int __cdecl MSVCRT__wfindnext(DWORD hand, MSVCRT_wfinddata_t * ft)
213 WIN32_FIND_DATAW find_data;
215 if (!FindNextFileW(hand, &find_data))
217 SET_THREAD_VAR(errno,MSVCRT_ENOENT);
218 return -1;
221 MSVCRT__wfttofd(&find_data,ft);
222 return 0;
225 /*********************************************************************
226 * _getcwd (MSVCRT.@)
228 char* __cdecl MSVCRT__getcwd(char * buf, int size)
230 char dir[_MAX_PATH];
231 int dir_len = GetCurrentDirectoryA(MAX_PATH,dir);
233 if (dir_len < 1)
234 return NULL; /* FIXME: Real return value untested */
236 if (!buf)
238 if (size < 0)
239 return MSVCRT__strdup(dir);
240 return MSVCRT__strndup(dir,size);
242 if (dir_len >= size)
244 SET_THREAD_VAR(errno,MSVCRT_ERANGE);
245 return NULL; /* buf too small */
247 strcpy(buf,dir);
248 return buf;
251 /*********************************************************************
252 * _wgetcwd (MSVCRT.@)
254 WCHAR* __cdecl MSVCRT__wgetcwd(WCHAR * buf, int size)
256 WCHAR dir[_MAX_PATH];
257 int dir_len = GetCurrentDirectoryW(MAX_PATH,dir);
259 if (dir_len < 1)
260 return NULL; /* FIXME: Real return value untested */
262 if (!buf)
264 if (size < 0)
265 return MSVCRT__wcsdup(dir);
266 return MSVCRT__wstrndup(dir,size);
268 if (dir_len >= size)
270 SET_THREAD_VAR(errno,MSVCRT_ERANGE);
271 return NULL; /* buf too small */
273 strcpyW(buf,dir);
274 return buf;
277 /*********************************************************************
278 * _getdrive (MSVCRT.@)
280 int __cdecl MSVCRT__getdrive(void)
282 char buffer[MAX_PATH];
283 if (!GetCurrentDirectoryA( sizeof(buffer), buffer )) return 0;
284 if (buffer[1] != ':') return 0;
285 return toupper(buffer[0]) - 'A' + 1;
288 /*********************************************************************
289 * _getdcwd (MSVCRT.@)
291 char* __cdecl MSVCRT__getdcwd(int drive, char * buf, int size)
293 static char* dummy;
295 TRACE(":drive %d(%c), size %d\n",drive, drive + 'A' - 1, size);
297 if (!drive || drive == MSVCRT__getdrive())
298 return MSVCRT__getcwd(buf,size); /* current */
299 else
301 char dir[_MAX_PATH];
302 char drivespec[4] = {'A', ':', '\\', 0};
303 int dir_len;
305 drivespec[0] += drive - 1;
306 if (GetDriveTypeA(drivespec) < DRIVE_REMOVABLE)
308 SET_THREAD_VAR(errno,MSVCRT_EACCES);
309 return NULL;
312 dir_len = GetFullPathNameA(drivespec,_MAX_PATH,dir,&dummy);
313 if (dir_len >= size || dir_len < 1)
315 SET_THREAD_VAR(errno,MSVCRT_ERANGE);
316 return NULL; /* buf too small */
319 TRACE(":returning '%s'\n", dir);
320 if (!buf)
321 return MSVCRT__strdup(dir); /* allocate */
323 strcpy(buf,dir);
325 return buf;
328 /*********************************************************************
329 * _wgetdcwd (MSVCRT.@)
331 WCHAR* __cdecl MSVCRT__wgetdcwd(int drive, WCHAR * buf, int size)
333 static WCHAR* dummy;
335 TRACE(":drive %d(%c), size %d\n",drive, drive + 'A' - 1, size);
337 if (!drive || drive == MSVCRT__getdrive())
338 return MSVCRT__wgetcwd(buf,size); /* current */
339 else
341 WCHAR dir[_MAX_PATH];
342 WCHAR drivespec[4] = {'A', ':', '\\', 0};
343 int dir_len;
345 drivespec[0] += drive - 1;
346 if (GetDriveTypeW(drivespec) < DRIVE_REMOVABLE)
348 SET_THREAD_VAR(errno,MSVCRT_EACCES);
349 return NULL;
352 dir_len = GetFullPathNameW(drivespec,_MAX_PATH,dir,&dummy);
353 if (dir_len >= size || dir_len < 1)
355 SET_THREAD_VAR(errno,MSVCRT_ERANGE);
356 return NULL; /* buf too small */
359 TRACE(":returning '%s'\n", debugstr_w(dir));
360 if (!buf)
361 return MSVCRT__wcsdup(dir); /* allocate */
362 strcpyW(buf,dir);
364 return buf;
367 /*********************************************************************
368 * _getdiskfree (MSVCRT.@)
370 unsigned int __cdecl MSVCRT__getdiskfree(unsigned int disk, MSVCRT_diskfree_t* d)
372 char drivespec[4] = {'@', ':', '\\', 0};
373 DWORD ret[4];
374 unsigned int err;
376 if (disk > 26)
377 return ERROR_INVALID_PARAMETER; /* MSVCRT doesn't set errno here */
379 drivespec[0] += disk; /* make a drive letter */
381 if (GetDiskFreeSpaceA(disk==0?NULL:drivespec,ret,ret+1,ret+2,ret+3))
383 d->cluster_sectors = (unsigned)ret[0];
384 d->sector_bytes = (unsigned)ret[1];
385 d->available = (unsigned)ret[2];
386 d->num_clusters = (unsigned)ret[3];
387 return 0;
389 err = GetLastError();
390 MSVCRT__set_errno(err);
391 return err;
394 /*********************************************************************
395 * _mkdir (MSVCRT.@)
397 int __cdecl MSVCRT__mkdir(const char * newdir)
399 if (CreateDirectoryA(newdir,NULL))
400 return 0;
401 MSVCRT__set_errno(GetLastError());
402 return -1;
405 /*********************************************************************
406 * _wmkdir (MSVCRT.@)
408 int __cdecl MSVCRT__wmkdir(const WCHAR* newdir)
410 if (CreateDirectoryW(newdir,NULL))
411 return 0;
412 MSVCRT__set_errno(GetLastError());
413 return -1;
416 /*********************************************************************
417 * _rmdir (MSVCRT.@)
419 int __cdecl MSVCRT__rmdir(const char * dir)
421 if (RemoveDirectoryA(dir))
422 return 0;
423 MSVCRT__set_errno(GetLastError());
424 return -1;
427 /*********************************************************************
428 * _wrmdir (MSVCRT.@)
430 int __cdecl MSVCRT__wrmdir(const WCHAR * dir)
432 if (RemoveDirectoryW(dir))
433 return 0;
434 MSVCRT__set_errno(GetLastError());
435 return -1;
438 /*********************************************************************
439 * _wsplitpath (MSVCRT.@)
441 void __cdecl MSVCRT__wsplitpath(const WCHAR *inpath, WCHAR *drv, WCHAR *dir,
442 WCHAR *fname, WCHAR *ext )
444 /* Modified PD code from 'snippets' collection. */
445 WCHAR ch, *ptr, *p;
446 WCHAR pathbuff[MAX_PATH],*path=pathbuff;
448 TRACE(":splitting path '%s'\n",debugstr_w(path));
449 wcscpy(pathbuff, inpath);
451 /* convert slashes to backslashes for searching */
452 for (ptr = (WCHAR*)path; *ptr; ++ptr)
453 if (*ptr == (WCHAR)L'/')
454 *ptr = (WCHAR)L'\\';
456 /* look for drive spec */
457 if ((ptr = wcschr(path, (WCHAR)L':')) != (WCHAR)L'\0')
459 ++ptr;
460 if (drv)
462 wcsncpy(drv, path, ptr - path);
463 drv[ptr - path] = (WCHAR)L'\0';
465 path = ptr;
467 else if (drv)
468 *drv = (WCHAR)L'\0';
470 /* find rightmost backslash or leftmost colon */
471 if ((ptr = wcsrchr(path, (WCHAR)L'\\')) == NULL)
472 ptr = (wcschr(path, (WCHAR)L':'));
474 if (!ptr)
476 ptr = (WCHAR *)path; /* no path */
477 if (dir)
478 *dir = (WCHAR)L'\0';
480 else
482 ++ptr; /* skip the delimiter */
483 if (dir)
485 ch = *ptr;
486 *ptr = (WCHAR)L'\0';
487 wcscpy(dir, path);
488 *ptr = ch;
492 if ((p = wcsrchr(ptr, (WCHAR)L'.')) == NULL)
494 if (fname)
495 wcscpy(fname, ptr);
496 if (ext)
497 *ext = (WCHAR)L'\0';
499 else
501 *p = (WCHAR)L'\0';
502 if (fname)
503 wcscpy(fname, ptr);
504 *p = (WCHAR)L'.';
505 if (ext)
506 wcscpy(ext, p);
509 /* Fix pathological case - Win returns ':' as part of the
510 * directory when no drive letter is given.
512 if (drv && drv[0] == (WCHAR)L':')
514 *drv = (WCHAR)L'\0';
515 if (dir)
517 pathbuff[0] = (WCHAR)L':';
518 pathbuff[1] = (WCHAR)L'\0';
519 wcscat(pathbuff,dir);
520 wcscpy(dir, pathbuff);
525 /* INTERNAL: Helper for _fullpath. Modified PD code from 'snippets'. */
526 static void fln_fix(char *path)
528 int dir_flag = 0, root_flag = 0;
529 char *r, *p, *q, *s;
531 /* Skip drive */
532 if (NULL == (r = strrchr(path, ':')))
533 r = path;
534 else
535 ++r;
537 /* Ignore leading slashes */
538 while ('\\' == *r)
539 if ('\\' == r[1])
540 strcpy(r, &r[1]);
541 else
543 root_flag = 1;
544 ++r;
547 p = r; /* Change "\\" to "\" */
548 while (NULL != (p = strchr(p, '\\')))
549 if ('\\' == p[1])
550 strcpy(p, &p[1]);
551 else
552 ++p;
554 while ('.' == *r) /* Scrunch leading ".\" */
556 if ('.' == r[1])
558 /* Ignore leading ".." */
559 for (p = (r += 2); *p && (*p != '\\'); ++p)
562 else
564 for (p = r + 1 ;*p && (*p != '\\'); ++p)
567 strcpy(r, p + ((*p) ? 1 : 0));
570 while ('\\' == path[strlen(path)-1]) /* Strip last '\\' */
572 dir_flag = 1;
573 path[strlen(path)-1] = '\0';
576 s = r;
578 /* Look for "\." in path */
580 while (NULL != (p = strstr(s, "\\.")))
582 if ('.' == p[2])
584 /* Execute this section if ".." found */
585 q = p - 1;
586 while (q > r) /* Backup one level */
588 if (*q == '\\')
589 break;
590 --q;
592 if (q > r)
594 strcpy(q, p + 3);
595 s = q;
597 else if ('.' != *q)
599 strcpy(q + ((*q == '\\') ? 1 : 0),
600 p + 3 + ((*(p + 3)) ? 1 : 0));
601 s = q;
603 else s = ++p;
605 else
607 /* Execute this section if "." found */
608 q = p + 2;
609 for ( ;*q && (*q != '\\'); ++q)
611 strcpy (p, q);
615 if (root_flag) /* Embedded ".." could have bubbled up to root */
617 for (p = r; *p && ('.' == *p || '\\' == *p); ++p)
619 if (r != p)
620 strcpy(r, p);
623 if (dir_flag)
624 strcat(path, "\\");
627 /*********************************************************************
628 * _fullpath (MSVCRT.@)
630 char *__cdecl MSVCRT__fullpath(char * absPath, const char* relPath, unsigned int size)
632 char drive[5],dir[MAX_PATH],file[MAX_PATH],ext[MAX_PATH];
633 char res[MAX_PATH];
634 size_t len;
636 res[0] = '\0';
638 if (!relPath || !*relPath)
639 return MSVCRT__getcwd(absPath, size);
641 if (size < 4)
643 SET_THREAD_VAR(errno,MSVCRT_ERANGE);
644 return NULL;
647 TRACE(":resolving relative path '%s'\n",relPath);
649 _splitpath(relPath, drive, dir, file, ext);
651 /* Get Directory and drive into 'res' */
652 if (!dir[0] || (dir[0] != '/' && dir[0] != '\\'))
654 /* Relative or no directory given */
655 MSVCRT__getdcwd(drive[0] ? toupper(drive[0]) - 'A' + 1 : 0, res, MAX_PATH);
656 strcat(res,"\\");
657 if (dir[0])
658 strcat(res,dir);
659 if (drive[0])
660 res[0] = drive[0]; /* If given a drive, preserve the letter case */
662 else
664 strcpy(res,drive);
665 strcat(res,dir);
668 strcat(res,"\\");
669 strcat(res, file);
670 strcat(res, ext);
671 fln_fix(res);
673 len = strlen(res);
674 if (len >= MAX_PATH || len >= (size_t)size)
675 return NULL; /* FIXME: errno? */
677 if (!absPath)
678 return MSVCRT__strdup(res);
679 strcpy(absPath,res);
680 return absPath;
683 /*********************************************************************
684 * _makepath (MSVCRT.@)
686 VOID __cdecl MSVCRT__makepath(char * path, const char * drive,
687 const char *directory, const char * filename,
688 const char * extension )
690 char ch;
691 TRACE("MSVCRT__makepath got %s %s %s %s\n", drive, directory,
692 filename, extension);
694 if ( !path )
695 return;
697 path[0] = 0;
698 if (drive && drive[0])
700 path[0] = drive[0];
701 path[1] = ':';
702 path[2] = 0;
704 if (directory && directory[0])
706 strcat(path, directory);
707 ch = path[strlen(path)-1];
708 if (ch != '/' && ch != '\\')
709 strcat(path,"\\");
711 if (filename && filename[0])
713 strcat(path, filename);
714 if (extension && extension[0])
716 if ( extension[0] != '.' )
717 strcat(path,".");
718 strcat(path,extension);
722 TRACE("MSVCRT__makepath returns %s\n",path);
726 /*********************************************************************
727 * _searchenv (MSVCRT.@)
729 void __cdecl MSVCRT__searchenv(const char* file, const char* env, char *buf)
731 char*envVal, *penv;
732 char curPath[MAX_PATH];
734 *buf = '\0';
736 /* Try CWD first */
737 if (GetFileAttributesA( file ) != 0xFFFFFFFF)
739 GetFullPathNameA( file, MAX_PATH, buf, NULL );
740 /* Sigh. This error is *always* set, regardless of success */
741 MSVCRT__set_errno(ERROR_FILE_NOT_FOUND);
742 return;
745 /* Search given environment variable */
746 envVal = MSVCRT_getenv(env);
747 if (!envVal)
749 MSVCRT__set_errno(ERROR_FILE_NOT_FOUND);
750 return;
753 penv = envVal;
754 TRACE(":searching for %s in paths %s\n", file, envVal);
758 char *end = penv;
760 while(*end && *end != ';') end++; /* Find end of next path */
761 if (penv == end || !*penv)
763 MSVCRT__set_errno(ERROR_FILE_NOT_FOUND);
764 return;
766 strncpy(curPath, penv, end - penv);
767 if (curPath[end - penv] != '/' || curPath[end - penv] != '\\')
769 curPath[end - penv] = '\\';
770 curPath[end - penv + 1] = '\0';
772 else
773 curPath[end - penv] = '\0';
775 strcat(curPath, file);
776 TRACE("Checking for file %s\n", curPath);
777 if (GetFileAttributesA( curPath ) != 0xFFFFFFFF)
779 strcpy(buf, curPath);
780 MSVCRT__set_errno(ERROR_FILE_NOT_FOUND);
781 return; /* Found */
783 penv = *end ? end + 1 : end;
784 } while(1);