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