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 const MSVCRT_wchar_t *p, *end;
439 if (inpath[0] && inpath[1] == ':')
449 else if (drv) drv[0] = 0;
451 /* look for end of directory part */
453 for (p = inpath; *p; p++) if (*p == '/' || *p == '\\') end = p + 1;
455 if (end) /* got a directory */
459 memcpy( dir, inpath, (end - inpath) * sizeof(MSVCRT_wchar_t) );
460 dir[end - inpath] = 0;
464 else if (dir) dir[0] = 0;
466 /* look for extension: what's after the last dot */
468 for (p = inpath; *p; p++) if (*p == '.') end = p;
470 if (!end) end = p; /* there's no extension */
474 memcpy( fname, inpath, (end - inpath) * sizeof(MSVCRT_wchar_t) );
475 fname[end - inpath] = 0;
477 if (ext) strcpyW( ext, end );
480 /* INTERNAL: Helper for _fullpath. Modified PD code from 'snippets'. */
481 static void wmsvcrt_fln_fix(MSVCRT_wchar_t *path)
483 int dir_flag = 0, root_flag = 0;
484 MSVCRT_wchar_t *r, *p, *q, *s;
485 MSVCRT_wchar_t szbsdot[] = { '\\', '.', 0 };
488 if (NULL == (r = strrchrW(path, ':')))
493 /* Ignore leading slashes */
503 p = r; /* Change "\\" to "\" */
504 while (NULL != (p = strchrW(p, '\\')))
510 while ('.' == *r) /* Scrunch leading ".\" */
514 /* Ignore leading ".." */
515 for (p = (r += 2); *p && (*p != '\\'); ++p)
520 for (p = r + 1 ;*p && (*p != '\\'); ++p)
523 strcpyW(r, p + ((*p) ? 1 : 0));
526 while ('\\' == path[strlenW(path)-1]) /* Strip last '\\' */
529 path[strlenW(path)-1] = '\0';
534 /* Look for "\." in path */
536 while (NULL != (p = strstrW(s, szbsdot)))
540 /* Execute this section if ".." found */
542 while (q > r) /* Backup one level */
555 strcpyW(q + ((*q == '\\') ? 1 : 0),
556 p + 3 + ((*(p + 3)) ? 1 : 0));
563 /* Execute this section if "." found */
565 for ( ;*q && (*q != '\\'); ++q)
571 if (root_flag) /* Embedded ".." could have bubbled up to root */
573 for (p = r; *p && ('.' == *p || '\\' == *p); ++p)
581 MSVCRT_wchar_t szbs[] = { '\\', 0 };
587 /*********************************************************************
588 * _wfullpath (MSVCRT.@)
590 MSVCRT_wchar_t *_wfullpath(MSVCRT_wchar_t * absPath, const MSVCRT_wchar_t* relPath, MSVCRT_size_t size)
592 MSVCRT_wchar_t drive[5],dir[MAX_PATH],file[MAX_PATH],ext[MAX_PATH];
593 MSVCRT_wchar_t res[MAX_PATH];
595 MSVCRT_wchar_t szbs[] = { '\\', 0 };
600 if (!relPath || !*relPath)
601 return _wgetcwd(absPath, size);
605 *MSVCRT__errno() = MSVCRT_ERANGE;
609 TRACE(":resolving relative path '%s'\n",debugstr_w(relPath));
611 _wsplitpath(relPath, drive, dir, file, ext);
613 /* Get Directory and drive into 'res' */
614 if (!dir[0] || (dir[0] != '/' && dir[0] != '\\'))
616 /* Relative or no directory given */
617 _wgetdcwd(drive[0] ? toupper(drive[0]) - 'A' + 1 : 0, res, MAX_PATH);
622 res[0] = drive[0]; /* If given a drive, preserve the letter case */
633 wmsvcrt_fln_fix(res);
636 if (len >= MAX_PATH || len >= (size_t)size)
637 return NULL; /* FIXME: errno? */
641 strcpyW(absPath,res);
645 /* INTERNAL: Helper for _fullpath. Modified PD code from 'snippets'. */
646 static void msvcrt_fln_fix(char *path)
648 int dir_flag = 0, root_flag = 0;
652 if (NULL == (r = strrchr(path, ':')))
657 /* Ignore leading slashes */
667 p = r; /* Change "\\" to "\" */
668 while (NULL != (p = strchr(p, '\\')))
674 while ('.' == *r) /* Scrunch leading ".\" */
678 /* Ignore leading ".." */
679 for (p = (r += 2); *p && (*p != '\\'); ++p)
684 for (p = r + 1 ;*p && (*p != '\\'); ++p)
687 strcpy(r, p + ((*p) ? 1 : 0));
690 while ('\\' == path[strlen(path)-1]) /* Strip last '\\' */
693 path[strlen(path)-1] = '\0';
698 /* Look for "\." in path */
700 while (NULL != (p = strstr(s, "\\.")))
704 /* Execute this section if ".." found */
706 while (q > r) /* Backup one level */
719 strcpy(q + ((*q == '\\') ? 1 : 0),
720 p + 3 + ((*(p + 3)) ? 1 : 0));
727 /* Execute this section if "." found */
729 for ( ;*q && (*q != '\\'); ++q)
735 if (root_flag) /* Embedded ".." could have bubbled up to root */
737 for (p = r; *p && ('.' == *p || '\\' == *p); ++p)
747 /*********************************************************************
748 * _fullpath (MSVCRT.@)
750 char *_fullpath(char * absPath, const char* relPath, unsigned int size)
752 char drive[5],dir[MAX_PATH],file[MAX_PATH],ext[MAX_PATH];
758 if (!relPath || !*relPath)
759 return _getcwd(absPath, size);
763 *MSVCRT__errno() = MSVCRT_ERANGE;
767 TRACE(":resolving relative path '%s'\n",relPath);
769 _splitpath(relPath, drive, dir, file, ext);
771 /* Get Directory and drive into 'res' */
772 if (!dir[0] || (dir[0] != '/' && dir[0] != '\\'))
774 /* Relative or no directory given */
775 _getdcwd(drive[0] ? toupper(drive[0]) - 'A' + 1 : 0, res, MAX_PATH);
780 res[0] = drive[0]; /* If given a drive, preserve the letter case */
794 if (len >= MAX_PATH || len >= (size_t)size)
795 return NULL; /* FIXME: errno? */
803 /*********************************************************************
804 * _makepath (MSVCRT.@)
806 VOID _makepath(char * path, const char * drive,
807 const char *directory, const char * filename,
808 const char * extension )
811 char tmpPath[MAX_PATH];
812 TRACE("got %s %s %s %s\n", debugstr_a(drive), debugstr_a(directory),
813 debugstr_a(filename), debugstr_a(extension) );
819 if (drive && drive[0])
821 tmpPath[0] = drive[0];
825 if (directory && directory[0])
827 strcat(tmpPath, directory);
828 ch = tmpPath[strlen(tmpPath)-1];
829 if (ch != '/' && ch != '\\')
830 strcat(tmpPath,"\\");
832 if (filename && filename[0])
834 strcat(tmpPath, filename);
835 if (extension && extension[0])
837 if ( extension[0] != '.' )
839 strcat(tmpPath,extension);
843 strcpy( path, tmpPath );
845 TRACE("returning %s\n",path);
848 /*********************************************************************
849 * _wmakepath (MSVCRT.@)
851 VOID _wmakepath(MSVCRT_wchar_t *path, const MSVCRT_wchar_t *drive, const MSVCRT_wchar_t *directory,
852 const MSVCRT_wchar_t *filename, const MSVCRT_wchar_t *extension)
855 TRACE("%s %s %s %s\n", debugstr_w(drive), debugstr_w(directory),
856 debugstr_w(filename), debugstr_w(extension));
862 if (drive && drive[0])
868 if (directory && directory[0])
870 strcatW(path, directory);
871 ch = path[strlenW(path) - 1];
872 if (ch != '/' && ch != '\\')
874 static const MSVCRT_wchar_t backslashW[] = {'\\',0};
875 strcatW(path, backslashW);
878 if (filename && filename[0])
880 strcatW(path, filename);
881 if (extension && extension[0])
883 if ( extension[0] != '.' )
885 static const MSVCRT_wchar_t dotW[] = {'.',0};
888 strcatW(path, extension);
892 TRACE("returning %s\n", debugstr_w(path));
895 /*********************************************************************
896 * _searchenv (MSVCRT.@)
898 void _searchenv(const char* file, const char* env, char *buf)
901 char curPath[MAX_PATH];
906 if (GetFileAttributesA( file ) != INVALID_FILE_ATTRIBUTES)
908 GetFullPathNameA( file, MAX_PATH, buf, NULL );
909 /* Sigh. This error is *always* set, regardless of success */
910 MSVCRT__set_errno(ERROR_FILE_NOT_FOUND);
914 /* Search given environment variable */
915 envVal = MSVCRT_getenv(env);
918 MSVCRT__set_errno(ERROR_FILE_NOT_FOUND);
923 TRACE(":searching for %s in paths %s\n", file, envVal);
929 while(*end && *end != ';') end++; /* Find end of next path */
930 if (penv == end || !*penv)
932 MSVCRT__set_errno(ERROR_FILE_NOT_FOUND);
935 strncpy(curPath, penv, end - penv);
936 if (curPath[end - penv] != '/' || curPath[end - penv] != '\\')
938 curPath[end - penv] = '\\';
939 curPath[end - penv + 1] = '\0';
942 curPath[end - penv] = '\0';
944 strcat(curPath, file);
945 TRACE("Checking for file %s\n", curPath);
946 if (GetFileAttributesA( curPath ) != INVALID_FILE_ATTRIBUTES)
948 strcpy(buf, curPath);
949 MSVCRT__set_errno(ERROR_FILE_NOT_FOUND);
952 penv = *end ? end + 1 : end;