2 * DOS file system functions
4 * Copyright 1993 Erik Bos
5 * Copyright 1996 Alexandre Julliard
17 #include <sys/ioctl.h>
20 #if defined(__svr4__) || defined(_SCO_DS)
21 #include <sys/statfs.h>
33 /* Define the VFAT ioctl to get both short and long file names */
34 /* FIXME: is it possible to get this to work on other systems? */
36 #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, long)
37 /* We want the real kernel dirent structure, not the libc one */
42 unsigned short d_reclen;
47 #undef VFAT_IOCTL_READDIR_BOTH /* just in case... */
50 /* Chars we don't want to see in DOS file names */
51 #define INVALID_DOS_CHARS "*?<>|\"+=,;[] \345"
53 static const char *DOSFS_Devices[][2] =
57 { "NUL", "/dev/null" },
70 #define GET_DRIVE(path) \
71 (((path)[1] == ':') ? toupper((path)[0]) - 'A' : DOSFS_CurDrive)
73 /* DOS extended error status */
74 WORD DOS_ExtendedError;
79 /* Info structure for FindFirstFile handle */
89 /* Directory info for DOSFS_ReadDir */
93 #ifdef VFAT_IOCTL_READDIR_BOTH
96 KERNEL_DIRENT dirent[2];
101 /***********************************************************************
104 * Return 1 if Unix file 'name' is also a valid MS-DOS name
105 * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
106 * File name can be terminated by '\0', '\\' or '/'.
108 static int DOSFS_ValidDOSName( const char *name, int ignore_case )
110 static const char invalid_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS;
111 const char *p = name;
112 const char *invalid = ignore_case ? (invalid_chars + 26) : invalid_chars;
117 /* Check for "." and ".." */
120 /* All other names beginning with '.' are invalid */
121 return (IS_END_OF_NAME(*p));
123 while (!IS_END_OF_NAME(*p))
125 if (strchr( invalid, *p )) return 0; /* Invalid char */
126 if (*p == '.') break; /* Start of the extension */
127 if (++len > 8) return 0; /* Name too long */
130 if (*p != '.') return 1; /* End of name */
132 if (IS_END_OF_NAME(*p)) return 0; /* Empty extension not allowed */
134 while (!IS_END_OF_NAME(*p))
136 if (strchr( invalid, *p )) return 0; /* Invalid char */
137 if (*p == '.') return 0; /* Second extension not allowed */
138 if (++len > 3) return 0; /* Extension too long */
145 /***********************************************************************
146 * DOSFS_ToDosFCBFormat
148 * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
149 * expanding wild cards and converting to upper-case in the process.
150 * File name can be terminated by '\0', '\\' or '/'.
151 * Return FALSE if the name is not a valid DOS name.
152 * 'buffer' must be at least 12 characters long.
154 BOOL32 DOSFS_ToDosFCBFormat( LPCSTR name, LPSTR buffer )
156 static const char invalid_chars[] = INVALID_DOS_CHARS;
157 const char *p = name;
160 /* Check for "." and ".." */
164 strcpy( buffer, ". " );
170 return (!*p || (*p == '/') || (*p == '\\'));
173 for (i = 0; i < 8; i++)
190 if (strchr( invalid_chars, *p )) return FALSE;
191 buffer[i] = toupper(*p);
199 /* Skip all chars after wildcard up to first dot */
200 while (*p && (*p != '/') && (*p != '\\') && (*p != '.')) p++;
204 /* Check if name too long */
205 if (*p && (*p != '/') && (*p != '\\') && (*p != '.')) return FALSE;
207 if (*p == '.') p++; /* Skip dot */
209 for (i = 8; i < 11; i++)
219 return FALSE; /* Second extension not allowed */
227 if (strchr( invalid_chars, *p )) return FALSE;
228 buffer[i] = toupper(*p);
238 /***********************************************************************
239 * DOSFS_ToDosDTAFormat
241 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
242 * converting to upper-case in the process.
243 * File name can be terminated by '\0', '\\' or '/'.
244 * 'buffer' must be at least 13 characters long.
246 static void DOSFS_ToDosDTAFormat( LPCSTR name, LPSTR buffer )
250 memcpy( buffer, name, 8 );
251 for (p = buffer + 8; (p > buffer) && (p[-1] == ' '); p--);
253 memcpy( p, name + 8, 3 );
254 for (p += 3; p[-1] == ' '; p--);
255 if (p[-1] == '.') p--;
260 /***********************************************************************
263 * Check a DOS file name against a mask (both in FCB format).
265 static int DOSFS_MatchShort( const char *mask, const char *name )
268 for (i = 11; i > 0; i--, mask++, name++)
269 if ((*mask != '?') && (*mask != *name)) return 0;
274 /***********************************************************************
277 * Check a long file name against a mask.
279 static int DOSFS_MatchLong( const char *mask, const char *name,
282 if (!strcmp( mask, "*.*" )) return 1;
283 while (*name && *mask)
288 while (*mask == '*') mask++; /* Skip consecutive '*' */
289 if (!*mask) return 1;
290 if (case_sensitive) while (*name && (*name != *mask)) name++;
291 else while (*name && (toupper(*name) != toupper(*mask))) name++;
292 if (!*name) return 0;
294 else if (*mask != '?')
298 if (*mask != *name) return 0;
300 else if (toupper(*mask) != toupper(*name)) return 0;
305 return (!*name && !*mask);
309 /***********************************************************************
312 static DOS_DIR *DOSFS_OpenDir( LPCSTR path )
314 DOS_DIR *dir = HeapAlloc( SystemHeap, 0, sizeof(*dir) );
317 DOS_ERROR( ER_OutOfMemory, EC_OutOfResource, SA_Abort, EL_Memory );
321 #ifdef VFAT_IOCTL_READDIR_BOTH
323 /* Check if the VFAT ioctl is supported on this directory */
325 if ((dir->fd = open( path, O_RDONLY )) != -1)
327 if (ioctl( dir->fd, VFAT_IOCTL_READDIR_BOTH, (long)dir->dirent ) == -1)
334 /* Set the file pointer back at the start of the directory */
335 lseek( dir->fd, 0, SEEK_SET );
340 #endif /* VFAT_IOCTL_READDIR_BOTH */
342 /* Now use the standard opendir/readdir interface */
344 if (!(dir->dir = opendir( path )))
346 HeapFree( SystemHeap, 0, dir );
353 /***********************************************************************
356 static void DOSFS_CloseDir( DOS_DIR *dir )
358 #ifdef VFAT_IOCTL_READDIR_BOTH
359 if (dir->fd != -1) close( dir->fd );
360 #endif /* VFAT_IOCTL_READDIR_BOTH */
361 if (dir->dir) closedir( dir->dir );
362 HeapFree( SystemHeap, 0, dir );
366 /***********************************************************************
369 static BOOL32 DOSFS_ReadDir( DOS_DIR *dir, LPCSTR *long_name,
372 struct dirent *dirent;
374 #ifdef VFAT_IOCTL_READDIR_BOTH
377 if (ioctl( dir->fd, VFAT_IOCTL_READDIR_BOTH, (long)dir->dirent ) != -1) {
378 if (!dir->dirent[0].d_reclen) return FALSE;
379 if (!DOSFS_ToDosFCBFormat( dir->dirent[0].d_name, dir->short_name ))
380 dir->short_name[0] = '\0';
381 *short_name = dir->short_name;
382 if (dir->dirent[1].d_name[0]) *long_name = dir->dirent[1].d_name;
383 else *long_name = dir->dirent[0].d_name;
387 #endif /* VFAT_IOCTL_READDIR_BOTH */
389 if (!(dirent = readdir( dir->dir ))) return FALSE;
390 *long_name = dirent->d_name;
396 /***********************************************************************
399 * Transform a Unix file name into a hashed DOS name. If the name is a valid
400 * DOS name, it is converted to upper-case; otherwise it is replaced by a
401 * hashed version that fits in 8.3 format.
402 * File name can be terminated by '\0', '\\' or '/'.
403 * 'buffer' must be at least 13 characters long.
405 static void DOSFS_Hash( LPCSTR name, LPSTR buffer, BOOL32 dir_format,
408 static const char invalid_chars[] = INVALID_DOS_CHARS "~.";
409 static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
416 if (dir_format) strcpy( buffer, " " );
418 if (DOSFS_ValidDOSName( name, ignore_case ))
420 /* Check for '.' and '..' */
424 if (!dir_format) buffer[1] = buffer[2] = '\0';
425 if (name[1] == '.') buffer[1] = '.';
429 /* Simply copy the name, converting to uppercase */
431 for (dst = buffer; !IS_END_OF_NAME(*name) && (*name != '.'); name++)
432 *dst++ = toupper(*name);
435 if (dir_format) dst = buffer + 8;
437 for (name++; !IS_END_OF_NAME(*name); name++)
438 *dst++ = toupper(*name);
440 if (!dir_format) *dst = '\0';
444 /* Compute the hash code of the file name */
445 /* If you know something about hash functions, feel free to */
446 /* insert a better algorithm here... */
449 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
450 hash = (hash<<3) ^ (hash>>5) ^ tolower(*p) ^ (tolower(p[1]) << 8);
451 hash = (hash<<3) ^ (hash>>5) ^ tolower(*p); /* Last character*/
455 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
456 hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
457 hash = (hash << 3) ^ (hash >> 5) ^ *p; /* Last character */
460 /* Find last dot for start of the extension */
461 for (p = name+1, ext = NULL; !IS_END_OF_NAME(*p); p++)
462 if (*p == '.') ext = p;
463 if (ext && IS_END_OF_NAME(ext[1]))
464 ext = NULL; /* Empty extension ignored */
466 /* Copy first 4 chars, replacing invalid chars with '_' */
467 for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
469 if (IS_END_OF_NAME(*p) || (p == ext)) break;
470 *dst++ = strchr( invalid_chars, *p ) ? '_' : toupper(*p);
472 /* Pad to 5 chars with '~' */
473 while (i-- >= 0) *dst++ = '~';
475 /* Insert hash code converted to 3 ASCII chars */
476 *dst++ = hash_chars[(hash >> 10) & 0x1f];
477 *dst++ = hash_chars[(hash >> 5) & 0x1f];
478 *dst++ = hash_chars[hash & 0x1f];
480 /* Copy the first 3 chars of the extension (if any) */
483 if (!dir_format) *dst++ = '.';
484 for (i = 3, ext++; (i > 0) && !IS_END_OF_NAME(*ext); i--, ext++)
485 *dst++ = strchr( invalid_chars, *ext ) ? '_' : toupper(*ext);
487 if (!dir_format) *dst = '\0';
491 /***********************************************************************
494 * Find the Unix file name in a given directory that corresponds to
495 * a file name (either in Unix or DOS format).
496 * File name can be terminated by '\0', '\\' or '/'.
497 * Return TRUE if OK, FALSE if no file name matches.
499 * 'long_buf' must be at least 'long_len' characters long. If the long name
500 * turns out to be larger than that, the function returns FALSE.
501 * 'short_buf' must be at least 13 characters long.
503 BOOL32 DOSFS_FindUnixName( LPCSTR path, LPCSTR name, LPSTR long_buf,
504 INT32 long_len, LPSTR short_buf, BOOL32 ignore_case)
507 LPCSTR long_name, short_name;
508 char dos_name[12], tmp_buf[13];
511 const char *p = strchr( name, '/' );
512 int len = p ? (int)(p - name) : strlen(name);
513 if ((p = strchr( name, '\\' ))) len = MIN( (int)(p - name), len );
514 if (long_len < len + 1) return FALSE;
516 dprintf_dosfs( stddeb, "DOSFS_FindUnixName: %s,%s\n", path, name );
518 if (!DOSFS_ToDosFCBFormat( name, dos_name )) dos_name[0] = '\0';
520 if (!(dir = DOSFS_OpenDir( path )))
522 dprintf_dosfs( stddeb, "DOSFS_FindUnixName(%s,%s): can't open dir: %s\n",
523 path, name, strerror(errno) );
527 while ((ret = DOSFS_ReadDir( dir, &long_name, &short_name )))
529 /* Check against Unix name */
530 if (len == strlen(long_name))
534 if (!lstrncmp32A( long_name, name, len )) break;
538 if (!lstrncmpi32A( long_name, name, len )) break;
543 /* Check against hashed DOS name */
546 DOSFS_Hash( long_name, tmp_buf, TRUE, ignore_case );
547 short_name = tmp_buf;
549 if (!strcmp( dos_name, short_name )) break;
554 if (long_buf) strcpy( long_buf, long_name );
558 DOSFS_ToDosDTAFormat( short_name, short_buf );
560 DOSFS_Hash( long_name, short_buf, FALSE, ignore_case );
562 dprintf_dosfs( stddeb, "DOSFS_FindUnixName(%s,%s) -> %s (%s)\n",
563 path, name, long_name, short_buf ? short_buf : "***");
566 dprintf_dosfs(stddeb,"DOSFS_FindUnixName(%s,%s) -> ** Not found **\n",
568 DOSFS_CloseDir( dir );
573 /***********************************************************************
576 * Check if a DOS file name represents a DOS device. Returns the name
577 * of the associated Unix device, or NULL if not found.
579 const char *DOSFS_IsDevice( const char *name )
584 if (name[0] && (name[1] == ':')) name += 2;
585 if ((p = strrchr( name, '/' ))) name = p + 1;
586 if ((p = strrchr( name, '\\' ))) name = p + 1;
587 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
589 const char *dev = DOSFS_Devices[i][0];
590 if (!lstrncmpi32A( dev, name, strlen(dev) ))
592 p = name + strlen( dev );
593 if (!*p || (*p == '.')) return DOSFS_Devices[i][1];
599 /***********************************************************************
602 * Get the drive specified by a given path name (DOS or Unix format).
604 static int DOSFS_GetPathDrive( const char **name )
607 const char *p = *name;
609 if (*p && (p[1] == ':'))
611 drive = toupper(*p) - 'A';
614 else if (*p == '/') /* Absolute Unix path? */
616 if ((drive = DRIVE_FindDriveRoot( name )) == -1)
618 fprintf( stderr, "Warning: %s not accessible from a DOS drive\n",
620 /* Assume it really was a DOS name */
621 drive = DRIVE_GetCurrentDrive();
624 else drive = DRIVE_GetCurrentDrive();
626 if (!DRIVE_IsValid(drive))
628 DOS_ERROR( ER_InvalidDrive, EC_MediaError, SA_Abort, EL_Disk );
635 /***********************************************************************
638 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
639 * Unix name / short DOS name pair.
640 * Return FALSE if one of the path components does not exist. The last path
641 * component is only checked if 'check_last' is non-zero.
642 * The buffers pointed to by 'long_buf' and 'short_buf' must be
643 * at least MAX_PATHNAME_LEN long.
645 BOOL32 DOSFS_GetFullName( LPCSTR name, BOOL32 check_last, DOS_FULL_NAME *full )
649 char *p_l, *p_s, *root;
651 dprintf_dosfs( stddeb, "DOSFS_GetFullName: %s (last=%d)\n",
654 if ((full->drive = DOSFS_GetPathDrive( &name )) == -1) return FALSE;
655 flags = DRIVE_GetFlags( full->drive );
657 lstrcpyn32A( full->long_name, DRIVE_GetRoot( full->drive ),
658 sizeof(full->long_name) );
659 if (full->long_name[1]) root = full->long_name + strlen(full->long_name);
660 else root = full->long_name; /* root directory */
662 strcpy( full->short_name, "A:\\" );
663 full->short_name[0] += full->drive;
665 if ((*name == '\\') || (*name == '/')) /* Absolute path */
667 while ((*name == '\\') || (*name == '/')) name++;
669 else /* Relative path */
671 lstrcpyn32A( root + 1, DRIVE_GetUnixCwd( full->drive ),
672 sizeof(full->long_name) - (root - full->long_name) - 1 );
673 if (root[1]) *root = '/';
674 lstrcpyn32A( full->short_name + 3, DRIVE_GetDosCwd( full->drive ),
675 sizeof(full->short_name) - 3 );
678 p_l = full->long_name[1] ? full->long_name + strlen(full->long_name)
680 p_s = full->short_name[3] ? full->short_name + strlen(full->short_name)
681 : full->short_name + 2;
684 while (*name && found)
686 /* Check for '.' and '..' */
690 if (IS_END_OF_NAME(name[1]))
693 while ((*name == '\\') || (*name == '/')) name++;
696 else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
699 while ((*name == '\\') || (*name == '/')) name++;
700 while ((p_l > root) && (*p_l != '/')) p_l--;
701 while ((p_s > full->short_name + 2) && (*p_s != '\\')) p_s--;
702 *p_l = *p_s = '\0'; /* Remove trailing separator */
707 /* Make sure buffers are large enough */
709 if ((p_s >= full->short_name + sizeof(full->short_name) - 14) ||
710 (p_l >= full->long_name + sizeof(full->long_name) - 1))
712 DOS_ERROR( ER_PathNotFound, EC_NotFound, SA_Abort, EL_Disk );
716 /* Get the long and short name matching the file name */
718 if ((found = DOSFS_FindUnixName( full->long_name, name, p_l + 1,
719 sizeof(full->long_name) - (p_l - full->long_name) - 1,
720 p_s + 1, !(flags & DRIVE_CASE_SENSITIVE) )))
726 while (!IS_END_OF_NAME(*name)) name++;
728 else if (!check_last)
732 while (!IS_END_OF_NAME(*name) &&
733 (p_s < full->short_name + sizeof(full->short_name) - 1) &&
734 (p_l < full->long_name + sizeof(full->long_name) - 1))
736 *p_l++ = *p_s++ = tolower(*name);
741 while ((*name == '\\') || (*name == '/')) name++;
748 DOS_ERROR( ER_FileNotFound, EC_NotFound, SA_Abort, EL_Disk );
751 if (*name) /* Not last */
753 DOS_ERROR( ER_PathNotFound, EC_NotFound, SA_Abort, EL_Disk );
757 if (!full->long_name[0]) strcpy( full->long_name, "/" );
758 if (!full->short_name[2]) strcpy( full->short_name + 2, "\\" );
759 dprintf_dosfs( stddeb, "DOSFS_GetFullName: returning %s = %s\n",
760 full->long_name, full->short_name );
765 /***********************************************************************
766 * GetShortPathName32A (KERNEL32.271)
768 DWORD WINAPI GetShortPathName32A( LPCSTR longpath, LPSTR shortpath,
771 DOS_FULL_NAME full_name;
773 /* FIXME: is it correct to always return a fully qualified short path? */
774 if (!DOSFS_GetFullName( longpath, TRUE, &full_name )) return 0;
775 lstrcpyn32A( shortpath, full_name.short_name, shortlen );
776 return strlen( full_name.short_name );
780 /***********************************************************************
781 * GetShortPathName32W (KERNEL32.272)
783 DWORD WINAPI GetShortPathName32W( LPCWSTR longpath, LPWSTR shortpath,
786 DOS_FULL_NAME full_name;
788 LPSTR longpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, longpath );
790 /* FIXME: is it correct to always return a fully qualified short path? */
791 if (DOSFS_GetFullName( longpathA, TRUE, &full_name ))
793 ret = strlen( full_name.short_name );
794 lstrcpynAtoW( shortpath, full_name.short_name, shortlen );
796 HeapFree( GetProcessHeap(), 0, longpathA );
801 /***********************************************************************
802 * GetLongPathName32A (KERNEL32.xxx)
804 DWORD WINAPI GetLongPathName32A( LPCSTR shortpath, LPSTR longpath,
807 DOS_FULL_NAME full_name;
809 /* FIXME: is it correct to always return a fully qualified short path? */
810 if (!DOSFS_GetFullName( shortpath, TRUE, &full_name )) return 0;
811 lstrcpyn32A( longpath, full_name.long_name, longlen );
812 return strlen( full_name.long_name );
816 /***********************************************************************
817 * GetLongPathName32W (KERNEL32.269)
819 DWORD WINAPI GetLongPathName32W( LPCWSTR shortpath, LPWSTR longpath,
822 DOS_FULL_NAME full_name;
824 LPSTR shortpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, shortpath );
826 /* FIXME: is it correct to always return a fully qualified short path? */
827 if (DOSFS_GetFullName( shortpathA, TRUE, &full_name ))
829 ret = strlen( full_name.short_name );
830 lstrcpynAtoW( longpath, full_name.long_name, longlen );
832 HeapFree( GetProcessHeap(), 0, shortpathA );
837 /***********************************************************************
838 * DOSFS_DoGetFullPathName
840 * Implementation of GetFullPathName32A/W.
842 static DWORD DOSFS_DoGetFullPathName( LPCSTR name, DWORD len, LPSTR result,
845 char buffer[MAX_PATHNAME_LEN];
849 dprintf_dosfs( stddeb, "GetFullPathName: converting %s\n", name );
851 if (!name || !result) return 0;
853 if ((drive = DOSFS_GetPathDrive( &name )) == -1) return 0;
857 if (IS_END_OF_NAME(*name) && (*name)) /* Absolute path */
859 while ((*name == '\\') || (*name == '/')) name++;
861 else /* Relative path or empty path */
864 lstrcpyn32A( p, DRIVE_GetDosCwd(drive), sizeof(buffer) - 3 );
865 if (*p) p += strlen(p); else p--;
867 if (!*name) /* empty path */
875 if (IS_END_OF_NAME(name[1]))
878 while ((*name == '\\') || (*name == '/')) name++;
881 else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
884 while ((*name == '\\') || (*name == '/')) name++;
885 while ((p > buffer + 2) && (*p != '\\')) p--;
886 *p = '\0'; /* Remove trailing separator */
890 if (p >= buffer + sizeof(buffer) - 1)
892 DOS_ERROR( ER_PathNotFound, EC_NotFound, SA_Abort, EL_Disk );
896 while (!IS_END_OF_NAME(*name) && (p < buffer + sizeof(buffer) - 1))
899 while ((*name == '\\') || (*name == '/')) name++;
907 if (!(DRIVE_GetFlags(drive) & DRIVE_CASE_PRESERVING))
908 CharUpper32A( buffer );
910 if (unicode) lstrcpynAtoW( (LPWSTR)result, buffer, len );
911 else lstrcpyn32A( result, buffer, len );
913 dprintf_dosfs( stddeb, "GetFullPathName: returning %s\n", buffer );
914 return strlen(buffer);
918 /***********************************************************************
919 * GetFullPathName32A (KERNEL32.272)
921 DWORD WINAPI GetFullPathName32A( LPCSTR name, DWORD len, LPSTR buffer,
924 DWORD ret = DOSFS_DoGetFullPathName( name, len, buffer, FALSE );
927 LPSTR p = buffer + strlen(buffer);
928 while ((p > buffer + 2) && (*p != '\\')) p--;
935 /***********************************************************************
936 * GetFullPathName32W (KERNEL32.273)
938 DWORD WINAPI GetFullPathName32W( LPCWSTR name, DWORD len, LPWSTR buffer,
941 LPSTR nameA = HEAP_strdupWtoA( GetProcessHeap(), 0, name );
942 DWORD ret = DOSFS_DoGetFullPathName( nameA, len, (LPSTR)buffer, TRUE );
943 HeapFree( GetProcessHeap(), 0, nameA );
946 LPWSTR p = buffer + lstrlen32W(buffer);
947 while ((p > buffer + 2) && (*p != '\\')) p--;
954 /***********************************************************************
957 * Find the next matching file. Return the number of entries read to find
958 * the matching one, or 0 if no more entries.
959 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
960 * file name mask. Either or both can be NULL.
962 int DOSFS_FindNext( const char *path, const char *short_mask,
963 const char *long_mask, int drive, BYTE attr,
964 int skip, WIN32_FIND_DATA32A *entry )
966 static DOS_DIR *dir = NULL;
968 static char buffer[MAX_PATHNAME_LEN];
969 static int cur_pos = 0;
970 static int drive_root = 0;
973 LPCSTR long_name, short_name;
975 BY_HANDLE_FILE_INFORMATION info;
977 if ((attr & ~(FA_UNUSED | FA_ARCHIVE | FA_RDONLY)) == FA_LABEL)
980 entry->dwFileAttributes = FILE_ATTRIBUTE_LABEL;
981 DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftCreationTime, 0 );
982 DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftLastAccessTime, 0 );
983 DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftLastWriteTime, 0 );
984 entry->nFileSizeHigh = 0;
985 entry->nFileSizeLow = 0;
986 entry->dwReserved0 = 0;
987 entry->dwReserved1 = 0;
988 DOSFS_ToDosDTAFormat( DRIVE_GetLabel( drive ), entry->cFileName );
989 strcpy( entry->cAlternateFileName, entry->cFileName );
993 /* Check the cached directory */
994 if (dir && !strcmp( buffer, path ) && (cur_pos <= skip)) skip -= cur_pos;
995 else /* Not in the cache, open it anew */
997 const char *drive_path;
998 dprintf_dosfs( stddeb, "DOSFS_FindNext: cache miss, path=%s skip=%d buf=%s cur=%d\n",
999 path, skip, buffer, cur_pos );
1001 if (dir) DOSFS_CloseDir(dir);
1002 if (!*path) path = "/";
1003 if (!(dir = DOSFS_OpenDir(path))) return 0;
1004 drive_path = path + strlen(DRIVE_GetRoot(drive));
1005 while ((*drive_path == '/') || (*drive_path == '\\')) drive_path++;
1006 drive_root = !*drive_path;
1007 dprintf_dosfs(stddeb, "DOSFS_FindNext: drive_root = %d\n", drive_root);
1008 lstrcpyn32A( buffer, path, sizeof(buffer) - 1 );
1010 strcat( buffer, "/" );
1011 p = buffer + strlen(buffer);
1012 attr |= FA_UNUSED | FA_ARCHIVE | FA_RDONLY;
1013 flags = DRIVE_GetFlags( drive );
1015 while (DOSFS_ReadDir( dir, &long_name, &short_name ))
1017 if (skip-- > 0) continue;
1020 /* Don't return '.' and '..' in the root of the drive */
1021 if (drive_root && (long_name[0] == '.') &&
1022 (!long_name[1] || ((long_name[1] == '.') && !long_name[2])))
1025 /* Check the long mask */
1029 if (!DOSFS_MatchLong( long_mask, long_name,
1030 flags & DRIVE_CASE_SENSITIVE )) continue;
1033 /* Check the short mask */
1039 DOSFS_Hash( long_name, dos_name, TRUE,
1040 !(flags & DRIVE_CASE_SENSITIVE) );
1041 short_name = dos_name;
1043 if (!DOSFS_MatchShort( short_mask, short_name )) continue;
1046 /* Check the file attributes */
1048 lstrcpyn32A( p, long_name, sizeof(buffer) - (int)(p - buffer) );
1049 if (!FILE_Stat( buffer, &info ))
1051 fprintf( stderr, "DOSFS_FindNext: can't stat %s\n", buffer );
1054 if (info.dwFileAttributes & ~attr) continue;
1056 /* We now have a matching entry; fill the result and return */
1058 entry->dwFileAttributes = info.dwFileAttributes;
1059 entry->ftCreationTime = info.ftCreationTime;
1060 entry->ftLastAccessTime = info.ftLastAccessTime;
1061 entry->ftLastWriteTime = info.ftLastWriteTime;
1062 entry->nFileSizeHigh = info.nFileSizeHigh;
1063 entry->nFileSizeLow = info.nFileSizeLow;
1066 DOSFS_ToDosDTAFormat( short_name, entry->cAlternateFileName );
1068 DOSFS_Hash( long_name, entry->cAlternateFileName, FALSE,
1069 !(flags & DRIVE_CASE_SENSITIVE) );
1071 lstrcpyn32A( entry->cFileName, long_name, sizeof(entry->cFileName) );
1072 if (!(flags & DRIVE_CASE_PRESERVING)) CharLower32A( entry->cFileName );
1073 dprintf_dosfs( stddeb, "DOSFS_FindNext: returning %s (%s) %02lx %ld\n",
1074 entry->cFileName, entry->cAlternateFileName,
1075 entry->dwFileAttributes, entry->nFileSizeLow );
1077 p[-1] = '\0'; /* Remove trailing slash in buffer */
1080 DOSFS_CloseDir( dir );
1082 return 0; /* End of directory */
1086 /*************************************************************************
1087 * FindFirstFile16 (KERNEL.413)
1089 HANDLE16 WINAPI FindFirstFile16( LPCSTR path, WIN32_FIND_DATA32A *data )
1091 DOS_FULL_NAME full_name;
1093 FIND_FIRST_INFO *info;
1095 if (!path) return 0;
1096 if (!DOSFS_GetFullName( path, FALSE, &full_name ))
1097 return INVALID_HANDLE_VALUE16;
1098 if (!(handle = GlobalAlloc16( GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO) )))
1099 return INVALID_HANDLE_VALUE16;
1100 info = (FIND_FIRST_INFO *)GlobalLock16( handle );
1101 info->path = HEAP_strdupA( SystemHeap, 0, full_name.long_name );
1102 info->mask = strrchr( info->path, '/' );
1103 *(info->mask++) = '\0';
1104 if (path[0] && (path[1] == ':')) info->drive = toupper(*path) - 'A';
1105 else info->drive = DRIVE_GetCurrentDrive();
1107 GlobalUnlock16( handle );
1108 if (!FindNextFile16( handle, data ))
1110 FindClose16( handle );
1111 DOS_ERROR( ER_NoMoreFiles, EC_MediaError, SA_Abort, EL_Disk );
1112 return INVALID_HANDLE_VALUE16;
1118 /*************************************************************************
1119 * FindFirstFile32A (KERNEL32.123)
1121 HANDLE32 WINAPI FindFirstFile32A( LPCSTR path, WIN32_FIND_DATA32A *data )
1123 HANDLE32 handle = FindFirstFile16( path, data );
1124 if (handle == INVALID_HANDLE_VALUE16) return INVALID_HANDLE_VALUE32;
1129 /*************************************************************************
1130 * FindFirstFile32W (KERNEL32.124)
1132 HANDLE32 WINAPI FindFirstFile32W( LPCWSTR path, WIN32_FIND_DATA32W *data )
1134 WIN32_FIND_DATA32A dataA;
1135 LPSTR pathA = HEAP_strdupWtoA( GetProcessHeap(), 0, path );
1136 HANDLE32 handle = FindFirstFile32A( pathA, &dataA );
1137 HeapFree( GetProcessHeap(), 0, pathA );
1138 if (handle != INVALID_HANDLE_VALUE32)
1140 data->dwFileAttributes = dataA.dwFileAttributes;
1141 data->ftCreationTime = dataA.ftCreationTime;
1142 data->ftLastAccessTime = dataA.ftLastAccessTime;
1143 data->ftLastWriteTime = dataA.ftLastWriteTime;
1144 data->nFileSizeHigh = dataA.nFileSizeHigh;
1145 data->nFileSizeLow = dataA.nFileSizeLow;
1146 lstrcpyAtoW( data->cFileName, dataA.cFileName );
1147 lstrcpyAtoW( data->cAlternateFileName, dataA.cAlternateFileName );
1153 /*************************************************************************
1154 * FindNextFile16 (KERNEL.414)
1156 BOOL16 WINAPI FindNextFile16( HANDLE16 handle, WIN32_FIND_DATA32A *data )
1158 FIND_FIRST_INFO *info;
1161 if (!(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
1163 DOS_ERROR( ER_InvalidHandle, EC_ProgramError, SA_Abort, EL_Disk );
1166 GlobalUnlock16( handle );
1169 DOS_ERROR( ER_NoMoreFiles, EC_MediaError, SA_Abort, EL_Disk );
1172 if (!(count = DOSFS_FindNext( info->path, NULL, info->mask, info->drive,
1173 0xff, info->skip, data )))
1175 HeapFree( SystemHeap, 0, info->path );
1176 info->path = info->mask = NULL;
1177 DOS_ERROR( ER_NoMoreFiles, EC_MediaError, SA_Abort, EL_Disk );
1180 info->skip += count;
1185 /*************************************************************************
1186 * FindNextFile32A (KERNEL32.126)
1188 BOOL32 WINAPI FindNextFile32A( HANDLE32 handle, WIN32_FIND_DATA32A *data )
1190 return FindNextFile16( handle, data );
1194 /*************************************************************************
1195 * FindNextFile32W (KERNEL32.127)
1197 BOOL32 WINAPI FindNextFile32W( HANDLE32 handle, WIN32_FIND_DATA32W *data )
1199 WIN32_FIND_DATA32A dataA;
1200 if (!FindNextFile32A( handle, &dataA )) return FALSE;
1201 data->dwFileAttributes = dataA.dwFileAttributes;
1202 data->ftCreationTime = dataA.ftCreationTime;
1203 data->ftLastAccessTime = dataA.ftLastAccessTime;
1204 data->ftLastWriteTime = dataA.ftLastWriteTime;
1205 data->nFileSizeHigh = dataA.nFileSizeHigh;
1206 data->nFileSizeLow = dataA.nFileSizeLow;
1207 lstrcpyAtoW( data->cFileName, dataA.cFileName );
1208 lstrcpyAtoW( data->cAlternateFileName, dataA.cAlternateFileName );
1213 /*************************************************************************
1214 * FindClose16 (KERNEL.415)
1216 BOOL16 WINAPI FindClose16( HANDLE16 handle )
1218 FIND_FIRST_INFO *info;
1220 if ((handle == INVALID_HANDLE_VALUE16) ||
1221 !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
1223 DOS_ERROR( ER_InvalidHandle, EC_ProgramError, SA_Abort, EL_Disk );
1226 if (info->path) HeapFree( SystemHeap, 0, info->path );
1227 GlobalUnlock16( handle );
1228 GlobalFree16( handle );
1233 /*************************************************************************
1234 * FindClose32 (KERNEL32.119)
1236 BOOL32 WINAPI FindClose32( HANDLE32 handle )
1238 return FindClose16( (HANDLE16)handle );
1242 /***********************************************************************
1243 * DOSFS_UnixTimeToFileTime
1245 * Convert a Unix time to FILETIME format.
1246 * The FILETIME structure is a 64-bit value representing the number of
1247 * 100-nanosecond intervals since January 1, 1601, 0:00.
1248 * 'remainder' is the nonnegative number of 100-ns intervals
1249 * corresponding to the time fraction smaller than 1 second that
1250 * couldn't be stored in the time_t value.
1252 void DOSFS_UnixTimeToFileTime( time_t unix_time, FILETIME *filetime,
1258 The time difference between 1 January 1601, 00:00:00 and
1259 1 January 1970, 00:00:00 is 369 years, plus the leap years
1260 from 1604 to 1968, excluding 1700, 1800, 1900.
1261 This makes (1968 - 1600) / 4 - 3 = 89 leap days, and a total
1264 Any day in that period had 24 * 60 * 60 = 86400 seconds.
1266 The time difference is 134774 * 86400 * 10000000, which can be written
1268 27111902 * 2^32 + 3577643008
1269 413 * 2^48 + 45534 * 2^32 + 54590 * 2^16 + 32768
1271 If you find that these constants are buggy, please change them in all
1272 instances in both conversion functions.
1275 There are two versions, one of them uses long long variables and
1276 is presumably faster but not ISO C. The other one uses standard C
1277 data types and operations but relies on the assumption that negative
1278 numbers are stored as 2's complement (-1 is 0xffff....). If this
1279 assumption is violated, dates before 1970 will not convert correctly.
1280 This should however work on any reasonable architecture where WINE
1285 Take care not to remove the casts. I have tested these functions
1286 (in both versions) for a lot of numbers. I would be interested in
1287 results on other compilers than GCC.
1289 The operations have been designed to account for the possibility
1290 of 64-bit time_t in future UNICES. Even the versions without
1291 internal long long numbers will work if time_t only is 64 bit.
1292 A 32-bit shift, which was necessary for that operation, turned out
1293 not to work correctly in GCC, besides giving the warning. So I
1294 used a double 16-bit shift instead. Numbers are in the ISO version
1295 represented by three limbs, the most significant with 32 bit, the
1296 other two with 16 bit each.
1298 As the modulo-operator % is not well-defined for negative numbers,
1299 negative divisors have been avoided in DOSFS_FileTimeToUnixTime.
1301 There might be quicker ways to do this in C. Certainly so in
1304 Claus Fischer, fischer@iue.tuwien.ac.at
1308 # define USE_LONG_LONG 1
1310 # define USE_LONG_LONG 0
1313 #if USE_LONG_LONG /* gcc supports long long type */
1315 long long int t = unix_time;
1317 t += 116444736000000000LL;
1319 filetime->dwLowDateTime = (UINT32)t;
1320 filetime->dwHighDateTime = (UINT32)(t >> 32);
1322 #else /* ISO version */
1324 UINT32 a0; /* 16 bit, low bits */
1325 UINT32 a1; /* 16 bit, medium bits */
1326 UINT32 a2; /* 32 bit, high bits */
1328 /* Copy the unix time to a2/a1/a0 */
1329 a0 = unix_time & 0xffff;
1330 a1 = (unix_time >> 16) & 0xffff;
1331 /* This is obsolete if unix_time is only 32 bits, but it does not hurt.
1332 Do not replace this by >> 32, it gives a compiler warning and it does
1334 a2 = (unix_time >= 0 ? (unix_time >> 16) >> 16 :
1335 ~((~unix_time >> 16) >> 16));
1337 /* Multiply a by 10000000 (a = a2/a1/a0)
1338 Split the factor into 10000 * 1000 which are both less than 0xffff. */
1340 a1 = a1 * 10000 + (a0 >> 16);
1341 a2 = a2 * 10000 + (a1 >> 16);
1346 a1 = a1 * 1000 + (a0 >> 16);
1347 a2 = a2 * 1000 + (a1 >> 16);
1351 /* Add the time difference and the remainder */
1352 a0 += 32768 + (remainder & 0xffff);
1353 a1 += 54590 + (remainder >> 16 ) + (a0 >> 16);
1354 a2 += 27111902 + (a1 >> 16);
1359 filetime->dwLowDateTime = (a1 << 16) + a0;
1360 filetime->dwHighDateTime = a2;
1365 /***********************************************************************
1366 * DOSFS_FileTimeToUnixTime
1368 * Convert a FILETIME format to Unix time.
1369 * If not NULL, 'remainder' contains the fractional part of the filetime,
1370 * in the range of [0..9999999] (even if time_t is negative).
1372 time_t DOSFS_FileTimeToUnixTime( const FILETIME *filetime, DWORD *remainder )
1374 /* Read the comment in the function DOSFS_UnixTimeToFileTime. */
1377 long long int t = filetime->dwHighDateTime;
1379 t += (UINT32)filetime->dwLowDateTime;
1380 t -= 116444736000000000LL;
1383 if (remainder) *remainder = 9999999 - (-t - 1) % 10000000;
1384 return -1 - ((-t - 1) / 10000000);
1388 if (remainder) *remainder = t % 10000000;
1389 return t / 10000000;
1392 #else /* ISO version */
1394 UINT32 a0; /* 16 bit, low bits */
1395 UINT32 a1; /* 16 bit, medium bits */
1396 UINT32 a2; /* 32 bit, high bits */
1397 UINT32 r; /* remainder of division */
1398 unsigned int carry; /* carry bit for subtraction */
1399 int negative; /* whether a represents a negative value */
1401 /* Copy the time values to a2/a1/a0 */
1402 a2 = (UINT32)filetime->dwHighDateTime;
1403 a1 = ((UINT32)filetime->dwLowDateTime ) >> 16;
1404 a0 = ((UINT32)filetime->dwLowDateTime ) & 0xffff;
1406 /* Subtract the time difference */
1407 if (a0 >= 32768 ) a0 -= 32768 , carry = 0;
1408 else a0 += (1 << 16) - 32768 , carry = 1;
1410 if (a1 >= 54590 + carry) a1 -= 54590 + carry, carry = 0;
1411 else a1 += (1 << 16) - 54590 - carry, carry = 1;
1413 a2 -= 27111902 + carry;
1415 /* If a is negative, replace a by (-1-a) */
1416 negative = (a2 >= ((UINT32)1) << 31);
1419 /* Set a to -a - 1 (a is a2/a1/a0) */
1425 /* Divide a by 10000000 (a = a2/a1/a0), put the rest into r.
1426 Split the divisor into 10000 * 1000 which are both less than 0xffff. */
1427 a1 += (a2 % 10000) << 16;
1429 a0 += (a1 % 10000) << 16;
1434 a1 += (a2 % 1000) << 16;
1436 a0 += (a1 % 1000) << 16;
1438 r += (a0 % 1000) * 10000;
1441 /* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
1444 /* Set a to -a - 1 (a is a2/a1/a0) */
1452 if (remainder) *remainder = r;
1454 /* Do not replace this by << 32, it gives a compiler warning and it does
1456 return ((((time_t)a2) << 16) << 16) + (a1 << 16) + a0;
1461 /***********************************************************************
1462 * DosDateTimeToFileTime (KERNEL32.76)
1464 BOOL32 WINAPI DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft)
1468 newtm.tm_sec = (fattime & 0x1f) * 2;
1469 newtm.tm_min = (fattime >> 5) & 0x3f;
1470 newtm.tm_hour = (fattime >> 11);
1471 newtm.tm_mday = (fatdate & 0x1f);
1472 newtm.tm_mon = ((fatdate >> 5) & 0x0f) - 1;
1473 newtm.tm_year = (fatdate >> 9) + 80;
1474 DOSFS_UnixTimeToFileTime( mktime( &newtm ), ft, 0 );
1479 /***********************************************************************
1480 * FileTimeToDosDateTime (KERNEL32.111)
1482 BOOL32 WINAPI FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate,
1485 time_t unixtime = DOSFS_FileTimeToUnixTime( ft, NULL );
1486 struct tm *tm = localtime( &unixtime );
1488 *fattime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
1490 *fatdate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
1496 /***********************************************************************
1497 * LocalFileTimeToFileTime (KERNEL32.373)
1499 BOOL32 WINAPI LocalFileTimeToFileTime( const FILETIME *localft,
1505 /* convert from local to UTC. Perhaps not correct. FIXME */
1506 time_t unixtime = DOSFS_FileTimeToUnixTime( localft, &remainder );
1507 xtm = gmtime( &unixtime );
1508 DOSFS_UnixTimeToFileTime( mktime(xtm), utcft, remainder );
1513 /***********************************************************************
1514 * FileTimeToLocalFileTime (KERNEL32.112)
1516 BOOL32 WINAPI FileTimeToLocalFileTime( const FILETIME *utcft,
1517 LPFILETIME localft )
1522 /* convert from UTC to local. Perhaps not correct. FIXME */
1523 time_t unixtime = DOSFS_FileTimeToUnixTime( utcft, &remainder );
1524 xtm = localtime( &unixtime );
1525 DOSFS_UnixTimeToFileTime( mktime(xtm), localft, remainder );
1530 /***********************************************************************
1531 * FileTimeToSystemTime (KERNEL32.113)
1533 BOOL32 WINAPI FileTimeToSystemTime( const FILETIME *ft, LPSYSTEMTIME syst )
1537 time_t xtime = DOSFS_FileTimeToUnixTime( ft, &remainder );
1538 xtm = gmtime(&xtime);
1539 syst->wYear = xtm->tm_year;
1540 syst->wMonth = xtm->tm_mon;
1541 syst->wDayOfWeek = xtm->tm_wday;
1542 syst->wDay = xtm->tm_mday;
1543 syst->wHour = xtm->tm_hour;
1544 syst->wMinute = xtm->tm_min;
1545 syst->wSecond = xtm->tm_sec;
1546 syst->wMilliseconds = remainder / 10000;
1550 /***********************************************************************
1551 * QueryDosDeviceA (KERNEL32.413)
1553 * returns array of strings terminated by \0, terminated by \0
1555 DWORD WINAPI QueryDosDevice32A(LPCSTR devname,LPSTR target,DWORD bufsize)
1560 dprintf_dosfs(stddeb,"QueryDosDevice(%s,...)\n",devname?devname:"<null>");
1562 /* return known MSDOS devices */
1563 lstrcpy32A(buffer,"CON COM1 COM2 LPT1 NUL ");
1564 while ((s=strchr(buffer,' ')))
1567 lstrcpyn32A(target,buffer,bufsize);
1568 return strlen(buffer);
1570 lstrcpy32A(buffer,"\\DEV\\");
1571 lstrcat32A(buffer,devname);
1572 if ((s=strchr(buffer,':'))) *s='\0';
1573 lstrcpyn32A(target,buffer,bufsize);
1574 return strlen(buffer);
1578 /***********************************************************************
1579 * QueryDosDeviceW (KERNEL32.414)
1581 * returns array of strings terminated by \0, terminated by \0
1583 DWORD WINAPI QueryDosDevice32W(LPCWSTR devname,LPWSTR target,DWORD bufsize)
1585 LPSTR devnameA = devname?HEAP_strdupWtoA(GetProcessHeap(),0,devname):NULL;
1586 LPSTR targetA = (LPSTR)HEAP_xalloc(GetProcessHeap(),0,bufsize);
1587 DWORD ret = QueryDosDevice32A(devnameA,targetA,bufsize);
1589 lstrcpynAtoW(target,targetA,bufsize);
1590 if (devnameA) HeapFree(GetProcessHeap(),0,devnameA);
1591 if (targetA) HeapFree(GetProcessHeap(),0,targetA);
1596 /***********************************************************************
1597 * SystemTimeToFileTime (KERNEL32.526)
1599 BOOL32 WINAPI SystemTimeToFileTime( const SYSTEMTIME *syst, LPFILETIME ft )
1603 xtm.tm_year = syst->wYear;
1604 xtm.tm_mon = syst->wMonth;
1605 xtm.tm_wday = syst->wDayOfWeek;
1606 xtm.tm_mday = syst->wDay;
1607 xtm.tm_hour = syst->wHour;
1608 xtm.tm_min = syst->wMinute;
1609 xtm.tm_sec = syst->wSecond;
1610 DOSFS_UnixTimeToFileTime( mktime(&xtm), ft, syst->wMilliseconds * 10000 );