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