Release 961117
[wine] / files / dos_fs.c
1 /*
2  * DOS file system functions
3  *
4  * Copyright 1993 Erik Bos
5  * Copyright 1996 Alexandre Julliard
6  */
7
8 #include <sys/types.h>
9 #include <ctype.h>
10 #include <dirent.h>
11 #include <string.h>
12 #include <stdlib.h>
13 #include <stdio.h>
14 #include <sys/stat.h>
15 #include <time.h>
16 #if defined(__svr4__) || defined(_SCO_DS)
17 #include <sys/statfs.h>
18 #endif
19
20 #include "windows.h"
21 #include "dos_fs.h"
22 #include "drive.h"
23 #include "file.h"
24 #include "msdos.h"
25 #include "stddebug.h"
26 #include "debug.h"
27 #include "xmalloc.h"
28 #include "string32.h"
29
30 /* Chars we don't want to see in DOS file names */
31 #define INVALID_DOS_CHARS  "*?<>|\"+=,;[] \345"
32
33 static const char *DOSFS_Devices[][2] =
34 {
35     { "CON",  "" },
36     { "PRN",  "" },
37     { "NUL",  "/dev/null" },
38     { "AUX",  "" },
39     { "LPT1", "" },
40     { "LPT2", "" },
41     { "LPT3", "" },
42     { "LPT4", "" },
43     { "COM1", "" },
44     { "COM2", "" },
45     { "COM3", "" },
46     { "COM4", "" }
47 };
48
49 #define GET_DRIVE(path) \
50     (((path)[1] == ':') ? toupper((path)[0]) - 'A' : DOSFS_CurDrive)
51
52     /* DOS extended error status */
53 WORD DOS_ExtendedError;
54 BYTE DOS_ErrorClass;
55 BYTE DOS_ErrorAction;
56 BYTE DOS_ErrorLocus;
57
58
59 /***********************************************************************
60  *           DOSFS_ValidDOSName
61  *
62  * Return 1 if Unix file 'name' is also a valid MS-DOS name
63  * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
64  * File name can be terminated by '\0', '\\' or '/'.
65  */
66 static int DOSFS_ValidDOSName( const char *name, int ignore_case )
67 {
68     static const char invalid_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS;
69     const char *p = name;
70     const char *invalid = ignore_case ? (invalid_chars + 26) : invalid_chars;
71     int len = 0;
72
73     if (*p == '.')
74     {
75         /* Check for "." and ".." */
76         p++;
77         if (*p == '.') p++;
78         /* All other names beginning with '.' are invalid */
79         return (IS_END_OF_NAME(*p));
80     }
81     while (!IS_END_OF_NAME(*p))
82     {
83         if (strchr( invalid, *p )) return 0;  /* Invalid char */
84         if (*p == '.') break;  /* Start of the extension */
85         if (++len > 8) return 0;  /* Name too long */
86         p++;
87     }
88     if (*p != '.') return 1;  /* End of name */
89     p++;
90     if (IS_END_OF_NAME(*p)) return 0;  /* Empty extension not allowed */
91     len = 0;
92     while (!IS_END_OF_NAME(*p))
93     {
94         if (strchr( invalid, *p )) return 0;  /* Invalid char */
95         if (*p == '.') return 0;  /* Second extension not allowed */
96         if (++len > 3) return 0;  /* Extension too long */
97         p++;
98     }
99     return 1;
100 }
101
102
103 /***********************************************************************
104  *           DOSFS_CheckDotDot
105  *
106  * Remove all '.' and '..' at the beginning of 'name'.
107  */
108 static const char * DOSFS_CheckDotDot( const char *name, char *buffer,
109                                        char sep , int *len )
110 {
111     char *p = buffer + strlen(buffer);
112
113     while (*name == '.')
114     {
115         if (IS_END_OF_NAME(name[1]))
116         {
117             name++;
118             while ((*name == '\\') || (*name == '/')) name++;
119         }
120         else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
121         {
122             name += 2;
123             while ((*name == '\\') || (*name == '/')) name++;
124             while ((p > buffer) && (*p != sep)) { p--; (*len)++; }
125             *p = '\0';  /* Remove trailing separator */
126         }
127         else break;
128     }
129     return name;
130 }
131
132
133 /***********************************************************************
134  *           DOSFS_ToDosFCBFormat
135  *
136  * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
137  * expanding wild cards and converting to upper-case in the process.
138  * File name can be terminated by '\0', '\\' or '/'.
139  * Return NULL if the name is not a valid DOS name.
140  */
141 const char *DOSFS_ToDosFCBFormat( const char *name )
142 {
143     static const char invalid_chars[] = INVALID_DOS_CHARS;
144     static char buffer[12];
145     const char *p = name;
146     int i;
147
148     /* Check for "." and ".." */
149     if (*p == '.')
150     {
151         p++;
152         strcpy( buffer, ".          " );
153         if (*p == '.') p++;
154         return (!*p || (*p == '/') || (*p == '\\')) ? buffer : NULL;
155     }
156
157     for (i = 0; i < 8; i++)
158     {
159         switch(*p)
160         {
161         case '\0':
162         case '\\':
163         case '/':
164         case '.':
165             buffer[i] = ' ';
166             break;
167         case '?':
168             p++;
169             /* fall through */
170         case '*':
171             buffer[i] = '?';
172             break;
173         default:
174             if (strchr( invalid_chars, *p )) return NULL;
175             buffer[i] = toupper(*p);
176             p++;
177             break;
178         }
179     }
180
181     if (*p == '*')
182     {
183         /* Skip all chars after wildcard up to first dot */
184         while (*p && (*p != '/') && (*p != '\\') && (*p != '.')) p++;
185     }
186     else
187     {
188         /* Check if name too long */
189         if (*p && (*p != '/') && (*p != '\\') && (*p != '.')) return NULL;
190     }
191     if (*p == '.') p++;  /* Skip dot */
192
193     for (i = 8; i < 11; i++)
194     {
195         switch(*p)
196         {
197         case '\0':
198         case '\\':
199         case '/':
200             buffer[i] = ' ';
201             break;
202         case '.':
203             return NULL;  /* Second extension not allowed */
204         case '?':
205             p++;
206             /* fall through */
207         case '*':
208             buffer[i] = '?';
209             break;
210         default:
211             if (strchr( invalid_chars, *p )) return NULL;
212             buffer[i] = toupper(*p);
213             p++;
214             break;
215         }
216     }
217     buffer[11] = '\0';
218     return buffer;
219 }
220
221
222 /***********************************************************************
223  *           DOSFS_ToDosDTAFormat
224  *
225  * Convert a file name from FCB to DTA format (name.ext, null-terminated)
226  * converting to upper-case in the process.
227  * File name can be terminated by '\0', '\\' or '/'.
228  * Return NULL if the name is not a valid DOS name.
229  */
230 const char *DOSFS_ToDosDTAFormat( const char *name )
231 {
232     static char buffer[13];
233     char *p;
234
235     memcpy( buffer, name, 8 );
236     for (p = buffer + 8; (p > buffer) && (p[-1] == ' '); p--);
237     *p++ = '.';
238     memcpy( p, name + 8, 3 );
239     for (p += 3; p[-1] == ' '; p--);
240     if (p[-1] == '.') p--;
241     *p = '\0';
242     return buffer;
243 }
244
245
246 /***********************************************************************
247  *           DOSFS_MatchShort
248  *
249  * Check a DOS file name against a mask (both in FCB format).
250  */
251 static int DOSFS_MatchShort( const char *mask, const char *name )
252 {
253     int i;
254     for (i = 11; i > 0; i--, mask++, name++)
255         if ((*mask != '?') && (*mask != *name)) return 0;
256     return 1;
257 }
258
259
260 /***********************************************************************
261  *           DOSFS_MatchLong
262  *
263  * Check a long file name against a mask.
264  */
265 static int DOSFS_MatchLong( const char *mask, const char *name,
266                             int case_sensitive )
267 {
268     while (*name && *mask)
269     {
270         if (*mask == '*')
271         {
272             mask++;
273             while (*mask == '*') mask++;  /* Skip consecutive '*' */
274             if (!*mask) return 1;
275             if (case_sensitive) while (*name && (*name != *mask)) name++;
276             else while (*name && (toupper(*name) != toupper(*mask))) name++;
277             if (!*name) return 0;
278         }
279         else if (*mask != '?')
280         {
281             if (case_sensitive)
282             {
283                 if (*mask != *name) return 0;
284             }
285             else if (toupper(*mask) != toupper(*name)) return 0;
286         }
287         mask++;
288         name++;
289     }
290     return (!*name && !*mask);
291 }
292
293
294 /***********************************************************************
295  *           DOSFS_ToDosDateTime
296  *
297  * Convert a Unix time in the DOS date/time format.
298  */
299 void DOSFS_ToDosDateTime( time_t unixtime, WORD *pDate, WORD *pTime )
300 {
301     struct tm *tm = localtime( &unixtime );
302     if (pTime)
303         *pTime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
304     if (pDate)
305         *pDate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
306                  + tm->tm_mday;
307 }
308
309 /***********************************************************************
310  *           DOSFS_DosDateTimeToUnixTime
311  *
312  * Convert from the DOS (FAT) date/time format into Unix time
313  * (borrowed from files/file.c)
314  */
315 time_t DOSFS_DosDateTimeToUnixTime( WORD date, WORD time )
316 {
317     struct tm newtm;
318
319     newtm.tm_sec  = (time & 0x1f) * 2;
320     newtm.tm_min  = (time >> 5) & 0x3f;
321     newtm.tm_hour = (time >> 11);
322     newtm.tm_mday = (date & 0x1f);
323     newtm.tm_mon  = ((date >> 5) & 0x0f) - 1;
324     newtm.tm_year = (date >> 9) + 80;
325     return mktime( &newtm );
326 }
327
328
329 /***********************************************************************
330  *           DOSFS_UnixTimeToFileTime
331  *
332  * Convert a Unix time to FILETIME format.
333  */
334 void DOSFS_UnixTimeToFileTime( time_t unixtime, FILETIME *filetime )
335 {
336     /* FIXME :-) */
337     filetime->dwLowDateTime  = unixtime;
338     filetime->dwHighDateTime = 0;
339 }
340
341
342 /***********************************************************************
343  *           DOSFS_FileTimeToUnixTime
344  *
345  * Convert a FILETIME format to Unix time.
346  */
347 time_t DOSFS_FileTimeToUnixTime( FILETIME *filetime )
348 {
349     /* FIXME :-) */
350     return filetime->dwLowDateTime;
351 }
352
353
354 /***********************************************************************
355  *           DOSFS_Hash
356  *
357  * Transform a Unix file name into a hashed DOS name. If the name is a valid
358  * DOS name, it is converted to upper-case; otherwise it is replaced by a
359  * hashed version that fits in 8.3 format.
360  * File name can be terminated by '\0', '\\' or '/'.
361  */
362 const char *DOSFS_Hash( const char *name, int dir_format, int ignore_case )
363 {
364     static const char invalid_chars[] = INVALID_DOS_CHARS "~.";
365     static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
366
367     static char buffer[13];
368     const char *p, *ext;
369     char *dst;
370     unsigned short hash;
371     int i;
372
373     if (dir_format) strcpy( buffer, "           " );
374
375     if (DOSFS_ValidDOSName( name, ignore_case ))
376     {
377         /* Check for '.' and '..' */
378         if (*name == '.')
379         {
380             buffer[0] = '.';
381             if (!dir_format) buffer[1] = buffer[2] = '\0';
382             if (name[1] == '.') buffer[1] = '.';
383             return buffer;
384         }
385
386         /* Simply copy the name, converting to uppercase */
387
388         for (dst = buffer; !IS_END_OF_NAME(*name) && (*name != '.'); name++)
389             *dst++ = toupper(*name);
390         if (*name == '.')
391         {
392             if (dir_format) dst = buffer + 8;
393             else *dst++ = '.';
394             for (name++; !IS_END_OF_NAME(*name); name++)
395                 *dst++ = toupper(*name);
396         }
397         if (!dir_format) *dst = '\0';
398     }
399     else
400     {
401         /* Compute the hash code of the file name */
402         /* If you know something about hash functions, feel free to */
403         /* insert a better algorithm here... */
404         if (ignore_case)
405         {
406             for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
407                 hash = (hash << 3) ^ (hash >> 5) ^ tolower(*p) ^ (tolower(p[1]) << 8);
408             hash = (hash << 3) ^ (hash >> 5) ^ tolower(*p); /* Last character*/
409         }
410         else
411         {
412             for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
413                 hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
414             hash = (hash << 3) ^ (hash >> 5) ^ *p;  /* Last character */
415         }
416
417         /* Find last dot for start of the extension */
418         for (p = name+1, ext = NULL; !IS_END_OF_NAME(*p); p++)
419             if (*p == '.') ext = p;
420         if (ext && IS_END_OF_NAME(ext[1]))
421             ext = NULL;  /* Empty extension ignored */
422
423         /* Copy first 4 chars, replacing invalid chars with '_' */
424         for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
425         {
426             if (IS_END_OF_NAME(*p) || (p == ext)) break;
427             *dst++ = strchr( invalid_chars, *p ) ? '_' : toupper(*p);
428         }
429         /* Pad to 5 chars with '~' */
430         while (i-- >= 0) *dst++ = '~';
431
432         /* Insert hash code converted to 3 ASCII chars */
433         *dst++ = hash_chars[(hash >> 10) & 0x1f];
434         *dst++ = hash_chars[(hash >> 5) & 0x1f];
435         *dst++ = hash_chars[hash & 0x1f];
436
437         /* Copy the first 3 chars of the extension (if any) */
438         if (ext)
439         {
440             if (!dir_format) *dst++ = '.';
441             for (i = 3, ext++; (i > 0) && !IS_END_OF_NAME(*ext); i--, ext++)
442                 *dst++ = toupper(*ext);
443         }
444         if (!dir_format) *dst = '\0';
445     }
446     return buffer;
447 }
448
449
450 /***********************************************************************
451  *           DOSFS_FindUnixName
452  *
453  * Find the Unix file name in a given directory that corresponds to
454  * a file name (either in Unix or DOS format).
455  * File name can be terminated by '\0', '\\' or '/'.
456  * Return 1 if OK, 0 if no file name matches.
457  */
458 static int DOSFS_FindUnixName( const char *path, const char *name,
459                                char *buffer, int maxlen, UINT32 drive_flags )
460 {
461     DIR *dir;
462     struct dirent *dirent;
463
464     const char *dos_name = DOSFS_ToDosFCBFormat( name );
465     const char *p = strchr( name, '/' );
466     int len = p ? (int)(p - name) : strlen(name);
467
468     dprintf_dosfs( stddeb, "DOSFS_FindUnixName: %s %s\n", path, name );
469
470     if ((p = strchr( name, '\\' ))) len = MIN( (int)(p - name), len );
471
472     if (!(dir = opendir( path )))
473     {
474         dprintf_dosfs( stddeb, "DOSFS_FindUnixName(%s,%s): can't open dir\n",
475                        path, name );
476         return 0;
477     }
478     while ((dirent = readdir( dir )) != NULL)
479     {
480         /* Check against Unix name */
481         if (len == strlen(dirent->d_name))
482         {
483             if (drive_flags & DRIVE_CASE_SENSITIVE)
484             {
485                 if (!lstrncmp32A( dirent->d_name, name, len )) break;
486             }
487             else
488             {
489                 if (!lstrncmpi32A( dirent->d_name, name, len )) break;
490             }
491         }
492         if (dos_name)
493         {
494             /* Check against hashed DOS name */
495             const char *hash_name = DOSFS_Hash( dirent->d_name, TRUE,
496                                        !(drive_flags & DRIVE_CASE_SENSITIVE) );
497             if (!strcmp( dos_name, hash_name )) break;
498         }
499     }
500     if (dirent) lstrcpyn32A( buffer, dirent->d_name, maxlen );
501     closedir( dir );
502     dprintf_dosfs( stddeb, "DOSFS_FindUnixName(%s,%s) -> %s\n",
503                    path, name, dirent ? buffer : "** Not found **" );
504     return (dirent != NULL);
505 }
506
507
508 /***********************************************************************
509  *           DOSFS_IsDevice
510  *
511  * Check if a DOS file name represents a DOS device. Returns the name
512  * of the associated Unix device, or NULL if not found.
513  */
514 const char *DOSFS_IsDevice( const char *name )
515 {
516     int i;
517     const char *p;
518
519     if (name[0] && (name[1] == ':')) name += 2;
520     if ((p = strrchr( name, '/' ))) name = p + 1;
521     if ((p = strrchr( name, '\\' ))) name = p + 1;
522     for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
523     {
524         const char *dev = DOSFS_Devices[i][0];
525         if (!lstrncmpi32A( dev, name, strlen(dev) ))
526         {
527             p = name + strlen( dev );
528             if (!*p || (*p == '.')) return DOSFS_Devices[i][1];
529         }
530     }
531     return NULL;
532 }
533
534
535 /***********************************************************************
536  *           DOSFS_GetUnixFileName
537  *
538  * Convert a file name (DOS or mixed DOS/Unix format) to a valid Unix name.
539  * Return NULL if one of the path components does not exist. The last path
540  * component is only checked if 'check_last' is non-zero.
541  */
542 const char * DOSFS_GetUnixFileName( const char * name, int check_last )
543 {
544     static char buffer[MAX_PATHNAME_LEN];
545     int drive, len, found;
546     UINT32 flags;
547     char *p, *root;
548
549     dprintf_dosfs( stddeb, "DOSFS_GetUnixFileName: %s\n", name );
550     if (name[0] && (name[1] == ':'))
551     {
552         drive = toupper(name[0]) - 'A';
553         name += 2;
554     }
555     else if (name[0] == '/') /* Absolute Unix path? */
556     {
557         if ((drive = DRIVE_FindDriveRoot( &name )) == -1)
558         {
559             fprintf( stderr, "Warning: %s not accessible from a DOS drive\n",
560                      name );
561             /* Assume it really was a DOS name */
562             drive = DRIVE_GetCurrentDrive();            
563         }
564     }
565     else drive = DRIVE_GetCurrentDrive();
566
567     if (!DRIVE_IsValid(drive))
568     {
569         DOS_ERROR( ER_InvalidDrive, EC_MediaError, SA_Abort, EL_Disk );
570         return NULL;
571     }
572     flags = DRIVE_GetFlags(drive);
573     lstrcpyn32A( buffer, DRIVE_GetRoot(drive), MAX_PATHNAME_LEN );
574     if (buffer[1]) root = buffer + strlen(buffer);
575     else root = buffer;  /* root directory */
576
577     if ((*name == '\\') || (*name == '/'))
578     {
579         while ((*name == '\\') || (*name == '/')) name++;
580     }
581     else
582     {
583         lstrcpyn32A( root + 1, DRIVE_GetUnixCwd(drive),
584                      MAX_PATHNAME_LEN - (int)(root - buffer) - 1 );
585         if (root[1]) *root = '/';
586     }
587
588     p = buffer[1] ? buffer + strlen(buffer) : buffer;
589     len = MAX_PATHNAME_LEN - strlen(buffer);
590     found = 1;
591     while (*name && found)
592     {
593         const char *newname = DOSFS_CheckDotDot( name, root, '/', &len );
594         if (newname != name)
595         {
596             p = root + strlen(root);
597             name = newname;
598             continue;
599         }
600         if (len <= 1)
601         {
602             DOS_ERROR( ER_PathNotFound, EC_NotFound, SA_Abort, EL_Disk );
603             return NULL;
604         }
605         if ((found = DOSFS_FindUnixName( buffer, name, p+1, len-1, flags )))
606         {
607             *p = '/';
608             len -= strlen(p);
609             p += strlen(p);
610             while (!IS_END_OF_NAME(*name)) name++;
611         }
612         else if (!check_last)
613         {
614             *p++ = '/';
615             for (len--; !IS_END_OF_NAME(*name) && (len > 1); name++, len--)
616                 *p++ = tolower(*name);
617             *p = '\0';
618         }
619         while ((*name == '\\') || (*name == '/')) name++;
620     }
621     if (!found)
622     {
623         if (check_last)
624         {
625             DOS_ERROR( ER_FileNotFound, EC_NotFound, SA_Abort, EL_Disk );
626             return NULL;
627         }
628         if (*name)  /* Not last */
629         {
630             DOS_ERROR( ER_PathNotFound, EC_NotFound, SA_Abort, EL_Disk );
631             return NULL;
632         }
633     }
634     if (!buffer[0]) strcpy( buffer, "/" );
635     dprintf_dosfs( stddeb, "DOSFS_GetUnixFileName: returning %s\n", buffer );
636     return buffer;
637 }
638
639
640 /***********************************************************************
641  *           DOSFS_GetDosTrueName
642  *
643  * Convert a file name (DOS or Unix format) to a complete DOS name.
644  * Return NULL if the path name is invalid or too long.
645  * The unix_format flag is a hint that the file name is in Unix format.
646  */
647 const char * DOSFS_GetDosTrueName( const char *name, int unix_format )
648 {
649     static char buffer[MAX_PATHNAME_LEN];
650     int drive, len;
651     UINT32 flags;
652     char *p;
653
654     dprintf_dosfs( stddeb, "DOSFS_GetDosTrueName(%s,%d)\n", name, unix_format);
655     if (name[0] && (name[1] == ':'))
656     {
657         drive = toupper(name[0]) - 'A';
658         name += 2;
659     }
660     else if (name[0] == '/') /* Absolute Unix path? */
661     {
662         if ((drive = DRIVE_FindDriveRoot( &name )) == -1)
663         {
664             fprintf( stderr, "Warning: %s not accessible from a DOS drive\n",
665                      name );
666             /* Assume it really was a DOS name */
667             drive = DRIVE_GetCurrentDrive();            
668         }
669     }
670     else drive = DRIVE_GetCurrentDrive();
671
672     if (!DRIVE_IsValid(drive))
673     {
674         DOS_ERROR( ER_InvalidDrive, EC_MediaError, SA_Abort, EL_Disk );
675         return NULL;
676     }
677
678     p = buffer;
679     *p++ = 'A' + drive;
680     *p++ = ':';
681     if (IS_END_OF_NAME(*name))
682     {
683         while ((*name == '\\') || (*name == '/')) name++;
684     }
685     else
686     {
687         *p++ = '\\';
688         lstrcpyn32A( p, DRIVE_GetDosCwd(drive), sizeof(buffer) - 3 );
689         if (*p) p += strlen(p); else p--;
690     }
691     *p = '\0';
692     len = MAX_PATHNAME_LEN - (int)(p - buffer);
693     flags = DRIVE_GetFlags(drive);
694
695     while (*name)
696     {
697         const char *newname = DOSFS_CheckDotDot( name, buffer+2, '\\', &len );
698         if (newname != name)
699         {
700             p = buffer + strlen(buffer);
701             name = newname;
702             continue;
703         }
704         if (len <= 1)
705         {
706             DOS_ERROR( ER_PathNotFound, EC_NotFound, SA_Abort, EL_Disk );
707             return NULL;
708         }
709         *p++ = '\\';
710         if (unix_format)  /* Hash it into a DOS name */
711         {
712             lstrcpyn32A( p, DOSFS_Hash( name, FALSE,
713                                      !(flags & DRIVE_CASE_SENSITIVE) ),
714                          len );
715             len -= strlen(p);
716             p += strlen(p);
717             while (!IS_END_OF_NAME(*name)) name++;
718         }
719         else  /* Already DOS format, simply upper-case it */
720         {
721             while (!IS_END_OF_NAME(*name) && (len > 1))
722             {
723                 *p++ = toupper(*name);
724                 name++;
725                 len--;
726             }
727             *p = '\0';
728         }
729         while ((*name == '\\') || (*name == '/')) name++;
730     }
731     if (!buffer[2])
732     {
733         buffer[2] = '\\';
734         buffer[3] = '\0';
735     }
736     dprintf_dosfs( stddeb, "DOSFS_GetDosTrueName: returning %s\n", buffer );
737     return buffer;
738 }
739
740
741 /***********************************************************************
742  *           DOSFS_FindNext
743  *
744  * Find the next matching file. Return the number of entries read to find
745  * the matching one, or 0 if no more entries.
746  * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
747  * file name mask. Either or both can be NULL.
748  */
749 int DOSFS_FindNext( const char *path, const char *short_mask,
750                     const char *long_mask, int drive, BYTE attr,
751                     int skip, DOS_DIRENT *entry )
752 {
753     static DIR *dir = NULL;
754     struct dirent *dirent;
755     int count = 0;
756     static char buffer[MAX_PATHNAME_LEN];
757     static int cur_pos = 0;
758     static int drive_root = 0;
759     char *p;
760     const char *hash_name;
761     UINT32 flags;
762
763     if ((attr & ~(FA_UNUSED | FA_ARCHIVE | FA_RDONLY)) == FA_LABEL)
764     {
765         if (skip) return 0;
766         strcpy( entry->name, DRIVE_GetLabel( drive ) );
767         entry->attr = FA_LABEL;
768         entry->size = 0;
769         DOSFS_ToDosDateTime( time(NULL), &entry->date, &entry->time );
770         return 1;
771     }
772
773     /* Check the cached directory */
774     if (dir && !strcmp( buffer, path ) && (cur_pos <= skip)) skip -= cur_pos;
775     else  /* Not in the cache, open it anew */
776     {
777         const char *drive_path;
778         dprintf_dosfs( stddeb, "DOSFS_FindNext: cache miss, path=%s skip=%d buf=%s cur=%d\n",
779                        path, skip, buffer, cur_pos );
780         cur_pos = skip;
781         if (dir) closedir(dir);
782         if (!*path) path = "/";
783         if (!(dir = opendir(path))) return 0;
784         drive_path = path;
785         drive_root = 0;
786         if (DRIVE_FindDriveRoot( &drive_path ) != -1)
787         {
788             while ((*drive_path == '/') || (*drive_path == '\\')) drive_path++;
789             if (!*drive_path) drive_root = 1;
790         }
791         dprintf_dosfs(stddeb, "DOSFS_FindNext: drive_root = %d\n", drive_root);
792         lstrcpyn32A( buffer, path, sizeof(buffer) - 1 );
793         
794     }
795     strcat( buffer, "/" );
796     p = buffer + strlen(buffer);
797     attr |= FA_UNUSED | FA_ARCHIVE | FA_RDONLY;
798     flags = DRIVE_GetFlags( drive );
799     hash_name = NULL;
800
801     while ((dirent = readdir( dir )) != NULL)
802     {
803         if (skip-- > 0) continue;
804         count++;
805
806         /* Don't return '.' and '..' in the root of the drive */
807         if (drive_root && (dirent->d_name[0] == '.') &&
808             (!dirent->d_name[1] ||
809              ((dirent->d_name[1] == '.') && !dirent->d_name[2]))) continue;
810
811         /* Check the long mask */
812
813         if (long_mask)
814         {
815             if (!DOSFS_MatchLong( long_mask, dirent->d_name,
816                                   flags & DRIVE_CASE_SENSITIVE )) continue;
817         }
818
819         /* Check the short mask */
820
821         if (short_mask)
822         {
823             hash_name = DOSFS_Hash( dirent->d_name, TRUE,
824                                     !(flags & DRIVE_CASE_SENSITIVE) );
825             if (!DOSFS_MatchShort( short_mask, hash_name )) continue;
826         }
827
828         /* Check the file attributes */
829
830         lstrcpyn32A( p, dirent->d_name, sizeof(buffer) - (int)(p - buffer) );
831         if (!FILE_Stat( buffer, &entry->attr, &entry->size,
832                         &entry->date, &entry->time ))
833         {
834             fprintf( stderr, "DOSFS_FindNext: can't stat %s\n", buffer );
835             continue;
836         }
837         if (entry->attr & ~attr) continue;
838
839         /* We now have a matching entry; fill the result and return */
840
841         if (!hash_name)
842             hash_name = DOSFS_Hash( dirent->d_name, TRUE,
843                                     !(flags & DRIVE_CASE_SENSITIVE) );
844         strcpy( entry->name, hash_name );
845         lstrcpyn32A( entry->unixname, dirent->d_name, sizeof(entry->unixname));
846         if (!(flags & DRIVE_CASE_PRESERVING)) AnsiLower( entry->unixname );
847         dprintf_dosfs( stddeb, "DOSFS_FindNext: returning %s %02x %ld\n",
848                        entry->name, entry->attr, entry->size );
849         cur_pos += count;
850         p[-1] = '\0';  /* Remove trailing slash in buffer */
851         return count;
852     }
853     closedir( dir );
854     dir = NULL;
855     return 0;  /* End of directory */
856 }
857
858
859 /***********************************************************************
860  *           GetShortPathNameA   (KERNEL32.271)
861  */
862 DWORD GetShortPathName32A( LPCSTR longpath, LPSTR shortpath, DWORD shortlen )
863 {
864     LPCSTR dostruename;
865
866     dprintf_dosfs( stddeb, "GetShortPathName32A(%s,%p,%ld)\n",
867                    longpath, shortpath, shortlen );
868
869     dostruename = DOSFS_GetDosTrueName( longpath, TRUE );
870     lstrcpyn32A( shortpath, dostruename, shortlen );
871     return strlen(dostruename);
872 }
873
874
875 /***********************************************************************
876  *           GetShortPathNameW   (KERNEL32.272)
877  */
878 DWORD GetShortPathName32W( LPCWSTR longpath, LPWSTR shortpath, DWORD shortlen )
879 {
880     LPSTR longpatha = STRING32_DupUniToAnsi( longpath );
881     LPCSTR dostruename = DOSFS_GetDosTrueName( longpatha, TRUE );
882     free( longpatha );
883     lstrcpynAtoW( shortpath, dostruename, shortlen );
884     return strlen(dostruename);
885 }
886
887
888 /***********************************************************************
889  *           GetFullPathNameA   (KERNEL32.272)
890  */
891 DWORD GetFullPathName32A( LPCSTR fn, DWORD buflen, LPSTR buf, LPSTR *lastpart) {
892         dprintf_file(stddeb,"GetFullPathNameA(%s)\n",fn);
893         /* FIXME */
894         lstrcpyn32A(buf,fn,buflen);
895         if (lastpart)
896                 *lastpart=strrchr(buf,'\\');
897         return strlen(fn);
898 }
899
900
901 /***********************************************************************
902  *           DosDateTimeToFileTime   (KERNEL32.76)
903  */
904 BOOL32 DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft )
905 {
906     time_t unixtime = DOSFS_DosDateTimeToUnixTime(fatdate,fattime);
907     DOSFS_UnixTimeToFileTime(unixtime,ft);
908     return TRUE;
909 }
910
911
912 /***********************************************************************
913  *           FileTimeToDosDateTime   (KERNEL32.111)
914  */
915 BOOL32 FileTimeToDosDateTime( LPFILETIME ft, LPWORD fatdate, LPWORD fattime)
916 {
917     time_t unixtime = DOSFS_FileTimeToUnixTime(ft);
918     DOSFS_ToDosDateTime(unixtime,fatdate,fattime);
919     return TRUE;
920 }
921
922
923 /***********************************************************************
924  *           LocalFileTimeToFileTime   (KERNEL32.373)
925  */
926 BOOL32 LocalFileTimeToFileTime( LPFILETIME localft, LPFILETIME utcft )
927 {
928     struct tm *xtm;
929
930     /* convert from local to UTC. Perhaps not correct. FIXME */
931     xtm = gmtime((time_t*)&(localft->dwLowDateTime));
932     utcft->dwLowDateTime  = mktime(xtm);
933     utcft->dwHighDateTime = 0;
934     return TRUE; 
935 }
936
937
938 /***********************************************************************
939  *           FileTimeToLocalFileTime   (KERNEL32.112)
940  */
941 BOOL32 FileTimeToLocalFileTime( LPFILETIME utcft, LPFILETIME localft )
942 {
943     struct tm *xtm;
944
945     /* convert from UTC to local. Perhaps not correct. FIXME */
946     xtm = localtime((time_t*)&(utcft->dwLowDateTime));
947     localft->dwLowDateTime  = mktime(xtm);
948     localft->dwHighDateTime = 0;
949     return TRUE; 
950 }
951
952
953 /***********************************************************************
954  *           FileTimeToSystemTime   (KERNEL32.113)
955  */
956 BOOL32 FileTimeToSystemTime( LPFILETIME ft, LPSYSTEMTIME syst )
957 {
958     struct tm *xtm;
959     time_t xtime = DOSFS_FileTimeToUnixTime(ft);
960     xtm = gmtime(&xtime);
961     syst->wYear      = xtm->tm_year;
962     syst->wMonth     = xtm->tm_mon;
963     syst->wDayOfWeek = xtm->tm_wday;
964     syst->wDay       = xtm->tm_mday;
965     syst->wHour      = xtm->tm_hour;
966     syst->wMinute    = xtm->tm_min;
967     syst->wSecond    = xtm->tm_sec;
968     syst->wMilliseconds = 0; /* FIXME */
969     return TRUE; 
970 }
971
972
973 /***********************************************************************
974  *           SystemTimeToFileTime   (KERNEL32.526)
975  */
976 BOOL32 SystemTimeToFileTime( LPSYSTEMTIME syst, LPFILETIME ft )
977 {
978     struct tm xtm;
979
980     xtm.tm_year = syst->wYear;
981     xtm.tm_mon  = syst->wMonth;
982     xtm.tm_wday = syst->wDayOfWeek;
983     xtm.tm_mday = syst->wDay;
984     xtm.tm_hour = syst->wHour;
985     xtm.tm_min  = syst->wMinute;
986     xtm.tm_sec  = syst->wSecond;
987     DOSFS_UnixTimeToFileTime(mktime(&xtm),ft);
988     return TRUE; 
989 }