winex11: Add a trace in ExtEscape to make wgl problems easier to diagnose.
[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  *              _wfullpath (MSVCRT.@)
740  *
741  * Unicode version of _fullpath.
742  */
743 MSVCRT_wchar_t * CDECL _wfullpath(MSVCRT_wchar_t * absPath, const MSVCRT_wchar_t* relPath, MSVCRT_size_t size)
744 {
745   DWORD rc;
746   WCHAR* buffer;
747   WCHAR* lastpart;
748   BOOL alloced = FALSE;
749
750   if (!relPath || !*relPath)
751     return _wgetcwd(absPath, size);
752
753   if (absPath == NULL)
754   {
755       buffer = MSVCRT_malloc(MAX_PATH * sizeof(WCHAR));
756       size = MAX_PATH;
757       alloced = TRUE;
758   }
759   else
760       buffer = absPath;
761
762   if (size < 4)
763   {
764     *MSVCRT__errno() = MSVCRT_ERANGE;
765     return NULL;
766   }
767
768   TRACE(":resolving relative path %s\n",debugstr_w(relPath));
769
770   rc = GetFullPathNameW(relPath,size,buffer,&lastpart);
771
772   if (rc > 0 && rc <= size )
773     return buffer;
774   else
775   {
776       if (alloced)
777           MSVCRT_free(buffer);
778         return NULL;
779   }
780 }
781
782 /*********************************************************************
783  *              _fullpath (MSVCRT.@)
784  *
785  * Create an absolute path from a relative path.
786  *
787  * PARAMS
788  *  absPath [O] Destination for absolute path
789  *  relPath [I] Relative path to convert to absolute
790  *  size    [I] Length of absPath in characters.
791  *
792  * RETURNS
793  * Success: If absPath is NULL, returns an allocated string containing the path.
794  *          Otherwise populates absPath with the path and returns it.
795  * Failure: NULL. errno indicates the error.
796  */
797 char * CDECL _fullpath(char * absPath, const char* relPath, unsigned int size)
798 {
799   DWORD rc;
800   char* lastpart;
801   char* buffer;
802   BOOL alloced = FALSE;
803
804   if (!relPath || !*relPath)
805     return _getcwd(absPath, size);
806
807   if (absPath == NULL)
808   {
809       buffer = MSVCRT_malloc(MAX_PATH);
810       size = MAX_PATH;
811       alloced = TRUE;
812   }
813   else
814       buffer = absPath;
815
816   if (size < 4)
817   {
818     *MSVCRT__errno() = MSVCRT_ERANGE;
819     return NULL;
820   }
821
822   TRACE(":resolving relative path '%s'\n",relPath);
823
824   rc = GetFullPathNameA(relPath,size,buffer,&lastpart);
825
826   if (rc > 0 && rc <= size)
827     return buffer;
828   else
829   {
830       if (alloced)
831           MSVCRT_free(buffer);
832         return NULL;
833   }
834 }
835
836 /*********************************************************************
837  *              _makepath (MSVCRT.@)
838  *
839  * Create a pathname.
840  *
841  * PARAMS
842  *  path      [O] Destination for created pathname
843  *  drive     [I] Drive letter (e.g. "A:")
844  *  directory [I] Directory
845  *  filename  [I] Name of the file, excluding extension
846  *  extension [I] File extension (e.g. ".TXT")
847  *
848  * RETURNS
849  *  Nothing. If path is not large enough to hold the resulting pathname,
850  *  random process memory will be overwritten.
851  */
852 VOID CDECL _makepath(char * path, const char * drive,
853                      const char *directory, const char * filename,
854                      const char * extension)
855 {
856     char *p = path;
857
858     TRACE("(%s %s %s %s)\n", debugstr_a(drive), debugstr_a(directory),
859           debugstr_a(filename), debugstr_a(extension) );
860
861     if ( !path )
862         return;
863
864     if (drive && drive[0])
865     {
866         *p++ = drive[0];
867         *p++ = ':';
868         *p = 0;
869     }
870     if (directory && directory[0])
871     {
872         strcpy(p, directory);
873         p += strlen(directory) - 1;
874         if (*p != '/' && *p != '\\') {
875             strcat(p, "\\");
876             p++;
877         }
878         p++;
879     }
880     if (filename && filename[0])
881     {
882         strcpy(p, filename);
883         if (extension && extension[0])
884         {
885             if ( extension[0] != '.' )
886                 strcat(p,".");
887             strcat(p,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 environment 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 }
1013
1014 /*********************************************************************
1015  *      _wsearchenv (MSVCRT.@)
1016  *
1017  * Unicode version of _searchenv
1018  */
1019 void CDECL _wsearchenv(const MSVCRT_wchar_t* file, const MSVCRT_wchar_t* env, MSVCRT_wchar_t *buf)
1020 {
1021   MSVCRT_wchar_t *envVal, *penv;
1022   MSVCRT_wchar_t curPath[MAX_PATH];
1023
1024   *buf = '\0';
1025
1026   /* Try CWD first */
1027   if (GetFileAttributesW( file ) != INVALID_FILE_ATTRIBUTES)
1028   {
1029     GetFullPathNameW( file, MAX_PATH, buf, NULL );
1030     /* Sigh. This error is *always* set, regardless of success */
1031     msvcrt_set_errno(ERROR_FILE_NOT_FOUND);
1032     return;
1033   }
1034
1035   /* Search given environment variable */
1036   envVal = _wgetenv(env);
1037   if (!envVal)
1038   {
1039     msvcrt_set_errno(ERROR_FILE_NOT_FOUND);
1040     return;
1041   }
1042
1043   penv = envVal;
1044   TRACE(":searching for %s in paths %s\n", debugstr_w(file), debugstr_w(envVal));
1045
1046   do
1047   {
1048     MSVCRT_wchar_t *end = penv;
1049
1050     while(*end && *end != ';') end++; /* Find end of next path */
1051     if (penv == end || !*penv)
1052     {
1053       msvcrt_set_errno(ERROR_FILE_NOT_FOUND);
1054       return;
1055     }
1056     memcpy(curPath, penv, (end - penv) * sizeof(MSVCRT_wchar_t));
1057     if (curPath[end - penv] != '/' || curPath[end - penv] != '\\')
1058     {
1059       curPath[end - penv] = '\\';
1060       curPath[end - penv + 1] = '\0';
1061     }
1062     else
1063       curPath[end - penv] = '\0';
1064
1065     strcatW(curPath, file);
1066     TRACE("Checking for file %s\n", debugstr_w(curPath));
1067     if (GetFileAttributesW( curPath ) != INVALID_FILE_ATTRIBUTES)
1068     {
1069       strcpyW(buf, curPath);
1070       msvcrt_set_errno(ERROR_FILE_NOT_FOUND);
1071       return; /* Found */
1072     }
1073     penv = *end ? end + 1 : end;
1074   } while(1);
1075 }