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