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