Set win_style of the main window to WS_OVERLAPPEDWINDOW.
[wine.git] / files / dos_fs.c
blobe558da2d7a52623292803fbe7ad8ee508d8190a5
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(UINT codepage, 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(codepage, 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(codepage, 0, de[1].d_name, -1, long_name, MAX_PATH);
512 else
513 MultiByteToWideChar(codepage, 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( UINT codepage, 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(codepage, 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( UINT codepage, 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( codepage, &dir, unix_path);
587 if(!r)
588 r = DOSFS_OpenDir_Normal( codepage, &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( DRIVE_GetCodepage(path->drive), 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(DRIVE_GetCodepage(path->drive), 0,
806 long_name, -1, long_buf, long_len, NULL, NULL);
807 if (short_buf)
809 if (short_name)
810 DOSFS_ToDosDTAFormat( short_name, short_buf );
811 else
812 DOSFS_Hash( long_name, short_buf, FALSE, ignore_case );
814 TRACE("(%s,%s) -> %s (%s)\n", path->long_name, debugstr_w(name),
815 debugstr_w(long_name), short_buf ? debugstr_w(short_buf) : "***");
817 else
818 WARN("%s not found in '%s'\n", debugstr_w(name), path->long_name);
819 DOSFS_CloseDir( dir );
820 return ret;
824 /***********************************************************************
825 * DOSFS_GetDevice
827 * Check if a DOS file name represents a DOS device and return the device.
829 const DOS_DEVICE *DOSFS_GetDevice( LPCWSTR name )
831 unsigned int i;
832 const WCHAR *p;
834 if (!name) return NULL; /* if wine_server_handle_to_fd was used */
835 if (name[0] && (name[1] == ':')) name += 2;
836 if ((p = strrchrW( name, '/' ))) name = p + 1;
837 if ((p = strrchrW( name, '\\' ))) name = p + 1;
838 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
840 const WCHAR *dev = DOSFS_Devices[i].name;
841 if (!strncmpiW( dev, name, strlenW(dev) ))
843 p = name + strlenW( dev );
844 if (!*p || (*p == '.') || (*p == ':')) return &DOSFS_Devices[i];
847 return NULL;
851 /***********************************************************************
852 * DOSFS_GetDeviceByHandle
854 const DOS_DEVICE *DOSFS_GetDeviceByHandle( HANDLE hFile )
856 const DOS_DEVICE *ret = NULL;
857 SERVER_START_REQ( get_device_id )
859 req->handle = hFile;
860 if (!wine_server_call( req ))
862 if ((reply->id >= 0) &&
863 (reply->id < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0])))
864 ret = &DOSFS_Devices[reply->id];
867 SERVER_END_REQ;
868 return ret;
872 /**************************************************************************
873 * DOSFS_CreateCommPort
875 static HANDLE DOSFS_CreateCommPort(LPCWSTR name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa)
877 HANDLE ret;
878 HKEY hkey;
879 DWORD dummy;
880 OBJECT_ATTRIBUTES attr;
881 UNICODE_STRING nameW;
882 WCHAR *devnameW;
883 char tmp[128];
884 char devname[40];
886 static const WCHAR serialportsW[] = {'M','a','c','h','i','n','e','\\',
887 'S','o','f','t','w','a','r','e','\\',
888 'W','i','n','e','\\','W','i','n','e','\\',
889 'C','o','n','f','i','g','\\',
890 'S','e','r','i','a','l','P','o','r','t','s',0};
892 TRACE_(file)("%s %lx %lx\n", debugstr_w(name), access, attributes);
894 attr.Length = sizeof(attr);
895 attr.RootDirectory = 0;
896 attr.ObjectName = &nameW;
897 attr.Attributes = 0;
898 attr.SecurityDescriptor = NULL;
899 attr.SecurityQualityOfService = NULL;
900 RtlInitUnicodeString( &nameW, serialportsW );
902 if (NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr )) return 0;
904 RtlInitUnicodeString( &nameW, name );
905 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
906 devnameW = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
907 else
908 devnameW = NULL;
910 NtClose( hkey );
912 if (!devnameW) return 0;
913 WideCharToMultiByte(CP_ACP, 0, devnameW, -1, devname, sizeof(devname), NULL, NULL);
915 TRACE("opening %s as %s\n", devname, debugstr_w(name));
917 SERVER_START_REQ( create_serial )
919 req->access = access;
920 req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
921 req->attributes = attributes;
922 req->sharing = FILE_SHARE_READ|FILE_SHARE_WRITE;
923 wine_server_add_data( req, devname, strlen(devname) );
924 SetLastError(0);
925 wine_server_call_err( req );
926 ret = reply->handle;
928 SERVER_END_REQ;
930 if(!ret)
931 ERR("Couldn't open device '%s' ! (check permissions)\n",devname);
932 else
933 TRACE("return %p\n", ret );
934 return ret;
937 /***********************************************************************
938 * DOSFS_OpenDevice
940 * Open a DOS device. This might not map 1:1 into the UNIX device concept.
941 * Returns 0 on failure.
943 HANDLE DOSFS_OpenDevice( LPCWSTR name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa )
945 unsigned int i;
946 const WCHAR *p;
947 HANDLE handle;
949 if (name[0] && (name[1] == ':')) name += 2;
950 if ((p = strrchrW( name, '/' ))) name = p + 1;
951 if ((p = strrchrW( name, '\\' ))) name = p + 1;
952 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
954 const WCHAR *dev = DOSFS_Devices[i].name;
955 if (!strncmpiW( dev, name, strlenW(dev) ))
957 p = name + strlenW( dev );
958 if (!*p || (*p == '.') || (*p == ':')) {
959 static const WCHAR nulW[] = {'N','U','L',0};
960 static const WCHAR conW[] = {'C','O','N',0};
961 static const WCHAR scsimgrW[] = {'S','C','S','I','M','G','R','$',0};
962 static const WCHAR hpscanW[] = {'H','P','S','C','A','N',0};
963 static const WCHAR emmxxxx0W[] = {'E','M','M','X','X','X','X','0',0};
964 /* got it */
965 if (!strcmpiW(DOSFS_Devices[i].name, nulW))
966 return FILE_CreateFile( "/dev/null", access,
967 FILE_SHARE_READ|FILE_SHARE_WRITE, sa,
968 OPEN_EXISTING, 0, 0, TRUE, DRIVE_UNKNOWN );
969 if (!strcmpiW(DOSFS_Devices[i].name, conW)) {
970 HANDLE to_dup;
971 switch (access & (GENERIC_READ|GENERIC_WRITE)) {
972 case GENERIC_READ:
973 to_dup = GetStdHandle( STD_INPUT_HANDLE );
974 break;
975 case GENERIC_WRITE:
976 to_dup = GetStdHandle( STD_OUTPUT_HANDLE );
977 break;
978 default:
979 FIXME("can't open CON read/write\n");
980 return 0;
982 if (!DuplicateHandle( GetCurrentProcess(), to_dup, GetCurrentProcess(),
983 &handle, 0,
984 sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle,
985 DUPLICATE_SAME_ACCESS ))
986 handle = 0;
987 return handle;
989 if (!strcmpiW(DOSFS_Devices[i].name, scsimgrW) ||
990 !strcmpiW(DOSFS_Devices[i].name, hpscanW) ||
991 !strcmpiW(DOSFS_Devices[i].name, emmxxxx0W))
993 return FILE_CreateDevice( i, access, sa );
996 if( (handle=DOSFS_CreateCommPort(DOSFS_Devices[i].name,access,attributes,sa)) )
997 return handle;
998 FIXME("device open %s not supported (yet)\n", debugstr_w(DOSFS_Devices[i].name));
999 return 0;
1003 return 0;
1007 /***********************************************************************
1008 * DOSFS_GetPathDrive
1010 * Get the drive specified by a given path name (DOS or Unix format).
1012 static int DOSFS_GetPathDrive( LPCWSTR *name )
1014 int drive;
1015 LPCWSTR p = *name;
1017 if (*p && (p[1] == ':'))
1019 drive = toupperW(*p) - 'A';
1020 *name += 2;
1022 else if (*p == '/') /* Absolute Unix path? */
1024 if ((drive = DRIVE_FindDriveRootW( name )) == -1)
1026 MESSAGE("Warning: %s not accessible from a configured DOS drive\n", debugstr_w(*name) );
1027 /* Assume it really was a DOS name */
1028 drive = DRIVE_GetCurrentDrive();
1031 else drive = DRIVE_GetCurrentDrive();
1033 if (!DRIVE_IsValid(drive))
1035 SetLastError( ERROR_INVALID_DRIVE );
1036 return -1;
1038 return drive;
1042 /***********************************************************************
1043 * DOSFS_GetFullName
1045 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
1046 * Unix name / short DOS name pair.
1047 * Return FALSE if one of the path components does not exist. The last path
1048 * component is only checked if 'check_last' is non-zero.
1049 * The buffers pointed to by 'long_buf' and 'short_buf' must be
1050 * at least MAX_PATHNAME_LEN long.
1052 BOOL DOSFS_GetFullName( LPCWSTR name, BOOL check_last, DOS_FULL_NAME *full )
1054 BOOL found;
1055 UINT flags, codepage;
1056 char *p_l, *root;
1057 LPWSTR p_s;
1058 static const WCHAR driveA_rootW[] = {'A',':','\\',0};
1059 static const WCHAR dos_rootW[] = {'\\',0};
1061 TRACE("%s (last=%d)\n", debugstr_w(name), check_last );
1063 if ((!*name) || (*name=='\n'))
1064 { /* error code for Win98 */
1065 SetLastError(ERROR_BAD_PATHNAME);
1066 return FALSE;
1069 if ((full->drive = DOSFS_GetPathDrive( &name )) == -1) return FALSE;
1070 flags = DRIVE_GetFlags( full->drive );
1071 codepage = DRIVE_GetCodepage(full->drive);
1073 lstrcpynA( full->long_name, DRIVE_GetRoot( full->drive ),
1074 sizeof(full->long_name) );
1075 if (full->long_name[1]) root = full->long_name + strlen(full->long_name);
1076 else root = full->long_name; /* root directory */
1078 strcpyW( full->short_name, driveA_rootW );
1079 full->short_name[0] += full->drive;
1081 if ((*name == '\\') || (*name == '/')) /* Absolute path */
1083 while ((*name == '\\') || (*name == '/')) name++;
1085 else /* Relative path */
1087 lstrcpynA( root + 1, DRIVE_GetUnixCwd( full->drive ),
1088 sizeof(full->long_name) - (root - full->long_name) - 1 );
1089 if (root[1]) *root = '/';
1090 lstrcpynW( full->short_name + 3, DRIVE_GetDosCwd( full->drive ),
1091 sizeof(full->short_name)/sizeof(full->short_name[0]) - 3 );
1094 p_l = full->long_name[1] ? full->long_name + strlen(full->long_name)
1095 : full->long_name;
1096 p_s = full->short_name[3] ? full->short_name + strlenW(full->short_name)
1097 : full->short_name + 2;
1098 found = TRUE;
1100 while (*name && found)
1102 /* Check for '.' and '..' */
1104 if (*name == '.')
1106 if (IS_END_OF_NAME(name[1]))
1108 name++;
1109 while ((*name == '\\') || (*name == '/')) name++;
1110 continue;
1112 else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
1114 name += 2;
1115 while ((*name == '\\') || (*name == '/')) name++;
1116 while ((p_l > root) && (*p_l != '/')) p_l--;
1117 while ((p_s > full->short_name + 2) && (*p_s != '\\')) p_s--;
1118 *p_l = *p_s = '\0'; /* Remove trailing separator */
1119 continue;
1123 /* Make sure buffers are large enough */
1125 if ((p_s >= full->short_name + sizeof(full->short_name)/sizeof(full->short_name[0]) - 14) ||
1126 (p_l >= full->long_name + sizeof(full->long_name) - 1))
1128 SetLastError( ERROR_PATH_NOT_FOUND );
1129 return FALSE;
1132 /* Get the long and short name matching the file name */
1134 if ((found = DOSFS_FindUnixName( full, name, p_l + 1,
1135 sizeof(full->long_name) - (p_l - full->long_name) - 1,
1136 p_s + 1, !(flags & DRIVE_CASE_SENSITIVE) )))
1138 *p_l++ = '/';
1139 p_l += strlen(p_l);
1140 *p_s++ = '\\';
1141 p_s += strlenW(p_s);
1142 while (!IS_END_OF_NAME(*name)) name++;
1144 else if (!check_last)
1146 *p_l++ = '/';
1147 *p_s++ = '\\';
1148 while (!IS_END_OF_NAME(*name) &&
1149 (p_s < full->short_name + sizeof(full->short_name)/sizeof(full->short_name[0]) - 1) &&
1150 (p_l < full->long_name + sizeof(full->long_name) - 1))
1152 WCHAR wch;
1153 *p_s++ = tolowerW(*name);
1154 /* If the drive is case-sensitive we want to create new */
1155 /* files in lower-case otherwise we can't reopen them */
1156 /* under the same short name. */
1157 if (flags & DRIVE_CASE_SENSITIVE) wch = tolowerW(*name);
1158 else wch = *name;
1159 p_l += WideCharToMultiByte(codepage, 0, &wch, 1, p_l, 2, NULL, NULL);
1160 name++;
1162 /* Ignore trailing dots and spaces */
1163 while(p_l[-1] == '.' || p_l[-1] == ' ') {
1164 --p_l;
1165 --p_s;
1167 *p_l = '\0';
1168 *p_s = '\0';
1170 while ((*name == '\\') || (*name == '/')) name++;
1173 if (!found)
1175 if (check_last)
1177 SetLastError( ERROR_FILE_NOT_FOUND );
1178 return FALSE;
1180 if (*name) /* Not last */
1182 SetLastError( ERROR_PATH_NOT_FOUND );
1183 return FALSE;
1186 if (!full->long_name[0]) strcpy( full->long_name, "/" );
1187 if (!full->short_name[2]) strcpyW( full->short_name + 2, dos_rootW );
1188 TRACE("returning %s = %s\n", full->long_name, debugstr_w(full->short_name) );
1189 return TRUE;
1193 /***********************************************************************
1194 * GetShortPathNameW (KERNEL32.@)
1196 * NOTES
1197 * observed:
1198 * longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1199 * longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0
1201 * more observations ( with NT 3.51 (WinDD) ):
1202 * longpath <= 8.3 -> just copy longpath to shortpath
1203 * longpath > 8.3 ->
1204 * a) file does not exist -> return 0, LastError = ERROR_FILE_NOT_FOUND
1205 * b) file does exist -> set the short filename.
1206 * - trailing slashes are reproduced in the short name, even if the
1207 * file is not a directory
1208 * - the absolute/relative path of the short name is reproduced like found
1209 * in the long name
1210 * - longpath and shortpath may have the same address
1211 * Peter Ganten, 1999
1213 DWORD WINAPI GetShortPathNameW( LPCWSTR longpath, LPWSTR shortpath, DWORD shortlen )
1215 DOS_FULL_NAME full_name;
1216 WCHAR tmpshortpath[MAX_PATHNAME_LEN];
1217 const WCHAR *p;
1218 DWORD sp = 0, lp = 0;
1219 int drive;
1220 DWORD tmplen;
1221 UINT flags;
1222 BOOL unixabsolute = *longpath == '/';
1224 TRACE("%s\n", debugstr_w(longpath));
1226 if (!longpath) {
1227 SetLastError(ERROR_INVALID_PARAMETER);
1228 return 0;
1230 if (!longpath[0]) {
1231 SetLastError(ERROR_BAD_PATHNAME);
1232 return 0;
1235 /* check for drive letter */
1236 if (!unixabsolute && longpath[1] == ':' ) {
1237 tmpshortpath[0] = longpath[0];
1238 tmpshortpath[1] = ':';
1239 sp = 2;
1242 if ( ( drive = DOSFS_GetPathDrive ( &longpath )) == -1 ) return 0;
1243 flags = DRIVE_GetFlags ( drive );
1245 if (unixabsolute && drive != DRIVE_GetCurrentDrive()) {
1246 tmpshortpath[0] = drive + 'A';
1247 tmpshortpath[1] = ':';
1248 sp = 2;
1251 while ( longpath[lp] ) {
1253 /* check for path delimiters and reproduce them */
1254 if ( longpath[lp] == '\\' || longpath[lp] == '/' ) {
1255 if (!sp || tmpshortpath[sp-1]!= '\\')
1257 /* strip double "\\" */
1258 tmpshortpath[sp] = '\\';
1259 sp++;
1261 tmpshortpath[sp]=0;/*terminate string*/
1262 lp++;
1263 continue;
1266 tmplen = 0;
1267 for(p = longpath + lp; *p && *p != '/' && *p != '\\'; p++)
1268 tmplen++;
1269 lstrcpynW(tmpshortpath + sp, longpath + lp, tmplen + 1);
1271 /* Check, if the current element is a valid dos name */
1272 if ( DOSFS_ValidDOSName ( longpath + lp, !(flags & DRIVE_CASE_SENSITIVE) ) ) {
1273 sp += tmplen;
1274 lp += tmplen;
1275 continue;
1278 /* Check if the file exists and use the existing file name */
1279 if ( DOSFS_GetFullName ( tmpshortpath, TRUE, &full_name ) ) {
1280 strcpyW(tmpshortpath + sp, strrchrW(full_name.short_name, '\\') + 1);
1281 sp += strlenW(tmpshortpath + sp);
1282 lp += tmplen;
1283 continue;
1286 TRACE("not found!\n" );
1287 SetLastError ( ERROR_FILE_NOT_FOUND );
1288 return 0;
1290 tmpshortpath[sp] = 0;
1292 tmplen = strlenW(tmpshortpath) + 1;
1293 if (tmplen <= shortlen)
1295 strcpyW(shortpath, tmpshortpath);
1296 TRACE("returning %s\n", debugstr_w(shortpath));
1297 tmplen--; /* length without 0 */
1300 return tmplen;
1304 /***********************************************************************
1305 * GetShortPathNameA (KERNEL32.@)
1307 DWORD WINAPI GetShortPathNameA( LPCSTR longpath, LPSTR shortpath, DWORD shortlen )
1309 UNICODE_STRING longpathW;
1310 WCHAR shortpathW[MAX_PATH];
1311 DWORD ret, retW;
1313 if (!longpath)
1315 SetLastError(ERROR_INVALID_PARAMETER);
1316 return 0;
1319 TRACE("%s\n", debugstr_a(longpath));
1321 if (!RtlCreateUnicodeStringFromAsciiz(&longpathW, longpath))
1323 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1324 return 0;
1327 retW = GetShortPathNameW(longpathW.Buffer, shortpathW, MAX_PATH);
1329 if (!retW)
1330 ret = 0;
1331 else if (retW > MAX_PATH)
1333 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1334 ret = 0;
1336 else
1338 ret = WideCharToMultiByte(CP_ACP, 0, shortpathW, -1, NULL, 0, NULL, NULL);
1339 if (ret <= shortlen)
1341 WideCharToMultiByte(CP_ACP, 0, shortpathW, -1, shortpath, shortlen, NULL, NULL);
1342 ret--; /* length without 0 */
1346 RtlFreeUnicodeString(&longpathW);
1347 return ret;
1351 /***********************************************************************
1352 * GetLongPathNameW (KERNEL32.@)
1354 * NOTES
1355 * observed (Win2000):
1356 * shortpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1357 * shortpath="": LastError=ERROR_PATH_NOT_FOUND, ret=0
1359 DWORD WINAPI GetLongPathNameW( LPCWSTR shortpath, LPWSTR longpath, DWORD longlen )
1361 DOS_FULL_NAME full_name;
1362 const char *root;
1363 LPWSTR p;
1364 int drive;
1365 UINT codepage;
1366 DWORD ret, len = 0;
1368 if (!shortpath) {
1369 SetLastError(ERROR_INVALID_PARAMETER);
1370 return 0;
1372 if (!shortpath[0]) {
1373 SetLastError(ERROR_PATH_NOT_FOUND);
1374 return 0;
1377 TRACE("%s,%p,%ld\n", debugstr_w(shortpath), longpath, longlen);
1379 if(shortpath[0]=='\\' && shortpath[1]=='\\')
1381 ERR("UNC pathname %s\n",debugstr_w(shortpath));
1382 lstrcpynW( longpath, full_name.short_name, longlen );
1383 return strlenW(longpath);
1386 if (!DOSFS_GetFullName( shortpath, TRUE, &full_name )) return 0;
1388 root = full_name.long_name;
1389 drive = DRIVE_FindDriveRoot(&root);
1390 codepage = DRIVE_GetCodepage(drive);
1392 ret = MultiByteToWideChar(codepage, 0, root, -1, NULL, 0);
1393 ret += 3; /* A:\ */
1394 /* reproduce terminating slash */
1395 if (ret > 4) /* if not drive root */
1397 len = strlenW(shortpath);
1398 if (shortpath[len - 1] == '\\' || shortpath[len - 1] == '/')
1399 len = 1;
1401 ret += len;
1402 if (ret <= longlen)
1404 longpath[0] = 'A' + drive;
1405 longpath[1] = ':';
1406 MultiByteToWideChar(codepage, 0, root, -1, longpath + 2, longlen - 2);
1407 for (p = longpath; *p; p++) if (*p == '/') *p = '\\';
1408 if (len)
1410 longpath[ret - 2] = '\\';
1411 longpath[ret - 1] = 0;
1413 TRACE("returning %s\n", debugstr_w(longpath));
1414 ret--; /* length without 0 */
1416 return ret;
1420 /***********************************************************************
1421 * GetLongPathNameA (KERNEL32.@)
1423 DWORD WINAPI GetLongPathNameA( LPCSTR shortpath, LPSTR longpath, DWORD longlen )
1425 UNICODE_STRING shortpathW;
1426 WCHAR longpathW[MAX_PATH];
1427 DWORD ret, retW;
1429 if (!shortpath)
1431 SetLastError(ERROR_INVALID_PARAMETER);
1432 return 0;
1435 TRACE("%s\n", debugstr_a(shortpath));
1437 if (!RtlCreateUnicodeStringFromAsciiz(&shortpathW, shortpath))
1439 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1440 return 0;
1443 retW = GetLongPathNameW(shortpathW.Buffer, longpathW, MAX_PATH);
1445 if (!retW)
1446 ret = 0;
1447 else if (retW > MAX_PATH)
1449 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1450 ret = 0;
1452 else
1454 ret = WideCharToMultiByte(CP_ACP, 0, longpathW, -1, NULL, 0, NULL, NULL);
1455 if (ret <= longlen)
1457 WideCharToMultiByte(CP_ACP, 0, longpathW, -1, longpath, longlen, NULL, NULL);
1458 ret--; /* length without 0 */
1462 RtlFreeUnicodeString(&shortpathW);
1463 return ret;
1467 /***********************************************************************
1468 * DOSFS_DoGetFullPathName
1470 * Implementation of GetFullPathNameA/W.
1472 * bon@elektron 000331:
1473 * A test for GetFullPathName with many pathological cases
1474 * now gives identical output for Wine and OSR2
1476 static DWORD DOSFS_DoGetFullPathName( LPCWSTR name, DWORD len, LPWSTR result )
1478 DWORD ret;
1479 DOS_FULL_NAME full_name;
1480 LPWSTR p, q;
1481 char *p_l;
1482 const char * root;
1483 WCHAR drivecur[] = {'C',':','.',0};
1484 WCHAR driveletter=0;
1485 int namelen,drive=0;
1486 static const WCHAR bkslashW[] = {'\\',0};
1487 static const WCHAR dotW[] = {'.',0};
1488 static const WCHAR updir_slashW[] = {'\\','.','.','\\',0};
1489 static const WCHAR curdirW[] = {'\\','.','\\',0};
1490 static const WCHAR updirW[] = {'\\','.','.',0};
1492 if (!name[0])
1494 SetLastError(ERROR_BAD_PATHNAME);
1495 return 0;
1498 TRACE("passed %s\n", debugstr_w(name));
1500 if (name[1]==':')
1501 /*drive letter given */
1503 driveletter = name[0];
1505 if ((name[1]==':') && ((name[2]=='\\') || (name[2]=='/')))
1506 /*absolute path given */
1508 strncpyW(full_name.short_name, name, MAX_PATHNAME_LEN);
1509 full_name.short_name[MAX_PATHNAME_LEN - 1] = 0; /* ensure 0 termination */
1510 drive = toupperW(name[0]) - 'A';
1512 else
1514 if (driveletter)
1515 drivecur[0]=driveletter;
1516 else if ((name[0]=='\\') || (name[0]=='/'))
1517 strcpyW(drivecur, bkslashW);
1518 else
1519 strcpyW(drivecur, dotW);
1521 if (!DOSFS_GetFullName( drivecur, FALSE, &full_name ))
1523 FIXME("internal: error getting drive/path\n");
1524 return 0;
1526 /* find path that drive letter substitutes*/
1527 drive = toupperW(full_name.short_name[0]) - 'A';
1528 root= DRIVE_GetRoot(drive);
1529 if (!root)
1531 FIXME("internal: error getting DOS Drive Root\n");
1532 return 0;
1534 if (!strcmp(root,"/"))
1536 /* we have just the last / and we need it. */
1537 p_l = full_name.long_name;
1539 else
1541 p_l = full_name.long_name + strlen(root);
1543 /* append long name (= unix name) to drive */
1544 MultiByteToWideChar(DRIVE_GetCodepage(drive), 0, p_l, -1,
1545 full_name.short_name + 2, MAX_PATHNAME_LEN - 3);
1546 /* append name to treat */
1547 namelen= strlenW(full_name.short_name);
1548 p = (LPWSTR)name;
1549 if (driveletter)
1550 p += 2; /* skip drive name when appending */
1551 if (namelen + 2 + strlenW(p) > MAX_PATHNAME_LEN)
1553 FIXME("internal error: buffer too small\n");
1554 return 0;
1556 full_name.short_name[namelen++] ='\\';
1557 full_name.short_name[namelen] = 0;
1558 strncpyW(full_name.short_name + namelen, p, MAX_PATHNAME_LEN - namelen);
1559 full_name.short_name[MAX_PATHNAME_LEN - 1] = 0; /* ensure 0 termination */
1561 /* reverse all slashes */
1562 for (p=full_name.short_name;
1563 p < full_name.short_name + strlenW(full_name.short_name);
1564 p++)
1566 if ( *p == '/' )
1567 *p = '\\';
1569 /* Use memmove, as areas overlap */
1570 /* Delete .. */
1571 while ((p = strstrW(full_name.short_name, updir_slashW)))
1573 if (p > full_name.short_name+2)
1575 *p = 0;
1576 q = strrchrW(full_name.short_name, '\\');
1577 memmove(q+1, p+4, (strlenW(p+4)+1) * sizeof(WCHAR));
1579 else
1581 memmove(full_name.short_name+3, p+4, (strlenW(p+4)+1) * sizeof(WCHAR));
1584 if ((full_name.short_name[2]=='.')&&(full_name.short_name[3]=='.'))
1586 /* This case istn't treated yet : c:..\test */
1587 memmove(full_name.short_name+2,full_name.short_name+4,
1588 (strlenW(full_name.short_name+4)+1) * sizeof(WCHAR));
1590 /* Delete . */
1591 while ((p = strstrW(full_name.short_name, curdirW)))
1593 *(p+1) = 0;
1594 memmove(p+1, p+3, (strlenW(p+3)+1) * sizeof(WCHAR));
1596 if (!(DRIVE_GetFlags(drive) & DRIVE_CASE_PRESERVING))
1597 for (p = full_name.short_name; *p; p++) *p = toupperW(*p);
1598 namelen = strlenW(full_name.short_name);
1599 if (!strcmpW(full_name.short_name+namelen-3, updirW))
1601 /* one more strange case: "c:\test\test1\.."
1602 return "c:\test" */
1603 *(full_name.short_name+namelen-3)=0;
1604 q = strrchrW(full_name.short_name, '\\');
1605 *q =0;
1607 if (full_name.short_name[namelen-1]=='.')
1608 full_name.short_name[(namelen--)-1] =0;
1609 if (!driveletter)
1610 if (full_name.short_name[namelen-1]=='\\')
1611 full_name.short_name[(namelen--)-1] =0;
1612 TRACE("got %s\n", debugstr_w(full_name.short_name));
1614 /* If the lpBuffer buffer is too small, the return value is the
1615 size of the buffer, in characters, required to hold the path
1616 plus the terminating \0 (tested against win95osr2, bon 001118)
1617 . */
1618 ret = strlenW(full_name.short_name);
1619 if (ret >= len )
1621 /* don't touch anything when the buffer is not large enough */
1622 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1623 return ret+1;
1625 if (result)
1627 strncpyW( result, full_name.short_name, len );
1628 result[len - 1] = 0; /* ensure 0 termination */
1631 TRACE("returning %s\n", debugstr_w(full_name.short_name) );
1632 return ret;
1636 /***********************************************************************
1637 * GetFullPathNameA (KERNEL32.@)
1638 * NOTES
1639 * if the path closed with '\', *lastpart is 0
1641 DWORD WINAPI GetFullPathNameA( LPCSTR name, DWORD len, LPSTR buffer,
1642 LPSTR *lastpart )
1644 UNICODE_STRING nameW;
1645 WCHAR bufferW[MAX_PATH];
1646 DWORD ret, retW;
1648 if (!name)
1650 SetLastError(ERROR_INVALID_PARAMETER);
1651 return 0;
1654 if (!RtlCreateUnicodeStringFromAsciiz(&nameW, name))
1656 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1657 return 0;
1660 retW = GetFullPathNameW( nameW.Buffer, MAX_PATH, bufferW, NULL);
1662 if (!retW)
1663 ret = 0;
1664 else if (retW > MAX_PATH)
1666 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1667 ret = 0;
1669 else
1671 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL);
1672 if (ret <= len)
1674 WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, len, NULL, NULL);
1675 ret--; /* length without 0 */
1677 if (lastpart)
1679 LPSTR p = buffer + strlen(buffer);
1681 if (*p != '\\')
1683 while ((p > buffer + 2) && (*p != '\\')) p--;
1684 *lastpart = p + 1;
1686 else *lastpart = NULL;
1691 RtlFreeUnicodeString(&nameW);
1692 return ret;
1696 /***********************************************************************
1697 * GetFullPathNameW (KERNEL32.@)
1699 DWORD WINAPI GetFullPathNameW( LPCWSTR name, DWORD len, LPWSTR buffer,
1700 LPWSTR *lastpart )
1702 DWORD ret = DOSFS_DoGetFullPathName( name, len, buffer );
1703 if (ret && (ret<=len) && buffer && lastpart)
1705 LPWSTR p = buffer + strlenW(buffer);
1706 if (*p != (WCHAR)'\\')
1708 while ((p > buffer + 2) && (*p != (WCHAR)'\\')) p--;
1709 *lastpart = p + 1;
1711 else *lastpart = NULL;
1713 return ret;
1717 /***********************************************************************
1718 * wine_get_unix_file_name (KERNEL32.@) Not a Windows API
1720 * Return the full Unix file name for a given path.
1721 * FIXME: convert dos file name to unicode
1723 BOOL WINAPI wine_get_unix_file_name( LPCSTR dos, LPSTR buffer, DWORD len )
1725 BOOL ret;
1726 DOS_FULL_NAME path;
1727 WCHAR dosW[MAX_PATHNAME_LEN];
1729 MultiByteToWideChar(CP_ACP, 0, dos, -1, dosW, MAX_PATHNAME_LEN);
1730 ret = DOSFS_GetFullName( dosW, FALSE, &path );
1731 if (ret && len)
1733 strncpy( buffer, path.long_name, len );
1734 buffer[len - 1] = 0; /* ensure 0 termination */
1736 return ret;
1740 /***********************************************************************
1741 * get_show_dir_symlinks_option
1743 static BOOL get_show_dir_symlinks_option(void)
1745 static const WCHAR WineW[] = {'M','a','c','h','i','n','e','\\',
1746 'S','o','f','t','w','a','r','e','\\',
1747 'W','i','n','e','\\','W','i','n','e','\\',
1748 'C','o','n','f','i','g','\\','W','i','n','e',0};
1749 static const WCHAR ShowDirSymlinksW[] = {'S','h','o','w','D','i','r','S','y','m','l','i','n','k','s',0};
1751 char tmp[80];
1752 HKEY hkey;
1753 DWORD dummy;
1754 OBJECT_ATTRIBUTES attr;
1755 UNICODE_STRING nameW;
1756 BOOL ret = FALSE;
1758 attr.Length = sizeof(attr);
1759 attr.RootDirectory = 0;
1760 attr.ObjectName = &nameW;
1761 attr.Attributes = 0;
1762 attr.SecurityDescriptor = NULL;
1763 attr.SecurityQualityOfService = NULL;
1764 RtlInitUnicodeString( &nameW, WineW );
1766 if (!NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr ))
1768 RtlInitUnicodeString( &nameW, ShowDirSymlinksW );
1769 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
1771 WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
1772 ret = IS_OPTION_TRUE( str[0] );
1774 NtClose( hkey );
1776 return ret;
1780 /***********************************************************************
1781 * DOSFS_FindNextEx
1783 static int DOSFS_FindNextEx( FIND_FIRST_INFO *info, WIN32_FIND_DATAW *entry )
1785 DWORD attr = info->attr | FA_UNUSED | FA_ARCHIVE | FA_RDONLY;
1786 UINT flags = DRIVE_GetFlags( info->drive );
1787 char *p, buffer[MAX_PATHNAME_LEN];
1788 const char *drive_path;
1789 int drive_root;
1790 LPCWSTR long_name, short_name;
1791 BY_HANDLE_FILE_INFORMATION fileinfo;
1792 WCHAR dos_name[13];
1793 BOOL is_symlink;
1795 if ((info->attr & ~(FA_UNUSED | FA_ARCHIVE | FA_RDONLY)) == FA_LABEL)
1797 if (info->cur_pos) return 0;
1798 entry->dwFileAttributes = FILE_ATTRIBUTE_LABEL;
1799 RtlSecondsSince1970ToTime( (time_t)0, (LARGE_INTEGER *)&entry->ftCreationTime );
1800 RtlSecondsSince1970ToTime( (time_t)0, (LARGE_INTEGER *)&entry->ftLastAccessTime );
1801 RtlSecondsSince1970ToTime( (time_t)0, (LARGE_INTEGER *)&entry->ftLastWriteTime );
1802 entry->nFileSizeHigh = 0;
1803 entry->nFileSizeLow = 0;
1804 entry->dwReserved0 = 0;
1805 entry->dwReserved1 = 0;
1806 DOSFS_ToDosDTAFormat( DRIVE_GetLabel( info->drive ), entry->cFileName );
1807 strcpyW( entry->cAlternateFileName, entry->cFileName );
1808 info->cur_pos++;
1809 TRACE("returning %s (%s) as label\n",
1810 debugstr_w(entry->cFileName), debugstr_w(entry->cAlternateFileName));
1811 return 1;
1814 drive_path = info->path + strlen(DRIVE_GetRoot( info->drive ));
1815 while ((*drive_path == '/') || (*drive_path == '\\')) drive_path++;
1816 drive_root = !*drive_path;
1818 lstrcpynA( buffer, info->path, sizeof(buffer) - 1 );
1819 strcat( buffer, "/" );
1820 p = buffer + strlen(buffer);
1822 while (DOSFS_ReadDir( info->u.dos_dir, &long_name, &short_name ))
1824 info->cur_pos++;
1826 /* Don't return '.' and '..' in the root of the drive */
1827 if (drive_root && (long_name[0] == '.') &&
1828 (!long_name[1] || ((long_name[1] == '.') && !long_name[2])))
1829 continue;
1831 /* Check the long mask */
1833 if (info->long_mask && *info->long_mask)
1835 if (!DOSFS_MatchLong( info->long_mask, long_name,
1836 flags & DRIVE_CASE_SENSITIVE )) continue;
1839 /* Check the short mask */
1841 if (info->short_mask)
1843 if (!short_name)
1845 DOSFS_Hash( long_name, dos_name, TRUE,
1846 !(flags & DRIVE_CASE_SENSITIVE) );
1847 short_name = dos_name;
1849 if (!DOSFS_MatchShort( info->short_mask, short_name )) continue;
1852 /* Check the file attributes */
1853 WideCharToMultiByte(DRIVE_GetCodepage(info->drive), 0, long_name, -1,
1854 p, sizeof(buffer) - (int)(p - buffer), NULL, NULL);
1855 if (!FILE_Stat( buffer, &fileinfo, &is_symlink ))
1857 WARN("can't stat %s\n", buffer);
1858 continue;
1860 if (is_symlink && (fileinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
1862 static int show_dir_symlinks = -1;
1863 if (show_dir_symlinks == -1)
1864 show_dir_symlinks = get_show_dir_symlinks_option();
1865 if (!show_dir_symlinks) continue;
1868 if (fileinfo.dwFileAttributes & ~attr) continue;
1870 /* We now have a matching entry; fill the result and return */
1872 entry->dwFileAttributes = fileinfo.dwFileAttributes;
1873 entry->ftCreationTime = fileinfo.ftCreationTime;
1874 entry->ftLastAccessTime = fileinfo.ftLastAccessTime;
1875 entry->ftLastWriteTime = fileinfo.ftLastWriteTime;
1876 entry->nFileSizeHigh = fileinfo.nFileSizeHigh;
1877 entry->nFileSizeLow = fileinfo.nFileSizeLow;
1879 if (short_name)
1880 DOSFS_ToDosDTAFormat( short_name, entry->cAlternateFileName );
1881 else
1882 DOSFS_Hash( long_name, entry->cAlternateFileName, FALSE,
1883 !(flags & DRIVE_CASE_SENSITIVE) );
1885 lstrcpynW( entry->cFileName, long_name, sizeof(entry->cFileName)/sizeof(entry->cFileName[0]) );
1886 if (!(flags & DRIVE_CASE_PRESERVING)) strlwrW( entry->cFileName );
1887 TRACE("returning %s (%s) %02lx %ld\n",
1888 debugstr_w(entry->cFileName), debugstr_w(entry->cAlternateFileName),
1889 entry->dwFileAttributes, entry->nFileSizeLow );
1890 return 1;
1892 return 0; /* End of directory */
1895 /***********************************************************************
1896 * DOSFS_FindNext
1898 * Find the next matching file. Return the number of entries read to find
1899 * the matching one, or 0 if no more entries.
1900 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
1901 * file name mask. Either or both can be NULL.
1903 * NOTE: This is supposed to be only called by the int21 emulation
1904 * routines, and so assumes that the Win16Mutex is held to
1905 * protect the static directory cache.
1907 int DOSFS_FindNext( const char *path, const char *short_mask,
1908 const char *long_mask, int drive, BYTE attr,
1909 int skip, WIN32_FIND_DATAA *entry )
1911 static FIND_FIRST_INFO info;
1912 LPCWSTR short_name, long_name;
1913 int count;
1914 UNICODE_STRING short_maskW, long_maskW;
1915 WIN32_FIND_DATAW entryW;
1917 TRACE("(%s, %s, %s, %x, %x, %x, %p)\n", debugstr_a(path),
1918 debugstr_a(short_mask), debugstr_a(long_mask), drive, attr, skip,
1919 entry);
1921 RtlCreateUnicodeStringFromAsciiz(&short_maskW, short_mask);
1922 RtlCreateUnicodeStringFromAsciiz(&long_maskW, long_mask);
1924 /* Check the cached directory */
1925 if (!(info.u.dos_dir && info.path == path && !strcmpW(info.short_mask, short_maskW.Buffer)
1926 && !strcmpW(info.long_mask, long_maskW.Buffer) && info.drive == drive
1927 && info.attr == attr && info.cur_pos <= skip))
1929 /* Not in the cache, open it anew */
1930 if (info.u.dos_dir) DOSFS_CloseDir( info.u.dos_dir );
1932 info.path = (LPSTR)path;
1933 RtlFreeHeap(GetProcessHeap(), 0, info.long_mask);
1934 RtlFreeHeap(GetProcessHeap(), 0, info.short_mask);
1935 info.long_mask = long_maskW.Buffer;
1936 info.short_mask = short_maskW.Buffer;
1937 info.attr = attr;
1938 info.drive = drive;
1939 info.cur_pos = 0;
1940 info.u.dos_dir = DOSFS_OpenDir( DRIVE_GetCodepage(drive), info.path );
1942 else
1944 RtlFreeUnicodeString(&short_maskW);
1945 RtlFreeUnicodeString(&long_maskW);
1948 /* Skip to desired position */
1949 while (info.cur_pos < skip)
1950 if (info.u.dos_dir && DOSFS_ReadDir( info.u.dos_dir, &long_name, &short_name ))
1951 info.cur_pos++;
1952 else
1953 break;
1955 if (info.u.dos_dir && info.cur_pos == skip && DOSFS_FindNextEx( &info, &entryW ))
1957 WideCharToMultiByte(CP_ACP, 0, entryW.cFileName, -1,
1958 entry->cFileName, sizeof(entry->cFileName), NULL, NULL);
1959 WideCharToMultiByte(CP_ACP, 0, entryW.cAlternateFileName, -1,
1960 entry->cAlternateFileName, sizeof(entry->cAlternateFileName), NULL, NULL);
1961 count = info.cur_pos - skip;
1963 entry->dwFileAttributes = entryW.dwFileAttributes;
1964 entry->nFileSizeHigh = entryW.nFileSizeHigh;
1965 entry->nFileSizeLow = entryW.nFileSizeLow;
1966 entry->ftCreationTime = entryW.ftCreationTime;
1967 entry->ftLastAccessTime = entryW.ftLastAccessTime;
1968 entry->ftLastWriteTime = entryW.ftLastWriteTime;
1971 else
1972 count = 0;
1974 if (!count)
1976 if (info.u.dos_dir) DOSFS_CloseDir( info.u.dos_dir );
1977 memset( &info, '\0', sizeof(info) );
1980 return count;
1983 /*************************************************************************
1984 * FindFirstFileExW (KERNEL32.@)
1986 HANDLE WINAPI FindFirstFileExW(
1987 LPCWSTR lpFileName,
1988 FINDEX_INFO_LEVELS fInfoLevelId,
1989 LPVOID lpFindFileData,
1990 FINDEX_SEARCH_OPS fSearchOp,
1991 LPVOID lpSearchFilter,
1992 DWORD dwAdditionalFlags)
1994 FIND_FIRST_INFO *info;
1996 if (!lpFileName)
1998 SetLastError(ERROR_PATH_NOT_FOUND);
1999 return INVALID_HANDLE_VALUE;
2002 if ((fSearchOp != FindExSearchNameMatch) || (dwAdditionalFlags != 0))
2004 FIXME("options not implemented 0x%08x 0x%08lx\n", fSearchOp, dwAdditionalFlags );
2005 return INVALID_HANDLE_VALUE;
2008 switch(fInfoLevelId)
2010 case FindExInfoStandard:
2012 WIN32_FIND_DATAW * data = (WIN32_FIND_DATAW *) lpFindFileData;
2013 char *p;
2014 INT long_mask_len;
2015 UINT codepage;
2017 data->dwReserved0 = data->dwReserved1 = 0x0;
2018 if (lpFileName[0] == '\\' && lpFileName[1] == '\\')
2020 ERR("UNC path name\n");
2021 if (!(info = HeapAlloc( GetProcessHeap(), 0, sizeof(FIND_FIRST_INFO)))) break;
2022 info->u.smb_dir = SMB_FindFirst(lpFileName);
2023 if(!info->u.smb_dir)
2025 HeapFree(GetProcessHeap(), 0, info);
2026 break;
2028 info->drive = -1;
2029 RtlInitializeCriticalSection( &info->cs );
2031 else
2033 DOS_FULL_NAME full_name;
2035 if (lpFileName[0] && lpFileName[1] == ':')
2037 /* don't allow root directories */
2038 if (!lpFileName[2] ||
2039 ((lpFileName[2] == '/' || lpFileName[2] == '\\') && !lpFileName[3]))
2041 SetLastError(ERROR_FILE_NOT_FOUND);
2042 return INVALID_HANDLE_VALUE;
2045 if (!DOSFS_GetFullName( lpFileName, FALSE, &full_name )) break;
2046 if (!(info = HeapAlloc( GetProcessHeap(), 0, sizeof(FIND_FIRST_INFO)))) break;
2047 RtlInitializeCriticalSection( &info->cs );
2048 info->path = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
2049 strcpy( info->path, full_name.long_name );
2051 codepage = DRIVE_GetCodepage(full_name.drive);
2052 p = strrchr( info->path, '/' );
2053 *p++ = '\0';
2054 long_mask_len = MultiByteToWideChar(codepage, 0, p, -1, NULL, 0);
2055 info->long_mask = HeapAlloc( GetProcessHeap(), 0, long_mask_len * sizeof(WCHAR) );
2056 MultiByteToWideChar(codepage, 0, p, -1, info->long_mask, long_mask_len);
2058 info->short_mask = NULL;
2059 info->attr = 0xff;
2060 info->drive = full_name.drive;
2061 info->cur_pos = 0;
2063 info->u.dos_dir = DOSFS_OpenDir( codepage, info->path );
2065 if (!FindNextFileW( (HANDLE) info, data ))
2067 FindClose( (HANDLE) info );
2068 SetLastError( ERROR_FILE_NOT_FOUND );
2069 break;
2071 return (HANDLE) info;
2073 break;
2074 default:
2075 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
2077 return INVALID_HANDLE_VALUE;
2080 /*************************************************************************
2081 * FindFirstFileA (KERNEL32.@)
2083 HANDLE WINAPI FindFirstFileA(
2084 LPCSTR lpFileName,
2085 WIN32_FIND_DATAA *lpFindData )
2087 return FindFirstFileExA(lpFileName, FindExInfoStandard, lpFindData,
2088 FindExSearchNameMatch, NULL, 0);
2091 /*************************************************************************
2092 * FindFirstFileExA (KERNEL32.@)
2094 HANDLE WINAPI FindFirstFileExA(
2095 LPCSTR lpFileName,
2096 FINDEX_INFO_LEVELS fInfoLevelId,
2097 LPVOID lpFindFileData,
2098 FINDEX_SEARCH_OPS fSearchOp,
2099 LPVOID lpSearchFilter,
2100 DWORD dwAdditionalFlags)
2102 HANDLE handle;
2103 WIN32_FIND_DATAA *dataA;
2104 WIN32_FIND_DATAW dataW;
2105 UNICODE_STRING pathW;
2107 if (!lpFileName)
2109 SetLastError(ERROR_PATH_NOT_FOUND);
2110 return INVALID_HANDLE_VALUE;
2113 if (!RtlCreateUnicodeStringFromAsciiz(&pathW, lpFileName))
2115 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2116 return INVALID_HANDLE_VALUE;
2119 handle = FindFirstFileExW(pathW.Buffer, fInfoLevelId, &dataW, fSearchOp, lpSearchFilter, dwAdditionalFlags);
2120 RtlFreeUnicodeString(&pathW);
2121 if (handle == INVALID_HANDLE_VALUE) return handle;
2123 dataA = (WIN32_FIND_DATAA *) lpFindFileData;
2124 dataA->dwFileAttributes = dataW.dwFileAttributes;
2125 dataA->ftCreationTime = dataW.ftCreationTime;
2126 dataA->ftLastAccessTime = dataW.ftLastAccessTime;
2127 dataA->ftLastWriteTime = dataW.ftLastWriteTime;
2128 dataA->nFileSizeHigh = dataW.nFileSizeHigh;
2129 dataA->nFileSizeLow = dataW.nFileSizeLow;
2130 WideCharToMultiByte( CP_ACP, 0, dataW.cFileName, -1,
2131 dataA->cFileName, sizeof(dataA->cFileName), NULL, NULL );
2132 WideCharToMultiByte( CP_ACP, 0, dataW.cAlternateFileName, -1,
2133 dataA->cAlternateFileName, sizeof(dataA->cAlternateFileName), NULL, NULL );
2134 return handle;
2137 /*************************************************************************
2138 * FindFirstFileW (KERNEL32.@)
2140 HANDLE WINAPI FindFirstFileW( LPCWSTR lpFileName, WIN32_FIND_DATAW *lpFindData )
2142 return FindFirstFileExW(lpFileName, FindExInfoStandard, lpFindData,
2143 FindExSearchNameMatch, NULL, 0);
2146 /*************************************************************************
2147 * FindNextFileW (KERNEL32.@)
2149 BOOL WINAPI FindNextFileW( HANDLE handle, WIN32_FIND_DATAW *data )
2151 FIND_FIRST_INFO *info;
2152 BOOL ret = FALSE;
2153 DWORD gle = ERROR_NO_MORE_FILES;
2155 if (handle == INVALID_HANDLE_VALUE)
2157 SetLastError( ERROR_INVALID_HANDLE );
2158 return ret;
2160 info = (FIND_FIRST_INFO*) handle;
2161 RtlEnterCriticalSection( &info->cs );
2162 if (info->drive == -1)
2164 ret = SMB_FindNext( info->u.smb_dir, data );
2165 if(!ret)
2167 SMB_CloseDir( info->u.smb_dir );
2168 HeapFree( GetProcessHeap(), 0, info->path );
2170 goto done;
2172 else if (!info->path || !info->u.dos_dir)
2174 goto done;
2176 else if (!DOSFS_FindNextEx( info, data ))
2178 DOSFS_CloseDir( info->u.dos_dir ); info->u.dos_dir = NULL;
2179 HeapFree( GetProcessHeap(), 0, info->path );
2180 info->path = NULL;
2181 HeapFree( GetProcessHeap(), 0, info->long_mask );
2182 info->long_mask = NULL;
2183 goto done;
2185 ret = TRUE;
2186 done:
2187 RtlLeaveCriticalSection( &info->cs );
2188 if( !ret ) SetLastError( gle );
2189 return ret;
2193 /*************************************************************************
2194 * FindNextFileA (KERNEL32.@)
2196 BOOL WINAPI FindNextFileA( HANDLE handle, WIN32_FIND_DATAA *data )
2198 WIN32_FIND_DATAW dataW;
2199 if (!FindNextFileW( handle, &dataW )) return FALSE;
2200 data->dwFileAttributes = dataW.dwFileAttributes;
2201 data->ftCreationTime = dataW.ftCreationTime;
2202 data->ftLastAccessTime = dataW.ftLastAccessTime;
2203 data->ftLastWriteTime = dataW.ftLastWriteTime;
2204 data->nFileSizeHigh = dataW.nFileSizeHigh;
2205 data->nFileSizeLow = dataW.nFileSizeLow;
2206 WideCharToMultiByte( CP_ACP, 0, dataW.cFileName, -1,
2207 data->cFileName, sizeof(data->cFileName), NULL, NULL );
2208 WideCharToMultiByte( CP_ACP, 0, dataW.cAlternateFileName, -1,
2209 data->cAlternateFileName,
2210 sizeof(data->cAlternateFileName), NULL, NULL );
2211 return TRUE;
2214 /*************************************************************************
2215 * FindClose (KERNEL32.@)
2217 BOOL WINAPI FindClose( HANDLE handle )
2219 FIND_FIRST_INFO *info = (FIND_FIRST_INFO*) handle;
2221 if (handle == INVALID_HANDLE_VALUE) goto error;
2223 __TRY
2225 RtlEnterCriticalSection( &info->cs );
2226 if (info)
2228 if (info->u.dos_dir) DOSFS_CloseDir( info->u.dos_dir );
2229 if (info->path) HeapFree( GetProcessHeap(), 0, info->path );
2230 if (info->long_mask) HeapFree( GetProcessHeap(), 0, info->long_mask );
2233 __EXCEPT(page_fault)
2235 WARN("Illegal handle %p\n", handle);
2236 SetLastError( ERROR_INVALID_HANDLE );
2237 return FALSE;
2239 __ENDTRY
2240 if (!info) goto error;
2241 RtlLeaveCriticalSection( &info->cs );
2242 RtlDeleteCriticalSection( &info->cs );
2243 HeapFree(GetProcessHeap(), 0, info);
2244 return TRUE;
2246 error:
2247 SetLastError( ERROR_INVALID_HANDLE );
2248 return FALSE;
2251 /***********************************************************************
2252 * MulDiv (KERNEL32.@)
2253 * RETURNS
2254 * Result of multiplication and division
2255 * -1: Overflow occurred or Divisor was 0
2257 INT WINAPI MulDiv(
2258 INT nMultiplicand,
2259 INT nMultiplier,
2260 INT nDivisor)
2262 #if SIZEOF_LONG_LONG >= 8
2263 long long ret;
2265 if (!nDivisor) return -1;
2267 /* We want to deal with a positive divisor to simplify the logic. */
2268 if (nDivisor < 0)
2270 nMultiplicand = - nMultiplicand;
2271 nDivisor = -nDivisor;
2274 /* If the result is positive, we "add" to round. else, we subtract to round. */
2275 if ( ( (nMultiplicand < 0) && (nMultiplier < 0) ) ||
2276 ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
2277 ret = (((long long)nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
2278 else
2279 ret = (((long long)nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
2281 if ((ret > 2147483647) || (ret < -2147483647)) return -1;
2282 return ret;
2283 #else
2284 if (!nDivisor) return -1;
2286 /* We want to deal with a positive divisor to simplify the logic. */
2287 if (nDivisor < 0)
2289 nMultiplicand = - nMultiplicand;
2290 nDivisor = -nDivisor;
2293 /* If the result is positive, we "add" to round. else, we subtract to round. */
2294 if ( ( (nMultiplicand < 0) && (nMultiplier < 0) ) ||
2295 ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
2296 return ((nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
2298 return ((nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
2300 #endif
2304 /***********************************************************************
2305 * DosDateTimeToFileTime (KERNEL32.@)
2307 BOOL WINAPI DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft)
2309 struct tm newtm;
2310 #ifndef HAVE_TIMEGM
2311 struct tm *gtm;
2312 time_t time1, time2;
2313 #endif
2315 newtm.tm_sec = (fattime & 0x1f) * 2;
2316 newtm.tm_min = (fattime >> 5) & 0x3f;
2317 newtm.tm_hour = (fattime >> 11);
2318 newtm.tm_mday = (fatdate & 0x1f);
2319 newtm.tm_mon = ((fatdate >> 5) & 0x0f) - 1;
2320 newtm.tm_year = (fatdate >> 9) + 80;
2321 #ifdef HAVE_TIMEGM
2322 RtlSecondsSince1970ToTime( timegm(&newtm), (LARGE_INTEGER *)ft );
2323 #else
2324 time1 = mktime(&newtm);
2325 gtm = gmtime(&time1);
2326 time2 = mktime(gtm);
2327 RtlSecondsSince1970ToTime( 2*time1-time2, (LARGE_INTEGER *)ft );
2328 #endif
2329 return TRUE;
2333 /***********************************************************************
2334 * FileTimeToDosDateTime (KERNEL32.@)
2336 BOOL WINAPI FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate,
2337 LPWORD fattime )
2339 LARGE_INTEGER li;
2340 ULONG t;
2341 time_t unixtime;
2342 struct tm* tm;
2344 li.s.LowPart = ft->dwLowDateTime;
2345 li.s.HighPart = ft->dwHighDateTime;
2346 RtlTimeToSecondsSince1970( &li, &t );
2347 unixtime = t;
2348 tm = gmtime( &unixtime );
2349 if (fattime)
2350 *fattime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
2351 if (fatdate)
2352 *fatdate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
2353 + tm->tm_mday;
2354 return TRUE;
2358 /***********************************************************************
2359 * QueryDosDeviceA (KERNEL32.@)
2361 * returns array of strings terminated by \0, terminated by \0
2363 DWORD WINAPI QueryDosDeviceA(LPCSTR devname,LPSTR target,DWORD bufsize)
2365 DWORD ret = 0, retW;
2366 LPWSTR targetW = (LPWSTR)HeapAlloc(GetProcessHeap(),0,
2367 bufsize * sizeof(WCHAR));
2368 UNICODE_STRING devnameW;
2370 if(devname) RtlCreateUnicodeStringFromAsciiz(&devnameW, devname);
2371 else devnameW.Buffer = NULL;
2373 retW = QueryDosDeviceW(devnameW.Buffer, targetW, bufsize);
2375 ret = WideCharToMultiByte(CP_ACP, 0, targetW, retW, target,
2376 bufsize, NULL, NULL);
2378 RtlFreeUnicodeString(&devnameW);
2379 if (targetW) HeapFree(GetProcessHeap(),0,targetW);
2380 return ret;
2384 /***********************************************************************
2385 * QueryDosDeviceW (KERNEL32.@)
2387 * returns array of strings terminated by \0, terminated by \0
2389 * FIXME
2390 * - Win9x returns for all calls ERROR_INVALID_PARAMETER
2391 * - the returned devices for devname == NULL is far from complete
2392 * - its not checked that the returned device exist
2394 DWORD WINAPI QueryDosDeviceW(LPCWSTR devname,LPWSTR target,DWORD bufsize)
2396 const WCHAR *pDev, *pName, *pNum = NULL;
2397 int numsiz=0;
2398 DWORD ret;
2400 TRACE("(%s,...)\n", debugstr_w(devname));
2401 if (!devname) {
2402 /* return known MSDOS devices */
2403 DWORD ret = 0;
2404 int i;
2405 static const WCHAR devices[][5] = {{'A','U','X',0},
2406 {'C','O','M','1',0},
2407 {'C','O','M','2',0},
2408 {'L','P','T','1',0},
2409 {'N','U','L',0,}};
2410 for(i=0; (i< (sizeof(devices)/sizeof(devices[0]))); i++) {
2411 DWORD len = strlenW(devices[i]);
2412 if(target && (bufsize >= ret + len + 2)) {
2413 strcpyW(target+ret, devices[i]);
2414 ret += len + 1;
2415 } else {
2416 /* in this case WinXP returns 0 */
2417 FIXME("function return is wrong for WinXP!\n");
2418 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2419 break;
2422 /* append drives here */
2423 if(target && bufsize > 0) target[ret++] = 0;
2424 FIXME("Returned list is not complete\n");
2425 return ret;
2427 /* In theory all that are possible and have been defined.
2428 * Now just those below, since mirc uses it to check for special files.
2430 * (It is more complex, and supports netmounted stuff, and \\.\ stuff,
2431 * but currently we just ignore that.)
2433 if (!strcmpiW(devname, auxW)) {
2434 pDev = dosW;
2435 pName = comW;
2436 numsiz = 1;
2437 pNum = oneW;
2438 } else if (!strcmpiW(devname, nulW)) {
2439 pDev = devW;
2440 pName = nullW;
2441 } else if (!strncmpiW(devname, comW, strlenW(comW))) {
2442 pDev = devW;
2443 pName = serW;
2444 pNum = devname + strlenW(comW);
2445 for(numsiz=0; isdigitW(*(pNum+numsiz)); numsiz++);
2446 if(*(pNum + numsiz)) {
2447 SetLastError(ERROR_FILE_NOT_FOUND);
2448 return 0;
2450 } else if (!strncmpiW(devname, lptW, strlenW(lptW))) {
2451 pDev = devW;
2452 pName = parW;
2453 pNum = devname + strlenW(lptW);
2454 for(numsiz=0; isdigitW(*(pNum+numsiz)); numsiz++);
2455 if(*(pNum + numsiz)) {
2456 SetLastError(ERROR_FILE_NOT_FOUND);
2457 return 0;
2459 } else {
2460 /* This might be a DOS device we do not handle yet ... */
2461 FIXME("(%s) not detected as DOS device!\n",debugstr_w(devname));
2463 /* Win9x set the error ERROR_INVALID_PARAMETER */
2464 SetLastError(ERROR_FILE_NOT_FOUND);
2465 return 0;
2467 FIXME("device %s may not exist on this computer\n", debugstr_w(devname));
2469 ret = strlenW(pDev) + strlenW(pName) + numsiz + 2;
2470 if (ret > bufsize) ret = 0;
2471 if (target && ret) {
2472 strcpyW(target,pDev);
2473 strcatW(target,pName);
2474 if (pNum) strcatW(target,pNum);
2475 target[ret-1] = 0;
2477 return ret;
2481 /***********************************************************************
2482 * DefineDosDeviceA (KERNEL32.@)
2484 BOOL WINAPI DefineDosDeviceA(DWORD flags,LPCSTR devname,LPCSTR targetpath) {
2485 FIXME("(0x%08lx,%s,%s),stub!\n",flags,devname,targetpath);
2486 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2487 return FALSE;