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)
36 /* We want the real kernel dirent structure, not the libc one */
41 unsigned short d_reclen;
46 #undef VFAT_IOCTL_READDIR_BOTH /* just in case... */
49 /* Chars we don't want to see in DOS file names */
50 #define INVALID_DOS_CHARS "*?<>|\"+=,;[] \345"
52 static const char *DOSFS_Devices[][2] =
56 { "NUL", "/dev/null" },
69 #define GET_DRIVE(path) \
70 (((path)[1] == ':') ? toupper((path)[0]) - 'A' : DOSFS_CurDrive)
72 /* DOS extended error status */
73 WORD DOS_ExtendedError;
78 /* Info structure for FindFirstFile handle */
88 /* Directory info for DOSFS_ReadDir */
92 #ifdef VFAT_IOCTL_READDIR_BOTH
95 KERNEL_DIRENT dirent[2];
100 /***********************************************************************
103 * Return 1 if Unix file 'name' is also a valid MS-DOS name
104 * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
105 * File name can be terminated by '\0', '\\' or '/'.
107 static int DOSFS_ValidDOSName( const char *name, int ignore_case )
109 static const char invalid_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS;
110 const char *p = name;
111 const char *invalid = ignore_case ? (invalid_chars + 26) : invalid_chars;
116 /* Check for "." and ".." */
119 /* All other names beginning with '.' are invalid */
120 return (IS_END_OF_NAME(*p));
122 while (!IS_END_OF_NAME(*p))
124 if (strchr( invalid, *p )) return 0; /* Invalid char */
125 if (*p == '.') break; /* Start of the extension */
126 if (++len > 8) return 0; /* Name too long */
129 if (*p != '.') return 1; /* End of name */
131 if (IS_END_OF_NAME(*p)) return 0; /* Empty extension not allowed */
133 while (!IS_END_OF_NAME(*p))
135 if (strchr( invalid, *p )) return 0; /* Invalid char */
136 if (*p == '.') return 0; /* Second extension not allowed */
137 if (++len > 3) return 0; /* Extension too long */
144 /***********************************************************************
145 * DOSFS_ToDosFCBFormat
147 * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
148 * expanding wild cards and converting to upper-case in the process.
149 * File name can be terminated by '\0', '\\' or '/'.
150 * Return FALSE if the name is not a valid DOS name.
151 * 'buffer' must be at least 12 characters long.
153 BOOL32 DOSFS_ToDosFCBFormat( LPCSTR name, LPSTR buffer )
155 static const char invalid_chars[] = INVALID_DOS_CHARS;
156 const char *p = name;
159 /* Check for "." and ".." */
163 strcpy( buffer, ". " );
169 return (!*p || (*p == '/') || (*p == '\\'));
172 for (i = 0; i < 8; i++)
189 if (strchr( invalid_chars, *p )) return FALSE;
190 buffer[i] = toupper(*p);
198 /* Skip all chars after wildcard up to first dot */
199 while (*p && (*p != '/') && (*p != '\\') && (*p != '.')) p++;
203 /* Check if name too long */
204 if (*p && (*p != '/') && (*p != '\\') && (*p != '.')) return FALSE;
206 if (*p == '.') p++; /* Skip dot */
208 for (i = 8; i < 11; i++)
218 return FALSE; /* Second extension not allowed */
226 if (strchr( invalid_chars, *p )) return FALSE;
227 buffer[i] = toupper(*p);
237 /***********************************************************************
238 * DOSFS_ToDosDTAFormat
240 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
241 * converting to upper-case in the process.
242 * File name can be terminated by '\0', '\\' or '/'.
243 * 'buffer' must be at least 13 characters long.
245 static void DOSFS_ToDosDTAFormat( LPCSTR name, LPSTR buffer )
249 memcpy( buffer, name, 8 );
250 for (p = buffer + 8; (p > buffer) && (p[-1] == ' '); p--);
252 memcpy( p, name + 8, 3 );
253 for (p += 3; p[-1] == ' '; p--);
254 if (p[-1] == '.') p--;
259 /***********************************************************************
262 * Check a DOS file name against a mask (both in FCB format).
264 static int DOSFS_MatchShort( const char *mask, const char *name )
267 for (i = 11; i > 0; i--, mask++, name++)
268 if ((*mask != '?') && (*mask != *name)) return 0;
273 /***********************************************************************
276 * Check a long file name against a mask.
278 static int DOSFS_MatchLong( const char *mask, const char *name,
281 if (!strcmp( mask, "*.*" )) return 1;
282 while (*name && *mask)
287 while (*mask == '*') mask++; /* Skip consecutive '*' */
288 if (!*mask) return 1;
289 if (case_sensitive) while (*name && (*name != *mask)) name++;
290 else while (*name && (toupper(*name) != toupper(*mask))) name++;
291 if (!*name) return 0;
293 else if (*mask != '?')
297 if (*mask != *name) return 0;
299 else if (toupper(*mask) != toupper(*name)) return 0;
304 return (!*name && !*mask);
308 /***********************************************************************
311 static DOS_DIR *DOSFS_OpenDir( LPCSTR path )
313 DOS_DIR *dir = HeapAlloc( SystemHeap, 0, sizeof(*dir) );
316 DOS_ERROR( ER_OutOfMemory, EC_OutOfResource, SA_Abort, EL_Memory );
320 #ifdef VFAT_IOCTL_READDIR_BOTH
322 /* Check if the VFAT ioctl is supported on this directory */
324 if ((dir->fd = open( path, O_RDONLY )) != -1)
326 if (ioctl( dir->fd, VFAT_IOCTL_READDIR_BOTH, (long)dir->dirent ) == -1)
333 /* Set the file pointer back at the start of the directory */
334 lseek( dir->fd, 0, SEEK_SET );
339 #endif /* VFAT_IOCTL_READDIR_BOTH */
341 /* Now use the standard opendir/readdir interface */
343 if (!(dir->dir = opendir( path )))
345 HeapFree( SystemHeap, 0, dir );
352 /***********************************************************************
355 static void DOSFS_CloseDir( DOS_DIR *dir )
357 #ifdef VFAT_IOCTL_READDIR_BOTH
358 if (dir->fd != -1) close( dir->fd );
359 #endif /* VFAT_IOCTL_READDIR_BOTH */
360 if (dir->dir) closedir( dir->dir );
361 HeapFree( SystemHeap, 0, dir );
365 /***********************************************************************
368 static BOOL32 DOSFS_ReadDir( DOS_DIR *dir, LPCSTR *long_name,
371 struct dirent *dirent;
373 #ifdef VFAT_IOCTL_READDIR_BOTH
376 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;
386 #endif /* VFAT_IOCTL_READDIR_BOTH */
388 if (!(dirent = readdir( dir->dir ))) return FALSE;
389 *long_name = dirent->d_name;
395 /***********************************************************************
398 * Transform a Unix file name into a hashed DOS name. If the name is a valid
399 * DOS name, it is converted to upper-case; otherwise it is replaced by a
400 * hashed version that fits in 8.3 format.
401 * File name can be terminated by '\0', '\\' or '/'.
402 * 'buffer' must be at least 13 characters long.
404 static void DOSFS_Hash( LPCSTR name, LPSTR buffer, BOOL32 dir_format,
407 static const char invalid_chars[] = INVALID_DOS_CHARS "~.";
408 static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
415 if (dir_format) strcpy( buffer, " " );
417 if (DOSFS_ValidDOSName( name, ignore_case ))
419 /* Check for '.' and '..' */
423 if (!dir_format) buffer[1] = buffer[2] = '\0';
424 if (name[1] == '.') buffer[1] = '.';
428 /* Simply copy the name, converting to uppercase */
430 for (dst = buffer; !IS_END_OF_NAME(*name) && (*name != '.'); name++)
431 *dst++ = toupper(*name);
434 if (dir_format) dst = buffer + 8;
436 for (name++; !IS_END_OF_NAME(*name); name++)
437 *dst++ = toupper(*name);
439 if (!dir_format) *dst = '\0';
443 /* Compute the hash code of the file name */
444 /* If you know something about hash functions, feel free to */
445 /* insert a better algorithm here... */
448 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
449 hash = (hash<<3) ^ (hash>>5) ^ tolower(*p) ^ (tolower(p[1]) << 8);
450 hash = (hash<<3) ^ (hash>>5) ^ tolower(*p); /* Last character*/
454 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
455 hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
456 hash = (hash << 3) ^ (hash >> 5) ^ *p; /* Last character */
459 /* Find last dot for start of the extension */
460 for (p = name+1, ext = NULL; !IS_END_OF_NAME(*p); p++)
461 if (*p == '.') ext = p;
462 if (ext && IS_END_OF_NAME(ext[1]))
463 ext = NULL; /* Empty extension ignored */
465 /* Copy first 4 chars, replacing invalid chars with '_' */
466 for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
468 if (IS_END_OF_NAME(*p) || (p == ext)) break;
469 *dst++ = strchr( invalid_chars, *p ) ? '_' : toupper(*p);
471 /* Pad to 5 chars with '~' */
472 while (i-- >= 0) *dst++ = '~';
474 /* Insert hash code converted to 3 ASCII chars */
475 *dst++ = hash_chars[(hash >> 10) & 0x1f];
476 *dst++ = hash_chars[(hash >> 5) & 0x1f];
477 *dst++ = hash_chars[hash & 0x1f];
479 /* Copy the first 3 chars of the extension (if any) */
482 if (!dir_format) *dst++ = '.';
483 for (i = 3, ext++; (i > 0) && !IS_END_OF_NAME(*ext); i--, ext++)
484 *dst++ = strchr( invalid_chars, *ext ) ? '_' : toupper(*ext);
486 if (!dir_format) *dst = '\0';
490 /***********************************************************************
493 * Find the Unix file name in a given directory that corresponds to
494 * a file name (either in Unix or DOS format).
495 * File name can be terminated by '\0', '\\' or '/'.
496 * Return TRUE if OK, FALSE if no file name matches.
498 * 'long_buf' must be at least 'long_len' characters long. If the long name
499 * turns out to be larger than that, the function returns FALSE.
500 * 'short_buf' must be at least 13 characters long.
502 BOOL32 DOSFS_FindUnixName( LPCSTR path, LPCSTR name, LPSTR long_buf,
503 INT32 long_len, LPSTR short_buf, BOOL32 ignore_case)
506 LPCSTR long_name, short_name;
507 char dos_name[12], tmp_buf[13];
510 const char *p = strchr( name, '/' );
511 int len = p ? (int)(p - name) : strlen(name);
512 if ((p = strchr( name, '\\' ))) len = MIN( (int)(p - name), len );
513 if (long_len < len + 1) return FALSE;
515 dprintf_dosfs( stddeb, "DOSFS_FindUnixName: %s,%s\n", path, name );
517 if (!DOSFS_ToDosFCBFormat( name, dos_name )) dos_name[0] = '\0';
519 if (!(dir = DOSFS_OpenDir( path )))
521 dprintf_dosfs( stddeb, "DOSFS_FindUnixName(%s,%s): can't open dir\n",
526 while ((ret = DOSFS_ReadDir( dir, &long_name, &short_name )))
528 /* Check against Unix name */
529 if (len == strlen(long_name))
533 if (!lstrncmp32A( long_name, name, len )) break;
537 if (!lstrncmpi32A( long_name, name, len )) break;
542 /* Check against hashed DOS name */
545 DOSFS_Hash( long_name, tmp_buf, TRUE, ignore_case );
546 short_name = tmp_buf;
548 if (!strcmp( dos_name, short_name )) break;
553 if (long_buf) strcpy( long_buf, long_name );
557 DOSFS_ToDosDTAFormat( short_name, short_buf );
559 DOSFS_Hash( long_name, short_buf, FALSE, ignore_case );
561 dprintf_dosfs( stddeb, "DOSFS_FindUnixName(%s,%s) -> %s (%s)\n",
562 path, name, long_name, short_buf ? short_buf : "***");
565 dprintf_dosfs(stddeb,"DOSFS_FindUnixName(%s,%s) -> ** Not found **\n",
567 DOSFS_CloseDir( dir );
572 /***********************************************************************
575 * Check if a DOS file name represents a DOS device. Returns the name
576 * of the associated Unix device, or NULL if not found.
578 const char *DOSFS_IsDevice( const char *name )
583 if (name[0] && (name[1] == ':')) name += 2;
584 if ((p = strrchr( name, '/' ))) name = p + 1;
585 if ((p = strrchr( name, '\\' ))) name = p + 1;
586 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
588 const char *dev = DOSFS_Devices[i][0];
589 if (!lstrncmpi32A( dev, name, strlen(dev) ))
591 p = name + strlen( dev );
592 if (!*p || (*p == '.')) return DOSFS_Devices[i][1];
598 /***********************************************************************
601 * Get the drive specified by a given path name (DOS or Unix format).
603 static int DOSFS_GetPathDrive( const char **name )
606 const char *p = *name;
608 if (*p && (p[1] == ':'))
610 drive = toupper(*p) - 'A';
613 else if (*p == '/') /* Absolute Unix path? */
615 if ((drive = DRIVE_FindDriveRoot( name )) == -1)
617 fprintf( stderr, "Warning: %s not accessible from a DOS drive\n",
619 /* Assume it really was a DOS name */
620 drive = DRIVE_GetCurrentDrive();
623 else drive = DRIVE_GetCurrentDrive();
625 if (!DRIVE_IsValid(drive))
627 DOS_ERROR( ER_InvalidDrive, EC_MediaError, SA_Abort, EL_Disk );
634 /***********************************************************************
637 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
638 * Unix name / short DOS name pair.
639 * Return FALSE if one of the path components does not exist. The last path
640 * component is only checked if 'check_last' is non-zero.
641 * The buffers pointed to by 'long_buf' and 'short_buf' must be
642 * at least MAX_PATHNAME_LEN long.
644 BOOL32 DOSFS_GetFullName( LPCSTR name, BOOL32 check_last, DOS_FULL_NAME *full )
648 char *p_l, *p_s, *root;
650 dprintf_dosfs( stddeb, "DOSFS_GetFullName: %s (last=%d)\n",
653 if ((full->drive = DOSFS_GetPathDrive( &name )) == -1) return FALSE;
654 flags = DRIVE_GetFlags( full->drive );
656 lstrcpyn32A( full->long_name, DRIVE_GetRoot( full->drive ),
657 sizeof(full->long_name) );
658 if (full->long_name[1]) root = full->long_name + strlen(full->long_name);
659 else root = full->long_name; /* root directory */
661 strcpy( full->short_name, "A:\\" );
662 full->short_name[0] += full->drive;
664 if ((*name == '\\') || (*name == '/')) /* Absolute path */
666 while ((*name == '\\') || (*name == '/')) name++;
668 else /* Relative path */
670 lstrcpyn32A( root + 1, DRIVE_GetUnixCwd( full->drive ),
671 sizeof(full->long_name) - (root - full->long_name) - 1 );
672 if (root[1]) *root = '/';
673 lstrcpyn32A( full->short_name + 3, DRIVE_GetDosCwd( full->drive ),
674 sizeof(full->short_name) - 3 );
677 p_l = full->long_name[1] ? full->long_name + strlen(full->long_name)
679 p_s = full->short_name[3] ? full->short_name + strlen(full->short_name)
680 : full->short_name + 2;
683 while (*name && found)
685 /* Check for '.' and '..' */
689 if (IS_END_OF_NAME(name[1]))
692 while ((*name == '\\') || (*name == '/')) name++;
695 else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
698 while ((*name == '\\') || (*name == '/')) name++;
699 while ((p_l > root) && (*p_l != '/')) p_l--;
700 while ((p_s > full->short_name + 2) && (*p_s != '\\')) p_s--;
701 *p_l = *p_s = '\0'; /* Remove trailing separator */
706 /* Make sure buffers are large enough */
708 if ((p_s >= full->short_name + sizeof(full->short_name) - 14) ||
709 (p_l >= full->long_name + sizeof(full->long_name) - 1))
711 DOS_ERROR( ER_PathNotFound, EC_NotFound, SA_Abort, EL_Disk );
715 /* Get the long and short name matching the file name */
717 if ((found = DOSFS_FindUnixName( full->long_name, name, p_l + 1,
718 sizeof(full->long_name) - (p_l - full->long_name) - 1,
719 p_s + 1, !(flags & DRIVE_CASE_SENSITIVE) )))
725 while (!IS_END_OF_NAME(*name)) name++;
727 else if (!check_last)
731 while (!IS_END_OF_NAME(*name) &&
732 (p_s < full->short_name + sizeof(full->short_name) - 1) &&
733 (p_l < full->long_name + sizeof(full->long_name) - 1))
735 *p_l++ = *p_s++ = tolower(*name);
740 while ((*name == '\\') || (*name == '/')) name++;
747 DOS_ERROR( ER_FileNotFound, EC_NotFound, SA_Abort, EL_Disk );
750 if (*name) /* Not last */
752 DOS_ERROR( ER_PathNotFound, EC_NotFound, SA_Abort, EL_Disk );
756 if (!full->long_name[0]) strcpy( full->long_name, "/" );
757 if (!full->short_name[2]) strcpy( full->short_name + 2, "\\" );
758 dprintf_dosfs( stddeb, "DOSFS_GetFullName: returning %s = %s\n",
759 full->long_name, full->short_name );
764 /***********************************************************************
765 * GetShortPathName32A (KERNEL32.271)
767 DWORD GetShortPathName32A( LPCSTR longpath, LPSTR shortpath, DWORD shortlen )
769 DOS_FULL_NAME full_name;
771 /* FIXME: is it correct to always return a fully qualified short path? */
772 if (!DOSFS_GetFullName( longpath, TRUE, &full_name )) return 0;
773 lstrcpyn32A( shortpath, full_name.short_name, shortlen );
774 return strlen( full_name.short_name );
778 /***********************************************************************
779 * GetShortPathName32W (KERNEL32.272)
781 DWORD GetShortPathName32W( LPCWSTR longpath, LPWSTR shortpath, DWORD shortlen )
783 DOS_FULL_NAME full_name;
785 LPSTR longpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, longpath );
787 /* FIXME: is it correct to always return a fully qualified short path? */
788 if (DOSFS_GetFullName( longpathA, TRUE, &full_name ))
790 ret = strlen( full_name.short_name );
791 lstrcpynAtoW( shortpath, full_name.short_name, shortlen );
793 HeapFree( GetProcessHeap(), 0, longpathA );
798 /***********************************************************************
799 * DOSFS_DoGetFullPathName
801 * Implementation of GetFullPathName32A/W.
803 static DWORD DOSFS_DoGetFullPathName( LPCSTR name, DWORD len, LPSTR result,
806 char buffer[MAX_PATHNAME_LEN];
810 dprintf_dosfs( stddeb, "GetFullPathName: converting %s\n", name );
812 if (!name || !result) return 0;
814 if ((drive = DOSFS_GetPathDrive( &name )) == -1) return 0;
818 if (IS_END_OF_NAME(*name)) /* Absolute path */
820 while ((*name == '\\') || (*name == '/')) name++;
822 else /* Relative path */
825 lstrcpyn32A( p, DRIVE_GetDosCwd(drive), sizeof(buffer) - 3 );
826 if (*p) p += strlen(p); else p--;
834 if (IS_END_OF_NAME(name[1]))
837 while ((*name == '\\') || (*name == '/')) name++;
840 else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
843 while ((*name == '\\') || (*name == '/')) name++;
844 while ((p > buffer + 2) && (*p != '\\')) p--;
845 *p = '\0'; /* Remove trailing separator */
849 if (p >= buffer + sizeof(buffer) - 1)
851 DOS_ERROR( ER_PathNotFound, EC_NotFound, SA_Abort, EL_Disk );
855 while (!IS_END_OF_NAME(*name) && (p < buffer + sizeof(buffer) - 1))
858 while ((*name == '\\') || (*name == '/')) name++;
866 if (!(DRIVE_GetFlags(drive) & DRIVE_CASE_PRESERVING))
867 CharUpper32A( buffer );
869 if (unicode) lstrcpynAtoW( (LPWSTR)result, buffer, len );
870 else lstrcpyn32A( result, buffer, len );
872 dprintf_dosfs( stddeb, "GetFullPathName: returning %s\n", buffer );
873 return strlen(buffer);
877 /***********************************************************************
878 * GetFullPathName32A (KERNEL32.272)
880 DWORD GetFullPathName32A(LPCSTR name, DWORD len, LPSTR buffer, LPSTR *lastpart)
882 DWORD ret = DOSFS_DoGetFullPathName( name, len, buffer, FALSE );
885 LPSTR p = buffer + strlen(buffer);
886 while ((p > buffer + 2) && (*p != '\\')) p--;
893 /***********************************************************************
894 * GetFullPathName32W (KERNEL32.273)
896 DWORD GetFullPathName32W( LPCWSTR name, DWORD len, LPWSTR buffer,
899 LPSTR nameA = HEAP_strdupWtoA( GetProcessHeap(), 0, name );
900 DWORD ret = DOSFS_DoGetFullPathName( nameA, len, (LPSTR)buffer, TRUE );
901 HeapFree( GetProcessHeap(), 0, nameA );
904 LPWSTR p = buffer + lstrlen32W(buffer);
905 while ((p > buffer + 2) && (*p != '\\')) p--;
912 /***********************************************************************
915 * Find the next matching file. Return the number of entries read to find
916 * the matching one, or 0 if no more entries.
917 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
918 * file name mask. Either or both can be NULL.
920 int DOSFS_FindNext( const char *path, const char *short_mask,
921 const char *long_mask, int drive, BYTE attr,
922 int skip, WIN32_FIND_DATA32A *entry )
924 static DOS_DIR *dir = NULL;
926 static char buffer[MAX_PATHNAME_LEN];
927 static int cur_pos = 0;
928 static int drive_root = 0;
931 LPCSTR long_name, short_name;
933 BY_HANDLE_FILE_INFORMATION info;
935 if ((attr & ~(FA_UNUSED | FA_ARCHIVE | FA_RDONLY)) == FA_LABEL)
938 entry->dwFileAttributes = FILE_ATTRIBUTE_LABEL;
939 DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftCreationTime, 0 );
940 DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftLastAccessTime, 0 );
941 DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftLastWriteTime, 0 );
942 entry->nFileSizeHigh = 0;
943 entry->nFileSizeLow = 0;
944 entry->dwReserved0 = 0;
945 entry->dwReserved1 = 0;
946 DOSFS_ToDosDTAFormat( DRIVE_GetLabel( drive ), entry->cFileName );
947 strcpy( entry->cAlternateFileName, entry->cFileName );
951 /* Check the cached directory */
952 if (dir && !strcmp( buffer, path ) && (cur_pos <= skip)) skip -= cur_pos;
953 else /* Not in the cache, open it anew */
955 const char *drive_path;
956 dprintf_dosfs( stddeb, "DOSFS_FindNext: cache miss, path=%s skip=%d buf=%s cur=%d\n",
957 path, skip, buffer, cur_pos );
959 if (dir) DOSFS_CloseDir(dir);
960 if (!*path) path = "/";
961 if (!(dir = DOSFS_OpenDir(path))) return 0;
962 drive_path = path + strlen(DRIVE_GetRoot(drive));
963 while ((*drive_path == '/') || (*drive_path == '\\')) drive_path++;
964 drive_root = !*drive_path;
965 dprintf_dosfs(stddeb, "DOSFS_FindNext: drive_root = %d\n", drive_root);
966 lstrcpyn32A( buffer, path, sizeof(buffer) - 1 );
968 strcat( buffer, "/" );
969 p = buffer + strlen(buffer);
970 attr |= FA_UNUSED | FA_ARCHIVE | FA_RDONLY;
971 flags = DRIVE_GetFlags( drive );
973 while (DOSFS_ReadDir( dir, &long_name, &short_name ))
975 if (skip-- > 0) continue;
978 /* Don't return '.' and '..' in the root of the drive */
979 if (drive_root && (long_name[0] == '.') &&
980 (!long_name[1] || ((long_name[1] == '.') && !long_name[2])))
983 /* Check the long mask */
987 if (!DOSFS_MatchLong( long_mask, long_name,
988 flags & DRIVE_CASE_SENSITIVE )) continue;
991 /* Check the short mask */
997 DOSFS_Hash( long_name, dos_name, TRUE,
998 !(flags & DRIVE_CASE_SENSITIVE) );
999 short_name = dos_name;
1001 if (!DOSFS_MatchShort( short_mask, short_name )) continue;
1004 /* Check the file attributes */
1006 lstrcpyn32A( p, long_name, sizeof(buffer) - (int)(p - buffer) );
1007 if (!FILE_Stat( buffer, &info ))
1009 fprintf( stderr, "DOSFS_FindNext: can't stat %s\n", buffer );
1012 if (info.dwFileAttributes & ~attr) continue;
1014 /* We now have a matching entry; fill the result and return */
1016 entry->dwFileAttributes = info.dwFileAttributes;
1017 entry->ftCreationTime = info.ftCreationTime;
1018 entry->ftLastAccessTime = info.ftLastAccessTime;
1019 entry->ftLastWriteTime = info.ftLastWriteTime;
1020 entry->nFileSizeHigh = info.nFileSizeHigh;
1021 entry->nFileSizeLow = info.nFileSizeLow;
1024 DOSFS_ToDosDTAFormat( short_name, entry->cAlternateFileName );
1026 DOSFS_Hash( long_name, entry->cAlternateFileName, FALSE,
1027 !(flags & DRIVE_CASE_SENSITIVE) );
1029 lstrcpyn32A( entry->cFileName, long_name, sizeof(entry->cFileName) );
1030 if (!(flags & DRIVE_CASE_PRESERVING)) CharLower32A( entry->cFileName );
1031 dprintf_dosfs( stddeb, "DOSFS_FindNext: returning %s (%s) %02lx %ld\n",
1032 entry->cFileName, entry->cAlternateFileName,
1033 entry->dwFileAttributes, entry->nFileSizeLow );
1035 p[-1] = '\0'; /* Remove trailing slash in buffer */
1038 DOSFS_CloseDir( dir );
1040 return 0; /* End of directory */
1044 /*************************************************************************
1045 * FindFirstFile16 (KERNEL.413)
1047 HANDLE16 FindFirstFile16( LPCSTR path, WIN32_FIND_DATA32A *data )
1049 DOS_FULL_NAME full_name;
1051 FIND_FIRST_INFO *info;
1053 if (!path) return 0;
1054 if (!DOSFS_GetFullName( path, FALSE, &full_name ))
1055 return INVALID_HANDLE_VALUE16;
1056 if (!(handle = GlobalAlloc16( GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO) )))
1057 return INVALID_HANDLE_VALUE16;
1058 info = (FIND_FIRST_INFO *)GlobalLock16( handle );
1059 info->path = HEAP_strdupA( SystemHeap, 0, full_name.long_name );
1060 info->mask = strrchr( info->path, '/' );
1061 *(info->mask++) = '\0';
1062 if (path[0] && (path[1] == ':')) info->drive = toupper(*path) - 'A';
1063 else info->drive = DRIVE_GetCurrentDrive();
1065 GlobalUnlock16( handle );
1066 if (!FindNextFile16( handle, data ))
1068 FindClose16( handle );
1069 DOS_ERROR( ER_NoMoreFiles, EC_MediaError, SA_Abort, EL_Disk );
1070 return INVALID_HANDLE_VALUE16;
1076 /*************************************************************************
1077 * FindFirstFile32A (KERNEL32.123)
1079 HANDLE32 FindFirstFile32A( LPCSTR path, WIN32_FIND_DATA32A *data )
1081 HANDLE32 handle = FindFirstFile16( path, data );
1082 if (handle == INVALID_HANDLE_VALUE16) return INVALID_HANDLE_VALUE32;
1087 /*************************************************************************
1088 * FindFirstFile32W (KERNEL32.124)
1090 HANDLE32 FindFirstFile32W( LPCWSTR path, WIN32_FIND_DATA32W *data )
1092 WIN32_FIND_DATA32A dataA;
1093 LPSTR pathA = HEAP_strdupWtoA( GetProcessHeap(), 0, path );
1094 HANDLE32 handle = FindFirstFile32A( pathA, &dataA );
1095 HeapFree( GetProcessHeap(), 0, pathA );
1096 if (handle != INVALID_HANDLE_VALUE32)
1098 data->dwFileAttributes = dataA.dwFileAttributes;
1099 data->ftCreationTime = dataA.ftCreationTime;
1100 data->ftLastAccessTime = dataA.ftLastAccessTime;
1101 data->ftLastWriteTime = dataA.ftLastWriteTime;
1102 data->nFileSizeHigh = dataA.nFileSizeHigh;
1103 data->nFileSizeLow = dataA.nFileSizeLow;
1104 lstrcpyAtoW( data->cFileName, dataA.cFileName );
1105 lstrcpyAtoW( data->cAlternateFileName, dataA.cAlternateFileName );
1111 /*************************************************************************
1112 * FindNextFile16 (KERNEL.414)
1114 BOOL16 FindNextFile16( HANDLE16 handle, WIN32_FIND_DATA32A *data )
1116 FIND_FIRST_INFO *info;
1119 if (!(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
1121 DOS_ERROR( ER_InvalidHandle, EC_ProgramError, SA_Abort, EL_Disk );
1124 GlobalUnlock16( handle );
1127 DOS_ERROR( ER_NoMoreFiles, EC_MediaError, SA_Abort, EL_Disk );
1130 if (!(count = DOSFS_FindNext( info->path, NULL, info->mask, info->drive,
1131 0xff, info->skip, data )))
1133 HeapFree( SystemHeap, 0, info->path );
1134 info->path = info->mask = NULL;
1135 DOS_ERROR( ER_NoMoreFiles, EC_MediaError, SA_Abort, EL_Disk );
1138 info->skip += count;
1143 /*************************************************************************
1144 * FindNextFile32A (KERNEL32.126)
1146 BOOL32 FindNextFile32A( HANDLE32 handle, WIN32_FIND_DATA32A *data )
1148 return FindNextFile16( handle, data );
1152 /*************************************************************************
1153 * FindNextFile32W (KERNEL32.127)
1155 BOOL32 FindNextFile32W( HANDLE32 handle, WIN32_FIND_DATA32W *data )
1157 WIN32_FIND_DATA32A dataA;
1158 if (!FindNextFile32A( handle, &dataA )) return FALSE;
1159 data->dwFileAttributes = dataA.dwFileAttributes;
1160 data->ftCreationTime = dataA.ftCreationTime;
1161 data->ftLastAccessTime = dataA.ftLastAccessTime;
1162 data->ftLastWriteTime = dataA.ftLastWriteTime;
1163 data->nFileSizeHigh = dataA.nFileSizeHigh;
1164 data->nFileSizeLow = dataA.nFileSizeLow;
1165 lstrcpyAtoW( data->cFileName, dataA.cFileName );
1166 lstrcpyAtoW( data->cAlternateFileName, dataA.cAlternateFileName );
1171 /*************************************************************************
1172 * FindClose16 (KERNEL.415)
1174 BOOL16 FindClose16( HANDLE16 handle )
1176 FIND_FIRST_INFO *info;
1178 if ((handle == INVALID_HANDLE_VALUE16) ||
1179 !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
1181 DOS_ERROR( ER_InvalidHandle, EC_ProgramError, SA_Abort, EL_Disk );
1184 if (info->path) HeapFree( SystemHeap, 0, info->path );
1185 GlobalUnlock16( handle );
1186 GlobalFree16( handle );
1191 /*************************************************************************
1192 * FindClose32 (KERNEL32.119)
1194 BOOL32 FindClose32( HANDLE32 handle )
1196 return FindClose16( (HANDLE16)handle );
1200 /***********************************************************************
1201 * DOSFS_UnixTimeToFileTime
1203 * Convert a Unix time to FILETIME format.
1204 * The FILETIME structure is a 64-bit value representing the number of
1205 * 100-nanosecond intervals since January 1, 1601.
1206 * 'remainder' is the fraction of 100-ns intervals smaller than 1 second
1207 * that couldn't be stored in the time_t value.
1209 void DOSFS_UnixTimeToFileTime( time_t unix_time, FILETIME *filetime,
1213 filetime->dwLowDateTime = unix_time;
1214 filetime->dwHighDateTime = 0;
1218 /***********************************************************************
1219 * DOSFS_FileTimeToUnixTime
1221 * Convert a FILETIME format to Unix time.
1222 * If not NULL, 'remainder' contains the fractional part of the filetime.
1224 time_t DOSFS_FileTimeToUnixTime( const FILETIME *filetime, DWORD *remainder )
1227 if (remainder) *remainder = 0;
1228 return filetime->dwLowDateTime;
1232 /***********************************************************************
1233 * DosDateTimeToFileTime (KERNEL32.76)
1235 BOOL32 DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft )
1239 newtm.tm_sec = (fattime & 0x1f) * 2;
1240 newtm.tm_min = (fattime >> 5) & 0x3f;
1241 newtm.tm_hour = (fattime >> 11);
1242 newtm.tm_mday = (fatdate & 0x1f);
1243 newtm.tm_mon = ((fatdate >> 5) & 0x0f) - 1;
1244 newtm.tm_year = (fatdate >> 9) + 80;
1245 DOSFS_UnixTimeToFileTime( mktime( &newtm ), ft, 0 );
1250 /***********************************************************************
1251 * FileTimeToDosDateTime (KERNEL32.111)
1253 BOOL32 FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate,
1256 time_t unixtime = DOSFS_FileTimeToUnixTime( ft, NULL );
1257 struct tm *tm = localtime( &unixtime );
1259 *fattime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
1261 *fatdate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
1267 /***********************************************************************
1268 * LocalFileTimeToFileTime (KERNEL32.373)
1270 BOOL32 LocalFileTimeToFileTime( const FILETIME *localft, LPFILETIME utcft )
1275 /* convert from local to UTC. Perhaps not correct. FIXME */
1276 time_t unixtime = DOSFS_FileTimeToUnixTime( localft, &remainder );
1277 xtm = gmtime( &unixtime );
1278 DOSFS_UnixTimeToFileTime( mktime(xtm), utcft, remainder );
1283 /***********************************************************************
1284 * FileTimeToLocalFileTime (KERNEL32.112)
1286 BOOL32 FileTimeToLocalFileTime( const FILETIME *utcft, LPFILETIME localft )
1291 /* convert from UTC to local. Perhaps not correct. FIXME */
1292 time_t unixtime = DOSFS_FileTimeToUnixTime( utcft, &remainder );
1293 xtm = localtime( &unixtime );
1294 DOSFS_UnixTimeToFileTime( mktime(xtm), localft, remainder );
1299 /***********************************************************************
1300 * FileTimeToSystemTime (KERNEL32.113)
1302 BOOL32 FileTimeToSystemTime( const FILETIME *ft, LPSYSTEMTIME syst )
1306 time_t xtime = DOSFS_FileTimeToUnixTime( ft, &remainder );
1307 xtm = gmtime(&xtime);
1308 syst->wYear = xtm->tm_year;
1309 syst->wMonth = xtm->tm_mon;
1310 syst->wDayOfWeek = xtm->tm_wday;
1311 syst->wDay = xtm->tm_mday;
1312 syst->wHour = xtm->tm_hour;
1313 syst->wMinute = xtm->tm_min;
1314 syst->wSecond = xtm->tm_sec;
1315 syst->wMilliseconds = remainder / 10000;
1319 /***********************************************************************
1320 * QueryDosDeviceA (KERNEL32.413)
1322 * returns array of strings terminated by \0, terminated by \0
1325 QueryDosDevice32A(LPCSTR devname,LPSTR target,DWORD bufsize)
1330 dprintf_dosfs(stddeb,"QueryDosDevice(%s,...)\n",devname?devname:"<null>");
1332 /* return known MSDOS devices */
1333 lstrcpy32A(buffer,"CON COM1 COM2 LPT1 NUL ");
1334 while ((s=strchr(buffer,' ')))
1337 lstrcpyn32A(target,buffer,bufsize);
1338 return strlen(buffer);
1340 lstrcpy32A(buffer,"\\DEV\\");
1341 lstrcat32A(buffer,devname);
1342 if ((s=strchr(buffer,':'))) *s='\0';
1343 lstrcpyn32A(target,buffer,bufsize);
1344 return strlen(buffer);
1348 /***********************************************************************
1349 * QueryDosDeviceW (KERNEL32.414)
1351 * returns array of strings terminated by \0, terminated by \0
1354 QueryDosDevice32W(LPCWSTR devname,LPWSTR target,DWORD bufsize)
1356 LPSTR devnameA = devname?HEAP_strdupWtoA(GetProcessHeap(),0,devname):NULL;
1357 LPSTR targetA = (LPSTR)HEAP_xalloc(GetProcessHeap(),0,bufsize);
1358 DWORD ret = QueryDosDevice32A(devnameA,targetA,bufsize);
1360 lstrcpynAtoW(target,targetA,bufsize);
1361 if (devnameA) HeapFree(GetProcessHeap(),0,devnameA);
1362 if (targetA) HeapFree(GetProcessHeap(),0,targetA);
1367 /***********************************************************************
1368 * SystemTimeToFileTime (KERNEL32.526)
1370 BOOL32 SystemTimeToFileTime( const SYSTEMTIME *syst, LPFILETIME ft )
1374 xtm.tm_year = syst->wYear;
1375 xtm.tm_mon = syst->wMonth;
1376 xtm.tm_wday = syst->wDayOfWeek;
1377 xtm.tm_mday = syst->wDay;
1378 xtm.tm_hour = syst->wHour;
1379 xtm.tm_min = syst->wMinute;
1380 xtm.tm_sec = syst->wSecond;
1381 DOSFS_UnixTimeToFileTime( mktime(&xtm), ft, syst->wMilliseconds * 10000 );