Merged msacm and msacm32 dlls.
[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 and spaces */
534     while (len > 1 && (name[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             /* Ignore trailing dots and spaces */
859             while(p_l[-1] == '.' || p_l[-1] == ' ') {
860                 --p_l;
861                 --p_s;
862             }
863             *p_l = *p_s = '\0';
864         }
865         while ((*name == '\\') || (*name == '/')) name++;
866     }
867
868     if (!found)
869     {
870         if (check_last)
871         {
872             SetLastError( ERROR_FILE_NOT_FOUND );
873             return FALSE;
874         }
875         if (*name)  /* Not last */
876         {
877             SetLastError( ERROR_PATH_NOT_FOUND );
878             return FALSE;
879         }
880     }
881     if (!full->long_name[0]) strcpy( full->long_name, "/" );
882     if (!full->short_name[2]) strcpy( full->short_name + 2, "\\" );
883     TRACE("returning %s = %s\n", full->long_name, full->short_name );
884     return TRUE;
885 }
886
887
888 /***********************************************************************
889  *           GetShortPathNameA   (KERNEL32.271)
890  *
891  * NOTES
892  *  observed:
893  *  longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
894  *  *longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0
895  * 
896  * more observations ( with NT 3.51 (WinDD) ):
897  * longpath <= 8.3 -> just copy longpath to shortpath
898  * longpath > 8.3  -> 
899  *             a) file does not exist -> return 0, LastError = ERROR_FILE_NOT_FOUND
900  *             b) file does exist     -> set the short filename.
901  * - trailing slashes are reproduced in the short name, even if the
902  *   file is not a directory
903  * - the absolute/relative path of the short name is reproduced like found
904  *   in the long name
905  * - longpath and shortpath may have the same adress
906  * Peter Ganten, 1999
907  */
908 DWORD WINAPI GetShortPathNameA( LPCSTR longpath, LPSTR shortpath,
909                                   DWORD shortlen )
910 {
911     DOS_FULL_NAME full_name;
912     LPSTR tmpshortpath;
913     DWORD sp = 0, lp = 0;
914     int tmplen, drive;
915     UINT flags;
916
917     TRACE("%s\n", debugstr_a(longpath));
918
919     if (!longpath) {
920       SetLastError(ERROR_INVALID_PARAMETER);
921       return 0;
922     }
923     if (!longpath[0]) {
924       SetLastError(ERROR_BAD_PATHNAME);
925       return 0;
926     }
927
928     if ( ( tmpshortpath = HeapAlloc ( GetProcessHeap(), 0, MAX_PATHNAME_LEN ) ) == NULL ) {
929       SetLastError ( ERROR_NOT_ENOUGH_MEMORY );
930       return 0;
931     }
932
933     /* check for drive letter */
934     if ( longpath[1] == ':' ) {
935       tmpshortpath[0] = longpath[0];
936       tmpshortpath[1] = ':';
937       sp = 2;
938     }
939
940     if ( ( drive = DOSFS_GetPathDrive ( &longpath )) == -1 ) return 0;
941     flags = DRIVE_GetFlags ( drive );
942
943     while ( longpath[lp] ) {
944
945       /* check for path delimiters and reproduce them */
946       if ( longpath[lp] == '\\' || longpath[lp] == '/' ) {
947         if (!sp || tmpshortpath[sp-1]!= '\\') 
948         {
949             /* strip double "\\" */
950             tmpshortpath[sp] = '\\';
951             sp++;
952         }
953         tmpshortpath[sp]=0;/*terminate string*/
954         lp++;
955         continue;
956       }
957
958       tmplen = strcspn ( longpath + lp, "\\/" ); 
959       lstrcpynA ( tmpshortpath+sp, longpath + lp, tmplen+1 );
960       
961       /* Check, if the current element is a valid dos name */
962       if ( DOSFS_ValidDOSName ( longpath + lp, !(flags & DRIVE_CASE_SENSITIVE) ) ) {
963         sp += tmplen;
964         lp += tmplen;
965         continue;
966       }
967
968       /* Check if the file exists and use the existing file name */
969       if ( DOSFS_GetFullName ( tmpshortpath, TRUE, &full_name ) ) {
970         lstrcpyA ( tmpshortpath+sp, strrchr ( full_name.short_name, '\\' ) + 1 );
971         sp += lstrlenA ( tmpshortpath+sp );
972         lp += tmplen;
973         continue;
974       }
975
976       TRACE("not found!\n" );
977       SetLastError ( ERROR_FILE_NOT_FOUND );
978       return 0;
979     }
980     tmpshortpath[sp] = 0;
981
982     lstrcpynA ( shortpath, tmpshortpath, shortlen );
983     TRACE("returning %s\n", debugstr_a(shortpath) );
984     tmplen = lstrlenA ( tmpshortpath );
985     HeapFree ( GetProcessHeap(), 0, tmpshortpath );
986     
987     return tmplen;
988 }
989
990
991 /***********************************************************************
992  *           GetShortPathNameW   (KERNEL32.272)
993  */
994 DWORD WINAPI GetShortPathNameW( LPCWSTR longpath, LPWSTR shortpath,
995                                   DWORD shortlen )
996 {
997     LPSTR longpathA, shortpathA;
998     DWORD ret = 0;
999
1000     longpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, longpath );
1001     shortpathA = HeapAlloc ( GetProcessHeap(), 0, shortlen );
1002
1003     ret = GetShortPathNameA ( longpathA, shortpathA, shortlen );
1004     lstrcpynAtoW ( shortpath, shortpathA, shortlen );
1005
1006     HeapFree( GetProcessHeap(), 0, longpathA );
1007     HeapFree( GetProcessHeap(), 0, shortpathA );
1008
1009     return ret;
1010 }
1011
1012
1013 /***********************************************************************
1014  *           GetLongPathNameA   (KERNEL32.xxx)
1015  */
1016 DWORD WINAPI GetLongPathNameA( LPCSTR shortpath, LPSTR longpath,
1017                                   DWORD longlen )
1018 {
1019     DOS_FULL_NAME full_name;
1020     char *p, *r, *ll, *ss;
1021     
1022     if (!DOSFS_GetFullName( shortpath, TRUE, &full_name )) return 0;
1023     lstrcpynA( longpath, full_name.short_name, longlen );
1024
1025     /* Do some hackery to get the long filename. */
1026
1027     if (longpath) {
1028      ss=longpath+strlen(longpath);
1029      ll=full_name.long_name+strlen(full_name.long_name);
1030      p=NULL;
1031      while (ss>=longpath)
1032      {
1033        /* FIXME: aren't we more paranoid, than needed? */
1034        while ((ss[0]=='\\') && (ss>=longpath)) ss--;
1035        p=ss;
1036        while ((ss[0]!='\\') && (ss>=longpath)) ss--;
1037        if (ss>=longpath) 
1038          {
1039          /* FIXME: aren't we more paranoid, than needed? */
1040          while ((ll[0]=='/') && (ll>=full_name.long_name)) ll--;
1041          while ((ll[0]!='/') && (ll>=full_name.long_name)) ll--;
1042          if (ll<full_name.long_name) 
1043               { 
1044               ERR("Bad longname! (ss=%s ll=%s)\n This should never happen !\n"
1045                   ,ss ,ll ); 
1046               return 0;
1047               }
1048          }
1049      }
1050
1051    /* FIXME: fix for names like "C:\\" (ie. with more '\'s) */
1052       if (p && p[2]) 
1053         {
1054         p+=1;
1055         if ((p-longpath)>0) longlen -= (p-longpath);
1056         lstrcpynA( p, ll , longlen);
1057
1058         /* Now, change all '/' to '\' */
1059         for (r=p; r<(p+longlen); r++ ) 
1060           if (r[0]=='/') r[0]='\\';
1061         return strlen(longpath) - strlen(p) + longlen;
1062         }
1063     }
1064
1065     return strlen(longpath);
1066 }
1067
1068
1069 /***********************************************************************
1070  *           GetLongPathNameW   (KERNEL32.269)
1071  */
1072 DWORD WINAPI GetLongPathNameW( LPCWSTR shortpath, LPWSTR longpath,
1073                                   DWORD longlen )
1074 {
1075     DOS_FULL_NAME full_name;
1076     DWORD ret = 0;
1077     LPSTR shortpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, shortpath );
1078
1079     /* FIXME: is it correct to always return a fully qualified short path? */
1080     if (DOSFS_GetFullName( shortpathA, TRUE, &full_name ))
1081     {
1082         ret = strlen( full_name.short_name );
1083         lstrcpynAtoW( longpath, full_name.long_name, longlen );
1084     }
1085     HeapFree( GetProcessHeap(), 0, shortpathA );
1086     return ret;
1087 }
1088
1089
1090 /***********************************************************************
1091  *           DOSFS_DoGetFullPathName
1092  *
1093  * Implementation of GetFullPathNameA/W.
1094  *
1095  * bon@elektron 000331:
1096  * A test for GetFullPathName with many patholotical case 
1097  * gives now identical output for Wine and OSR2
1098  */
1099 static DWORD DOSFS_DoGetFullPathName( LPCSTR name, DWORD len, LPSTR result,
1100                                       BOOL unicode )
1101 {
1102     DWORD ret;
1103     DOS_FULL_NAME full_name;
1104     char *p,*q;
1105     const char * root;
1106     char drivecur[]="c:.";
1107     char driveletter=0;
1108     int namelen,drive=0;
1109
1110     if ((strlen(name) >1)&& (name[1]==':'))
1111       /*drive letter given */
1112       {
1113         driveletter = name[0];
1114       }
1115     if ((strlen(name) >2)&& (name[1]==':') &&
1116              ((name[2]=='\\') || (name[2]=='/')))
1117       /*absolute path given */
1118       {
1119         lstrcpynA(full_name.short_name,name,MAX_PATHNAME_LEN);
1120         drive = (int)toupper(name[0]) - 'A';
1121       }
1122     else
1123       {
1124         if (driveletter)
1125           drivecur[0]=driveletter;
1126         else
1127           strcpy(drivecur,".");
1128         if (!DOSFS_GetFullName( drivecur, FALSE, &full_name ))
1129           {
1130             FIXME("internal: error getting drive/path\n");
1131             return 0;
1132           }
1133         /* find path that drive letter substitutes*/
1134         drive = (int)toupper(full_name.short_name[0]) -0x41;
1135         root= DRIVE_GetRoot(drive);
1136         if (!root)
1137           {
1138             FIXME("internal: error getting DOS Drive Root\n");
1139             return 0;
1140           }
1141         p= full_name.long_name +strlen(root);
1142         /* append long name (= unix name) to drive */
1143         lstrcpynA(full_name.short_name+2,p,MAX_PATHNAME_LEN-3);
1144         /* append name to treat */
1145         namelen= strlen(full_name.short_name);
1146         p = (char*)name;
1147         if (driveletter)
1148           p += +2; /* skip drive name when appending */
1149         if (namelen +2  + strlen(p) > MAX_PATHNAME_LEN)
1150           {
1151             FIXME("internal error: buffer too small\n");
1152              return 0;
1153           }
1154         full_name.short_name[namelen++] ='\\';
1155         full_name.short_name[namelen] = 0;
1156         lstrcpynA(full_name.short_name +namelen,p,MAX_PATHNAME_LEN-namelen);
1157       }
1158     /* reverse all slashes */
1159     for (p=full_name.short_name;
1160          p < full_name.short_name+strlen(full_name.short_name);
1161          p++)
1162       {
1163         if ( *p == '/' )
1164           *p = '\\';
1165       }
1166      /* Use memmove, as areas overlap*/
1167      /* Delete .. */
1168     while ((p = strstr(full_name.short_name,"\\..\\")))
1169       {
1170         if (p > full_name.short_name+2)
1171           {
1172             *p = 0;
1173             q = strrchr(full_name.short_name,'\\');
1174             memmove(q+1,p+4,strlen(p+4)+1);
1175           }
1176         else
1177           {
1178             memmove(full_name.short_name+3,p+4,strlen(p+4)+1);
1179           }
1180       }
1181     if ((full_name.short_name[2]=='.')&&(full_name.short_name[3]=='.'))
1182         {
1183           /* This case istn't treated yet : c:..\test */
1184           memmove(full_name.short_name+2,full_name.short_name+4,
1185                   strlen(full_name.short_name+4)+1);
1186         }
1187      /* Delete . */
1188     while ((p = strstr(full_name.short_name,"\\.\\")))
1189       {
1190         *(p+1) = 0;
1191         memmove(p+1,p+3,strlen(p+3)+1);
1192       }
1193     if (!(DRIVE_GetFlags(drive) & DRIVE_CASE_PRESERVING))
1194       CharUpperA( full_name.short_name );
1195     namelen=strlen(full_name.short_name);
1196     if (!strcmp(full_name.short_name+namelen-3,"\\.."))
1197         {
1198           /* one more starnge case: "c:\test\test1\.." 
1199            return "c:\test"*/
1200           *(full_name.short_name+namelen-3)=0;
1201           q = strrchr(full_name.short_name,'\\');
1202           *q =0;
1203         }
1204     if (full_name.short_name[namelen-1]=='.')
1205         full_name.short_name[(namelen--)-1] =0;
1206     if (!driveletter)
1207       if (full_name.short_name[namelen-1]=='\\')
1208         full_name.short_name[(namelen--)-1] =0;
1209     TRACE("got %s\n",full_name.short_name);
1210
1211     /* If the lpBuffer buffer is too small, the return value is the 
1212     size of the buffer, in characters, required to hold the path 
1213     plus the terminating \0 (tested against win95osr, bon 001118)
1214     . */
1215     ret = strlen(full_name.short_name);
1216     if (ret >= len )
1217       {
1218         /* don't touch anything when the buffer is not large enough */
1219         SetLastError( ERROR_INSUFFICIENT_BUFFER );
1220         return ret+1;
1221       }
1222     if (result)
1223     {
1224         if (unicode)
1225             lstrcpynAtoW( (LPWSTR)result, full_name.short_name, len );
1226         else
1227             lstrcpynA( result, full_name.short_name, len );
1228     }
1229
1230     TRACE("returning '%s'\n", full_name.short_name );
1231     return ret;
1232 }
1233
1234
1235 /***********************************************************************
1236  *           GetFullPathNameA   (KERNEL32.272)
1237  * NOTES
1238  *   if the path closed with '\', *lastpart is 0 
1239  */
1240 DWORD WINAPI GetFullPathNameA( LPCSTR name, DWORD len, LPSTR buffer,
1241                                  LPSTR *lastpart )
1242 {
1243     DWORD ret = DOSFS_DoGetFullPathName( name, len, buffer, FALSE );
1244     if (ret && (ret<=len) && buffer && lastpart)
1245     {
1246         LPSTR p = buffer + strlen(buffer);
1247
1248         if (*p != '\\')
1249         {
1250           while ((p > buffer + 2) && (*p != '\\')) p--;
1251           *lastpart = p + 1;
1252         }
1253         else *lastpart = NULL;
1254     }
1255     return ret;
1256 }
1257
1258
1259 /***********************************************************************
1260  *           GetFullPathNameW   (KERNEL32.273)
1261  */
1262 DWORD WINAPI GetFullPathNameW( LPCWSTR name, DWORD len, LPWSTR buffer,
1263                                  LPWSTR *lastpart )
1264 {
1265     LPSTR nameA = HEAP_strdupWtoA( GetProcessHeap(), 0, name );
1266     DWORD ret = DOSFS_DoGetFullPathName( nameA, len, (LPSTR)buffer, TRUE );
1267     HeapFree( GetProcessHeap(), 0, nameA );
1268     if (ret && (ret<=len) && buffer && lastpart)
1269     {
1270         LPWSTR p = buffer + lstrlenW(buffer);
1271         if (*p != (WCHAR)'\\')
1272         {
1273             while ((p > buffer + 2) && (*p != (WCHAR)'\\')) p--;
1274             *lastpart = p + 1;
1275         }
1276         else *lastpart = NULL;  
1277     }
1278     return ret;
1279 }
1280
1281 /***********************************************************************
1282  *           DOSFS_FindNextEx
1283  */
1284 static int DOSFS_FindNextEx( FIND_FIRST_INFO *info, WIN32_FIND_DATAA *entry )
1285 {
1286     BYTE attr = info->attr | FA_UNUSED | FA_ARCHIVE | FA_RDONLY;
1287     UINT flags = DRIVE_GetFlags( info->drive );
1288     char *p, buffer[MAX_PATHNAME_LEN];
1289     const char *drive_path;
1290     int drive_root;
1291     LPCSTR long_name, short_name;
1292     BY_HANDLE_FILE_INFORMATION fileinfo;
1293     char dos_name[13];
1294
1295     if ((info->attr & ~(FA_UNUSED | FA_ARCHIVE | FA_RDONLY)) == FA_LABEL)
1296     {
1297         if (info->cur_pos) return 0;
1298         entry->dwFileAttributes  = FILE_ATTRIBUTE_LABEL;
1299         DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftCreationTime, 0 );
1300         DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftLastAccessTime, 0 );
1301         DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftLastWriteTime, 0 );
1302         entry->nFileSizeHigh     = 0;
1303         entry->nFileSizeLow      = 0;
1304         entry->dwReserved0       = 0;
1305         entry->dwReserved1       = 0;
1306         DOSFS_ToDosDTAFormat( DRIVE_GetLabel( info->drive ), entry->cFileName );
1307         strcpy( entry->cAlternateFileName, entry->cFileName ); 
1308         info->cur_pos++;
1309         return 1;
1310     }
1311
1312     drive_path = info->path + strlen(DRIVE_GetRoot( info->drive ));
1313     while ((*drive_path == '/') || (*drive_path == '\\')) drive_path++;
1314     drive_root = !*drive_path;
1315
1316     lstrcpynA( buffer, info->path, sizeof(buffer) - 1 );
1317     strcat( buffer, "/" );
1318     p = buffer + strlen(buffer);
1319
1320     while (DOSFS_ReadDir( info->dir, &long_name, &short_name ))
1321     {
1322         info->cur_pos++;
1323
1324         /* Don't return '.' and '..' in the root of the drive */
1325         if (drive_root && (long_name[0] == '.') &&
1326             (!long_name[1] || ((long_name[1] == '.') && !long_name[2])))
1327             continue;
1328
1329         /* Check the long mask */
1330
1331         if (info->long_mask)
1332         {
1333             if (!DOSFS_MatchLong( info->long_mask, long_name,
1334                                   flags & DRIVE_CASE_SENSITIVE )) continue;
1335         }
1336
1337         /* Check the short mask */
1338
1339         if (info->short_mask)
1340         {
1341             if (!short_name)
1342             {
1343                 DOSFS_Hash( long_name, dos_name, TRUE,
1344                             !(flags & DRIVE_CASE_SENSITIVE) );
1345                 short_name = dos_name;
1346             }
1347             if (!DOSFS_MatchShort( info->short_mask, short_name )) continue;
1348         }
1349
1350         /* Check the file attributes */
1351
1352         lstrcpynA( p, long_name, sizeof(buffer) - (int)(p - buffer) );
1353         if (!FILE_Stat( buffer, &fileinfo ))
1354         {
1355             WARN("can't stat %s\n", buffer);
1356             continue;
1357         }
1358         if (fileinfo.dwFileAttributes & ~attr) continue;
1359
1360         /* We now have a matching entry; fill the result and return */
1361
1362         entry->dwFileAttributes = fileinfo.dwFileAttributes;
1363         entry->ftCreationTime   = fileinfo.ftCreationTime;
1364         entry->ftLastAccessTime = fileinfo.ftLastAccessTime;
1365         entry->ftLastWriteTime  = fileinfo.ftLastWriteTime;
1366         entry->nFileSizeHigh    = fileinfo.nFileSizeHigh;
1367         entry->nFileSizeLow     = fileinfo.nFileSizeLow;
1368
1369         if (short_name)
1370             DOSFS_ToDosDTAFormat( short_name, entry->cAlternateFileName );
1371         else
1372             DOSFS_Hash( long_name, entry->cAlternateFileName, FALSE,
1373                         !(flags & DRIVE_CASE_SENSITIVE) );
1374
1375         lstrcpynA( entry->cFileName, long_name, sizeof(entry->cFileName) );
1376         if (!(flags & DRIVE_CASE_PRESERVING)) CharLowerA( entry->cFileName );
1377         TRACE("returning %s (%s) %02lx %ld\n",
1378               entry->cFileName, entry->cAlternateFileName,
1379               entry->dwFileAttributes, entry->nFileSizeLow );
1380         return 1;
1381     }
1382     return 0;  /* End of directory */
1383 }
1384
1385 /***********************************************************************
1386  *           DOSFS_FindNext
1387  *
1388  * Find the next matching file. Return the number of entries read to find
1389  * the matching one, or 0 if no more entries.
1390  * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
1391  * file name mask. Either or both can be NULL.
1392  *
1393  * NOTE: This is supposed to be only called by the int21 emulation
1394  *       routines. Thus, we should own the Win16Mutex anyway.
1395  *       Nevertheless, we explicitly enter it to ensure the static
1396  *       directory cache is protected.
1397  */
1398 int DOSFS_FindNext( const char *path, const char *short_mask,
1399                     const char *long_mask, int drive, BYTE attr,
1400                     int skip, WIN32_FIND_DATAA *entry )
1401 {
1402     static FIND_FIRST_INFO info = { NULL };
1403     LPCSTR short_name, long_name;
1404     int count;
1405
1406     SYSLEVEL_EnterWin16Lock();
1407
1408     /* Check the cached directory */
1409     if (!(info.dir && info.path == path && info.short_mask == short_mask
1410                    && info.long_mask == long_mask && info.drive == drive
1411                    && info.attr == attr && info.cur_pos <= skip))
1412     {  
1413         /* Not in the cache, open it anew */
1414         if (info.dir) DOSFS_CloseDir( info.dir );
1415
1416         info.path = (LPSTR)path;
1417         info.long_mask = (LPSTR)long_mask;
1418         info.short_mask = (LPSTR)short_mask;
1419         info.attr = attr;
1420         info.drive = drive;
1421         info.cur_pos = 0;
1422         info.dir = DOSFS_OpenDir( info.path );
1423     }
1424
1425     /* Skip to desired position */
1426     while (info.cur_pos < skip)
1427         if (info.dir && DOSFS_ReadDir( info.dir, &long_name, &short_name ))
1428             info.cur_pos++;
1429         else
1430             break;
1431
1432     if (info.dir && info.cur_pos == skip && DOSFS_FindNextEx( &info, entry ))
1433         count = info.cur_pos - skip;
1434     else
1435         count = 0;
1436
1437     if (!count)
1438     {
1439         if (info.dir) DOSFS_CloseDir( info.dir );
1440         memset( &info, '\0', sizeof(info) );
1441     }
1442
1443     SYSLEVEL_LeaveWin16Lock();
1444
1445     return count;
1446 }
1447
1448 /*************************************************************************
1449  *           FindFirstFileExA  (KERNEL32)
1450  */
1451 HANDLE WINAPI FindFirstFileExA(
1452         LPCSTR lpFileName,
1453         FINDEX_INFO_LEVELS fInfoLevelId,
1454         LPVOID lpFindFileData,
1455         FINDEX_SEARCH_OPS fSearchOp,
1456         LPVOID lpSearchFilter,
1457         DWORD dwAdditionalFlags)
1458 {
1459     DOS_FULL_NAME full_name;
1460     HGLOBAL handle;
1461     FIND_FIRST_INFO *info;
1462     
1463     if ((fSearchOp != FindExSearchNameMatch) || (dwAdditionalFlags != 0))
1464     {
1465         FIXME("options not implemented 0x%08x 0x%08lx\n", fSearchOp, dwAdditionalFlags );
1466         return INVALID_HANDLE_VALUE;
1467     }
1468
1469     switch(fInfoLevelId)
1470     {
1471       case FindExInfoStandard:
1472         {
1473           WIN32_FIND_DATAA * data = (WIN32_FIND_DATAA *) lpFindFileData;
1474           data->dwReserved0 = data->dwReserved1 = 0x0;
1475           if (!lpFileName) return 0;
1476           if (!DOSFS_GetFullName( lpFileName, FALSE, &full_name )) break;
1477           if (!(handle = GlobalAlloc(GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO)))) break;
1478           info = (FIND_FIRST_INFO *)GlobalLock( handle );
1479           info->path = HEAP_strdupA( GetProcessHeap(), 0, full_name.long_name );
1480           info->long_mask = strrchr( info->path, '/' );
1481           *(info->long_mask++) = '\0';
1482           info->short_mask = NULL;
1483           info->attr = 0xff;
1484           if (lpFileName[0] && (lpFileName[1] == ':'))
1485               info->drive = toupper(*lpFileName) - 'A';
1486           else info->drive = DRIVE_GetCurrentDrive();
1487           info->cur_pos = 0;
1488
1489           info->dir = DOSFS_OpenDir( info->path );
1490
1491           GlobalUnlock( handle );
1492           if (!FindNextFileA( handle, data ))
1493           {
1494               FindClose( handle );
1495               SetLastError( ERROR_NO_MORE_FILES );
1496               break;
1497           }
1498           return handle;
1499         }
1500         break;
1501       default:
1502         FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
1503     }
1504     return INVALID_HANDLE_VALUE;
1505 }
1506
1507 /*************************************************************************
1508  *           FindFirstFileA   (KERNEL32.123)
1509  */
1510 HANDLE WINAPI FindFirstFileA(
1511         LPCSTR lpFileName,
1512         WIN32_FIND_DATAA *lpFindData )
1513 {
1514     return FindFirstFileExA(lpFileName, FindExInfoStandard, lpFindData,
1515                             FindExSearchNameMatch, NULL, 0);
1516 }
1517
1518 /*************************************************************************
1519  *           FindFirstFileExW   (KERNEL32)
1520  */
1521 HANDLE WINAPI FindFirstFileExW(
1522         LPCWSTR lpFileName,
1523         FINDEX_INFO_LEVELS fInfoLevelId,
1524         LPVOID lpFindFileData,
1525         FINDEX_SEARCH_OPS fSearchOp,
1526         LPVOID lpSearchFilter,
1527         DWORD dwAdditionalFlags)
1528 {
1529     HANDLE handle;
1530     WIN32_FIND_DATAA dataA;
1531     LPVOID _lpFindFileData;
1532     LPSTR pathA;
1533
1534     switch(fInfoLevelId)
1535     {
1536       case FindExInfoStandard:
1537         {
1538           _lpFindFileData = &dataA;
1539         }
1540         break;
1541       default:
1542         FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
1543         return INVALID_HANDLE_VALUE;
1544     }
1545
1546     pathA = HEAP_strdupWtoA( GetProcessHeap(), 0, lpFileName );
1547     handle = FindFirstFileExA(pathA, fInfoLevelId, _lpFindFileData, fSearchOp, lpSearchFilter, dwAdditionalFlags);
1548     HeapFree( GetProcessHeap(), 0, pathA );
1549     if (handle == INVALID_HANDLE_VALUE) return handle;
1550     
1551     switch(fInfoLevelId)
1552     {
1553       case FindExInfoStandard:
1554         {
1555           WIN32_FIND_DATAW *dataW = (WIN32_FIND_DATAW*) lpFindFileData;
1556           dataW->dwFileAttributes = dataA.dwFileAttributes;
1557           dataW->ftCreationTime   = dataA.ftCreationTime;
1558           dataW->ftLastAccessTime = dataA.ftLastAccessTime;
1559           dataW->ftLastWriteTime  = dataA.ftLastWriteTime;
1560           dataW->nFileSizeHigh    = dataA.nFileSizeHigh;
1561           dataW->nFileSizeLow     = dataA.nFileSizeLow;
1562           lstrcpyAtoW( dataW->cFileName, dataA.cFileName );
1563           lstrcpyAtoW( dataW->cAlternateFileName, dataA.cAlternateFileName );
1564         }
1565         break;
1566       default:
1567         FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
1568         return INVALID_HANDLE_VALUE;
1569     }
1570     return handle;
1571 }
1572
1573 /*************************************************************************
1574  *           FindFirstFileW   (KERNEL32.124)
1575  */
1576 HANDLE WINAPI FindFirstFileW( LPCWSTR lpFileName, WIN32_FIND_DATAW *lpFindData )
1577 {
1578     return FindFirstFileExW(lpFileName, FindExInfoStandard, lpFindData,
1579                             FindExSearchNameMatch, NULL, 0);
1580 }
1581
1582 /*************************************************************************
1583  *           FindNextFileA   (KERNEL32.126)
1584  */
1585 BOOL WINAPI FindNextFileA( HANDLE handle, WIN32_FIND_DATAA *data )
1586 {
1587     FIND_FIRST_INFO *info;
1588
1589     if ((handle == INVALID_HANDLE_VALUE) || 
1590        !(info = (FIND_FIRST_INFO *)GlobalLock( handle )))
1591     {
1592         SetLastError( ERROR_INVALID_HANDLE );
1593         return FALSE;
1594     }
1595     GlobalUnlock( handle );
1596     if (!info->path || !info->dir)
1597     {
1598         SetLastError( ERROR_NO_MORE_FILES );
1599         return FALSE;
1600     }
1601     if (!DOSFS_FindNextEx( info, data ))
1602     {
1603         DOSFS_CloseDir( info->dir ); info->dir = NULL;
1604         HeapFree( GetProcessHeap(), 0, info->path );
1605         info->path = info->long_mask = NULL;
1606         SetLastError( ERROR_NO_MORE_FILES );
1607         return FALSE;
1608     }
1609     return TRUE;
1610 }
1611
1612
1613 /*************************************************************************
1614  *           FindNextFileW   (KERNEL32.127)
1615  */
1616 BOOL WINAPI FindNextFileW( HANDLE handle, WIN32_FIND_DATAW *data )
1617 {
1618     WIN32_FIND_DATAA dataA;
1619     if (!FindNextFileA( handle, &dataA )) return FALSE;
1620     data->dwFileAttributes = dataA.dwFileAttributes;
1621     data->ftCreationTime   = dataA.ftCreationTime;
1622     data->ftLastAccessTime = dataA.ftLastAccessTime;
1623     data->ftLastWriteTime  = dataA.ftLastWriteTime;
1624     data->nFileSizeHigh    = dataA.nFileSizeHigh;
1625     data->nFileSizeLow     = dataA.nFileSizeLow;
1626     lstrcpyAtoW( data->cFileName, dataA.cFileName );
1627     lstrcpyAtoW( data->cAlternateFileName, dataA.cAlternateFileName );
1628     return TRUE;
1629 }
1630
1631 /*************************************************************************
1632  *           FindClose   (KERNEL32.119)
1633  */
1634 BOOL WINAPI FindClose( HANDLE handle )
1635 {
1636     FIND_FIRST_INFO *info;
1637
1638     if ((handle == INVALID_HANDLE_VALUE) ||
1639         !(info = (FIND_FIRST_INFO *)GlobalLock( handle )))
1640     {
1641         SetLastError( ERROR_INVALID_HANDLE );
1642         return FALSE;
1643     }
1644     if (info->dir) DOSFS_CloseDir( info->dir );
1645     if (info->path) HeapFree( GetProcessHeap(), 0, info->path );
1646     GlobalUnlock( handle );
1647     GlobalFree( handle );
1648     return TRUE;
1649 }
1650
1651 /***********************************************************************
1652  *           DOSFS_UnixTimeToFileTime
1653  *
1654  * Convert a Unix time to FILETIME format.
1655  * The FILETIME structure is a 64-bit value representing the number of
1656  * 100-nanosecond intervals since January 1, 1601, 0:00.
1657  * 'remainder' is the nonnegative number of 100-ns intervals
1658  * corresponding to the time fraction smaller than 1 second that
1659  * couldn't be stored in the time_t value.
1660  */
1661 void DOSFS_UnixTimeToFileTime( time_t unix_time, FILETIME *filetime,
1662                                DWORD remainder )
1663 {
1664     /* NOTES:
1665
1666        CONSTANTS: 
1667        The time difference between 1 January 1601, 00:00:00 and
1668        1 January 1970, 00:00:00 is 369 years, plus the leap years
1669        from 1604 to 1968, excluding 1700, 1800, 1900.
1670        This makes (1968 - 1600) / 4 - 3 = 89 leap days, and a total
1671        of 134774 days.
1672
1673        Any day in that period had 24 * 60 * 60 = 86400 seconds.
1674
1675        The time difference is 134774 * 86400 * 10000000, which can be written
1676        116444736000000000
1677        27111902 * 2^32 + 3577643008
1678        413 * 2^48 + 45534 * 2^32 + 54590 * 2^16 + 32768
1679
1680        If you find that these constants are buggy, please change them in all
1681        instances in both conversion functions.
1682
1683        VERSIONS:
1684        There are two versions, one of them uses long long variables and
1685        is presumably faster but not ISO C. The other one uses standard C
1686        data types and operations but relies on the assumption that negative
1687        numbers are stored as 2's complement (-1 is 0xffff....). If this
1688        assumption is violated, dates before 1970 will not convert correctly.
1689        This should however work on any reasonable architecture where WINE
1690        will run.
1691
1692        DETAILS:
1693        
1694        Take care not to remove the casts. I have tested these functions
1695        (in both versions) for a lot of numbers. I would be interested in
1696        results on other compilers than GCC.
1697
1698        The operations have been designed to account for the possibility
1699        of 64-bit time_t in future UNICES. Even the versions without
1700        internal long long numbers will work if time_t only is 64 bit.
1701        A 32-bit shift, which was necessary for that operation, turned out
1702        not to work correctly in GCC, besides giving the warning. So I
1703        used a double 16-bit shift instead. Numbers are in the ISO version
1704        represented by three limbs, the most significant with 32 bit, the
1705        other two with 16 bit each.
1706
1707        As the modulo-operator % is not well-defined for negative numbers,
1708        negative divisors have been avoided in DOSFS_FileTimeToUnixTime.
1709
1710        There might be quicker ways to do this in C. Certainly so in
1711        assembler.
1712
1713        Claus Fischer, fischer@iue.tuwien.ac.at
1714        */
1715
1716 #if SIZEOF_LONG_LONG >= 8
1717 #  define USE_LONG_LONG 1
1718 #else
1719 #  define USE_LONG_LONG 0
1720 #endif
1721
1722 #if USE_LONG_LONG               /* gcc supports long long type */
1723
1724     long long int t = unix_time;
1725     t *= 10000000;
1726     t += 116444736000000000LL;
1727     t += remainder;
1728     filetime->dwLowDateTime  = (UINT)t;
1729     filetime->dwHighDateTime = (UINT)(t >> 32);
1730
1731 #else  /* ISO version */
1732
1733     UINT a0;                    /* 16 bit, low    bits */
1734     UINT a1;                    /* 16 bit, medium bits */
1735     UINT a2;                    /* 32 bit, high   bits */
1736
1737     /* Copy the unix time to a2/a1/a0 */
1738     a0 =  unix_time & 0xffff;
1739     a1 = (unix_time >> 16) & 0xffff;
1740     /* This is obsolete if unix_time is only 32 bits, but it does not hurt.
1741        Do not replace this by >> 32, it gives a compiler warning and it does
1742        not work. */
1743     a2 = (unix_time >= 0 ? (unix_time >> 16) >> 16 :
1744           ~((~unix_time >> 16) >> 16));
1745
1746     /* Multiply a by 10000000 (a = a2/a1/a0)
1747        Split the factor into 10000 * 1000 which are both less than 0xffff. */
1748     a0 *= 10000;
1749     a1 = a1 * 10000 + (a0 >> 16);
1750     a2 = a2 * 10000 + (a1 >> 16);
1751     a0 &= 0xffff;
1752     a1 &= 0xffff;
1753
1754     a0 *= 1000;
1755     a1 = a1 * 1000 + (a0 >> 16);
1756     a2 = a2 * 1000 + (a1 >> 16);
1757     a0 &= 0xffff;
1758     a1 &= 0xffff;
1759
1760     /* Add the time difference and the remainder */
1761     a0 += 32768 + (remainder & 0xffff);
1762     a1 += 54590 + (remainder >> 16   ) + (a0 >> 16);
1763     a2 += 27111902                     + (a1 >> 16);
1764     a0 &= 0xffff;
1765     a1 &= 0xffff;
1766
1767     /* Set filetime */
1768     filetime->dwLowDateTime  = (a1 << 16) + a0;
1769     filetime->dwHighDateTime = a2;
1770 #endif
1771 }
1772
1773
1774 /***********************************************************************
1775  *           DOSFS_FileTimeToUnixTime
1776  *
1777  * Convert a FILETIME format to Unix time.
1778  * If not NULL, 'remainder' contains the fractional part of the filetime,
1779  * in the range of [0..9999999] (even if time_t is negative).
1780  */
1781 time_t DOSFS_FileTimeToUnixTime( const FILETIME *filetime, DWORD *remainder )
1782 {
1783     /* Read the comment in the function DOSFS_UnixTimeToFileTime. */
1784 #if USE_LONG_LONG
1785
1786     long long int t = filetime->dwHighDateTime;
1787     t <<= 32;
1788     t += (UINT)filetime->dwLowDateTime;
1789     t -= 116444736000000000LL;
1790     if (t < 0)
1791     {
1792         if (remainder) *remainder = 9999999 - (-t - 1) % 10000000;
1793         return -1 - ((-t - 1) / 10000000);
1794     }
1795     else
1796     {
1797         if (remainder) *remainder = t % 10000000;
1798         return t / 10000000;
1799     }
1800
1801 #else  /* ISO version */
1802
1803     UINT a0;                    /* 16 bit, low    bits */
1804     UINT a1;                    /* 16 bit, medium bits */
1805     UINT a2;                    /* 32 bit, high   bits */
1806     UINT r;                     /* remainder of division */
1807     unsigned int carry;         /* carry bit for subtraction */
1808     int negative;               /* whether a represents a negative value */
1809
1810     /* Copy the time values to a2/a1/a0 */
1811     a2 =  (UINT)filetime->dwHighDateTime;
1812     a1 = ((UINT)filetime->dwLowDateTime ) >> 16;
1813     a0 = ((UINT)filetime->dwLowDateTime ) & 0xffff;
1814
1815     /* Subtract the time difference */
1816     if (a0 >= 32768           ) a0 -=             32768        , carry = 0;
1817     else                        a0 += (1 << 16) - 32768        , carry = 1;
1818
1819     if (a1 >= 54590    + carry) a1 -=             54590 + carry, carry = 0;
1820     else                        a1 += (1 << 16) - 54590 - carry, carry = 1;
1821
1822     a2 -= 27111902 + carry;
1823     
1824     /* If a is negative, replace a by (-1-a) */
1825     negative = (a2 >= ((UINT)1) << 31);
1826     if (negative)
1827     {
1828         /* Set a to -a - 1 (a is a2/a1/a0) */
1829         a0 = 0xffff - a0;
1830         a1 = 0xffff - a1;
1831         a2 = ~a2;
1832     }
1833
1834     /* Divide a by 10000000 (a = a2/a1/a0), put the rest into r.
1835        Split the divisor into 10000 * 1000 which are both less than 0xffff. */
1836     a1 += (a2 % 10000) << 16;
1837     a2 /=       10000;
1838     a0 += (a1 % 10000) << 16;
1839     a1 /=       10000;
1840     r   =  a0 % 10000;
1841     a0 /=       10000;
1842
1843     a1 += (a2 % 1000) << 16;
1844     a2 /=       1000;
1845     a0 += (a1 % 1000) << 16;
1846     a1 /=       1000;
1847     r  += (a0 % 1000) * 10000;
1848     a0 /=       1000;
1849
1850     /* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
1851     if (negative)
1852     {
1853         /* Set a to -a - 1 (a is a2/a1/a0) */
1854         a0 = 0xffff - a0;
1855         a1 = 0xffff - a1;
1856         a2 = ~a2;
1857
1858         r  = 9999999 - r;
1859     }
1860
1861     if (remainder) *remainder = r;
1862
1863     /* Do not replace this by << 32, it gives a compiler warning and it does
1864        not work. */
1865     return ((((time_t)a2) << 16) << 16) + (a1 << 16) + a0;
1866 #endif
1867 }
1868
1869
1870 /***********************************************************************
1871  *           DosDateTimeToFileTime   (KERNEL32.76)
1872  */
1873 BOOL WINAPI DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft)
1874 {
1875     struct tm newtm;
1876
1877     newtm.tm_sec  = (fattime & 0x1f) * 2;
1878     newtm.tm_min  = (fattime >> 5) & 0x3f;
1879     newtm.tm_hour = (fattime >> 11);
1880     newtm.tm_mday = (fatdate & 0x1f);
1881     newtm.tm_mon  = ((fatdate >> 5) & 0x0f) - 1;
1882     newtm.tm_year = (fatdate >> 9) + 80;
1883     DOSFS_UnixTimeToFileTime( mktime( &newtm ), ft, 0 );
1884     return TRUE;
1885 }
1886
1887
1888 /***********************************************************************
1889  *           FileTimeToDosDateTime   (KERNEL32.111)
1890  */
1891 BOOL WINAPI FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate,
1892                                      LPWORD fattime )
1893 {
1894     time_t unixtime = DOSFS_FileTimeToUnixTime( ft, NULL );
1895     struct tm *tm = localtime( &unixtime );
1896     if (fattime)
1897         *fattime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
1898     if (fatdate)
1899         *fatdate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
1900                    + tm->tm_mday;
1901     return TRUE;
1902 }
1903
1904
1905 /***********************************************************************
1906  *           LocalFileTimeToFileTime   (KERNEL32.373)
1907  */
1908 BOOL WINAPI LocalFileTimeToFileTime( const FILETIME *localft,
1909                                        LPFILETIME utcft )
1910 {
1911     struct tm *xtm;
1912     DWORD remainder;
1913
1914     /* convert from local to UTC. Perhaps not correct. FIXME */
1915     time_t unixtime = DOSFS_FileTimeToUnixTime( localft, &remainder );
1916     xtm = gmtime( &unixtime );
1917     DOSFS_UnixTimeToFileTime( mktime(xtm), utcft, remainder );
1918     return TRUE; 
1919 }
1920
1921
1922 /***********************************************************************
1923  *           FileTimeToLocalFileTime   (KERNEL32.112)
1924  */
1925 BOOL WINAPI FileTimeToLocalFileTime( const FILETIME *utcft,
1926                                        LPFILETIME localft )
1927 {
1928     DWORD remainder;
1929     /* convert from UTC to local. Perhaps not correct. FIXME */
1930     time_t unixtime = DOSFS_FileTimeToUnixTime( utcft, &remainder );
1931 #ifdef HAVE_TIMEGM
1932     struct tm *xtm = localtime( &unixtime );
1933     time_t localtime;
1934
1935     localtime = timegm(xtm);
1936     DOSFS_UnixTimeToFileTime( localtime, localft, remainder );
1937
1938 #else
1939     struct tm *xtm,*gtm;
1940     time_t time1,time2;
1941
1942     xtm = localtime( &unixtime );
1943     gtm = gmtime( &unixtime );
1944     time1 = mktime(xtm);
1945     time2 = mktime(gtm);
1946     DOSFS_UnixTimeToFileTime( 2*time1-time2, localft, remainder );
1947 #endif
1948     return TRUE; 
1949 }
1950
1951
1952 /***********************************************************************
1953  *           FileTimeToSystemTime   (KERNEL32.113)
1954  */
1955 BOOL WINAPI FileTimeToSystemTime( const FILETIME *ft, LPSYSTEMTIME syst )
1956 {
1957     struct tm *xtm;
1958     DWORD remainder;
1959     time_t xtime = DOSFS_FileTimeToUnixTime( ft, &remainder );
1960     xtm = gmtime(&xtime);
1961     syst->wYear         = xtm->tm_year+1900;
1962     syst->wMonth        = xtm->tm_mon + 1;
1963     syst->wDayOfWeek    = xtm->tm_wday;
1964     syst->wDay          = xtm->tm_mday;
1965     syst->wHour         = xtm->tm_hour;
1966     syst->wMinute       = xtm->tm_min;
1967     syst->wSecond       = xtm->tm_sec;
1968     syst->wMilliseconds = remainder / 10000;
1969     return TRUE; 
1970 }
1971
1972 /***********************************************************************
1973  *           QueryDosDeviceA   (KERNEL32.413)
1974  *
1975  * returns array of strings terminated by \0, terminated by \0
1976  */
1977 DWORD WINAPI QueryDosDeviceA(LPCSTR devname,LPSTR target,DWORD bufsize)
1978 {
1979     LPSTR s;
1980     char  buffer[200];
1981
1982     TRACE("(%s,...)\n", devname ? devname : "<null>");
1983     if (!devname) {
1984         /* return known MSDOS devices */
1985         strcpy(buffer,"CON COM1 COM2 LPT1 NUL ");
1986         while ((s=strchr(buffer,' ')))
1987                 *s='\0';
1988
1989         lstrcpynA(target,buffer,bufsize);
1990         return strlen(buffer);
1991     }
1992     strcpy(buffer,"\\DEV\\");
1993     strcat(buffer,devname);
1994     if ((s=strchr(buffer,':'))) *s='\0';
1995     lstrcpynA(target,buffer,bufsize);
1996     return strlen(buffer);
1997 }
1998
1999
2000 /***********************************************************************
2001  *           QueryDosDeviceW   (KERNEL32.414)
2002  *
2003  * returns array of strings terminated by \0, terminated by \0
2004  */
2005 DWORD WINAPI QueryDosDeviceW(LPCWSTR devname,LPWSTR target,DWORD bufsize)
2006 {
2007     LPSTR devnameA = devname?HEAP_strdupWtoA(GetProcessHeap(),0,devname):NULL;
2008     LPSTR targetA = (LPSTR)HeapAlloc(GetProcessHeap(),0,bufsize);
2009     DWORD ret = QueryDosDeviceA(devnameA,targetA,bufsize);
2010
2011     lstrcpynAtoW(target,targetA,bufsize);
2012     if (devnameA) HeapFree(GetProcessHeap(),0,devnameA);
2013     if (targetA) HeapFree(GetProcessHeap(),0,targetA);
2014     return ret;
2015 }
2016
2017
2018 /***********************************************************************
2019  *           SystemTimeToFileTime   (KERNEL32.526)
2020  */
2021 BOOL WINAPI SystemTimeToFileTime( const SYSTEMTIME *syst, LPFILETIME ft )
2022 {
2023 #ifdef HAVE_TIMEGM
2024     struct tm xtm;
2025     time_t utctime;
2026 #else
2027     struct tm xtm,*local_tm,*utc_tm;
2028     time_t localtim,utctime;
2029 #endif
2030
2031     xtm.tm_year = syst->wYear-1900;
2032     xtm.tm_mon  = syst->wMonth - 1;
2033     xtm.tm_wday = syst->wDayOfWeek;
2034     xtm.tm_mday = syst->wDay;
2035     xtm.tm_hour = syst->wHour;
2036     xtm.tm_min  = syst->wMinute;
2037     xtm.tm_sec  = syst->wSecond; /* this is UTC */
2038     xtm.tm_isdst = -1;
2039 #ifdef HAVE_TIMEGM
2040     utctime = timegm(&xtm);
2041     DOSFS_UnixTimeToFileTime( utctime, ft, 
2042                               syst->wMilliseconds * 10000 );
2043 #else
2044     localtim = mktime(&xtm);    /* now we've got local time */
2045     local_tm = localtime(&localtim);
2046     utc_tm = gmtime(&localtim);
2047     utctime = mktime(utc_tm);
2048     DOSFS_UnixTimeToFileTime( 2*localtim -utctime, ft, 
2049                               syst->wMilliseconds * 10000 );
2050 #endif
2051     return TRUE; 
2052 }
2053
2054 /***********************************************************************
2055  *           DefineDosDeviceA       (KERNEL32.182)
2056  */
2057 BOOL WINAPI DefineDosDeviceA(DWORD flags,LPCSTR devname,LPCSTR targetpath) {
2058         FIXME("(0x%08lx,%s,%s),stub!\n",flags,devname,targetpath);
2059         SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2060         return FALSE;
2061 }
2062
2063 /*
2064    --- 16 bit functions ---
2065 */
2066
2067 /*************************************************************************
2068  *           FindFirstFile16   (KERNEL.413)
2069  */
2070 HANDLE16 WINAPI FindFirstFile16( LPCSTR path, WIN32_FIND_DATAA *data )
2071 {
2072     DOS_FULL_NAME full_name;
2073     HGLOBAL16 handle;
2074     FIND_FIRST_INFO *info;
2075
2076     data->dwReserved0 = data->dwReserved1 = 0x0;
2077     if (!path) return 0;
2078     if (!DOSFS_GetFullName( path, FALSE, &full_name ))
2079         return INVALID_HANDLE_VALUE16;
2080     if (!(handle = GlobalAlloc16( GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO) )))
2081         return INVALID_HANDLE_VALUE16;
2082     info = (FIND_FIRST_INFO *)GlobalLock16( handle );
2083     info->path = HEAP_strdupA( SystemHeap, 0, full_name.long_name );
2084     info->long_mask = strrchr( info->path, '/' );
2085     if (info->long_mask )
2086         *(info->long_mask++) = '\0';
2087     info->short_mask = NULL;
2088     info->attr = 0xff;
2089     if (path[0] && (path[1] == ':')) info->drive = toupper(*path) - 'A';
2090     else info->drive = DRIVE_GetCurrentDrive();
2091     info->cur_pos = 0;
2092
2093     info->dir = DOSFS_OpenDir( info->path );
2094
2095     GlobalUnlock16( handle );
2096     if (!FindNextFile16( handle, data ))
2097     {
2098         FindClose16( handle );
2099         SetLastError( ERROR_NO_MORE_FILES );
2100         return INVALID_HANDLE_VALUE16;
2101     }
2102     return handle;
2103 }
2104
2105 /*************************************************************************
2106  *           FindNextFile16   (KERNEL.414)
2107  */
2108 BOOL16 WINAPI FindNextFile16( HANDLE16 handle, WIN32_FIND_DATAA *data )
2109 {
2110     FIND_FIRST_INFO *info;
2111
2112     if ((handle == INVALID_HANDLE_VALUE16) ||
2113        !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
2114     {
2115         SetLastError( ERROR_INVALID_HANDLE );
2116         return FALSE;
2117     }
2118     GlobalUnlock16( handle );
2119     if (!info->path || !info->dir)
2120     {
2121         SetLastError( ERROR_NO_MORE_FILES );
2122         return FALSE;
2123     }
2124     if (!DOSFS_FindNextEx( info, data ))
2125     {
2126         DOSFS_CloseDir( info->dir ); info->dir = NULL;
2127         HeapFree( SystemHeap, 0, info->path );
2128         info->path = info->long_mask = NULL;
2129         SetLastError( ERROR_NO_MORE_FILES );
2130         return FALSE;
2131     }
2132     return TRUE;
2133 }
2134
2135 /*************************************************************************
2136  *           FindClose16   (KERNEL.415)
2137  */
2138 BOOL16 WINAPI FindClose16( HANDLE16 handle )
2139 {
2140     FIND_FIRST_INFO *info;
2141
2142     if ((handle == INVALID_HANDLE_VALUE16) ||
2143         !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
2144     {
2145         SetLastError( ERROR_INVALID_HANDLE );
2146         return FALSE;
2147     }
2148     if (info->dir) DOSFS_CloseDir( info->dir );
2149     if (info->path) HeapFree( SystemHeap, 0, info->path );
2150     GlobalUnlock16( handle );
2151     GlobalFree16( handle );
2152     return TRUE;
2153 }
2154