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