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