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