lz32/tests: Fix some typos in error 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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( (const LARGE_INTEGER *)&fd->ftCreationTime, &dw );
51   ft->time_create = dw;
52   RtlTimeToSecondsSince1970( (const LARGE_INTEGER *)&fd->ftLastAccessTime, &dw );
53   ft->time_access = dw;
54   RtlTimeToSecondsSince1970( (const 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( (const LARGE_INTEGER *)&fd->ftCreationTime, &dw );
71   ft->time_create = dw;
72   RtlTimeToSecondsSince1970( (const LARGE_INTEGER *)&fd->ftLastAccessTime, &dw );
73   ft->time_access = dw;
74   RtlTimeToSecondsSince1970( (const 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( (const LARGE_INTEGER *)&fd->ftCreationTime, &dw );
91   ft->time_create = dw;
92   RtlTimeToSecondsSince1970( (const LARGE_INTEGER *)&fd->ftLastAccessTime, &dw );
93   ft->time_access = dw;
94   RtlTimeToSecondsSince1970( (const 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( (const LARGE_INTEGER *)&fd->ftCreationTime, &dw );
111   ft->time_create = dw;
112   RtlTimeToSecondsSince1970( (const LARGE_INTEGER *)&fd->ftLastAccessTime, &dw );
113   ft->time_access = dw;
114   RtlTimeToSecondsSince1970( (const 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 CDECL _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 CDECL _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 CDECL _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 CDECL MSVCRT__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 CDECL 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 CDECL 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 CDECL 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 CDECL 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 CDECL 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 CDECL 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 CDECL 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 CDECL 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* CDECL _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* CDECL _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 CDECL _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* CDECL _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* CDECL _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 CDECL 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 CDECL _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 CDECL _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 CDECL _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 CDECL _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 CDECL _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 /*********************************************************************
742  *              _wfullpath (MSVCRT.@)
743  *
744  * Unicode version of _fullpath.
745  */
746 MSVCRT_wchar_t * CDECL _wfullpath(MSVCRT_wchar_t * absPath, const MSVCRT_wchar_t* relPath, MSVCRT_size_t size)
747 {
748   DWORD rc;
749   WCHAR* buffer;
750   WCHAR* lastpart;
751   BOOL alloced = FALSE;
752
753   if (!relPath || !*relPath)
754     return _wgetcwd(absPath, size);
755
756   if (absPath == NULL)
757   {
758       buffer = MSVCRT_malloc(MAX_PATH * sizeof(WCHAR));
759       size = MAX_PATH;
760       alloced = TRUE;
761   }
762   else
763       buffer = absPath;
764
765   if (size < 4)
766   {
767     *MSVCRT__errno() = MSVCRT_ERANGE;
768     return NULL;
769   }
770
771   TRACE(":resolving relative path '%s'\n",debugstr_w(relPath));
772
773   rc = GetFullPathNameW(relPath,size,buffer,&lastpart);
774
775   if (rc > 0 && rc <= size )
776     return buffer;
777   else
778   {
779       if (alloced)
780           MSVCRT_free(buffer);
781         return NULL;
782   }
783 }
784
785 /*********************************************************************
786  *              _fullpath (MSVCRT.@)
787  *
788  * Create an absolute path from a relative path.
789  *
790  * PARAMS
791  *  absPath [O] Destination for absolute path
792  *  relPath [I] Relative path to convert to absolute
793  *  size    [I] Length of absPath in characters.
794  *
795  * RETURNS
796  * Success: If absPath is NULL, returns an allocated string containing the path.
797  *          Otherwise populates absPath with the path and returns it.
798  * Failure: NULL. errno indicates the error.
799  */
800 char * CDECL _fullpath(char * absPath, const char* relPath, unsigned int size)
801 {
802   DWORD rc;
803   char* lastpart;
804   char* buffer;
805   BOOL alloced = FALSE;
806
807   if (!relPath || !*relPath)
808     return _getcwd(absPath, size);
809
810   if (absPath == NULL)
811   {
812       buffer = MSVCRT_malloc(MAX_PATH);
813       size = MAX_PATH;
814       alloced = TRUE;
815   }
816   else
817       buffer = absPath;
818
819   if (size < 4)
820   {
821     *MSVCRT__errno() = MSVCRT_ERANGE;
822     return NULL;
823   }
824
825   TRACE(":resolving relative path '%s'\n",relPath);
826
827   rc = GetFullPathNameA(relPath,size,buffer,&lastpart);
828
829   if (rc > 0 && rc <= size)
830     return buffer;
831   else
832   {
833       if (alloced)
834           MSVCRT_free(buffer);
835         return NULL;
836   }
837 }
838
839 /*********************************************************************
840  *              _makepath (MSVCRT.@)
841  *
842  * Create a pathname.
843  *
844  * PARAMS
845  *  path      [O] Destination for created pathname
846  *  drive     [I] Drive letter (e.g. "A:")
847  *  directory [I] Directory
848  *  filename  [I] Name of the file, excluding extension
849  *  extension [I] File extension (e.g. ".TXT")
850  *
851  * RETURNS
852  *  Nothing. If path is not large enough to hold the resulting pathname,
853  *  random process memory will be overwritten.
854  */
855 VOID CDECL _makepath(char * path, const char * drive,
856                      const char *directory, const char * filename,
857                      const char * extension)
858 {
859     char ch;
860
861     TRACE("(%s %s %s %s)\n", debugstr_a(drive), debugstr_a(directory),
862           debugstr_a(filename), debugstr_a(extension) );
863
864     if ( !path )
865         return;
866
867     path[0] = '\0';
868     if (drive && drive[0])
869     {
870         path[0] = drive[0];
871         path[1] = ':';
872         path[2] = 0;
873     }
874     if (directory && directory[0])
875     {
876         strcat(path, directory);
877         ch = path[strlen(path)-1];
878         if (ch != '/' && ch != '\\')
879             strcat(path,"\\");
880     }
881     if (filename && filename[0])
882     {
883         strcat(path, filename);
884         if (extension && extension[0])
885         {
886             if ( extension[0] != '.' )
887                 strcat(path,".");
888             strcat(path,extension);
889         }
890     }
891     TRACE("returning %s\n",path);
892 }
893
894 /*********************************************************************
895  *              _wmakepath (MSVCRT.@)
896  *
897  * Unicode version of _wmakepath.
898  */
899 VOID CDECL _wmakepath(MSVCRT_wchar_t *path, const MSVCRT_wchar_t *drive, const MSVCRT_wchar_t *directory,
900                       const MSVCRT_wchar_t *filename, const MSVCRT_wchar_t *extension)
901 {
902     MSVCRT_wchar_t ch;
903     TRACE("%s %s %s %s\n", debugstr_w(drive), debugstr_w(directory),
904           debugstr_w(filename), debugstr_w(extension));
905
906     if ( !path )
907         return;
908
909     path[0] = 0;
910     if (drive && drive[0])
911     {
912         path[0] = drive[0];
913         path[1] = ':';
914         path[2] = 0;
915     }
916     if (directory && directory[0])
917     {
918         strcatW(path, directory);
919         ch = path[strlenW(path) - 1];
920         if (ch != '/' && ch != '\\')
921         {
922             static const MSVCRT_wchar_t backslashW[] = {'\\',0};
923             strcatW(path, backslashW);
924         }
925     }
926     if (filename && filename[0])
927     {
928         strcatW(path, filename);
929         if (extension && extension[0])
930         {
931             if ( extension[0] != '.' )
932             {
933                 static const MSVCRT_wchar_t dotW[] = {'.',0};
934                 strcatW(path, dotW);
935             }
936             strcatW(path, extension);
937         }
938     }
939
940     TRACE("returning %s\n", debugstr_w(path));
941 }
942
943 /*********************************************************************
944  *              _searchenv (MSVCRT.@)
945  *
946  * Search for a file in a list of paths from an envronment variable.
947  *
948  * PARAMS
949  *  file   [I] Name of the file to search for.
950  *  env    [I] Name of the environment variable containing a list of paths.
951  *  buf    [O] Destination for the found file path.
952  *
953  * RETURNS
954  *  Nothing. If the file is not found, buf will contain an empty string
955  *  and errno is set.
956  */
957 void CDECL _searchenv(const char* file, const char* env, char *buf)
958 {
959   char*envVal, *penv;
960   char curPath[MAX_PATH];
961
962   *buf = '\0';
963
964   /* Try CWD first */
965   if (GetFileAttributesA( file ) != INVALID_FILE_ATTRIBUTES)
966   {
967     GetFullPathNameA( file, MAX_PATH, buf, NULL );
968     /* Sigh. This error is *always* set, regardless of success */
969     msvcrt_set_errno(ERROR_FILE_NOT_FOUND);
970     return;
971   }
972
973   /* Search given environment variable */
974   envVal = MSVCRT_getenv(env);
975   if (!envVal)
976   {
977     msvcrt_set_errno(ERROR_FILE_NOT_FOUND);
978     return;
979   }
980
981   penv = envVal;
982   TRACE(":searching for %s in paths %s\n", file, envVal);
983
984   do
985   {
986     char *end = penv;
987
988     while(*end && *end != ';') end++; /* Find end of next path */
989     if (penv == end || !*penv)
990     {
991       msvcrt_set_errno(ERROR_FILE_NOT_FOUND);
992       return;
993     }
994     memcpy(curPath, penv, end - penv);
995     if (curPath[end - penv] != '/' || curPath[end - penv] != '\\')
996     {
997       curPath[end - penv] = '\\';
998       curPath[end - penv + 1] = '\0';
999     }
1000     else
1001       curPath[end - penv] = '\0';
1002
1003     strcat(curPath, file);
1004     TRACE("Checking for file %s\n", curPath);
1005     if (GetFileAttributesA( curPath ) != INVALID_FILE_ATTRIBUTES)
1006     {
1007       strcpy(buf, curPath);
1008       msvcrt_set_errno(ERROR_FILE_NOT_FOUND);
1009       return; /* Found */
1010     }
1011     penv = *end ? end + 1 : end;
1012   } while(1);
1013 }