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