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
24 #include <sys/types.h>
28 #ifdef HAVE_SYS_ERRNO_H
29 #include <sys/errno.h>
36 #ifdef HAVE_SYS_IOCTL_H
37 #include <sys/ioctl.h>
44 #define NONAMELESSUNION
45 #define NONAMELESSSTRUCT
52 #include "wine/unicode.h"
53 #include "wine/winbase16.h"
59 #include "wine/server.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? */
72 /* We want the real kernel dirent structure, not the libc one */
77 unsigned short d_reclen;
81 #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, KERNEL_DIRENT [2] )
83 /* To avoid blocking on non-directories in DOSFS_OpenDir_VFAT*/
85 # define O_DIRECTORY 0200000 /* must be directory */
89 #undef VFAT_IOCTL_READDIR_BOTH /* just in case... */
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
141 /* Info structure for FindFirstFile handle */
144 char *path; /* unix path */
158 static WINE_EXCEPTION_FILTER(page_fault)
160 if (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION)
161 return EXCEPTION_EXECUTE_HANDLER;
162 return EXCEPTION_CONTINUE_SEARCH;
166 /***********************************************************************
169 * Return 1 if Unix file 'name' is also a valid MS-DOS name
170 * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
171 * File name can be terminated by '\0', '\\' or '/'.
173 static int DOSFS_ValidDOSName( LPCWSTR name, int ignore_case )
175 static const char invalid_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS;
176 const WCHAR *p = name;
177 const char *invalid = ignore_case ? (invalid_chars + 26) : invalid_chars;
182 /* Check for "." and ".." */
185 /* All other names beginning with '.' are invalid */
186 return (IS_END_OF_NAME(*p));
188 while (!IS_END_OF_NAME(*p))
190 if (*p < 256 && strchr( invalid, (char)*p )) return 0; /* Invalid char */
191 if (*p == '.') break; /* Start of the extension */
192 if (++len > 8) return 0; /* Name too long */
195 if (*p != '.') return 1; /* End of name */
197 if (IS_END_OF_NAME(*p)) return 0; /* Empty extension not allowed */
199 while (!IS_END_OF_NAME(*p))
201 if (*p < 256 && strchr( invalid, (char)*p )) return 0; /* Invalid char */
202 if (*p == '.') return 0; /* Second extension not allowed */
203 if (++len > 3) return 0; /* Extension too long */
210 /***********************************************************************
211 * DOSFS_ToDosFCBFormat
213 * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
214 * expanding wild cards and converting to upper-case in the process.
215 * File name can be terminated by '\0', '\\' or '/'.
216 * Return FALSE if the name is not a valid DOS name.
217 * 'buffer' must be at least 12 characters long.
219 BOOL DOSFS_ToDosFCBFormat( LPCWSTR name, LPWSTR buffer )
221 static const char invalid_chars[] = INVALID_DOS_CHARS;
225 /* Check for "." and ".." */
230 for(i = 1; i < 11; i++) buffer[i] = ' ';
237 return (!*p || (*p == '/') || (*p == '\\'));
240 for (i = 0; i < 8; i++)
257 if (*p < 256 && strchr( invalid_chars, (char)*p )) return FALSE;
258 buffer[i] = toupperW(*p);
266 /* Skip all chars after wildcard up to first dot */
267 while (*p && (*p != '/') && (*p != '\\') && (*p != '.')) p++;
271 /* Check if name too long */
272 if (*p && (*p != '/') && (*p != '\\') && (*p != '.')) return FALSE;
274 if (*p == '.') p++; /* Skip dot */
276 for (i = 8; i < 11; i++)
286 return FALSE; /* Second extension not allowed */
294 if (*p < 256 && strchr( invalid_chars, (char)*p )) return FALSE;
295 buffer[i] = toupperW(*p);
302 /* at most 3 character of the extension are processed
303 * is something behind this ?
305 while (*p == '*' || *p == ' ') p++; /* skip wildcards and spaces */
306 return IS_END_OF_NAME(*p);
310 /***********************************************************************
311 * DOSFS_ToDosDTAFormat
313 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
314 * converting to upper-case in the process.
315 * File name can be terminated by '\0', '\\' or '/'.
316 * 'buffer' must be at least 13 characters long.
318 static void DOSFS_ToDosDTAFormat( LPCWSTR name, LPWSTR buffer )
322 memcpy( buffer, name, 8 * sizeof(WCHAR) );
324 while ((p > buffer) && (p[-1] == ' ')) p--;
326 memcpy( p, name + 8, 3 * sizeof(WCHAR) );
328 while (p[-1] == ' ') p--;
329 if (p[-1] == '.') p--;
334 /***********************************************************************
337 * Check a DOS file name against a mask (both in FCB format).
339 static int DOSFS_MatchShort( LPCWSTR mask, LPCWSTR name )
342 for (i = 11; i > 0; i--, mask++, name++)
343 if ((*mask != '?') && (*mask != *name)) return 0;
348 /***********************************************************************
351 * Check a long file name against a mask.
353 * Tests (done in W95 DOS shell - case insensitive):
354 * *.txt test1.test.txt *
356 * *.t??????.t* test1.ta.tornado.txt *
357 * *tornado* test1.ta.tornado.txt *
358 * t*t test1.ta.tornado.txt *
360 * ?est??? test1.txt -
361 * *test1.txt* test1.txt *
362 * h?l?o*t.dat hellothisisatest.dat *
364 static int DOSFS_MatchLong( LPCWSTR mask, LPCWSTR name, int case_sensitive )
366 LPCWSTR lastjoker = NULL;
367 LPCWSTR next_to_retry = NULL;
368 static const WCHAR asterisk_dot_asterisk[] = {'*','.','*',0};
370 TRACE("(%s, %s, %x)\n", debugstr_w(mask), debugstr_w(name), case_sensitive);
372 if (!strcmpW( mask, asterisk_dot_asterisk )) return 1;
373 while (*name && *mask)
378 while (*mask == '*') mask++; /* Skip consecutive '*' */
380 if (!*mask) return 1; /* end of mask is all '*', so match */
382 /* skip to the next match after the joker(s) */
383 if (case_sensitive) while (*name && (*name != *mask)) name++;
384 else while (*name && (toupperW(*name) != toupperW(*mask))) name++;
387 next_to_retry = name;
389 else if (*mask != '?')
394 if (*mask != *name) mismatch = 1;
398 if (toupperW(*mask) != toupperW(*name)) mismatch = 1;
412 else /* mismatch ! */
414 if (lastjoker) /* we had an '*', so we can try unlimitedly */
418 /* this scan sequence was a mismatch, so restart
419 * 1 char after the first char we checked last time */
421 name = next_to_retry;
424 return 0; /* bad luck */
433 while ((*mask == '.') || (*mask == '*'))
434 mask++; /* Ignore trailing '.' or '*' in mask */
435 return (!*name && !*mask);
439 /***********************************************************************
442 * Used to construct an array of filenames in DOSFS_OpenDir
444 static BOOL DOSFS_AddDirEntry(DOS_DIR **dir, LPCWSTR name, LPCWSTR dosname)
446 int extra1 = strlenW(name) + 1;
447 int extra2 = strlenW(dosname) + 1;
449 /* if we need more, at minimum double the size */
450 if( (extra1 + extra2 + (*dir)->used) > (*dir)->size)
452 int more = (*dir)->size;
455 if(more<(extra1+extra2))
456 more = extra1+extra2;
458 t = HeapReAlloc(GetProcessHeap(), 0, *dir, sizeof(**dir) +
459 ((*dir)->size + more)*sizeof(WCHAR) );
462 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
463 ERR("Out of memory caching directory structure %d %d %d\n",
464 (*dir)->size, more, (*dir)->used);
468 (*dir)->size += more;
471 /* at this point, the dir structure is big enough to hold these names */
472 strcpyW(&(*dir)->names[(*dir)->used], name);
473 (*dir)->used += extra1;
474 strcpyW(&(*dir)->names[(*dir)->used], dosname);
475 (*dir)->used += extra2;
481 /***********************************************************************
484 static BOOL DOSFS_OpenDir_VFAT(UINT codepage, DOS_DIR **dir, const char *unix_path)
486 #ifdef VFAT_IOCTL_READDIR_BOTH
488 int fd = open( unix_path, O_RDONLY|O_DIRECTORY );
491 /* Check if the VFAT ioctl is supported on this directory */
498 WCHAR long_name[MAX_PATH];
499 WCHAR short_name[12];
501 r = (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de ) != -1);
506 MultiByteToWideChar(codepage, 0, de[0].d_name, -1, long_name, MAX_PATH);
507 if (!DOSFS_ToDosFCBFormat( long_name, short_name ))
508 short_name[0] = '\0';
510 MultiByteToWideChar(codepage, 0, de[1].d_name, -1, long_name, MAX_PATH);
512 MultiByteToWideChar(codepage, 0, de[0].d_name, -1, long_name, MAX_PATH);
513 r = DOSFS_AddDirEntry(dir, long_name, short_name );
519 static const WCHAR empty_strW[] = { 0 };
520 DOSFS_AddDirEntry(dir, empty_strW, empty_strW);
526 #endif /* VFAT_IOCTL_READDIR_BOTH */
530 /***********************************************************************
531 * DOSFS_OpenDir_Normal
533 * Now use the standard opendir/readdir interface
535 static BOOL DOSFS_OpenDir_Normal( UINT codepage, DOS_DIR **dir, const char *unix_path )
537 DIR *unixdir = opendir( unix_path );
539 static const WCHAR empty_strW[] = { 0 };
545 WCHAR long_name[MAX_PATH];
546 struct dirent *de = readdir(unixdir);
550 MultiByteToWideChar(codepage, 0, de->d_name, -1, long_name, MAX_PATH);
551 r = DOSFS_AddDirEntry(dir, long_name, empty_strW);
556 DOSFS_AddDirEntry(dir, empty_strW, empty_strW);
561 /***********************************************************************
564 static DOS_DIR *DOSFS_OpenDir( UINT codepage, const char *unix_path )
566 const int init_size = 0x100;
567 DOS_DIR *dir = HeapAlloc( GetProcessHeap(), 0, sizeof(*dir) + init_size*sizeof (WCHAR));
570 TRACE("%s\n",debugstr_a(unix_path));
574 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
578 dir->size = init_size;
580 /* Treat empty path as root directory. This simplifies path split into
581 directory and mask in several other places */
582 if (!*unix_path) unix_path = "/";
584 r = DOSFS_OpenDir_VFAT( codepage, &dir, unix_path);
587 r = DOSFS_OpenDir_Normal( codepage, &dir, unix_path);
591 HeapFree(GetProcessHeap(), 0, dir);
600 /***********************************************************************
603 static void DOSFS_CloseDir( DOS_DIR *dir )
605 HeapFree( GetProcessHeap(), 0, dir );
609 /***********************************************************************
612 static BOOL DOSFS_ReadDir( DOS_DIR *dir, LPCWSTR *long_name,
613 LPCWSTR *short_name )
620 /* the long pathname is first */
621 ln = &dir->names[dir->used];
626 dir->used += (strlenW(ln) + 1);
628 /* followed by the short path name */
629 sn = &dir->names[dir->used];
634 dir->used += (strlenW(sn) + 1);
640 /***********************************************************************
643 * Transform a Unix file name into a hashed DOS name. If the name is a valid
644 * DOS name, it is converted to upper-case; otherwise it is replaced by a
645 * hashed version that fits in 8.3 format.
646 * File name can be terminated by '\0', '\\' or '/'.
647 * 'buffer' must be at least 13 characters long.
649 static void DOSFS_Hash( LPCWSTR name, LPWSTR buffer, BOOL dir_format,
652 static const char invalid_chars[] = INVALID_DOS_CHARS "~.";
653 static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
662 for(i = 0; i < 11; i++) buffer[i] = ' ';
666 if (DOSFS_ValidDOSName( name, ignore_case ))
668 /* Check for '.' and '..' */
672 if (!dir_format) buffer[1] = buffer[2] = '\0';
673 if (name[1] == '.') buffer[1] = '.';
677 /* Simply copy the name, converting to uppercase */
679 for (dst = buffer; !IS_END_OF_NAME(*name) && (*name != '.'); name++)
680 *dst++ = toupperW(*name);
683 if (dir_format) dst = buffer + 8;
685 for (name++; !IS_END_OF_NAME(*name); name++)
686 *dst++ = toupperW(*name);
688 if (!dir_format) *dst = '\0';
692 /* Compute the hash code of the file name */
693 /* If you know something about hash functions, feel free to */
694 /* insert a better algorithm here... */
697 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
698 hash = (hash<<3) ^ (hash>>5) ^ tolowerW(*p) ^ (tolowerW(p[1]) << 8);
699 hash = (hash<<3) ^ (hash>>5) ^ tolowerW(*p); /* Last character */
703 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
704 hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
705 hash = (hash << 3) ^ (hash >> 5) ^ *p; /* Last character */
708 /* Find last dot for start of the extension */
709 for (p = name+1, ext = NULL; !IS_END_OF_NAME(*p); p++)
710 if (*p == '.') ext = p;
711 if (ext && IS_END_OF_NAME(ext[1]))
712 ext = NULL; /* Empty extension ignored */
714 /* Copy first 4 chars, replacing invalid chars with '_' */
715 for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
717 if (IS_END_OF_NAME(*p) || (p == ext)) break;
718 *dst++ = (*p < 256 && strchr( invalid_chars, (char)*p )) ? '_' : toupperW(*p);
720 /* Pad to 5 chars with '~' */
721 while (i-- >= 0) *dst++ = '~';
723 /* Insert hash code converted to 3 ASCII chars */
724 *dst++ = hash_chars[(hash >> 10) & 0x1f];
725 *dst++ = hash_chars[(hash >> 5) & 0x1f];
726 *dst++ = hash_chars[hash & 0x1f];
728 /* Copy the first 3 chars of the extension (if any) */
731 if (!dir_format) *dst++ = '.';
732 for (i = 3, ext++; (i > 0) && !IS_END_OF_NAME(*ext); i--, ext++)
733 *dst++ = (*ext < 256 && strchr( invalid_chars, (char)*ext )) ? '_' : toupperW(*ext);
735 if (!dir_format) *dst = '\0';
739 /***********************************************************************
742 * Find the Unix file name in a given directory that corresponds to
743 * a file name (either in Unix or DOS format).
744 * File name can be terminated by '\0', '\\' or '/'.
745 * Return TRUE if OK, FALSE if no file name matches.
747 * 'long_buf' must be at least 'long_len' characters long. If the long name
748 * turns out to be larger than that, the function returns FALSE.
749 * 'short_buf' must be at least 13 characters long.
751 BOOL DOSFS_FindUnixName( const DOS_FULL_NAME *path, LPCWSTR name, char *long_buf,
752 INT long_len, LPWSTR short_buf, BOOL ignore_case)
755 LPCWSTR long_name, short_name;
756 WCHAR dos_name[12], tmp_buf[13];
759 LPCWSTR p = strchrW( name, '/' );
760 int len = p ? (int)(p - name) : strlenW(name);
761 if ((p = strchrW( name, '\\' ))) len = min( (int)(p - name), len );
762 /* Ignore trailing dots and spaces */
763 while (len > 1 && (name[len-1] == '.' || name[len-1] == ' ')) len--;
764 if (long_len < len + 1) return FALSE;
766 TRACE("%s,%s\n", path->long_name, debugstr_w(name) );
768 if (!DOSFS_ToDosFCBFormat( name, dos_name )) dos_name[0] = '\0';
770 if (!(dir = DOSFS_OpenDir( DRIVE_GetCodepage(path->drive), path->long_name )))
772 WARN("(%s,%s): can't open dir: %s\n",
773 path->long_name, debugstr_w(name), strerror(errno) );
777 while ((ret = DOSFS_ReadDir( dir, &long_name, &short_name )))
779 /* Check against Unix name */
780 if (len == strlenW(long_name))
784 if (!strncmpW( long_name, name, len )) break;
788 if (!strncmpiW( long_name, name, len )) break;
793 /* Check against hashed DOS name */
796 DOSFS_Hash( long_name, tmp_buf, TRUE, ignore_case );
797 short_name = tmp_buf;
799 if (!strcmpW( dos_name, short_name )) break;
804 if (long_buf) WideCharToMultiByte(DRIVE_GetCodepage(path->drive), 0,
805 long_name, -1, long_buf, long_len, NULL, NULL);
809 DOSFS_ToDosDTAFormat( short_name, short_buf );
811 DOSFS_Hash( long_name, short_buf, FALSE, ignore_case );
813 TRACE("(%s,%s) -> %s (%s)\n", path->long_name, debugstr_w(name),
814 debugstr_w(long_name), short_buf ? debugstr_w(short_buf) : "***");
817 WARN("%s not found in '%s'\n", debugstr_w(name), path->long_name);
818 DOSFS_CloseDir( dir );
823 /***********************************************************************
826 * Check if a DOS file name represents a DOS device and return the device.
828 const DOS_DEVICE *DOSFS_GetDevice( LPCWSTR name )
833 if (!name) return NULL; /* if wine_server_handle_to_fd was used */
834 if (name[0] && (name[1] == ':')) name += 2;
835 if ((p = strrchrW( name, '/' ))) name = p + 1;
836 if ((p = strrchrW( name, '\\' ))) name = p + 1;
837 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
839 const WCHAR *dev = DOSFS_Devices[i].name;
840 if (!strncmpiW( dev, name, strlenW(dev) ))
842 p = name + strlenW( dev );
843 if (!*p || (*p == '.') || (*p == ':')) return &DOSFS_Devices[i];
850 /***********************************************************************
851 * DOSFS_GetDeviceByHandle
853 const DOS_DEVICE *DOSFS_GetDeviceByHandle( HANDLE hFile )
855 const DOS_DEVICE *ret = NULL;
856 SERVER_START_REQ( get_device_id )
859 if (!wine_server_call( req ))
861 if ((reply->id >= 0) &&
862 (reply->id < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0])))
863 ret = &DOSFS_Devices[reply->id];
871 /**************************************************************************
872 * DOSFS_CreateCommPort
874 static HANDLE DOSFS_CreateCommPort(LPCWSTR name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa)
879 OBJECT_ATTRIBUTES attr;
880 UNICODE_STRING nameW;
885 static const WCHAR serialportsW[] = {'M','a','c','h','i','n','e','\\',
886 'S','o','f','t','w','a','r','e','\\',
887 'W','i','n','e','\\','W','i','n','e','\\',
888 'C','o','n','f','i','g','\\',
889 'S','e','r','i','a','l','P','o','r','t','s',0};
891 TRACE_(file)("%s %lx %lx\n", debugstr_w(name), access, attributes);
893 attr.Length = sizeof(attr);
894 attr.RootDirectory = 0;
895 attr.ObjectName = &nameW;
897 attr.SecurityDescriptor = NULL;
898 attr.SecurityQualityOfService = NULL;
899 RtlInitUnicodeString( &nameW, serialportsW );
901 if (NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr )) return 0;
903 RtlInitUnicodeString( &nameW, name );
904 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
905 devnameW = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
911 if (!devnameW) return 0;
912 WideCharToMultiByte(CP_ACP, 0, devnameW, -1, devname, sizeof(devname), NULL, NULL);
914 TRACE("opening %s as %s\n", devname, debugstr_w(name));
916 SERVER_START_REQ( create_serial )
918 req->access = access;
919 req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
920 req->attributes = attributes;
921 req->sharing = FILE_SHARE_READ|FILE_SHARE_WRITE;
922 wine_server_add_data( req, devname, strlen(devname) );
924 wine_server_call_err( req );
930 ERR("Couldn't open device '%s' ! (check permissions)\n",devname);
932 TRACE("return %p\n", ret );
936 /***********************************************************************
939 * Open a DOS device. This might not map 1:1 into the UNIX device concept.
940 * Returns 0 on failure.
942 HANDLE DOSFS_OpenDevice( LPCWSTR name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa )
948 if (name[0] && (name[1] == ':')) name += 2;
949 if ((p = strrchrW( name, '/' ))) name = p + 1;
950 if ((p = strrchrW( name, '\\' ))) name = p + 1;
951 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
953 const WCHAR *dev = DOSFS_Devices[i].name;
954 if (!strncmpiW( dev, name, strlenW(dev) ))
956 p = name + strlenW( dev );
957 if (!*p || (*p == '.') || (*p == ':')) {
958 static const WCHAR nulW[] = {'N','U','L',0};
959 static const WCHAR conW[] = {'C','O','N',0};
960 static const WCHAR scsimgrW[] = {'S','C','S','I','M','G','R','$',0};
961 static const WCHAR hpscanW[] = {'H','P','S','C','A','N',0};
962 static const WCHAR emmxxxx0W[] = {'E','M','M','X','X','X','X','0',0};
964 if (!strcmpiW(DOSFS_Devices[i].name, nulW))
965 return FILE_CreateFile( "/dev/null", access,
966 FILE_SHARE_READ|FILE_SHARE_WRITE, sa,
967 OPEN_EXISTING, 0, 0, TRUE, DRIVE_UNKNOWN );
968 if (!strcmpiW(DOSFS_Devices[i].name, conW)) {
970 switch (access & (GENERIC_READ|GENERIC_WRITE)) {
972 to_dup = GetStdHandle( STD_INPUT_HANDLE );
975 to_dup = GetStdHandle( STD_OUTPUT_HANDLE );
978 FIXME("can't open CON read/write\n");
981 if (!DuplicateHandle( GetCurrentProcess(), to_dup, GetCurrentProcess(),
983 sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle,
984 DUPLICATE_SAME_ACCESS ))
988 if (!strcmpiW(DOSFS_Devices[i].name, scsimgrW) ||
989 !strcmpiW(DOSFS_Devices[i].name, hpscanW) ||
990 !strcmpiW(DOSFS_Devices[i].name, emmxxxx0W))
992 return FILE_CreateDevice( i, access, sa );
995 if( (handle=DOSFS_CreateCommPort(DOSFS_Devices[i].name,access,attributes,sa)) )
997 FIXME("device open %s not supported (yet)\n", debugstr_w(DOSFS_Devices[i].name));
1006 /***********************************************************************
1007 * DOSFS_GetPathDrive
1009 * Get the drive specified by a given path name (DOS or Unix format).
1011 static int DOSFS_GetPathDrive( LPCWSTR *name )
1016 if (*p && (p[1] == ':'))
1018 drive = toupperW(*p) - 'A';
1021 else if (*p == '/') /* Absolute Unix path? */
1023 if ((drive = DRIVE_FindDriveRootW( name )) == -1)
1025 MESSAGE("Warning: %s not accessible from a configured DOS drive\n", debugstr_w(*name) );
1026 /* Assume it really was a DOS name */
1027 drive = DRIVE_GetCurrentDrive();
1030 else drive = DRIVE_GetCurrentDrive();
1032 if (!DRIVE_IsValid(drive))
1034 SetLastError( ERROR_INVALID_DRIVE );
1041 /***********************************************************************
1044 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
1045 * Unix name / short DOS name pair.
1046 * Return FALSE if one of the path components does not exist. The last path
1047 * component is only checked if 'check_last' is non-zero.
1048 * The buffers pointed to by 'long_buf' and 'short_buf' must be
1049 * at least MAX_PATHNAME_LEN long.
1051 BOOL DOSFS_GetFullName( LPCWSTR name, BOOL check_last, DOS_FULL_NAME *full )
1054 UINT flags, codepage;
1057 static const WCHAR driveA_rootW[] = {'A',':','\\',0};
1058 static const WCHAR dos_rootW[] = {'\\',0};
1060 TRACE("%s (last=%d)\n", debugstr_w(name), check_last );
1062 if ((!*name) || (*name=='\n'))
1063 { /* error code for Win98 */
1064 SetLastError(ERROR_BAD_PATHNAME);
1068 if ((full->drive = DOSFS_GetPathDrive( &name )) == -1) return FALSE;
1069 flags = DRIVE_GetFlags( full->drive );
1070 codepage = DRIVE_GetCodepage(full->drive);
1072 lstrcpynA( full->long_name, DRIVE_GetRoot( full->drive ),
1073 sizeof(full->long_name) );
1074 if (full->long_name[1]) root = full->long_name + strlen(full->long_name);
1075 else root = full->long_name; /* root directory */
1077 strcpyW( full->short_name, driveA_rootW );
1078 full->short_name[0] += full->drive;
1080 if ((*name == '\\') || (*name == '/')) /* Absolute path */
1082 while ((*name == '\\') || (*name == '/')) name++;
1084 else /* Relative path */
1086 lstrcpynA( root + 1, DRIVE_GetUnixCwd( full->drive ),
1087 sizeof(full->long_name) - (root - full->long_name) - 1 );
1088 if (root[1]) *root = '/';
1089 lstrcpynW( full->short_name + 3, DRIVE_GetDosCwd( full->drive ),
1090 sizeof(full->short_name)/sizeof(full->short_name[0]) - 3 );
1093 p_l = full->long_name[1] ? full->long_name + strlen(full->long_name)
1095 p_s = full->short_name[3] ? full->short_name + strlenW(full->short_name)
1096 : full->short_name + 2;
1099 while (*name && found)
1101 /* Check for '.' and '..' */
1105 if (IS_END_OF_NAME(name[1]))
1108 while ((*name == '\\') || (*name == '/')) name++;
1111 else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
1114 while ((*name == '\\') || (*name == '/')) name++;
1115 while ((p_l > root) && (*p_l != '/')) p_l--;
1116 while ((p_s > full->short_name + 2) && (*p_s != '\\')) p_s--;
1117 *p_l = *p_s = '\0'; /* Remove trailing separator */
1122 /* Make sure buffers are large enough */
1124 if ((p_s >= full->short_name + sizeof(full->short_name)/sizeof(full->short_name[0]) - 14) ||
1125 (p_l >= full->long_name + sizeof(full->long_name) - 1))
1127 SetLastError( ERROR_PATH_NOT_FOUND );
1131 /* Get the long and short name matching the file name */
1133 if ((found = DOSFS_FindUnixName( full, name, p_l + 1,
1134 sizeof(full->long_name) - (p_l - full->long_name) - 1,
1135 p_s + 1, !(flags & DRIVE_CASE_SENSITIVE) )))
1140 p_s += strlenW(p_s);
1141 while (!IS_END_OF_NAME(*name)) name++;
1143 else if (!check_last)
1147 while (!IS_END_OF_NAME(*name) &&
1148 (p_s < full->short_name + sizeof(full->short_name)/sizeof(full->short_name[0]) - 1) &&
1149 (p_l < full->long_name + sizeof(full->long_name) - 1))
1152 *p_s++ = tolowerW(*name);
1153 /* If the drive is case-sensitive we want to create new */
1154 /* files in lower-case otherwise we can't reopen them */
1155 /* under the same short name. */
1156 if (flags & DRIVE_CASE_SENSITIVE) wch = tolowerW(*name);
1158 p_l += WideCharToMultiByte(codepage, 0, &wch, 1, p_l, 2, NULL, NULL);
1161 /* Ignore trailing dots and spaces */
1162 while(p_l[-1] == '.' || p_l[-1] == ' ') {
1169 while ((*name == '\\') || (*name == '/')) name++;
1176 SetLastError( ERROR_FILE_NOT_FOUND );
1179 if (*name) /* Not last */
1181 SetLastError( ERROR_PATH_NOT_FOUND );
1185 if (!full->long_name[0]) strcpy( full->long_name, "/" );
1186 if (!full->short_name[2]) strcpyW( full->short_name + 2, dos_rootW );
1187 TRACE("returning %s = %s\n", full->long_name, debugstr_w(full->short_name) );
1192 /***********************************************************************
1193 * GetShortPathNameW (KERNEL32.@)
1197 * longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1198 * longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0
1200 * more observations ( with NT 3.51 (WinDD) ):
1201 * longpath <= 8.3 -> just copy longpath to shortpath
1203 * a) file does not exist -> return 0, LastError = ERROR_FILE_NOT_FOUND
1204 * b) file does exist -> set the short filename.
1205 * - trailing slashes are reproduced in the short name, even if the
1206 * file is not a directory
1207 * - the absolute/relative path of the short name is reproduced like found
1209 * - longpath and shortpath may have the same address
1210 * Peter Ganten, 1999
1212 DWORD WINAPI GetShortPathNameW( LPCWSTR longpath, LPWSTR shortpath, DWORD shortlen )
1214 DOS_FULL_NAME full_name;
1215 WCHAR tmpshortpath[MAX_PATHNAME_LEN];
1217 DWORD sp = 0, lp = 0;
1221 BOOL unixabsolute = *longpath == '/';
1223 TRACE("%s\n", debugstr_w(longpath));
1226 SetLastError(ERROR_INVALID_PARAMETER);
1230 SetLastError(ERROR_BAD_PATHNAME);
1234 /* check for drive letter */
1235 if (!unixabsolute && longpath[1] == ':' ) {
1236 tmpshortpath[0] = longpath[0];
1237 tmpshortpath[1] = ':';
1241 if ( ( drive = DOSFS_GetPathDrive ( &longpath )) == -1 ) return 0;
1242 flags = DRIVE_GetFlags ( drive );
1244 if (unixabsolute && drive != DRIVE_GetCurrentDrive()) {
1245 tmpshortpath[0] = drive + 'A';
1246 tmpshortpath[1] = ':';
1250 while ( longpath[lp] ) {
1252 /* check for path delimiters and reproduce them */
1253 if ( longpath[lp] == '\\' || longpath[lp] == '/' ) {
1254 if (!sp || tmpshortpath[sp-1]!= '\\')
1256 /* strip double "\\" */
1257 tmpshortpath[sp] = '\\';
1260 tmpshortpath[sp]=0;/*terminate string*/
1266 for(p = longpath + lp; *p && *p != '/' && *p != '\\'; p++)
1268 lstrcpynW(tmpshortpath + sp, longpath + lp, tmplen + 1);
1270 /* Check, if the current element is a valid dos name */
1271 if ( DOSFS_ValidDOSName ( longpath + lp, !(flags & DRIVE_CASE_SENSITIVE) ) ) {
1277 /* Check if the file exists and use the existing file name */
1278 if ( DOSFS_GetFullName ( tmpshortpath, TRUE, &full_name ) ) {
1279 strcpyW(tmpshortpath + sp, strrchrW(full_name.short_name, '\\') + 1);
1280 sp += strlenW(tmpshortpath + sp);
1285 TRACE("not found!\n" );
1286 SetLastError ( ERROR_FILE_NOT_FOUND );
1289 tmpshortpath[sp] = 0;
1291 tmplen = strlenW(tmpshortpath) + 1;
1292 if (tmplen <= shortlen)
1294 strcpyW(shortpath, tmpshortpath);
1295 TRACE("returning %s\n", debugstr_w(shortpath));
1296 tmplen--; /* length without 0 */
1303 /***********************************************************************
1304 * GetShortPathNameA (KERNEL32.@)
1306 DWORD WINAPI GetShortPathNameA( LPCSTR longpath, LPSTR shortpath, DWORD shortlen )
1308 UNICODE_STRING longpathW;
1309 WCHAR shortpathW[MAX_PATH];
1314 SetLastError(ERROR_INVALID_PARAMETER);
1318 TRACE("%s\n", debugstr_a(longpath));
1320 if (!RtlCreateUnicodeStringFromAsciiz(&longpathW, longpath))
1322 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1326 retW = GetShortPathNameW(longpathW.Buffer, shortpathW, MAX_PATH);
1330 else if (retW > MAX_PATH)
1332 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1337 ret = WideCharToMultiByte(CP_ACP, 0, shortpathW, -1, NULL, 0, NULL, NULL);
1338 if (ret <= shortlen)
1340 WideCharToMultiByte(CP_ACP, 0, shortpathW, -1, shortpath, shortlen, NULL, NULL);
1341 ret--; /* length without 0 */
1345 RtlFreeUnicodeString(&longpathW);
1350 /***********************************************************************
1351 * GetLongPathNameW (KERNEL32.@)
1354 * observed (Win2000):
1355 * shortpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1356 * shortpath="": LastError=ERROR_PATH_NOT_FOUND, ret=0
1358 DWORD WINAPI GetLongPathNameW( LPCWSTR shortpath, LPWSTR longpath, DWORD longlen )
1360 DOS_FULL_NAME full_name;
1368 SetLastError(ERROR_INVALID_PARAMETER);
1371 if (!shortpath[0]) {
1372 SetLastError(ERROR_PATH_NOT_FOUND);
1376 TRACE("%s,%p,%ld\n", debugstr_w(shortpath), longpath, longlen);
1378 if(shortpath[0]=='\\' && shortpath[1]=='\\')
1380 ERR("UNC pathname %s\n",debugstr_w(shortpath));
1381 lstrcpynW( longpath, full_name.short_name, longlen );
1382 return strlenW(longpath);
1385 if (!DOSFS_GetFullName( shortpath, TRUE, &full_name )) return 0;
1387 root = full_name.long_name;
1388 drive = DRIVE_FindDriveRoot(&root);
1389 codepage = DRIVE_GetCodepage(drive);
1391 ret = MultiByteToWideChar(codepage, 0, root, -1, NULL, 0);
1393 /* reproduce terminating slash */
1394 if (ret > 4) /* if not drive root */
1396 len = strlenW(shortpath);
1397 if (shortpath[len - 1] == '\\' || shortpath[len - 1] == '/')
1403 longpath[0] = 'A' + drive;
1405 MultiByteToWideChar(codepage, 0, root, -1, longpath + 2, longlen - 2);
1406 for (p = longpath; *p; p++) if (*p == '/') *p = '\\';
1409 longpath[ret - 2] = '\\';
1410 longpath[ret - 1] = 0;
1412 TRACE("returning %s\n", debugstr_w(longpath));
1413 ret--; /* length without 0 */
1419 /***********************************************************************
1420 * GetLongPathNameA (KERNEL32.@)
1422 DWORD WINAPI GetLongPathNameA( LPCSTR shortpath, LPSTR longpath, DWORD longlen )
1424 UNICODE_STRING shortpathW;
1425 WCHAR longpathW[MAX_PATH];
1430 SetLastError(ERROR_INVALID_PARAMETER);
1434 TRACE("%s\n", debugstr_a(shortpath));
1436 if (!RtlCreateUnicodeStringFromAsciiz(&shortpathW, shortpath))
1438 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1442 retW = GetLongPathNameW(shortpathW.Buffer, longpathW, MAX_PATH);
1446 else if (retW > MAX_PATH)
1448 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1453 ret = WideCharToMultiByte(CP_ACP, 0, longpathW, -1, NULL, 0, NULL, NULL);
1456 WideCharToMultiByte(CP_ACP, 0, longpathW, -1, longpath, longlen, NULL, NULL);
1457 ret--; /* length without 0 */
1461 RtlFreeUnicodeString(&shortpathW);
1466 /***********************************************************************
1467 * DOSFS_DoGetFullPathName
1469 * Implementation of GetFullPathNameA/W.
1471 * bon@elektron 000331:
1472 * A test for GetFullPathName with many pathological cases
1473 * now gives identical output for Wine and OSR2
1475 static DWORD DOSFS_DoGetFullPathName( LPCWSTR name, DWORD len, LPWSTR result )
1478 DOS_FULL_NAME full_name;
1482 WCHAR drivecur[] = {'C',':','.',0};
1483 WCHAR driveletter=0;
1484 int namelen,drive=0;
1485 static const WCHAR bkslashW[] = {'\\',0};
1486 static const WCHAR dotW[] = {'.',0};
1487 static const WCHAR updir_slashW[] = {'\\','.','.','\\',0};
1488 static const WCHAR curdirW[] = {'\\','.','\\',0};
1489 static const WCHAR updirW[] = {'\\','.','.',0};
1493 SetLastError(ERROR_BAD_PATHNAME);
1497 TRACE("passed %s\n", debugstr_w(name));
1500 /*drive letter given */
1502 driveletter = name[0];
1504 if ((name[1]==':') && ((name[2]=='\\') || (name[2]=='/')))
1505 /*absolute path given */
1507 strncpyW(full_name.short_name, name, MAX_PATHNAME_LEN);
1508 full_name.short_name[MAX_PATHNAME_LEN - 1] = 0; /* ensure 0 termination */
1509 drive = toupperW(name[0]) - 'A';
1514 drivecur[0]=driveletter;
1515 else if ((name[0]=='\\') || (name[0]=='/'))
1516 strcpyW(drivecur, bkslashW);
1518 strcpyW(drivecur, dotW);
1520 if (!DOSFS_GetFullName( drivecur, FALSE, &full_name ))
1522 FIXME("internal: error getting drive/path\n");
1525 /* find path that drive letter substitutes*/
1526 drive = toupperW(full_name.short_name[0]) - 'A';
1527 root= DRIVE_GetRoot(drive);
1530 FIXME("internal: error getting DOS Drive Root\n");
1533 if (!strcmp(root,"/"))
1535 /* we have just the last / and we need it. */
1536 p_l = full_name.long_name;
1540 p_l = full_name.long_name + strlen(root);
1542 /* append long name (= unix name) to drive */
1543 MultiByteToWideChar(DRIVE_GetCodepage(drive), 0, p_l, -1,
1544 full_name.short_name + 2, MAX_PATHNAME_LEN - 3);
1545 /* append name to treat */
1546 namelen= strlenW(full_name.short_name);
1549 p += 2; /* skip drive name when appending */
1550 if (namelen + 2 + strlenW(p) > MAX_PATHNAME_LEN)
1552 FIXME("internal error: buffer too small\n");
1555 full_name.short_name[namelen++] ='\\';
1556 full_name.short_name[namelen] = 0;
1557 strncpyW(full_name.short_name + namelen, p, MAX_PATHNAME_LEN - namelen);
1558 full_name.short_name[MAX_PATHNAME_LEN - 1] = 0; /* ensure 0 termination */
1560 /* reverse all slashes */
1561 for (p=full_name.short_name;
1562 p < full_name.short_name + strlenW(full_name.short_name);
1568 /* Use memmove, as areas overlap */
1570 while ((p = strstrW(full_name.short_name, updir_slashW)))
1572 if (p > full_name.short_name+2)
1575 q = strrchrW(full_name.short_name, '\\');
1576 memmove(q+1, p+4, (strlenW(p+4)+1) * sizeof(WCHAR));
1580 memmove(full_name.short_name+3, p+4, (strlenW(p+4)+1) * sizeof(WCHAR));
1583 if ((full_name.short_name[2]=='.')&&(full_name.short_name[3]=='.'))
1585 /* This case istn't treated yet : c:..\test */
1586 memmove(full_name.short_name+2,full_name.short_name+4,
1587 (strlenW(full_name.short_name+4)+1) * sizeof(WCHAR));
1590 while ((p = strstrW(full_name.short_name, curdirW)))
1593 memmove(p+1, p+3, (strlenW(p+3)+1) * sizeof(WCHAR));
1595 if (!(DRIVE_GetFlags(drive) & DRIVE_CASE_PRESERVING))
1596 for (p = full_name.short_name; *p; p++) *p = toupperW(*p);
1597 namelen = strlenW(full_name.short_name);
1598 if (!strcmpW(full_name.short_name+namelen-3, updirW))
1600 /* one more strange case: "c:\test\test1\.."
1602 *(full_name.short_name+namelen-3)=0;
1603 q = strrchrW(full_name.short_name, '\\');
1606 if (full_name.short_name[namelen-1]=='.')
1607 full_name.short_name[(namelen--)-1] =0;
1609 if (full_name.short_name[namelen-1]=='\\')
1610 full_name.short_name[(namelen--)-1] =0;
1611 TRACE("got %s\n", debugstr_w(full_name.short_name));
1613 /* If the lpBuffer buffer is too small, the return value is the
1614 size of the buffer, in characters, required to hold the path
1615 plus the terminating \0 (tested against win95osr2, bon 001118)
1617 ret = strlenW(full_name.short_name);
1620 /* don't touch anything when the buffer is not large enough */
1621 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1626 strncpyW( result, full_name.short_name, len );
1627 result[len - 1] = 0; /* ensure 0 termination */
1630 TRACE("returning %s\n", debugstr_w(full_name.short_name) );
1635 /***********************************************************************
1636 * GetFullPathNameA (KERNEL32.@)
1638 * if the path closed with '\', *lastpart is 0
1640 DWORD WINAPI GetFullPathNameA( LPCSTR name, DWORD len, LPSTR buffer,
1643 UNICODE_STRING nameW;
1644 WCHAR bufferW[MAX_PATH];
1649 SetLastError(ERROR_INVALID_PARAMETER);
1653 if (!RtlCreateUnicodeStringFromAsciiz(&nameW, name))
1655 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1659 retW = GetFullPathNameW( nameW.Buffer, MAX_PATH, bufferW, NULL);
1663 else if (retW > MAX_PATH)
1665 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1670 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL);
1673 WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, len, NULL, NULL);
1674 ret--; /* length without 0 */
1678 LPSTR p = buffer + strlen(buffer);
1682 while ((p > buffer + 2) && (*p != '\\')) p--;
1685 else *lastpart = NULL;
1690 RtlFreeUnicodeString(&nameW);
1695 /***********************************************************************
1696 * GetFullPathNameW (KERNEL32.@)
1698 DWORD WINAPI GetFullPathNameW( LPCWSTR name, DWORD len, LPWSTR buffer,
1701 DWORD ret = DOSFS_DoGetFullPathName( name, len, buffer );
1702 if (ret && (ret<=len) && buffer && lastpart)
1704 LPWSTR p = buffer + strlenW(buffer);
1705 if (*p != (WCHAR)'\\')
1707 while ((p > buffer + 2) && (*p != (WCHAR)'\\')) p--;
1710 else *lastpart = NULL;
1716 /***********************************************************************
1717 * wine_get_unix_file_name (KERNEL32.@) Not a Windows API
1719 * Return the full Unix file name for a given path.
1720 * FIXME: convert dos file name to unicode
1722 BOOL WINAPI wine_get_unix_file_name( LPCSTR dos, LPSTR buffer, DWORD len )
1726 WCHAR dosW[MAX_PATHNAME_LEN];
1728 MultiByteToWideChar(CP_ACP, 0, dos, -1, dosW, MAX_PATHNAME_LEN);
1729 ret = DOSFS_GetFullName( dosW, FALSE, &path );
1732 strncpy( buffer, path.long_name, len );
1733 buffer[len - 1] = 0; /* ensure 0 termination */
1739 /***********************************************************************
1740 * get_show_dir_symlinks_option
1742 static BOOL get_show_dir_symlinks_option(void)
1744 static const WCHAR WineW[] = {'M','a','c','h','i','n','e','\\',
1745 'S','o','f','t','w','a','r','e','\\',
1746 'W','i','n','e','\\','W','i','n','e','\\',
1747 'C','o','n','f','i','g','\\','W','i','n','e',0};
1748 static const WCHAR ShowDirSymlinksW[] = {'S','h','o','w','D','i','r','S','y','m','l','i','n','k','s',0};
1753 OBJECT_ATTRIBUTES attr;
1754 UNICODE_STRING nameW;
1757 attr.Length = sizeof(attr);
1758 attr.RootDirectory = 0;
1759 attr.ObjectName = &nameW;
1760 attr.Attributes = 0;
1761 attr.SecurityDescriptor = NULL;
1762 attr.SecurityQualityOfService = NULL;
1763 RtlInitUnicodeString( &nameW, WineW );
1765 if (!NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr ))
1767 RtlInitUnicodeString( &nameW, ShowDirSymlinksW );
1768 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
1770 WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
1771 ret = IS_OPTION_TRUE( str[0] );
1779 /***********************************************************************
1782 static int DOSFS_FindNextEx( FIND_FIRST_INFO *info, WIN32_FIND_DATAW *entry )
1784 DWORD attr = info->attr | FA_UNUSED | FA_ARCHIVE | FA_RDONLY;
1785 UINT flags = DRIVE_GetFlags( info->drive );
1786 char *p, buffer[MAX_PATHNAME_LEN];
1787 const char *drive_path;
1789 LPCWSTR long_name, short_name;
1790 BY_HANDLE_FILE_INFORMATION fileinfo;
1794 if ((info->attr & ~(FA_UNUSED | FA_ARCHIVE | FA_RDONLY)) == FA_LABEL)
1796 if (info->cur_pos) return 0;
1797 entry->dwFileAttributes = FILE_ATTRIBUTE_LABEL;
1798 RtlSecondsSince1970ToTime( (time_t)0, (LARGE_INTEGER *)&entry->ftCreationTime );
1799 RtlSecondsSince1970ToTime( (time_t)0, (LARGE_INTEGER *)&entry->ftLastAccessTime );
1800 RtlSecondsSince1970ToTime( (time_t)0, (LARGE_INTEGER *)&entry->ftLastWriteTime );
1801 entry->nFileSizeHigh = 0;
1802 entry->nFileSizeLow = 0;
1803 entry->dwReserved0 = 0;
1804 entry->dwReserved1 = 0;
1805 DOSFS_ToDosDTAFormat( DRIVE_GetLabel( info->drive ), entry->cFileName );
1806 strcpyW( entry->cAlternateFileName, entry->cFileName );
1808 TRACE("returning %s (%s) as label\n",
1809 debugstr_w(entry->cFileName), debugstr_w(entry->cAlternateFileName));
1813 drive_path = info->path + strlen(DRIVE_GetRoot( info->drive ));
1814 while ((*drive_path == '/') || (*drive_path == '\\')) drive_path++;
1815 drive_root = !*drive_path;
1817 lstrcpynA( buffer, info->path, sizeof(buffer) - 1 );
1818 strcat( buffer, "/" );
1819 p = buffer + strlen(buffer);
1821 while (DOSFS_ReadDir( info->u.dos_dir, &long_name, &short_name ))
1825 /* Don't return '.' and '..' in the root of the drive */
1826 if (drive_root && (long_name[0] == '.') &&
1827 (!long_name[1] || ((long_name[1] == '.') && !long_name[2])))
1830 /* Check the long mask */
1832 if (info->long_mask && *info->long_mask)
1834 if (!DOSFS_MatchLong( info->long_mask, long_name,
1835 flags & DRIVE_CASE_SENSITIVE )) continue;
1838 /* Check the short mask */
1840 if (info->short_mask)
1844 DOSFS_Hash( long_name, dos_name, TRUE,
1845 !(flags & DRIVE_CASE_SENSITIVE) );
1846 short_name = dos_name;
1848 if (!DOSFS_MatchShort( info->short_mask, short_name )) continue;
1851 /* Check the file attributes */
1852 WideCharToMultiByte(DRIVE_GetCodepage(info->drive), 0, long_name, -1,
1853 p, sizeof(buffer) - (int)(p - buffer), NULL, NULL);
1854 if (!FILE_Stat( buffer, &fileinfo, &is_symlink ))
1856 WARN("can't stat %s\n", buffer);
1859 if (is_symlink && (fileinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
1861 static int show_dir_symlinks = -1;
1862 if (show_dir_symlinks == -1)
1863 show_dir_symlinks = get_show_dir_symlinks_option();
1864 if (!show_dir_symlinks) continue;
1867 if (fileinfo.dwFileAttributes & ~attr) continue;
1869 /* We now have a matching entry; fill the result and return */
1871 entry->dwFileAttributes = fileinfo.dwFileAttributes;
1872 entry->ftCreationTime = fileinfo.ftCreationTime;
1873 entry->ftLastAccessTime = fileinfo.ftLastAccessTime;
1874 entry->ftLastWriteTime = fileinfo.ftLastWriteTime;
1875 entry->nFileSizeHigh = fileinfo.nFileSizeHigh;
1876 entry->nFileSizeLow = fileinfo.nFileSizeLow;
1879 DOSFS_ToDosDTAFormat( short_name, entry->cAlternateFileName );
1881 DOSFS_Hash( long_name, entry->cAlternateFileName, FALSE,
1882 !(flags & DRIVE_CASE_SENSITIVE) );
1884 lstrcpynW( entry->cFileName, long_name, sizeof(entry->cFileName)/sizeof(entry->cFileName[0]) );
1885 if (!(flags & DRIVE_CASE_PRESERVING)) strlwrW( entry->cFileName );
1886 TRACE("returning %s (%s) %02lx %ld\n",
1887 debugstr_w(entry->cFileName), debugstr_w(entry->cAlternateFileName),
1888 entry->dwFileAttributes, entry->nFileSizeLow );
1891 return 0; /* End of directory */
1894 /***********************************************************************
1897 * Find the next matching file. Return the number of entries read to find
1898 * the matching one, or 0 if no more entries.
1899 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
1900 * file name mask. Either or both can be NULL.
1902 * NOTE: This is supposed to be only called by the int21 emulation
1903 * routines. Thus, we should own the Win16Mutex anyway.
1904 * Nevertheless, we explicitly enter it to ensure the static
1905 * directory cache is protected.
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;
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,
1923 RtlCreateUnicodeStringFromAsciiz(&short_maskW, short_mask);
1924 RtlCreateUnicodeStringFromAsciiz(&long_maskW, long_mask);
1926 /* Check the cached directory */
1927 if (!(info.u.dos_dir && info.path == path && !strcmpW(info.short_mask, short_maskW.Buffer)
1928 && !strcmpW(info.long_mask, long_maskW.Buffer) && info.drive == drive
1929 && info.attr == attr && info.cur_pos <= skip))
1931 /* Not in the cache, open it anew */
1932 if (info.u.dos_dir) DOSFS_CloseDir( info.u.dos_dir );
1934 info.path = (LPSTR)path;
1935 RtlFreeHeap(GetProcessHeap(), 0, info.long_mask);
1936 RtlFreeHeap(GetProcessHeap(), 0, info.short_mask);
1937 info.long_mask = long_maskW.Buffer;
1938 info.short_mask = short_maskW.Buffer;
1942 info.u.dos_dir = DOSFS_OpenDir( DRIVE_GetCodepage(drive), info.path );
1946 RtlFreeUnicodeString(&short_maskW);
1947 RtlFreeUnicodeString(&long_maskW);
1950 /* Skip to desired position */
1951 while (info.cur_pos < skip)
1952 if (info.u.dos_dir && DOSFS_ReadDir( info.u.dos_dir, &long_name, &short_name ))
1957 if (info.u.dos_dir && info.cur_pos == skip && DOSFS_FindNextEx( &info, &entryW ))
1959 WideCharToMultiByte(CP_ACP, 0, entryW.cFileName, -1,
1960 entry->cFileName, sizeof(entry->cFileName), NULL, NULL);
1961 WideCharToMultiByte(CP_ACP, 0, entryW.cAlternateFileName, -1,
1962 entry->cAlternateFileName, sizeof(entry->cAlternateFileName), NULL, NULL);
1963 count = info.cur_pos - skip;
1965 entry->dwFileAttributes = entryW.dwFileAttributes;
1966 entry->nFileSizeHigh = entryW.nFileSizeHigh;
1967 entry->nFileSizeLow = entryW.nFileSizeLow;
1968 entry->ftCreationTime = entryW.ftCreationTime;
1969 entry->ftLastAccessTime = entryW.ftLastAccessTime;
1970 entry->ftLastWriteTime = entryW.ftLastWriteTime;
1978 if (info.u.dos_dir) DOSFS_CloseDir( info.u.dos_dir );
1979 memset( &info, '\0', sizeof(info) );
1987 /*************************************************************************
1988 * FindFirstFileExW (KERNEL32.@)
1990 HANDLE WINAPI FindFirstFileExW(
1992 FINDEX_INFO_LEVELS fInfoLevelId,
1993 LPVOID lpFindFileData,
1994 FINDEX_SEARCH_OPS fSearchOp,
1995 LPVOID lpSearchFilter,
1996 DWORD dwAdditionalFlags)
1999 FIND_FIRST_INFO *info;
2003 SetLastError(ERROR_PATH_NOT_FOUND);
2004 return INVALID_HANDLE_VALUE;
2007 if ((fSearchOp != FindExSearchNameMatch) || (dwAdditionalFlags != 0))
2009 FIXME("options not implemented 0x%08x 0x%08lx\n", fSearchOp, dwAdditionalFlags );
2010 return INVALID_HANDLE_VALUE;
2013 switch(fInfoLevelId)
2015 case FindExInfoStandard:
2017 WIN32_FIND_DATAW * data = (WIN32_FIND_DATAW *) lpFindFileData;
2022 data->dwReserved0 = data->dwReserved1 = 0x0;
2023 if (lpFileName[0] == '\\' && lpFileName[1] == '\\')
2025 ERR("UNC path name\n");
2026 if (!(handle = GlobalAlloc(GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO)))) break;
2028 info = (FIND_FIRST_INFO *)GlobalLock( handle );
2029 info->u.smb_dir = SMB_FindFirst(lpFileName);
2030 if(!info->u.smb_dir)
2032 GlobalUnlock( handle );
2039 GlobalUnlock( handle );
2043 DOS_FULL_NAME full_name;
2045 if (lpFileName[0] && lpFileName[1] == ':')
2047 /* don't allow root directories */
2048 if (!lpFileName[2] ||
2049 ((lpFileName[2] == '/' || lpFileName[2] == '\\') && !lpFileName[3]))
2051 SetLastError(ERROR_FILE_NOT_FOUND);
2052 return INVALID_HANDLE_VALUE;
2055 if (!DOSFS_GetFullName( lpFileName, FALSE, &full_name )) break;
2056 if (!(handle = GlobalAlloc(GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO)))) break;
2057 info = (FIND_FIRST_INFO *)GlobalLock( handle );
2058 info->path = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
2059 strcpy( info->path, full_name.long_name );
2061 codepage = DRIVE_GetCodepage(full_name.drive);
2062 p = strrchr( info->path, '/' );
2064 long_mask_len = MultiByteToWideChar(codepage, 0, p, -1, NULL, 0);
2065 info->long_mask = HeapAlloc( GetProcessHeap(), 0, long_mask_len * sizeof(WCHAR) );
2066 MultiByteToWideChar(codepage, 0, p, -1, info->long_mask, long_mask_len);
2068 info->short_mask = NULL;
2070 info->drive = full_name.drive;
2073 info->u.dos_dir = DOSFS_OpenDir( codepage, info->path );
2074 GlobalUnlock( handle );
2076 if (!FindNextFileW( handle, data ))
2078 FindClose( handle );
2079 SetLastError( ERROR_FILE_NOT_FOUND );
2086 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
2088 return INVALID_HANDLE_VALUE;
2091 /*************************************************************************
2092 * FindFirstFileA (KERNEL32.@)
2094 HANDLE WINAPI FindFirstFileA(
2096 WIN32_FIND_DATAA *lpFindData )
2098 return FindFirstFileExA(lpFileName, FindExInfoStandard, lpFindData,
2099 FindExSearchNameMatch, NULL, 0);
2102 /*************************************************************************
2103 * FindFirstFileExA (KERNEL32.@)
2105 HANDLE WINAPI FindFirstFileExA(
2107 FINDEX_INFO_LEVELS fInfoLevelId,
2108 LPVOID lpFindFileData,
2109 FINDEX_SEARCH_OPS fSearchOp,
2110 LPVOID lpSearchFilter,
2111 DWORD dwAdditionalFlags)
2114 WIN32_FIND_DATAA *dataA;
2115 WIN32_FIND_DATAW dataW;
2116 UNICODE_STRING pathW;
2120 SetLastError(ERROR_PATH_NOT_FOUND);
2121 return INVALID_HANDLE_VALUE;
2124 if (!RtlCreateUnicodeStringFromAsciiz(&pathW, lpFileName))
2126 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2127 return INVALID_HANDLE_VALUE;
2130 handle = FindFirstFileExW(pathW.Buffer, fInfoLevelId, &dataW, fSearchOp, lpSearchFilter, dwAdditionalFlags);
2131 RtlFreeUnicodeString(&pathW);
2132 if (handle == INVALID_HANDLE_VALUE) return handle;
2134 dataA = (WIN32_FIND_DATAA *) lpFindFileData;
2135 dataA->dwFileAttributes = dataW.dwFileAttributes;
2136 dataA->ftCreationTime = dataW.ftCreationTime;
2137 dataA->ftLastAccessTime = dataW.ftLastAccessTime;
2138 dataA->ftLastWriteTime = dataW.ftLastWriteTime;
2139 dataA->nFileSizeHigh = dataW.nFileSizeHigh;
2140 dataA->nFileSizeLow = dataW.nFileSizeLow;
2141 WideCharToMultiByte( CP_ACP, 0, dataW.cFileName, -1,
2142 dataA->cFileName, sizeof(dataA->cFileName), NULL, NULL );
2143 WideCharToMultiByte( CP_ACP, 0, dataW.cAlternateFileName, -1,
2144 dataA->cAlternateFileName, sizeof(dataA->cAlternateFileName), NULL, NULL );
2148 /*************************************************************************
2149 * FindFirstFileW (KERNEL32.@)
2151 HANDLE WINAPI FindFirstFileW( LPCWSTR lpFileName, WIN32_FIND_DATAW *lpFindData )
2153 return FindFirstFileExW(lpFileName, FindExInfoStandard, lpFindData,
2154 FindExSearchNameMatch, NULL, 0);
2157 /*************************************************************************
2158 * FindNextFileW (KERNEL32.@)
2160 BOOL WINAPI FindNextFileW( HANDLE handle, WIN32_FIND_DATAW *data )
2162 FIND_FIRST_INFO *info;
2164 DWORD gle = ERROR_NO_MORE_FILES;
2166 if ((handle == INVALID_HANDLE_VALUE) ||
2167 !(info = (FIND_FIRST_INFO *)GlobalLock( handle )))
2169 SetLastError( ERROR_INVALID_HANDLE );
2172 if (info->drive == -1)
2174 ret = SMB_FindNext( info->u.smb_dir, data );
2177 SMB_CloseDir( info->u.smb_dir );
2178 HeapFree( GetProcessHeap(), 0, info->path );
2182 else if (!info->path || !info->u.dos_dir)
2186 else if (!DOSFS_FindNextEx( info, data ))
2188 DOSFS_CloseDir( info->u.dos_dir ); info->u.dos_dir = NULL;
2189 HeapFree( GetProcessHeap(), 0, info->path );
2191 HeapFree( GetProcessHeap(), 0, info->long_mask );
2192 info->long_mask = NULL;
2197 GlobalUnlock( handle );
2198 if( !ret ) SetLastError( gle );
2203 /*************************************************************************
2204 * FindNextFileA (KERNEL32.@)
2206 BOOL WINAPI FindNextFileA( HANDLE handle, WIN32_FIND_DATAA *data )
2208 WIN32_FIND_DATAW dataW;
2209 if (!FindNextFileW( handle, &dataW )) return FALSE;
2210 data->dwFileAttributes = dataW.dwFileAttributes;
2211 data->ftCreationTime = dataW.ftCreationTime;
2212 data->ftLastAccessTime = dataW.ftLastAccessTime;
2213 data->ftLastWriteTime = dataW.ftLastWriteTime;
2214 data->nFileSizeHigh = dataW.nFileSizeHigh;
2215 data->nFileSizeLow = dataW.nFileSizeLow;
2216 WideCharToMultiByte( CP_ACP, 0, dataW.cFileName, -1,
2217 data->cFileName, sizeof(data->cFileName), NULL, NULL );
2218 WideCharToMultiByte( CP_ACP, 0, dataW.cAlternateFileName, -1,
2219 data->cAlternateFileName,
2220 sizeof(data->cAlternateFileName), NULL, NULL );
2224 /*************************************************************************
2225 * FindClose (KERNEL32.@)
2227 BOOL WINAPI FindClose( HANDLE handle )
2229 FIND_FIRST_INFO *info;
2231 if (handle == INVALID_HANDLE_VALUE) goto error;
2235 if ((info = (FIND_FIRST_INFO *)GlobalLock( handle )))
2237 if (info->u.dos_dir) DOSFS_CloseDir( info->u.dos_dir );
2238 if (info->path) HeapFree( GetProcessHeap(), 0, info->path );
2239 if (info->long_mask) HeapFree( GetProcessHeap(), 0, info->long_mask );
2242 __EXCEPT(page_fault)
2244 WARN("Illegal handle %p\n", handle);
2245 SetLastError( ERROR_INVALID_HANDLE );
2249 if (!info) goto error;
2250 GlobalUnlock( handle );
2251 GlobalFree( handle );
2255 SetLastError( ERROR_INVALID_HANDLE );
2259 /***********************************************************************
2260 * MulDiv (KERNEL32.@)
2262 * Result of multiplication and division
2263 * -1: Overflow occurred or Divisor was 0
2270 #if SIZEOF_LONG_LONG >= 8
2273 if (!nDivisor) return -1;
2275 /* We want to deal with a positive divisor to simplify the logic. */
2278 nMultiplicand = - nMultiplicand;
2279 nDivisor = -nDivisor;
2282 /* If the result is positive, we "add" to round. else, we subtract to round. */
2283 if ( ( (nMultiplicand < 0) && (nMultiplier < 0) ) ||
2284 ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
2285 ret = (((long long)nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
2287 ret = (((long long)nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
2289 if ((ret > 2147483647) || (ret < -2147483647)) return -1;
2292 if (!nDivisor) return -1;
2294 /* We want to deal with a positive divisor to simplify the logic. */
2297 nMultiplicand = - nMultiplicand;
2298 nDivisor = -nDivisor;
2301 /* If the result is positive, we "add" to round. else, we subtract to round. */
2302 if ( ( (nMultiplicand < 0) && (nMultiplier < 0) ) ||
2303 ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
2304 return ((nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
2306 return ((nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
2312 /***********************************************************************
2313 * DosDateTimeToFileTime (KERNEL32.@)
2315 BOOL WINAPI DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft)
2320 time_t time1, time2;
2323 newtm.tm_sec = (fattime & 0x1f) * 2;
2324 newtm.tm_min = (fattime >> 5) & 0x3f;
2325 newtm.tm_hour = (fattime >> 11);
2326 newtm.tm_mday = (fatdate & 0x1f);
2327 newtm.tm_mon = ((fatdate >> 5) & 0x0f) - 1;
2328 newtm.tm_year = (fatdate >> 9) + 80;
2330 RtlSecondsSince1970ToTime( timegm(&newtm), (LARGE_INTEGER *)ft );
2332 time1 = mktime(&newtm);
2333 gtm = gmtime(&time1);
2334 time2 = mktime(gtm);
2335 RtlSecondsSince1970ToTime( 2*time1-time2, (LARGE_INTEGER *)ft );
2341 /***********************************************************************
2342 * FileTimeToDosDateTime (KERNEL32.@)
2344 BOOL WINAPI FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate,
2352 li.s.LowPart = ft->dwLowDateTime;
2353 li.s.HighPart = ft->dwHighDateTime;
2354 RtlTimeToSecondsSince1970( &li, &t );
2356 tm = gmtime( &unixtime );
2358 *fattime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
2360 *fatdate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
2366 /***********************************************************************
2367 * QueryDosDeviceA (KERNEL32.@)
2369 * returns array of strings terminated by \0, terminated by \0
2371 DWORD WINAPI QueryDosDeviceA(LPCSTR devname,LPSTR target,DWORD bufsize)
2373 DWORD ret = 0, retW;
2374 LPWSTR targetW = (LPWSTR)HeapAlloc(GetProcessHeap(),0,
2375 bufsize * sizeof(WCHAR));
2376 UNICODE_STRING devnameW;
2378 if(devname) RtlCreateUnicodeStringFromAsciiz(&devnameW, devname);
2379 else devnameW.Buffer = NULL;
2381 retW = QueryDosDeviceW(devnameW.Buffer, targetW, bufsize);
2383 ret = WideCharToMultiByte(CP_ACP, 0, targetW, retW, target,
2384 bufsize, NULL, NULL);
2386 RtlFreeUnicodeString(&devnameW);
2387 if (targetW) HeapFree(GetProcessHeap(),0,targetW);
2392 /***********************************************************************
2393 * QueryDosDeviceW (KERNEL32.@)
2395 * returns array of strings terminated by \0, terminated by \0
2398 * - Win9x returns for all calls ERROR_INVALID_PARAMETER
2399 * - the returned devices for devname == NULL is far from complete
2400 * - its not checked that the returned device exist
2402 DWORD WINAPI QueryDosDeviceW(LPCWSTR devname,LPWSTR target,DWORD bufsize)
2404 const WCHAR *pDev, *pName, *pNum = NULL;
2408 TRACE("(%s,...)\n", debugstr_w(devname));
2410 /* return known MSDOS devices */
2413 static const WCHAR devices[][5] = {{'A','U','X',0},
2414 {'C','O','M','1',0},
2415 {'C','O','M','2',0},
2416 {'L','P','T','1',0},
2418 for(i=0; (i< (sizeof(devices)/sizeof(devices[0]))); i++) {
2419 DWORD len = strlenW(devices[i]);
2420 if(target && (bufsize >= ret + len + 2)) {
2421 lstrcpyW(target+ret, devices[i]);
2424 /* in this case WinXP returns 0 */
2425 FIXME("function return is wrong for WinXP!\n");
2426 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2430 /* append drives here */
2431 if(target && bufsize > 0) target[ret++] = 0;
2432 FIXME("Returned list is not complete\n");
2435 /* In theory all that are possible and have been defined.
2436 * Now just those below, since mirc uses it to check for special files.
2438 * (It is more complex, and supports netmounted stuff, and \\.\ stuff,
2439 * but currently we just ignore that.)
2441 if (!strcmpiW(devname, auxW)) {
2446 } else if (!strcmpiW(devname, nulW)) {
2449 } else if (!strncmpiW(devname, comW, strlenW(comW))) {
2452 pNum = devname + strlenW(comW);
2453 for(numsiz=0; isdigitW(*(pNum+numsiz)); numsiz++);
2454 if(*(pNum + numsiz)) {
2455 SetLastError(ERROR_FILE_NOT_FOUND);
2458 } else if (!strncmpiW(devname, lptW, strlenW(lptW))) {
2461 pNum = devname + strlenW(lptW);
2462 for(numsiz=0; isdigitW(*(pNum+numsiz)); numsiz++);
2463 if(*(pNum + numsiz)) {
2464 SetLastError(ERROR_FILE_NOT_FOUND);
2468 /* This might be a DOS device we do not handle yet ... */
2469 FIXME("(%s) not detected as DOS device!\n",debugstr_w(devname));
2471 /* Win9x set the error ERROR_INVALID_PARAMETER */
2472 SetLastError(ERROR_FILE_NOT_FOUND);
2475 FIXME("device %s may not exist on this computer\n", debugstr_w(devname));
2477 ret = strlenW(pDev) + strlenW(pName) + numsiz + 2;
2478 if (ret > bufsize) ret = 0;
2479 if (target && ret) {
2480 lstrcpyW(target,pDev);
2481 lstrcatW(target,pName);
2482 if (pNum) lstrcatW(target,pNum);
2489 /***********************************************************************
2490 * DefineDosDeviceA (KERNEL32.@)
2492 BOOL WINAPI DefineDosDeviceA(DWORD flags,LPCSTR devname,LPCSTR targetpath) {
2493 FIXME("(0x%08lx,%s,%s),stub!\n",flags,devname,targetpath);
2494 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2499 --- 16 bit functions ---
2502 /*************************************************************************
2503 * FindFirstFile (KERNEL.413)
2505 HANDLE16 WINAPI FindFirstFile16( LPCSTR path, WIN32_FIND_DATAA *data )
2507 DOS_FULL_NAME full_name;
2509 FIND_FIRST_INFO *info;
2510 WCHAR pathW[MAX_PATH];
2515 data->dwReserved0 = data->dwReserved1 = 0x0;
2516 if (!path) return INVALID_HANDLE_VALUE16;
2517 MultiByteToWideChar(CP_ACP, 0, path, -1, pathW, MAX_PATH);
2518 if (!DOSFS_GetFullName( pathW, FALSE, &full_name ))
2519 return INVALID_HANDLE_VALUE16;
2520 if (!(handle = GlobalAlloc16( GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO) )))
2521 return INVALID_HANDLE_VALUE16;
2522 info = (FIND_FIRST_INFO *)GlobalLock16( handle );
2523 info->path = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
2524 strcpy( info->path, full_name.long_name );
2526 codepage = DRIVE_GetCodepage(full_name.drive);
2527 p = strrchr( info->path, '/' );
2529 long_mask_len = MultiByteToWideChar(codepage, 0, p, -1, NULL, 0);
2530 info->long_mask = HeapAlloc( GetProcessHeap(), 0, long_mask_len * sizeof(WCHAR) );
2531 MultiByteToWideChar(codepage, 0, p, -1, info->long_mask, long_mask_len);
2533 info->short_mask = NULL;
2535 info->drive = full_name.drive;
2538 info->u.dos_dir = DOSFS_OpenDir( codepage, info->path );
2540 GlobalUnlock16( handle );
2541 if (!FindNextFile16( handle, data ))
2543 FindClose16( handle );
2544 SetLastError( ERROR_NO_MORE_FILES );
2545 return INVALID_HANDLE_VALUE16;
2550 /*************************************************************************
2551 * FindNextFile (KERNEL.414)
2553 BOOL16 WINAPI FindNextFile16( HANDLE16 handle, WIN32_FIND_DATAA *data )
2555 FIND_FIRST_INFO *info;
2556 WIN32_FIND_DATAW dataW;
2558 DWORD gle = ERROR_NO_MORE_FILES;
2560 if ((handle == INVALID_HANDLE_VALUE16) ||
2561 !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
2563 SetLastError( ERROR_INVALID_HANDLE );
2566 if (!info->path || !info->u.dos_dir)
2570 if (!DOSFS_FindNextEx( info, &dataW ))
2572 DOSFS_CloseDir( info->u.dos_dir ); info->u.dos_dir = NULL;
2573 HeapFree( GetProcessHeap(), 0, info->path );
2575 HeapFree( GetProcessHeap(), 0, info->long_mask );
2576 info->long_mask = NULL;
2582 data->dwFileAttributes = dataW.dwFileAttributes;
2583 data->ftCreationTime = dataW.ftCreationTime;
2584 data->ftLastAccessTime = dataW.ftLastAccessTime;
2585 data->ftLastWriteTime = dataW.ftLastWriteTime;
2586 data->nFileSizeHigh = dataW.nFileSizeHigh;
2587 data->nFileSizeLow = dataW.nFileSizeLow;
2588 WideCharToMultiByte( CP_ACP, 0, dataW.cFileName, -1,
2589 data->cFileName, sizeof(data->cFileName), NULL, NULL );
2590 WideCharToMultiByte( CP_ACP, 0, dataW.cAlternateFileName, -1,
2591 data->cAlternateFileName,
2592 sizeof(data->cAlternateFileName), NULL, NULL );
2594 if( !ret ) SetLastError( gle );
2595 GlobalUnlock16( handle );
2600 /*************************************************************************
2601 * FindClose (KERNEL.415)
2603 BOOL16 WINAPI FindClose16( HANDLE16 handle )
2605 FIND_FIRST_INFO *info;
2607 if ((handle == INVALID_HANDLE_VALUE16) ||
2608 !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
2610 SetLastError( ERROR_INVALID_HANDLE );
2613 if (info->u.dos_dir) DOSFS_CloseDir( info->u.dos_dir );
2614 if (info->path) HeapFree( GetProcessHeap(), 0, info->path );
2615 if (info->long_mask) HeapFree( GetProcessHeap(), 0, info->long_mask );
2616 GlobalUnlock16( handle );
2617 GlobalFree16( handle );