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