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