- fix wrong hexadecimal GetLastError() output
[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 };
83
84 #define GET_DRIVE(path) \
85     (((path)[1] == ':') ? FILE_toupper((path)[0]) - 'A' : DOSFS_CurDrive)
86
87 /* Directory info for DOSFS_ReadDir */
88 typedef struct
89 {
90     DIR           *dir;
91 #ifdef VFAT_IOCTL_READDIR_BOTH
92     int            fd;
93     char           short_name[12];
94     KERNEL_DIRENT  dirent[2];
95 #endif
96 } DOS_DIR;
97
98 /* Info structure for FindFirstFile handle */
99 typedef struct
100 {
101     LPSTR path;
102     LPSTR long_mask;
103     LPSTR short_mask;
104     BYTE  attr;
105     int   drive;
106     int   cur_pos;
107     DOS_DIR *dir;
108 } FIND_FIRST_INFO;
109
110
111 static WINE_EXCEPTION_FILTER(page_fault)
112 {
113     if (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION)
114         return EXCEPTION_EXECUTE_HANDLER;
115     return EXCEPTION_CONTINUE_SEARCH;
116 }
117
118
119 /***********************************************************************
120  *           DOSFS_ValidDOSName
121  *
122  * Return 1 if Unix file 'name' is also a valid MS-DOS name
123  * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
124  * File name can be terminated by '\0', '\\' or '/'.
125  */
126 static int DOSFS_ValidDOSName( const char *name, int ignore_case )
127 {
128     static const char invalid_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS;
129     const char *p = name;
130     const char *invalid = ignore_case ? (invalid_chars + 26) : invalid_chars;
131     int len = 0;
132
133     if (*p == '.')
134     {
135         /* Check for "." and ".." */
136         p++;
137         if (*p == '.') p++;
138         /* All other names beginning with '.' are invalid */
139         return (IS_END_OF_NAME(*p));
140     }
141     while (!IS_END_OF_NAME(*p))
142     {
143         if (strchr( invalid, *p )) return 0;  /* Invalid char */
144         if (*p == '.') break;  /* Start of the extension */
145         if (++len > 8) return 0;  /* Name too long */
146         p++;
147     }
148     if (*p != '.') return 1;  /* End of name */
149     p++;
150     if (IS_END_OF_NAME(*p)) return 0;  /* Empty extension not allowed */
151     len = 0;
152     while (!IS_END_OF_NAME(*p))
153     {
154         if (strchr( invalid, *p )) return 0;  /* Invalid char */
155         if (*p == '.') return 0;  /* Second extension not allowed */
156         if (++len > 3) return 0;  /* Extension too long */
157         p++;
158     }
159     return 1;
160 }
161
162
163 /***********************************************************************
164  *           DOSFS_ToDosFCBFormat
165  *
166  * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
167  * expanding wild cards and converting to upper-case in the process.
168  * File name can be terminated by '\0', '\\' or '/'.
169  * Return FALSE if the name is not a valid DOS name.
170  * 'buffer' must be at least 12 characters long.
171  */
172 BOOL DOSFS_ToDosFCBFormat( LPCSTR name, LPSTR buffer )
173 {
174     static const char invalid_chars[] = INVALID_DOS_CHARS;
175     const char *p = name;
176     int i;
177
178     /* Check for "." and ".." */
179     if (*p == '.')
180     {
181         p++;
182         strcpy( buffer, ".          " );
183         if (*p == '.')
184         {
185             buffer[1] = '.';
186             p++;
187         }
188         return (!*p || (*p == '/') || (*p == '\\'));
189     }
190
191     for (i = 0; i < 8; i++)
192     {
193         switch(*p)
194         {
195         case '\0':
196         case '\\':
197         case '/':
198         case '.':
199             buffer[i] = ' ';
200             break;
201         case '?':
202             p++;
203             /* fall through */
204         case '*':
205             buffer[i] = '?';
206             break;
207         default:
208             if (strchr( invalid_chars, *p )) return FALSE;
209             buffer[i] = FILE_toupper(*p);
210             p++;
211             break;
212         }
213     }
214
215     if (*p == '*')
216     {
217         /* Skip all chars after wildcard up to first dot */
218         while (*p && (*p != '/') && (*p != '\\') && (*p != '.')) p++;
219     }
220     else
221     {
222         /* Check if name too long */
223         if (*p && (*p != '/') && (*p != '\\') && (*p != '.')) return FALSE;
224     }
225     if (*p == '.') p++;  /* Skip dot */
226
227     for (i = 8; i < 11; i++)
228     {
229         switch(*p)
230         {
231         case '\0':
232         case '\\':
233         case '/':
234             buffer[i] = ' ';
235             break;
236         case '.':
237             return FALSE;  /* Second extension not allowed */
238         case '?':
239             p++;
240             /* fall through */
241         case '*':
242             buffer[i] = '?';
243             break;
244         default:
245             if (strchr( invalid_chars, *p )) return FALSE;
246             buffer[i] = FILE_toupper(*p);
247             p++;
248             break;
249         }
250     }
251     buffer[11] = '\0';
252
253     /* at most 3 character of the extension are processed
254      * is something behind this ? 
255      */
256     while (*p == '*' || *p == ' ') p++; /* skip wildcards and spaces */
257     return IS_END_OF_NAME(*p);
258 }
259
260
261 /***********************************************************************
262  *           DOSFS_ToDosDTAFormat
263  *
264  * Convert a file name from FCB to DTA format (name.ext, null-terminated)
265  * converting to upper-case in the process.
266  * File name can be terminated by '\0', '\\' or '/'.
267  * 'buffer' must be at least 13 characters long.
268  */
269 static void DOSFS_ToDosDTAFormat( LPCSTR name, LPSTR buffer )
270 {
271     char *p;
272
273     memcpy( buffer, name, 8 );
274     p = buffer + 8;
275     while ((p > buffer) && (p[-1] == ' ')) p--;
276     *p++ = '.';
277     memcpy( p, name + 8, 3 );
278     p += 3;
279     while (p[-1] == ' ') p--;
280     if (p[-1] == '.') p--;
281     *p = '\0';
282 }
283
284
285 /***********************************************************************
286  *           DOSFS_MatchShort
287  *
288  * Check a DOS file name against a mask (both in FCB format).
289  */
290 static int DOSFS_MatchShort( const char *mask, const char *name )
291 {
292     int i;
293     for (i = 11; i > 0; i--, mask++, name++)
294         if ((*mask != '?') && (*mask != *name)) return 0;
295     return 1;
296 }
297
298
299 /***********************************************************************
300  *           DOSFS_MatchLong
301  *
302  * Check a long file name against a mask.
303  *
304  * Tests (done in W95 DOS shell - case insensitive):
305  * *.txt                        test1.test.txt                          *
306  * *st1*                        test1.txt                               *
307  * *.t??????.t*                 test1.ta.tornado.txt                    *
308  * *tornado*                    test1.ta.tornado.txt                    *
309  * t*t                          test1.ta.tornado.txt                    *
310  * ?est*                        test1.txt                               *
311  * ?est???                      test1.txt                               -
312  * *test1.txt*                  test1.txt                               * 
313  * h?l?o*t.dat                  hellothisisatest.dat                    *
314  */
315 static int DOSFS_MatchLong( const char *mask, const char *name,
316                             int case_sensitive )
317 {
318     const char *lastjoker = NULL;
319     const char *next_to_retry = NULL;
320
321     if (!strcmp( mask, "*.*" )) return 1;
322     while (*name && *mask)
323     {
324         if (*mask == '*')
325         {
326             mask++;
327             while (*mask == '*') mask++;  /* Skip consecutive '*' */
328             lastjoker = mask;
329             if (!*mask) return 1; /* end of mask is all '*', so match */
330
331             /* skip to the next match after the joker(s) */
332             if (case_sensitive) while (*name && (*name != *mask)) name++;
333             else while (*name && (FILE_toupper(*name) != FILE_toupper(*mask))) name++;
334
335             if (!*name) break;
336             next_to_retry = name;
337         }
338         else if (*mask != '?')
339         {
340             int mismatch = 0;
341             if (case_sensitive)
342             {
343                 if (*mask != *name) mismatch = 1;
344             }
345             else
346             {
347                 if (FILE_toupper(*mask) != FILE_toupper(*name)) mismatch = 1;
348             }
349             if (!mismatch)
350             {
351                 mask++;
352                 name++;
353                 if (*mask == '\0')
354                 {
355                     if (*name == '\0')
356                         return 1;
357                     if (lastjoker)
358                         mask = lastjoker;
359                 }
360             }
361             else /* mismatch ! */
362             {
363                 if (lastjoker) /* we had an '*', so we can try unlimitedly */
364                 {
365                     mask = lastjoker;
366
367                     /* this scan sequence was a mismatch, so restart
368                      * 1 char after the first char we checked last time */
369                     next_to_retry++;
370                     name = next_to_retry;
371                 }
372                 else
373                     return 0; /* bad luck */
374             }
375         }
376         else /* '?' */
377         {
378             mask++;
379             name++;
380         }
381     }
382     while ((*mask == '.') || (*mask == '*'))
383         mask++;  /* Ignore trailing '.' or '*' in mask */
384     return (!*name && !*mask);
385 }
386
387
388 /***********************************************************************
389  *           DOSFS_OpenDir
390  */
391 static DOS_DIR *DOSFS_OpenDir( LPCSTR path )
392 {
393     DOS_DIR *dir = HeapAlloc( GetProcessHeap(), 0, sizeof(*dir) );
394     if (!dir)
395     {
396         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
397         return NULL;
398     }
399
400     /* Treat empty path as root directory. This simplifies path split into
401        directory and mask in several other places */
402     if (!*path) path = "/";
403
404 #ifdef VFAT_IOCTL_READDIR_BOTH
405
406     /* Check if the VFAT ioctl is supported on this directory */
407
408     if ((dir->fd = open( path, O_RDONLY )) != -1)
409     {
410         if (ioctl( dir->fd, VFAT_IOCTL_READDIR_BOTH, (long)dir->dirent ) == -1)
411         {
412             close( dir->fd );
413             dir->fd = -1;
414         }
415         else
416         {
417             /* Set the file pointer back at the start of the directory */
418             lseek( dir->fd, 0, SEEK_SET );
419             dir->dir = NULL;
420             return dir;
421         }
422     }
423 #endif  /* VFAT_IOCTL_READDIR_BOTH */
424
425     /* Now use the standard opendir/readdir interface */
426
427     if (!(dir->dir = opendir( path )))
428     {
429         HeapFree( GetProcessHeap(), 0, dir );
430         return NULL;
431     }
432     return dir;
433 }
434
435
436 /***********************************************************************
437  *           DOSFS_CloseDir
438  */
439 static void DOSFS_CloseDir( DOS_DIR *dir )
440 {
441 #ifdef VFAT_IOCTL_READDIR_BOTH
442     if (dir->fd != -1) close( dir->fd );
443 #endif  /* VFAT_IOCTL_READDIR_BOTH */
444     if (dir->dir) closedir( dir->dir );
445     HeapFree( GetProcessHeap(), 0, dir );
446 }
447
448
449 /***********************************************************************
450  *           DOSFS_ReadDir
451  */
452 static BOOL DOSFS_ReadDir( DOS_DIR *dir, LPCSTR *long_name,
453                              LPCSTR *short_name )
454 {
455     struct dirent *dirent;
456
457 #ifdef VFAT_IOCTL_READDIR_BOTH
458     if (dir->fd != -1)
459     {
460         if (ioctl( dir->fd, VFAT_IOCTL_READDIR_BOTH, (long)dir->dirent ) != -1) {
461             if (!dir->dirent[0].d_reclen) return FALSE;
462             if (!DOSFS_ToDosFCBFormat( dir->dirent[0].d_name, dir->short_name ))
463                 dir->short_name[0] = '\0';
464             *short_name = dir->short_name;
465             if (dir->dirent[1].d_name[0]) *long_name = dir->dirent[1].d_name;
466             else *long_name = dir->dirent[0].d_name;
467             return TRUE;
468         }
469     }
470 #endif  /* VFAT_IOCTL_READDIR_BOTH */
471
472     if (!(dirent = readdir( dir->dir ))) return FALSE;
473     *long_name  = dirent->d_name;
474     *short_name = NULL;
475     return TRUE;
476 }
477
478
479 /***********************************************************************
480  *           DOSFS_Hash
481  *
482  * Transform a Unix file name into a hashed DOS name. If the name is a valid
483  * DOS name, it is converted to upper-case; otherwise it is replaced by a
484  * hashed version that fits in 8.3 format.
485  * File name can be terminated by '\0', '\\' or '/'.
486  * 'buffer' must be at least 13 characters long.
487  */
488 static void DOSFS_Hash( LPCSTR name, LPSTR buffer, BOOL dir_format,
489                         BOOL ignore_case )
490 {
491     static const char invalid_chars[] = INVALID_DOS_CHARS "~.";
492     static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
493
494     const char *p, *ext;
495     char *dst;
496     unsigned short hash;
497     int i;
498
499     if (dir_format) strcpy( buffer, "           " );
500
501     if (DOSFS_ValidDOSName( name, ignore_case ))
502     {
503         /* Check for '.' and '..' */
504         if (*name == '.')
505         {
506             buffer[0] = '.';
507             if (!dir_format) buffer[1] = buffer[2] = '\0';
508             if (name[1] == '.') buffer[1] = '.';
509             return;
510         }
511
512         /* Simply copy the name, converting to uppercase */
513
514         for (dst = buffer; !IS_END_OF_NAME(*name) && (*name != '.'); name++)
515             *dst++ = FILE_toupper(*name);
516         if (*name == '.')
517         {
518             if (dir_format) dst = buffer + 8;
519             else *dst++ = '.';
520             for (name++; !IS_END_OF_NAME(*name); name++)
521                 *dst++ = FILE_toupper(*name);
522         }
523         if (!dir_format) *dst = '\0';
524         return;
525     }
526
527     /* Compute the hash code of the file name */
528     /* If you know something about hash functions, feel free to */
529     /* insert a better algorithm here... */
530     if (ignore_case)
531     {
532         for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
533             hash = (hash<<3) ^ (hash>>5) ^ FILE_tolower(*p) ^ (FILE_tolower(p[1]) << 8);
534         hash = (hash<<3) ^ (hash>>5) ^ FILE_tolower(*p); /* Last character*/
535     }
536     else
537     {
538         for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
539             hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
540         hash = (hash << 3) ^ (hash >> 5) ^ *p;  /* Last character */
541     }
542
543     /* Find last dot for start of the extension */
544     for (p = name+1, ext = NULL; !IS_END_OF_NAME(*p); p++)
545         if (*p == '.') ext = p;
546     if (ext && IS_END_OF_NAME(ext[1]))
547         ext = NULL;  /* Empty extension ignored */
548
549     /* Copy first 4 chars, replacing invalid chars with '_' */
550     for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
551     {
552         if (IS_END_OF_NAME(*p) || (p == ext)) break;
553         *dst++ = strchr( invalid_chars, *p ) ? '_' : FILE_toupper(*p);
554     }
555     /* Pad to 5 chars with '~' */
556     while (i-- >= 0) *dst++ = '~';
557
558     /* Insert hash code converted to 3 ASCII chars */
559     *dst++ = hash_chars[(hash >> 10) & 0x1f];
560     *dst++ = hash_chars[(hash >> 5) & 0x1f];
561     *dst++ = hash_chars[hash & 0x1f];
562
563     /* Copy the first 3 chars of the extension (if any) */
564     if (ext)
565     {
566         if (!dir_format) *dst++ = '.';
567         for (i = 3, ext++; (i > 0) && !IS_END_OF_NAME(*ext); i--, ext++)
568             *dst++ = strchr( invalid_chars, *ext ) ? '_' : FILE_toupper(*ext);
569     }
570     if (!dir_format) *dst = '\0';
571 }
572
573
574 /***********************************************************************
575  *           DOSFS_FindUnixName
576  *
577  * Find the Unix file name in a given directory that corresponds to
578  * a file name (either in Unix or DOS format).
579  * File name can be terminated by '\0', '\\' or '/'.
580  * Return TRUE if OK, FALSE if no file name matches.
581  *
582  * 'long_buf' must be at least 'long_len' characters long. If the long name
583  * turns out to be larger than that, the function returns FALSE.
584  * 'short_buf' must be at least 13 characters long.
585  */
586 BOOL DOSFS_FindUnixName( LPCSTR path, LPCSTR name, LPSTR long_buf,
587                            INT long_len, LPSTR short_buf, BOOL ignore_case)
588 {
589     DOS_DIR *dir;
590     LPCSTR long_name, short_name;
591     char dos_name[12], tmp_buf[13];
592     BOOL ret;
593
594     const char *p = strchr( name, '/' );
595     int len = p ? (int)(p - name) : strlen(name);
596     if ((p = strchr( name, '\\' ))) len = min( (int)(p - name), len );
597     /* Ignore trailing dots and spaces */
598     while (len > 1 && (name[len-1] == '.' || name[len-1] == ' ')) len--;
599     if (long_len < len + 1) return FALSE;
600
601     TRACE("%s,%s\n", path, name );
602
603     if (!DOSFS_ToDosFCBFormat( name, dos_name )) dos_name[0] = '\0';
604
605     if (!(dir = DOSFS_OpenDir( path )))
606     {
607         WARN("(%s,%s): can't open dir: %s\n",
608                        path, name, strerror(errno) );
609         return FALSE;
610     }
611
612     while ((ret = DOSFS_ReadDir( dir, &long_name, &short_name )))
613     {
614         /* Check against Unix name */
615         if (len == strlen(long_name))
616         {
617             if (!ignore_case)
618             {
619                 if (!strncmp( long_name, name, len )) break;
620             }
621             else
622             {
623                 if (!FILE_strncasecmp( long_name, name, len )) break;
624             }
625         }
626         if (dos_name[0])
627         {
628             /* Check against hashed DOS name */
629             if (!short_name)
630             {
631                 DOSFS_Hash( long_name, tmp_buf, TRUE, ignore_case );
632                 short_name = tmp_buf;
633             }
634             if (!strcmp( dos_name, short_name )) break;
635         }
636     }
637     if (ret)
638     {
639         if (long_buf) strcpy( long_buf, long_name );
640         if (short_buf)
641         {
642             if (short_name)
643                 DOSFS_ToDosDTAFormat( short_name, short_buf );
644             else
645                 DOSFS_Hash( long_name, short_buf, FALSE, ignore_case );
646         }
647         TRACE("(%s,%s) -> %s (%s)\n",
648               path, name, long_name, short_buf ? short_buf : "***");
649     }
650     else
651         WARN("'%s' not found in '%s'\n", name, path);
652     DOSFS_CloseDir( dir );
653     return ret;
654 }
655
656
657 /***********************************************************************
658  *           DOSFS_GetDevice
659  *
660  * Check if a DOS file name represents a DOS device and return the device.
661  */
662 const DOS_DEVICE *DOSFS_GetDevice( const char *name )
663 {
664     int i;
665     const char *p;
666
667     if (!name) return NULL; /* if FILE_DupUnixHandle was used */
668     if (name[0] && (name[1] == ':')) name += 2;
669     if ((p = strrchr( name, '/' ))) name = p + 1;
670     if ((p = strrchr( name, '\\' ))) name = p + 1;
671     for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
672     {
673         const char *dev = DOSFS_Devices[i].name;
674         if (!FILE_strncasecmp( dev, name, strlen(dev) ))
675         {
676             p = name + strlen( dev );
677             if (!*p || (*p == '.') || (*p == ':')) return &DOSFS_Devices[i];
678         }
679     }
680     return NULL;
681 }
682
683
684 /***********************************************************************
685  *           DOSFS_GetDeviceByHandle
686  */
687 const DOS_DEVICE *DOSFS_GetDeviceByHandle( HFILE hFile )
688 {
689     const DOS_DEVICE *ret = NULL;
690     SERVER_START_REQ( get_file_info )
691     {
692         req->handle = hFile;
693         if (!SERVER_CALL() && (req->type == FILE_TYPE_UNKNOWN))
694         {
695             if ((req->attr >= 0) &&
696                 (req->attr < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0])))
697                 ret = &DOSFS_Devices[req->attr];
698         }
699     }
700     SERVER_END_REQ;
701     return ret;
702 }
703
704
705 /**************************************************************************
706  *         DOSFS_CreateCommPort
707  */
708 static HANDLE DOSFS_CreateCommPort(LPCSTR name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa)
709 {
710     HANDLE ret;
711     char devname[40];
712     size_t len;
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     len = strlen(devname);
723     SERVER_START_VAR_REQ( create_serial, len )
724     {
725         req->access  = access;
726         req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
727         req->attributes = attributes;
728         req->sharing = FILE_SHARE_READ|FILE_SHARE_WRITE;
729         memcpy( server_data_ptr(req), devname, len );
730         SetLastError(0);
731         SERVER_CALL_ERR();
732         ret = req->handle;
733     }
734     SERVER_END_VAR_REQ;
735
736     if(!ret)
737         ERR("Couldn't open device '%s' ! (check permissions)\n",devname);
738     else
739         TRACE("return %08X\n", ret );
740     return ret;
741 }
742
743 /***********************************************************************
744  *           DOSFS_OpenDevice
745  *
746  * Open a DOS device. This might not map 1:1 into the UNIX device concept.
747  * Returns 0 on failure.
748  */
749 HANDLE DOSFS_OpenDevice( const char *name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa )
750 {
751     int i;
752     const char *p;
753     HANDLE handle;
754
755     if (name[0] && (name[1] == ':')) name += 2;
756     if ((p = strrchr( name, '/' ))) name = p + 1;
757     if ((p = strrchr( name, '\\' ))) name = p + 1;
758     for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
759     {
760         const char *dev = DOSFS_Devices[i].name;
761         if (!FILE_strncasecmp( dev, name, strlen(dev) ))
762         {
763             p = name + strlen( dev );
764             if (!*p || (*p == '.') || (*p == ':')) {
765                 /* got it */
766                 if (!strcmp(DOSFS_Devices[i].name,"NUL"))
767                     return FILE_CreateFile( "/dev/null", access,
768                                             FILE_SHARE_READ|FILE_SHARE_WRITE, sa,
769                                             OPEN_EXISTING, 0, 0, TRUE, DRIVE_UNKNOWN );
770                 if (!strcmp(DOSFS_Devices[i].name,"CON")) {
771                         HANDLE to_dup;
772                         switch (access & (GENERIC_READ|GENERIC_WRITE)) {
773                         case GENERIC_READ:
774                                 to_dup = GetStdHandle( STD_INPUT_HANDLE );
775                                 break;
776                         case GENERIC_WRITE:
777                                 to_dup = GetStdHandle( STD_OUTPUT_HANDLE );
778                                 break;
779                         default:
780                                 FIXME("can't open CON read/write\n");
781                                 return 0;
782                         }
783                         if (!DuplicateHandle( GetCurrentProcess(), to_dup, GetCurrentProcess(),
784                                               &handle, 0, 
785                                               sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle, 
786                                               DUPLICATE_SAME_ACCESS ))
787                             handle = 0;
788                         return handle;
789                 }
790                 if (!strcmp(DOSFS_Devices[i].name,"SCSIMGR$") ||
791                     !strcmp(DOSFS_Devices[i].name,"HPSCAN"))
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