For static libs (.a) we need to pass the actual filename to winebuild,
[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 #include "wine/port.h"
24
25 #include <sys/types.h>
26 #include <ctype.h>
27 #include <dirent.h>
28 #include <errno.h>
29 #ifdef HAVE_SYS_ERRNO_H
30 #include <sys/errno.h>
31 #endif
32 #include <fcntl.h>
33 #include <stdarg.h>
34 #include <string.h>
35 #include <stdlib.h>
36 #include <sys/stat.h>
37 #ifdef HAVE_SYS_IOCTL_H
38 #include <sys/ioctl.h>
39 #endif
40 #include <time.h>
41 #ifdef HAVE_UNISTD_H
42 # include <unistd.h>
43 #endif
44
45 #define NONAMELESSUNION
46 #define NONAMELESSSTRUCT
47 #include "ntstatus.h"
48 #include "windef.h"
49 #include "winbase.h"
50 #include "winerror.h"
51 #include "wingdi.h"
52
53 #include "wine/unicode.h"
54 #include "wine/winbase16.h"
55 #include "drive.h"
56 #include "file.h"
57 #include "winternl.h"
58 #include "wine/server.h"
59 #include "wine/exception.h"
60 #include "excpt.h"
61
62 #include "smb.h"
63
64 #include "wine/debug.h"
65
66 WINE_DEFAULT_DEBUG_CHANNEL(dosfs);
67 WINE_DECLARE_DEBUG_CHANNEL(file);
68
69 /* Define the VFAT ioctl to get both short and long file names */
70 /* FIXME: is it possible to get this to work on other systems? */
71 #ifdef linux
72 /* We want the real kernel dirent structure, not the libc one */
73 typedef struct
74 {
75     long d_ino;
76     long d_off;
77     unsigned short d_reclen;
78     char d_name[256];
79 } KERNEL_DIRENT;
80
81 #define VFAT_IOCTL_READDIR_BOTH  _IOR('r', 1, KERNEL_DIRENT [2] )
82
83 /* To avoid blocking on non-directories in DOSFS_OpenDir_VFAT*/
84 #ifndef O_DIRECTORY
85 # define O_DIRECTORY    0200000 /* must be directory */
86 #endif
87
88 #else   /* linux */
89 #undef VFAT_IOCTL_READDIR_BOTH  /* just in case... */
90 #endif  /* linux */
91
92 #define IS_OPTION_TRUE(ch) ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1')
93
94 /* Chars we don't want to see in DOS file names */
95 #define INVALID_DOS_CHARS  "*?<>|\"+=,;[] \345"
96
97 /* DOS device descriptor */
98 typedef struct
99 {
100     const WCHAR name[5];
101 } DOS_DEVICE;
102
103 static const DOS_DEVICE DOSFS_Devices[] =
104 /* name, device flags (see Int 21/AX=0x4400) */
105 {
106     { {'C','O','N',0} },
107     { {'P','R','N',0} },
108     { {'N','U','L',0} },
109     { {'A','U','X',0} },
110     { {'L','P','T','1',0} },
111     { {'L','P','T','2',0} },
112     { {'L','P','T','3',0} },
113     { {'L','P','T','4',0} },
114     { {'C','O','M','1',0} },
115     { {'C','O','M','2',0} },
116     { {'C','O','M','3',0} },
117     { {'C','O','M','4',0} }
118 };
119
120 static const WCHAR devW[] = {'\\','D','e','v','i','c','e','\\',0};
121 static const WCHAR dosW[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\',0};
122
123 static const WCHAR auxW[] = {'A','U','X',0};
124 static const WCHAR comW[] = {'C','O','M',0};
125 static const WCHAR lptW[] = {'L','P','T',0};
126 static const WCHAR nulW[] = {'N','U','L',0};
127
128 static const WCHAR nullW[] = {'N','u','l','l',0};
129 static const WCHAR parW[] = {'P','a','r','a','l','l','e','l',0};
130 static const WCHAR serW[] = {'S','e','r','i','a','l',0};
131 static const WCHAR oneW[] = {'1',0};
132
133 /*
134  * Directory info for DOSFS_ReadDir
135  * contains the names of *all* the files in the directory
136  */
137 typedef struct
138 {
139     int used;
140     int size;
141     WCHAR names[1];
142 } DOS_DIR;
143
144 /* Info structure for FindFirstFile handle */
145 typedef struct
146 {
147     char *path; /* unix path */
148     LPWSTR long_mask;
149     int   drive;
150     int   cur_pos;
151     CRITICAL_SECTION cs;
152     union
153     {
154         DOS_DIR *dos_dir;
155         SMB_DIR *smb_dir;
156     } u;
157 } FIND_FIRST_INFO;
158
159
160 static WINE_EXCEPTION_FILTER(page_fault)
161 {
162     if (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION)
163         return EXCEPTION_EXECUTE_HANDLER;
164     return EXCEPTION_CONTINUE_SEARCH;
165 }
166
167
168 /***********************************************************************
169  *           DOSFS_ValidDOSName
170  *
171  * Return 1 if Unix file 'name' is also a valid MS-DOS name
172  * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
173  * File name can be terminated by '\0', '\\' or '/'.
174  */
175 static int DOSFS_ValidDOSName( LPCWSTR name, int ignore_case )
176 {
177     static const char invalid_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS;
178     const WCHAR *p = name;
179     const char *invalid = ignore_case ? (invalid_chars + 26) : invalid_chars;
180     int len = 0;
181
182     if (*p == '.')
183     {
184         /* Check for "." and ".." */
185         p++;
186         if (*p == '.') p++;
187         /* All other names beginning with '.' are invalid */
188         return (IS_END_OF_NAME(*p));
189     }
190     while (!IS_END_OF_NAME(*p))
191     {
192         if (*p < 256 && strchr( invalid, (char)*p )) return 0;  /* Invalid char */
193         if (*p == '.') break;  /* Start of the extension */
194         if (++len > 8) return 0;  /* Name too long */
195         p++;
196     }
197     if (*p != '.') return 1;  /* End of name */
198     p++;
199     if (IS_END_OF_NAME(*p)) return 0;  /* Empty extension not allowed */
200     len = 0;
201     while (!IS_END_OF_NAME(*p))
202     {
203         if (*p < 256 && strchr( invalid, (char)*p )) return 0;  /* Invalid char */
204         if (*p == '.') return 0;  /* Second extension not allowed */
205         if (++len > 3) return 0;  /* Extension too long */
206         p++;
207     }
208     return 1;
209 }
210
211
212 /***********************************************************************
213  *           DOSFS_ToDosFCBFormat
214  *
215  * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
216  * expanding wild cards and converting to upper-case in the process.
217  * File name can be terminated by '\0', '\\' or '/'.
218  * Return FALSE if the name is not a valid DOS name.
219  * 'buffer' must be at least 12 characters long.
220  */
221 BOOL DOSFS_ToDosFCBFormat( LPCWSTR name, LPWSTR buffer )
222 {
223     static const char invalid_chars[] = INVALID_DOS_CHARS;
224     LPCWSTR p = name;
225     int i;
226
227     /* Check for "." and ".." */
228     if (*p == '.')
229     {
230         p++;
231         buffer[0] = '.';
232         for(i = 1; i < 11; i++) buffer[i] = ' ';
233         buffer[11] = 0;
234         if (*p == '.')
235         {
236             buffer[1] = '.';
237             p++;
238         }
239         return (!*p || (*p == '/') || (*p == '\\'));
240     }
241
242     for (i = 0; i < 8; i++)
243     {
244         switch(*p)
245         {
246         case '\0':
247         case '\\':
248         case '/':
249         case '.':
250             buffer[i] = ' ';
251             break;
252         case '?':
253             p++;
254             /* fall through */
255         case '*':
256             buffer[i] = '?';
257             break;
258         default:
259             if (*p < 256 && strchr( invalid_chars, (char)*p )) return FALSE;
260             buffer[i] = toupperW(*p);
261             p++;
262             break;
263         }
264     }
265
266     if (*p == '*')
267     {
268         /* Skip all chars after wildcard up to first dot */
269         while (*p && (*p != '/') && (*p != '\\') && (*p != '.')) p++;
270     }
271     else
272     {
273         /* Check if name too long */
274         if (*p && (*p != '/') && (*p != '\\') && (*p != '.')) return FALSE;
275     }
276     if (*p == '.') p++;  /* Skip dot */
277
278     for (i = 8; i < 11; i++)
279     {
280         switch(*p)
281         {
282         case '\0':
283         case '\\':
284         case '/':
285             buffer[i] = ' ';
286             break;
287         case '.':
288             return FALSE;  /* Second extension not allowed */
289         case '?':
290             p++;
291             /* fall through */
292         case '*':
293             buffer[i] = '?';
294             break;
295         default:
296             if (*p < 256 && strchr( invalid_chars, (char)*p )) return FALSE;
297             buffer[i] = toupperW(*p);
298             p++;
299             break;
300         }
301     }
302     buffer[11] = '\0';
303
304     /* at most 3 character of the extension are processed
305      * is something behind this ?
306      */
307     while (*p == '*' || *p == ' ') p++; /* skip wildcards and spaces */
308     return IS_END_OF_NAME(*p);
309 }
310
311
312 /***********************************************************************
313  *           DOSFS_ToDosDTAFormat
314  *
315  * Convert a file name from FCB to DTA format (name.ext, null-terminated)
316  * converting to upper-case in the process.
317  * File name can be terminated by '\0', '\\' or '/'.
318  * 'buffer' must be at least 13 characters long.
319  */
320 static void DOSFS_ToDosDTAFormat( LPCWSTR name, LPWSTR buffer )
321 {
322     LPWSTR p;
323
324     memcpy( buffer, name, 8 * sizeof(WCHAR) );
325     p = buffer + 8;
326     while ((p > buffer) && (p[-1] == ' ')) p--;
327     *p++ = '.';
328     memcpy( p, name + 8, 3 * sizeof(WCHAR) );
329     p += 3;
330     while (p[-1] == ' ') p--;
331     if (p[-1] == '.') p--;
332     *p = '\0';
333 }
334
335
336 /***********************************************************************
337  *           DOSFS_MatchLong
338  *
339  * Check a long file name against a mask.
340  *
341  * Tests (done in W95 DOS shell - case insensitive):
342  * *.txt                        test1.test.txt                          *
343  * *st1*                        test1.txt                               *
344  * *.t??????.t*                 test1.ta.tornado.txt                    *
345  * *tornado*                    test1.ta.tornado.txt                    *
346  * t*t                          test1.ta.tornado.txt                    *
347  * ?est*                        test1.txt                               *
348  * ?est???                      test1.txt                               -
349  * *test1.txt*                  test1.txt                               *
350  * h?l?o*t.dat                  hellothisisatest.dat                    *
351  */
352 static int DOSFS_MatchLong( LPCWSTR mask, LPCWSTR name, int case_sensitive )
353 {
354     LPCWSTR lastjoker = NULL;
355     LPCWSTR next_to_retry = NULL;
356     static const WCHAR asterisk_dot_asterisk[] = {'*','.','*',0};
357
358     TRACE("(%s, %s, %x)\n", debugstr_w(mask), debugstr_w(name), case_sensitive);
359
360     if (!strcmpW( mask, asterisk_dot_asterisk )) return 1;
361     while (*name && *mask)
362     {
363         if (*mask == '*')
364         {
365             mask++;
366             while (*mask == '*') mask++;  /* Skip consecutive '*' */
367             lastjoker = mask;
368             if (!*mask) return 1; /* end of mask is all '*', so match */
369
370             /* skip to the next match after the joker(s) */
371             if (case_sensitive) while (*name && (*name != *mask)) name++;
372             else while (*name && (toupperW(*name) != toupperW(*mask))) name++;
373
374             if (!*name) break;
375             next_to_retry = name;
376         }
377         else if (*mask != '?')
378         {
379             int mismatch = 0;
380             if (case_sensitive)
381             {
382                 if (*mask != *name) mismatch = 1;
383             }
384             else
385             {
386                 if (toupperW(*mask) != toupperW(*name)) mismatch = 1;
387             }
388             if (!mismatch)
389             {
390                 mask++;
391                 name++;
392                 if (*mask == '\0')
393                 {
394                     if (*name == '\0')
395                         return 1;
396                     if (lastjoker)
397                         mask = lastjoker;
398                 }
399             }
400             else /* mismatch ! */
401             {
402                 if (lastjoker) /* we had an '*', so we can try unlimitedly */
403                 {
404                     mask = lastjoker;
405
406                     /* this scan sequence was a mismatch, so restart
407                      * 1 char after the first char we checked last time */
408                     next_to_retry++;
409                     name = next_to_retry;
410                 }
411                 else
412                     return 0; /* bad luck */
413             }
414         }
415         else /* '?' */
416         {
417             mask++;
418             name++;
419         }
420     }
421     while ((*mask == '.') || (*mask == '*'))
422         mask++;  /* Ignore trailing '.' or '*' in mask */
423     return (!*name && !*mask);
424 }
425
426
427 /***********************************************************************
428  *           DOSFS_AddDirEntry
429  *
430  *  Used to construct an array of filenames in DOSFS_OpenDir
431  */
432 static BOOL DOSFS_AddDirEntry(DOS_DIR **dir, LPCWSTR name, LPCWSTR dosname)
433 {
434     int extra1 = strlenW(name) + 1;
435     int extra2 = strlenW(dosname) + 1;
436
437     /* if we need more, at minimum double the size */
438     if( (extra1 + extra2 + (*dir)->used) > (*dir)->size)
439     {
440         int more = (*dir)->size;
441         DOS_DIR *t;
442
443         if(more<(extra1+extra2))
444             more = extra1+extra2;
445
446         t = HeapReAlloc(GetProcessHeap(), 0, *dir, sizeof(**dir) + 
447                         ((*dir)->size + more)*sizeof(WCHAR) );
448         if(!t)
449         {
450             SetLastError( ERROR_NOT_ENOUGH_MEMORY );
451             ERR("Out of memory caching directory structure %d %d %d\n",
452                  (*dir)->size, more, (*dir)->used);
453             return FALSE;
454         }
455         (*dir) = t;
456         (*dir)->size += more;
457     }
458
459     /* at this point, the dir structure is big enough to hold these names */
460     strcpyW(&(*dir)->names[(*dir)->used], name);
461     (*dir)->used += extra1;
462     strcpyW(&(*dir)->names[(*dir)->used], dosname);
463     (*dir)->used += extra2;
464
465     return TRUE;
466 }
467
468
469 /***********************************************************************
470  *           DOSFS_OpenDir_VFAT
471  */
472 static BOOL DOSFS_OpenDir_VFAT(DOS_DIR **dir, const char *unix_path)
473 {
474 #ifdef VFAT_IOCTL_READDIR_BOTH
475     KERNEL_DIRENT de[2];
476     int fd = open( unix_path, O_RDONLY|O_DIRECTORY );
477     BOOL r = TRUE;
478
479     /* Check if the VFAT ioctl is supported on this directory */
480
481     if ( fd<0 )
482         return FALSE;
483
484     while (1)
485     {
486         WCHAR long_name[MAX_PATH];
487         WCHAR short_name[12];
488
489         r = (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de ) != -1);
490         if(!r)
491             break;
492         if (!de[0].d_reclen)
493             break;
494         MultiByteToWideChar(CP_UNIXCP, 0, de[0].d_name, -1, long_name, MAX_PATH);
495         if (!DOSFS_ToDosFCBFormat( long_name, short_name ))
496             short_name[0] = '\0';
497         if (de[1].d_name[0])
498             MultiByteToWideChar(CP_UNIXCP, 0, de[1].d_name, -1, long_name, MAX_PATH);
499         else
500             MultiByteToWideChar(CP_UNIXCP, 0, de[0].d_name, -1, long_name, MAX_PATH);
501         r = DOSFS_AddDirEntry(dir, long_name, short_name );
502         if(!r)
503             break;
504     }
505     if(r)
506     {
507         static const WCHAR empty_strW[] = { 0 };
508         DOSFS_AddDirEntry(dir, empty_strW, empty_strW);
509     }
510     close(fd);
511     return r;
512 #else
513     return FALSE;
514 #endif  /* VFAT_IOCTL_READDIR_BOTH */
515 }
516
517
518 /***********************************************************************
519  *           DOSFS_OpenDir_Normal
520  *
521  * Now use the standard opendir/readdir interface
522  */
523 static BOOL DOSFS_OpenDir_Normal( DOS_DIR **dir, const char *unix_path )
524 {
525     DIR *unixdir = opendir( unix_path );
526     BOOL r = TRUE;
527     static const WCHAR empty_strW[] = { 0 };
528
529     if(!unixdir)
530         return FALSE;
531     while(1)
532     {
533         WCHAR long_name[MAX_PATH];
534         struct dirent *de = readdir(unixdir);
535
536         if(!de)
537             break;
538         MultiByteToWideChar(CP_UNIXCP, 0, de->d_name, -1, long_name, MAX_PATH);
539         r = DOSFS_AddDirEntry(dir, long_name, empty_strW);
540         if(!r)
541             break;
542     }
543     if(r)
544         DOSFS_AddDirEntry(dir, empty_strW, empty_strW);
545     closedir(unixdir);
546     return r;
547 }
548
549 /***********************************************************************
550  *           DOSFS_OpenDir
551  */
552 static DOS_DIR *DOSFS_OpenDir( const char *unix_path )
553 {
554     const int init_size = 0x100;
555     DOS_DIR *dir = HeapAlloc( GetProcessHeap(), 0, sizeof(*dir) + init_size*sizeof (WCHAR));
556     BOOL r;
557
558     TRACE("%s\n",debugstr_a(unix_path));
559
560     if (!dir)
561     {
562         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
563         return NULL;
564     }
565     dir->used = 0;
566     dir->size = init_size;
567
568     /* Treat empty path as root directory. This simplifies path split into
569        directory and mask in several other places */
570     if (!*unix_path) unix_path = "/";
571
572     r = DOSFS_OpenDir_VFAT( &dir, unix_path);
573
574     if(!r)
575         r = DOSFS_OpenDir_Normal( &dir, unix_path);
576
577     if(!r)
578     {
579         HeapFree(GetProcessHeap(), 0, dir);
580         return NULL;
581     }
582     dir->used = 0;
583
584     return dir;
585 }
586
587
588 /***********************************************************************
589  *           DOSFS_CloseDir
590  */
591 static void DOSFS_CloseDir( DOS_DIR *dir )
592 {
593     HeapFree( GetProcessHeap(), 0, dir );
594 }
595
596
597 /***********************************************************************
598  *           DOSFS_ReadDir
599  */
600 static BOOL DOSFS_ReadDir( DOS_DIR *dir, LPCWSTR *long_name,
601                              LPCWSTR *short_name )
602 {
603     LPCWSTR sn, ln;
604
605     if (!dir)
606        return FALSE;
607
608     /* the long pathname is first */
609     ln = &dir->names[dir->used];
610     if(ln[0])
611         *long_name  = ln;
612     else
613         return FALSE;
614     dir->used += (strlenW(ln) + 1);
615
616     /* followed by the short path name */
617     sn = &dir->names[dir->used];
618     if(sn[0])
619         *short_name = sn;
620     else
621         *short_name = NULL;
622     dir->used += (strlenW(sn) + 1);
623
624     return TRUE;
625 }
626
627
628 /***********************************************************************
629  *           DOSFS_Hash
630  *
631  * Transform a Unix file name into a hashed DOS name. If the name is a valid
632  * DOS name, it is converted to upper-case; otherwise it is replaced by a
633  * hashed version that fits in 8.3 format.
634  * File name can be terminated by '\0', '\\' or '/'.
635  * 'buffer' must be at least 13 characters long.
636  */
637 static void DOSFS_Hash( LPCWSTR name, LPWSTR buffer, BOOL dir_format,
638                         BOOL ignore_case )
639 {
640     static const char invalid_chars[] = INVALID_DOS_CHARS "~.";
641     static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
642
643     LPCWSTR p, ext;
644     LPWSTR dst;
645     unsigned short hash;
646     int i;
647
648     if (dir_format)
649     {
650         for(i = 0; i < 11; i++) buffer[i] = ' ';
651         buffer[11] = 0;
652     }
653
654     if (DOSFS_ValidDOSName( name, ignore_case ))
655     {
656         /* Check for '.' and '..' */
657         if (*name == '.')
658         {
659             buffer[0] = '.';
660             if (!dir_format) buffer[1] = buffer[2] = '\0';
661             if (name[1] == '.') buffer[1] = '.';
662             return;
663         }
664
665         /* Simply copy the name, converting to uppercase */
666
667         for (dst = buffer; !IS_END_OF_NAME(*name) && (*name != '.'); name++)
668             *dst++ = toupperW(*name);
669         if (*name == '.')
670         {
671             if (dir_format) dst = buffer + 8;
672             else *dst++ = '.';
673             for (name++; !IS_END_OF_NAME(*name); name++)
674                 *dst++ = toupperW(*name);
675         }
676         if (!dir_format) *dst = '\0';
677         return;
678     }
679
680     /* Compute the hash code of the file name */
681     /* If you know something about hash functions, feel free to */
682     /* insert a better algorithm here... */
683     if (ignore_case)
684     {
685         for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
686             hash = (hash<<3) ^ (hash>>5) ^ tolowerW(*p) ^ (tolowerW(p[1]) << 8);
687         hash = (hash<<3) ^ (hash>>5) ^ tolowerW(*p); /* Last character */
688     }
689     else
690     {
691         for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
692             hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
693         hash = (hash << 3) ^ (hash >> 5) ^ *p;  /* Last character */
694     }
695
696     /* Find last dot for start of the extension */
697     for (p = name+1, ext = NULL; !IS_END_OF_NAME(*p); p++)
698         if (*p == '.') ext = p;
699     if (ext && IS_END_OF_NAME(ext[1]))
700         ext = NULL;  /* Empty extension ignored */
701
702     /* Copy first 4 chars, replacing invalid chars with '_' */
703     for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
704     {
705         if (IS_END_OF_NAME(*p) || (p == ext)) break;
706         *dst++ = (*p < 256 && strchr( invalid_chars, (char)*p )) ? '_' : toupperW(*p);
707     }
708     /* Pad to 5 chars with '~' */
709     while (i-- >= 0) *dst++ = '~';
710
711     /* Insert hash code converted to 3 ASCII chars */
712     *dst++ = hash_chars[(hash >> 10) & 0x1f];
713     *dst++ = hash_chars[(hash >> 5) & 0x1f];
714     *dst++ = hash_chars[hash & 0x1f];
715
716     /* Copy the first 3 chars of the extension (if any) */
717     if (ext)
718     {
719         if (!dir_format) *dst++ = '.';
720         for (i = 3, ext++; (i > 0) && !IS_END_OF_NAME(*ext); i--, ext++)
721             *dst++ = (*ext < 256 && strchr( invalid_chars, (char)*ext )) ? '_' : toupperW(*ext);
722     }
723     if (!dir_format) *dst = '\0';
724 }
725
726
727 /***********************************************************************
728  *           DOSFS_FindUnixName
729  *
730  * Find the Unix file name in a given directory that corresponds to
731  * a file name (either in Unix or DOS format).
732  * File name can be terminated by '\0', '\\' or '/'.
733  * Return TRUE if OK, FALSE if no file name matches.
734  *
735  * 'long_buf' must be at least 'long_len' characters long. If the long name
736  * turns out to be larger than that, the function returns FALSE.
737  * 'short_buf' must be at least 13 characters long.
738  */
739 BOOL DOSFS_FindUnixName( const DOS_FULL_NAME *path, LPCWSTR name, char *long_buf,
740                          INT long_len, LPWSTR short_buf, BOOL ignore_case)
741 {
742     DOS_DIR *dir;
743     LPCWSTR long_name, short_name;
744     WCHAR dos_name[12], tmp_buf[13];
745     BOOL ret;
746
747     LPCWSTR p = strchrW( name, '/' );
748     int len = p ? (int)(p - name) : strlenW(name);
749     if ((p = strchrW( name, '\\' ))) len = min( (int)(p - name), len );
750     /* Ignore trailing dots and spaces */
751     while (len > 1 && (name[len-1] == '.' || name[len-1] == ' ')) len--;
752     if (long_len < len + 1) return FALSE;
753
754     TRACE("%s,%s\n", path->long_name, debugstr_w(name) );
755
756     if (!DOSFS_ToDosFCBFormat( name, dos_name )) dos_name[0] = '\0';
757
758     if (!(dir = DOSFS_OpenDir( path->long_name )))
759     {
760         WARN("(%s,%s): can't open dir: %s\n",
761              path->long_name, debugstr_w(name), strerror(errno) );
762         return FALSE;
763     }
764
765     while ((ret = DOSFS_ReadDir( dir, &long_name, &short_name )))
766     {
767         /* Check against Unix name */
768         if (len == strlenW(long_name))
769         {
770             if (!ignore_case)
771             {
772                 if (!strncmpW( long_name, name, len )) break;
773             }
774             else
775             {
776                 if (!strncmpiW( long_name, name, len )) break;
777             }
778         }
779         if (dos_name[0])
780         {
781             /* Check against hashed DOS name */
782             if (!short_name)
783             {
784                 DOSFS_Hash( long_name, tmp_buf, TRUE, ignore_case );
785                 short_name = tmp_buf;
786             }
787             if (!strcmpW( dos_name, short_name )) break;
788         }
789     }
790     if (ret)
791     {
792         if (long_buf) WideCharToMultiByte(CP_UNIXCP, 0, long_name, -1, long_buf, long_len, NULL, NULL);
793         if (short_buf)
794         {
795             if (short_name)
796                 DOSFS_ToDosDTAFormat( short_name, short_buf );
797             else
798                 DOSFS_Hash( long_name, short_buf, FALSE, ignore_case );
799         }
800         TRACE("(%s,%s) -> %s (%s)\n", path->long_name, debugstr_w(name),
801               debugstr_w(long_name), short_buf ? debugstr_w(short_buf) : "***");
802     }
803     else
804         WARN("%s not found in '%s'\n", debugstr_w(name), path->long_name);
805     DOSFS_CloseDir( dir );
806     return ret;
807 }
808
809
810 /**************************************************************************
811  *         DOSFS_CreateCommPort
812  */
813 static HANDLE DOSFS_CreateCommPort(LPCWSTR name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa)
814 {
815     HANDLE ret;
816     HKEY hkey;
817     DWORD dummy;
818     OBJECT_ATTRIBUTES attr;
819     UNICODE_STRING nameW;
820     WCHAR *devnameW;
821     char tmp[128];
822     char devname[40];
823
824     static const WCHAR serialportsW[] = {'M','a','c','h','i','n','e','\\',
825                                          'S','o','f','t','w','a','r','e','\\',
826                                          'W','i','n','e','\\','W','i','n','e','\\',
827                                          'C','o','n','f','i','g','\\',
828                                          'S','e','r','i','a','l','P','o','r','t','s',0};
829
830     TRACE_(file)("%s %lx %lx\n", debugstr_w(name), access, attributes);
831
832     attr.Length = sizeof(attr);
833     attr.RootDirectory = 0;
834     attr.ObjectName = &nameW;
835     attr.Attributes = 0;
836     attr.SecurityDescriptor = NULL;
837     attr.SecurityQualityOfService = NULL;
838     RtlInitUnicodeString( &nameW, serialportsW );
839
840     if (NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr )) return 0;
841
842     RtlInitUnicodeString( &nameW, name );
843     if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
844         devnameW = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
845     else
846         devnameW = NULL;
847
848     NtClose( hkey );
849
850     if (!devnameW) return 0;
851     WideCharToMultiByte(CP_ACP, 0, devnameW, -1, devname, sizeof(devname), NULL, NULL);
852
853     TRACE("opening %s as %s\n", devname, debugstr_w(name));
854
855     SERVER_START_REQ( create_serial )
856     {
857         req->access  = access;
858         req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
859         req->attributes = attributes;
860         req->sharing = FILE_SHARE_READ|FILE_SHARE_WRITE;
861         wine_server_add_data( req, devname, strlen(devname) );
862         SetLastError(0);
863         wine_server_call_err( req );
864         ret = reply->handle;
865     }
866     SERVER_END_REQ;
867
868     if(!ret)
869         ERR("Couldn't open device '%s' ! (check permissions)\n",devname);
870     else
871         TRACE("return %p\n", ret );
872     return ret;
873 }
874
875 /***********************************************************************
876  *           DOSFS_OpenDevice
877  *
878  * Open a DOS device. This might not map 1:1 into the UNIX device concept.
879  * Returns 0 on failure.
880  */
881 HANDLE DOSFS_OpenDevice( LPCWSTR name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa )
882 {
883     unsigned int i;
884     const WCHAR *p;
885     HANDLE handle;
886
887     if (name[0] && (name[1] == ':')) name += 2;
888     if ((p = strrchrW( name, '/' ))) name = p + 1;
889     if ((p = strrchrW( name, '\\' ))) name = p + 1;
890     for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
891     {
892         const WCHAR *dev = DOSFS_Devices[i].name;
893         if (!strncmpiW( dev, name, strlenW(dev) ))
894         {
895             p = name + strlenW( dev );
896             if (!*p || (*p == '.') || (*p == ':')) {
897                 static const WCHAR nulW[] = {'N','U','L',0};
898                 static const WCHAR conW[] = {'C','O','N',0};
899                 /* got it */
900                 if (!strcmpiW(DOSFS_Devices[i].name, nulW))
901                     return FILE_CreateFile( "/dev/null", access,
902                                             FILE_SHARE_READ|FILE_SHARE_WRITE, sa,
903                                             OPEN_EXISTING, 0, 0, TRUE, DRIVE_UNKNOWN );
904                 if (!strcmpiW(DOSFS_Devices[i].name, conW)) {
905                         HANDLE to_dup;
906                         switch (access & (GENERIC_READ|GENERIC_WRITE)) {
907                         case GENERIC_READ:
908                                 to_dup = GetStdHandle( STD_INPUT_HANDLE );
909                                 break;
910                         case GENERIC_WRITE:
911                                 to_dup = GetStdHandle( STD_OUTPUT_HANDLE );
912                                 break;
913                         default:
914                                 FIXME("can't open CON read/write\n");
915                                 return 0;
916                         }
917                         if (!DuplicateHandle( GetCurrentProcess(), to_dup, GetCurrentProcess(),
918                                               &handle, 0,
919                                               sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle,
920                                               DUPLICATE_SAME_ACCESS ))
921                             handle = 0;
922                         return handle;
923                 }
924
925                 if( (handle=DOSFS_CreateCommPort(DOSFS_Devices[i].name,access,attributes,sa)) )
926                     return handle;
927                 FIXME("device open %s not supported (yet)\n", debugstr_w(DOSFS_Devices[i].name));
928                 return 0;
929             }
930         }
931     }
932     return 0;
933 }
934
935
936 /***********************************************************************
937  *           DOSFS_GetPathDrive
938  *
939  * Get the drive specified by a given path name (DOS or Unix format).
940  */
941 static int DOSFS_GetPathDrive( LPCWSTR *name )
942 {
943     int drive;
944     LPCWSTR p = *name;
945
946     if (*p && (p[1] == ':'))
947     {
948         drive = toupperW(*p) - 'A';
949         *name += 2;
950     }
951     else if (*p == '/') /* Absolute Unix path? */
952     {
953         if ((drive = DRIVE_FindDriveRootW( name )) == -1)
954         {
955             MESSAGE("Warning: %s not accessible from a configured DOS drive\n", debugstr_w(*name) );
956             /* Assume it really was a DOS name */
957             drive = DRIVE_GetCurrentDrive();
958         }
959     }
960     else drive = DRIVE_GetCurrentDrive();
961
962     if (!DRIVE_IsValid(drive))
963     {
964         SetLastError( ERROR_INVALID_DRIVE );
965         return -1;
966     }
967     return drive;
968 }
969
970
971 /***********************************************************************
972  *           DOSFS_GetFullName
973  *
974  * Convert a file name (DOS or mixed DOS/Unix format) to a valid
975  * Unix name / short DOS name pair.
976  * Return FALSE if one of the path components does not exist. The last path
977  * component is only checked if 'check_last' is non-zero.
978  * The buffers pointed to by 'long_buf' and 'short_buf' must be
979  * at least MAX_PATHNAME_LEN long.
980  */
981 BOOL DOSFS_GetFullName( LPCWSTR name, BOOL check_last, DOS_FULL_NAME *full )
982 {
983     BOOL found;
984     UINT flags;
985     char *p_l, *root;
986     LPWSTR p_s;
987     static const WCHAR driveA_rootW[] = {'A',':','\\',0};
988     static const WCHAR dos_rootW[] = {'\\',0};
989
990     TRACE("%s (last=%d)\n", debugstr_w(name), check_last );
991
992     if ((!*name) || (*name=='\n'))
993     { /* error code for Win98 */
994         SetLastError(ERROR_BAD_PATHNAME);
995         return FALSE;
996     }
997
998     if ((full->drive = DOSFS_GetPathDrive( &name )) == -1) return FALSE;
999     flags = DRIVE_GetFlags( full->drive );
1000
1001     lstrcpynA( full->long_name, DRIVE_GetRoot( full->drive ),
1002                  sizeof(full->long_name) );
1003     if (full->long_name[1]) root = full->long_name + strlen(full->long_name);
1004     else root = full->long_name;  /* root directory */
1005
1006     strcpyW( full->short_name, driveA_rootW );
1007     full->short_name[0] += full->drive;
1008
1009     if ((*name == '\\') || (*name == '/'))  /* Absolute path */
1010     {
1011         while ((*name == '\\') || (*name == '/')) name++;
1012     }
1013     else  /* Relative path */
1014     {
1015         lstrcpynA( root + 1, DRIVE_GetUnixCwd( full->drive ),
1016                      sizeof(full->long_name) - (root - full->long_name) - 1 );
1017         if (root[1]) *root = '/';
1018         lstrcpynW( full->short_name + 3, DRIVE_GetDosCwd( full->drive ),
1019                    sizeof(full->short_name)/sizeof(full->short_name[0]) - 3 );
1020     }
1021
1022     p_l = full->long_name[1] ? full->long_name + strlen(full->long_name)
1023                              : full->long_name;
1024     p_s = full->short_name[3] ? full->short_name + strlenW(full->short_name)
1025                               : full->short_name + 2;
1026     found = TRUE;
1027
1028     while (*name && found)
1029     {
1030         /* Check for '.' and '..' */
1031
1032         if (*name == '.')
1033         {
1034             if (IS_END_OF_NAME(name[1]))
1035             {
1036                 name++;
1037                 while ((*name == '\\') || (*name == '/')) name++;
1038                 continue;
1039             }
1040             else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
1041             {
1042                 name += 2;
1043                 while ((*name == '\\') || (*name == '/')) name++;
1044                 while ((p_l > root) && (*p_l != '/')) p_l--;
1045                 while ((p_s > full->short_name + 2) && (*p_s != '\\')) p_s--;
1046                 *p_l = *p_s = '\0';  /* Remove trailing separator */
1047                 continue;
1048             }
1049         }
1050
1051         /* Make sure buffers are large enough */
1052
1053         if ((p_s >= full->short_name + sizeof(full->short_name)/sizeof(full->short_name[0]) - 14) ||
1054             (p_l >= full->long_name + sizeof(full->long_name) - 1))
1055         {
1056             SetLastError( ERROR_PATH_NOT_FOUND );
1057             return FALSE;
1058         }
1059
1060         /* Get the long and short name matching the file name */
1061
1062         if ((found = DOSFS_FindUnixName( full, name, p_l + 1,
1063                          sizeof(full->long_name) - (p_l - full->long_name) - 1,
1064                          p_s + 1, !(flags & DRIVE_CASE_SENSITIVE) )))
1065         {
1066             *p_l++ = '/';
1067             p_l   += strlen(p_l);
1068             *p_s++ = '\\';
1069             p_s   += strlenW(p_s);
1070             while (!IS_END_OF_NAME(*name)) name++;
1071         }
1072         else if (!check_last)
1073         {
1074             *p_l++ = '/';
1075             *p_s++ = '\\';
1076             while (!IS_END_OF_NAME(*name) &&
1077                    (p_s < full->short_name + sizeof(full->short_name)/sizeof(full->short_name[0]) - 1) &&
1078                    (p_l < full->long_name + sizeof(full->long_name) - 1))
1079             {
1080                 WCHAR wch;
1081                 *p_s++ = tolowerW(*name);
1082                 /* If the drive is case-sensitive we want to create new */
1083                 /* files in lower-case otherwise we can't reopen them   */
1084                 /* under the same short name. */
1085                 if (flags & DRIVE_CASE_SENSITIVE) wch = tolowerW(*name);
1086                 else wch = *name;
1087                 p_l += WideCharToMultiByte(CP_UNIXCP, 0, &wch, 1, p_l, 2, NULL, NULL);
1088                 name++;
1089             }
1090             /* Ignore trailing dots and spaces */
1091             while(p_l[-1] == '.' || p_l[-1] == ' ') {
1092                 --p_l;
1093                 --p_s;
1094             }
1095             *p_l = '\0';
1096             *p_s = '\0';
1097         }
1098         while ((*name == '\\') || (*name == '/')) name++;
1099     }
1100
1101     if (!found)
1102     {
1103         if (check_last)
1104         {
1105             SetLastError( ERROR_FILE_NOT_FOUND );
1106             return FALSE;
1107         }
1108         if (*name)  /* Not last */
1109         {
1110             SetLastError( ERROR_PATH_NOT_FOUND );
1111             return FALSE;
1112         }
1113     }
1114     if (!full->long_name[0]) strcpy( full->long_name, "/" );
1115     if (!full->short_name[2]) strcpyW( full->short_name + 2, dos_rootW );
1116     TRACE("returning %s = %s\n", full->long_name, debugstr_w(full->short_name) );
1117     return TRUE;
1118 }
1119
1120
1121 /***********************************************************************
1122  *           wine_get_unix_file_name (KERNEL32.@) Not a Windows API
1123  *
1124  * Return the full Unix file name for a given path.
1125  */
1126 BOOL WINAPI wine_get_unix_file_name( LPCWSTR dosW, LPSTR buffer, DWORD len )
1127 {
1128     BOOL ret;
1129     DOS_FULL_NAME path;
1130
1131     ret = DOSFS_GetFullName( dosW, FALSE, &path );
1132     if (ret && len)
1133     {
1134         strncpy( buffer, path.long_name, len );
1135         buffer[len - 1] = 0; /* ensure 0 termination */
1136     }
1137     return ret;
1138 }
1139
1140
1141 /***********************************************************************
1142  *           get_show_dir_symlinks_option
1143  */
1144 static BOOL get_show_dir_symlinks_option(void)
1145 {
1146     static const WCHAR WineW[] = {'M','a','c','h','i','n','e','\\',
1147                                   'S','o','f','t','w','a','r','e','\\',
1148                                   'W','i','n','e','\\','W','i','n','e','\\',
1149                                   'C','o','n','f','i','g','\\','W','i','n','e',0};
1150     static const WCHAR ShowDirSymlinksW[] = {'S','h','o','w','D','i','r','S','y','m','l','i','n','k','s',0};
1151
1152     char tmp[80];
1153     HKEY hkey;
1154     DWORD dummy;
1155     OBJECT_ATTRIBUTES attr;
1156     UNICODE_STRING nameW;
1157     BOOL ret = FALSE;
1158
1159     attr.Length = sizeof(attr);
1160     attr.RootDirectory = 0;
1161     attr.ObjectName = &nameW;
1162     attr.Attributes = 0;
1163     attr.SecurityDescriptor = NULL;
1164     attr.SecurityQualityOfService = NULL;
1165     RtlInitUnicodeString( &nameW, WineW );
1166
1167     if (!NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr ))
1168     {
1169         RtlInitUnicodeString( &nameW, ShowDirSymlinksW );
1170         if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
1171         {
1172             WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
1173             ret = IS_OPTION_TRUE( str[0] );
1174         }
1175         NtClose( hkey );
1176     }
1177     return ret;
1178 }
1179
1180
1181 /***********************************************************************
1182  *           DOSFS_FindNextEx
1183  */
1184 static int DOSFS_FindNextEx( FIND_FIRST_INFO *info, WIN32_FIND_DATAW *entry )
1185 {
1186     UINT flags = DRIVE_GetFlags( info->drive );
1187     char *p, buffer[MAX_PATHNAME_LEN];
1188     const char *drive_path;
1189     int drive_root;
1190     LPCWSTR long_name, short_name;
1191     BY_HANDLE_FILE_INFORMATION fileinfo;
1192     BOOL is_symlink;
1193
1194     drive_path = info->path + strlen(DRIVE_GetRoot( info->drive ));
1195     while ((*drive_path == '/') || (*drive_path == '\\')) drive_path++;
1196     drive_root = !*drive_path;
1197
1198     lstrcpynA( buffer, info->path, sizeof(buffer) - 1 );
1199     strcat( buffer, "/" );
1200     p = buffer + strlen(buffer);
1201
1202     while (DOSFS_ReadDir( info->u.dos_dir, &long_name, &short_name ))
1203     {
1204         info->cur_pos++;
1205
1206         /* Don't return '.' and '..' in the root of the drive */
1207         if (drive_root && (long_name[0] == '.') &&
1208             (!long_name[1] || ((long_name[1] == '.') && !long_name[2])))
1209             continue;
1210
1211         /* Check the long mask */
1212
1213         if (info->long_mask && *info->long_mask)
1214         {
1215             if (!DOSFS_MatchLong( info->long_mask, long_name,
1216                                   flags & DRIVE_CASE_SENSITIVE )) continue;
1217         }
1218
1219         /* Check the file attributes */
1220         WideCharToMultiByte(CP_UNIXCP, 0, long_name, -1,
1221                             p, sizeof(buffer) - (int)(p - buffer), NULL, NULL);
1222         if (!FILE_Stat( buffer, &fileinfo, &is_symlink ))
1223         {
1224             WARN("can't stat %s\n", buffer);
1225             continue;
1226         }
1227         if (is_symlink && (fileinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
1228         {
1229             static int show_dir_symlinks = -1;
1230             if (show_dir_symlinks == -1)
1231                 show_dir_symlinks = get_show_dir_symlinks_option();
1232             if (!show_dir_symlinks) continue;
1233         }
1234
1235         /* We now have a matching entry; fill the result and return */
1236
1237         entry->dwFileAttributes = fileinfo.dwFileAttributes;
1238         entry->ftCreationTime   = fileinfo.ftCreationTime;
1239         entry->ftLastAccessTime = fileinfo.ftLastAccessTime;
1240         entry->ftLastWriteTime  = fileinfo.ftLastWriteTime;
1241         entry->nFileSizeHigh    = fileinfo.nFileSizeHigh;
1242         entry->nFileSizeLow     = fileinfo.nFileSizeLow;
1243
1244         if (short_name)
1245             DOSFS_ToDosDTAFormat( short_name, entry->cAlternateFileName );
1246         else
1247             DOSFS_Hash( long_name, entry->cAlternateFileName, FALSE,
1248                         !(flags & DRIVE_CASE_SENSITIVE) );
1249
1250         lstrcpynW( entry->cFileName, long_name, sizeof(entry->cFileName)/sizeof(entry->cFileName[0]) );
1251         if (!(flags & DRIVE_CASE_PRESERVING)) strlwrW( entry->cFileName );
1252         TRACE("returning %s (%s) %02lx %ld\n",
1253               debugstr_w(entry->cFileName), debugstr_w(entry->cAlternateFileName),
1254               entry->dwFileAttributes, entry->nFileSizeLow );
1255         return 1;
1256     }
1257     return 0;  /* End of directory */
1258 }
1259
1260 /*************************************************************************
1261  *           FindFirstFileExW  (KERNEL32.@)
1262  */
1263 HANDLE WINAPI FindFirstFileExW(
1264         LPCWSTR lpFileName,
1265         FINDEX_INFO_LEVELS fInfoLevelId,
1266         LPVOID lpFindFileData,
1267         FINDEX_SEARCH_OPS fSearchOp,
1268         LPVOID lpSearchFilter,
1269         DWORD dwAdditionalFlags)
1270 {
1271     FIND_FIRST_INFO *info;
1272
1273     if (!lpFileName)
1274     {
1275         SetLastError(ERROR_PATH_NOT_FOUND);
1276         return INVALID_HANDLE_VALUE;
1277     }
1278
1279     if ((fSearchOp != FindExSearchNameMatch) || (dwAdditionalFlags != 0))
1280     {
1281         FIXME("options not implemented 0x%08x 0x%08lx\n", fSearchOp, dwAdditionalFlags );
1282         return INVALID_HANDLE_VALUE;
1283     }
1284
1285     switch(fInfoLevelId)
1286     {
1287       case FindExInfoStandard:
1288         {
1289           WIN32_FIND_DATAW * data = (WIN32_FIND_DATAW *) lpFindFileData;
1290           char *p;
1291           INT long_mask_len;
1292
1293           data->dwReserved0 = data->dwReserved1 = 0x0;
1294           if (lpFileName[0] == '\\' && lpFileName[1] == '\\')
1295           {
1296               ERR("UNC path name\n");
1297               if (!(info = HeapAlloc( GetProcessHeap(), 0, sizeof(FIND_FIRST_INFO)))) break;
1298               info->u.smb_dir = SMB_FindFirst(lpFileName);
1299               if(!info->u.smb_dir)
1300               {
1301                  HeapFree(GetProcessHeap(), 0, info);
1302                  break;
1303               }
1304               info->drive = -1;
1305               RtlInitializeCriticalSection( &info->cs );
1306           }
1307           else
1308           {
1309             DOS_FULL_NAME full_name;
1310
1311             if (lpFileName[0] && lpFileName[1] == ':')
1312             {
1313                 /* don't allow root directories */
1314                 if (!lpFileName[2] ||
1315                     ((lpFileName[2] == '/' || lpFileName[2] == '\\') && !lpFileName[3]))
1316                 {
1317                     SetLastError(ERROR_FILE_NOT_FOUND);
1318                     return INVALID_HANDLE_VALUE;
1319                 }
1320             }
1321             if (!DOSFS_GetFullName( lpFileName, FALSE, &full_name )) break;
1322             if (!(info = HeapAlloc( GetProcessHeap(), 0, sizeof(FIND_FIRST_INFO)))) break;
1323             RtlInitializeCriticalSection( &info->cs );
1324             info->path = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
1325             strcpy( info->path, full_name.long_name );
1326
1327             p = strrchr( info->path, '/' );
1328             *p++ = '\0';
1329             long_mask_len = MultiByteToWideChar(CP_UNIXCP, 0, p, -1, NULL, 0);
1330             info->long_mask = HeapAlloc( GetProcessHeap(), 0, long_mask_len * sizeof(WCHAR) );
1331             MultiByteToWideChar(CP_UNIXCP, 0, p, -1, info->long_mask, long_mask_len);
1332
1333             info->drive = full_name.drive;
1334             info->cur_pos = 0;
1335
1336             info->u.dos_dir = DOSFS_OpenDir( info->path );
1337           }
1338           if (!FindNextFileW( (HANDLE) info, data ))
1339           {
1340               FindClose( (HANDLE) info );
1341               SetLastError( ERROR_FILE_NOT_FOUND );
1342               break;
1343           }
1344           return (HANDLE) info;
1345         }
1346         break;
1347       default:
1348         FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
1349     }
1350     return INVALID_HANDLE_VALUE;
1351 }
1352
1353 /*************************************************************************
1354  *           FindFirstFileA   (KERNEL32.@)
1355  */
1356 HANDLE WINAPI FindFirstFileA(
1357         LPCSTR lpFileName,
1358         WIN32_FIND_DATAA *lpFindData )
1359 {
1360     return FindFirstFileExA(lpFileName, FindExInfoStandard, lpFindData,
1361                             FindExSearchNameMatch, NULL, 0);
1362 }
1363
1364 /*************************************************************************
1365  *           FindFirstFileExA   (KERNEL32.@)
1366  */
1367 HANDLE WINAPI FindFirstFileExA(
1368         LPCSTR lpFileName,
1369         FINDEX_INFO_LEVELS fInfoLevelId,
1370         LPVOID lpFindFileData,
1371         FINDEX_SEARCH_OPS fSearchOp,
1372         LPVOID lpSearchFilter,
1373         DWORD dwAdditionalFlags)
1374 {
1375     HANDLE handle;
1376     WIN32_FIND_DATAA *dataA;
1377     WIN32_FIND_DATAW dataW;
1378     UNICODE_STRING pathW;
1379
1380     if (!lpFileName)
1381     {
1382         SetLastError(ERROR_PATH_NOT_FOUND);
1383         return INVALID_HANDLE_VALUE;
1384     }
1385
1386     if (!RtlCreateUnicodeStringFromAsciiz(&pathW, lpFileName))
1387     {
1388         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1389         return INVALID_HANDLE_VALUE;
1390     }
1391
1392     handle = FindFirstFileExW(pathW.Buffer, fInfoLevelId, &dataW, fSearchOp, lpSearchFilter, dwAdditionalFlags);
1393     RtlFreeUnicodeString(&pathW);
1394     if (handle == INVALID_HANDLE_VALUE) return handle;
1395
1396     dataA = (WIN32_FIND_DATAA *) lpFindFileData;
1397     dataA->dwFileAttributes = dataW.dwFileAttributes;
1398     dataA->ftCreationTime   = dataW.ftCreationTime;
1399     dataA->ftLastAccessTime = dataW.ftLastAccessTime;
1400     dataA->ftLastWriteTime  = dataW.ftLastWriteTime;
1401     dataA->nFileSizeHigh    = dataW.nFileSizeHigh;
1402     dataA->nFileSizeLow     = dataW.nFileSizeLow;
1403     WideCharToMultiByte( CP_ACP, 0, dataW.cFileName, -1,
1404                          dataA->cFileName, sizeof(dataA->cFileName), NULL, NULL );
1405     WideCharToMultiByte( CP_ACP, 0, dataW.cAlternateFileName, -1,
1406                          dataA->cAlternateFileName, sizeof(dataA->cAlternateFileName), NULL, NULL );
1407     return handle;
1408 }
1409
1410 /*************************************************************************
1411  *           FindFirstFileW   (KERNEL32.@)
1412  */
1413 HANDLE WINAPI FindFirstFileW( LPCWSTR lpFileName, WIN32_FIND_DATAW *lpFindData )
1414 {
1415     return FindFirstFileExW(lpFileName, FindExInfoStandard, lpFindData,
1416                             FindExSearchNameMatch, NULL, 0);
1417 }
1418
1419 /*************************************************************************
1420  *           FindNextFileW   (KERNEL32.@)
1421  */
1422 BOOL WINAPI FindNextFileW( HANDLE handle, WIN32_FIND_DATAW *data )
1423 {
1424     FIND_FIRST_INFO *info;
1425     BOOL ret = FALSE;
1426     DWORD gle = ERROR_NO_MORE_FILES;
1427
1428     if (handle == INVALID_HANDLE_VALUE)
1429     {
1430         SetLastError( ERROR_INVALID_HANDLE );
1431         return ret;
1432     }
1433     info = (FIND_FIRST_INFO*) handle;
1434     RtlEnterCriticalSection( &info->cs );
1435     if (info->drive == -1)
1436     {
1437         ret = SMB_FindNext( info->u.smb_dir, data );
1438         if(!ret)
1439         {
1440             SMB_CloseDir( info->u.smb_dir );
1441             HeapFree( GetProcessHeap(), 0, info->path );
1442         }
1443         goto done;
1444     }
1445     else if (!info->path || !info->u.dos_dir)
1446     {
1447         goto done;
1448     }
1449     else if (!DOSFS_FindNextEx( info, data ))
1450     {
1451         DOSFS_CloseDir( info->u.dos_dir ); info->u.dos_dir = NULL;
1452         HeapFree( GetProcessHeap(), 0, info->path );
1453         info->path = NULL;
1454         HeapFree( GetProcessHeap(), 0, info->long_mask );
1455         info->long_mask = NULL;
1456         goto done;
1457     }
1458     ret = TRUE;
1459 done:
1460     RtlLeaveCriticalSection( &info->cs );
1461     if( !ret ) SetLastError( gle );
1462     return ret;
1463 }
1464
1465
1466 /*************************************************************************
1467  *           FindNextFileA   (KERNEL32.@)
1468  */
1469 BOOL WINAPI FindNextFileA( HANDLE handle, WIN32_FIND_DATAA *data )
1470 {
1471     WIN32_FIND_DATAW dataW;
1472     if (!FindNextFileW( handle, &dataW )) return FALSE;
1473     data->dwFileAttributes = dataW.dwFileAttributes;
1474     data->ftCreationTime   = dataW.ftCreationTime;
1475     data->ftLastAccessTime = dataW.ftLastAccessTime;
1476     data->ftLastWriteTime  = dataW.ftLastWriteTime;
1477     data->nFileSizeHigh    = dataW.nFileSizeHigh;
1478     data->nFileSizeLow     = dataW.nFileSizeLow;
1479     WideCharToMultiByte( CP_ACP, 0, dataW.cFileName, -1,
1480                          data->cFileName, sizeof(data->cFileName), NULL, NULL );
1481     WideCharToMultiByte( CP_ACP, 0, dataW.cAlternateFileName, -1,
1482                          data->cAlternateFileName,
1483                          sizeof(data->cAlternateFileName), NULL, NULL );
1484     return TRUE;
1485 }
1486
1487 /*************************************************************************
1488  *           FindClose   (KERNEL32.@)
1489  */
1490 BOOL WINAPI FindClose( HANDLE handle )
1491 {
1492     FIND_FIRST_INFO *info = (FIND_FIRST_INFO*) handle;
1493
1494     if (handle == INVALID_HANDLE_VALUE) goto error;
1495
1496     __TRY
1497     {
1498         RtlEnterCriticalSection( &info->cs );
1499         if (info)
1500         {
1501             if (info->u.dos_dir) DOSFS_CloseDir( info->u.dos_dir );
1502             if (info->path) HeapFree( GetProcessHeap(), 0, info->path );
1503             if (info->long_mask) HeapFree( GetProcessHeap(), 0, info->long_mask );
1504         }
1505     }
1506     __EXCEPT(page_fault)
1507     {
1508         WARN("Illegal handle %p\n", handle);
1509         SetLastError( ERROR_INVALID_HANDLE );
1510         return FALSE;
1511     }
1512     __ENDTRY
1513     if (!info) goto error;
1514     RtlLeaveCriticalSection( &info->cs );
1515     RtlDeleteCriticalSection( &info->cs );
1516     HeapFree(GetProcessHeap(), 0, info);
1517     return TRUE;
1518
1519  error:
1520     SetLastError( ERROR_INVALID_HANDLE );
1521     return FALSE;
1522 }
1523
1524 /***********************************************************************
1525  *           MulDiv   (KERNEL32.@)
1526  * RETURNS
1527  *      Result of multiplication and division
1528  *      -1: Overflow occurred or Divisor was 0
1529  */
1530 INT WINAPI MulDiv(
1531              INT nMultiplicand,
1532              INT nMultiplier,
1533              INT nDivisor)
1534 {
1535 #if SIZEOF_LONG_LONG >= 8
1536     long long ret;
1537
1538     if (!nDivisor) return -1;
1539
1540     /* We want to deal with a positive divisor to simplify the logic. */
1541     if (nDivisor < 0)
1542     {
1543       nMultiplicand = - nMultiplicand;
1544       nDivisor = -nDivisor;
1545     }
1546
1547     /* If the result is positive, we "add" to round. else, we subtract to round. */
1548     if ( ( (nMultiplicand <  0) && (nMultiplier <  0) ) ||
1549          ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
1550       ret = (((long long)nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
1551     else
1552       ret = (((long long)nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
1553
1554     if ((ret > 2147483647) || (ret < -2147483647)) return -1;
1555     return ret;
1556 #else
1557     if (!nDivisor) return -1;
1558
1559     /* We want to deal with a positive divisor to simplify the logic. */
1560     if (nDivisor < 0)
1561     {
1562       nMultiplicand = - nMultiplicand;
1563       nDivisor = -nDivisor;
1564     }
1565
1566     /* If the result is positive, we "add" to round. else, we subtract to round. */
1567     if ( ( (nMultiplicand <  0) && (nMultiplier <  0) ) ||
1568          ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
1569       return ((nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
1570
1571     return ((nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
1572
1573 #endif
1574 }
1575
1576
1577 /***********************************************************************
1578  *           DosDateTimeToFileTime   (KERNEL32.@)
1579  */
1580 BOOL WINAPI DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft)
1581 {
1582     struct tm newtm;
1583 #ifndef HAVE_TIMEGM
1584     struct tm *gtm;
1585     time_t time1, time2;
1586 #endif
1587
1588     newtm.tm_sec  = (fattime & 0x1f) * 2;
1589     newtm.tm_min  = (fattime >> 5) & 0x3f;
1590     newtm.tm_hour = (fattime >> 11);
1591     newtm.tm_mday = (fatdate & 0x1f);
1592     newtm.tm_mon  = ((fatdate >> 5) & 0x0f) - 1;
1593     newtm.tm_year = (fatdate >> 9) + 80;
1594 #ifdef HAVE_TIMEGM
1595     RtlSecondsSince1970ToTime( timegm(&newtm), (LARGE_INTEGER *)ft );
1596 #else
1597     time1 = mktime(&newtm);
1598     gtm = gmtime(&time1);
1599     time2 = mktime(gtm);
1600     RtlSecondsSince1970ToTime( 2*time1-time2, (LARGE_INTEGER *)ft );
1601 #endif
1602     return TRUE;
1603 }
1604
1605
1606 /***********************************************************************
1607  *           FileTimeToDosDateTime   (KERNEL32.@)
1608  */
1609 BOOL WINAPI FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate,
1610                                      LPWORD fattime )
1611 {
1612     LARGE_INTEGER       li;
1613     ULONG               t;
1614     time_t              unixtime;
1615     struct tm*          tm;
1616
1617     li.u.LowPart = ft->dwLowDateTime;
1618     li.u.HighPart = ft->dwHighDateTime;
1619     RtlTimeToSecondsSince1970( &li, &t );
1620     unixtime = t;
1621     tm = gmtime( &unixtime );
1622     if (fattime)
1623         *fattime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
1624     if (fatdate)
1625         *fatdate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
1626                    + tm->tm_mday;
1627     return TRUE;
1628 }
1629
1630
1631 /***********************************************************************
1632  *           QueryDosDeviceA   (KERNEL32.@)
1633  *
1634  * returns array of strings terminated by \0, terminated by \0
1635  */
1636 DWORD WINAPI QueryDosDeviceA(LPCSTR devname,LPSTR target,DWORD bufsize)
1637 {
1638     DWORD ret = 0, retW;
1639     LPWSTR targetW = (LPWSTR)HeapAlloc(GetProcessHeap(),0,
1640                                      bufsize * sizeof(WCHAR));
1641     UNICODE_STRING devnameW;
1642
1643     if(devname) RtlCreateUnicodeStringFromAsciiz(&devnameW, devname);
1644     else devnameW.Buffer = NULL;
1645
1646     retW = QueryDosDeviceW(devnameW.Buffer, targetW, bufsize);
1647
1648     ret = WideCharToMultiByte(CP_ACP, 0, targetW, retW, target,
1649                                         bufsize, NULL, NULL);
1650
1651     RtlFreeUnicodeString(&devnameW);
1652     if (targetW) HeapFree(GetProcessHeap(),0,targetW);
1653     return ret;
1654 }
1655
1656
1657 /***********************************************************************
1658  *           QueryDosDeviceW   (KERNEL32.@)
1659  *
1660  * returns array of strings terminated by \0, terminated by \0
1661  *
1662  * FIXME
1663  *      - Win9x returns for all calls ERROR_INVALID_PARAMETER 
1664  *      - the returned devices for devname == NULL is far from complete
1665  *      - its not checked that the returned device exist
1666  */
1667 DWORD WINAPI QueryDosDeviceW(LPCWSTR devname,LPWSTR target,DWORD bufsize)
1668 {
1669     const WCHAR *pDev, *pName, *pNum = NULL;
1670     int    numsiz=0;
1671     DWORD  ret;
1672
1673     TRACE("(%s,...)\n", debugstr_w(devname));
1674     if (!devname) {
1675         /* return known MSDOS devices */
1676         DWORD ret = 0;
1677         int i;
1678         static const WCHAR devices[][5] = {{'A','U','X',0},
1679                                            {'C','O','M','1',0},
1680                                            {'C','O','M','2',0},
1681                                            {'L','P','T','1',0},
1682                                            {'N','U','L',0,}};
1683         for(i=0; (i< (sizeof(devices)/sizeof(devices[0]))); i++) {
1684             DWORD len = strlenW(devices[i]);
1685             if(target && (bufsize >= ret + len + 2)) {
1686                 strcpyW(target+ret, devices[i]);
1687                 ret += len + 1;
1688             } else {
1689                 /* in this case WinXP returns 0 */
1690                 FIXME("function return is wrong for WinXP!\n");
1691                 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1692                 break;
1693             }
1694         }
1695         /* append drives here */
1696         if(target && bufsize > 0) target[ret++] = 0;
1697         FIXME("Returned list is not complete\n");
1698         return ret;
1699     }
1700     /* In theory all that are possible and have been defined.
1701      * Now just those below, since mirc uses it to check for special files.
1702      *
1703      * (It is more complex, and supports netmounted stuff, and \\.\ stuff,
1704      *  but currently we just ignore that.)
1705      */
1706     if (!strcmpiW(devname, auxW)) {
1707         pDev   = dosW;
1708         pName  = comW;
1709         numsiz = 1;
1710         pNum   = oneW;
1711     } else if (!strcmpiW(devname, nulW)) {
1712         pDev  = devW;
1713         pName = nullW;
1714     } else if (!strncmpiW(devname, comW, strlenW(comW))) {
1715         pDev  = devW;
1716         pName = serW;
1717         pNum  = devname + strlenW(comW);
1718         for(numsiz=0; isdigitW(*(pNum+numsiz)); numsiz++);
1719         if(*(pNum + numsiz)) {
1720             SetLastError(ERROR_FILE_NOT_FOUND);
1721             return 0;
1722         }
1723     } else if (!strncmpiW(devname, lptW, strlenW(lptW))) {
1724         pDev  = devW;
1725         pName = parW;
1726         pNum  = devname + strlenW(lptW);
1727         for(numsiz=0; isdigitW(*(pNum+numsiz)); numsiz++);
1728         if(*(pNum + numsiz)) {
1729             SetLastError(ERROR_FILE_NOT_FOUND);
1730         return 0;
1731     }
1732     } else {
1733         /* This might be a DOS device we do not handle yet ... */
1734         FIXME("(%s) not detected as DOS device!\n",debugstr_w(devname));
1735
1736         /* Win9x set the error ERROR_INVALID_PARAMETER */
1737         SetLastError(ERROR_FILE_NOT_FOUND);
1738         return 0;
1739 }
1740     FIXME("device %s may not exist on this computer\n", debugstr_w(devname));
1741
1742     ret = strlenW(pDev) + strlenW(pName) + numsiz + 2;
1743     if (ret > bufsize) ret = 0;
1744     if (target && ret) {
1745         strcpyW(target,pDev);
1746         strcatW(target,pName);
1747         if (pNum) strcatW(target,pNum);
1748         target[ret-1] = 0;
1749     }
1750     return ret;
1751 }