Use sigsetjmp instead of setjmp in Wine internal exception handlers to
[wine/wine64.git] / files / dos_fs.c
blob3712c00b6616a7fee0090ee5060515032371ceb4
1 /*
2 * DOS file system functions
4 * Copyright 1993 Erik Bos
5 * Copyright 1996 Alexandre Julliard
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.
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.
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
22 #include "config.h"
24 #include <sys/types.h>
25 #include <ctype.h>
26 #include <dirent.h>
27 #include <errno.h>
28 #ifdef HAVE_SYS_ERRNO_H
29 #include <sys/errno.h>
30 #endif
31 #include <fcntl.h>
32 #include <stdarg.h>
33 #include <string.h>
34 #include <stdlib.h>
35 #include <sys/stat.h>
36 #ifdef HAVE_SYS_IOCTL_H
37 #include <sys/ioctl.h>
38 #endif
39 #include <time.h>
40 #ifdef HAVE_UNISTD_H
41 # include <unistd.h>
42 #endif
44 #define NONAMELESSUNION
45 #define NONAMELESSSTRUCT
46 #include "ntstatus.h"
47 #include "windef.h"
48 #include "winbase.h"
49 #include "winerror.h"
50 #include "wingdi.h"
52 #include "wine/unicode.h"
53 #include "wine/winbase16.h"
54 #include "drive.h"
55 #include "file.h"
56 #include "heap.h"
57 #include "msdos.h"
58 #include "winternl.h"
59 #include "wine/server.h"
60 #include "excpt.h"
62 #include "smb.h"
64 #include "wine/debug.h"
66 WINE_DEFAULT_DEBUG_CHANNEL(dosfs);
67 WINE_DECLARE_DEBUG_CHANNEL(file);
69 /* Define the VFAT ioctl to get both short and long file names */
70 /* FIXME: is it possible to get this to work on other systems? */
71 #ifdef linux
72 /* We want the real kernel dirent structure, not the libc one */
73 typedef struct
75 long d_ino;
76 long d_off;
77 unsigned short d_reclen;
78 char d_name[256];
79 } KERNEL_DIRENT;
81 #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, KERNEL_DIRENT [2] )
83 /* To avoid blocking on non-directories in DOSFS_OpenDir_VFAT*/
84 #ifndef O_DIRECTORY
85 # define O_DIRECTORY 0200000 /* must be directory */
86 #endif
88 #else /* linux */
89 #undef VFAT_IOCTL_READDIR_BOTH /* just in case... */
90 #endif /* linux */
92 #define IS_OPTION_TRUE(ch) ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1')
94 /* Chars we don't want to see in DOS file names */
95 #define INVALID_DOS_CHARS "*?<>|\"+=,;[] \345"
97 static const DOS_DEVICE DOSFS_Devices[] =
98 /* name, device flags (see Int 21/AX=0x4400) */
100 { {'C','O','N',0}, 0xc0d3 },
101 { {'P','R','N',0}, 0xa0c0 },
102 { {'N','U','L',0}, 0x80c4 },
103 { {'A','U','X',0}, 0x80c0 },
104 { {'L','P','T','1',0}, 0xa0c0 },
105 { {'L','P','T','2',0}, 0xa0c0 },
106 { {'L','P','T','3',0}, 0xa0c0 },
107 { {'L','P','T','4',0}, 0xc0d3 },
108 { {'C','O','M','1',0}, 0x80c0 },
109 { {'C','O','M','2',0}, 0x80c0 },
110 { {'C','O','M','3',0}, 0x80c0 },
111 { {'C','O','M','4',0}, 0x80c0 },
112 { {'S','C','S','I','M','G','R','$',0}, 0xc0c0 },
113 { {'H','P','S','C','A','N',0}, 0xc0c0 },
114 { {'E','M','M','X','X','X','X','0',0}, 0x0000 }
117 static const WCHAR devW[] = {'\\','D','e','v','i','c','e','\\',0};
118 static const WCHAR dosW[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\',0};
120 static const WCHAR auxW[] = {'A','U','X',0};
121 static const WCHAR comW[] = {'C','O','M',0};
122 static const WCHAR lptW[] = {'L','P','T',0};
123 static const WCHAR nulW[] = {'N','U','L',0};
125 static const WCHAR nullW[] = {'N','u','l','l',0};
126 static const WCHAR parW[] = {'P','a','r','a','l','l','e','l',0};
127 static const WCHAR serW[] = {'S','e','r','i','a','l',0};
128 static const WCHAR oneW[] = {'1',0};
131 * Directory info for DOSFS_ReadDir
132 * contains the names of *all* the files in the directory
134 typedef struct
136 int used;
137 int size;
138 WCHAR names[1];
139 } DOS_DIR;
141 /* Info structure for FindFirstFile handle */
142 typedef struct
144 char *path; /* unix path */
145 LPWSTR long_mask;
146 LPWSTR short_mask;
147 BYTE attr;
148 int drive;
149 int cur_pos;
150 CRITICAL_SECTION cs;
151 union
153 DOS_DIR *dos_dir;
154 SMB_DIR *smb_dir;
155 } u;
156 } FIND_FIRST_INFO;
159 static WINE_EXCEPTION_FILTER(page_fault)
161 if (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION)
162 return EXCEPTION_EXECUTE_HANDLER;
163 return EXCEPTION_CONTINUE_SEARCH;
167 /***********************************************************************
168 * DOSFS_ValidDOSName
170 * Return 1 if Unix file 'name' is also a valid MS-DOS name
171 * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
172 * File name can be terminated by '\0', '\\' or '/'.
174 static int DOSFS_ValidDOSName( LPCWSTR name, int ignore_case )
176 static const char invalid_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS;
177 const WCHAR *p = name;
178 const char *invalid = ignore_case ? (invalid_chars + 26) : invalid_chars;
179 int len = 0;
181 if (*p == '.')
183 /* Check for "." and ".." */
184 p++;
185 if (*p == '.') p++;
186 /* All other names beginning with '.' are invalid */
187 return (IS_END_OF_NAME(*p));
189 while (!IS_END_OF_NAME(*p))
191 if (*p < 256 && strchr( invalid, (char)*p )) return 0; /* Invalid char */
192 if (*p == '.') break; /* Start of the extension */
193 if (++len > 8) return 0; /* Name too long */
194 p++;
196 if (*p != '.') return 1; /* End of name */
197 p++;
198 if (IS_END_OF_NAME(*p)) return 0; /* Empty extension not allowed */
199 len = 0;
200 while (!IS_END_OF_NAME(*p))
202 if (*p < 256 && strchr( invalid, (char)*p )) return 0; /* Invalid char */
203 if (*p == '.') return 0; /* Second extension not allowed */
204 if (++len > 3) return 0; /* Extension too long */
205 p++;
207 return 1;
211 /***********************************************************************
212 * DOSFS_ToDosFCBFormat
214 * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
215 * expanding wild cards and converting to upper-case in the process.
216 * File name can be terminated by '\0', '\\' or '/'.
217 * Return FALSE if the name is not a valid DOS name.
218 * 'buffer' must be at least 12 characters long.
220 BOOL DOSFS_ToDosFCBFormat( LPCWSTR name, LPWSTR buffer )
222 static const char invalid_chars[] = INVALID_DOS_CHARS;
223 LPCWSTR p = name;
224 int i;
226 /* Check for "." and ".." */
227 if (*p == '.')
229 p++;
230 buffer[0] = '.';
231 for(i = 1; i < 11; i++) buffer[i] = ' ';
232 buffer[11] = 0;
233 if (*p == '.')
235 buffer[1] = '.';
236 p++;
238 return (!*p || (*p == '/') || (*p == '\\'));
241 for (i = 0; i < 8; i++)
243 switch(*p)
245 case '\0':
246 case '\\':
247 case '/':
248 case '.':
249 buffer[i] = ' ';
250 break;
251 case '?':
252 p++;
253 /* fall through */
254 case '*':
255 buffer[i] = '?';
256 break;
257 default:
258 if (*p < 256 && strchr( invalid_chars, (char)*p )) return FALSE;
259 buffer[i] = toupperW(*p);
260 p++;
261 break;
265 if (*p == '*')
267 /* Skip all chars after wildcard up to first dot */
268 while (*p && (*p != '/') && (*p != '\\') && (*p != '.')) p++;
270 else
272 /* Check if name too long */
273 if (*p && (*p != '/') && (*p != '\\') && (*p != '.')) return FALSE;
275 if (*p == '.') p++; /* Skip dot */
277 for (i = 8; i < 11; i++)
279 switch(*p)
281 case '\0':
282 case '\\':
283 case '/':
284 buffer[i] = ' ';
285 break;
286 case '.':
287 return FALSE; /* Second extension not allowed */
288 case '?':
289 p++;
290 /* fall through */
291 case '*':
292 buffer[i] = '?';
293 break;
294 default:
295 if (*p < 256 && strchr( invalid_chars, (char)*p )) return FALSE;
296 buffer[i] = toupperW(*p);
297 p++;
298 break;
301 buffer[11] = '\0';
303 /* at most 3 character of the extension are processed
304 * is something behind this ?
306 while (*p == '*' || *p == ' ') p++; /* skip wildcards and spaces */
307 return IS_END_OF_NAME(*p);
311 /***********************************************************************
312 * DOSFS_ToDosDTAFormat
314 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
315 * converting to upper-case in the process.
316 * File name can be terminated by '\0', '\\' or '/'.
317 * 'buffer' must be at least 13 characters long.
319 static void DOSFS_ToDosDTAFormat( LPCWSTR name, LPWSTR buffer )
321 LPWSTR p;
323 memcpy( buffer, name, 8 * sizeof(WCHAR) );
324 p = buffer + 8;
325 while ((p > buffer) && (p[-1] == ' ')) p--;
326 *p++ = '.';
327 memcpy( p, name + 8, 3 * sizeof(WCHAR) );
328 p += 3;
329 while (p[-1] == ' ') p--;
330 if (p[-1] == '.') p--;
331 *p = '\0';
335 /***********************************************************************
336 * DOSFS_MatchShort
338 * Check a DOS file name against a mask (both in FCB format).
340 static int DOSFS_MatchShort( LPCWSTR mask, LPCWSTR name )
342 int i;
343 for (i = 11; i > 0; i--, mask++, name++)
344 if ((*mask != '?') && (*mask != *name)) return 0;
345 return 1;
349 /***********************************************************************
350 * DOSFS_MatchLong
352 * Check a long file name against a mask.
354 * Tests (done in W95 DOS shell - case insensitive):
355 * *.txt test1.test.txt *
356 * *st1* test1.txt *
357 * *.t??????.t* test1.ta.tornado.txt *
358 * *tornado* test1.ta.tornado.txt *
359 * t*t test1.ta.tornado.txt *
360 * ?est* test1.txt *
361 * ?est??? test1.txt -
362 * *test1.txt* test1.txt *
363 * h?l?o*t.dat hellothisisatest.dat *
365 static int DOSFS_MatchLong( LPCWSTR mask, LPCWSTR name, int case_sensitive )
367 LPCWSTR lastjoker = NULL;
368 LPCWSTR next_to_retry = NULL;
369 static const WCHAR asterisk_dot_asterisk[] = {'*','.','*',0};
371 TRACE("(%s, %s, %x)\n", debugstr_w(mask), debugstr_w(name), case_sensitive);
373 if (!strcmpW( mask, asterisk_dot_asterisk )) return 1;
374 while (*name && *mask)
376 if (*mask == '*')
378 mask++;
379 while (*mask == '*') mask++; /* Skip consecutive '*' */
380 lastjoker = mask;
381 if (!*mask) return 1; /* end of mask is all '*', so match */
383 /* skip to the next match after the joker(s) */
384 if (case_sensitive) while (*name && (*name != *mask)) name++;
385 else while (*name && (toupperW(*name) != toupperW(*mask))) name++;
387 if (!*name) break;
388 next_to_retry = name;
390 else if (*mask != '?')
392 int mismatch = 0;
393 if (case_sensitive)
395 if (*mask != *name) mismatch = 1;
397 else
399 if (toupperW(*mask) != toupperW(*name)) mismatch = 1;
401 if (!mismatch)
403 mask++;
404 name++;
405 if (*mask == '\0')
407 if (*name == '\0')
408 return 1;
409 if (lastjoker)
410 mask = lastjoker;
413 else /* mismatch ! */
415 if (lastjoker) /* we had an '*', so we can try unlimitedly */
417 mask = lastjoker;
419 /* this scan sequence was a mismatch, so restart
420 * 1 char after the first char we checked last time */
421 next_to_retry++;
422 name = next_to_retry;
424 else
425 return 0; /* bad luck */
428 else /* '?' */
430 mask++;
431 name++;
434 while ((*mask == '.') || (*mask == '*'))
435 mask++; /* Ignore trailing '.' or '*' in mask */
436 return (!*name && !*mask);
440 /***********************************************************************
441 * DOSFS_AddDirEntry
443 * Used to construct an array of filenames in DOSFS_OpenDir
445 static BOOL DOSFS_AddDirEntry(DOS_DIR **dir, LPCWSTR name, LPCWSTR dosname)
447 int extra1 = strlenW(name) + 1;
448 int extra2 = strlenW(dosname) + 1;
450 /* if we need more, at minimum double the size */
451 if( (extra1 + extra2 + (*dir)->used) > (*dir)->size)
453 int more = (*dir)->size;
454 DOS_DIR *t;
456 if(more<(extra1+extra2))
457 more = extra1+extra2;
459 t = HeapReAlloc(GetProcessHeap(), 0, *dir, sizeof(**dir) +
460 ((*dir)->size + more)*sizeof(WCHAR) );
461 if(!t)
463 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
464 ERR("Out of memory caching directory structure %d %d %d\n",
465 (*dir)->size, more, (*dir)->used);
466 return FALSE;
468 (*dir) = t;
469 (*dir)->size += more;
472 /* at this point, the dir structure is big enough to hold these names */
473 strcpyW(&(*dir)->names[(*dir)->used], name);
474 (*dir)->used += extra1;
475 strcpyW(&(*dir)->names[(*dir)->used], dosname);
476 (*dir)->used += extra2;
478 return TRUE;
482 /***********************************************************************
483 * DOSFS_OpenDir_VFAT
485 static BOOL DOSFS_OpenDir_VFAT(DOS_DIR **dir, const char *unix_path)
487 #ifdef VFAT_IOCTL_READDIR_BOTH
488 KERNEL_DIRENT de[2];
489 int fd = open( unix_path, O_RDONLY|O_DIRECTORY );
490 BOOL r = TRUE;
492 /* Check if the VFAT ioctl is supported on this directory */
494 if ( fd<0 )
495 return FALSE;
497 while (1)
499 WCHAR long_name[MAX_PATH];
500 WCHAR short_name[12];
502 r = (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de ) != -1);
503 if(!r)
504 break;
505 if (!de[0].d_reclen)
506 break;
507 MultiByteToWideChar(CP_UNIXCP, 0, de[0].d_name, -1, long_name, MAX_PATH);
508 if (!DOSFS_ToDosFCBFormat( long_name, short_name ))
509 short_name[0] = '\0';
510 if (de[1].d_name[0])
511 MultiByteToWideChar(CP_UNIXCP, 0, de[1].d_name, -1, long_name, MAX_PATH);
512 else
513 MultiByteToWideChar(CP_UNIXCP, 0, de[0].d_name, -1, long_name, MAX_PATH);
514 r = DOSFS_AddDirEntry(dir, long_name, short_name );
515 if(!r)
516 break;
518 if(r)
520 static const WCHAR empty_strW[] = { 0 };
521 DOSFS_AddDirEntry(dir, empty_strW, empty_strW);
523 close(fd);
524 return r;
525 #else
526 return FALSE;
527 #endif /* VFAT_IOCTL_READDIR_BOTH */
531 /***********************************************************************
532 * DOSFS_OpenDir_Normal
534 * Now use the standard opendir/readdir interface
536 static BOOL DOSFS_OpenDir_Normal( DOS_DIR **dir, const char *unix_path )
538 DIR *unixdir = opendir( unix_path );
539 BOOL r = TRUE;
540 static const WCHAR empty_strW[] = { 0 };
542 if(!unixdir)
543 return FALSE;
544 while(1)
546 WCHAR long_name[MAX_PATH];
547 struct dirent *de = readdir(unixdir);
549 if(!de)
550 break;
551 MultiByteToWideChar(CP_UNIXCP, 0, de->d_name, -1, long_name, MAX_PATH);
552 r = DOSFS_AddDirEntry(dir, long_name, empty_strW);
553 if(!r)
554 break;
556 if(r)
557 DOSFS_AddDirEntry(dir, empty_strW, empty_strW);
558 closedir(unixdir);
559 return r;
562 /***********************************************************************
563 * DOSFS_OpenDir
565 static DOS_DIR *DOSFS_OpenDir( const char *unix_path )
567 const int init_size = 0x100;
568 DOS_DIR *dir = HeapAlloc( GetProcessHeap(), 0, sizeof(*dir) + init_size*sizeof (WCHAR));
569 BOOL r;
571 TRACE("%s\n",debugstr_a(unix_path));
573 if (!dir)
575 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
576 return NULL;
578 dir->used = 0;
579 dir->size = init_size;
581 /* Treat empty path as root directory. This simplifies path split into
582 directory and mask in several other places */
583 if (!*unix_path) unix_path = "/";
585 r = DOSFS_OpenDir_VFAT( &dir, unix_path);
587 if(!r)
588 r = DOSFS_OpenDir_Normal( &dir, unix_path);
590 if(!r)
592 HeapFree(GetProcessHeap(), 0, dir);
593 return NULL;
595 dir->used = 0;
597 return dir;
601 /***********************************************************************
602 * DOSFS_CloseDir
604 static void DOSFS_CloseDir( DOS_DIR *dir )
606 HeapFree( GetProcessHeap(), 0, dir );
610 /***********************************************************************
611 * DOSFS_ReadDir
613 static BOOL DOSFS_ReadDir( DOS_DIR *dir, LPCWSTR *long_name,
614 LPCWSTR *short_name )
616 LPCWSTR sn, ln;
618 if (!dir)
619 return FALSE;
621 /* the long pathname is first */
622 ln = &dir->names[dir->used];
623 if(ln[0])
624 *long_name = ln;
625 else
626 return FALSE;
627 dir->used += (strlenW(ln) + 1);
629 /* followed by the short path name */
630 sn = &dir->names[dir->used];
631 if(sn[0])
632 *short_name = sn;
633 else
634 *short_name = NULL;
635 dir->used += (strlenW(sn) + 1);
637 return TRUE;
641 /***********************************************************************
642 * DOSFS_Hash
644 * Transform a Unix file name into a hashed DOS name. If the name is a valid
645 * DOS name, it is converted to upper-case; otherwise it is replaced by a
646 * hashed version that fits in 8.3 format.
647 * File name can be terminated by '\0', '\\' or '/'.
648 * 'buffer' must be at least 13 characters long.
650 static void DOSFS_Hash( LPCWSTR name, LPWSTR buffer, BOOL dir_format,
651 BOOL ignore_case )
653 static const char invalid_chars[] = INVALID_DOS_CHARS "~.";
654 static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
656 LPCWSTR p, ext;
657 LPWSTR dst;
658 unsigned short hash;
659 int i;
661 if (dir_format)
663 for(i = 0; i < 11; i++) buffer[i] = ' ';
664 buffer[11] = 0;
667 if (DOSFS_ValidDOSName( name, ignore_case ))
669 /* Check for '.' and '..' */
670 if (*name == '.')
672 buffer[0] = '.';
673 if (!dir_format) buffer[1] = buffer[2] = '\0';
674 if (name[1] == '.') buffer[1] = '.';
675 return;
678 /* Simply copy the name, converting to uppercase */
680 for (dst = buffer; !IS_END_OF_NAME(*name) && (*name != '.'); name++)
681 *dst++ = toupperW(*name);
682 if (*name == '.')
684 if (dir_format) dst = buffer + 8;
685 else *dst++ = '.';
686 for (name++; !IS_END_OF_NAME(*name); name++)
687 *dst++ = toupperW(*name);
689 if (!dir_format) *dst = '\0';
690 return;
693 /* Compute the hash code of the file name */
694 /* If you know something about hash functions, feel free to */
695 /* insert a better algorithm here... */
696 if (ignore_case)
698 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
699 hash = (hash<<3) ^ (hash>>5) ^ tolowerW(*p) ^ (tolowerW(p[1]) << 8);
700 hash = (hash<<3) ^ (hash>>5) ^ tolowerW(*p); /* Last character */
702 else
704 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
705 hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
706 hash = (hash << 3) ^ (hash >> 5) ^ *p; /* Last character */
709 /* Find last dot for start of the extension */
710 for (p = name+1, ext = NULL; !IS_END_OF_NAME(*p); p++)
711 if (*p == '.') ext = p;
712 if (ext && IS_END_OF_NAME(ext[1]))
713 ext = NULL; /* Empty extension ignored */
715 /* Copy first 4 chars, replacing invalid chars with '_' */
716 for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
718 if (IS_END_OF_NAME(*p) || (p == ext)) break;
719 *dst++ = (*p < 256 && strchr( invalid_chars, (char)*p )) ? '_' : toupperW(*p);
721 /* Pad to 5 chars with '~' */
722 while (i-- >= 0) *dst++ = '~';
724 /* Insert hash code converted to 3 ASCII chars */
725 *dst++ = hash_chars[(hash >> 10) & 0x1f];
726 *dst++ = hash_chars[(hash >> 5) & 0x1f];
727 *dst++ = hash_chars[hash & 0x1f];
729 /* Copy the first 3 chars of the extension (if any) */
730 if (ext)
732 if (!dir_format) *dst++ = '.';
733 for (i = 3, ext++; (i > 0) && !IS_END_OF_NAME(*ext); i--, ext++)
734 *dst++ = (*ext < 256 && strchr( invalid_chars, (char)*ext )) ? '_' : toupperW(*ext);
736 if (!dir_format) *dst = '\0';
740 /***********************************************************************
741 * DOSFS_FindUnixName
743 * Find the Unix file name in a given directory that corresponds to
744 * a file name (either in Unix or DOS format).
745 * File name can be terminated by '\0', '\\' or '/'.
746 * Return TRUE if OK, FALSE if no file name matches.
748 * 'long_buf' must be at least 'long_len' characters long. If the long name
749 * turns out to be larger than that, the function returns FALSE.
750 * 'short_buf' must be at least 13 characters long.
752 BOOL DOSFS_FindUnixName( const DOS_FULL_NAME *path, LPCWSTR name, char *long_buf,
753 INT long_len, LPWSTR short_buf, BOOL ignore_case)
755 DOS_DIR *dir;
756 LPCWSTR long_name, short_name;
757 WCHAR dos_name[12], tmp_buf[13];
758 BOOL ret;
760 LPCWSTR p = strchrW( name, '/' );
761 int len = p ? (int)(p - name) : strlenW(name);
762 if ((p = strchrW( name, '\\' ))) len = min( (int)(p - name), len );
763 /* Ignore trailing dots and spaces */
764 while (len > 1 && (name[len-1] == '.' || name[len-1] == ' ')) len--;
765 if (long_len < len + 1) return FALSE;
767 TRACE("%s,%s\n", path->long_name, debugstr_w(name) );
769 if (!DOSFS_ToDosFCBFormat( name, dos_name )) dos_name[0] = '\0';
771 if (!(dir = DOSFS_OpenDir( path->long_name )))
773 WARN("(%s,%s): can't open dir: %s\n",
774 path->long_name, debugstr_w(name), strerror(errno) );
775 return FALSE;
778 while ((ret = DOSFS_ReadDir( dir, &long_name, &short_name )))
780 /* Check against Unix name */
781 if (len == strlenW(long_name))
783 if (!ignore_case)
785 if (!strncmpW( long_name, name, len )) break;
787 else
789 if (!strncmpiW( long_name, name, len )) break;
792 if (dos_name[0])
794 /* Check against hashed DOS name */
795 if (!short_name)
797 DOSFS_Hash( long_name, tmp_buf, TRUE, ignore_case );
798 short_name = tmp_buf;
800 if (!strcmpW( dos_name, short_name )) break;
803 if (ret)
805 if (long_buf) WideCharToMultiByte(CP_UNIXCP, 0, long_name, -1, long_buf, long_len, NULL, NULL);
806 if (short_buf)
808 if (short_name)
809 DOSFS_ToDosDTAFormat( short_name, short_buf );
810 else
811 DOSFS_Hash( long_name, short_buf, FALSE, ignore_case );
813 TRACE("(%s,%s) -> %s (%s)\n", path->long_name, debugstr_w(name),
814 debugstr_w(long_name), short_buf ? debugstr_w(short_buf) : "***");
816 else
817 WARN("%s not found in '%s'\n", debugstr_w(name), path->long_name);
818 DOSFS_CloseDir( dir );
819 return ret;
823 /***********************************************************************
824 * DOSFS_GetDevice
826 * Check if a DOS file name represents a DOS device and return the device.
828 const DOS_DEVICE *DOSFS_GetDevice( LPCWSTR name )
830 unsigned int i;
831 const WCHAR *p;
833 if (!name) return NULL; /* if wine_server_handle_to_fd was used */
834 if (name[0] && (name[1] == ':')) name += 2;
835 if ((p = strrchrW( name, '/' ))) name = p + 1;
836 if ((p = strrchrW( name, '\\' ))) name = p + 1;
837 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
839 const WCHAR *dev = DOSFS_Devices[i].name;
840 if (!strncmpiW( dev, name, strlenW(dev) ))
842 p = name + strlenW( dev );
843 if (!*p || (*p == '.') || (*p == ':')) return &DOSFS_Devices[i];
846 return NULL;
850 /***********************************************************************
851 * DOSFS_GetDeviceByHandle
853 const DOS_DEVICE *DOSFS_GetDeviceByHandle( HANDLE hFile )
855 const DOS_DEVICE *ret = NULL;
856 SERVER_START_REQ( get_device_id )
858 req->handle = hFile;
859 if (!wine_server_call( req ))
861 if ((reply->id >= 0) &&
862 (reply->id < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0])))
863 ret = &DOSFS_Devices[reply->id];
866 SERVER_END_REQ;
867 return ret;
871 /**************************************************************************
872 * DOSFS_CreateCommPort
874 static HANDLE DOSFS_CreateCommPort(LPCWSTR name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa)
876 HANDLE ret;
877 HKEY hkey;
878 DWORD dummy;
879 OBJECT_ATTRIBUTES attr;
880 UNICODE_STRING nameW;
881 WCHAR *devnameW;
882 char tmp[128];
883 char devname[40];
885 static const WCHAR serialportsW[] = {'M','a','c','h','i','n','e','\\',
886 'S','o','f','t','w','a','r','e','\\',
887 'W','i','n','e','\\','W','i','n','e','\\',
888 'C','o','n','f','i','g','\\',
889 'S','e','r','i','a','l','P','o','r','t','s',0};
891 TRACE_(file)("%s %lx %lx\n", debugstr_w(name), access, attributes);
893 attr.Length = sizeof(attr);
894 attr.RootDirectory = 0;
895 attr.ObjectName = &nameW;
896 attr.Attributes = 0;
897 attr.SecurityDescriptor = NULL;
898 attr.SecurityQualityOfService = NULL;
899 RtlInitUnicodeString( &nameW, serialportsW );
901 if (NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr )) return 0;
903 RtlInitUnicodeString( &nameW, name );
904 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
905 devnameW = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
906 else
907 devnameW = NULL;
909 NtClose( hkey );
911 if (!devnameW) return 0;
912 WideCharToMultiByte(CP_ACP, 0, devnameW, -1, devname, sizeof(devname), NULL, NULL);
914 TRACE("opening %s as %s\n", devname, debugstr_w(name));
916 SERVER_START_REQ( create_serial )
918 req->access = access;
919 req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
920 req->attributes = attributes;
921 req->sharing = FILE_SHARE_READ|FILE_SHARE_WRITE;
922 wine_server_add_data( req, devname, strlen(devname) );
923 SetLastError(0);
924 wine_server_call_err( req );
925 ret = reply->handle;
927 SERVER_END_REQ;
929 if(!ret)
930 ERR("Couldn't open device '%s' ! (check permissions)\n",devname);
931 else
932 TRACE("return %p\n", ret );
933 return ret;
936 /***********************************************************************
937 * DOSFS_OpenDevice
939 * Open a DOS device. This might not map 1:1 into the UNIX device concept.
940 * Returns 0 on failure.
942 HANDLE DOSFS_OpenDevice( LPCWSTR name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa )
944 unsigned int i;
945 const WCHAR *p;
946 HANDLE handle;
948 if (name[0] && (name[1] == ':')) name += 2;
949 if ((p = strrchrW( name, '/' ))) name = p + 1;
950 if ((p = strrchrW( name, '\\' ))) name = p + 1;
951 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
953 const WCHAR *dev = DOSFS_Devices[i].name;
954 if (!strncmpiW( dev, name, strlenW(dev) ))
956 p = name + strlenW( dev );
957 if (!*p || (*p == '.') || (*p == ':')) {
958 static const WCHAR nulW[] = {'N','U','L',0};
959 static const WCHAR conW[] = {'C','O','N',0};
960 static const WCHAR scsimgrW[] = {'S','C','S','I','M','G','R','$',0};
961 static const WCHAR hpscanW[] = {'H','P','S','C','A','N',0};
962 static const WCHAR emmxxxx0W[] = {'E','M','M','X','X','X','X','0',0};
963 /* got it */
964 if (!strcmpiW(DOSFS_Devices[i].name, nulW))
965 return FILE_CreateFile( "/dev/null", access,
966 FILE_SHARE_READ|FILE_SHARE_WRITE, sa,
967 OPEN_EXISTING, 0, 0, TRUE, DRIVE_UNKNOWN );
968 if (!strcmpiW(DOSFS_Devices[i].name, conW)) {
969 HANDLE to_dup;
970 switch (access & (GENERIC_READ|GENERIC_WRITE)) {
971 case GENERIC_READ:
972 to_dup = GetStdHandle( STD_INPUT_HANDLE );
973 break;
974 case GENERIC_WRITE:
975 to_dup = GetStdHandle( STD_OUTPUT_HANDLE );
976 break;
977 default:
978 FIXME("can't open CON read/write\n");
979 return 0;
981 if (!DuplicateHandle( GetCurrentProcess(), to_dup, GetCurrentProcess(),
982 &handle, 0,
983 sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle,
984 DUPLICATE_SAME_ACCESS ))
985 handle = 0;
986 return handle;
988 if (!strcmpiW(DOSFS_Devices[i].name, scsimgrW) ||
989 !strcmpiW(DOSFS_Devices[i].name, hpscanW) ||
990 !strcmpiW(DOSFS_Devices[i].name, emmxxxx0W))
992 return FILE_CreateDevice( i, access, sa );
995 if( (handle=DOSFS_CreateCommPort(DOSFS_Devices[i].name,access,attributes,sa)) )
996 return handle;
997 FIXME("device open %s not supported (yet)\n", debugstr_w(DOSFS_Devices[i].name));
998 return 0;
1002 return 0;
1006 /***********************************************************************
1007 * DOSFS_GetPathDrive
1009 * Get the drive specified by a given path name (DOS or Unix format).
1011 static int DOSFS_GetPathDrive( LPCWSTR *name )
1013 int drive;
1014 LPCWSTR p = *name;
1016 if (*p && (p[1] == ':'))
1018 drive = toupperW(*p) - 'A';
1019 *name += 2;
1021 else if (*p == '/') /* Absolute Unix path? */
1023 if ((drive = DRIVE_FindDriveRootW( name )) == -1)
1025 MESSAGE("Warning: %s not accessible from a configured DOS drive\n", debugstr_w(*name) );
1026 /* Assume it really was a DOS name */
1027 drive = DRIVE_GetCurrentDrive();
1030 else drive = DRIVE_GetCurrentDrive();
1032 if (!DRIVE_IsValid(drive))
1034 SetLastError( ERROR_INVALID_DRIVE );
1035 return -1;
1037 return drive;
1041 /***********************************************************************
1042 * DOSFS_GetFullName
1044 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
1045 * Unix name / short DOS name pair.
1046 * Return FALSE if one of the path components does not exist. The last path
1047 * component is only checked if 'check_last' is non-zero.
1048 * The buffers pointed to by 'long_buf' and 'short_buf' must be
1049 * at least MAX_PATHNAME_LEN long.
1051 BOOL DOSFS_GetFullName( LPCWSTR name, BOOL check_last, DOS_FULL_NAME *full )
1053 BOOL found;
1054 UINT flags;
1055 char *p_l, *root;
1056 LPWSTR p_s;
1057 static const WCHAR driveA_rootW[] = {'A',':','\\',0};
1058 static const WCHAR dos_rootW[] = {'\\',0};
1060 TRACE("%s (last=%d)\n", debugstr_w(name), check_last );
1062 if ((!*name) || (*name=='\n'))
1063 { /* error code for Win98 */
1064 SetLastError(ERROR_BAD_PATHNAME);
1065 return FALSE;
1068 if ((full->drive = DOSFS_GetPathDrive( &name )) == -1) return FALSE;
1069 flags = DRIVE_GetFlags( full->drive );
1071 lstrcpynA( full->long_name, DRIVE_GetRoot( full->drive ),
1072 sizeof(full->long_name) );
1073 if (full->long_name[1]) root = full->long_name + strlen(full->long_name);
1074 else root = full->long_name; /* root directory */
1076 strcpyW( full->short_name, driveA_rootW );
1077 full->short_name[0] += full->drive;
1079 if ((*name == '\\') || (*name == '/')) /* Absolute path */
1081 while ((*name == '\\') || (*name == '/')) name++;
1083 else /* Relative path */
1085 lstrcpynA( root + 1, DRIVE_GetUnixCwd( full->drive ),
1086 sizeof(full->long_name) - (root - full->long_name) - 1 );
1087 if (root[1]) *root = '/';
1088 lstrcpynW( full->short_name + 3, DRIVE_GetDosCwd( full->drive ),
1089 sizeof(full->short_name)/sizeof(full->short_name[0]) - 3 );
1092 p_l = full->long_name[1] ? full->long_name + strlen(full->long_name)
1093 : full->long_name;
1094 p_s = full->short_name[3] ? full->short_name + strlenW(full->short_name)
1095 : full->short_name + 2;
1096 found = TRUE;
1098 while (*name && found)
1100 /* Check for '.' and '..' */
1102 if (*name == '.')
1104 if (IS_END_OF_NAME(name[1]))
1106 name++;
1107 while ((*name == '\\') || (*name == '/')) name++;
1108 continue;
1110 else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
1112 name += 2;
1113 while ((*name == '\\') || (*name == '/')) name++;
1114 while ((p_l > root) && (*p_l != '/')) p_l--;
1115 while ((p_s > full->short_name + 2) && (*p_s != '\\')) p_s--;
1116 *p_l = *p_s = '\0'; /* Remove trailing separator */
1117 continue;
1121 /* Make sure buffers are large enough */
1123 if ((p_s >= full->short_name + sizeof(full->short_name)/sizeof(full->short_name[0]) - 14) ||
1124 (p_l >= full->long_name + sizeof(full->long_name) - 1))
1126 SetLastError( ERROR_PATH_NOT_FOUND );
1127 return FALSE;
1130 /* Get the long and short name matching the file name */
1132 if ((found = DOSFS_FindUnixName( full, name, p_l + 1,
1133 sizeof(full->long_name) - (p_l - full->long_name) - 1,
1134 p_s + 1, !(flags & DRIVE_CASE_SENSITIVE) )))
1136 *p_l++ = '/';
1137 p_l += strlen(p_l);
1138 *p_s++ = '\\';
1139 p_s += strlenW(p_s);
1140 while (!IS_END_OF_NAME(*name)) name++;
1142 else if (!check_last)
1144 *p_l++ = '/';
1145 *p_s++ = '\\';
1146 while (!IS_END_OF_NAME(*name) &&
1147 (p_s < full->short_name + sizeof(full->short_name)/sizeof(full->short_name[0]) - 1) &&
1148 (p_l < full->long_name + sizeof(full->long_name) - 1))
1150 WCHAR wch;
1151 *p_s++ = tolowerW(*name);
1152 /* If the drive is case-sensitive we want to create new */
1153 /* files in lower-case otherwise we can't reopen them */
1154 /* under the same short name. */
1155 if (flags & DRIVE_CASE_SENSITIVE) wch = tolowerW(*name);
1156 else wch = *name;
1157 p_l += WideCharToMultiByte(CP_UNIXCP, 0, &wch, 1, p_l, 2, NULL, NULL);
1158 name++;
1160 /* Ignore trailing dots and spaces */
1161 while(p_l[-1] == '.' || p_l[-1] == ' ') {
1162 --p_l;
1163 --p_s;
1165 *p_l = '\0';
1166 *p_s = '\0';
1168 while ((*name == '\\') || (*name == '/')) name++;
1171 if (!found)
1173 if (check_last)
1175 SetLastError( ERROR_FILE_NOT_FOUND );
1176 return FALSE;
1178 if (*name) /* Not last */
1180 SetLastError( ERROR_PATH_NOT_FOUND );
1181 return FALSE;
1184 if (!full->long_name[0]) strcpy( full->long_name, "/" );
1185 if (!full->short_name[2]) strcpyW( full->short_name + 2, dos_rootW );
1186 TRACE("returning %s = %s\n", full->long_name, debugstr_w(full->short_name) );
1187 return TRUE;
1191 /***********************************************************************
1192 * GetShortPathNameW (KERNEL32.@)
1194 * NOTES
1195 * observed:
1196 * longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1197 * longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0
1199 * more observations ( with NT 3.51 (WinDD) ):
1200 * longpath <= 8.3 -> just copy longpath to shortpath
1201 * longpath > 8.3 ->
1202 * a) file does not exist -> return 0, LastError = ERROR_FILE_NOT_FOUND
1203 * b) file does exist -> set the short filename.
1204 * - trailing slashes are reproduced in the short name, even if the
1205 * file is not a directory
1206 * - the absolute/relative path of the short name is reproduced like found
1207 * in the long name
1208 * - longpath and shortpath may have the same address
1209 * Peter Ganten, 1999
1211 DWORD WINAPI GetShortPathNameW( LPCWSTR longpath, LPWSTR shortpath, DWORD shortlen )
1213 DOS_FULL_NAME full_name;
1214 WCHAR tmpshortpath[MAX_PATHNAME_LEN];
1215 const WCHAR *p;
1216 DWORD sp = 0, lp = 0;
1217 int drive;
1218 DWORD tmplen;
1219 UINT flags;
1220 BOOL unixabsolute = *longpath == '/';
1222 TRACE("%s\n", debugstr_w(longpath));
1224 if (!longpath) {
1225 SetLastError(ERROR_INVALID_PARAMETER);
1226 return 0;
1228 if (!longpath[0]) {
1229 SetLastError(ERROR_BAD_PATHNAME);
1230 return 0;
1233 /* check for drive letter */
1234 if (!unixabsolute && longpath[1] == ':' ) {
1235 tmpshortpath[0] = longpath[0];
1236 tmpshortpath[1] = ':';
1237 sp = 2;
1240 if ( ( drive = DOSFS_GetPathDrive ( &longpath )) == -1 ) return 0;
1241 flags = DRIVE_GetFlags ( drive );
1243 if (unixabsolute && drive != DRIVE_GetCurrentDrive()) {
1244 tmpshortpath[0] = drive + 'A';
1245 tmpshortpath[1] = ':';
1246 sp = 2;
1249 while ( longpath[lp] ) {
1251 /* check for path delimiters and reproduce them */
1252 if ( longpath[lp] == '\\' || longpath[lp] == '/' ) {
1253 if (!sp || tmpshortpath[sp-1]!= '\\')
1255 /* strip double "\\" */
1256 tmpshortpath[sp] = '\\';
1257 sp++;
1259 tmpshortpath[sp]=0;/*terminate string*/
1260 lp++;
1261 continue;
1264 tmplen = 0;
1265 for(p = longpath + lp; *p && *p != '/' && *p != '\\'; p++)
1266 tmplen++;
1267 lstrcpynW(tmpshortpath + sp, longpath + lp, tmplen + 1);
1269 /* Check, if the current element is a valid dos name */
1270 if ( DOSFS_ValidDOSName ( longpath + lp, !(flags & DRIVE_CASE_SENSITIVE) ) ) {
1271 sp += tmplen;
1272 lp += tmplen;
1273 continue;
1276 /* Check if the file exists and use the existing file name */
1277 if ( DOSFS_GetFullName ( tmpshortpath, TRUE, &full_name ) ) {
1278 strcpyW(tmpshortpath + sp, strrchrW(full_name.short_name, '\\') + 1);
1279 sp += strlenW(tmpshortpath + sp);
1280 lp += tmplen;
1281 continue;
1284 TRACE("not found!\n" );
1285 SetLastError ( ERROR_FILE_NOT_FOUND );
1286 return 0;
1288 tmpshortpath[sp] = 0;
1290 tmplen = strlenW(tmpshortpath) + 1;
1291 if (tmplen <= shortlen)
1293 strcpyW(shortpath, tmpshortpath);
1294 TRACE("returning %s\n", debugstr_w(shortpath));
1295 tmplen--; /* length without 0 */
1298 return tmplen;
1302 /***********************************************************************
1303 * GetShortPathNameA (KERNEL32.@)
1305 DWORD WINAPI GetShortPathNameA( LPCSTR longpath, LPSTR shortpath, DWORD shortlen )
1307 UNICODE_STRING longpathW;
1308 WCHAR shortpathW[MAX_PATH];
1309 DWORD ret, retW;
1311 if (!longpath)
1313 SetLastError(ERROR_INVALID_PARAMETER);
1314 return 0;
1317 TRACE("%s\n", debugstr_a(longpath));
1319 if (!RtlCreateUnicodeStringFromAsciiz(&longpathW, longpath))
1321 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1322 return 0;
1325 retW = GetShortPathNameW(longpathW.Buffer, shortpathW, MAX_PATH);
1327 if (!retW)
1328 ret = 0;
1329 else if (retW > MAX_PATH)
1331 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1332 ret = 0;
1334 else
1336 ret = WideCharToMultiByte(CP_ACP, 0, shortpathW, -1, NULL, 0, NULL, NULL);
1337 if (ret <= shortlen)
1339 WideCharToMultiByte(CP_ACP, 0, shortpathW, -1, shortpath, shortlen, NULL, NULL);
1340 ret--; /* length without 0 */
1344 RtlFreeUnicodeString(&longpathW);
1345 return ret;
1349 /***********************************************************************
1350 * GetLongPathNameW (KERNEL32.@)
1352 * NOTES
1353 * observed (Win2000):
1354 * shortpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1355 * shortpath="": LastError=ERROR_PATH_NOT_FOUND, ret=0
1357 DWORD WINAPI GetLongPathNameW( LPCWSTR shortpath, LPWSTR longpath, DWORD longlen )
1359 DOS_FULL_NAME full_name;
1360 const char *root;
1361 LPWSTR p;
1362 int drive;
1363 DWORD ret, len = 0;
1365 if (!shortpath) {
1366 SetLastError(ERROR_INVALID_PARAMETER);
1367 return 0;
1369 if (!shortpath[0]) {
1370 SetLastError(ERROR_PATH_NOT_FOUND);
1371 return 0;
1374 TRACE("%s,%p,%ld\n", debugstr_w(shortpath), longpath, longlen);
1376 if(shortpath[0]=='\\' && shortpath[1]=='\\')
1378 ERR("UNC pathname %s\n",debugstr_w(shortpath));
1379 lstrcpynW( longpath, full_name.short_name, longlen );
1380 return strlenW(longpath);
1383 if (!DOSFS_GetFullName( shortpath, TRUE, &full_name )) return 0;
1385 root = full_name.long_name;
1386 drive = DRIVE_FindDriveRoot(&root);
1388 ret = MultiByteToWideChar(CP_UNIXCP, 0, root, -1, NULL, 0);
1389 ret += 3; /* A:\ */
1390 /* reproduce terminating slash */
1391 if (ret > 4) /* if not drive root */
1393 len = strlenW(shortpath);
1394 if (shortpath[len - 1] == '\\' || shortpath[len - 1] == '/')
1395 len = 1;
1397 ret += len;
1398 if (ret <= longlen)
1400 longpath[0] = 'A' + drive;
1401 longpath[1] = ':';
1402 MultiByteToWideChar(CP_UNIXCP, 0, root, -1, longpath + 2, longlen - 2);
1403 for (p = longpath; *p; p++) if (*p == '/') *p = '\\';
1404 if (len)
1406 longpath[ret - 2] = '\\';
1407 longpath[ret - 1] = 0;
1409 TRACE("returning %s\n", debugstr_w(longpath));
1410 ret--; /* length without 0 */
1412 return ret;
1416 /***********************************************************************
1417 * GetLongPathNameA (KERNEL32.@)
1419 DWORD WINAPI GetLongPathNameA( LPCSTR shortpath, LPSTR longpath, DWORD longlen )
1421 UNICODE_STRING shortpathW;
1422 WCHAR longpathW[MAX_PATH];
1423 DWORD ret, retW;
1425 if (!shortpath)
1427 SetLastError(ERROR_INVALID_PARAMETER);
1428 return 0;
1431 TRACE("%s\n", debugstr_a(shortpath));
1433 if (!RtlCreateUnicodeStringFromAsciiz(&shortpathW, shortpath))
1435 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1436 return 0;
1439 retW = GetLongPathNameW(shortpathW.Buffer, longpathW, MAX_PATH);
1441 if (!retW)
1442 ret = 0;
1443 else if (retW > MAX_PATH)
1445 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1446 ret = 0;
1448 else
1450 ret = WideCharToMultiByte(CP_ACP, 0, longpathW, -1, NULL, 0, NULL, NULL);
1451 if (ret <= longlen)
1453 WideCharToMultiByte(CP_ACP, 0, longpathW, -1, longpath, longlen, NULL, NULL);
1454 ret--; /* length without 0 */
1458 RtlFreeUnicodeString(&shortpathW);
1459 return ret;
1463 /***********************************************************************
1464 * DOSFS_DoGetFullPathName
1466 * Implementation of GetFullPathNameA/W.
1468 * bon@elektron 000331:
1469 * A test for GetFullPathName with many pathological cases
1470 * now gives identical output for Wine and OSR2
1472 static DWORD DOSFS_DoGetFullPathName( LPCWSTR name, DWORD len, LPWSTR result )
1474 DWORD ret;
1475 DOS_FULL_NAME full_name;
1476 LPWSTR p, q;
1477 char *p_l;
1478 const char * root;
1479 WCHAR drivecur[] = {'C',':','.',0};
1480 WCHAR driveletter=0;
1481 int namelen,drive=0;
1482 static const WCHAR bkslashW[] = {'\\',0};
1483 static const WCHAR dotW[] = {'.',0};
1484 static const WCHAR updir_slashW[] = {'\\','.','.','\\',0};
1485 static const WCHAR curdirW[] = {'\\','.','\\',0};
1486 static const WCHAR updirW[] = {'\\','.','.',0};
1488 if (!name[0])
1490 SetLastError(ERROR_BAD_PATHNAME);
1491 return 0;
1494 TRACE("passed %s\n", debugstr_w(name));
1496 if (name[1]==':')
1497 /*drive letter given */
1499 driveletter = name[0];
1501 if ((name[1]==':') && ((name[2]=='\\') || (name[2]=='/')))
1502 /*absolute path given */
1504 strncpyW(full_name.short_name, name, MAX_PATHNAME_LEN);
1505 full_name.short_name[MAX_PATHNAME_LEN - 1] = 0; /* ensure 0 termination */
1506 drive = toupperW(name[0]) - 'A';
1508 else
1510 if (driveletter)
1511 drivecur[0]=driveletter;
1512 else if ((name[0]=='\\') || (name[0]=='/'))
1513 strcpyW(drivecur, bkslashW);
1514 else
1515 strcpyW(drivecur, dotW);
1517 if (!DOSFS_GetFullName( drivecur, FALSE, &full_name ))
1519 FIXME("internal: error getting drive/path\n");
1520 return 0;
1522 /* find path that drive letter substitutes*/
1523 drive = toupperW(full_name.short_name[0]) - 'A';
1524 root= DRIVE_GetRoot(drive);
1525 if (!root)
1527 FIXME("internal: error getting DOS Drive Root\n");
1528 return 0;
1530 if (!strcmp(root,"/"))
1532 /* we have just the last / and we need it. */
1533 p_l = full_name.long_name;
1535 else
1537 p_l = full_name.long_name + strlen(root);
1539 /* append long name (= unix name) to drive */
1540 MultiByteToWideChar(CP_UNIXCP, 0, p_l, -1, full_name.short_name + 2, MAX_PATHNAME_LEN - 3);
1541 /* append name to treat */
1542 namelen= strlenW(full_name.short_name);
1543 p = (LPWSTR)name;
1544 if (driveletter)
1545 p += 2; /* skip drive name when appending */
1546 if (namelen + 2 + strlenW(p) > MAX_PATHNAME_LEN)
1548 FIXME("internal error: buffer too small\n");
1549 return 0;
1551 full_name.short_name[namelen++] ='\\';
1552 full_name.short_name[namelen] = 0;
1553 strncpyW(full_name.short_name + namelen, p, MAX_PATHNAME_LEN - namelen);
1554 full_name.short_name[MAX_PATHNAME_LEN - 1] = 0; /* ensure 0 termination */
1556 /* reverse all slashes */
1557 for (p=full_name.short_name;
1558 p < full_name.short_name + strlenW(full_name.short_name);
1559 p++)
1561 if ( *p == '/' )
1562 *p = '\\';
1564 /* Use memmove, as areas overlap */
1565 /* Delete .. */
1566 while ((p = strstrW(full_name.short_name, updir_slashW)))
1568 if (p > full_name.short_name+2)
1570 *p = 0;
1571 q = strrchrW(full_name.short_name, '\\');
1572 memmove(q+1, p+4, (strlenW(p+4)+1) * sizeof(WCHAR));
1574 else
1576 memmove(full_name.short_name+3, p+4, (strlenW(p+4)+1) * sizeof(WCHAR));
1579 if ((full_name.short_name[2]=='.')&&(full_name.short_name[3]=='.'))
1581 /* This case istn't treated yet : c:..\test */
1582 memmove(full_name.short_name+2,full_name.short_name+4,
1583 (strlenW(full_name.short_name+4)+1) * sizeof(WCHAR));
1585 /* Delete . */
1586 while ((p = strstrW(full_name.short_name, curdirW)))
1588 *(p+1) = 0;
1589 memmove(p+1, p+3, (strlenW(p+3)+1) * sizeof(WCHAR));
1591 if (!(DRIVE_GetFlags(drive) & DRIVE_CASE_PRESERVING))
1592 for (p = full_name.short_name; *p; p++) *p = toupperW(*p);
1593 namelen = strlenW(full_name.short_name);
1594 if (!strcmpW(full_name.short_name+namelen-3, updirW))
1596 /* one more strange case: "c:\test\test1\.."
1597 return "c:\test" */
1598 *(full_name.short_name+namelen-3)=0;
1599 q = strrchrW(full_name.short_name, '\\');
1600 *q =0;
1602 if (full_name.short_name[namelen-1]=='.')
1603 full_name.short_name[(namelen--)-1] =0;
1604 if (!driveletter)
1605 if (full_name.short_name[namelen-1]=='\\')
1606 full_name.short_name[(namelen--)-1] =0;
1607 TRACE("got %s\n", debugstr_w(full_name.short_name));
1609 /* If the lpBuffer buffer is too small, the return value is the
1610 size of the buffer, in characters, required to hold the path
1611 plus the terminating \0 (tested against win95osr2, bon 001118)
1612 . */
1613 ret = strlenW(full_name.short_name);
1614 if (ret >= len )
1616 /* don't touch anything when the buffer is not large enough */
1617 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1618 return ret+1;
1620 if (result)
1622 strncpyW( result, full_name.short_name, len );
1623 result[len - 1] = 0; /* ensure 0 termination */
1626 TRACE("returning %s\n", debugstr_w(full_name.short_name) );
1627 return ret;
1631 /***********************************************************************
1632 * GetFullPathNameA (KERNEL32.@)
1633 * NOTES
1634 * if the path closed with '\', *lastpart is 0
1636 DWORD WINAPI GetFullPathNameA( LPCSTR name, DWORD len, LPSTR buffer,
1637 LPSTR *lastpart )
1639 UNICODE_STRING nameW;
1640 WCHAR bufferW[MAX_PATH];
1641 DWORD ret, retW;
1643 if (!name)
1645 SetLastError(ERROR_INVALID_PARAMETER);
1646 return 0;
1649 if (!RtlCreateUnicodeStringFromAsciiz(&nameW, name))
1651 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1652 return 0;
1655 retW = GetFullPathNameW( nameW.Buffer, MAX_PATH, bufferW, NULL);
1657 if (!retW)
1658 ret = 0;
1659 else if (retW > MAX_PATH)
1661 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1662 ret = 0;
1664 else
1666 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL);
1667 if (ret <= len)
1669 WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, len, NULL, NULL);
1670 ret--; /* length without 0 */
1672 if (lastpart)
1674 LPSTR p = buffer + strlen(buffer);
1676 if (*p != '\\')
1678 while ((p > buffer + 2) && (*p != '\\')) p--;
1679 *lastpart = p + 1;
1681 else *lastpart = NULL;
1686 RtlFreeUnicodeString(&nameW);
1687 return ret;
1691 /***********************************************************************
1692 * GetFullPathNameW (KERNEL32.@)
1694 DWORD WINAPI GetFullPathNameW( LPCWSTR name, DWORD len, LPWSTR buffer,
1695 LPWSTR *lastpart )
1697 DWORD ret = DOSFS_DoGetFullPathName( name, len, buffer );
1698 if (ret && (ret<=len) && buffer && lastpart)
1700 LPWSTR p = buffer + strlenW(buffer);
1701 if (*p != (WCHAR)'\\')
1703 while ((p > buffer + 2) && (*p != (WCHAR)'\\')) p--;
1704 *lastpart = p + 1;
1706 else *lastpart = NULL;
1708 return ret;
1712 /***********************************************************************
1713 * wine_get_unix_file_name (KERNEL32.@) Not a Windows API
1715 * Return the full Unix file name for a given path.
1716 * FIXME: convert dos file name to unicode
1718 BOOL WINAPI wine_get_unix_file_name( LPCSTR dos, LPSTR buffer, DWORD len )
1720 BOOL ret;
1721 DOS_FULL_NAME path;
1722 WCHAR dosW[MAX_PATHNAME_LEN];
1724 MultiByteToWideChar(CP_ACP, 0, dos, -1, dosW, MAX_PATHNAME_LEN);
1725 ret = DOSFS_GetFullName( dosW, FALSE, &path );
1726 if (ret && len)
1728 strncpy( buffer, path.long_name, len );
1729 buffer[len - 1] = 0; /* ensure 0 termination */
1731 return ret;
1735 /***********************************************************************
1736 * get_show_dir_symlinks_option
1738 static BOOL get_show_dir_symlinks_option(void)
1740 static const WCHAR WineW[] = {'M','a','c','h','i','n','e','\\',
1741 'S','o','f','t','w','a','r','e','\\',
1742 'W','i','n','e','\\','W','i','n','e','\\',
1743 'C','o','n','f','i','g','\\','W','i','n','e',0};
1744 static const WCHAR ShowDirSymlinksW[] = {'S','h','o','w','D','i','r','S','y','m','l','i','n','k','s',0};
1746 char tmp[80];
1747 HKEY hkey;
1748 DWORD dummy;
1749 OBJECT_ATTRIBUTES attr;
1750 UNICODE_STRING nameW;
1751 BOOL ret = FALSE;
1753 attr.Length = sizeof(attr);
1754 attr.RootDirectory = 0;
1755 attr.ObjectName = &nameW;
1756 attr.Attributes = 0;
1757 attr.SecurityDescriptor = NULL;
1758 attr.SecurityQualityOfService = NULL;
1759 RtlInitUnicodeString( &nameW, WineW );
1761 if (!NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr ))
1763 RtlInitUnicodeString( &nameW, ShowDirSymlinksW );
1764 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
1766 WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
1767 ret = IS_OPTION_TRUE( str[0] );
1769 NtClose( hkey );
1771 return ret;
1775 /***********************************************************************
1776 * DOSFS_FindNextEx
1778 static int DOSFS_FindNextEx( FIND_FIRST_INFO *info, WIN32_FIND_DATAW *entry )
1780 DWORD attr = info->attr | FA_UNUSED | FA_ARCHIVE | FA_RDONLY;
1781 UINT flags = DRIVE_GetFlags( info->drive );
1782 char *p, buffer[MAX_PATHNAME_LEN];
1783 const char *drive_path;
1784 int drive_root;
1785 LPCWSTR long_name, short_name;
1786 BY_HANDLE_FILE_INFORMATION fileinfo;
1787 WCHAR dos_name[13];
1788 BOOL is_symlink;
1790 if ((info->attr & ~(FA_UNUSED | FA_ARCHIVE | FA_RDONLY)) == FA_LABEL)
1792 if (info->cur_pos) return 0;
1793 entry->dwFileAttributes = FILE_ATTRIBUTE_LABEL;
1794 RtlSecondsSince1970ToTime( (time_t)0, (LARGE_INTEGER *)&entry->ftCreationTime );
1795 RtlSecondsSince1970ToTime( (time_t)0, (LARGE_INTEGER *)&entry->ftLastAccessTime );
1796 RtlSecondsSince1970ToTime( (time_t)0, (LARGE_INTEGER *)&entry->ftLastWriteTime );
1797 entry->nFileSizeHigh = 0;
1798 entry->nFileSizeLow = 0;
1799 entry->dwReserved0 = 0;
1800 entry->dwReserved1 = 0;
1801 DOSFS_ToDosDTAFormat( DRIVE_GetLabel( info->drive ), entry->cFileName );
1802 strcpyW( entry->cAlternateFileName, entry->cFileName );
1803 info->cur_pos++;
1804 TRACE("returning %s (%s) as label\n",
1805 debugstr_w(entry->cFileName), debugstr_w(entry->cAlternateFileName));
1806 return 1;
1809 drive_path = info->path + strlen(DRIVE_GetRoot( info->drive ));
1810 while ((*drive_path == '/') || (*drive_path == '\\')) drive_path++;
1811 drive_root = !*drive_path;
1813 lstrcpynA( buffer, info->path, sizeof(buffer) - 1 );
1814 strcat( buffer, "/" );
1815 p = buffer + strlen(buffer);
1817 while (DOSFS_ReadDir( info->u.dos_dir, &long_name, &short_name ))
1819 info->cur_pos++;
1821 /* Don't return '.' and '..' in the root of the drive */
1822 if (drive_root && (long_name[0] == '.') &&
1823 (!long_name[1] || ((long_name[1] == '.') && !long_name[2])))
1824 continue;
1826 /* Check the long mask */
1828 if (info->long_mask && *info->long_mask)
1830 if (!DOSFS_MatchLong( info->long_mask, long_name,
1831 flags & DRIVE_CASE_SENSITIVE )) continue;
1834 /* Check the short mask */
1836 if (info->short_mask)
1838 if (!short_name)
1840 DOSFS_Hash( long_name, dos_name, TRUE,
1841 !(flags & DRIVE_CASE_SENSITIVE) );
1842 short_name = dos_name;
1844 if (!DOSFS_MatchShort( info->short_mask, short_name )) continue;
1847 /* Check the file attributes */
1848 WideCharToMultiByte(CP_UNIXCP, 0, long_name, -1,
1849 p, sizeof(buffer) - (int)(p - buffer), NULL, NULL);
1850 if (!FILE_Stat( buffer, &fileinfo, &is_symlink ))
1852 WARN("can't stat %s\n", buffer);
1853 continue;
1855 if (is_symlink && (fileinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
1857 static int show_dir_symlinks = -1;
1858 if (show_dir_symlinks == -1)
1859 show_dir_symlinks = get_show_dir_symlinks_option();
1860 if (!show_dir_symlinks) continue;
1863 if (fileinfo.dwFileAttributes & ~attr) continue;
1865 /* We now have a matching entry; fill the result and return */
1867 entry->dwFileAttributes = fileinfo.dwFileAttributes;
1868 entry->ftCreationTime = fileinfo.ftCreationTime;
1869 entry->ftLastAccessTime = fileinfo.ftLastAccessTime;
1870 entry->ftLastWriteTime = fileinfo.ftLastWriteTime;
1871 entry->nFileSizeHigh = fileinfo.nFileSizeHigh;
1872 entry->nFileSizeLow = fileinfo.nFileSizeLow;
1874 if (short_name)
1875 DOSFS_ToDosDTAFormat( short_name, entry->cAlternateFileName );
1876 else
1877 DOSFS_Hash( long_name, entry->cAlternateFileName, FALSE,
1878 !(flags & DRIVE_CASE_SENSITIVE) );
1880 lstrcpynW( entry->cFileName, long_name, sizeof(entry->cFileName)/sizeof(entry->cFileName[0]) );
1881 if (!(flags & DRIVE_CASE_PRESERVING)) strlwrW( entry->cFileName );
1882 TRACE("returning %s (%s) %02lx %ld\n",
1883 debugstr_w(entry->cFileName), debugstr_w(entry->cAlternateFileName),
1884 entry->dwFileAttributes, entry->nFileSizeLow );
1885 return 1;
1887 return 0; /* End of directory */
1890 /***********************************************************************
1891 * DOSFS_FindNext
1893 * Find the next matching file. Return the number of entries read to find
1894 * the matching one, or 0 if no more entries.
1895 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
1896 * file name mask. Either or both can be NULL.
1898 * NOTE: This is supposed to be only called by the int21 emulation
1899 * routines, and so assumes that the Win16Mutex is held to
1900 * protect the static directory cache.
1902 int DOSFS_FindNext( const char *path, const char *short_mask,
1903 const char *long_mask, int drive, BYTE attr,
1904 int skip, WIN32_FIND_DATAA *entry )
1906 static FIND_FIRST_INFO info;
1907 LPCWSTR short_name, long_name;
1908 int count;
1909 UNICODE_STRING short_maskW, long_maskW;
1910 WIN32_FIND_DATAW entryW;
1912 TRACE("(%s, %s, %s, %x, %x, %x, %p)\n", debugstr_a(path),
1913 debugstr_a(short_mask), debugstr_a(long_mask), drive, attr, skip,
1914 entry);
1916 RtlCreateUnicodeStringFromAsciiz(&short_maskW, short_mask);
1917 RtlCreateUnicodeStringFromAsciiz(&long_maskW, long_mask);
1919 /* Check the cached directory */
1920 if (!(info.u.dos_dir && info.path == path && !strcmpW(info.short_mask, short_maskW.Buffer)
1921 && !strcmpW(info.long_mask, long_maskW.Buffer) && info.drive == drive
1922 && info.attr == attr && info.cur_pos <= skip))
1924 /* Not in the cache, open it anew */
1925 if (info.u.dos_dir) DOSFS_CloseDir( info.u.dos_dir );
1927 info.path = (LPSTR)path;
1928 RtlFreeHeap(GetProcessHeap(), 0, info.long_mask);
1929 RtlFreeHeap(GetProcessHeap(), 0, info.short_mask);
1930 info.long_mask = long_maskW.Buffer;
1931 info.short_mask = short_maskW.Buffer;
1932 info.attr = attr;
1933 info.drive = drive;
1934 info.cur_pos = 0;
1935 info.u.dos_dir = DOSFS_OpenDir( info.path );
1937 else
1939 RtlFreeUnicodeString(&short_maskW);
1940 RtlFreeUnicodeString(&long_maskW);
1943 /* Skip to desired position */
1944 while (info.cur_pos < skip)
1945 if (info.u.dos_dir && DOSFS_ReadDir( info.u.dos_dir, &long_name, &short_name ))
1946 info.cur_pos++;
1947 else
1948 break;
1950 if (info.u.dos_dir && info.cur_pos == skip && DOSFS_FindNextEx( &info, &entryW ))
1952 WideCharToMultiByte(CP_ACP, 0, entryW.cFileName, -1,
1953 entry->cFileName, sizeof(entry->cFileName), NULL, NULL);
1954 WideCharToMultiByte(CP_ACP, 0, entryW.cAlternateFileName, -1,
1955 entry->cAlternateFileName, sizeof(entry->cAlternateFileName), NULL, NULL);
1956 count = info.cur_pos - skip;
1958 entry->dwFileAttributes = entryW.dwFileAttributes;
1959 entry->nFileSizeHigh = entryW.nFileSizeHigh;
1960 entry->nFileSizeLow = entryW.nFileSizeLow;
1961 entry->ftCreationTime = entryW.ftCreationTime;
1962 entry->ftLastAccessTime = entryW.ftLastAccessTime;
1963 entry->ftLastWriteTime = entryW.ftLastWriteTime;
1966 else
1967 count = 0;
1969 if (!count)
1971 if (info.u.dos_dir) DOSFS_CloseDir( info.u.dos_dir );
1972 memset( &info, '\0', sizeof(info) );
1975 return count;
1978 /*************************************************************************
1979 * FindFirstFileExW (KERNEL32.@)
1981 HANDLE WINAPI FindFirstFileExW(
1982 LPCWSTR lpFileName,
1983 FINDEX_INFO_LEVELS fInfoLevelId,
1984 LPVOID lpFindFileData,
1985 FINDEX_SEARCH_OPS fSearchOp,
1986 LPVOID lpSearchFilter,
1987 DWORD dwAdditionalFlags)
1989 FIND_FIRST_INFO *info;
1991 if (!lpFileName)
1993 SetLastError(ERROR_PATH_NOT_FOUND);
1994 return INVALID_HANDLE_VALUE;
1997 if ((fSearchOp != FindExSearchNameMatch) || (dwAdditionalFlags != 0))
1999 FIXME("options not implemented 0x%08x 0x%08lx\n", fSearchOp, dwAdditionalFlags );
2000 return INVALID_HANDLE_VALUE;
2003 switch(fInfoLevelId)
2005 case FindExInfoStandard:
2007 WIN32_FIND_DATAW * data = (WIN32_FIND_DATAW *) lpFindFileData;
2008 char *p;
2009 INT long_mask_len;
2011 data->dwReserved0 = data->dwReserved1 = 0x0;
2012 if (lpFileName[0] == '\\' && lpFileName[1] == '\\')
2014 ERR("UNC path name\n");
2015 if (!(info = HeapAlloc( GetProcessHeap(), 0, sizeof(FIND_FIRST_INFO)))) break;
2016 info->u.smb_dir = SMB_FindFirst(lpFileName);
2017 if(!info->u.smb_dir)
2019 HeapFree(GetProcessHeap(), 0, info);
2020 break;
2022 info->drive = -1;
2023 RtlInitializeCriticalSection( &info->cs );
2025 else
2027 DOS_FULL_NAME full_name;
2029 if (lpFileName[0] && lpFileName[1] == ':')
2031 /* don't allow root directories */
2032 if (!lpFileName[2] ||
2033 ((lpFileName[2] == '/' || lpFileName[2] == '\\') && !lpFileName[3]))
2035 SetLastError(ERROR_FILE_NOT_FOUND);
2036 return INVALID_HANDLE_VALUE;
2039 if (!DOSFS_GetFullName( lpFileName, FALSE, &full_name )) break;
2040 if (!(info = HeapAlloc( GetProcessHeap(), 0, sizeof(FIND_FIRST_INFO)))) break;
2041 RtlInitializeCriticalSection( &info->cs );
2042 info->path = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
2043 strcpy( info->path, full_name.long_name );
2045 p = strrchr( info->path, '/' );
2046 *p++ = '\0';
2047 long_mask_len = MultiByteToWideChar(CP_UNIXCP, 0, p, -1, NULL, 0);
2048 info->long_mask = HeapAlloc( GetProcessHeap(), 0, long_mask_len * sizeof(WCHAR) );
2049 MultiByteToWideChar(CP_UNIXCP, 0, p, -1, info->long_mask, long_mask_len);
2051 info->short_mask = NULL;
2052 info->attr = 0xff;
2053 info->drive = full_name.drive;
2054 info->cur_pos = 0;
2056 info->u.dos_dir = DOSFS_OpenDir( info->path );
2058 if (!FindNextFileW( (HANDLE) info, data ))
2060 FindClose( (HANDLE) info );
2061 SetLastError( ERROR_FILE_NOT_FOUND );
2062 break;
2064 return (HANDLE) info;
2066 break;
2067 default:
2068 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
2070 return INVALID_HANDLE_VALUE;
2073 /*************************************************************************
2074 * FindFirstFileA (KERNEL32.@)
2076 HANDLE WINAPI FindFirstFileA(
2077 LPCSTR lpFileName,
2078 WIN32_FIND_DATAA *lpFindData )
2080 return FindFirstFileExA(lpFileName, FindExInfoStandard, lpFindData,
2081 FindExSearchNameMatch, NULL, 0);
2084 /*************************************************************************
2085 * FindFirstFileExA (KERNEL32.@)
2087 HANDLE WINAPI FindFirstFileExA(
2088 LPCSTR lpFileName,
2089 FINDEX_INFO_LEVELS fInfoLevelId,
2090 LPVOID lpFindFileData,
2091 FINDEX_SEARCH_OPS fSearchOp,
2092 LPVOID lpSearchFilter,
2093 DWORD dwAdditionalFlags)
2095 HANDLE handle;
2096 WIN32_FIND_DATAA *dataA;
2097 WIN32_FIND_DATAW dataW;
2098 UNICODE_STRING pathW;
2100 if (!lpFileName)
2102 SetLastError(ERROR_PATH_NOT_FOUND);
2103 return INVALID_HANDLE_VALUE;
2106 if (!RtlCreateUnicodeStringFromAsciiz(&pathW, lpFileName))
2108 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2109 return INVALID_HANDLE_VALUE;
2112 handle = FindFirstFileExW(pathW.Buffer, fInfoLevelId, &dataW, fSearchOp, lpSearchFilter, dwAdditionalFlags);
2113 RtlFreeUnicodeString(&pathW);
2114 if (handle == INVALID_HANDLE_VALUE) return handle;
2116 dataA = (WIN32_FIND_DATAA *) lpFindFileData;
2117 dataA->dwFileAttributes = dataW.dwFileAttributes;
2118 dataA->ftCreationTime = dataW.ftCreationTime;
2119 dataA->ftLastAccessTime = dataW.ftLastAccessTime;
2120 dataA->ftLastWriteTime = dataW.ftLastWriteTime;
2121 dataA->nFileSizeHigh = dataW.nFileSizeHigh;
2122 dataA->nFileSizeLow = dataW.nFileSizeLow;
2123 WideCharToMultiByte( CP_ACP, 0, dataW.cFileName, -1,
2124 dataA->cFileName, sizeof(dataA->cFileName), NULL, NULL );
2125 WideCharToMultiByte( CP_ACP, 0, dataW.cAlternateFileName, -1,
2126 dataA->cAlternateFileName, sizeof(dataA->cAlternateFileName), NULL, NULL );
2127 return handle;
2130 /*************************************************************************
2131 * FindFirstFileW (KERNEL32.@)
2133 HANDLE WINAPI FindFirstFileW( LPCWSTR lpFileName, WIN32_FIND_DATAW *lpFindData )
2135 return FindFirstFileExW(lpFileName, FindExInfoStandard, lpFindData,
2136 FindExSearchNameMatch, NULL, 0);
2139 /*************************************************************************
2140 * FindNextFileW (KERNEL32.@)
2142 BOOL WINAPI FindNextFileW( HANDLE handle, WIN32_FIND_DATAW *data )
2144 FIND_FIRST_INFO *info;
2145 BOOL ret = FALSE;
2146 DWORD gle = ERROR_NO_MORE_FILES;
2148 if (handle == INVALID_HANDLE_VALUE)
2150 SetLastError( ERROR_INVALID_HANDLE );
2151 return ret;
2153 info = (FIND_FIRST_INFO*) handle;
2154 RtlEnterCriticalSection( &info->cs );
2155 if (info->drive == -1)
2157 ret = SMB_FindNext( info->u.smb_dir, data );
2158 if(!ret)
2160 SMB_CloseDir( info->u.smb_dir );
2161 HeapFree( GetProcessHeap(), 0, info->path );
2163 goto done;
2165 else if (!info->path || !info->u.dos_dir)
2167 goto done;
2169 else if (!DOSFS_FindNextEx( info, data ))
2171 DOSFS_CloseDir( info->u.dos_dir ); info->u.dos_dir = NULL;
2172 HeapFree( GetProcessHeap(), 0, info->path );
2173 info->path = NULL;
2174 HeapFree( GetProcessHeap(), 0, info->long_mask );
2175 info->long_mask = NULL;
2176 goto done;
2178 ret = TRUE;
2179 done:
2180 RtlLeaveCriticalSection( &info->cs );
2181 if( !ret ) SetLastError( gle );
2182 return ret;
2186 /*************************************************************************
2187 * FindNextFileA (KERNEL32.@)
2189 BOOL WINAPI FindNextFileA( HANDLE handle, WIN32_FIND_DATAA *data )
2191 WIN32_FIND_DATAW dataW;
2192 if (!FindNextFileW( handle, &dataW )) return FALSE;
2193 data->dwFileAttributes = dataW.dwFileAttributes;
2194 data->ftCreationTime = dataW.ftCreationTime;
2195 data->ftLastAccessTime = dataW.ftLastAccessTime;
2196 data->ftLastWriteTime = dataW.ftLastWriteTime;
2197 data->nFileSizeHigh = dataW.nFileSizeHigh;
2198 data->nFileSizeLow = dataW.nFileSizeLow;
2199 WideCharToMultiByte( CP_ACP, 0, dataW.cFileName, -1,
2200 data->cFileName, sizeof(data->cFileName), NULL, NULL );
2201 WideCharToMultiByte( CP_ACP, 0, dataW.cAlternateFileName, -1,
2202 data->cAlternateFileName,
2203 sizeof(data->cAlternateFileName), NULL, NULL );
2204 return TRUE;
2207 /*************************************************************************
2208 * FindClose (KERNEL32.@)
2210 BOOL WINAPI FindClose( HANDLE handle )
2212 FIND_FIRST_INFO *info = (FIND_FIRST_INFO*) handle;
2214 if (handle == INVALID_HANDLE_VALUE) goto error;
2216 __TRY
2218 RtlEnterCriticalSection( &info->cs );
2219 if (info)
2221 if (info->u.dos_dir) DOSFS_CloseDir( info->u.dos_dir );
2222 if (info->path) HeapFree( GetProcessHeap(), 0, info->path );
2223 if (info->long_mask) HeapFree( GetProcessHeap(), 0, info->long_mask );
2226 __EXCEPT(page_fault)
2228 WARN("Illegal handle %p\n", handle);
2229 SetLastError( ERROR_INVALID_HANDLE );
2230 return FALSE;
2232 __ENDTRY
2233 if (!info) goto error;
2234 RtlLeaveCriticalSection( &info->cs );
2235 RtlDeleteCriticalSection( &info->cs );
2236 HeapFree(GetProcessHeap(), 0, info);
2237 return TRUE;
2239 error:
2240 SetLastError( ERROR_INVALID_HANDLE );
2241 return FALSE;
2244 /***********************************************************************
2245 * MulDiv (KERNEL32.@)
2246 * RETURNS
2247 * Result of multiplication and division
2248 * -1: Overflow occurred or Divisor was 0
2250 INT WINAPI MulDiv(
2251 INT nMultiplicand,
2252 INT nMultiplier,
2253 INT nDivisor)
2255 #if SIZEOF_LONG_LONG >= 8
2256 long long ret;
2258 if (!nDivisor) return -1;
2260 /* We want to deal with a positive divisor to simplify the logic. */
2261 if (nDivisor < 0)
2263 nMultiplicand = - nMultiplicand;
2264 nDivisor = -nDivisor;
2267 /* If the result is positive, we "add" to round. else, we subtract to round. */
2268 if ( ( (nMultiplicand < 0) && (nMultiplier < 0) ) ||
2269 ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
2270 ret = (((long long)nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
2271 else
2272 ret = (((long long)nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
2274 if ((ret > 2147483647) || (ret < -2147483647)) return -1;
2275 return ret;
2276 #else
2277 if (!nDivisor) return -1;
2279 /* We want to deal with a positive divisor to simplify the logic. */
2280 if (nDivisor < 0)
2282 nMultiplicand = - nMultiplicand;
2283 nDivisor = -nDivisor;
2286 /* If the result is positive, we "add" to round. else, we subtract to round. */
2287 if ( ( (nMultiplicand < 0) && (nMultiplier < 0) ) ||
2288 ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
2289 return ((nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
2291 return ((nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
2293 #endif
2297 /***********************************************************************
2298 * DosDateTimeToFileTime (KERNEL32.@)
2300 BOOL WINAPI DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft)
2302 struct tm newtm;
2303 #ifndef HAVE_TIMEGM
2304 struct tm *gtm;
2305 time_t time1, time2;
2306 #endif
2308 newtm.tm_sec = (fattime & 0x1f) * 2;
2309 newtm.tm_min = (fattime >> 5) & 0x3f;
2310 newtm.tm_hour = (fattime >> 11);
2311 newtm.tm_mday = (fatdate & 0x1f);
2312 newtm.tm_mon = ((fatdate >> 5) & 0x0f) - 1;
2313 newtm.tm_year = (fatdate >> 9) + 80;
2314 #ifdef HAVE_TIMEGM
2315 RtlSecondsSince1970ToTime( timegm(&newtm), (LARGE_INTEGER *)ft );
2316 #else
2317 time1 = mktime(&newtm);
2318 gtm = gmtime(&time1);
2319 time2 = mktime(gtm);
2320 RtlSecondsSince1970ToTime( 2*time1-time2, (LARGE_INTEGER *)ft );
2321 #endif
2322 return TRUE;
2326 /***********************************************************************
2327 * FileTimeToDosDateTime (KERNEL32.@)
2329 BOOL WINAPI FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate,
2330 LPWORD fattime )
2332 LARGE_INTEGER li;
2333 ULONG t;
2334 time_t unixtime;
2335 struct tm* tm;
2337 li.s.LowPart = ft->dwLowDateTime;
2338 li.s.HighPart = ft->dwHighDateTime;
2339 RtlTimeToSecondsSince1970( &li, &t );
2340 unixtime = t;
2341 tm = gmtime( &unixtime );
2342 if (fattime)
2343 *fattime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
2344 if (fatdate)
2345 *fatdate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
2346 + tm->tm_mday;
2347 return TRUE;
2351 /***********************************************************************
2352 * QueryDosDeviceA (KERNEL32.@)
2354 * returns array of strings terminated by \0, terminated by \0
2356 DWORD WINAPI QueryDosDeviceA(LPCSTR devname,LPSTR target,DWORD bufsize)
2358 DWORD ret = 0, retW;
2359 LPWSTR targetW = (LPWSTR)HeapAlloc(GetProcessHeap(),0,
2360 bufsize * sizeof(WCHAR));
2361 UNICODE_STRING devnameW;
2363 if(devname) RtlCreateUnicodeStringFromAsciiz(&devnameW, devname);
2364 else devnameW.Buffer = NULL;
2366 retW = QueryDosDeviceW(devnameW.Buffer, targetW, bufsize);
2368 ret = WideCharToMultiByte(CP_ACP, 0, targetW, retW, target,
2369 bufsize, NULL, NULL);
2371 RtlFreeUnicodeString(&devnameW);
2372 if (targetW) HeapFree(GetProcessHeap(),0,targetW);
2373 return ret;
2377 /***********************************************************************
2378 * QueryDosDeviceW (KERNEL32.@)
2380 * returns array of strings terminated by \0, terminated by \0
2382 * FIXME
2383 * - Win9x returns for all calls ERROR_INVALID_PARAMETER
2384 * - the returned devices for devname == NULL is far from complete
2385 * - its not checked that the returned device exist
2387 DWORD WINAPI QueryDosDeviceW(LPCWSTR devname,LPWSTR target,DWORD bufsize)
2389 const WCHAR *pDev, *pName, *pNum = NULL;
2390 int numsiz=0;
2391 DWORD ret;
2393 TRACE("(%s,...)\n", debugstr_w(devname));
2394 if (!devname) {
2395 /* return known MSDOS devices */
2396 DWORD ret = 0;
2397 int i;
2398 static const WCHAR devices[][5] = {{'A','U','X',0},
2399 {'C','O','M','1',0},
2400 {'C','O','M','2',0},
2401 {'L','P','T','1',0},
2402 {'N','U','L',0,}};
2403 for(i=0; (i< (sizeof(devices)/sizeof(devices[0]))); i++) {
2404 DWORD len = strlenW(devices[i]);
2405 if(target && (bufsize >= ret + len + 2)) {
2406 strcpyW(target+ret, devices[i]);
2407 ret += len + 1;
2408 } else {
2409 /* in this case WinXP returns 0 */
2410 FIXME("function return is wrong for WinXP!\n");
2411 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2412 break;
2415 /* append drives here */
2416 if(target && bufsize > 0) target[ret++] = 0;
2417 FIXME("Returned list is not complete\n");
2418 return ret;
2420 /* In theory all that are possible and have been defined.
2421 * Now just those below, since mirc uses it to check for special files.
2423 * (It is more complex, and supports netmounted stuff, and \\.\ stuff,
2424 * but currently we just ignore that.)
2426 if (!strcmpiW(devname, auxW)) {
2427 pDev = dosW;
2428 pName = comW;
2429 numsiz = 1;
2430 pNum = oneW;
2431 } else if (!strcmpiW(devname, nulW)) {
2432 pDev = devW;
2433 pName = nullW;
2434 } else if (!strncmpiW(devname, comW, strlenW(comW))) {
2435 pDev = devW;
2436 pName = serW;
2437 pNum = devname + strlenW(comW);
2438 for(numsiz=0; isdigitW(*(pNum+numsiz)); numsiz++);
2439 if(*(pNum + numsiz)) {
2440 SetLastError(ERROR_FILE_NOT_FOUND);
2441 return 0;
2443 } else if (!strncmpiW(devname, lptW, strlenW(lptW))) {
2444 pDev = devW;
2445 pName = parW;
2446 pNum = devname + strlenW(lptW);
2447 for(numsiz=0; isdigitW(*(pNum+numsiz)); numsiz++);
2448 if(*(pNum + numsiz)) {
2449 SetLastError(ERROR_FILE_NOT_FOUND);
2450 return 0;
2452 } else {
2453 /* This might be a DOS device we do not handle yet ... */
2454 FIXME("(%s) not detected as DOS device!\n",debugstr_w(devname));
2456 /* Win9x set the error ERROR_INVALID_PARAMETER */
2457 SetLastError(ERROR_FILE_NOT_FOUND);
2458 return 0;
2460 FIXME("device %s may not exist on this computer\n", debugstr_w(devname));
2462 ret = strlenW(pDev) + strlenW(pName) + numsiz + 2;
2463 if (ret > bufsize) ret = 0;
2464 if (target && ret) {
2465 strcpyW(target,pDev);
2466 strcatW(target,pName);
2467 if (pNum) strcatW(target,pNum);
2468 target[ret-1] = 0;
2470 return ret;
2474 /***********************************************************************
2475 * DefineDosDeviceA (KERNEL32.@)
2477 BOOL WINAPI DefineDosDeviceA(DWORD flags,LPCSTR devname,LPCSTR targetpath) {
2478 FIXME("(0x%08lx,%s,%s),stub!\n",flags,devname,targetpath);
2479 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2480 return FALSE;