Added an unknown VxD error code.
[wine] / dlls / msvcrt / dir.c
1 /*
2  * msvcrt.dll drive/directory functions
3  *
4  * Copyright 1996,1998 Marcus Meissner
5  * Copyright 1996 Jukka Iivonen
6  * Copyright 1997,2000 Uwe Bonnes
7  * Copyright 2000 Jon Griffiths
8  */
9
10 #include <time.h>
11 #include "ntddk.h"
12 #include "wine/unicode.h"
13 #include "msvcrt.h"
14 #include "ms_errno.h"
15
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"
22
23 DEFAULT_DEBUG_CHANNEL(msvcrt);
24
25 /* INTERNAL: Translate finddata_t to PWIN32_FIND_DATAA */
26 static void msvcrt_fttofd(LPWIN32_FIND_DATAA fd, struct _finddata_t* ft)
27 {
28   DWORD dw;
29
30   if (fd->dwFileAttributes == FILE_ATTRIBUTE_NORMAL)
31     ft->attrib = 0;
32   else
33     ft->attrib = fd->dwFileAttributes;
34
35   RtlTimeToSecondsSince1970( &fd->ftCreationTime, &dw );
36   ft->time_create = dw;
37   RtlTimeToSecondsSince1970( &fd->ftLastAccessTime, &dw );
38   ft->time_access = dw;
39   RtlTimeToSecondsSince1970( &fd->ftLastWriteTime, &dw );
40   ft->time_write = dw;
41   ft->size = fd->nFileSizeLow;
42   strcpy(ft->name, fd->cFileName);
43 }
44
45 /* INTERNAL: Translate wfinddata_t to PWIN32_FIND_DATAA */
46 static void msvcrt_wfttofd(LPWIN32_FIND_DATAW fd, struct _wfinddata_t* ft)
47 {
48   DWORD dw;
49
50   if (fd->dwFileAttributes == FILE_ATTRIBUTE_NORMAL)
51     ft->attrib = 0;
52   else
53     ft->attrib = fd->dwFileAttributes;
54
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   strcpyW(ft->name, fd->cFileName);
63 }
64
65 /*********************************************************************
66  *              _chdir (MSVCRT.@)
67  */
68 int _chdir(const char * newdir)
69 {
70   if (!SetCurrentDirectoryA(newdir))
71   {
72     MSVCRT__set_errno(newdir?GetLastError():0);
73     return -1;
74   }
75   return 0;
76 }
77
78 /*********************************************************************
79  *              _wchdir (MSVCRT.@)
80  */
81 int _wchdir(const WCHAR * newdir)
82 {
83   if (!SetCurrentDirectoryW(newdir))
84   {
85     MSVCRT__set_errno(newdir?GetLastError():0);
86     return -1;
87   }
88   return 0;
89 }
90
91 /*********************************************************************
92  *              _chdrive (MSVCRT.@)
93  */
94 int _chdrive(int newdrive)
95 {
96   char buffer[3] = "A:";
97   buffer[0] += newdrive - 1;
98   if (!SetCurrentDirectoryA( buffer ))
99   {
100     MSVCRT__set_errno(GetLastError());
101     if (newdrive <= 0)
102       SET_THREAD_VAR(errno,MSVCRT_EACCES);
103     return -1;
104   }
105   return 0;
106 }
107
108 /*********************************************************************
109  *              _findclose (MSVCRT.@)
110  */
111 int _findclose(long hand)
112 {
113   TRACE(":handle %ld\n",hand);
114   if (!FindClose((HANDLE)hand))
115   {
116     MSVCRT__set_errno(GetLastError());
117     return -1;
118   }
119   return 0;
120 }
121
122 /*********************************************************************
123  *              _findfirst (MSVCRT.@)
124  */
125 long _findfirst(const char * fspec, struct _finddata_t* ft)
126 {
127   WIN32_FIND_DATAA find_data;
128   HANDLE hfind;
129
130   hfind  = FindFirstFileA(fspec, &find_data);
131   if (hfind == INVALID_HANDLE_VALUE)
132   {
133     MSVCRT__set_errno(GetLastError());
134     return -1;
135   }
136   msvcrt_fttofd(&find_data,ft);
137   TRACE(":got handle %d\n",hfind);
138   return hfind;
139 }
140
141 /*********************************************************************
142  *              _wfindfirst (MSVCRT.@)
143  */
144 long _wfindfirst(const WCHAR * fspec, struct _wfinddata_t* ft)
145 {
146   WIN32_FIND_DATAW find_data;
147   HANDLE hfind;
148
149   hfind  = FindFirstFileW(fspec, &find_data);
150   if (hfind == INVALID_HANDLE_VALUE)
151   {
152     MSVCRT__set_errno(GetLastError());
153     return -1;
154   }
155   msvcrt_wfttofd(&find_data,ft);
156   TRACE(":got handle %d\n",hfind);
157   return hfind;
158 }
159
160 /*********************************************************************
161  *              _findnext (MSVCRT.@)
162  */
163 int _findnext(long hand, struct _finddata_t * ft)
164 {
165   WIN32_FIND_DATAA find_data;
166
167   if (!FindNextFileA(hand, &find_data))
168   {
169     SET_THREAD_VAR(errno,MSVCRT_ENOENT);
170     return -1;
171   }
172
173   msvcrt_fttofd(&find_data,ft);
174   return 0;
175 }
176
177 /*********************************************************************
178  *              _wfindnext (MSVCRT.@)
179  */
180 int _wfindnext(long hand, struct _wfinddata_t * ft)
181 {
182   WIN32_FIND_DATAW find_data;
183
184   if (!FindNextFileW(hand, &find_data))
185   {
186     SET_THREAD_VAR(errno,MSVCRT_ENOENT);
187     return -1;
188   }
189
190   msvcrt_wfttofd(&find_data,ft);
191   return 0;
192 }
193
194 /*********************************************************************
195  *              _getcwd (MSVCRT.@)
196  */
197 char* _getcwd(char * buf, int size)
198 {
199   char dir[_MAX_PATH];
200   int dir_len = GetCurrentDirectoryA(MAX_PATH,dir);
201
202   if (dir_len < 1)
203     return NULL; /* FIXME: Real return value untested */
204
205   if (!buf)
206   {
207     if (size < 0)
208       return _strdup(dir);
209     return msvcrt_strndup(dir,size);
210   }
211   if (dir_len >= size)
212   {
213     SET_THREAD_VAR(errno,MSVCRT_ERANGE);
214     return NULL; /* buf too small */
215   }
216   strcpy(buf,dir);
217   return buf;
218 }
219
220 /*********************************************************************
221  *              _wgetcwd (MSVCRT.@)
222  */
223 WCHAR* _wgetcwd(WCHAR * buf, int size)
224 {
225   WCHAR dir[_MAX_PATH];
226   int dir_len = GetCurrentDirectoryW(MAX_PATH,dir);
227
228   if (dir_len < 1)
229     return NULL; /* FIXME: Real return value untested */
230
231   if (!buf)
232   {
233     if (size < 0)
234       return _wcsdup(dir);
235     return msvcrt_wstrndup(dir,size);
236   }
237   if (dir_len >= size)
238   {
239     SET_THREAD_VAR(errno,MSVCRT_ERANGE);
240     return NULL; /* buf too small */
241   }
242   strcpyW(buf,dir);
243   return buf;
244 }
245
246 /*********************************************************************
247  *              _getdrive (MSVCRT.@)
248  */
249 int _getdrive(void)
250 {
251     char buffer[MAX_PATH];
252     if (!GetCurrentDirectoryA( sizeof(buffer), buffer )) return 0;
253     if (buffer[1] != ':') return 0;
254     return toupper(buffer[0]) - 'A' + 1;
255 }
256
257 /*********************************************************************
258  *              _getdcwd (MSVCRT.@)
259  */
260 char* _getdcwd(int drive, char * buf, int size)
261 {
262   static char* dummy;
263
264   TRACE(":drive %d(%c), size %d\n",drive, drive + 'A' - 1, size);
265
266   if (!drive || drive == _getdrive())
267     return _getcwd(buf,size); /* current */
268   else
269   {
270     char dir[_MAX_PATH];
271     char drivespec[4] = {'A', ':', '\\', 0};
272     int dir_len;
273
274     drivespec[0] += drive - 1;
275     if (GetDriveTypeA(drivespec) < DRIVE_REMOVABLE)
276     {
277       SET_THREAD_VAR(errno,MSVCRT_EACCES);
278       return NULL;
279     }
280
281     dir_len = GetFullPathNameA(drivespec,_MAX_PATH,dir,&dummy);
282     if (dir_len >= size || dir_len < 1)
283     {
284       SET_THREAD_VAR(errno,MSVCRT_ERANGE);
285       return NULL; /* buf too small */
286     }
287
288     TRACE(":returning '%s'\n", dir);
289     if (!buf)
290       return _strdup(dir); /* allocate */
291
292     strcpy(buf,dir);
293   }
294   return buf;
295 }
296
297 /*********************************************************************
298  *              _wgetdcwd (MSVCRT.@)
299  */
300 WCHAR* _wgetdcwd(int drive, WCHAR * buf, int size)
301 {
302   static WCHAR* dummy;
303
304   TRACE(":drive %d(%c), size %d\n",drive, drive + 'A' - 1, size);
305
306   if (!drive || drive == _getdrive())
307     return _wgetcwd(buf,size); /* current */
308   else
309   {
310     WCHAR dir[_MAX_PATH];
311     WCHAR drivespec[4] = {'A', ':', '\\', 0};
312     int dir_len;
313
314     drivespec[0] += drive - 1;
315     if (GetDriveTypeW(drivespec) < DRIVE_REMOVABLE)
316     {
317       SET_THREAD_VAR(errno,MSVCRT_EACCES);
318       return NULL;
319     }
320
321     dir_len = GetFullPathNameW(drivespec,_MAX_PATH,dir,&dummy);
322     if (dir_len >= size || dir_len < 1)
323     {
324       SET_THREAD_VAR(errno,MSVCRT_ERANGE);
325       return NULL; /* buf too small */
326     }
327
328     TRACE(":returning %s\n", debugstr_w(dir));
329     if (!buf)
330       return _wcsdup(dir); /* allocate */
331     strcpyW(buf,dir);
332   }
333   return buf;
334 }
335
336 /*********************************************************************
337  *              _getdiskfree (MSVCRT.@)
338  */
339 unsigned int _getdiskfree(unsigned int disk, struct _diskfree_t* d)
340 {
341   char drivespec[4] = {'@', ':', '\\', 0};
342   DWORD ret[4];
343   unsigned int err;
344
345   if (disk > 26)
346     return ERROR_INVALID_PARAMETER; /* MSVCRT doesn't set errno here */
347
348   drivespec[0] += disk; /* make a drive letter */
349
350   if (GetDiskFreeSpaceA(disk==0?NULL:drivespec,ret,ret+1,ret+2,ret+3))
351   {
352     d->sectors_per_cluster = (unsigned)ret[0];
353     d->bytes_per_sector = (unsigned)ret[1];
354     d->avail_clusters = (unsigned)ret[2];
355     d->total_clusters = (unsigned)ret[3];
356     return 0;
357   }
358   err = GetLastError();
359   MSVCRT__set_errno(err);
360   return err;
361 }
362
363 /*********************************************************************
364  *              _mkdir (MSVCRT.@)
365  */
366 int _mkdir(const char * newdir)
367 {
368   if (CreateDirectoryA(newdir,NULL))
369     return 0;
370   MSVCRT__set_errno(GetLastError());
371   return -1;
372 }
373
374 /*********************************************************************
375  *              _wmkdir (MSVCRT.@)
376  */
377 int _wmkdir(const WCHAR* newdir)
378 {
379   if (CreateDirectoryW(newdir,NULL))
380     return 0;
381   MSVCRT__set_errno(GetLastError());
382   return -1;
383 }
384
385 /*********************************************************************
386  *              _rmdir (MSVCRT.@)
387  */
388 int _rmdir(const char * dir)
389 {
390   if (RemoveDirectoryA(dir))
391     return 0;
392   MSVCRT__set_errno(GetLastError());
393   return -1;
394 }
395
396 /*********************************************************************
397  *              _wrmdir (MSVCRT.@)
398  */
399 int _wrmdir(const WCHAR * dir)
400 {
401   if (RemoveDirectoryW(dir))
402     return 0;
403   MSVCRT__set_errno(GetLastError());
404   return -1;
405 }
406
407 /*********************************************************************
408  *              _wsplitpath (MSVCRT.@)
409  */
410 void _wsplitpath(const WCHAR *inpath, WCHAR *drv, WCHAR *dir,
411                                 WCHAR *fname, WCHAR *ext )
412 {
413   /* Modified PD code from 'snippets' collection. */
414   WCHAR ch, *ptr, *p;
415   WCHAR pathbuff[MAX_PATH],*path=pathbuff;
416
417   TRACE(":splitting path %s\n",debugstr_w(path));
418   /* FIXME: Should be an strncpyW or something */
419   strcpyW(pathbuff, inpath);
420
421   /* convert slashes to backslashes for searching */
422   for (ptr = (WCHAR*)path; *ptr; ++ptr)
423     if (*ptr == (WCHAR)L'/')
424       *ptr = (WCHAR)L'\\';
425
426   /* look for drive spec */
427   if ((ptr = strchrW(path, (WCHAR)L':')) != (WCHAR)L'\0')
428   {
429     ++ptr;
430     if (drv)
431     {
432       strncpyW(drv, path, ptr - path);
433       drv[ptr - path] = (WCHAR)L'\0';
434     }
435     path = ptr;
436   }
437   else if (drv)
438     *drv = (WCHAR)L'\0';
439
440   /* find rightmost backslash or leftmost colon */
441   if ((ptr = strrchrW(path, (WCHAR)L'\\')) == NULL)
442     ptr = (strchrW(path, (WCHAR)L':'));
443
444   if (!ptr)
445   {
446     ptr = (WCHAR *)path; /* no path */
447     if (dir)
448       *dir = (WCHAR)L'\0';
449   }
450   else
451   {
452     ++ptr; /* skip the delimiter */
453     if (dir)
454     {
455       ch = *ptr;
456       *ptr = (WCHAR)L'\0';
457       strcpyW(dir, path);
458       *ptr = ch;
459     }
460   }
461
462   if ((p = strrchrW(ptr, (WCHAR)L'.')) == NULL)
463   {
464     if (fname)
465       strcpyW(fname, ptr);
466     if (ext)
467       *ext = (WCHAR)L'\0';
468   }
469   else
470   {
471     *p = (WCHAR)L'\0';
472     if (fname)
473       strcpyW(fname, ptr);
474     *p = (WCHAR)L'.';
475     if (ext)
476       strcpyW(ext, p);
477   }
478
479   /* Fix pathological case - Win returns ':' as part of the
480    * directory when no drive letter is given.
481    */
482   if (drv && drv[0] == (WCHAR)L':')
483   {
484     *drv = (WCHAR)L'\0';
485     if (dir)
486     {
487       pathbuff[0] = (WCHAR)L':';
488       pathbuff[1] = (WCHAR)L'\0';
489       strcatW(pathbuff,dir);
490       strcpyW(dir, pathbuff);
491     }
492   }
493 }
494
495 /* INTERNAL: Helper for _fullpath. Modified PD code from 'snippets'. */
496 static void msvcrt_fln_fix(char *path)
497 {
498   int dir_flag = 0, root_flag = 0;
499   char *r, *p, *q, *s;
500
501   /* Skip drive */
502   if (NULL == (r = strrchr(path, ':')))
503     r = path;
504   else
505     ++r;
506
507   /* Ignore leading slashes */
508   while ('\\' == *r)
509     if ('\\' == r[1])
510       strcpy(r, &r[1]);
511     else
512     {
513       root_flag = 1;
514       ++r;
515     }
516
517   p = r; /* Change "\\" to "\" */
518   while (NULL != (p = strchr(p, '\\')))
519     if ('\\' ==  p[1])
520       strcpy(p, &p[1]);
521     else
522       ++p;
523
524   while ('.' == *r) /* Scrunch leading ".\" */
525   {
526     if ('.' == r[1])
527     {
528       /* Ignore leading ".." */
529       for (p = (r += 2); *p && (*p != '\\'); ++p)
530         ;
531     }
532     else
533     {
534       for (p = r + 1 ;*p && (*p != '\\'); ++p)
535         ;
536     }
537     strcpy(r, p + ((*p) ? 1 : 0));
538   }
539
540   while ('\\' == path[strlen(path)-1])   /* Strip last '\\' */
541   {
542     dir_flag = 1;
543     path[strlen(path)-1] = '\0';
544   }
545
546   s = r;
547
548   /* Look for "\." in path */
549
550   while (NULL != (p = strstr(s, "\\.")))
551   {
552     if ('.' == p[2])
553     {
554       /* Execute this section if ".." found */
555       q = p - 1;
556       while (q > r)           /* Backup one level           */
557       {
558         if (*q == '\\')
559           break;
560         --q;
561       }
562       if (q > r)
563       {
564         strcpy(q, p + 3);
565         s = q;
566       }
567       else if ('.' != *q)
568       {
569         strcpy(q + ((*q == '\\') ? 1 : 0),
570                p + 3 + ((*(p + 3)) ? 1 : 0));
571         s = q;
572       }
573       else  s = ++p;
574     }
575     else
576     {
577       /* Execute this section if "." found */
578       q = p + 2;
579       for ( ;*q && (*q != '\\'); ++q)
580         ;
581       strcpy (p, q);
582     }
583   }
584
585   if (root_flag)  /* Embedded ".." could have bubbled up to root  */
586   {
587     for (p = r; *p && ('.' == *p || '\\' == *p); ++p)
588       ;
589     if (r != p)
590       strcpy(r, p);
591   }
592
593   if (dir_flag)
594     strcat(path, "\\");
595 }
596
597 /*********************************************************************
598  *              _fullpath (MSVCRT.@)
599  */
600 char *_fullpath(char * absPath, const char* relPath, unsigned int size)
601 {
602   char drive[5],dir[MAX_PATH],file[MAX_PATH],ext[MAX_PATH];
603   char res[MAX_PATH];
604   size_t len;
605
606   res[0] = '\0';
607
608   if (!relPath || !*relPath)
609     return _getcwd(absPath, size);
610
611   if (size < 4)
612   {
613     SET_THREAD_VAR(errno,MSVCRT_ERANGE);
614     return NULL;
615   }
616
617   TRACE(":resolving relative path '%s'\n",relPath);
618
619   _splitpath(relPath, drive, dir, file, ext);
620
621   /* Get Directory and drive into 'res' */
622   if (!dir[0] || (dir[0] != '/' && dir[0] != '\\'))
623   {
624     /* Relative or no directory given */
625     _getdcwd(drive[0] ? toupper(drive[0]) - 'A' + 1 :  0, res, MAX_PATH);
626     strcat(res,"\\");
627     if (dir[0])
628       strcat(res,dir);
629     if (drive[0])
630       res[0] = drive[0]; /* If given a drive, preserve the letter case */
631   }
632   else
633   {
634     strcpy(res,drive);
635     strcat(res,dir);
636   }
637
638   strcat(res,"\\");
639   strcat(res, file);
640   strcat(res, ext);
641   msvcrt_fln_fix(res);
642
643   len = strlen(res);
644   if (len >= MAX_PATH || len >= (size_t)size)
645     return NULL; /* FIXME: errno? */
646
647   if (!absPath)
648     return _strdup(res);
649   strcpy(absPath,res);
650   return absPath;
651 }
652
653 /*********************************************************************
654  *              _makepath (MSVCRT.@)
655  */
656 VOID _makepath(char * path, const char * drive,
657                               const char *directory, const char * filename,
658                               const char * extension )
659 {
660     char ch;
661     TRACE("got %s %s %s %s\n", drive, directory,
662           filename, extension);
663
664     if ( !path )
665         return;
666
667     path[0] = 0;
668     if (drive && drive[0])
669     {
670         path[0] = drive[0];
671         path[1] = ':';
672         path[2] = 0;
673     }
674     if (directory && directory[0])
675     {
676         strcat(path, directory);
677         ch = path[strlen(path)-1];
678         if (ch != '/' && ch != '\\')
679             strcat(path,"\\");
680     }
681     if (filename && filename[0])
682     {
683         strcat(path, filename);
684         if (extension && extension[0])
685         {
686             if ( extension[0] != '.' )
687                 strcat(path,".");
688             strcat(path,extension);
689         }
690     }
691
692     TRACE("returning %s\n",path);
693 }
694
695
696 /*********************************************************************
697  *              _searchenv (MSVCRT.@)
698  */
699 void _searchenv(const char* file, const char* env, char *buf)
700 {
701   char*envVal, *penv;
702   char curPath[MAX_PATH];
703
704   *buf = '\0';
705
706   /* Try CWD first */
707   if (GetFileAttributesA( file ) != 0xFFFFFFFF)
708   {
709     GetFullPathNameA( file, MAX_PATH, buf, NULL );
710     /* Sigh. This error is *always* set, regardless of success */
711     MSVCRT__set_errno(ERROR_FILE_NOT_FOUND);
712     return;
713   }
714
715   /* Search given environment variable */
716   envVal = MSVCRT_getenv(env);
717   if (!envVal)
718   {
719     MSVCRT__set_errno(ERROR_FILE_NOT_FOUND);
720     return;
721   }
722
723   penv = envVal;
724   TRACE(":searching for %s in paths %s\n", file, envVal);
725
726   do
727   {
728     char *end = penv;
729
730     while(*end && *end != ';') end++; /* Find end of next path */
731     if (penv == end || !*penv)
732     {
733       MSVCRT__set_errno(ERROR_FILE_NOT_FOUND);
734       return;
735     }
736     strncpy(curPath, penv, end - penv);
737     if (curPath[end - penv] != '/' || curPath[end - penv] != '\\')
738     {
739       curPath[end - penv] = '\\';
740       curPath[end - penv + 1] = '\0';
741     }
742     else
743       curPath[end - penv] = '\0';
744
745     strcat(curPath, file);
746     TRACE("Checking for file %s\n", curPath);
747     if (GetFileAttributesA( curPath ) != 0xFFFFFFFF)
748     {
749       strcpy(buf, curPath);
750       MSVCRT__set_errno(ERROR_FILE_NOT_FOUND);
751       return; /* Found */
752     }
753     penv = *end ? end + 1 : end;
754   } while(1);
755 }
756