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