Release 971221
[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 <errno.h>
12 #include <fcntl.h>
13 #include <string.h>
14 #include <stdlib.h>
15 #include <stdio.h>
16 #include <sys/stat.h>
17 #include <sys/ioctl.h>
18 #include <time.h>
19 #include <unistd.h>
20 #if defined(__svr4__) || defined(_SCO_DS)
21 #include <sys/statfs.h>
22 #endif
23
24 #include "windows.h"
25 #include "winerror.h"
26 #include "drive.h"
27 #include "file.h"
28 #include "heap.h"
29 #include "msdos.h"
30 #include "stddebug.h"
31 #include "debug.h"
32
33 /* Define the VFAT ioctl to get both short and long file names */
34 /* FIXME: is it possible to get this to work on other systems? */
35 #ifdef linux
36 #define VFAT_IOCTL_READDIR_BOTH  _IOR('r', 1, long)
37 /* We want the real kernel dirent structure, not the libc one */
38 typedef struct
39 {
40     long d_ino;
41     long d_off;
42     unsigned short d_reclen;
43     char d_name[256];
44 } KERNEL_DIRENT;
45
46 #else   /* linux */
47 #undef VFAT_IOCTL_READDIR_BOTH  /* just in case... */
48 #endif  /* linux */
49
50 /* Chars we don't want to see in DOS file names */
51 #define INVALID_DOS_CHARS  "*?<>|\"+=,;[] \345"
52
53 static const char *DOSFS_Devices[][2] =
54 {
55     { "CON",  "" },
56     { "PRN",  "" },
57     { "NUL",  "/dev/null" },
58     { "AUX",  "" },
59     { "LPT1", "" },
60     { "LPT2", "" },
61     { "LPT3", "" },
62     { "LPT4", "" },
63     { "COM1", "" },
64     { "COM2", "" },
65     { "COM3", "" },
66     { "COM4", "" }
67 };
68
69
70 #define GET_DRIVE(path) \
71     (((path)[1] == ':') ? toupper((path)[0]) - 'A' : DOSFS_CurDrive)
72
73     /* DOS extended error status */
74 WORD DOS_ExtendedError;
75 BYTE DOS_ErrorClass;
76 BYTE DOS_ErrorAction;
77 BYTE DOS_ErrorLocus;
78
79 /* Info structure for FindFirstFile handle */
80 typedef struct
81 {
82     LPSTR path;
83     LPSTR mask;
84     int   drive;
85     int   skip;
86 } FIND_FIRST_INFO;
87
88
89 /* Directory info for DOSFS_ReadDir */
90 typedef struct
91 {
92     DIR           *dir;
93 #ifdef VFAT_IOCTL_READDIR_BOTH
94     int            fd;
95     char           short_name[12];
96     KERNEL_DIRENT  dirent[2];
97 #endif
98 } DOS_DIR;
99
100
101 /***********************************************************************
102  *           DOSFS_ValidDOSName
103  *
104  * Return 1 if Unix file 'name' is also a valid MS-DOS name
105  * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
106  * File name can be terminated by '\0', '\\' or '/'.
107  */
108 static int DOSFS_ValidDOSName( const char *name, int ignore_case )
109 {
110     static const char invalid_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS;
111     const char *p = name;
112     const char *invalid = ignore_case ? (invalid_chars + 26) : invalid_chars;
113     int len = 0;
114
115     if (*p == '.')
116     {
117         /* Check for "." and ".." */
118         p++;
119         if (*p == '.') p++;
120         /* All other names beginning with '.' are invalid */
121         return (IS_END_OF_NAME(*p));
122     }
123     while (!IS_END_OF_NAME(*p))
124     {
125         if (strchr( invalid, *p )) return 0;  /* Invalid char */
126         if (*p == '.') break;  /* Start of the extension */
127         if (++len > 8) return 0;  /* Name too long */
128         p++;
129     }
130     if (*p != '.') return 1;  /* End of name */
131     p++;
132     if (IS_END_OF_NAME(*p)) return 0;  /* Empty extension not allowed */
133     len = 0;
134     while (!IS_END_OF_NAME(*p))
135     {
136         if (strchr( invalid, *p )) return 0;  /* Invalid char */
137         if (*p == '.') return 0;  /* Second extension not allowed */
138         if (++len > 3) return 0;  /* Extension too long */
139         p++;
140     }
141     return 1;
142 }
143
144
145 /***********************************************************************
146  *           DOSFS_ToDosFCBFormat
147  *
148  * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
149  * expanding wild cards and converting to upper-case in the process.
150  * File name can be terminated by '\0', '\\' or '/'.
151  * Return FALSE if the name is not a valid DOS name.
152  * 'buffer' must be at least 12 characters long.
153  */
154 BOOL32 DOSFS_ToDosFCBFormat( LPCSTR name, LPSTR buffer )
155 {
156     static const char invalid_chars[] = INVALID_DOS_CHARS;
157     const char *p = name;
158     int i;
159
160     /* Check for "." and ".." */
161     if (*p == '.')
162     {
163         p++;
164         strcpy( buffer, ".          " );
165         if (*p == '.')
166         {
167             buffer[1] = '.';
168             p++;
169         }
170         return (!*p || (*p == '/') || (*p == '\\'));
171     }
172
173     for (i = 0; i < 8; i++)
174     {
175         switch(*p)
176         {
177         case '\0':
178         case '\\':
179         case '/':
180         case '.':
181             buffer[i] = ' ';
182             break;
183         case '?':
184             p++;
185             /* fall through */
186         case '*':
187             buffer[i] = '?';
188             break;
189         default:
190             if (strchr( invalid_chars, *p )) return FALSE;
191             buffer[i] = toupper(*p);
192             p++;
193             break;
194         }
195     }
196
197     if (*p == '*')
198     {
199         /* Skip all chars after wildcard up to first dot */
200         while (*p && (*p != '/') && (*p != '\\') && (*p != '.')) p++;
201     }
202     else
203     {
204         /* Check if name too long */
205         if (*p && (*p != '/') && (*p != '\\') && (*p != '.')) return FALSE;
206     }
207     if (*p == '.') p++;  /* Skip dot */
208
209     for (i = 8; i < 11; i++)
210     {
211         switch(*p)
212         {
213         case '\0':
214         case '\\':
215         case '/':
216             buffer[i] = ' ';
217             break;
218         case '.':
219             return FALSE;  /* Second extension not allowed */
220         case '?':
221             p++;
222             /* fall through */
223         case '*':
224             buffer[i] = '?';
225             break;
226         default:
227             if (strchr( invalid_chars, *p )) return FALSE;
228             buffer[i] = toupper(*p);
229             p++;
230             break;
231         }
232     }
233     buffer[11] = '\0';
234     return TRUE;
235 }
236
237
238 /***********************************************************************
239  *           DOSFS_ToDosDTAFormat
240  *
241  * Convert a file name from FCB to DTA format (name.ext, null-terminated)
242  * converting to upper-case in the process.
243  * File name can be terminated by '\0', '\\' or '/'.
244  * 'buffer' must be at least 13 characters long.
245  */
246 static void DOSFS_ToDosDTAFormat( LPCSTR name, LPSTR buffer )
247 {
248     char *p;
249
250     memcpy( buffer, name, 8 );
251     for (p = buffer + 8; (p > buffer) && (p[-1] == ' '); p--);
252     *p++ = '.';
253     memcpy( p, name + 8, 3 );
254     for (p += 3; p[-1] == ' '; p--);
255     if (p[-1] == '.') p--;
256     *p = '\0';
257 }
258
259
260 /***********************************************************************
261  *           DOSFS_MatchShort
262  *
263  * Check a DOS file name against a mask (both in FCB format).
264  */
265 static int DOSFS_MatchShort( const char *mask, const char *name )
266 {
267     int i;
268     for (i = 11; i > 0; i--, mask++, name++)
269         if ((*mask != '?') && (*mask != *name)) return 0;
270     return 1;
271 }
272
273
274 /***********************************************************************
275  *           DOSFS_MatchLong
276  *
277  * Check a long file name against a mask.
278  */
279 static int DOSFS_MatchLong( const char *mask, const char *name,
280                             int case_sensitive )
281 {
282     if (!strcmp( mask, "*.*" )) return 1;
283     while (*name && *mask)
284     {
285         if (*mask == '*')
286         {
287             mask++;
288             while (*mask == '*') mask++;  /* Skip consecutive '*' */
289             if (!*mask) return 1;
290             if (case_sensitive) while (*name && (*name != *mask)) name++;
291             else while (*name && (toupper(*name) != toupper(*mask))) name++;
292             if (!*name) return 0;
293         }
294         else if (*mask != '?')
295         {
296             if (case_sensitive)
297             {
298                 if (*mask != *name) return 0;
299             }
300             else if (toupper(*mask) != toupper(*name)) return 0;
301         }
302         mask++;
303         name++;
304     }
305     return (!*name && !*mask);
306 }
307
308
309 /***********************************************************************
310  *           DOSFS_OpenDir
311  */
312 static DOS_DIR *DOSFS_OpenDir( LPCSTR path )
313 {
314     DOS_DIR *dir = HeapAlloc( SystemHeap, 0, sizeof(*dir) );
315     if (!dir)
316     {
317         DOS_ERROR( ER_OutOfMemory, EC_OutOfResource, SA_Abort, EL_Memory );
318         return NULL;
319     }
320
321 #ifdef VFAT_IOCTL_READDIR_BOTH
322
323     /* Check if the VFAT ioctl is supported on this directory */
324
325     if ((dir->fd = open( path, O_RDONLY )) != -1)
326     {
327         if (ioctl( dir->fd, VFAT_IOCTL_READDIR_BOTH, (long)dir->dirent ) == -1)
328         {
329             close( dir->fd );
330             dir->fd = -1;
331         }
332         else
333         {
334             /* Set the file pointer back at the start of the directory */
335             lseek( dir->fd, 0, SEEK_SET );
336             dir->dir = NULL;
337             return dir;
338         }
339     }
340 #endif  /* VFAT_IOCTL_READDIR_BOTH */
341
342     /* Now use the standard opendir/readdir interface */
343
344     if (!(dir->dir = opendir( path )))
345     {
346         HeapFree( SystemHeap, 0, dir );
347         return NULL;
348     }
349     return dir;
350 }
351
352
353 /***********************************************************************
354  *           DOSFS_CloseDir
355  */
356 static void DOSFS_CloseDir( DOS_DIR *dir )
357 {
358 #ifdef VFAT_IOCTL_READDIR_BOTH
359     if (dir->fd != -1) close( dir->fd );
360 #endif  /* VFAT_IOCTL_READDIR_BOTH */
361     if (dir->dir) closedir( dir->dir );
362     HeapFree( SystemHeap, 0, dir );
363 }
364
365
366 /***********************************************************************
367  *           DOSFS_ReadDir
368  */
369 static BOOL32 DOSFS_ReadDir( DOS_DIR *dir, LPCSTR *long_name,
370                              LPCSTR *short_name )
371 {
372     struct dirent *dirent;
373
374 #ifdef VFAT_IOCTL_READDIR_BOTH
375     if (dir->fd != -1)
376     {
377         if (ioctl( dir->fd, VFAT_IOCTL_READDIR_BOTH, (long)dir->dirent ) != -1) {
378             if (!dir->dirent[0].d_reclen) return FALSE;
379             if (!DOSFS_ToDosFCBFormat( dir->dirent[0].d_name, dir->short_name ))
380                 dir->short_name[0] = '\0';
381             *short_name = dir->short_name;
382             if (dir->dirent[1].d_name[0]) *long_name = dir->dirent[1].d_name;
383             else *long_name = dir->dirent[0].d_name;
384             return TRUE;
385         }
386     }
387 #endif  /* VFAT_IOCTL_READDIR_BOTH */
388
389     if (!(dirent = readdir( dir->dir ))) return FALSE;
390     *long_name  = dirent->d_name;
391     *short_name = NULL;
392     return TRUE;
393 }
394
395
396 /***********************************************************************
397  *           DOSFS_Hash
398  *
399  * Transform a Unix file name into a hashed DOS name. If the name is a valid
400  * DOS name, it is converted to upper-case; otherwise it is replaced by a
401  * hashed version that fits in 8.3 format.
402  * File name can be terminated by '\0', '\\' or '/'.
403  * 'buffer' must be at least 13 characters long.
404  */
405 static void DOSFS_Hash( LPCSTR name, LPSTR buffer, BOOL32 dir_format,
406                         BOOL32 ignore_case )
407 {
408     static const char invalid_chars[] = INVALID_DOS_CHARS "~.";
409     static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
410
411     const char *p, *ext;
412     char *dst;
413     unsigned short hash;
414     int i;
415
416     if (dir_format) strcpy( buffer, "           " );
417
418     if (DOSFS_ValidDOSName( name, ignore_case ))
419     {
420         /* Check for '.' and '..' */
421         if (*name == '.')
422         {
423             buffer[0] = '.';
424             if (!dir_format) buffer[1] = buffer[2] = '\0';
425             if (name[1] == '.') buffer[1] = '.';
426             return;
427         }
428
429         /* Simply copy the name, converting to uppercase */
430
431         for (dst = buffer; !IS_END_OF_NAME(*name) && (*name != '.'); name++)
432             *dst++ = toupper(*name);
433         if (*name == '.')
434         {
435             if (dir_format) dst = buffer + 8;
436             else *dst++ = '.';
437             for (name++; !IS_END_OF_NAME(*name); name++)
438                 *dst++ = toupper(*name);
439         }
440         if (!dir_format) *dst = '\0';
441         return;
442     }
443
444     /* Compute the hash code of the file name */
445     /* If you know something about hash functions, feel free to */
446     /* insert a better algorithm here... */
447     if (ignore_case)
448     {
449         for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
450             hash = (hash<<3) ^ (hash>>5) ^ tolower(*p) ^ (tolower(p[1]) << 8);
451         hash = (hash<<3) ^ (hash>>5) ^ tolower(*p); /* Last character*/
452     }
453     else
454     {
455         for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
456             hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
457         hash = (hash << 3) ^ (hash >> 5) ^ *p;  /* Last character */
458     }
459
460     /* Find last dot for start of the extension */
461     for (p = name+1, ext = NULL; !IS_END_OF_NAME(*p); p++)
462         if (*p == '.') ext = p;
463     if (ext && IS_END_OF_NAME(ext[1]))
464         ext = NULL;  /* Empty extension ignored */
465
466     /* Copy first 4 chars, replacing invalid chars with '_' */
467     for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
468     {
469         if (IS_END_OF_NAME(*p) || (p == ext)) break;
470         *dst++ = strchr( invalid_chars, *p ) ? '_' : toupper(*p);
471     }
472     /* Pad to 5 chars with '~' */
473     while (i-- >= 0) *dst++ = '~';
474
475     /* Insert hash code converted to 3 ASCII chars */
476     *dst++ = hash_chars[(hash >> 10) & 0x1f];
477     *dst++ = hash_chars[(hash >> 5) & 0x1f];
478     *dst++ = hash_chars[hash & 0x1f];
479
480     /* Copy the first 3 chars of the extension (if any) */
481     if (ext)
482     {
483         if (!dir_format) *dst++ = '.';
484         for (i = 3, ext++; (i > 0) && !IS_END_OF_NAME(*ext); i--, ext++)
485             *dst++ = strchr( invalid_chars, *ext ) ? '_' : toupper(*ext);
486     }
487     if (!dir_format) *dst = '\0';
488 }
489
490
491 /***********************************************************************
492  *           DOSFS_FindUnixName
493  *
494  * Find the Unix file name in a given directory that corresponds to
495  * a file name (either in Unix or DOS format).
496  * File name can be terminated by '\0', '\\' or '/'.
497  * Return TRUE if OK, FALSE if no file name matches.
498  *
499  * 'long_buf' must be at least 'long_len' characters long. If the long name
500  * turns out to be larger than that, the function returns FALSE.
501  * 'short_buf' must be at least 13 characters long.
502  */
503 BOOL32 DOSFS_FindUnixName( LPCSTR path, LPCSTR name, LPSTR long_buf,
504                            INT32 long_len, LPSTR short_buf, BOOL32 ignore_case)
505 {
506     DOS_DIR *dir;
507     LPCSTR long_name, short_name;
508     char dos_name[12], tmp_buf[13];
509     BOOL32 ret;
510
511     const char *p = strchr( name, '/' );
512     int len = p ? (int)(p - name) : strlen(name);
513     if ((p = strchr( name, '\\' ))) len = MIN( (int)(p - name), len );
514     if (long_len < len + 1) return FALSE;
515
516     dprintf_dosfs( stddeb, "DOSFS_FindUnixName: %s,%s\n", path, name );
517
518     if (!DOSFS_ToDosFCBFormat( name, dos_name )) dos_name[0] = '\0';
519
520     if (!(dir = DOSFS_OpenDir( path )))
521     {
522         dprintf_dosfs( stddeb, "DOSFS_FindUnixName(%s,%s): can't open dir: %s\n",
523                        path, name, strerror(errno) );
524         return FALSE;
525     }
526
527     while ((ret = DOSFS_ReadDir( dir, &long_name, &short_name )))
528     {
529         /* Check against Unix name */
530         if (len == strlen(long_name))
531         {
532             if (!ignore_case)
533             {
534                 if (!lstrncmp32A( long_name, name, len )) break;
535             }
536             else
537             {
538                 if (!lstrncmpi32A( long_name, name, len )) break;
539             }
540         }
541         if (dos_name[0])
542         {
543             /* Check against hashed DOS name */
544             if (!short_name)
545             {
546                 DOSFS_Hash( long_name, tmp_buf, TRUE, ignore_case );
547                 short_name = tmp_buf;
548             }
549             if (!strcmp( dos_name, short_name )) break;
550         }
551     }
552     if (ret)
553     {
554         if (long_buf) strcpy( long_buf, long_name );
555         if (short_buf)
556         {
557             if (short_name)
558                 DOSFS_ToDosDTAFormat( short_name, short_buf );
559             else
560                 DOSFS_Hash( long_name, short_buf, FALSE, ignore_case );
561         }
562         dprintf_dosfs( stddeb, "DOSFS_FindUnixName(%s,%s) -> %s (%s)\n",
563                        path, name, long_name, short_buf ? short_buf : "***");
564     }
565     else
566         dprintf_dosfs(stddeb,"DOSFS_FindUnixName(%s,%s) -> ** Not found **\n",
567                       path, name );
568     DOSFS_CloseDir( dir );
569     return ret;
570 }
571
572
573 /***********************************************************************
574  *           DOSFS_IsDevice
575  *
576  * Check if a DOS file name represents a DOS device. Returns the name
577  * of the associated Unix device, or NULL if not found.
578  */
579 const char *DOSFS_IsDevice( const char *name )
580 {
581     int i;
582     const char *p;
583
584     if (name[0] && (name[1] == ':')) name += 2;
585     if ((p = strrchr( name, '/' ))) name = p + 1;
586     if ((p = strrchr( name, '\\' ))) name = p + 1;
587     for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
588     {
589         const char *dev = DOSFS_Devices[i][0];
590         if (!lstrncmpi32A( dev, name, strlen(dev) ))
591         {
592             p = name + strlen( dev );
593             if (!*p || (*p == '.')) return DOSFS_Devices[i][1];
594         }
595     }
596     return NULL;
597 }
598
599 /***********************************************************************
600  *           DOSFS_GetPathDrive
601  *
602  * Get the drive specified by a given path name (DOS or Unix format).
603  */
604 static int DOSFS_GetPathDrive( const char **name )
605 {
606     int drive;
607     const char *p = *name;
608
609     if (*p && (p[1] == ':'))
610     {
611         drive = toupper(*p) - 'A';
612         *name += 2;
613     }
614     else if (*p == '/') /* Absolute Unix path? */
615     {
616         if ((drive = DRIVE_FindDriveRoot( name )) == -1)
617         {
618             fprintf( stderr, "Warning: %s not accessible from a DOS drive\n",
619                      *name );
620             /* Assume it really was a DOS name */
621             drive = DRIVE_GetCurrentDrive();            
622         }
623     }
624     else drive = DRIVE_GetCurrentDrive();
625
626     if (!DRIVE_IsValid(drive))
627     {
628         DOS_ERROR( ER_InvalidDrive, EC_MediaError, SA_Abort, EL_Disk );
629         return -1;
630     }
631     return drive;
632 }
633
634
635 /***********************************************************************
636  *           DOSFS_GetFullName
637  *
638  * Convert a file name (DOS or mixed DOS/Unix format) to a valid
639  * Unix name / short DOS name pair.
640  * Return FALSE if one of the path components does not exist. The last path
641  * component is only checked if 'check_last' is non-zero.
642  * The buffers pointed to by 'long_buf' and 'short_buf' must be
643  * at least MAX_PATHNAME_LEN long.
644  */
645 BOOL32 DOSFS_GetFullName( LPCSTR name, BOOL32 check_last, DOS_FULL_NAME *full )
646 {
647     BOOL32 found;
648     UINT32 flags;
649     char *p_l, *p_s, *root;
650
651     dprintf_dosfs( stddeb, "DOSFS_GetFullName: %s (last=%d)\n",
652                    name, check_last );
653
654     if ((full->drive = DOSFS_GetPathDrive( &name )) == -1) return FALSE;
655     flags = DRIVE_GetFlags( full->drive );
656
657     lstrcpyn32A( full->long_name, DRIVE_GetRoot( full->drive ),
658                  sizeof(full->long_name) );
659     if (full->long_name[1]) root = full->long_name + strlen(full->long_name);
660     else root = full->long_name;  /* root directory */
661
662     strcpy( full->short_name, "A:\\" );
663     full->short_name[0] += full->drive;
664
665     if ((*name == '\\') || (*name == '/'))  /* Absolute path */
666     {
667         while ((*name == '\\') || (*name == '/')) name++;
668     }
669     else  /* Relative path */
670     {
671         lstrcpyn32A( root + 1, DRIVE_GetUnixCwd( full->drive ),
672                      sizeof(full->long_name) - (root - full->long_name) - 1 );
673         if (root[1]) *root = '/';
674         lstrcpyn32A( full->short_name + 3, DRIVE_GetDosCwd( full->drive ),
675                      sizeof(full->short_name) - 3 );
676     }
677
678     p_l = full->long_name[1] ? full->long_name + strlen(full->long_name)
679                              : full->long_name;
680     p_s = full->short_name[3] ? full->short_name + strlen(full->short_name)
681                               : full->short_name + 2;
682     found = TRUE;
683
684     while (*name && found)
685     {
686         /* Check for '.' and '..' */
687
688         if (*name == '.')
689         {
690             if (IS_END_OF_NAME(name[1]))
691             {
692                 name++;
693                 while ((*name == '\\') || (*name == '/')) name++;
694                 continue;
695             }
696             else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
697             {
698                 name += 2;
699                 while ((*name == '\\') || (*name == '/')) name++;
700                 while ((p_l > root) && (*p_l != '/')) p_l--;
701                 while ((p_s > full->short_name + 2) && (*p_s != '\\')) p_s--;
702                 *p_l = *p_s = '\0';  /* Remove trailing separator */
703                 continue;
704             }
705         }
706
707         /* Make sure buffers are large enough */
708
709         if ((p_s >= full->short_name + sizeof(full->short_name) - 14) ||
710             (p_l >= full->long_name + sizeof(full->long_name) - 1))
711         {
712             DOS_ERROR( ER_PathNotFound, EC_NotFound, SA_Abort, EL_Disk );
713             return FALSE;
714         }
715
716         /* Get the long and short name matching the file name */
717
718         if ((found = DOSFS_FindUnixName( full->long_name, name, p_l + 1,
719                          sizeof(full->long_name) - (p_l - full->long_name) - 1,
720                          p_s + 1, !(flags & DRIVE_CASE_SENSITIVE) )))
721         {
722             *p_l++ = '/';
723             p_l   += strlen(p_l);
724             *p_s++ = '\\';
725             p_s   += strlen(p_s);
726             while (!IS_END_OF_NAME(*name)) name++;
727         }
728         else if (!check_last)
729         {
730             *p_l++ = '/';
731             *p_s++ = '\\';
732             while (!IS_END_OF_NAME(*name) &&
733                    (p_s < full->short_name + sizeof(full->short_name) - 1) &&
734                    (p_l < full->long_name + sizeof(full->long_name) - 1))
735             {
736                 *p_l++ = *p_s++ = tolower(*name);
737                 name++;
738             }
739             *p_l = *p_s = '\0';
740         }
741         while ((*name == '\\') || (*name == '/')) name++;
742     }
743
744     if (!found)
745     {
746         if (check_last)
747         {
748             DOS_ERROR( ER_FileNotFound, EC_NotFound, SA_Abort, EL_Disk );
749             return FALSE;
750         }
751         if (*name)  /* Not last */
752         {
753             DOS_ERROR( ER_PathNotFound, EC_NotFound, SA_Abort, EL_Disk );
754             return FALSE;
755         }
756     }
757     if (!full->long_name[0]) strcpy( full->long_name, "/" );
758     if (!full->short_name[2]) strcpy( full->short_name + 2, "\\" );
759     dprintf_dosfs( stddeb, "DOSFS_GetFullName: returning %s = %s\n",
760                    full->long_name, full->short_name );
761     return TRUE;
762 }
763
764
765 /***********************************************************************
766  *           GetShortPathName32A   (KERNEL32.271)
767  */
768 DWORD WINAPI GetShortPathName32A( LPCSTR longpath, LPSTR shortpath,
769                                   DWORD shortlen )
770 {
771     DOS_FULL_NAME full_name;
772
773     /* FIXME: is it correct to always return a fully qualified short path? */
774     if (!DOSFS_GetFullName( longpath, TRUE, &full_name )) return 0;
775     lstrcpyn32A( shortpath, full_name.short_name, shortlen );
776     return strlen( full_name.short_name );
777 }
778
779
780 /***********************************************************************
781  *           GetShortPathName32W   (KERNEL32.272)
782  */
783 DWORD WINAPI GetShortPathName32W( LPCWSTR longpath, LPWSTR shortpath,
784                                   DWORD shortlen )
785 {
786     DOS_FULL_NAME full_name;
787     DWORD ret = 0;
788     LPSTR longpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, longpath );
789
790     /* FIXME: is it correct to always return a fully qualified short path? */
791     if (DOSFS_GetFullName( longpathA, TRUE, &full_name ))
792     {
793         ret = strlen( full_name.short_name );
794         lstrcpynAtoW( shortpath, full_name.short_name, shortlen );
795     }
796     HeapFree( GetProcessHeap(), 0, longpathA );
797     return ret;
798 }
799
800
801 /***********************************************************************
802  *           GetLongPathName32A   (KERNEL32.xxx)
803  */
804 DWORD WINAPI GetLongPathName32A( LPCSTR shortpath, LPSTR longpath,
805                                   DWORD longlen )
806 {
807     DOS_FULL_NAME full_name;
808
809     /* FIXME: is it correct to always return a fully qualified short path? */
810     if (!DOSFS_GetFullName( shortpath, TRUE, &full_name )) return 0;
811     lstrcpyn32A( longpath, full_name.long_name, longlen );
812     return strlen( full_name.long_name );
813 }
814
815
816 /***********************************************************************
817  *           GetLongPathName32W   (KERNEL32.269)
818  */
819 DWORD WINAPI GetLongPathName32W( LPCWSTR shortpath, LPWSTR longpath,
820                                   DWORD longlen )
821 {
822     DOS_FULL_NAME full_name;
823     DWORD ret = 0;
824     LPSTR shortpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, shortpath );
825
826     /* FIXME: is it correct to always return a fully qualified short path? */
827     if (DOSFS_GetFullName( shortpathA, TRUE, &full_name ))
828     {
829         ret = strlen( full_name.short_name );
830         lstrcpynAtoW( longpath, full_name.long_name, longlen );
831     }
832     HeapFree( GetProcessHeap(), 0, shortpathA );
833     return ret;
834 }
835
836
837 /***********************************************************************
838  *           DOSFS_DoGetFullPathName
839  *
840  * Implementation of GetFullPathName32A/W.
841  */
842 static DWORD DOSFS_DoGetFullPathName( LPCSTR name, DWORD len, LPSTR result,
843                                       BOOL32 unicode )
844 {
845     char buffer[MAX_PATHNAME_LEN];
846     int drive;
847     char *p;
848
849     dprintf_dosfs( stddeb, "GetFullPathName: converting %s\n", name );
850
851     if (!name || !result) return 0;
852
853     if ((drive = DOSFS_GetPathDrive( &name )) == -1) return 0;
854     p = buffer;
855     *p++ = 'A' + drive;
856     *p++ = ':';
857     if (IS_END_OF_NAME(*name) && (*name))  /* Absolute path */
858     {
859         while ((*name == '\\') || (*name == '/')) name++;
860     }
861     else  /* Relative path or empty path */
862     {
863         *p++ = '\\';
864         lstrcpyn32A( p, DRIVE_GetDosCwd(drive), sizeof(buffer) - 3 );
865         if (*p) p += strlen(p); else p--;
866     }
867     if (!*name) /* empty path */
868       *p++ = '\\';
869     *p = '\0';
870
871     while (*name)
872     {
873         if (*name == '.')
874         {
875             if (IS_END_OF_NAME(name[1]))
876             {
877                 name++;
878                 while ((*name == '\\') || (*name == '/')) name++;
879                 continue;
880             }
881             else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
882             {
883                 name += 2;
884                 while ((*name == '\\') || (*name == '/')) name++;
885                 while ((p > buffer + 2) && (*p != '\\')) p--;
886                 *p = '\0';  /* Remove trailing separator */
887                 continue;
888             }
889         }
890         if (p >= buffer + sizeof(buffer) - 1)
891         {
892             DOS_ERROR( ER_PathNotFound, EC_NotFound, SA_Abort, EL_Disk );
893             return 0;
894         }
895         *p++ = '\\';
896         while (!IS_END_OF_NAME(*name) && (p < buffer + sizeof(buffer) - 1))
897             *p++ = *name++;
898         *p = '\0';
899         while ((*name == '\\') || (*name == '/')) name++;
900     }
901
902     if (!buffer[2])
903     {
904         buffer[2] = '\\';
905         buffer[3] = '\0';
906     }
907     if (!(DRIVE_GetFlags(drive) & DRIVE_CASE_PRESERVING))
908         CharUpper32A( buffer );
909
910     if (unicode) lstrcpynAtoW( (LPWSTR)result, buffer, len );
911     else lstrcpyn32A( result, buffer, len );
912
913     dprintf_dosfs( stddeb, "GetFullPathName: returning %s\n", buffer );
914     return strlen(buffer);
915 }
916
917
918 /***********************************************************************
919  *           GetFullPathName32A   (KERNEL32.272)
920  */
921 DWORD WINAPI GetFullPathName32A( LPCSTR name, DWORD len, LPSTR buffer,
922                                  LPSTR *lastpart )
923 {
924     DWORD ret = DOSFS_DoGetFullPathName( name, len, buffer, FALSE );
925     if (ret && lastpart)
926     {
927         LPSTR p = buffer + strlen(buffer);
928         while ((p > buffer + 2) && (*p != '\\')) p--;
929         *lastpart = p + 1;
930     }
931     return ret;
932 }
933
934
935 /***********************************************************************
936  *           GetFullPathName32W   (KERNEL32.273)
937  */
938 DWORD WINAPI GetFullPathName32W( LPCWSTR name, DWORD len, LPWSTR buffer,
939                                  LPWSTR *lastpart )
940 {
941     LPSTR nameA = HEAP_strdupWtoA( GetProcessHeap(), 0, name );
942     DWORD ret = DOSFS_DoGetFullPathName( nameA, len, (LPSTR)buffer, TRUE );
943     HeapFree( GetProcessHeap(), 0, nameA );
944     if (ret && lastpart)
945     {
946         LPWSTR p = buffer + lstrlen32W(buffer);
947         while ((p > buffer + 2) && (*p != '\\')) p--;
948         *lastpart = p + 1;
949     }
950     return ret;
951 }
952
953
954 /***********************************************************************
955  *           DOSFS_FindNext
956  *
957  * Find the next matching file. Return the number of entries read to find
958  * the matching one, or 0 if no more entries.
959  * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
960  * file name mask. Either or both can be NULL.
961  */
962 int DOSFS_FindNext( const char *path, const char *short_mask,
963                     const char *long_mask, int drive, BYTE attr,
964                     int skip, WIN32_FIND_DATA32A *entry )
965 {
966     static DOS_DIR *dir = NULL;
967     int count = 0;
968     static char buffer[MAX_PATHNAME_LEN];
969     static int cur_pos = 0;
970     static int drive_root = 0;
971     char *p;
972     char dos_name[13];
973     LPCSTR long_name, short_name;
974     UINT32 flags;
975     BY_HANDLE_FILE_INFORMATION info;
976
977     if ((attr & ~(FA_UNUSED | FA_ARCHIVE | FA_RDONLY)) == FA_LABEL)
978     {
979         if (skip) return 0;
980         entry->dwFileAttributes  = FILE_ATTRIBUTE_LABEL;
981         DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftCreationTime, 0 );
982         DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftLastAccessTime, 0 );
983         DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftLastWriteTime, 0 );
984         entry->nFileSizeHigh     = 0;
985         entry->nFileSizeLow      = 0;
986         entry->dwReserved0       = 0;
987         entry->dwReserved1       = 0;
988         DOSFS_ToDosDTAFormat( DRIVE_GetLabel( drive ), entry->cFileName );
989         strcpy( entry->cAlternateFileName, entry->cFileName );
990         return 1;
991     }
992
993     /* Check the cached directory */
994     if (dir && !strcmp( buffer, path ) && (cur_pos <= skip)) skip -= cur_pos;
995     else  /* Not in the cache, open it anew */
996     {
997         const char *drive_path;
998         dprintf_dosfs( stddeb, "DOSFS_FindNext: cache miss, path=%s skip=%d buf=%s cur=%d\n",
999                        path, skip, buffer, cur_pos );
1000         cur_pos = skip;
1001         if (dir) DOSFS_CloseDir(dir);
1002         if (!*path) path = "/";
1003         if (!(dir = DOSFS_OpenDir(path))) return 0;
1004         drive_path = path + strlen(DRIVE_GetRoot(drive));
1005         while ((*drive_path == '/') || (*drive_path == '\\')) drive_path++;
1006         drive_root = !*drive_path;
1007         dprintf_dosfs(stddeb, "DOSFS_FindNext: drive_root = %d\n", drive_root);
1008         lstrcpyn32A( buffer, path, sizeof(buffer) - 1 );
1009     }
1010     strcat( buffer, "/" );
1011     p = buffer + strlen(buffer);
1012     attr |= FA_UNUSED | FA_ARCHIVE | FA_RDONLY;
1013     flags = DRIVE_GetFlags( drive );
1014
1015     while (DOSFS_ReadDir( dir, &long_name, &short_name ))
1016     {
1017         if (skip-- > 0) continue;
1018         count++;
1019
1020         /* Don't return '.' and '..' in the root of the drive */
1021         if (drive_root && (long_name[0] == '.') &&
1022             (!long_name[1] || ((long_name[1] == '.') && !long_name[2])))
1023             continue;
1024
1025         /* Check the long mask */
1026
1027         if (long_mask)
1028         {
1029             if (!DOSFS_MatchLong( long_mask, long_name,
1030                                   flags & DRIVE_CASE_SENSITIVE )) continue;
1031         }
1032
1033         /* Check the short mask */
1034
1035         if (short_mask)
1036         {
1037             if (!short_name)
1038             {
1039                 DOSFS_Hash( long_name, dos_name, TRUE,
1040                             !(flags & DRIVE_CASE_SENSITIVE) );
1041                 short_name = dos_name;
1042             }
1043             if (!DOSFS_MatchShort( short_mask, short_name )) continue;
1044         }
1045
1046         /* Check the file attributes */
1047
1048         lstrcpyn32A( p, long_name, sizeof(buffer) - (int)(p - buffer) );
1049         if (!FILE_Stat( buffer, &info ))
1050         {
1051             fprintf( stderr, "DOSFS_FindNext: can't stat %s\n", buffer );
1052             continue;
1053         }
1054         if (info.dwFileAttributes & ~attr) continue;
1055
1056         /* We now have a matching entry; fill the result and return */
1057
1058         entry->dwFileAttributes = info.dwFileAttributes;
1059         entry->ftCreationTime   = info.ftCreationTime;
1060         entry->ftLastAccessTime = info.ftLastAccessTime;
1061         entry->ftLastWriteTime  = info.ftLastWriteTime;
1062         entry->nFileSizeHigh    = info.nFileSizeHigh;
1063         entry->nFileSizeLow     = info.nFileSizeLow;
1064
1065         if (short_name)
1066             DOSFS_ToDosDTAFormat( short_name, entry->cAlternateFileName );
1067         else
1068             DOSFS_Hash( long_name, entry->cAlternateFileName, FALSE,
1069                         !(flags & DRIVE_CASE_SENSITIVE) );
1070
1071         lstrcpyn32A( entry->cFileName, long_name, sizeof(entry->cFileName) );
1072         if (!(flags & DRIVE_CASE_PRESERVING)) CharLower32A( entry->cFileName );
1073         dprintf_dosfs( stddeb, "DOSFS_FindNext: returning %s (%s) %02lx %ld\n",
1074                        entry->cFileName, entry->cAlternateFileName,
1075                        entry->dwFileAttributes, entry->nFileSizeLow );
1076         cur_pos += count;
1077         p[-1] = '\0';  /* Remove trailing slash in buffer */
1078         return count;
1079     }
1080     DOSFS_CloseDir( dir );
1081     dir = NULL;
1082     return 0;  /* End of directory */
1083 }
1084
1085
1086 /*************************************************************************
1087  *           FindFirstFile16   (KERNEL.413)
1088  */
1089 HANDLE16 WINAPI FindFirstFile16( LPCSTR path, WIN32_FIND_DATA32A *data )
1090 {
1091     DOS_FULL_NAME full_name;
1092     HGLOBAL16 handle;
1093     FIND_FIRST_INFO *info;
1094
1095     if (!path) return 0;
1096     if (!DOSFS_GetFullName( path, FALSE, &full_name ))
1097         return INVALID_HANDLE_VALUE16;
1098     if (!(handle = GlobalAlloc16( GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO) )))
1099         return INVALID_HANDLE_VALUE16;
1100     info = (FIND_FIRST_INFO *)GlobalLock16( handle );
1101     info->path = HEAP_strdupA( SystemHeap, 0, full_name.long_name );
1102     info->mask = strrchr( info->path, '/' );
1103     *(info->mask++) = '\0';
1104     if (path[0] && (path[1] == ':')) info->drive = toupper(*path) - 'A';
1105     else info->drive = DRIVE_GetCurrentDrive();
1106     info->skip = 0;
1107     GlobalUnlock16( handle );
1108     if (!FindNextFile16( handle, data ))
1109     {
1110         FindClose16( handle );
1111         DOS_ERROR( ER_NoMoreFiles, EC_MediaError, SA_Abort, EL_Disk );
1112         return INVALID_HANDLE_VALUE16;
1113     }
1114     return handle;
1115 }
1116
1117
1118 /*************************************************************************
1119  *           FindFirstFile32A   (KERNEL32.123)
1120  */
1121 HANDLE32 WINAPI FindFirstFile32A( LPCSTR path, WIN32_FIND_DATA32A *data )
1122 {
1123     HANDLE32 handle = FindFirstFile16( path, data );
1124     if (handle == INVALID_HANDLE_VALUE16) return INVALID_HANDLE_VALUE32;
1125     return handle;
1126 }
1127
1128
1129 /*************************************************************************
1130  *           FindFirstFile32W   (KERNEL32.124)
1131  */
1132 HANDLE32 WINAPI FindFirstFile32W( LPCWSTR path, WIN32_FIND_DATA32W *data )
1133 {
1134     WIN32_FIND_DATA32A dataA;
1135     LPSTR pathA = HEAP_strdupWtoA( GetProcessHeap(), 0, path );
1136     HANDLE32 handle = FindFirstFile32A( pathA, &dataA );
1137     HeapFree( GetProcessHeap(), 0, pathA );
1138     if (handle != INVALID_HANDLE_VALUE32)
1139     {
1140         data->dwFileAttributes = dataA.dwFileAttributes;
1141         data->ftCreationTime   = dataA.ftCreationTime;
1142         data->ftLastAccessTime = dataA.ftLastAccessTime;
1143         data->ftLastWriteTime  = dataA.ftLastWriteTime;
1144         data->nFileSizeHigh    = dataA.nFileSizeHigh;
1145         data->nFileSizeLow     = dataA.nFileSizeLow;
1146         lstrcpyAtoW( data->cFileName, dataA.cFileName );
1147         lstrcpyAtoW( data->cAlternateFileName, dataA.cAlternateFileName );
1148     }
1149     return handle;
1150 }
1151
1152
1153 /*************************************************************************
1154  *           FindNextFile16   (KERNEL.414)
1155  */
1156 BOOL16 WINAPI FindNextFile16( HANDLE16 handle, WIN32_FIND_DATA32A *data )
1157 {
1158     FIND_FIRST_INFO *info;
1159     int count;
1160
1161     if (!(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
1162     {
1163         DOS_ERROR( ER_InvalidHandle, EC_ProgramError, SA_Abort, EL_Disk );
1164         return FALSE;
1165     }
1166     GlobalUnlock16( handle );
1167     if (!info->path)
1168     {
1169         DOS_ERROR( ER_NoMoreFiles, EC_MediaError, SA_Abort, EL_Disk );
1170         return FALSE;
1171     }
1172     if (!(count = DOSFS_FindNext( info->path, NULL, info->mask, info->drive,
1173                                   0xff, info->skip, data )))
1174     {
1175         HeapFree( SystemHeap, 0, info->path );
1176         info->path = info->mask = NULL;
1177         DOS_ERROR( ER_NoMoreFiles, EC_MediaError, SA_Abort, EL_Disk );
1178         return FALSE;
1179     }
1180     info->skip += count;
1181     return TRUE;
1182 }
1183
1184
1185 /*************************************************************************
1186  *           FindNextFile32A   (KERNEL32.126)
1187  */
1188 BOOL32 WINAPI FindNextFile32A( HANDLE32 handle, WIN32_FIND_DATA32A *data )
1189 {
1190     return FindNextFile16( handle, data );
1191 }
1192
1193
1194 /*************************************************************************
1195  *           FindNextFile32W   (KERNEL32.127)
1196  */
1197 BOOL32 WINAPI FindNextFile32W( HANDLE32 handle, WIN32_FIND_DATA32W *data )
1198 {
1199     WIN32_FIND_DATA32A dataA;
1200     if (!FindNextFile32A( handle, &dataA )) return FALSE;
1201     data->dwFileAttributes = dataA.dwFileAttributes;
1202     data->ftCreationTime   = dataA.ftCreationTime;
1203     data->ftLastAccessTime = dataA.ftLastAccessTime;
1204     data->ftLastWriteTime  = dataA.ftLastWriteTime;
1205     data->nFileSizeHigh    = dataA.nFileSizeHigh;
1206     data->nFileSizeLow     = dataA.nFileSizeLow;
1207     lstrcpyAtoW( data->cFileName, dataA.cFileName );
1208     lstrcpyAtoW( data->cAlternateFileName, dataA.cAlternateFileName );
1209     return TRUE;
1210 }
1211
1212
1213 /*************************************************************************
1214  *           FindClose16   (KERNEL.415)
1215  */
1216 BOOL16 WINAPI FindClose16( HANDLE16 handle )
1217 {
1218     FIND_FIRST_INFO *info;
1219
1220     if ((handle == INVALID_HANDLE_VALUE16) ||
1221         !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
1222     {
1223         DOS_ERROR( ER_InvalidHandle, EC_ProgramError, SA_Abort, EL_Disk );
1224         return FALSE;
1225     }
1226     if (info->path) HeapFree( SystemHeap, 0, info->path );
1227     GlobalUnlock16( handle );
1228     GlobalFree16( handle );
1229     return TRUE;
1230 }
1231
1232
1233 /*************************************************************************
1234  *           FindClose32   (KERNEL32.119)
1235  */
1236 BOOL32 WINAPI FindClose32( HANDLE32 handle )
1237 {
1238     return FindClose16( (HANDLE16)handle );
1239 }
1240
1241
1242 /***********************************************************************
1243  *           DOSFS_UnixTimeToFileTime
1244  *
1245  * Convert a Unix time to FILETIME format.
1246  * The FILETIME structure is a 64-bit value representing the number of
1247  * 100-nanosecond intervals since January 1, 1601, 0:00.
1248  * 'remainder' is the nonnegative number of 100-ns intervals
1249  * corresponding to the time fraction smaller than 1 second that
1250  * couldn't be stored in the time_t value.
1251  */
1252 void DOSFS_UnixTimeToFileTime( time_t unix_time, FILETIME *filetime,
1253                                DWORD remainder )
1254 {
1255     /* NOTES:
1256
1257        CONSTANTS: 
1258        The time difference between 1 January 1601, 00:00:00 and
1259        1 January 1970, 00:00:00 is 369 years, plus the leap years
1260        from 1604 to 1968, excluding 1700, 1800, 1900.
1261        This makes (1968 - 1600) / 4 - 3 = 89 leap days, and a total
1262        of 134774 days.
1263
1264        Any day in that period had 24 * 60 * 60 = 86400 seconds.
1265
1266        The time difference is 134774 * 86400 * 10000000, which can be written
1267        116444736000000000
1268        27111902 * 2^32 + 3577643008
1269        413 * 2^48 + 45534 * 2^32 + 54590 * 2^16 + 32768
1270
1271        If you find that these constants are buggy, please change them in all
1272        instances in both conversion functions.
1273
1274        VERSIONS:
1275        There are two versions, one of them uses long long variables and
1276        is presumably faster but not ISO C. The other one uses standard C
1277        data types and operations but relies on the assumption that negative
1278        numbers are stored as 2's complement (-1 is 0xffff....). If this
1279        assumption is violated, dates before 1970 will not convert correctly.
1280        This should however work on any reasonable architecture where WINE
1281        will run.
1282
1283        DETAILS:
1284        
1285        Take care not to remove the casts. I have tested these functions
1286        (in both versions) for a lot of numbers. I would be interested in
1287        results on other compilers than GCC.
1288
1289        The operations have been designed to account for the possibility
1290        of 64-bit time_t in future UNICES. Even the versions without
1291        internal long long numbers will work if time_t only is 64 bit.
1292        A 32-bit shift, which was necessary for that operation, turned out
1293        not to work correctly in GCC, besides giving the warning. So I
1294        used a double 16-bit shift instead. Numbers are in the ISO version
1295        represented by three limbs, the most significant with 32 bit, the
1296        other two with 16 bit each.
1297
1298        As the modulo-operator % is not well-defined for negative numbers,
1299        negative divisors have been avoided in DOSFS_FileTimeToUnixTime.
1300
1301        There might be quicker ways to do this in C. Certainly so in
1302        assembler.
1303
1304        Claus Fischer, fischer@iue.tuwien.ac.at
1305        */
1306
1307 #if __GNUC__
1308 #  define USE_LONG_LONG 1
1309 #else
1310 #  define USE_LONG_LONG 0
1311 #endif
1312
1313 #if USE_LONG_LONG               /* gcc supports long long type */
1314
1315     long long int t = unix_time;
1316     t *= 10000000;
1317     t += 116444736000000000LL;
1318     t += remainder;
1319     filetime->dwLowDateTime  = (UINT32)t;
1320     filetime->dwHighDateTime = (UINT32)(t >> 32);
1321
1322 #else  /* ISO version */
1323
1324     UINT32 a0;                  /* 16 bit, low    bits */
1325     UINT32 a1;                  /* 16 bit, medium bits */
1326     UINT32 a2;                  /* 32 bit, high   bits */
1327
1328     /* Copy the unix time to a2/a1/a0 */
1329     a0 =  unix_time & 0xffff;
1330     a1 = (unix_time >> 16) & 0xffff;
1331     /* This is obsolete if unix_time is only 32 bits, but it does not hurt.
1332        Do not replace this by >> 32, it gives a compiler warning and it does
1333        not work. */
1334     a2 = (unix_time >= 0 ? (unix_time >> 16) >> 16 :
1335           ~((~unix_time >> 16) >> 16));
1336
1337     /* Multiply a by 10000000 (a = a2/a1/a0)
1338        Split the factor into 10000 * 1000 which are both less than 0xffff. */
1339     a0 *= 10000;
1340     a1 = a1 * 10000 + (a0 >> 16);
1341     a2 = a2 * 10000 + (a1 >> 16);
1342     a0 &= 0xffff;
1343     a1 &= 0xffff;
1344
1345     a0 *= 1000;
1346     a1 = a1 * 1000 + (a0 >> 16);
1347     a2 = a2 * 1000 + (a1 >> 16);
1348     a0 &= 0xffff;
1349     a1 &= 0xffff;
1350
1351     /* Add the time difference and the remainder */
1352     a0 += 32768 + (remainder & 0xffff);
1353     a1 += 54590 + (remainder >> 16   ) + (a0 >> 16);
1354     a2 += 27111902                     + (a1 >> 16);
1355     a0 &= 0xffff;
1356     a1 &= 0xffff;
1357
1358     /* Set filetime */
1359     filetime->dwLowDateTime  = (a1 << 16) + a0;
1360     filetime->dwHighDateTime = a2;
1361 #endif
1362 }
1363
1364
1365 /***********************************************************************
1366  *           DOSFS_FileTimeToUnixTime
1367  *
1368  * Convert a FILETIME format to Unix time.
1369  * If not NULL, 'remainder' contains the fractional part of the filetime,
1370  * in the range of [0..9999999] (even if time_t is negative).
1371  */
1372 time_t DOSFS_FileTimeToUnixTime( const FILETIME *filetime, DWORD *remainder )
1373 {
1374     /* Read the comment in the function DOSFS_UnixTimeToFileTime. */
1375 #if USE_LONG_LONG
1376
1377     long long int t = filetime->dwHighDateTime;
1378     t <<= 32;
1379     t += (UINT32)filetime->dwLowDateTime;
1380     t -= 116444736000000000LL;
1381     if (t < 0)
1382     {
1383         if (remainder) *remainder = 9999999 - (-t - 1) % 10000000;
1384         return -1 - ((-t - 1) / 10000000);
1385     }
1386     else
1387     {
1388         if (remainder) *remainder = t % 10000000;
1389         return t / 10000000;
1390     }
1391
1392 #else  /* ISO version */
1393
1394     UINT32 a0;                  /* 16 bit, low    bits */
1395     UINT32 a1;                  /* 16 bit, medium bits */
1396     UINT32 a2;                  /* 32 bit, high   bits */
1397     UINT32 r;                   /* remainder of division */
1398     unsigned int carry;         /* carry bit for subtraction */
1399     int negative;               /* whether a represents a negative value */
1400
1401     /* Copy the time values to a2/a1/a0 */
1402     a2 =  (UINT32)filetime->dwHighDateTime;
1403     a1 = ((UINT32)filetime->dwLowDateTime ) >> 16;
1404     a0 = ((UINT32)filetime->dwLowDateTime ) & 0xffff;
1405
1406     /* Subtract the time difference */
1407     if (a0 >= 32768           ) a0 -=             32768        , carry = 0;
1408     else                        a0 += (1 << 16) - 32768        , carry = 1;
1409
1410     if (a1 >= 54590    + carry) a1 -=             54590 + carry, carry = 0;
1411     else                        a1 += (1 << 16) - 54590 - carry, carry = 1;
1412
1413     a2 -= 27111902 + carry;
1414     
1415     /* If a is negative, replace a by (-1-a) */
1416     negative = (a2 >= ((UINT32)1) << 31);
1417     if (negative)
1418     {
1419         /* Set a to -a - 1 (a is a2/a1/a0) */
1420         a0 = 0xffff - a0;
1421         a1 = 0xffff - a1;
1422         a2 = ~a2;
1423     }
1424
1425     /* Divide a by 10000000 (a = a2/a1/a0), put the rest into r.
1426        Split the divisor into 10000 * 1000 which are both less than 0xffff. */
1427     a1 += (a2 % 10000) << 16;
1428     a2 /=       10000;
1429     a0 += (a1 % 10000) << 16;
1430     a1 /=       10000;
1431     r   =  a0 % 10000;
1432     a0 /=       10000;
1433
1434     a1 += (a2 % 1000) << 16;
1435     a2 /=       1000;
1436     a0 += (a1 % 1000) << 16;
1437     a1 /=       1000;
1438     r  += (a0 % 1000) * 10000;
1439     a0 /=       1000;
1440
1441     /* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
1442     if (negative)
1443     {
1444         /* Set a to -a - 1 (a is a2/a1/a0) */
1445         a0 = 0xffff - a0;
1446         a1 = 0xffff - a1;
1447         a2 = ~a2;
1448
1449         r  = 9999999 - r;
1450     }
1451
1452     if (remainder) *remainder = r;
1453
1454     /* Do not replace this by << 32, it gives a compiler warning and it does
1455        not work. */
1456     return ((((time_t)a2) << 16) << 16) + (a1 << 16) + a0;
1457 #endif
1458 }
1459
1460
1461 /***********************************************************************
1462  *           DosDateTimeToFileTime   (KERNEL32.76)
1463  */
1464 BOOL32 WINAPI DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft)
1465 {
1466     struct tm newtm;
1467
1468     newtm.tm_sec  = (fattime & 0x1f) * 2;
1469     newtm.tm_min  = (fattime >> 5) & 0x3f;
1470     newtm.tm_hour = (fattime >> 11);
1471     newtm.tm_mday = (fatdate & 0x1f);
1472     newtm.tm_mon  = ((fatdate >> 5) & 0x0f) - 1;
1473     newtm.tm_year = (fatdate >> 9) + 80;
1474     DOSFS_UnixTimeToFileTime( mktime( &newtm ), ft, 0 );
1475     return TRUE;
1476 }
1477
1478
1479 /***********************************************************************
1480  *           FileTimeToDosDateTime   (KERNEL32.111)
1481  */
1482 BOOL32 WINAPI FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate,
1483                                      LPWORD fattime )
1484 {
1485     time_t unixtime = DOSFS_FileTimeToUnixTime( ft, NULL );
1486     struct tm *tm = localtime( &unixtime );
1487     if (fattime)
1488         *fattime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
1489     if (fatdate)
1490         *fatdate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
1491                    + tm->tm_mday;
1492     return TRUE;
1493 }
1494
1495
1496 /***********************************************************************
1497  *           LocalFileTimeToFileTime   (KERNEL32.373)
1498  */
1499 BOOL32 WINAPI LocalFileTimeToFileTime( const FILETIME *localft,
1500                                        LPFILETIME utcft )
1501 {
1502     struct tm *xtm;
1503     DWORD remainder;
1504
1505     /* convert from local to UTC. Perhaps not correct. FIXME */
1506     time_t unixtime = DOSFS_FileTimeToUnixTime( localft, &remainder );
1507     xtm = gmtime( &unixtime );
1508     DOSFS_UnixTimeToFileTime( mktime(xtm), utcft, remainder );
1509     return TRUE; 
1510 }
1511
1512
1513 /***********************************************************************
1514  *           FileTimeToLocalFileTime   (KERNEL32.112)
1515  */
1516 BOOL32 WINAPI FileTimeToLocalFileTime( const FILETIME *utcft,
1517                                        LPFILETIME localft )
1518 {
1519     struct tm *xtm;
1520     DWORD remainder;
1521
1522     /* convert from UTC to local. Perhaps not correct. FIXME */
1523     time_t unixtime = DOSFS_FileTimeToUnixTime( utcft, &remainder );
1524     xtm = localtime( &unixtime );
1525     DOSFS_UnixTimeToFileTime( mktime(xtm), localft, remainder );
1526     return TRUE; 
1527 }
1528
1529
1530 /***********************************************************************
1531  *           FileTimeToSystemTime   (KERNEL32.113)
1532  */
1533 BOOL32 WINAPI FileTimeToSystemTime( const FILETIME *ft, LPSYSTEMTIME syst )
1534 {
1535     struct tm *xtm;
1536     DWORD remainder;
1537     time_t xtime = DOSFS_FileTimeToUnixTime( ft, &remainder );
1538     xtm = gmtime(&xtime);
1539     syst->wYear         = xtm->tm_year;
1540     syst->wMonth        = xtm->tm_mon;
1541     syst->wDayOfWeek    = xtm->tm_wday;
1542     syst->wDay          = xtm->tm_mday;
1543     syst->wHour         = xtm->tm_hour;
1544     syst->wMinute       = xtm->tm_min;
1545     syst->wSecond       = xtm->tm_sec;
1546     syst->wMilliseconds = remainder / 10000;
1547     return TRUE; 
1548 }
1549
1550 /***********************************************************************
1551  *           QueryDosDeviceA   (KERNEL32.413)
1552  *
1553  * returns array of strings terminated by \0, terminated by \0
1554  */
1555 DWORD WINAPI QueryDosDevice32A(LPCSTR devname,LPSTR target,DWORD bufsize)
1556 {
1557     LPSTR s;
1558     char  buffer[200];
1559
1560     dprintf_dosfs(stddeb,"QueryDosDevice(%s,...)\n",devname?devname:"<null>");
1561     if (!devname) {
1562         /* return known MSDOS devices */
1563         lstrcpy32A(buffer,"CON COM1 COM2 LPT1 NUL ");
1564         while ((s=strchr(buffer,' ')))
1565                 *s='\0';
1566
1567         lstrcpyn32A(target,buffer,bufsize);
1568         return strlen(buffer);
1569     }
1570     lstrcpy32A(buffer,"\\DEV\\");
1571     lstrcat32A(buffer,devname);
1572     if ((s=strchr(buffer,':'))) *s='\0';
1573     lstrcpyn32A(target,buffer,bufsize);
1574     return strlen(buffer);
1575 }
1576
1577
1578 /***********************************************************************
1579  *           QueryDosDeviceW   (KERNEL32.414)
1580  *
1581  * returns array of strings terminated by \0, terminated by \0
1582  */
1583 DWORD WINAPI QueryDosDevice32W(LPCWSTR devname,LPWSTR target,DWORD bufsize)
1584 {
1585     LPSTR devnameA = devname?HEAP_strdupWtoA(GetProcessHeap(),0,devname):NULL;
1586     LPSTR targetA = (LPSTR)HEAP_xalloc(GetProcessHeap(),0,bufsize);
1587     DWORD ret = QueryDosDevice32A(devnameA,targetA,bufsize);
1588
1589     lstrcpynAtoW(target,targetA,bufsize);
1590     if (devnameA) HeapFree(GetProcessHeap(),0,devnameA);
1591     if (targetA) HeapFree(GetProcessHeap(),0,targetA);
1592     return ret;
1593 }
1594
1595
1596 /***********************************************************************
1597  *           SystemTimeToFileTime   (KERNEL32.526)
1598  */
1599 BOOL32 WINAPI SystemTimeToFileTime( const SYSTEMTIME *syst, LPFILETIME ft )
1600 {
1601     struct tm xtm;
1602
1603     xtm.tm_year = syst->wYear;
1604     xtm.tm_mon  = syst->wMonth;
1605     xtm.tm_wday = syst->wDayOfWeek;
1606     xtm.tm_mday = syst->wDay;
1607     xtm.tm_hour = syst->wHour;
1608     xtm.tm_min  = syst->wMinute;
1609     xtm.tm_sec  = syst->wSecond;
1610     DOSFS_UnixTimeToFileTime( mktime(&xtm), ft, syst->wMilliseconds * 10000 );
1611     return TRUE; 
1612 }