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