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