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