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