msvcrt: Implement _findfirst64i32 and _findnext64i32.
[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_DATAA to finddata64_t  */
100 static void msvcrt_fttofd64( const WIN32_FIND_DATAA *fd, struct MSVCRT__finddata64_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   strcpy(ft->name, fd->cFileName);
117 }
118
119 /* INTERNAL: Translate WIN32_FIND_DATAA to finddata64i32_t  */
120 static void msvcrt_fttofd64i32( const WIN32_FIND_DATAA *fd, struct MSVCRT__finddata64i32_t* ft)
121 {
122   DWORD dw;
123
124   if (fd->dwFileAttributes == FILE_ATTRIBUTE_NORMAL)
125     ft->attrib = 0;
126   else
127     ft->attrib = fd->dwFileAttributes;
128
129   RtlTimeToSecondsSince1970( (const LARGE_INTEGER *)&fd->ftCreationTime, &dw );
130   ft->time_create = dw;
131   RtlTimeToSecondsSince1970( (const LARGE_INTEGER *)&fd->ftLastAccessTime, &dw );
132   ft->time_access = dw;
133   RtlTimeToSecondsSince1970( (const LARGE_INTEGER *)&fd->ftLastWriteTime, &dw );
134   ft->time_write = dw;
135   ft->size = fd->nFileSizeLow;
136   strcpy(ft->name, fd->cFileName);
137 }
138
139 /* INTERNAL: Translate WIN32_FIND_DATAW to wfinddatai64_t  */
140 static void msvcrt_wfttofdi64( const WIN32_FIND_DATAW *fd, struct MSVCRT__wfinddatai64_t* ft)
141 {
142   DWORD dw;
143
144   if (fd->dwFileAttributes == FILE_ATTRIBUTE_NORMAL)
145     ft->attrib = 0;
146   else
147     ft->attrib = fd->dwFileAttributes;
148
149   RtlTimeToSecondsSince1970( (const LARGE_INTEGER *)&fd->ftCreationTime, &dw );
150   ft->time_create = dw;
151   RtlTimeToSecondsSince1970( (const LARGE_INTEGER *)&fd->ftLastAccessTime, &dw );
152   ft->time_access = dw;
153   RtlTimeToSecondsSince1970( (const LARGE_INTEGER *)&fd->ftLastWriteTime, &dw );
154   ft->time_write = dw;
155   ft->size = ((__int64)fd->nFileSizeHigh) << 32 | fd->nFileSizeLow;
156   strcpyW(ft->name, fd->cFileName);
157 }
158
159 /*********************************************************************
160  *              _chdir (MSVCRT.@)
161  *
162  * Change the current working directory.
163  *
164  * PARAMS
165  *  newdir [I] Directory to change to
166  *
167  * RETURNS
168  *  Success: 0. The current working directory is set to newdir.
169  *  Failure: -1. errno indicates the error.
170  *
171  * NOTES
172  *  See SetCurrentDirectoryA.
173  */
174 int CDECL MSVCRT__chdir(const char * newdir)
175 {
176   if (!SetCurrentDirectoryA(newdir))
177   {
178     msvcrt_set_errno(newdir?GetLastError():0);
179     return -1;
180   }
181   return 0;
182 }
183
184 /*********************************************************************
185  *              _wchdir (MSVCRT.@)
186  *
187  * Unicode version of _chdir.
188  */
189 int CDECL _wchdir(const MSVCRT_wchar_t * newdir)
190 {
191   if (!SetCurrentDirectoryW(newdir))
192   {
193     msvcrt_set_errno(newdir?GetLastError():0);
194     return -1;
195   }
196   return 0;
197 }
198
199 /*********************************************************************
200  *              _chdrive (MSVCRT.@)
201  *
202  * Change the current drive.
203  *
204  * PARAMS
205  *  newdrive [I] Drive number to change to (1 = 'A', 2 = 'B', ...)
206  *
207  * RETURNS
208  *  Success: 0. The current drive is set to newdrive.
209  *  Failure: -1. errno indicates the error.
210  *
211  * NOTES
212  *  See SetCurrentDirectoryA.
213  */
214 int CDECL _chdrive(int newdrive)
215 {
216   WCHAR buffer[3] = {'A', ':', 0};
217
218   buffer[0] += newdrive - 1;
219   if (!SetCurrentDirectoryW( buffer ))
220   {
221     msvcrt_set_errno(GetLastError());
222     if (newdrive <= 0)
223       *MSVCRT__errno() = MSVCRT_EACCES;
224     return -1;
225   }
226   return 0;
227 }
228
229 /*********************************************************************
230  *              _findclose (MSVCRT.@)
231  *
232  * Close a handle returned by _findfirst().
233  *
234  * PARAMS
235  *  hand [I] Handle to close
236  *
237  * RETURNS
238  *  Success: 0. All resources associated with hand are freed.
239  *  Failure: -1. errno indicates the error.
240  *
241  * NOTES
242  *  See FindClose.
243  */
244 int CDECL MSVCRT__findclose(MSVCRT_intptr_t hand)
245 {
246   TRACE(":handle %ld\n",hand);
247   if (!FindClose((HANDLE)hand))
248   {
249     msvcrt_set_errno(GetLastError());
250     return -1;
251   }
252   return 0;
253 }
254
255 /*********************************************************************
256  *              _findfirst (MSVCRT.@)
257  *
258  * Open a handle for iterating through a directory.
259  *
260  * PARAMS
261  *  fspec [I] File specification of files to iterate.
262  *  ft    [O] Information for the first file found.
263  *
264  * RETURNS
265  *  Success: A handle suitable for passing to _findnext() and _findclose().
266  *           ft is populated with the details of the found file.
267  *  Failure: -1. errno indicates the error.
268  *
269  * NOTES
270  *  See FindFirstFileA.
271  */
272 MSVCRT_intptr_t CDECL MSVCRT__findfirst(const char * fspec, struct MSVCRT__finddata_t* ft)
273 {
274   WIN32_FIND_DATAA find_data;
275   HANDLE hfind;
276
277   hfind  = FindFirstFileA(fspec, &find_data);
278   if (hfind == INVALID_HANDLE_VALUE)
279   {
280     msvcrt_set_errno(GetLastError());
281     return -1;
282   }
283   msvcrt_fttofd(&find_data,ft);
284   TRACE(":got handle %p\n",hfind);
285   return (MSVCRT_intptr_t)hfind;
286 }
287
288 /*********************************************************************
289  *              _wfindfirst (MSVCRT.@)
290  *
291  * Unicode version of _findfirst.
292  */
293 MSVCRT_intptr_t CDECL MSVCRT__wfindfirst(const MSVCRT_wchar_t * fspec, struct MSVCRT__wfinddata_t* ft)
294 {
295   WIN32_FIND_DATAW find_data;
296   HANDLE hfind;
297
298   hfind  = FindFirstFileW(fspec, &find_data);
299   if (hfind == INVALID_HANDLE_VALUE)
300   {
301     msvcrt_set_errno(GetLastError());
302     return -1;
303   }
304   msvcrt_wfttofd(&find_data,ft);
305   TRACE(":got handle %p\n",hfind);
306   return (MSVCRT_intptr_t)hfind;
307 }
308
309 /*********************************************************************
310  *              _findfirsti64 (MSVCRT.@)
311  *
312  * 64-bit version of _findfirst.
313  */
314 MSVCRT_intptr_t CDECL MSVCRT__findfirsti64(const char * fspec, struct MSVCRT__finddatai64_t* ft)
315 {
316   WIN32_FIND_DATAA find_data;
317   HANDLE hfind;
318
319   hfind  = FindFirstFileA(fspec, &find_data);
320   if (hfind == INVALID_HANDLE_VALUE)
321   {
322     msvcrt_set_errno(GetLastError());
323     return -1;
324   }
325   msvcrt_fttofdi64(&find_data,ft);
326   TRACE(":got handle %p\n",hfind);
327   return (MSVCRT_intptr_t)hfind;
328 }
329
330 /*********************************************************************
331  *              _findfirst64 (MSVCRT.@)
332  *
333  * 64-bit version of _findfirst.
334  */
335 MSVCRT_intptr_t CDECL MSVCRT__findfirst64(const char * fspec, struct MSVCRT__finddata64_t* ft)
336 {
337   WIN32_FIND_DATAA find_data;
338   HANDLE hfind;
339
340   hfind  = FindFirstFileA(fspec, &find_data);
341   if (hfind == INVALID_HANDLE_VALUE)
342   {
343     msvcrt_set_errno(GetLastError());
344     return -1;
345   }
346   msvcrt_fttofd64(&find_data,ft);
347   TRACE(":got handle %p\n",hfind);
348   return (MSVCRT_intptr_t)hfind;
349 }
350
351 /*********************************************************************
352  *              _findfirst64i32 (MSVCRT.@)
353  *
354  * 64-bit/32-bit version of _findfirst.
355  */
356 MSVCRT_intptr_t CDECL MSVCRT__findfirst64i32(const char * fspec, struct MSVCRT__finddata64i32_t* ft)
357 {
358   WIN32_FIND_DATAA find_data;
359   HANDLE hfind;
360
361   hfind  = FindFirstFileA(fspec, &find_data);
362   if (hfind == INVALID_HANDLE_VALUE)
363   {
364     msvcrt_set_errno(GetLastError());
365     return -1;
366   }
367   msvcrt_fttofd64i32(&find_data,ft);
368   TRACE(":got handle %p\n",hfind);
369   return (MSVCRT_intptr_t)hfind;
370 }
371
372 /*********************************************************************
373  *              _wfindfirsti64 (MSVCRT.@)
374  *
375  * Unicode version of _findfirsti64.
376  */
377 MSVCRT_intptr_t CDECL MSVCRT__wfindfirsti64(const MSVCRT_wchar_t * fspec, struct MSVCRT__wfinddatai64_t* ft)
378 {
379   WIN32_FIND_DATAW find_data;
380   HANDLE hfind;
381
382   hfind  = FindFirstFileW(fspec, &find_data);
383   if (hfind == INVALID_HANDLE_VALUE)
384   {
385     msvcrt_set_errno(GetLastError());
386     return -1;
387   }
388   msvcrt_wfttofdi64(&find_data,ft);
389   TRACE(":got handle %p\n",hfind);
390   return (MSVCRT_intptr_t)hfind;
391 }
392
393 /*********************************************************************
394  *              _findnext (MSVCRT.@)
395  *
396  * Find the next file from a file search handle.
397  *
398  * PARAMS
399  *  hand  [I] Handle to the search returned from _findfirst().
400  *  ft    [O] Information for the file found.
401  *
402  * RETURNS
403  *  Success: 0. ft is populated with the details of the found file.
404  *  Failure: -1. errno indicates the error.
405  *
406  * NOTES
407  *  See FindNextFileA.
408  */
409 int CDECL MSVCRT__findnext(MSVCRT_intptr_t hand, struct MSVCRT__finddata_t * ft)
410 {
411   WIN32_FIND_DATAA find_data;
412
413   if (!FindNextFileA((HANDLE)hand, &find_data))
414   {
415     *MSVCRT__errno() = MSVCRT_ENOENT;
416     return -1;
417   }
418
419   msvcrt_fttofd(&find_data,ft);
420   return 0;
421 }
422
423 /*********************************************************************
424  *              _wfindnext (MSVCRT.@)
425  *
426  * Unicode version of _findnext.
427  */
428 int CDECL MSVCRT__wfindnext(MSVCRT_intptr_t hand, struct MSVCRT__wfinddata_t * ft)
429 {
430   WIN32_FIND_DATAW find_data;
431
432   if (!FindNextFileW((HANDLE)hand, &find_data))
433   {
434     *MSVCRT__errno() = MSVCRT_ENOENT;
435     return -1;
436   }
437
438   msvcrt_wfttofd(&find_data,ft);
439   return 0;
440 }
441
442 /*********************************************************************
443  *              _findnexti64 (MSVCRT.@)
444  *
445  * 64-bit version of _findnext.
446  */
447 int CDECL MSVCRT__findnexti64(MSVCRT_intptr_t hand, struct MSVCRT__finddatai64_t * ft)
448 {
449   WIN32_FIND_DATAA find_data;
450
451   if (!FindNextFileA((HANDLE)hand, &find_data))
452   {
453     *MSVCRT__errno() = MSVCRT_ENOENT;
454     return -1;
455   }
456
457   msvcrt_fttofdi64(&find_data,ft);
458   return 0;
459 }
460
461 /*********************************************************************
462  *              _findnext64 (MSVCRT.@)
463  *
464  * 64-bit version of _findnext.
465  */
466 int CDECL MSVCRT__findnext64(long hand, struct MSVCRT__finddata64_t * ft)
467 {
468   WIN32_FIND_DATAA find_data;
469
470   if (!FindNextFileA((HANDLE)hand, &find_data))
471   {
472     *MSVCRT__errno() = MSVCRT_ENOENT;
473     return -1;
474   }
475
476   msvcrt_fttofd64(&find_data,ft);
477   return 0;
478 }
479
480 /*********************************************************************
481  *              _findnext64i32 (MSVCRT.@)
482  *
483  * 64-bit/32-bit version of _findnext.
484  */
485 int CDECL MSVCRT__findnext64i32(long hand, struct MSVCRT__finddata64i32_t * ft)
486 {
487   WIN32_FIND_DATAA find_data;
488
489   if (!FindNextFileA((HANDLE)hand, &find_data))
490   {
491     *MSVCRT__errno() = MSVCRT_ENOENT;
492     return -1;
493   }
494
495   msvcrt_fttofd64i32(&find_data,ft);
496   return 0;
497 }
498
499 /*********************************************************************
500  *              _wfindnexti64 (MSVCRT.@)
501  *
502  * Unicode version of _findnexti64.
503  */
504 int CDECL MSVCRT__wfindnexti64(MSVCRT_intptr_t hand, struct MSVCRT__wfinddatai64_t * ft)
505 {
506   WIN32_FIND_DATAW find_data;
507
508   if (!FindNextFileW((HANDLE)hand, &find_data))
509   {
510     *MSVCRT__errno() = MSVCRT_ENOENT;
511     return -1;
512   }
513
514   msvcrt_wfttofdi64(&find_data,ft);
515   return 0;
516 }
517
518 /*********************************************************************
519  *              _getcwd (MSVCRT.@)
520  *
521  * Get the current working directory.
522  *
523  * PARAMS
524  *  buf  [O] Destination for current working directory.
525  *  size [I] Size of buf in characters
526  *
527  * RETURNS
528  * Success: If buf is NULL, returns an allocated string containing the path.
529  *          Otherwise populates buf with the path and returns it.
530  * Failure: NULL. errno indicates the error.
531  */
532 char* CDECL _getcwd(char * buf, int size)
533 {
534   char dir[MAX_PATH];
535   int dir_len = GetCurrentDirectoryA(MAX_PATH,dir);
536
537   if (dir_len < 1)
538     return NULL; /* FIXME: Real return value untested */
539
540   if (!buf)
541   {
542       if (size <= dir_len) size = dir_len + 1;
543       if (!(buf = MSVCRT_malloc( size ))) return NULL;
544   }
545   else if (dir_len >= size)
546   {
547     *MSVCRT__errno() = MSVCRT_ERANGE;
548     return NULL; /* buf too small */
549   }
550   strcpy(buf,dir);
551   return buf;
552 }
553
554 /*********************************************************************
555  *              _wgetcwd (MSVCRT.@)
556  *
557  * Unicode version of _getcwd.
558  */
559 MSVCRT_wchar_t* CDECL _wgetcwd(MSVCRT_wchar_t * buf, int size)
560 {
561   MSVCRT_wchar_t dir[MAX_PATH];
562   int dir_len = GetCurrentDirectoryW(MAX_PATH,dir);
563
564   if (dir_len < 1)
565     return NULL; /* FIXME: Real return value untested */
566
567   if (!buf)
568   {
569       if (size <= dir_len) size = dir_len + 1;
570       if (!(buf = MSVCRT_malloc( size * sizeof(WCHAR) ))) return NULL;
571   }
572   if (dir_len >= size)
573   {
574     *MSVCRT__errno() = MSVCRT_ERANGE;
575     return NULL; /* buf too small */
576   }
577   strcpyW(buf,dir);
578   return buf;
579 }
580
581 /*********************************************************************
582  *              _getdrive (MSVCRT.@)
583  *
584  * Get the current drive number.
585  *
586  * PARAMS
587  *  None.
588  *
589  * RETURNS
590  *  Success: The drive letter number from 1 to 26 ("A:" to "Z:").
591  *  Failure: 0.
592  */
593 int CDECL _getdrive(void)
594 {
595     WCHAR buffer[MAX_PATH];
596     if (GetCurrentDirectoryW( MAX_PATH, buffer ) &&
597         buffer[0] >= 'A' && buffer[0] <= 'z' && buffer[1] == ':')
598         return toupperW(buffer[0]) - 'A' + 1;
599     return 0;
600 }
601
602 /*********************************************************************
603  *              _getdcwd (MSVCRT.@)
604  *
605  * Get the current working directory on a given disk.
606  * 
607  * PARAMS
608  *  drive [I] Drive letter to get the current working directory from.
609  *  buf   [O] Destination for the current working directory.
610  *  size  [I] Length of drive in characters.
611  *
612  * RETURNS
613  *  Success: If drive is NULL, returns an allocated string containing the path.
614  *           Otherwise populates drive with the path and returns it.
615  *  Failure: NULL. errno indicates the error.
616  */
617 char* CDECL _getdcwd(int drive, char * buf, int size)
618 {
619   static char* dummy;
620
621   TRACE(":drive %d(%c), size %d\n",drive, drive + 'A' - 1, size);
622
623   if (!drive || drive == _getdrive())
624     return _getcwd(buf,size); /* current */
625   else
626   {
627     char dir[MAX_PATH];
628     char drivespec[4] = {'A', ':', 0};
629     int dir_len;
630
631     drivespec[0] += drive - 1;
632     if (GetDriveTypeA(drivespec) < DRIVE_REMOVABLE)
633     {
634       *MSVCRT__errno() = MSVCRT_EACCES;
635       return NULL;
636     }
637
638     dir_len = GetFullPathNameA(drivespec,MAX_PATH,dir,&dummy);
639     if (dir_len >= size || dir_len < 1)
640     {
641       *MSVCRT__errno() = MSVCRT_ERANGE;
642       return NULL; /* buf too small */
643     }
644
645     TRACE(":returning '%s'\n", dir);
646     if (!buf)
647       return _strdup(dir); /* allocate */
648
649     strcpy(buf,dir);
650   }
651   return buf;
652 }
653
654 /*********************************************************************
655  *              _wgetdcwd (MSVCRT.@)
656  *
657  * Unicode version of _wgetdcwd.
658  */
659 MSVCRT_wchar_t* CDECL _wgetdcwd(int drive, MSVCRT_wchar_t * buf, int size)
660 {
661   static MSVCRT_wchar_t* dummy;
662
663   TRACE(":drive %d(%c), size %d\n",drive, drive + 'A' - 1, size);
664
665   if (!drive || drive == _getdrive())
666     return _wgetcwd(buf,size); /* current */
667   else
668   {
669     MSVCRT_wchar_t dir[MAX_PATH];
670     MSVCRT_wchar_t drivespec[4] = {'A', ':', '\\', 0};
671     int dir_len;
672
673     drivespec[0] += drive - 1;
674     if (GetDriveTypeW(drivespec) < DRIVE_REMOVABLE)
675     {
676       *MSVCRT__errno() = MSVCRT_EACCES;
677       return NULL;
678     }
679
680     dir_len = GetFullPathNameW(drivespec,MAX_PATH,dir,&dummy);
681     if (dir_len >= size || dir_len < 1)
682     {
683       *MSVCRT__errno() = MSVCRT_ERANGE;
684       return NULL; /* buf too small */
685     }
686
687     TRACE(":returning %s\n", debugstr_w(dir));
688     if (!buf)
689       return _wcsdup(dir); /* allocate */
690     strcpyW(buf,dir);
691   }
692   return buf;
693 }
694
695 /*********************************************************************
696  *              _getdiskfree (MSVCRT.@)
697  *
698  * Get information about the free space on a drive.
699  *
700  * PARAMS
701  *  disk [I] Drive number to get information about (1 = 'A', 2 = 'B', ...)
702  *  info [O] Destination for the resulting information.
703  *
704  * RETURNS
705  *  Success: 0. info is updated with the free space information.
706  *  Failure: An error code from GetLastError().
707  *
708  * NOTES
709  *  See GetLastError().
710  */
711 unsigned int CDECL MSVCRT__getdiskfree(unsigned int disk, struct MSVCRT__diskfree_t * d)
712 {
713   WCHAR drivespec[4] = {'@', ':', '\\', 0};
714   DWORD ret[4];
715   unsigned int err;
716
717   if (disk > 26)
718     return ERROR_INVALID_PARAMETER; /* MSVCRT doesn't set errno here */
719
720   drivespec[0] += disk; /* make a drive letter */
721
722   if (GetDiskFreeSpaceW(disk==0?NULL:drivespec,ret,ret+1,ret+2,ret+3))
723   {
724     d->sectors_per_cluster = ret[0];
725     d->bytes_per_sector = ret[1];
726     d->avail_clusters = ret[2];
727     d->total_clusters = ret[3];
728     return 0;
729   }
730   err = GetLastError();
731   msvcrt_set_errno(err);
732   return err;
733 }
734
735 /*********************************************************************
736  *              _mkdir (MSVCRT.@)
737  *
738  * Create a directory.
739  *
740  * PARAMS
741  *  newdir [I] Name of directory to create.
742  *
743  * RETURNS
744  *  Success: 0. The directory indicated by newdir is created.
745  *  Failure: -1. errno indicates the error.
746  *
747  * NOTES
748  *  See CreateDirectoryA.
749  */
750 int CDECL MSVCRT__mkdir(const char * newdir)
751 {
752   if (CreateDirectoryA(newdir,NULL))
753     return 0;
754   msvcrt_set_errno(GetLastError());
755   return -1;
756 }
757
758 /*********************************************************************
759  *              _wmkdir (MSVCRT.@)
760  *
761  * Unicode version of _mkdir.
762  */
763 int CDECL _wmkdir(const MSVCRT_wchar_t* newdir)
764 {
765   if (CreateDirectoryW(newdir,NULL))
766     return 0;
767   msvcrt_set_errno(GetLastError());
768   return -1;
769 }
770
771 /*********************************************************************
772  *              _rmdir (MSVCRT.@)
773  *
774  * Delete a directory.
775  *
776  * PARAMS
777  *  dir [I] Name of directory to delete.
778  *
779  * RETURNS
780  *  Success: 0. The directory indicated by newdir is deleted.
781  *  Failure: -1. errno indicates the error.
782  *
783  * NOTES
784  *  See RemoveDirectoryA.
785  */
786 int CDECL MSVCRT__rmdir(const char * dir)
787 {
788   if (RemoveDirectoryA(dir))
789     return 0;
790   msvcrt_set_errno(GetLastError());
791   return -1;
792 }
793
794 /*********************************************************************
795  *              _wrmdir (MSVCRT.@)
796  *
797  * Unicode version of _rmdir.
798  */
799 int CDECL _wrmdir(const MSVCRT_wchar_t * dir)
800 {
801   if (RemoveDirectoryW(dir))
802     return 0;
803   msvcrt_set_errno(GetLastError());
804   return -1;
805 }
806
807 /******************************************************************
808  *              _splitpath_s (MSVCRT.@)
809  */
810 int _splitpath_s(const char* inpath,
811         char* drive, MSVCRT_size_t sz_drive,
812         char* dir, MSVCRT_size_t sz_dir,
813         char* fname, MSVCRT_size_t sz_fname,
814         char* ext, MSVCRT_size_t sz_ext)
815 {
816     const char *p, *end;
817
818     if (!inpath || (!drive && sz_drive) ||
819             (drive && !sz_drive) ||
820             (!dir && sz_dir) ||
821             (dir && !sz_dir) ||
822             (!fname && sz_fname) ||
823             (fname && !sz_fname) ||
824             (!ext && sz_ext) ||
825             (ext && !sz_ext))
826     {
827         *MSVCRT__errno() = MSVCRT_EINVAL;
828         return MSVCRT_EINVAL;
829     }
830
831     if (inpath[0] && inpath[1] == ':')
832     {
833         if (drive)
834         {
835             if (sz_drive <= 2) goto do_error;
836             drive[0] = inpath[0];
837             drive[1] = inpath[1];
838             drive[2] = 0;
839         }
840         inpath += 2;
841     }
842     else if (drive) drive[0] = '\0';
843
844     /* look for end of directory part */
845     end = NULL;
846     for (p = inpath; *p; p++) if (*p == '/' || *p == '\\') end = p + 1;
847
848     if (end)  /* got a directory */
849     {
850         if (dir)
851         {
852             if (sz_dir <= end - inpath) goto do_error;
853             memcpy( dir, inpath, (end - inpath) );
854             dir[end - inpath] = 0;
855         }
856         inpath = end;
857     }
858     else if (dir) dir[0] = 0;
859
860     /* look for extension: what's after the last dot */
861     end = NULL;
862     for (p = inpath; *p; p++) if (*p == '.') end = p;
863
864     if (!end) end = p; /* there's no extension */
865
866     if (fname)
867     {
868         if (sz_fname <= end - inpath) goto do_error;
869         memcpy( fname, inpath, (end - inpath) );
870         fname[end - inpath] = 0;
871     }
872     if (ext)
873     {
874         if (sz_ext <= strlen(end)) goto do_error;
875         strcpy( ext, end );
876     }
877     return 0;
878 do_error:
879     if (drive)  drive[0] = '\0';
880     if (dir)    dir[0] = '\0';
881     if (fname)  fname[0]= '\0';
882     if (ext)    ext[0]= '\0';
883     *MSVCRT__errno() = MSVCRT_ERANGE;
884     return MSVCRT_ERANGE;
885 }
886
887 /*********************************************************************
888  *              _splitpath (MSVCRT.@)
889  */
890 void CDECL _splitpath(const char *inpath, char *drv, char *dir,
891         char *fname, char *ext)
892 {
893     _splitpath_s(inpath, drv, drv?MSVCRT__MAX_DRIVE:0, dir, dir?MSVCRT__MAX_DIR:0,
894             fname, fname?MSVCRT__MAX_FNAME:0, ext, ext?MSVCRT__MAX_EXT:0);
895 }
896
897 /******************************************************************
898  *              _wsplitpath_s (MSVCRT.@)
899  *
900  * Secure version of _wsplitpath
901  */
902 int _wsplitpath_s(const MSVCRT_wchar_t* inpath,
903                   MSVCRT_wchar_t* drive, MSVCRT_size_t sz_drive,
904                   MSVCRT_wchar_t* dir, MSVCRT_size_t sz_dir,
905                   MSVCRT_wchar_t* fname, MSVCRT_size_t sz_fname,
906                   MSVCRT_wchar_t* ext, MSVCRT_size_t sz_ext)
907 {
908     const MSVCRT_wchar_t *p, *end;
909
910     if (!inpath || (!drive && sz_drive) ||
911             (drive && !sz_drive) ||
912             (!dir && sz_dir) ||
913             (dir && !sz_dir) ||
914             (!fname && sz_fname) ||
915             (fname && !sz_fname) ||
916             (!ext && sz_ext) ||
917             (ext && !sz_ext))
918     {
919         *MSVCRT__errno() = MSVCRT_EINVAL;
920         return MSVCRT_EINVAL;
921     }
922
923     if (inpath[0] && inpath[1] == ':')
924     {
925         if (drive)
926         {
927             if (sz_drive <= 2) goto do_error;
928             drive[0] = inpath[0];
929             drive[1] = inpath[1];
930             drive[2] = 0;
931         }
932         inpath += 2;
933     }
934     else if (drive) drive[0] = '\0';
935
936     /* look for end of directory part */
937     end = NULL;
938     for (p = inpath; *p; p++) if (*p == '/' || *p == '\\') end = p + 1;
939
940     if (end)  /* got a directory */
941     {
942         if (dir)
943         {
944             if (sz_dir <= end - inpath) goto do_error;
945             memcpy( dir, inpath, (end - inpath) * sizeof(MSVCRT_wchar_t) );
946             dir[end - inpath] = 0;
947         }
948         inpath = end;
949     }
950     else if (dir) dir[0] = 0;
951
952     /* look for extension: what's after the last dot */
953     end = NULL;
954     for (p = inpath; *p; p++) if (*p == '.') end = p;
955
956     if (!end) end = p; /* there's no extension */
957
958     if (fname)
959     {
960         if (sz_fname <= end - inpath) goto do_error;
961         memcpy( fname, inpath, (end - inpath) * sizeof(MSVCRT_wchar_t) );
962         fname[end - inpath] = 0;
963     }
964     if (ext)
965     {
966         if (sz_ext <= strlenW(end)) goto do_error;
967         strcpyW( ext, end );
968     }
969     return 0;
970 do_error:
971     if (drive)  drive[0] = '\0';
972     if (dir)    dir[0] = '\0';
973     if (fname)  fname[0]= '\0';
974     if (ext)    ext[0]= '\0';
975     *MSVCRT__errno() = MSVCRT_ERANGE;
976     return MSVCRT_ERANGE;
977 }
978
979 /*********************************************************************
980  *              _wsplitpath (MSVCRT.@)
981  *
982  * Unicode version of _splitpath.
983  */
984 void CDECL _wsplitpath(const MSVCRT_wchar_t *inpath, MSVCRT_wchar_t *drv, MSVCRT_wchar_t *dir,
985         MSVCRT_wchar_t *fname, MSVCRT_wchar_t *ext)
986 {
987     _wsplitpath_s(inpath, drv, drv?MSVCRT__MAX_DRIVE:0, dir, dir?MSVCRT__MAX_DIR:0,
988             fname, fname?MSVCRT__MAX_FNAME:0, ext, ext?MSVCRT__MAX_EXT:0);
989 }
990
991 /*********************************************************************
992  *              _wfullpath (MSVCRT.@)
993  *
994  * Unicode version of _fullpath.
995  */
996 MSVCRT_wchar_t * CDECL _wfullpath(MSVCRT_wchar_t * absPath, const MSVCRT_wchar_t* relPath, MSVCRT_size_t size)
997 {
998   DWORD rc;
999   WCHAR* buffer;
1000   WCHAR* lastpart;
1001   BOOL alloced = FALSE;
1002
1003   if (!relPath || !*relPath)
1004     return _wgetcwd(absPath, size);
1005
1006   if (absPath == NULL)
1007   {
1008       buffer = MSVCRT_malloc(MAX_PATH * sizeof(WCHAR));
1009       size = MAX_PATH;
1010       alloced = TRUE;
1011   }
1012   else
1013       buffer = absPath;
1014
1015   if (size < 4)
1016   {
1017     *MSVCRT__errno() = MSVCRT_ERANGE;
1018     return NULL;
1019   }
1020
1021   TRACE(":resolving relative path %s\n",debugstr_w(relPath));
1022
1023   rc = GetFullPathNameW(relPath,size,buffer,&lastpart);
1024
1025   if (rc > 0 && rc <= size )
1026     return buffer;
1027   else
1028   {
1029       if (alloced)
1030           MSVCRT_free(buffer);
1031         return NULL;
1032   }
1033 }
1034
1035 /*********************************************************************
1036  *              _fullpath (MSVCRT.@)
1037  *
1038  * Create an absolute path from a relative path.
1039  *
1040  * PARAMS
1041  *  absPath [O] Destination for absolute path
1042  *  relPath [I] Relative path to convert to absolute
1043  *  size    [I] Length of absPath in characters.
1044  *
1045  * RETURNS
1046  * Success: If absPath is NULL, returns an allocated string containing the path.
1047  *          Otherwise populates absPath with the path and returns it.
1048  * Failure: NULL. errno indicates the error.
1049  */
1050 char * CDECL _fullpath(char * absPath, const char* relPath, unsigned int size)
1051 {
1052   DWORD rc;
1053   char* lastpart;
1054   char* buffer;
1055   BOOL alloced = FALSE;
1056
1057   if (!relPath || !*relPath)
1058     return _getcwd(absPath, size);
1059
1060   if (absPath == NULL)
1061   {
1062       buffer = MSVCRT_malloc(MAX_PATH);
1063       size = MAX_PATH;
1064       alloced = TRUE;
1065   }
1066   else
1067       buffer = absPath;
1068
1069   if (size < 4)
1070   {
1071     *MSVCRT__errno() = MSVCRT_ERANGE;
1072     return NULL;
1073   }
1074
1075   TRACE(":resolving relative path '%s'\n",relPath);
1076
1077   rc = GetFullPathNameA(relPath,size,buffer,&lastpart);
1078
1079   if (rc > 0 && rc <= size)
1080     return buffer;
1081   else
1082   {
1083       if (alloced)
1084           MSVCRT_free(buffer);
1085         return NULL;
1086   }
1087 }
1088
1089 /*********************************************************************
1090  *              _makepath (MSVCRT.@)
1091  *
1092  * Create a pathname.
1093  *
1094  * PARAMS
1095  *  path      [O] Destination for created pathname
1096  *  drive     [I] Drive letter (e.g. "A:")
1097  *  directory [I] Directory
1098  *  filename  [I] Name of the file, excluding extension
1099  *  extension [I] File extension (e.g. ".TXT")
1100  *
1101  * RETURNS
1102  *  Nothing. If path is not large enough to hold the resulting pathname,
1103  *  random process memory will be overwritten.
1104  */
1105 VOID CDECL _makepath(char * path, const char * drive,
1106                      const char *directory, const char * filename,
1107                      const char * extension)
1108 {
1109     char *p = path;
1110
1111     TRACE("(%s %s %s %s)\n", debugstr_a(drive), debugstr_a(directory),
1112           debugstr_a(filename), debugstr_a(extension) );
1113
1114     if ( !path )
1115         return;
1116
1117     if (drive && drive[0])
1118     {
1119         *p++ = drive[0];
1120         *p++ = ':';
1121     }
1122     if (directory && directory[0])
1123     {
1124         unsigned int len = strlen(directory);
1125         memmove(p, directory, len);
1126         p += len;
1127         if (p[-1] != '/' && p[-1] != '\\')
1128             *p++ = '\\';
1129     }
1130     if (filename && filename[0])
1131     {
1132         unsigned int len = strlen(filename);
1133         memmove(p, filename, len);
1134         p += len;
1135     }
1136     if (extension && extension[0])
1137     {
1138         if (extension[0] != '.')
1139             *p++ = '.';
1140         strcpy(p, extension);
1141     }
1142     else
1143         *p = '\0';
1144     TRACE("returning %s\n",path);
1145 }
1146
1147 /*********************************************************************
1148  *              _wmakepath (MSVCRT.@)
1149  *
1150  * Unicode version of _wmakepath.
1151  */
1152 VOID CDECL _wmakepath(MSVCRT_wchar_t *path, const MSVCRT_wchar_t *drive, const MSVCRT_wchar_t *directory,
1153                       const MSVCRT_wchar_t *filename, const MSVCRT_wchar_t *extension)
1154 {
1155     MSVCRT_wchar_t *p = path;
1156
1157     TRACE("%s %s %s %s\n", debugstr_w(drive), debugstr_w(directory),
1158           debugstr_w(filename), debugstr_w(extension));
1159
1160     if ( !path )
1161         return;
1162
1163     if (drive && drive[0])
1164     {
1165         *p++ = drive[0];
1166         *p++ = ':';
1167     }
1168     if (directory && directory[0])
1169     {
1170         unsigned int len = strlenW(directory);
1171         memmove(p, directory, len * sizeof(MSVCRT_wchar_t));
1172         p += len;
1173         if (p[-1] != '/' && p[-1] != '\\')
1174             *p++ = '\\';
1175     }
1176     if (filename && filename[0])
1177     {
1178         unsigned int len = strlenW(filename);
1179         memmove(p, filename, len * sizeof(MSVCRT_wchar_t));
1180         p += len;
1181     }
1182     if (extension && extension[0])
1183     {
1184         if (extension[0] != '.')
1185             *p++ = '.';
1186         strcpyW(p, extension);
1187     }
1188     else
1189         *p = '\0';
1190
1191     TRACE("returning %s\n", debugstr_w(path));
1192 }
1193
1194 /*********************************************************************
1195  *              _makepath_s (MSVCRT.@)
1196  *
1197  * Safe version of _makepath.
1198  */
1199 int CDECL _makepath_s(char *path, MSVCRT_size_t size, const char *drive,
1200                       const char *directory, const char *filename,
1201                       const char *extension)
1202 {
1203     char *p = path;
1204
1205     if (!path || !size)
1206     {
1207         *MSVCRT__errno() = MSVCRT_EINVAL;
1208         return MSVCRT_EINVAL;
1209     }
1210
1211     if (drive && drive[0])
1212     {
1213         if (size <= 2)
1214             goto range;
1215
1216         *p++ = drive[0];
1217         *p++ = ':';
1218         size -= 2;
1219     }
1220
1221     if (directory && directory[0])
1222     {
1223         unsigned int len = strlen(directory);
1224         unsigned int needs_separator = directory[len - 1] != '/' && directory[len - 1] != '\\';
1225         unsigned int copylen = min(size - 1, len);
1226
1227         if (size < 2)
1228             goto range;
1229
1230         memmove(p, directory, copylen);
1231
1232         if (size <= len)
1233             goto range;
1234
1235         p += copylen;
1236         size -= copylen;
1237
1238         if (needs_separator)
1239         {
1240             if (size < 2)
1241                 goto range;
1242
1243             *p++ = '\\';
1244             size -= 1;
1245         }
1246     }
1247
1248     if (filename && filename[0])
1249     {
1250         unsigned int len = strlen(filename);
1251         unsigned int copylen = min(size - 1, len);
1252
1253         if (size < 2)
1254             goto range;
1255
1256         memmove(p, filename, copylen);
1257
1258         if (size <= len)
1259             goto range;
1260
1261         p += len;
1262         size -= len;
1263     }
1264
1265     if (extension && extension[0])
1266     {
1267         unsigned int len = strlen(extension);
1268         unsigned int needs_period = extension[0] != '.';
1269         unsigned int copylen;
1270
1271         if (size < 2)
1272             goto range;
1273
1274         if (needs_period)
1275         {
1276             *p++ = '.';
1277             size -= 1;
1278         }
1279
1280         copylen = min(size - 1, len);
1281         memcpy(p, extension, copylen);
1282
1283         if (size <= len)
1284             goto range;
1285
1286         p += copylen;
1287     }
1288
1289     *p = '\0';
1290     return 0;
1291
1292 range:
1293     path[0] = '\0';
1294     *MSVCRT__errno() = MSVCRT_ERANGE;
1295     return MSVCRT_ERANGE;
1296 }
1297
1298 /*********************************************************************
1299  *              _wmakepath_s (MSVCRT.@)
1300  *
1301  * Safe version of _wmakepath.
1302  */
1303 int CDECL _wmakepath_s(MSVCRT_wchar_t *path, MSVCRT_size_t size, const MSVCRT_wchar_t *drive,
1304                        const MSVCRT_wchar_t *directory, const MSVCRT_wchar_t *filename,
1305                        const MSVCRT_wchar_t *extension)
1306 {
1307     MSVCRT_wchar_t *p = path;
1308
1309     if (!path || !size)
1310     {
1311         *MSVCRT__errno() = MSVCRT_EINVAL;
1312         return MSVCRT_EINVAL;
1313     }
1314
1315     if (drive && drive[0])
1316     {
1317         if (size <= 2)
1318             goto range;
1319
1320         *p++ = drive[0];
1321         *p++ = ':';
1322         size -= 2;
1323     }
1324
1325     if (directory && directory[0])
1326     {
1327         unsigned int len = strlenW(directory);
1328         unsigned int needs_separator = directory[len - 1] != '/' && directory[len - 1] != '\\';
1329         unsigned int copylen = min(size - 1, len);
1330
1331         if (size < 2)
1332             goto range;
1333
1334         memmove(p, directory, copylen * sizeof(MSVCRT_wchar_t));
1335
1336         if (size <= len)
1337             goto range;
1338
1339         p += copylen;
1340         size -= copylen;
1341
1342         if (needs_separator)
1343         {
1344             if (size < 2)
1345                 goto range;
1346
1347             *p++ = '\\';
1348             size -= 1;
1349         }
1350     }
1351
1352     if (filename && filename[0])
1353     {
1354         unsigned int len = strlenW(filename);
1355         unsigned int copylen = min(size - 1, len);
1356
1357         if (size < 2)
1358             goto range;
1359
1360         memmove(p, filename, copylen * sizeof(MSVCRT_wchar_t));
1361
1362         if (size <= len)
1363             goto range;
1364
1365         p += len;
1366         size -= len;
1367     }
1368
1369     if (extension && extension[0])
1370     {
1371         unsigned int len = strlenW(extension);
1372         unsigned int needs_period = extension[0] != '.';
1373         unsigned int copylen;
1374
1375         if (size < 2)
1376             goto range;
1377
1378         if (needs_period)
1379         {
1380             *p++ = '.';
1381             size -= 1;
1382         }
1383
1384         copylen = min(size - 1, len);
1385         memcpy(p, extension, copylen * sizeof(MSVCRT_wchar_t));
1386
1387         if (size <= len)
1388             goto range;
1389
1390         p += copylen;
1391     }
1392
1393     *p = '\0';
1394     return 0;
1395
1396 range:
1397     path[0] = '\0';
1398     *MSVCRT__errno() = MSVCRT_ERANGE;
1399     return MSVCRT_ERANGE;
1400 }
1401
1402 /*********************************************************************
1403  *              _searchenv (MSVCRT.@)
1404  *
1405  * Search for a file in a list of paths from an environment variable.
1406  *
1407  * PARAMS
1408  *  file   [I] Name of the file to search for.
1409  *  env    [I] Name of the environment variable containing a list of paths.
1410  *  buf    [O] Destination for the found file path.
1411  *
1412  * RETURNS
1413  *  Nothing. If the file is not found, buf will contain an empty string
1414  *  and errno is set.
1415  */
1416 void CDECL _searchenv(const char* file, const char* env, char *buf)
1417 {
1418   char*envVal, *penv;
1419   char curPath[MAX_PATH];
1420
1421   *buf = '\0';
1422
1423   /* Try CWD first */
1424   if (GetFileAttributesA( file ) != INVALID_FILE_ATTRIBUTES)
1425   {
1426     GetFullPathNameA( file, MAX_PATH, buf, NULL );
1427     /* Sigh. This error is *always* set, regardless of success */
1428     msvcrt_set_errno(ERROR_FILE_NOT_FOUND);
1429     return;
1430   }
1431
1432   /* Search given environment variable */
1433   envVal = MSVCRT_getenv(env);
1434   if (!envVal)
1435   {
1436     msvcrt_set_errno(ERROR_FILE_NOT_FOUND);
1437     return;
1438   }
1439
1440   penv = envVal;
1441   TRACE(":searching for %s in paths %s\n", file, envVal);
1442
1443   do
1444   {
1445     char *end = penv;
1446
1447     while(*end && *end != ';') end++; /* Find end of next path */
1448     if (penv == end || !*penv)
1449     {
1450       msvcrt_set_errno(ERROR_FILE_NOT_FOUND);
1451       return;
1452     }
1453     memcpy(curPath, penv, end - penv);
1454     if (curPath[end - penv] != '/' && curPath[end - penv] != '\\')
1455     {
1456       curPath[end - penv] = '\\';
1457       curPath[end - penv + 1] = '\0';
1458     }
1459     else
1460       curPath[end - penv] = '\0';
1461
1462     strcat(curPath, file);
1463     TRACE("Checking for file %s\n", curPath);
1464     if (GetFileAttributesA( curPath ) != INVALID_FILE_ATTRIBUTES)
1465     {
1466       strcpy(buf, curPath);
1467       msvcrt_set_errno(ERROR_FILE_NOT_FOUND);
1468       return; /* Found */
1469     }
1470     penv = *end ? end + 1 : end;
1471   } while(1);
1472 }
1473
1474 /*********************************************************************
1475  *      _wsearchenv (MSVCRT.@)
1476  *
1477  * Unicode version of _searchenv
1478  */
1479 void CDECL _wsearchenv(const MSVCRT_wchar_t* file, const MSVCRT_wchar_t* env, MSVCRT_wchar_t *buf)
1480 {
1481   MSVCRT_wchar_t *envVal, *penv;
1482   MSVCRT_wchar_t curPath[MAX_PATH];
1483
1484   *buf = '\0';
1485
1486   /* Try CWD first */
1487   if (GetFileAttributesW( file ) != INVALID_FILE_ATTRIBUTES)
1488   {
1489     GetFullPathNameW( file, MAX_PATH, buf, NULL );
1490     /* Sigh. This error is *always* set, regardless of success */
1491     msvcrt_set_errno(ERROR_FILE_NOT_FOUND);
1492     return;
1493   }
1494
1495   /* Search given environment variable */
1496   envVal = _wgetenv(env);
1497   if (!envVal)
1498   {
1499     msvcrt_set_errno(ERROR_FILE_NOT_FOUND);
1500     return;
1501   }
1502
1503   penv = envVal;
1504   TRACE(":searching for %s in paths %s\n", debugstr_w(file), debugstr_w(envVal));
1505
1506   do
1507   {
1508     MSVCRT_wchar_t *end = penv;
1509
1510     while(*end && *end != ';') end++; /* Find end of next path */
1511     if (penv == end || !*penv)
1512     {
1513       msvcrt_set_errno(ERROR_FILE_NOT_FOUND);
1514       return;
1515     }
1516     memcpy(curPath, penv, (end - penv) * sizeof(MSVCRT_wchar_t));
1517     if (curPath[end - penv] != '/' && curPath[end - penv] != '\\')
1518     {
1519       curPath[end - penv] = '\\';
1520       curPath[end - penv + 1] = '\0';
1521     }
1522     else
1523       curPath[end - penv] = '\0';
1524
1525     strcatW(curPath, file);
1526     TRACE("Checking for file %s\n", debugstr_w(curPath));
1527     if (GetFileAttributesW( curPath ) != INVALID_FILE_ATTRIBUTES)
1528     {
1529       strcpyW(buf, curPath);
1530       msvcrt_set_errno(ERROR_FILE_NOT_FOUND);
1531       return; /* Found */
1532     }
1533     penv = *end ? end + 1 : end;
1534   } while(1);
1535 }