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
12 #include "wine/unicode.h"
16 DEFAULT_DEBUG_CHANNEL(msvcrt
);
18 typedef struct MSVCRT_finddata_t
21 time_t time_create
; /* -1 when N/A */
22 time_t time_access
; /* -1 when N/A */
24 unsigned long size
; /* FIXME: 64 bit ??*/
28 typedef struct MSVCRT_wfinddata_t
31 time_t time_create
; /* -1 when N/A */
32 time_t time_access
; /* -1 when N/A */
34 unsigned long size
; /* FIXME: 64 bit ??*/
38 typedef struct __MSVCRT_diskfree_t
{
39 unsigned num_clusters
;
41 unsigned cluster_sectors
;
42 unsigned sector_bytes
;
45 /* INTERNAL: Translate finddata_t to PWIN32_FIND_DATAA */
46 static void MSVCRT__fttofd(LPWIN32_FIND_DATAA fd
, MSVCRT_finddata_t
* ft
)
50 if (fd
->dwFileAttributes
== FILE_ATTRIBUTE_NORMAL
)
53 ft
->attrib
= fd
->dwFileAttributes
;
55 RtlTimeToSecondsSince1970( &fd
->ftCreationTime
, &dw
);
57 RtlTimeToSecondsSince1970( &fd
->ftLastAccessTime
, &dw
);
59 RtlTimeToSecondsSince1970( &fd
->ftLastWriteTime
, &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
)
70 if (fd
->dwFileAttributes
== FILE_ATTRIBUTE_NORMAL
)
73 ft
->attrib
= fd
->dwFileAttributes
;
75 RtlTimeToSecondsSince1970( &fd
->ftCreationTime
, &dw
);
77 RtlTimeToSecondsSince1970( &fd
->ftLastAccessTime
, &dw
);
79 RtlTimeToSecondsSince1970( &fd
->ftLastWriteTime
, &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 /*********************************************************************
99 int __cdecl
MSVCRT__chdir(const char * newdir
)
101 if (!SetCurrentDirectoryA(newdir
))
103 MSVCRT__set_errno(newdir
?GetLastError():0);
109 /*********************************************************************
112 int __cdecl
MSVCRT__wchdir(const WCHAR
* newdir
)
114 if (!SetCurrentDirectoryW(newdir
))
116 MSVCRT__set_errno(newdir
?GetLastError():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());
133 SET_THREAD_VAR(errno
,MSVCRT_EACCES
);
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());
153 /*********************************************************************
154 * _findfirst (MSVCRT.@)
156 DWORD __cdecl
MSVCRT__findfirst(const char * fspec
, MSVCRT_finddata_t
* ft
)
158 WIN32_FIND_DATAA find_data
;
161 hfind
= FindFirstFileA(fspec
, &find_data
);
162 if (hfind
== INVALID_HANDLE_VALUE
)
164 MSVCRT__set_errno(GetLastError());
167 MSVCRT__fttofd(&find_data
,ft
);
168 TRACE(":got handle %d\n",hfind
);
172 /*********************************************************************
173 * _wfindfirst (MSVCRT.@)
175 DWORD __cdecl
MSVCRT__wfindfirst(const WCHAR
* fspec
, MSVCRT_wfinddata_t
* ft
)
177 WIN32_FIND_DATAW find_data
;
180 hfind
= FindFirstFileW(fspec
, &find_data
);
181 if (hfind
== INVALID_HANDLE_VALUE
)
183 MSVCRT__set_errno(GetLastError());
186 MSVCRT__wfttofd(&find_data
,ft
);
187 TRACE(":got handle %d\n",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
);
204 MSVCRT__fttofd(&find_data
,ft
);
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
);
221 MSVCRT__wfttofd(&find_data
,ft
);
225 /*********************************************************************
228 char* __cdecl
MSVCRT__getcwd(char * buf
, int size
)
231 int dir_len
= GetCurrentDirectoryA(MAX_PATH
,dir
);
234 return NULL
; /* FIXME: Real return value untested */
239 return MSVCRT__strdup(dir
);
240 return MSVCRT__strndup(dir
,size
);
244 SET_THREAD_VAR(errno
,MSVCRT_ERANGE
);
245 return NULL
; /* buf too small */
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
);
260 return NULL
; /* FIXME: Real return value untested */
265 return MSVCRT__wcsdup(dir
);
266 return MSVCRT__wstrndup(dir
,size
);
270 SET_THREAD_VAR(errno
,MSVCRT_ERANGE
);
271 return NULL
; /* buf too small */
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
)
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 */
302 char drivespec
[4] = {'A', ':', '\\', 0};
305 drivespec
[0] += drive
- 1;
306 if (GetDriveTypeA(drivespec
) < DRIVE_REMOVABLE
)
308 SET_THREAD_VAR(errno
,MSVCRT_EACCES
);
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
);
321 return MSVCRT__strdup(dir
); /* allocate */
328 /*********************************************************************
329 * _wgetdcwd (MSVCRT.@)
331 WCHAR
* __cdecl
MSVCRT__wgetdcwd(int drive
, WCHAR
* buf
, int size
)
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 */
341 WCHAR dir
[_MAX_PATH
];
342 WCHAR drivespec
[4] = {'A', ':', '\\', 0};
345 drivespec
[0] += drive
- 1;
346 if (GetDriveTypeW(drivespec
) < DRIVE_REMOVABLE
)
348 SET_THREAD_VAR(errno
,MSVCRT_EACCES
);
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
));
361 return MSVCRT__wcsdup(dir
); /* allocate */
367 /*********************************************************************
368 * _getdiskfree (MSVCRT.@)
370 unsigned int __cdecl
MSVCRT__getdiskfree(unsigned int disk
, MSVCRT_diskfree_t
* d
)
372 char drivespec
[4] = {'@', ':', '\\', 0};
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];
389 err
= GetLastError();
390 MSVCRT__set_errno(err
);
394 /*********************************************************************
397 int __cdecl
MSVCRT__mkdir(const char * newdir
)
399 if (CreateDirectoryA(newdir
,NULL
))
401 MSVCRT__set_errno(GetLastError());
405 /*********************************************************************
408 int __cdecl
MSVCRT__wmkdir(const WCHAR
* newdir
)
410 if (CreateDirectoryW(newdir
,NULL
))
412 MSVCRT__set_errno(GetLastError());
416 /*********************************************************************
419 int __cdecl
MSVCRT__rmdir(const char * dir
)
421 if (RemoveDirectoryA(dir
))
423 MSVCRT__set_errno(GetLastError());
427 /*********************************************************************
430 int __cdecl
MSVCRT__wrmdir(const WCHAR
* dir
)
432 if (RemoveDirectoryW(dir
))
434 MSVCRT__set_errno(GetLastError());
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. */
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
'/')
456 /* look for drive spec */
457 if ((ptr
= wcschr(path
, (WCHAR
)L
':')) != (WCHAR
)L
'\0')
462 wcsncpy(drv
, path
, ptr
- path
);
463 drv
[ptr
- path
] = (WCHAR
)L
'\0';
470 /* find rightmost backslash or leftmost colon */
471 if ((ptr
= wcsrchr(path
, (WCHAR
)L
'\\')) == NULL
)
472 ptr
= (wcschr(path
, (WCHAR
)L
':'));
476 ptr
= (WCHAR
*)path
; /* no path */
482 ++ptr
; /* skip the delimiter */
492 if ((p
= wcsrchr(ptr
, (WCHAR
)L
'.')) == NULL
)
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
':')
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;
532 if (NULL
== (r
= strrchr(path
, ':')))
537 /* Ignore leading slashes */
547 p
= r
; /* Change "\\" to "\" */
548 while (NULL
!= (p
= strchr(p
, '\\')))
554 while ('.' == *r
) /* Scrunch leading ".\" */
558 /* Ignore leading ".." */
559 for (p
= (r
+= 2); *p
&& (*p
!= '\\'); ++p
)
564 for (p
= r
+ 1 ;*p
&& (*p
!= '\\'); ++p
)
567 strcpy(r
, p
+ ((*p
) ? 1 : 0));
570 while ('\\' == path
[strlen(path
)-1]) /* Strip last '\\' */
573 path
[strlen(path
)-1] = '\0';
578 /* Look for "\." in path */
580 while (NULL
!= (p
= strstr(s
, "\\.")))
584 /* Execute this section if ".." found */
586 while (q
> r
) /* Backup one level */
599 strcpy(q
+ ((*q
== '\\') ? 1 : 0),
600 p
+ 3 + ((*(p
+ 3)) ? 1 : 0));
607 /* Execute this section if "." found */
609 for ( ;*q
&& (*q
!= '\\'); ++q
)
615 if (root_flag
) /* Embedded ".." could have bubbled up to root */
617 for (p
= r
; *p
&& ('.' == *p
|| '\\' == *p
); ++p
)
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
];
638 if (!relPath
|| !*relPath
)
639 return MSVCRT__getcwd(absPath
, size
);
643 SET_THREAD_VAR(errno
,MSVCRT_ERANGE
);
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
);
660 res
[0] = drive
[0]; /* If given a drive, preserve the letter case */
674 if (len
>= MAX_PATH
|| len
>= (size_t)size
)
675 return NULL
; /* FIXME: errno? */
678 return MSVCRT__strdup(res
);
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
)
691 TRACE("MSVCRT__makepath got %s %s %s %s\n", drive
, directory
,
692 filename
, extension
);
698 if (drive
&& drive
[0])
704 if (directory
&& directory
[0])
706 strcat(path
, directory
);
707 ch
= path
[strlen(path
)-1];
708 if (ch
!= '/' && ch
!= '\\')
711 if (filename
&& filename
[0])
713 strcat(path
, filename
);
714 if (extension
&& extension
[0])
716 if ( extension
[0] != '.' )
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
)
732 char curPath
[MAX_PATH
];
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
);
745 /* Search given environment variable */
746 envVal
= MSVCRT_getenv(env
);
749 MSVCRT__set_errno(ERROR_FILE_NOT_FOUND
);
754 TRACE(":searching for %s in paths %s\n", file
, envVal
);
760 while(*end
&& *end
!= ';') end
++; /* Find end of next path */
761 if (penv
== end
|| !*penv
)
763 MSVCRT__set_errno(ERROR_FILE_NOT_FOUND
);
766 strncpy(curPath
, penv
, end
- penv
);
767 if (curPath
[end
- penv
] != '/' || curPath
[end
- penv
] != '\\')
769 curPath
[end
- penv
] = '\\';
770 curPath
[end
- penv
+ 1] = '\0';
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
);
783 penv
= *end
? end
+ 1 : end
;