- Migrate CRTDLL to MSVCRT.
[wine/hacks.git] / dlls / msvcrt / dir.c
blob84cdb625ed2fa4dc30c8ff103eb0ec8c2de3a7c4
1 /*
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
8 */
10 #include <time.h>
11 #include "ntddk.h"
12 #include "wine/unicode.h"
13 #include "msvcrt.h"
14 #include "ms_errno.h"
16 DEFAULT_DEBUG_CHANNEL(msvcrt);
18 typedef struct MSVCRT_finddata_t
20 unsigned attrib;
21 time_t time_create; /* -1 when N/A */
22 time_t time_access; /* -1 when N/A */
23 time_t time_write;
24 unsigned long size; /* FIXME: 64 bit ??*/
25 char name[MAX_PATH];
26 } MSVCRT_finddata_t;
28 typedef struct MSVCRT_wfinddata_t
30 unsigned attrib;
31 time_t time_create; /* -1 when N/A */
32 time_t time_access; /* -1 when N/A */
33 time_t time_write;
34 unsigned long size; /* FIXME: 64 bit ??*/
35 WCHAR name[MAX_PATH];
36 } MSVCRT_wfinddata_t;
38 typedef struct __MSVCRT_diskfree_t {
39 unsigned num_clusters;
40 unsigned available;
41 unsigned cluster_sectors;
42 unsigned sector_bytes;
43 } MSVCRT_diskfree_t;
45 /* INTERNAL: Translate finddata_t to PWIN32_FIND_DATAA */
46 static void MSVCRT__fttofd(LPWIN32_FIND_DATAA fd, MSVCRT_finddata_t* ft)
48 DWORD dw;
50 if (fd->dwFileAttributes == FILE_ATTRIBUTE_NORMAL)
51 ft->attrib = 0;
52 else
53 ft->attrib = fd->dwFileAttributes;
55 RtlTimeToSecondsSince1970( &fd->ftCreationTime, &dw );
56 ft->time_create = dw;
57 RtlTimeToSecondsSince1970( &fd->ftLastAccessTime, &dw );
58 ft->time_access = dw;
59 RtlTimeToSecondsSince1970( &fd->ftLastWriteTime, &dw );
60 ft->time_write = 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)
68 DWORD dw;
70 if (fd->dwFileAttributes == FILE_ATTRIBUTE_NORMAL)
71 ft->attrib = 0;
72 else
73 ft->attrib = fd->dwFileAttributes;
75 RtlTimeToSecondsSince1970( &fd->ftCreationTime, &dw );
76 ft->time_create = dw;
77 RtlTimeToSecondsSince1970( &fd->ftLastAccessTime, &dw );
78 ft->time_access = dw;
79 RtlTimeToSecondsSince1970( &fd->ftLastWriteTime, &dw );
80 ft->time_write = 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 /*********************************************************************
91 * _chdir (MSVCRT.@)
93 int __cdecl MSVCRT__chdir(const char * newdir)
95 if (!SetCurrentDirectoryA(newdir))
97 MSVCRT__set_errno(newdir?GetLastError():0);
98 return -1;
100 return 0;
103 /*********************************************************************
104 * _wchdir (MSVCRT.@)
106 int __cdecl MSVCRT__wchdir(const WCHAR * newdir)
108 if (!SetCurrentDirectoryW(newdir))
110 MSVCRT__set_errno(newdir?GetLastError():0);
111 return -1;
113 return 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());
126 if (newdrive <= 0)
127 SET_THREAD_VAR(errno,MSVCRT_EACCES);
128 return -1;
130 return 0;
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());
142 return -1;
144 return 0;
147 /*********************************************************************
148 * _findfirst (MSVCRT.@)
150 DWORD __cdecl MSVCRT__findfirst(const char * fspec, MSVCRT_finddata_t* ft)
152 WIN32_FIND_DATAA find_data;
153 HANDLE hfind;
155 hfind = FindFirstFileA(fspec, &find_data);
156 if (hfind == INVALID_HANDLE_VALUE)
158 MSVCRT__set_errno(GetLastError());
159 return -1;
161 MSVCRT__fttofd(&find_data,ft);
162 TRACE(":got handle %d\n",hfind);
163 return hfind;
166 /*********************************************************************
167 * _wfindfirst (MSVCRT.@)
169 DWORD __cdecl MSVCRT__wfindfirst(const WCHAR * fspec, MSVCRT_wfinddata_t* ft)
171 WIN32_FIND_DATAW find_data;
172 HANDLE hfind;
174 hfind = FindFirstFileW(fspec, &find_data);
175 if (hfind == INVALID_HANDLE_VALUE)
177 MSVCRT__set_errno(GetLastError());
178 return -1;
180 MSVCRT__wfttofd(&find_data,ft);
181 TRACE(":got handle %d\n",hfind);
182 return 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);
195 return -1;
198 MSVCRT__fttofd(&find_data,ft);
199 return 0;
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);
212 return -1;
215 MSVCRT__wfttofd(&find_data,ft);
216 return 0;
219 /*********************************************************************
220 * _getcwd (MSVCRT.@)
222 char* __cdecl MSVCRT__getcwd(char * buf, int size)
224 char dir[_MAX_PATH];
225 int dir_len = GetCurrentDirectoryA(MAX_PATH,dir);
227 if (dir_len < 1)
228 return NULL; /* FIXME: Real return value untested */
230 if (!buf)
232 if (size < 0)
233 return MSVCRT__strdup(dir);
234 return MSVCRT__strndup(dir,size);
236 if (dir_len >= size)
238 SET_THREAD_VAR(errno,MSVCRT_ERANGE);
239 return NULL; /* buf too small */
241 strcpy(buf,dir);
242 return buf;
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);
253 if (dir_len < 1)
254 return NULL; /* FIXME: Real return value untested */
256 if (!buf)
258 if (size < 0)
259 return MSVCRT__wcsdup(dir);
260 return MSVCRT__wstrndup(dir,size);
262 if (dir_len >= size)
264 SET_THREAD_VAR(errno,MSVCRT_ERANGE);
265 return NULL; /* buf too small */
267 strcpyW(buf,dir);
268 return buf;
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)
287 static char* dummy;
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 */
293 else
295 char dir[_MAX_PATH];
296 char drivespec[4] = {'A', ':', '\\', 0};
297 int dir_len;
299 drivespec[0] += drive - 1;
300 if (GetDriveTypeA(drivespec) < DRIVE_REMOVABLE)
302 SET_THREAD_VAR(errno,MSVCRT_EACCES);
303 return NULL;
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);
314 if (!buf)
315 return MSVCRT__strdup(dir); /* allocate */
317 strcpy(buf,dir);
319 return buf;
322 /*********************************************************************
323 * _wgetdcwd (MSVCRT.@)
325 WCHAR* __cdecl MSVCRT__wgetdcwd(int drive, WCHAR * buf, int size)
327 static WCHAR* dummy;
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 */
333 else
335 WCHAR dir[_MAX_PATH];
336 WCHAR drivespec[4] = {'A', ':', '\\', 0};
337 int dir_len;
339 drivespec[0] += drive - 1;
340 if (GetDriveTypeW(drivespec) < DRIVE_REMOVABLE)
342 SET_THREAD_VAR(errno,MSVCRT_EACCES);
343 return NULL;
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));
354 if (!buf)
355 return MSVCRT__wcsdup(dir); /* allocate */
356 strcpyW(buf,dir);
358 return buf;
361 /*********************************************************************
362 * _getdiskfree (MSVCRT.@)
364 unsigned int __cdecl MSVCRT__getdiskfree(unsigned int disk, MSVCRT_diskfree_t* d)
366 char drivespec[4] = {'@', ':', '\\', 0};
367 DWORD ret[4];
368 unsigned int err;
370 if (disk > 26)
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];
381 return 0;
383 err = GetLastError();
384 MSVCRT__set_errno(err);
385 return err;
388 /*********************************************************************
389 * _mkdir (MSVCRT.@)
391 int __cdecl MSVCRT__mkdir(const char * newdir)
393 if (CreateDirectoryA(newdir,NULL))
394 return 0;
395 MSVCRT__set_errno(GetLastError());
396 return -1;
399 /*********************************************************************
400 * _wmkdir (MSVCRT.@)
402 int __cdecl MSVCRT__wmkdir(const WCHAR* newdir)
404 if (CreateDirectoryW(newdir,NULL))
405 return 0;
406 MSVCRT__set_errno(GetLastError());
407 return -1;
410 /*********************************************************************
411 * _rmdir (MSVCRT.@)
413 int __cdecl MSVCRT__rmdir(const char * dir)
415 if (RemoveDirectoryA(dir))
416 return 0;
417 MSVCRT__set_errno(GetLastError());
418 return -1;
421 /*********************************************************************
422 * _wrmdir (MSVCRT.@)
424 int __cdecl MSVCRT__wrmdir(const WCHAR * dir)
426 if (RemoveDirectoryW(dir))
427 return 0;
428 MSVCRT__set_errno(GetLastError());
429 return -1;
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. */
439 char ch, *ptr, *p;
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)
447 if ('/' == *ptr)
448 *ptr = '\\';
450 /* look for drive spec */
451 if ('\0' != (ptr = strchr(path, ':')))
453 ++ptr;
454 if (drv)
456 strncpy(drv, path, ptr - path);
457 drv[ptr - path] = '\0';
459 path = ptr;
461 else if (drv)
462 *drv = '\0';
464 /* find rightmost backslash or leftmost colon */
465 if (NULL == (ptr = strrchr(path, '\\')))
466 ptr = (strchr(path, ':'));
468 if (!ptr)
470 ptr = (char *)path; /* no path */
471 if (dir)
472 *dir = '\0';
474 else
476 ++ptr; /* skip the delimiter */
477 if (dir)
479 ch = *ptr;
480 *ptr = '\0';
481 strcpy(dir, path);
482 *ptr = ch;
486 if (NULL == (p = strrchr(ptr, '.')))
488 if (fname)
489 strcpy(fname, ptr);
490 if (ext)
491 *ext = '\0';
493 else
495 *p = '\0';
496 if (fname)
497 strcpy(fname, ptr);
498 *p = '.';
499 if (ext)
500 strcpy(ext, p);
503 /* Fix pathological case - Win returns ':' as part of the
504 * directory when no drive letter is given.
506 if (drv && drv[0] == ':')
508 *drv = '\0';
509 if (dir)
511 pathbuff[0] = ':';
512 pathbuff[1] = '\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;
523 char *r, *p, *q, *s;
525 /* Skip drive */
526 if (NULL == (r = strrchr(path, ':')))
527 r = path;
528 else
529 ++r;
531 /* Ignore leading slashes */
532 while ('\\' == *r)
533 if ('\\' == r[1])
534 strcpy(r, &r[1]);
535 else
537 root_flag = 1;
538 ++r;
541 p = r; /* Change "\\" to "\" */
542 while (NULL != (p = strchr(p, '\\')))
543 if ('\\' == p[1])
544 strcpy(p, &p[1]);
545 else
546 ++p;
548 while ('.' == *r) /* Scrunch leading ".\" */
550 if ('.' == r[1])
552 /* Ignore leading ".." */
553 for (p = (r += 2); *p && (*p != '\\'); ++p)
556 else
558 for (p = r + 1 ;*p && (*p != '\\'); ++p)
561 strcpy(r, p + ((*p) ? 1 : 0));
564 while ('\\' == path[strlen(path)-1]) /* Strip last '\\' */
566 dir_flag = 1;
567 path[strlen(path)-1] = '\0';
570 s = r;
572 /* Look for "\." in path */
574 while (NULL != (p = strstr(s, "\\.")))
576 if ('.' == p[2])
578 /* Execute this section if ".." found */
579 q = p - 1;
580 while (q > r) /* Backup one level */
582 if (*q == '\\')
583 break;
584 --q;
586 if (q > r)
588 strcpy(q, p + 3);
589 s = q;
591 else if ('.' != *q)
593 strcpy(q + ((*q == '\\') ? 1 : 0),
594 p + 3 + ((*(p + 3)) ? 1 : 0));
595 s = q;
597 else s = ++p;
599 else
601 /* Execute this section if "." found */
602 q = p + 2;
603 for ( ;*q && (*q != '\\'); ++q)
605 strcpy (p, q);
609 if (root_flag) /* Embedded ".." could have bubbled up to root */
611 for (p = r; *p && ('.' == *p || '\\' == *p); ++p)
613 if (r != p)
614 strcpy(r, p);
617 if (dir_flag)
618 strcat(path, "\\");
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];
627 char res[MAX_PATH];
628 size_t len;
630 res[0] = '\0';
632 if (!relPath || !*relPath)
633 return MSVCRT__getcwd(absPath, size);
635 if (size < 4)
637 SET_THREAD_VAR(errno,MSVCRT_ERANGE);
638 return NULL;
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);
650 strcat(res,"\\");
651 if (dir[0])
652 strcat(res,dir);
653 if (drive[0])
654 res[0] = drive[0]; /* If given a drive, preserve the letter case */
656 else
658 strcpy(res,drive);
659 strcat(res,dir);
662 strcat(res,"\\");
663 strcat(res, file);
664 strcat(res, ext);
665 fln_fix(res);
667 len = strlen(res);
668 if (len >= MAX_PATH || len >= (size_t)size)
669 return NULL; /* FIXME: errno? */
671 if (!absPath)
672 return MSVCRT__strdup(res);
673 strcpy(absPath,res);
674 return absPath;
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 )
684 char ch;
685 TRACE("MSVCRT__makepath got %s %s %s %s\n", drive, directory,
686 filename, extension);
688 if ( !path )
689 return;
691 path[0] = 0;
692 if (drive && drive[0])
694 path[0] = drive[0];
695 path[1] = ':';
696 path[2] = 0;
698 if (directory && directory[0])
700 strcat(path, directory);
701 ch = path[strlen(path)-1];
702 if (ch != '/' && ch != '\\')
703 strcat(path,"\\");
705 if (filename && filename[0])
707 strcat(path, filename);
708 if (extension && extension[0])
710 if ( extension[0] != '.' )
711 strcat(path,".");
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)
725 char*envVal, *penv;
726 char curPath[MAX_PATH];
728 *buf = '\0';
730 /* Try CWD first */
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);
736 return;
739 /* Search given environment variable */
740 envVal = MSVCRT_getenv(env);
741 if (!envVal)
743 MSVCRT__set_errno(ERROR_FILE_NOT_FOUND);
744 return;
747 penv = envVal;
748 TRACE(":searching for %s in paths %s\n", file, envVal);
752 LPSTR end = penv;
754 while(*end && *end != ';') end++; /* Find end of next path */
755 if (penv == end || !*penv)
757 MSVCRT__set_errno(ERROR_FILE_NOT_FOUND);
758 return;
760 strncpy(curPath, penv, end - penv);
761 if (curPath[end - penv] != '/' || curPath[end - penv] != '\\')
763 curPath[end - penv] = '\\';
764 curPath[end - penv + 1] = '\0';
766 else
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);
775 return; /* Found */
777 penv = *end ? end + 1 : end;
778 } while(1);