Fix the error returned by GetLongPathNameA.
[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)
709 {
710     HANDLE ret;
711     char devname[40];
712     size_t len;
713
714     TRACE("%s %lx\n", name, access);
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 = 0;  /*FIXME*/
727         req->sharing = FILE_SHARE_READ|FILE_SHARE_WRITE;
728         memcpy( server_data_ptr(req), devname, len );
729         SetLastError(0);
730         SERVER_CALL_ERR();
731         ret = req->handle;
732     }
733     SERVER_END_VAR_REQ;
734
735     if(!ret)
736         ERR("Couldn't open %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 )
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, NULL,
768                                             OPEN_EXISTING, 0, 0, TRUE );
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, FALSE, DUPLICATE_SAME_ACCESS ))
784                             handle = 0;
785                         return handle;
786                 }
787                 if (!strcmp(DOSFS_Devices[i].name,"SCSIMGR$") ||
788                     !strcmp(DOSFS_Devices[i].name,"HPSCAN"))
789                 {
790                     return FILE_CreateDevice( i, access, NULL );
791                 }
792
793                 if( (handle=DOSFS_CreateCommPort(DOSFS_Devices[i].name,access)) )
794                     return handle;
795                 FIXME("device open %s not supported (yet)\n",DOSFS_Devices[i].name);
796                 return 0;
797             }
798         }
799     }
800     return 0;
801 }
802
803
804 /***********************************************************************
805  *           DOSFS_GetPathDrive
806  *
807  * Get the drive specified by a given path name (DOS or Unix format).
808  */
809 static int DOSFS_GetPathDrive( const char **name )
810 {
811     int drive;
812     const char *p = *name;
813
814     if (*p && (p[1] == ':'))
815     {
816         drive = FILE_toupper(*p) - 'A';
817         *name += 2;
818     }
819     else if (*p == '/') /* Absolute Unix path? */
820     {
821         if ((drive = DRIVE_FindDriveRoot( name )) == -1)
822         {
823             MESSAGE("Warning: %s not accessible from a DOS drive\n", *name );
824             /* Assume it really was a DOS name */
825             drive = DRIVE_GetCurrentDrive();            
826         }
827     }
828     else drive = DRIVE_GetCurrentDrive();
829
830     if (!DRIVE_IsValid(drive))
831     {
832         SetLastError( ERROR_INVALID_DRIVE );
833         return -1;
834     }
835     return drive;
836 }
837
838
839 /***********************************************************************
840  *           DOSFS_GetFullName
841  *
842  * Convert a file name (DOS or mixed DOS/Unix format) to a valid
843  * Unix name / short DOS name pair.
844  * Return FALSE if one of the path components does not exist. The last path
845  * component is only checked if 'check_last' is non-zero.
846  * The buffers pointed to by 'long_buf' and 'short_buf' must be
847  * at least MAX_PATHNAME_LEN long.
848  */
849 BOOL DOSFS_GetFullName( LPCSTR name, BOOL check_last, DOS_FULL_NAME *full )
850 {
851     BOOL found;
852     UINT flags;
853     char *p_l, *p_s, *root;
854
855     TRACE("%s (last=%d)\n", name, check_last );
856
857     if ((!*name) || (*name=='\n'))
858     { /* error code for Win98 */
859         SetLastError(ERROR_BAD_PATHNAME);
860         return FALSE;
861     }
862
863     if ((full->drive = DOSFS_GetPathDrive( &name )) == -1) return FALSE;
864     flags = DRIVE_GetFlags( full->drive );
865
866     lstrcpynA( full->long_name, DRIVE_GetRoot( full->drive ),
867                  sizeof(full->long_name) );
868     if (full->long_name[1]) root = full->long_name + strlen(full->long_name);
869     else root = full->long_name;  /* root directory */
870
871     strcpy( full->short_name, "A:\\" );
872     full->short_name[0] += full->drive;
873
874     if ((*name == '\\') || (*name == '/'))  /* Absolute path */
875     {
876         while ((*name == '\\') || (*name == '/')) name++;
877     }
878     else  /* Relative path */
879     {
880         lstrcpynA( root + 1, DRIVE_GetUnixCwd( full->drive ),
881                      sizeof(full->long_name) - (root - full->long_name) - 1 );
882         if (root[1]) *root = '/';
883         lstrcpynA( full->short_name + 3, DRIVE_GetDosCwd( full->drive ),
884                      sizeof(full->short_name) - 3 );
885     }
886
887     p_l = full->long_name[1] ? full->long_name + strlen(full->long_name)
888                              : full->long_name;
889     p_s = full->short_name[3] ? full->short_name + strlen(full->short_name)
890                               : full->short_name + 2;
891     found = TRUE;
892
893     while (*name && found)
894     {
895         /* Check for '.' and '..' */
896
897         if (*name == '.')
898         {
899             if (IS_END_OF_NAME(name[1]))
900             {
901                 name++;
902                 while ((*name == '\\') || (*name == '/')) name++;
903                 continue;
904             }
905             else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
906             {
907                 name += 2;
908                 while ((*name == '\\') || (*name == '/')) name++;
909                 while ((p_l > root) && (*p_l != '/')) p_l--;
910                 while ((p_s > full->short_name + 2) && (*p_s != '\\')) p_s--;
911                 *p_l = *p_s = '\0';  /* Remove trailing separator */
912                 continue;
913             }
914         }
915
916         /* Make sure buffers are large enough */
917
918         if ((p_s >= full->short_name + sizeof(full->short_name) - 14) ||
919             (p_l >= full->long_name + sizeof(full->long_name) - 1))
920         {
921             SetLastError( ERROR_PATH_NOT_FOUND );
922             return FALSE;
923         }
924
925         /* Get the long and short name matching the file name */
926
927         if ((found = DOSFS_FindUnixName( full->long_name, name, p_l + 1,
928                          sizeof(full->long_name) - (p_l - full->long_name) - 1,
929                          p_s + 1, !(flags & DRIVE_CASE_SENSITIVE) )))
930         {
931             *p_l++ = '/';
932             p_l   += strlen(p_l);
933             *p_s++ = '\\';
934             p_s   += strlen(p_s);
935             while (!IS_END_OF_NAME(*name)) name++;
936         }
937         else if (!check_last)
938         {
939             *p_l++ = '/';
940             *p_s++ = '\\';
941             while (!IS_END_OF_NAME(*name) &&
942                    (p_s < full->short_name + sizeof(full->short_name) - 1) &&
943                    (p_l < full->long_name + sizeof(full->long_name) - 1))
944             {
945                 *p_s++ = FILE_tolower(*name);
946                 /* If the drive is case-sensitive we want to create new */
947                 /* files in lower-case otherwise we can't reopen them   */
948                 /* under the same short name. */
949                 if (flags & DRIVE_CASE_SENSITIVE) *p_l++ = FILE_tolower(*name);
950                 else *p_l++ = *name;
951                 name++;
952             }
953             /* Ignore trailing dots and spaces */
954             while(p_l[-1] == '.' || p_l[-1] == ' ') {
955                 --p_l;
956                 --p_s;
957             }
958             *p_l = *p_s = '\0';
959         }
960         while ((*name == '\\') || (*name == '/')) name++;
961     }
962
963     if (!found)
964     {
965         if (check_last)
966         {
967             SetLastError( ERROR_FILE_NOT_FOUND );
968             return FALSE;
969         }
970         if (*name)  /* Not last */
971         {
972             SetLastError( ERROR_PATH_NOT_FOUND );
973             return FALSE;
974         }
975     }
976     if (!full->long_name[0]) strcpy( full->long_name, "/" );
977     if (!full->short_name[2]) strcpy( full->short_name + 2, "\\" );
978     TRACE("returning %s = %s\n", full->long_name, full->short_name );
979     return TRUE;
980 }
981
982
983 /***********************************************************************
984  *           GetShortPathNameA   (KERNEL32.@)
985  *
986  * NOTES
987  *  observed:
988  *  longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
989  *  longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0
990  * 
991  * more observations ( with NT 3.51 (WinDD) ):
992  * longpath <= 8.3 -> just copy longpath to shortpath
993  * longpath > 8.3  -> 
994  *             a) file does not exist -> return 0, LastError = ERROR_FILE_NOT_FOUND
995  *             b) file does exist     -> set the short filename.
996  * - trailing slashes are reproduced in the short name, even if the
997  *   file is not a directory
998  * - the absolute/relative path of the short name is reproduced like found
999  *   in the long name
1000  * - longpath and shortpath may have the same address
1001  * Peter Ganten, 1999
1002  */
1003 DWORD WINAPI GetShortPathNameA( LPCSTR longpath, LPSTR shortpath,
1004                                   DWORD shortlen )
1005 {
1006     DOS_FULL_NAME full_name;
1007     LPSTR tmpshortpath;
1008     DWORD sp = 0, lp = 0;
1009     int tmplen, drive;
1010     UINT flags;
1011
1012     TRACE("%s\n", debugstr_a(longpath));
1013
1014     if (!longpath) {
1015       SetLastError(ERROR_INVALID_PARAMETER);
1016       return 0;
1017     }
1018     if (!longpath[0]) {
1019       SetLastError(ERROR_BAD_PATHNAME);
1020       return 0;
1021     }
1022
1023     if ( ( tmpshortpath = HeapAlloc ( GetProcessHeap(), 0, MAX_PATHNAME_LEN ) ) == NULL ) {
1024       SetLastError ( ERROR_NOT_ENOUGH_MEMORY );
1025       return 0;
1026     }
1027
1028     /* check for drive letter */
1029     if ( longpath[1] == ':' ) {
1030       tmpshortpath[0] = longpath[0];
1031       tmpshortpath[1] = ':';
1032       sp = 2;
1033     }
1034
1035     if ( ( drive = DOSFS_GetPathDrive ( &longpath )) == -1 ) return 0;
1036     flags = DRIVE_GetFlags ( drive );
1037
1038     while ( longpath[lp] ) {
1039
1040       /* check for path delimiters and reproduce them */
1041       if ( longpath[lp] == '\\' || longpath[lp] == '/' ) {
1042         if (!sp || tmpshortpath[sp-1]!= '\\') 
1043         {
1044             /* strip double "\\" */
1045             tmpshortpath[sp] = '\\';
1046             sp++;
1047         }
1048         tmpshortpath[sp]=0;/*terminate string*/
1049         lp++;
1050         continue;
1051       }
1052
1053       tmplen = strcspn ( longpath + lp, "\\/" ); 
1054       lstrcpynA ( tmpshortpath+sp, longpath + lp, tmplen+1 );
1055       
1056       /* Check, if the current element is a valid dos name */
1057       if ( DOSFS_ValidDOSName ( longpath + lp, !(flags & DRIVE_CASE_SENSITIVE) ) ) {
1058         sp += tmplen;
1059         lp += tmplen;
1060         continue;
1061       }
1062
1063       /* Check if the file exists and use the existing file name */
1064       if ( DOSFS_GetFullName ( tmpshortpath, TRUE, &full_name ) ) {
1065         strcpy( tmpshortpath+sp, strrchr ( full_name.short_name, '\\' ) + 1 );
1066         sp += strlen ( tmpshortpath+sp );
1067         lp += tmplen;
1068         continue;
1069       }
1070
1071       TRACE("not found!\n" );
1072       SetLastError ( ERROR_FILE_NOT_FOUND );
1073       return 0;
1074     }
1075     tmpshortpath[sp] = 0;
1076
1077     lstrcpynA ( shortpath, tmpshortpath, shortlen );
1078     TRACE("returning %s\n", debugstr_a(shortpath) );
1079     tmplen = strlen ( tmpshortpath );
1080     HeapFree ( GetProcessHeap(), 0, tmpshortpath );
1081     
1082     return tmplen;
1083 }
1084
1085
1086 /***********************************************************************
1087  *           GetShortPathNameW   (KERNEL32.@)
1088  */
1089 DWORD WINAPI GetShortPathNameW( LPCWSTR longpath, LPWSTR shortpath,
1090                                   DWORD shortlen )
1091 {
1092     LPSTR longpathA, shortpathA;
1093     DWORD ret = 0;
1094
1095     longpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, longpath );
1096     shortpathA = HeapAlloc ( GetProcessHeap(), 0, shortlen );
1097
1098     ret = GetShortPathNameA ( longpathA, shortpathA, shortlen );
1099     if (shortlen > 0 && !MultiByteToWideChar( CP_ACP, 0, shortpathA, -1, shortpath, shortlen ))
1100         shortpath[shortlen-1] = 0;
1101     HeapFree( GetProcessHeap(), 0, longpathA );
1102     HeapFree( GetProcessHeap(), 0, shortpathA );
1103
1104     return ret;
1105 }
1106
1107
1108 /***********************************************************************
1109  *           GetLongPathNameA   (KERNEL32.@)
1110  *
1111  * NOTES
1112  *  observed (Win2000):
1113  *  shortpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1114  *  shortpath="":   LastError=ERROR_PATH_NOT_FOUND, ret=0
1115  */
1116 DWORD WINAPI GetLongPathNameA( LPCSTR shortpath, LPSTR longpath,
1117                                   DWORD longlen )
1118 {
1119     DOS_FULL_NAME full_name;
1120     char *p, *r, *ll, *ss;
1121
1122     if (!shortpath) {
1123       SetLastError(ERROR_INVALID_PARAMETER);
1124       return 0;
1125     }
1126     if (!shortpath[0]) {
1127       SetLastError(ERROR_PATH_NOT_FOUND);
1128       return 0;
1129     }
1130
1131     if (!DOSFS_GetFullName( shortpath, TRUE, &full_name )) return 0;
1132     lstrcpynA( longpath, full_name.short_name, longlen );
1133
1134     /* Do some hackery to get the long filename. */
1135
1136     if (longpath) {
1137      ss=longpath+strlen(longpath);
1138      ll=full_name.long_name+strlen(full_name.long_name);
1139      p=NULL;
1140      while (ss>=longpath)
1141      {
1142        /* FIXME: aren't we more paranoid, than needed? */
1143        while ((ss[0]=='\\') && (ss>=longpath)) ss--;
1144        p=ss;
1145        while ((ss[0]!='\\') && (ss>=longpath)) ss--;
1146        if (ss>=longpath) 
1147          {
1148          /* FIXME: aren't we more paranoid, than needed? */
1149          while ((ll[0]=='/') && (ll>=full_name.long_name)) ll--;
1150          while ((ll[0]!='/') && (ll>=full_name.long_name)) ll--;
1151          if (ll<full_name.long_name) 
1152               { 
1153               ERR("Bad longname! (ss=%s ll=%s)\n This should never happen !\n"
1154                   ,ss ,ll ); 
1155               return 0;
1156               }
1157          }
1158      }
1159
1160    /* FIXME: fix for names like "C:\\" (ie. with more '\'s) */
1161       if (p && p[2]) 
1162         {
1163         p+=1;
1164         if ((p-longpath)>0) longlen -= (p-longpath);
1165         lstrcpynA( p, ll , longlen);
1166
1167         /* Now, change all '/' to '\' */
1168         for (r=p; r<(p+longlen); r++ ) 
1169           if (r[0]=='/') r[0]='\\';
1170         return strlen(longpath) - strlen(p) + longlen;
1171         }
1172     }
1173
1174     return strlen(longpath);
1175 }
1176
1177
1178 /***********************************************************************
1179  *           GetLongPathNameW   (KERNEL32.@)
1180  */
1181 DWORD WINAPI GetLongPathNameW( LPCWSTR shortpath, LPWSTR longpath,
1182                                   DWORD longlen )
1183 {
1184     DOS_FULL_NAME full_name;
1185     DWORD ret = 0;
1186     LPSTR shortpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, shortpath );
1187
1188     /* FIXME: is it correct to always return a fully qualified short path? */
1189     if (DOSFS_GetFullName( shortpathA, TRUE, &full_name ))
1190     {
1191         ret = strlen( full_name.short_name );
1192         if (longlen > 0 && !MultiByteToWideChar( CP_ACP, 0, full_name.long_name, -1,
1193                                                  longpath, longlen ))
1194             longpath[longlen-1] = 0;
1195     }
1196     HeapFree( GetProcessHeap(), 0, shortpathA );
1197     return ret;
1198 }
1199
1200
1201 /***********************************************************************
1202  *           DOSFS_DoGetFullPathName
1203  *
1204  * Implementation of GetFullPathNameA/W.
1205  *
1206  * bon@elektron 000331:
1207  * A test for GetFullPathName with many pathological cases 
1208  * now gives identical output for Wine and OSR2
1209  */
1210 static DWORD DOSFS_DoGetFullPathName( LPCSTR name, DWORD len, LPSTR result,
1211                                       BOOL unicode )
1212 {
1213     DWORD ret;
1214     DOS_FULL_NAME full_name;
1215     char *p,*q;
1216     const char * root;
1217     char drivecur[]="c:.";
1218     char driveletter=0;
1219     int namelen,drive=0;
1220
1221     if ((strlen(name) >1)&& (name[1]==':'))
1222       /*drive letter given */
1223       {
1224         driveletter = name[0];
1225       }
1226     if ((strlen(name) >2)&& (name[1]==':') &&
1227              ((name[2]=='\\') || (name[2]=='/')))
1228       /*absolute path given */
1229       {
1230         lstrcpynA(full_name.short_name,name,MAX_PATHNAME_LEN);
1231         drive = (int)FILE_toupper(name[0]) - 'A';
1232       }
1233     else
1234       {
1235         if (driveletter)
1236           drivecur[0]=driveletter;
1237         else
1238           strcpy(drivecur,".");
1239         if (!DOSFS_GetFullName( drivecur, FALSE, &full_name ))
1240           {
1241             FIXME("internal: error getting drive/path\n");
1242             return 0;
1243           }
1244         /* find path that drive letter substitutes*/
1245         drive = (int)FILE_toupper(full_name.short_name[0]) -0x41;
1246         root= DRIVE_GetRoot(drive);
1247         if (!root)
1248           {
1249             FIXME("internal: error getting DOS Drive Root\n");
1250             return 0;
1251           }
1252         if (!strcmp(root,"/"))
1253           {
1254             /* we have just the last / and we need it. */
1255             p= full_name.long_name;
1256           }
1257         else
1258           {
1259             p= full_name.long_name +strlen(root);
1260           }
1261         /* append long name (= unix name) to drive */
1262         lstrcpynA(full_name.short_name+2,p,MAX_PATHNAME_LEN-3);
1263         /* append name to treat */
1264         namelen= strlen(full_name.short_name);
1265         p = (char*)name;
1266         if (driveletter)
1267           p += +2; /* skip drive name when appending */
1268         if (namelen +2  + strlen(p) > MAX_PATHNAME_LEN)
1269           {
1270             FIXME("internal error: buffer too small\n");
1271              return 0;
1272           }
1273         full_name.short_name[namelen++] ='\\';
1274         full_name.short_name[namelen] = 0;
1275         lstrcpynA(full_name.short_name +namelen,p,MAX_PATHNAME_LEN-namelen);
1276       }
1277     /* reverse all slashes */
1278     for (p=full_name.short_name;
1279          p < full_name.short_name+strlen(full_name.short_name);
1280          p++)
1281       {
1282         if ( *p == '/' )
1283           *p = '\\';
1284       }
1285      /* Use memmove, as areas overlap*/
1286      /* Delete .. */
1287     while ((p = strstr(full_name.short_name,"\\..\\")))
1288       {
1289         if (p > full_name.short_name+2)
1290           {
1291             *p = 0;
1292             q = strrchr(full_name.short_name,'\\');
1293             memmove(q+1,p+4,strlen(p+4)+1);
1294           }
1295         else
1296           {
1297             memmove(full_name.short_name+3,p+4,strlen(p+4)+1);
1298           }
1299       }
1300     if ((full_name.short_name[2]=='.')&&(full_name.short_name[3]=='.'))
1301         {
1302           /* This case istn't treated yet : c:..\test */
1303           memmove(full_name.short_name+2,full_name.short_name+4,
1304                   strlen(full_name.short_name+4)+1);
1305         }
1306      /* Delete . */
1307     while ((p = strstr(full_name.short_name,"\\.\\")))
1308       {
1309         *(p+1) = 0;
1310         memmove(p+1,p+3,strlen(p+3)+1);
1311       }
1312     if (!(DRIVE_GetFlags(drive) & DRIVE_CASE_PRESERVING))
1313         for (p = full_name.short_name; *p; p++) *p = FILE_toupper(*p);
1314     namelen=strlen(full_name.short_name);
1315     if (!strcmp(full_name.short_name+namelen-3,"\\.."))
1316         {
1317           /* one more starnge case: "c:\test\test1\.." 
1318            return "c:\test"*/
1319           *(full_name.short_name+namelen-3)=0;
1320           q = strrchr(full_name.short_name,'\\');
1321           *q =0;
1322         }
1323     if (full_name.short_name[namelen-1]=='.')
1324         full_name.short_name[(namelen--)-1] =0;
1325     if (!driveletter)
1326       if (full_name.short_name[namelen-1]=='\\')
1327         full_name.short_name[(namelen--)-1] =0;
1328     TRACE("got %s\n",full_name.short_name);
1329
1330     /* If the lpBuffer buffer is too small, the return value is the 
1331     size of the buffer, in characters, required to hold the path 
1332     plus the terminating \0 (tested against win95osr, bon 001118)
1333     . */
1334     ret = strlen(full_name.short_name);
1335     if (ret >= len )
1336       {
1337         /* don't touch anything when the buffer is not large enough */
1338         SetLastError( ERROR_INSUFFICIENT_BUFFER );
1339         return ret+1;
1340       }
1341     if (result)
1342     {
1343         if (unicode)
1344             MultiByteToWideChar( CP_ACP, 0, full_name.short_name, -1, (LPWSTR)result, len );
1345         else
1346             lstrcpynA( result, full_name.short_name, len );
1347     }
1348
1349     TRACE("returning '%s'\n", full_name.short_name );
1350     return ret;
1351 }
1352
1353
1354 /***********************************************************************
1355  *           GetFullPathNameA   (KERNEL32.@)
1356  * NOTES
1357  *   if the path closed with '\', *lastpart is 0 
1358  */
1359 DWORD WINAPI GetFullPathNameA( LPCSTR name, DWORD len, LPSTR buffer,
1360                                  LPSTR *lastpart )
1361 {
1362     DWORD ret = DOSFS_DoGetFullPathName( name, len, buffer, FALSE );
1363     if (ret && (ret<=len) && buffer && lastpart)
1364     {
1365         LPSTR p = buffer + strlen(buffer);
1366
1367         if (*p != '\\')
1368         {
1369           while ((p > buffer + 2) && (*p != '\\')) p--;
1370           *lastpart = p + 1;
1371         }
1372         else *lastpart = NULL;
1373     }
1374     return ret;
1375 }
1376
1377
1378 /***********************************************************************
1379  *           GetFullPathNameW   (KERNEL32.@)
1380  */
1381 DWORD WINAPI GetFullPathNameW( LPCWSTR name, DWORD len, LPWSTR buffer,
1382                                  LPWSTR *lastpart )
1383 {
1384     LPSTR nameA = HEAP_strdupWtoA( GetProcessHeap(), 0, name );
1385     DWORD ret = DOSFS_DoGetFullPathName( nameA, len, (LPSTR)buffer, TRUE );
1386     HeapFree( GetProcessHeap(), 0, nameA );
1387     if (ret && (ret<=len) && buffer && lastpart)
1388     {
1389         LPWSTR p = buffer + strlenW(buffer);
1390         if (*p != (WCHAR)'\\')
1391         {
1392             while ((p > buffer + 2) && (*p != (WCHAR)'\\')) p--;
1393             *lastpart = p + 1;
1394         }
1395         else *lastpart = NULL;  
1396     }
1397     return ret;
1398 }
1399
1400
1401 /***********************************************************************
1402  *           wine_get_unix_file_name (KERNEL32.@) Not a Windows API
1403  *
1404  * Return the full Unix file name for a given path.
1405  */
1406 BOOL WINAPI wine_get_unix_file_name( LPCSTR dos, LPSTR buffer, DWORD len )
1407 {
1408     BOOL ret;
1409     DOS_FULL_NAME path;
1410     if ((ret = DOSFS_GetFullName( dos, FALSE, &path ))) lstrcpynA( buffer, path.long_name, len );
1411     return ret;
1412 }
1413
1414
1415 /***********************************************************************
1416  *           DOSFS_FindNextEx
1417  */
1418 static int DOSFS_FindNextEx( FIND_FIRST_INFO *info, WIN32_FIND_DATAA *entry )
1419 {
1420     DWORD attr = info->attr | FA_UNUSED | FA_ARCHIVE | FA_RDONLY | FILE_ATTRIBUTE_SYMLINK;
1421     UINT flags = DRIVE_GetFlags( info->drive );
1422     char *p, buffer[MAX_PATHNAME_LEN];
1423     const char *drive_path;
1424     int drive_root;
1425     LPCSTR long_name, short_name;
1426     BY_HANDLE_FILE_INFORMATION fileinfo;
1427     char dos_name[13];
1428
1429     if ((info->attr & ~(FA_UNUSED | FA_ARCHIVE | FA_RDONLY)) == FA_LABEL)
1430     {
1431         if (info->cur_pos) return 0;
1432         entry->dwFileAttributes  = FILE_ATTRIBUTE_LABEL;
1433         RtlSecondsSince1970ToTime( (time_t)0, &entry->ftCreationTime );
1434         RtlSecondsSince1970ToTime( (time_t)0, &entry->ftLastAccessTime );
1435         RtlSecondsSince1970ToTime( (time_t)0, &entry->ftLastWriteTime );
1436         entry->nFileSizeHigh     = 0;
1437         entry->nFileSizeLow      = 0;
1438         entry->dwReserved0       = 0;
1439         entry->dwReserved1       = 0;
1440         DOSFS_ToDosDTAFormat( DRIVE_GetLabel( info->drive ), entry->cFileName );
1441         strcpy( entry->cAlternateFileName, entry->cFileName ); 
1442         info->cur_pos++;
1443         TRACE("returning %s (%s) as label\n",
1444                entry->cFileName, entry->cAlternateFileName);
1445         return 1;
1446     }
1447
1448     drive_path = info->path + strlen(DRIVE_GetRoot( info->drive ));
1449     while ((*drive_path == '/') || (*drive_path == '\\')) drive_path++;
1450     drive_root = !*drive_path;
1451
1452     lstrcpynA( buffer, info->path, sizeof(buffer) - 1 );
1453     strcat( buffer, "/" );
1454     p = buffer + strlen(buffer);
1455
1456     while (DOSFS_ReadDir( info->dir, &long_name, &short_name ))
1457     {
1458         info->cur_pos++;
1459
1460         /* Don't return '.' and '..' in the root of the drive */
1461         if (drive_root && (long_name[0] == '.') &&
1462             (!long_name[1] || ((long_name[1] == '.') && !long_name[2])))
1463             continue;
1464
1465         /* Check the long mask */
1466
1467         if (info->long_mask)
1468         {
1469             if (!DOSFS_MatchLong( info->long_mask, long_name,
1470                                   flags & DRIVE_CASE_SENSITIVE )) continue;
1471         }
1472
1473         /* Check the short mask */
1474
1475         if (info->short_mask)
1476         {
1477             if (!short_name)
1478             {
1479                 DOSFS_Hash( long_name, dos_name, TRUE,
1480                             !(flags & DRIVE_CASE_SENSITIVE) );
1481                 short_name = dos_name;
1482             }
1483             if (!DOSFS_MatchShort( info->short_mask, short_name )) continue;
1484         }
1485
1486         /* Check the file attributes */
1487
1488         lstrcpynA( p, long_name, sizeof(buffer) - (int)(p - buffer) );
1489         if (!FILE_Stat( buffer, &fileinfo ))
1490         {
1491             WARN("can't stat %s\n", buffer);
1492             continue;
1493         }
1494         if ((fileinfo.dwFileAttributes & FILE_ATTRIBUTE_SYMLINK) &&
1495             (fileinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
1496         {
1497             static int show_dir_symlinks = -1;
1498             if (show_dir_symlinks == -1)
1499                 show_dir_symlinks = PROFILE_GetWineIniBool("wine", "ShowDirSymlinks", 0);
1500             if (!show_dir_symlinks) continue;
1501         }
1502
1503         if (fileinfo.dwFileAttributes & ~attr) continue;
1504
1505         /* We now have a matching entry; fill the result and return */
1506
1507         entry->dwFileAttributes = fileinfo.dwFileAttributes;
1508         entry->ftCreationTime   = fileinfo.ftCreationTime;
1509         entry->ftLastAccessTime = fileinfo.ftLastAccessTime;
1510         entry->ftLastWriteTime  = fileinfo.ftLastWriteTime;
1511         entry->nFileSizeHigh    = fileinfo.nFileSizeHigh;
1512         entry->nFileSizeLow     = fileinfo.nFileSizeLow;
1513
1514         if (short_name)
1515             DOSFS_ToDosDTAFormat( short_name, entry->cAlternateFileName );
1516         else
1517             DOSFS_Hash( long_name, entry->cAlternateFileName, FALSE,
1518                         !(flags & DRIVE_CASE_SENSITIVE) );
1519
1520         lstrcpynA( entry->cFileName, long_name, sizeof(entry->cFileName) );
1521         if (!(flags & DRIVE_CASE_PRESERVING)) _strlwr( entry->cFileName );
1522         TRACE("returning %s (%s) %02lx %ld\n",
1523               entry->cFileName, entry->cAlternateFileName,
1524               entry->dwFileAttributes, entry->nFileSizeLow );
1525         return 1;
1526     }
1527     return 0;  /* End of directory */
1528 }
1529
1530 /***********************************************************************
1531  *           DOSFS_FindNext
1532  *
1533  * Find the next matching file. Return the number of entries read to find
1534  * the matching one, or 0 if no more entries.
1535  * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
1536  * file name mask. Either or both can be NULL.
1537  *
1538  * NOTE: This is supposed to be only called by the int21 emulation
1539  *       routines. Thus, we should own the Win16Mutex anyway.
1540  *       Nevertheless, we explicitly enter it to ensure the static
1541  *       directory cache is protected.
1542  */
1543 int DOSFS_FindNext( const char *path, const char *short_mask,
1544                     const char *long_mask, int drive, BYTE attr,
1545                     int skip, WIN32_FIND_DATAA *entry )
1546 {
1547     static FIND_FIRST_INFO info;
1548     LPCSTR short_name, long_name;
1549     int count;
1550
1551     _EnterWin16Lock();
1552
1553     /* Check the cached directory */
1554     if (!(info.dir && info.path == path && info.short_mask == short_mask
1555                    && info.long_mask == long_mask && info.drive == drive
1556                    && info.attr == attr && info.cur_pos <= skip))
1557     {  
1558         /* Not in the cache, open it anew */
1559         if (info.dir) DOSFS_CloseDir( info.dir );
1560
1561         info.path = (LPSTR)path;
1562         info.long_mask = (LPSTR)long_mask;
1563         info.short_mask = (LPSTR)short_mask;
1564         info.attr = attr;
1565         info.drive = drive;
1566         info.cur_pos = 0;
1567         info.dir = DOSFS_OpenDir( info.path );
1568     }
1569
1570     /* Skip to desired position */
1571     while (info.cur_pos < skip)
1572         if (info.dir && DOSFS_ReadDir( info.dir, &long_name, &short_name ))
1573             info.cur_pos++;
1574         else
1575             break;
1576
1577     if (info.dir && info.cur_pos == skip && DOSFS_FindNextEx( &info, entry ))
1578         count = info.cur_pos - skip;
1579     else
1580         count = 0;
1581
1582     if (!count)
1583     {
1584         if (info.dir) DOSFS_CloseDir( info.dir );
1585         memset( &info, '\0', sizeof(info) );
1586     }
1587
1588     _LeaveWin16Lock();
1589
1590     return count;
1591 }
1592
1593 /*************************************************************************
1594  *           FindFirstFileExA  (KERNEL32.@)
1595  */
1596 HANDLE WINAPI FindFirstFileExA(
1597         LPCSTR lpFileName,
1598         FINDEX_INFO_LEVELS fInfoLevelId,
1599         LPVOID lpFindFileData,
1600         FINDEX_SEARCH_OPS fSearchOp,
1601         LPVOID lpSearchFilter,
1602         DWORD dwAdditionalFlags)
1603 {
1604     DOS_FULL_NAME full_name;
1605     HGLOBAL handle;
1606     FIND_FIRST_INFO *info;
1607     
1608     if ((fSearchOp != FindExSearchNameMatch) || (dwAdditionalFlags != 0))
1609     {
1610         FIXME("options not implemented 0x%08x 0x%08lx\n", fSearchOp, dwAdditionalFlags );
1611         return INVALID_HANDLE_VALUE;
1612     }
1613
1614     switch(fInfoLevelId)
1615     {
1616       case FindExInfoStandard:
1617         {
1618           WIN32_FIND_DATAA * data = (WIN32_FIND_DATAA *) lpFindFileData;
1619           data->dwReserved0 = data->dwReserved1 = 0x0;
1620           if (!lpFileName) return 0;
1621           if (!DOSFS_GetFullName( lpFileName, FALSE, &full_name )) break;
1622           if (!(handle = GlobalAlloc(GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO)))) break;
1623           info = (FIND_FIRST_INFO *)GlobalLock( handle );
1624           info->path = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
1625           strcpy( info->path, full_name.long_name );
1626           info->long_mask = strrchr( info->path, '/' );
1627           *(info->long_mask++) = '\0';
1628           info->short_mask = NULL;
1629           info->attr = 0xff;
1630           if (lpFileName[0] && (lpFileName[1] == ':'))
1631               info->drive = FILE_toupper(*lpFileName) - 'A';
1632           else info->drive = DRIVE_GetCurrentDrive();
1633           info->cur_pos = 0;
1634
1635           info->dir = DOSFS_OpenDir( info->path );
1636
1637           GlobalUnlock( handle );
1638           if (!FindNextFileA( handle, data ))
1639           {
1640               FindClose( handle );
1641               SetLastError( ERROR_NO_MORE_FILES );
1642               break;
1643           }
1644           return handle;
1645         }
1646         break;
1647       default:
1648         FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
1649     }
1650     return INVALID_HANDLE_VALUE;
1651 }
1652
1653 /*************************************************************************
1654  *           FindFirstFileA   (KERNEL32.@)
1655  */
1656 HANDLE WINAPI FindFirstFileA(
1657         LPCSTR lpFileName,
1658         WIN32_FIND_DATAA *lpFindData )
1659 {
1660     return FindFirstFileExA(lpFileName, FindExInfoStandard, lpFindData,
1661                             FindExSearchNameMatch, NULL, 0);
1662 }
1663
1664 /*************************************************************************
1665  *           FindFirstFileExW   (KERNEL32.@)
1666  */
1667 HANDLE WINAPI FindFirstFileExW(
1668         LPCWSTR lpFileName,
1669         FINDEX_INFO_LEVELS fInfoLevelId,
1670         LPVOID lpFindFileData,
1671         FINDEX_SEARCH_OPS fSearchOp,
1672         LPVOID lpSearchFilter,
1673         DWORD dwAdditionalFlags)
1674 {
1675     HANDLE handle;
1676     WIN32_FIND_DATAA dataA;
1677     LPVOID _lpFindFileData;
1678     LPSTR pathA;
1679
1680     switch(fInfoLevelId)
1681     {
1682       case FindExInfoStandard:
1683         {
1684           _lpFindFileData = &dataA;
1685         }
1686         break;
1687       default:
1688         FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
1689         return INVALID_HANDLE_VALUE;
1690     }
1691
1692     pathA = HEAP_strdupWtoA( GetProcessHeap(), 0, lpFileName );
1693     handle = FindFirstFileExA(pathA, fInfoLevelId, _lpFindFileData, fSearchOp, lpSearchFilter, dwAdditionalFlags);
1694     HeapFree( GetProcessHeap(), 0, pathA );
1695     if (handle == INVALID_HANDLE_VALUE) return handle;
1696     
1697     switch(fInfoLevelId)
1698     {
1699       case FindExInfoStandard:
1700         {
1701           WIN32_FIND_DATAW *dataW = (WIN32_FIND_DATAW*) lpFindFileData;
1702           dataW->dwFileAttributes = dataA.dwFileAttributes;
1703           dataW->ftCreationTime   = dataA.ftCreationTime;
1704           dataW->ftLastAccessTime = dataA.ftLastAccessTime;
1705           dataW->ftLastWriteTime  = dataA.ftLastWriteTime;
1706           dataW->nFileSizeHigh    = dataA.nFileSizeHigh;
1707           dataW->nFileSizeLow     = dataA.nFileSizeLow;
1708           MultiByteToWideChar( CP_ACP, 0, dataA.cFileName, -1,
1709                                dataW->cFileName, sizeof(dataW->cFileName)/sizeof(WCHAR) );
1710           MultiByteToWideChar( CP_ACP, 0, dataA.cAlternateFileName, -1,
1711                                dataW->cAlternateFileName,
1712                                sizeof(dataW->cAlternateFileName)/sizeof(WCHAR) );
1713         }
1714         break;
1715       default:
1716         FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
1717         return INVALID_HANDLE_VALUE;
1718     }
1719     return handle;
1720 }
1721
1722 /*************************************************************************
1723  *           FindFirstFileW   (KERNEL32.@)
1724  */
1725 HANDLE WINAPI FindFirstFileW( LPCWSTR lpFileName, WIN32_FIND_DATAW *lpFindData )
1726 {
1727     return FindFirstFileExW(lpFileName, FindExInfoStandard, lpFindData,
1728                             FindExSearchNameMatch, NULL, 0);
1729 }
1730
1731 /*************************************************************************
1732  *           FindNextFileA   (KERNEL32.@)
1733  */
1734 BOOL WINAPI FindNextFileA( HANDLE handle, WIN32_FIND_DATAA *data )
1735 {
1736     FIND_FIRST_INFO *info;
1737
1738     if ((handle == INVALID_HANDLE_VALUE) || 
1739        !(info = (FIND_FIRST_INFO *)GlobalLock( handle )))
1740     {
1741         SetLastError( ERROR_INVALID_HANDLE );
1742         return FALSE;
1743     }
1744     GlobalUnlock( handle );
1745     if (!info->path || !info->dir)
1746     {
1747         SetLastError( ERROR_NO_MORE_FILES );
1748         return FALSE;
1749     }
1750     if (!DOSFS_FindNextEx( info, data ))
1751     {
1752         DOSFS_CloseDir( info->dir ); info->dir = NULL;
1753         HeapFree( GetProcessHeap(), 0, info->path );
1754         info->path = info->long_mask = NULL;
1755         SetLastError( ERROR_NO_MORE_FILES );
1756         return FALSE;
1757     }
1758     return TRUE;
1759 }
1760
1761
1762 /*************************************************************************
1763  *           FindNextFileW   (KERNEL32.@)
1764  */
1765 BOOL WINAPI FindNextFileW( HANDLE handle, WIN32_FIND_DATAW *data )
1766 {
1767     WIN32_FIND_DATAA dataA;
1768     if (!FindNextFileA( handle, &dataA )) return FALSE;
1769     data->dwFileAttributes = dataA.dwFileAttributes;
1770     data->ftCreationTime   = dataA.ftCreationTime;
1771     data->ftLastAccessTime = dataA.ftLastAccessTime;
1772     data->ftLastWriteTime  = dataA.ftLastWriteTime;
1773     data->nFileSizeHigh    = dataA.nFileSizeHigh;
1774     data->nFileSizeLow     = dataA.nFileSizeLow;
1775     MultiByteToWideChar( CP_ACP, 0, dataA.cFileName, -1,
1776                          data->cFileName, sizeof(data->cFileName)/sizeof(WCHAR) );
1777     MultiByteToWideChar( CP_ACP, 0, dataA.cAlternateFileName, -1,
1778                          data->cAlternateFileName,
1779                          sizeof(data->cAlternateFileName)/sizeof(WCHAR) );
1780     return TRUE;
1781 }
1782
1783 /*************************************************************************
1784  *           FindClose   (KERNEL32.@)
1785  */
1786 BOOL WINAPI FindClose( HANDLE handle )
1787 {
1788     FIND_FIRST_INFO *info;
1789
1790     if ((handle == INVALID_HANDLE_VALUE) ||
1791         !(info = (FIND_FIRST_INFO *)GlobalLock( handle )))
1792     {
1793         SetLastError( ERROR_INVALID_HANDLE );
1794         return FALSE;
1795     }
1796     __TRY
1797     {
1798         if (info->dir) DOSFS_CloseDir( info->dir );
1799         if (info->path) HeapFree( GetProcessHeap(), 0, info->path );
1800     }
1801     __EXCEPT(page_fault)
1802     {
1803         WARN("Illegal handle %x\n", handle);
1804         SetLastError( ERROR_INVALID_HANDLE );
1805         return FALSE;
1806     }
1807     __ENDTRY
1808     GlobalUnlock( handle );
1809     GlobalFree( handle );
1810     return TRUE;
1811 }
1812
1813 /***********************************************************************
1814  *           DOSFS_UnixTimeToFileTime
1815  *
1816  * Convert a Unix time to FILETIME format.
1817  * The FILETIME structure is a 64-bit value representing the number of
1818  * 100-nanosecond intervals since January 1, 1601, 0:00.
1819  * 'remainder' is the nonnegative number of 100-ns intervals
1820  * corresponding to the time fraction smaller than 1 second that
1821  * couldn't be stored in the time_t value.
1822  */
1823 void DOSFS_UnixTimeToFileTime( time_t unix_time, FILETIME *filetime,
1824                                DWORD remainder )
1825 {
1826     /* NOTES:
1827
1828        CONSTANTS: 
1829        The time difference between 1 January 1601, 00:00:00 and
1830        1 January 1970, 00:00:00 is 369 years, plus the leap years
1831        from 1604 to 1968, excluding 1700, 1800, 1900.
1832        This makes (1968 - 1600) / 4 - 3 = 89 leap days, and a total
1833        of 134774 days.
1834
1835        Any day in that period had 24 * 60 * 60 = 86400 seconds.
1836
1837        The time difference is 134774 * 86400 * 10000000, which can be written
1838        116444736000000000
1839        27111902 * 2^32 + 3577643008
1840        413 * 2^48 + 45534 * 2^32 + 54590 * 2^16 + 32768
1841
1842        If you find that these constants are buggy, please change them in all
1843        instances in both conversion functions.
1844
1845        VERSIONS:
1846        There are two versions, one of them uses long long variables and
1847        is presumably faster but not ISO C. The other one uses standard C
1848        data types and operations but relies on the assumption that negative
1849        numbers are stored as 2's complement (-1 is 0xffff....). If this
1850        assumption is violated, dates before 1970 will not convert correctly.
1851        This should however work on any reasonable architecture where WINE
1852        will run.
1853
1854        DETAILS:
1855        
1856        Take care not to remove the casts. I have tested these functions
1857        (in both versions) for a lot of numbers. I would be interested in
1858        results on other compilers than GCC.
1859
1860        The operations have been designed to account for the possibility
1861        of 64-bit time_t in future UNICES. Even the versions without
1862        internal long long numbers will work if time_t only is 64 bit.
1863        A 32-bit shift, which was necessary for that operation, turned out
1864        not to work correctly in GCC, besides giving the warning. So I
1865        used a double 16-bit shift instead. Numbers are in the ISO version
1866        represented by three limbs, the most significant with 32 bit, the
1867        other two with 16 bit each.
1868
1869        As the modulo-operator % is not well-defined for negative numbers,
1870        negative divisors have been avoided in DOSFS_FileTimeToUnixTime.
1871
1872        There might be quicker ways to do this in C. Certainly so in
1873        assembler.
1874
1875        Claus Fischer, fischer@iue.tuwien.ac.at
1876        */
1877
1878 #if SIZEOF_LONG_LONG >= 8
1879 #  define USE_LONG_LONG 1
1880 #else
1881 #  define USE_LONG_LONG 0
1882 #endif
1883
1884 #if USE_LONG_LONG               /* gcc supports long long type */
1885
1886     long long int t = unix_time;
1887     t *= 10000000;
1888     t += 116444736000000000LL;
1889     t += remainder;
1890     filetime->dwLowDateTime  = (UINT)t;
1891     filetime->dwHighDateTime = (UINT)(t >> 32);
1892
1893 #else  /* ISO version */
1894
1895     UINT a0;                    /* 16 bit, low    bits */
1896     UINT a1;                    /* 16 bit, medium bits */
1897     UINT a2;                    /* 32 bit, high   bits */
1898
1899     /* Copy the unix time to a2/a1/a0 */
1900     a0 =  unix_time & 0xffff;
1901     a1 = (unix_time >> 16) & 0xffff;
1902     /* This is obsolete if unix_time is only 32 bits, but it does not hurt.
1903        Do not replace this by >> 32, it gives a compiler warning and it does
1904        not work. */
1905     a2 = (unix_time >= 0 ? (unix_time >> 16) >> 16 :
1906           ~((~unix_time >> 16) >> 16));
1907
1908     /* Multiply a by 10000000 (a = a2/a1/a0)
1909        Split the factor into 10000 * 1000 which are both less than 0xffff. */
1910     a0 *= 10000;
1911     a1 = a1 * 10000 + (a0 >> 16);
1912     a2 = a2 * 10000 + (a1 >> 16);
1913     a0 &= 0xffff;
1914     a1 &= 0xffff;
1915
1916     a0 *= 1000;
1917     a1 = a1 * 1000 + (a0 >> 16);
1918     a2 = a2 * 1000 + (a1 >> 16);
1919     a0 &= 0xffff;
1920     a1 &= 0xffff;
1921
1922     /* Add the time difference and the remainder */
1923     a0 += 32768 + (remainder & 0xffff);
1924     a1 += 54590 + (remainder >> 16   ) + (a0 >> 16);
1925     a2 += 27111902                     + (a1 >> 16);
1926     a0 &= 0xffff;
1927     a1 &= 0xffff;
1928
1929     /* Set filetime */
1930     filetime->dwLowDateTime  = (a1 << 16) + a0;
1931     filetime->dwHighDateTime = a2;
1932 #endif
1933 }
1934
1935
1936 /***********************************************************************
1937  *           DOSFS_FileTimeToUnixTime
1938  *
1939  * Convert a FILETIME format to Unix time.
1940  * If not NULL, 'remainder' contains the fractional part of the filetime,
1941  * in the range of [0..9999999] (even if time_t is negative).
1942  */
1943 time_t DOSFS_FileTimeToUnixTime( const FILETIME *filetime, DWORD *remainder )
1944 {
1945     /* Read the comment in the function DOSFS_UnixTimeToFileTime. */
1946 #if USE_LONG_LONG
1947
1948     long long int t = filetime->dwHighDateTime;
1949     t <<= 32;
1950     t += (UINT)filetime->dwLowDateTime;
1951     t -= 116444736000000000LL;
1952     if (t < 0)
1953     {
1954         if (remainder) *remainder = 9999999 - (-t - 1) % 10000000;
1955         return -1 - ((-t - 1) / 10000000);
1956     }
1957     else
1958     {
1959         if (remainder) *remainder = t % 10000000;
1960         return t / 10000000;
1961     }
1962
1963 #else  /* ISO version */
1964
1965     UINT a0;                    /* 16 bit, low    bits */
1966     UINT a1;                    /* 16 bit, medium bits */
1967     UINT a2;                    /* 32 bit, high   bits */
1968     UINT r;                     /* remainder of division */
1969     unsigned int carry;         /* carry bit for subtraction */
1970     int negative;               /* whether a represents a negative value */
1971
1972     /* Copy the time values to a2/a1/a0 */
1973     a2 =  (UINT)filetime->dwHighDateTime;
1974     a1 = ((UINT)filetime->dwLowDateTime ) >> 16;
1975     a0 = ((UINT)filetime->dwLowDateTime ) & 0xffff;
1976
1977     /* Subtract the time difference */
1978     if (a0 >= 32768           ) a0 -=             32768        , carry = 0;
1979     else                        a0 += (1 << 16) - 32768        , carry = 1;
1980
1981     if (a1 >= 54590    + carry) a1 -=             54590 + carry, carry = 0;
1982     else                        a1 += (1 << 16) - 54590 - carry, carry = 1;
1983
1984     a2 -= 27111902 + carry;
1985     
1986     /* If a is negative, replace a by (-1-a) */
1987     negative = (a2 >= ((UINT)1) << 31);
1988     if (negative)
1989     {
1990         /* Set a to -a - 1 (a is a2/a1/a0) */
1991         a0 = 0xffff - a0;
1992         a1 = 0xffff - a1;
1993         a2 = ~a2;
1994     }
1995
1996     /* Divide a by 10000000 (a = a2/a1/a0), put the rest into r.
1997        Split the divisor into 10000 * 1000 which are both less than 0xffff. */
1998     a1 += (a2 % 10000) << 16;
1999     a2 /=       10000;
2000     a0 += (a1 % 10000) << 16;
2001     a1 /=       10000;
2002     r   =  a0 % 10000;
2003     a0 /=       10000;
2004
2005     a1 += (a2 % 1000) << 16;
2006     a2 /=       1000;
2007     a0 += (a1 % 1000) << 16;
2008     a1 /=       1000;
2009     r  += (a0 % 1000) * 10000;
2010     a0 /=       1000;
2011
2012     /* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
2013     if (negative)
2014     {
2015         /* Set a to -a - 1 (a is a2/a1/a0) */
2016         a0 = 0xffff - a0;
2017         a1 = 0xffff - a1;
2018         a2 = ~a2;
2019
2020         r  = 9999999 - r;
2021     }
2022
2023     if (remainder) *remainder = r;
2024
2025     /* Do not replace this by << 32, it gives a compiler warning and it does
2026        not work. */
2027     return ((((time_t)a2) << 16) << 16) + (a1 << 16) + a0;
2028 #endif
2029 }
2030
2031
2032 /***********************************************************************
2033  *           MulDiv   (KERNEL32.@)
2034  * RETURNS
2035  *      Result of multiplication and division
2036  *      -1: Overflow occurred or Divisor was 0
2037  */
2038 INT WINAPI MulDiv(
2039              INT nMultiplicand, 
2040              INT nMultiplier,
2041              INT nDivisor)
2042 {
2043 #if SIZEOF_LONG_LONG >= 8
2044     long long ret;
2045
2046     if (!nDivisor) return -1;
2047
2048     /* We want to deal with a positive divisor to simplify the logic. */
2049     if (nDivisor < 0)
2050     {
2051       nMultiplicand = - nMultiplicand;
2052       nDivisor = -nDivisor;
2053     }
2054
2055     /* If the result is positive, we "add" to round. else, we subtract to round. */
2056     if ( ( (nMultiplicand <  0) && (nMultiplier <  0) ) ||
2057          ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
2058       ret = (((long long)nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
2059     else
2060       ret = (((long long)nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
2061
2062     if ((ret > 2147483647) || (ret < -2147483647)) return -1;
2063     return ret;
2064 #else
2065     if (!nDivisor) return -1;
2066
2067     /* We want to deal with a positive divisor to simplify the logic. */
2068     if (nDivisor < 0)
2069     {
2070       nMultiplicand = - nMultiplicand;
2071       nDivisor = -nDivisor;
2072     }
2073
2074     /* If the result is positive, we "add" to round. else, we subtract to round. */
2075     if ( ( (nMultiplicand <  0) && (nMultiplier <  0) ) ||
2076          ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
2077       return ((nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
2078  
2079     return ((nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
2080     
2081 #endif
2082 }
2083
2084
2085 /***********************************************************************
2086  *           DosDateTimeToFileTime   (KERNEL32.@)
2087  */
2088 BOOL WINAPI DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft)
2089 {
2090     struct tm newtm;
2091
2092     newtm.tm_sec  = (fattime & 0x1f) * 2;
2093     newtm.tm_min  = (fattime >> 5) & 0x3f;
2094     newtm.tm_hour = (fattime >> 11);
2095     newtm.tm_mday = (fatdate & 0x1f);
2096     newtm.tm_mon  = ((fatdate >> 5) & 0x0f) - 1;
2097     newtm.tm_year = (fatdate >> 9) + 80;
2098     RtlSecondsSince1970ToTime( mktime( &newtm ), ft );
2099     return TRUE;
2100 }
2101
2102
2103 /***********************************************************************
2104  *           FileTimeToDosDateTime   (KERNEL32.@)
2105  */
2106 BOOL WINAPI FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate,
2107                                      LPWORD fattime )
2108 {
2109     time_t unixtime = DOSFS_FileTimeToUnixTime( ft, NULL );
2110     struct tm *tm = localtime( &unixtime );
2111     if (fattime)
2112         *fattime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
2113     if (fatdate)
2114         *fatdate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
2115                    + tm->tm_mday;
2116     return TRUE;
2117 }
2118
2119
2120 /***********************************************************************
2121  *           LocalFileTimeToFileTime   (KERNEL32.@)
2122  */
2123 BOOL WINAPI LocalFileTimeToFileTime( const FILETIME *localft,
2124                                        LPFILETIME utcft )
2125 {
2126     struct tm *xtm;
2127     DWORD remainder;
2128
2129     /* convert from local to UTC. Perhaps not correct. FIXME */
2130     time_t unixtime = DOSFS_FileTimeToUnixTime( localft, &remainder );
2131     xtm = gmtime( &unixtime );
2132     DOSFS_UnixTimeToFileTime( mktime(xtm), utcft, remainder );
2133     return TRUE; 
2134 }
2135
2136
2137 /***********************************************************************
2138  *           FileTimeToLocalFileTime   (KERNEL32.@)
2139  */
2140 BOOL WINAPI FileTimeToLocalFileTime( const FILETIME *utcft,
2141                                        LPFILETIME localft )
2142 {
2143     DWORD remainder;
2144     /* convert from UTC to local. Perhaps not correct. FIXME */
2145     time_t unixtime = DOSFS_FileTimeToUnixTime( utcft, &remainder );
2146 #ifdef HAVE_TIMEGM
2147     struct tm *xtm = localtime( &unixtime );
2148     time_t localtime;
2149
2150     localtime = timegm(xtm);
2151     DOSFS_UnixTimeToFileTime( localtime, localft, remainder );
2152
2153 #else
2154     struct tm *xtm,*gtm;
2155     time_t time1,time2;
2156
2157     xtm = localtime( &unixtime );
2158     gtm = gmtime( &unixtime );
2159     time1 = mktime(xtm);
2160     time2 = mktime(gtm);
2161     DOSFS_UnixTimeToFileTime( 2*time1-time2, localft, remainder );
2162 #endif
2163     return TRUE; 
2164 }
2165
2166
2167 /***********************************************************************
2168  *           FileTimeToSystemTime   (KERNEL32.@)
2169  */
2170 BOOL WINAPI FileTimeToSystemTime( const FILETIME *ft, LPSYSTEMTIME syst )
2171 {
2172     struct tm *xtm;
2173     DWORD remainder;
2174     time_t xtime = DOSFS_FileTimeToUnixTime( ft, &remainder );
2175     xtm = gmtime(&xtime);
2176     syst->wYear         = xtm->tm_year+1900;
2177     syst->wMonth        = xtm->tm_mon + 1;
2178     syst->wDayOfWeek    = xtm->tm_wday;
2179     syst->wDay          = xtm->tm_mday;
2180     syst->wHour         = xtm->tm_hour;
2181     syst->wMinute       = xtm->tm_min;
2182     syst->wSecond       = xtm->tm_sec;
2183     syst->wMilliseconds = remainder / 10000;
2184     return TRUE; 
2185 }
2186
2187 /***********************************************************************
2188  *           QueryDosDeviceA   (KERNEL32.@)
2189  *
2190  * returns array of strings terminated by \0, terminated by \0
2191  */
2192 DWORD WINAPI QueryDosDeviceA(LPCSTR devname,LPSTR target,DWORD bufsize)
2193 {
2194     LPSTR s;
2195     char  buffer[200];
2196
2197     TRACE("(%s,...)\n", devname ? devname : "<null>");
2198     if (!devname) {
2199         /* return known MSDOS devices */
2200         static const char devices[24] = "CON\0COM1\0COM2\0LPT1\0NUL\0\0";
2201         memcpy( target, devices, min(bufsize,sizeof(devices)) );
2202         return min(bufsize,sizeof(devices));
2203     }
2204     /* In theory all that are possible and have been defined.
2205      * Now just those below, since mirc uses it to check for special files.
2206      *
2207      * (It is more complex, and supports netmounted stuff, and \\.\ stuff, 
2208      *  but currently we just ignore that.)
2209      */
2210 #define CHECK(x) (strstr(devname,#x)==devname)
2211     if (CHECK(con) || CHECK(com) || CHECK(lpt) || CHECK(nul)) {
2212         strcpy(buffer,"\\DEV\\");
2213         strcat(buffer,devname);
2214         if ((s=strchr(buffer,':'))) *s='\0';
2215         lstrcpynA(target,buffer,bufsize);
2216         return strlen(buffer)+1;
2217     } else {
2218         if (strchr(devname,':') || devname[0]=='\\') {
2219             /* This might be a DOS device we do not handle yet ... */
2220             FIXME("(%s) not detected as DOS device!\n",devname);
2221         }
2222         SetLastError(ERROR_DEV_NOT_EXIST);
2223         return 0;
2224     }
2225
2226 }
2227
2228
2229 /***********************************************************************
2230  *           QueryDosDeviceW   (KERNEL32.@)
2231  *
2232  * returns array of strings terminated by \0, terminated by \0
2233  */
2234 DWORD WINAPI QueryDosDeviceW(LPCWSTR devname,LPWSTR target,DWORD bufsize)
2235 {
2236     LPSTR devnameA = devname?HEAP_strdupWtoA(GetProcessHeap(),0,devname):NULL;
2237     LPSTR targetA = (LPSTR)HeapAlloc(GetProcessHeap(),0,bufsize);
2238     DWORD ret = QueryDosDeviceA(devnameA,targetA,bufsize);
2239
2240     ret = MultiByteToWideChar( CP_ACP, 0, targetA, ret, target, bufsize );
2241     if (devnameA) HeapFree(GetProcessHeap(),0,devnameA);
2242     if (targetA) HeapFree(GetProcessHeap(),0,targetA);
2243     return ret;
2244 }
2245
2246
2247 /***********************************************************************
2248  *           SystemTimeToFileTime   (KERNEL32.@)
2249  */
2250 BOOL WINAPI SystemTimeToFileTime( const SYSTEMTIME *syst, LPFILETIME ft )
2251 {
2252 #ifdef HAVE_TIMEGM
2253     struct tm xtm;
2254     time_t utctime;
2255 #else
2256     struct tm xtm,*local_tm,*utc_tm;
2257     time_t localtim,utctime;
2258 #endif
2259
2260     xtm.tm_year = syst->wYear-1900;
2261     xtm.tm_mon  = syst->wMonth - 1;
2262     xtm.tm_wday = syst->wDayOfWeek;
2263     xtm.tm_mday = syst->wDay;
2264     xtm.tm_hour = syst->wHour;
2265     xtm.tm_min  = syst->wMinute;
2266     xtm.tm_sec  = syst->wSecond; /* this is UTC */
2267     xtm.tm_isdst = -1;
2268 #ifdef HAVE_TIMEGM
2269     utctime = timegm(&xtm);
2270     DOSFS_UnixTimeToFileTime( utctime, ft, 
2271                               syst->wMilliseconds * 10000 );
2272 #else
2273     localtim = mktime(&xtm);    /* now we've got local time */
2274     local_tm = localtime(&localtim);
2275     utc_tm = gmtime(&localtim);
2276     utctime = mktime(utc_tm);
2277     DOSFS_UnixTimeToFileTime( 2*localtim -utctime, ft, 
2278                               syst->wMilliseconds * 10000 );
2279 #endif
2280     return TRUE; 
2281 }
2282
2283 /***********************************************************************
2284  *           DefineDosDeviceA       (KERNEL32.@)
2285  */
2286 BOOL WINAPI DefineDosDeviceA(DWORD flags,LPCSTR devname,LPCSTR targetpath) {
2287         FIXME("(0x%08lx,%s,%s),stub!\n",flags,devname,targetpath);
2288         SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2289         return FALSE;
2290 }
2291
2292 /*
2293    --- 16 bit functions ---
2294 */
2295
2296 /*************************************************************************
2297  *           FindFirstFile   (KERNEL.413)
2298  */
2299 HANDLE16 WINAPI FindFirstFile16( LPCSTR path, WIN32_FIND_DATAA *data )
2300 {
2301     DOS_FULL_NAME full_name;
2302     HGLOBAL16 handle;
2303     FIND_FIRST_INFO *info;
2304
2305     data->dwReserved0 = data->dwReserved1 = 0x0;
2306     if (!path) return 0;
2307     if (!DOSFS_GetFullName( path, FALSE, &full_name ))
2308         return INVALID_HANDLE_VALUE16;
2309     if (!(handle = GlobalAlloc16( GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO) )))
2310         return INVALID_HANDLE_VALUE16;
2311     info = (FIND_FIRST_INFO *)GlobalLock16( handle );
2312     info->path = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
2313     strcpy( info->path, full_name.long_name );
2314     info->long_mask = strrchr( info->path, '/' );
2315     if (info->long_mask )
2316         *(info->long_mask++) = '\0';
2317     info->short_mask = NULL;
2318     info->attr = 0xff;
2319     if (path[0] && (path[1] == ':')) info->drive = FILE_toupper(*path) - 'A';
2320     else info->drive = DRIVE_GetCurrentDrive();
2321     info->cur_pos = 0;
2322
2323     info->dir = DOSFS_OpenDir( info->path );
2324
2325     GlobalUnlock16( handle );
2326     if (!FindNextFile16( handle, data ))
2327     {
2328         FindClose16( handle );
2329         SetLastError( ERROR_NO_MORE_FILES );
2330         return INVALID_HANDLE_VALUE16;
2331     }
2332     return handle;
2333 }
2334
2335 /*************************************************************************
2336  *           FindNextFile   (KERNEL.414)
2337  */
2338 BOOL16 WINAPI FindNextFile16( HANDLE16 handle, WIN32_FIND_DATAA *data )
2339 {
2340     FIND_FIRST_INFO *info;
2341
2342     if ((handle == INVALID_HANDLE_VALUE16) ||
2343        !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
2344     {
2345         SetLastError( ERROR_INVALID_HANDLE );
2346         return FALSE;
2347     }
2348     GlobalUnlock16( handle );
2349     if (!info->path || !info->dir)
2350     {
2351         SetLastError( ERROR_NO_MORE_FILES );
2352         return FALSE;
2353     }
2354     if (!DOSFS_FindNextEx( info, data ))
2355     {
2356         DOSFS_CloseDir( info->dir ); info->dir = NULL;
2357         HeapFree( GetProcessHeap(), 0, info->path );
2358         info->path = info->long_mask = NULL;
2359         SetLastError( ERROR_NO_MORE_FILES );
2360         return FALSE;
2361     }
2362     return TRUE;
2363 }
2364
2365 /*************************************************************************
2366  *           FindClose   (KERNEL.415)
2367  */
2368 BOOL16 WINAPI FindClose16( HANDLE16 handle )
2369 {
2370     FIND_FIRST_INFO *info;
2371
2372     if ((handle == INVALID_HANDLE_VALUE16) ||
2373         !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
2374     {
2375         SetLastError( ERROR_INVALID_HANDLE );
2376         return FALSE;
2377     }
2378     if (info->dir) DOSFS_CloseDir( info->dir );
2379     if (info->path) HeapFree( GetProcessHeap(), 0, info->path );
2380     GlobalUnlock16( handle );
2381     GlobalFree16( handle );
2382     return TRUE;
2383 }
2384