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>
45 #include "wine/unicode.h"
46 #include "wine/winbase16.h"
52 #include "wine/server.h"
53 #include "msvcrt/excpt.h"
55 #include "wine/debug.h"
57 WINE_DEFAULT_DEBUG_CHANNEL(dosfs);
58 WINE_DECLARE_DEBUG_CHANNEL(file);
60 /* Define the VFAT ioctl to get both short and long file names */
61 /* FIXME: is it possible to get this to work on other systems? */
63 /* We want the real kernel dirent structure, not the libc one */
68 unsigned short d_reclen;
72 #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, KERNEL_DIRENT [2] )
75 #undef VFAT_IOCTL_READDIR_BOTH /* just in case... */
78 /* Chars we don't want to see in DOS file names */
79 #define INVALID_DOS_CHARS "*?<>|\"+=,;[] \345"
81 static const DOS_DEVICE DOSFS_Devices[] =
82 /* name, device flags (see Int 21/AX=0x4400) */
96 { "SCSIMGR$", 0xc0c0 },
98 { "EMMXXXX0", 0x0000 }
101 #define GET_DRIVE(path) \
102 (((path)[1] == ':') ? FILE_toupper((path)[0]) - 'A' : DOSFS_CurDrive)
104 /* Directory info for DOSFS_ReadDir */
108 #ifdef VFAT_IOCTL_READDIR_BOTH
111 KERNEL_DIRENT dirent[2];
115 /* Info structure for FindFirstFile handle */
128 static WINE_EXCEPTION_FILTER(page_fault)
130 if (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION)
131 return EXCEPTION_EXECUTE_HANDLER;
132 return EXCEPTION_CONTINUE_SEARCH;
136 /***********************************************************************
139 * Return 1 if Unix file 'name' is also a valid MS-DOS name
140 * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
141 * File name can be terminated by '\0', '\\' or '/'.
143 static int DOSFS_ValidDOSName( const char *name, int ignore_case )
145 static const char invalid_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS;
146 const char *p = name;
147 const char *invalid = ignore_case ? (invalid_chars + 26) : invalid_chars;
152 /* Check for "." and ".." */
155 /* All other names beginning with '.' are invalid */
156 return (IS_END_OF_NAME(*p));
158 while (!IS_END_OF_NAME(*p))
160 if (strchr( invalid, *p )) return 0; /* Invalid char */
161 if (*p == '.') break; /* Start of the extension */
162 if (++len > 8) return 0; /* Name too long */
165 if (*p != '.') return 1; /* End of name */
167 if (IS_END_OF_NAME(*p)) return 0; /* Empty extension not allowed */
169 while (!IS_END_OF_NAME(*p))
171 if (strchr( invalid, *p )) return 0; /* Invalid char */
172 if (*p == '.') return 0; /* Second extension not allowed */
173 if (++len > 3) return 0; /* Extension too long */
180 /***********************************************************************
181 * DOSFS_ToDosFCBFormat
183 * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
184 * expanding wild cards and converting to upper-case in the process.
185 * File name can be terminated by '\0', '\\' or '/'.
186 * Return FALSE if the name is not a valid DOS name.
187 * 'buffer' must be at least 12 characters long.
189 BOOL DOSFS_ToDosFCBFormat( LPCSTR name, LPSTR buffer )
191 static const char invalid_chars[] = INVALID_DOS_CHARS;
192 const char *p = name;
195 /* Check for "." and ".." */
199 strcpy( buffer, ". " );
205 return (!*p || (*p == '/') || (*p == '\\'));
208 for (i = 0; i < 8; i++)
225 if (strchr( invalid_chars, *p )) return FALSE;
226 buffer[i] = FILE_toupper(*p);
234 /* Skip all chars after wildcard up to first dot */
235 while (*p && (*p != '/') && (*p != '\\') && (*p != '.')) p++;
239 /* Check if name too long */
240 if (*p && (*p != '/') && (*p != '\\') && (*p != '.')) return FALSE;
242 if (*p == '.') p++; /* Skip dot */
244 for (i = 8; i < 11; i++)
254 return FALSE; /* Second extension not allowed */
262 if (strchr( invalid_chars, *p )) return FALSE;
263 buffer[i] = FILE_toupper(*p);
270 /* at most 3 character of the extension are processed
271 * is something behind this ?
273 while (*p == '*' || *p == ' ') p++; /* skip wildcards and spaces */
274 return IS_END_OF_NAME(*p);
278 /***********************************************************************
279 * DOSFS_ToDosDTAFormat
281 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
282 * converting to upper-case in the process.
283 * File name can be terminated by '\0', '\\' or '/'.
284 * 'buffer' must be at least 13 characters long.
286 static void DOSFS_ToDosDTAFormat( LPCSTR name, LPSTR buffer )
290 memcpy( buffer, name, 8 );
292 while ((p > buffer) && (p[-1] == ' ')) p--;
294 memcpy( p, name + 8, 3 );
296 while (p[-1] == ' ') p--;
297 if (p[-1] == '.') p--;
302 /***********************************************************************
305 * Check a DOS file name against a mask (both in FCB format).
307 static int DOSFS_MatchShort( const char *mask, const char *name )
310 for (i = 11; i > 0; i--, mask++, name++)
311 if ((*mask != '?') && (*mask != *name)) return 0;
316 /***********************************************************************
319 * Check a long file name against a mask.
321 * Tests (done in W95 DOS shell - case insensitive):
322 * *.txt test1.test.txt *
324 * *.t??????.t* test1.ta.tornado.txt *
325 * *tornado* test1.ta.tornado.txt *
326 * t*t test1.ta.tornado.txt *
328 * ?est??? test1.txt -
329 * *test1.txt* test1.txt *
330 * h?l?o*t.dat hellothisisatest.dat *
332 static int DOSFS_MatchLong( const char *mask, const char *name,
335 const char *lastjoker = NULL;
336 const char *next_to_retry = NULL;
338 if (!strcmp( mask, "*.*" )) return 1;
339 while (*name && *mask)
344 while (*mask == '*') mask++; /* Skip consecutive '*' */
346 if (!*mask) return 1; /* end of mask is all '*', so match */
348 /* skip to the next match after the joker(s) */
349 if (case_sensitive) while (*name && (*name != *mask)) name++;
350 else while (*name && (FILE_toupper(*name) != FILE_toupper(*mask))) name++;
353 next_to_retry = name;
355 else if (*mask != '?')
360 if (*mask != *name) mismatch = 1;
364 if (FILE_toupper(*mask) != FILE_toupper(*name)) mismatch = 1;
378 else /* mismatch ! */
380 if (lastjoker) /* we had an '*', so we can try unlimitedly */
384 /* this scan sequence was a mismatch, so restart
385 * 1 char after the first char we checked last time */
387 name = next_to_retry;
390 return 0; /* bad luck */
399 while ((*mask == '.') || (*mask == '*'))
400 mask++; /* Ignore trailing '.' or '*' in mask */
401 return (!*name && !*mask);
405 /***********************************************************************
408 static DOS_DIR *DOSFS_OpenDir( LPCSTR path )
410 DOS_DIR *dir = HeapAlloc( GetProcessHeap(), 0, sizeof(*dir) );
413 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
417 /* Treat empty path as root directory. This simplifies path split into
418 directory and mask in several other places */
419 if (!*path) path = "/";
421 #ifdef VFAT_IOCTL_READDIR_BOTH
423 /* Check if the VFAT ioctl is supported on this directory */
425 if ((dir->fd = open( path, O_RDONLY )) != -1)
427 if (ioctl( dir->fd, VFAT_IOCTL_READDIR_BOTH, (long)dir->dirent ) == -1)
434 /* Set the file pointer back at the start of the directory */
435 lseek( dir->fd, 0, SEEK_SET );
440 #endif /* VFAT_IOCTL_READDIR_BOTH */
442 /* Now use the standard opendir/readdir interface */
444 if (!(dir->dir = opendir( path )))
446 HeapFree( GetProcessHeap(), 0, dir );
453 /***********************************************************************
456 static void DOSFS_CloseDir( DOS_DIR *dir )
458 #ifdef VFAT_IOCTL_READDIR_BOTH
459 if (dir->fd != -1) close( dir->fd );
460 #endif /* VFAT_IOCTL_READDIR_BOTH */
461 if (dir->dir) closedir( dir->dir );
462 HeapFree( GetProcessHeap(), 0, dir );
466 /***********************************************************************
469 static BOOL DOSFS_ReadDir( DOS_DIR *dir, LPCSTR *long_name,
472 struct dirent *dirent;
474 #ifdef VFAT_IOCTL_READDIR_BOTH
477 if (ioctl( dir->fd, VFAT_IOCTL_READDIR_BOTH, (long)dir->dirent ) != -1) {
478 if (!dir->dirent[0].d_reclen) return FALSE;
479 if (!DOSFS_ToDosFCBFormat( dir->dirent[0].d_name, dir->short_name ))
480 dir->short_name[0] = '\0';
481 *short_name = dir->short_name;
482 if (dir->dirent[1].d_name[0]) *long_name = dir->dirent[1].d_name;
483 else *long_name = dir->dirent[0].d_name;
487 #endif /* VFAT_IOCTL_READDIR_BOTH */
489 if (!(dirent = readdir( dir->dir ))) return FALSE;
490 *long_name = dirent->d_name;
496 /***********************************************************************
499 * Transform a Unix file name into a hashed DOS name. If the name is a valid
500 * DOS name, it is converted to upper-case; otherwise it is replaced by a
501 * hashed version that fits in 8.3 format.
502 * File name can be terminated by '\0', '\\' or '/'.
503 * 'buffer' must be at least 13 characters long.
505 static void DOSFS_Hash( LPCSTR name, LPSTR buffer, BOOL dir_format,
508 static const char invalid_chars[] = INVALID_DOS_CHARS "~.";
509 static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
516 if (dir_format) strcpy( buffer, " " );
518 if (DOSFS_ValidDOSName( name, ignore_case ))
520 /* Check for '.' and '..' */
524 if (!dir_format) buffer[1] = buffer[2] = '\0';
525 if (name[1] == '.') buffer[1] = '.';
529 /* Simply copy the name, converting to uppercase */
531 for (dst = buffer; !IS_END_OF_NAME(*name) && (*name != '.'); name++)
532 *dst++ = FILE_toupper(*name);
535 if (dir_format) dst = buffer + 8;
537 for (name++; !IS_END_OF_NAME(*name); name++)
538 *dst++ = FILE_toupper(*name);
540 if (!dir_format) *dst = '\0';
544 /* Compute the hash code of the file name */
545 /* If you know something about hash functions, feel free to */
546 /* insert a better algorithm here... */
549 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
550 hash = (hash<<3) ^ (hash>>5) ^ FILE_tolower(*p) ^ (FILE_tolower(p[1]) << 8);
551 hash = (hash<<3) ^ (hash>>5) ^ FILE_tolower(*p); /* Last character*/
555 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
556 hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
557 hash = (hash << 3) ^ (hash >> 5) ^ *p; /* Last character */
560 /* Find last dot for start of the extension */
561 for (p = name+1, ext = NULL; !IS_END_OF_NAME(*p); p++)
562 if (*p == '.') ext = p;
563 if (ext && IS_END_OF_NAME(ext[1]))
564 ext = NULL; /* Empty extension ignored */
566 /* Copy first 4 chars, replacing invalid chars with '_' */
567 for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
569 if (IS_END_OF_NAME(*p) || (p == ext)) break;
570 *dst++ = strchr( invalid_chars, *p ) ? '_' : FILE_toupper(*p);
572 /* Pad to 5 chars with '~' */
573 while (i-- >= 0) *dst++ = '~';
575 /* Insert hash code converted to 3 ASCII chars */
576 *dst++ = hash_chars[(hash >> 10) & 0x1f];
577 *dst++ = hash_chars[(hash >> 5) & 0x1f];
578 *dst++ = hash_chars[hash & 0x1f];
580 /* Copy the first 3 chars of the extension (if any) */
583 if (!dir_format) *dst++ = '.';
584 for (i = 3, ext++; (i > 0) && !IS_END_OF_NAME(*ext); i--, ext++)
585 *dst++ = strchr( invalid_chars, *ext ) ? '_' : FILE_toupper(*ext);
587 if (!dir_format) *dst = '\0';
591 /***********************************************************************
594 * Find the Unix file name in a given directory that corresponds to
595 * a file name (either in Unix or DOS format).
596 * File name can be terminated by '\0', '\\' or '/'.
597 * Return TRUE if OK, FALSE if no file name matches.
599 * 'long_buf' must be at least 'long_len' characters long. If the long name
600 * turns out to be larger than that, the function returns FALSE.
601 * 'short_buf' must be at least 13 characters long.
603 BOOL DOSFS_FindUnixName( LPCSTR path, LPCSTR name, LPSTR long_buf,
604 INT long_len, LPSTR short_buf, BOOL ignore_case)
607 LPCSTR long_name, short_name;
608 char dos_name[12], tmp_buf[13];
611 const char *p = strchr( name, '/' );
612 int len = p ? (int)(p - name) : strlen(name);
613 if ((p = strchr( name, '\\' ))) len = min( (int)(p - name), len );
614 /* Ignore trailing dots and spaces */
615 while (len > 1 && (name[len-1] == '.' || name[len-1] == ' ')) len--;
616 if (long_len < len + 1) return FALSE;
618 TRACE("%s,%s\n", path, name );
620 if (!DOSFS_ToDosFCBFormat( name, dos_name )) dos_name[0] = '\0';
622 if (!(dir = DOSFS_OpenDir( path )))
624 WARN("(%s,%s): can't open dir: %s\n",
625 path, name, strerror(errno) );
629 while ((ret = DOSFS_ReadDir( dir, &long_name, &short_name )))
631 /* Check against Unix name */
632 if (len == strlen(long_name))
636 if (!strncmp( long_name, name, len )) break;
640 if (!FILE_strncasecmp( long_name, name, len )) break;
645 /* Check against hashed DOS name */
648 DOSFS_Hash( long_name, tmp_buf, TRUE, ignore_case );
649 short_name = tmp_buf;
651 if (!strcmp( dos_name, short_name )) break;
656 if (long_buf) strcpy( long_buf, long_name );
660 DOSFS_ToDosDTAFormat( short_name, short_buf );
662 DOSFS_Hash( long_name, short_buf, FALSE, ignore_case );
664 TRACE("(%s,%s) -> %s (%s)\n",
665 path, name, long_name, short_buf ? short_buf : "***");
668 WARN("'%s' not found in '%s'\n", name, path);
669 DOSFS_CloseDir( dir );
674 /***********************************************************************
677 * Check if a DOS file name represents a DOS device and return the device.
679 const DOS_DEVICE *DOSFS_GetDevice( const char *name )
684 if (!name) return NULL; /* if FILE_DupUnixHandle was used */
685 if (name[0] && (name[1] == ':')) name += 2;
686 if ((p = strrchr( name, '/' ))) name = p + 1;
687 if ((p = strrchr( name, '\\' ))) name = p + 1;
688 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
690 const char *dev = DOSFS_Devices[i].name;
691 if (!FILE_strncasecmp( dev, name, strlen(dev) ))
693 p = name + strlen( dev );
694 if (!*p || (*p == '.') || (*p == ':')) return &DOSFS_Devices[i];
701 /***********************************************************************
702 * DOSFS_GetDeviceByHandle
704 const DOS_DEVICE *DOSFS_GetDeviceByHandle( HFILE hFile )
706 const DOS_DEVICE *ret = NULL;
707 SERVER_START_REQ( get_file_info )
710 if (!wine_server_call( req ) && (reply->type == FILE_TYPE_UNKNOWN))
712 if ((reply->attr >= 0) &&
713 (reply->attr < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0])))
714 ret = &DOSFS_Devices[reply->attr];
722 /**************************************************************************
723 * DOSFS_CreateCommPort
725 static HANDLE DOSFS_CreateCommPort(LPCSTR name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa)
730 TRACE_(file)("%s %lx %lx\n", name, access, attributes);
732 PROFILE_GetWineIniString("serialports",name,"",devname,sizeof devname);
736 TRACE("opening %s as %s\n", devname, name);
738 SERVER_START_REQ( create_serial )
740 req->access = access;
741 req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
742 req->attributes = attributes;
743 req->sharing = FILE_SHARE_READ|FILE_SHARE_WRITE;
744 wine_server_add_data( req, devname, strlen(devname) );
746 wine_server_call_err( req );
752 ERR("Couldn't open device '%s' ! (check permissions)\n",devname);
754 TRACE("return %08X\n", ret );
758 /***********************************************************************
761 * Open a DOS device. This might not map 1:1 into the UNIX device concept.
762 * Returns 0 on failure.
764 HANDLE DOSFS_OpenDevice( const char *name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa )
770 if (name[0] && (name[1] == ':')) name += 2;
771 if ((p = strrchr( name, '/' ))) name = p + 1;
772 if ((p = strrchr( name, '\\' ))) name = p + 1;
773 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
775 const char *dev = DOSFS_Devices[i].name;
776 if (!FILE_strncasecmp( dev, name, strlen(dev) ))
778 p = name + strlen( dev );
779 if (!*p || (*p == '.') || (*p == ':')) {
781 if (!strcmp(DOSFS_Devices[i].name,"NUL"))
782 return FILE_CreateFile( "/dev/null", access,
783 FILE_SHARE_READ|FILE_SHARE_WRITE, sa,
784 OPEN_EXISTING, 0, 0, TRUE, DRIVE_UNKNOWN );
785 if (!strcmp(DOSFS_Devices[i].name,"CON")) {
787 switch (access & (GENERIC_READ|GENERIC_WRITE)) {
789 to_dup = GetStdHandle( STD_INPUT_HANDLE );
792 to_dup = GetStdHandle( STD_OUTPUT_HANDLE );
795 FIXME("can't open CON read/write\n");
798 if (!DuplicateHandle( GetCurrentProcess(), to_dup, GetCurrentProcess(),
800 sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle,
801 DUPLICATE_SAME_ACCESS ))
805 if (!strcmp(DOSFS_Devices[i].name,"SCSIMGR$") ||
806 !strcmp(DOSFS_Devices[i].name,"HPSCAN") ||
807 !strcmp(DOSFS_Devices[i].name,"EMMXXXX0"))
809 return FILE_CreateDevice( i, access, sa );
812 if( (handle=DOSFS_CreateCommPort(DOSFS_Devices[i].name,access,attributes,sa)) )
814 FIXME("device open %s not supported (yet)\n",DOSFS_Devices[i].name);
823 /***********************************************************************
826 * Get the drive specified by a given path name (DOS or Unix format).
828 static int DOSFS_GetPathDrive( const char **name )
831 const char *p = *name;
833 if (*p && (p[1] == ':'))
835 drive = FILE_toupper(*p) - 'A';
838 else if (*p == '/') /* Absolute Unix path? */
840 if ((drive = DRIVE_FindDriveRoot( name )) == -1)
842 MESSAGE("Warning: %s not accessible from a configured DOS drive\n", *name );
843 /* Assume it really was a DOS name */
844 drive = DRIVE_GetCurrentDrive();
847 else drive = DRIVE_GetCurrentDrive();
849 if (!DRIVE_IsValid(drive))
851 SetLastError( ERROR_INVALID_DRIVE );
858 /***********************************************************************
861 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
862 * Unix name / short DOS name pair.
863 * Return FALSE if one of the path components does not exist. The last path
864 * component is only checked if 'check_last' is non-zero.
865 * The buffers pointed to by 'long_buf' and 'short_buf' must be
866 * at least MAX_PATHNAME_LEN long.
868 BOOL DOSFS_GetFullName( LPCSTR name, BOOL check_last, DOS_FULL_NAME *full )
872 char *p_l, *p_s, *root;
874 TRACE("%s (last=%d)\n", name, check_last );
876 if ((!*name) || (*name=='\n'))
877 { /* error code for Win98 */
878 SetLastError(ERROR_BAD_PATHNAME);
882 if ((full->drive = DOSFS_GetPathDrive( &name )) == -1) return FALSE;
883 flags = DRIVE_GetFlags( full->drive );
885 lstrcpynA( full->long_name, DRIVE_GetRoot( full->drive ),
886 sizeof(full->long_name) );
887 if (full->long_name[1]) root = full->long_name + strlen(full->long_name);
888 else root = full->long_name; /* root directory */
890 strcpy( full->short_name, "A:\\" );
891 full->short_name[0] += full->drive;
893 if ((*name == '\\') || (*name == '/')) /* Absolute path */
895 while ((*name == '\\') || (*name == '/')) name++;
897 else /* Relative path */
899 lstrcpynA( root + 1, DRIVE_GetUnixCwd( full->drive ),
900 sizeof(full->long_name) - (root - full->long_name) - 1 );
901 if (root[1]) *root = '/';
902 lstrcpynA( full->short_name + 3, DRIVE_GetDosCwd( full->drive ),
903 sizeof(full->short_name) - 3 );
906 p_l = full->long_name[1] ? full->long_name + strlen(full->long_name)
908 p_s = full->short_name[3] ? full->short_name + strlen(full->short_name)
909 : full->short_name + 2;
912 while (*name && found)
914 /* Check for '.' and '..' */
918 if (IS_END_OF_NAME(name[1]))
921 while ((*name == '\\') || (*name == '/')) name++;
924 else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
927 while ((*name == '\\') || (*name == '/')) name++;
928 while ((p_l > root) && (*p_l != '/')) p_l--;
929 while ((p_s > full->short_name + 2) && (*p_s != '\\')) p_s--;
930 *p_l = *p_s = '\0'; /* Remove trailing separator */
935 /* Make sure buffers are large enough */
937 if ((p_s >= full->short_name + sizeof(full->short_name) - 14) ||
938 (p_l >= full->long_name + sizeof(full->long_name) - 1))
940 SetLastError( ERROR_PATH_NOT_FOUND );
944 /* Get the long and short name matching the file name */
946 if ((found = DOSFS_FindUnixName( full->long_name, name, p_l + 1,
947 sizeof(full->long_name) - (p_l - full->long_name) - 1,
948 p_s + 1, !(flags & DRIVE_CASE_SENSITIVE) )))
954 while (!IS_END_OF_NAME(*name)) name++;
956 else if (!check_last)
960 while (!IS_END_OF_NAME(*name) &&
961 (p_s < full->short_name + sizeof(full->short_name) - 1) &&
962 (p_l < full->long_name + sizeof(full->long_name) - 1))
964 *p_s++ = FILE_tolower(*name);
965 /* If the drive is case-sensitive we want to create new */
966 /* files in lower-case otherwise we can't reopen them */
967 /* under the same short name. */
968 if (flags & DRIVE_CASE_SENSITIVE) *p_l++ = FILE_tolower(*name);
972 /* Ignore trailing dots and spaces */
973 while(p_l[-1] == '.' || p_l[-1] == ' ') {
979 while ((*name == '\\') || (*name == '/')) name++;
986 SetLastError( ERROR_FILE_NOT_FOUND );
989 if (*name) /* Not last */
991 SetLastError( ERROR_PATH_NOT_FOUND );
995 if (!full->long_name[0]) strcpy( full->long_name, "/" );
996 if (!full->short_name[2]) strcpy( full->short_name + 2, "\\" );
997 TRACE("returning %s = %s\n", full->long_name, full->short_name );
1002 /***********************************************************************
1003 * GetShortPathNameA (KERNEL32.@)
1007 * longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1008 * longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0
1010 * more observations ( with NT 3.51 (WinDD) ):
1011 * longpath <= 8.3 -> just copy longpath to shortpath
1013 * a) file does not exist -> return 0, LastError = ERROR_FILE_NOT_FOUND
1014 * b) file does exist -> set the short filename.
1015 * - trailing slashes are reproduced in the short name, even if the
1016 * file is not a directory
1017 * - the absolute/relative path of the short name is reproduced like found
1019 * - longpath and shortpath may have the same address
1020 * Peter Ganten, 1999
1022 DWORD WINAPI GetShortPathNameA( LPCSTR longpath, LPSTR shortpath,
1025 DOS_FULL_NAME full_name;
1027 DWORD sp = 0, lp = 0;
1031 TRACE("%s\n", debugstr_a(longpath));
1034 SetLastError(ERROR_INVALID_PARAMETER);
1038 SetLastError(ERROR_BAD_PATHNAME);
1042 if ( ( tmpshortpath = HeapAlloc ( GetProcessHeap(), 0, MAX_PATHNAME_LEN ) ) == NULL ) {
1043 SetLastError ( ERROR_NOT_ENOUGH_MEMORY );
1047 if ( ( drive = DOSFS_GetPathDrive ( &longpath )) == -1 ) return 0;
1048 flags = DRIVE_GetFlags ( drive );
1050 tmpshortpath[0] = drive + 'A';
1051 tmpshortpath[1] = ':';
1054 while ( longpath[lp] ) {
1056 /* check for path delimiters and reproduce them */
1057 if ( longpath[lp] == '\\' || longpath[lp] == '/' ) {
1058 if (!sp || tmpshortpath[sp-1]!= '\\')
1060 /* strip double "\\" */
1061 tmpshortpath[sp] = '\\';
1064 tmpshortpath[sp]=0;/*terminate string*/
1069 tmplen = strcspn ( longpath + lp, "\\/" );
1070 lstrcpynA ( tmpshortpath+sp, longpath + lp, tmplen+1 );
1072 /* Check, if the current element is a valid dos name */
1073 if ( DOSFS_ValidDOSName ( longpath + lp, !(flags & DRIVE_CASE_SENSITIVE) ) ) {
1079 /* Check if the file exists and use the existing file name */
1080 if ( DOSFS_GetFullName ( tmpshortpath, TRUE, &full_name ) ) {
1081 strcpy( tmpshortpath+sp, strrchr ( full_name.short_name, '\\' ) + 1 );
1082 sp += strlen ( tmpshortpath+sp );
1087 TRACE("not found!\n" );
1088 SetLastError ( ERROR_FILE_NOT_FOUND );
1091 tmpshortpath[sp] = 0;
1093 lstrcpynA ( shortpath, tmpshortpath, shortlen );
1094 TRACE("returning %s\n", debugstr_a(shortpath) );
1095 tmplen = strlen ( tmpshortpath );
1096 HeapFree ( GetProcessHeap(), 0, tmpshortpath );
1102 /***********************************************************************
1103 * GetShortPathNameW (KERNEL32.@)
1105 DWORD WINAPI GetShortPathNameW( LPCWSTR longpath, LPWSTR shortpath,
1108 LPSTR longpathA, shortpathA;
1111 longpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, longpath );
1112 shortpathA = HeapAlloc ( GetProcessHeap(), 0, shortlen );
1114 ret = GetShortPathNameA ( longpathA, shortpathA, shortlen );
1115 if (shortlen > 0 && !MultiByteToWideChar( CP_ACP, 0, shortpathA, -1, shortpath, shortlen ))
1116 shortpath[shortlen-1] = 0;
1117 HeapFree( GetProcessHeap(), 0, longpathA );
1118 HeapFree( GetProcessHeap(), 0, shortpathA );
1124 /***********************************************************************
1125 * GetLongPathNameA (KERNEL32.@)
1128 * observed (Win2000):
1129 * shortpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1130 * shortpath="": LastError=ERROR_PATH_NOT_FOUND, ret=0
1132 DWORD WINAPI GetLongPathNameA( LPCSTR shortpath, LPSTR longpath,
1135 DOS_FULL_NAME full_name;
1136 char *p, *r, *ll, *ss;
1139 SetLastError(ERROR_INVALID_PARAMETER);
1142 if (!shortpath[0]) {
1143 SetLastError(ERROR_PATH_NOT_FOUND);
1147 if (!DOSFS_GetFullName( shortpath, TRUE, &full_name )) return 0;
1148 lstrcpynA( longpath, full_name.short_name, longlen );
1150 /* Do some hackery to get the long filename. */
1153 ss=longpath+strlen(longpath);
1154 ll=full_name.long_name+strlen(full_name.long_name);
1156 while (ss>=longpath)
1158 /* FIXME: aren't we more paranoid, than needed? */
1159 while ((ss[0]=='\\') && (ss>=longpath)) ss--;
1161 while ((ss[0]!='\\') && (ss>=longpath)) ss--;
1164 /* FIXME: aren't we more paranoid, than needed? */
1165 while ((ll[0]=='/') && (ll>=full_name.long_name)) ll--;
1166 while ((ll[0]!='/') && (ll>=full_name.long_name)) ll--;
1167 if (ll<full_name.long_name)
1169 ERR("Bad longname! (ss=%s ll=%s)\n This should never happen !\n"
1176 /* FIXME: fix for names like "C:\\" (ie. with more '\'s) */
1180 if ((p-longpath)>0) longlen -= (p-longpath);
1181 lstrcpynA( p, ll , longlen);
1183 /* Now, change all '/' to '\' */
1184 for (r=p; r<(p+longlen); r++ )
1185 if (r[0]=='/') r[0]='\\';
1186 return strlen(longpath) - strlen(p) + longlen;
1190 return strlen(longpath);
1194 /***********************************************************************
1195 * GetLongPathNameW (KERNEL32.@)
1197 DWORD WINAPI GetLongPathNameW( LPCWSTR shortpath, LPWSTR longpath,
1200 DOS_FULL_NAME full_name;
1202 LPSTR shortpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, shortpath );
1204 /* FIXME: is it correct to always return a fully qualified short path? */
1205 if (DOSFS_GetFullName( shortpathA, TRUE, &full_name ))
1207 ret = strlen( full_name.short_name );
1208 if (longlen > 0 && !MultiByteToWideChar( CP_ACP, 0, full_name.long_name, -1,
1209 longpath, longlen ))
1210 longpath[longlen-1] = 0;
1212 HeapFree( GetProcessHeap(), 0, shortpathA );
1217 /***********************************************************************
1218 * DOSFS_DoGetFullPathName
1220 * Implementation of GetFullPathNameA/W.
1222 * bon@elektron 000331:
1223 * A test for GetFullPathName with many pathological cases
1224 * now gives identical output for Wine and OSR2
1226 static DWORD DOSFS_DoGetFullPathName( LPCSTR name, DWORD len, LPSTR result,
1230 DOS_FULL_NAME full_name;
1233 char drivecur[]="c:.";
1235 int namelen,drive=0;
1237 if (!name[0]) return 0;
1239 TRACE("passed '%s'\n", name);
1242 /*drive letter given */
1244 driveletter = name[0];
1246 if ((name[1]==':') && ((name[2]=='\\') || (name[2]=='/')))
1247 /*absolute path given */
1249 lstrcpynA(full_name.short_name,name,MAX_PATHNAME_LEN);
1250 drive = (int)FILE_toupper(name[0]) - 'A';
1255 drivecur[0]=driveletter;
1256 else if ((name[0]=='\\') || (name[0]=='/'))
1257 strcpy(drivecur,"\\");
1259 strcpy(drivecur,".");
1261 if (!DOSFS_GetFullName( drivecur, FALSE, &full_name ))
1263 FIXME("internal: error getting drive/path\n");
1266 /* find path that drive letter substitutes*/
1267 drive = (int)FILE_toupper(full_name.short_name[0]) -0x41;
1268 root= DRIVE_GetRoot(drive);
1271 FIXME("internal: error getting DOS Drive Root\n");
1274 if (!strcmp(root,"/"))
1276 /* we have just the last / and we need it. */
1277 p= full_name.long_name;
1281 p= full_name.long_name +strlen(root);
1283 /* append long name (= unix name) to drive */
1284 lstrcpynA(full_name.short_name+2,p,MAX_PATHNAME_LEN-3);
1285 /* append name to treat */
1286 namelen= strlen(full_name.short_name);
1289 p += +2; /* skip drive name when appending */
1290 if (namelen +2 + strlen(p) > MAX_PATHNAME_LEN)
1292 FIXME("internal error: buffer too small\n");
1295 full_name.short_name[namelen++] ='\\';
1296 full_name.short_name[namelen] = 0;
1297 lstrcpynA(full_name.short_name +namelen,p,MAX_PATHNAME_LEN-namelen);
1299 /* reverse all slashes */
1300 for (p=full_name.short_name;
1301 p < full_name.short_name+strlen(full_name.short_name);
1307 /* Use memmove, as areas overlap */
1309 while ((p = strstr(full_name.short_name,"\\..\\")))
1311 if (p > full_name.short_name+2)
1314 q = strrchr(full_name.short_name,'\\');
1315 memmove(q+1,p+4,strlen(p+4)+1);
1319 memmove(full_name.short_name+3,p+4,strlen(p+4)+1);
1322 if ((full_name.short_name[2]=='.')&&(full_name.short_name[3]=='.'))
1324 /* This case istn't treated yet : c:..\test */
1325 memmove(full_name.short_name+2,full_name.short_name+4,
1326 strlen(full_name.short_name+4)+1);
1329 while ((p = strstr(full_name.short_name,"\\.\\")))
1332 memmove(p+1,p+3,strlen(p+3)+1);
1334 if (!(DRIVE_GetFlags(drive) & DRIVE_CASE_PRESERVING))
1335 for (p = full_name.short_name; *p; p++) *p = FILE_toupper(*p);
1336 namelen=strlen(full_name.short_name);
1337 if (!strcmp(full_name.short_name+namelen-3,"\\.."))
1339 /* one more strange case: "c:\test\test1\.."
1341 *(full_name.short_name+namelen-3)=0;
1342 q = strrchr(full_name.short_name,'\\');
1345 if (full_name.short_name[namelen-1]=='.')
1346 full_name.short_name[(namelen--)-1] =0;
1348 if (full_name.short_name[namelen-1]=='\\')
1349 full_name.short_name[(namelen--)-1] =0;
1350 TRACE("got %s\n",full_name.short_name);
1352 /* If the lpBuffer buffer is too small, the return value is the
1353 size of the buffer, in characters, required to hold the path
1354 plus the terminating \0 (tested against win95osr2, bon 001118)
1356 ret = strlen(full_name.short_name);
1359 /* don't touch anything when the buffer is not large enough */
1360 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1366 MultiByteToWideChar( CP_ACP, 0, full_name.short_name, -1, (LPWSTR)result, len );
1368 lstrcpynA( result, full_name.short_name, len );
1371 TRACE("returning '%s'\n", full_name.short_name );
1376 /***********************************************************************
1377 * GetFullPathNameA (KERNEL32.@)
1379 * if the path closed with '\', *lastpart is 0
1381 DWORD WINAPI GetFullPathNameA( LPCSTR name, DWORD len, LPSTR buffer,
1384 DWORD ret = DOSFS_DoGetFullPathName( name, len, buffer, FALSE );
1385 if (ret && (ret<=len) && buffer && lastpart)
1387 LPSTR p = buffer + strlen(buffer);
1391 while ((p > buffer + 2) && (*p != '\\')) p--;
1394 else *lastpart = NULL;
1400 /***********************************************************************
1401 * GetFullPathNameW (KERNEL32.@)
1403 DWORD WINAPI GetFullPathNameW( LPCWSTR name, DWORD len, LPWSTR buffer,
1406 LPSTR nameA = HEAP_strdupWtoA( GetProcessHeap(), 0, name );
1407 DWORD ret = DOSFS_DoGetFullPathName( nameA, len, (LPSTR)buffer, TRUE );
1408 HeapFree( GetProcessHeap(), 0, nameA );
1409 if (ret && (ret<=len) && buffer && lastpart)
1411 LPWSTR p = buffer + strlenW(buffer);
1412 if (*p != (WCHAR)'\\')
1414 while ((p > buffer + 2) && (*p != (WCHAR)'\\')) p--;
1417 else *lastpart = NULL;
1423 /***********************************************************************
1424 * wine_get_unix_file_name (KERNEL32.@) Not a Windows API
1426 * Return the full Unix file name for a given path.
1428 BOOL WINAPI wine_get_unix_file_name( LPCSTR dos, LPSTR buffer, DWORD len )
1432 if ((ret = DOSFS_GetFullName( dos, FALSE, &path ))) lstrcpynA( buffer, path.long_name, len );
1437 /***********************************************************************
1440 static int DOSFS_FindNextEx( FIND_FIRST_INFO *info, WIN32_FIND_DATAA *entry )
1442 DWORD attr = info->attr | FA_UNUSED | FA_ARCHIVE | FA_RDONLY | FILE_ATTRIBUTE_SYMLINK;
1443 UINT flags = DRIVE_GetFlags( info->drive );
1444 char *p, buffer[MAX_PATHNAME_LEN];
1445 const char *drive_path;
1447 LPCSTR long_name, short_name;
1448 BY_HANDLE_FILE_INFORMATION fileinfo;
1451 if ((info->attr & ~(FA_UNUSED | FA_ARCHIVE | FA_RDONLY)) == FA_LABEL)
1453 if (info->cur_pos) return 0;
1454 entry->dwFileAttributes = FILE_ATTRIBUTE_LABEL;
1455 RtlSecondsSince1970ToTime( (time_t)0, &entry->ftCreationTime );
1456 RtlSecondsSince1970ToTime( (time_t)0, &entry->ftLastAccessTime );
1457 RtlSecondsSince1970ToTime( (time_t)0, &entry->ftLastWriteTime );
1458 entry->nFileSizeHigh = 0;
1459 entry->nFileSizeLow = 0;
1460 entry->dwReserved0 = 0;
1461 entry->dwReserved1 = 0;
1462 DOSFS_ToDosDTAFormat( DRIVE_GetLabel( info->drive ), entry->cFileName );
1463 strcpy( entry->cAlternateFileName, entry->cFileName );
1465 TRACE("returning %s (%s) as label\n",
1466 entry->cFileName, entry->cAlternateFileName);
1470 drive_path = info->path + strlen(DRIVE_GetRoot( info->drive ));
1471 while ((*drive_path == '/') || (*drive_path == '\\')) drive_path++;
1472 drive_root = !*drive_path;
1474 lstrcpynA( buffer, info->path, sizeof(buffer) - 1 );
1475 strcat( buffer, "/" );
1476 p = buffer + strlen(buffer);
1478 while (DOSFS_ReadDir( info->dir, &long_name, &short_name ))
1482 /* Don't return '.' and '..' in the root of the drive */
1483 if (drive_root && (long_name[0] == '.') &&
1484 (!long_name[1] || ((long_name[1] == '.') && !long_name[2])))
1487 /* Check the long mask */
1489 if (info->long_mask)
1491 if (!DOSFS_MatchLong( info->long_mask, long_name,
1492 flags & DRIVE_CASE_SENSITIVE )) continue;
1495 /* Check the short mask */
1497 if (info->short_mask)
1501 DOSFS_Hash( long_name, dos_name, TRUE,
1502 !(flags & DRIVE_CASE_SENSITIVE) );
1503 short_name = dos_name;
1505 if (!DOSFS_MatchShort( info->short_mask, short_name )) continue;
1508 /* Check the file attributes */
1510 lstrcpynA( p, long_name, sizeof(buffer) - (int)(p - buffer) );
1511 if (!FILE_Stat( buffer, &fileinfo ))
1513 WARN("can't stat %s\n", buffer);
1516 if ((fileinfo.dwFileAttributes & FILE_ATTRIBUTE_SYMLINK) &&
1517 (fileinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
1519 static int show_dir_symlinks = -1;
1520 if (show_dir_symlinks == -1)
1521 show_dir_symlinks = PROFILE_GetWineIniBool("wine", "ShowDirSymlinks", 0);
1522 if (!show_dir_symlinks) continue;
1525 if (fileinfo.dwFileAttributes & ~attr) continue;
1527 /* We now have a matching entry; fill the result and return */
1529 entry->dwFileAttributes = fileinfo.dwFileAttributes;
1530 entry->ftCreationTime = fileinfo.ftCreationTime;
1531 entry->ftLastAccessTime = fileinfo.ftLastAccessTime;
1532 entry->ftLastWriteTime = fileinfo.ftLastWriteTime;
1533 entry->nFileSizeHigh = fileinfo.nFileSizeHigh;
1534 entry->nFileSizeLow = fileinfo.nFileSizeLow;
1537 DOSFS_ToDosDTAFormat( short_name, entry->cAlternateFileName );
1539 DOSFS_Hash( long_name, entry->cAlternateFileName, FALSE,
1540 !(flags & DRIVE_CASE_SENSITIVE) );
1542 lstrcpynA( entry->cFileName, long_name, sizeof(entry->cFileName) );
1543 if (!(flags & DRIVE_CASE_PRESERVING)) _strlwr( entry->cFileName );
1544 TRACE("returning %s (%s) %02lx %ld\n",
1545 entry->cFileName, entry->cAlternateFileName,
1546 entry->dwFileAttributes, entry->nFileSizeLow );
1549 return 0; /* End of directory */
1552 /***********************************************************************
1555 * Find the next matching file. Return the number of entries read to find
1556 * the matching one, or 0 if no more entries.
1557 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
1558 * file name mask. Either or both can be NULL.
1560 * NOTE: This is supposed to be only called by the int21 emulation
1561 * routines. Thus, we should own the Win16Mutex anyway.
1562 * Nevertheless, we explicitly enter it to ensure the static
1563 * directory cache is protected.
1565 int DOSFS_FindNext( const char *path, const char *short_mask,
1566 const char *long_mask, int drive, BYTE attr,
1567 int skip, WIN32_FIND_DATAA *entry )
1569 static FIND_FIRST_INFO info;
1570 LPCSTR short_name, long_name;
1575 /* Check the cached directory */
1576 if (!(info.dir && info.path == path && info.short_mask == short_mask
1577 && info.long_mask == long_mask && info.drive == drive
1578 && info.attr == attr && info.cur_pos <= skip))
1580 /* Not in the cache, open it anew */
1581 if (info.dir) DOSFS_CloseDir( info.dir );
1583 info.path = (LPSTR)path;
1584 info.long_mask = (LPSTR)long_mask;
1585 info.short_mask = (LPSTR)short_mask;
1589 info.dir = DOSFS_OpenDir( info.path );
1592 /* Skip to desired position */
1593 while (info.cur_pos < skip)
1594 if (info.dir && DOSFS_ReadDir( info.dir, &long_name, &short_name ))
1599 if (info.dir && info.cur_pos == skip && DOSFS_FindNextEx( &info, entry ))
1600 count = info.cur_pos - skip;
1606 if (info.dir) DOSFS_CloseDir( info.dir );
1607 memset( &info, '\0', sizeof(info) );
1615 /*************************************************************************
1616 * FindFirstFileExA (KERNEL32.@)
1618 HANDLE WINAPI FindFirstFileExA(
1620 FINDEX_INFO_LEVELS fInfoLevelId,
1621 LPVOID lpFindFileData,
1622 FINDEX_SEARCH_OPS fSearchOp,
1623 LPVOID lpSearchFilter,
1624 DWORD dwAdditionalFlags)
1626 DOS_FULL_NAME full_name;
1628 FIND_FIRST_INFO *info;
1630 if ((fSearchOp != FindExSearchNameMatch) || (dwAdditionalFlags != 0))
1632 FIXME("options not implemented 0x%08x 0x%08lx\n", fSearchOp, dwAdditionalFlags );
1633 return INVALID_HANDLE_VALUE;
1636 switch(fInfoLevelId)
1638 case FindExInfoStandard:
1640 WIN32_FIND_DATAA * data = (WIN32_FIND_DATAA *) lpFindFileData;
1641 data->dwReserved0 = data->dwReserved1 = 0x0;
1642 if (!lpFileName) return 0;
1643 if (!DOSFS_GetFullName( lpFileName, FALSE, &full_name )) break;
1644 if (!(handle = GlobalAlloc(GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO)))) break;
1645 info = (FIND_FIRST_INFO *)GlobalLock( handle );
1646 info->path = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
1647 strcpy( info->path, full_name.long_name );
1648 info->long_mask = strrchr( info->path, '/' );
1649 *(info->long_mask++) = '\0';
1650 info->short_mask = NULL;
1652 if (lpFileName[0] && (lpFileName[1] == ':'))
1653 info->drive = FILE_toupper(*lpFileName) - 'A';
1654 else info->drive = DRIVE_GetCurrentDrive();
1657 info->dir = DOSFS_OpenDir( info->path );
1659 GlobalUnlock( handle );
1660 if (!FindNextFileA( handle, data ))
1662 FindClose( handle );
1663 SetLastError( ERROR_NO_MORE_FILES );
1670 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
1672 return INVALID_HANDLE_VALUE;
1675 /*************************************************************************
1676 * FindFirstFileA (KERNEL32.@)
1678 HANDLE WINAPI FindFirstFileA(
1680 WIN32_FIND_DATAA *lpFindData )
1682 return FindFirstFileExA(lpFileName, FindExInfoStandard, lpFindData,
1683 FindExSearchNameMatch, NULL, 0);
1686 /*************************************************************************
1687 * FindFirstFileExW (KERNEL32.@)
1689 HANDLE WINAPI FindFirstFileExW(
1691 FINDEX_INFO_LEVELS fInfoLevelId,
1692 LPVOID lpFindFileData,
1693 FINDEX_SEARCH_OPS fSearchOp,
1694 LPVOID lpSearchFilter,
1695 DWORD dwAdditionalFlags)
1698 WIN32_FIND_DATAA dataA;
1699 LPVOID _lpFindFileData;
1702 switch(fInfoLevelId)
1704 case FindExInfoStandard:
1706 _lpFindFileData = &dataA;
1710 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
1711 return INVALID_HANDLE_VALUE;
1714 pathA = HEAP_strdupWtoA( GetProcessHeap(), 0, lpFileName );
1715 handle = FindFirstFileExA(pathA, fInfoLevelId, _lpFindFileData, fSearchOp, lpSearchFilter, dwAdditionalFlags);
1716 HeapFree( GetProcessHeap(), 0, pathA );
1717 if (handle == INVALID_HANDLE_VALUE) return handle;
1719 switch(fInfoLevelId)
1721 case FindExInfoStandard:
1723 WIN32_FIND_DATAW *dataW = (WIN32_FIND_DATAW*) lpFindFileData;
1724 dataW->dwFileAttributes = dataA.dwFileAttributes;
1725 dataW->ftCreationTime = dataA.ftCreationTime;
1726 dataW->ftLastAccessTime = dataA.ftLastAccessTime;
1727 dataW->ftLastWriteTime = dataA.ftLastWriteTime;
1728 dataW->nFileSizeHigh = dataA.nFileSizeHigh;
1729 dataW->nFileSizeLow = dataA.nFileSizeLow;
1730 MultiByteToWideChar( CP_ACP, 0, dataA.cFileName, -1,
1731 dataW->cFileName, sizeof(dataW->cFileName)/sizeof(WCHAR) );
1732 MultiByteToWideChar( CP_ACP, 0, dataA.cAlternateFileName, -1,
1733 dataW->cAlternateFileName,
1734 sizeof(dataW->cAlternateFileName)/sizeof(WCHAR) );
1738 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
1739 return INVALID_HANDLE_VALUE;
1744 /*************************************************************************
1745 * FindFirstFileW (KERNEL32.@)
1747 HANDLE WINAPI FindFirstFileW( LPCWSTR lpFileName, WIN32_FIND_DATAW *lpFindData )
1749 return FindFirstFileExW(lpFileName, FindExInfoStandard, lpFindData,
1750 FindExSearchNameMatch, NULL, 0);
1753 /*************************************************************************
1754 * FindNextFileA (KERNEL32.@)
1756 BOOL WINAPI FindNextFileA( HANDLE handle, WIN32_FIND_DATAA *data )
1758 FIND_FIRST_INFO *info;
1760 if ((handle == INVALID_HANDLE_VALUE) ||
1761 !(info = (FIND_FIRST_INFO *)GlobalLock( handle )))
1763 SetLastError( ERROR_INVALID_HANDLE );
1766 GlobalUnlock( handle );
1767 if (!info->path || !info->dir)
1769 SetLastError( ERROR_NO_MORE_FILES );
1772 if (!DOSFS_FindNextEx( info, data ))
1774 DOSFS_CloseDir( info->dir ); info->dir = NULL;
1775 HeapFree( GetProcessHeap(), 0, info->path );
1776 info->path = info->long_mask = NULL;
1777 SetLastError( ERROR_NO_MORE_FILES );
1784 /*************************************************************************
1785 * FindNextFileW (KERNEL32.@)
1787 BOOL WINAPI FindNextFileW( HANDLE handle, WIN32_FIND_DATAW *data )
1789 WIN32_FIND_DATAA dataA;
1790 if (!FindNextFileA( handle, &dataA )) return FALSE;
1791 data->dwFileAttributes = dataA.dwFileAttributes;
1792 data->ftCreationTime = dataA.ftCreationTime;
1793 data->ftLastAccessTime = dataA.ftLastAccessTime;
1794 data->ftLastWriteTime = dataA.ftLastWriteTime;
1795 data->nFileSizeHigh = dataA.nFileSizeHigh;
1796 data->nFileSizeLow = dataA.nFileSizeLow;
1797 MultiByteToWideChar( CP_ACP, 0, dataA.cFileName, -1,
1798 data->cFileName, sizeof(data->cFileName)/sizeof(WCHAR) );
1799 MultiByteToWideChar( CP_ACP, 0, dataA.cAlternateFileName, -1,
1800 data->cAlternateFileName,
1801 sizeof(data->cAlternateFileName)/sizeof(WCHAR) );
1805 /*************************************************************************
1806 * FindClose (KERNEL32.@)
1808 BOOL WINAPI FindClose( HANDLE handle )
1810 FIND_FIRST_INFO *info;
1812 if (handle == INVALID_HANDLE_VALUE) goto error;
1816 if ((info = (FIND_FIRST_INFO *)GlobalLock( handle )))
1818 if (info->dir) DOSFS_CloseDir( info->dir );
1819 if (info->path) HeapFree( GetProcessHeap(), 0, info->path );
1822 __EXCEPT(page_fault)
1824 WARN("Illegal handle %x\n", handle);
1825 SetLastError( ERROR_INVALID_HANDLE );
1829 if (!info) goto error;
1830 GlobalUnlock( handle );
1831 GlobalFree( handle );
1835 SetLastError( ERROR_INVALID_HANDLE );
1839 /***********************************************************************
1840 * DOSFS_UnixTimeToFileTime
1842 * Convert a Unix time to FILETIME format.
1843 * The FILETIME structure is a 64-bit value representing the number of
1844 * 100-nanosecond intervals since January 1, 1601, 0:00.
1845 * 'remainder' is the nonnegative number of 100-ns intervals
1846 * corresponding to the time fraction smaller than 1 second that
1847 * couldn't be stored in the time_t value.
1849 void DOSFS_UnixTimeToFileTime( time_t unix_time, FILETIME *filetime,
1855 The time difference between 1 January 1601, 00:00:00 and
1856 1 January 1970, 00:00:00 is 369 years, plus the leap years
1857 from 1604 to 1968, excluding 1700, 1800, 1900.
1858 This makes (1968 - 1600) / 4 - 3 = 89 leap days, and a total
1861 Any day in that period had 24 * 60 * 60 = 86400 seconds.
1863 The time difference is 134774 * 86400 * 10000000, which can be written
1865 27111902 * 2^32 + 3577643008
1866 413 * 2^48 + 45534 * 2^32 + 54590 * 2^16 + 32768
1868 If you find that these constants are buggy, please change them in all
1869 instances in both conversion functions.
1872 There are two versions, one of them uses long long variables and
1873 is presumably faster but not ISO C. The other one uses standard C
1874 data types and operations but relies on the assumption that negative
1875 numbers are stored as 2's complement (-1 is 0xffff....). If this
1876 assumption is violated, dates before 1970 will not convert correctly.
1877 This should however work on any reasonable architecture where WINE
1882 Take care not to remove the casts. I have tested these functions
1883 (in both versions) for a lot of numbers. I would be interested in
1884 results on other compilers than GCC.
1886 The operations have been designed to account for the possibility
1887 of 64-bit time_t in future UNICES. Even the versions without
1888 internal long long numbers will work if time_t only is 64 bit.
1889 A 32-bit shift, which was necessary for that operation, turned out
1890 not to work correctly in GCC, besides giving the warning. So I
1891 used a double 16-bit shift instead. Numbers are in the ISO version
1892 represented by three limbs, the most significant with 32 bit, the
1893 other two with 16 bit each.
1895 As the modulo-operator % is not well-defined for negative numbers,
1896 negative divisors have been avoided in DOSFS_FileTimeToUnixTime.
1898 There might be quicker ways to do this in C. Certainly so in
1901 Claus Fischer, fischer@iue.tuwien.ac.at
1904 #if SIZEOF_LONG_LONG >= 8
1905 # define USE_LONG_LONG 1
1907 # define USE_LONG_LONG 0
1910 #if USE_LONG_LONG /* gcc supports long long type */
1912 long long int t = unix_time;
1914 t += 116444736000000000LL;
1916 filetime->dwLowDateTime = (UINT)t;
1917 filetime->dwHighDateTime = (UINT)(t >> 32);
1919 #else /* ISO version */
1921 UINT a0; /* 16 bit, low bits */
1922 UINT a1; /* 16 bit, medium bits */
1923 UINT a2; /* 32 bit, high bits */
1925 /* Copy the unix time to a2/a1/a0 */
1926 a0 = unix_time & 0xffff;
1927 a1 = (unix_time >> 16) & 0xffff;
1928 /* This is obsolete if unix_time is only 32 bits, but it does not hurt.
1929 Do not replace this by >> 32, it gives a compiler warning and it does
1931 a2 = (unix_time >= 0 ? (unix_time >> 16) >> 16 :
1932 ~((~unix_time >> 16) >> 16));
1934 /* Multiply a by 10000000 (a = a2/a1/a0)
1935 Split the factor into 10000 * 1000 which are both less than 0xffff. */
1937 a1 = a1 * 10000 + (a0 >> 16);
1938 a2 = a2 * 10000 + (a1 >> 16);
1943 a1 = a1 * 1000 + (a0 >> 16);
1944 a2 = a2 * 1000 + (a1 >> 16);
1948 /* Add the time difference and the remainder */
1949 a0 += 32768 + (remainder & 0xffff);
1950 a1 += 54590 + (remainder >> 16 ) + (a0 >> 16);
1951 a2 += 27111902 + (a1 >> 16);
1956 filetime->dwLowDateTime = (a1 << 16) + a0;
1957 filetime->dwHighDateTime = a2;
1962 /***********************************************************************
1963 * DOSFS_FileTimeToUnixTime
1965 * Convert a FILETIME format to Unix time.
1966 * If not NULL, 'remainder' contains the fractional part of the filetime,
1967 * in the range of [0..9999999] (even if time_t is negative).
1969 time_t DOSFS_FileTimeToUnixTime( const FILETIME *filetime, DWORD *remainder )
1971 /* Read the comment in the function DOSFS_UnixTimeToFileTime. */
1974 long long int t = filetime->dwHighDateTime;
1976 t += (UINT)filetime->dwLowDateTime;
1977 t -= 116444736000000000LL;
1980 if (remainder) *remainder = 9999999 - (-t - 1) % 10000000;
1981 return -1 - ((-t - 1) / 10000000);
1985 if (remainder) *remainder = t % 10000000;
1986 return t / 10000000;
1989 #else /* ISO version */
1991 UINT a0; /* 16 bit, low bits */
1992 UINT a1; /* 16 bit, medium bits */
1993 UINT a2; /* 32 bit, high bits */
1994 UINT r; /* remainder of division */
1995 unsigned int carry; /* carry bit for subtraction */
1996 int negative; /* whether a represents a negative value */
1998 /* Copy the time values to a2/a1/a0 */
1999 a2 = (UINT)filetime->dwHighDateTime;
2000 a1 = ((UINT)filetime->dwLowDateTime ) >> 16;
2001 a0 = ((UINT)filetime->dwLowDateTime ) & 0xffff;
2003 /* Subtract the time difference */
2004 if (a0 >= 32768 ) a0 -= 32768 , carry = 0;
2005 else a0 += (1 << 16) - 32768 , carry = 1;
2007 if (a1 >= 54590 + carry) a1 -= 54590 + carry, carry = 0;
2008 else a1 += (1 << 16) - 54590 - carry, carry = 1;
2010 a2 -= 27111902 + carry;
2012 /* If a is negative, replace a by (-1-a) */
2013 negative = (a2 >= ((UINT)1) << 31);
2016 /* Set a to -a - 1 (a is a2/a1/a0) */
2022 /* Divide a by 10000000 (a = a2/a1/a0), put the rest into r.
2023 Split the divisor into 10000 * 1000 which are both less than 0xffff. */
2024 a1 += (a2 % 10000) << 16;
2026 a0 += (a1 % 10000) << 16;
2031 a1 += (a2 % 1000) << 16;
2033 a0 += (a1 % 1000) << 16;
2035 r += (a0 % 1000) * 10000;
2038 /* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
2041 /* Set a to -a - 1 (a is a2/a1/a0) */
2049 if (remainder) *remainder = r;
2051 /* Do not replace this by << 32, it gives a compiler warning and it does
2053 return ((((time_t)a2) << 16) << 16) + (a1 << 16) + a0;
2058 /***********************************************************************
2059 * MulDiv (KERNEL32.@)
2061 * Result of multiplication and division
2062 * -1: Overflow occurred or Divisor was 0
2069 #if SIZEOF_LONG_LONG >= 8
2072 if (!nDivisor) return -1;
2074 /* We want to deal with a positive divisor to simplify the logic. */
2077 nMultiplicand = - nMultiplicand;
2078 nDivisor = -nDivisor;
2081 /* If the result is positive, we "add" to round. else, we subtract to round. */
2082 if ( ( (nMultiplicand < 0) && (nMultiplier < 0) ) ||
2083 ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
2084 ret = (((long long)nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
2086 ret = (((long long)nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
2088 if ((ret > 2147483647) || (ret < -2147483647)) return -1;
2091 if (!nDivisor) return -1;
2093 /* We want to deal with a positive divisor to simplify the logic. */
2096 nMultiplicand = - nMultiplicand;
2097 nDivisor = -nDivisor;
2100 /* If the result is positive, we "add" to round. else, we subtract to round. */
2101 if ( ( (nMultiplicand < 0) && (nMultiplier < 0) ) ||
2102 ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
2103 return ((nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
2105 return ((nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
2111 /***********************************************************************
2112 * DosDateTimeToFileTime (KERNEL32.@)
2114 BOOL WINAPI DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft)
2119 time_t time1, time2;
2122 newtm.tm_sec = (fattime & 0x1f) * 2;
2123 newtm.tm_min = (fattime >> 5) & 0x3f;
2124 newtm.tm_hour = (fattime >> 11);
2125 newtm.tm_mday = (fatdate & 0x1f);
2126 newtm.tm_mon = ((fatdate >> 5) & 0x0f) - 1;
2127 newtm.tm_year = (fatdate >> 9) + 80;
2129 RtlSecondsSince1970ToTime( timegm(&newtm), ft );
2131 time1 = mktime(&newtm);
2132 gtm = gmtime(&time1);
2133 time2 = mktime(gtm);
2134 RtlSecondsSince1970ToTime( 2*time1-time2, ft );
2140 /***********************************************************************
2141 * FileTimeToDosDateTime (KERNEL32.@)
2143 BOOL WINAPI FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate,
2146 time_t unixtime = DOSFS_FileTimeToUnixTime( ft, NULL );
2147 struct tm *tm = gmtime( &unixtime );
2149 *fattime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
2151 *fatdate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
2157 /***********************************************************************
2158 * LocalFileTimeToFileTime (KERNEL32.@)
2160 BOOL WINAPI LocalFileTimeToFileTime( const FILETIME *localft,
2167 /* Converts from local to UTC. */
2168 time_t localtime = DOSFS_FileTimeToUnixTime( localft, &remainder );
2169 xtm = gmtime( &localtime );
2170 utctime = mktime(xtm);
2171 if(xtm->tm_isdst > 0) utctime-=3600;
2172 DOSFS_UnixTimeToFileTime( utctime, utcft, remainder );
2177 /***********************************************************************
2178 * FileTimeToLocalFileTime (KERNEL32.@)
2180 BOOL WINAPI FileTimeToLocalFileTime( const FILETIME *utcft,
2181 LPFILETIME localft )
2184 /* Converts from UTC to local. */
2185 time_t unixtime = DOSFS_FileTimeToUnixTime( utcft, &remainder );
2187 struct tm *xtm = localtime( &unixtime );
2190 localtime = timegm(xtm);
2191 DOSFS_UnixTimeToFileTime( localtime, localft, remainder );
2197 xtm = gmtime( &unixtime );
2199 if(xtm->tm_isdst > 0) time-=3600;
2200 DOSFS_UnixTimeToFileTime( 2*unixtime-time, localft, remainder );
2206 /***********************************************************************
2207 * FileTimeToSystemTime (KERNEL32.@)
2209 BOOL WINAPI FileTimeToSystemTime( const FILETIME *ft, LPSYSTEMTIME syst )
2213 time_t xtime = DOSFS_FileTimeToUnixTime( ft, &remainder );
2214 xtm = gmtime(&xtime);
2215 syst->wYear = xtm->tm_year+1900;
2216 syst->wMonth = xtm->tm_mon + 1;
2217 syst->wDayOfWeek = xtm->tm_wday;
2218 syst->wDay = xtm->tm_mday;
2219 syst->wHour = xtm->tm_hour;
2220 syst->wMinute = xtm->tm_min;
2221 syst->wSecond = xtm->tm_sec;
2222 syst->wMilliseconds = remainder / 10000;
2226 /***********************************************************************
2227 * QueryDosDeviceA (KERNEL32.@)
2229 * returns array of strings terminated by \0, terminated by \0
2231 DWORD WINAPI QueryDosDeviceA(LPCSTR devname,LPSTR target,DWORD bufsize)
2236 TRACE("(%s,...)\n", devname ? devname : "<null>");
2238 /* return known MSDOS devices */
2239 static const char devices[24] = "CON\0COM1\0COM2\0LPT1\0NUL\0\0";
2240 memcpy( target, devices, min(bufsize,sizeof(devices)) );
2241 return min(bufsize,sizeof(devices));
2243 /* In theory all that are possible and have been defined.
2244 * Now just those below, since mirc uses it to check for special files.
2246 * (It is more complex, and supports netmounted stuff, and \\.\ stuff,
2247 * but currently we just ignore that.)
2249 #define CHECK(x) (strstr(devname,#x)==devname)
2250 if (CHECK(con) || CHECK(com) || CHECK(lpt) || CHECK(nul)) {
2251 strcpy(buffer,"\\DEV\\");
2252 strcat(buffer,devname);
2253 if ((s=strchr(buffer,':'))) *s='\0';
2254 lstrcpynA(target,buffer,bufsize);
2255 return strlen(buffer)+1;
2257 if (strchr(devname,':') || devname[0]=='\\') {
2258 /* This might be a DOS device we do not handle yet ... */
2259 FIXME("(%s) not detected as DOS device!\n",devname);
2261 SetLastError(ERROR_DEV_NOT_EXIST);
2268 /***********************************************************************
2269 * QueryDosDeviceW (KERNEL32.@)
2271 * returns array of strings terminated by \0, terminated by \0
2273 DWORD WINAPI QueryDosDeviceW(LPCWSTR devname,LPWSTR target,DWORD bufsize)
2275 LPSTR devnameA = devname?HEAP_strdupWtoA(GetProcessHeap(),0,devname):NULL;
2276 LPSTR targetA = (LPSTR)HeapAlloc(GetProcessHeap(),0,bufsize);
2277 DWORD ret = QueryDosDeviceA(devnameA,targetA,bufsize);
2279 ret = MultiByteToWideChar( CP_ACP, 0, targetA, ret, target, bufsize );
2280 if (devnameA) HeapFree(GetProcessHeap(),0,devnameA);
2281 if (targetA) HeapFree(GetProcessHeap(),0,targetA);
2286 /***********************************************************************
2287 * SystemTimeToFileTime (KERNEL32.@)
2289 BOOL WINAPI SystemTimeToFileTime( const SYSTEMTIME *syst, LPFILETIME ft )
2295 struct tm xtm,*utc_tm;
2296 time_t localtim,utctime;
2299 xtm.tm_year = syst->wYear-1900;
2300 xtm.tm_mon = syst->wMonth - 1;
2301 xtm.tm_wday = syst->wDayOfWeek;
2302 xtm.tm_mday = syst->wDay;
2303 xtm.tm_hour = syst->wHour;
2304 xtm.tm_min = syst->wMinute;
2305 xtm.tm_sec = syst->wSecond; /* this is UTC */
2308 utctime = timegm(&xtm);
2309 DOSFS_UnixTimeToFileTime( utctime, ft,
2310 syst->wMilliseconds * 10000 );
2312 localtim = mktime(&xtm); /* now we've got local time */
2313 utc_tm = gmtime(&localtim);
2314 utctime = mktime(utc_tm);
2315 DOSFS_UnixTimeToFileTime( 2*localtim -utctime, ft,
2316 syst->wMilliseconds * 10000 );
2321 /***********************************************************************
2322 * DefineDosDeviceA (KERNEL32.@)
2324 BOOL WINAPI DefineDosDeviceA(DWORD flags,LPCSTR devname,LPCSTR targetpath) {
2325 FIXME("(0x%08lx,%s,%s),stub!\n",flags,devname,targetpath);
2326 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2331 --- 16 bit functions ---
2334 /*************************************************************************
2335 * FindFirstFile (KERNEL.413)
2337 HANDLE16 WINAPI FindFirstFile16( LPCSTR path, WIN32_FIND_DATAA *data )
2339 DOS_FULL_NAME full_name;
2341 FIND_FIRST_INFO *info;
2343 data->dwReserved0 = data->dwReserved1 = 0x0;
2344 if (!path) return 0;
2345 if (!DOSFS_GetFullName( path, FALSE, &full_name ))
2346 return INVALID_HANDLE_VALUE16;
2347 if (!(handle = GlobalAlloc16( GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO) )))
2348 return INVALID_HANDLE_VALUE16;
2349 info = (FIND_FIRST_INFO *)GlobalLock16( handle );
2350 info->path = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
2351 strcpy( info->path, full_name.long_name );
2352 info->long_mask = strrchr( info->path, '/' );
2353 if (info->long_mask )
2354 *(info->long_mask++) = '\0';
2355 info->short_mask = NULL;
2357 if (path[0] && (path[1] == ':')) info->drive = FILE_toupper(*path) - 'A';
2358 else info->drive = DRIVE_GetCurrentDrive();
2361 info->dir = DOSFS_OpenDir( info->path );
2363 GlobalUnlock16( handle );
2364 if (!FindNextFile16( handle, data ))
2366 FindClose16( handle );
2367 SetLastError( ERROR_NO_MORE_FILES );
2368 return INVALID_HANDLE_VALUE16;
2373 /*************************************************************************
2374 * FindNextFile (KERNEL.414)
2376 BOOL16 WINAPI FindNextFile16( HANDLE16 handle, WIN32_FIND_DATAA *data )
2378 FIND_FIRST_INFO *info;
2380 if ((handle == INVALID_HANDLE_VALUE16) ||
2381 !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
2383 SetLastError( ERROR_INVALID_HANDLE );
2386 GlobalUnlock16( handle );
2387 if (!info->path || !info->dir)
2389 SetLastError( ERROR_NO_MORE_FILES );
2392 if (!DOSFS_FindNextEx( info, data ))
2394 DOSFS_CloseDir( info->dir ); info->dir = NULL;
2395 HeapFree( GetProcessHeap(), 0, info->path );
2396 info->path = info->long_mask = NULL;
2397 SetLastError( ERROR_NO_MORE_FILES );
2403 /*************************************************************************
2404 * FindClose (KERNEL.415)
2406 BOOL16 WINAPI FindClose16( HANDLE16 handle )
2408 FIND_FIRST_INFO *info;
2410 if ((handle == INVALID_HANDLE_VALUE16) ||
2411 !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
2413 SetLastError( ERROR_INVALID_HANDLE );
2416 if (info->dir) DOSFS_CloseDir( info->dir );
2417 if (info->path) HeapFree( GetProcessHeap(), 0, info->path );
2418 GlobalUnlock16( handle );
2419 GlobalFree16( handle );