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 WINAPI
GetShortPathName32A( LPCSTR longpath
, LPSTR shortpath
,
770 DOS_FULL_NAME full_name
;
772 /* FIXME: is it correct to always return a fully qualified short path? */
773 if (!DOSFS_GetFullName( longpath
, TRUE
, &full_name
)) return 0;
774 lstrcpyn32A( shortpath
, full_name
.short_name
, shortlen
);
775 return strlen( full_name
.short_name
);
779 /***********************************************************************
780 * GetShortPathName32W (KERNEL32.272)
782 DWORD WINAPI
GetShortPathName32W( LPCWSTR longpath
, LPWSTR shortpath
,
785 DOS_FULL_NAME full_name
;
787 LPSTR longpathA
= HEAP_strdupWtoA( GetProcessHeap(), 0, longpath
);
789 /* FIXME: is it correct to always return a fully qualified short path? */
790 if (DOSFS_GetFullName( longpathA
, TRUE
, &full_name
))
792 ret
= strlen( full_name
.short_name
);
793 lstrcpynAtoW( shortpath
, full_name
.short_name
, shortlen
);
795 HeapFree( GetProcessHeap(), 0, longpathA
);
800 /***********************************************************************
801 * DOSFS_DoGetFullPathName
803 * Implementation of GetFullPathName32A/W.
805 static DWORD
DOSFS_DoGetFullPathName( LPCSTR name
, DWORD len
, LPSTR result
,
808 char buffer
[MAX_PATHNAME_LEN
];
812 dprintf_dosfs( stddeb
, "GetFullPathName: converting %s\n", name
);
814 if (!name
|| !result
) return 0;
816 if ((drive
= DOSFS_GetPathDrive( &name
)) == -1) return 0;
820 if (IS_END_OF_NAME(*name
)) /* Absolute path */
822 while ((*name
== '\\') || (*name
== '/')) name
++;
824 else /* Relative path */
827 lstrcpyn32A( p
, DRIVE_GetDosCwd(drive
), sizeof(buffer
) - 3 );
828 if (*p
) p
+= strlen(p
); else p
--;
836 if (IS_END_OF_NAME(name
[1]))
839 while ((*name
== '\\') || (*name
== '/')) name
++;
842 else if ((name
[1] == '.') && IS_END_OF_NAME(name
[2]))
845 while ((*name
== '\\') || (*name
== '/')) name
++;
846 while ((p
> buffer
+ 2) && (*p
!= '\\')) p
--;
847 *p
= '\0'; /* Remove trailing separator */
851 if (p
>= buffer
+ sizeof(buffer
) - 1)
853 DOS_ERROR( ER_PathNotFound
, EC_NotFound
, SA_Abort
, EL_Disk
);
857 while (!IS_END_OF_NAME(*name
) && (p
< buffer
+ sizeof(buffer
) - 1))
860 while ((*name
== '\\') || (*name
== '/')) name
++;
868 if (!(DRIVE_GetFlags(drive
) & DRIVE_CASE_PRESERVING
))
869 CharUpper32A( buffer
);
871 if (unicode
) lstrcpynAtoW( (LPWSTR
)result
, buffer
, len
);
872 else lstrcpyn32A( result
, buffer
, len
);
874 dprintf_dosfs( stddeb
, "GetFullPathName: returning %s\n", buffer
);
875 return strlen(buffer
);
879 /***********************************************************************
880 * GetFullPathName32A (KERNEL32.272)
882 DWORD WINAPI
GetFullPathName32A( LPCSTR name
, DWORD len
, LPSTR buffer
,
885 DWORD ret
= DOSFS_DoGetFullPathName( name
, len
, buffer
, FALSE
);
888 LPSTR p
= buffer
+ strlen(buffer
);
889 while ((p
> buffer
+ 2) && (*p
!= '\\')) p
--;
896 /***********************************************************************
897 * GetFullPathName32W (KERNEL32.273)
899 DWORD WINAPI
GetFullPathName32W( LPCWSTR name
, DWORD len
, LPWSTR buffer
,
902 LPSTR nameA
= HEAP_strdupWtoA( GetProcessHeap(), 0, name
);
903 DWORD ret
= DOSFS_DoGetFullPathName( nameA
, len
, (LPSTR
)buffer
, TRUE
);
904 HeapFree( GetProcessHeap(), 0, nameA
);
907 LPWSTR p
= buffer
+ lstrlen32W(buffer
);
908 while ((p
> buffer
+ 2) && (*p
!= '\\')) p
--;
915 /***********************************************************************
918 * Find the next matching file. Return the number of entries read to find
919 * the matching one, or 0 if no more entries.
920 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
921 * file name mask. Either or both can be NULL.
923 int DOSFS_FindNext( const char *path
, const char *short_mask
,
924 const char *long_mask
, int drive
, BYTE attr
,
925 int skip
, WIN32_FIND_DATA32A
*entry
)
927 static DOS_DIR
*dir
= NULL
;
929 static char buffer
[MAX_PATHNAME_LEN
];
930 static int cur_pos
= 0;
931 static int drive_root
= 0;
934 LPCSTR long_name
, short_name
;
936 BY_HANDLE_FILE_INFORMATION info
;
938 if ((attr
& ~(FA_UNUSED
| FA_ARCHIVE
| FA_RDONLY
)) == FA_LABEL
)
941 entry
->dwFileAttributes
= FILE_ATTRIBUTE_LABEL
;
942 DOSFS_UnixTimeToFileTime( (time_t)0, &entry
->ftCreationTime
, 0 );
943 DOSFS_UnixTimeToFileTime( (time_t)0, &entry
->ftLastAccessTime
, 0 );
944 DOSFS_UnixTimeToFileTime( (time_t)0, &entry
->ftLastWriteTime
, 0 );
945 entry
->nFileSizeHigh
= 0;
946 entry
->nFileSizeLow
= 0;
947 entry
->dwReserved0
= 0;
948 entry
->dwReserved1
= 0;
949 DOSFS_ToDosDTAFormat( DRIVE_GetLabel( drive
), entry
->cFileName
);
950 strcpy( entry
->cAlternateFileName
, entry
->cFileName
);
954 /* Check the cached directory */
955 if (dir
&& !strcmp( buffer
, path
) && (cur_pos
<= skip
)) skip
-= cur_pos
;
956 else /* Not in the cache, open it anew */
958 const char *drive_path
;
959 dprintf_dosfs( stddeb
, "DOSFS_FindNext: cache miss, path=%s skip=%d buf=%s cur=%d\n",
960 path
, skip
, buffer
, cur_pos
);
962 if (dir
) DOSFS_CloseDir(dir
);
963 if (!*path
) path
= "/";
964 if (!(dir
= DOSFS_OpenDir(path
))) return 0;
965 drive_path
= path
+ strlen(DRIVE_GetRoot(drive
));
966 while ((*drive_path
== '/') || (*drive_path
== '\\')) drive_path
++;
967 drive_root
= !*drive_path
;
968 dprintf_dosfs(stddeb
, "DOSFS_FindNext: drive_root = %d\n", drive_root
);
969 lstrcpyn32A( buffer
, path
, sizeof(buffer
) - 1 );
971 strcat( buffer
, "/" );
972 p
= buffer
+ strlen(buffer
);
973 attr
|= FA_UNUSED
| FA_ARCHIVE
| FA_RDONLY
;
974 flags
= DRIVE_GetFlags( drive
);
976 while (DOSFS_ReadDir( dir
, &long_name
, &short_name
))
978 if (skip
-- > 0) continue;
981 /* Don't return '.' and '..' in the root of the drive */
982 if (drive_root
&& (long_name
[0] == '.') &&
983 (!long_name
[1] || ((long_name
[1] == '.') && !long_name
[2])))
986 /* Check the long mask */
990 if (!DOSFS_MatchLong( long_mask
, long_name
,
991 flags
& DRIVE_CASE_SENSITIVE
)) continue;
994 /* Check the short mask */
1000 DOSFS_Hash( long_name
, dos_name
, TRUE
,
1001 !(flags
& DRIVE_CASE_SENSITIVE
) );
1002 short_name
= dos_name
;
1004 if (!DOSFS_MatchShort( short_mask
, short_name
)) continue;
1007 /* Check the file attributes */
1009 lstrcpyn32A( p
, long_name
, sizeof(buffer
) - (int)(p
- buffer
) );
1010 if (!FILE_Stat( buffer
, &info
))
1012 fprintf( stderr
, "DOSFS_FindNext: can't stat %s\n", buffer
);
1015 if (info
.dwFileAttributes
& ~attr
) continue;
1017 /* We now have a matching entry; fill the result and return */
1019 entry
->dwFileAttributes
= info
.dwFileAttributes
;
1020 entry
->ftCreationTime
= info
.ftCreationTime
;
1021 entry
->ftLastAccessTime
= info
.ftLastAccessTime
;
1022 entry
->ftLastWriteTime
= info
.ftLastWriteTime
;
1023 entry
->nFileSizeHigh
= info
.nFileSizeHigh
;
1024 entry
->nFileSizeLow
= info
.nFileSizeLow
;
1027 DOSFS_ToDosDTAFormat( short_name
, entry
->cAlternateFileName
);
1029 DOSFS_Hash( long_name
, entry
->cAlternateFileName
, FALSE
,
1030 !(flags
& DRIVE_CASE_SENSITIVE
) );
1032 lstrcpyn32A( entry
->cFileName
, long_name
, sizeof(entry
->cFileName
) );
1033 if (!(flags
& DRIVE_CASE_PRESERVING
)) CharLower32A( entry
->cFileName
);
1034 dprintf_dosfs( stddeb
, "DOSFS_FindNext: returning %s (%s) %02lx %ld\n",
1035 entry
->cFileName
, entry
->cAlternateFileName
,
1036 entry
->dwFileAttributes
, entry
->nFileSizeLow
);
1038 p
[-1] = '\0'; /* Remove trailing slash in buffer */
1041 DOSFS_CloseDir( dir
);
1043 return 0; /* End of directory */
1047 /*************************************************************************
1048 * FindFirstFile16 (KERNEL.413)
1050 HANDLE16 WINAPI
FindFirstFile16( LPCSTR path
, WIN32_FIND_DATA32A
*data
)
1052 DOS_FULL_NAME full_name
;
1054 FIND_FIRST_INFO
*info
;
1056 if (!path
) return 0;
1057 if (!DOSFS_GetFullName( path
, FALSE
, &full_name
))
1058 return INVALID_HANDLE_VALUE16
;
1059 if (!(handle
= GlobalAlloc16( GMEM_MOVEABLE
, sizeof(FIND_FIRST_INFO
) )))
1060 return INVALID_HANDLE_VALUE16
;
1061 info
= (FIND_FIRST_INFO
*)GlobalLock16( handle
);
1062 info
->path
= HEAP_strdupA( SystemHeap
, 0, full_name
.long_name
);
1063 info
->mask
= strrchr( info
->path
, '/' );
1064 *(info
->mask
++) = '\0';
1065 if (path
[0] && (path
[1] == ':')) info
->drive
= toupper(*path
) - 'A';
1066 else info
->drive
= DRIVE_GetCurrentDrive();
1068 GlobalUnlock16( handle
);
1069 if (!FindNextFile16( handle
, data
))
1071 FindClose16( handle
);
1072 DOS_ERROR( ER_NoMoreFiles
, EC_MediaError
, SA_Abort
, EL_Disk
);
1073 return INVALID_HANDLE_VALUE16
;
1079 /*************************************************************************
1080 * FindFirstFile32A (KERNEL32.123)
1082 HANDLE32 WINAPI
FindFirstFile32A( LPCSTR path
, WIN32_FIND_DATA32A
*data
)
1084 HANDLE32 handle
= FindFirstFile16( path
, data
);
1085 if (handle
== INVALID_HANDLE_VALUE16
) return INVALID_HANDLE_VALUE32
;
1090 /*************************************************************************
1091 * FindFirstFile32W (KERNEL32.124)
1093 HANDLE32 WINAPI
FindFirstFile32W( LPCWSTR path
, WIN32_FIND_DATA32W
*data
)
1095 WIN32_FIND_DATA32A dataA
;
1096 LPSTR pathA
= HEAP_strdupWtoA( GetProcessHeap(), 0, path
);
1097 HANDLE32 handle
= FindFirstFile32A( pathA
, &dataA
);
1098 HeapFree( GetProcessHeap(), 0, pathA
);
1099 if (handle
!= INVALID_HANDLE_VALUE32
)
1101 data
->dwFileAttributes
= dataA
.dwFileAttributes
;
1102 data
->ftCreationTime
= dataA
.ftCreationTime
;
1103 data
->ftLastAccessTime
= dataA
.ftLastAccessTime
;
1104 data
->ftLastWriteTime
= dataA
.ftLastWriteTime
;
1105 data
->nFileSizeHigh
= dataA
.nFileSizeHigh
;
1106 data
->nFileSizeLow
= dataA
.nFileSizeLow
;
1107 lstrcpyAtoW( data
->cFileName
, dataA
.cFileName
);
1108 lstrcpyAtoW( data
->cAlternateFileName
, dataA
.cAlternateFileName
);
1114 /*************************************************************************
1115 * FindNextFile16 (KERNEL.414)
1117 BOOL16 WINAPI
FindNextFile16( HANDLE16 handle
, WIN32_FIND_DATA32A
*data
)
1119 FIND_FIRST_INFO
*info
;
1122 if (!(info
= (FIND_FIRST_INFO
*)GlobalLock16( handle
)))
1124 DOS_ERROR( ER_InvalidHandle
, EC_ProgramError
, SA_Abort
, EL_Disk
);
1127 GlobalUnlock16( handle
);
1130 DOS_ERROR( ER_NoMoreFiles
, EC_MediaError
, SA_Abort
, EL_Disk
);
1133 if (!(count
= DOSFS_FindNext( info
->path
, NULL
, info
->mask
, info
->drive
,
1134 0xff, info
->skip
, data
)))
1136 HeapFree( SystemHeap
, 0, info
->path
);
1137 info
->path
= info
->mask
= NULL
;
1138 DOS_ERROR( ER_NoMoreFiles
, EC_MediaError
, SA_Abort
, EL_Disk
);
1141 info
->skip
+= count
;
1146 /*************************************************************************
1147 * FindNextFile32A (KERNEL32.126)
1149 BOOL32 WINAPI
FindNextFile32A( HANDLE32 handle
, WIN32_FIND_DATA32A
*data
)
1151 return FindNextFile16( handle
, data
);
1155 /*************************************************************************
1156 * FindNextFile32W (KERNEL32.127)
1158 BOOL32 WINAPI
FindNextFile32W( HANDLE32 handle
, WIN32_FIND_DATA32W
*data
)
1160 WIN32_FIND_DATA32A dataA
;
1161 if (!FindNextFile32A( handle
, &dataA
)) return FALSE
;
1162 data
->dwFileAttributes
= dataA
.dwFileAttributes
;
1163 data
->ftCreationTime
= dataA
.ftCreationTime
;
1164 data
->ftLastAccessTime
= dataA
.ftLastAccessTime
;
1165 data
->ftLastWriteTime
= dataA
.ftLastWriteTime
;
1166 data
->nFileSizeHigh
= dataA
.nFileSizeHigh
;
1167 data
->nFileSizeLow
= dataA
.nFileSizeLow
;
1168 lstrcpyAtoW( data
->cFileName
, dataA
.cFileName
);
1169 lstrcpyAtoW( data
->cAlternateFileName
, dataA
.cAlternateFileName
);
1174 /*************************************************************************
1175 * FindClose16 (KERNEL.415)
1177 BOOL16 WINAPI
FindClose16( HANDLE16 handle
)
1179 FIND_FIRST_INFO
*info
;
1181 if ((handle
== INVALID_HANDLE_VALUE16
) ||
1182 !(info
= (FIND_FIRST_INFO
*)GlobalLock16( handle
)))
1184 DOS_ERROR( ER_InvalidHandle
, EC_ProgramError
, SA_Abort
, EL_Disk
);
1187 if (info
->path
) HeapFree( SystemHeap
, 0, info
->path
);
1188 GlobalUnlock16( handle
);
1189 GlobalFree16( handle
);
1194 /*************************************************************************
1195 * FindClose32 (KERNEL32.119)
1197 BOOL32 WINAPI
FindClose32( HANDLE32 handle
)
1199 return FindClose16( (HANDLE16
)handle
);
1203 /***********************************************************************
1204 * DOSFS_UnixTimeToFileTime
1206 * Convert a Unix time to FILETIME format.
1207 * The FILETIME structure is a 64-bit value representing the number of
1208 * 100-nanosecond intervals since January 1, 1601, 0:00.
1209 * 'remainder' is the nonnegative number of 100-ns intervals
1210 * corresponding to the time fraction smaller than 1 second that
1211 * couldn't be stored in the time_t value.
1213 void DOSFS_UnixTimeToFileTime( time_t unix_time
, FILETIME
*filetime
,
1219 The time difference between 1 January 1601, 00:00:00 and
1220 1 January 1970, 00:00:00 is 369 years, plus the leap years
1221 from 1604 to 1968, excluding 1700, 1800, 1900.
1222 This makes (1968 - 1600) / 4 - 3 = 89 leap days, and a total
1225 Any day in that period had 24 * 60 * 60 = 86400 seconds.
1227 The time difference is 134774 * 86400 * 10000000, which can be written
1229 27111902 * 2^32 + 3577643008
1230 413 * 2^48 + 45534 * 2^32 + 54590 * 2^16 + 32768
1232 If you find that these constants are buggy, please change them in all
1233 instances in both conversion functions.
1236 There are two versions, one of them uses long long variables and
1237 is presumably faster but not ISO C. The other one uses standard C
1238 data types and operations but relies on the assumption that negative
1239 numbers are stored as 2's complement (-1 is 0xffff....). If this
1240 assumption is violated, dates before 1970 will not convert correctly.
1241 This should however work on any reasonable architecture where WINE
1246 Take care not to remove the casts. I have tested these functions
1247 (in both versions) for a lot of numbers. I would be interested in
1248 results on other compilers than GCC.
1250 The operations have been designed to account for the possibility
1251 of 64-bit time_t in future UNICES. Even the versions without
1252 internal long long numbers will work if time_t only is 64 bit.
1253 A 32-bit shift, which was necessary for that operation, turned out
1254 not to work correctly in GCC, besides giving the warning. So I
1255 used a double 16-bit shift instead. Numbers are in the ISO version
1256 represented by three limbs, the most significant with 32 bit, the
1257 other two with 16 bit each.
1259 As the modulo-operator % is not well-defined for negative numbers,
1260 negative divisors have been avoided in DOSFS_FileTimeToUnixTime.
1262 There might be quicker ways to do this in C. Certainly so in
1265 Claus Fischer, fischer@iue.tuwien.ac.at
1269 # define USE_LONG_LONG 1
1271 # define USE_LONG_LONG 0
1274 #if USE_LONG_LONG /* gcc supports long long type */
1276 long long int t
= unix_time
;
1278 t
+= 116444736000000000LL;
1280 filetime
->dwLowDateTime
= (UINT32
)t
;
1281 filetime
->dwHighDateTime
= (UINT32
)(t
>> 32);
1283 #else /* ISO version */
1285 UINT32 a0
; /* 16 bit, low bits */
1286 UINT32 a1
; /* 16 bit, medium bits */
1287 UINT32 a2
; /* 32 bit, high bits */
1289 /* Copy the unix time to a2/a1/a0 */
1290 a0
= unix_time
& 0xffff;
1291 a1
= (unix_time
>> 16) & 0xffff;
1292 /* This is obsolete if unix_time is only 32 bits, but it does not hurt.
1293 Do not replace this by >> 32, it gives a compiler warning and it does
1295 a2
= (unix_time
>= 0 ? (unix_time
>> 16) >> 16 :
1296 ~((~unix_time
>> 16) >> 16));
1298 /* Multiply a by 10000000 (a = a2/a1/a0)
1299 Split the factor into 10000 * 1000 which are both less than 0xffff. */
1301 a1
= a1
* 10000 + (a0
>> 16);
1302 a2
= a2
* 10000 + (a1
>> 16);
1307 a1
= a1
* 1000 + (a0
>> 16);
1308 a2
= a2
* 1000 + (a1
>> 16);
1312 /* Add the time difference and the remainder */
1313 a0
+= 32768 + (remainder
& 0xffff);
1314 a1
+= 54590 + (remainder
>> 16 ) + (a0
>> 16);
1315 a2
+= 27111902 + (a1
>> 16);
1320 filetime
->dwLowDateTime
= (a1
<< 16) + a0
;
1321 filetime
->dwHighDateTime
= a2
;
1326 /***********************************************************************
1327 * DOSFS_FileTimeToUnixTime
1329 * Convert a FILETIME format to Unix time.
1330 * If not NULL, 'remainder' contains the fractional part of the filetime,
1331 * in the range of [0..9999999] (even if time_t is negative).
1333 time_t DOSFS_FileTimeToUnixTime( const FILETIME
*filetime
, DWORD
*remainder
)
1335 /* Read the comment in the function DOSFS_UnixTimeToFileTime. */
1338 long long int t
= filetime
->dwHighDateTime
;
1340 t
+= (UINT32
)filetime
->dwLowDateTime
;
1341 t
-= 116444736000000000LL;
1344 if (remainder
) *remainder
= 9999999 - (-t
- 1) % 10000000;
1345 return -1 - ((-t
- 1) / 10000000);
1349 if (remainder
) *remainder
= t
% 10000000;
1350 return t
/ 10000000;
1353 #else /* ISO version */
1355 UINT32 a0
; /* 16 bit, low bits */
1356 UINT32 a1
; /* 16 bit, medium bits */
1357 UINT32 a2
; /* 32 bit, high bits */
1358 UINT32 r
; /* remainder of division */
1359 unsigned int carry
; /* carry bit for subtraction */
1360 int negative
; /* whether a represents a negative value */
1362 /* Copy the time values to a2/a1/a0 */
1363 a2
= (UINT32
)filetime
->dwHighDateTime
;
1364 a1
= ((UINT32
)filetime
->dwLowDateTime
) >> 16;
1365 a0
= ((UINT32
)filetime
->dwLowDateTime
) & 0xffff;
1367 /* Subtract the time difference */
1368 if (a0
>= 32768 ) a0
-= 32768 , carry
= 0;
1369 else a0
+= (1 << 16) - 32768 , carry
= 1;
1371 if (a1
>= 54590 + carry
) a1
-= 54590 + carry
, carry
= 0;
1372 else a1
+= (1 << 16) - 54590 - carry
, carry
= 1;
1374 a2
-= 27111902 + carry
;
1376 /* If a is negative, replace a by (-1-a) */
1377 negative
= (a2
>= ((UINT32
)1) << 31);
1380 /* Set a to -a - 1 (a is a2/a1/a0) */
1386 /* Divide a by 10000000 (a = a2/a1/a0), put the rest into r.
1387 Split the divisor into 10000 * 1000 which are both less than 0xffff. */
1388 a1
+= (a2
% 10000) << 16;
1390 a0
+= (a1
% 10000) << 16;
1395 a1
+= (a2
% 1000) << 16;
1397 a0
+= (a1
% 1000) << 16;
1399 r
+= (a0
% 1000) * 10000;
1402 /* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
1405 /* Set a to -a - 1 (a is a2/a1/a0) */
1413 if (remainder
) *remainder
= r
;
1415 /* Do not replace this by << 32, it gives a compiler warning and it does
1417 return ((((time_t)a2
) << 16) << 16) + (a1
<< 16) + a0
;
1422 /***********************************************************************
1423 * DosDateTimeToFileTime (KERNEL32.76)
1425 BOOL32 WINAPI
DosDateTimeToFileTime( WORD fatdate
, WORD fattime
, LPFILETIME ft
)
1429 newtm
.tm_sec
= (fattime
& 0x1f) * 2;
1430 newtm
.tm_min
= (fattime
>> 5) & 0x3f;
1431 newtm
.tm_hour
= (fattime
>> 11);
1432 newtm
.tm_mday
= (fatdate
& 0x1f);
1433 newtm
.tm_mon
= ((fatdate
>> 5) & 0x0f) - 1;
1434 newtm
.tm_year
= (fatdate
>> 9) + 80;
1435 DOSFS_UnixTimeToFileTime( mktime( &newtm
), ft
, 0 );
1440 /***********************************************************************
1441 * FileTimeToDosDateTime (KERNEL32.111)
1443 BOOL32 WINAPI
FileTimeToDosDateTime( const FILETIME
*ft
, LPWORD fatdate
,
1446 time_t unixtime
= DOSFS_FileTimeToUnixTime( ft
, NULL
);
1447 struct tm
*tm
= localtime( &unixtime
);
1449 *fattime
= (tm
->tm_hour
<< 11) + (tm
->tm_min
<< 5) + (tm
->tm_sec
/ 2);
1451 *fatdate
= ((tm
->tm_year
- 80) << 9) + ((tm
->tm_mon
+ 1) << 5)
1457 /***********************************************************************
1458 * LocalFileTimeToFileTime (KERNEL32.373)
1460 BOOL32 WINAPI
LocalFileTimeToFileTime( const FILETIME
*localft
,
1466 /* convert from local to UTC. Perhaps not correct. FIXME */
1467 time_t unixtime
= DOSFS_FileTimeToUnixTime( localft
, &remainder
);
1468 xtm
= gmtime( &unixtime
);
1469 DOSFS_UnixTimeToFileTime( mktime(xtm
), utcft
, remainder
);
1474 /***********************************************************************
1475 * FileTimeToLocalFileTime (KERNEL32.112)
1477 BOOL32 WINAPI
FileTimeToLocalFileTime( const FILETIME
*utcft
,
1478 LPFILETIME localft
)
1483 /* convert from UTC to local. Perhaps not correct. FIXME */
1484 time_t unixtime
= DOSFS_FileTimeToUnixTime( utcft
, &remainder
);
1485 xtm
= localtime( &unixtime
);
1486 DOSFS_UnixTimeToFileTime( mktime(xtm
), localft
, remainder
);
1491 /***********************************************************************
1492 * FileTimeToSystemTime (KERNEL32.113)
1494 BOOL32 WINAPI
FileTimeToSystemTime( const FILETIME
*ft
, LPSYSTEMTIME syst
)
1498 time_t xtime
= DOSFS_FileTimeToUnixTime( ft
, &remainder
);
1499 xtm
= gmtime(&xtime
);
1500 syst
->wYear
= xtm
->tm_year
;
1501 syst
->wMonth
= xtm
->tm_mon
;
1502 syst
->wDayOfWeek
= xtm
->tm_wday
;
1503 syst
->wDay
= xtm
->tm_mday
;
1504 syst
->wHour
= xtm
->tm_hour
;
1505 syst
->wMinute
= xtm
->tm_min
;
1506 syst
->wSecond
= xtm
->tm_sec
;
1507 syst
->wMilliseconds
= remainder
/ 10000;
1511 /***********************************************************************
1512 * QueryDosDeviceA (KERNEL32.413)
1514 * returns array of strings terminated by \0, terminated by \0
1516 DWORD WINAPI
QueryDosDevice32A(LPCSTR devname
,LPSTR target
,DWORD bufsize
)
1521 dprintf_dosfs(stddeb
,"QueryDosDevice(%s,...)\n",devname
?devname
:"<null>");
1523 /* return known MSDOS devices */
1524 lstrcpy32A(buffer
,"CON COM1 COM2 LPT1 NUL ");
1525 while ((s
=strchr(buffer
,' ')))
1528 lstrcpyn32A(target
,buffer
,bufsize
);
1529 return strlen(buffer
);
1531 lstrcpy32A(buffer
,"\\DEV\\");
1532 lstrcat32A(buffer
,devname
);
1533 if ((s
=strchr(buffer
,':'))) *s
='\0';
1534 lstrcpyn32A(target
,buffer
,bufsize
);
1535 return strlen(buffer
);
1539 /***********************************************************************
1540 * QueryDosDeviceW (KERNEL32.414)
1542 * returns array of strings terminated by \0, terminated by \0
1544 DWORD WINAPI
QueryDosDevice32W(LPCWSTR devname
,LPWSTR target
,DWORD bufsize
)
1546 LPSTR devnameA
= devname
?HEAP_strdupWtoA(GetProcessHeap(),0,devname
):NULL
;
1547 LPSTR targetA
= (LPSTR
)HEAP_xalloc(GetProcessHeap(),0,bufsize
);
1548 DWORD ret
= QueryDosDevice32A(devnameA
,targetA
,bufsize
);
1550 lstrcpynAtoW(target
,targetA
,bufsize
);
1551 if (devnameA
) HeapFree(GetProcessHeap(),0,devnameA
);
1552 if (targetA
) HeapFree(GetProcessHeap(),0,targetA
);
1557 /***********************************************************************
1558 * SystemTimeToFileTime (KERNEL32.526)
1560 BOOL32 WINAPI
SystemTimeToFileTime( const SYSTEMTIME
*syst
, LPFILETIME ft
)
1564 xtm
.tm_year
= syst
->wYear
;
1565 xtm
.tm_mon
= syst
->wMonth
;
1566 xtm
.tm_wday
= syst
->wDayOfWeek
;
1567 xtm
.tm_mday
= syst
->wDay
;
1568 xtm
.tm_hour
= syst
->wHour
;
1569 xtm
.tm_min
= syst
->wMinute
;
1570 xtm
.tm_sec
= syst
->wSecond
;
1571 DOSFS_UnixTimeToFileTime( mktime(&xtm
), ft
, syst
->wMilliseconds
* 10000 );