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