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