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