A few small fixes.
[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  * 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.
13  *
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.
18  *
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
22  */
23
24 #include "config.h"
25 #include "wine/port.h"
26
27 #include <stdarg.h>
28 #include <time.h>
29
30 #include "windef.h"
31 #include "winbase.h"
32 #include "winreg.h"
33 #include "winternl.h"
34 #include "wine/unicode.h"
35 #include "msvcrt.h"
36 #include "msvcrt/errno.h"
37
38 #include "wine/unicode.h"
39 #include "msvcrt/io.h"
40 #include "msvcrt/stdlib.h"
41 #include "msvcrt/string.h"
42 #include "msvcrt/dos.h"
43
44 #include "wine/debug.h"
45
46 WINE_DEFAULT_DEBUG_CHANNEL(msvcrt);
47
48 /* INTERNAL: Translate WIN32_FIND_DATAA to finddata_t  */
49 static void msvcrt_fttofd( const WIN32_FIND_DATAA *fd, struct MSVCRT__finddata_t* ft)
50 {
51   DWORD dw;
52
53   if (fd->dwFileAttributes == FILE_ATTRIBUTE_NORMAL)
54     ft->attrib = 0;
55   else
56     ft->attrib = fd->dwFileAttributes;
57
58   RtlTimeToSecondsSince1970( (LARGE_INTEGER *)&fd->ftCreationTime, &dw );
59   ft->time_create = dw;
60   RtlTimeToSecondsSince1970( (LARGE_INTEGER *)&fd->ftLastAccessTime, &dw );
61   ft->time_access = dw;
62   RtlTimeToSecondsSince1970( (LARGE_INTEGER *)&fd->ftLastWriteTime, &dw );
63   ft->time_write = dw;
64   ft->size = fd->nFileSizeLow;
65   strcpy(ft->name, fd->cFileName);
66 }
67
68 /* INTERNAL: Translate WIN32_FIND_DATAW to wfinddata_t  */
69 static void msvcrt_wfttofd( const WIN32_FIND_DATAW *fd, struct MSVCRT__wfinddata_t* ft)
70 {
71   DWORD dw;
72
73   if (fd->dwFileAttributes == FILE_ATTRIBUTE_NORMAL)
74     ft->attrib = 0;
75   else
76     ft->attrib = fd->dwFileAttributes;
77
78   RtlTimeToSecondsSince1970( (LARGE_INTEGER *)&fd->ftCreationTime, &dw );
79   ft->time_create = dw;
80   RtlTimeToSecondsSince1970( (LARGE_INTEGER *)&fd->ftLastAccessTime, &dw );
81   ft->time_access = dw;
82   RtlTimeToSecondsSince1970( (LARGE_INTEGER *)&fd->ftLastWriteTime, &dw );
83   ft->time_write = dw;
84   ft->size = fd->nFileSizeLow;
85   strcpyW(ft->name, fd->cFileName);
86 }
87
88 /* INTERNAL: Translate WIN32_FIND_DATAA to finddatai64_t  */
89 static void msvcrt_fttofdi64( const WIN32_FIND_DATAA *fd, struct MSVCRT__finddatai64_t* ft)
90 {
91   DWORD dw;
92
93   if (fd->dwFileAttributes == FILE_ATTRIBUTE_NORMAL)
94     ft->attrib = 0;
95   else
96     ft->attrib = fd->dwFileAttributes;
97
98   RtlTimeToSecondsSince1970( (LARGE_INTEGER *)&fd->ftCreationTime, &dw );
99   ft->time_create = dw;
100   RtlTimeToSecondsSince1970( (LARGE_INTEGER *)&fd->ftLastAccessTime, &dw );
101   ft->time_access = dw;
102   RtlTimeToSecondsSince1970( (LARGE_INTEGER *)&fd->ftLastWriteTime, &dw );
103   ft->time_write = dw;
104   ft->size = ((__int64)fd->nFileSizeHigh) << 32 | fd->nFileSizeLow;
105   strcpy(ft->name, fd->cFileName);
106 }
107
108 /* INTERNAL: Translate WIN32_FIND_DATAW to wfinddatai64_t  */
109 static void msvcrt_wfttofdi64( const WIN32_FIND_DATAW *fd, struct MSVCRT__wfinddatai64_t* ft)
110 {
111   DWORD dw;
112
113   if (fd->dwFileAttributes == FILE_ATTRIBUTE_NORMAL)
114     ft->attrib = 0;
115   else
116     ft->attrib = fd->dwFileAttributes;
117
118   RtlTimeToSecondsSince1970( (LARGE_INTEGER *)&fd->ftCreationTime, &dw );
119   ft->time_create = dw;
120   RtlTimeToSecondsSince1970( (LARGE_INTEGER *)&fd->ftLastAccessTime, &dw );
121   ft->time_access = dw;
122   RtlTimeToSecondsSince1970( (LARGE_INTEGER *)&fd->ftLastWriteTime, &dw );
123   ft->time_write = dw;
124   ft->size = ((__int64)fd->nFileSizeHigh) << 32 | fd->nFileSizeLow;
125   strcpyW(ft->name, fd->cFileName);
126 }
127
128 /*********************************************************************
129  *              _chdir (MSVCRT.@)
130  *
131  * Change the current working directory.
132  *
133  * PARAMS
134  *  newdir [I] Directory to change to
135  *
136  * RETURNS
137  *  Success: 0. The current working directory is set to newdir.
138  *  Failure: -1. errno indicates the error.
139  *
140  * NOTES
141  *  See SetCurrentDirectoryA.
142  */
143 int _chdir(const char * newdir)
144 {
145   if (!SetCurrentDirectoryA(newdir))
146   {
147     MSVCRT__set_errno(newdir?GetLastError():0);
148     return -1;
149   }
150   return 0;
151 }
152
153 /*********************************************************************
154  *              _wchdir (MSVCRT.@)
155  *
156  * Unicode version of _chdir.
157  */
158 int _wchdir(const MSVCRT_wchar_t * newdir)
159 {
160   if (!SetCurrentDirectoryW(newdir))
161   {
162     MSVCRT__set_errno(newdir?GetLastError():0);
163     return -1;
164   }
165   return 0;
166 }
167
168 /*********************************************************************
169  *              _chdrive (MSVCRT.@)
170  *
171  * Change the current drive.
172  *
173  * PARAMS
174  *  newdrive [I] Drive number to change to (1 = 'A', 2 = 'B', ...)
175  *
176  * RETURNS
177  *  Success: 0. The current drive is set to newdrive.
178  *  Failure: -1. errno indicates the error.
179  *
180  * NOTES
181  *  See SetCurrentDirectoryA.
182  */
183 int _chdrive(int newdrive)
184 {
185   WCHAR buffer[3] = {'A', ':', 0};
186
187   buffer[0] += newdrive - 1;
188   if (!SetCurrentDirectoryW( buffer ))
189   {
190     MSVCRT__set_errno(GetLastError());
191     if (newdrive <= 0)
192       *MSVCRT__errno() = MSVCRT_EACCES;
193     return -1;
194   }
195   return 0;
196 }
197
198 /*********************************************************************
199  *              _findclose (MSVCRT.@)
200  *
201  * Close a handle returned by _findfirst().
202  *
203  * PARAMS
204  *  hand [I] Handle to close
205  *
206  * RETURNS
207  *  Success: 0. All resources associated with hand are freed.
208  *  Failure: -1. errno indicates the error.
209  *
210  * NOTES
211  *  See FindClose.
212  */
213 int _findclose(long hand)
214 {
215   TRACE(":handle %ld\n",hand);
216   if (!FindClose((HANDLE)hand))
217   {
218     MSVCRT__set_errno(GetLastError());
219     return -1;
220   }
221   return 0;
222 }
223
224 /*********************************************************************
225  *              _findfirst (MSVCRT.@)
226  *
227  * Open a handle for iterating through a directory.
228  *
229  * PARAMS
230  *  fspec [I] File specification of files to iterate.
231  *  ft    [O] Information for the first file found.
232  *
233  * RETURNS
234  *  Success: A handle suitable for passing to _findnext() and _findclose().
235  *           ft is populated with the details of the found file.
236  *  Failure: -1. errno indicates the error.
237  *
238  * NOTES
239  *  See FindFirstFileA.
240  */
241 long MSVCRT__findfirst(const char * fspec, struct MSVCRT__finddata_t* ft)
242 {
243   WIN32_FIND_DATAA find_data;
244   HANDLE hfind;
245
246   hfind  = FindFirstFileA(fspec, &find_data);
247   if (hfind == INVALID_HANDLE_VALUE)
248   {
249     MSVCRT__set_errno(GetLastError());
250     return -1;
251   }
252   msvcrt_fttofd(&find_data,ft);
253   TRACE(":got handle %p\n",hfind);
254   return (long)hfind;
255 }
256
257 /*********************************************************************
258  *              _wfindfirst (MSVCRT.@)
259  *
260  * Unicode version of _findfirst.
261  */
262 long MSVCRT__wfindfirst(const MSVCRT_wchar_t * fspec, struct MSVCRT__wfinddata_t* ft)
263 {
264   WIN32_FIND_DATAW find_data;
265   HANDLE hfind;
266
267   hfind  = FindFirstFileW(fspec, &find_data);
268   if (hfind == INVALID_HANDLE_VALUE)
269   {
270     MSVCRT__set_errno(GetLastError());
271     return -1;
272   }
273   msvcrt_wfttofd(&find_data,ft);
274   TRACE(":got handle %p\n",hfind);
275   return (long)hfind;
276 }
277
278 /*********************************************************************
279  *              _findfirsti64 (MSVCRT.@)
280  *
281  * 64-bit version of _findfirst.
282  */
283 long MSVCRT__findfirsti64(const char * fspec, struct MSVCRT__finddatai64_t* ft)
284 {
285   WIN32_FIND_DATAA find_data;
286   HANDLE hfind;
287
288   hfind  = FindFirstFileA(fspec, &find_data);
289   if (hfind == INVALID_HANDLE_VALUE)
290   {
291     MSVCRT__set_errno(GetLastError());
292     return -1;
293   }
294   msvcrt_fttofdi64(&find_data,ft);
295   TRACE(":got handle %p\n",hfind);
296   return (long)hfind;
297 }
298
299 /*********************************************************************
300  *              _wfindfirsti64 (MSVCRT.@)
301  *
302  * Unicode version of _findfirsti64.
303  */
304 long MSVCRT__wfindfirsti64(const MSVCRT_wchar_t * fspec, struct MSVCRT__wfinddatai64_t* ft)
305 {
306   WIN32_FIND_DATAW find_data;
307   HANDLE hfind;
308
309   hfind  = FindFirstFileW(fspec, &find_data);
310   if (hfind == INVALID_HANDLE_VALUE)
311   {
312     MSVCRT__set_errno(GetLastError());
313     return -1;
314   }
315   msvcrt_wfttofdi64(&find_data,ft);
316   TRACE(":got handle %p\n",hfind);
317   return (long)hfind;
318 }
319
320 /*********************************************************************
321  *              _findnext (MSVCRT.@)
322  *
323  * Find the next file from a file search handle.
324  *
325  * PARAMS
326  *  hand  [I] Handle to the search returned from _findfirst().
327  *  ft    [O] Information for the file found.
328  *
329  * RETURNS
330  *  Success: 0. ft is populated with the details of the found file.
331  *  Failure: -1. errno indicates the error.
332  *
333  * NOTES
334  *  See FindNextFileA.
335  */
336 int MSVCRT__findnext(long hand, struct MSVCRT__finddata_t * ft)
337 {
338   WIN32_FIND_DATAA find_data;
339
340   if (!FindNextFileA((HANDLE)hand, &find_data))
341   {
342     *MSVCRT__errno() = MSVCRT_ENOENT;
343     return -1;
344   }
345
346   msvcrt_fttofd(&find_data,ft);
347   return 0;
348 }
349
350 /*********************************************************************
351  *              _wfindnext (MSVCRT.@)
352  *
353  * Unicode version of _findnext.
354  */
355 int MSVCRT__wfindnext(long hand, struct MSVCRT__wfinddata_t * ft)
356 {
357   WIN32_FIND_DATAW find_data;
358
359   if (!FindNextFileW((HANDLE)hand, &find_data))
360   {
361     *MSVCRT__errno() = MSVCRT_ENOENT;
362     return -1;
363   }
364
365   msvcrt_wfttofd(&find_data,ft);
366   return 0;
367 }
368
369 /*********************************************************************
370  *              _findnexti64 (MSVCRT.@)
371  *
372  * 64-bit version of _findnext.
373  */
374 int MSVCRT__findnexti64(long hand, struct MSVCRT__finddatai64_t * ft)
375 {
376   WIN32_FIND_DATAA find_data;
377
378   if (!FindNextFileA((HANDLE)hand, &find_data))
379   {
380     *MSVCRT__errno() = MSVCRT_ENOENT;
381     return -1;
382   }
383
384   msvcrt_fttofdi64(&find_data,ft);
385   return 0;
386 }
387
388 /*********************************************************************
389  *              _wfindnexti64 (MSVCRT.@)
390  *
391  * Unicode version of _findnexti64.
392  */
393 int MSVCRT__wfindnexti64(long hand, struct MSVCRT__wfinddatai64_t * ft)
394 {
395   WIN32_FIND_DATAW find_data;
396
397   if (!FindNextFileW((HANDLE)hand, &find_data))
398   {
399     *MSVCRT__errno() = MSVCRT_ENOENT;
400     return -1;
401   }
402
403   msvcrt_wfttofdi64(&find_data,ft);
404   return 0;
405 }
406
407 /*********************************************************************
408  *              _getcwd (MSVCRT.@)
409  *
410  * Get the current working directory.
411  *
412  * PARAMS
413  *  buf  [O] Destination for current working directory.
414  *  size [I] Size of buf in characters
415  *
416  * RETURNS
417  * Success: If buf is NULL, returns an allocated string containing the path.
418  *          Otherwise populates buf with the path and returns it.
419  * Failure: NULL. errno indicates the error.
420  */
421 char* _getcwd(char * buf, int size)
422 {
423   char dir[MAX_PATH];
424   int dir_len = GetCurrentDirectoryA(MAX_PATH,dir);
425
426   if (dir_len < 1)
427     return NULL; /* FIXME: Real return value untested */
428
429   if (!buf)
430   {
431     if (size < 0)
432       return _strdup(dir);
433     return msvcrt_strndup(dir,size);
434   }
435   if (dir_len >= size)
436   {
437     *MSVCRT__errno() = MSVCRT_ERANGE;
438     return NULL; /* buf too small */
439   }
440   strcpy(buf,dir);
441   return buf;
442 }
443
444 /*********************************************************************
445  *              _wgetcwd (MSVCRT.@)
446  *
447  * Unicode version of _getcwd.
448  */
449 MSVCRT_wchar_t* _wgetcwd(MSVCRT_wchar_t * buf, int size)
450 {
451   MSVCRT_wchar_t dir[MAX_PATH];
452   int dir_len = GetCurrentDirectoryW(MAX_PATH,dir);
453
454   if (dir_len < 1)
455     return NULL; /* FIXME: Real return value untested */
456
457   if (!buf)
458   {
459     if (size < 0)
460       return _wcsdup(dir);
461     return msvcrt_wstrndup(dir,size);
462   }
463   if (dir_len >= size)
464   {
465     *MSVCRT__errno() = MSVCRT_ERANGE;
466     return NULL; /* buf too small */
467   }
468   strcpyW(buf,dir);
469   return buf;
470 }
471
472 /*********************************************************************
473  *              _getdrive (MSVCRT.@)
474  *
475  * Get the current drive number.
476  *
477  * PARAMS
478  *  None.
479  *
480  * RETURNS
481  *  Success: The drive letter number from 1 to 26 ("A:" to "Z:").
482  *  Failure: 0.
483  */
484 int _getdrive(void)
485 {
486     WCHAR buffer[MAX_PATH];
487     if (GetCurrentDirectoryW( MAX_PATH, buffer ) &&
488         buffer[0] >= 'A' && buffer[0] <= 'z' && buffer[1] == ':')
489         return toupperW(buffer[0]) - 'A' + 1;
490     return 0;
491 }
492
493 /*********************************************************************
494  *              _getdcwd (MSVCRT.@)
495  *
496  * Get the current working directory on a given disk.
497  * 
498  * PARAMS
499  *  drive [I] Drive letter to get the current working directory from.
500  *  buf   [O] Destination for the current working directory.
501  *  size  [I] Length of drive in characters.
502  *
503  * RETURNS
504  *  Success: If drive is NULL, returns an allocated string containing the path.
505  *           Otherwise populates drive with the path and returns it.
506  *  Failure: NULL. errno indicates the error.
507  */
508 char* _getdcwd(int drive, char * buf, int size)
509 {
510   static char* dummy;
511
512   TRACE(":drive %d(%c), size %d\n",drive, drive + 'A' - 1, size);
513
514   if (!drive || drive == _getdrive())
515     return _getcwd(buf,size); /* current */
516   else
517   {
518     char dir[MAX_PATH];
519     char drivespec[4] = {'A', ':', '\\', 0};
520     int dir_len;
521
522     drivespec[0] += drive - 1;
523     if (GetDriveTypeA(drivespec) < DRIVE_REMOVABLE)
524     {
525       *MSVCRT__errno() = MSVCRT_EACCES;
526       return NULL;
527     }
528
529     dir_len = GetFullPathNameA(drivespec,MAX_PATH,dir,&dummy);
530     if (dir_len >= size || dir_len < 1)
531     {
532       *MSVCRT__errno() = MSVCRT_ERANGE;
533       return NULL; /* buf too small */
534     }
535
536     TRACE(":returning '%s'\n", dir);
537     if (!buf)
538       return _strdup(dir); /* allocate */
539
540     strcpy(buf,dir);
541   }
542   return buf;
543 }
544
545 /*********************************************************************
546  *              _wgetdcwd (MSVCRT.@)
547  *
548  * Unicode version of _wgetdcwd.
549  */
550 MSVCRT_wchar_t* _wgetdcwd(int drive, MSVCRT_wchar_t * buf, int size)
551 {
552   static MSVCRT_wchar_t* dummy;
553
554   TRACE(":drive %d(%c), size %d\n",drive, drive + 'A' - 1, size);
555
556   if (!drive || drive == _getdrive())
557     return _wgetcwd(buf,size); /* current */
558   else
559   {
560     MSVCRT_wchar_t dir[MAX_PATH];
561     MSVCRT_wchar_t drivespec[4] = {'A', ':', '\\', 0};
562     int dir_len;
563
564     drivespec[0] += drive - 1;
565     if (GetDriveTypeW(drivespec) < DRIVE_REMOVABLE)
566     {
567       *MSVCRT__errno() = MSVCRT_EACCES;
568       return NULL;
569     }
570
571     dir_len = GetFullPathNameW(drivespec,MAX_PATH,dir,&dummy);
572     if (dir_len >= size || dir_len < 1)
573     {
574       *MSVCRT__errno() = MSVCRT_ERANGE;
575       return NULL; /* buf too small */
576     }
577
578     TRACE(":returning %s\n", debugstr_w(dir));
579     if (!buf)
580       return _wcsdup(dir); /* allocate */
581     strcpyW(buf,dir);
582   }
583   return buf;
584 }
585
586 /*********************************************************************
587  *              _getdiskfree (MSVCRT.@)
588  *
589  * Get information about the free space on a drive.
590  *
591  * PARAMS
592  *  disk [I] Drive number to get information about (1 = 'A', 2 = 'B', ...)
593  *  info [O] Destination for the resulting information.
594  *
595  * RETURNS
596  *  Success: 0. info is updated with the free space information.
597  *  Failure: An error code from GetLastError().
598  *
599  * NOTES
600  *  See GetLastError().
601  */
602 unsigned int MSVCRT__getdiskfree(unsigned int disk, struct MSVCRT__diskfree_t * d)
603 {
604   WCHAR drivespec[4] = {'@', ':', '\\', 0};
605   DWORD ret[4];
606   unsigned int err;
607
608   if (disk > 26)
609     return ERROR_INVALID_PARAMETER; /* MSVCRT doesn't set errno here */
610
611   drivespec[0] += disk; /* make a drive letter */
612
613   if (GetDiskFreeSpaceW(disk==0?NULL:drivespec,ret,ret+1,ret+2,ret+3))
614   {
615     d->sectors_per_cluster = (unsigned)ret[0];
616     d->bytes_per_sector = (unsigned)ret[1];
617     d->avail_clusters = (unsigned)ret[2];
618     d->total_clusters = (unsigned)ret[3];
619     return 0;
620   }
621   err = GetLastError();
622   MSVCRT__set_errno(err);
623   return err;
624 }
625
626 /*********************************************************************
627  *              _mkdir (MSVCRT.@)
628  *
629  * Create a directory.
630  *
631  * PARAMS
632  *  newdir [I] Name of directory to create.
633  *
634  * RETURNS
635  *  Success: 0. The directory indicated by newdir is created.
636  *  Failure: -1. errno indicates the error.
637  *
638  * NOTES
639  *  See CreateDirectoryA.
640  */
641 int _mkdir(const char * newdir)
642 {
643   if (CreateDirectoryA(newdir,NULL))
644     return 0;
645   MSVCRT__set_errno(GetLastError());
646   return -1;
647 }
648
649 /*********************************************************************
650  *              _wmkdir (MSVCRT.@)
651  *
652  * Unicode version of _mkdir.
653  */
654 int _wmkdir(const MSVCRT_wchar_t* newdir)
655 {
656   if (CreateDirectoryW(newdir,NULL))
657     return 0;
658   MSVCRT__set_errno(GetLastError());
659   return -1;
660 }
661
662 /*********************************************************************
663  *              _rmdir (MSVCRT.@)
664  *
665  * Delete a directory.
666  *
667  * PARAMS
668  *  dir [I] Name of directory to delete.
669  *
670  * RETURNS
671  *  Success: 0. The directory indicated by newdir is deleted.
672  *  Failure: -1. errno indicates the error.
673  *
674  * NOTES
675  *  See RemoveDirectoryA.
676  */
677 int _rmdir(const char * dir)
678 {
679   if (RemoveDirectoryA(dir))
680     return 0;
681   MSVCRT__set_errno(GetLastError());
682   return -1;
683 }
684
685 /*********************************************************************
686  *              _wrmdir (MSVCRT.@)
687  *
688  * Unicode version of _rmdir.
689  */
690 int _wrmdir(const MSVCRT_wchar_t * dir)
691 {
692   if (RemoveDirectoryW(dir))
693     return 0;
694   MSVCRT__set_errno(GetLastError());
695   return -1;
696 }
697
698 /*********************************************************************
699  *              _wsplitpath (MSVCRT.@)
700  *
701  * Unicode version of _splitpath.
702  */
703 void _wsplitpath(const MSVCRT_wchar_t *inpath, MSVCRT_wchar_t *drv, MSVCRT_wchar_t *dir,
704                  MSVCRT_wchar_t *fname, MSVCRT_wchar_t *ext )
705 {
706     const MSVCRT_wchar_t *p, *end;
707
708     if (inpath[0] && inpath[1] == ':')
709     {
710         if (drv)
711         {
712             drv[0] = inpath[0];
713             drv[1] = inpath[1];
714             drv[2] = 0;
715         }
716         inpath += 2;
717     }
718     else if (drv) drv[0] = 0;
719
720     /* look for end of directory part */
721     end = NULL;
722     for (p = inpath; *p; p++) if (*p == '/' || *p == '\\') end = p + 1;
723
724     if (end)  /* got a directory */
725     {
726         if (dir)
727         {
728             memcpy( dir, inpath, (end - inpath) * sizeof(MSVCRT_wchar_t) );
729             dir[end - inpath] = 0;
730         }
731         inpath = end;
732     }
733     else if (dir) dir[0] = 0;
734
735     /* look for extension: what's after the last dot */
736     end = NULL;
737     for (p = inpath; *p; p++) if (*p == '.') end = p;
738
739     if (!end) end = p; /* there's no extension */
740
741     if (fname)
742     {
743         memcpy( fname, inpath, (end - inpath) * sizeof(MSVCRT_wchar_t) );
744         fname[end - inpath] = 0;
745     }
746     if (ext) strcpyW( ext, end );
747 }
748
749 /* INTERNAL: Helper for _fullpath. Modified PD code from 'snippets'. */
750 static void wmsvcrt_fln_fix(MSVCRT_wchar_t *path)
751 {
752   int dir_flag = 0, root_flag = 0;
753   MSVCRT_wchar_t *r, *p, *q, *s;
754   MSVCRT_wchar_t szbsdot[] = { '\\', '.', 0 };
755
756   /* Skip drive */
757   if (NULL == (r = strrchrW(path, ':')))
758     r = path;
759   else
760     ++r;
761
762   /* Ignore leading slashes */
763   while ('\\' == *r)
764     if ('\\' == r[1])
765       strcpyW(r, &r[1]);
766     else
767     {
768       root_flag = 1;
769       ++r;
770     }
771
772   p = r; /* Change "\\" to "\" */
773   while (NULL != (p = strchrW(p, '\\')))
774     if ('\\' ==  p[1])
775       strcpyW(p, &p[1]);
776     else
777       ++p;
778
779   while ('.' == *r) /* Scrunch leading ".\" */
780   {
781     if ('.' == r[1])
782     {
783       /* Ignore leading ".." */
784       for (p = (r += 2); *p && (*p != '\\'); ++p)
785         ;
786     }
787     else
788     {
789       for (p = r + 1 ;*p && (*p != '\\'); ++p)
790         ;
791     }
792     strcpyW(r, p + ((*p) ? 1 : 0));
793   }
794
795   while ('\\' == path[strlenW(path)-1])   /* Strip last '\\' */
796   {
797     dir_flag = 1;
798     path[strlenW(path)-1] = '\0';
799   }
800
801   s = r;
802
803   /* Look for "\." in path */
804
805   while (NULL != (p = strstrW(s, szbsdot)))
806   {
807     if ('.' == p[2])
808     {
809       /* Execute this section if ".." found */
810       q = p - 1;
811       while (q > r)           /* Backup one level           */
812       {
813         if (*q == '\\')
814           break;
815         --q;
816       }
817       if (q > r)
818       {
819         strcpyW(q, p + 3);
820         s = q;
821       }
822       else if ('.' != *q)
823       {
824         strcpyW(q + ((*q == '\\') ? 1 : 0),
825                 p + 3 + ((*(p + 3)) ? 1 : 0));
826         s = q;
827       }
828       else  s = ++p;
829     }
830     else
831     {
832       /* Execute this section if "." found */
833       q = p + 2;
834       for ( ;*q && (*q != '\\'); ++q)
835         ;
836       strcpyW (p, q);
837     }
838   }
839
840   if (root_flag)  /* Embedded ".." could have bubbled up to root  */
841   {
842     for (p = r; *p && ('.' == *p || '\\' == *p); ++p)
843       ;
844     if (r != p)
845       strcpyW(r, p);
846   }
847
848   if (dir_flag)
849   {
850     MSVCRT_wchar_t szbs[] = { '\\', 0 };
851
852     strcatW(path, szbs);
853   }
854 }
855
856 /*********************************************************************
857  *              _wfullpath (MSVCRT.@)
858  *
859  * Unicode version of _fullpath.
860  */
861 MSVCRT_wchar_t *_wfullpath(MSVCRT_wchar_t * absPath, const MSVCRT_wchar_t* relPath, MSVCRT_size_t size)
862 {
863   MSVCRT_wchar_t drive[5],dir[MAX_PATH],file[MAX_PATH],ext[MAX_PATH];
864   MSVCRT_wchar_t res[MAX_PATH];
865   size_t len;
866   MSVCRT_wchar_t szbs[] = { '\\', 0 };
867
868
869   res[0] = '\0';
870
871   if (!relPath || !*relPath)
872     return _wgetcwd(absPath, size);
873
874   if (size < 4)
875   {
876     *MSVCRT__errno() = MSVCRT_ERANGE;
877     return NULL;
878   }
879
880   TRACE(":resolving relative path '%s'\n",debugstr_w(relPath));
881
882   _wsplitpath(relPath, drive, dir, file, ext);
883
884   /* Get Directory and drive into 'res' */
885   if (!dir[0] || (dir[0] != '/' && dir[0] != '\\'))
886   {
887     /* Relative or no directory given */
888     _wgetdcwd(drive[0] ? toupper(drive[0]) - 'A' + 1 :  0, res, MAX_PATH);
889     strcatW(res,szbs);
890     if (dir[0])
891       strcatW(res,dir);
892     if (drive[0])
893       res[0] = drive[0]; /* If given a drive, preserve the letter case */
894   }
895   else
896   {
897     strcpyW(res,drive);
898     strcatW(res,dir);
899   }
900
901   strcatW(res,szbs);
902   strcatW(res, file);
903   strcatW(res, ext);
904   wmsvcrt_fln_fix(res);
905
906   len = strlenW(res);
907   if (len >= MAX_PATH || len >= (size_t)size)
908     return NULL; /* FIXME: errno? */
909
910   if (!absPath)
911     return _wcsdup(res);
912   strcpyW(absPath,res);
913   return absPath;
914 }
915
916 /* INTERNAL: Helper for _fullpath. Modified PD code from 'snippets'. */
917 static void msvcrt_fln_fix(char *path)
918 {
919   int dir_flag = 0, root_flag = 0;
920   char *r, *p, *q, *s;
921
922   /* Skip drive */
923   if (NULL == (r = strrchr(path, ':')))
924     r = path;
925   else
926     ++r;
927
928   /* Ignore leading slashes */
929   while ('\\' == *r)
930     if ('\\' == r[1])
931       strcpy(r, &r[1]);
932     else
933     {
934       root_flag = 1;
935       ++r;
936     }
937
938   p = r; /* Change "\\" to "\" */
939   while (NULL != (p = strchr(p, '\\')))
940     if ('\\' ==  p[1])
941       strcpy(p, &p[1]);
942     else
943       ++p;
944
945   while ('.' == *r) /* Scrunch leading ".\" */
946   {
947     if ('.' == r[1])
948     {
949       /* Ignore leading ".." */
950       for (p = (r += 2); *p && (*p != '\\'); ++p)
951         ;
952     }
953     else
954     {
955       for (p = r + 1 ;*p && (*p != '\\'); ++p)
956         ;
957     }
958     strcpy(r, p + ((*p) ? 1 : 0));
959   }
960
961   while ('\\' == path[strlen(path)-1])   /* Strip last '\\' */
962   {
963     dir_flag = 1;
964     path[strlen(path)-1] = '\0';
965   }
966
967   s = r;
968
969   /* Look for "\." in path */
970
971   while (NULL != (p = strstr(s, "\\.")))
972   {
973     if ('.' == p[2])
974     {
975       /* Execute this section if ".." found */
976       q = p - 1;
977       while (q > r)           /* Backup one level           */
978       {
979         if (*q == '\\')
980           break;
981         --q;
982       }
983       if (q > r)
984       {
985         strcpy(q, p + 3);
986         s = q;
987       }
988       else if ('.' != *q)
989       {
990         strcpy(q + ((*q == '\\') ? 1 : 0),
991                p + 3 + ((*(p + 3)) ? 1 : 0));
992         s = q;
993       }
994       else  s = ++p;
995     }
996     else
997     {
998       /* Execute this section if "." found */
999       q = p + 2;
1000       for ( ;*q && (*q != '\\'); ++q)
1001         ;
1002       strcpy (p, q);
1003     }
1004   }
1005
1006   if (root_flag)  /* Embedded ".." could have bubbled up to root  */
1007   {
1008     for (p = r; *p && ('.' == *p || '\\' == *p); ++p)
1009       ;
1010     if (r != p)
1011       strcpy(r, p);
1012   }
1013
1014   if (dir_flag)
1015     strcat(path, "\\");
1016 }
1017
1018 /*********************************************************************
1019  *              _fullpath (MSVCRT.@)
1020  *
1021  * Create an absolute path from a relative path.
1022  *
1023  * PARAMS
1024  *  absPath [O] Destination for absolute path
1025  *  relPath [I] Relative path to convert to absolute
1026  *  size    [I] Length of absPath in characters.
1027  *
1028  * RETURNS
1029  * Success: If absPath is NULL, returns an allocated string containing the path.
1030  *          Otherwise populates absPath with the path and returns it.
1031  * Failure: NULL. errno indicates the error.
1032  */
1033 char *_fullpath(char * absPath, const char* relPath, unsigned int size)
1034 {
1035   char drive[5],dir[MAX_PATH],file[MAX_PATH],ext[MAX_PATH];
1036   char res[MAX_PATH];
1037   size_t len;
1038
1039   res[0] = '\0';
1040
1041   if (!relPath || !*relPath)
1042     return _getcwd(absPath, size);
1043
1044   if (size < 4)
1045   {
1046     *MSVCRT__errno() = MSVCRT_ERANGE;
1047     return NULL;
1048   }
1049
1050   TRACE(":resolving relative path '%s'\n",relPath);
1051
1052   _splitpath(relPath, drive, dir, file, ext);
1053
1054   /* Get Directory and drive into 'res' */
1055   if (!dir[0] || (dir[0] != '/' && dir[0] != '\\'))
1056   {
1057     /* Relative or no directory given */
1058     _getdcwd(drive[0] ? toupper(drive[0]) - 'A' + 1 :  0, res, MAX_PATH);
1059     strcat(res,"\\");
1060     if (dir[0])
1061       strcat(res,dir);
1062     if (drive[0])
1063       res[0] = drive[0]; /* If given a drive, preserve the letter case */
1064   }
1065   else
1066   {
1067     strcpy(res,drive);
1068     strcat(res,dir);
1069   }
1070
1071   strcat(res,"\\");
1072   strcat(res, file);
1073   strcat(res, ext);
1074   msvcrt_fln_fix(res);
1075
1076   len = strlen(res);
1077   if (len >= MAX_PATH || len >= (size_t)size)
1078     return NULL; /* FIXME: errno? */
1079
1080   if (!absPath)
1081     return _strdup(res);
1082   strcpy(absPath,res);
1083   return absPath;
1084 }
1085
1086 /*********************************************************************
1087  *              _makepath (MSVCRT.@)
1088  *
1089  * Create a pathname.
1090  *
1091  * PARAMS
1092  *  path      [O] Destination for created pathname
1093  *  drive     [I] Drive letter (e.g. "A:")
1094  *  directory [I] Directory
1095  *  filename  [I] Name of the file, excluding extension
1096  *  extension [I] File extension (e.g. ".TXT")
1097  *
1098  * RETURNS
1099  *  Nothing. If path is not large enough to hold the resulting pathname,
1100  *  random process memory will be overwritten.
1101  */
1102 VOID _makepath(char * path, const char * drive,
1103                const char *directory, const char * filename,
1104                const char * extension)
1105 {
1106     char ch;
1107     char tmpPath[MAX_PATH];
1108     TRACE("got %s %s %s %s\n", debugstr_a(drive), debugstr_a(directory),
1109           debugstr_a(filename), debugstr_a(extension) );
1110
1111     if ( !path )
1112         return;
1113
1114     tmpPath[0] = '\0';
1115     if (drive && drive[0])
1116     {
1117         tmpPath[0] = drive[0];
1118         tmpPath[1] = ':';
1119         tmpPath[2] = 0;
1120     }
1121     if (directory && directory[0])
1122     {
1123         strcat(tmpPath, directory);
1124         ch = tmpPath[strlen(tmpPath)-1];
1125         if (ch != '/' && ch != '\\')
1126             strcat(tmpPath,"\\");
1127     }
1128     if (filename && filename[0])
1129     {
1130         strcat(tmpPath, filename);
1131         if (extension && extension[0])
1132         {
1133             if ( extension[0] != '.' )
1134                 strcat(tmpPath,".");
1135             strcat(tmpPath,extension);
1136         }
1137     }
1138
1139     strcpy( path, tmpPath );
1140
1141     TRACE("returning %s\n",path);
1142 }
1143
1144 /*********************************************************************
1145  *              _wmakepath (MSVCRT.@)
1146  *
1147  * Unicode version of _wmakepath.
1148  */
1149 VOID _wmakepath(MSVCRT_wchar_t *path, const MSVCRT_wchar_t *drive, const MSVCRT_wchar_t *directory,
1150                 const MSVCRT_wchar_t *filename, const MSVCRT_wchar_t *extension)
1151 {
1152     MSVCRT_wchar_t ch;
1153     TRACE("%s %s %s %s\n", debugstr_w(drive), debugstr_w(directory),
1154           debugstr_w(filename), debugstr_w(extension));
1155
1156     if ( !path )
1157         return;
1158
1159     path[0] = 0;
1160     if (drive && drive[0])
1161     {
1162         path[0] = drive[0];
1163         path[1] = ':';
1164         path[2] = 0;
1165     }
1166     if (directory && directory[0])
1167     {
1168         strcatW(path, directory);
1169         ch = path[strlenW(path) - 1];
1170         if (ch != '/' && ch != '\\')
1171         {
1172             static const MSVCRT_wchar_t backslashW[] = {'\\',0};
1173             strcatW(path, backslashW);
1174         }
1175     }
1176     if (filename && filename[0])
1177     {
1178         strcatW(path, filename);
1179         if (extension && extension[0])
1180         {
1181             if ( extension[0] != '.' )
1182             {
1183                 static const MSVCRT_wchar_t dotW[] = {'.',0};
1184                 strcatW(path, dotW);
1185             }
1186             strcatW(path, extension);
1187         }
1188     }
1189
1190     TRACE("returning %s\n", debugstr_w(path));
1191 }
1192
1193 /*********************************************************************
1194  *              _searchenv (MSVCRT.@)
1195  *
1196  * Search for a file in a list of paths from an envronment variable.
1197  *
1198  * PARAMS
1199  *  file   [I] Name of the file to search for.
1200  *  env    [I] Name of the environment variable containing a list of paths.
1201  *  buf    [O] Destination for the found file path.
1202  *
1203  * RETURNS
1204  *  Nothing. If the file is not found, buf will contain an empty string
1205  *  and errno is set.
1206  */
1207 void _searchenv(const char* file, const char* env, char *buf)
1208 {
1209   char*envVal, *penv;
1210   char curPath[MAX_PATH];
1211
1212   *buf = '\0';
1213
1214   /* Try CWD first */
1215   if (GetFileAttributesA( file ) != INVALID_FILE_ATTRIBUTES)
1216   {
1217     GetFullPathNameA( file, MAX_PATH, buf, NULL );
1218     /* Sigh. This error is *always* set, regardless of success */
1219     MSVCRT__set_errno(ERROR_FILE_NOT_FOUND);
1220     return;
1221   }
1222
1223   /* Search given environment variable */
1224   envVal = MSVCRT_getenv(env);
1225   if (!envVal)
1226   {
1227     MSVCRT__set_errno(ERROR_FILE_NOT_FOUND);
1228     return;
1229   }
1230
1231   penv = envVal;
1232   TRACE(":searching for %s in paths %s\n", file, envVal);
1233
1234   do
1235   {
1236     char *end = penv;
1237
1238     while(*end && *end != ';') end++; /* Find end of next path */
1239     if (penv == end || !*penv)
1240     {
1241       MSVCRT__set_errno(ERROR_FILE_NOT_FOUND);
1242       return;
1243     }
1244     strncpy(curPath, penv, end - penv);
1245     if (curPath[end - penv] != '/' || curPath[end - penv] != '\\')
1246     {
1247       curPath[end - penv] = '\\';
1248       curPath[end - penv + 1] = '\0';
1249     }
1250     else
1251       curPath[end - penv] = '\0';
1252
1253     strcat(curPath, file);
1254     TRACE("Checking for file %s\n", curPath);
1255     if (GetFileAttributesA( curPath ) != INVALID_FILE_ATTRIBUTES)
1256     {
1257       strcpy(buf, curPath);
1258       MSVCRT__set_errno(ERROR_FILE_NOT_FOUND);
1259       return; /* Found */
1260     }
1261     penv = *end ? end + 1 : end;
1262   } while(1);
1263 }