2 * DOS file system functions
4 * Copyright 1993 Erik Bos
5 * Copyright 1996 Alexandre Julliard
16 #include <sys/ioctl.h>
19 #if defined(__svr4__) || defined(_SCO_DS)
20 #include <sys/statfs.h>
32 /* Define the VFAT ioctl to get both short and long file names */
33 /* FIXME: is it possible to get this to work on other systems? */
35 #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, long)
37 #undef VFAT_IOCTL_READDIR_BOTH /* just in case... */
40 /* Chars we don't want to see in DOS file names */
41 #define INVALID_DOS_CHARS "*?<>|\"+=,;[] \345"
43 static const char *DOSFS_Devices[][2] =
47 { "NUL", "/dev/null" },
60 #define GET_DRIVE(path) \
61 (((path)[1] == ':') ? toupper((path)[0]) - 'A' : DOSFS_CurDrive)
63 /* DOS extended error status */
64 WORD DOS_ExtendedError;
69 /* Info structure for FindFirstFile handle */
79 /* Directory info for DOSFS_ReadDir */
83 #ifdef VFAT_IOCTL_READDIR_BOTH
86 struct dirent dirent[2];
91 /***********************************************************************
94 * Return 1 if Unix file 'name' is also a valid MS-DOS name
95 * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
96 * File name can be terminated by '\0', '\\' or '/'.
98 static int DOSFS_ValidDOSName( const char *name, int ignore_case )
100 static const char invalid_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS;
101 const char *p = name;
102 const char *invalid = ignore_case ? (invalid_chars + 26) : invalid_chars;
107 /* Check for "." and ".." */
110 /* All other names beginning with '.' are invalid */
111 return (IS_END_OF_NAME(*p));
113 while (!IS_END_OF_NAME(*p))
115 if (strchr( invalid, *p )) return 0; /* Invalid char */
116 if (*p == '.') break; /* Start of the extension */
117 if (++len > 8) return 0; /* Name too long */
120 if (*p != '.') return 1; /* End of name */
122 if (IS_END_OF_NAME(*p)) return 0; /* Empty extension not allowed */
124 while (!IS_END_OF_NAME(*p))
126 if (strchr( invalid, *p )) return 0; /* Invalid char */
127 if (*p == '.') return 0; /* Second extension not allowed */
128 if (++len > 3) return 0; /* Extension too long */
135 /***********************************************************************
136 * DOSFS_ToDosFCBFormat
138 * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
139 * expanding wild cards and converting to upper-case in the process.
140 * File name can be terminated by '\0', '\\' or '/'.
141 * Return FALSE if the name is not a valid DOS name.
142 * 'buffer' must be at least 12 characters long.
144 BOOL32 DOSFS_ToDosFCBFormat( LPCSTR name, LPSTR buffer )
146 static const char invalid_chars[] = INVALID_DOS_CHARS;
147 const char *p = name;
150 /* Check for "." and ".." */
154 strcpy( buffer, ". " );
160 return (!*p || (*p == '/') || (*p == '\\'));
163 for (i = 0; i < 8; i++)
180 if (strchr( invalid_chars, *p )) return FALSE;
181 buffer[i] = toupper(*p);
189 /* Skip all chars after wildcard up to first dot */
190 while (*p && (*p != '/') && (*p != '\\') && (*p != '.')) p++;
194 /* Check if name too long */
195 if (*p && (*p != '/') && (*p != '\\') && (*p != '.')) return FALSE;
197 if (*p == '.') p++; /* Skip dot */
199 for (i = 8; i < 11; i++)
209 return FALSE; /* Second extension not allowed */
217 if (strchr( invalid_chars, *p )) return FALSE;
218 buffer[i] = toupper(*p);
228 /***********************************************************************
229 * DOSFS_ToDosDTAFormat
231 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
232 * converting to upper-case in the process.
233 * File name can be terminated by '\0', '\\' or '/'.
234 * 'buffer' must be at least 13 characters long.
236 static void DOSFS_ToDosDTAFormat( LPCSTR name, LPSTR buffer )
240 memcpy( buffer, name, 8 );
241 for (p = buffer + 8; (p > buffer) && (p[-1] == ' '); p--);
243 memcpy( p, name + 8, 3 );
244 for (p += 3; p[-1] == ' '; p--);
245 if (p[-1] == '.') p--;
250 /***********************************************************************
253 * Check a DOS file name against a mask (both in FCB format).
255 static int DOSFS_MatchShort( const char *mask, const char *name )
258 for (i = 11; i > 0; i--, mask++, name++)
259 if ((*mask != '?') && (*mask != *name)) return 0;
264 /***********************************************************************
267 * Check a long file name against a mask.
269 static int DOSFS_MatchLong( const char *mask, const char *name,
272 if (!strcmp( mask, "*.*" )) return 1;
273 while (*name && *mask)
278 while (*mask == '*') mask++; /* Skip consecutive '*' */
279 if (!*mask) return 1;
280 if (case_sensitive) while (*name && (*name != *mask)) name++;
281 else while (*name && (toupper(*name) != toupper(*mask))) name++;
282 if (!*name) return 0;
284 else if (*mask != '?')
288 if (*mask != *name) return 0;
290 else if (toupper(*mask) != toupper(*name)) return 0;
295 return (!*name && !*mask);
299 /***********************************************************************
302 static DOS_DIR *DOSFS_OpenDir( LPCSTR path )
304 DOS_DIR *dir = HeapAlloc( SystemHeap, 0, sizeof(*dir) );
307 DOS_ERROR( ER_OutOfMemory, EC_OutOfResource, SA_Abort, EL_Memory );
311 #ifdef VFAT_IOCTL_READDIR_BOTH
313 /* Check if the VFAT ioctl is supported on this directory */
315 if ((dir->fd = open( path, O_RDONLY )) != -1)
317 if (ioctl( dir->fd, VFAT_IOCTL_READDIR_BOTH, (long)dir->dirent ) == -1)
324 /* Set the file pointer back at the start of the directory */
325 lseek( dir->fd, 0, SEEK_SET );
330 #endif /* VFAT_IOCTL_READDIR_BOTH */
332 /* Now use the standard opendir/readdir interface */
334 if (!(dir->dir = opendir( path )))
336 HeapFree( SystemHeap, 0, dir );
343 /***********************************************************************
346 static void DOSFS_CloseDir( DOS_DIR *dir )
348 #ifdef VFAT_IOCTL_READDIR_BOTH
349 if (dir->fd != -1) close( dir->fd );
350 #endif /* VFAT_IOCTL_READDIR_BOTH */
351 if (dir->dir) closedir( dir->dir );
352 HeapFree( SystemHeap, 0, dir );
356 /***********************************************************************
359 static BOOL32 DOSFS_ReadDir( DOS_DIR *dir, LPCSTR *long_name,
362 struct dirent *dirent;
364 #ifdef VFAT_IOCTL_READDIR_BOTH
367 if (ioctl( dir->fd, VFAT_IOCTL_READDIR_BOTH, (long)dir->dirent ) == -1)
369 if (!dir->dirent[0].d_reclen) return FALSE;
370 if (!DOSFS_ToDosFCBFormat( dir->dirent[0].d_name, dir->short_name ))
371 dir->short_name[0] = '\0';
372 *short_name = dir->short_name;
373 if (dir->dirent[1].d_name[0]) *long_name = dir->dirent[1].d_name;
374 else *long_name = dir->dirent[0].d_name;
377 #endif /* VFAT_IOCTL_READDIR_BOTH */
379 if (!(dirent = readdir( dir->dir ))) return FALSE;
380 *long_name = dirent->d_name;
386 /***********************************************************************
389 * Transform a Unix file name into a hashed DOS name. If the name is a valid
390 * DOS name, it is converted to upper-case; otherwise it is replaced by a
391 * hashed version that fits in 8.3 format.
392 * File name can be terminated by '\0', '\\' or '/'.
393 * 'buffer' must be at least 13 characters long.
395 static void DOSFS_Hash( LPCSTR name, LPSTR buffer, BOOL32 dir_format,
398 static const char invalid_chars[] = INVALID_DOS_CHARS "~.";
399 static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
406 if (dir_format) strcpy( buffer, " " );
408 if (DOSFS_ValidDOSName( name, ignore_case ))
410 /* Check for '.' and '..' */
414 if (!dir_format) buffer[1] = buffer[2] = '\0';
415 if (name[1] == '.') buffer[1] = '.';
419 /* Simply copy the name, converting to uppercase */
421 for (dst = buffer; !IS_END_OF_NAME(*name) && (*name != '.'); name++)
422 *dst++ = toupper(*name);
425 if (dir_format) dst = buffer + 8;
427 for (name++; !IS_END_OF_NAME(*name); name++)
428 *dst++ = toupper(*name);
430 if (!dir_format) *dst = '\0';
434 /* Compute the hash code of the file name */
435 /* If you know something about hash functions, feel free to */
436 /* insert a better algorithm here... */
439 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
440 hash = (hash<<3) ^ (hash>>5) ^ tolower(*p) ^ (tolower(p[1]) << 8);
441 hash = (hash<<3) ^ (hash>>5) ^ tolower(*p); /* Last character*/
445 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
446 hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
447 hash = (hash << 3) ^ (hash >> 5) ^ *p; /* Last character */
450 /* Find last dot for start of the extension */
451 for (p = name+1, ext = NULL; !IS_END_OF_NAME(*p); p++)
452 if (*p == '.') ext = p;
453 if (ext && IS_END_OF_NAME(ext[1]))
454 ext = NULL; /* Empty extension ignored */
456 /* Copy first 4 chars, replacing invalid chars with '_' */
457 for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
459 if (IS_END_OF_NAME(*p) || (p == ext)) break;
460 *dst++ = strchr( invalid_chars, *p ) ? '_' : toupper(*p);
462 /* Pad to 5 chars with '~' */
463 while (i-- >= 0) *dst++ = '~';
465 /* Insert hash code converted to 3 ASCII chars */
466 *dst++ = hash_chars[(hash >> 10) & 0x1f];
467 *dst++ = hash_chars[(hash >> 5) & 0x1f];
468 *dst++ = hash_chars[hash & 0x1f];
470 /* Copy the first 3 chars of the extension (if any) */
473 if (!dir_format) *dst++ = '.';
474 for (i = 3, ext++; (i > 0) && !IS_END_OF_NAME(*ext); i--, ext++)
475 *dst++ = strchr( invalid_chars, *ext ) ? '_' : toupper(*ext);
477 if (!dir_format) *dst = '\0';
481 /***********************************************************************
484 * Find the Unix file name in a given directory that corresponds to
485 * a file name (either in Unix or DOS format).
486 * File name can be terminated by '\0', '\\' or '/'.
487 * Return TRUE if OK, FALSE if no file name matches.
489 * 'long_buf' must be at least 'long_len' characters long. If the long name
490 * turns out to be larger than that, the function returns FALSE.
491 * 'short_buf' must be at least 13 characters long.
493 BOOL32 DOSFS_FindUnixName( LPCSTR path, LPCSTR name, LPSTR long_buf,
494 INT32 long_len, LPSTR short_buf, BOOL32 ignore_case)
497 LPCSTR long_name, short_name;
498 char dos_name[12], tmp_buf[13];
501 const char *p = strchr( name, '/' );
502 int len = p ? (int)(p - name) : strlen(name);
503 if ((p = strchr( name, '\\' ))) len = MIN( (int)(p - name), len );
504 if (long_len < len + 1) return FALSE;
506 dprintf_dosfs( stddeb, "DOSFS_FindUnixName: %s,%s\n", path, name );
508 if (!DOSFS_ToDosFCBFormat( name, dos_name )) dos_name[0] = '\0';
510 if (!(dir = DOSFS_OpenDir( path )))
512 dprintf_dosfs( stddeb, "DOSFS_FindUnixName(%s,%s): can't open dir\n",
517 while ((ret = DOSFS_ReadDir( dir, &long_name, &short_name )))
519 /* Check against Unix name */
520 if (len == strlen(long_name))
524 if (!lstrncmp32A( long_name, name, len )) break;
528 if (!lstrncmpi32A( long_name, name, len )) break;
533 /* Check against hashed DOS name */
536 DOSFS_Hash( long_name, tmp_buf, TRUE, ignore_case );
537 short_name = tmp_buf;
539 if (!strcmp( dos_name, short_name )) break;
544 if (long_buf) strcpy( long_buf, long_name );
548 DOSFS_ToDosDTAFormat( short_name, short_buf );
550 DOSFS_Hash( long_name, short_buf, FALSE, ignore_case );
552 dprintf_dosfs( stddeb, "DOSFS_FindUnixName(%s,%s) -> %s (%s)\n",
553 path, name, long_name, short_buf ? short_buf : "***");
556 dprintf_dosfs(stddeb,"DOSFS_FindUnixName(%s,%s) -> ** Not found **\n",
558 DOSFS_CloseDir( dir );
563 /***********************************************************************
566 * Check if a DOS file name represents a DOS device. Returns the name
567 * of the associated Unix device, or NULL if not found.
569 const char *DOSFS_IsDevice( const char *name )
574 if (name[0] && (name[1] == ':')) name += 2;
575 if ((p = strrchr( name, '/' ))) name = p + 1;
576 if ((p = strrchr( name, '\\' ))) name = p + 1;
577 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
579 const char *dev = DOSFS_Devices[i][0];
580 if (!lstrncmpi32A( dev, name, strlen(dev) ))
582 p = name + strlen( dev );
583 if (!*p || (*p == '.')) return DOSFS_Devices[i][1];
589 /***********************************************************************
592 * Get the drive specified by a given path name (DOS or Unix format).
594 static int DOSFS_GetPathDrive( const char **name )
597 const char *p = *name;
599 if (*p && (p[1] == ':'))
601 drive = toupper(*p) - 'A';
604 else if (*p == '/') /* Absolute Unix path? */
606 if ((drive = DRIVE_FindDriveRoot( name )) == -1)
608 fprintf( stderr, "Warning: %s not accessible from a DOS drive\n",
610 /* Assume it really was a DOS name */
611 drive = DRIVE_GetCurrentDrive();
614 else drive = DRIVE_GetCurrentDrive();
616 if (!DRIVE_IsValid(drive))
618 DOS_ERROR( ER_InvalidDrive, EC_MediaError, SA_Abort, EL_Disk );
625 /***********************************************************************
628 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
629 * Unix name / short DOS name pair.
630 * Return FALSE if one of the path components does not exist. The last path
631 * component is only checked if 'check_last' is non-zero.
632 * The buffers pointed to by 'long_buf' and 'short_buf' must be
633 * at least MAX_PATHNAME_LEN long.
635 BOOL32 DOSFS_GetFullName( LPCSTR name, BOOL32 check_last, DOS_FULL_NAME *full )
639 char *p_l, *p_s, *root;
641 dprintf_dosfs( stddeb, "DOSFS_GetFullName: %s (last=%d)\n",
644 if ((full->drive = DOSFS_GetPathDrive( &name )) == -1) return FALSE;
645 flags = DRIVE_GetFlags( full->drive );
647 lstrcpyn32A( full->long_name, DRIVE_GetRoot( full->drive ),
648 sizeof(full->long_name) );
649 if (full->long_name[1]) root = full->long_name + strlen(full->long_name);
650 else root = full->long_name; /* root directory */
652 strcpy( full->short_name, "A:\\" );
653 full->short_name[0] += full->drive;
655 if ((*name == '\\') || (*name == '/')) /* Absolute path */
657 while ((*name == '\\') || (*name == '/')) name++;
659 else /* Relative path */
661 lstrcpyn32A( root + 1, DRIVE_GetUnixCwd( full->drive ),
662 sizeof(full->long_name) - (root - full->long_name) - 1 );
663 if (root[1]) *root = '/';
664 lstrcpyn32A( full->short_name + 3, DRIVE_GetDosCwd( full->drive ),
665 sizeof(full->short_name) - 3 );
668 p_l = full->long_name[1] ? full->long_name + strlen(full->long_name)
670 p_s = full->short_name[3] ? full->short_name + strlen(full->short_name)
671 : full->short_name + 2;
674 while (*name && found)
676 /* Check for '.' and '..' */
680 if (IS_END_OF_NAME(name[1]))
683 while ((*name == '\\') || (*name == '/')) name++;
686 else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
689 while ((*name == '\\') || (*name == '/')) name++;
690 while ((p_l > root) && (*p_l != '/')) p_l--;
691 while ((p_s > full->short_name + 2) && (*p_s != '\\')) p_s--;
692 *p_l = *p_s = '\0'; /* Remove trailing separator */
697 /* Make sure buffers are large enough */
699 if ((p_s >= full->short_name + sizeof(full->short_name) - 14) ||
700 (p_l >= full->long_name + sizeof(full->long_name) - 1))
702 DOS_ERROR( ER_PathNotFound, EC_NotFound, SA_Abort, EL_Disk );
706 /* Get the long and short name matching the file name */
708 if ((found = DOSFS_FindUnixName( full->long_name, name, p_l + 1,
709 sizeof(full->long_name) - (p_l - full->long_name) - 1,
710 p_s + 1, !(flags & DRIVE_CASE_SENSITIVE) )))
716 while (!IS_END_OF_NAME(*name)) name++;
718 else if (!check_last)
722 while (!IS_END_OF_NAME(*name) &&
723 (p_s < full->short_name + sizeof(full->short_name) - 1) &&
724 (p_l < full->long_name + sizeof(full->long_name) - 1))
726 *p_l++ = *p_s++ = tolower(*name);
731 while ((*name == '\\') || (*name == '/')) name++;
738 DOS_ERROR( ER_FileNotFound, EC_NotFound, SA_Abort, EL_Disk );
741 if (*name) /* Not last */
743 DOS_ERROR( ER_PathNotFound, EC_NotFound, SA_Abort, EL_Disk );
747 if (!full->long_name[0]) strcpy( full->long_name, "/" );
748 if (!full->short_name[2]) strcpy( full->short_name + 2, "\\" );
749 dprintf_dosfs( stddeb, "DOSFS_GetFullName: returning %s = %s\n",
750 full->long_name, full->short_name );
755 /***********************************************************************
756 * GetShortPathName32A (KERNEL32.271)
758 DWORD GetShortPathName32A( LPCSTR longpath, LPSTR shortpath, DWORD shortlen )
760 DOS_FULL_NAME full_name;
762 /* FIXME: is it correct to always return a fully qualified short path? */
763 if (!DOSFS_GetFullName( longpath, TRUE, &full_name )) return 0;
764 lstrcpyn32A( shortpath, full_name.short_name, shortlen );
765 return strlen( full_name.short_name );
769 /***********************************************************************
770 * GetShortPathName32W (KERNEL32.272)
772 DWORD GetShortPathName32W( LPCWSTR longpath, LPWSTR shortpath, DWORD shortlen )
774 DOS_FULL_NAME full_name;
776 LPSTR longpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, longpath );
778 /* FIXME: is it correct to always return a fully qualified short path? */
779 if (DOSFS_GetFullName( longpathA, TRUE, &full_name ))
781 ret = strlen( full_name.short_name );
782 lstrcpynAtoW( shortpath, full_name.short_name, shortlen );
784 HeapFree( GetProcessHeap(), 0, longpathA );
789 /***********************************************************************
790 * DOSFS_DoGetFullPathName
792 * Implementation of GetFullPathName32A/W.
794 static DWORD DOSFS_DoGetFullPathName( LPCSTR name, DWORD len, LPSTR result,
797 char buffer[MAX_PATHNAME_LEN];
801 dprintf_dosfs( stddeb, "GetFullPathName: converting %s\n", name );
803 if ((drive = DOSFS_GetPathDrive( &name )) == -1) return 0;
807 if (IS_END_OF_NAME(*name)) /* Absolute path */
809 while ((*name == '\\') || (*name == '/')) name++;
811 else /* Relative path */
814 lstrcpyn32A( p, DRIVE_GetDosCwd(drive), sizeof(buffer) - 3 );
815 if (*p) p += strlen(p); else p--;
823 if (IS_END_OF_NAME(name[1]))
826 while ((*name == '\\') || (*name == '/')) name++;
829 else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
832 while ((*name == '\\') || (*name == '/')) name++;
833 while ((p > buffer + 2) && (*p != '\\')) p--;
834 *p = '\0'; /* Remove trailing separator */
838 if (p >= buffer + sizeof(buffer) - 1)
840 DOS_ERROR( ER_PathNotFound, EC_NotFound, SA_Abort, EL_Disk );
844 while (!IS_END_OF_NAME(*name) && (p < buffer + sizeof(buffer) - 1))
847 while ((*name == '\\') || (*name == '/')) name++;
855 if (!(DRIVE_GetFlags(drive) & DRIVE_CASE_PRESERVING))
856 CharUpper32A( buffer );
858 if (unicode) lstrcpynAtoW( (LPWSTR)result, buffer, len );
859 else lstrcpyn32A( result, buffer, len );
861 dprintf_dosfs( stddeb, "GetFullPathName: returning %s\n", buffer );
862 return strlen(buffer);
866 /***********************************************************************
867 * GetFullPathName32A (KERNEL32.272)
869 DWORD GetFullPathName32A(LPCSTR name, DWORD len, LPSTR buffer, LPSTR *lastpart)
871 DWORD ret = DOSFS_DoGetFullPathName( name, len, buffer, FALSE );
874 LPSTR p = buffer + strlen(buffer);
875 while ((p > buffer + 2) && (*p != '\\')) p--;
882 /***********************************************************************
883 * GetFullPathName32W (KERNEL32.273)
885 DWORD GetFullPathName32W( LPCWSTR name, DWORD len, LPWSTR buffer,
888 LPSTR nameA = HEAP_strdupWtoA( GetProcessHeap(), 0, name );
889 DWORD ret = DOSFS_DoGetFullPathName( nameA, len, (LPSTR)buffer, TRUE );
890 HeapFree( GetProcessHeap(), 0, nameA );
893 LPWSTR p = buffer + lstrlen32W(buffer);
894 while ((p > buffer + 2) && (*p != '\\')) p--;
901 /***********************************************************************
904 * Find the next matching file. Return the number of entries read to find
905 * the matching one, or 0 if no more entries.
906 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
907 * file name mask. Either or both can be NULL.
909 int DOSFS_FindNext( const char *path, const char *short_mask,
910 const char *long_mask, int drive, BYTE attr,
911 int skip, WIN32_FIND_DATA32A *entry )
913 static DOS_DIR *dir = NULL;
915 static char buffer[MAX_PATHNAME_LEN];
916 static int cur_pos = 0;
917 static int drive_root = 0;
920 LPCSTR long_name, short_name;
922 BY_HANDLE_FILE_INFORMATION info;
924 if ((attr & ~(FA_UNUSED | FA_ARCHIVE | FA_RDONLY)) == FA_LABEL)
927 entry->dwFileAttributes = FILE_ATTRIBUTE_LABEL;
928 DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftCreationTime, 0 );
929 DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftLastAccessTime, 0 );
930 DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftLastWriteTime, 0 );
931 entry->nFileSizeHigh = 0;
932 entry->nFileSizeLow = 0;
933 entry->dwReserved0 = 0;
934 entry->dwReserved1 = 0;
935 DOSFS_ToDosDTAFormat( DRIVE_GetLabel( drive ), entry->cFileName );
936 strcpy( entry->cAlternateFileName, entry->cFileName );
940 /* Check the cached directory */
941 if (dir && !strcmp( buffer, path ) && (cur_pos <= skip)) skip -= cur_pos;
942 else /* Not in the cache, open it anew */
944 const char *drive_path;
945 dprintf_dosfs( stddeb, "DOSFS_FindNext: cache miss, path=%s skip=%d buf=%s cur=%d\n",
946 path, skip, buffer, cur_pos );
948 if (dir) DOSFS_CloseDir(dir);
949 if (!*path) path = "/";
950 if (!(dir = DOSFS_OpenDir(path))) return 0;
953 if (DRIVE_FindDriveRoot( &drive_path ) != -1)
955 while ((*drive_path == '/') || (*drive_path == '\\')) drive_path++;
956 if (!*drive_path) drive_root = 1;
958 dprintf_dosfs(stddeb, "DOSFS_FindNext: drive_root = %d\n", drive_root);
959 lstrcpyn32A( buffer, path, sizeof(buffer) - 1 );
962 strcat( buffer, "/" );
963 p = buffer + strlen(buffer);
964 attr |= FA_UNUSED | FA_ARCHIVE | FA_RDONLY;
965 flags = DRIVE_GetFlags( drive );
967 while (DOSFS_ReadDir( dir, &long_name, &short_name ))
969 if (skip-- > 0) continue;
972 /* Don't return '.' and '..' in the root of the drive */
973 if (drive_root && (long_name[0] == '.') &&
974 (!long_name[1] || ((long_name[1] == '.') && !long_name[2])))
977 /* Check the long mask */
981 if (!DOSFS_MatchLong( long_mask, long_name,
982 flags & DRIVE_CASE_SENSITIVE )) continue;
985 /* Check the short mask */
991 DOSFS_Hash( long_name, dos_name, TRUE,
992 !(flags & DRIVE_CASE_SENSITIVE) );
993 short_name = dos_name;
995 if (!DOSFS_MatchShort( short_mask, short_name )) continue;
998 /* Check the file attributes */
1000 lstrcpyn32A( p, long_name, sizeof(buffer) - (int)(p - buffer) );
1001 if (!FILE_Stat( buffer, &info ))
1003 fprintf( stderr, "DOSFS_FindNext: can't stat %s\n", buffer );
1006 if (info.dwFileAttributes & ~attr) continue;
1008 /* We now have a matching entry; fill the result and return */
1010 entry->dwFileAttributes = info.dwFileAttributes;
1011 entry->ftCreationTime = info.ftCreationTime;
1012 entry->ftLastAccessTime = info.ftLastAccessTime;
1013 entry->ftLastWriteTime = info.ftLastWriteTime;
1014 entry->nFileSizeHigh = info.nFileSizeHigh;
1015 entry->nFileSizeLow = info.nFileSizeLow;
1018 DOSFS_ToDosDTAFormat( short_name, entry->cAlternateFileName );
1020 DOSFS_Hash( long_name, entry->cAlternateFileName, FALSE,
1021 !(flags & DRIVE_CASE_SENSITIVE) );
1023 lstrcpyn32A( entry->cFileName, long_name, sizeof(entry->cFileName) );
1024 if (!(flags & DRIVE_CASE_PRESERVING)) CharLower32A( entry->cFileName );
1025 dprintf_dosfs( stddeb, "DOSFS_FindNext: returning %s (%s) %02lx %ld\n",
1026 entry->cFileName, entry->cAlternateFileName,
1027 entry->dwFileAttributes, entry->nFileSizeLow );
1029 p[-1] = '\0'; /* Remove trailing slash in buffer */
1032 DOSFS_CloseDir( dir );
1034 return 0; /* End of directory */
1038 /*************************************************************************
1039 * FindFirstFile16 (KERNEL.413)
1041 HANDLE16 FindFirstFile16( LPCSTR path, WIN32_FIND_DATA32A *data )
1043 DOS_FULL_NAME full_name;
1045 FIND_FIRST_INFO *info;
1047 if (!path) return 0;
1048 if (!DOSFS_GetFullName( path, FALSE, &full_name ))
1049 return INVALID_HANDLE_VALUE16;
1050 if (!(handle = GlobalAlloc16( GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO) )))
1051 return INVALID_HANDLE_VALUE16;
1052 info = (FIND_FIRST_INFO *)GlobalLock16( handle );
1053 info->path = HEAP_strdupA( SystemHeap, 0, full_name.long_name );
1054 info->mask = strrchr( info->path, '/' );
1055 *(info->mask++) = '\0';
1056 if (path[0] && (path[1] == ':')) info->drive = toupper(*path) - 'A';
1057 else info->drive = DRIVE_GetCurrentDrive();
1059 GlobalUnlock16( handle );
1060 if (!FindNextFile16( handle, data ))
1062 FindClose16( handle );
1063 DOS_ERROR( ER_NoMoreFiles, EC_MediaError, SA_Abort, EL_Disk );
1064 return INVALID_HANDLE_VALUE16;
1070 /*************************************************************************
1071 * FindFirstFile32A (KERNEL32.123)
1073 HANDLE32 FindFirstFile32A( LPCSTR path, WIN32_FIND_DATA32A *data )
1075 HANDLE32 handle = FindFirstFile16( path, data );
1076 if (handle == INVALID_HANDLE_VALUE16) return INVALID_HANDLE_VALUE32;
1081 /*************************************************************************
1082 * FindFirstFile32W (KERNEL32.124)
1084 HANDLE32 FindFirstFile32W( LPCWSTR path, WIN32_FIND_DATA32W *data )
1086 WIN32_FIND_DATA32A dataA;
1087 LPSTR pathA = HEAP_strdupWtoA( GetProcessHeap(), 0, path );
1088 HANDLE32 handle = FindFirstFile32A( pathA, &dataA );
1089 HeapFree( GetProcessHeap(), 0, pathA );
1090 if (handle != INVALID_HANDLE_VALUE32)
1092 data->dwFileAttributes = dataA.dwFileAttributes;
1093 data->ftCreationTime = dataA.ftCreationTime;
1094 data->ftLastAccessTime = dataA.ftLastAccessTime;
1095 data->ftLastWriteTime = dataA.ftLastWriteTime;
1096 data->nFileSizeHigh = dataA.nFileSizeHigh;
1097 data->nFileSizeLow = dataA.nFileSizeLow;
1098 lstrcpyAtoW( data->cFileName, dataA.cFileName );
1099 lstrcpyAtoW( data->cAlternateFileName, dataA.cAlternateFileName );
1105 /*************************************************************************
1106 * FindNextFile16 (KERNEL.414)
1108 BOOL16 FindNextFile16( HANDLE16 handle, WIN32_FIND_DATA32A *data )
1110 FIND_FIRST_INFO *info;
1113 if (!(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
1115 DOS_ERROR( ER_InvalidHandle, EC_ProgramError, SA_Abort, EL_Disk );
1118 GlobalUnlock16( handle );
1121 DOS_ERROR( ER_NoMoreFiles, EC_MediaError, SA_Abort, EL_Disk );
1124 if (!(count = DOSFS_FindNext( info->path, NULL, info->mask, info->drive,
1125 0xff, info->skip, data )))
1127 HeapFree( SystemHeap, 0, info->path );
1128 info->path = info->mask = NULL;
1129 DOS_ERROR( ER_NoMoreFiles, EC_MediaError, SA_Abort, EL_Disk );
1132 info->skip += count;
1137 /*************************************************************************
1138 * FindNextFile32A (KERNEL32.126)
1140 BOOL32 FindNextFile32A( HANDLE32 handle, WIN32_FIND_DATA32A *data )
1142 return FindNextFile16( handle, data );
1146 /*************************************************************************
1147 * FindNextFile32W (KERNEL32.127)
1149 BOOL32 FindNextFile32W( HANDLE32 handle, WIN32_FIND_DATA32W *data )
1151 WIN32_FIND_DATA32A dataA;
1152 if (!FindNextFile32A( handle, &dataA )) return FALSE;
1153 data->dwFileAttributes = dataA.dwFileAttributes;
1154 data->ftCreationTime = dataA.ftCreationTime;
1155 data->ftLastAccessTime = dataA.ftLastAccessTime;
1156 data->ftLastWriteTime = dataA.ftLastWriteTime;
1157 data->nFileSizeHigh = dataA.nFileSizeHigh;
1158 data->nFileSizeLow = dataA.nFileSizeLow;
1159 lstrcpyAtoW( data->cFileName, dataA.cFileName );
1160 lstrcpyAtoW( data->cAlternateFileName, dataA.cAlternateFileName );
1165 /*************************************************************************
1166 * FindClose16 (KERNEL.415)
1168 BOOL16 FindClose16( HANDLE16 handle )
1170 FIND_FIRST_INFO *info;
1172 if ((handle == INVALID_HANDLE_VALUE16) ||
1173 !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
1175 DOS_ERROR( ER_InvalidHandle, EC_ProgramError, SA_Abort, EL_Disk );
1178 if (info->path) HeapFree( SystemHeap, 0, info->path );
1179 GlobalUnlock16( handle );
1180 GlobalFree16( handle );
1185 /*************************************************************************
1186 * FindClose32 (KERNEL32.119)
1188 BOOL32 FindClose32( HANDLE32 handle )
1190 return FindClose16( (HANDLE16)handle );
1194 /***********************************************************************
1195 * DOSFS_UnixTimeToFileTime
1197 * Convert a Unix time to FILETIME format.
1198 * The FILETIME structure is a 64-bit value representing the number of
1199 * 100-nanosecond intervals since January 1, 1601.
1200 * 'remainder' is the fraction of 100-ns intervals smaller than 1 second
1201 * that couldn't be stored in the time_t value.
1203 void DOSFS_UnixTimeToFileTime( time_t unix_time, FILETIME *filetime,
1207 filetime->dwLowDateTime = unix_time;
1208 filetime->dwHighDateTime = 0;
1212 /***********************************************************************
1213 * DOSFS_FileTimeToUnixTime
1215 * Convert a FILETIME format to Unix time.
1216 * If not NULL, 'remainder' contains the fractional part of the filetime.
1218 time_t DOSFS_FileTimeToUnixTime( const FILETIME *filetime, DWORD *remainder )
1221 if (remainder) *remainder = 0;
1222 return filetime->dwLowDateTime;
1226 /***********************************************************************
1227 * DosDateTimeToFileTime (KERNEL32.76)
1229 BOOL32 DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft )
1233 newtm.tm_sec = (fattime & 0x1f) * 2;
1234 newtm.tm_min = (fattime >> 5) & 0x3f;
1235 newtm.tm_hour = (fattime >> 11);
1236 newtm.tm_mday = (fatdate & 0x1f);
1237 newtm.tm_mon = ((fatdate >> 5) & 0x0f) - 1;
1238 newtm.tm_year = (fatdate >> 9) + 80;
1239 DOSFS_UnixTimeToFileTime( mktime( &newtm ), ft, 0 );
1244 /***********************************************************************
1245 * FileTimeToDosDateTime (KERNEL32.111)
1247 BOOL32 FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate,
1250 time_t unixtime = DOSFS_FileTimeToUnixTime( ft, NULL );
1251 struct tm *tm = localtime( &unixtime );
1253 *fattime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
1255 *fatdate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
1261 /***********************************************************************
1262 * LocalFileTimeToFileTime (KERNEL32.373)
1264 BOOL32 LocalFileTimeToFileTime( const FILETIME *localft, LPFILETIME utcft )
1269 /* convert from local to UTC. Perhaps not correct. FIXME */
1270 time_t unixtime = DOSFS_FileTimeToUnixTime( localft, &remainder );
1271 xtm = gmtime( &unixtime );
1272 DOSFS_UnixTimeToFileTime( mktime(xtm), utcft, remainder );
1277 /***********************************************************************
1278 * FileTimeToLocalFileTime (KERNEL32.112)
1280 BOOL32 FileTimeToLocalFileTime( const FILETIME *utcft, LPFILETIME localft )
1285 /* convert from UTC to local. Perhaps not correct. FIXME */
1286 time_t unixtime = DOSFS_FileTimeToUnixTime( utcft, &remainder );
1287 xtm = localtime( &unixtime );
1288 DOSFS_UnixTimeToFileTime( mktime(xtm), localft, remainder );
1293 /***********************************************************************
1294 * FileTimeToSystemTime (KERNEL32.113)
1296 BOOL32 FileTimeToSystemTime( const FILETIME *ft, LPSYSTEMTIME syst )
1300 time_t xtime = DOSFS_FileTimeToUnixTime( ft, &remainder );
1301 xtm = gmtime(&xtime);
1302 syst->wYear = xtm->tm_year;
1303 syst->wMonth = xtm->tm_mon;
1304 syst->wDayOfWeek = xtm->tm_wday;
1305 syst->wDay = xtm->tm_mday;
1306 syst->wHour = xtm->tm_hour;
1307 syst->wMinute = xtm->tm_min;
1308 syst->wSecond = xtm->tm_sec;
1309 syst->wMilliseconds = remainder / 10000;
1313 /***********************************************************************
1314 * QueryDosDeviceA (KERNEL32.413)
1316 * returns array of strings terminated by \0, terminated by \0
1319 QueryDosDevice32A(LPCSTR devname,LPSTR target,DWORD bufsize)
1324 dprintf_dosfs(stddeb,"QueryDosDevice(%s,...)\n",devname?devname:"<null>");
1326 /* return known MSDOS devices */
1327 lstrcpy32A(buffer,"CON COM1 COM2 LPT1 NUL ");
1328 while ((s=strchr(buffer,' ')))
1331 lstrcpyn32A(target,buffer,bufsize);
1332 return strlen(buffer);
1334 lstrcpy32A(buffer,"\\DEV\\");
1335 lstrcat32A(buffer,devname);
1336 if ((s=strchr(buffer,':'))) *s='\0';
1337 lstrcpyn32A(target,buffer,bufsize);
1338 return strlen(buffer);
1342 /***********************************************************************
1343 * QueryDosDeviceW (KERNEL32.414)
1345 * returns array of strings terminated by \0, terminated by \0
1348 QueryDosDevice32W(LPCWSTR devname,LPWSTR target,DWORD bufsize)
1350 LPSTR devnameA = devname?HEAP_strdupWtoA(GetProcessHeap(),0,devname):NULL;
1351 LPSTR targetA = (LPSTR)HEAP_xalloc(GetProcessHeap(),0,bufsize);
1352 DWORD ret = QueryDosDevice32A(devnameA,targetA,bufsize);
1354 lstrcpynAtoW(target,targetA,bufsize);
1355 if (devnameA) HeapFree(GetProcessHeap(),0,devnameA);
1356 if (targetA) HeapFree(GetProcessHeap(),0,targetA);
1361 /***********************************************************************
1362 * SystemTimeToFileTime (KERNEL32.526)
1364 BOOL32 SystemTimeToFileTime( const SYSTEMTIME *syst, LPFILETIME ft )
1368 xtm.tm_year = syst->wYear;
1369 xtm.tm_mon = syst->wMonth;
1370 xtm.tm_wday = syst->wDayOfWeek;
1371 xtm.tm_mday = syst->wDay;
1372 xtm.tm_hour = syst->wHour;
1373 xtm.tm_min = syst->wMinute;
1374 xtm.tm_sec = syst->wSecond;
1375 DOSFS_UnixTimeToFileTime( mktime(&xtm), ft, syst->wMilliseconds * 10000 );