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