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