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 #include "wine/unicode.h"
17 #include "msvcrt/direct.h"
18 #include "msvcrt/dos.h"
19 #include "msvcrt/io.h"
20 #include "msvcrt/stdlib.h"
21 #include "msvcrt/string.h"
23 #include "wine/debug.h"
25 WINE_DEFAULT_DEBUG_CHANNEL(msvcrt);
27 /* INTERNAL: Translate finddata_t to PWIN32_FIND_DATAA */
28 static void msvcrt_fttofd(LPWIN32_FIND_DATAA fd, struct _finddata_t* ft)
32 if (fd->dwFileAttributes == FILE_ATTRIBUTE_NORMAL)
35 ft->attrib = fd->dwFileAttributes;
37 RtlTimeToSecondsSince1970( &fd->ftCreationTime, &dw );
39 RtlTimeToSecondsSince1970( &fd->ftLastAccessTime, &dw );
41 RtlTimeToSecondsSince1970( &fd->ftLastWriteTime, &dw );
43 ft->size = fd->nFileSizeLow;
44 strcpy(ft->name, fd->cFileName);
47 /* INTERNAL: Translate wfinddata_t to PWIN32_FIND_DATAA */
48 static void msvcrt_wfttofd(LPWIN32_FIND_DATAW fd, struct _wfinddata_t* ft)
52 if (fd->dwFileAttributes == FILE_ATTRIBUTE_NORMAL)
55 ft->attrib = fd->dwFileAttributes;
57 RtlTimeToSecondsSince1970( &fd->ftCreationTime, &dw );
59 RtlTimeToSecondsSince1970( &fd->ftLastAccessTime, &dw );
61 RtlTimeToSecondsSince1970( &fd->ftLastWriteTime, &dw );
63 ft->size = fd->nFileSizeLow;
64 strcpyW(ft->name, fd->cFileName);
67 /*********************************************************************
70 int _chdir(const char * newdir)
72 if (!SetCurrentDirectoryA(newdir))
74 MSVCRT__set_errno(newdir?GetLastError():0);
80 /*********************************************************************
83 int _wchdir(const WCHAR * newdir)
85 if (!SetCurrentDirectoryW(newdir))
87 MSVCRT__set_errno(newdir?GetLastError():0);
93 /*********************************************************************
96 int _chdrive(int newdrive)
98 char buffer[3] = "A:";
99 buffer[0] += newdrive - 1;
100 if (!SetCurrentDirectoryA( buffer ))
102 MSVCRT__set_errno(GetLastError());
104 SET_THREAD_VAR(errno,MSVCRT_EACCES);
110 /*********************************************************************
111 * _findclose (MSVCRT.@)
113 int _findclose(long hand)
115 TRACE(":handle %ld\n",hand);
116 if (!FindClose((HANDLE)hand))
118 MSVCRT__set_errno(GetLastError());
124 /*********************************************************************
125 * _findfirst (MSVCRT.@)
127 long _findfirst(const char * fspec, struct _finddata_t* ft)
129 WIN32_FIND_DATAA find_data;
132 hfind = FindFirstFileA(fspec, &find_data);
133 if (hfind == INVALID_HANDLE_VALUE)
135 MSVCRT__set_errno(GetLastError());
138 msvcrt_fttofd(&find_data,ft);
139 TRACE(":got handle %d\n",hfind);
143 /*********************************************************************
144 * _wfindfirst (MSVCRT.@)
146 long _wfindfirst(const WCHAR * fspec, struct _wfinddata_t* ft)
148 WIN32_FIND_DATAW find_data;
151 hfind = FindFirstFileW(fspec, &find_data);
152 if (hfind == INVALID_HANDLE_VALUE)
154 MSVCRT__set_errno(GetLastError());
157 msvcrt_wfttofd(&find_data,ft);
158 TRACE(":got handle %d\n",hfind);
162 /*********************************************************************
163 * _findnext (MSVCRT.@)
165 int _findnext(long hand, struct _finddata_t * ft)
167 WIN32_FIND_DATAA find_data;
169 if (!FindNextFileA(hand, &find_data))
171 SET_THREAD_VAR(errno,MSVCRT_ENOENT);
175 msvcrt_fttofd(&find_data,ft);
179 /*********************************************************************
180 * _wfindnext (MSVCRT.@)
182 int _wfindnext(long hand, struct _wfinddata_t * ft)
184 WIN32_FIND_DATAW find_data;
186 if (!FindNextFileW(hand, &find_data))
188 SET_THREAD_VAR(errno,MSVCRT_ENOENT);
192 msvcrt_wfttofd(&find_data,ft);
196 /*********************************************************************
199 char* _getcwd(char * buf, int size)
202 int dir_len = GetCurrentDirectoryA(MAX_PATH,dir);
205 return NULL; /* FIXME: Real return value untested */
211 return msvcrt_strndup(dir,size);
215 SET_THREAD_VAR(errno,MSVCRT_ERANGE);
216 return NULL; /* buf too small */
222 /*********************************************************************
223 * _wgetcwd (MSVCRT.@)
225 WCHAR* _wgetcwd(WCHAR * buf, int size)
227 WCHAR dir[_MAX_PATH];
228 int dir_len = GetCurrentDirectoryW(MAX_PATH,dir);
231 return NULL; /* FIXME: Real return value untested */
237 return msvcrt_wstrndup(dir,size);
241 SET_THREAD_VAR(errno,MSVCRT_ERANGE);
242 return NULL; /* buf too small */
248 /*********************************************************************
249 * _getdrive (MSVCRT.@)
253 char buffer[MAX_PATH];
254 if (!GetCurrentDirectoryA( sizeof(buffer), buffer )) return 0;
255 if (buffer[1] != ':') return 0;
256 return toupper(buffer[0]) - 'A' + 1;
259 /*********************************************************************
260 * _getdcwd (MSVCRT.@)
262 char* _getdcwd(int drive, char * buf, int size)
266 TRACE(":drive %d(%c), size %d\n",drive, drive + 'A' - 1, size);
268 if (!drive || drive == _getdrive())
269 return _getcwd(buf,size); /* current */
273 char drivespec[4] = {'A', ':', '\\', 0};
276 drivespec[0] += drive - 1;
277 if (GetDriveTypeA(drivespec) < DRIVE_REMOVABLE)
279 SET_THREAD_VAR(errno,MSVCRT_EACCES);
283 dir_len = GetFullPathNameA(drivespec,_MAX_PATH,dir,&dummy);
284 if (dir_len >= size || dir_len < 1)
286 SET_THREAD_VAR(errno,MSVCRT_ERANGE);
287 return NULL; /* buf too small */
290 TRACE(":returning '%s'\n", dir);
292 return _strdup(dir); /* allocate */
299 /*********************************************************************
300 * _wgetdcwd (MSVCRT.@)
302 WCHAR* _wgetdcwd(int drive, WCHAR * buf, int size)
306 TRACE(":drive %d(%c), size %d\n",drive, drive + 'A' - 1, size);
308 if (!drive || drive == _getdrive())
309 return _wgetcwd(buf,size); /* current */
312 WCHAR dir[_MAX_PATH];
313 WCHAR drivespec[4] = {'A', ':', '\\', 0};
316 drivespec[0] += drive - 1;
317 if (GetDriveTypeW(drivespec) < DRIVE_REMOVABLE)
319 SET_THREAD_VAR(errno,MSVCRT_EACCES);
323 dir_len = GetFullPathNameW(drivespec,_MAX_PATH,dir,&dummy);
324 if (dir_len >= size || dir_len < 1)
326 SET_THREAD_VAR(errno,MSVCRT_ERANGE);
327 return NULL; /* buf too small */
330 TRACE(":returning %s\n", debugstr_w(dir));
332 return _wcsdup(dir); /* allocate */
338 /*********************************************************************
339 * _getdiskfree (MSVCRT.@)
341 unsigned int _getdiskfree(unsigned int disk, struct _diskfree_t* d)
343 char drivespec[4] = {'@', ':', '\\', 0};
348 return ERROR_INVALID_PARAMETER; /* MSVCRT doesn't set errno here */
350 drivespec[0] += disk; /* make a drive letter */
352 if (GetDiskFreeSpaceA(disk==0?NULL:drivespec,ret,ret+1,ret+2,ret+3))
354 d->sectors_per_cluster = (unsigned)ret[0];
355 d->bytes_per_sector = (unsigned)ret[1];
356 d->avail_clusters = (unsigned)ret[2];
357 d->total_clusters = (unsigned)ret[3];
360 err = GetLastError();
361 MSVCRT__set_errno(err);
365 /*********************************************************************
368 int _mkdir(const char * newdir)
370 if (CreateDirectoryA(newdir,NULL))
372 MSVCRT__set_errno(GetLastError());
376 /*********************************************************************
379 int _wmkdir(const WCHAR* newdir)
381 if (CreateDirectoryW(newdir,NULL))
383 MSVCRT__set_errno(GetLastError());
387 /*********************************************************************
390 int _rmdir(const char * dir)
392 if (RemoveDirectoryA(dir))
394 MSVCRT__set_errno(GetLastError());
398 /*********************************************************************
401 int _wrmdir(const WCHAR * dir)
403 if (RemoveDirectoryW(dir))
405 MSVCRT__set_errno(GetLastError());
409 /*********************************************************************
410 * _wsplitpath (MSVCRT.@)
412 void _wsplitpath(const WCHAR *inpath, WCHAR *drv, WCHAR *dir,
413 WCHAR *fname, WCHAR *ext )
415 /* Modified PD code from 'snippets' collection. */
417 WCHAR pathbuff[MAX_PATH],*path=pathbuff;
419 TRACE(":splitting path %s\n",debugstr_w(path));
420 /* FIXME: Should be an strncpyW or something */
421 strcpyW(pathbuff, inpath);
423 /* convert slashes to backslashes for searching */
424 for (ptr = (WCHAR*)path; *ptr; ++ptr)
425 if (*ptr == (WCHAR)L'/')
428 /* look for drive spec */
429 if ((ptr = strchrW(path, (WCHAR)L':')) != (WCHAR)L'\0')
434 strncpyW(drv, path, ptr - path);
435 drv[ptr - path] = (WCHAR)L'\0';
442 /* find rightmost backslash or leftmost colon */
443 if ((ptr = strrchrW(path, (WCHAR)L'\\')) == NULL)
444 ptr = (strchrW(path, (WCHAR)L':'));
448 ptr = (WCHAR *)path; /* no path */
454 ++ptr; /* skip the delimiter */
464 if ((p = strrchrW(ptr, (WCHAR)L'.')) == NULL)
481 /* Fix pathological case - Win returns ':' as part of the
482 * directory when no drive letter is given.
484 if (drv && drv[0] == (WCHAR)L':')
489 pathbuff[0] = (WCHAR)L':';
490 pathbuff[1] = (WCHAR)L'\0';
491 strcatW(pathbuff,dir);
492 strcpyW(dir, pathbuff);
497 /* INTERNAL: Helper for _fullpath. Modified PD code from 'snippets'. */
498 static void msvcrt_fln_fix(char *path)
500 int dir_flag = 0, root_flag = 0;
504 if (NULL == (r = strrchr(path, ':')))
509 /* Ignore leading slashes */
519 p = r; /* Change "\\" to "\" */
520 while (NULL != (p = strchr(p, '\\')))
526 while ('.' == *r) /* Scrunch leading ".\" */
530 /* Ignore leading ".." */
531 for (p = (r += 2); *p && (*p != '\\'); ++p)
536 for (p = r + 1 ;*p && (*p != '\\'); ++p)
539 strcpy(r, p + ((*p) ? 1 : 0));
542 while ('\\' == path[strlen(path)-1]) /* Strip last '\\' */
545 path[strlen(path)-1] = '\0';
550 /* Look for "\." in path */
552 while (NULL != (p = strstr(s, "\\.")))
556 /* Execute this section if ".." found */
558 while (q > r) /* Backup one level */
571 strcpy(q + ((*q == '\\') ? 1 : 0),
572 p + 3 + ((*(p + 3)) ? 1 : 0));
579 /* Execute this section if "." found */
581 for ( ;*q && (*q != '\\'); ++q)
587 if (root_flag) /* Embedded ".." could have bubbled up to root */
589 for (p = r; *p && ('.' == *p || '\\' == *p); ++p)
599 /*********************************************************************
600 * _fullpath (MSVCRT.@)
602 char *_fullpath(char * absPath, const char* relPath, unsigned int size)
604 char drive[5],dir[MAX_PATH],file[MAX_PATH],ext[MAX_PATH];
610 if (!relPath || !*relPath)
611 return _getcwd(absPath, size);
615 SET_THREAD_VAR(errno,MSVCRT_ERANGE);
619 TRACE(":resolving relative path '%s'\n",relPath);
621 _splitpath(relPath, drive, dir, file, ext);
623 /* Get Directory and drive into 'res' */
624 if (!dir[0] || (dir[0] != '/' && dir[0] != '\\'))
626 /* Relative or no directory given */
627 _getdcwd(drive[0] ? toupper(drive[0]) - 'A' + 1 : 0, res, MAX_PATH);
632 res[0] = drive[0]; /* If given a drive, preserve the letter case */
646 if (len >= MAX_PATH || len >= (size_t)size)
647 return NULL; /* FIXME: errno? */
655 /*********************************************************************
656 * _makepath (MSVCRT.@)
658 VOID _makepath(char * path, const char * drive,
659 const char *directory, const char * filename,
660 const char * extension )
663 TRACE("got %s %s %s %s\n", drive, directory,
664 filename, extension);
670 if (drive && drive[0])
676 if (directory && directory[0])
678 strcat(path, directory);
679 ch = path[strlen(path)-1];
680 if (ch != '/' && ch != '\\')
683 if (filename && filename[0])
685 strcat(path, filename);
686 if (extension && extension[0])
688 if ( extension[0] != '.' )
690 strcat(path,extension);
694 TRACE("returning %s\n",path);
697 /*********************************************************************
698 * _wmakepath (MSVCRT.@)
700 VOID _wmakepath(WCHAR *path, const WCHAR *drive, const WCHAR *directory,
701 const WCHAR *filename, const WCHAR *extension)
704 TRACE("%s %s %s %s\n", debugstr_w(drive), debugstr_w(directory),
705 debugstr_w(filename), debugstr_w(extension));
711 if (drive && drive[0])
717 if (directory && directory[0])
719 strcatW(path, directory);
720 ch = path[strlenW(path) - 1];
721 if (ch != '/' && ch != '\\')
723 static const WCHAR backslashW[] = {'\\',0};
724 strcatW(path, backslashW);
727 if (filename && filename[0])
729 strcatW(path, filename);
730 if (extension && extension[0])
732 if ( extension[0] != '.' )
734 static const WCHAR dotW[] = {'.',0};
737 strcatW(path, extension);
741 TRACE("returning %s\n", debugstr_w(path));
744 /*********************************************************************
745 * _searchenv (MSVCRT.@)
747 void _searchenv(const char* file, const char* env, char *buf)
750 char curPath[MAX_PATH];
755 if (GetFileAttributesA( file ) != 0xFFFFFFFF)
757 GetFullPathNameA( file, MAX_PATH, buf, NULL );
758 /* Sigh. This error is *always* set, regardless of success */
759 MSVCRT__set_errno(ERROR_FILE_NOT_FOUND);
763 /* Search given environment variable */
764 envVal = MSVCRT_getenv(env);
767 MSVCRT__set_errno(ERROR_FILE_NOT_FOUND);
772 TRACE(":searching for %s in paths %s\n", file, envVal);
778 while(*end && *end != ';') end++; /* Find end of next path */
779 if (penv == end || !*penv)
781 MSVCRT__set_errno(ERROR_FILE_NOT_FOUND);
784 strncpy(curPath, penv, end - penv);
785 if (curPath[end - penv] != '/' || curPath[end - penv] != '\\')
787 curPath[end - penv] = '\\';
788 curPath[end - penv + 1] = '\0';
791 curPath[end - penv] = '\0';
793 strcat(curPath, file);
794 TRACE("Checking for file %s\n", curPath);
795 if (GetFileAttributesA( curPath ) != 0xFFFFFFFF)
797 strcpy(buf, curPath);
798 MSVCRT__set_errno(ERROR_FILE_NOT_FOUND);
801 penv = *end ? end + 1 : end;