- Path changes are saved to the struct correctly.
[wine/multimedia.git] / files / dos_fs.c
blob8a23b322a92e71aae2a17e3cd345de7526990731
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 <string.h>
33 #include <stdlib.h>
34 #include <sys/stat.h>
35 #ifdef HAVE_SYS_IOCTL_H
36 #include <sys/ioctl.h>
37 #endif
38 #include <time.h>
39 #ifdef HAVE_UNISTD_H
40 # include <unistd.h>
41 #endif
43 #define NONAMELESSUNION
44 #define NONAMELESSSTRUCT
45 #include "windef.h"
46 #include "winerror.h"
47 #include "wingdi.h"
49 #include "wine/unicode.h"
50 #include "wine/winbase16.h"
51 #include "drive.h"
52 #include "file.h"
53 #include "heap.h"
54 #include "msdos.h"
55 #include "winternl.h"
56 #include "wine/server.h"
57 #include "excpt.h"
59 #include "smb.h"
61 #include "wine/debug.h"
63 WINE_DEFAULT_DEBUG_CHANNEL(dosfs);
64 WINE_DECLARE_DEBUG_CHANNEL(file);
66 /* Define the VFAT ioctl to get both short and long file names */
67 /* FIXME: is it possible to get this to work on other systems? */
68 #ifdef linux
69 /* We want the real kernel dirent structure, not the libc one */
70 typedef struct
72 long d_ino;
73 long d_off;
74 unsigned short d_reclen;
75 char d_name[256];
76 } KERNEL_DIRENT;
78 #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, KERNEL_DIRENT [2] )
80 /* To avoid blocking on non-directories in DOSFS_OpenDir_VFAT*/
81 #ifndef O_DIRECTORY
82 # define O_DIRECTORY 0200000 /* must be directory */
83 #endif
85 #else /* linux */
86 #undef VFAT_IOCTL_READDIR_BOTH /* just in case... */
87 #endif /* linux */
89 #define IS_OPTION_TRUE(ch) ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1')
91 /* Chars we don't want to see in DOS file names */
92 #define INVALID_DOS_CHARS "*?<>|\"+=,;[] \345"
94 static const DOS_DEVICE DOSFS_Devices[] =
95 /* name, device flags (see Int 21/AX=0x4400) */
97 { {'C','O','N',0}, 0xc0d3 },
98 { {'P','R','N',0}, 0xa0c0 },
99 { {'N','U','L',0}, 0x80c4 },
100 { {'A','U','X',0}, 0x80c0 },
101 { {'L','P','T','1',0}, 0xa0c0 },
102 { {'L','P','T','2',0}, 0xa0c0 },
103 { {'L','P','T','3',0}, 0xa0c0 },
104 { {'L','P','T','4',0}, 0xc0d3 },
105 { {'C','O','M','1',0}, 0x80c0 },
106 { {'C','O','M','2',0}, 0x80c0 },
107 { {'C','O','M','3',0}, 0x80c0 },
108 { {'C','O','M','4',0}, 0x80c0 },
109 { {'S','C','S','I','M','G','R','$',0}, 0xc0c0 },
110 { {'H','P','S','C','A','N',0}, 0xc0c0 },
111 { {'E','M','M','X','X','X','X','0',0}, 0x0000 }
114 static const WCHAR devW[] = {'\\','D','e','v','i','c','e','\\',0};
115 static const WCHAR dosW[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\',0};
117 static const WCHAR auxW[] = {'A','U','X',0};
118 static const WCHAR comW[] = {'C','O','M',0};
119 static const WCHAR lptW[] = {'L','P','T',0};
120 static const WCHAR nulW[] = {'N','U','L',0};
122 static const WCHAR nullW[] = {'N','u','l','l',0};
123 static const WCHAR parW[] = {'P','a','r','a','l','l','e','l',0};
124 static const WCHAR serW[] = {'S','e','r','i','a','l',0};
125 static const WCHAR oneW[] = {'1',0};
128 * Directory info for DOSFS_ReadDir
129 * contains the names of *all* the files in the directory
131 typedef struct
133 int used;
134 int size;
135 WCHAR names[1];
136 } DOS_DIR;
138 /* Info structure for FindFirstFile handle */
139 typedef struct
141 char *path; /* unix path */
142 LPWSTR long_mask;
143 LPWSTR short_mask;
144 BYTE attr;
145 int drive;
146 int cur_pos;
147 union
149 DOS_DIR *dos_dir;
150 SMB_DIR *smb_dir;
151 } u;
152 } FIND_FIRST_INFO;
155 static WINE_EXCEPTION_FILTER(page_fault)
157 if (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION)
158 return EXCEPTION_EXECUTE_HANDLER;
159 return EXCEPTION_CONTINUE_SEARCH;
163 /***********************************************************************
164 * DOSFS_ValidDOSName
166 * Return 1 if Unix file 'name' is also a valid MS-DOS name
167 * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
168 * File name can be terminated by '\0', '\\' or '/'.
170 static int DOSFS_ValidDOSName( LPCWSTR name, int ignore_case )
172 static const char invalid_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS;
173 const WCHAR *p = name;
174 const char *invalid = ignore_case ? (invalid_chars + 26) : invalid_chars;
175 int len = 0;
177 if (*p == '.')
179 /* Check for "." and ".." */
180 p++;
181 if (*p == '.') p++;
182 /* All other names beginning with '.' are invalid */
183 return (IS_END_OF_NAME(*p));
185 while (!IS_END_OF_NAME(*p))
187 if (*p < 256 && strchr( invalid, (char)*p )) return 0; /* Invalid char */
188 if (*p == '.') break; /* Start of the extension */
189 if (++len > 8) return 0; /* Name too long */
190 p++;
192 if (*p != '.') return 1; /* End of name */
193 p++;
194 if (IS_END_OF_NAME(*p)) return 0; /* Empty extension not allowed */
195 len = 0;
196 while (!IS_END_OF_NAME(*p))
198 if (*p < 256 && strchr( invalid, (char)*p )) return 0; /* Invalid char */
199 if (*p == '.') return 0; /* Second extension not allowed */
200 if (++len > 3) return 0; /* Extension too long */
201 p++;
203 return 1;
207 /***********************************************************************
208 * DOSFS_ToDosFCBFormat
210 * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
211 * expanding wild cards and converting to upper-case in the process.
212 * File name can be terminated by '\0', '\\' or '/'.
213 * Return FALSE if the name is not a valid DOS name.
214 * 'buffer' must be at least 12 characters long.
216 BOOL DOSFS_ToDosFCBFormat( LPCWSTR name, LPWSTR buffer )
218 static const char invalid_chars[] = INVALID_DOS_CHARS;
219 LPCWSTR p = name;
220 int i;
222 /* Check for "." and ".." */
223 if (*p == '.')
225 p++;
226 buffer[0] = '.';
227 for(i = 1; i < 11; i++) buffer[i] = ' ';
228 buffer[11] = 0;
229 if (*p == '.')
231 buffer[1] = '.';
232 p++;
234 return (!*p || (*p == '/') || (*p == '\\'));
237 for (i = 0; i < 8; i++)
239 switch(*p)
241 case '\0':
242 case '\\':
243 case '/':
244 case '.':
245 buffer[i] = ' ';
246 break;
247 case '?':
248 p++;
249 /* fall through */
250 case '*':
251 buffer[i] = '?';
252 break;
253 default:
254 if (*p < 256 && strchr( invalid_chars, (char)*p )) return FALSE;
255 buffer[i] = toupperW(*p);
256 p++;
257 break;
261 if (*p == '*')
263 /* Skip all chars after wildcard up to first dot */
264 while (*p && (*p != '/') && (*p != '\\') && (*p != '.')) p++;
266 else
268 /* Check if name too long */
269 if (*p && (*p != '/') && (*p != '\\') && (*p != '.')) return FALSE;
271 if (*p == '.') p++; /* Skip dot */
273 for (i = 8; i < 11; i++)
275 switch(*p)
277 case '\0':
278 case '\\':
279 case '/':
280 buffer[i] = ' ';
281 break;
282 case '.':
283 return FALSE; /* Second extension not allowed */
284 case '?':
285 p++;
286 /* fall through */
287 case '*':
288 buffer[i] = '?';
289 break;
290 default:
291 if (*p < 256 && strchr( invalid_chars, (char)*p )) return FALSE;
292 buffer[i] = toupperW(*p);
293 p++;
294 break;
297 buffer[11] = '\0';
299 /* at most 3 character of the extension are processed
300 * is something behind this ?
302 while (*p == '*' || *p == ' ') p++; /* skip wildcards and spaces */
303 return IS_END_OF_NAME(*p);
307 /***********************************************************************
308 * DOSFS_ToDosDTAFormat
310 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
311 * converting to upper-case in the process.
312 * File name can be terminated by '\0', '\\' or '/'.
313 * 'buffer' must be at least 13 characters long.
315 static void DOSFS_ToDosDTAFormat( LPCWSTR name, LPWSTR buffer )
317 LPWSTR p;
319 memcpy( buffer, name, 8 * sizeof(WCHAR) );
320 p = buffer + 8;
321 while ((p > buffer) && (p[-1] == ' ')) p--;
322 *p++ = '.';
323 memcpy( p, name + 8, 3 * sizeof(WCHAR) );
324 p += 3;
325 while (p[-1] == ' ') p--;
326 if (p[-1] == '.') p--;
327 *p = '\0';
331 /***********************************************************************
332 * DOSFS_MatchShort
334 * Check a DOS file name against a mask (both in FCB format).
336 static int DOSFS_MatchShort( LPCWSTR mask, LPCWSTR name )
338 int i;
339 for (i = 11; i > 0; i--, mask++, name++)
340 if ((*mask != '?') && (*mask != *name)) return 0;
341 return 1;
345 /***********************************************************************
346 * DOSFS_MatchLong
348 * Check a long file name against a mask.
350 * Tests (done in W95 DOS shell - case insensitive):
351 * *.txt test1.test.txt *
352 * *st1* test1.txt *
353 * *.t??????.t* test1.ta.tornado.txt *
354 * *tornado* test1.ta.tornado.txt *
355 * t*t test1.ta.tornado.txt *
356 * ?est* test1.txt *
357 * ?est??? test1.txt -
358 * *test1.txt* test1.txt *
359 * h?l?o*t.dat hellothisisatest.dat *
361 static int DOSFS_MatchLong( LPCWSTR mask, LPCWSTR name, int case_sensitive )
363 LPCWSTR lastjoker = NULL;
364 LPCWSTR next_to_retry = NULL;
365 static const WCHAR asterisk_dot_asterisk[] = {'*','.','*',0};
367 TRACE("(%s, %s, %x)\n", debugstr_w(mask), debugstr_w(name), case_sensitive);
369 if (!strcmpW( mask, asterisk_dot_asterisk )) return 1;
370 while (*name && *mask)
372 if (*mask == '*')
374 mask++;
375 while (*mask == '*') mask++; /* Skip consecutive '*' */
376 lastjoker = mask;
377 if (!*mask) return 1; /* end of mask is all '*', so match */
379 /* skip to the next match after the joker(s) */
380 if (case_sensitive) while (*name && (*name != *mask)) name++;
381 else while (*name && (toupperW(*name) != toupperW(*mask))) name++;
383 if (!*name) break;
384 next_to_retry = name;
386 else if (*mask != '?')
388 int mismatch = 0;
389 if (case_sensitive)
391 if (*mask != *name) mismatch = 1;
393 else
395 if (toupperW(*mask) != toupperW(*name)) mismatch = 1;
397 if (!mismatch)
399 mask++;
400 name++;
401 if (*mask == '\0')
403 if (*name == '\0')
404 return 1;
405 if (lastjoker)
406 mask = lastjoker;
409 else /* mismatch ! */
411 if (lastjoker) /* we had an '*', so we can try unlimitedly */
413 mask = lastjoker;
415 /* this scan sequence was a mismatch, so restart
416 * 1 char after the first char we checked last time */
417 next_to_retry++;
418 name = next_to_retry;
420 else
421 return 0; /* bad luck */
424 else /* '?' */
426 mask++;
427 name++;
430 while ((*mask == '.') || (*mask == '*'))
431 mask++; /* Ignore trailing '.' or '*' in mask */
432 return (!*name && !*mask);
436 /***********************************************************************
437 * DOSFS_AddDirEntry
439 * Used to construct an array of filenames in DOSFS_OpenDir
441 static BOOL DOSFS_AddDirEntry(DOS_DIR **dir, LPCWSTR name, LPCWSTR dosname)
443 int extra1 = strlenW(name) + 1;
444 int extra2 = strlenW(dosname) + 1;
446 /* if we need more, at minimum double the size */
447 if( (extra1 + extra2 + (*dir)->used) > (*dir)->size)
449 int more = (*dir)->size;
450 DOS_DIR *t;
452 if(more<(extra1+extra2))
453 more = extra1+extra2;
455 t = HeapReAlloc(GetProcessHeap(), 0, *dir, sizeof(**dir) +
456 ((*dir)->size + more)*sizeof(WCHAR) );
457 if(!t)
459 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
460 ERR("Out of memory caching directory structure %d %d %d\n",
461 (*dir)->size, more, (*dir)->used);
462 return FALSE;
464 (*dir) = t;
465 (*dir)->size += more;
468 /* at this point, the dir structure is big enough to hold these names */
469 strcpyW(&(*dir)->names[(*dir)->used], name);
470 (*dir)->used += extra1;
471 strcpyW(&(*dir)->names[(*dir)->used], dosname);
472 (*dir)->used += extra2;
474 return TRUE;
478 /***********************************************************************
479 * DOSFS_OpenDir_VFAT
481 static BOOL DOSFS_OpenDir_VFAT(UINT codepage, DOS_DIR **dir, const char *unix_path)
483 #ifdef VFAT_IOCTL_READDIR_BOTH
484 KERNEL_DIRENT de[2];
485 int fd = open( unix_path, O_RDONLY|O_DIRECTORY );
486 BOOL r = TRUE;
488 /* Check if the VFAT ioctl is supported on this directory */
490 if ( fd<0 )
491 return FALSE;
493 while (1)
495 WCHAR long_name[MAX_PATH];
496 WCHAR short_name[12];
498 r = (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de ) != -1);
499 if(!r)
500 break;
501 if (!de[0].d_reclen)
502 break;
503 MultiByteToWideChar(codepage, 0, de[0].d_name, -1, long_name, MAX_PATH);
504 if (!DOSFS_ToDosFCBFormat( long_name, short_name ))
505 short_name[0] = '\0';
506 if (de[1].d_name[0])
507 MultiByteToWideChar(codepage, 0, de[1].d_name, -1, long_name, MAX_PATH);
508 else
509 MultiByteToWideChar(codepage, 0, de[0].d_name, -1, long_name, MAX_PATH);
510 r = DOSFS_AddDirEntry(dir, long_name, short_name );
511 if(!r)
512 break;
514 if(r)
516 static const WCHAR empty_strW[] = { 0 };
517 DOSFS_AddDirEntry(dir, empty_strW, empty_strW);
519 close(fd);
520 return r;
521 #else
522 return FALSE;
523 #endif /* VFAT_IOCTL_READDIR_BOTH */
527 /***********************************************************************
528 * DOSFS_OpenDir_Normal
530 * Now use the standard opendir/readdir interface
532 static BOOL DOSFS_OpenDir_Normal( UINT codepage, DOS_DIR **dir, const char *unix_path )
534 DIR *unixdir = opendir( unix_path );
535 BOOL r = TRUE;
536 static const WCHAR empty_strW[] = { 0 };
538 if(!unixdir)
539 return FALSE;
540 while(1)
542 WCHAR long_name[MAX_PATH];
543 struct dirent *de = readdir(unixdir);
545 if(!de)
546 break;
547 MultiByteToWideChar(codepage, 0, de->d_name, -1, long_name, MAX_PATH);
548 r = DOSFS_AddDirEntry(dir, long_name, empty_strW);
549 if(!r)
550 break;
552 if(r)
553 DOSFS_AddDirEntry(dir, empty_strW, empty_strW);
554 closedir(unixdir);
555 return r;
558 /***********************************************************************
559 * DOSFS_OpenDir
561 static DOS_DIR *DOSFS_OpenDir( UINT codepage, const char *unix_path )
563 const int init_size = 0x100;
564 DOS_DIR *dir = HeapAlloc( GetProcessHeap(), 0, sizeof(*dir) + init_size*sizeof (WCHAR));
565 BOOL r;
567 TRACE("%s\n",debugstr_a(unix_path));
569 if (!dir)
571 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
572 return NULL;
574 dir->used = 0;
575 dir->size = init_size;
577 /* Treat empty path as root directory. This simplifies path split into
578 directory and mask in several other places */
579 if (!*unix_path) unix_path = "/";
581 r = DOSFS_OpenDir_VFAT( codepage, &dir, unix_path);
583 if(!r)
584 r = DOSFS_OpenDir_Normal( codepage, &dir, unix_path);
586 if(!r)
588 HeapFree(GetProcessHeap(), 0, dir);
589 return NULL;
591 dir->used = 0;
593 return dir;
597 /***********************************************************************
598 * DOSFS_CloseDir
600 static void DOSFS_CloseDir( DOS_DIR *dir )
602 HeapFree( GetProcessHeap(), 0, dir );
606 /***********************************************************************
607 * DOSFS_ReadDir
609 static BOOL DOSFS_ReadDir( DOS_DIR *dir, LPCWSTR *long_name,
610 LPCWSTR *short_name )
612 LPCWSTR sn, ln;
614 if (!dir)
615 return FALSE;
617 /* the long pathname is first */
618 ln = &dir->names[dir->used];
619 if(ln[0])
620 *long_name = ln;
621 else
622 return FALSE;
623 dir->used += (strlenW(ln) + 1);
625 /* followed by the short path name */
626 sn = &dir->names[dir->used];
627 if(sn[0])
628 *short_name = sn;
629 else
630 *short_name = NULL;
631 dir->used += (strlenW(sn) + 1);
633 return TRUE;
637 /***********************************************************************
638 * DOSFS_Hash
640 * Transform a Unix file name into a hashed DOS name. If the name is a valid
641 * DOS name, it is converted to upper-case; otherwise it is replaced by a
642 * hashed version that fits in 8.3 format.
643 * File name can be terminated by '\0', '\\' or '/'.
644 * 'buffer' must be at least 13 characters long.
646 static void DOSFS_Hash( LPCWSTR name, LPWSTR buffer, BOOL dir_format,
647 BOOL ignore_case )
649 static const char invalid_chars[] = INVALID_DOS_CHARS "~.";
650 static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
652 LPCWSTR p, ext;
653 LPWSTR dst;
654 unsigned short hash;
655 int i;
657 if (dir_format)
659 for(i = 0; i < 11; i++) buffer[i] = ' ';
660 buffer[11] = 0;
663 if (DOSFS_ValidDOSName( name, ignore_case ))
665 /* Check for '.' and '..' */
666 if (*name == '.')
668 buffer[0] = '.';
669 if (!dir_format) buffer[1] = buffer[2] = '\0';
670 if (name[1] == '.') buffer[1] = '.';
671 return;
674 /* Simply copy the name, converting to uppercase */
676 for (dst = buffer; !IS_END_OF_NAME(*name) && (*name != '.'); name++)
677 *dst++ = toupperW(*name);
678 if (*name == '.')
680 if (dir_format) dst = buffer + 8;
681 else *dst++ = '.';
682 for (name++; !IS_END_OF_NAME(*name); name++)
683 *dst++ = toupperW(*name);
685 if (!dir_format) *dst = '\0';
686 return;
689 /* Compute the hash code of the file name */
690 /* If you know something about hash functions, feel free to */
691 /* insert a better algorithm here... */
692 if (ignore_case)
694 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
695 hash = (hash<<3) ^ (hash>>5) ^ tolowerW(*p) ^ (tolowerW(p[1]) << 8);
696 hash = (hash<<3) ^ (hash>>5) ^ tolowerW(*p); /* Last character */
698 else
700 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
701 hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
702 hash = (hash << 3) ^ (hash >> 5) ^ *p; /* Last character */
705 /* Find last dot for start of the extension */
706 for (p = name+1, ext = NULL; !IS_END_OF_NAME(*p); p++)
707 if (*p == '.') ext = p;
708 if (ext && IS_END_OF_NAME(ext[1]))
709 ext = NULL; /* Empty extension ignored */
711 /* Copy first 4 chars, replacing invalid chars with '_' */
712 for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
714 if (IS_END_OF_NAME(*p) || (p == ext)) break;
715 *dst++ = (*p < 256 && strchr( invalid_chars, (char)*p )) ? '_' : toupperW(*p);
717 /* Pad to 5 chars with '~' */
718 while (i-- >= 0) *dst++ = '~';
720 /* Insert hash code converted to 3 ASCII chars */
721 *dst++ = hash_chars[(hash >> 10) & 0x1f];
722 *dst++ = hash_chars[(hash >> 5) & 0x1f];
723 *dst++ = hash_chars[hash & 0x1f];
725 /* Copy the first 3 chars of the extension (if any) */
726 if (ext)
728 if (!dir_format) *dst++ = '.';
729 for (i = 3, ext++; (i > 0) && !IS_END_OF_NAME(*ext); i--, ext++)
730 *dst++ = (*ext < 256 && strchr( invalid_chars, (char)*ext )) ? '_' : toupperW(*ext);
732 if (!dir_format) *dst = '\0';
736 /***********************************************************************
737 * DOSFS_FindUnixName
739 * Find the Unix file name in a given directory that corresponds to
740 * a file name (either in Unix or DOS format).
741 * File name can be terminated by '\0', '\\' or '/'.
742 * Return TRUE if OK, FALSE if no file name matches.
744 * 'long_buf' must be at least 'long_len' characters long. If the long name
745 * turns out to be larger than that, the function returns FALSE.
746 * 'short_buf' must be at least 13 characters long.
748 BOOL DOSFS_FindUnixName( const DOS_FULL_NAME *path, LPCWSTR name, char *long_buf,
749 INT long_len, LPWSTR short_buf, BOOL ignore_case)
751 DOS_DIR *dir;
752 LPCWSTR long_name, short_name;
753 WCHAR dos_name[12], tmp_buf[13];
754 BOOL ret;
756 LPCWSTR p = strchrW( name, '/' );
757 int len = p ? (int)(p - name) : strlenW(name);
758 if ((p = strchrW( name, '\\' ))) len = min( (int)(p - name), len );
759 /* Ignore trailing dots and spaces */
760 while (len > 1 && (name[len-1] == '.' || name[len-1] == ' ')) len--;
761 if (long_len < len + 1) return FALSE;
763 TRACE("%s,%s\n", path->long_name, debugstr_w(name) );
765 if (!DOSFS_ToDosFCBFormat( name, dos_name )) dos_name[0] = '\0';
767 if (!(dir = DOSFS_OpenDir( DRIVE_GetCodepage(path->drive), path->long_name )))
769 WARN("(%s,%s): can't open dir: %s\n",
770 path->long_name, debugstr_w(name), strerror(errno) );
771 return FALSE;
774 while ((ret = DOSFS_ReadDir( dir, &long_name, &short_name )))
776 /* Check against Unix name */
777 if (len == strlenW(long_name))
779 if (!ignore_case)
781 if (!strncmpW( long_name, name, len )) break;
783 else
785 if (!strncmpiW( long_name, name, len )) break;
788 if (dos_name[0])
790 /* Check against hashed DOS name */
791 if (!short_name)
793 DOSFS_Hash( long_name, tmp_buf, TRUE, ignore_case );
794 short_name = tmp_buf;
796 if (!strcmpW( dos_name, short_name )) break;
799 if (ret)
801 if (long_buf) WideCharToMultiByte(DRIVE_GetCodepage(path->drive), 0,
802 long_name, -1, long_buf, long_len, NULL, NULL);
803 if (short_buf)
805 if (short_name)
806 DOSFS_ToDosDTAFormat( short_name, short_buf );
807 else
808 DOSFS_Hash( long_name, short_buf, FALSE, ignore_case );
810 TRACE("(%s,%s) -> %s (%s)\n", path->long_name, debugstr_w(name),
811 debugstr_w(long_name), short_buf ? debugstr_w(short_buf) : "***");
813 else
814 WARN("%s not found in '%s'\n", debugstr_w(name), path->long_name);
815 DOSFS_CloseDir( dir );
816 return ret;
820 /***********************************************************************
821 * DOSFS_GetDevice
823 * Check if a DOS file name represents a DOS device and return the device.
825 const DOS_DEVICE *DOSFS_GetDevice( LPCWSTR name )
827 unsigned int i;
828 const WCHAR *p;
830 if (!name) return NULL; /* if wine_server_handle_to_fd was used */
831 if (name[0] && (name[1] == ':')) name += 2;
832 if ((p = strrchrW( name, '/' ))) name = p + 1;
833 if ((p = strrchrW( name, '\\' ))) name = p + 1;
834 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
836 const WCHAR *dev = DOSFS_Devices[i].name;
837 if (!strncmpiW( dev, name, strlenW(dev) ))
839 p = name + strlenW( dev );
840 if (!*p || (*p == '.') || (*p == ':')) return &DOSFS_Devices[i];
843 return NULL;
847 /***********************************************************************
848 * DOSFS_GetDeviceByHandle
850 const DOS_DEVICE *DOSFS_GetDeviceByHandle( HANDLE hFile )
852 const DOS_DEVICE *ret = NULL;
853 SERVER_START_REQ( get_device_id )
855 req->handle = hFile;
856 if (!wine_server_call( req ))
858 if ((reply->id >= 0) &&
859 (reply->id < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0])))
860 ret = &DOSFS_Devices[reply->id];
863 SERVER_END_REQ;
864 return ret;
868 /**************************************************************************
869 * DOSFS_CreateCommPort
871 static HANDLE DOSFS_CreateCommPort(LPCWSTR name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa)
873 HANDLE ret;
874 HKEY hkey;
875 DWORD dummy;
876 OBJECT_ATTRIBUTES attr;
877 UNICODE_STRING nameW;
878 WCHAR *devnameW;
879 char tmp[128];
880 char devname[40];
882 static const WCHAR serialportsW[] = {'M','a','c','h','i','n','e','\\',
883 'S','o','f','t','w','a','r','e','\\',
884 'W','i','n','e','\\','W','i','n','e','\\',
885 'C','o','n','f','i','g','\\',
886 'S','e','r','i','a','l','P','o','r','t','s',0};
888 TRACE_(file)("%s %lx %lx\n", debugstr_w(name), access, attributes);
890 attr.Length = sizeof(attr);
891 attr.RootDirectory = 0;
892 attr.ObjectName = &nameW;
893 attr.Attributes = 0;
894 attr.SecurityDescriptor = NULL;
895 attr.SecurityQualityOfService = NULL;
896 RtlInitUnicodeString( &nameW, serialportsW );
898 if (NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr )) return 0;
900 RtlInitUnicodeString( &nameW, name );
901 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
902 devnameW = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
903 else
904 devnameW = NULL;
906 NtClose( hkey );
908 if (!devnameW) return 0;
909 WideCharToMultiByte(CP_ACP, 0, devnameW, -1, devname, sizeof(devname), NULL, NULL);
911 TRACE("opening %s as %s\n", devname, debugstr_w(name));
913 SERVER_START_REQ( create_serial )
915 req->access = access;
916 req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
917 req->attributes = attributes;
918 req->sharing = FILE_SHARE_READ|FILE_SHARE_WRITE;
919 wine_server_add_data( req, devname, strlen(devname) );
920 SetLastError(0);
921 wine_server_call_err( req );
922 ret = reply->handle;
924 SERVER_END_REQ;
926 if(!ret)
927 ERR("Couldn't open device '%s' ! (check permissions)\n",devname);
928 else
929 TRACE("return %p\n", ret );
930 return ret;
933 /***********************************************************************
934 * DOSFS_OpenDevice
936 * Open a DOS device. This might not map 1:1 into the UNIX device concept.
937 * Returns 0 on failure.
939 HANDLE DOSFS_OpenDevice( LPCWSTR name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa )
941 unsigned int i;
942 const WCHAR *p;
943 HANDLE handle;
945 if (name[0] && (name[1] == ':')) name += 2;
946 if ((p = strrchrW( name, '/' ))) name = p + 1;
947 if ((p = strrchrW( name, '\\' ))) name = p + 1;
948 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
950 const WCHAR *dev = DOSFS_Devices[i].name;
951 if (!strncmpiW( dev, name, strlenW(dev) ))
953 p = name + strlenW( dev );
954 if (!*p || (*p == '.') || (*p == ':')) {
955 static const WCHAR nulW[] = {'N','U','L',0};
956 static const WCHAR conW[] = {'C','O','N',0};
957 static const WCHAR scsimgrW[] = {'S','C','S','I','M','G','R','$',0};
958 static const WCHAR hpscanW[] = {'H','P','S','C','A','N',0};
959 static const WCHAR emmxxxx0W[] = {'E','M','M','X','X','X','X','0',0};
960 /* got it */
961 if (!strcmpiW(DOSFS_Devices[i].name, nulW))
962 return FILE_CreateFile( "/dev/null", access,
963 FILE_SHARE_READ|FILE_SHARE_WRITE, sa,
964 OPEN_EXISTING, 0, 0, TRUE, DRIVE_UNKNOWN );
965 if (!strcmpiW(DOSFS_Devices[i].name, conW)) {
966 HANDLE to_dup;
967 switch (access & (GENERIC_READ|GENERIC_WRITE)) {
968 case GENERIC_READ:
969 to_dup = GetStdHandle( STD_INPUT_HANDLE );
970 break;
971 case GENERIC_WRITE:
972 to_dup = GetStdHandle( STD_OUTPUT_HANDLE );
973 break;
974 default:
975 FIXME("can't open CON read/write\n");
976 return 0;
978 if (!DuplicateHandle( GetCurrentProcess(), to_dup, GetCurrentProcess(),
979 &handle, 0,
980 sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle,
981 DUPLICATE_SAME_ACCESS ))
982 handle = 0;
983 return handle;
985 if (!strcmpiW(DOSFS_Devices[i].name, scsimgrW) ||
986 !strcmpiW(DOSFS_Devices[i].name, hpscanW) ||
987 !strcmpiW(DOSFS_Devices[i].name, emmxxxx0W))
989 return FILE_CreateDevice( i, access, sa );
992 if( (handle=DOSFS_CreateCommPort(DOSFS_Devices[i].name,access,attributes,sa)) )
993 return handle;
994 FIXME("device open %s not supported (yet)\n", debugstr_w(DOSFS_Devices[i].name));
995 return 0;
999 return 0;
1003 /***********************************************************************
1004 * DOSFS_GetPathDrive
1006 * Get the drive specified by a given path name (DOS or Unix format).
1008 static int DOSFS_GetPathDrive( LPCWSTR *name )
1010 int drive;
1011 LPCWSTR p = *name;
1013 if (*p && (p[1] == ':'))
1015 drive = toupperW(*p) - 'A';
1016 *name += 2;
1018 else if (*p == '/') /* Absolute Unix path? */
1020 if ((drive = DRIVE_FindDriveRootW( name )) == -1)
1022 MESSAGE("Warning: %s not accessible from a configured DOS drive\n", debugstr_w(*name) );
1023 /* Assume it really was a DOS name */
1024 drive = DRIVE_GetCurrentDrive();
1027 else drive = DRIVE_GetCurrentDrive();
1029 if (!DRIVE_IsValid(drive))
1031 SetLastError( ERROR_INVALID_DRIVE );
1032 return -1;
1034 return drive;
1038 /***********************************************************************
1039 * DOSFS_GetFullName
1041 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
1042 * Unix name / short DOS name pair.
1043 * Return FALSE if one of the path components does not exist. The last path
1044 * component is only checked if 'check_last' is non-zero.
1045 * The buffers pointed to by 'long_buf' and 'short_buf' must be
1046 * at least MAX_PATHNAME_LEN long.
1048 BOOL DOSFS_GetFullName( LPCWSTR name, BOOL check_last, DOS_FULL_NAME *full )
1050 BOOL found;
1051 UINT flags, codepage;
1052 char *p_l, *root;
1053 LPWSTR p_s;
1054 static const WCHAR driveA_rootW[] = {'A',':','\\',0};
1055 static const WCHAR dos_rootW[] = {'\\',0};
1057 TRACE("%s (last=%d)\n", debugstr_w(name), check_last );
1059 if ((!*name) || (*name=='\n'))
1060 { /* error code for Win98 */
1061 SetLastError(ERROR_BAD_PATHNAME);
1062 return FALSE;
1065 if ((full->drive = DOSFS_GetPathDrive( &name )) == -1) return FALSE;
1066 flags = DRIVE_GetFlags( full->drive );
1067 codepage = DRIVE_GetCodepage(full->drive);
1069 lstrcpynA( full->long_name, DRIVE_GetRoot( full->drive ),
1070 sizeof(full->long_name) );
1071 if (full->long_name[1]) root = full->long_name + strlen(full->long_name);
1072 else root = full->long_name; /* root directory */
1074 strcpyW( full->short_name, driveA_rootW );
1075 full->short_name[0] += full->drive;
1077 if ((*name == '\\') || (*name == '/')) /* Absolute path */
1079 while ((*name == '\\') || (*name == '/')) name++;
1081 else /* Relative path */
1083 lstrcpynA( root + 1, DRIVE_GetUnixCwd( full->drive ),
1084 sizeof(full->long_name) - (root - full->long_name) - 1 );
1085 if (root[1]) *root = '/';
1086 lstrcpynW( full->short_name + 3, DRIVE_GetDosCwd( full->drive ),
1087 sizeof(full->short_name)/sizeof(full->short_name[0]) - 3 );
1090 p_l = full->long_name[1] ? full->long_name + strlen(full->long_name)
1091 : full->long_name;
1092 p_s = full->short_name[3] ? full->short_name + strlenW(full->short_name)
1093 : full->short_name + 2;
1094 found = TRUE;
1096 while (*name && found)
1098 /* Check for '.' and '..' */
1100 if (*name == '.')
1102 if (IS_END_OF_NAME(name[1]))
1104 name++;
1105 while ((*name == '\\') || (*name == '/')) name++;
1106 continue;
1108 else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
1110 name += 2;
1111 while ((*name == '\\') || (*name == '/')) name++;
1112 while ((p_l > root) && (*p_l != '/')) p_l--;
1113 while ((p_s > full->short_name + 2) && (*p_s != '\\')) p_s--;
1114 *p_l = *p_s = '\0'; /* Remove trailing separator */
1115 continue;
1119 /* Make sure buffers are large enough */
1121 if ((p_s >= full->short_name + sizeof(full->short_name)/sizeof(full->short_name[0]) - 14) ||
1122 (p_l >= full->long_name + sizeof(full->long_name) - 1))
1124 SetLastError( ERROR_PATH_NOT_FOUND );
1125 return FALSE;
1128 /* Get the long and short name matching the file name */
1130 if ((found = DOSFS_FindUnixName( full, name, p_l + 1,
1131 sizeof(full->long_name) - (p_l - full->long_name) - 1,
1132 p_s + 1, !(flags & DRIVE_CASE_SENSITIVE) )))
1134 *p_l++ = '/';
1135 p_l += strlen(p_l);
1136 *p_s++ = '\\';
1137 p_s += strlenW(p_s);
1138 while (!IS_END_OF_NAME(*name)) name++;
1140 else if (!check_last)
1142 *p_l++ = '/';
1143 *p_s++ = '\\';
1144 while (!IS_END_OF_NAME(*name) &&
1145 (p_s < full->short_name + sizeof(full->short_name)/sizeof(full->short_name[0]) - 1) &&
1146 (p_l < full->long_name + sizeof(full->long_name) - 1))
1148 WCHAR wch;
1149 *p_s++ = tolowerW(*name);
1150 /* If the drive is case-sensitive we want to create new */
1151 /* files in lower-case otherwise we can't reopen them */
1152 /* under the same short name. */
1153 if (flags & DRIVE_CASE_SENSITIVE) wch = tolowerW(*name);
1154 else wch = *name;
1155 p_l += WideCharToMultiByte(codepage, 0, &wch, 1, p_l, 2, NULL, NULL);
1156 name++;
1158 /* Ignore trailing dots and spaces */
1159 while(p_l[-1] == '.' || p_l[-1] == ' ') {
1160 --p_l;
1161 --p_s;
1163 *p_l = '\0';
1164 *p_s = '\0';
1166 while ((*name == '\\') || (*name == '/')) name++;
1169 if (!found)
1171 if (check_last)
1173 SetLastError( ERROR_FILE_NOT_FOUND );
1174 return FALSE;
1176 if (*name) /* Not last */
1178 SetLastError( ERROR_PATH_NOT_FOUND );
1179 return FALSE;
1182 if (!full->long_name[0]) strcpy( full->long_name, "/" );
1183 if (!full->short_name[2]) strcpyW( full->short_name + 2, dos_rootW );
1184 TRACE("returning %s = %s\n", full->long_name, debugstr_w(full->short_name) );
1185 return TRUE;
1189 /***********************************************************************
1190 * GetShortPathNameW (KERNEL32.@)
1192 * NOTES
1193 * observed:
1194 * longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1195 * longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0
1197 * more observations ( with NT 3.51 (WinDD) ):
1198 * longpath <= 8.3 -> just copy longpath to shortpath
1199 * longpath > 8.3 ->
1200 * a) file does not exist -> return 0, LastError = ERROR_FILE_NOT_FOUND
1201 * b) file does exist -> set the short filename.
1202 * - trailing slashes are reproduced in the short name, even if the
1203 * file is not a directory
1204 * - the absolute/relative path of the short name is reproduced like found
1205 * in the long name
1206 * - longpath and shortpath may have the same address
1207 * Peter Ganten, 1999
1209 DWORD WINAPI GetShortPathNameW( LPCWSTR longpath, LPWSTR shortpath, DWORD shortlen )
1211 DOS_FULL_NAME full_name;
1212 WCHAR tmpshortpath[MAX_PATHNAME_LEN];
1213 const WCHAR *p;
1214 DWORD sp = 0, lp = 0;
1215 int drive;
1216 DWORD tmplen;
1217 UINT flags;
1218 BOOL unixabsolute = *longpath == '/';
1220 TRACE("%s\n", debugstr_w(longpath));
1222 if (!longpath) {
1223 SetLastError(ERROR_INVALID_PARAMETER);
1224 return 0;
1226 if (!longpath[0]) {
1227 SetLastError(ERROR_BAD_PATHNAME);
1228 return 0;
1231 /* check for drive letter */
1232 if (!unixabsolute && longpath[1] == ':' ) {
1233 tmpshortpath[0] = longpath[0];
1234 tmpshortpath[1] = ':';
1235 sp = 2;
1238 if ( ( drive = DOSFS_GetPathDrive ( &longpath )) == -1 ) return 0;
1239 flags = DRIVE_GetFlags ( drive );
1241 if (unixabsolute && drive != DRIVE_GetCurrentDrive()) {
1242 tmpshortpath[0] = drive + 'A';
1243 tmpshortpath[1] = ':';
1244 sp = 2;
1247 while ( longpath[lp] ) {
1249 /* check for path delimiters and reproduce them */
1250 if ( longpath[lp] == '\\' || longpath[lp] == '/' ) {
1251 if (!sp || tmpshortpath[sp-1]!= '\\')
1253 /* strip double "\\" */
1254 tmpshortpath[sp] = '\\';
1255 sp++;
1257 tmpshortpath[sp]=0;/*terminate string*/
1258 lp++;
1259 continue;
1262 tmplen = 0;
1263 for(p = longpath + lp; *p && *p != '/' && *p != '\\'; p++)
1264 tmplen++;
1265 lstrcpynW(tmpshortpath + sp, longpath + lp, tmplen + 1);
1267 /* Check, if the current element is a valid dos name */
1268 if ( DOSFS_ValidDOSName ( longpath + lp, !(flags & DRIVE_CASE_SENSITIVE) ) ) {
1269 sp += tmplen;
1270 lp += tmplen;
1271 continue;
1274 /* Check if the file exists and use the existing file name */
1275 if ( DOSFS_GetFullName ( tmpshortpath, TRUE, &full_name ) ) {
1276 strcpyW(tmpshortpath + sp, strrchrW(full_name.short_name, '\\') + 1);
1277 sp += strlenW(tmpshortpath + sp);
1278 lp += tmplen;
1279 continue;
1282 TRACE("not found!\n" );
1283 SetLastError ( ERROR_FILE_NOT_FOUND );
1284 return 0;
1286 tmpshortpath[sp] = 0;
1288 tmplen = strlenW(tmpshortpath) + 1;
1289 if (tmplen <= shortlen)
1291 strcpyW(shortpath, tmpshortpath);
1292 TRACE("returning %s\n", debugstr_w(shortpath));
1293 tmplen--; /* length without 0 */
1296 return tmplen;
1300 /***********************************************************************
1301 * GetShortPathNameA (KERNEL32.@)
1303 DWORD WINAPI GetShortPathNameA( LPCSTR longpath, LPSTR shortpath, DWORD shortlen )
1305 UNICODE_STRING longpathW;
1306 WCHAR shortpathW[MAX_PATH];
1307 DWORD ret, retW;
1309 if (!longpath)
1311 SetLastError(ERROR_INVALID_PARAMETER);
1312 return 0;
1315 TRACE("%s\n", debugstr_a(longpath));
1317 if (!RtlCreateUnicodeStringFromAsciiz(&longpathW, longpath))
1319 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1320 return 0;
1323 retW = GetShortPathNameW(longpathW.Buffer, shortpathW, MAX_PATH);
1325 if (!retW)
1326 ret = 0;
1327 else if (retW > MAX_PATH)
1329 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1330 ret = 0;
1332 else
1334 ret = WideCharToMultiByte(CP_ACP, 0, shortpathW, -1, NULL, 0, NULL, NULL);
1335 if (ret <= shortlen)
1337 WideCharToMultiByte(CP_ACP, 0, shortpathW, -1, shortpath, shortlen, NULL, NULL);
1338 ret--; /* length without 0 */
1342 RtlFreeUnicodeString(&longpathW);
1343 return ret;
1347 /***********************************************************************
1348 * GetLongPathNameW (KERNEL32.@)
1350 * NOTES
1351 * observed (Win2000):
1352 * shortpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1353 * shortpath="": LastError=ERROR_PATH_NOT_FOUND, ret=0
1355 DWORD WINAPI GetLongPathNameW( LPCWSTR shortpath, LPWSTR longpath, DWORD longlen )
1357 DOS_FULL_NAME full_name;
1358 const char *root;
1359 LPWSTR p;
1360 int drive;
1361 UINT codepage;
1362 DWORD ret, len = 0;
1364 if (!shortpath) {
1365 SetLastError(ERROR_INVALID_PARAMETER);
1366 return 0;
1368 if (!shortpath[0]) {
1369 SetLastError(ERROR_PATH_NOT_FOUND);
1370 return 0;
1373 TRACE("%s,%p,%ld\n", debugstr_w(shortpath), longpath, longlen);
1375 if(shortpath[0]=='\\' && shortpath[1]=='\\')
1377 ERR("UNC pathname %s\n",debugstr_w(shortpath));
1378 lstrcpynW( longpath, full_name.short_name, longlen );
1379 return strlenW(longpath);
1382 if (!DOSFS_GetFullName( shortpath, TRUE, &full_name )) return 0;
1384 root = full_name.long_name;
1385 drive = DRIVE_FindDriveRoot(&root);
1386 codepage = DRIVE_GetCodepage(drive);
1388 ret = MultiByteToWideChar(codepage, 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(codepage, 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(DRIVE_GetCodepage(drive), 0, p_l, -1,
1541 full_name.short_name + 2, MAX_PATHNAME_LEN - 3);
1542 /* append name to treat */
1543 namelen= strlenW(full_name.short_name);
1544 p = (LPWSTR)name;
1545 if (driveletter)
1546 p += 2; /* skip drive name when appending */
1547 if (namelen + 2 + strlenW(p) > MAX_PATHNAME_LEN)
1549 FIXME("internal error: buffer too small\n");
1550 return 0;
1552 full_name.short_name[namelen++] ='\\';
1553 full_name.short_name[namelen] = 0;
1554 strncpyW(full_name.short_name + namelen, p, MAX_PATHNAME_LEN - namelen);
1555 full_name.short_name[MAX_PATHNAME_LEN - 1] = 0; /* ensure 0 termination */
1557 /* reverse all slashes */
1558 for (p=full_name.short_name;
1559 p < full_name.short_name + strlenW(full_name.short_name);
1560 p++)
1562 if ( *p == '/' )
1563 *p = '\\';
1565 /* Use memmove, as areas overlap */
1566 /* Delete .. */
1567 while ((p = strstrW(full_name.short_name, updir_slashW)))
1569 if (p > full_name.short_name+2)
1571 *p = 0;
1572 q = strrchrW(full_name.short_name, '\\');
1573 memmove(q+1, p+4, (strlenW(p+4)+1) * sizeof(WCHAR));
1575 else
1577 memmove(full_name.short_name+3, p+4, (strlenW(p+4)+1) * sizeof(WCHAR));
1580 if ((full_name.short_name[2]=='.')&&(full_name.short_name[3]=='.'))
1582 /* This case istn't treated yet : c:..\test */
1583 memmove(full_name.short_name+2,full_name.short_name+4,
1584 (strlenW(full_name.short_name+4)+1) * sizeof(WCHAR));
1586 /* Delete . */
1587 while ((p = strstrW(full_name.short_name, curdirW)))
1589 *(p+1) = 0;
1590 memmove(p+1, p+3, (strlenW(p+3)+1) * sizeof(WCHAR));
1592 if (!(DRIVE_GetFlags(drive) & DRIVE_CASE_PRESERVING))
1593 for (p = full_name.short_name; *p; p++) *p = toupperW(*p);
1594 namelen = strlenW(full_name.short_name);
1595 if (!strcmpW(full_name.short_name+namelen-3, updirW))
1597 /* one more strange case: "c:\test\test1\.."
1598 return "c:\test" */
1599 *(full_name.short_name+namelen-3)=0;
1600 q = strrchrW(full_name.short_name, '\\');
1601 *q =0;
1603 if (full_name.short_name[namelen-1]=='.')
1604 full_name.short_name[(namelen--)-1] =0;
1605 if (!driveletter)
1606 if (full_name.short_name[namelen-1]=='\\')
1607 full_name.short_name[(namelen--)-1] =0;
1608 TRACE("got %s\n", debugstr_w(full_name.short_name));
1610 /* If the lpBuffer buffer is too small, the return value is the
1611 size of the buffer, in characters, required to hold the path
1612 plus the terminating \0 (tested against win95osr2, bon 001118)
1613 . */
1614 ret = strlenW(full_name.short_name);
1615 if (ret >= len )
1617 /* don't touch anything when the buffer is not large enough */
1618 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1619 return ret+1;
1621 if (result)
1623 strncpyW( result, full_name.short_name, len );
1624 result[len - 1] = 0; /* ensure 0 termination */
1627 TRACE("returning %s\n", debugstr_w(full_name.short_name) );
1628 return ret;
1632 /***********************************************************************
1633 * GetFullPathNameA (KERNEL32.@)
1634 * NOTES
1635 * if the path closed with '\', *lastpart is 0
1637 DWORD WINAPI GetFullPathNameA( LPCSTR name, DWORD len, LPSTR buffer,
1638 LPSTR *lastpart )
1640 UNICODE_STRING nameW;
1641 WCHAR bufferW[MAX_PATH];
1642 DWORD ret, retW;
1644 if (!name)
1646 SetLastError(ERROR_INVALID_PARAMETER);
1647 return 0;
1650 if (!RtlCreateUnicodeStringFromAsciiz(&nameW, name))
1652 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1653 return 0;
1656 retW = GetFullPathNameW( nameW.Buffer, MAX_PATH, bufferW, NULL);
1658 if (!retW)
1659 ret = 0;
1660 else if (retW > MAX_PATH)
1662 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1663 ret = 0;
1665 else
1667 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL);
1668 if (ret <= len)
1670 WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, len, NULL, NULL);
1671 ret--; /* length without 0 */
1673 if (lastpart)
1675 LPSTR p = buffer + strlen(buffer);
1677 if (*p != '\\')
1679 while ((p > buffer + 2) && (*p != '\\')) p--;
1680 *lastpart = p + 1;
1682 else *lastpart = NULL;
1687 RtlFreeUnicodeString(&nameW);
1688 return ret;
1692 /***********************************************************************
1693 * GetFullPathNameW (KERNEL32.@)
1695 DWORD WINAPI GetFullPathNameW( LPCWSTR name, DWORD len, LPWSTR buffer,
1696 LPWSTR *lastpart )
1698 DWORD ret = DOSFS_DoGetFullPathName( name, len, buffer );
1699 if (ret && (ret<=len) && buffer && lastpart)
1701 LPWSTR p = buffer + strlenW(buffer);
1702 if (*p != (WCHAR)'\\')
1704 while ((p > buffer + 2) && (*p != (WCHAR)'\\')) p--;
1705 *lastpart = p + 1;
1707 else *lastpart = NULL;
1709 return ret;
1713 /***********************************************************************
1714 * wine_get_unix_file_name (KERNEL32.@) Not a Windows API
1716 * Return the full Unix file name for a given path.
1717 * FIXME: convert dos file name to unicode
1719 BOOL WINAPI wine_get_unix_file_name( LPCSTR dos, LPSTR buffer, DWORD len )
1721 BOOL ret;
1722 DOS_FULL_NAME path;
1723 WCHAR dosW[MAX_PATHNAME_LEN];
1725 MultiByteToWideChar(CP_ACP, 0, dos, -1, dosW, MAX_PATHNAME_LEN);
1726 ret = DOSFS_GetFullName( dosW, FALSE, &path );
1727 if (ret && len)
1729 strncpy( buffer, path.long_name, len );
1730 buffer[len - 1] = 0; /* ensure 0 termination */
1732 return ret;
1736 /***********************************************************************
1737 * get_show_dir_symlinks_option
1739 static BOOL get_show_dir_symlinks_option(void)
1741 static const WCHAR WineW[] = {'M','a','c','h','i','n','e','\\',
1742 'S','o','f','t','w','a','r','e','\\',
1743 'W','i','n','e','\\','W','i','n','e','\\',
1744 'C','o','n','f','i','g','\\','W','i','n','e',0};
1745 static const WCHAR ShowDirSymlinksW[] = {'S','h','o','w','D','i','r','S','y','m','l','i','n','k','s',0};
1747 char tmp[80];
1748 HKEY hkey;
1749 DWORD dummy;
1750 OBJECT_ATTRIBUTES attr;
1751 UNICODE_STRING nameW;
1752 BOOL ret = FALSE;
1754 attr.Length = sizeof(attr);
1755 attr.RootDirectory = 0;
1756 attr.ObjectName = &nameW;
1757 attr.Attributes = 0;
1758 attr.SecurityDescriptor = NULL;
1759 attr.SecurityQualityOfService = NULL;
1760 RtlInitUnicodeString( &nameW, WineW );
1762 if (!NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr ))
1764 RtlInitUnicodeString( &nameW, ShowDirSymlinksW );
1765 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
1767 WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
1768 ret = IS_OPTION_TRUE( str[0] );
1770 NtClose( hkey );
1772 return ret;
1776 /***********************************************************************
1777 * DOSFS_FindNextEx
1779 static int DOSFS_FindNextEx( FIND_FIRST_INFO *info, WIN32_FIND_DATAW *entry )
1781 DWORD attr = info->attr | FA_UNUSED | FA_ARCHIVE | FA_RDONLY;
1782 UINT flags = DRIVE_GetFlags( info->drive );
1783 char *p, buffer[MAX_PATHNAME_LEN];
1784 const char *drive_path;
1785 int drive_root;
1786 LPCWSTR long_name, short_name;
1787 BY_HANDLE_FILE_INFORMATION fileinfo;
1788 WCHAR dos_name[13];
1789 BOOL is_symlink;
1791 if ((info->attr & ~(FA_UNUSED | FA_ARCHIVE | FA_RDONLY)) == FA_LABEL)
1793 if (info->cur_pos) return 0;
1794 entry->dwFileAttributes = FILE_ATTRIBUTE_LABEL;
1795 RtlSecondsSince1970ToTime( (time_t)0, (LARGE_INTEGER *)&entry->ftCreationTime );
1796 RtlSecondsSince1970ToTime( (time_t)0, (LARGE_INTEGER *)&entry->ftLastAccessTime );
1797 RtlSecondsSince1970ToTime( (time_t)0, (LARGE_INTEGER *)&entry->ftLastWriteTime );
1798 entry->nFileSizeHigh = 0;
1799 entry->nFileSizeLow = 0;
1800 entry->dwReserved0 = 0;
1801 entry->dwReserved1 = 0;
1802 DOSFS_ToDosDTAFormat( DRIVE_GetLabel( info->drive ), entry->cFileName );
1803 strcpyW( entry->cAlternateFileName, entry->cFileName );
1804 info->cur_pos++;
1805 TRACE("returning %s (%s) as label\n",
1806 debugstr_w(entry->cFileName), debugstr_w(entry->cAlternateFileName));
1807 return 1;
1810 drive_path = info->path + strlen(DRIVE_GetRoot( info->drive ));
1811 while ((*drive_path == '/') || (*drive_path == '\\')) drive_path++;
1812 drive_root = !*drive_path;
1814 lstrcpynA( buffer, info->path, sizeof(buffer) - 1 );
1815 strcat( buffer, "/" );
1816 p = buffer + strlen(buffer);
1818 while (DOSFS_ReadDir( info->u.dos_dir, &long_name, &short_name ))
1820 info->cur_pos++;
1822 /* Don't return '.' and '..' in the root of the drive */
1823 if (drive_root && (long_name[0] == '.') &&
1824 (!long_name[1] || ((long_name[1] == '.') && !long_name[2])))
1825 continue;
1827 /* Check the long mask */
1829 if (info->long_mask && *info->long_mask)
1831 if (!DOSFS_MatchLong( info->long_mask, long_name,
1832 flags & DRIVE_CASE_SENSITIVE )) continue;
1835 /* Check the short mask */
1837 if (info->short_mask)
1839 if (!short_name)
1841 DOSFS_Hash( long_name, dos_name, TRUE,
1842 !(flags & DRIVE_CASE_SENSITIVE) );
1843 short_name = dos_name;
1845 if (!DOSFS_MatchShort( info->short_mask, short_name )) continue;
1848 /* Check the file attributes */
1849 WideCharToMultiByte(DRIVE_GetCodepage(info->drive), 0, long_name, -1,
1850 p, sizeof(buffer) - (int)(p - buffer), NULL, NULL);
1851 if (!FILE_Stat( buffer, &fileinfo, &is_symlink ))
1853 WARN("can't stat %s\n", buffer);
1854 continue;
1856 if (is_symlink && (fileinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
1858 static int show_dir_symlinks = -1;
1859 if (show_dir_symlinks == -1)
1860 show_dir_symlinks = get_show_dir_symlinks_option();
1861 if (!show_dir_symlinks) continue;
1864 if (fileinfo.dwFileAttributes & ~attr) continue;
1866 /* We now have a matching entry; fill the result and return */
1868 entry->dwFileAttributes = fileinfo.dwFileAttributes;
1869 entry->ftCreationTime = fileinfo.ftCreationTime;
1870 entry->ftLastAccessTime = fileinfo.ftLastAccessTime;
1871 entry->ftLastWriteTime = fileinfo.ftLastWriteTime;
1872 entry->nFileSizeHigh = fileinfo.nFileSizeHigh;
1873 entry->nFileSizeLow = fileinfo.nFileSizeLow;
1875 if (short_name)
1876 DOSFS_ToDosDTAFormat( short_name, entry->cAlternateFileName );
1877 else
1878 DOSFS_Hash( long_name, entry->cAlternateFileName, FALSE,
1879 !(flags & DRIVE_CASE_SENSITIVE) );
1881 lstrcpynW( entry->cFileName, long_name, sizeof(entry->cFileName)/sizeof(entry->cFileName[0]) );
1882 if (!(flags & DRIVE_CASE_PRESERVING)) strlwrW( entry->cFileName );
1883 TRACE("returning %s (%s) %02lx %ld\n",
1884 debugstr_w(entry->cFileName), debugstr_w(entry->cAlternateFileName),
1885 entry->dwFileAttributes, entry->nFileSizeLow );
1886 return 1;
1888 return 0; /* End of directory */
1891 /***********************************************************************
1892 * DOSFS_FindNext
1894 * Find the next matching file. Return the number of entries read to find
1895 * the matching one, or 0 if no more entries.
1896 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
1897 * file name mask. Either or both can be NULL.
1899 * NOTE: This is supposed to be only called by the int21 emulation
1900 * routines. Thus, we should own the Win16Mutex anyway.
1901 * Nevertheless, we explicitly enter it to ensure the static
1902 * directory cache is protected.
1904 int DOSFS_FindNext( const char *path, const char *short_mask,
1905 const char *long_mask, int drive, BYTE attr,
1906 int skip, WIN32_FIND_DATAA *entry )
1908 static FIND_FIRST_INFO info;
1909 LPCWSTR short_name, long_name;
1910 int count;
1911 UNICODE_STRING short_maskW, long_maskW;
1912 WIN32_FIND_DATAW entryW;
1914 TRACE("(%s, %s, %s, %x, %x, %x, %p)\n", debugstr_a(path),
1915 debugstr_a(short_mask), debugstr_a(long_mask), drive, attr, skip,
1916 entry);
1918 _EnterWin16Lock();
1920 RtlCreateUnicodeStringFromAsciiz(&short_maskW, short_mask);
1921 RtlCreateUnicodeStringFromAsciiz(&long_maskW, long_mask);
1923 /* Check the cached directory */
1924 if (!(info.u.dos_dir && info.path == path && !strcmpW(info.short_mask, short_maskW.Buffer)
1925 && !strcmpW(info.long_mask, long_maskW.Buffer) && info.drive == drive
1926 && info.attr == attr && info.cur_pos <= skip))
1928 /* Not in the cache, open it anew */
1929 if (info.u.dos_dir) DOSFS_CloseDir( info.u.dos_dir );
1931 info.path = (LPSTR)path;
1932 RtlFreeHeap(GetProcessHeap(), 0, info.long_mask);
1933 RtlFreeHeap(GetProcessHeap(), 0, info.short_mask);
1934 info.long_mask = long_maskW.Buffer;
1935 info.short_mask = short_maskW.Buffer;
1936 info.attr = attr;
1937 info.drive = drive;
1938 info.cur_pos = 0;
1939 info.u.dos_dir = DOSFS_OpenDir( DRIVE_GetCodepage(drive), info.path );
1941 else
1943 RtlFreeUnicodeString(&short_maskW);
1944 RtlFreeUnicodeString(&long_maskW);
1947 /* Skip to desired position */
1948 while (info.cur_pos < skip)
1949 if (info.u.dos_dir && DOSFS_ReadDir( info.u.dos_dir, &long_name, &short_name ))
1950 info.cur_pos++;
1951 else
1952 break;
1954 if (info.u.dos_dir && info.cur_pos == skip && DOSFS_FindNextEx( &info, &entryW ))
1956 WideCharToMultiByte(CP_ACP, 0, entryW.cFileName, -1,
1957 entry->cFileName, sizeof(entry->cFileName), NULL, NULL);
1958 WideCharToMultiByte(CP_ACP, 0, entryW.cAlternateFileName, -1,
1959 entry->cAlternateFileName, sizeof(entry->cAlternateFileName), NULL, NULL);
1960 count = info.cur_pos - skip;
1962 entry->dwFileAttributes = entryW.dwFileAttributes;
1963 entry->nFileSizeHigh = entryW.nFileSizeHigh;
1964 entry->nFileSizeLow = entryW.nFileSizeLow;
1965 entry->ftCreationTime = entryW.ftCreationTime;
1966 entry->ftLastAccessTime = entryW.ftLastAccessTime;
1967 entry->ftLastWriteTime = entryW.ftLastWriteTime;
1970 else
1971 count = 0;
1973 if (!count)
1975 if (info.u.dos_dir) DOSFS_CloseDir( info.u.dos_dir );
1976 memset( &info, '\0', sizeof(info) );
1979 _LeaveWin16Lock();
1981 return count;
1984 /*************************************************************************
1985 * FindFirstFileExW (KERNEL32.@)
1987 HANDLE WINAPI FindFirstFileExW(
1988 LPCWSTR lpFileName,
1989 FINDEX_INFO_LEVELS fInfoLevelId,
1990 LPVOID lpFindFileData,
1991 FINDEX_SEARCH_OPS fSearchOp,
1992 LPVOID lpSearchFilter,
1993 DWORD dwAdditionalFlags)
1995 HGLOBAL handle;
1996 FIND_FIRST_INFO *info;
1998 if (!lpFileName)
2000 SetLastError(ERROR_PATH_NOT_FOUND);
2001 return INVALID_HANDLE_VALUE;
2004 if ((fSearchOp != FindExSearchNameMatch) || (dwAdditionalFlags != 0))
2006 FIXME("options not implemented 0x%08x 0x%08lx\n", fSearchOp, dwAdditionalFlags );
2007 return INVALID_HANDLE_VALUE;
2010 switch(fInfoLevelId)
2012 case FindExInfoStandard:
2014 WIN32_FIND_DATAW * data = (WIN32_FIND_DATAW *) lpFindFileData;
2015 char *p;
2016 INT long_mask_len;
2017 UINT codepage;
2019 data->dwReserved0 = data->dwReserved1 = 0x0;
2020 if (lpFileName[0] == '\\' && lpFileName[1] == '\\')
2022 ERR("UNC path name\n");
2023 if (!(handle = GlobalAlloc(GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO)))) break;
2025 info = (FIND_FIRST_INFO *)GlobalLock( handle );
2026 info->u.smb_dir = SMB_FindFirst(lpFileName);
2027 if(!info->u.smb_dir)
2029 GlobalUnlock( handle );
2030 GlobalFree(handle);
2031 break;
2034 info->drive = -1;
2036 GlobalUnlock( handle );
2038 else
2040 DOS_FULL_NAME full_name;
2042 if (lpFileName[0] && lpFileName[1] == ':')
2044 /* don't allow root directories */
2045 if (!lpFileName[2] ||
2046 ((lpFileName[2] == '/' || lpFileName[2] == '\\') && !lpFileName[3]))
2048 SetLastError(ERROR_FILE_NOT_FOUND);
2049 return INVALID_HANDLE_VALUE;
2052 if (!DOSFS_GetFullName( lpFileName, FALSE, &full_name )) break;
2053 if (!(handle = GlobalAlloc(GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO)))) break;
2054 info = (FIND_FIRST_INFO *)GlobalLock( handle );
2055 info->path = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
2056 strcpy( info->path, full_name.long_name );
2058 codepage = DRIVE_GetCodepage(full_name.drive);
2059 p = strrchr( info->path, '/' );
2060 *p++ = '\0';
2061 long_mask_len = MultiByteToWideChar(codepage, 0, p, -1, NULL, 0);
2062 info->long_mask = HeapAlloc( GetProcessHeap(), 0, long_mask_len * sizeof(WCHAR) );
2063 MultiByteToWideChar(codepage, 0, p, -1, info->long_mask, long_mask_len);
2065 info->short_mask = NULL;
2066 info->attr = 0xff;
2067 info->drive = full_name.drive;
2068 info->cur_pos = 0;
2070 info->u.dos_dir = DOSFS_OpenDir( codepage, info->path );
2071 GlobalUnlock( handle );
2073 if (!FindNextFileW( handle, data ))
2075 FindClose( handle );
2076 SetLastError( ERROR_FILE_NOT_FOUND );
2077 break;
2079 return handle;
2081 break;
2082 default:
2083 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
2085 return INVALID_HANDLE_VALUE;
2088 /*************************************************************************
2089 * FindFirstFileA (KERNEL32.@)
2091 HANDLE WINAPI FindFirstFileA(
2092 LPCSTR lpFileName,
2093 WIN32_FIND_DATAA *lpFindData )
2095 return FindFirstFileExA(lpFileName, FindExInfoStandard, lpFindData,
2096 FindExSearchNameMatch, NULL, 0);
2099 /*************************************************************************
2100 * FindFirstFileExA (KERNEL32.@)
2102 HANDLE WINAPI FindFirstFileExA(
2103 LPCSTR lpFileName,
2104 FINDEX_INFO_LEVELS fInfoLevelId,
2105 LPVOID lpFindFileData,
2106 FINDEX_SEARCH_OPS fSearchOp,
2107 LPVOID lpSearchFilter,
2108 DWORD dwAdditionalFlags)
2110 HANDLE handle;
2111 WIN32_FIND_DATAA *dataA;
2112 WIN32_FIND_DATAW dataW;
2113 UNICODE_STRING pathW;
2115 if (!lpFileName)
2117 SetLastError(ERROR_PATH_NOT_FOUND);
2118 return INVALID_HANDLE_VALUE;
2121 if (!RtlCreateUnicodeStringFromAsciiz(&pathW, lpFileName))
2123 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2124 return INVALID_HANDLE_VALUE;
2127 handle = FindFirstFileExW(pathW.Buffer, fInfoLevelId, &dataW, fSearchOp, lpSearchFilter, dwAdditionalFlags);
2128 RtlFreeUnicodeString(&pathW);
2129 if (handle == INVALID_HANDLE_VALUE) return handle;
2131 dataA = (WIN32_FIND_DATAA *) lpFindFileData;
2132 dataA->dwFileAttributes = dataW.dwFileAttributes;
2133 dataA->ftCreationTime = dataW.ftCreationTime;
2134 dataA->ftLastAccessTime = dataW.ftLastAccessTime;
2135 dataA->ftLastWriteTime = dataW.ftLastWriteTime;
2136 dataA->nFileSizeHigh = dataW.nFileSizeHigh;
2137 dataA->nFileSizeLow = dataW.nFileSizeLow;
2138 WideCharToMultiByte( CP_ACP, 0, dataW.cFileName, -1,
2139 dataA->cFileName, sizeof(dataA->cFileName), NULL, NULL );
2140 WideCharToMultiByte( CP_ACP, 0, dataW.cAlternateFileName, -1,
2141 dataA->cAlternateFileName, sizeof(dataA->cAlternateFileName), NULL, NULL );
2142 return handle;
2145 /*************************************************************************
2146 * FindFirstFileW (KERNEL32.@)
2148 HANDLE WINAPI FindFirstFileW( LPCWSTR lpFileName, WIN32_FIND_DATAW *lpFindData )
2150 return FindFirstFileExW(lpFileName, FindExInfoStandard, lpFindData,
2151 FindExSearchNameMatch, NULL, 0);
2154 /*************************************************************************
2155 * FindNextFileW (KERNEL32.@)
2157 BOOL WINAPI FindNextFileW( HANDLE handle, WIN32_FIND_DATAW *data )
2159 FIND_FIRST_INFO *info;
2160 BOOL ret = FALSE;
2161 DWORD gle = ERROR_NO_MORE_FILES;
2163 if ((handle == INVALID_HANDLE_VALUE) ||
2164 !(info = (FIND_FIRST_INFO *)GlobalLock( handle )))
2166 SetLastError( ERROR_INVALID_HANDLE );
2167 return ret;
2169 if (info->drive == -1)
2171 ret = SMB_FindNext( info->u.smb_dir, data );
2172 if(!ret)
2174 SMB_CloseDir( info->u.smb_dir );
2175 HeapFree( GetProcessHeap(), 0, info->path );
2177 goto done;
2179 else if (!info->path || !info->u.dos_dir)
2181 goto done;
2183 else if (!DOSFS_FindNextEx( info, data ))
2185 DOSFS_CloseDir( info->u.dos_dir ); info->u.dos_dir = NULL;
2186 HeapFree( GetProcessHeap(), 0, info->path );
2187 info->path = NULL;
2188 HeapFree( GetProcessHeap(), 0, info->long_mask );
2189 info->long_mask = NULL;
2190 goto done;
2192 ret = TRUE;
2193 done:
2194 GlobalUnlock( handle );
2195 if( !ret ) SetLastError( gle );
2196 return ret;
2200 /*************************************************************************
2201 * FindNextFileA (KERNEL32.@)
2203 BOOL WINAPI FindNextFileA( HANDLE handle, WIN32_FIND_DATAA *data )
2205 WIN32_FIND_DATAW dataW;
2206 if (!FindNextFileW( handle, &dataW )) return FALSE;
2207 data->dwFileAttributes = dataW.dwFileAttributes;
2208 data->ftCreationTime = dataW.ftCreationTime;
2209 data->ftLastAccessTime = dataW.ftLastAccessTime;
2210 data->ftLastWriteTime = dataW.ftLastWriteTime;
2211 data->nFileSizeHigh = dataW.nFileSizeHigh;
2212 data->nFileSizeLow = dataW.nFileSizeLow;
2213 WideCharToMultiByte( CP_ACP, 0, dataW.cFileName, -1,
2214 data->cFileName, sizeof(data->cFileName), NULL, NULL );
2215 WideCharToMultiByte( CP_ACP, 0, dataW.cAlternateFileName, -1,
2216 data->cAlternateFileName,
2217 sizeof(data->cAlternateFileName), NULL, NULL );
2218 return TRUE;
2221 /*************************************************************************
2222 * FindClose (KERNEL32.@)
2224 BOOL WINAPI FindClose( HANDLE handle )
2226 FIND_FIRST_INFO *info;
2228 if (handle == INVALID_HANDLE_VALUE) goto error;
2230 __TRY
2232 if ((info = (FIND_FIRST_INFO *)GlobalLock( handle )))
2234 if (info->u.dos_dir) DOSFS_CloseDir( info->u.dos_dir );
2235 if (info->path) HeapFree( GetProcessHeap(), 0, info->path );
2236 if (info->long_mask) HeapFree( GetProcessHeap(), 0, info->long_mask );
2239 __EXCEPT(page_fault)
2241 WARN("Illegal handle %p\n", handle);
2242 SetLastError( ERROR_INVALID_HANDLE );
2243 return FALSE;
2245 __ENDTRY
2246 if (!info) goto error;
2247 GlobalUnlock( handle );
2248 GlobalFree( handle );
2249 return TRUE;
2251 error:
2252 SetLastError( ERROR_INVALID_HANDLE );
2253 return FALSE;
2256 /***********************************************************************
2257 * MulDiv (KERNEL32.@)
2258 * RETURNS
2259 * Result of multiplication and division
2260 * -1: Overflow occurred or Divisor was 0
2262 INT WINAPI MulDiv(
2263 INT nMultiplicand,
2264 INT nMultiplier,
2265 INT nDivisor)
2267 #if SIZEOF_LONG_LONG >= 8
2268 long long ret;
2270 if (!nDivisor) return -1;
2272 /* We want to deal with a positive divisor to simplify the logic. */
2273 if (nDivisor < 0)
2275 nMultiplicand = - nMultiplicand;
2276 nDivisor = -nDivisor;
2279 /* If the result is positive, we "add" to round. else, we subtract to round. */
2280 if ( ( (nMultiplicand < 0) && (nMultiplier < 0) ) ||
2281 ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
2282 ret = (((long long)nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
2283 else
2284 ret = (((long long)nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
2286 if ((ret > 2147483647) || (ret < -2147483647)) return -1;
2287 return ret;
2288 #else
2289 if (!nDivisor) return -1;
2291 /* We want to deal with a positive divisor to simplify the logic. */
2292 if (nDivisor < 0)
2294 nMultiplicand = - nMultiplicand;
2295 nDivisor = -nDivisor;
2298 /* If the result is positive, we "add" to round. else, we subtract to round. */
2299 if ( ( (nMultiplicand < 0) && (nMultiplier < 0) ) ||
2300 ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
2301 return ((nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
2303 return ((nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
2305 #endif
2309 /***********************************************************************
2310 * DosDateTimeToFileTime (KERNEL32.@)
2312 BOOL WINAPI DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft)
2314 struct tm newtm;
2315 #ifndef HAVE_TIMEGM
2316 struct tm *gtm;
2317 time_t time1, time2;
2318 #endif
2320 newtm.tm_sec = (fattime & 0x1f) * 2;
2321 newtm.tm_min = (fattime >> 5) & 0x3f;
2322 newtm.tm_hour = (fattime >> 11);
2323 newtm.tm_mday = (fatdate & 0x1f);
2324 newtm.tm_mon = ((fatdate >> 5) & 0x0f) - 1;
2325 newtm.tm_year = (fatdate >> 9) + 80;
2326 #ifdef HAVE_TIMEGM
2327 RtlSecondsSince1970ToTime( timegm(&newtm), (LARGE_INTEGER *)ft );
2328 #else
2329 time1 = mktime(&newtm);
2330 gtm = gmtime(&time1);
2331 time2 = mktime(gtm);
2332 RtlSecondsSince1970ToTime( 2*time1-time2, (LARGE_INTEGER *)ft );
2333 #endif
2334 return TRUE;
2338 /***********************************************************************
2339 * FileTimeToDosDateTime (KERNEL32.@)
2341 BOOL WINAPI FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate,
2342 LPWORD fattime )
2344 LARGE_INTEGER li;
2345 ULONG t;
2346 time_t unixtime;
2347 struct tm* tm;
2349 li.s.LowPart = ft->dwLowDateTime;
2350 li.s.HighPart = ft->dwHighDateTime;
2351 RtlTimeToSecondsSince1970( &li, &t );
2352 unixtime = t;
2353 tm = gmtime( &unixtime );
2354 if (fattime)
2355 *fattime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
2356 if (fatdate)
2357 *fatdate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
2358 + tm->tm_mday;
2359 return TRUE;
2363 /***********************************************************************
2364 * QueryDosDeviceA (KERNEL32.@)
2366 * returns array of strings terminated by \0, terminated by \0
2368 DWORD WINAPI QueryDosDeviceA(LPCSTR devname,LPSTR target,DWORD bufsize)
2370 DWORD ret = 0, retW;
2371 LPWSTR targetW = (LPWSTR)HeapAlloc(GetProcessHeap(),0,
2372 bufsize * sizeof(WCHAR));
2373 UNICODE_STRING devnameW;
2375 if(devname) RtlCreateUnicodeStringFromAsciiz(&devnameW, devname);
2376 else devnameW.Buffer = NULL;
2378 retW = QueryDosDeviceW(devnameW.Buffer, targetW, bufsize);
2380 ret = WideCharToMultiByte(CP_ACP, 0, targetW, retW, target,
2381 bufsize, NULL, NULL);
2383 RtlFreeUnicodeString(&devnameW);
2384 if (targetW) HeapFree(GetProcessHeap(),0,targetW);
2385 return ret;
2389 /***********************************************************************
2390 * QueryDosDeviceW (KERNEL32.@)
2392 * returns array of strings terminated by \0, terminated by \0
2394 * FIXME
2395 * - Win9x returns for all calls ERROR_INVALID_PARAMETER
2396 * - the returned devices for devname == NULL is far from complete
2397 * - its not checked that the returned device exist
2399 DWORD WINAPI QueryDosDeviceW(LPCWSTR devname,LPWSTR target,DWORD bufsize)
2401 const WCHAR *pDev, *pName, *pNum = NULL;
2402 int numsiz=0;
2403 DWORD ret;
2405 TRACE("(%s,...)\n", debugstr_w(devname));
2406 if (!devname) {
2407 /* return known MSDOS devices */
2408 DWORD ret = 0;
2409 int i;
2410 static const WCHAR devices[][5] = {{'A','U','X',0},
2411 {'C','O','M','1',0},
2412 {'C','O','M','2',0},
2413 {'L','P','T','1',0},
2414 {'N','U','L',0,}};
2415 for(i=0; (i< (sizeof(devices)/sizeof(devices[0]))); i++) {
2416 DWORD len = strlenW(devices[i]);
2417 if(target && (bufsize >= ret + len + 2)) {
2418 lstrcpyW(target+ret, devices[i]);
2419 ret += len + 1;
2420 } else {
2421 /* in this case WinXP returns 0 */
2422 FIXME("function return is wrong for WinXP!\n");
2423 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2424 break;
2427 /* append drives here */
2428 if(target && bufsize > 0) target[ret++] = 0;
2429 FIXME("Returned list is not complete\n");
2430 return ret;
2432 /* In theory all that are possible and have been defined.
2433 * Now just those below, since mirc uses it to check for special files.
2435 * (It is more complex, and supports netmounted stuff, and \\.\ stuff,
2436 * but currently we just ignore that.)
2438 if (!strcmpiW(devname, auxW)) {
2439 pDev = dosW;
2440 pName = comW;
2441 numsiz = 1;
2442 pNum = oneW;
2443 } else if (!strcmpiW(devname, nulW)) {
2444 pDev = devW;
2445 pName = nullW;
2446 } else if (!strncmpiW(devname, comW, strlenW(comW))) {
2447 pDev = devW;
2448 pName = serW;
2449 pNum = devname + strlenW(comW);
2450 for(numsiz=0; isdigitW(*(pNum+numsiz)); numsiz++);
2451 if(*(pNum + numsiz)) {
2452 SetLastError(ERROR_FILE_NOT_FOUND);
2453 return 0;
2455 } else if (!strncmpiW(devname, lptW, strlenW(lptW))) {
2456 pDev = devW;
2457 pName = parW;
2458 pNum = devname + strlenW(lptW);
2459 for(numsiz=0; isdigitW(*(pNum+numsiz)); numsiz++);
2460 if(*(pNum + numsiz)) {
2461 SetLastError(ERROR_FILE_NOT_FOUND);
2462 return 0;
2464 } else {
2465 /* This might be a DOS device we do not handle yet ... */
2466 FIXME("(%s) not detected as DOS device!\n",debugstr_w(devname));
2468 /* Win9x set the error ERROR_INVALID_PARAMETER */
2469 SetLastError(ERROR_FILE_NOT_FOUND);
2470 return 0;
2472 FIXME("device %s may not exist on this computer\n", debugstr_w(devname));
2474 ret = strlenW(pDev) + strlenW(pName) + numsiz + 2;
2475 if (ret > bufsize) ret = 0;
2476 if (target && ret) {
2477 lstrcpyW(target,pDev);
2478 lstrcatW(target,pName);
2479 if (pNum) lstrcatW(target,pNum);
2480 target[ret-1] = 0;
2482 return ret;
2486 /***********************************************************************
2487 * DefineDosDeviceA (KERNEL32.@)
2489 BOOL WINAPI DefineDosDeviceA(DWORD flags,LPCSTR devname,LPCSTR targetpath) {
2490 FIXME("(0x%08lx,%s,%s),stub!\n",flags,devname,targetpath);
2491 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2492 return FALSE;
2496 --- 16 bit functions ---
2499 /*************************************************************************
2500 * FindFirstFile (KERNEL.413)
2502 HANDLE16 WINAPI FindFirstFile16( LPCSTR path, WIN32_FIND_DATAA *data )
2504 DOS_FULL_NAME full_name;
2505 HGLOBAL16 handle;
2506 FIND_FIRST_INFO *info;
2507 WCHAR pathW[MAX_PATH];
2508 char *p;
2509 INT long_mask_len;
2510 UINT codepage;
2512 data->dwReserved0 = data->dwReserved1 = 0x0;
2513 if (!path) return INVALID_HANDLE_VALUE16;
2514 MultiByteToWideChar(CP_ACP, 0, path, -1, pathW, MAX_PATH);
2515 if (!DOSFS_GetFullName( pathW, FALSE, &full_name ))
2516 return INVALID_HANDLE_VALUE16;
2517 if (!(handle = GlobalAlloc16( GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO) )))
2518 return INVALID_HANDLE_VALUE16;
2519 info = (FIND_FIRST_INFO *)GlobalLock16( handle );
2520 info->path = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
2521 strcpy( info->path, full_name.long_name );
2523 codepage = DRIVE_GetCodepage(full_name.drive);
2524 p = strrchr( info->path, '/' );
2525 *p++ = '\0';
2526 long_mask_len = MultiByteToWideChar(codepage, 0, p, -1, NULL, 0);
2527 info->long_mask = HeapAlloc( GetProcessHeap(), 0, long_mask_len * sizeof(WCHAR) );
2528 MultiByteToWideChar(codepage, 0, p, -1, info->long_mask, long_mask_len);
2530 info->short_mask = NULL;
2531 info->attr = 0xff;
2532 info->drive = full_name.drive;
2533 info->cur_pos = 0;
2535 info->u.dos_dir = DOSFS_OpenDir( codepage, info->path );
2537 GlobalUnlock16( handle );
2538 if (!FindNextFile16( handle, data ))
2540 FindClose16( handle );
2541 SetLastError( ERROR_NO_MORE_FILES );
2542 return INVALID_HANDLE_VALUE16;
2544 return handle;
2547 /*************************************************************************
2548 * FindNextFile (KERNEL.414)
2550 BOOL16 WINAPI FindNextFile16( HANDLE16 handle, WIN32_FIND_DATAA *data )
2552 FIND_FIRST_INFO *info;
2553 WIN32_FIND_DATAW dataW;
2554 BOOL ret = FALSE;
2555 DWORD gle = ERROR_NO_MORE_FILES;
2557 if ((handle == INVALID_HANDLE_VALUE16) ||
2558 !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
2560 SetLastError( ERROR_INVALID_HANDLE );
2561 return ret;
2563 if (!info->path || !info->u.dos_dir)
2565 goto done;
2567 if (!DOSFS_FindNextEx( info, &dataW ))
2569 DOSFS_CloseDir( info->u.dos_dir ); info->u.dos_dir = NULL;
2570 HeapFree( GetProcessHeap(), 0, info->path );
2571 info->path = NULL;
2572 HeapFree( GetProcessHeap(), 0, info->long_mask );
2573 info->long_mask = NULL;
2574 goto done;
2577 ret = TRUE;
2579 data->dwFileAttributes = dataW.dwFileAttributes;
2580 data->ftCreationTime = dataW.ftCreationTime;
2581 data->ftLastAccessTime = dataW.ftLastAccessTime;
2582 data->ftLastWriteTime = dataW.ftLastWriteTime;
2583 data->nFileSizeHigh = dataW.nFileSizeHigh;
2584 data->nFileSizeLow = dataW.nFileSizeLow;
2585 WideCharToMultiByte( CP_ACP, 0, dataW.cFileName, -1,
2586 data->cFileName, sizeof(data->cFileName), NULL, NULL );
2587 WideCharToMultiByte( CP_ACP, 0, dataW.cAlternateFileName, -1,
2588 data->cAlternateFileName,
2589 sizeof(data->cAlternateFileName), NULL, NULL );
2590 done:
2591 if( !ret ) SetLastError( gle );
2592 GlobalUnlock16( handle );
2594 return ret;
2597 /*************************************************************************
2598 * FindClose (KERNEL.415)
2600 BOOL16 WINAPI FindClose16( HANDLE16 handle )
2602 FIND_FIRST_INFO *info;
2604 if ((handle == INVALID_HANDLE_VALUE16) ||
2605 !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
2607 SetLastError( ERROR_INVALID_HANDLE );
2608 return FALSE;
2610 if (info->u.dos_dir) DOSFS_CloseDir( info->u.dos_dir );
2611 if (info->path) HeapFree( GetProcessHeap(), 0, info->path );
2612 if (info->long_mask) HeapFree( GetProcessHeap(), 0, info->long_mask );
2613 GlobalUnlock16( handle );
2614 GlobalFree16( handle );
2615 return TRUE;