2 * DOS file system functions
4 * Copyright 1993 Erik Bos
5 * Copyright 1996 Alexandre Julliard
10 #include <sys/types.h>
14 #ifdef HAVE_SYS_ERRNO_H
15 #include <sys/errno.h>
21 #include <sys/ioctl.h>
29 #include "wine/unicode.h"
30 #include "wine/winbase16.h"
37 #include "wine/server.h"
39 #include "debugtools.h"
41 DEFAULT_DEBUG_CHANNEL(dosfs);
42 DECLARE_DEBUG_CHANNEL(file);
44 /* Define the VFAT ioctl to get both short and long file names */
45 /* FIXME: is it possible to get this to work on other systems? */
47 /* We want the real kernel dirent structure, not the libc one */
52 unsigned short d_reclen;
56 #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, KERNEL_DIRENT [2] )
59 #undef VFAT_IOCTL_READDIR_BOTH /* just in case... */
62 /* Chars we don't want to see in DOS file names */
63 #define INVALID_DOS_CHARS "*?<>|\"+=,;[] \345"
65 static const DOS_DEVICE DOSFS_Devices[] =
66 /* name, device flags (see Int 21/AX=0x4400) */
80 { "SCSIMGR$", 0xc0c0 },
82 { "EMMXXXX0", 0x0000 }
85 #define GET_DRIVE(path) \
86 (((path)[1] == ':') ? FILE_toupper((path)[0]) - 'A' : DOSFS_CurDrive)
88 /* Directory info for DOSFS_ReadDir */
92 #ifdef VFAT_IOCTL_READDIR_BOTH
95 KERNEL_DIRENT dirent[2];
99 /* Info structure for FindFirstFile handle */
112 static WINE_EXCEPTION_FILTER(page_fault)
114 if (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION)
115 return EXCEPTION_EXECUTE_HANDLER;
116 return EXCEPTION_CONTINUE_SEARCH;
120 /***********************************************************************
123 * Return 1 if Unix file 'name' is also a valid MS-DOS name
124 * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
125 * File name can be terminated by '\0', '\\' or '/'.
127 static int DOSFS_ValidDOSName( const char *name, int ignore_case )
129 static const char invalid_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS;
130 const char *p = name;
131 const char *invalid = ignore_case ? (invalid_chars + 26) : invalid_chars;
136 /* Check for "." and ".." */
139 /* All other names beginning with '.' are invalid */
140 return (IS_END_OF_NAME(*p));
142 while (!IS_END_OF_NAME(*p))
144 if (strchr( invalid, *p )) return 0; /* Invalid char */
145 if (*p == '.') break; /* Start of the extension */
146 if (++len > 8) return 0; /* Name too long */
149 if (*p != '.') return 1; /* End of name */
151 if (IS_END_OF_NAME(*p)) return 0; /* Empty extension not allowed */
153 while (!IS_END_OF_NAME(*p))
155 if (strchr( invalid, *p )) return 0; /* Invalid char */
156 if (*p == '.') return 0; /* Second extension not allowed */
157 if (++len > 3) return 0; /* Extension too long */
164 /***********************************************************************
165 * DOSFS_ToDosFCBFormat
167 * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
168 * expanding wild cards and converting to upper-case in the process.
169 * File name can be terminated by '\0', '\\' or '/'.
170 * Return FALSE if the name is not a valid DOS name.
171 * 'buffer' must be at least 12 characters long.
173 BOOL DOSFS_ToDosFCBFormat( LPCSTR name, LPSTR buffer )
175 static const char invalid_chars[] = INVALID_DOS_CHARS;
176 const char *p = name;
179 /* Check for "." and ".." */
183 strcpy( buffer, ". " );
189 return (!*p || (*p == '/') || (*p == '\\'));
192 for (i = 0; i < 8; i++)
209 if (strchr( invalid_chars, *p )) return FALSE;
210 buffer[i] = FILE_toupper(*p);
218 /* Skip all chars after wildcard up to first dot */
219 while (*p && (*p != '/') && (*p != '\\') && (*p != '.')) p++;
223 /* Check if name too long */
224 if (*p && (*p != '/') && (*p != '\\') && (*p != '.')) return FALSE;
226 if (*p == '.') p++; /* Skip dot */
228 for (i = 8; i < 11; i++)
238 return FALSE; /* Second extension not allowed */
246 if (strchr( invalid_chars, *p )) return FALSE;
247 buffer[i] = FILE_toupper(*p);
254 /* at most 3 character of the extension are processed
255 * is something behind this ?
257 while (*p == '*' || *p == ' ') p++; /* skip wildcards and spaces */
258 return IS_END_OF_NAME(*p);
262 /***********************************************************************
263 * DOSFS_ToDosDTAFormat
265 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
266 * converting to upper-case in the process.
267 * File name can be terminated by '\0', '\\' or '/'.
268 * 'buffer' must be at least 13 characters long.
270 static void DOSFS_ToDosDTAFormat( LPCSTR name, LPSTR buffer )
274 memcpy( buffer, name, 8 );
276 while ((p > buffer) && (p[-1] == ' ')) p--;
278 memcpy( p, name + 8, 3 );
280 while (p[-1] == ' ') p--;
281 if (p[-1] == '.') p--;
286 /***********************************************************************
289 * Check a DOS file name against a mask (both in FCB format).
291 static int DOSFS_MatchShort( const char *mask, const char *name )
294 for (i = 11; i > 0; i--, mask++, name++)
295 if ((*mask != '?') && (*mask != *name)) return 0;
300 /***********************************************************************
303 * Check a long file name against a mask.
305 * Tests (done in W95 DOS shell - case insensitive):
306 * *.txt test1.test.txt *
308 * *.t??????.t* test1.ta.tornado.txt *
309 * *tornado* test1.ta.tornado.txt *
310 * t*t test1.ta.tornado.txt *
312 * ?est??? test1.txt -
313 * *test1.txt* test1.txt *
314 * h?l?o*t.dat hellothisisatest.dat *
316 static int DOSFS_MatchLong( const char *mask, const char *name,
319 const char *lastjoker = NULL;
320 const char *next_to_retry = NULL;
322 if (!strcmp( mask, "*.*" )) return 1;
323 while (*name && *mask)
328 while (*mask == '*') mask++; /* Skip consecutive '*' */
330 if (!*mask) return 1; /* end of mask is all '*', so match */
332 /* skip to the next match after the joker(s) */
333 if (case_sensitive) while (*name && (*name != *mask)) name++;
334 else while (*name && (FILE_toupper(*name) != FILE_toupper(*mask))) name++;
337 next_to_retry = name;
339 else if (*mask != '?')
344 if (*mask != *name) mismatch = 1;
348 if (FILE_toupper(*mask) != FILE_toupper(*name)) mismatch = 1;
362 else /* mismatch ! */
364 if (lastjoker) /* we had an '*', so we can try unlimitedly */
368 /* this scan sequence was a mismatch, so restart
369 * 1 char after the first char we checked last time */
371 name = next_to_retry;
374 return 0; /* bad luck */
383 while ((*mask == '.') || (*mask == '*'))
384 mask++; /* Ignore trailing '.' or '*' in mask */
385 return (!*name && !*mask);
389 /***********************************************************************
392 static DOS_DIR *DOSFS_OpenDir( LPCSTR path )
394 DOS_DIR *dir = HeapAlloc( GetProcessHeap(), 0, sizeof(*dir) );
397 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
401 /* Treat empty path as root directory. This simplifies path split into
402 directory and mask in several other places */
403 if (!*path) path = "/";
405 #ifdef VFAT_IOCTL_READDIR_BOTH
407 /* Check if the VFAT ioctl is supported on this directory */
409 if ((dir->fd = open( path, O_RDONLY )) != -1)
411 if (ioctl( dir->fd, VFAT_IOCTL_READDIR_BOTH, (long)dir->dirent ) == -1)
418 /* Set the file pointer back at the start of the directory */
419 lseek( dir->fd, 0, SEEK_SET );
424 #endif /* VFAT_IOCTL_READDIR_BOTH */
426 /* Now use the standard opendir/readdir interface */
428 if (!(dir->dir = opendir( path )))
430 HeapFree( GetProcessHeap(), 0, dir );
437 /***********************************************************************
440 static void DOSFS_CloseDir( DOS_DIR *dir )
442 #ifdef VFAT_IOCTL_READDIR_BOTH
443 if (dir->fd != -1) close( dir->fd );
444 #endif /* VFAT_IOCTL_READDIR_BOTH */
445 if (dir->dir) closedir( dir->dir );
446 HeapFree( GetProcessHeap(), 0, dir );
450 /***********************************************************************
453 static BOOL DOSFS_ReadDir( DOS_DIR *dir, LPCSTR *long_name,
456 struct dirent *dirent;
458 #ifdef VFAT_IOCTL_READDIR_BOTH
461 if (ioctl( dir->fd, VFAT_IOCTL_READDIR_BOTH, (long)dir->dirent ) != -1) {
462 if (!dir->dirent[0].d_reclen) return FALSE;
463 if (!DOSFS_ToDosFCBFormat( dir->dirent[0].d_name, dir->short_name ))
464 dir->short_name[0] = '\0';
465 *short_name = dir->short_name;
466 if (dir->dirent[1].d_name[0]) *long_name = dir->dirent[1].d_name;
467 else *long_name = dir->dirent[0].d_name;
471 #endif /* VFAT_IOCTL_READDIR_BOTH */
473 if (!(dirent = readdir( dir->dir ))) return FALSE;
474 *long_name = dirent->d_name;
480 /***********************************************************************
483 * Transform a Unix file name into a hashed DOS name. If the name is a valid
484 * DOS name, it is converted to upper-case; otherwise it is replaced by a
485 * hashed version that fits in 8.3 format.
486 * File name can be terminated by '\0', '\\' or '/'.
487 * 'buffer' must be at least 13 characters long.
489 static void DOSFS_Hash( LPCSTR name, LPSTR buffer, BOOL dir_format,
492 static const char invalid_chars[] = INVALID_DOS_CHARS "~.";
493 static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
500 if (dir_format) strcpy( buffer, " " );
502 if (DOSFS_ValidDOSName( name, ignore_case ))
504 /* Check for '.' and '..' */
508 if (!dir_format) buffer[1] = buffer[2] = '\0';
509 if (name[1] == '.') buffer[1] = '.';
513 /* Simply copy the name, converting to uppercase */
515 for (dst = buffer; !IS_END_OF_NAME(*name) && (*name != '.'); name++)
516 *dst++ = FILE_toupper(*name);
519 if (dir_format) dst = buffer + 8;
521 for (name++; !IS_END_OF_NAME(*name); name++)
522 *dst++ = FILE_toupper(*name);
524 if (!dir_format) *dst = '\0';
528 /* Compute the hash code of the file name */
529 /* If you know something about hash functions, feel free to */
530 /* insert a better algorithm here... */
533 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
534 hash = (hash<<3) ^ (hash>>5) ^ FILE_tolower(*p) ^ (FILE_tolower(p[1]) << 8);
535 hash = (hash<<3) ^ (hash>>5) ^ FILE_tolower(*p); /* Last character*/
539 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
540 hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
541 hash = (hash << 3) ^ (hash >> 5) ^ *p; /* Last character */
544 /* Find last dot for start of the extension */
545 for (p = name+1, ext = NULL; !IS_END_OF_NAME(*p); p++)
546 if (*p == '.') ext = p;
547 if (ext && IS_END_OF_NAME(ext[1]))
548 ext = NULL; /* Empty extension ignored */
550 /* Copy first 4 chars, replacing invalid chars with '_' */
551 for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
553 if (IS_END_OF_NAME(*p) || (p == ext)) break;
554 *dst++ = strchr( invalid_chars, *p ) ? '_' : FILE_toupper(*p);
556 /* Pad to 5 chars with '~' */
557 while (i-- >= 0) *dst++ = '~';
559 /* Insert hash code converted to 3 ASCII chars */
560 *dst++ = hash_chars[(hash >> 10) & 0x1f];
561 *dst++ = hash_chars[(hash >> 5) & 0x1f];
562 *dst++ = hash_chars[hash & 0x1f];
564 /* Copy the first 3 chars of the extension (if any) */
567 if (!dir_format) *dst++ = '.';
568 for (i = 3, ext++; (i > 0) && !IS_END_OF_NAME(*ext); i--, ext++)
569 *dst++ = strchr( invalid_chars, *ext ) ? '_' : FILE_toupper(*ext);
571 if (!dir_format) *dst = '\0';
575 /***********************************************************************
578 * Find the Unix file name in a given directory that corresponds to
579 * a file name (either in Unix or DOS format).
580 * File name can be terminated by '\0', '\\' or '/'.
581 * Return TRUE if OK, FALSE if no file name matches.
583 * 'long_buf' must be at least 'long_len' characters long. If the long name
584 * turns out to be larger than that, the function returns FALSE.
585 * 'short_buf' must be at least 13 characters long.
587 BOOL DOSFS_FindUnixName( LPCSTR path, LPCSTR name, LPSTR long_buf,
588 INT long_len, LPSTR short_buf, BOOL ignore_case)
591 LPCSTR long_name, short_name;
592 char dos_name[12], tmp_buf[13];
595 const char *p = strchr( name, '/' );
596 int len = p ? (int)(p - name) : strlen(name);
597 if ((p = strchr( name, '\\' ))) len = min( (int)(p - name), len );
598 /* Ignore trailing dots and spaces */
599 while (len > 1 && (name[len-1] == '.' || name[len-1] == ' ')) len--;
600 if (long_len < len + 1) return FALSE;
602 TRACE("%s,%s\n", path, name );
604 if (!DOSFS_ToDosFCBFormat( name, dos_name )) dos_name[0] = '\0';
606 if (!(dir = DOSFS_OpenDir( path )))
608 WARN("(%s,%s): can't open dir: %s\n",
609 path, name, strerror(errno) );
613 while ((ret = DOSFS_ReadDir( dir, &long_name, &short_name )))
615 /* Check against Unix name */
616 if (len == strlen(long_name))
620 if (!strncmp( long_name, name, len )) break;
624 if (!FILE_strncasecmp( long_name, name, len )) break;
629 /* Check against hashed DOS name */
632 DOSFS_Hash( long_name, tmp_buf, TRUE, ignore_case );
633 short_name = tmp_buf;
635 if (!strcmp( dos_name, short_name )) break;
640 if (long_buf) strcpy( long_buf, long_name );
644 DOSFS_ToDosDTAFormat( short_name, short_buf );
646 DOSFS_Hash( long_name, short_buf, FALSE, ignore_case );
648 TRACE("(%s,%s) -> %s (%s)\n",
649 path, name, long_name, short_buf ? short_buf : "***");
652 WARN("'%s' not found in '%s'\n", name, path);
653 DOSFS_CloseDir( dir );
658 /***********************************************************************
661 * Check if a DOS file name represents a DOS device and return the device.
663 const DOS_DEVICE *DOSFS_GetDevice( const char *name )
668 if (!name) return NULL; /* if FILE_DupUnixHandle was used */
669 if (name[0] && (name[1] == ':')) name += 2;
670 if ((p = strrchr( name, '/' ))) name = p + 1;
671 if ((p = strrchr( name, '\\' ))) name = p + 1;
672 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
674 const char *dev = DOSFS_Devices[i].name;
675 if (!FILE_strncasecmp( dev, name, strlen(dev) ))
677 p = name + strlen( dev );
678 if (!*p || (*p == '.') || (*p == ':')) return &DOSFS_Devices[i];
685 /***********************************************************************
686 * DOSFS_GetDeviceByHandle
688 const DOS_DEVICE *DOSFS_GetDeviceByHandle( HFILE hFile )
690 const DOS_DEVICE *ret = NULL;
691 SERVER_START_REQ( get_file_info )
694 if (!wine_server_call( req ) && (reply->type == FILE_TYPE_UNKNOWN))
696 if ((reply->attr >= 0) &&
697 (reply->attr < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0])))
698 ret = &DOSFS_Devices[reply->attr];
706 /**************************************************************************
707 * DOSFS_CreateCommPort
709 static HANDLE DOSFS_CreateCommPort(LPCSTR name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa)
714 TRACE_(file)("%s %lx %lx\n", name, access, attributes);
716 PROFILE_GetWineIniString("serialports",name,"",devname,sizeof devname);
720 TRACE("opening %s as %s\n", devname, name);
722 SERVER_START_REQ( create_serial )
724 req->access = access;
725 req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
726 req->attributes = attributes;
727 req->sharing = FILE_SHARE_READ|FILE_SHARE_WRITE;
728 wine_server_add_data( req, devname, strlen(devname) );
730 wine_server_call_err( req );
736 ERR("Couldn't open device '%s' ! (check permissions)\n",devname);
738 TRACE("return %08X\n", ret );
742 /***********************************************************************
745 * Open a DOS device. This might not map 1:1 into the UNIX device concept.
746 * Returns 0 on failure.
748 HANDLE DOSFS_OpenDevice( const char *name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa )
754 if (name[0] && (name[1] == ':')) name += 2;
755 if ((p = strrchr( name, '/' ))) name = p + 1;
756 if ((p = strrchr( name, '\\' ))) name = p + 1;
757 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
759 const char *dev = DOSFS_Devices[i].name;
760 if (!FILE_strncasecmp( dev, name, strlen(dev) ))
762 p = name + strlen( dev );
763 if (!*p || (*p == '.') || (*p == ':')) {
765 if (!strcmp(DOSFS_Devices[i].name,"NUL"))
766 return FILE_CreateFile( "/dev/null", access,
767 FILE_SHARE_READ|FILE_SHARE_WRITE, sa,
768 OPEN_EXISTING, 0, 0, TRUE, DRIVE_UNKNOWN );
769 if (!strcmp(DOSFS_Devices[i].name,"CON")) {
771 switch (access & (GENERIC_READ|GENERIC_WRITE)) {
773 to_dup = GetStdHandle( STD_INPUT_HANDLE );
776 to_dup = GetStdHandle( STD_OUTPUT_HANDLE );
779 FIXME("can't open CON read/write\n");
782 if (!DuplicateHandle( GetCurrentProcess(), to_dup, GetCurrentProcess(),
784 sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle,
785 DUPLICATE_SAME_ACCESS ))
789 if (!strcmp(DOSFS_Devices[i].name,"SCSIMGR$") ||
790 !strcmp(DOSFS_Devices[i].name,"HPSCAN") ||
791 !strcmp(DOSFS_Devices[i].name,"EMMXXXX0"))
793 return FILE_CreateDevice( i, access, sa );
796 if( (handle=DOSFS_CreateCommPort(DOSFS_Devices[i].name,access,attributes,sa)) )
798 FIXME("device open %s not supported (yet)\n",DOSFS_Devices[i].name);
807 /***********************************************************************
810 * Get the drive specified by a given path name (DOS or Unix format).
812 static int DOSFS_GetPathDrive( const char **name )
815 const char *p = *name;
817 if (*p && (p[1] == ':'))
819 drive = FILE_toupper(*p) - 'A';
822 else if (*p == '/') /* Absolute Unix path? */
824 if ((drive = DRIVE_FindDriveRoot( name )) == -1)
826 MESSAGE("Warning: %s not accessible from a configured DOS drive\n", *name );
827 /* Assume it really was a DOS name */
828 drive = DRIVE_GetCurrentDrive();
831 else drive = DRIVE_GetCurrentDrive();
833 if (!DRIVE_IsValid(drive))
835 SetLastError( ERROR_INVALID_DRIVE );
842 /***********************************************************************
845 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
846 * Unix name / short DOS name pair.
847 * Return FALSE if one of the path components does not exist. The last path
848 * component is only checked if 'check_last' is non-zero.
849 * The buffers pointed to by 'long_buf' and 'short_buf' must be
850 * at least MAX_PATHNAME_LEN long.
852 BOOL DOSFS_GetFullName( LPCSTR name, BOOL check_last, DOS_FULL_NAME *full )
856 char *p_l, *p_s, *root;
858 TRACE("%s (last=%d)\n", name, check_last );
860 if ((!*name) || (*name=='\n'))
861 { /* error code for Win98 */
862 SetLastError(ERROR_BAD_PATHNAME);
866 if ((full->drive = DOSFS_GetPathDrive( &name )) == -1) return FALSE;
867 flags = DRIVE_GetFlags( full->drive );
869 lstrcpynA( full->long_name, DRIVE_GetRoot( full->drive ),
870 sizeof(full->long_name) );
871 if (full->long_name[1]) root = full->long_name + strlen(full->long_name);
872 else root = full->long_name; /* root directory */
874 strcpy( full->short_name, "A:\\" );
875 full->short_name[0] += full->drive;
877 if ((*name == '\\') || (*name == '/')) /* Absolute path */
879 while ((*name == '\\') || (*name == '/')) name++;
881 else /* Relative path */
883 lstrcpynA( root + 1, DRIVE_GetUnixCwd( full->drive ),
884 sizeof(full->long_name) - (root - full->long_name) - 1 );
885 if (root[1]) *root = '/';
886 lstrcpynA( full->short_name + 3, DRIVE_GetDosCwd( full->drive ),
887 sizeof(full->short_name) - 3 );
890 p_l = full->long_name[1] ? full->long_name + strlen(full->long_name)
892 p_s = full->short_name[3] ? full->short_name + strlen(full->short_name)
893 : full->short_name + 2;
896 while (*name && found)
898 /* Check for '.' and '..' */
902 if (IS_END_OF_NAME(name[1]))
905 while ((*name == '\\') || (*name == '/')) name++;
908 else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
911 while ((*name == '\\') || (*name == '/')) name++;
912 while ((p_l > root) && (*p_l != '/')) p_l--;
913 while ((p_s > full->short_name + 2) && (*p_s != '\\')) p_s--;
914 *p_l = *p_s = '\0'; /* Remove trailing separator */
919 /* Make sure buffers are large enough */
921 if ((p_s >= full->short_name + sizeof(full->short_name) - 14) ||
922 (p_l >= full->long_name + sizeof(full->long_name) - 1))
924 SetLastError( ERROR_PATH_NOT_FOUND );
928 /* Get the long and short name matching the file name */
930 if ((found = DOSFS_FindUnixName( full->long_name, name, p_l + 1,
931 sizeof(full->long_name) - (p_l - full->long_name) - 1,
932 p_s + 1, !(flags & DRIVE_CASE_SENSITIVE) )))
938 while (!IS_END_OF_NAME(*name)) name++;
940 else if (!check_last)
944 while (!IS_END_OF_NAME(*name) &&
945 (p_s < full->short_name + sizeof(full->short_name) - 1) &&
946 (p_l < full->long_name + sizeof(full->long_name) - 1))
948 *p_s++ = FILE_tolower(*name);
949 /* If the drive is case-sensitive we want to create new */
950 /* files in lower-case otherwise we can't reopen them */
951 /* under the same short name. */
952 if (flags & DRIVE_CASE_SENSITIVE) *p_l++ = FILE_tolower(*name);
956 /* Ignore trailing dots and spaces */
957 while(p_l[-1] == '.' || p_l[-1] == ' ') {
963 while ((*name == '\\') || (*name == '/')) name++;
970 SetLastError( ERROR_FILE_NOT_FOUND );
973 if (*name) /* Not last */
975 SetLastError( ERROR_PATH_NOT_FOUND );
979 if (!full->long_name[0]) strcpy( full->long_name, "/" );
980 if (!full->short_name[2]) strcpy( full->short_name + 2, "\\" );
981 TRACE("returning %s = %s\n", full->long_name, full->short_name );
986 /***********************************************************************
987 * GetShortPathNameA (KERNEL32.@)
991 * longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
992 * longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0
994 * more observations ( with NT 3.51 (WinDD) ):
995 * longpath <= 8.3 -> just copy longpath to shortpath
997 * a) file does not exist -> return 0, LastError = ERROR_FILE_NOT_FOUND
998 * b) file does exist -> set the short filename.
999 * - trailing slashes are reproduced in the short name, even if the
1000 * file is not a directory
1001 * - the absolute/relative path of the short name is reproduced like found
1003 * - longpath and shortpath may have the same address
1004 * Peter Ganten, 1999
1006 DWORD WINAPI GetShortPathNameA( LPCSTR longpath, LPSTR shortpath,
1009 DOS_FULL_NAME full_name;
1011 DWORD sp = 0, lp = 0;
1015 TRACE("%s\n", debugstr_a(longpath));
1018 SetLastError(ERROR_INVALID_PARAMETER);
1022 SetLastError(ERROR_BAD_PATHNAME);
1026 if ( ( tmpshortpath = HeapAlloc ( GetProcessHeap(), 0, MAX_PATHNAME_LEN ) ) == NULL ) {
1027 SetLastError ( ERROR_NOT_ENOUGH_MEMORY );
1031 /* check for drive letter */
1032 if ( longpath[1] == ':' ) {
1033 tmpshortpath[0] = longpath[0];
1034 tmpshortpath[1] = ':';
1038 if ( ( drive = DOSFS_GetPathDrive ( &longpath )) == -1 ) return 0;
1039 flags = DRIVE_GetFlags ( drive );
1041 while ( longpath[lp] ) {
1043 /* check for path delimiters and reproduce them */
1044 if ( longpath[lp] == '\\' || longpath[lp] == '/' ) {
1045 if (!sp || tmpshortpath[sp-1]!= '\\')
1047 /* strip double "\\" */
1048 tmpshortpath[sp] = '\\';
1051 tmpshortpath[sp]=0;/*terminate string*/
1056 tmplen = strcspn ( longpath + lp, "\\/" );
1057 lstrcpynA ( tmpshortpath+sp, longpath + lp, tmplen+1 );
1059 /* Check, if the current element is a valid dos name */
1060 if ( DOSFS_ValidDOSName ( longpath + lp, !(flags & DRIVE_CASE_SENSITIVE) ) ) {
1066 /* Check if the file exists and use the existing file name */
1067 if ( DOSFS_GetFullName ( tmpshortpath, TRUE, &full_name ) ) {
1068 strcpy( tmpshortpath+sp, strrchr ( full_name.short_name, '\\' ) + 1 );
1069 sp += strlen ( tmpshortpath+sp );
1074 TRACE("not found!\n" );
1075 SetLastError ( ERROR_FILE_NOT_FOUND );
1078 tmpshortpath[sp] = 0;
1080 lstrcpynA ( shortpath, tmpshortpath, shortlen );
1081 TRACE("returning %s\n", debugstr_a(shortpath) );
1082 tmplen = strlen ( tmpshortpath );
1083 HeapFree ( GetProcessHeap(), 0, tmpshortpath );
1089 /***********************************************************************
1090 * GetShortPathNameW (KERNEL32.@)
1092 DWORD WINAPI GetShortPathNameW( LPCWSTR longpath, LPWSTR shortpath,
1095 LPSTR longpathA, shortpathA;
1098 longpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, longpath );
1099 shortpathA = HeapAlloc ( GetProcessHeap(), 0, shortlen );
1101 ret = GetShortPathNameA ( longpathA, shortpathA, shortlen );
1102 if (shortlen > 0 && !MultiByteToWideChar( CP_ACP, 0, shortpathA, -1, shortpath, shortlen ))
1103 shortpath[shortlen-1] = 0;
1104 HeapFree( GetProcessHeap(), 0, longpathA );
1105 HeapFree( GetProcessHeap(), 0, shortpathA );
1111 /***********************************************************************
1112 * GetLongPathNameA (KERNEL32.@)
1115 * observed (Win2000):
1116 * shortpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1117 * shortpath="": LastError=ERROR_PATH_NOT_FOUND, ret=0
1119 DWORD WINAPI GetLongPathNameA( LPCSTR shortpath, LPSTR longpath,
1122 DOS_FULL_NAME full_name;
1123 char *p, *r, *ll, *ss;
1126 SetLastError(ERROR_INVALID_PARAMETER);
1129 if (!shortpath[0]) {
1130 SetLastError(ERROR_PATH_NOT_FOUND);
1134 if (!DOSFS_GetFullName( shortpath, TRUE, &full_name )) return 0;
1135 lstrcpynA( longpath, full_name.short_name, longlen );
1137 /* Do some hackery to get the long filename. */
1140 ss=longpath+strlen(longpath);
1141 ll=full_name.long_name+strlen(full_name.long_name);
1143 while (ss>=longpath)
1145 /* FIXME: aren't we more paranoid, than needed? */
1146 while ((ss[0]=='\\') && (ss>=longpath)) ss--;
1148 while ((ss[0]!='\\') && (ss>=longpath)) ss--;
1151 /* FIXME: aren't we more paranoid, than needed? */
1152 while ((ll[0]=='/') && (ll>=full_name.long_name)) ll--;
1153 while ((ll[0]!='/') && (ll>=full_name.long_name)) ll--;
1154 if (ll<full_name.long_name)
1156 ERR("Bad longname! (ss=%s ll=%s)\n This should never happen !\n"
1163 /* FIXME: fix for names like "C:\\" (ie. with more '\'s) */
1167 if ((p-longpath)>0) longlen -= (p-longpath);
1168 lstrcpynA( p, ll , longlen);
1170 /* Now, change all '/' to '\' */
1171 for (r=p; r<(p+longlen); r++ )
1172 if (r[0]=='/') r[0]='\\';
1173 return strlen(longpath) - strlen(p) + longlen;
1177 return strlen(longpath);
1181 /***********************************************************************
1182 * GetLongPathNameW (KERNEL32.@)
1184 DWORD WINAPI GetLongPathNameW( LPCWSTR shortpath, LPWSTR longpath,
1187 DOS_FULL_NAME full_name;
1189 LPSTR shortpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, shortpath );
1191 /* FIXME: is it correct to always return a fully qualified short path? */
1192 if (DOSFS_GetFullName( shortpathA, TRUE, &full_name ))
1194 ret = strlen( full_name.short_name );
1195 if (longlen > 0 && !MultiByteToWideChar( CP_ACP, 0, full_name.long_name, -1,
1196 longpath, longlen ))
1197 longpath[longlen-1] = 0;
1199 HeapFree( GetProcessHeap(), 0, shortpathA );
1204 /***********************************************************************
1205 * DOSFS_DoGetFullPathName
1207 * Implementation of GetFullPathNameA/W.
1209 * bon@elektron 000331:
1210 * A test for GetFullPathName with many pathological cases
1211 * now gives identical output for Wine and OSR2
1213 static DWORD DOSFS_DoGetFullPathName( LPCSTR name, DWORD len, LPSTR result,
1217 DOS_FULL_NAME full_name;
1220 char drivecur[]="c:.";
1222 int namelen,drive=0;
1224 if ((strlen(name) >1)&& (name[1]==':'))
1225 /* drive letter given */
1227 driveletter = name[0];
1229 if ((strlen(name) >2)&& (name[1]==':') &&
1230 ((name[2]=='\\') || (name[2]=='/')))
1231 /* absolute path given */
1233 lstrcpynA(full_name.short_name,name,MAX_PATHNAME_LEN);
1234 drive = (int)FILE_toupper(name[0]) - 'A';
1239 drivecur[0]=driveletter;
1241 strcpy(drivecur,".");
1242 if (!DOSFS_GetFullName( drivecur, FALSE, &full_name ))
1244 FIXME("internal: error getting drive/path\n");
1247 /* find path that drive letter substitutes*/
1248 drive = (int)FILE_toupper(full_name.short_name[0]) -0x41;
1249 root= DRIVE_GetRoot(drive);
1252 FIXME("internal: error getting DOS Drive Root\n");
1255 if (!strcmp(root,"/"))
1257 /* we have just the last / and we need it. */
1258 p= full_name.long_name;
1262 p= full_name.long_name +strlen(root);
1264 /* append long name (= unix name) to drive */
1265 lstrcpynA(full_name.short_name+2,p,MAX_PATHNAME_LEN-3);
1266 /* append name to treat */
1267 namelen= strlen(full_name.short_name);
1270 p += +2; /* skip drive name when appending */
1271 if (namelen +2 + strlen(p) > MAX_PATHNAME_LEN)
1273 FIXME("internal error: buffer too small\n");
1276 full_name.short_name[namelen++] ='\\';
1277 full_name.short_name[namelen] = 0;
1278 lstrcpynA(full_name.short_name +namelen,p,MAX_PATHNAME_LEN-namelen);
1280 /* reverse all slashes */
1281 for (p=full_name.short_name;
1282 p < full_name.short_name+strlen(full_name.short_name);
1288 /* Use memmove, as areas overlap */
1290 while ((p = strstr(full_name.short_name,"\\..\\")))
1292 if (p > full_name.short_name+2)
1295 q = strrchr(full_name.short_name,'\\');
1296 memmove(q+1,p+4,strlen(p+4)+1);
1300 memmove(full_name.short_name+3,p+4,strlen(p+4)+1);
1303 if ((full_name.short_name[2]=='.')&&(full_name.short_name[3]=='.'))
1305 /* This case istn't treated yet : c:..\test */
1306 memmove(full_name.short_name+2,full_name.short_name+4,
1307 strlen(full_name.short_name+4)+1);
1310 while ((p = strstr(full_name.short_name,"\\.\\")))
1313 memmove(p+1,p+3,strlen(p+3)+1);
1315 if (!(DRIVE_GetFlags(drive) & DRIVE_CASE_PRESERVING))
1316 for (p = full_name.short_name; *p; p++) *p = FILE_toupper(*p);
1317 namelen=strlen(full_name.short_name);
1318 if (!strcmp(full_name.short_name+namelen-3,"\\.."))
1320 /* one more strange case: "c:\test\test1\.."
1322 *(full_name.short_name+namelen-3)=0;
1323 q = strrchr(full_name.short_name,'\\');
1326 if (full_name.short_name[namelen-1]=='.')
1327 full_name.short_name[(namelen--)-1] =0;
1329 if (full_name.short_name[namelen-1]=='\\')
1330 full_name.short_name[(namelen--)-1] =0;
1331 TRACE("got %s\n",full_name.short_name);
1333 /* If the lpBuffer buffer is too small, the return value is the
1334 size of the buffer, in characters, required to hold the path
1335 plus the terminating \0 (tested against win95osr2, bon 001118)
1337 ret = strlen(full_name.short_name);
1340 /* don't touch anything when the buffer is not large enough */
1341 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1347 MultiByteToWideChar( CP_ACP, 0, full_name.short_name, -1, (LPWSTR)result, len );
1349 lstrcpynA( result, full_name.short_name, len );
1352 TRACE("returning '%s'\n", full_name.short_name );
1357 /***********************************************************************
1358 * GetFullPathNameA (KERNEL32.@)
1360 * if the path closed with '\', *lastpart is 0
1362 DWORD WINAPI GetFullPathNameA( LPCSTR name, DWORD len, LPSTR buffer,
1365 DWORD ret = DOSFS_DoGetFullPathName( name, len, buffer, FALSE );
1366 if (ret && (ret<=len) && buffer && lastpart)
1368 LPSTR p = buffer + strlen(buffer);
1372 while ((p > buffer + 2) && (*p != '\\')) p--;
1375 else *lastpart = NULL;
1381 /***********************************************************************
1382 * GetFullPathNameW (KERNEL32.@)
1384 DWORD WINAPI GetFullPathNameW( LPCWSTR name, DWORD len, LPWSTR buffer,
1387 LPSTR nameA = HEAP_strdupWtoA( GetProcessHeap(), 0, name );
1388 DWORD ret = DOSFS_DoGetFullPathName( nameA, len, (LPSTR)buffer, TRUE );
1389 HeapFree( GetProcessHeap(), 0, nameA );
1390 if (ret && (ret<=len) && buffer && lastpart)
1392 LPWSTR p = buffer + strlenW(buffer);
1393 if (*p != (WCHAR)'\\')
1395 while ((p > buffer + 2) && (*p != (WCHAR)'\\')) p--;
1398 else *lastpart = NULL;
1404 /***********************************************************************
1405 * wine_get_unix_file_name (KERNEL32.@) Not a Windows API
1407 * Return the full Unix file name for a given path.
1409 BOOL WINAPI wine_get_unix_file_name( LPCSTR dos, LPSTR buffer, DWORD len )
1413 if ((ret = DOSFS_GetFullName( dos, FALSE, &path ))) lstrcpynA( buffer, path.long_name, len );
1418 /***********************************************************************
1421 static int DOSFS_FindNextEx( FIND_FIRST_INFO *info, WIN32_FIND_DATAA *entry )
1423 DWORD attr = info->attr | FA_UNUSED | FA_ARCHIVE | FA_RDONLY | FILE_ATTRIBUTE_SYMLINK;
1424 UINT flags = DRIVE_GetFlags( info->drive );
1425 char *p, buffer[MAX_PATHNAME_LEN];
1426 const char *drive_path;
1428 LPCSTR long_name, short_name;
1429 BY_HANDLE_FILE_INFORMATION fileinfo;
1432 if ((info->attr & ~(FA_UNUSED | FA_ARCHIVE | FA_RDONLY)) == FA_LABEL)
1434 if (info->cur_pos) return 0;
1435 entry->dwFileAttributes = FILE_ATTRIBUTE_LABEL;
1436 RtlSecondsSince1970ToTime( (time_t)0, &entry->ftCreationTime );
1437 RtlSecondsSince1970ToTime( (time_t)0, &entry->ftLastAccessTime );
1438 RtlSecondsSince1970ToTime( (time_t)0, &entry->ftLastWriteTime );
1439 entry->nFileSizeHigh = 0;
1440 entry->nFileSizeLow = 0;
1441 entry->dwReserved0 = 0;
1442 entry->dwReserved1 = 0;
1443 DOSFS_ToDosDTAFormat( DRIVE_GetLabel( info->drive ), entry->cFileName );
1444 strcpy( entry->cAlternateFileName, entry->cFileName );
1446 TRACE("returning %s (%s) as label\n",
1447 entry->cFileName, entry->cAlternateFileName);
1451 drive_path = info->path + strlen(DRIVE_GetRoot( info->drive ));
1452 while ((*drive_path == '/') || (*drive_path == '\\')) drive_path++;
1453 drive_root = !*drive_path;
1455 lstrcpynA( buffer, info->path, sizeof(buffer) - 1 );
1456 strcat( buffer, "/" );
1457 p = buffer + strlen(buffer);
1459 while (DOSFS_ReadDir( info->dir, &long_name, &short_name ))
1463 /* Don't return '.' and '..' in the root of the drive */
1464 if (drive_root && (long_name[0] == '.') &&
1465 (!long_name[1] || ((long_name[1] == '.') && !long_name[2])))
1468 /* Check the long mask */
1470 if (info->long_mask)
1472 if (!DOSFS_MatchLong( info->long_mask, long_name,
1473 flags & DRIVE_CASE_SENSITIVE )) continue;
1476 /* Check the short mask */
1478 if (info->short_mask)
1482 DOSFS_Hash( long_name, dos_name, TRUE,
1483 !(flags & DRIVE_CASE_SENSITIVE) );
1484 short_name = dos_name;
1486 if (!DOSFS_MatchShort( info->short_mask, short_name )) continue;
1489 /* Check the file attributes */
1491 lstrcpynA( p, long_name, sizeof(buffer) - (int)(p - buffer) );
1492 if (!FILE_Stat( buffer, &fileinfo ))
1494 WARN("can't stat %s\n", buffer);
1497 if ((fileinfo.dwFileAttributes & FILE_ATTRIBUTE_SYMLINK) &&
1498 (fileinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
1500 static int show_dir_symlinks = -1;
1501 if (show_dir_symlinks == -1)
1502 show_dir_symlinks = PROFILE_GetWineIniBool("wine", "ShowDirSymlinks", 0);
1503 if (!show_dir_symlinks) continue;
1506 if (fileinfo.dwFileAttributes & ~attr) continue;
1508 /* We now have a matching entry; fill the result and return */
1510 entry->dwFileAttributes = fileinfo.dwFileAttributes;
1511 entry->ftCreationTime = fileinfo.ftCreationTime;
1512 entry->ftLastAccessTime = fileinfo.ftLastAccessTime;
1513 entry->ftLastWriteTime = fileinfo.ftLastWriteTime;
1514 entry->nFileSizeHigh = fileinfo.nFileSizeHigh;
1515 entry->nFileSizeLow = fileinfo.nFileSizeLow;
1518 DOSFS_ToDosDTAFormat( short_name, entry->cAlternateFileName );
1520 DOSFS_Hash( long_name, entry->cAlternateFileName, FALSE,
1521 !(flags & DRIVE_CASE_SENSITIVE) );
1523 lstrcpynA( entry->cFileName, long_name, sizeof(entry->cFileName) );
1524 if (!(flags & DRIVE_CASE_PRESERVING)) _strlwr( entry->cFileName );
1525 TRACE("returning %s (%s) %02lx %ld\n",
1526 entry->cFileName, entry->cAlternateFileName,
1527 entry->dwFileAttributes, entry->nFileSizeLow );
1530 return 0; /* End of directory */
1533 /***********************************************************************
1536 * Find the next matching file. Return the number of entries read to find
1537 * the matching one, or 0 if no more entries.
1538 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
1539 * file name mask. Either or both can be NULL.
1541 * NOTE: This is supposed to be only called by the int21 emulation
1542 * routines. Thus, we should own the Win16Mutex anyway.
1543 * Nevertheless, we explicitly enter it to ensure the static
1544 * directory cache is protected.
1546 int DOSFS_FindNext( const char *path, const char *short_mask,
1547 const char *long_mask, int drive, BYTE attr,
1548 int skip, WIN32_FIND_DATAA *entry )
1550 static FIND_FIRST_INFO info;
1551 LPCSTR short_name, long_name;
1556 /* Check the cached directory */
1557 if (!(info.dir && info.path == path && info.short_mask == short_mask
1558 && info.long_mask == long_mask && info.drive == drive
1559 && info.attr == attr && info.cur_pos <= skip))
1561 /* Not in the cache, open it anew */
1562 if (info.dir) DOSFS_CloseDir( info.dir );
1564 info.path = (LPSTR)path;
1565 info.long_mask = (LPSTR)long_mask;
1566 info.short_mask = (LPSTR)short_mask;
1570 info.dir = DOSFS_OpenDir( info.path );
1573 /* Skip to desired position */
1574 while (info.cur_pos < skip)
1575 if (info.dir && DOSFS_ReadDir( info.dir, &long_name, &short_name ))
1580 if (info.dir && info.cur_pos == skip && DOSFS_FindNextEx( &info, entry ))
1581 count = info.cur_pos - skip;
1587 if (info.dir) DOSFS_CloseDir( info.dir );
1588 memset( &info, '\0', sizeof(info) );
1596 /*************************************************************************
1597 * FindFirstFileExA (KERNEL32.@)
1599 HANDLE WINAPI FindFirstFileExA(
1601 FINDEX_INFO_LEVELS fInfoLevelId,
1602 LPVOID lpFindFileData,
1603 FINDEX_SEARCH_OPS fSearchOp,
1604 LPVOID lpSearchFilter,
1605 DWORD dwAdditionalFlags)
1607 DOS_FULL_NAME full_name;
1609 FIND_FIRST_INFO *info;
1611 if ((fSearchOp != FindExSearchNameMatch) || (dwAdditionalFlags != 0))
1613 FIXME("options not implemented 0x%08x 0x%08lx\n", fSearchOp, dwAdditionalFlags );
1614 return INVALID_HANDLE_VALUE;
1617 switch(fInfoLevelId)
1619 case FindExInfoStandard:
1621 WIN32_FIND_DATAA * data = (WIN32_FIND_DATAA *) lpFindFileData;
1622 data->dwReserved0 = data->dwReserved1 = 0x0;
1623 if (!lpFileName) return 0;
1624 if (!DOSFS_GetFullName( lpFileName, FALSE, &full_name )) break;
1625 if (!(handle = GlobalAlloc(GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO)))) break;
1626 info = (FIND_FIRST_INFO *)GlobalLock( handle );
1627 info->path = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
1628 strcpy( info->path, full_name.long_name );
1629 info->long_mask = strrchr( info->path, '/' );
1630 *(info->long_mask++) = '\0';
1631 info->short_mask = NULL;
1633 if (lpFileName[0] && (lpFileName[1] == ':'))
1634 info->drive = FILE_toupper(*lpFileName) - 'A';
1635 else info->drive = DRIVE_GetCurrentDrive();
1638 info->dir = DOSFS_OpenDir( info->path );
1640 GlobalUnlock( handle );
1641 if (!FindNextFileA( handle, data ))
1643 FindClose( handle );
1644 SetLastError( ERROR_NO_MORE_FILES );
1651 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
1653 return INVALID_HANDLE_VALUE;
1656 /*************************************************************************
1657 * FindFirstFileA (KERNEL32.@)
1659 HANDLE WINAPI FindFirstFileA(
1661 WIN32_FIND_DATAA *lpFindData )
1663 return FindFirstFileExA(lpFileName, FindExInfoStandard, lpFindData,
1664 FindExSearchNameMatch, NULL, 0);
1667 /*************************************************************************
1668 * FindFirstFileExW (KERNEL32.@)
1670 HANDLE WINAPI FindFirstFileExW(
1672 FINDEX_INFO_LEVELS fInfoLevelId,
1673 LPVOID lpFindFileData,
1674 FINDEX_SEARCH_OPS fSearchOp,
1675 LPVOID lpSearchFilter,
1676 DWORD dwAdditionalFlags)
1679 WIN32_FIND_DATAA dataA;
1680 LPVOID _lpFindFileData;
1683 switch(fInfoLevelId)
1685 case FindExInfoStandard:
1687 _lpFindFileData = &dataA;
1691 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
1692 return INVALID_HANDLE_VALUE;
1695 pathA = HEAP_strdupWtoA( GetProcessHeap(), 0, lpFileName );
1696 handle = FindFirstFileExA(pathA, fInfoLevelId, _lpFindFileData, fSearchOp, lpSearchFilter, dwAdditionalFlags);
1697 HeapFree( GetProcessHeap(), 0, pathA );
1698 if (handle == INVALID_HANDLE_VALUE) return handle;
1700 switch(fInfoLevelId)
1702 case FindExInfoStandard:
1704 WIN32_FIND_DATAW *dataW = (WIN32_FIND_DATAW*) lpFindFileData;
1705 dataW->dwFileAttributes = dataA.dwFileAttributes;
1706 dataW->ftCreationTime = dataA.ftCreationTime;
1707 dataW->ftLastAccessTime = dataA.ftLastAccessTime;
1708 dataW->ftLastWriteTime = dataA.ftLastWriteTime;
1709 dataW->nFileSizeHigh = dataA.nFileSizeHigh;
1710 dataW->nFileSizeLow = dataA.nFileSizeLow;
1711 MultiByteToWideChar( CP_ACP, 0, dataA.cFileName, -1,
1712 dataW->cFileName, sizeof(dataW->cFileName)/sizeof(WCHAR) );
1713 MultiByteToWideChar( CP_ACP, 0, dataA.cAlternateFileName, -1,
1714 dataW->cAlternateFileName,
1715 sizeof(dataW->cAlternateFileName)/sizeof(WCHAR) );
1719 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
1720 return INVALID_HANDLE_VALUE;
1725 /*************************************************************************
1726 * FindFirstFileW (KERNEL32.@)
1728 HANDLE WINAPI FindFirstFileW( LPCWSTR lpFileName, WIN32_FIND_DATAW *lpFindData )
1730 return FindFirstFileExW(lpFileName, FindExInfoStandard, lpFindData,
1731 FindExSearchNameMatch, NULL, 0);
1734 /*************************************************************************
1735 * FindNextFileA (KERNEL32.@)
1737 BOOL WINAPI FindNextFileA( HANDLE handle, WIN32_FIND_DATAA *data )
1739 FIND_FIRST_INFO *info;
1741 if ((handle == INVALID_HANDLE_VALUE) ||
1742 !(info = (FIND_FIRST_INFO *)GlobalLock( handle )))
1744 SetLastError( ERROR_INVALID_HANDLE );
1747 GlobalUnlock( handle );
1748 if (!info->path || !info->dir)
1750 SetLastError( ERROR_NO_MORE_FILES );
1753 if (!DOSFS_FindNextEx( info, data ))
1755 DOSFS_CloseDir( info->dir ); info->dir = NULL;
1756 HeapFree( GetProcessHeap(), 0, info->path );
1757 info->path = info->long_mask = NULL;
1758 SetLastError( ERROR_NO_MORE_FILES );
1765 /*************************************************************************
1766 * FindNextFileW (KERNEL32.@)
1768 BOOL WINAPI FindNextFileW( HANDLE handle, WIN32_FIND_DATAW *data )
1770 WIN32_FIND_DATAA dataA;
1771 if (!FindNextFileA( handle, &dataA )) return FALSE;
1772 data->dwFileAttributes = dataA.dwFileAttributes;
1773 data->ftCreationTime = dataA.ftCreationTime;
1774 data->ftLastAccessTime = dataA.ftLastAccessTime;
1775 data->ftLastWriteTime = dataA.ftLastWriteTime;
1776 data->nFileSizeHigh = dataA.nFileSizeHigh;
1777 data->nFileSizeLow = dataA.nFileSizeLow;
1778 MultiByteToWideChar( CP_ACP, 0, dataA.cFileName, -1,
1779 data->cFileName, sizeof(data->cFileName)/sizeof(WCHAR) );
1780 MultiByteToWideChar( CP_ACP, 0, dataA.cAlternateFileName, -1,
1781 data->cAlternateFileName,
1782 sizeof(data->cAlternateFileName)/sizeof(WCHAR) );
1786 /*************************************************************************
1787 * FindClose (KERNEL32.@)
1789 BOOL WINAPI FindClose( HANDLE handle )
1791 FIND_FIRST_INFO *info;
1793 if ((handle == INVALID_HANDLE_VALUE) ||
1794 !(info = (FIND_FIRST_INFO *)GlobalLock( handle )))
1796 SetLastError( ERROR_INVALID_HANDLE );
1801 if (info->dir) DOSFS_CloseDir( info->dir );
1802 if (info->path) HeapFree( GetProcessHeap(), 0, info->path );
1804 __EXCEPT(page_fault)
1806 WARN("Illegal handle %x\n", handle);
1807 SetLastError( ERROR_INVALID_HANDLE );
1811 GlobalUnlock( handle );
1812 GlobalFree( handle );
1816 /***********************************************************************
1817 * DOSFS_UnixTimeToFileTime
1819 * Convert a Unix time to FILETIME format.
1820 * The FILETIME structure is a 64-bit value representing the number of
1821 * 100-nanosecond intervals since January 1, 1601, 0:00.
1822 * 'remainder' is the nonnegative number of 100-ns intervals
1823 * corresponding to the time fraction smaller than 1 second that
1824 * couldn't be stored in the time_t value.
1826 void DOSFS_UnixTimeToFileTime( time_t unix_time, FILETIME *filetime,
1832 The time difference between 1 January 1601, 00:00:00 and
1833 1 January 1970, 00:00:00 is 369 years, plus the leap years
1834 from 1604 to 1968, excluding 1700, 1800, 1900.
1835 This makes (1968 - 1600) / 4 - 3 = 89 leap days, and a total
1838 Any day in that period had 24 * 60 * 60 = 86400 seconds.
1840 The time difference is 134774 * 86400 * 10000000, which can be written
1842 27111902 * 2^32 + 3577643008
1843 413 * 2^48 + 45534 * 2^32 + 54590 * 2^16 + 32768
1845 If you find that these constants are buggy, please change them in all
1846 instances in both conversion functions.
1849 There are two versions, one of them uses long long variables and
1850 is presumably faster but not ISO C. The other one uses standard C
1851 data types and operations but relies on the assumption that negative
1852 numbers are stored as 2's complement (-1 is 0xffff....). If this
1853 assumption is violated, dates before 1970 will not convert correctly.
1854 This should however work on any reasonable architecture where WINE
1859 Take care not to remove the casts. I have tested these functions
1860 (in both versions) for a lot of numbers. I would be interested in
1861 results on other compilers than GCC.
1863 The operations have been designed to account for the possibility
1864 of 64-bit time_t in future UNICES. Even the versions without
1865 internal long long numbers will work if time_t only is 64 bit.
1866 A 32-bit shift, which was necessary for that operation, turned out
1867 not to work correctly in GCC, besides giving the warning. So I
1868 used a double 16-bit shift instead. Numbers are in the ISO version
1869 represented by three limbs, the most significant with 32 bit, the
1870 other two with 16 bit each.
1872 As the modulo-operator % is not well-defined for negative numbers,
1873 negative divisors have been avoided in DOSFS_FileTimeToUnixTime.
1875 There might be quicker ways to do this in C. Certainly so in
1878 Claus Fischer, fischer@iue.tuwien.ac.at
1881 #if SIZEOF_LONG_LONG >= 8
1882 # define USE_LONG_LONG 1
1884 # define USE_LONG_LONG 0
1887 #if USE_LONG_LONG /* gcc supports long long type */
1889 long long int t = unix_time;
1891 t += 116444736000000000LL;
1893 filetime->dwLowDateTime = (UINT)t;
1894 filetime->dwHighDateTime = (UINT)(t >> 32);
1896 #else /* ISO version */
1898 UINT a0; /* 16 bit, low bits */
1899 UINT a1; /* 16 bit, medium bits */
1900 UINT a2; /* 32 bit, high bits */
1902 /* Copy the unix time to a2/a1/a0 */
1903 a0 = unix_time & 0xffff;
1904 a1 = (unix_time >> 16) & 0xffff;
1905 /* This is obsolete if unix_time is only 32 bits, but it does not hurt.
1906 Do not replace this by >> 32, it gives a compiler warning and it does
1908 a2 = (unix_time >= 0 ? (unix_time >> 16) >> 16 :
1909 ~((~unix_time >> 16) >> 16));
1911 /* Multiply a by 10000000 (a = a2/a1/a0)
1912 Split the factor into 10000 * 1000 which are both less than 0xffff. */
1914 a1 = a1 * 10000 + (a0 >> 16);
1915 a2 = a2 * 10000 + (a1 >> 16);
1920 a1 = a1 * 1000 + (a0 >> 16);
1921 a2 = a2 * 1000 + (a1 >> 16);
1925 /* Add the time difference and the remainder */
1926 a0 += 32768 + (remainder & 0xffff);
1927 a1 += 54590 + (remainder >> 16 ) + (a0 >> 16);
1928 a2 += 27111902 + (a1 >> 16);
1933 filetime->dwLowDateTime = (a1 << 16) + a0;
1934 filetime->dwHighDateTime = a2;
1939 /***********************************************************************
1940 * DOSFS_FileTimeToUnixTime
1942 * Convert a FILETIME format to Unix time.
1943 * If not NULL, 'remainder' contains the fractional part of the filetime,
1944 * in the range of [0..9999999] (even if time_t is negative).
1946 time_t DOSFS_FileTimeToUnixTime( const FILETIME *filetime, DWORD *remainder )
1948 /* Read the comment in the function DOSFS_UnixTimeToFileTime. */
1951 long long int t = filetime->dwHighDateTime;
1953 t += (UINT)filetime->dwLowDateTime;
1954 t -= 116444736000000000LL;
1957 if (remainder) *remainder = 9999999 - (-t - 1) % 10000000;
1958 return -1 - ((-t - 1) / 10000000);
1962 if (remainder) *remainder = t % 10000000;
1963 return t / 10000000;
1966 #else /* ISO version */
1968 UINT a0; /* 16 bit, low bits */
1969 UINT a1; /* 16 bit, medium bits */
1970 UINT a2; /* 32 bit, high bits */
1971 UINT r; /* remainder of division */
1972 unsigned int carry; /* carry bit for subtraction */
1973 int negative; /* whether a represents a negative value */
1975 /* Copy the time values to a2/a1/a0 */
1976 a2 = (UINT)filetime->dwHighDateTime;
1977 a1 = ((UINT)filetime->dwLowDateTime ) >> 16;
1978 a0 = ((UINT)filetime->dwLowDateTime ) & 0xffff;
1980 /* Subtract the time difference */
1981 if (a0 >= 32768 ) a0 -= 32768 , carry = 0;
1982 else a0 += (1 << 16) - 32768 , carry = 1;
1984 if (a1 >= 54590 + carry) a1 -= 54590 + carry, carry = 0;
1985 else a1 += (1 << 16) - 54590 - carry, carry = 1;
1987 a2 -= 27111902 + carry;
1989 /* If a is negative, replace a by (-1-a) */
1990 negative = (a2 >= ((UINT)1) << 31);
1993 /* Set a to -a - 1 (a is a2/a1/a0) */
1999 /* Divide a by 10000000 (a = a2/a1/a0), put the rest into r.
2000 Split the divisor into 10000 * 1000 which are both less than 0xffff. */
2001 a1 += (a2 % 10000) << 16;
2003 a0 += (a1 % 10000) << 16;
2008 a1 += (a2 % 1000) << 16;
2010 a0 += (a1 % 1000) << 16;
2012 r += (a0 % 1000) * 10000;
2015 /* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
2018 /* Set a to -a - 1 (a is a2/a1/a0) */
2026 if (remainder) *remainder = r;
2028 /* Do not replace this by << 32, it gives a compiler warning and it does
2030 return ((((time_t)a2) << 16) << 16) + (a1 << 16) + a0;
2035 /***********************************************************************
2036 * MulDiv (KERNEL32.@)
2038 * Result of multiplication and division
2039 * -1: Overflow occurred or Divisor was 0
2046 #if SIZEOF_LONG_LONG >= 8
2049 if (!nDivisor) return -1;
2051 /* We want to deal with a positive divisor to simplify the logic. */
2054 nMultiplicand = - nMultiplicand;
2055 nDivisor = -nDivisor;
2058 /* If the result is positive, we "add" to round. else, we subtract to round. */
2059 if ( ( (nMultiplicand < 0) && (nMultiplier < 0) ) ||
2060 ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
2061 ret = (((long long)nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
2063 ret = (((long long)nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
2065 if ((ret > 2147483647) || (ret < -2147483647)) return -1;
2068 if (!nDivisor) return -1;
2070 /* We want to deal with a positive divisor to simplify the logic. */
2073 nMultiplicand = - nMultiplicand;
2074 nDivisor = -nDivisor;
2077 /* If the result is positive, we "add" to round. else, we subtract to round. */
2078 if ( ( (nMultiplicand < 0) && (nMultiplier < 0) ) ||
2079 ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
2080 return ((nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
2082 return ((nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
2088 /***********************************************************************
2089 * DosDateTimeToFileTime (KERNEL32.@)
2091 BOOL WINAPI DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft)
2095 newtm.tm_sec = (fattime & 0x1f) * 2;
2096 newtm.tm_min = (fattime >> 5) & 0x3f;
2097 newtm.tm_hour = (fattime >> 11);
2098 newtm.tm_mday = (fatdate & 0x1f);
2099 newtm.tm_mon = ((fatdate >> 5) & 0x0f) - 1;
2100 newtm.tm_year = (fatdate >> 9) + 80;
2101 RtlSecondsSince1970ToTime( mktime( &newtm ), ft );
2106 /***********************************************************************
2107 * FileTimeToDosDateTime (KERNEL32.@)
2109 BOOL WINAPI FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate,
2112 time_t unixtime = DOSFS_FileTimeToUnixTime( ft, NULL );
2113 struct tm *tm = localtime( &unixtime );
2115 *fattime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
2117 *fatdate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
2123 /***********************************************************************
2124 * LocalFileTimeToFileTime (KERNEL32.@)
2126 BOOL WINAPI LocalFileTimeToFileTime( const FILETIME *localft,
2132 /* convert from local to UTC. Perhaps not correct. FIXME */
2133 time_t unixtime = DOSFS_FileTimeToUnixTime( localft, &remainder );
2134 xtm = gmtime( &unixtime );
2135 DOSFS_UnixTimeToFileTime( mktime(xtm), utcft, remainder );
2140 /***********************************************************************
2141 * FileTimeToLocalFileTime (KERNEL32.@)
2143 BOOL WINAPI FileTimeToLocalFileTime( const FILETIME *utcft,
2144 LPFILETIME localft )
2147 /* convert from UTC to local. Perhaps not correct. FIXME */
2148 time_t unixtime = DOSFS_FileTimeToUnixTime( utcft, &remainder );
2150 struct tm *xtm = localtime( &unixtime );
2153 localtime = timegm(xtm);
2154 DOSFS_UnixTimeToFileTime( localtime, localft, remainder );
2157 struct tm *xtm,*gtm;
2160 xtm = localtime( &unixtime );
2161 gtm = gmtime( &unixtime );
2162 time1 = mktime(xtm);
2163 time2 = mktime(gtm);
2164 DOSFS_UnixTimeToFileTime( 2*time1-time2, localft, remainder );
2170 /***********************************************************************
2171 * FileTimeToSystemTime (KERNEL32.@)
2173 BOOL WINAPI FileTimeToSystemTime( const FILETIME *ft, LPSYSTEMTIME syst )
2177 time_t xtime = DOSFS_FileTimeToUnixTime( ft, &remainder );
2178 xtm = gmtime(&xtime);
2179 syst->wYear = xtm->tm_year+1900;
2180 syst->wMonth = xtm->tm_mon + 1;
2181 syst->wDayOfWeek = xtm->tm_wday;
2182 syst->wDay = xtm->tm_mday;
2183 syst->wHour = xtm->tm_hour;
2184 syst->wMinute = xtm->tm_min;
2185 syst->wSecond = xtm->tm_sec;
2186 syst->wMilliseconds = remainder / 10000;
2190 /***********************************************************************
2191 * QueryDosDeviceA (KERNEL32.@)
2193 * returns array of strings terminated by \0, terminated by \0
2195 DWORD WINAPI QueryDosDeviceA(LPCSTR devname,LPSTR target,DWORD bufsize)
2200 TRACE("(%s,...)\n", devname ? devname : "<null>");
2202 /* return known MSDOS devices */
2203 static const char devices[24] = "CON\0COM1\0COM2\0LPT1\0NUL\0\0";
2204 memcpy( target, devices, min(bufsize,sizeof(devices)) );
2205 return min(bufsize,sizeof(devices));
2207 /* In theory all that are possible and have been defined.
2208 * Now just those below, since mirc uses it to check for special files.
2210 * (It is more complex, and supports netmounted stuff, and \\.\ stuff,
2211 * but currently we just ignore that.)
2213 #define CHECK(x) (strstr(devname,#x)==devname)
2214 if (CHECK(con) || CHECK(com) || CHECK(lpt) || CHECK(nul)) {
2215 strcpy(buffer,"\\DEV\\");
2216 strcat(buffer,devname);
2217 if ((s=strchr(buffer,':'))) *s='\0';
2218 lstrcpynA(target,buffer,bufsize);
2219 return strlen(buffer)+1;
2221 if (strchr(devname,':') || devname[0]=='\\') {
2222 /* This might be a DOS device we do not handle yet ... */
2223 FIXME("(%s) not detected as DOS device!\n",devname);
2225 SetLastError(ERROR_DEV_NOT_EXIST);
2232 /***********************************************************************
2233 * QueryDosDeviceW (KERNEL32.@)
2235 * returns array of strings terminated by \0, terminated by \0
2237 DWORD WINAPI QueryDosDeviceW(LPCWSTR devname,LPWSTR target,DWORD bufsize)
2239 LPSTR devnameA = devname?HEAP_strdupWtoA(GetProcessHeap(),0,devname):NULL;
2240 LPSTR targetA = (LPSTR)HeapAlloc(GetProcessHeap(),0,bufsize);
2241 DWORD ret = QueryDosDeviceA(devnameA,targetA,bufsize);
2243 ret = MultiByteToWideChar( CP_ACP, 0, targetA, ret, target, bufsize );
2244 if (devnameA) HeapFree(GetProcessHeap(),0,devnameA);
2245 if (targetA) HeapFree(GetProcessHeap(),0,targetA);
2250 /***********************************************************************
2251 * SystemTimeToFileTime (KERNEL32.@)
2253 BOOL WINAPI SystemTimeToFileTime( const SYSTEMTIME *syst, LPFILETIME ft )
2259 struct tm xtm,*local_tm,*utc_tm;
2260 time_t localtim,utctime;
2263 xtm.tm_year = syst->wYear-1900;
2264 xtm.tm_mon = syst->wMonth - 1;
2265 xtm.tm_wday = syst->wDayOfWeek;
2266 xtm.tm_mday = syst->wDay;
2267 xtm.tm_hour = syst->wHour;
2268 xtm.tm_min = syst->wMinute;
2269 xtm.tm_sec = syst->wSecond; /* this is UTC */
2272 utctime = timegm(&xtm);
2273 DOSFS_UnixTimeToFileTime( utctime, ft,
2274 syst->wMilliseconds * 10000 );
2276 localtim = mktime(&xtm); /* now we've got local time */
2277 local_tm = localtime(&localtim);
2278 utc_tm = gmtime(&localtim);
2279 utctime = mktime(utc_tm);
2280 DOSFS_UnixTimeToFileTime( 2*localtim -utctime, ft,
2281 syst->wMilliseconds * 10000 );
2286 /***********************************************************************
2287 * DefineDosDeviceA (KERNEL32.@)
2289 BOOL WINAPI DefineDosDeviceA(DWORD flags,LPCSTR devname,LPCSTR targetpath) {
2290 FIXME("(0x%08lx,%s,%s),stub!\n",flags,devname,targetpath);
2291 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2296 --- 16 bit functions ---
2299 /*************************************************************************
2300 * FindFirstFile (KERNEL.413)
2302 HANDLE16 WINAPI FindFirstFile16( LPCSTR path, WIN32_FIND_DATAA *data )
2304 DOS_FULL_NAME full_name;
2306 FIND_FIRST_INFO *info;
2308 data->dwReserved0 = data->dwReserved1 = 0x0;
2309 if (!path) return 0;
2310 if (!DOSFS_GetFullName( path, FALSE, &full_name ))
2311 return INVALID_HANDLE_VALUE16;
2312 if (!(handle = GlobalAlloc16( GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO) )))
2313 return INVALID_HANDLE_VALUE16;
2314 info = (FIND_FIRST_INFO *)GlobalLock16( handle );
2315 info->path = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
2316 strcpy( info->path, full_name.long_name );
2317 info->long_mask = strrchr( info->path, '/' );
2318 if (info->long_mask )
2319 *(info->long_mask++) = '\0';
2320 info->short_mask = NULL;
2322 if (path[0] && (path[1] == ':')) info->drive = FILE_toupper(*path) - 'A';
2323 else info->drive = DRIVE_GetCurrentDrive();
2326 info->dir = DOSFS_OpenDir( info->path );
2328 GlobalUnlock16( handle );
2329 if (!FindNextFile16( handle, data ))
2331 FindClose16( handle );
2332 SetLastError( ERROR_NO_MORE_FILES );
2333 return INVALID_HANDLE_VALUE16;
2338 /*************************************************************************
2339 * FindNextFile (KERNEL.414)
2341 BOOL16 WINAPI FindNextFile16( HANDLE16 handle, WIN32_FIND_DATAA *data )
2343 FIND_FIRST_INFO *info;
2345 if ((handle == INVALID_HANDLE_VALUE16) ||
2346 !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
2348 SetLastError( ERROR_INVALID_HANDLE );
2351 GlobalUnlock16( handle );
2352 if (!info->path || !info->dir)
2354 SetLastError( ERROR_NO_MORE_FILES );
2357 if (!DOSFS_FindNextEx( info, data ))
2359 DOSFS_CloseDir( info->dir ); info->dir = NULL;
2360 HeapFree( GetProcessHeap(), 0, info->path );
2361 info->path = info->long_mask = NULL;
2362 SetLastError( ERROR_NO_MORE_FILES );
2368 /*************************************************************************
2369 * FindClose (KERNEL.415)
2371 BOOL16 WINAPI FindClose16( HANDLE16 handle )
2373 FIND_FIRST_INFO *info;
2375 if ((handle == INVALID_HANDLE_VALUE16) ||
2376 !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
2378 SetLastError( ERROR_INVALID_HANDLE );
2381 if (info->dir) DOSFS_CloseDir( info->dir );
2382 if (info->path) HeapFree( GetProcessHeap(), 0, info->path );
2383 GlobalUnlock16( handle );
2384 GlobalFree16( handle );