Fixed definitions of TTTOOLINFOA/W_V1_SIZE and
[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   /* Modified PD code from 'snippets' collection. */
438   MSVCRT_wchar_t ch, *ptr, *p;
439   MSVCRT_wchar_t pathbuff[MAX_PATH],*path=pathbuff;
440
441   /* FIXME: Should be an strncpyW or something */
442   strcpyW(pathbuff, inpath);
443   TRACE(":splitting path %s\n",debugstr_w(path));
444
445   /* convert slashes to backslashes for searching */
446   for (ptr = (MSVCRT_wchar_t*)path; *ptr; ++ptr)
447     if (*ptr == '/')
448       *ptr = '\\';
449
450   /* look for drive spec */
451   if ((ptr = strchrW(path, ':')) != 0)
452   {
453     ++ptr;
454     if (drv)
455     {
456       strncpyW(drv, path, ptr - path);
457       drv[ptr - path] = 0;
458     }
459     path = ptr;
460   }
461   else if (drv)
462     *drv = 0;
463
464   /* find rightmost backslash or leftmost colon */
465   if ((ptr = strrchrW(path, '\\')) == NULL)
466     ptr = (strchrW(path, ':'));
467
468   if (!ptr)
469   {
470     ptr = (MSVCRT_wchar_t *)path; /* no path */
471     if (dir)
472       *dir = 0;
473   }
474   else
475   {
476     ++ptr; /* skip the delimiter */
477     if (dir)
478     {
479       ch = *ptr;
480       *ptr = 0;
481       strcpyW(dir, path);
482       *ptr = ch;
483     }
484   }
485
486   if ((p = strrchrW(ptr, '.')) == NULL)
487   {
488     if (fname)
489       strcpyW(fname, ptr);
490     if (ext)
491       *ext = 0;
492   }
493   else
494   {
495     *p = 0;
496     if (fname)
497       strcpyW(fname, ptr);
498     *p = '.';
499     if (ext)
500       strcpyW(ext, p);
501   }
502
503   /* Fix pathological case - Win returns ':' as part of the
504    * directory when no drive letter is given.
505    */
506   if (drv && drv[0] == ':')
507   {
508     *drv = 0;
509     if (dir)
510     {
511       pathbuff[0] = ':';
512       pathbuff[1] = 0;
513       strcatW(pathbuff,dir);
514       strcpyW(dir, pathbuff);
515     }
516   }
517 }
518
519 /* INTERNAL: Helper for _fullpath. Modified PD code from 'snippets'. */
520 static void wmsvcrt_fln_fix(MSVCRT_wchar_t *path)
521 {
522   int dir_flag = 0, root_flag = 0;
523   MSVCRT_wchar_t *r, *p, *q, *s;
524   MSVCRT_wchar_t szbsdot[] = { '\\', '.', 0 };
525
526   /* Skip drive */
527   if (NULL == (r = strrchrW(path, ':')))
528     r = path;
529   else
530     ++r;
531
532   /* Ignore leading slashes */
533   while ('\\' == *r)
534     if ('\\' == r[1])
535       strcpyW(r, &r[1]);
536     else
537     {
538       root_flag = 1;
539       ++r;
540     }
541
542   p = r; /* Change "\\" to "\" */
543   while (NULL != (p = strchrW(p, '\\')))
544     if ('\\' ==  p[1])
545       strcpyW(p, &p[1]);
546     else
547       ++p;
548
549   while ('.' == *r) /* Scrunch leading ".\" */
550   {
551     if ('.' == r[1])
552     {
553       /* Ignore leading ".." */
554       for (p = (r += 2); *p && (*p != '\\'); ++p)
555         ;
556     }
557     else
558     {
559       for (p = r + 1 ;*p && (*p != '\\'); ++p)
560         ;
561     }
562     strcpyW(r, p + ((*p) ? 1 : 0));
563   }
564
565   while ('\\' == path[strlenW(path)-1])   /* Strip last '\\' */
566   {
567     dir_flag = 1;
568     path[strlenW(path)-1] = '\0';
569   }
570
571   s = r;
572
573   /* Look for "\." in path */
574
575   while (NULL != (p = strstrW(s, szbsdot)))
576   {
577     if ('.' == p[2])
578     {
579       /* Execute this section if ".." found */
580       q = p - 1;
581       while (q > r)           /* Backup one level           */
582       {
583         if (*q == '\\')
584           break;
585         --q;
586       }
587       if (q > r)
588       {
589         strcpyW(q, p + 3);
590         s = q;
591       }
592       else if ('.' != *q)
593       {
594         strcpyW(q + ((*q == '\\') ? 1 : 0),
595                p + 3 + ((*(p + 3)) ? 1 : 0));
596         s = q;
597       }
598       else  s = ++p;
599     }
600     else
601     {
602       /* Execute this section if "." found */
603       q = p + 2;
604       for ( ;*q && (*q != '\\'); ++q)
605         ;
606       strcpyW (p, q);
607     }
608   }
609
610   if (root_flag)  /* Embedded ".." could have bubbled up to root  */
611   {
612     for (p = r; *p && ('.' == *p || '\\' == *p); ++p)
613       ;
614     if (r != p)
615       strcpyW(r, p);
616   }
617
618   if (dir_flag)
619   {
620     MSVCRT_wchar_t szbs[] = { '\\', 0 };
621     
622     strcatW(path, szbs);
623   }
624 }
625
626 /*********************************************************************
627  *              _wfullpath (MSVCRT.@)
628  */
629 MSVCRT_wchar_t *_wfullpath(MSVCRT_wchar_t * absPath, const MSVCRT_wchar_t* relPath, unsigned int size)
630 {
631   MSVCRT_wchar_t drive[5],dir[MAX_PATH],file[MAX_PATH],ext[MAX_PATH];
632   MSVCRT_wchar_t res[MAX_PATH];
633   size_t len;
634   MSVCRT_wchar_t szbs[] = { '\\', 0 };
635
636
637   res[0] = '\0';
638
639   if (!relPath || !*relPath)
640     return _wgetcwd(absPath, size);
641
642   if (size < 4)
643   {
644     *MSVCRT__errno() = MSVCRT_ERANGE;
645     return NULL;
646   }
647
648   TRACE(":resolving relative path '%s'\n",debugstr_w(relPath));
649
650   _wsplitpath(relPath, drive, dir, file, ext);
651
652   /* Get Directory and drive into 'res' */
653   if (!dir[0] || (dir[0] != '/' && dir[0] != '\\'))
654   {
655     /* Relative or no directory given */
656     _wgetdcwd(drive[0] ? toupper(drive[0]) - 'A' + 1 :  0, res, MAX_PATH);
657     strcatW(res,szbs);
658     if (dir[0])
659       strcatW(res,dir);
660     if (drive[0])
661       res[0] = drive[0]; /* If given a drive, preserve the letter case */
662   }
663   else
664   {
665     strcpyW(res,drive);
666     strcatW(res,dir);
667   }
668
669   strcatW(res,szbs);
670   strcatW(res, file);
671   strcatW(res, ext);
672   wmsvcrt_fln_fix(res);
673
674   len = strlenW(res);
675   if (len >= MAX_PATH || len >= (size_t)size)
676     return NULL; /* FIXME: errno? */
677
678   if (!absPath)
679     return _wcsdup(res);
680   strcpyW(absPath,res);
681   return absPath;
682 }
683
684 /* INTERNAL: Helper for _fullpath. Modified PD code from 'snippets'. */
685 static void msvcrt_fln_fix(char *path)
686 {
687   int dir_flag = 0, root_flag = 0;
688   char *r, *p, *q, *s;
689
690   /* Skip drive */
691   if (NULL == (r = strrchr(path, ':')))
692     r = path;
693   else
694     ++r;
695
696   /* Ignore leading slashes */
697   while ('\\' == *r)
698     if ('\\' == r[1])
699       strcpy(r, &r[1]);
700     else
701     {
702       root_flag = 1;
703       ++r;
704     }
705
706   p = r; /* Change "\\" to "\" */
707   while (NULL != (p = strchr(p, '\\')))
708     if ('\\' ==  p[1])
709       strcpy(p, &p[1]);
710     else
711       ++p;
712
713   while ('.' == *r) /* Scrunch leading ".\" */
714   {
715     if ('.' == r[1])
716     {
717       /* Ignore leading ".." */
718       for (p = (r += 2); *p && (*p != '\\'); ++p)
719         ;
720     }
721     else
722     {
723       for (p = r + 1 ;*p && (*p != '\\'); ++p)
724         ;
725     }
726     strcpy(r, p + ((*p) ? 1 : 0));
727   }
728
729   while ('\\' == path[strlen(path)-1])   /* Strip last '\\' */
730   {
731     dir_flag = 1;
732     path[strlen(path)-1] = '\0';
733   }
734
735   s = r;
736
737   /* Look for "\." in path */
738
739   while (NULL != (p = strstr(s, "\\.")))
740   {
741     if ('.' == p[2])
742     {
743       /* Execute this section if ".." found */
744       q = p - 1;
745       while (q > r)           /* Backup one level           */
746       {
747         if (*q == '\\')
748           break;
749         --q;
750       }
751       if (q > r)
752       {
753         strcpy(q, p + 3);
754         s = q;
755       }
756       else if ('.' != *q)
757       {
758         strcpy(q + ((*q == '\\') ? 1 : 0),
759                p + 3 + ((*(p + 3)) ? 1 : 0));
760         s = q;
761       }
762       else  s = ++p;
763     }
764     else
765     {
766       /* Execute this section if "." found */
767       q = p + 2;
768       for ( ;*q && (*q != '\\'); ++q)
769         ;
770       strcpy (p, q);
771     }
772   }
773
774   if (root_flag)  /* Embedded ".." could have bubbled up to root  */
775   {
776     for (p = r; *p && ('.' == *p || '\\' == *p); ++p)
777       ;
778     if (r != p)
779       strcpy(r, p);
780   }
781
782   if (dir_flag)
783     strcat(path, "\\");
784 }
785
786 /*********************************************************************
787  *              _fullpath (MSVCRT.@)
788  */
789 char *_fullpath(char * absPath, const char* relPath, unsigned int size)
790 {
791   char drive[5],dir[MAX_PATH],file[MAX_PATH],ext[MAX_PATH];
792   char res[MAX_PATH];
793   size_t len;
794
795   res[0] = '\0';
796
797   if (!relPath || !*relPath)
798     return _getcwd(absPath, size);
799
800   if (size < 4)
801   {
802     *MSVCRT__errno() = MSVCRT_ERANGE;
803     return NULL;
804   }
805
806   TRACE(":resolving relative path '%s'\n",relPath);
807
808   _splitpath(relPath, drive, dir, file, ext);
809
810   /* Get Directory and drive into 'res' */
811   if (!dir[0] || (dir[0] != '/' && dir[0] != '\\'))
812   {
813     /* Relative or no directory given */
814     _getdcwd(drive[0] ? toupper(drive[0]) - 'A' + 1 :  0, res, MAX_PATH);
815     strcat(res,"\\");
816     if (dir[0])
817       strcat(res,dir);
818     if (drive[0])
819       res[0] = drive[0]; /* If given a drive, preserve the letter case */
820   }
821   else
822   {
823     strcpy(res,drive);
824     strcat(res,dir);
825   }
826
827   strcat(res,"\\");
828   strcat(res, file);
829   strcat(res, ext);
830   msvcrt_fln_fix(res);
831
832   len = strlen(res);
833   if (len >= MAX_PATH || len >= (size_t)size)
834     return NULL; /* FIXME: errno? */
835
836   if (!absPath)
837     return _strdup(res);
838   strcpy(absPath,res);
839   return absPath;
840 }
841
842 /*********************************************************************
843  *              _makepath (MSVCRT.@)
844  */
845 VOID _makepath(char * path, const char * drive,
846                               const char *directory, const char * filename,
847                               const char * extension )
848 {
849     char ch;
850     char tmpPath[MAX_PATH];
851     TRACE("got %s %s %s %s\n", debugstr_a(drive), debugstr_a(directory),
852           debugstr_a(filename), debugstr_a(extension) );
853
854     if ( !path )
855         return;
856
857     tmpPath[0] = '\0';
858     if (drive && drive[0])
859     {
860         tmpPath[0] = drive[0];
861         tmpPath[1] = ':';
862         tmpPath[2] = 0;
863     }
864     if (directory && directory[0])
865     {
866         strcat(tmpPath, directory);
867         ch = tmpPath[strlen(tmpPath)-1];
868         if (ch != '/' && ch != '\\')
869             strcat(tmpPath,"\\");
870     }
871     if (filename && filename[0])
872     {
873         strcat(tmpPath, filename);
874         if (extension && extension[0])
875         {
876             if ( extension[0] != '.' )
877                 strcat(tmpPath,".");
878             strcat(tmpPath,extension);
879         }
880     }
881
882     strcpy( path, tmpPath );
883
884     TRACE("returning %s\n",path);
885 }
886
887 /*********************************************************************
888  *              _wmakepath (MSVCRT.@)
889  */
890 VOID _wmakepath(MSVCRT_wchar_t *path, const MSVCRT_wchar_t *drive, const MSVCRT_wchar_t *directory,
891                 const MSVCRT_wchar_t *filename, const MSVCRT_wchar_t *extension)
892 {
893     MSVCRT_wchar_t ch;
894     TRACE("%s %s %s %s\n", debugstr_w(drive), debugstr_w(directory),
895           debugstr_w(filename), debugstr_w(extension));
896
897     if ( !path )
898         return;
899
900     path[0] = 0;
901     if (drive && drive[0])
902     {
903         path[0] = drive[0];
904         path[1] = ':';
905         path[2] = 0;
906     }
907     if (directory && directory[0])
908     {
909         strcatW(path, directory);
910         ch = path[strlenW(path) - 1];
911         if (ch != '/' && ch != '\\')
912         {
913             static const MSVCRT_wchar_t backslashW[] = {'\\',0};
914             strcatW(path, backslashW);
915         }
916     }
917     if (filename && filename[0])
918     {
919         strcatW(path, filename);
920         if (extension && extension[0])
921         {
922             if ( extension[0] != '.' )
923             {
924                 static const MSVCRT_wchar_t dotW[] = {'.',0};
925                 strcatW(path, dotW);
926             }
927             strcatW(path, extension);
928         }
929     }
930
931     TRACE("returning %s\n", debugstr_w(path));
932 }
933
934 /*********************************************************************
935  *              _searchenv (MSVCRT.@)
936  */
937 void _searchenv(const char* file, const char* env, char *buf)
938 {
939   char*envVal, *penv;
940   char curPath[MAX_PATH];
941
942   *buf = '\0';
943
944   /* Try CWD first */
945   if (GetFileAttributesA( file ) != 0xFFFFFFFF)
946   {
947     GetFullPathNameA( file, MAX_PATH, buf, NULL );
948     /* Sigh. This error is *always* set, regardless of success */
949     MSVCRT__set_errno(ERROR_FILE_NOT_FOUND);
950     return;
951   }
952
953   /* Search given environment variable */
954   envVal = MSVCRT_getenv(env);
955   if (!envVal)
956   {
957     MSVCRT__set_errno(ERROR_FILE_NOT_FOUND);
958     return;
959   }
960
961   penv = envVal;
962   TRACE(":searching for %s in paths %s\n", file, envVal);
963
964   do
965   {
966     char *end = penv;
967
968     while(*end && *end != ';') end++; /* Find end of next path */
969     if (penv == end || !*penv)
970     {
971       MSVCRT__set_errno(ERROR_FILE_NOT_FOUND);
972       return;
973     }
974     strncpy(curPath, penv, end - penv);
975     if (curPath[end - penv] != '/' || curPath[end - penv] != '\\')
976     {
977       curPath[end - penv] = '\\';
978       curPath[end - penv + 1] = '\0';
979     }
980     else
981       curPath[end - penv] = '\0';
982
983     strcat(curPath, file);
984     TRACE("Checking for file %s\n", curPath);
985     if (GetFileAttributesA( curPath ) != 0xFFFFFFFF)
986     {
987       strcpy(buf, curPath);
988       MSVCRT__set_errno(ERROR_FILE_NOT_FOUND);
989       return; /* Found */
990     }
991     penv = *end ? end + 1 : end;
992   } while(1);
993 }