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
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 #include "wine/port.h"
34 #include "wine/unicode.h"
36 #include "msvcrt/errno.h"
38 #include "wine/unicode.h"
39 #include "msvcrt/direct.h"
40 #include "msvcrt/dos.h"
41 #include "msvcrt/io.h"
42 #include "msvcrt/stdlib.h"
43 #include "msvcrt/string.h"
45 #include "wine/debug.h"
47 WINE_DEFAULT_DEBUG_CHANNEL(msvcrt);
49 /* INTERNAL: Translate finddata_t to PWIN32_FIND_DATAA */
50 static void msvcrt_fttofd(LPWIN32_FIND_DATAA fd, struct _finddata_t* ft)
54 if (fd->dwFileAttributes == FILE_ATTRIBUTE_NORMAL)
57 ft->attrib = fd->dwFileAttributes;
59 RtlTimeToSecondsSince1970( (LARGE_INTEGER *)&fd->ftCreationTime, &dw );
61 RtlTimeToSecondsSince1970( (LARGE_INTEGER *)&fd->ftLastAccessTime, &dw );
63 RtlTimeToSecondsSince1970( (LARGE_INTEGER *)&fd->ftLastWriteTime, &dw );
65 ft->size = fd->nFileSizeLow;
66 strcpy(ft->name, fd->cFileName);
69 /* INTERNAL: Translate wfinddata_t to PWIN32_FIND_DATAA */
70 static void msvcrt_wfttofd(LPWIN32_FIND_DATAW fd, struct _wfinddata_t* ft)
74 if (fd->dwFileAttributes == FILE_ATTRIBUTE_NORMAL)
77 ft->attrib = fd->dwFileAttributes;
79 RtlTimeToSecondsSince1970( (LARGE_INTEGER *)&fd->ftCreationTime, &dw );
81 RtlTimeToSecondsSince1970( (LARGE_INTEGER *)&fd->ftLastAccessTime, &dw );
83 RtlTimeToSecondsSince1970( (LARGE_INTEGER *)&fd->ftLastWriteTime, &dw );
85 ft->size = fd->nFileSizeLow;
86 strcpyW(ft->name, fd->cFileName);
89 /*********************************************************************
92 int _chdir(const char * newdir)
94 if (!SetCurrentDirectoryA(newdir))
96 MSVCRT__set_errno(newdir?GetLastError():0);
102 /*********************************************************************
105 int _wchdir(const MSVCRT_wchar_t * newdir)
107 if (!SetCurrentDirectoryW(newdir))
109 MSVCRT__set_errno(newdir?GetLastError():0);
115 /*********************************************************************
116 * _chdrive (MSVCRT.@)
118 int _chdrive(int newdrive)
120 char buffer[3] = "A:";
121 buffer[0] += newdrive - 1;
122 if (!SetCurrentDirectoryA( buffer ))
124 MSVCRT__set_errno(GetLastError());
126 *MSVCRT__errno() = MSVCRT_EACCES;
132 /*********************************************************************
133 * _findclose (MSVCRT.@)
135 int _findclose(long hand)
137 TRACE(":handle %ld\n",hand);
138 if (!FindClose((HANDLE)hand))
140 MSVCRT__set_errno(GetLastError());
146 /*********************************************************************
147 * _findfirst (MSVCRT.@)
149 long _findfirst(const char * fspec, struct _finddata_t* ft)
151 WIN32_FIND_DATAA find_data;
154 hfind = FindFirstFileA(fspec, &find_data);
155 if (hfind == INVALID_HANDLE_VALUE)
157 MSVCRT__set_errno(GetLastError());
160 msvcrt_fttofd(&find_data,ft);
161 TRACE(":got handle %p\n",hfind);
165 /*********************************************************************
166 * _wfindfirst (MSVCRT.@)
168 long _wfindfirst(const MSVCRT_wchar_t * fspec, struct _wfinddata_t* ft)
170 WIN32_FIND_DATAW find_data;
173 hfind = FindFirstFileW(fspec, &find_data);
174 if (hfind == INVALID_HANDLE_VALUE)
176 MSVCRT__set_errno(GetLastError());
179 msvcrt_wfttofd(&find_data,ft);
180 TRACE(":got handle %p\n",hfind);
184 /*********************************************************************
185 * _findnext (MSVCRT.@)
187 int _findnext(long hand, struct _finddata_t * ft)
189 WIN32_FIND_DATAA find_data;
191 if (!FindNextFileA((HANDLE)hand, &find_data))
193 *MSVCRT__errno() = MSVCRT_ENOENT;
197 msvcrt_fttofd(&find_data,ft);
201 /*********************************************************************
202 * _wfindnext (MSVCRT.@)
204 int _wfindnext(long hand, struct _wfinddata_t * ft)
206 WIN32_FIND_DATAW find_data;
208 if (!FindNextFileW((HANDLE)hand, &find_data))
210 *MSVCRT__errno() = MSVCRT_ENOENT;
214 msvcrt_wfttofd(&find_data,ft);
218 /*********************************************************************
221 char* _getcwd(char * buf, int size)
224 int dir_len = GetCurrentDirectoryA(MAX_PATH,dir);
227 return NULL; /* FIXME: Real return value untested */
233 return msvcrt_strndup(dir,size);
237 *MSVCRT__errno() = MSVCRT_ERANGE;
238 return NULL; /* buf too small */
244 /*********************************************************************
245 * _wgetcwd (MSVCRT.@)
247 MSVCRT_wchar_t* _wgetcwd(MSVCRT_wchar_t * buf, int size)
249 MSVCRT_wchar_t dir[MAX_PATH];
250 int dir_len = GetCurrentDirectoryW(MAX_PATH,dir);
253 return NULL; /* FIXME: Real return value untested */
259 return msvcrt_wstrndup(dir,size);
263 *MSVCRT__errno() = MSVCRT_ERANGE;
264 return NULL; /* buf too small */
270 /*********************************************************************
271 * _getdrive (MSVCRT.@)
275 char buffer[MAX_PATH];
276 if (!GetCurrentDirectoryA( sizeof(buffer), buffer )) return 0;
277 if (buffer[1] != ':') return 0;
278 return toupper(buffer[0]) - 'A' + 1;
281 /*********************************************************************
282 * _getdcwd (MSVCRT.@)
284 char* _getdcwd(int drive, char * buf, int size)
288 TRACE(":drive %d(%c), size %d\n",drive, drive + 'A' - 1, size);
290 if (!drive || drive == _getdrive())
291 return _getcwd(buf,size); /* current */
295 char drivespec[4] = {'A', ':', '\\', 0};
298 drivespec[0] += drive - 1;
299 if (GetDriveTypeA(drivespec) < DRIVE_REMOVABLE)
301 *MSVCRT__errno() = MSVCRT_EACCES;
305 dir_len = GetFullPathNameA(drivespec,MAX_PATH,dir,&dummy);
306 if (dir_len >= size || dir_len < 1)
308 *MSVCRT__errno() = MSVCRT_ERANGE;
309 return NULL; /* buf too small */
312 TRACE(":returning '%s'\n", dir);
314 return _strdup(dir); /* allocate */
321 /*********************************************************************
322 * _wgetdcwd (MSVCRT.@)
324 MSVCRT_wchar_t* _wgetdcwd(int drive, MSVCRT_wchar_t * buf, int size)
326 static MSVCRT_wchar_t* dummy;
328 TRACE(":drive %d(%c), size %d\n",drive, drive + 'A' - 1, size);
330 if (!drive || drive == _getdrive())
331 return _wgetcwd(buf,size); /* current */
334 MSVCRT_wchar_t dir[MAX_PATH];
335 MSVCRT_wchar_t drivespec[4] = {'A', ':', '\\', 0};
338 drivespec[0] += drive - 1;
339 if (GetDriveTypeW(drivespec) < DRIVE_REMOVABLE)
341 *MSVCRT__errno() = MSVCRT_EACCES;
345 dir_len = GetFullPathNameW(drivespec,MAX_PATH,dir,&dummy);
346 if (dir_len >= size || dir_len < 1)
348 *MSVCRT__errno() = MSVCRT_ERANGE;
349 return NULL; /* buf too small */
352 TRACE(":returning %s\n", debugstr_w(dir));
354 return _wcsdup(dir); /* allocate */
360 /*********************************************************************
361 * _getdiskfree (MSVCRT.@)
363 unsigned int _getdiskfree(unsigned int disk, struct _diskfree_t* d)
365 char drivespec[4] = {'@', ':', '\\', 0};
370 return ERROR_INVALID_PARAMETER; /* MSVCRT doesn't set errno here */
372 drivespec[0] += disk; /* make a drive letter */
374 if (GetDiskFreeSpaceA(disk==0?NULL:drivespec,ret,ret+1,ret+2,ret+3))
376 d->sectors_per_cluster = (unsigned)ret[0];
377 d->bytes_per_sector = (unsigned)ret[1];
378 d->avail_clusters = (unsigned)ret[2];
379 d->total_clusters = (unsigned)ret[3];
382 err = GetLastError();
383 MSVCRT__set_errno(err);
387 /*********************************************************************
390 int _mkdir(const char * newdir)
392 if (CreateDirectoryA(newdir,NULL))
394 MSVCRT__set_errno(GetLastError());
398 /*********************************************************************
401 int _wmkdir(const MSVCRT_wchar_t* newdir)
403 if (CreateDirectoryW(newdir,NULL))
405 MSVCRT__set_errno(GetLastError());
409 /*********************************************************************
412 int _rmdir(const char * dir)
414 if (RemoveDirectoryA(dir))
416 MSVCRT__set_errno(GetLastError());
420 /*********************************************************************
423 int _wrmdir(const MSVCRT_wchar_t * dir)
425 if (RemoveDirectoryW(dir))
427 MSVCRT__set_errno(GetLastError());
431 /*********************************************************************
432 * _wsplitpath (MSVCRT.@)
434 void _wsplitpath(const MSVCRT_wchar_t *inpath, MSVCRT_wchar_t *drv, MSVCRT_wchar_t *dir,
435 MSVCRT_wchar_t *fname, MSVCRT_wchar_t *ext )
437 /* Modified PD code from 'snippets' collection. */
438 MSVCRT_wchar_t ch, *ptr, *p;
439 MSVCRT_wchar_t pathbuff[MAX_PATH],*path=pathbuff;
441 /* FIXME: Should be an strncpyW or something */
442 strcpyW(pathbuff, inpath);
443 TRACE(":splitting path %s\n",debugstr_w(path));
445 /* convert slashes to backslashes for searching */
446 for (ptr = (MSVCRT_wchar_t*)path; *ptr; ++ptr)
450 /* look for drive spec */
451 if ((ptr = strchrW(path, ':')) != 0)
456 strncpyW(drv, path, ptr - path);
464 /* find rightmost backslash or leftmost colon */
465 if ((ptr = strrchrW(path, '\\')) == NULL)
466 ptr = (strchrW(path, ':'));
470 ptr = (MSVCRT_wchar_t *)path; /* no path */
476 ++ptr; /* skip the delimiter */
486 if ((p = strrchrW(ptr, '.')) == NULL)
503 /* Fix pathological case - Win returns ':' as part of the
504 * directory when no drive letter is given.
506 if (drv && drv[0] == ':')
513 strcatW(pathbuff,dir);
514 strcpyW(dir, pathbuff);
519 /* INTERNAL: Helper for _fullpath. Modified PD code from 'snippets'. */
520 static void wmsvcrt_fln_fix(MSVCRT_wchar_t *path)
522 int dir_flag = 0, root_flag = 0;
523 MSVCRT_wchar_t *r, *p, *q, *s;
524 MSVCRT_wchar_t szbsdot[] = { '\\', '.', 0 };
527 if (NULL == (r = strrchrW(path, ':')))
532 /* Ignore leading slashes */
542 p = r; /* Change "\\" to "\" */
543 while (NULL != (p = strchrW(p, '\\')))
549 while ('.' == *r) /* Scrunch leading ".\" */
553 /* Ignore leading ".." */
554 for (p = (r += 2); *p && (*p != '\\'); ++p)
559 for (p = r + 1 ;*p && (*p != '\\'); ++p)
562 strcpyW(r, p + ((*p) ? 1 : 0));
565 while ('\\' == path[strlenW(path)-1]) /* Strip last '\\' */
568 path[strlenW(path)-1] = '\0';
573 /* Look for "\." in path */
575 while (NULL != (p = strstrW(s, szbsdot)))
579 /* Execute this section if ".." found */
581 while (q > r) /* Backup one level */
594 strcpyW(q + ((*q == '\\') ? 1 : 0),
595 p + 3 + ((*(p + 3)) ? 1 : 0));
602 /* Execute this section if "." found */
604 for ( ;*q && (*q != '\\'); ++q)
610 if (root_flag) /* Embedded ".." could have bubbled up to root */
612 for (p = r; *p && ('.' == *p || '\\' == *p); ++p)
620 MSVCRT_wchar_t szbs[] = { '\\', 0 };
626 /*********************************************************************
627 * _wfullpath (MSVCRT.@)
629 MSVCRT_wchar_t *_wfullpath(MSVCRT_wchar_t * absPath, const MSVCRT_wchar_t* relPath, unsigned int size)
631 MSVCRT_wchar_t drive[5],dir[MAX_PATH],file[MAX_PATH],ext[MAX_PATH];
632 MSVCRT_wchar_t res[MAX_PATH];
634 MSVCRT_wchar_t szbs[] = { '\\', 0 };
639 if (!relPath || !*relPath)
640 return _wgetcwd(absPath, size);
644 *MSVCRT__errno() = MSVCRT_ERANGE;
648 TRACE(":resolving relative path '%s'\n",debugstr_w(relPath));
650 _wsplitpath(relPath, drive, dir, file, ext);
652 /* Get Directory and drive into 'res' */
653 if (!dir[0] || (dir[0] != '/' && dir[0] != '\\'))
655 /* Relative or no directory given */
656 _wgetdcwd(drive[0] ? toupper(drive[0]) - 'A' + 1 : 0, res, MAX_PATH);
661 res[0] = drive[0]; /* If given a drive, preserve the letter case */
672 wmsvcrt_fln_fix(res);
675 if (len >= MAX_PATH || len >= (size_t)size)
676 return NULL; /* FIXME: errno? */
680 strcpyW(absPath,res);
684 /* INTERNAL: Helper for _fullpath. Modified PD code from 'snippets'. */
685 static void msvcrt_fln_fix(char *path)
687 int dir_flag = 0, root_flag = 0;
691 if (NULL == (r = strrchr(path, ':')))
696 /* Ignore leading slashes */
706 p = r; /* Change "\\" to "\" */
707 while (NULL != (p = strchr(p, '\\')))
713 while ('.' == *r) /* Scrunch leading ".\" */
717 /* Ignore leading ".." */
718 for (p = (r += 2); *p && (*p != '\\'); ++p)
723 for (p = r + 1 ;*p && (*p != '\\'); ++p)
726 strcpy(r, p + ((*p) ? 1 : 0));
729 while ('\\' == path[strlen(path)-1]) /* Strip last '\\' */
732 path[strlen(path)-1] = '\0';
737 /* Look for "\." in path */
739 while (NULL != (p = strstr(s, "\\.")))
743 /* Execute this section if ".." found */
745 while (q > r) /* Backup one level */
758 strcpy(q + ((*q == '\\') ? 1 : 0),
759 p + 3 + ((*(p + 3)) ? 1 : 0));
766 /* Execute this section if "." found */
768 for ( ;*q && (*q != '\\'); ++q)
774 if (root_flag) /* Embedded ".." could have bubbled up to root */
776 for (p = r; *p && ('.' == *p || '\\' == *p); ++p)
786 /*********************************************************************
787 * _fullpath (MSVCRT.@)
789 char *_fullpath(char * absPath, const char* relPath, unsigned int size)
791 char drive[5],dir[MAX_PATH],file[MAX_PATH],ext[MAX_PATH];
797 if (!relPath || !*relPath)
798 return _getcwd(absPath, size);
802 *MSVCRT__errno() = MSVCRT_ERANGE;
806 TRACE(":resolving relative path '%s'\n",relPath);
808 _splitpath(relPath, drive, dir, file, ext);
810 /* Get Directory and drive into 'res' */
811 if (!dir[0] || (dir[0] != '/' && dir[0] != '\\'))
813 /* Relative or no directory given */
814 _getdcwd(drive[0] ? toupper(drive[0]) - 'A' + 1 : 0, res, MAX_PATH);
819 res[0] = drive[0]; /* If given a drive, preserve the letter case */
833 if (len >= MAX_PATH || len >= (size_t)size)
834 return NULL; /* FIXME: errno? */
842 /*********************************************************************
843 * _makepath (MSVCRT.@)
845 VOID _makepath(char * path, const char * drive,
846 const char *directory, const char * filename,
847 const char * extension )
850 char tmpPath[MAX_PATH];
851 TRACE("got %s %s %s %s\n", debugstr_a(drive), debugstr_a(directory),
852 debugstr_a(filename), debugstr_a(extension) );
858 if (drive && drive[0])
860 tmpPath[0] = drive[0];
864 if (directory && directory[0])
866 strcat(tmpPath, directory);
867 ch = tmpPath[strlen(tmpPath)-1];
868 if (ch != '/' && ch != '\\')
869 strcat(tmpPath,"\\");
871 if (filename && filename[0])
873 strcat(tmpPath, filename);
874 if (extension && extension[0])
876 if ( extension[0] != '.' )
878 strcat(tmpPath,extension);
882 strcpy( path, tmpPath );
884 TRACE("returning %s\n",path);
887 /*********************************************************************
888 * _wmakepath (MSVCRT.@)
890 VOID _wmakepath(MSVCRT_wchar_t *path, const MSVCRT_wchar_t *drive, const MSVCRT_wchar_t *directory,
891 const MSVCRT_wchar_t *filename, const MSVCRT_wchar_t *extension)
894 TRACE("%s %s %s %s\n", debugstr_w(drive), debugstr_w(directory),
895 debugstr_w(filename), debugstr_w(extension));
901 if (drive && drive[0])
907 if (directory && directory[0])
909 strcatW(path, directory);
910 ch = path[strlenW(path) - 1];
911 if (ch != '/' && ch != '\\')
913 static const MSVCRT_wchar_t backslashW[] = {'\\',0};
914 strcatW(path, backslashW);
917 if (filename && filename[0])
919 strcatW(path, filename);
920 if (extension && extension[0])
922 if ( extension[0] != '.' )
924 static const MSVCRT_wchar_t dotW[] = {'.',0};
927 strcatW(path, extension);
931 TRACE("returning %s\n", debugstr_w(path));
934 /*********************************************************************
935 * _searchenv (MSVCRT.@)
937 void _searchenv(const char* file, const char* env, char *buf)
940 char curPath[MAX_PATH];
945 if (GetFileAttributesA( file ) != 0xFFFFFFFF)
947 GetFullPathNameA( file, MAX_PATH, buf, NULL );
948 /* Sigh. This error is *always* set, regardless of success */
949 MSVCRT__set_errno(ERROR_FILE_NOT_FOUND);
953 /* Search given environment variable */
954 envVal = MSVCRT_getenv(env);
957 MSVCRT__set_errno(ERROR_FILE_NOT_FOUND);
962 TRACE(":searching for %s in paths %s\n", file, envVal);
968 while(*end && *end != ';') end++; /* Find end of next path */
969 if (penv == end || !*penv)
971 MSVCRT__set_errno(ERROR_FILE_NOT_FOUND);
974 strncpy(curPath, penv, end - penv);
975 if (curPath[end - penv] != '/' || curPath[end - penv] != '\\')
977 curPath[end - penv] = '\\';
978 curPath[end - penv + 1] = '\0';
981 curPath[end - penv] = '\0';
983 strcat(curPath, file);
984 TRACE("Checking for file %s\n", curPath);
985 if (GetFileAttributesA( curPath ) != 0xFFFFFFFF)
987 strcpy(buf, curPath);
988 MSVCRT__set_errno(ERROR_FILE_NOT_FOUND);
991 penv = *end ? end + 1 : end;