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