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 #include <sys/ioctl.h>
43 #include "wine/unicode.h"
44 #include "wine/winbase16.h"
51 #include "wine/server.h"
52 #include "msvcrt/excpt.h"
54 #include "wine/debug.h"
56 WINE_DEFAULT_DEBUG_CHANNEL(dosfs);
57 WINE_DECLARE_DEBUG_CHANNEL(file);
59 /* Define the VFAT ioctl to get both short and long file names */
60 /* FIXME: is it possible to get this to work on other systems? */
62 /* We want the real kernel dirent structure, not the libc one */
67 unsigned short d_reclen;
71 #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, KERNEL_DIRENT [2] )
74 #undef VFAT_IOCTL_READDIR_BOTH /* just in case... */
77 /* Chars we don't want to see in DOS file names */
78 #define INVALID_DOS_CHARS "*?<>|\"+=,;[] \345"
80 static const DOS_DEVICE DOSFS_Devices[] =
81 /* name, device flags (see Int 21/AX=0x4400) */
95 { "SCSIMGR$", 0xc0c0 },
97 { "EMMXXXX0", 0x0000 }
100 #define GET_DRIVE(path) \
101 (((path)[1] == ':') ? FILE_toupper((path)[0]) - 'A' : DOSFS_CurDrive)
103 /* Directory info for DOSFS_ReadDir */
107 #ifdef VFAT_IOCTL_READDIR_BOTH
110 KERNEL_DIRENT dirent[2];
114 /* Info structure for FindFirstFile handle */
127 static WINE_EXCEPTION_FILTER(page_fault)
129 if (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION)
130 return EXCEPTION_EXECUTE_HANDLER;
131 return EXCEPTION_CONTINUE_SEARCH;
135 /***********************************************************************
138 * Return 1 if Unix file 'name' is also a valid MS-DOS name
139 * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
140 * File name can be terminated by '\0', '\\' or '/'.
142 static int DOSFS_ValidDOSName( const char *name, int ignore_case )
144 static const char invalid_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS;
145 const char *p = name;
146 const char *invalid = ignore_case ? (invalid_chars + 26) : invalid_chars;
151 /* Check for "." and ".." */
154 /* All other names beginning with '.' are invalid */
155 return (IS_END_OF_NAME(*p));
157 while (!IS_END_OF_NAME(*p))
159 if (strchr( invalid, *p )) return 0; /* Invalid char */
160 if (*p == '.') break; /* Start of the extension */
161 if (++len > 8) return 0; /* Name too long */
164 if (*p != '.') return 1; /* End of name */
166 if (IS_END_OF_NAME(*p)) return 0; /* Empty extension not allowed */
168 while (!IS_END_OF_NAME(*p))
170 if (strchr( invalid, *p )) return 0; /* Invalid char */
171 if (*p == '.') return 0; /* Second extension not allowed */
172 if (++len > 3) return 0; /* Extension too long */
179 /***********************************************************************
180 * DOSFS_ToDosFCBFormat
182 * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
183 * expanding wild cards and converting to upper-case in the process.
184 * File name can be terminated by '\0', '\\' or '/'.
185 * Return FALSE if the name is not a valid DOS name.
186 * 'buffer' must be at least 12 characters long.
188 BOOL DOSFS_ToDosFCBFormat( LPCSTR name, LPSTR buffer )
190 static const char invalid_chars[] = INVALID_DOS_CHARS;
191 const char *p = name;
194 /* Check for "." and ".." */
198 strcpy( buffer, ". " );
204 return (!*p || (*p == '/') || (*p == '\\'));
207 for (i = 0; i < 8; i++)
224 if (strchr( invalid_chars, *p )) return FALSE;
225 buffer[i] = FILE_toupper(*p);
233 /* Skip all chars after wildcard up to first dot */
234 while (*p && (*p != '/') && (*p != '\\') && (*p != '.')) p++;
238 /* Check if name too long */
239 if (*p && (*p != '/') && (*p != '\\') && (*p != '.')) return FALSE;
241 if (*p == '.') p++; /* Skip dot */
243 for (i = 8; i < 11; i++)
253 return FALSE; /* Second extension not allowed */
261 if (strchr( invalid_chars, *p )) return FALSE;
262 buffer[i] = FILE_toupper(*p);
269 /* at most 3 character of the extension are processed
270 * is something behind this ?
272 while (*p == '*' || *p == ' ') p++; /* skip wildcards and spaces */
273 return IS_END_OF_NAME(*p);
277 /***********************************************************************
278 * DOSFS_ToDosDTAFormat
280 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
281 * converting to upper-case in the process.
282 * File name can be terminated by '\0', '\\' or '/'.
283 * 'buffer' must be at least 13 characters long.
285 static void DOSFS_ToDosDTAFormat( LPCSTR name, LPSTR buffer )
289 memcpy( buffer, name, 8 );
291 while ((p > buffer) && (p[-1] == ' ')) p--;
293 memcpy( p, name + 8, 3 );
295 while (p[-1] == ' ') p--;
296 if (p[-1] == '.') p--;
301 /***********************************************************************
304 * Check a DOS file name against a mask (both in FCB format).
306 static int DOSFS_MatchShort( const char *mask, const char *name )
309 for (i = 11; i > 0; i--, mask++, name++)
310 if ((*mask != '?') && (*mask != *name)) return 0;
315 /***********************************************************************
318 * Check a long file name against a mask.
320 * Tests (done in W95 DOS shell - case insensitive):
321 * *.txt test1.test.txt *
323 * *.t??????.t* test1.ta.tornado.txt *
324 * *tornado* test1.ta.tornado.txt *
325 * t*t test1.ta.tornado.txt *
327 * ?est??? test1.txt -
328 * *test1.txt* test1.txt *
329 * h?l?o*t.dat hellothisisatest.dat *
331 static int DOSFS_MatchLong( const char *mask, const char *name,
334 const char *lastjoker = NULL;
335 const char *next_to_retry = NULL;
337 if (!strcmp( mask, "*.*" )) return 1;
338 while (*name && *mask)
343 while (*mask == '*') mask++; /* Skip consecutive '*' */
345 if (!*mask) return 1; /* end of mask is all '*', so match */
347 /* skip to the next match after the joker(s) */
348 if (case_sensitive) while (*name && (*name != *mask)) name++;
349 else while (*name && (FILE_toupper(*name) != FILE_toupper(*mask))) name++;
352 next_to_retry = name;
354 else if (*mask != '?')
359 if (*mask != *name) mismatch = 1;
363 if (FILE_toupper(*mask) != FILE_toupper(*name)) mismatch = 1;
377 else /* mismatch ! */
379 if (lastjoker) /* we had an '*', so we can try unlimitedly */
383 /* this scan sequence was a mismatch, so restart
384 * 1 char after the first char we checked last time */
386 name = next_to_retry;
389 return 0; /* bad luck */
398 while ((*mask == '.') || (*mask == '*'))
399 mask++; /* Ignore trailing '.' or '*' in mask */
400 return (!*name && !*mask);
404 /***********************************************************************
407 static DOS_DIR *DOSFS_OpenDir( LPCSTR path )
409 DOS_DIR *dir = HeapAlloc( GetProcessHeap(), 0, sizeof(*dir) );
412 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
416 /* Treat empty path as root directory. This simplifies path split into
417 directory and mask in several other places */
418 if (!*path) path = "/";
420 #ifdef VFAT_IOCTL_READDIR_BOTH
422 /* Check if the VFAT ioctl is supported on this directory */
424 if ((dir->fd = open( path, O_RDONLY )) != -1)
426 if (ioctl( dir->fd, VFAT_IOCTL_READDIR_BOTH, (long)dir->dirent ) == -1)
433 /* Set the file pointer back at the start of the directory */
434 lseek( dir->fd, 0, SEEK_SET );
439 #endif /* VFAT_IOCTL_READDIR_BOTH */
441 /* Now use the standard opendir/readdir interface */
443 if (!(dir->dir = opendir( path )))
445 HeapFree( GetProcessHeap(), 0, dir );
452 /***********************************************************************
455 static void DOSFS_CloseDir( DOS_DIR *dir )
457 #ifdef VFAT_IOCTL_READDIR_BOTH
458 if (dir->fd != -1) close( dir->fd );
459 #endif /* VFAT_IOCTL_READDIR_BOTH */
460 if (dir->dir) closedir( dir->dir );
461 HeapFree( GetProcessHeap(), 0, dir );
465 /***********************************************************************
468 static BOOL DOSFS_ReadDir( DOS_DIR *dir, LPCSTR *long_name,
471 struct dirent *dirent;
473 #ifdef VFAT_IOCTL_READDIR_BOTH
476 if (ioctl( dir->fd, VFAT_IOCTL_READDIR_BOTH, (long)dir->dirent ) != -1) {
477 if (!dir->dirent[0].d_reclen) return FALSE;
478 if (!DOSFS_ToDosFCBFormat( dir->dirent[0].d_name, dir->short_name ))
479 dir->short_name[0] = '\0';
480 *short_name = dir->short_name;
481 if (dir->dirent[1].d_name[0]) *long_name = dir->dirent[1].d_name;
482 else *long_name = dir->dirent[0].d_name;
486 #endif /* VFAT_IOCTL_READDIR_BOTH */
488 if (!(dirent = readdir( dir->dir ))) return FALSE;
489 *long_name = dirent->d_name;
495 /***********************************************************************
498 * Transform a Unix file name into a hashed DOS name. If the name is a valid
499 * DOS name, it is converted to upper-case; otherwise it is replaced by a
500 * hashed version that fits in 8.3 format.
501 * File name can be terminated by '\0', '\\' or '/'.
502 * 'buffer' must be at least 13 characters long.
504 static void DOSFS_Hash( LPCSTR name, LPSTR buffer, BOOL dir_format,
507 static const char invalid_chars[] = INVALID_DOS_CHARS "~.";
508 static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
515 if (dir_format) strcpy( buffer, " " );
517 if (DOSFS_ValidDOSName( name, ignore_case ))
519 /* Check for '.' and '..' */
523 if (!dir_format) buffer[1] = buffer[2] = '\0';
524 if (name[1] == '.') buffer[1] = '.';
528 /* Simply copy the name, converting to uppercase */
530 for (dst = buffer; !IS_END_OF_NAME(*name) && (*name != '.'); name++)
531 *dst++ = FILE_toupper(*name);
534 if (dir_format) dst = buffer + 8;
536 for (name++; !IS_END_OF_NAME(*name); name++)
537 *dst++ = FILE_toupper(*name);
539 if (!dir_format) *dst = '\0';
543 /* Compute the hash code of the file name */
544 /* If you know something about hash functions, feel free to */
545 /* insert a better algorithm here... */
548 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
549 hash = (hash<<3) ^ (hash>>5) ^ FILE_tolower(*p) ^ (FILE_tolower(p[1]) << 8);
550 hash = (hash<<3) ^ (hash>>5) ^ FILE_tolower(*p); /* Last character*/
554 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
555 hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
556 hash = (hash << 3) ^ (hash >> 5) ^ *p; /* Last character */
559 /* Find last dot for start of the extension */
560 for (p = name+1, ext = NULL; !IS_END_OF_NAME(*p); p++)
561 if (*p == '.') ext = p;
562 if (ext && IS_END_OF_NAME(ext[1]))
563 ext = NULL; /* Empty extension ignored */
565 /* Copy first 4 chars, replacing invalid chars with '_' */
566 for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
568 if (IS_END_OF_NAME(*p) || (p == ext)) break;
569 *dst++ = strchr( invalid_chars, *p ) ? '_' : FILE_toupper(*p);
571 /* Pad to 5 chars with '~' */
572 while (i-- >= 0) *dst++ = '~';
574 /* Insert hash code converted to 3 ASCII chars */
575 *dst++ = hash_chars[(hash >> 10) & 0x1f];
576 *dst++ = hash_chars[(hash >> 5) & 0x1f];
577 *dst++ = hash_chars[hash & 0x1f];
579 /* Copy the first 3 chars of the extension (if any) */
582 if (!dir_format) *dst++ = '.';
583 for (i = 3, ext++; (i > 0) && !IS_END_OF_NAME(*ext); i--, ext++)
584 *dst++ = strchr( invalid_chars, *ext ) ? '_' : FILE_toupper(*ext);
586 if (!dir_format) *dst = '\0';
590 /***********************************************************************
593 * Find the Unix file name in a given directory that corresponds to
594 * a file name (either in Unix or DOS format).
595 * File name can be terminated by '\0', '\\' or '/'.
596 * Return TRUE if OK, FALSE if no file name matches.
598 * 'long_buf' must be at least 'long_len' characters long. If the long name
599 * turns out to be larger than that, the function returns FALSE.
600 * 'short_buf' must be at least 13 characters long.
602 BOOL DOSFS_FindUnixName( LPCSTR path, LPCSTR name, LPSTR long_buf,
603 INT long_len, LPSTR short_buf, BOOL ignore_case)
606 LPCSTR long_name, short_name;
607 char dos_name[12], tmp_buf[13];
610 const char *p = strchr( name, '/' );
611 int len = p ? (int)(p - name) : strlen(name);
612 if ((p = strchr( name, '\\' ))) len = min( (int)(p - name), len );
613 /* Ignore trailing dots and spaces */
614 while (len > 1 && (name[len-1] == '.' || name[len-1] == ' ')) len--;
615 if (long_len < len + 1) return FALSE;
617 TRACE("%s,%s\n", path, name );
619 if (!DOSFS_ToDosFCBFormat( name, dos_name )) dos_name[0] = '\0';
621 if (!(dir = DOSFS_OpenDir( path )))
623 WARN("(%s,%s): can't open dir: %s\n",
624 path, name, strerror(errno) );
628 while ((ret = DOSFS_ReadDir( dir, &long_name, &short_name )))
630 /* Check against Unix name */
631 if (len == strlen(long_name))
635 if (!strncmp( long_name, name, len )) break;
639 if (!FILE_strncasecmp( long_name, name, len )) break;
644 /* Check against hashed DOS name */
647 DOSFS_Hash( long_name, tmp_buf, TRUE, ignore_case );
648 short_name = tmp_buf;
650 if (!strcmp( dos_name, short_name )) break;
655 if (long_buf) strcpy( long_buf, long_name );
659 DOSFS_ToDosDTAFormat( short_name, short_buf );
661 DOSFS_Hash( long_name, short_buf, FALSE, ignore_case );
663 TRACE("(%s,%s) -> %s (%s)\n",
664 path, name, long_name, short_buf ? short_buf : "***");
667 WARN("'%s' not found in '%s'\n", name, path);
668 DOSFS_CloseDir( dir );
673 /***********************************************************************
676 * Check if a DOS file name represents a DOS device and return the device.
678 const DOS_DEVICE *DOSFS_GetDevice( const char *name )
683 if (!name) return NULL; /* if FILE_DupUnixHandle was used */
684 if (name[0] && (name[1] == ':')) name += 2;
685 if ((p = strrchr( name, '/' ))) name = p + 1;
686 if ((p = strrchr( name, '\\' ))) name = p + 1;
687 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
689 const char *dev = DOSFS_Devices[i].name;
690 if (!FILE_strncasecmp( dev, name, strlen(dev) ))
692 p = name + strlen( dev );
693 if (!*p || (*p == '.') || (*p == ':')) return &DOSFS_Devices[i];
700 /***********************************************************************
701 * DOSFS_GetDeviceByHandle
703 const DOS_DEVICE *DOSFS_GetDeviceByHandle( HFILE hFile )
705 const DOS_DEVICE *ret = NULL;
706 SERVER_START_REQ( get_file_info )
709 if (!wine_server_call( req ) && (reply->type == FILE_TYPE_UNKNOWN))
711 if ((reply->attr >= 0) &&
712 (reply->attr < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0])))
713 ret = &DOSFS_Devices[reply->attr];
721 /**************************************************************************
722 * DOSFS_CreateCommPort
724 static HANDLE DOSFS_CreateCommPort(LPCSTR name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa)
729 TRACE_(file)("%s %lx %lx\n", name, access, attributes);
731 PROFILE_GetWineIniString("serialports",name,"",devname,sizeof devname);
735 TRACE("opening %s as %s\n", devname, name);
737 SERVER_START_REQ( create_serial )
739 req->access = access;
740 req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
741 req->attributes = attributes;
742 req->sharing = FILE_SHARE_READ|FILE_SHARE_WRITE;
743 wine_server_add_data( req, devname, strlen(devname) );
745 wine_server_call_err( req );
751 ERR("Couldn't open device '%s' ! (check permissions)\n",devname);
753 TRACE("return %08X\n", ret );
757 /***********************************************************************
760 * Open a DOS device. This might not map 1:1 into the UNIX device concept.
761 * Returns 0 on failure.
763 HANDLE DOSFS_OpenDevice( const char *name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa )
769 if (name[0] && (name[1] == ':')) name += 2;
770 if ((p = strrchr( name, '/' ))) name = p + 1;
771 if ((p = strrchr( name, '\\' ))) name = p + 1;
772 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
774 const char *dev = DOSFS_Devices[i].name;
775 if (!FILE_strncasecmp( dev, name, strlen(dev) ))
777 p = name + strlen( dev );
778 if (!*p || (*p == '.') || (*p == ':')) {
780 if (!strcmp(DOSFS_Devices[i].name,"NUL"))
781 return FILE_CreateFile( "/dev/null", access,
782 FILE_SHARE_READ|FILE_SHARE_WRITE, sa,
783 OPEN_EXISTING, 0, 0, TRUE, DRIVE_UNKNOWN );
784 if (!strcmp(DOSFS_Devices[i].name,"CON")) {
786 switch (access & (GENERIC_READ|GENERIC_WRITE)) {
788 to_dup = GetStdHandle( STD_INPUT_HANDLE );
791 to_dup = GetStdHandle( STD_OUTPUT_HANDLE );
794 FIXME("can't open CON read/write\n");
797 if (!DuplicateHandle( GetCurrentProcess(), to_dup, GetCurrentProcess(),
799 sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle,
800 DUPLICATE_SAME_ACCESS ))
804 if (!strcmp(DOSFS_Devices[i].name,"SCSIMGR$") ||
805 !strcmp(DOSFS_Devices[i].name,"HPSCAN") ||
806 !strcmp(DOSFS_Devices[i].name,"EMMXXXX0"))
808 return FILE_CreateDevice( i, access, sa );
811 if( (handle=DOSFS_CreateCommPort(DOSFS_Devices[i].name,access,attributes,sa)) )
813 FIXME("device open %s not supported (yet)\n",DOSFS_Devices[i].name);
822 /***********************************************************************
825 * Get the drive specified by a given path name (DOS or Unix format).
827 static int DOSFS_GetPathDrive( const char **name )
830 const char *p = *name;
832 if (*p && (p[1] == ':'))
834 drive = FILE_toupper(*p) - 'A';
837 else if (*p == '/') /* Absolute Unix path? */
839 if ((drive = DRIVE_FindDriveRoot( name )) == -1)
841 MESSAGE("Warning: %s not accessible from a configured DOS drive\n", *name );
842 /* Assume it really was a DOS name */
843 drive = DRIVE_GetCurrentDrive();
846 else drive = DRIVE_GetCurrentDrive();
848 if (!DRIVE_IsValid(drive))
850 SetLastError( ERROR_INVALID_DRIVE );
857 /***********************************************************************
860 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
861 * Unix name / short DOS name pair.
862 * Return FALSE if one of the path components does not exist. The last path
863 * component is only checked if 'check_last' is non-zero.
864 * The buffers pointed to by 'long_buf' and 'short_buf' must be
865 * at least MAX_PATHNAME_LEN long.
867 BOOL DOSFS_GetFullName( LPCSTR name, BOOL check_last, DOS_FULL_NAME *full )
869 BOOL unixabsolute = *name == '/';
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 if (!unixabsolute) /* 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;
1030 BOOL unixabsolute = *longpath == '/';
1032 TRACE("%s\n", debugstr_a(longpath));
1035 SetLastError(ERROR_INVALID_PARAMETER);
1039 SetLastError(ERROR_BAD_PATHNAME);
1043 if ( ( tmpshortpath = HeapAlloc ( GetProcessHeap(), 0, MAX_PATHNAME_LEN ) ) == NULL ) {
1044 SetLastError ( ERROR_NOT_ENOUGH_MEMORY );
1048 /* check for drive letter */
1049 if ( longpath[1] == ':' ) {
1050 tmpshortpath[0] = longpath[0];
1051 tmpshortpath[1] = ':';
1055 if ( ( drive = DOSFS_GetPathDrive ( &longpath )) == -1 ) return 0;
1056 flags = DRIVE_GetFlags ( drive );
1058 if ( unixabsolute ) {
1059 tmpshortpath[0] = drive + 'A';
1060 tmpshortpath[1] = ':';
1061 tmpshortpath[2] = '\\';
1065 while ( longpath[lp] ) {
1067 /* check for path delimiters and reproduce them */
1068 if ( longpath[lp] == '\\' || longpath[lp] == '/' ) {
1069 if (!sp || tmpshortpath[sp-1]!= '\\')
1071 /* strip double "\\" */
1072 tmpshortpath[sp] = '\\';
1075 tmpshortpath[sp]=0;/*terminate string*/
1080 tmplen = strcspn ( longpath + lp, "\\/" );
1081 lstrcpynA ( tmpshortpath+sp, longpath + lp, tmplen+1 );
1083 /* Check, if the current element is a valid dos name */
1084 if ( DOSFS_ValidDOSName ( longpath + lp, !(flags & DRIVE_CASE_SENSITIVE) ) ) {
1090 /* Check if the file exists and use the existing file name */
1091 if ( DOSFS_GetFullName ( tmpshortpath, TRUE, &full_name ) ) {
1092 strcpy( tmpshortpath+sp, strrchr ( full_name.short_name, '\\' ) + 1 );
1093 sp += strlen ( tmpshortpath+sp );
1098 TRACE("not found!\n" );
1099 SetLastError ( ERROR_FILE_NOT_FOUND );
1102 tmpshortpath[sp] = 0;
1104 lstrcpynA ( shortpath, tmpshortpath, shortlen );
1105 TRACE("returning %s\n", debugstr_a(shortpath) );
1106 tmplen = strlen ( tmpshortpath );
1107 HeapFree ( GetProcessHeap(), 0, tmpshortpath );
1113 /***********************************************************************
1114 * GetShortPathNameW (KERNEL32.@)
1116 DWORD WINAPI GetShortPathNameW( LPCWSTR longpath, LPWSTR shortpath,
1119 LPSTR longpathA, shortpathA;
1122 longpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, longpath );
1123 shortpathA = HeapAlloc ( GetProcessHeap(), 0, shortlen );
1125 ret = GetShortPathNameA ( longpathA, shortpathA, shortlen );
1126 if (shortlen > 0 && !MultiByteToWideChar( CP_ACP, 0, shortpathA, -1, shortpath, shortlen ))
1127 shortpath[shortlen-1] = 0;
1128 HeapFree( GetProcessHeap(), 0, longpathA );
1129 HeapFree( GetProcessHeap(), 0, shortpathA );
1135 /***********************************************************************
1136 * GetLongPathNameA (KERNEL32.@)
1139 * observed (Win2000):
1140 * shortpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1141 * shortpath="": LastError=ERROR_PATH_NOT_FOUND, ret=0
1143 DWORD WINAPI GetLongPathNameA( LPCSTR shortpath, LPSTR longpath,
1146 DOS_FULL_NAME full_name;
1147 char *p, *r, *ll, *ss;
1150 SetLastError(ERROR_INVALID_PARAMETER);
1153 if (!shortpath[0]) {
1154 SetLastError(ERROR_PATH_NOT_FOUND);
1158 if (!DOSFS_GetFullName( shortpath, TRUE, &full_name )) return 0;
1159 lstrcpynA( longpath, full_name.short_name, longlen );
1161 /* Do some hackery to get the long filename. */
1164 ss=longpath+strlen(longpath);
1165 ll=full_name.long_name+strlen(full_name.long_name);
1167 while (ss>=longpath)
1169 /* FIXME: aren't we more paranoid, than needed? */
1170 while ((ss[0]=='\\') && (ss>=longpath)) ss--;
1172 while ((ss[0]!='\\') && (ss>=longpath)) ss--;
1175 /* FIXME: aren't we more paranoid, than needed? */
1176 while ((ll[0]=='/') && (ll>=full_name.long_name)) ll--;
1177 while ((ll[0]!='/') && (ll>=full_name.long_name)) ll--;
1178 if (ll<full_name.long_name)
1180 ERR("Bad longname! (ss=%s ll=%s)\n This should never happen !\n"
1187 /* FIXME: fix for names like "C:\\" (ie. with more '\'s) */
1191 if ((p-longpath)>0) longlen -= (p-longpath);
1192 lstrcpynA( p, ll , longlen);
1194 /* Now, change all '/' to '\' */
1195 for (r=p; r<(p+longlen); r++ )
1196 if (r[0]=='/') r[0]='\\';
1197 return strlen(longpath) - strlen(p) + longlen;
1201 return strlen(longpath);
1205 /***********************************************************************
1206 * GetLongPathNameW (KERNEL32.@)
1208 DWORD WINAPI GetLongPathNameW( LPCWSTR shortpath, LPWSTR longpath,
1211 DOS_FULL_NAME full_name;
1213 LPSTR shortpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, shortpath );
1215 /* FIXME: is it correct to always return a fully qualified short path? */
1216 if (DOSFS_GetFullName( shortpathA, TRUE, &full_name ))
1218 ret = strlen( full_name.short_name );
1219 if (longlen > 0 && !MultiByteToWideChar( CP_ACP, 0, full_name.long_name, -1,
1220 longpath, longlen ))
1221 longpath[longlen-1] = 0;
1223 HeapFree( GetProcessHeap(), 0, shortpathA );
1228 /***********************************************************************
1229 * DOSFS_DoGetFullPathName
1231 * Implementation of GetFullPathNameA/W.
1233 * bon@elektron 000331:
1234 * A test for GetFullPathName with many pathological cases
1235 * now gives identical output for Wine and OSR2
1237 static DWORD DOSFS_DoGetFullPathName( LPCSTR name, DWORD len, LPSTR result,
1241 DOS_FULL_NAME full_name;
1244 char drivecur[]="c:.";
1246 int namelen,drive=0;
1248 if (!name[0]) return 0;
1250 TRACE("passed '%s'\n", name);
1253 /*drive letter given */
1255 driveletter = name[0];
1257 if ((name[1]==':') && ((name[2]=='\\') || (name[2]=='/')))
1258 /*absolute path given */
1260 lstrcpynA(full_name.short_name,name,MAX_PATHNAME_LEN);
1261 drive = (int)FILE_toupper(name[0]) - 'A';
1266 drivecur[0]=driveletter;
1267 else if ((name[0]=='\\') || (name[0]=='/'))
1268 strcpy(drivecur,"\\");
1270 strcpy(drivecur,".");
1272 if (!DOSFS_GetFullName( drivecur, FALSE, &full_name ))
1274 FIXME("internal: error getting drive/path\n");
1277 /* find path that drive letter substitutes*/
1278 drive = (int)FILE_toupper(full_name.short_name[0]) -0x41;
1279 root= DRIVE_GetRoot(drive);
1282 FIXME("internal: error getting DOS Drive Root\n");
1285 if (!strcmp(root,"/"))
1287 /* we have just the last / and we need it. */
1288 p= full_name.long_name;
1292 p= full_name.long_name +strlen(root);
1294 /* append long name (= unix name) to drive */
1295 lstrcpynA(full_name.short_name+2,p,MAX_PATHNAME_LEN-3);
1296 /* append name to treat */
1297 namelen= strlen(full_name.short_name);
1300 p += +2; /* skip drive name when appending */
1301 if (namelen +2 + strlen(p) > MAX_PATHNAME_LEN)
1303 FIXME("internal error: buffer too small\n");
1306 full_name.short_name[namelen++] ='\\';
1307 full_name.short_name[namelen] = 0;
1308 lstrcpynA(full_name.short_name +namelen,p,MAX_PATHNAME_LEN-namelen);
1310 /* reverse all slashes */
1311 for (p=full_name.short_name;
1312 p < full_name.short_name+strlen(full_name.short_name);
1318 /* Use memmove, as areas overlap */
1320 while ((p = strstr(full_name.short_name,"\\..\\")))
1322 if (p > full_name.short_name+2)
1325 q = strrchr(full_name.short_name,'\\');
1326 memmove(q+1,p+4,strlen(p+4)+1);
1330 memmove(full_name.short_name+3,p+4,strlen(p+4)+1);
1333 if ((full_name.short_name[2]=='.')&&(full_name.short_name[3]=='.'))
1335 /* This case istn't treated yet : c:..\test */
1336 memmove(full_name.short_name+2,full_name.short_name+4,
1337 strlen(full_name.short_name+4)+1);
1340 while ((p = strstr(full_name.short_name,"\\.\\")))
1343 memmove(p+1,p+3,strlen(p+3)+1);
1345 if (!(DRIVE_GetFlags(drive) & DRIVE_CASE_PRESERVING))
1346 for (p = full_name.short_name; *p; p++) *p = FILE_toupper(*p);
1347 namelen=strlen(full_name.short_name);
1348 if (!strcmp(full_name.short_name+namelen-3,"\\.."))
1350 /* one more strange case: "c:\test\test1\.."
1352 *(full_name.short_name+namelen-3)=0;
1353 q = strrchr(full_name.short_name,'\\');
1356 if (full_name.short_name[namelen-1]=='.')
1357 full_name.short_name[(namelen--)-1] =0;
1359 if (full_name.short_name[namelen-1]=='\\')
1360 full_name.short_name[(namelen--)-1] =0;
1361 TRACE("got %s\n",full_name.short_name);
1363 /* If the lpBuffer buffer is too small, the return value is the
1364 size of the buffer, in characters, required to hold the path
1365 plus the terminating \0 (tested against win95osr2, bon 001118)
1367 ret = strlen(full_name.short_name);
1370 /* don't touch anything when the buffer is not large enough */
1371 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1377 MultiByteToWideChar( CP_ACP, 0, full_name.short_name, -1, (LPWSTR)result, len );
1379 lstrcpynA( result, full_name.short_name, len );
1382 TRACE("returning '%s'\n", full_name.short_name );
1387 /***********************************************************************
1388 * GetFullPathNameA (KERNEL32.@)
1390 * if the path closed with '\', *lastpart is 0
1392 DWORD WINAPI GetFullPathNameA( LPCSTR name, DWORD len, LPSTR buffer,
1395 DWORD ret = DOSFS_DoGetFullPathName( name, len, buffer, FALSE );
1396 if (ret && (ret<=len) && buffer && lastpart)
1398 LPSTR p = buffer + strlen(buffer);
1402 while ((p > buffer + 2) && (*p != '\\')) p--;
1405 else *lastpart = NULL;
1411 /***********************************************************************
1412 * GetFullPathNameW (KERNEL32.@)
1414 DWORD WINAPI GetFullPathNameW( LPCWSTR name, DWORD len, LPWSTR buffer,
1417 LPSTR nameA = HEAP_strdupWtoA( GetProcessHeap(), 0, name );
1418 DWORD ret = DOSFS_DoGetFullPathName( nameA, len, (LPSTR)buffer, TRUE );
1419 HeapFree( GetProcessHeap(), 0, nameA );
1420 if (ret && (ret<=len) && buffer && lastpart)
1422 LPWSTR p = buffer + strlenW(buffer);
1423 if (*p != (WCHAR)'\\')
1425 while ((p > buffer + 2) && (*p != (WCHAR)'\\')) p--;
1428 else *lastpart = NULL;
1434 /***********************************************************************
1435 * wine_get_unix_file_name (KERNEL32.@) Not a Windows API
1437 * Return the full Unix file name for a given path.
1439 BOOL WINAPI wine_get_unix_file_name( LPCSTR dos, LPSTR buffer, DWORD len )
1443 if ((ret = DOSFS_GetFullName( dos, FALSE, &path ))) lstrcpynA( buffer, path.long_name, len );
1448 /***********************************************************************
1451 static int DOSFS_FindNextEx( FIND_FIRST_INFO *info, WIN32_FIND_DATAA *entry )
1453 DWORD attr = info->attr | FA_UNUSED | FA_ARCHIVE | FA_RDONLY | FILE_ATTRIBUTE_SYMLINK;
1454 UINT flags = DRIVE_GetFlags( info->drive );
1455 char *p, buffer[MAX_PATHNAME_LEN];
1456 const char *drive_path;
1458 LPCSTR long_name, short_name;
1459 BY_HANDLE_FILE_INFORMATION fileinfo;
1462 if ((info->attr & ~(FA_UNUSED | FA_ARCHIVE | FA_RDONLY)) == FA_LABEL)
1464 if (info->cur_pos) return 0;
1465 entry->dwFileAttributes = FILE_ATTRIBUTE_LABEL;
1466 RtlSecondsSince1970ToTime( (time_t)0, &entry->ftCreationTime );
1467 RtlSecondsSince1970ToTime( (time_t)0, &entry->ftLastAccessTime );
1468 RtlSecondsSince1970ToTime( (time_t)0, &entry->ftLastWriteTime );
1469 entry->nFileSizeHigh = 0;
1470 entry->nFileSizeLow = 0;
1471 entry->dwReserved0 = 0;
1472 entry->dwReserved1 = 0;
1473 DOSFS_ToDosDTAFormat( DRIVE_GetLabel( info->drive ), entry->cFileName );
1474 strcpy( entry->cAlternateFileName, entry->cFileName );
1476 TRACE("returning %s (%s) as label\n",
1477 entry->cFileName, entry->cAlternateFileName);
1481 drive_path = info->path + strlen(DRIVE_GetRoot( info->drive ));
1482 while ((*drive_path == '/') || (*drive_path == '\\')) drive_path++;
1483 drive_root = !*drive_path;
1485 lstrcpynA( buffer, info->path, sizeof(buffer) - 1 );
1486 strcat( buffer, "/" );
1487 p = buffer + strlen(buffer);
1489 while (DOSFS_ReadDir( info->dir, &long_name, &short_name ))
1493 /* Don't return '.' and '..' in the root of the drive */
1494 if (drive_root && (long_name[0] == '.') &&
1495 (!long_name[1] || ((long_name[1] == '.') && !long_name[2])))
1498 /* Check the long mask */
1500 if (info->long_mask)
1502 if (!DOSFS_MatchLong( info->long_mask, long_name,
1503 flags & DRIVE_CASE_SENSITIVE )) continue;
1506 /* Check the short mask */
1508 if (info->short_mask)
1512 DOSFS_Hash( long_name, dos_name, TRUE,
1513 !(flags & DRIVE_CASE_SENSITIVE) );
1514 short_name = dos_name;
1516 if (!DOSFS_MatchShort( info->short_mask, short_name )) continue;
1519 /* Check the file attributes */
1521 lstrcpynA( p, long_name, sizeof(buffer) - (int)(p - buffer) );
1522 if (!FILE_Stat( buffer, &fileinfo ))
1524 WARN("can't stat %s\n", buffer);
1527 if ((fileinfo.dwFileAttributes & FILE_ATTRIBUTE_SYMLINK) &&
1528 (fileinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
1530 static int show_dir_symlinks = -1;
1531 if (show_dir_symlinks == -1)
1532 show_dir_symlinks = PROFILE_GetWineIniBool("wine", "ShowDirSymlinks", 0);
1533 if (!show_dir_symlinks) continue;
1536 if (fileinfo.dwFileAttributes & ~attr) continue;
1538 /* We now have a matching entry; fill the result and return */
1540 entry->dwFileAttributes = fileinfo.dwFileAttributes;
1541 entry->ftCreationTime = fileinfo.ftCreationTime;
1542 entry->ftLastAccessTime = fileinfo.ftLastAccessTime;
1543 entry->ftLastWriteTime = fileinfo.ftLastWriteTime;
1544 entry->nFileSizeHigh = fileinfo.nFileSizeHigh;
1545 entry->nFileSizeLow = fileinfo.nFileSizeLow;
1548 DOSFS_ToDosDTAFormat( short_name, entry->cAlternateFileName );
1550 DOSFS_Hash( long_name, entry->cAlternateFileName, FALSE,
1551 !(flags & DRIVE_CASE_SENSITIVE) );
1553 lstrcpynA( entry->cFileName, long_name, sizeof(entry->cFileName) );
1554 if (!(flags & DRIVE_CASE_PRESERVING)) _strlwr( entry->cFileName );
1555 TRACE("returning %s (%s) %02lx %ld\n",
1556 entry->cFileName, entry->cAlternateFileName,
1557 entry->dwFileAttributes, entry->nFileSizeLow );
1560 return 0; /* End of directory */
1563 /***********************************************************************
1566 * Find the next matching file. Return the number of entries read to find
1567 * the matching one, or 0 if no more entries.
1568 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
1569 * file name mask. Either or both can be NULL.
1571 * NOTE: This is supposed to be only called by the int21 emulation
1572 * routines. Thus, we should own the Win16Mutex anyway.
1573 * Nevertheless, we explicitly enter it to ensure the static
1574 * directory cache is protected.
1576 int DOSFS_FindNext( const char *path, const char *short_mask,
1577 const char *long_mask, int drive, BYTE attr,
1578 int skip, WIN32_FIND_DATAA *entry )
1580 static FIND_FIRST_INFO info;
1581 LPCSTR short_name, long_name;
1586 /* Check the cached directory */
1587 if (!(info.dir && info.path == path && info.short_mask == short_mask
1588 && info.long_mask == long_mask && info.drive == drive
1589 && info.attr == attr && info.cur_pos <= skip))
1591 /* Not in the cache, open it anew */
1592 if (info.dir) DOSFS_CloseDir( info.dir );
1594 info.path = (LPSTR)path;
1595 info.long_mask = (LPSTR)long_mask;
1596 info.short_mask = (LPSTR)short_mask;
1600 info.dir = DOSFS_OpenDir( info.path );
1603 /* Skip to desired position */
1604 while (info.cur_pos < skip)
1605 if (info.dir && DOSFS_ReadDir( info.dir, &long_name, &short_name ))
1610 if (info.dir && info.cur_pos == skip && DOSFS_FindNextEx( &info, entry ))
1611 count = info.cur_pos - skip;
1617 if (info.dir) DOSFS_CloseDir( info.dir );
1618 memset( &info, '\0', sizeof(info) );
1626 /*************************************************************************
1627 * FindFirstFileExA (KERNEL32.@)
1629 HANDLE WINAPI FindFirstFileExA(
1631 FINDEX_INFO_LEVELS fInfoLevelId,
1632 LPVOID lpFindFileData,
1633 FINDEX_SEARCH_OPS fSearchOp,
1634 LPVOID lpSearchFilter,
1635 DWORD dwAdditionalFlags)
1637 DOS_FULL_NAME full_name;
1639 FIND_FIRST_INFO *info;
1641 if ((fSearchOp != FindExSearchNameMatch) || (dwAdditionalFlags != 0))
1643 FIXME("options not implemented 0x%08x 0x%08lx\n", fSearchOp, dwAdditionalFlags );
1644 return INVALID_HANDLE_VALUE;
1647 switch(fInfoLevelId)
1649 case FindExInfoStandard:
1651 WIN32_FIND_DATAA * data = (WIN32_FIND_DATAA *) lpFindFileData;
1652 data->dwReserved0 = data->dwReserved1 = 0x0;
1653 if (!lpFileName) return 0;
1654 if (!DOSFS_GetFullName( lpFileName, FALSE, &full_name )) break;
1655 if (!(handle = GlobalAlloc(GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO)))) break;
1656 info = (FIND_FIRST_INFO *)GlobalLock( handle );
1657 info->path = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
1658 strcpy( info->path, full_name.long_name );
1659 info->long_mask = strrchr( info->path, '/' );
1660 *(info->long_mask++) = '\0';
1661 info->short_mask = NULL;
1663 if (lpFileName[0] && (lpFileName[1] == ':'))
1664 info->drive = FILE_toupper(*lpFileName) - 'A';
1665 else info->drive = DRIVE_GetCurrentDrive();
1668 info->dir = DOSFS_OpenDir( info->path );
1670 GlobalUnlock( handle );
1671 if (!FindNextFileA( handle, data ))
1673 FindClose( handle );
1674 SetLastError( ERROR_NO_MORE_FILES );
1681 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
1683 return INVALID_HANDLE_VALUE;
1686 /*************************************************************************
1687 * FindFirstFileA (KERNEL32.@)
1689 HANDLE WINAPI FindFirstFileA(
1691 WIN32_FIND_DATAA *lpFindData )
1693 return FindFirstFileExA(lpFileName, FindExInfoStandard, lpFindData,
1694 FindExSearchNameMatch, NULL, 0);
1697 /*************************************************************************
1698 * FindFirstFileExW (KERNEL32.@)
1700 HANDLE WINAPI FindFirstFileExW(
1702 FINDEX_INFO_LEVELS fInfoLevelId,
1703 LPVOID lpFindFileData,
1704 FINDEX_SEARCH_OPS fSearchOp,
1705 LPVOID lpSearchFilter,
1706 DWORD dwAdditionalFlags)
1709 WIN32_FIND_DATAA dataA;
1710 LPVOID _lpFindFileData;
1713 switch(fInfoLevelId)
1715 case FindExInfoStandard:
1717 _lpFindFileData = &dataA;
1721 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
1722 return INVALID_HANDLE_VALUE;
1725 pathA = HEAP_strdupWtoA( GetProcessHeap(), 0, lpFileName );
1726 handle = FindFirstFileExA(pathA, fInfoLevelId, _lpFindFileData, fSearchOp, lpSearchFilter, dwAdditionalFlags);
1727 HeapFree( GetProcessHeap(), 0, pathA );
1728 if (handle == INVALID_HANDLE_VALUE) return handle;
1730 switch(fInfoLevelId)
1732 case FindExInfoStandard:
1734 WIN32_FIND_DATAW *dataW = (WIN32_FIND_DATAW*) lpFindFileData;
1735 dataW->dwFileAttributes = dataA.dwFileAttributes;
1736 dataW->ftCreationTime = dataA.ftCreationTime;
1737 dataW->ftLastAccessTime = dataA.ftLastAccessTime;
1738 dataW->ftLastWriteTime = dataA.ftLastWriteTime;
1739 dataW->nFileSizeHigh = dataA.nFileSizeHigh;
1740 dataW->nFileSizeLow = dataA.nFileSizeLow;
1741 MultiByteToWideChar( CP_ACP, 0, dataA.cFileName, -1,
1742 dataW->cFileName, sizeof(dataW->cFileName)/sizeof(WCHAR) );
1743 MultiByteToWideChar( CP_ACP, 0, dataA.cAlternateFileName, -1,
1744 dataW->cAlternateFileName,
1745 sizeof(dataW->cAlternateFileName)/sizeof(WCHAR) );
1749 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
1750 return INVALID_HANDLE_VALUE;
1755 /*************************************************************************
1756 * FindFirstFileW (KERNEL32.@)
1758 HANDLE WINAPI FindFirstFileW( LPCWSTR lpFileName, WIN32_FIND_DATAW *lpFindData )
1760 return FindFirstFileExW(lpFileName, FindExInfoStandard, lpFindData,
1761 FindExSearchNameMatch, NULL, 0);
1764 /*************************************************************************
1765 * FindNextFileA (KERNEL32.@)
1767 BOOL WINAPI FindNextFileA( HANDLE handle, WIN32_FIND_DATAA *data )
1769 FIND_FIRST_INFO *info;
1771 if ((handle == INVALID_HANDLE_VALUE) ||
1772 !(info = (FIND_FIRST_INFO *)GlobalLock( handle )))
1774 SetLastError( ERROR_INVALID_HANDLE );
1777 GlobalUnlock( handle );
1778 if (!info->path || !info->dir)
1780 SetLastError( ERROR_NO_MORE_FILES );
1783 if (!DOSFS_FindNextEx( info, data ))
1785 DOSFS_CloseDir( info->dir ); info->dir = NULL;
1786 HeapFree( GetProcessHeap(), 0, info->path );
1787 info->path = info->long_mask = NULL;
1788 SetLastError( ERROR_NO_MORE_FILES );
1795 /*************************************************************************
1796 * FindNextFileW (KERNEL32.@)
1798 BOOL WINAPI FindNextFileW( HANDLE handle, WIN32_FIND_DATAW *data )
1800 WIN32_FIND_DATAA dataA;
1801 if (!FindNextFileA( handle, &dataA )) return FALSE;
1802 data->dwFileAttributes = dataA.dwFileAttributes;
1803 data->ftCreationTime = dataA.ftCreationTime;
1804 data->ftLastAccessTime = dataA.ftLastAccessTime;
1805 data->ftLastWriteTime = dataA.ftLastWriteTime;
1806 data->nFileSizeHigh = dataA.nFileSizeHigh;
1807 data->nFileSizeLow = dataA.nFileSizeLow;
1808 MultiByteToWideChar( CP_ACP, 0, dataA.cFileName, -1,
1809 data->cFileName, sizeof(data->cFileName)/sizeof(WCHAR) );
1810 MultiByteToWideChar( CP_ACP, 0, dataA.cAlternateFileName, -1,
1811 data->cAlternateFileName,
1812 sizeof(data->cAlternateFileName)/sizeof(WCHAR) );
1816 /*************************************************************************
1817 * FindClose (KERNEL32.@)
1819 BOOL WINAPI FindClose( HANDLE handle )
1821 FIND_FIRST_INFO *info;
1823 if (handle == INVALID_HANDLE_VALUE) goto error;
1827 if ((info = (FIND_FIRST_INFO *)GlobalLock( handle )))
1829 if (info->dir) DOSFS_CloseDir( info->dir );
1830 if (info->path) HeapFree( GetProcessHeap(), 0, info->path );
1833 __EXCEPT(page_fault)
1835 WARN("Illegal handle %x\n", handle);
1836 SetLastError( ERROR_INVALID_HANDLE );
1840 if (!info) goto error;
1841 GlobalUnlock( handle );
1842 GlobalFree( handle );
1846 SetLastError( ERROR_INVALID_HANDLE );
1850 /***********************************************************************
1851 * DOSFS_UnixTimeToFileTime
1853 * Convert a Unix time to FILETIME format.
1854 * The FILETIME structure is a 64-bit value representing the number of
1855 * 100-nanosecond intervals since January 1, 1601, 0:00.
1856 * 'remainder' is the nonnegative number of 100-ns intervals
1857 * corresponding to the time fraction smaller than 1 second that
1858 * couldn't be stored in the time_t value.
1860 void DOSFS_UnixTimeToFileTime( time_t unix_time, FILETIME *filetime,
1866 The time difference between 1 January 1601, 00:00:00 and
1867 1 January 1970, 00:00:00 is 369 years, plus the leap years
1868 from 1604 to 1968, excluding 1700, 1800, 1900.
1869 This makes (1968 - 1600) / 4 - 3 = 89 leap days, and a total
1872 Any day in that period had 24 * 60 * 60 = 86400 seconds.
1874 The time difference is 134774 * 86400 * 10000000, which can be written
1876 27111902 * 2^32 + 3577643008
1877 413 * 2^48 + 45534 * 2^32 + 54590 * 2^16 + 32768
1879 If you find that these constants are buggy, please change them in all
1880 instances in both conversion functions.
1883 There are two versions, one of them uses long long variables and
1884 is presumably faster but not ISO C. The other one uses standard C
1885 data types and operations but relies on the assumption that negative
1886 numbers are stored as 2's complement (-1 is 0xffff....). If this
1887 assumption is violated, dates before 1970 will not convert correctly.
1888 This should however work on any reasonable architecture where WINE
1893 Take care not to remove the casts. I have tested these functions
1894 (in both versions) for a lot of numbers. I would be interested in
1895 results on other compilers than GCC.
1897 The operations have been designed to account for the possibility
1898 of 64-bit time_t in future UNICES. Even the versions without
1899 internal long long numbers will work if time_t only is 64 bit.
1900 A 32-bit shift, which was necessary for that operation, turned out
1901 not to work correctly in GCC, besides giving the warning. So I
1902 used a double 16-bit shift instead. Numbers are in the ISO version
1903 represented by three limbs, the most significant with 32 bit, the
1904 other two with 16 bit each.
1906 As the modulo-operator % is not well-defined for negative numbers,
1907 negative divisors have been avoided in DOSFS_FileTimeToUnixTime.
1909 There might be quicker ways to do this in C. Certainly so in
1912 Claus Fischer, fischer@iue.tuwien.ac.at
1915 #if SIZEOF_LONG_LONG >= 8
1916 # define USE_LONG_LONG 1
1918 # define USE_LONG_LONG 0
1921 #if USE_LONG_LONG /* gcc supports long long type */
1923 long long int t = unix_time;
1925 t += 116444736000000000LL;
1927 filetime->dwLowDateTime = (UINT)t;
1928 filetime->dwHighDateTime = (UINT)(t >> 32);
1930 #else /* ISO version */
1932 UINT a0; /* 16 bit, low bits */
1933 UINT a1; /* 16 bit, medium bits */
1934 UINT a2; /* 32 bit, high bits */
1936 /* Copy the unix time to a2/a1/a0 */
1937 a0 = unix_time & 0xffff;
1938 a1 = (unix_time >> 16) & 0xffff;
1939 /* This is obsolete if unix_time is only 32 bits, but it does not hurt.
1940 Do not replace this by >> 32, it gives a compiler warning and it does
1942 a2 = (unix_time >= 0 ? (unix_time >> 16) >> 16 :
1943 ~((~unix_time >> 16) >> 16));
1945 /* Multiply a by 10000000 (a = a2/a1/a0)
1946 Split the factor into 10000 * 1000 which are both less than 0xffff. */
1948 a1 = a1 * 10000 + (a0 >> 16);
1949 a2 = a2 * 10000 + (a1 >> 16);
1954 a1 = a1 * 1000 + (a0 >> 16);
1955 a2 = a2 * 1000 + (a1 >> 16);
1959 /* Add the time difference and the remainder */
1960 a0 += 32768 + (remainder & 0xffff);
1961 a1 += 54590 + (remainder >> 16 ) + (a0 >> 16);
1962 a2 += 27111902 + (a1 >> 16);
1967 filetime->dwLowDateTime = (a1 << 16) + a0;
1968 filetime->dwHighDateTime = a2;
1973 /***********************************************************************
1974 * DOSFS_FileTimeToUnixTime
1976 * Convert a FILETIME format to Unix time.
1977 * If not NULL, 'remainder' contains the fractional part of the filetime,
1978 * in the range of [0..9999999] (even if time_t is negative).
1980 time_t DOSFS_FileTimeToUnixTime( const FILETIME *filetime, DWORD *remainder )
1982 /* Read the comment in the function DOSFS_UnixTimeToFileTime. */
1985 long long int t = filetime->dwHighDateTime;
1987 t += (UINT)filetime->dwLowDateTime;
1988 t -= 116444736000000000LL;
1991 if (remainder) *remainder = 9999999 - (-t - 1) % 10000000;
1992 return -1 - ((-t - 1) / 10000000);
1996 if (remainder) *remainder = t % 10000000;
1997 return t / 10000000;
2000 #else /* ISO version */
2002 UINT a0; /* 16 bit, low bits */
2003 UINT a1; /* 16 bit, medium bits */
2004 UINT a2; /* 32 bit, high bits */
2005 UINT r; /* remainder of division */
2006 unsigned int carry; /* carry bit for subtraction */
2007 int negative; /* whether a represents a negative value */
2009 /* Copy the time values to a2/a1/a0 */
2010 a2 = (UINT)filetime->dwHighDateTime;
2011 a1 = ((UINT)filetime->dwLowDateTime ) >> 16;
2012 a0 = ((UINT)filetime->dwLowDateTime ) & 0xffff;
2014 /* Subtract the time difference */
2015 if (a0 >= 32768 ) a0 -= 32768 , carry = 0;
2016 else a0 += (1 << 16) - 32768 , carry = 1;
2018 if (a1 >= 54590 + carry) a1 -= 54590 + carry, carry = 0;
2019 else a1 += (1 << 16) - 54590 - carry, carry = 1;
2021 a2 -= 27111902 + carry;
2023 /* If a is negative, replace a by (-1-a) */
2024 negative = (a2 >= ((UINT)1) << 31);
2027 /* Set a to -a - 1 (a is a2/a1/a0) */
2033 /* Divide a by 10000000 (a = a2/a1/a0), put the rest into r.
2034 Split the divisor into 10000 * 1000 which are both less than 0xffff. */
2035 a1 += (a2 % 10000) << 16;
2037 a0 += (a1 % 10000) << 16;
2042 a1 += (a2 % 1000) << 16;
2044 a0 += (a1 % 1000) << 16;
2046 r += (a0 % 1000) * 10000;
2049 /* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
2052 /* Set a to -a - 1 (a is a2/a1/a0) */
2060 if (remainder) *remainder = r;
2062 /* Do not replace this by << 32, it gives a compiler warning and it does
2064 return ((((time_t)a2) << 16) << 16) + (a1 << 16) + a0;
2069 /***********************************************************************
2070 * MulDiv (KERNEL32.@)
2072 * Result of multiplication and division
2073 * -1: Overflow occurred or Divisor was 0
2080 #if SIZEOF_LONG_LONG >= 8
2083 if (!nDivisor) return -1;
2085 /* We want to deal with a positive divisor to simplify the logic. */
2088 nMultiplicand = - nMultiplicand;
2089 nDivisor = -nDivisor;
2092 /* If the result is positive, we "add" to round. else, we subtract to round. */
2093 if ( ( (nMultiplicand < 0) && (nMultiplier < 0) ) ||
2094 ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
2095 ret = (((long long)nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
2097 ret = (((long long)nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
2099 if ((ret > 2147483647) || (ret < -2147483647)) return -1;
2102 if (!nDivisor) return -1;
2104 /* We want to deal with a positive divisor to simplify the logic. */
2107 nMultiplicand = - nMultiplicand;
2108 nDivisor = -nDivisor;
2111 /* If the result is positive, we "add" to round. else, we subtract to round. */
2112 if ( ( (nMultiplicand < 0) && (nMultiplier < 0) ) ||
2113 ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
2114 return ((nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
2116 return ((nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
2122 /***********************************************************************
2123 * DosDateTimeToFileTime (KERNEL32.@)
2125 BOOL WINAPI DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft)
2129 newtm.tm_sec = (fattime & 0x1f) * 2;
2130 newtm.tm_min = (fattime >> 5) & 0x3f;
2131 newtm.tm_hour = (fattime >> 11);
2132 newtm.tm_mday = (fatdate & 0x1f);
2133 newtm.tm_mon = ((fatdate >> 5) & 0x0f) - 1;
2134 newtm.tm_year = (fatdate >> 9) + 80;
2135 RtlSecondsSince1970ToTime( mktime( &newtm ), 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 = localtime( &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,
2166 /* convert from local to UTC. Perhaps not correct. FIXME */
2167 time_t unixtime = DOSFS_FileTimeToUnixTime( localft, &remainder );
2168 xtm = gmtime( &unixtime );
2169 DOSFS_UnixTimeToFileTime( mktime(xtm), utcft, remainder );
2174 /***********************************************************************
2175 * FileTimeToLocalFileTime (KERNEL32.@)
2177 BOOL WINAPI FileTimeToLocalFileTime( const FILETIME *utcft,
2178 LPFILETIME localft )
2181 /* convert from UTC to local. Perhaps not correct. FIXME */
2182 time_t unixtime = DOSFS_FileTimeToUnixTime( utcft, &remainder );
2184 struct tm *xtm = localtime( &unixtime );
2187 localtime = timegm(xtm);
2188 DOSFS_UnixTimeToFileTime( localtime, localft, remainder );
2191 struct tm *xtm,*gtm;
2194 xtm = localtime( &unixtime );
2195 gtm = gmtime( &unixtime );
2196 time1 = mktime(xtm);
2197 time2 = mktime(gtm);
2198 DOSFS_UnixTimeToFileTime( 2*time1-time2, localft, remainder );
2204 /***********************************************************************
2205 * FileTimeToSystemTime (KERNEL32.@)
2207 BOOL WINAPI FileTimeToSystemTime( const FILETIME *ft, LPSYSTEMTIME syst )
2211 time_t xtime = DOSFS_FileTimeToUnixTime( ft, &remainder );
2212 xtm = gmtime(&xtime);
2213 syst->wYear = xtm->tm_year+1900;
2214 syst->wMonth = xtm->tm_mon + 1;
2215 syst->wDayOfWeek = xtm->tm_wday;
2216 syst->wDay = xtm->tm_mday;
2217 syst->wHour = xtm->tm_hour;
2218 syst->wMinute = xtm->tm_min;
2219 syst->wSecond = xtm->tm_sec;
2220 syst->wMilliseconds = remainder / 10000;
2224 /***********************************************************************
2225 * QueryDosDeviceA (KERNEL32.@)
2227 * returns array of strings terminated by \0, terminated by \0
2229 DWORD WINAPI QueryDosDeviceA(LPCSTR devname,LPSTR target,DWORD bufsize)
2234 TRACE("(%s,...)\n", devname ? devname : "<null>");
2236 /* return known MSDOS devices */
2237 static const char devices[24] = "CON\0COM1\0COM2\0LPT1\0NUL\0\0";
2238 memcpy( target, devices, min(bufsize,sizeof(devices)) );
2239 return min(bufsize,sizeof(devices));
2241 /* In theory all that are possible and have been defined.
2242 * Now just those below, since mirc uses it to check for special files.
2244 * (It is more complex, and supports netmounted stuff, and \\.\ stuff,
2245 * but currently we just ignore that.)
2247 #define CHECK(x) (strstr(devname,#x)==devname)
2248 if (CHECK(con) || CHECK(com) || CHECK(lpt) || CHECK(nul)) {
2249 strcpy(buffer,"\\DEV\\");
2250 strcat(buffer,devname);
2251 if ((s=strchr(buffer,':'))) *s='\0';
2252 lstrcpynA(target,buffer,bufsize);
2253 return strlen(buffer)+1;
2255 if (strchr(devname,':') || devname[0]=='\\') {
2256 /* This might be a DOS device we do not handle yet ... */
2257 FIXME("(%s) not detected as DOS device!\n",devname);
2259 SetLastError(ERROR_DEV_NOT_EXIST);
2266 /***********************************************************************
2267 * QueryDosDeviceW (KERNEL32.@)
2269 * returns array of strings terminated by \0, terminated by \0
2271 DWORD WINAPI QueryDosDeviceW(LPCWSTR devname,LPWSTR target,DWORD bufsize)
2273 LPSTR devnameA = devname?HEAP_strdupWtoA(GetProcessHeap(),0,devname):NULL;
2274 LPSTR targetA = (LPSTR)HeapAlloc(GetProcessHeap(),0,bufsize);
2275 DWORD ret = QueryDosDeviceA(devnameA,targetA,bufsize);
2277 ret = MultiByteToWideChar( CP_ACP, 0, targetA, ret, target, bufsize );
2278 if (devnameA) HeapFree(GetProcessHeap(),0,devnameA);
2279 if (targetA) HeapFree(GetProcessHeap(),0,targetA);
2284 /***********************************************************************
2285 * SystemTimeToFileTime (KERNEL32.@)
2287 BOOL WINAPI SystemTimeToFileTime( const SYSTEMTIME *syst, LPFILETIME ft )
2293 struct tm xtm,*local_tm,*utc_tm;
2294 time_t localtim,utctime;
2297 xtm.tm_year = syst->wYear-1900;
2298 xtm.tm_mon = syst->wMonth - 1;
2299 xtm.tm_wday = syst->wDayOfWeek;
2300 xtm.tm_mday = syst->wDay;
2301 xtm.tm_hour = syst->wHour;
2302 xtm.tm_min = syst->wMinute;
2303 xtm.tm_sec = syst->wSecond; /* this is UTC */
2306 utctime = timegm(&xtm);
2307 DOSFS_UnixTimeToFileTime( utctime, ft,
2308 syst->wMilliseconds * 10000 );
2310 localtim = mktime(&xtm); /* now we've got local time */
2311 local_tm = localtime(&localtim);
2312 utc_tm = gmtime(&localtim);
2313 utctime = mktime(utc_tm);
2314 DOSFS_UnixTimeToFileTime( 2*localtim -utctime, ft,
2315 syst->wMilliseconds * 10000 );
2320 /***********************************************************************
2321 * DefineDosDeviceA (KERNEL32.@)
2323 BOOL WINAPI DefineDosDeviceA(DWORD flags,LPCSTR devname,LPCSTR targetpath) {
2324 FIXME("(0x%08lx,%s,%s),stub!\n",flags,devname,targetpath);
2325 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2330 --- 16 bit functions ---
2333 /*************************************************************************
2334 * FindFirstFile (KERNEL.413)
2336 HANDLE16 WINAPI FindFirstFile16( LPCSTR path, WIN32_FIND_DATAA *data )
2338 DOS_FULL_NAME full_name;
2340 FIND_FIRST_INFO *info;
2342 data->dwReserved0 = data->dwReserved1 = 0x0;
2343 if (!path) return 0;
2344 if (!DOSFS_GetFullName( path, FALSE, &full_name ))
2345 return INVALID_HANDLE_VALUE16;
2346 if (!(handle = GlobalAlloc16( GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO) )))
2347 return INVALID_HANDLE_VALUE16;
2348 info = (FIND_FIRST_INFO *)GlobalLock16( handle );
2349 info->path = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
2350 strcpy( info->path, full_name.long_name );
2351 info->long_mask = strrchr( info->path, '/' );
2352 if (info->long_mask )
2353 *(info->long_mask++) = '\0';
2354 info->short_mask = NULL;
2356 if (path[0] && (path[1] == ':')) info->drive = FILE_toupper(*path) - 'A';
2357 else info->drive = DRIVE_GetCurrentDrive();
2360 info->dir = DOSFS_OpenDir( info->path );
2362 GlobalUnlock16( handle );
2363 if (!FindNextFile16( handle, data ))
2365 FindClose16( handle );
2366 SetLastError( ERROR_NO_MORE_FILES );
2367 return INVALID_HANDLE_VALUE16;
2372 /*************************************************************************
2373 * FindNextFile (KERNEL.414)
2375 BOOL16 WINAPI FindNextFile16( HANDLE16 handle, WIN32_FIND_DATAA *data )
2377 FIND_FIRST_INFO *info;
2379 if ((handle == INVALID_HANDLE_VALUE16) ||
2380 !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
2382 SetLastError( ERROR_INVALID_HANDLE );
2385 GlobalUnlock16( handle );
2386 if (!info->path || !info->dir)
2388 SetLastError( ERROR_NO_MORE_FILES );
2391 if (!DOSFS_FindNextEx( info, data ))
2393 DOSFS_CloseDir( info->dir ); info->dir = NULL;
2394 HeapFree( GetProcessHeap(), 0, info->path );
2395 info->path = info->long_mask = NULL;
2396 SetLastError( ERROR_NO_MORE_FILES );
2402 /*************************************************************************
2403 * FindClose (KERNEL.415)
2405 BOOL16 WINAPI FindClose16( HANDLE16 handle )
2407 FIND_FIRST_INFO *info;
2409 if ((handle == INVALID_HANDLE_VALUE16) ||
2410 !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
2412 SetLastError( ERROR_INVALID_HANDLE );
2415 if (info->dir) DOSFS_CloseDir( info->dir );
2416 if (info->path) HeapFree( GetProcessHeap(), 0, info->path );
2417 GlobalUnlock16( handle );
2418 GlobalFree16( handle );