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