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>
35 #ifdef HAVE_SYS_IOCTL_H
36 #include <sys/ioctl.h>
47 #include "wine/unicode.h"
48 #include "wine/winbase16.h"
54 #include "wine/server.h"
59 #include "wine/debug.h"
61 WINE_DEFAULT_DEBUG_CHANNEL(dosfs);
62 WINE_DECLARE_DEBUG_CHANNEL(file);
64 /* Define the VFAT ioctl to get both short and long file names */
65 /* FIXME: is it possible to get this to work on other systems? */
67 /* We want the real kernel dirent structure, not the libc one */
72 unsigned short d_reclen;
76 #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, KERNEL_DIRENT [2] )
79 #undef VFAT_IOCTL_READDIR_BOTH /* just in case... */
82 /* Chars we don't want to see in DOS file names */
83 #define INVALID_DOS_CHARS "*?<>|\"+=,;[] \345"
85 static const DOS_DEVICE DOSFS_Devices[] =
86 /* name, device flags (see Int 21/AX=0x4400) */
88 { {'C','O','N',0}, 0xc0d3 },
89 { {'P','R','N',0}, 0xa0c0 },
90 { {'N','U','L',0}, 0x80c4 },
91 { {'A','U','X',0}, 0x80c0 },
92 { {'L','P','T','1',0}, 0xa0c0 },
93 { {'L','P','T','2',0}, 0xa0c0 },
94 { {'L','P','T','3',0}, 0xa0c0 },
95 { {'L','P','T','4',0}, 0xc0d3 },
96 { {'C','O','M','1',0}, 0x80c0 },
97 { {'C','O','M','2',0}, 0x80c0 },
98 { {'C','O','M','3',0}, 0x80c0 },
99 { {'C','O','M','4',0}, 0x80c0 },
100 { {'S','C','S','I','M','G','R','$',0}, 0xc0c0 },
101 { {'H','P','S','C','A','N',0}, 0xc0c0 },
102 { {'E','M','M','X','X','X','X','0',0}, 0x0000 }
106 * Directory info for DOSFS_ReadDir
107 * contains the names of *all* the files in the directory
116 /* Info structure for FindFirstFile handle */
119 char *path; /* unix path */
133 static WINE_EXCEPTION_FILTER(page_fault)
135 if (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION)
136 return EXCEPTION_EXECUTE_HANDLER;
137 return EXCEPTION_CONTINUE_SEARCH;
141 /***********************************************************************
144 * Return 1 if Unix file 'name' is also a valid MS-DOS name
145 * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
146 * File name can be terminated by '\0', '\\' or '/'.
148 static int DOSFS_ValidDOSName( LPCWSTR name, int ignore_case )
150 static const char invalid_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS;
151 const WCHAR *p = name;
152 const char *invalid = ignore_case ? (invalid_chars + 26) : invalid_chars;
157 /* Check for "." and ".." */
160 /* All other names beginning with '.' are invalid */
161 return (IS_END_OF_NAME(*p));
163 while (!IS_END_OF_NAME(*p))
165 if (*p < 256 && strchr( invalid, (char)*p )) return 0; /* Invalid char */
166 if (*p == '.') break; /* Start of the extension */
167 if (++len > 8) return 0; /* Name too long */
170 if (*p != '.') return 1; /* End of name */
172 if (IS_END_OF_NAME(*p)) return 0; /* Empty extension not allowed */
174 while (!IS_END_OF_NAME(*p))
176 if (*p < 256 && strchr( invalid, (char)*p )) return 0; /* Invalid char */
177 if (*p == '.') return 0; /* Second extension not allowed */
178 if (++len > 3) return 0; /* Extension too long */
185 /***********************************************************************
186 * DOSFS_ToDosFCBFormat
188 * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
189 * expanding wild cards and converting to upper-case in the process.
190 * File name can be terminated by '\0', '\\' or '/'.
191 * Return FALSE if the name is not a valid DOS name.
192 * 'buffer' must be at least 12 characters long.
194 BOOL DOSFS_ToDosFCBFormat( LPCWSTR name, LPWSTR buffer )
196 static const char invalid_chars[] = INVALID_DOS_CHARS;
200 TRACE("(%s, %p)\n", debugstr_w(name), buffer);
202 /* Check for "." and ".." */
207 for(i = 1; i < 11; i++) buffer[i] = ' ';
214 return (!*p || (*p == '/') || (*p == '\\'));
217 for (i = 0; i < 8; i++)
234 if (*p < 256 && strchr( invalid_chars, (char)*p )) return FALSE;
235 buffer[i] = toupperW(*p);
243 /* Skip all chars after wildcard up to first dot */
244 while (*p && (*p != '/') && (*p != '\\') && (*p != '.')) p++;
248 /* Check if name too long */
249 if (*p && (*p != '/') && (*p != '\\') && (*p != '.')) return FALSE;
251 if (*p == '.') p++; /* Skip dot */
253 for (i = 8; i < 11; i++)
263 return FALSE; /* Second extension not allowed */
271 if (*p < 256 && strchr( invalid_chars, (char)*p )) return FALSE;
272 buffer[i] = toupperW(*p);
279 /* at most 3 character of the extension are processed
280 * is something behind this ?
282 while (*p == '*' || *p == ' ') p++; /* skip wildcards and spaces */
283 return IS_END_OF_NAME(*p);
287 /***********************************************************************
288 * DOSFS_ToDosDTAFormat
290 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
291 * converting to upper-case in the process.
292 * File name can be terminated by '\0', '\\' or '/'.
293 * 'buffer' must be at least 13 characters long.
295 static void DOSFS_ToDosDTAFormat( LPCWSTR name, LPWSTR buffer )
299 memcpy( buffer, name, 8 * sizeof(WCHAR) );
301 while ((p > buffer) && (p[-1] == ' ')) p--;
303 memcpy( p, name + 8, 3 * sizeof(WCHAR) );
305 while (p[-1] == ' ') p--;
306 if (p[-1] == '.') p--;
311 /***********************************************************************
314 * Check a DOS file name against a mask (both in FCB format).
316 static int DOSFS_MatchShort( LPCWSTR mask, LPCWSTR name )
319 for (i = 11; i > 0; i--, mask++, name++)
320 if ((*mask != '?') && (*mask != *name)) return 0;
325 /***********************************************************************
328 * Check a long file name against a mask.
330 * Tests (done in W95 DOS shell - case insensitive):
331 * *.txt test1.test.txt *
333 * *.t??????.t* test1.ta.tornado.txt *
334 * *tornado* test1.ta.tornado.txt *
335 * t*t test1.ta.tornado.txt *
337 * ?est??? test1.txt -
338 * *test1.txt* test1.txt *
339 * h?l?o*t.dat hellothisisatest.dat *
341 static int DOSFS_MatchLong( LPCWSTR mask, LPCWSTR name, int case_sensitive )
343 LPCWSTR lastjoker = NULL;
344 LPCWSTR next_to_retry = NULL;
345 static const WCHAR asterisk_dot_asterisk[] = {'*','.','*',0};
347 TRACE("(%s, %s, %x)\n", debugstr_w(mask), debugstr_w(name), case_sensitive);
349 if (!strcmpW( mask, asterisk_dot_asterisk )) return 1;
350 while (*name && *mask)
355 while (*mask == '*') mask++; /* Skip consecutive '*' */
357 if (!*mask) return 1; /* end of mask is all '*', so match */
359 /* skip to the next match after the joker(s) */
360 if (case_sensitive) while (*name && (*name != *mask)) name++;
361 else while (*name && (toupperW(*name) != toupperW(*mask))) name++;
364 next_to_retry = name;
366 else if (*mask != '?')
371 if (*mask != *name) mismatch = 1;
375 if (toupperW(*mask) != toupperW(*name)) mismatch = 1;
389 else /* mismatch ! */
391 if (lastjoker) /* we had an '*', so we can try unlimitedly */
395 /* this scan sequence was a mismatch, so restart
396 * 1 char after the first char we checked last time */
398 name = next_to_retry;
401 return 0; /* bad luck */
410 while ((*mask == '.') || (*mask == '*'))
411 mask++; /* Ignore trailing '.' or '*' in mask */
412 return (!*name && !*mask);
416 /***********************************************************************
419 * Used to construct an array of filenames in DOSFS_OpenDir
421 static BOOL DOSFS_AddDirEntry(DOS_DIR **dir, LPCWSTR name, LPCWSTR dosname)
423 int extra1 = (strlenW(name) + 1) * sizeof(WCHAR);
424 int extra2 = (strlenW(dosname) + 1) * sizeof(WCHAR);
426 /* if we need more, at minimum double the size */
427 if( (extra1 + extra2 + (*dir)->used) > (*dir)->size)
429 int more = (*dir)->size;
432 if(more<(extra1+extra2))
433 more = extra1+extra2;
435 t = HeapReAlloc(GetProcessHeap(), 0, *dir, sizeof(**dir) + (*dir)->size + more );
438 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
439 ERR("Out of memory caching directory structure %d %d %d\n",
440 (*dir)->size, more, (*dir)->used);
444 (*dir)->size += more;
447 /* at this point, the dir structure is big enough to hold these names */
448 strcpyW((LPWSTR)&(*dir)->names[(*dir)->used], name);
449 (*dir)->used += extra1;
450 strcpyW((LPWSTR)&(*dir)->names[(*dir)->used], dosname);
451 (*dir)->used += extra2;
457 /***********************************************************************
460 static BOOL DOSFS_OpenDir_VFAT(UINT codepage, DOS_DIR **dir, const char *unix_path)
462 #ifdef VFAT_IOCTL_READDIR_BOTH
464 int fd = open( unix_path, O_RDONLY );
467 /* Check if the VFAT ioctl is supported on this directory */
474 WCHAR long_name[MAX_PATH];
475 WCHAR short_name[12];
477 r = (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de ) != -1);
482 MultiByteToWideChar(codepage, 0, de[0].d_name, -1, long_name, MAX_PATH);
483 if (!DOSFS_ToDosFCBFormat( long_name, short_name ))
484 short_name[0] = '\0';
486 MultiByteToWideChar(codepage, 0, de[1].d_name, -1, long_name, MAX_PATH);
488 MultiByteToWideChar(codepage, 0, de[0].d_name, -1, long_name, MAX_PATH);
489 r = DOSFS_AddDirEntry(dir, long_name, short_name );
495 static const WCHAR empty_strW[] = { 0 };
496 DOSFS_AddDirEntry(dir, empty_strW, empty_strW);
502 #endif /* VFAT_IOCTL_READDIR_BOTH */
506 /***********************************************************************
507 * DOSFS_OpenDir_Normal
509 * Now use the standard opendir/readdir interface
511 static BOOL DOSFS_OpenDir_Normal( UINT codepage, DOS_DIR **dir, const char *unix_path )
513 DIR *unixdir = opendir( unix_path );
515 static const WCHAR empty_strW[] = { 0 };
521 WCHAR long_name[MAX_PATH];
522 struct dirent *de = readdir(unixdir);
526 MultiByteToWideChar(codepage, 0, de->d_name, -1, long_name, MAX_PATH);
527 r = DOSFS_AddDirEntry(dir, long_name, empty_strW);
532 DOSFS_AddDirEntry(dir, empty_strW, empty_strW);
537 /***********************************************************************
540 static DOS_DIR *DOSFS_OpenDir( UINT codepage, const char *unix_path )
542 const int init_size = 0x100;
543 DOS_DIR *dir = HeapAlloc( GetProcessHeap(), 0, sizeof(*dir) + init_size);
546 TRACE("%s\n",debugstr_a(unix_path));
550 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
554 dir->size = init_size;
556 /* Treat empty path as root directory. This simplifies path split into
557 directory and mask in several other places */
558 if (!*unix_path) unix_path = "/";
560 r = DOSFS_OpenDir_VFAT( codepage, &dir, unix_path);
563 r = DOSFS_OpenDir_Normal( codepage, &dir, unix_path);
567 HeapFree(GetProcessHeap(), 0, dir);
576 /***********************************************************************
579 static void DOSFS_CloseDir( DOS_DIR *dir )
581 HeapFree( GetProcessHeap(), 0, dir );
585 /***********************************************************************
588 static BOOL DOSFS_ReadDir( DOS_DIR *dir, LPCWSTR *long_name,
589 LPCWSTR *short_name )
596 /* the long pathname is first */
597 ln = (LPCWSTR)&dir->names[dir->used];
602 dir->used += (strlenW(ln) + 1) * sizeof(WCHAR);
604 /* followed by the short path name */
605 sn = (LPCWSTR)&dir->names[dir->used];
610 dir->used += (strlenW(sn) + 1) * sizeof(WCHAR);
612 TRACE("Read: long_name: %s, short_name: %s\n",
613 debugstr_w(*long_name), debugstr_w(*short_name));
619 /***********************************************************************
622 * Transform a Unix file name into a hashed DOS name. If the name is a valid
623 * DOS name, it is converted to upper-case; otherwise it is replaced by a
624 * hashed version that fits in 8.3 format.
625 * File name can be terminated by '\0', '\\' or '/'.
626 * 'buffer' must be at least 13 characters long.
628 static void DOSFS_Hash( LPCWSTR name, LPWSTR buffer, BOOL dir_format,
631 static const char invalid_chars[] = INVALID_DOS_CHARS "~.";
632 static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
641 for(i = 0; i < 11; i++) buffer[i] = ' ';
645 if (DOSFS_ValidDOSName( name, ignore_case ))
647 /* Check for '.' and '..' */
651 if (!dir_format) buffer[1] = buffer[2] = '\0';
652 if (name[1] == '.') buffer[1] = '.';
656 /* Simply copy the name, converting to uppercase */
658 for (dst = buffer; !IS_END_OF_NAME(*name) && (*name != '.'); name++)
659 *dst++ = toupperW(*name);
662 if (dir_format) dst = buffer + 8;
664 for (name++; !IS_END_OF_NAME(*name); name++)
665 *dst++ = toupperW(*name);
667 if (!dir_format) *dst = '\0';
671 /* Compute the hash code of the file name */
672 /* If you know something about hash functions, feel free to */
673 /* insert a better algorithm here... */
676 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
677 hash = (hash<<3) ^ (hash>>5) ^ tolowerW(*p) ^ (tolowerW(p[1]) << 8);
678 hash = (hash<<3) ^ (hash>>5) ^ tolowerW(*p); /* Last character */
682 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
683 hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
684 hash = (hash << 3) ^ (hash >> 5) ^ *p; /* Last character */
687 /* Find last dot for start of the extension */
688 for (p = name+1, ext = NULL; !IS_END_OF_NAME(*p); p++)
689 if (*p == '.') ext = p;
690 if (ext && IS_END_OF_NAME(ext[1]))
691 ext = NULL; /* Empty extension ignored */
693 /* Copy first 4 chars, replacing invalid chars with '_' */
694 for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
696 if (IS_END_OF_NAME(*p) || (p == ext)) break;
697 *dst++ = (*p < 256 && strchr( invalid_chars, (char)*p )) ? '_' : toupperW(*p);
699 /* Pad to 5 chars with '~' */
700 while (i-- >= 0) *dst++ = '~';
702 /* Insert hash code converted to 3 ASCII chars */
703 *dst++ = hash_chars[(hash >> 10) & 0x1f];
704 *dst++ = hash_chars[(hash >> 5) & 0x1f];
705 *dst++ = hash_chars[hash & 0x1f];
707 /* Copy the first 3 chars of the extension (if any) */
710 if (!dir_format) *dst++ = '.';
711 for (i = 3, ext++; (i > 0) && !IS_END_OF_NAME(*ext); i--, ext++)
712 *dst++ = (*ext < 256 && strchr( invalid_chars, (char)*ext )) ? '_' : toupperW(*ext);
714 if (!dir_format) *dst = '\0';
718 /***********************************************************************
721 * Find the Unix file name in a given directory that corresponds to
722 * a file name (either in Unix or DOS format).
723 * File name can be terminated by '\0', '\\' or '/'.
724 * Return TRUE if OK, FALSE if no file name matches.
726 * 'long_buf' must be at least 'long_len' characters long. If the long name
727 * turns out to be larger than that, the function returns FALSE.
728 * 'short_buf' must be at least 13 characters long.
730 BOOL DOSFS_FindUnixName( const DOS_FULL_NAME *path, LPCWSTR name, char *long_buf,
731 INT long_len, LPWSTR short_buf, BOOL ignore_case)
734 LPCWSTR long_name, short_name;
735 WCHAR dos_name[12], tmp_buf[13];
738 LPCWSTR p = strchrW( name, '/' );
739 int len = p ? (int)(p - name) : strlenW(name);
740 if ((p = strchrW( name, '\\' ))) len = min( (int)(p - name), len );
741 /* Ignore trailing dots and spaces */
742 while (len > 1 && (name[len-1] == '.' || name[len-1] == ' ')) len--;
743 if (long_len < len + 1) return FALSE;
745 TRACE("%s,%s\n", path->long_name, debugstr_w(name) );
747 if (!DOSFS_ToDosFCBFormat( name, dos_name )) dos_name[0] = '\0';
749 if (!(dir = DOSFS_OpenDir( DRIVE_GetCodepage(path->drive), path->long_name )))
751 WARN("(%s,%s): can't open dir: %s\n",
752 path->long_name, debugstr_w(name), strerror(errno) );
756 while ((ret = DOSFS_ReadDir( dir, &long_name, &short_name )))
758 /* Check against Unix name */
759 if (len == strlenW(long_name))
763 if (!strncmpW( long_name, name, len )) break;
767 if (!strncmpiW( long_name, name, len )) break;
772 /* Check against hashed DOS name */
775 DOSFS_Hash( long_name, tmp_buf, TRUE, ignore_case );
776 short_name = tmp_buf;
778 if (!strcmpW( dos_name, short_name )) break;
783 if (long_buf) WideCharToMultiByte(DRIVE_GetCodepage(path->drive), 0,
784 long_name, -1, long_buf, long_len, NULL, NULL);
788 DOSFS_ToDosDTAFormat( short_name, short_buf );
790 DOSFS_Hash( long_name, short_buf, FALSE, ignore_case );
792 TRACE("(%s,%s) -> %s (%s)\n", path->long_name, debugstr_w(name),
793 debugstr_w(long_name), short_buf ? debugstr_w(short_buf) : "***");
796 WARN("%s not found in '%s'\n", debugstr_w(name), path->long_name);
797 DOSFS_CloseDir( dir );
802 /***********************************************************************
805 * Check if a DOS file name represents a DOS device and return the device.
807 const DOS_DEVICE *DOSFS_GetDevice( LPCWSTR name )
812 if (!name) return NULL; /* if FILE_DupUnixHandle was used */
813 if (name[0] && (name[1] == ':')) name += 2;
814 if ((p = strrchrW( name, '/' ))) name = p + 1;
815 if ((p = strrchrW( name, '\\' ))) name = p + 1;
816 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
818 const WCHAR *dev = DOSFS_Devices[i].name;
819 if (!strncmpiW( dev, name, strlenW(dev) ))
821 p = name + strlenW( dev );
822 if (!*p || (*p == '.') || (*p == ':')) return &DOSFS_Devices[i];
829 /***********************************************************************
830 * DOSFS_GetDeviceByHandle
832 const DOS_DEVICE *DOSFS_GetDeviceByHandle( HANDLE hFile )
834 const DOS_DEVICE *ret = NULL;
835 SERVER_START_REQ( get_device_id )
838 if (!wine_server_call( req ))
840 if ((reply->id >= 0) &&
841 (reply->id < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0])))
842 ret = &DOSFS_Devices[reply->id];
850 /**************************************************************************
851 * DOSFS_CreateCommPort
853 static HANDLE DOSFS_CreateCommPort(LPCWSTR name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa)
858 static const WCHAR serialportsW[] = {'s','e','r','i','a','l','p','o','r','t','s',0};
859 static const WCHAR empty_strW[] = { 0 };
861 TRACE_(file)("%s %lx %lx\n", debugstr_w(name), access, attributes);
863 PROFILE_GetWineIniString(serialportsW, name, empty_strW, devnameW, 40);
867 WideCharToMultiByte(CP_ACP, 0, devnameW, -1, devname, sizeof(devname), NULL, NULL);
869 TRACE("opening %s as %s\n", devname, debugstr_w(name));
871 SERVER_START_REQ( create_serial )
873 req->access = access;
874 req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
875 req->attributes = attributes;
876 req->sharing = FILE_SHARE_READ|FILE_SHARE_WRITE;
877 wine_server_add_data( req, devname, strlen(devname) );
879 wine_server_call_err( req );
885 ERR("Couldn't open device '%s' ! (check permissions)\n",devname);
887 TRACE("return %p\n", ret );
891 /***********************************************************************
894 * Open a DOS device. This might not map 1:1 into the UNIX device concept.
895 * Returns 0 on failure.
897 HANDLE DOSFS_OpenDevice( LPCWSTR name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa )
903 if (name[0] && (name[1] == ':')) name += 2;
904 if ((p = strrchrW( name, '/' ))) name = p + 1;
905 if ((p = strrchrW( name, '\\' ))) name = p + 1;
906 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
908 const WCHAR *dev = DOSFS_Devices[i].name;
909 if (!strncmpiW( dev, name, strlenW(dev) ))
911 p = name + strlenW( dev );
912 if (!*p || (*p == '.') || (*p == ':')) {
913 static const WCHAR nulW[] = {'N','U','L',0};
914 static const WCHAR conW[] = {'C','O','N',0};
915 static const WCHAR scsimgrW[] = {'S','C','S','I','M','G','R','$',0};
916 static const WCHAR hpscanW[] = {'H','P','S','C','A','N',0};
917 static const WCHAR emmxxxx0W[] = {'E','M','M','X','X','X','X','0',0};
919 if (!strcmpiW(DOSFS_Devices[i].name, nulW))
920 return FILE_CreateFile( "/dev/null", access,
921 FILE_SHARE_READ|FILE_SHARE_WRITE, sa,
922 OPEN_EXISTING, 0, 0, TRUE, DRIVE_UNKNOWN );
923 if (!strcmpiW(DOSFS_Devices[i].name, conW)) {
925 switch (access & (GENERIC_READ|GENERIC_WRITE)) {
927 to_dup = GetStdHandle( STD_INPUT_HANDLE );
930 to_dup = GetStdHandle( STD_OUTPUT_HANDLE );
933 FIXME("can't open CON read/write\n");
936 if (!DuplicateHandle( GetCurrentProcess(), to_dup, GetCurrentProcess(),
938 sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle,
939 DUPLICATE_SAME_ACCESS ))
943 if (!strcmpiW(DOSFS_Devices[i].name, scsimgrW) ||
944 !strcmpiW(DOSFS_Devices[i].name, hpscanW) ||
945 !strcmpiW(DOSFS_Devices[i].name, emmxxxx0W))
947 return FILE_CreateDevice( i, access, sa );
950 if( (handle=DOSFS_CreateCommPort(DOSFS_Devices[i].name,access,attributes,sa)) )
952 FIXME("device open %s not supported (yet)\n", debugstr_w(DOSFS_Devices[i].name));
961 /***********************************************************************
964 * Get the drive specified by a given path name (DOS or Unix format).
966 static int DOSFS_GetPathDrive( LPCWSTR *name )
971 if (*p && (p[1] == ':'))
973 drive = toupperW(*p) - 'A';
976 else if (*p == '/') /* Absolute Unix path? */
978 if ((drive = DRIVE_FindDriveRootW( name )) == -1)
980 MESSAGE("Warning: %s not accessible from a configured DOS drive\n", debugstr_w(*name) );
981 /* Assume it really was a DOS name */
982 drive = DRIVE_GetCurrentDrive();
985 else drive = DRIVE_GetCurrentDrive();
987 if (!DRIVE_IsValid(drive))
989 SetLastError( ERROR_INVALID_DRIVE );
996 /***********************************************************************
999 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
1000 * Unix name / short DOS name pair.
1001 * Return FALSE if one of the path components does not exist. The last path
1002 * component is only checked if 'check_last' is non-zero.
1003 * The buffers pointed to by 'long_buf' and 'short_buf' must be
1004 * at least MAX_PATHNAME_LEN long.
1006 BOOL DOSFS_GetFullName( LPCWSTR name, BOOL check_last, DOS_FULL_NAME *full )
1009 UINT flags, codepage;
1012 static const WCHAR driveA_rootW[] = {'A',':','\\',0};
1013 static const WCHAR dos_rootW[] = {'\\',0};
1015 TRACE("%s (last=%d)\n", debugstr_w(name), check_last );
1017 if ((!*name) || (*name=='\n'))
1018 { /* error code for Win98 */
1019 SetLastError(ERROR_BAD_PATHNAME);
1023 if ((full->drive = DOSFS_GetPathDrive( &name )) == -1) return FALSE;
1024 flags = DRIVE_GetFlags( full->drive );
1025 codepage = DRIVE_GetCodepage(full->drive);
1027 lstrcpynA( full->long_name, DRIVE_GetRoot( full->drive ),
1028 sizeof(full->long_name) );
1029 if (full->long_name[1]) root = full->long_name + strlen(full->long_name);
1030 else root = full->long_name; /* root directory */
1032 strcpyW( full->short_name, driveA_rootW );
1033 full->short_name[0] += full->drive;
1035 if ((*name == '\\') || (*name == '/')) /* Absolute path */
1037 while ((*name == '\\') || (*name == '/')) name++;
1039 else /* Relative path */
1041 lstrcpynA( root + 1, DRIVE_GetUnixCwd( full->drive ),
1042 sizeof(full->long_name) - (root - full->long_name) - 1 );
1043 if (root[1]) *root = '/';
1044 lstrcpynW( full->short_name + 3, DRIVE_GetDosCwd( full->drive ),
1045 sizeof(full->short_name)/sizeof(full->short_name[0]) - 3 );
1048 p_l = full->long_name[1] ? full->long_name + strlen(full->long_name)
1050 p_s = full->short_name[3] ? full->short_name + strlenW(full->short_name)
1051 : full->short_name + 2;
1054 while (*name && found)
1056 /* Check for '.' and '..' */
1060 if (IS_END_OF_NAME(name[1]))
1063 while ((*name == '\\') || (*name == '/')) name++;
1066 else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
1069 while ((*name == '\\') || (*name == '/')) name++;
1070 while ((p_l > root) && (*p_l != '/')) p_l--;
1071 while ((p_s > full->short_name + 2) && (*p_s != '\\')) p_s--;
1072 *p_l = *p_s = '\0'; /* Remove trailing separator */
1077 /* Make sure buffers are large enough */
1079 if ((p_s >= full->short_name + sizeof(full->short_name)/sizeof(full->short_name[0]) - 14) ||
1080 (p_l >= full->long_name + sizeof(full->long_name) - 1))
1082 SetLastError( ERROR_PATH_NOT_FOUND );
1086 /* Get the long and short name matching the file name */
1088 if ((found = DOSFS_FindUnixName( full, name, p_l + 1,
1089 sizeof(full->long_name) - (p_l - full->long_name) - 1,
1090 p_s + 1, !(flags & DRIVE_CASE_SENSITIVE) )))
1095 p_s += strlenW(p_s);
1096 while (!IS_END_OF_NAME(*name)) name++;
1098 else if (!check_last)
1102 while (!IS_END_OF_NAME(*name) &&
1103 (p_s < full->short_name + sizeof(full->short_name)/sizeof(full->short_name[0]) - 1) &&
1104 (p_l < full->long_name + sizeof(full->long_name) - 1))
1107 *p_s++ = tolowerW(*name);
1108 /* If the drive is case-sensitive we want to create new */
1109 /* files in lower-case otherwise we can't reopen them */
1110 /* under the same short name. */
1111 if (flags & DRIVE_CASE_SENSITIVE) wch = tolowerW(*name);
1113 p_l += WideCharToMultiByte(codepage, 0, &wch, 1, p_l, 2, NULL, NULL);
1116 /* Ignore trailing dots and spaces */
1117 while(p_l[-1] == '.' || p_l[-1] == ' ') {
1124 while ((*name == '\\') || (*name == '/')) name++;
1131 SetLastError( ERROR_FILE_NOT_FOUND );
1134 if (*name) /* Not last */
1136 SetLastError( ERROR_PATH_NOT_FOUND );
1140 if (!full->long_name[0]) strcpy( full->long_name, "/" );
1141 if (!full->short_name[2]) strcpyW( full->short_name + 2, dos_rootW );
1142 TRACE("returning %s = %s\n", full->long_name, debugstr_w(full->short_name) );
1147 /***********************************************************************
1148 * GetShortPathNameW (KERNEL32.@)
1152 * longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1153 * longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0
1155 * more observations ( with NT 3.51 (WinDD) ):
1156 * longpath <= 8.3 -> just copy longpath to shortpath
1158 * a) file does not exist -> return 0, LastError = ERROR_FILE_NOT_FOUND
1159 * b) file does exist -> set the short filename.
1160 * - trailing slashes are reproduced in the short name, even if the
1161 * file is not a directory
1162 * - the absolute/relative path of the short name is reproduced like found
1164 * - longpath and shortpath may have the same address
1165 * Peter Ganten, 1999
1167 DWORD WINAPI GetShortPathNameW( LPCWSTR longpath, LPWSTR shortpath, DWORD shortlen )
1169 DOS_FULL_NAME full_name;
1170 WCHAR tmpshortpath[MAX_PATHNAME_LEN];
1172 DWORD sp = 0, lp = 0;
1176 BOOL unixabsolute = *longpath == '/';
1178 TRACE("%s\n", debugstr_w(longpath));
1181 SetLastError(ERROR_INVALID_PARAMETER);
1185 SetLastError(ERROR_BAD_PATHNAME);
1189 /* check for drive letter */
1190 if (!unixabsolute && longpath[1] == ':' ) {
1191 tmpshortpath[0] = longpath[0];
1192 tmpshortpath[1] = ':';
1196 if ( ( drive = DOSFS_GetPathDrive ( &longpath )) == -1 ) return 0;
1197 flags = DRIVE_GetFlags ( drive );
1199 if (unixabsolute && drive != DRIVE_GetCurrentDrive()) {
1200 tmpshortpath[0] = drive + 'A';
1201 tmpshortpath[1] = ':';
1205 while ( longpath[lp] ) {
1207 /* check for path delimiters and reproduce them */
1208 if ( longpath[lp] == '\\' || longpath[lp] == '/' ) {
1209 if (!sp || tmpshortpath[sp-1]!= '\\')
1211 /* strip double "\\" */
1212 tmpshortpath[sp] = '\\';
1215 tmpshortpath[sp]=0;/*terminate string*/
1221 for(p = longpath + lp; *p && *p != '/' && *p != '\\'; p++)
1223 lstrcpynW(tmpshortpath + sp, longpath + lp, tmplen + 1);
1225 /* Check, if the current element is a valid dos name */
1226 if ( DOSFS_ValidDOSName ( longpath + lp, !(flags & DRIVE_CASE_SENSITIVE) ) ) {
1232 /* Check if the file exists and use the existing file name */
1233 if ( DOSFS_GetFullName ( tmpshortpath, TRUE, &full_name ) ) {
1234 strcpyW(tmpshortpath + sp, strrchrW(full_name.short_name, '\\') + 1);
1235 sp += strlenW(tmpshortpath + sp);
1240 TRACE("not found!\n" );
1241 SetLastError ( ERROR_FILE_NOT_FOUND );
1244 tmpshortpath[sp] = 0;
1246 tmplen = strlenW(tmpshortpath) + 1;
1247 if (tmplen <= shortlen)
1249 strcpyW(shortpath, tmpshortpath);
1250 TRACE("returning %s\n", debugstr_w(shortpath));
1251 tmplen--; /* length without 0 */
1258 /***********************************************************************
1259 * GetShortPathNameA (KERNEL32.@)
1261 DWORD WINAPI GetShortPathNameA( LPCSTR longpath, LPSTR shortpath, DWORD shortlen )
1263 UNICODE_STRING longpathW;
1264 WCHAR shortpathW[MAX_PATH];
1269 SetLastError(ERROR_INVALID_PARAMETER);
1273 TRACE("%s\n", debugstr_a(longpath));
1275 if (!RtlCreateUnicodeStringFromAsciiz(&longpathW, longpath))
1277 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1281 retW = GetShortPathNameW(longpathW.Buffer, shortpathW, MAX_PATH);
1285 else if (retW > MAX_PATH)
1287 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1292 ret = WideCharToMultiByte(CP_ACP, 0, shortpathW, -1, NULL, 0, NULL, NULL);
1293 if (ret <= shortlen)
1295 WideCharToMultiByte(CP_ACP, 0, shortpathW, -1, shortpath, shortlen, NULL, NULL);
1296 ret--; /* length without 0 */
1300 RtlFreeUnicodeString(&longpathW);
1305 /***********************************************************************
1306 * GetLongPathNameW (KERNEL32.@)
1309 * observed (Win2000):
1310 * shortpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1311 * shortpath="": LastError=ERROR_PATH_NOT_FOUND, ret=0
1313 DWORD WINAPI GetLongPathNameW( LPCWSTR shortpath, LPWSTR longpath, DWORD longlen )
1315 DOS_FULL_NAME full_name;
1323 SetLastError(ERROR_INVALID_PARAMETER);
1326 if (!shortpath[0]) {
1327 SetLastError(ERROR_PATH_NOT_FOUND);
1331 TRACE("%s,%p,%ld\n", debugstr_w(shortpath), longpath, longlen);
1333 if(shortpath[0]=='\\' && shortpath[1]=='\\')
1335 ERR("UNC pathname %s\n",debugstr_w(shortpath));
1336 lstrcpynW( longpath, full_name.short_name, longlen );
1337 return strlenW(longpath);
1340 if (!DOSFS_GetFullName( shortpath, TRUE, &full_name )) return 0;
1342 root = full_name.long_name;
1343 drive = DRIVE_FindDriveRoot(&root);
1344 codepage = DRIVE_GetCodepage(drive);
1346 ret = MultiByteToWideChar(codepage, 0, root, -1, NULL, 0);
1348 /* reproduce terminating slash */
1349 if (ret > 4) /* if not drive root */
1351 len = strlenW(shortpath);
1352 if (shortpath[len - 1] == '\\' || shortpath[len - 1] == '/')
1358 longpath[0] = 'A' + drive;
1360 MultiByteToWideChar(codepage, 0, root, -1, longpath + 2, longlen - 2);
1361 for (p = longpath; *p; p++) if (*p == '/') *p = '\\';
1364 longpath[ret - 2] = '\\';
1365 longpath[ret - 1] = 0;
1367 TRACE("returning %s\n", debugstr_w(longpath));
1368 ret--; /* length without 0 */
1374 /***********************************************************************
1375 * GetLongPathNameA (KERNEL32.@)
1377 DWORD WINAPI GetLongPathNameA( LPCSTR shortpath, LPSTR longpath, DWORD longlen )
1379 UNICODE_STRING shortpathW;
1380 WCHAR longpathW[MAX_PATH];
1385 SetLastError(ERROR_INVALID_PARAMETER);
1389 TRACE("%s\n", debugstr_a(shortpath));
1391 if (!RtlCreateUnicodeStringFromAsciiz(&shortpathW, shortpath))
1393 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1397 retW = GetLongPathNameW(shortpathW.Buffer, longpathW, MAX_PATH);
1401 else if (retW > MAX_PATH)
1403 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1408 ret = WideCharToMultiByte(CP_ACP, 0, longpathW, -1, NULL, 0, NULL, NULL);
1411 WideCharToMultiByte(CP_ACP, 0, longpathW, -1, longpath, longlen, NULL, NULL);
1412 ret--; /* length without 0 */
1416 RtlFreeUnicodeString(&shortpathW);
1421 /***********************************************************************
1422 * DOSFS_DoGetFullPathName
1424 * Implementation of GetFullPathNameA/W.
1426 * bon@elektron 000331:
1427 * A test for GetFullPathName with many pathological cases
1428 * now gives identical output for Wine and OSR2
1430 static DWORD DOSFS_DoGetFullPathName( LPCWSTR name, DWORD len, LPWSTR result )
1433 DOS_FULL_NAME full_name;
1437 WCHAR drivecur[] = {'C',':','.',0};
1438 WCHAR driveletter=0;
1439 int namelen,drive=0;
1440 static const WCHAR bkslashW[] = {'\\',0};
1441 static const WCHAR dotW[] = {'.',0};
1442 static const WCHAR updir_slashW[] = {'\\','.','.','\\',0};
1443 static const WCHAR curdirW[] = {'\\','.','\\',0};
1444 static const WCHAR updirW[] = {'\\','.','.',0};
1448 SetLastError(ERROR_BAD_PATHNAME);
1452 TRACE("passed %s\n", debugstr_w(name));
1455 /*drive letter given */
1457 driveletter = name[0];
1459 if ((name[1]==':') && ((name[2]=='\\') || (name[2]=='/')))
1460 /*absolute path given */
1462 strncpyW(full_name.short_name, name, MAX_PATHNAME_LEN);
1463 full_name.short_name[MAX_PATHNAME_LEN - 1] = 0; /* ensure 0 termination */
1464 drive = toupperW(name[0]) - 'A';
1469 drivecur[0]=driveletter;
1470 else if ((name[0]=='\\') || (name[0]=='/'))
1471 strcpyW(drivecur, bkslashW);
1473 strcpyW(drivecur, dotW);
1475 if (!DOSFS_GetFullName( drivecur, FALSE, &full_name ))
1477 FIXME("internal: error getting drive/path\n");
1480 /* find path that drive letter substitutes*/
1481 drive = toupperW(full_name.short_name[0]) - 'A';
1482 root= DRIVE_GetRoot(drive);
1485 FIXME("internal: error getting DOS Drive Root\n");
1488 if (!strcmp(root,"/"))
1490 /* we have just the last / and we need it. */
1491 p_l = full_name.long_name;
1495 p_l = full_name.long_name + strlen(root);
1497 /* append long name (= unix name) to drive */
1498 MultiByteToWideChar(DRIVE_GetCodepage(drive), 0, p_l, -1,
1499 full_name.short_name + 2, MAX_PATHNAME_LEN - 3);
1500 /* append name to treat */
1501 namelen= strlenW(full_name.short_name);
1504 p += 2; /* skip drive name when appending */
1505 if (namelen + 2 + strlenW(p) > MAX_PATHNAME_LEN)
1507 FIXME("internal error: buffer too small\n");
1510 full_name.short_name[namelen++] ='\\';
1511 full_name.short_name[namelen] = 0;
1512 strncpyW(full_name.short_name + namelen, p, MAX_PATHNAME_LEN - namelen);
1513 full_name.short_name[MAX_PATHNAME_LEN - 1] = 0; /* ensure 0 termination */
1515 /* reverse all slashes */
1516 for (p=full_name.short_name;
1517 p < full_name.short_name + strlenW(full_name.short_name);
1523 /* Use memmove, as areas overlap */
1525 while ((p = strstrW(full_name.short_name, updir_slashW)))
1527 if (p > full_name.short_name+2)
1530 q = strrchrW(full_name.short_name, '\\');
1531 memmove(q+1, p+4, (strlenW(p+4)+1) * sizeof(WCHAR));
1535 memmove(full_name.short_name+3, p+4, (strlenW(p+4)+1) * sizeof(WCHAR));
1538 if ((full_name.short_name[2]=='.')&&(full_name.short_name[3]=='.'))
1540 /* This case istn't treated yet : c:..\test */
1541 memmove(full_name.short_name+2,full_name.short_name+4,
1542 (strlenW(full_name.short_name+4)+1) * sizeof(WCHAR));
1545 while ((p = strstrW(full_name.short_name, curdirW)))
1548 memmove(p+1, p+3, (strlenW(p+3)+1) * sizeof(WCHAR));
1550 if (!(DRIVE_GetFlags(drive) & DRIVE_CASE_PRESERVING))
1551 for (p = full_name.short_name; *p; p++) *p = toupperW(*p);
1552 namelen = strlenW(full_name.short_name);
1553 if (!strcmpW(full_name.short_name+namelen-3, updirW))
1555 /* one more strange case: "c:\test\test1\.."
1557 *(full_name.short_name+namelen-3)=0;
1558 q = strrchrW(full_name.short_name, '\\');
1561 if (full_name.short_name[namelen-1]=='.')
1562 full_name.short_name[(namelen--)-1] =0;
1564 if (full_name.short_name[namelen-1]=='\\')
1565 full_name.short_name[(namelen--)-1] =0;
1566 TRACE("got %s\n", debugstr_w(full_name.short_name));
1568 /* If the lpBuffer buffer is too small, the return value is the
1569 size of the buffer, in characters, required to hold the path
1570 plus the terminating \0 (tested against win95osr2, bon 001118)
1572 ret = strlenW(full_name.short_name);
1575 /* don't touch anything when the buffer is not large enough */
1576 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1581 strncpyW( result, full_name.short_name, len );
1582 result[len - 1] = 0; /* ensure 0 termination */
1585 TRACE("returning %s\n", debugstr_w(full_name.short_name) );
1590 /***********************************************************************
1591 * GetFullPathNameA (KERNEL32.@)
1593 * if the path closed with '\', *lastpart is 0
1595 DWORD WINAPI GetFullPathNameA( LPCSTR name, DWORD len, LPSTR buffer,
1598 UNICODE_STRING nameW;
1599 WCHAR bufferW[MAX_PATH];
1604 SetLastError(ERROR_INVALID_PARAMETER);
1608 if (!RtlCreateUnicodeStringFromAsciiz(&nameW, name))
1610 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1614 retW = GetFullPathNameW( nameW.Buffer, MAX_PATH, bufferW, NULL);
1618 else if (retW > MAX_PATH)
1620 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1625 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL);
1628 WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, len, NULL, NULL);
1629 ret--; /* length without 0 */
1633 LPSTR p = buffer + strlen(buffer);
1637 while ((p > buffer + 2) && (*p != '\\')) p--;
1640 else *lastpart = NULL;
1645 RtlFreeUnicodeString(&nameW);
1650 /***********************************************************************
1651 * GetFullPathNameW (KERNEL32.@)
1653 DWORD WINAPI GetFullPathNameW( LPCWSTR name, DWORD len, LPWSTR buffer,
1656 DWORD ret = DOSFS_DoGetFullPathName( name, len, buffer );
1657 if (ret && (ret<=len) && buffer && lastpart)
1659 LPWSTR p = buffer + strlenW(buffer);
1660 if (*p != (WCHAR)'\\')
1662 while ((p > buffer + 2) && (*p != (WCHAR)'\\')) p--;
1665 else *lastpart = NULL;
1671 /***********************************************************************
1672 * wine_get_unix_file_name (KERNEL32.@) Not a Windows API
1674 * Return the full Unix file name for a given path.
1675 * FIXME: convert dos file name to unicode
1677 BOOL WINAPI wine_get_unix_file_name( LPCSTR dos, LPSTR buffer, DWORD len )
1681 WCHAR dosW[MAX_PATHNAME_LEN];
1683 MultiByteToWideChar(CP_ACP, 0, dos, -1, dosW, MAX_PATHNAME_LEN);
1684 ret = DOSFS_GetFullName( dosW, FALSE, &path );
1687 strncpy( buffer, path.long_name, len );
1688 buffer[len - 1] = 0; /* ensure 0 termination */
1694 /***********************************************************************
1697 static int DOSFS_FindNextEx( FIND_FIRST_INFO *info, WIN32_FIND_DATAW *entry )
1699 DWORD attr = info->attr | FA_UNUSED | FA_ARCHIVE | FA_RDONLY;
1700 UINT flags = DRIVE_GetFlags( info->drive );
1701 char *p, buffer[MAX_PATHNAME_LEN];
1702 const char *drive_path;
1704 LPCWSTR long_name, short_name;
1705 BY_HANDLE_FILE_INFORMATION fileinfo;
1709 if ((info->attr & ~(FA_UNUSED | FA_ARCHIVE | FA_RDONLY)) == FA_LABEL)
1711 if (info->cur_pos) return 0;
1712 entry->dwFileAttributes = FILE_ATTRIBUTE_LABEL;
1713 RtlSecondsSince1970ToTime( (time_t)0, (LARGE_INTEGER *)&entry->ftCreationTime );
1714 RtlSecondsSince1970ToTime( (time_t)0, (LARGE_INTEGER *)&entry->ftLastAccessTime );
1715 RtlSecondsSince1970ToTime( (time_t)0, (LARGE_INTEGER *)&entry->ftLastWriteTime );
1716 entry->nFileSizeHigh = 0;
1717 entry->nFileSizeLow = 0;
1718 entry->dwReserved0 = 0;
1719 entry->dwReserved1 = 0;
1720 DOSFS_ToDosDTAFormat( DRIVE_GetLabel( info->drive ), entry->cFileName );
1721 strcpyW( entry->cAlternateFileName, entry->cFileName );
1723 TRACE("returning %s (%s) as label\n",
1724 debugstr_w(entry->cFileName), debugstr_w(entry->cAlternateFileName));
1728 drive_path = info->path + strlen(DRIVE_GetRoot( info->drive ));
1729 while ((*drive_path == '/') || (*drive_path == '\\')) drive_path++;
1730 drive_root = !*drive_path;
1732 lstrcpynA( buffer, info->path, sizeof(buffer) - 1 );
1733 strcat( buffer, "/" );
1734 p = buffer + strlen(buffer);
1736 while (DOSFS_ReadDir( info->u.dos_dir, &long_name, &short_name ))
1740 /* Don't return '.' and '..' in the root of the drive */
1741 if (drive_root && (long_name[0] == '.') &&
1742 (!long_name[1] || ((long_name[1] == '.') && !long_name[2])))
1745 /* Check the long mask */
1747 if (info->long_mask && *info->long_mask)
1749 if (!DOSFS_MatchLong( info->long_mask, long_name,
1750 flags & DRIVE_CASE_SENSITIVE )) continue;
1753 /* Check the short mask */
1755 if (info->short_mask)
1759 DOSFS_Hash( long_name, dos_name, TRUE,
1760 !(flags & DRIVE_CASE_SENSITIVE) );
1761 short_name = dos_name;
1763 if (!DOSFS_MatchShort( info->short_mask, short_name )) continue;
1766 /* Check the file attributes */
1767 WideCharToMultiByte(DRIVE_GetCodepage(info->drive), 0, long_name, -1,
1768 p, sizeof(buffer) - (int)(p - buffer), NULL, NULL);
1769 if (!FILE_Stat( buffer, &fileinfo, &is_symlink ))
1771 WARN("can't stat %s\n", buffer);
1774 if (is_symlink && (fileinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
1776 static const WCHAR wineW[] = {'w','i','n','e',0};
1777 static const WCHAR ShowDirSymlinksW[] = {'S','h','o','w','D','i','r','S','y','m','l','i','n','k','s',0};
1778 static int show_dir_symlinks = -1;
1779 if (show_dir_symlinks == -1)
1780 show_dir_symlinks = PROFILE_GetWineIniBool(wineW, ShowDirSymlinksW, 0);
1781 if (!show_dir_symlinks) continue;
1784 if (fileinfo.dwFileAttributes & ~attr) continue;
1786 /* We now have a matching entry; fill the result and return */
1788 entry->dwFileAttributes = fileinfo.dwFileAttributes;
1789 entry->ftCreationTime = fileinfo.ftCreationTime;
1790 entry->ftLastAccessTime = fileinfo.ftLastAccessTime;
1791 entry->ftLastWriteTime = fileinfo.ftLastWriteTime;
1792 entry->nFileSizeHigh = fileinfo.nFileSizeHigh;
1793 entry->nFileSizeLow = fileinfo.nFileSizeLow;
1796 DOSFS_ToDosDTAFormat( short_name, entry->cAlternateFileName );
1798 DOSFS_Hash( long_name, entry->cAlternateFileName, FALSE,
1799 !(flags & DRIVE_CASE_SENSITIVE) );
1801 lstrcpynW( entry->cFileName, long_name, sizeof(entry->cFileName)/sizeof(entry->cFileName[0]) );
1802 if (!(flags & DRIVE_CASE_PRESERVING)) strlwrW( entry->cFileName );
1803 TRACE("returning %s (%s) %02lx %ld\n",
1804 debugstr_w(entry->cFileName), debugstr_w(entry->cAlternateFileName),
1805 entry->dwFileAttributes, entry->nFileSizeLow );
1808 return 0; /* End of directory */
1811 /***********************************************************************
1814 * Find the next matching file. Return the number of entries read to find
1815 * the matching one, or 0 if no more entries.
1816 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
1817 * file name mask. Either or both can be NULL.
1819 * NOTE: This is supposed to be only called by the int21 emulation
1820 * routines. Thus, we should own the Win16Mutex anyway.
1821 * Nevertheless, we explicitly enter it to ensure the static
1822 * directory cache is protected.
1824 int DOSFS_FindNext( const char *path, const char *short_mask,
1825 const char *long_mask, int drive, BYTE attr,
1826 int skip, WIN32_FIND_DATAA *entry )
1828 static FIND_FIRST_INFO info;
1829 LPCWSTR short_name, long_name;
1831 UNICODE_STRING short_maskW, long_maskW;
1832 WIN32_FIND_DATAW entryW;
1834 TRACE("(%s, %s, %s, %x, %x, %x, %p)\n", debugstr_a(path),
1835 debugstr_a(short_mask), debugstr_a(long_mask), drive, attr, skip,
1840 RtlCreateUnicodeStringFromAsciiz(&short_maskW, short_mask);
1841 RtlCreateUnicodeStringFromAsciiz(&long_maskW, long_mask);
1843 /* Check the cached directory */
1844 if (!(info.u.dos_dir && info.path == path && !strcmpW(info.short_mask, short_maskW.Buffer)
1845 && !strcmpW(info.long_mask, long_maskW.Buffer) && info.drive == drive
1846 && info.attr == attr && info.cur_pos <= skip))
1848 /* Not in the cache, open it anew */
1849 if (info.u.dos_dir) DOSFS_CloseDir( info.u.dos_dir );
1851 info.path = (LPSTR)path;
1852 RtlFreeHeap(GetProcessHeap(), 0, info.long_mask);
1853 RtlFreeHeap(GetProcessHeap(), 0, info.short_mask);
1854 info.long_mask = long_maskW.Buffer;
1855 info.short_mask = short_maskW.Buffer;
1859 info.u.dos_dir = DOSFS_OpenDir( DRIVE_GetCodepage(drive), info.path );
1863 RtlFreeUnicodeString(&short_maskW);
1864 RtlFreeUnicodeString(&long_maskW);
1867 /* Skip to desired position */
1868 while (info.cur_pos < skip)
1869 if (info.u.dos_dir && DOSFS_ReadDir( info.u.dos_dir, &long_name, &short_name ))
1874 if (info.u.dos_dir && info.cur_pos == skip && DOSFS_FindNextEx( &info, &entryW ))
1876 WideCharToMultiByte(CP_ACP, 0, entryW.cFileName, -1,
1877 entry->cFileName, sizeof(entry->cFileName), NULL, NULL);
1878 WideCharToMultiByte(CP_ACP, 0, entryW.cAlternateFileName, -1,
1879 entry->cAlternateFileName, sizeof(entry->cAlternateFileName), NULL, NULL);
1880 count = info.cur_pos - skip;
1882 entry->dwFileAttributes = entryW.dwFileAttributes;
1883 entry->nFileSizeHigh = entryW.nFileSizeHigh;
1884 entry->nFileSizeLow = entryW.nFileSizeLow;
1885 entry->ftCreationTime = entryW.ftCreationTime;
1886 entry->ftLastAccessTime = entryW.ftLastAccessTime;
1887 entry->ftLastWriteTime = entryW.ftLastWriteTime;
1895 if (info.u.dos_dir) DOSFS_CloseDir( info.u.dos_dir );
1896 memset( &info, '\0', sizeof(info) );
1904 /*************************************************************************
1905 * FindFirstFileExW (KERNEL32.@)
1907 HANDLE WINAPI FindFirstFileExW(
1909 FINDEX_INFO_LEVELS fInfoLevelId,
1910 LPVOID lpFindFileData,
1911 FINDEX_SEARCH_OPS fSearchOp,
1912 LPVOID lpSearchFilter,
1913 DWORD dwAdditionalFlags)
1916 FIND_FIRST_INFO *info;
1920 SetLastError(ERROR_PATH_NOT_FOUND);
1921 return INVALID_HANDLE_VALUE;
1924 if ((fSearchOp != FindExSearchNameMatch) || (dwAdditionalFlags != 0))
1926 FIXME("options not implemented 0x%08x 0x%08lx\n", fSearchOp, dwAdditionalFlags );
1927 return INVALID_HANDLE_VALUE;
1930 switch(fInfoLevelId)
1932 case FindExInfoStandard:
1934 WIN32_FIND_DATAW * data = (WIN32_FIND_DATAW *) lpFindFileData;
1939 data->dwReserved0 = data->dwReserved1 = 0x0;
1940 if (!lpFileName) return 0;
1941 if (lpFileName[0] == '\\' && lpFileName[1] == '\\')
1943 ERR("UNC path name\n");
1944 if (!(handle = GlobalAlloc(GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO)))) break;
1946 info = (FIND_FIRST_INFO *)GlobalLock( handle );
1947 info->u.smb_dir = SMB_FindFirst(lpFileName);
1948 if(!info->u.smb_dir)
1950 GlobalUnlock( handle );
1957 GlobalUnlock( handle );
1961 DOS_FULL_NAME full_name;
1963 if (!DOSFS_GetFullName( lpFileName, FALSE, &full_name )) break;
1964 if (!(handle = GlobalAlloc(GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO)))) break;
1965 info = (FIND_FIRST_INFO *)GlobalLock( handle );
1966 info->path = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
1967 strcpy( info->path, full_name.long_name );
1969 codepage = DRIVE_GetCodepage(full_name.drive);
1970 p = strrchr( info->path, '/' );
1972 long_mask_len = MultiByteToWideChar(codepage, 0, p, -1, NULL, 0);
1973 info->long_mask = HeapAlloc( GetProcessHeap(), 0, long_mask_len * sizeof(WCHAR) );
1974 MultiByteToWideChar(codepage, 0, p, -1, info->long_mask, long_mask_len);
1976 info->short_mask = NULL;
1978 info->drive = full_name.drive;
1981 info->u.dos_dir = DOSFS_OpenDir( codepage, info->path );
1982 GlobalUnlock( handle );
1984 if (!FindNextFileW( handle, data ))
1986 FindClose( handle );
1987 SetLastError( ERROR_FILE_NOT_FOUND );
1994 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
1996 return INVALID_HANDLE_VALUE;
1999 /*************************************************************************
2000 * FindFirstFileA (KERNEL32.@)
2002 HANDLE WINAPI FindFirstFileA(
2004 WIN32_FIND_DATAA *lpFindData )
2006 return FindFirstFileExA(lpFileName, FindExInfoStandard, lpFindData,
2007 FindExSearchNameMatch, NULL, 0);
2010 /*************************************************************************
2011 * FindFirstFileExA (KERNEL32.@)
2013 HANDLE WINAPI FindFirstFileExA(
2015 FINDEX_INFO_LEVELS fInfoLevelId,
2016 LPVOID lpFindFileData,
2017 FINDEX_SEARCH_OPS fSearchOp,
2018 LPVOID lpSearchFilter,
2019 DWORD dwAdditionalFlags)
2022 WIN32_FIND_DATAA *dataA;
2023 WIN32_FIND_DATAW dataW;
2024 UNICODE_STRING pathW;
2028 SetLastError(ERROR_PATH_NOT_FOUND);
2029 return INVALID_HANDLE_VALUE;
2032 if (!RtlCreateUnicodeStringFromAsciiz(&pathW, lpFileName))
2034 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2035 return INVALID_HANDLE_VALUE;
2038 handle = FindFirstFileExW(pathW.Buffer, fInfoLevelId, &dataW, fSearchOp, lpSearchFilter, dwAdditionalFlags);
2039 RtlFreeUnicodeString(&pathW);
2040 if (handle == INVALID_HANDLE_VALUE) return handle;
2042 dataA = (WIN32_FIND_DATAA *) lpFindFileData;
2043 dataA->dwFileAttributes = dataW.dwFileAttributes;
2044 dataA->ftCreationTime = dataW.ftCreationTime;
2045 dataA->ftLastAccessTime = dataW.ftLastAccessTime;
2046 dataA->ftLastWriteTime = dataW.ftLastWriteTime;
2047 dataA->nFileSizeHigh = dataW.nFileSizeHigh;
2048 dataA->nFileSizeLow = dataW.nFileSizeLow;
2049 WideCharToMultiByte( CP_ACP, 0, dataW.cFileName, -1,
2050 dataA->cFileName, sizeof(dataA->cFileName), NULL, NULL );
2051 WideCharToMultiByte( CP_ACP, 0, dataW.cAlternateFileName, -1,
2052 dataA->cAlternateFileName, sizeof(dataA->cAlternateFileName), NULL, NULL );
2056 /*************************************************************************
2057 * FindFirstFileW (KERNEL32.@)
2059 HANDLE WINAPI FindFirstFileW( LPCWSTR lpFileName, WIN32_FIND_DATAW *lpFindData )
2061 return FindFirstFileExW(lpFileName, FindExInfoStandard, lpFindData,
2062 FindExSearchNameMatch, NULL, 0);
2065 /*************************************************************************
2066 * FindNextFileW (KERNEL32.@)
2068 BOOL WINAPI FindNextFileW( HANDLE handle, WIN32_FIND_DATAW *data )
2070 FIND_FIRST_INFO *info;
2072 DWORD gle = ERROR_NO_MORE_FILES;
2074 if ((handle == INVALID_HANDLE_VALUE) ||
2075 !(info = (FIND_FIRST_INFO *)GlobalLock( handle )))
2077 SetLastError( ERROR_INVALID_HANDLE );
2080 if (info->drive == -1)
2082 ret = SMB_FindNext( info->u.smb_dir, data );
2085 SMB_CloseDir( info->u.smb_dir );
2086 HeapFree( GetProcessHeap(), 0, info->path );
2090 else if (!info->path || !info->u.dos_dir)
2094 else if (!DOSFS_FindNextEx( info, data ))
2096 DOSFS_CloseDir( info->u.dos_dir ); info->u.dos_dir = NULL;
2097 HeapFree( GetProcessHeap(), 0, info->path );
2099 HeapFree( GetProcessHeap(), 0, info->long_mask );
2100 info->long_mask = NULL;
2105 GlobalUnlock( handle );
2106 if( !ret ) SetLastError( gle );
2111 /*************************************************************************
2112 * FindNextFileA (KERNEL32.@)
2114 BOOL WINAPI FindNextFileA( HANDLE handle, WIN32_FIND_DATAA *data )
2116 WIN32_FIND_DATAW dataW;
2117 if (!FindNextFileW( handle, &dataW )) return FALSE;
2118 data->dwFileAttributes = dataW.dwFileAttributes;
2119 data->ftCreationTime = dataW.ftCreationTime;
2120 data->ftLastAccessTime = dataW.ftLastAccessTime;
2121 data->ftLastWriteTime = dataW.ftLastWriteTime;
2122 data->nFileSizeHigh = dataW.nFileSizeHigh;
2123 data->nFileSizeLow = dataW.nFileSizeLow;
2124 WideCharToMultiByte( CP_ACP, 0, dataW.cFileName, -1,
2125 data->cFileName, sizeof(data->cFileName), NULL, NULL );
2126 WideCharToMultiByte( CP_ACP, 0, dataW.cAlternateFileName, -1,
2127 data->cAlternateFileName,
2128 sizeof(data->cAlternateFileName), NULL, NULL );
2132 /*************************************************************************
2133 * FindClose (KERNEL32.@)
2135 BOOL WINAPI FindClose( HANDLE handle )
2137 FIND_FIRST_INFO *info;
2139 if (handle == INVALID_HANDLE_VALUE) goto error;
2143 if ((info = (FIND_FIRST_INFO *)GlobalLock( handle )))
2145 if (info->u.dos_dir) DOSFS_CloseDir( info->u.dos_dir );
2146 if (info->path) HeapFree( GetProcessHeap(), 0, info->path );
2147 if (info->long_mask) HeapFree( GetProcessHeap(), 0, info->long_mask );
2150 __EXCEPT(page_fault)
2152 WARN("Illegal handle %p\n", handle);
2153 SetLastError( ERROR_INVALID_HANDLE );
2157 if (!info) goto error;
2158 GlobalUnlock( handle );
2159 GlobalFree( handle );
2163 SetLastError( ERROR_INVALID_HANDLE );
2167 /***********************************************************************
2168 * DOSFS_UnixTimeToFileTime
2170 * Convert a Unix time to FILETIME format.
2171 * The FILETIME structure is a 64-bit value representing the number of
2172 * 100-nanosecond intervals since January 1, 1601, 0:00.
2173 * 'remainder' is the nonnegative number of 100-ns intervals
2174 * corresponding to the time fraction smaller than 1 second that
2175 * couldn't be stored in the time_t value.
2177 void DOSFS_UnixTimeToFileTime( time_t unix_time, FILETIME *filetime,
2183 The time difference between 1 January 1601, 00:00:00 and
2184 1 January 1970, 00:00:00 is 369 years, plus the leap years
2185 from 1604 to 1968, excluding 1700, 1800, 1900.
2186 This makes (1968 - 1600) / 4 - 3 = 89 leap days, and a total
2189 Any day in that period had 24 * 60 * 60 = 86400 seconds.
2191 The time difference is 134774 * 86400 * 10000000, which can be written
2193 27111902 * 2^32 + 3577643008
2194 413 * 2^48 + 45534 * 2^32 + 54590 * 2^16 + 32768
2196 If you find that these constants are buggy, please change them in all
2197 instances in both conversion functions.
2200 There are two versions, one of them uses long long variables and
2201 is presumably faster but not ISO C. The other one uses standard C
2202 data types and operations but relies on the assumption that negative
2203 numbers are stored as 2's complement (-1 is 0xffff....). If this
2204 assumption is violated, dates before 1970 will not convert correctly.
2205 This should however work on any reasonable architecture where WINE
2210 Take care not to remove the casts. I have tested these functions
2211 (in both versions) for a lot of numbers. I would be interested in
2212 results on other compilers than GCC.
2214 The operations have been designed to account for the possibility
2215 of 64-bit time_t in future UNICES. Even the versions without
2216 internal long long numbers will work if time_t only is 64 bit.
2217 A 32-bit shift, which was necessary for that operation, turned out
2218 not to work correctly in GCC, besides giving the warning. So I
2219 used a double 16-bit shift instead. Numbers are in the ISO version
2220 represented by three limbs, the most significant with 32 bit, the
2221 other two with 16 bit each.
2223 As the modulo-operator % is not well-defined for negative numbers,
2224 negative divisors have been avoided in DOSFS_FileTimeToUnixTime.
2226 There might be quicker ways to do this in C. Certainly so in
2229 Claus Fischer, fischer@iue.tuwien.ac.at
2232 #if SIZEOF_LONG_LONG >= 8
2233 # define USE_LONG_LONG 1
2235 # define USE_LONG_LONG 0
2238 #if USE_LONG_LONG /* gcc supports long long type */
2240 long long int t = unix_time;
2242 t += 116444736000000000LL;
2244 filetime->dwLowDateTime = (UINT)t;
2245 filetime->dwHighDateTime = (UINT)(t >> 32);
2247 #else /* ISO version */
2249 UINT a0; /* 16 bit, low bits */
2250 UINT a1; /* 16 bit, medium bits */
2251 UINT a2; /* 32 bit, high bits */
2253 /* Copy the unix time to a2/a1/a0 */
2254 a0 = unix_time & 0xffff;
2255 a1 = (unix_time >> 16) & 0xffff;
2256 /* This is obsolete if unix_time is only 32 bits, but it does not hurt.
2257 Do not replace this by >> 32, it gives a compiler warning and it does
2259 a2 = (unix_time >= 0 ? (unix_time >> 16) >> 16 :
2260 ~((~unix_time >> 16) >> 16));
2262 /* Multiply a by 10000000 (a = a2/a1/a0)
2263 Split the factor into 10000 * 1000 which are both less than 0xffff. */
2265 a1 = a1 * 10000 + (a0 >> 16);
2266 a2 = a2 * 10000 + (a1 >> 16);
2271 a1 = a1 * 1000 + (a0 >> 16);
2272 a2 = a2 * 1000 + (a1 >> 16);
2276 /* Add the time difference and the remainder */
2277 a0 += 32768 + (remainder & 0xffff);
2278 a1 += 54590 + (remainder >> 16 ) + (a0 >> 16);
2279 a2 += 27111902 + (a1 >> 16);
2284 filetime->dwLowDateTime = (a1 << 16) + a0;
2285 filetime->dwHighDateTime = a2;
2290 /***********************************************************************
2291 * DOSFS_FileTimeToUnixTime
2293 * Convert a FILETIME format to Unix time.
2294 * If not NULL, 'remainder' contains the fractional part of the filetime,
2295 * in the range of [0..9999999] (even if time_t is negative).
2297 time_t DOSFS_FileTimeToUnixTime( const FILETIME *filetime, DWORD *remainder )
2299 /* Read the comment in the function DOSFS_UnixTimeToFileTime. */
2302 long long int t = filetime->dwHighDateTime;
2304 t += (UINT)filetime->dwLowDateTime;
2305 t -= 116444736000000000LL;
2308 if (remainder) *remainder = 9999999 - (-t - 1) % 10000000;
2309 return -1 - ((-t - 1) / 10000000);
2313 if (remainder) *remainder = t % 10000000;
2314 return t / 10000000;
2317 #else /* ISO version */
2319 UINT a0; /* 16 bit, low bits */
2320 UINT a1; /* 16 bit, medium bits */
2321 UINT a2; /* 32 bit, high bits */
2322 UINT r; /* remainder of division */
2323 unsigned int carry; /* carry bit for subtraction */
2324 int negative; /* whether a represents a negative value */
2326 /* Copy the time values to a2/a1/a0 */
2327 a2 = (UINT)filetime->dwHighDateTime;
2328 a1 = ((UINT)filetime->dwLowDateTime ) >> 16;
2329 a0 = ((UINT)filetime->dwLowDateTime ) & 0xffff;
2331 /* Subtract the time difference */
2332 if (a0 >= 32768 ) a0 -= 32768 , carry = 0;
2333 else a0 += (1 << 16) - 32768 , carry = 1;
2335 if (a1 >= 54590 + carry) a1 -= 54590 + carry, carry = 0;
2336 else a1 += (1 << 16) - 54590 - carry, carry = 1;
2338 a2 -= 27111902 + carry;
2340 /* If a is negative, replace a by (-1-a) */
2341 negative = (a2 >= ((UINT)1) << 31);
2344 /* Set a to -a - 1 (a is a2/a1/a0) */
2350 /* Divide a by 10000000 (a = a2/a1/a0), put the rest into r.
2351 Split the divisor into 10000 * 1000 which are both less than 0xffff. */
2352 a1 += (a2 % 10000) << 16;
2354 a0 += (a1 % 10000) << 16;
2359 a1 += (a2 % 1000) << 16;
2361 a0 += (a1 % 1000) << 16;
2363 r += (a0 % 1000) * 10000;
2366 /* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
2369 /* Set a to -a - 1 (a is a2/a1/a0) */
2377 if (remainder) *remainder = r;
2379 /* Do not replace this by << 32, it gives a compiler warning and it does
2381 return ((((time_t)a2) << 16) << 16) + (a1 << 16) + a0;
2386 /***********************************************************************
2387 * MulDiv (KERNEL32.@)
2389 * Result of multiplication and division
2390 * -1: Overflow occurred or Divisor was 0
2397 #if SIZEOF_LONG_LONG >= 8
2400 if (!nDivisor) return -1;
2402 /* We want to deal with a positive divisor to simplify the logic. */
2405 nMultiplicand = - nMultiplicand;
2406 nDivisor = -nDivisor;
2409 /* If the result is positive, we "add" to round. else, we subtract to round. */
2410 if ( ( (nMultiplicand < 0) && (nMultiplier < 0) ) ||
2411 ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
2412 ret = (((long long)nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
2414 ret = (((long long)nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
2416 if ((ret > 2147483647) || (ret < -2147483647)) return -1;
2419 if (!nDivisor) return -1;
2421 /* We want to deal with a positive divisor to simplify the logic. */
2424 nMultiplicand = - nMultiplicand;
2425 nDivisor = -nDivisor;
2428 /* If the result is positive, we "add" to round. else, we subtract to round. */
2429 if ( ( (nMultiplicand < 0) && (nMultiplier < 0) ) ||
2430 ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
2431 return ((nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
2433 return ((nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
2439 /***********************************************************************
2440 * DosDateTimeToFileTime (KERNEL32.@)
2442 BOOL WINAPI DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft)
2447 time_t time1, time2;
2450 newtm.tm_sec = (fattime & 0x1f) * 2;
2451 newtm.tm_min = (fattime >> 5) & 0x3f;
2452 newtm.tm_hour = (fattime >> 11);
2453 newtm.tm_mday = (fatdate & 0x1f);
2454 newtm.tm_mon = ((fatdate >> 5) & 0x0f) - 1;
2455 newtm.tm_year = (fatdate >> 9) + 80;
2457 RtlSecondsSince1970ToTime( timegm(&newtm), (LARGE_INTEGER *)ft );
2459 time1 = mktime(&newtm);
2460 gtm = gmtime(&time1);
2461 time2 = mktime(gtm);
2462 RtlSecondsSince1970ToTime( 2*time1-time2, (LARGE_INTEGER *)ft );
2468 /***********************************************************************
2469 * FileTimeToDosDateTime (KERNEL32.@)
2471 BOOL WINAPI FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate,
2474 time_t unixtime = DOSFS_FileTimeToUnixTime( ft, NULL );
2475 struct tm *tm = gmtime( &unixtime );
2477 *fattime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
2479 *fatdate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
2485 /***********************************************************************
2486 * QueryDosDeviceA (KERNEL32.@)
2488 * returns array of strings terminated by \0, terminated by \0
2490 DWORD WINAPI QueryDosDeviceA(LPCSTR devname,LPSTR target,DWORD bufsize)
2495 TRACE("(%s,...)\n", devname ? devname : "<null>");
2497 /* return known MSDOS devices */
2498 static const char devices[24] = "CON\0COM1\0COM2\0LPT1\0NUL\0\0";
2499 memcpy( target, devices, min(bufsize,sizeof(devices)) );
2500 return min(bufsize,sizeof(devices));
2502 /* In theory all that are possible and have been defined.
2503 * Now just those below, since mirc uses it to check for special files.
2505 * (It is more complex, and supports netmounted stuff, and \\.\ stuff,
2506 * but currently we just ignore that.)
2508 #define CHECK(x) (strstr(devname,#x)==devname)
2509 if (CHECK(con) || CHECK(com) || CHECK(lpt) || CHECK(nul)) {
2510 strcpy(buffer,"\\DEV\\");
2511 strcat(buffer,devname);
2512 if ((s=strchr(buffer,':'))) *s='\0';
2513 lstrcpynA(target,buffer,bufsize);
2514 return strlen(buffer)+1;
2516 if (strchr(devname,':') || devname[0]=='\\') {
2517 /* This might be a DOS device we do not handle yet ... */
2518 FIXME("(%s) not detected as DOS device!\n",devname);
2520 SetLastError(ERROR_DEV_NOT_EXIST);
2527 /***********************************************************************
2528 * QueryDosDeviceW (KERNEL32.@)
2530 * returns array of strings terminated by \0, terminated by \0
2532 DWORD WINAPI QueryDosDeviceW(LPCWSTR devname,LPWSTR target,DWORD bufsize)
2534 LPSTR devnameA = devname?HEAP_strdupWtoA(GetProcessHeap(),0,devname):NULL;
2535 LPSTR targetA = (LPSTR)HeapAlloc(GetProcessHeap(),0,bufsize);
2536 DWORD ret = QueryDosDeviceA(devnameA,targetA,bufsize);
2538 ret = MultiByteToWideChar( CP_ACP, 0, targetA, ret, target, bufsize );
2539 if (devnameA) HeapFree(GetProcessHeap(),0,devnameA);
2540 if (targetA) HeapFree(GetProcessHeap(),0,targetA);
2545 /***********************************************************************
2546 * DefineDosDeviceA (KERNEL32.@)
2548 BOOL WINAPI DefineDosDeviceA(DWORD flags,LPCSTR devname,LPCSTR targetpath) {
2549 FIXME("(0x%08lx,%s,%s),stub!\n",flags,devname,targetpath);
2550 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2555 --- 16 bit functions ---
2558 /*************************************************************************
2559 * FindFirstFile (KERNEL.413)
2561 HANDLE16 WINAPI FindFirstFile16( LPCSTR path, WIN32_FIND_DATAA *data )
2563 DOS_FULL_NAME full_name;
2565 FIND_FIRST_INFO *info;
2566 WCHAR pathW[MAX_PATH];
2571 data->dwReserved0 = data->dwReserved1 = 0x0;
2572 if (!path) return INVALID_HANDLE_VALUE16;
2573 MultiByteToWideChar(CP_ACP, 0, path, -1, pathW, MAX_PATH);
2574 if (!DOSFS_GetFullName( pathW, FALSE, &full_name ))
2575 return INVALID_HANDLE_VALUE16;
2576 if (!(handle = GlobalAlloc16( GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO) )))
2577 return INVALID_HANDLE_VALUE16;
2578 info = (FIND_FIRST_INFO *)GlobalLock16( handle );
2579 info->path = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
2580 strcpy( info->path, full_name.long_name );
2582 codepage = DRIVE_GetCodepage(full_name.drive);
2583 p = strrchr( info->path, '/' );
2585 long_mask_len = MultiByteToWideChar(codepage, 0, p, -1, NULL, 0);
2586 info->long_mask = HeapAlloc( GetProcessHeap(), 0, long_mask_len * sizeof(WCHAR) );
2587 MultiByteToWideChar(codepage, 0, p, -1, info->long_mask, long_mask_len);
2589 info->short_mask = NULL;
2591 info->drive = full_name.drive;
2594 info->u.dos_dir = DOSFS_OpenDir( codepage, info->path );
2596 GlobalUnlock16( handle );
2597 if (!FindNextFile16( handle, data ))
2599 FindClose16( handle );
2600 SetLastError( ERROR_NO_MORE_FILES );
2601 return INVALID_HANDLE_VALUE16;
2606 /*************************************************************************
2607 * FindNextFile (KERNEL.414)
2609 BOOL16 WINAPI FindNextFile16( HANDLE16 handle, WIN32_FIND_DATAA *data )
2611 FIND_FIRST_INFO *info;
2612 WIN32_FIND_DATAW dataW;
2614 DWORD gle = ERROR_NO_MORE_FILES;
2616 if ((handle == INVALID_HANDLE_VALUE16) ||
2617 !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
2619 SetLastError( ERROR_INVALID_HANDLE );
2622 if (!info->path || !info->u.dos_dir)
2626 if (!DOSFS_FindNextEx( info, &dataW ))
2628 DOSFS_CloseDir( info->u.dos_dir ); info->u.dos_dir = NULL;
2629 HeapFree( GetProcessHeap(), 0, info->path );
2631 HeapFree( GetProcessHeap(), 0, info->long_mask );
2632 info->long_mask = NULL;
2638 data->dwFileAttributes = dataW.dwFileAttributes;
2639 data->ftCreationTime = dataW.ftCreationTime;
2640 data->ftLastAccessTime = dataW.ftLastAccessTime;
2641 data->ftLastWriteTime = dataW.ftLastWriteTime;
2642 data->nFileSizeHigh = dataW.nFileSizeHigh;
2643 data->nFileSizeLow = dataW.nFileSizeLow;
2644 WideCharToMultiByte( CP_ACP, 0, dataW.cFileName, -1,
2645 data->cFileName, sizeof(data->cFileName), NULL, NULL );
2646 WideCharToMultiByte( CP_ACP, 0, dataW.cAlternateFileName, -1,
2647 data->cAlternateFileName,
2648 sizeof(data->cAlternateFileName), NULL, NULL );
2650 if( !ret ) SetLastError( gle );
2651 GlobalUnlock16( handle );
2656 /*************************************************************************
2657 * FindClose (KERNEL.415)
2659 BOOL16 WINAPI FindClose16( HANDLE16 handle )
2661 FIND_FIRST_INFO *info;
2663 if ((handle == INVALID_HANDLE_VALUE16) ||
2664 !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
2666 SetLastError( ERROR_INVALID_HANDLE );
2669 if (info->u.dos_dir) DOSFS_CloseDir( info->u.dos_dir );
2670 if (info->path) HeapFree( GetProcessHeap(), 0, info->path );
2671 if (info->long_mask) HeapFree( GetProcessHeap(), 0, info->long_mask );
2672 GlobalUnlock16( handle );
2673 GlobalFree16( handle );