msvcrt: Implemented splitpath_s.
[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 MSVCRT__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 <= dir_len) size = dir_len + 1;
423       if (!(buf = MSVCRT_malloc( size ))) return NULL;
424   }
425   else if (dir_len >= size)
426   {
427     *MSVCRT__errno() = MSVCRT_ERANGE;
428     return NULL; /* buf too small */
429   }
430   strcpy(buf,dir);
431   return buf;
432 }
433
434 /*********************************************************************
435  *              _wgetcwd (MSVCRT.@)
436  *
437  * Unicode version of _getcwd.
438  */
439 MSVCRT_wchar_t* CDECL _wgetcwd(MSVCRT_wchar_t * buf, int size)
440 {
441   MSVCRT_wchar_t dir[MAX_PATH];
442   int dir_len = GetCurrentDirectoryW(MAX_PATH,dir);
443
444   if (dir_len < 1)
445     return NULL; /* FIXME: Real return value untested */
446
447   if (!buf)
448   {
449       if (size <= dir_len) size = dir_len + 1;
450       if (!(buf = MSVCRT_malloc( size * sizeof(WCHAR) ))) return NULL;
451   }
452   if (dir_len >= size)
453   {
454     *MSVCRT__errno() = MSVCRT_ERANGE;
455     return NULL; /* buf too small */
456   }
457   strcpyW(buf,dir);
458   return buf;
459 }
460
461 /*********************************************************************
462  *              _getdrive (MSVCRT.@)
463  *
464  * Get the current drive number.
465  *
466  * PARAMS
467  *  None.
468  *
469  * RETURNS
470  *  Success: The drive letter number from 1 to 26 ("A:" to "Z:").
471  *  Failure: 0.
472  */
473 int CDECL _getdrive(void)
474 {
475     WCHAR buffer[MAX_PATH];
476     if (GetCurrentDirectoryW( MAX_PATH, buffer ) &&
477         buffer[0] >= 'A' && buffer[0] <= 'z' && buffer[1] == ':')
478         return toupperW(buffer[0]) - 'A' + 1;
479     return 0;
480 }
481
482 /*********************************************************************
483  *              _getdcwd (MSVCRT.@)
484  *
485  * Get the current working directory on a given disk.
486  * 
487  * PARAMS
488  *  drive [I] Drive letter to get the current working directory from.
489  *  buf   [O] Destination for the current working directory.
490  *  size  [I] Length of drive in characters.
491  *
492  * RETURNS
493  *  Success: If drive is NULL, returns an allocated string containing the path.
494  *           Otherwise populates drive with the path and returns it.
495  *  Failure: NULL. errno indicates the error.
496  */
497 char* CDECL _getdcwd(int drive, char * buf, int size)
498 {
499   static char* dummy;
500
501   TRACE(":drive %d(%c), size %d\n",drive, drive + 'A' - 1, size);
502
503   if (!drive || drive == _getdrive())
504     return _getcwd(buf,size); /* current */
505   else
506   {
507     char dir[MAX_PATH];
508     char drivespec[4] = {'A', ':', 0};
509     int dir_len;
510
511     drivespec[0] += drive - 1;
512     if (GetDriveTypeA(drivespec) < DRIVE_REMOVABLE)
513     {
514       *MSVCRT__errno() = MSVCRT_EACCES;
515       return NULL;
516     }
517
518     dir_len = GetFullPathNameA(drivespec,MAX_PATH,dir,&dummy);
519     if (dir_len >= size || dir_len < 1)
520     {
521       *MSVCRT__errno() = MSVCRT_ERANGE;
522       return NULL; /* buf too small */
523     }
524
525     TRACE(":returning '%s'\n", dir);
526     if (!buf)
527       return _strdup(dir); /* allocate */
528
529     strcpy(buf,dir);
530   }
531   return buf;
532 }
533
534 /*********************************************************************
535  *              _wgetdcwd (MSVCRT.@)
536  *
537  * Unicode version of _wgetdcwd.
538  */
539 MSVCRT_wchar_t* CDECL _wgetdcwd(int drive, MSVCRT_wchar_t * buf, int size)
540 {
541   static MSVCRT_wchar_t* dummy;
542
543   TRACE(":drive %d(%c), size %d\n",drive, drive + 'A' - 1, size);
544
545   if (!drive || drive == _getdrive())
546     return _wgetcwd(buf,size); /* current */
547   else
548   {
549     MSVCRT_wchar_t dir[MAX_PATH];
550     MSVCRT_wchar_t drivespec[4] = {'A', ':', '\\', 0};
551     int dir_len;
552
553     drivespec[0] += drive - 1;
554     if (GetDriveTypeW(drivespec) < DRIVE_REMOVABLE)
555     {
556       *MSVCRT__errno() = MSVCRT_EACCES;
557       return NULL;
558     }
559
560     dir_len = GetFullPathNameW(drivespec,MAX_PATH,dir,&dummy);
561     if (dir_len >= size || dir_len < 1)
562     {
563       *MSVCRT__errno() = MSVCRT_ERANGE;
564       return NULL; /* buf too small */
565     }
566
567     TRACE(":returning %s\n", debugstr_w(dir));
568     if (!buf)
569       return _wcsdup(dir); /* allocate */
570     strcpyW(buf,dir);
571   }
572   return buf;
573 }
574
575 /*********************************************************************
576  *              _getdiskfree (MSVCRT.@)
577  *
578  * Get information about the free space on a drive.
579  *
580  * PARAMS
581  *  disk [I] Drive number to get information about (1 = 'A', 2 = 'B', ...)
582  *  info [O] Destination for the resulting information.
583  *
584  * RETURNS
585  *  Success: 0. info is updated with the free space information.
586  *  Failure: An error code from GetLastError().
587  *
588  * NOTES
589  *  See GetLastError().
590  */
591 unsigned int CDECL MSVCRT__getdiskfree(unsigned int disk, struct MSVCRT__diskfree_t * d)
592 {
593   WCHAR drivespec[4] = {'@', ':', '\\', 0};
594   DWORD ret[4];
595   unsigned int err;
596
597   if (disk > 26)
598     return ERROR_INVALID_PARAMETER; /* MSVCRT doesn't set errno here */
599
600   drivespec[0] += disk; /* make a drive letter */
601
602   if (GetDiskFreeSpaceW(disk==0?NULL:drivespec,ret,ret+1,ret+2,ret+3))
603   {
604     d->sectors_per_cluster = ret[0];
605     d->bytes_per_sector = ret[1];
606     d->avail_clusters = ret[2];
607     d->total_clusters = ret[3];
608     return 0;
609   }
610   err = GetLastError();
611   msvcrt_set_errno(err);
612   return err;
613 }
614
615 /*********************************************************************
616  *              _mkdir (MSVCRT.@)
617  *
618  * Create a directory.
619  *
620  * PARAMS
621  *  newdir [I] Name of directory to create.
622  *
623  * RETURNS
624  *  Success: 0. The directory indicated by newdir is created.
625  *  Failure: -1. errno indicates the error.
626  *
627  * NOTES
628  *  See CreateDirectoryA.
629  */
630 int CDECL MSVCRT__mkdir(const char * newdir)
631 {
632   if (CreateDirectoryA(newdir,NULL))
633     return 0;
634   msvcrt_set_errno(GetLastError());
635   return -1;
636 }
637
638 /*********************************************************************
639  *              _wmkdir (MSVCRT.@)
640  *
641  * Unicode version of _mkdir.
642  */
643 int CDECL _wmkdir(const MSVCRT_wchar_t* newdir)
644 {
645   if (CreateDirectoryW(newdir,NULL))
646     return 0;
647   msvcrt_set_errno(GetLastError());
648   return -1;
649 }
650
651 /*********************************************************************
652  *              _rmdir (MSVCRT.@)
653  *
654  * Delete a directory.
655  *
656  * PARAMS
657  *  dir [I] Name of directory to delete.
658  *
659  * RETURNS
660  *  Success: 0. The directory indicated by newdir is deleted.
661  *  Failure: -1. errno indicates the error.
662  *
663  * NOTES
664  *  See RemoveDirectoryA.
665  */
666 int CDECL MSVCRT__rmdir(const char * dir)
667 {
668   if (RemoveDirectoryA(dir))
669     return 0;
670   msvcrt_set_errno(GetLastError());
671   return -1;
672 }
673
674 /*********************************************************************
675  *              _wrmdir (MSVCRT.@)
676  *
677  * Unicode version of _rmdir.
678  */
679 int CDECL _wrmdir(const MSVCRT_wchar_t * dir)
680 {
681   if (RemoveDirectoryW(dir))
682     return 0;
683   msvcrt_set_errno(GetLastError());
684   return -1;
685 }
686
687 /*********************************************************************
688  *              _wsplitpath (MSVCRT.@)
689  *
690  * Unicode version of _splitpath.
691  */
692 void CDECL _wsplitpath(const MSVCRT_wchar_t *inpath, MSVCRT_wchar_t *drv, MSVCRT_wchar_t *dir,
693                        MSVCRT_wchar_t *fname, MSVCRT_wchar_t *ext )
694 {
695     const MSVCRT_wchar_t *p, *end;
696
697     if (inpath[0] && inpath[1] == ':')
698     {
699         if (drv)
700         {
701             drv[0] = inpath[0];
702             drv[1] = inpath[1];
703             drv[2] = 0;
704         }
705         inpath += 2;
706     }
707     else if (drv) drv[0] = 0;
708
709     /* look for end of directory part */
710     end = NULL;
711     for (p = inpath; *p; p++) if (*p == '/' || *p == '\\') end = p + 1;
712
713     if (end)  /* got a directory */
714     {
715         if (dir)
716         {
717             memcpy( dir, inpath, (end - inpath) * sizeof(MSVCRT_wchar_t) );
718             dir[end - inpath] = 0;
719         }
720         inpath = end;
721     }
722     else if (dir) dir[0] = 0;
723
724     /* look for extension: what's after the last dot */
725     end = NULL;
726     for (p = inpath; *p; p++) if (*p == '.') end = p;
727
728     if (!end) end = p; /* there's no extension */
729
730     if (fname)
731     {
732         memcpy( fname, inpath, (end - inpath) * sizeof(MSVCRT_wchar_t) );
733         fname[end - inpath] = 0;
734     }
735     if (ext) strcpyW( ext, end );
736 }
737
738 /******************************************************************
739  *              _wsplitpath_s (MSVCRT.@)
740  *
741  * Secure version of _wsplitpath
742  */
743 int _wsplitpath_s(const MSVCRT_wchar_t* inpath,
744                   MSVCRT_wchar_t* drive, MSVCRT_size_t sz_drive,
745                   MSVCRT_wchar_t* dir, MSVCRT_size_t sz_dir,
746                   MSVCRT_wchar_t* fname, MSVCRT_size_t sz_fname,
747                   MSVCRT_wchar_t* ext, MSVCRT_size_t sz_ext)
748 {
749     const MSVCRT_wchar_t *p, *end;
750
751     if (!inpath) return MSVCRT_EINVAL;
752     if (!drive && sz_drive) return MSVCRT_EINVAL;
753     if (drive && !sz_drive) return MSVCRT_EINVAL;
754     if (!dir && sz_dir) return MSVCRT_EINVAL;
755     if (dir && !sz_dir) return MSVCRT_EINVAL;
756     if (!fname && sz_fname) return MSVCRT_EINVAL;
757     if (fname && !sz_fname) return MSVCRT_EINVAL;
758     if (!ext && sz_ext) return MSVCRT_EINVAL;
759     if (ext && !sz_ext) return MSVCRT_EINVAL;
760
761     if (inpath[0] && inpath[1] == ':')
762     {
763         if (drive)
764         {
765             if (sz_drive <= 2) goto do_error;
766             drive[0] = inpath[0];
767             drive[1] = inpath[1];
768             drive[2] = 0;
769         }
770         inpath += 2;
771     }
772     else if (drive) drive[0] = '\0';
773
774     /* look for end of directory part */
775     end = NULL;
776     for (p = inpath; *p; p++) if (*p == '/' || *p == '\\') end = p + 1;
777
778     if (end)  /* got a directory */
779     {
780         if (dir)
781         {
782             if (sz_dir <= end - inpath) goto do_error;
783             memcpy( dir, inpath, (end - inpath) * sizeof(MSVCRT_wchar_t) );
784             dir[end - inpath] = 0;
785         }
786         inpath = end;
787     }
788     else if (dir) dir[0] = 0;
789
790     /* look for extension: what's after the last dot */
791     end = NULL;
792     for (p = inpath; *p; p++) if (*p == '.') end = p;
793
794     if (!end) end = p; /* there's no extension */
795
796     if (fname)
797     {
798         if (sz_fname <= end - inpath) goto do_error;
799         memcpy( fname, inpath, (end - inpath) * sizeof(MSVCRT_wchar_t) );
800         fname[end - inpath] = 0;
801     }
802     if (ext)
803     {
804         if (sz_ext <= strlenW(end)) goto do_error;
805         strcpyW( ext, end );
806     }
807     return 0;
808 do_error:
809     if (drive)  drive[0] = '\0';
810     if (dir)    dir[0] = '\0';
811     if (fname)  fname[0]= '\0';
812     if (ext)    ext[0]= '\0';
813     return MSVCRT_ERANGE;
814 }
815
816 /*********************************************************************
817  *              _wfullpath (MSVCRT.@)
818  *
819  * Unicode version of _fullpath.
820  */
821 MSVCRT_wchar_t * CDECL _wfullpath(MSVCRT_wchar_t * absPath, const MSVCRT_wchar_t* relPath, MSVCRT_size_t size)
822 {
823   DWORD rc;
824   WCHAR* buffer;
825   WCHAR* lastpart;
826   BOOL alloced = FALSE;
827
828   if (!relPath || !*relPath)
829     return _wgetcwd(absPath, size);
830
831   if (absPath == NULL)
832   {
833       buffer = MSVCRT_malloc(MAX_PATH * sizeof(WCHAR));
834       size = MAX_PATH;
835       alloced = TRUE;
836   }
837   else
838       buffer = absPath;
839
840   if (size < 4)
841   {
842     *MSVCRT__errno() = MSVCRT_ERANGE;
843     return NULL;
844   }
845
846   TRACE(":resolving relative path %s\n",debugstr_w(relPath));
847
848   rc = GetFullPathNameW(relPath,size,buffer,&lastpart);
849
850   if (rc > 0 && rc <= size )
851     return buffer;
852   else
853   {
854       if (alloced)
855           MSVCRT_free(buffer);
856         return NULL;
857   }
858 }
859
860 /*********************************************************************
861  *              _fullpath (MSVCRT.@)
862  *
863  * Create an absolute path from a relative path.
864  *
865  * PARAMS
866  *  absPath [O] Destination for absolute path
867  *  relPath [I] Relative path to convert to absolute
868  *  size    [I] Length of absPath in characters.
869  *
870  * RETURNS
871  * Success: If absPath is NULL, returns an allocated string containing the path.
872  *          Otherwise populates absPath with the path and returns it.
873  * Failure: NULL. errno indicates the error.
874  */
875 char * CDECL _fullpath(char * absPath, const char* relPath, unsigned int size)
876 {
877   DWORD rc;
878   char* lastpart;
879   char* buffer;
880   BOOL alloced = FALSE;
881
882   if (!relPath || !*relPath)
883     return _getcwd(absPath, size);
884
885   if (absPath == NULL)
886   {
887       buffer = MSVCRT_malloc(MAX_PATH);
888       size = MAX_PATH;
889       alloced = TRUE;
890   }
891   else
892       buffer = absPath;
893
894   if (size < 4)
895   {
896     *MSVCRT__errno() = MSVCRT_ERANGE;
897     return NULL;
898   }
899
900   TRACE(":resolving relative path '%s'\n",relPath);
901
902   rc = GetFullPathNameA(relPath,size,buffer,&lastpart);
903
904   if (rc > 0 && rc <= size)
905     return buffer;
906   else
907   {
908       if (alloced)
909           MSVCRT_free(buffer);
910         return NULL;
911   }
912 }
913
914 /*********************************************************************
915  *              _makepath (MSVCRT.@)
916  *
917  * Create a pathname.
918  *
919  * PARAMS
920  *  path      [O] Destination for created pathname
921  *  drive     [I] Drive letter (e.g. "A:")
922  *  directory [I] Directory
923  *  filename  [I] Name of the file, excluding extension
924  *  extension [I] File extension (e.g. ".TXT")
925  *
926  * RETURNS
927  *  Nothing. If path is not large enough to hold the resulting pathname,
928  *  random process memory will be overwritten.
929  */
930 VOID CDECL _makepath(char * path, const char * drive,
931                      const char *directory, const char * filename,
932                      const char * extension)
933 {
934     char *p = path;
935
936     TRACE("(%s %s %s %s)\n", debugstr_a(drive), debugstr_a(directory),
937           debugstr_a(filename), debugstr_a(extension) );
938
939     if ( !path )
940         return;
941
942     if (drive && drive[0])
943     {
944         *p++ = drive[0];
945         *p++ = ':';
946     }
947     if (directory && directory[0])
948     {
949         unsigned int len = strlen(directory);
950         memmove(p, directory, len);
951         p += len;
952         if (p[-1] != '/' && p[-1] != '\\')
953             *p++ = '\\';
954     }
955     if (filename && filename[0])
956     {
957         unsigned int len = strlen(filename);
958         memmove(p, filename, len);
959         p += len;
960     }
961     if (extension && extension[0])
962     {
963         if (extension[0] != '.')
964             *p++ = '.';
965         strcpy(p, extension);
966     }
967     else
968         *p = '\0';
969     TRACE("returning %s\n",path);
970 }
971
972 /*********************************************************************
973  *              _wmakepath (MSVCRT.@)
974  *
975  * Unicode version of _wmakepath.
976  */
977 VOID CDECL _wmakepath(MSVCRT_wchar_t *path, const MSVCRT_wchar_t *drive, const MSVCRT_wchar_t *directory,
978                       const MSVCRT_wchar_t *filename, const MSVCRT_wchar_t *extension)
979 {
980     MSVCRT_wchar_t *p = path;
981
982     TRACE("%s %s %s %s\n", debugstr_w(drive), debugstr_w(directory),
983           debugstr_w(filename), debugstr_w(extension));
984
985     if ( !path )
986         return;
987
988     if (drive && drive[0])
989     {
990         *p++ = drive[0];
991         *p++ = ':';
992     }
993     if (directory && directory[0])
994     {
995         unsigned int len = strlenW(directory);
996         memmove(p, directory, len * sizeof(MSVCRT_wchar_t));
997         p += len;
998         if (p[-1] != '/' && p[-1] != '\\')
999             *p++ = '\\';
1000     }
1001     if (filename && filename[0])
1002     {
1003         unsigned int len = strlenW(filename);
1004         memmove(p, filename, len * sizeof(MSVCRT_wchar_t));
1005         p += len;
1006     }
1007     if (extension && extension[0])
1008     {
1009         if (extension[0] != '.')
1010             *p++ = '.';
1011         strcpyW(p, extension);
1012     }
1013     else
1014         *p = '\0';
1015
1016     TRACE("returning %s\n", debugstr_w(path));
1017 }
1018
1019 /*********************************************************************
1020  *              _searchenv (MSVCRT.@)
1021  *
1022  * Search for a file in a list of paths from an environment variable.
1023  *
1024  * PARAMS
1025  *  file   [I] Name of the file to search for.
1026  *  env    [I] Name of the environment variable containing a list of paths.
1027  *  buf    [O] Destination for the found file path.
1028  *
1029  * RETURNS
1030  *  Nothing. If the file is not found, buf will contain an empty string
1031  *  and errno is set.
1032  */
1033 void CDECL _searchenv(const char* file, const char* env, char *buf)
1034 {
1035   char*envVal, *penv;
1036   char curPath[MAX_PATH];
1037
1038   *buf = '\0';
1039
1040   /* Try CWD first */
1041   if (GetFileAttributesA( file ) != INVALID_FILE_ATTRIBUTES)
1042   {
1043     GetFullPathNameA( file, MAX_PATH, buf, NULL );
1044     /* Sigh. This error is *always* set, regardless of success */
1045     msvcrt_set_errno(ERROR_FILE_NOT_FOUND);
1046     return;
1047   }
1048
1049   /* Search given environment variable */
1050   envVal = MSVCRT_getenv(env);
1051   if (!envVal)
1052   {
1053     msvcrt_set_errno(ERROR_FILE_NOT_FOUND);
1054     return;
1055   }
1056
1057   penv = envVal;
1058   TRACE(":searching for %s in paths %s\n", file, envVal);
1059
1060   do
1061   {
1062     char *end = penv;
1063
1064     while(*end && *end != ';') end++; /* Find end of next path */
1065     if (penv == end || !*penv)
1066     {
1067       msvcrt_set_errno(ERROR_FILE_NOT_FOUND);
1068       return;
1069     }
1070     memcpy(curPath, penv, end - penv);
1071     if (curPath[end - penv] != '/' && curPath[end - penv] != '\\')
1072     {
1073       curPath[end - penv] = '\\';
1074       curPath[end - penv + 1] = '\0';
1075     }
1076     else
1077       curPath[end - penv] = '\0';
1078
1079     strcat(curPath, file);
1080     TRACE("Checking for file %s\n", curPath);
1081     if (GetFileAttributesA( curPath ) != INVALID_FILE_ATTRIBUTES)
1082     {
1083       strcpy(buf, curPath);
1084       msvcrt_set_errno(ERROR_FILE_NOT_FOUND);
1085       return; /* Found */
1086     }
1087     penv = *end ? end + 1 : end;
1088   } while(1);
1089 }
1090
1091 /*********************************************************************
1092  *      _wsearchenv (MSVCRT.@)
1093  *
1094  * Unicode version of _searchenv
1095  */
1096 void CDECL _wsearchenv(const MSVCRT_wchar_t* file, const MSVCRT_wchar_t* env, MSVCRT_wchar_t *buf)
1097 {
1098   MSVCRT_wchar_t *envVal, *penv;
1099   MSVCRT_wchar_t curPath[MAX_PATH];
1100
1101   *buf = '\0';
1102
1103   /* Try CWD first */
1104   if (GetFileAttributesW( file ) != INVALID_FILE_ATTRIBUTES)
1105   {
1106     GetFullPathNameW( file, MAX_PATH, buf, NULL );
1107     /* Sigh. This error is *always* set, regardless of success */
1108     msvcrt_set_errno(ERROR_FILE_NOT_FOUND);
1109     return;
1110   }
1111
1112   /* Search given environment variable */
1113   envVal = _wgetenv(env);
1114   if (!envVal)
1115   {
1116     msvcrt_set_errno(ERROR_FILE_NOT_FOUND);
1117     return;
1118   }
1119
1120   penv = envVal;
1121   TRACE(":searching for %s in paths %s\n", debugstr_w(file), debugstr_w(envVal));
1122
1123   do
1124   {
1125     MSVCRT_wchar_t *end = penv;
1126
1127     while(*end && *end != ';') end++; /* Find end of next path */
1128     if (penv == end || !*penv)
1129     {
1130       msvcrt_set_errno(ERROR_FILE_NOT_FOUND);
1131       return;
1132     }
1133     memcpy(curPath, penv, (end - penv) * sizeof(MSVCRT_wchar_t));
1134     if (curPath[end - penv] != '/' && curPath[end - penv] != '\\')
1135     {
1136       curPath[end - penv] = '\\';
1137       curPath[end - penv + 1] = '\0';
1138     }
1139     else
1140       curPath[end - penv] = '\0';
1141
1142     strcatW(curPath, file);
1143     TRACE("Checking for file %s\n", debugstr_w(curPath));
1144     if (GetFileAttributesW( curPath ) != INVALID_FILE_ATTRIBUTES)
1145     {
1146       strcpyW(buf, curPath);
1147       msvcrt_set_errno(ERROR_FILE_NOT_FOUND);
1148       return; /* Found */
1149     }
1150     penv = *end ? end + 1 : end;
1151   } while(1);
1152 }