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 *);
90 /*********************************************************************
93 int __cdecl
MSVCRT__chdir(const char * newdir
)
95 if (!SetCurrentDirectoryA(newdir
))
97 MSVCRT__set_errno(newdir
?GetLastError():0);
103 /*********************************************************************
106 int __cdecl
MSVCRT__wchdir(const WCHAR
* newdir
)
108 if (!SetCurrentDirectoryW(newdir
))
110 MSVCRT__set_errno(newdir
?GetLastError():0);
116 /*********************************************************************
117 * _chdrive (MSVCRT.@)
119 int __cdecl
MSVCRT__chdrive(int newdrive
)
121 char buffer
[3] = "A:";
122 buffer
[0] += newdrive
- 1;
123 if (!SetCurrentDirectoryA( buffer
))
125 MSVCRT__set_errno(GetLastError());
127 SET_THREAD_VAR(errno
,MSVCRT_EACCES
);
133 /*********************************************************************
134 * _findclose (MSVCRT.@)
136 int __cdecl
MSVCRT__findclose(DWORD hand
)
138 TRACE(":handle %ld\n",hand
);
139 if (!FindClose((HANDLE
)hand
))
141 MSVCRT__set_errno(GetLastError());
147 /*********************************************************************
148 * _findfirst (MSVCRT.@)
150 DWORD __cdecl
MSVCRT__findfirst(const char * fspec
, MSVCRT_finddata_t
* ft
)
152 WIN32_FIND_DATAA find_data
;
155 hfind
= FindFirstFileA(fspec
, &find_data
);
156 if (hfind
== INVALID_HANDLE_VALUE
)
158 MSVCRT__set_errno(GetLastError());
161 MSVCRT__fttofd(&find_data
,ft
);
162 TRACE(":got handle %d\n",hfind
);
166 /*********************************************************************
167 * _wfindfirst (MSVCRT.@)
169 DWORD __cdecl
MSVCRT__wfindfirst(const WCHAR
* fspec
, MSVCRT_wfinddata_t
* ft
)
171 WIN32_FIND_DATAW find_data
;
174 hfind
= FindFirstFileW(fspec
, &find_data
);
175 if (hfind
== INVALID_HANDLE_VALUE
)
177 MSVCRT__set_errno(GetLastError());
180 MSVCRT__wfttofd(&find_data
,ft
);
181 TRACE(":got handle %d\n",hfind
);
185 /*********************************************************************
186 * _findnext (MSVCRT.@)
188 int __cdecl
MSVCRT__findnext(DWORD hand
, MSVCRT_finddata_t
* ft
)
190 WIN32_FIND_DATAA find_data
;
192 if (!FindNextFileA(hand
, &find_data
))
194 SET_THREAD_VAR(errno
,MSVCRT_ENOENT
);
198 MSVCRT__fttofd(&find_data
,ft
);
202 /*********************************************************************
203 * _wfindnext (MSVCRT.@)
205 int __cdecl
MSVCRT__wfindnext(DWORD hand
, MSVCRT_wfinddata_t
* ft
)
207 WIN32_FIND_DATAW find_data
;
209 if (!FindNextFileW(hand
, &find_data
))
211 SET_THREAD_VAR(errno
,MSVCRT_ENOENT
);
215 MSVCRT__wfttofd(&find_data
,ft
);
219 /*********************************************************************
222 char* __cdecl
MSVCRT__getcwd(char * buf
, int size
)
225 int dir_len
= GetCurrentDirectoryA(MAX_PATH
,dir
);
228 return NULL
; /* FIXME: Real return value untested */
233 return MSVCRT__strdup(dir
);
234 return MSVCRT__strndup(dir
,size
);
238 SET_THREAD_VAR(errno
,MSVCRT_ERANGE
);
239 return NULL
; /* buf too small */
245 /*********************************************************************
246 * _wgetcwd (MSVCRT.@)
248 WCHAR
* __cdecl
MSVCRT__wgetcwd(WCHAR
* buf
, int size
)
250 WCHAR dir
[_MAX_PATH
];
251 int dir_len
= GetCurrentDirectoryW(MAX_PATH
,dir
);
254 return NULL
; /* FIXME: Real return value untested */
259 return MSVCRT__wcsdup(dir
);
260 return MSVCRT__wstrndup(dir
,size
);
264 SET_THREAD_VAR(errno
,MSVCRT_ERANGE
);
265 return NULL
; /* buf too small */
271 /*********************************************************************
272 * _getdrive (MSVCRT.@)
274 int __cdecl
MSVCRT__getdrive(void)
276 char buffer
[MAX_PATH
];
277 if (!GetCurrentDirectoryA( sizeof(buffer
), buffer
)) return 0;
278 if (buffer
[1] != ':') return 0;
279 return toupper(buffer
[0]) - 'A' + 1;
282 /*********************************************************************
283 * _getdcwd (MSVCRT.@)
285 char* __cdecl
MSVCRT__getdcwd(int drive
, char * buf
, int size
)
289 TRACE(":drive %d(%c), size %d\n",drive
, drive
+ 'A' - 1, size
);
291 if (!drive
|| drive
== MSVCRT__getdrive())
292 return MSVCRT__getcwd(buf
,size
); /* current */
296 char drivespec
[4] = {'A', ':', '\\', 0};
299 drivespec
[0] += drive
- 1;
300 if (GetDriveTypeA(drivespec
) < DRIVE_REMOVABLE
)
302 SET_THREAD_VAR(errno
,MSVCRT_EACCES
);
306 dir_len
= GetFullPathNameA(drivespec
,_MAX_PATH
,dir
,&dummy
);
307 if (dir_len
>= size
|| dir_len
< 1)
309 SET_THREAD_VAR(errno
,MSVCRT_ERANGE
);
310 return NULL
; /* buf too small */
313 TRACE(":returning '%s'\n", dir
);
315 return MSVCRT__strdup(dir
); /* allocate */
322 /*********************************************************************
323 * _wgetdcwd (MSVCRT.@)
325 WCHAR
* __cdecl
MSVCRT__wgetdcwd(int drive
, WCHAR
* buf
, int size
)
329 TRACE(":drive %d(%c), size %d\n",drive
, drive
+ 'A' - 1, size
);
331 if (!drive
|| drive
== MSVCRT__getdrive())
332 return MSVCRT__wgetcwd(buf
,size
); /* current */
335 WCHAR dir
[_MAX_PATH
];
336 WCHAR drivespec
[4] = {'A', ':', '\\', 0};
339 drivespec
[0] += drive
- 1;
340 if (GetDriveTypeW(drivespec
) < DRIVE_REMOVABLE
)
342 SET_THREAD_VAR(errno
,MSVCRT_EACCES
);
346 dir_len
= GetFullPathNameW(drivespec
,_MAX_PATH
,dir
,&dummy
);
347 if (dir_len
>= size
|| dir_len
< 1)
349 SET_THREAD_VAR(errno
,MSVCRT_ERANGE
);
350 return NULL
; /* buf too small */
353 TRACE(":returning '%s'\n", debugstr_w(dir
));
355 return MSVCRT__wcsdup(dir
); /* allocate */
361 /*********************************************************************
362 * _getdiskfree (MSVCRT.@)
364 unsigned int __cdecl
MSVCRT__getdiskfree(unsigned int disk
, MSVCRT_diskfree_t
* d
)
366 char drivespec
[4] = {'@', ':', '\\', 0};
371 return ERROR_INVALID_PARAMETER
; /* MSVCRT doesn't set errno here */
373 drivespec
[0] += disk
; /* make a drive letter */
375 if (GetDiskFreeSpaceA(disk
==0?NULL
:drivespec
,ret
,ret
+1,ret
+2,ret
+3))
377 d
->cluster_sectors
= (unsigned)ret
[0];
378 d
->sector_bytes
= (unsigned)ret
[1];
379 d
->available
= (unsigned)ret
[2];
380 d
->num_clusters
= (unsigned)ret
[3];
383 err
= GetLastError();
384 MSVCRT__set_errno(err
);
388 /*********************************************************************
391 int __cdecl
MSVCRT__mkdir(const char * newdir
)
393 if (CreateDirectoryA(newdir
,NULL
))
395 MSVCRT__set_errno(GetLastError());
399 /*********************************************************************
402 int __cdecl
MSVCRT__wmkdir(const WCHAR
* newdir
)
404 if (CreateDirectoryW(newdir
,NULL
))
406 MSVCRT__set_errno(GetLastError());
410 /*********************************************************************
413 int __cdecl
MSVCRT__rmdir(const char * dir
)
415 if (RemoveDirectoryA(dir
))
417 MSVCRT__set_errno(GetLastError());
421 /*********************************************************************
424 int __cdecl
MSVCRT__wrmdir(const WCHAR
* dir
)
426 if (RemoveDirectoryW(dir
))
428 MSVCRT__set_errno(GetLastError());
432 /*********************************************************************
433 * _splitpath (MSVCRT.@)
435 void __cdecl
MSVCRT__splitpath(const char* inpath
, char * drv
, char * dir
,
436 char* fname
, char * ext
)
438 /* Modified PD code from 'snippets' collection. */
440 char pathbuff
[MAX_PATH
],*path
=pathbuff
;
442 TRACE(":splitting path '%s'\n",path
);
443 strcpy(pathbuff
, inpath
);
445 /* convert slashes to backslashes for searching */
446 for (ptr
= (char*)path
; *ptr
; ++ptr
)
450 /* look for drive spec */
451 if ('\0' != (ptr
= strchr(path
, ':')))
456 strncpy(drv
, path
, ptr
- path
);
457 drv
[ptr
- path
] = '\0';
464 /* find rightmost backslash or leftmost colon */
465 if (NULL
== (ptr
= strrchr(path
, '\\')))
466 ptr
= (strchr(path
, ':'));
470 ptr
= (char *)path
; /* no path */
476 ++ptr
; /* skip the delimiter */
486 if (NULL
== (p
= strrchr(ptr
, '.')))
503 /* Fix pathological case - Win returns ':' as part of the
504 * directory when no drive letter is given.
506 if (drv
&& drv
[0] == ':')
513 strcat(pathbuff
,dir
);
514 strcpy(dir
,pathbuff
);
519 /* INTERNAL: Helper for _fullpath. Modified PD code from 'snippets'. */
520 static void fln_fix(char *path
)
522 int dir_flag
= 0, root_flag
= 0;
526 if (NULL
== (r
= strrchr(path
, ':')))
531 /* Ignore leading slashes */
541 p
= r
; /* Change "\\" to "\" */
542 while (NULL
!= (p
= strchr(p
, '\\')))
548 while ('.' == *r
) /* Scrunch leading ".\" */
552 /* Ignore leading ".." */
553 for (p
= (r
+= 2); *p
&& (*p
!= '\\'); ++p
)
558 for (p
= r
+ 1 ;*p
&& (*p
!= '\\'); ++p
)
561 strcpy(r
, p
+ ((*p
) ? 1 : 0));
564 while ('\\' == path
[strlen(path
)-1]) /* Strip last '\\' */
567 path
[strlen(path
)-1] = '\0';
572 /* Look for "\." in path */
574 while (NULL
!= (p
= strstr(s
, "\\.")))
578 /* Execute this section if ".." found */
580 while (q
> r
) /* Backup one level */
593 strcpy(q
+ ((*q
== '\\') ? 1 : 0),
594 p
+ 3 + ((*(p
+ 3)) ? 1 : 0));
601 /* Execute this section if "." found */
603 for ( ;*q
&& (*q
!= '\\'); ++q
)
609 if (root_flag
) /* Embedded ".." could have bubbled up to root */
611 for (p
= r
; *p
&& ('.' == *p
|| '\\' == *p
); ++p
)
621 /*********************************************************************
622 * _fullpath (MSVCRT.@)
624 LPSTR __cdecl
MSVCRT__fullpath(char * absPath
, const char* relPath
, unsigned int size
)
626 char drive
[5],dir
[MAX_PATH
],file
[MAX_PATH
],ext
[MAX_PATH
];
632 if (!relPath
|| !*relPath
)
633 return MSVCRT__getcwd(absPath
, size
);
637 SET_THREAD_VAR(errno
,MSVCRT_ERANGE
);
641 TRACE(":resolving relative path '%s'\n",relPath
);
643 MSVCRT__splitpath(relPath
, drive
, dir
, file
, ext
);
645 /* Get Directory and drive into 'res' */
646 if (!dir
[0] || (dir
[0] != '/' && dir
[0] != '\\'))
648 /* Relative or no directory given */
649 MSVCRT__getdcwd(drive
[0] ? toupper(drive
[0]) - 'A' + 1 : 0, res
, MAX_PATH
);
654 res
[0] = drive
[0]; /* If given a drive, preserve the letter case */
668 if (len
>= MAX_PATH
|| len
>= (size_t)size
)
669 return NULL
; /* FIXME: errno? */
672 return MSVCRT__strdup(res
);
677 /*********************************************************************
678 * _makepath (MSVCRT.@)
680 VOID __cdecl
MSVCRT__makepath(char * path
, const char * drive
,
681 const char *directory
, const char * filename
,
682 const char * extension
)
685 TRACE("MSVCRT__makepath got %s %s %s %s\n", drive
, directory
,
686 filename
, extension
);
692 if (drive
&& drive
[0])
698 if (directory
&& directory
[0])
700 strcat(path
, directory
);
701 ch
= path
[strlen(path
)-1];
702 if (ch
!= '/' && ch
!= '\\')
705 if (filename
&& filename
[0])
707 strcat(path
, filename
);
708 if (extension
&& extension
[0])
710 if ( extension
[0] != '.' )
712 strcat(path
,extension
);
716 TRACE("MSVCRT__makepath returns %s\n",path
);
720 /*********************************************************************
721 * _searchenv (MSVCRT.@)
723 void __cdecl
MSVCRT__searchenv(const char* file
, const char* env
, char *buf
)
726 char curPath
[MAX_PATH
];
731 if (GetFileAttributesA( file
) != 0xFFFFFFFF)
733 GetFullPathNameA( file
, MAX_PATH
, buf
, NULL
);
734 /* Sigh. This error is *always* set, regardless of success */
735 MSVCRT__set_errno(ERROR_FILE_NOT_FOUND
);
739 /* Search given environment variable */
740 envVal
= MSVCRT_getenv(env
);
743 MSVCRT__set_errno(ERROR_FILE_NOT_FOUND
);
748 TRACE(":searching for %s in paths %s\n", file
, envVal
);
754 while(*end
&& *end
!= ';') end
++; /* Find end of next path */
755 if (penv
== end
|| !*penv
)
757 MSVCRT__set_errno(ERROR_FILE_NOT_FOUND
);
760 strncpy(curPath
, penv
, end
- penv
);
761 if (curPath
[end
- penv
] != '/' || curPath
[end
- penv
] != '\\')
763 curPath
[end
- penv
] = '\\';
764 curPath
[end
- penv
+ 1] = '\0';
767 curPath
[end
- penv
] = '\0';
769 strcat(curPath
, file
);
770 TRACE("Checking for file %s\n", curPath
);
771 if (GetFileAttributesA( curPath
) != 0xFFFFFFFF)
773 strcpy(buf
, curPath
);
774 MSVCRT__set_errno(ERROR_FILE_NOT_FOUND
);
777 penv
= *end
? end
+ 1 : end
;