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"
57 #include "wine/server.h"
62 #include "wine/debug.h"
64 WINE_DEFAULT_DEBUG_CHANNEL(dosfs);
65 WINE_DECLARE_DEBUG_CHANNEL(file);
67 /* Define the VFAT ioctl to get both short and long file names */
68 /* FIXME: is it possible to get this to work on other systems? */
70 /* We want the real kernel dirent structure, not the libc one */
75 unsigned short d_reclen;
79 #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, KERNEL_DIRENT [2] )
81 /* To avoid blocking on non-directories in DOSFS_OpenDir_VFAT*/
83 # define O_DIRECTORY 0200000 /* must be directory */
87 #undef VFAT_IOCTL_READDIR_BOTH /* just in case... */
90 #define IS_OPTION_TRUE(ch) ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1')
92 /* Chars we don't want to see in DOS file names */
93 #define INVALID_DOS_CHARS "*?<>|\"+=,;[] \345"
95 static const DOS_DEVICE DOSFS_Devices[] =
96 /* name, device flags (see Int 21/AX=0x4400) */
98 { {'C','O','N',0}, 0xc0d3 },
99 { {'P','R','N',0}, 0xa0c0 },
100 { {'N','U','L',0}, 0x80c4 },
101 { {'A','U','X',0}, 0x80c0 },
102 { {'L','P','T','1',0}, 0xa0c0 },
103 { {'L','P','T','2',0}, 0xa0c0 },
104 { {'L','P','T','3',0}, 0xa0c0 },
105 { {'L','P','T','4',0}, 0xc0d3 },
106 { {'C','O','M','1',0}, 0x80c0 },
107 { {'C','O','M','2',0}, 0x80c0 },
108 { {'C','O','M','3',0}, 0x80c0 },
109 { {'C','O','M','4',0}, 0x80c0 },
110 { {'S','C','S','I','M','G','R','$',0}, 0xc0c0 },
111 { {'H','P','S','C','A','N',0}, 0xc0c0 },
112 { {'E','M','M','X','X','X','X','0',0}, 0x0000 }
115 static const WCHAR devW[] = {'\\','D','e','v','i','c','e','\\',0};
116 static const WCHAR dosW[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\',0};
118 static const WCHAR auxW[] = {'A','U','X',0};
119 static const WCHAR comW[] = {'C','O','M',0};
120 static const WCHAR lptW[] = {'L','P','T',0};
121 static const WCHAR nulW[] = {'N','U','L',0};
123 static const WCHAR nullW[] = {'N','u','l','l',0};
124 static const WCHAR parW[] = {'P','a','r','a','l','l','e','l',0};
125 static const WCHAR serW[] = {'S','e','r','i','a','l',0};
126 static const WCHAR oneW[] = {'1',0};
129 * Directory info for DOSFS_ReadDir
130 * contains the names of *all* the files in the directory
139 /* Info structure for FindFirstFile handle */
142 char *path; /* unix path */
155 static WINE_EXCEPTION_FILTER(page_fault)
157 if (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION)
158 return EXCEPTION_EXECUTE_HANDLER;
159 return EXCEPTION_CONTINUE_SEARCH;
163 /***********************************************************************
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;
179 /* Check for "." and ".." */
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 */
192 if (*p != '.') return 1; /* End of name */
194 if (IS_END_OF_NAME(*p)) return 0; /* Empty extension not allowed */
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 */
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;
222 /* Check for "." and ".." */
227 for(i = 1; i < 11; i++) buffer[i] = ' ';
234 return (!*p || (*p == '/') || (*p == '\\'));
237 for (i = 0; i < 8; i++)
254 if (*p < 256 && strchr( invalid_chars, (char)*p )) return FALSE;
255 buffer[i] = toupperW(*p);
263 /* Skip all chars after wildcard up to first dot */
264 while (*p && (*p != '/') && (*p != '\\') && (*p != '.')) p++;
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++)
283 return FALSE; /* Second extension not allowed */
291 if (*p < 256 && strchr( invalid_chars, (char)*p )) return FALSE;
292 buffer[i] = toupperW(*p);
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 )
319 memcpy( buffer, name, 8 * sizeof(WCHAR) );
321 while ((p > buffer) && (p[-1] == ' ')) p--;
323 memcpy( p, name + 8, 3 * sizeof(WCHAR) );
325 while (p[-1] == ' ') p--;
326 if (p[-1] == '.') p--;
331 /***********************************************************************
334 * Check a long file name against a mask.
336 * Tests (done in W95 DOS shell - case insensitive):
337 * *.txt test1.test.txt *
339 * *.t??????.t* test1.ta.tornado.txt *
340 * *tornado* test1.ta.tornado.txt *
341 * t*t test1.ta.tornado.txt *
343 * ?est??? test1.txt -
344 * *test1.txt* test1.txt *
345 * h?l?o*t.dat hellothisisatest.dat *
347 static int DOSFS_MatchLong( LPCWSTR mask, LPCWSTR name, int case_sensitive )
349 LPCWSTR lastjoker = NULL;
350 LPCWSTR next_to_retry = NULL;
351 static const WCHAR asterisk_dot_asterisk[] = {'*','.','*',0};
353 TRACE("(%s, %s, %x)\n", debugstr_w(mask), debugstr_w(name), case_sensitive);
355 if (!strcmpW( mask, asterisk_dot_asterisk )) return 1;
356 while (*name && *mask)
361 while (*mask == '*') mask++; /* Skip consecutive '*' */
363 if (!*mask) return 1; /* end of mask is all '*', so match */
365 /* skip to the next match after the joker(s) */
366 if (case_sensitive) while (*name && (*name != *mask)) name++;
367 else while (*name && (toupperW(*name) != toupperW(*mask))) name++;
370 next_to_retry = name;
372 else if (*mask != '?')
377 if (*mask != *name) mismatch = 1;
381 if (toupperW(*mask) != toupperW(*name)) mismatch = 1;
395 else /* mismatch ! */
397 if (lastjoker) /* we had an '*', so we can try unlimitedly */
401 /* this scan sequence was a mismatch, so restart
402 * 1 char after the first char we checked last time */
404 name = next_to_retry;
407 return 0; /* bad luck */
416 while ((*mask == '.') || (*mask == '*'))
417 mask++; /* Ignore trailing '.' or '*' in mask */
418 return (!*name && !*mask);
422 /***********************************************************************
425 * Used to construct an array of filenames in DOSFS_OpenDir
427 static BOOL DOSFS_AddDirEntry(DOS_DIR **dir, LPCWSTR name, LPCWSTR dosname)
429 int extra1 = strlenW(name) + 1;
430 int extra2 = strlenW(dosname) + 1;
432 /* if we need more, at minimum double the size */
433 if( (extra1 + extra2 + (*dir)->used) > (*dir)->size)
435 int more = (*dir)->size;
438 if(more<(extra1+extra2))
439 more = extra1+extra2;
441 t = HeapReAlloc(GetProcessHeap(), 0, *dir, sizeof(**dir) +
442 ((*dir)->size + more)*sizeof(WCHAR) );
445 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
446 ERR("Out of memory caching directory structure %d %d %d\n",
447 (*dir)->size, more, (*dir)->used);
451 (*dir)->size += more;
454 /* at this point, the dir structure is big enough to hold these names */
455 strcpyW(&(*dir)->names[(*dir)->used], name);
456 (*dir)->used += extra1;
457 strcpyW(&(*dir)->names[(*dir)->used], dosname);
458 (*dir)->used += extra2;
464 /***********************************************************************
467 static BOOL DOSFS_OpenDir_VFAT(DOS_DIR **dir, const char *unix_path)
469 #ifdef VFAT_IOCTL_READDIR_BOTH
471 int fd = open( unix_path, O_RDONLY|O_DIRECTORY );
474 /* Check if the VFAT ioctl is supported on this directory */
481 WCHAR long_name[MAX_PATH];
482 WCHAR short_name[12];
484 r = (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de ) != -1);
489 MultiByteToWideChar(CP_UNIXCP, 0, de[0].d_name, -1, long_name, MAX_PATH);
490 if (!DOSFS_ToDosFCBFormat( long_name, short_name ))
491 short_name[0] = '\0';
493 MultiByteToWideChar(CP_UNIXCP, 0, de[1].d_name, -1, long_name, MAX_PATH);
495 MultiByteToWideChar(CP_UNIXCP, 0, de[0].d_name, -1, long_name, MAX_PATH);
496 r = DOSFS_AddDirEntry(dir, long_name, short_name );
502 static const WCHAR empty_strW[] = { 0 };
503 DOSFS_AddDirEntry(dir, empty_strW, empty_strW);
509 #endif /* VFAT_IOCTL_READDIR_BOTH */
513 /***********************************************************************
514 * DOSFS_OpenDir_Normal
516 * Now use the standard opendir/readdir interface
518 static BOOL DOSFS_OpenDir_Normal( DOS_DIR **dir, const char *unix_path )
520 DIR *unixdir = opendir( unix_path );
522 static const WCHAR empty_strW[] = { 0 };
528 WCHAR long_name[MAX_PATH];
529 struct dirent *de = readdir(unixdir);
533 MultiByteToWideChar(CP_UNIXCP, 0, de->d_name, -1, long_name, MAX_PATH);
534 r = DOSFS_AddDirEntry(dir, long_name, empty_strW);
539 DOSFS_AddDirEntry(dir, empty_strW, empty_strW);
544 /***********************************************************************
547 static DOS_DIR *DOSFS_OpenDir( const char *unix_path )
549 const int init_size = 0x100;
550 DOS_DIR *dir = HeapAlloc( GetProcessHeap(), 0, sizeof(*dir) + init_size*sizeof (WCHAR));
553 TRACE("%s\n",debugstr_a(unix_path));
557 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
561 dir->size = init_size;
563 /* Treat empty path as root directory. This simplifies path split into
564 directory and mask in several other places */
565 if (!*unix_path) unix_path = "/";
567 r = DOSFS_OpenDir_VFAT( &dir, unix_path);
570 r = DOSFS_OpenDir_Normal( &dir, unix_path);
574 HeapFree(GetProcessHeap(), 0, dir);
583 /***********************************************************************
586 static void DOSFS_CloseDir( DOS_DIR *dir )
588 HeapFree( GetProcessHeap(), 0, dir );
592 /***********************************************************************
595 static BOOL DOSFS_ReadDir( DOS_DIR *dir, LPCWSTR *long_name,
596 LPCWSTR *short_name )
603 /* the long pathname is first */
604 ln = &dir->names[dir->used];
609 dir->used += (strlenW(ln) + 1);
611 /* followed by the short path name */
612 sn = &dir->names[dir->used];
617 dir->used += (strlenW(sn) + 1);
623 /***********************************************************************
626 * Transform a Unix file name into a hashed DOS name. If the name is a valid
627 * DOS name, it is converted to upper-case; otherwise it is replaced by a
628 * hashed version that fits in 8.3 format.
629 * File name can be terminated by '\0', '\\' or '/'.
630 * 'buffer' must be at least 13 characters long.
632 static void DOSFS_Hash( LPCWSTR name, LPWSTR buffer, BOOL dir_format,
635 static const char invalid_chars[] = INVALID_DOS_CHARS "~.";
636 static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
645 for(i = 0; i < 11; i++) buffer[i] = ' ';
649 if (DOSFS_ValidDOSName( name, ignore_case ))
651 /* Check for '.' and '..' */
655 if (!dir_format) buffer[1] = buffer[2] = '\0';
656 if (name[1] == '.') buffer[1] = '.';
660 /* Simply copy the name, converting to uppercase */
662 for (dst = buffer; !IS_END_OF_NAME(*name) && (*name != '.'); name++)
663 *dst++ = toupperW(*name);
666 if (dir_format) dst = buffer + 8;
668 for (name++; !IS_END_OF_NAME(*name); name++)
669 *dst++ = toupperW(*name);
671 if (!dir_format) *dst = '\0';
675 /* Compute the hash code of the file name */
676 /* If you know something about hash functions, feel free to */
677 /* insert a better algorithm here... */
680 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
681 hash = (hash<<3) ^ (hash>>5) ^ tolowerW(*p) ^ (tolowerW(p[1]) << 8);
682 hash = (hash<<3) ^ (hash>>5) ^ tolowerW(*p); /* Last character */
686 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
687 hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
688 hash = (hash << 3) ^ (hash >> 5) ^ *p; /* Last character */
691 /* Find last dot for start of the extension */
692 for (p = name+1, ext = NULL; !IS_END_OF_NAME(*p); p++)
693 if (*p == '.') ext = p;
694 if (ext && IS_END_OF_NAME(ext[1]))
695 ext = NULL; /* Empty extension ignored */
697 /* Copy first 4 chars, replacing invalid chars with '_' */
698 for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
700 if (IS_END_OF_NAME(*p) || (p == ext)) break;
701 *dst++ = (*p < 256 && strchr( invalid_chars, (char)*p )) ? '_' : toupperW(*p);
703 /* Pad to 5 chars with '~' */
704 while (i-- >= 0) *dst++ = '~';
706 /* Insert hash code converted to 3 ASCII chars */
707 *dst++ = hash_chars[(hash >> 10) & 0x1f];
708 *dst++ = hash_chars[(hash >> 5) & 0x1f];
709 *dst++ = hash_chars[hash & 0x1f];
711 /* Copy the first 3 chars of the extension (if any) */
714 if (!dir_format) *dst++ = '.';
715 for (i = 3, ext++; (i > 0) && !IS_END_OF_NAME(*ext); i--, ext++)
716 *dst++ = (*ext < 256 && strchr( invalid_chars, (char)*ext )) ? '_' : toupperW(*ext);
718 if (!dir_format) *dst = '\0';
722 /***********************************************************************
725 * Find the Unix file name in a given directory that corresponds to
726 * a file name (either in Unix or DOS format).
727 * File name can be terminated by '\0', '\\' or '/'.
728 * Return TRUE if OK, FALSE if no file name matches.
730 * 'long_buf' must be at least 'long_len' characters long. If the long name
731 * turns out to be larger than that, the function returns FALSE.
732 * 'short_buf' must be at least 13 characters long.
734 BOOL DOSFS_FindUnixName( const DOS_FULL_NAME *path, LPCWSTR name, char *long_buf,
735 INT long_len, LPWSTR short_buf, BOOL ignore_case)
738 LPCWSTR long_name, short_name;
739 WCHAR dos_name[12], tmp_buf[13];
742 LPCWSTR p = strchrW( name, '/' );
743 int len = p ? (int)(p - name) : strlenW(name);
744 if ((p = strchrW( name, '\\' ))) len = min( (int)(p - name), len );
745 /* Ignore trailing dots and spaces */
746 while (len > 1 && (name[len-1] == '.' || name[len-1] == ' ')) len--;
747 if (long_len < len + 1) return FALSE;
749 TRACE("%s,%s\n", path->long_name, debugstr_w(name) );
751 if (!DOSFS_ToDosFCBFormat( name, dos_name )) dos_name[0] = '\0';
753 if (!(dir = DOSFS_OpenDir( path->long_name )))
755 WARN("(%s,%s): can't open dir: %s\n",
756 path->long_name, debugstr_w(name), strerror(errno) );
760 while ((ret = DOSFS_ReadDir( dir, &long_name, &short_name )))
762 /* Check against Unix name */
763 if (len == strlenW(long_name))
767 if (!strncmpW( long_name, name, len )) break;
771 if (!strncmpiW( long_name, name, len )) break;
776 /* Check against hashed DOS name */
779 DOSFS_Hash( long_name, tmp_buf, TRUE, ignore_case );
780 short_name = tmp_buf;
782 if (!strcmpW( dos_name, short_name )) break;
787 if (long_buf) WideCharToMultiByte(CP_UNIXCP, 0, long_name, -1, long_buf, long_len, NULL, NULL);
791 DOSFS_ToDosDTAFormat( short_name, short_buf );
793 DOSFS_Hash( long_name, short_buf, FALSE, ignore_case );
795 TRACE("(%s,%s) -> %s (%s)\n", path->long_name, debugstr_w(name),
796 debugstr_w(long_name), short_buf ? debugstr_w(short_buf) : "***");
799 WARN("%s not found in '%s'\n", debugstr_w(name), path->long_name);
800 DOSFS_CloseDir( dir );
805 /***********************************************************************
808 * Check if a DOS file name represents a DOS device and return the device.
810 const DOS_DEVICE *DOSFS_GetDevice( LPCWSTR name )
815 if (!name) return NULL; /* if wine_server_handle_to_fd was used */
816 if (name[0] && (name[1] == ':')) name += 2;
817 if ((p = strrchrW( name, '/' ))) name = p + 1;
818 if ((p = strrchrW( name, '\\' ))) name = p + 1;
819 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
821 const WCHAR *dev = DOSFS_Devices[i].name;
822 if (!strncmpiW( dev, name, strlenW(dev) ))
824 p = name + strlenW( dev );
825 if (!*p || (*p == '.') || (*p == ':')) return &DOSFS_Devices[i];
832 /***********************************************************************
833 * DOSFS_GetDeviceByHandle
835 const DOS_DEVICE *DOSFS_GetDeviceByHandle( HANDLE hFile )
837 const DOS_DEVICE *ret = NULL;
838 SERVER_START_REQ( get_device_id )
841 if (!wine_server_call( req ))
843 if ((reply->id >= 0) &&
844 (reply->id < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0])))
845 ret = &DOSFS_Devices[reply->id];
853 /**************************************************************************
854 * DOSFS_CreateCommPort
856 static HANDLE DOSFS_CreateCommPort(LPCWSTR name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa)
861 OBJECT_ATTRIBUTES attr;
862 UNICODE_STRING nameW;
867 static const WCHAR serialportsW[] = {'M','a','c','h','i','n','e','\\',
868 'S','o','f','t','w','a','r','e','\\',
869 'W','i','n','e','\\','W','i','n','e','\\',
870 'C','o','n','f','i','g','\\',
871 'S','e','r','i','a','l','P','o','r','t','s',0};
873 TRACE_(file)("%s %lx %lx\n", debugstr_w(name), access, attributes);
875 attr.Length = sizeof(attr);
876 attr.RootDirectory = 0;
877 attr.ObjectName = &nameW;
879 attr.SecurityDescriptor = NULL;
880 attr.SecurityQualityOfService = NULL;
881 RtlInitUnicodeString( &nameW, serialportsW );
883 if (NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr )) return 0;
885 RtlInitUnicodeString( &nameW, name );
886 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
887 devnameW = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
893 if (!devnameW) return 0;
894 WideCharToMultiByte(CP_ACP, 0, devnameW, -1, devname, sizeof(devname), NULL, NULL);
896 TRACE("opening %s as %s\n", devname, debugstr_w(name));
898 SERVER_START_REQ( create_serial )
900 req->access = access;
901 req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
902 req->attributes = attributes;
903 req->sharing = FILE_SHARE_READ|FILE_SHARE_WRITE;
904 wine_server_add_data( req, devname, strlen(devname) );
906 wine_server_call_err( req );
912 ERR("Couldn't open device '%s' ! (check permissions)\n",devname);
914 TRACE("return %p\n", ret );
918 /***********************************************************************
921 * Open a DOS device. This might not map 1:1 into the UNIX device concept.
922 * Returns 0 on failure.
924 HANDLE DOSFS_OpenDevice( LPCWSTR name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa )
930 if (name[0] && (name[1] == ':')) name += 2;
931 if ((p = strrchrW( name, '/' ))) name = p + 1;
932 if ((p = strrchrW( name, '\\' ))) name = p + 1;
933 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
935 const WCHAR *dev = DOSFS_Devices[i].name;
936 if (!strncmpiW( dev, name, strlenW(dev) ))
938 p = name + strlenW( dev );
939 if (!*p || (*p == '.') || (*p == ':')) {
940 static const WCHAR nulW[] = {'N','U','L',0};
941 static const WCHAR conW[] = {'C','O','N',0};
942 static const WCHAR scsimgrW[] = {'S','C','S','I','M','G','R','$',0};
943 static const WCHAR hpscanW[] = {'H','P','S','C','A','N',0};
944 static const WCHAR emmxxxx0W[] = {'E','M','M','X','X','X','X','0',0};
946 if (!strcmpiW(DOSFS_Devices[i].name, nulW))
947 return FILE_CreateFile( "/dev/null", access,
948 FILE_SHARE_READ|FILE_SHARE_WRITE, sa,
949 OPEN_EXISTING, 0, 0, TRUE, DRIVE_UNKNOWN );
950 if (!strcmpiW(DOSFS_Devices[i].name, conW)) {
952 switch (access & (GENERIC_READ|GENERIC_WRITE)) {
954 to_dup = GetStdHandle( STD_INPUT_HANDLE );
957 to_dup = GetStdHandle( STD_OUTPUT_HANDLE );
960 FIXME("can't open CON read/write\n");
963 if (!DuplicateHandle( GetCurrentProcess(), to_dup, GetCurrentProcess(),
965 sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle,
966 DUPLICATE_SAME_ACCESS ))
970 if (!strcmpiW(DOSFS_Devices[i].name, scsimgrW) ||
971 !strcmpiW(DOSFS_Devices[i].name, hpscanW) ||
972 !strcmpiW(DOSFS_Devices[i].name, emmxxxx0W))
974 return FILE_CreateDevice( i, access, sa );
977 if( (handle=DOSFS_CreateCommPort(DOSFS_Devices[i].name,access,attributes,sa)) )
979 FIXME("device open %s not supported (yet)\n", debugstr_w(DOSFS_Devices[i].name));
988 /***********************************************************************
991 * Get the drive specified by a given path name (DOS or Unix format).
993 static int DOSFS_GetPathDrive( LPCWSTR *name )
998 if (*p && (p[1] == ':'))
1000 drive = toupperW(*p) - 'A';
1003 else if (*p == '/') /* Absolute Unix path? */
1005 if ((drive = DRIVE_FindDriveRootW( name )) == -1)
1007 MESSAGE("Warning: %s not accessible from a configured DOS drive\n", debugstr_w(*name) );
1008 /* Assume it really was a DOS name */
1009 drive = DRIVE_GetCurrentDrive();
1012 else drive = DRIVE_GetCurrentDrive();
1014 if (!DRIVE_IsValid(drive))
1016 SetLastError( ERROR_INVALID_DRIVE );
1023 /***********************************************************************
1026 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
1027 * Unix name / short DOS name pair.
1028 * Return FALSE if one of the path components does not exist. The last path
1029 * component is only checked if 'check_last' is non-zero.
1030 * The buffers pointed to by 'long_buf' and 'short_buf' must be
1031 * at least MAX_PATHNAME_LEN long.
1033 BOOL DOSFS_GetFullName( LPCWSTR name, BOOL check_last, DOS_FULL_NAME *full )
1039 static const WCHAR driveA_rootW[] = {'A',':','\\',0};
1040 static const WCHAR dos_rootW[] = {'\\',0};
1042 TRACE("%s (last=%d)\n", debugstr_w(name), check_last );
1044 if ((!*name) || (*name=='\n'))
1045 { /* error code for Win98 */
1046 SetLastError(ERROR_BAD_PATHNAME);
1050 if ((full->drive = DOSFS_GetPathDrive( &name )) == -1) return FALSE;
1051 flags = DRIVE_GetFlags( full->drive );
1053 lstrcpynA( full->long_name, DRIVE_GetRoot( full->drive ),
1054 sizeof(full->long_name) );
1055 if (full->long_name[1]) root = full->long_name + strlen(full->long_name);
1056 else root = full->long_name; /* root directory */
1058 strcpyW( full->short_name, driveA_rootW );
1059 full->short_name[0] += full->drive;
1061 if ((*name == '\\') || (*name == '/')) /* Absolute path */
1063 while ((*name == '\\') || (*name == '/')) name++;
1065 else /* Relative path */
1067 lstrcpynA( root + 1, DRIVE_GetUnixCwd( full->drive ),
1068 sizeof(full->long_name) - (root - full->long_name) - 1 );
1069 if (root[1]) *root = '/';
1070 lstrcpynW( full->short_name + 3, DRIVE_GetDosCwd( full->drive ),
1071 sizeof(full->short_name)/sizeof(full->short_name[0]) - 3 );
1074 p_l = full->long_name[1] ? full->long_name + strlen(full->long_name)
1076 p_s = full->short_name[3] ? full->short_name + strlenW(full->short_name)
1077 : full->short_name + 2;
1080 while (*name && found)
1082 /* Check for '.' and '..' */
1086 if (IS_END_OF_NAME(name[1]))
1089 while ((*name == '\\') || (*name == '/')) name++;
1092 else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
1095 while ((*name == '\\') || (*name == '/')) name++;
1096 while ((p_l > root) && (*p_l != '/')) p_l--;
1097 while ((p_s > full->short_name + 2) && (*p_s != '\\')) p_s--;
1098 *p_l = *p_s = '\0'; /* Remove trailing separator */
1103 /* Make sure buffers are large enough */
1105 if ((p_s >= full->short_name + sizeof(full->short_name)/sizeof(full->short_name[0]) - 14) ||
1106 (p_l >= full->long_name + sizeof(full->long_name) - 1))
1108 SetLastError( ERROR_PATH_NOT_FOUND );
1112 /* Get the long and short name matching the file name */
1114 if ((found = DOSFS_FindUnixName( full, name, p_l + 1,
1115 sizeof(full->long_name) - (p_l - full->long_name) - 1,
1116 p_s + 1, !(flags & DRIVE_CASE_SENSITIVE) )))
1121 p_s += strlenW(p_s);
1122 while (!IS_END_OF_NAME(*name)) name++;
1124 else if (!check_last)
1128 while (!IS_END_OF_NAME(*name) &&
1129 (p_s < full->short_name + sizeof(full->short_name)/sizeof(full->short_name[0]) - 1) &&
1130 (p_l < full->long_name + sizeof(full->long_name) - 1))
1133 *p_s++ = tolowerW(*name);
1134 /* If the drive is case-sensitive we want to create new */
1135 /* files in lower-case otherwise we can't reopen them */
1136 /* under the same short name. */
1137 if (flags & DRIVE_CASE_SENSITIVE) wch = tolowerW(*name);
1139 p_l += WideCharToMultiByte(CP_UNIXCP, 0, &wch, 1, p_l, 2, NULL, NULL);
1142 /* Ignore trailing dots and spaces */
1143 while(p_l[-1] == '.' || p_l[-1] == ' ') {
1150 while ((*name == '\\') || (*name == '/')) name++;
1157 SetLastError( ERROR_FILE_NOT_FOUND );
1160 if (*name) /* Not last */
1162 SetLastError( ERROR_PATH_NOT_FOUND );
1166 if (!full->long_name[0]) strcpy( full->long_name, "/" );
1167 if (!full->short_name[2]) strcpyW( full->short_name + 2, dos_rootW );
1168 TRACE("returning %s = %s\n", full->long_name, debugstr_w(full->short_name) );
1173 /***********************************************************************
1174 * GetShortPathNameW (KERNEL32.@)
1178 * longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1179 * longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0
1181 * more observations ( with NT 3.51 (WinDD) ):
1182 * longpath <= 8.3 -> just copy longpath to shortpath
1184 * a) file does not exist -> return 0, LastError = ERROR_FILE_NOT_FOUND
1185 * b) file does exist -> set the short filename.
1186 * - trailing slashes are reproduced in the short name, even if the
1187 * file is not a directory
1188 * - the absolute/relative path of the short name is reproduced like found
1190 * - longpath and shortpath may have the same address
1191 * Peter Ganten, 1999
1193 DWORD WINAPI GetShortPathNameW( LPCWSTR longpath, LPWSTR shortpath, DWORD shortlen )
1195 DOS_FULL_NAME full_name;
1196 WCHAR tmpshortpath[MAX_PATHNAME_LEN];
1198 DWORD sp = 0, lp = 0;
1202 BOOL unixabsolute = *longpath == '/';
1204 TRACE("%s\n", debugstr_w(longpath));
1207 SetLastError(ERROR_INVALID_PARAMETER);
1211 SetLastError(ERROR_BAD_PATHNAME);
1215 /* check for drive letter */
1216 if (!unixabsolute && longpath[1] == ':' ) {
1217 tmpshortpath[0] = longpath[0];
1218 tmpshortpath[1] = ':';
1222 if ( ( drive = DOSFS_GetPathDrive ( &longpath )) == -1 ) return 0;
1223 flags = DRIVE_GetFlags ( drive );
1225 if (unixabsolute && drive != DRIVE_GetCurrentDrive()) {
1226 tmpshortpath[0] = drive + 'A';
1227 tmpshortpath[1] = ':';
1231 while ( longpath[lp] ) {
1233 /* check for path delimiters and reproduce them */
1234 if ( longpath[lp] == '\\' || longpath[lp] == '/' ) {
1235 if (!sp || tmpshortpath[sp-1]!= '\\')
1237 /* strip double "\\" */
1238 tmpshortpath[sp] = '\\';
1241 tmpshortpath[sp]=0;/*terminate string*/
1247 for(p = longpath + lp; *p && *p != '/' && *p != '\\'; p++)
1249 lstrcpynW(tmpshortpath + sp, longpath + lp, tmplen + 1);
1251 /* Check, if the current element is a valid dos name */
1252 if ( DOSFS_ValidDOSName ( longpath + lp, !(flags & DRIVE_CASE_SENSITIVE) ) ) {
1258 /* Check if the file exists and use the existing file name */
1259 if ( DOSFS_GetFullName ( tmpshortpath, TRUE, &full_name ) ) {
1260 strcpyW(tmpshortpath + sp, strrchrW(full_name.short_name, '\\') + 1);
1261 sp += strlenW(tmpshortpath + sp);
1266 TRACE("not found!\n" );
1267 SetLastError ( ERROR_FILE_NOT_FOUND );
1270 tmpshortpath[sp] = 0;
1272 tmplen = strlenW(tmpshortpath) + 1;
1273 if (tmplen <= shortlen)
1275 strcpyW(shortpath, tmpshortpath);
1276 TRACE("returning %s\n", debugstr_w(shortpath));
1277 tmplen--; /* length without 0 */
1284 /***********************************************************************
1285 * GetShortPathNameA (KERNEL32.@)
1287 DWORD WINAPI GetShortPathNameA( LPCSTR longpath, LPSTR shortpath, DWORD shortlen )
1289 UNICODE_STRING longpathW;
1290 WCHAR shortpathW[MAX_PATH];
1295 SetLastError(ERROR_INVALID_PARAMETER);
1299 TRACE("%s\n", debugstr_a(longpath));
1301 if (!RtlCreateUnicodeStringFromAsciiz(&longpathW, longpath))
1303 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1307 retW = GetShortPathNameW(longpathW.Buffer, shortpathW, MAX_PATH);
1311 else if (retW > MAX_PATH)
1313 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1318 ret = WideCharToMultiByte(CP_ACP, 0, shortpathW, -1, NULL, 0, NULL, NULL);
1319 if (ret <= shortlen)
1321 WideCharToMultiByte(CP_ACP, 0, shortpathW, -1, shortpath, shortlen, NULL, NULL);
1322 ret--; /* length without 0 */
1326 RtlFreeUnicodeString(&longpathW);
1331 /***********************************************************************
1332 * GetLongPathNameW (KERNEL32.@)
1335 * observed (Win2000):
1336 * shortpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1337 * shortpath="": LastError=ERROR_PATH_NOT_FOUND, ret=0
1339 DWORD WINAPI GetLongPathNameW( LPCWSTR shortpath, LPWSTR longpath, DWORD longlen )
1341 DOS_FULL_NAME full_name;
1348 SetLastError(ERROR_INVALID_PARAMETER);
1351 if (!shortpath[0]) {
1352 SetLastError(ERROR_PATH_NOT_FOUND);
1356 TRACE("%s,%p,%ld\n", debugstr_w(shortpath), longpath, longlen);
1358 if(shortpath[0]=='\\' && shortpath[1]=='\\')
1360 ERR("UNC pathname %s\n",debugstr_w(shortpath));
1361 lstrcpynW( longpath, full_name.short_name, longlen );
1362 return strlenW(longpath);
1365 if (!DOSFS_GetFullName( shortpath, TRUE, &full_name )) return 0;
1367 root = full_name.long_name;
1368 drive = DRIVE_FindDriveRoot(&root);
1370 ret = MultiByteToWideChar(CP_UNIXCP, 0, root, -1, NULL, 0);
1372 /* reproduce terminating slash */
1373 if (ret > 4) /* if not drive root */
1375 len = strlenW(shortpath);
1376 if (shortpath[len - 1] == '\\' || shortpath[len - 1] == '/')
1382 longpath[0] = 'A' + drive;
1384 MultiByteToWideChar(CP_UNIXCP, 0, root, -1, longpath + 2, longlen - 2);
1385 for (p = longpath; *p; p++) if (*p == '/') *p = '\\';
1388 longpath[ret - 2] = '\\';
1389 longpath[ret - 1] = 0;
1391 TRACE("returning %s\n", debugstr_w(longpath));
1392 ret--; /* length without 0 */
1398 /***********************************************************************
1399 * GetLongPathNameA (KERNEL32.@)
1401 DWORD WINAPI GetLongPathNameA( LPCSTR shortpath, LPSTR longpath, DWORD longlen )
1403 UNICODE_STRING shortpathW;
1404 WCHAR longpathW[MAX_PATH];
1409 SetLastError(ERROR_INVALID_PARAMETER);
1413 TRACE("%s\n", debugstr_a(shortpath));
1415 if (!RtlCreateUnicodeStringFromAsciiz(&shortpathW, shortpath))
1417 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1421 retW = GetLongPathNameW(shortpathW.Buffer, longpathW, MAX_PATH);
1425 else if (retW > MAX_PATH)
1427 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1432 ret = WideCharToMultiByte(CP_ACP, 0, longpathW, -1, NULL, 0, NULL, NULL);
1435 WideCharToMultiByte(CP_ACP, 0, longpathW, -1, longpath, longlen, NULL, NULL);
1436 ret--; /* length without 0 */
1440 RtlFreeUnicodeString(&shortpathW);
1445 /***********************************************************************
1446 * DOSFS_DoGetFullPathName
1448 * Implementation of GetFullPathNameA/W.
1450 * bon@elektron 000331:
1451 * A test for GetFullPathName with many pathological cases
1452 * now gives identical output for Wine and OSR2
1454 static DWORD DOSFS_DoGetFullPathName( LPCWSTR name, DWORD len, LPWSTR result )
1457 DOS_FULL_NAME full_name;
1461 WCHAR drivecur[] = {'C',':','.',0};
1462 WCHAR driveletter=0;
1463 int namelen,drive=0;
1464 static const WCHAR bkslashW[] = {'\\',0};
1465 static const WCHAR dotW[] = {'.',0};
1466 static const WCHAR updir_slashW[] = {'\\','.','.','\\',0};
1467 static const WCHAR curdirW[] = {'\\','.','\\',0};
1468 static const WCHAR updirW[] = {'\\','.','.',0};
1472 SetLastError(ERROR_BAD_PATHNAME);
1476 TRACE("passed %s\n", debugstr_w(name));
1479 /*drive letter given */
1481 driveletter = name[0];
1483 if ((name[1]==':') && ((name[2]=='\\') || (name[2]=='/')))
1484 /*absolute path given */
1486 strncpyW(full_name.short_name, name, MAX_PATHNAME_LEN);
1487 full_name.short_name[MAX_PATHNAME_LEN - 1] = 0; /* ensure 0 termination */
1488 drive = toupperW(name[0]) - 'A';
1493 drivecur[0]=driveletter;
1494 else if ((name[0]=='\\') || (name[0]=='/'))
1495 strcpyW(drivecur, bkslashW);
1497 strcpyW(drivecur, dotW);
1499 if (!DOSFS_GetFullName( drivecur, FALSE, &full_name ))
1501 FIXME("internal: error getting drive/path\n");
1504 /* find path that drive letter substitutes*/
1505 drive = toupperW(full_name.short_name[0]) - 'A';
1506 root= DRIVE_GetRoot(drive);
1509 FIXME("internal: error getting DOS Drive Root\n");
1512 if (!strcmp(root,"/"))
1514 /* we have just the last / and we need it. */
1515 p_l = full_name.long_name;
1519 p_l = full_name.long_name + strlen(root);
1521 /* append long name (= unix name) to drive */
1522 MultiByteToWideChar(CP_UNIXCP, 0, p_l, -1, full_name.short_name + 2, MAX_PATHNAME_LEN - 3);
1523 /* append name to treat */
1524 namelen= strlenW(full_name.short_name);
1527 p += 2; /* skip drive name when appending */
1528 if (namelen + 2 + strlenW(p) > MAX_PATHNAME_LEN)
1530 FIXME("internal error: buffer too small\n");
1533 full_name.short_name[namelen++] ='\\';
1534 full_name.short_name[namelen] = 0;
1535 strncpyW(full_name.short_name + namelen, p, MAX_PATHNAME_LEN - namelen);
1536 full_name.short_name[MAX_PATHNAME_LEN - 1] = 0; /* ensure 0 termination */
1538 /* reverse all slashes */
1539 for (p=full_name.short_name;
1540 p < full_name.short_name + strlenW(full_name.short_name);
1546 /* Use memmove, as areas overlap */
1548 while ((p = strstrW(full_name.short_name, updir_slashW)))
1550 if (p > full_name.short_name+2)
1553 q = strrchrW(full_name.short_name, '\\');
1554 memmove(q+1, p+4, (strlenW(p+4)+1) * sizeof(WCHAR));
1558 memmove(full_name.short_name+3, p+4, (strlenW(p+4)+1) * sizeof(WCHAR));
1561 if ((full_name.short_name[2]=='.')&&(full_name.short_name[3]=='.'))
1563 /* This case istn't treated yet : c:..\test */
1564 memmove(full_name.short_name+2,full_name.short_name+4,
1565 (strlenW(full_name.short_name+4)+1) * sizeof(WCHAR));
1568 while ((p = strstrW(full_name.short_name, curdirW)))
1571 memmove(p+1, p+3, (strlenW(p+3)+1) * sizeof(WCHAR));
1573 if (!(DRIVE_GetFlags(drive) & DRIVE_CASE_PRESERVING))
1574 for (p = full_name.short_name; *p; p++) *p = toupperW(*p);
1575 namelen = strlenW(full_name.short_name);
1576 if (!strcmpW(full_name.short_name+namelen-3, updirW))
1578 /* one more strange case: "c:\test\test1\.."
1580 *(full_name.short_name+namelen-3)=0;
1581 q = strrchrW(full_name.short_name, '\\');
1584 if (full_name.short_name[namelen-1]=='.')
1585 full_name.short_name[(namelen--)-1] =0;
1587 if (full_name.short_name[namelen-1]=='\\')
1588 full_name.short_name[(namelen--)-1] =0;
1589 TRACE("got %s\n", debugstr_w(full_name.short_name));
1591 /* If the lpBuffer buffer is too small, the return value is the
1592 size of the buffer, in characters, required to hold the path
1593 plus the terminating \0 (tested against win95osr2, bon 001118)
1595 ret = strlenW(full_name.short_name);
1598 /* don't touch anything when the buffer is not large enough */
1599 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1604 strncpyW( result, full_name.short_name, len );
1605 result[len - 1] = 0; /* ensure 0 termination */
1608 TRACE("returning %s\n", debugstr_w(full_name.short_name) );
1613 /***********************************************************************
1614 * GetFullPathNameA (KERNEL32.@)
1616 * if the path closed with '\', *lastpart is 0
1618 DWORD WINAPI GetFullPathNameA( LPCSTR name, DWORD len, LPSTR buffer,
1621 UNICODE_STRING nameW;
1622 WCHAR bufferW[MAX_PATH];
1627 SetLastError(ERROR_INVALID_PARAMETER);
1631 if (!RtlCreateUnicodeStringFromAsciiz(&nameW, name))
1633 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1637 retW = GetFullPathNameW( nameW.Buffer, MAX_PATH, bufferW, NULL);
1641 else if (retW > MAX_PATH)
1643 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1648 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL);
1651 WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, len, NULL, NULL);
1652 ret--; /* length without 0 */
1656 LPSTR p = buffer + strlen(buffer);
1660 while ((p > buffer + 2) && (*p != '\\')) p--;
1663 else *lastpart = NULL;
1668 RtlFreeUnicodeString(&nameW);
1673 /***********************************************************************
1674 * GetFullPathNameW (KERNEL32.@)
1676 DWORD WINAPI GetFullPathNameW( LPCWSTR name, DWORD len, LPWSTR buffer,
1679 DWORD ret = DOSFS_DoGetFullPathName( name, len, buffer );
1680 if (ret && (ret<=len) && buffer && lastpart)
1682 LPWSTR p = buffer + strlenW(buffer);
1683 if (*p != (WCHAR)'\\')
1685 while ((p > buffer + 2) && (*p != (WCHAR)'\\')) p--;
1688 else *lastpart = NULL;
1694 /***********************************************************************
1695 * wine_get_unix_file_name (KERNEL32.@) Not a Windows API
1697 * Return the full Unix file name for a given path.
1698 * FIXME: convert dos file name to unicode
1700 BOOL WINAPI wine_get_unix_file_name( LPCSTR dos, LPSTR buffer, DWORD len )
1704 WCHAR dosW[MAX_PATHNAME_LEN];
1706 MultiByteToWideChar(CP_ACP, 0, dos, -1, dosW, MAX_PATHNAME_LEN);
1707 ret = DOSFS_GetFullName( dosW, FALSE, &path );
1710 strncpy( buffer, path.long_name, len );
1711 buffer[len - 1] = 0; /* ensure 0 termination */
1717 /***********************************************************************
1718 * get_show_dir_symlinks_option
1720 static BOOL get_show_dir_symlinks_option(void)
1722 static const WCHAR WineW[] = {'M','a','c','h','i','n','e','\\',
1723 'S','o','f','t','w','a','r','e','\\',
1724 'W','i','n','e','\\','W','i','n','e','\\',
1725 'C','o','n','f','i','g','\\','W','i','n','e',0};
1726 static const WCHAR ShowDirSymlinksW[] = {'S','h','o','w','D','i','r','S','y','m','l','i','n','k','s',0};
1731 OBJECT_ATTRIBUTES attr;
1732 UNICODE_STRING nameW;
1735 attr.Length = sizeof(attr);
1736 attr.RootDirectory = 0;
1737 attr.ObjectName = &nameW;
1738 attr.Attributes = 0;
1739 attr.SecurityDescriptor = NULL;
1740 attr.SecurityQualityOfService = NULL;
1741 RtlInitUnicodeString( &nameW, WineW );
1743 if (!NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr ))
1745 RtlInitUnicodeString( &nameW, ShowDirSymlinksW );
1746 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
1748 WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
1749 ret = IS_OPTION_TRUE( str[0] );
1757 /***********************************************************************
1760 static int DOSFS_FindNextEx( FIND_FIRST_INFO *info, WIN32_FIND_DATAW *entry )
1762 UINT flags = DRIVE_GetFlags( info->drive );
1763 char *p, buffer[MAX_PATHNAME_LEN];
1764 const char *drive_path;
1766 LPCWSTR long_name, short_name;
1767 BY_HANDLE_FILE_INFORMATION fileinfo;
1770 drive_path = info->path + strlen(DRIVE_GetRoot( info->drive ));
1771 while ((*drive_path == '/') || (*drive_path == '\\')) drive_path++;
1772 drive_root = !*drive_path;
1774 lstrcpynA( buffer, info->path, sizeof(buffer) - 1 );
1775 strcat( buffer, "/" );
1776 p = buffer + strlen(buffer);
1778 while (DOSFS_ReadDir( info->u.dos_dir, &long_name, &short_name ))
1782 /* Don't return '.' and '..' in the root of the drive */
1783 if (drive_root && (long_name[0] == '.') &&
1784 (!long_name[1] || ((long_name[1] == '.') && !long_name[2])))
1787 /* Check the long mask */
1789 if (info->long_mask && *info->long_mask)
1791 if (!DOSFS_MatchLong( info->long_mask, long_name,
1792 flags & DRIVE_CASE_SENSITIVE )) continue;
1795 /* Check the file attributes */
1796 WideCharToMultiByte(CP_UNIXCP, 0, long_name, -1,
1797 p, sizeof(buffer) - (int)(p - buffer), NULL, NULL);
1798 if (!FILE_Stat( buffer, &fileinfo, &is_symlink ))
1800 WARN("can't stat %s\n", buffer);
1803 if (is_symlink && (fileinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
1805 static int show_dir_symlinks = -1;
1806 if (show_dir_symlinks == -1)
1807 show_dir_symlinks = get_show_dir_symlinks_option();
1808 if (!show_dir_symlinks) continue;
1811 /* We now have a matching entry; fill the result and return */
1813 entry->dwFileAttributes = fileinfo.dwFileAttributes;
1814 entry->ftCreationTime = fileinfo.ftCreationTime;
1815 entry->ftLastAccessTime = fileinfo.ftLastAccessTime;
1816 entry->ftLastWriteTime = fileinfo.ftLastWriteTime;
1817 entry->nFileSizeHigh = fileinfo.nFileSizeHigh;
1818 entry->nFileSizeLow = fileinfo.nFileSizeLow;
1821 DOSFS_ToDosDTAFormat( short_name, entry->cAlternateFileName );
1823 DOSFS_Hash( long_name, entry->cAlternateFileName, FALSE,
1824 !(flags & DRIVE_CASE_SENSITIVE) );
1826 lstrcpynW( entry->cFileName, long_name, sizeof(entry->cFileName)/sizeof(entry->cFileName[0]) );
1827 if (!(flags & DRIVE_CASE_PRESERVING)) strlwrW( entry->cFileName );
1828 TRACE("returning %s (%s) %02lx %ld\n",
1829 debugstr_w(entry->cFileName), debugstr_w(entry->cAlternateFileName),
1830 entry->dwFileAttributes, entry->nFileSizeLow );
1833 return 0; /* End of directory */
1836 /*************************************************************************
1837 * FindFirstFileExW (KERNEL32.@)
1839 HANDLE WINAPI FindFirstFileExW(
1841 FINDEX_INFO_LEVELS fInfoLevelId,
1842 LPVOID lpFindFileData,
1843 FINDEX_SEARCH_OPS fSearchOp,
1844 LPVOID lpSearchFilter,
1845 DWORD dwAdditionalFlags)
1847 FIND_FIRST_INFO *info;
1851 SetLastError(ERROR_PATH_NOT_FOUND);
1852 return INVALID_HANDLE_VALUE;
1855 if ((fSearchOp != FindExSearchNameMatch) || (dwAdditionalFlags != 0))
1857 FIXME("options not implemented 0x%08x 0x%08lx\n", fSearchOp, dwAdditionalFlags );
1858 return INVALID_HANDLE_VALUE;
1861 switch(fInfoLevelId)
1863 case FindExInfoStandard:
1865 WIN32_FIND_DATAW * data = (WIN32_FIND_DATAW *) lpFindFileData;
1869 data->dwReserved0 = data->dwReserved1 = 0x0;
1870 if (lpFileName[0] == '\\' && lpFileName[1] == '\\')
1872 ERR("UNC path name\n");
1873 if (!(info = HeapAlloc( GetProcessHeap(), 0, sizeof(FIND_FIRST_INFO)))) break;
1874 info->u.smb_dir = SMB_FindFirst(lpFileName);
1875 if(!info->u.smb_dir)
1877 HeapFree(GetProcessHeap(), 0, info);
1881 RtlInitializeCriticalSection( &info->cs );
1885 DOS_FULL_NAME full_name;
1887 if (lpFileName[0] && lpFileName[1] == ':')
1889 /* don't allow root directories */
1890 if (!lpFileName[2] ||
1891 ((lpFileName[2] == '/' || lpFileName[2] == '\\') && !lpFileName[3]))
1893 SetLastError(ERROR_FILE_NOT_FOUND);
1894 return INVALID_HANDLE_VALUE;
1897 if (!DOSFS_GetFullName( lpFileName, FALSE, &full_name )) break;
1898 if (!(info = HeapAlloc( GetProcessHeap(), 0, sizeof(FIND_FIRST_INFO)))) break;
1899 RtlInitializeCriticalSection( &info->cs );
1900 info->path = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
1901 strcpy( info->path, full_name.long_name );
1903 p = strrchr( info->path, '/' );
1905 long_mask_len = MultiByteToWideChar(CP_UNIXCP, 0, p, -1, NULL, 0);
1906 info->long_mask = HeapAlloc( GetProcessHeap(), 0, long_mask_len * sizeof(WCHAR) );
1907 MultiByteToWideChar(CP_UNIXCP, 0, p, -1, info->long_mask, long_mask_len);
1909 info->drive = full_name.drive;
1912 info->u.dos_dir = DOSFS_OpenDir( info->path );
1914 if (!FindNextFileW( (HANDLE) info, data ))
1916 FindClose( (HANDLE) info );
1917 SetLastError( ERROR_FILE_NOT_FOUND );
1920 return (HANDLE) info;
1924 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
1926 return INVALID_HANDLE_VALUE;
1929 /*************************************************************************
1930 * FindFirstFileA (KERNEL32.@)
1932 HANDLE WINAPI FindFirstFileA(
1934 WIN32_FIND_DATAA *lpFindData )
1936 return FindFirstFileExA(lpFileName, FindExInfoStandard, lpFindData,
1937 FindExSearchNameMatch, NULL, 0);
1940 /*************************************************************************
1941 * FindFirstFileExA (KERNEL32.@)
1943 HANDLE WINAPI FindFirstFileExA(
1945 FINDEX_INFO_LEVELS fInfoLevelId,
1946 LPVOID lpFindFileData,
1947 FINDEX_SEARCH_OPS fSearchOp,
1948 LPVOID lpSearchFilter,
1949 DWORD dwAdditionalFlags)
1952 WIN32_FIND_DATAA *dataA;
1953 WIN32_FIND_DATAW dataW;
1954 UNICODE_STRING pathW;
1958 SetLastError(ERROR_PATH_NOT_FOUND);
1959 return INVALID_HANDLE_VALUE;
1962 if (!RtlCreateUnicodeStringFromAsciiz(&pathW, lpFileName))
1964 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1965 return INVALID_HANDLE_VALUE;
1968 handle = FindFirstFileExW(pathW.Buffer, fInfoLevelId, &dataW, fSearchOp, lpSearchFilter, dwAdditionalFlags);
1969 RtlFreeUnicodeString(&pathW);
1970 if (handle == INVALID_HANDLE_VALUE) return handle;
1972 dataA = (WIN32_FIND_DATAA *) lpFindFileData;
1973 dataA->dwFileAttributes = dataW.dwFileAttributes;
1974 dataA->ftCreationTime = dataW.ftCreationTime;
1975 dataA->ftLastAccessTime = dataW.ftLastAccessTime;
1976 dataA->ftLastWriteTime = dataW.ftLastWriteTime;
1977 dataA->nFileSizeHigh = dataW.nFileSizeHigh;
1978 dataA->nFileSizeLow = dataW.nFileSizeLow;
1979 WideCharToMultiByte( CP_ACP, 0, dataW.cFileName, -1,
1980 dataA->cFileName, sizeof(dataA->cFileName), NULL, NULL );
1981 WideCharToMultiByte( CP_ACP, 0, dataW.cAlternateFileName, -1,
1982 dataA->cAlternateFileName, sizeof(dataA->cAlternateFileName), NULL, NULL );
1986 /*************************************************************************
1987 * FindFirstFileW (KERNEL32.@)
1989 HANDLE WINAPI FindFirstFileW( LPCWSTR lpFileName, WIN32_FIND_DATAW *lpFindData )
1991 return FindFirstFileExW(lpFileName, FindExInfoStandard, lpFindData,
1992 FindExSearchNameMatch, NULL, 0);
1995 /*************************************************************************
1996 * FindNextFileW (KERNEL32.@)
1998 BOOL WINAPI FindNextFileW( HANDLE handle, WIN32_FIND_DATAW *data )
2000 FIND_FIRST_INFO *info;
2002 DWORD gle = ERROR_NO_MORE_FILES;
2004 if (handle == INVALID_HANDLE_VALUE)
2006 SetLastError( ERROR_INVALID_HANDLE );
2009 info = (FIND_FIRST_INFO*) handle;
2010 RtlEnterCriticalSection( &info->cs );
2011 if (info->drive == -1)
2013 ret = SMB_FindNext( info->u.smb_dir, data );
2016 SMB_CloseDir( info->u.smb_dir );
2017 HeapFree( GetProcessHeap(), 0, info->path );
2021 else if (!info->path || !info->u.dos_dir)
2025 else if (!DOSFS_FindNextEx( info, data ))
2027 DOSFS_CloseDir( info->u.dos_dir ); info->u.dos_dir = NULL;
2028 HeapFree( GetProcessHeap(), 0, info->path );
2030 HeapFree( GetProcessHeap(), 0, info->long_mask );
2031 info->long_mask = NULL;
2036 RtlLeaveCriticalSection( &info->cs );
2037 if( !ret ) SetLastError( gle );
2042 /*************************************************************************
2043 * FindNextFileA (KERNEL32.@)
2045 BOOL WINAPI FindNextFileA( HANDLE handle, WIN32_FIND_DATAA *data )
2047 WIN32_FIND_DATAW dataW;
2048 if (!FindNextFileW( handle, &dataW )) return FALSE;
2049 data->dwFileAttributes = dataW.dwFileAttributes;
2050 data->ftCreationTime = dataW.ftCreationTime;
2051 data->ftLastAccessTime = dataW.ftLastAccessTime;
2052 data->ftLastWriteTime = dataW.ftLastWriteTime;
2053 data->nFileSizeHigh = dataW.nFileSizeHigh;
2054 data->nFileSizeLow = dataW.nFileSizeLow;
2055 WideCharToMultiByte( CP_ACP, 0, dataW.cFileName, -1,
2056 data->cFileName, sizeof(data->cFileName), NULL, NULL );
2057 WideCharToMultiByte( CP_ACP, 0, dataW.cAlternateFileName, -1,
2058 data->cAlternateFileName,
2059 sizeof(data->cAlternateFileName), NULL, NULL );
2063 /*************************************************************************
2064 * FindClose (KERNEL32.@)
2066 BOOL WINAPI FindClose( HANDLE handle )
2068 FIND_FIRST_INFO *info = (FIND_FIRST_INFO*) handle;
2070 if (handle == INVALID_HANDLE_VALUE) goto error;
2074 RtlEnterCriticalSection( &info->cs );
2077 if (info->u.dos_dir) DOSFS_CloseDir( info->u.dos_dir );
2078 if (info->path) HeapFree( GetProcessHeap(), 0, info->path );
2079 if (info->long_mask) HeapFree( GetProcessHeap(), 0, info->long_mask );
2082 __EXCEPT(page_fault)
2084 WARN("Illegal handle %p\n", handle);
2085 SetLastError( ERROR_INVALID_HANDLE );
2089 if (!info) goto error;
2090 RtlLeaveCriticalSection( &info->cs );
2091 RtlDeleteCriticalSection( &info->cs );
2092 HeapFree(GetProcessHeap(), 0, info);
2096 SetLastError( ERROR_INVALID_HANDLE );
2100 /***********************************************************************
2101 * MulDiv (KERNEL32.@)
2103 * Result of multiplication and division
2104 * -1: Overflow occurred or Divisor was 0
2111 #if SIZEOF_LONG_LONG >= 8
2114 if (!nDivisor) return -1;
2116 /* We want to deal with a positive divisor to simplify the logic. */
2119 nMultiplicand = - nMultiplicand;
2120 nDivisor = -nDivisor;
2123 /* If the result is positive, we "add" to round. else, we subtract to round. */
2124 if ( ( (nMultiplicand < 0) && (nMultiplier < 0) ) ||
2125 ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
2126 ret = (((long long)nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
2128 ret = (((long long)nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
2130 if ((ret > 2147483647) || (ret < -2147483647)) return -1;
2133 if (!nDivisor) return -1;
2135 /* We want to deal with a positive divisor to simplify the logic. */
2138 nMultiplicand = - nMultiplicand;
2139 nDivisor = -nDivisor;
2142 /* If the result is positive, we "add" to round. else, we subtract to round. */
2143 if ( ( (nMultiplicand < 0) && (nMultiplier < 0) ) ||
2144 ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
2145 return ((nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
2147 return ((nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
2153 /***********************************************************************
2154 * DosDateTimeToFileTime (KERNEL32.@)
2156 BOOL WINAPI DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft)
2161 time_t time1, time2;
2164 newtm.tm_sec = (fattime & 0x1f) * 2;
2165 newtm.tm_min = (fattime >> 5) & 0x3f;
2166 newtm.tm_hour = (fattime >> 11);
2167 newtm.tm_mday = (fatdate & 0x1f);
2168 newtm.tm_mon = ((fatdate >> 5) & 0x0f) - 1;
2169 newtm.tm_year = (fatdate >> 9) + 80;
2171 RtlSecondsSince1970ToTime( timegm(&newtm), (LARGE_INTEGER *)ft );
2173 time1 = mktime(&newtm);
2174 gtm = gmtime(&time1);
2175 time2 = mktime(gtm);
2176 RtlSecondsSince1970ToTime( 2*time1-time2, (LARGE_INTEGER *)ft );
2182 /***********************************************************************
2183 * FileTimeToDosDateTime (KERNEL32.@)
2185 BOOL WINAPI FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate,
2193 li.s.LowPart = ft->dwLowDateTime;
2194 li.s.HighPart = ft->dwHighDateTime;
2195 RtlTimeToSecondsSince1970( &li, &t );
2197 tm = gmtime( &unixtime );
2199 *fattime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
2201 *fatdate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
2207 /***********************************************************************
2208 * QueryDosDeviceA (KERNEL32.@)
2210 * returns array of strings terminated by \0, terminated by \0
2212 DWORD WINAPI QueryDosDeviceA(LPCSTR devname,LPSTR target,DWORD bufsize)
2214 DWORD ret = 0, retW;
2215 LPWSTR targetW = (LPWSTR)HeapAlloc(GetProcessHeap(),0,
2216 bufsize * sizeof(WCHAR));
2217 UNICODE_STRING devnameW;
2219 if(devname) RtlCreateUnicodeStringFromAsciiz(&devnameW, devname);
2220 else devnameW.Buffer = NULL;
2222 retW = QueryDosDeviceW(devnameW.Buffer, targetW, bufsize);
2224 ret = WideCharToMultiByte(CP_ACP, 0, targetW, retW, target,
2225 bufsize, NULL, NULL);
2227 RtlFreeUnicodeString(&devnameW);
2228 if (targetW) HeapFree(GetProcessHeap(),0,targetW);
2233 /***********************************************************************
2234 * QueryDosDeviceW (KERNEL32.@)
2236 * returns array of strings terminated by \0, terminated by \0
2239 * - Win9x returns for all calls ERROR_INVALID_PARAMETER
2240 * - the returned devices for devname == NULL is far from complete
2241 * - its not checked that the returned device exist
2243 DWORD WINAPI QueryDosDeviceW(LPCWSTR devname,LPWSTR target,DWORD bufsize)
2245 const WCHAR *pDev, *pName, *pNum = NULL;
2249 TRACE("(%s,...)\n", debugstr_w(devname));
2251 /* return known MSDOS devices */
2254 static const WCHAR devices[][5] = {{'A','U','X',0},
2255 {'C','O','M','1',0},
2256 {'C','O','M','2',0},
2257 {'L','P','T','1',0},
2259 for(i=0; (i< (sizeof(devices)/sizeof(devices[0]))); i++) {
2260 DWORD len = strlenW(devices[i]);
2261 if(target && (bufsize >= ret + len + 2)) {
2262 strcpyW(target+ret, devices[i]);
2265 /* in this case WinXP returns 0 */
2266 FIXME("function return is wrong for WinXP!\n");
2267 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2271 /* append drives here */
2272 if(target && bufsize > 0) target[ret++] = 0;
2273 FIXME("Returned list is not complete\n");
2276 /* In theory all that are possible and have been defined.
2277 * Now just those below, since mirc uses it to check for special files.
2279 * (It is more complex, and supports netmounted stuff, and \\.\ stuff,
2280 * but currently we just ignore that.)
2282 if (!strcmpiW(devname, auxW)) {
2287 } else if (!strcmpiW(devname, nulW)) {
2290 } else if (!strncmpiW(devname, comW, strlenW(comW))) {
2293 pNum = devname + strlenW(comW);
2294 for(numsiz=0; isdigitW(*(pNum+numsiz)); numsiz++);
2295 if(*(pNum + numsiz)) {
2296 SetLastError(ERROR_FILE_NOT_FOUND);
2299 } else if (!strncmpiW(devname, lptW, strlenW(lptW))) {
2302 pNum = devname + strlenW(lptW);
2303 for(numsiz=0; isdigitW(*(pNum+numsiz)); numsiz++);
2304 if(*(pNum + numsiz)) {
2305 SetLastError(ERROR_FILE_NOT_FOUND);
2309 /* This might be a DOS device we do not handle yet ... */
2310 FIXME("(%s) not detected as DOS device!\n",debugstr_w(devname));
2312 /* Win9x set the error ERROR_INVALID_PARAMETER */
2313 SetLastError(ERROR_FILE_NOT_FOUND);
2316 FIXME("device %s may not exist on this computer\n", debugstr_w(devname));
2318 ret = strlenW(pDev) + strlenW(pName) + numsiz + 2;
2319 if (ret > bufsize) ret = 0;
2320 if (target && ret) {
2321 strcpyW(target,pDev);
2322 strcatW(target,pName);
2323 if (pNum) strcatW(target,pNum);