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