INT21_GetFreeDiskSpace(): The drive parameter is found in the DL
[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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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 "winreg.h"
33 #include "winternl.h"
34 #include "wine/unicode.h"
35 #include "msvcrt.h"
36 #include "msvcrt/errno.h"
37
38 #include "wine/unicode.h"
39 #include "msvcrt/direct.h"
40 #include "msvcrt/dos.h"
41 #include "msvcrt/io.h"
42 #include "msvcrt/stdlib.h"
43 #include "msvcrt/string.h"
44
45 #include "wine/debug.h"
46
47 WINE_DEFAULT_DEBUG_CHANNEL(msvcrt);
48
49 /* INTERNAL: Translate finddata_t to PWIN32_FIND_DATAA */
50 static void msvcrt_fttofd(LPWIN32_FIND_DATAA fd, struct _finddata_t* ft)
51 {
52   DWORD dw;
53
54   if (fd->dwFileAttributes == FILE_ATTRIBUTE_NORMAL)
55     ft->attrib = 0;
56   else
57     ft->attrib = fd->dwFileAttributes;
58
59   RtlTimeToSecondsSince1970( (LARGE_INTEGER *)&fd->ftCreationTime, &dw );
60   ft->time_create = dw;
61   RtlTimeToSecondsSince1970( (LARGE_INTEGER *)&fd->ftLastAccessTime, &dw );
62   ft->time_access = dw;
63   RtlTimeToSecondsSince1970( (LARGE_INTEGER *)&fd->ftLastWriteTime, &dw );
64   ft->time_write = dw;
65   ft->size = fd->nFileSizeLow;
66   strcpy(ft->name, fd->cFileName);
67 }
68
69 /* INTERNAL: Translate wfinddata_t to PWIN32_FIND_DATAA */
70 static void msvcrt_wfttofd(LPWIN32_FIND_DATAW fd, struct _wfinddata_t* ft)
71 {
72   DWORD dw;
73
74   if (fd->dwFileAttributes == FILE_ATTRIBUTE_NORMAL)
75     ft->attrib = 0;
76   else
77     ft->attrib = fd->dwFileAttributes;
78
79   RtlTimeToSecondsSince1970( (LARGE_INTEGER *)&fd->ftCreationTime, &dw );
80   ft->time_create = dw;
81   RtlTimeToSecondsSince1970( (LARGE_INTEGER *)&fd->ftLastAccessTime, &dw );
82   ft->time_access = dw;
83   RtlTimeToSecondsSince1970( (LARGE_INTEGER *)&fd->ftLastWriteTime, &dw );
84   ft->time_write = dw;
85   ft->size = fd->nFileSizeLow;
86   strcpyW(ft->name, fd->cFileName);
87 }
88
89 /*********************************************************************
90  *              _chdir (MSVCRT.@)
91  */
92 int _chdir(const char * newdir)
93 {
94   if (!SetCurrentDirectoryA(newdir))
95   {
96     MSVCRT__set_errno(newdir?GetLastError():0);
97     return -1;
98   }
99   return 0;
100 }
101
102 /*********************************************************************
103  *              _wchdir (MSVCRT.@)
104  */
105 int _wchdir(const MSVCRT_wchar_t * newdir)
106 {
107   if (!SetCurrentDirectoryW(newdir))
108   {
109     MSVCRT__set_errno(newdir?GetLastError():0);
110     return -1;
111   }
112   return 0;
113 }
114
115 /*********************************************************************
116  *              _chdrive (MSVCRT.@)
117  */
118 int _chdrive(int newdrive)
119 {
120   char buffer[3] = "A:";
121   buffer[0] += newdrive - 1;
122   if (!SetCurrentDirectoryA( buffer ))
123   {
124     MSVCRT__set_errno(GetLastError());
125     if (newdrive <= 0)
126       *MSVCRT__errno() = MSVCRT_EACCES;
127     return -1;
128   }
129   return 0;
130 }
131
132 /*********************************************************************
133  *              _findclose (MSVCRT.@)
134  */
135 int _findclose(long hand)
136 {
137   TRACE(":handle %ld\n",hand);
138   if (!FindClose((HANDLE)hand))
139   {
140     MSVCRT__set_errno(GetLastError());
141     return -1;
142   }
143   return 0;
144 }
145
146 /*********************************************************************
147  *              _findfirst (MSVCRT.@)
148  */
149 long _findfirst(const char * fspec, struct _finddata_t* ft)
150 {
151   WIN32_FIND_DATAA find_data;
152   HANDLE hfind;
153
154   hfind  = FindFirstFileA(fspec, &find_data);
155   if (hfind == INVALID_HANDLE_VALUE)
156   {
157     MSVCRT__set_errno(GetLastError());
158     return -1;
159   }
160   msvcrt_fttofd(&find_data,ft);
161   TRACE(":got handle %p\n",hfind);
162   return (long)hfind;
163 }
164
165 /*********************************************************************
166  *              _wfindfirst (MSVCRT.@)
167  */
168 long _wfindfirst(const MSVCRT_wchar_t * fspec, struct _wfinddata_t* ft)
169 {
170   WIN32_FIND_DATAW find_data;
171   HANDLE hfind;
172
173   hfind  = FindFirstFileW(fspec, &find_data);
174   if (hfind == INVALID_HANDLE_VALUE)
175   {
176     MSVCRT__set_errno(GetLastError());
177     return -1;
178   }
179   msvcrt_wfttofd(&find_data,ft);
180   TRACE(":got handle %p\n",hfind);
181   return (long)hfind;
182 }
183
184 /*********************************************************************
185  *              _findnext (MSVCRT.@)
186  */
187 int _findnext(long hand, struct _finddata_t * ft)
188 {
189   WIN32_FIND_DATAA find_data;
190
191   if (!FindNextFileA((HANDLE)hand, &find_data))
192   {
193     *MSVCRT__errno() = MSVCRT_ENOENT;
194     return -1;
195   }
196
197   msvcrt_fttofd(&find_data,ft);
198   return 0;
199 }
200
201 /*********************************************************************
202  *              _wfindnext (MSVCRT.@)
203  */
204 int _wfindnext(long hand, struct _wfinddata_t * ft)
205 {
206   WIN32_FIND_DATAW find_data;
207
208   if (!FindNextFileW((HANDLE)hand, &find_data))
209   {
210     *MSVCRT__errno() = MSVCRT_ENOENT;
211     return -1;
212   }
213
214   msvcrt_wfttofd(&find_data,ft);
215   return 0;
216 }
217
218 /*********************************************************************
219  *              _getcwd (MSVCRT.@)
220  */
221 char* _getcwd(char * buf, int size)
222 {
223   char dir[MAX_PATH];
224   int dir_len = GetCurrentDirectoryA(MAX_PATH,dir);
225
226   if (dir_len < 1)
227     return NULL; /* FIXME: Real return value untested */
228
229   if (!buf)
230   {
231     if (size < 0)
232       return _strdup(dir);
233     return msvcrt_strndup(dir,size);
234   }
235   if (dir_len >= size)
236   {
237     *MSVCRT__errno() = MSVCRT_ERANGE;
238     return NULL; /* buf too small */
239   }
240   strcpy(buf,dir);
241   return buf;
242 }
243
244 /*********************************************************************
245  *              _wgetcwd (MSVCRT.@)
246  */
247 MSVCRT_wchar_t* _wgetcwd(MSVCRT_wchar_t * buf, int size)
248 {
249   MSVCRT_wchar_t dir[MAX_PATH];
250   int dir_len = GetCurrentDirectoryW(MAX_PATH,dir);
251
252   if (dir_len < 1)
253     return NULL; /* FIXME: Real return value untested */
254
255   if (!buf)
256   {
257     if (size < 0)
258       return _wcsdup(dir);
259     return msvcrt_wstrndup(dir,size);
260   }
261   if (dir_len >= size)
262   {
263     *MSVCRT__errno() = MSVCRT_ERANGE;
264     return NULL; /* buf too small */
265   }
266   strcpyW(buf,dir);
267   return buf;
268 }
269
270 /*********************************************************************
271  *              _getdrive (MSVCRT.@)
272  */
273 int _getdrive(void)
274 {
275     char buffer[MAX_PATH];
276     if (!GetCurrentDirectoryA( sizeof(buffer), buffer )) return 0;
277     if (buffer[1] != ':') return 0;
278     return toupper(buffer[0]) - 'A' + 1;
279 }
280
281 /*********************************************************************
282  *              _getdcwd (MSVCRT.@)
283  */
284 char* _getdcwd(int drive, char * buf, int size)
285 {
286   static char* dummy;
287
288   TRACE(":drive %d(%c), size %d\n",drive, drive + 'A' - 1, size);
289
290   if (!drive || drive == _getdrive())
291     return _getcwd(buf,size); /* current */
292   else
293   {
294     char dir[MAX_PATH];
295     char drivespec[4] = {'A', ':', '\\', 0};
296     int dir_len;
297
298     drivespec[0] += drive - 1;
299     if (GetDriveTypeA(drivespec) < DRIVE_REMOVABLE)
300     {
301       *MSVCRT__errno() = MSVCRT_EACCES;
302       return NULL;
303     }
304
305     dir_len = GetFullPathNameA(drivespec,MAX_PATH,dir,&dummy);
306     if (dir_len >= size || dir_len < 1)
307     {
308       *MSVCRT__errno() = MSVCRT_ERANGE;
309       return NULL; /* buf too small */
310     }
311
312     TRACE(":returning '%s'\n", dir);
313     if (!buf)
314       return _strdup(dir); /* allocate */
315
316     strcpy(buf,dir);
317   }
318   return buf;
319 }
320
321 /*********************************************************************
322  *              _wgetdcwd (MSVCRT.@)
323  */
324 MSVCRT_wchar_t* _wgetdcwd(int drive, MSVCRT_wchar_t * buf, int size)
325 {
326   static MSVCRT_wchar_t* dummy;
327
328   TRACE(":drive %d(%c), size %d\n",drive, drive + 'A' - 1, size);
329
330   if (!drive || drive == _getdrive())
331     return _wgetcwd(buf,size); /* current */
332   else
333   {
334     MSVCRT_wchar_t dir[MAX_PATH];
335     MSVCRT_wchar_t drivespec[4] = {'A', ':', '\\', 0};
336     int dir_len;
337
338     drivespec[0] += drive - 1;
339     if (GetDriveTypeW(drivespec) < DRIVE_REMOVABLE)
340     {
341       *MSVCRT__errno() = MSVCRT_EACCES;
342       return NULL;
343     }
344
345     dir_len = GetFullPathNameW(drivespec,MAX_PATH,dir,&dummy);
346     if (dir_len >= size || dir_len < 1)
347     {
348       *MSVCRT__errno() = MSVCRT_ERANGE;
349       return NULL; /* buf too small */
350     }
351
352     TRACE(":returning %s\n", debugstr_w(dir));
353     if (!buf)
354       return _wcsdup(dir); /* allocate */
355     strcpyW(buf,dir);
356   }
357   return buf;
358 }
359
360 /*********************************************************************
361  *              _getdiskfree (MSVCRT.@)
362  */
363 unsigned int _getdiskfree(unsigned int disk, struct _diskfree_t* d)
364 {
365   char drivespec[4] = {'@', ':', '\\', 0};
366   DWORD ret[4];
367   unsigned int err;
368
369   if (disk > 26)
370     return ERROR_INVALID_PARAMETER; /* MSVCRT doesn't set errno here */
371
372   drivespec[0] += disk; /* make a drive letter */
373
374   if (GetDiskFreeSpaceA(disk==0?NULL:drivespec,ret,ret+1,ret+2,ret+3))
375   {
376     d->sectors_per_cluster = (unsigned)ret[0];
377     d->bytes_per_sector = (unsigned)ret[1];
378     d->avail_clusters = (unsigned)ret[2];
379     d->total_clusters = (unsigned)ret[3];
380     return 0;
381   }
382   err = GetLastError();
383   MSVCRT__set_errno(err);
384   return err;
385 }
386
387 /*********************************************************************
388  *              _mkdir (MSVCRT.@)
389  */
390 int _mkdir(const char * newdir)
391 {
392   if (CreateDirectoryA(newdir,NULL))
393     return 0;
394   MSVCRT__set_errno(GetLastError());
395   return -1;
396 }
397
398 /*********************************************************************
399  *              _wmkdir (MSVCRT.@)
400  */
401 int _wmkdir(const MSVCRT_wchar_t* newdir)
402 {
403   if (CreateDirectoryW(newdir,NULL))
404     return 0;
405   MSVCRT__set_errno(GetLastError());
406   return -1;
407 }
408
409 /*********************************************************************
410  *              _rmdir (MSVCRT.@)
411  */
412 int _rmdir(const char * dir)
413 {
414   if (RemoveDirectoryA(dir))
415     return 0;
416   MSVCRT__set_errno(GetLastError());
417   return -1;
418 }
419
420 /*********************************************************************
421  *              _wrmdir (MSVCRT.@)
422  */
423 int _wrmdir(const MSVCRT_wchar_t * dir)
424 {
425   if (RemoveDirectoryW(dir))
426     return 0;
427   MSVCRT__set_errno(GetLastError());
428   return -1;
429 }
430
431 /*********************************************************************
432  *              _wsplitpath (MSVCRT.@)
433  */
434 void _wsplitpath(const MSVCRT_wchar_t *inpath, MSVCRT_wchar_t *drv, MSVCRT_wchar_t *dir,
435                  MSVCRT_wchar_t *fname, MSVCRT_wchar_t *ext )
436 {
437     const MSVCRT_wchar_t *p, *end;
438
439     if (inpath[0] && inpath[1] == ':')
440     {
441         if (drv)
442         {
443             drv[0] = inpath[0];
444             drv[1] = inpath[1];
445             drv[2] = 0;
446         }
447         inpath += 2;
448     }
449     else if (drv) drv[0] = 0;
450
451     /* look for end of directory part */
452     end = NULL;
453     for (p = inpath; *p; p++) if (*p == '/' || *p == '\\') end = p + 1;
454
455     if (end)  /* got a directory */
456     {
457         if (dir)
458         {
459             memcpy( dir, inpath, (end - inpath) * sizeof(MSVCRT_wchar_t) );
460             dir[end - inpath] = 0;
461         }
462         inpath = end;
463     }
464     else if (dir) dir[0] = 0;
465
466     /* look for extension: what's after the last dot */
467     end = NULL;
468     for (p = inpath; *p; p++) if (*p == '.') end = p;
469
470     if (!end) end = p; /* there's no extension */
471
472     if (fname)
473     {
474         memcpy( fname, inpath, (end - inpath) * sizeof(MSVCRT_wchar_t) );
475         fname[end - inpath] = 0;
476     }
477     if (ext) strcpyW( ext, end );
478 }
479
480 /* INTERNAL: Helper for _fullpath. Modified PD code from 'snippets'. */
481 static void wmsvcrt_fln_fix(MSVCRT_wchar_t *path)
482 {
483   int dir_flag = 0, root_flag = 0;
484   MSVCRT_wchar_t *r, *p, *q, *s;
485   MSVCRT_wchar_t szbsdot[] = { '\\', '.', 0 };
486
487   /* Skip drive */
488   if (NULL == (r = strrchrW(path, ':')))
489     r = path;
490   else
491     ++r;
492
493   /* Ignore leading slashes */
494   while ('\\' == *r)
495     if ('\\' == r[1])
496       strcpyW(r, &r[1]);
497     else
498     {
499       root_flag = 1;
500       ++r;
501     }
502
503   p = r; /* Change "\\" to "\" */
504   while (NULL != (p = strchrW(p, '\\')))
505     if ('\\' ==  p[1])
506       strcpyW(p, &p[1]);
507     else
508       ++p;
509
510   while ('.' == *r) /* Scrunch leading ".\" */
511   {
512     if ('.' == r[1])
513     {
514       /* Ignore leading ".." */
515       for (p = (r += 2); *p && (*p != '\\'); ++p)
516         ;
517     }
518     else
519     {
520       for (p = r + 1 ;*p && (*p != '\\'); ++p)
521         ;
522     }
523     strcpyW(r, p + ((*p) ? 1 : 0));
524   }
525
526   while ('\\' == path[strlenW(path)-1])   /* Strip last '\\' */
527   {
528     dir_flag = 1;
529     path[strlenW(path)-1] = '\0';
530   }
531
532   s = r;
533
534   /* Look for "\." in path */
535
536   while (NULL != (p = strstrW(s, szbsdot)))
537   {
538     if ('.' == p[2])
539     {
540       /* Execute this section if ".." found */
541       q = p - 1;
542       while (q > r)           /* Backup one level           */
543       {
544         if (*q == '\\')
545           break;
546         --q;
547       }
548       if (q > r)
549       {
550         strcpyW(q, p + 3);
551         s = q;
552       }
553       else if ('.' != *q)
554       {
555         strcpyW(q + ((*q == '\\') ? 1 : 0),
556                p + 3 + ((*(p + 3)) ? 1 : 0));
557         s = q;
558       }
559       else  s = ++p;
560     }
561     else
562     {
563       /* Execute this section if "." found */
564       q = p + 2;
565       for ( ;*q && (*q != '\\'); ++q)
566         ;
567       strcpyW (p, q);
568     }
569   }
570
571   if (root_flag)  /* Embedded ".." could have bubbled up to root  */
572   {
573     for (p = r; *p && ('.' == *p || '\\' == *p); ++p)
574       ;
575     if (r != p)
576       strcpyW(r, p);
577   }
578
579   if (dir_flag)
580   {
581     MSVCRT_wchar_t szbs[] = { '\\', 0 };
582     
583     strcatW(path, szbs);
584   }
585 }
586
587 /*********************************************************************
588  *              _wfullpath (MSVCRT.@)
589  */
590 MSVCRT_wchar_t *_wfullpath(MSVCRT_wchar_t * absPath, const MSVCRT_wchar_t* relPath, MSVCRT_size_t size)
591 {
592   MSVCRT_wchar_t drive[5],dir[MAX_PATH],file[MAX_PATH],ext[MAX_PATH];
593   MSVCRT_wchar_t res[MAX_PATH];
594   size_t len;
595   MSVCRT_wchar_t szbs[] = { '\\', 0 };
596
597
598   res[0] = '\0';
599
600   if (!relPath || !*relPath)
601     return _wgetcwd(absPath, size);
602
603   if (size < 4)
604   {
605     *MSVCRT__errno() = MSVCRT_ERANGE;
606     return NULL;
607   }
608
609   TRACE(":resolving relative path '%s'\n",debugstr_w(relPath));
610
611   _wsplitpath(relPath, drive, dir, file, ext);
612
613   /* Get Directory and drive into 'res' */
614   if (!dir[0] || (dir[0] != '/' && dir[0] != '\\'))
615   {
616     /* Relative or no directory given */
617     _wgetdcwd(drive[0] ? toupper(drive[0]) - 'A' + 1 :  0, res, MAX_PATH);
618     strcatW(res,szbs);
619     if (dir[0])
620       strcatW(res,dir);
621     if (drive[0])
622       res[0] = drive[0]; /* If given a drive, preserve the letter case */
623   }
624   else
625   {
626     strcpyW(res,drive);
627     strcatW(res,dir);
628   }
629
630   strcatW(res,szbs);
631   strcatW(res, file);
632   strcatW(res, ext);
633   wmsvcrt_fln_fix(res);
634
635   len = strlenW(res);
636   if (len >= MAX_PATH || len >= (size_t)size)
637     return NULL; /* FIXME: errno? */
638
639   if (!absPath)
640     return _wcsdup(res);
641   strcpyW(absPath,res);
642   return absPath;
643 }
644
645 /* INTERNAL: Helper for _fullpath. Modified PD code from 'snippets'. */
646 static void msvcrt_fln_fix(char *path)
647 {
648   int dir_flag = 0, root_flag = 0;
649   char *r, *p, *q, *s;
650
651   /* Skip drive */
652   if (NULL == (r = strrchr(path, ':')))
653     r = path;
654   else
655     ++r;
656
657   /* Ignore leading slashes */
658   while ('\\' == *r)
659     if ('\\' == r[1])
660       strcpy(r, &r[1]);
661     else
662     {
663       root_flag = 1;
664       ++r;
665     }
666
667   p = r; /* Change "\\" to "\" */
668   while (NULL != (p = strchr(p, '\\')))
669     if ('\\' ==  p[1])
670       strcpy(p, &p[1]);
671     else
672       ++p;
673
674   while ('.' == *r) /* Scrunch leading ".\" */
675   {
676     if ('.' == r[1])
677     {
678       /* Ignore leading ".." */
679       for (p = (r += 2); *p && (*p != '\\'); ++p)
680         ;
681     }
682     else
683     {
684       for (p = r + 1 ;*p && (*p != '\\'); ++p)
685         ;
686     }
687     strcpy(r, p + ((*p) ? 1 : 0));
688   }
689
690   while ('\\' == path[strlen(path)-1])   /* Strip last '\\' */
691   {
692     dir_flag = 1;
693     path[strlen(path)-1] = '\0';
694   }
695
696   s = r;
697
698   /* Look for "\." in path */
699
700   while (NULL != (p = strstr(s, "\\.")))
701   {
702     if ('.' == p[2])
703     {
704       /* Execute this section if ".." found */
705       q = p - 1;
706       while (q > r)           /* Backup one level           */
707       {
708         if (*q == '\\')
709           break;
710         --q;
711       }
712       if (q > r)
713       {
714         strcpy(q, p + 3);
715         s = q;
716       }
717       else if ('.' != *q)
718       {
719         strcpy(q + ((*q == '\\') ? 1 : 0),
720                p + 3 + ((*(p + 3)) ? 1 : 0));
721         s = q;
722       }
723       else  s = ++p;
724     }
725     else
726     {
727       /* Execute this section if "." found */
728       q = p + 2;
729       for ( ;*q && (*q != '\\'); ++q)
730         ;
731       strcpy (p, q);
732     }
733   }
734
735   if (root_flag)  /* Embedded ".." could have bubbled up to root  */
736   {
737     for (p = r; *p && ('.' == *p || '\\' == *p); ++p)
738       ;
739     if (r != p)
740       strcpy(r, p);
741   }
742
743   if (dir_flag)
744     strcat(path, "\\");
745 }
746
747 /*********************************************************************
748  *              _fullpath (MSVCRT.@)
749  */
750 char *_fullpath(char * absPath, const char* relPath, unsigned int size)
751 {
752   char drive[5],dir[MAX_PATH],file[MAX_PATH],ext[MAX_PATH];
753   char res[MAX_PATH];
754   size_t len;
755
756   res[0] = '\0';
757
758   if (!relPath || !*relPath)
759     return _getcwd(absPath, size);
760
761   if (size < 4)
762   {
763     *MSVCRT__errno() = MSVCRT_ERANGE;
764     return NULL;
765   }
766
767   TRACE(":resolving relative path '%s'\n",relPath);
768
769   _splitpath(relPath, drive, dir, file, ext);
770
771   /* Get Directory and drive into 'res' */
772   if (!dir[0] || (dir[0] != '/' && dir[0] != '\\'))
773   {
774     /* Relative or no directory given */
775     _getdcwd(drive[0] ? toupper(drive[0]) - 'A' + 1 :  0, res, MAX_PATH);
776     strcat(res,"\\");
777     if (dir[0])
778       strcat(res,dir);
779     if (drive[0])
780       res[0] = drive[0]; /* If given a drive, preserve the letter case */
781   }
782   else
783   {
784     strcpy(res,drive);
785     strcat(res,dir);
786   }
787
788   strcat(res,"\\");
789   strcat(res, file);
790   strcat(res, ext);
791   msvcrt_fln_fix(res);
792
793   len = strlen(res);
794   if (len >= MAX_PATH || len >= (size_t)size)
795     return NULL; /* FIXME: errno? */
796
797   if (!absPath)
798     return _strdup(res);
799   strcpy(absPath,res);
800   return absPath;
801 }
802
803 /*********************************************************************
804  *              _makepath (MSVCRT.@)
805  */
806 VOID _makepath(char * path, const char * drive,
807                               const char *directory, const char * filename,
808                               const char * extension )
809 {
810     char ch;
811     char tmpPath[MAX_PATH];
812     TRACE("got %s %s %s %s\n", debugstr_a(drive), debugstr_a(directory),
813           debugstr_a(filename), debugstr_a(extension) );
814
815     if ( !path )
816         return;
817
818     tmpPath[0] = '\0';
819     if (drive && drive[0])
820     {
821         tmpPath[0] = drive[0];
822         tmpPath[1] = ':';
823         tmpPath[2] = 0;
824     }
825     if (directory && directory[0])
826     {
827         strcat(tmpPath, directory);
828         ch = tmpPath[strlen(tmpPath)-1];
829         if (ch != '/' && ch != '\\')
830             strcat(tmpPath,"\\");
831     }
832     if (filename && filename[0])
833     {
834         strcat(tmpPath, filename);
835         if (extension && extension[0])
836         {
837             if ( extension[0] != '.' )
838                 strcat(tmpPath,".");
839             strcat(tmpPath,extension);
840         }
841     }
842
843     strcpy( path, tmpPath );
844
845     TRACE("returning %s\n",path);
846 }
847
848 /*********************************************************************
849  *              _wmakepath (MSVCRT.@)
850  */
851 VOID _wmakepath(MSVCRT_wchar_t *path, const MSVCRT_wchar_t *drive, const MSVCRT_wchar_t *directory,
852                 const MSVCRT_wchar_t *filename, const MSVCRT_wchar_t *extension)
853 {
854     MSVCRT_wchar_t ch;
855     TRACE("%s %s %s %s\n", debugstr_w(drive), debugstr_w(directory),
856           debugstr_w(filename), debugstr_w(extension));
857
858     if ( !path )
859         return;
860
861     path[0] = 0;
862     if (drive && drive[0])
863     {
864         path[0] = drive[0];
865         path[1] = ':';
866         path[2] = 0;
867     }
868     if (directory && directory[0])
869     {
870         strcatW(path, directory);
871         ch = path[strlenW(path) - 1];
872         if (ch != '/' && ch != '\\')
873         {
874             static const MSVCRT_wchar_t backslashW[] = {'\\',0};
875             strcatW(path, backslashW);
876         }
877     }
878     if (filename && filename[0])
879     {
880         strcatW(path, filename);
881         if (extension && extension[0])
882         {
883             if ( extension[0] != '.' )
884             {
885                 static const MSVCRT_wchar_t dotW[] = {'.',0};
886                 strcatW(path, dotW);
887             }
888             strcatW(path, extension);
889         }
890     }
891
892     TRACE("returning %s\n", debugstr_w(path));
893 }
894
895 /*********************************************************************
896  *              _searchenv (MSVCRT.@)
897  */
898 void _searchenv(const char* file, const char* env, char *buf)
899 {
900   char*envVal, *penv;
901   char curPath[MAX_PATH];
902
903   *buf = '\0';
904
905   /* Try CWD first */
906   if (GetFileAttributesA( file ) != INVALID_FILE_ATTRIBUTES)
907   {
908     GetFullPathNameA( file, MAX_PATH, buf, NULL );
909     /* Sigh. This error is *always* set, regardless of success */
910     MSVCRT__set_errno(ERROR_FILE_NOT_FOUND);
911     return;
912   }
913
914   /* Search given environment variable */
915   envVal = MSVCRT_getenv(env);
916   if (!envVal)
917   {
918     MSVCRT__set_errno(ERROR_FILE_NOT_FOUND);
919     return;
920   }
921
922   penv = envVal;
923   TRACE(":searching for %s in paths %s\n", file, envVal);
924
925   do
926   {
927     char *end = penv;
928
929     while(*end && *end != ';') end++; /* Find end of next path */
930     if (penv == end || !*penv)
931     {
932       MSVCRT__set_errno(ERROR_FILE_NOT_FOUND);
933       return;
934     }
935     strncpy(curPath, penv, end - penv);
936     if (curPath[end - penv] != '/' || curPath[end - penv] != '\\')
937     {
938       curPath[end - penv] = '\\';
939       curPath[end - penv + 1] = '\0';
940     }
941     else
942       curPath[end - penv] = '\0';
943
944     strcat(curPath, file);
945     TRACE("Checking for file %s\n", curPath);
946     if (GetFileAttributesA( curPath ) != INVALID_FILE_ATTRIBUTES)
947     {
948       strcpy(buf, curPath);
949       MSVCRT__set_errno(ERROR_FILE_NOT_FOUND);
950       return; /* Found */
951     }
952     penv = *end ? end + 1 : end;
953   } while(1);
954 }