2 * DOS file system functions
4 * Copyright 1993 Erik Bos
5 * Copyright 1996 Alexandre Julliard
17 #include <sys/ioctl.h>
29 /* Define the VFAT ioctl to get both short and long file names */
30 /* FIXME: is it possible to get this to work on other systems? */
32 #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, long)
33 /* We want the real kernel dirent structure, not the libc one */
38 unsigned short d_reclen
;
43 #undef VFAT_IOCTL_READDIR_BOTH /* just in case... */
46 /* Chars we don't want to see in DOS file names */
47 #define INVALID_DOS_CHARS "*?<>|\"+=,;[] \345"
49 static const DOS_DEVICE DOSFS_Devices
[] =
50 /* name, device flags (see Int 21/AX=0x4400) */
64 { "SCSIMGR$", 0xc0c0 },
68 #define GET_DRIVE(path) \
69 (((path)[1] == ':') ? toupper((path)[0]) - 'A' : DOSFS_CurDrive)
71 /* DOS extended error status */
72 WORD DOS_ExtendedError
;
77 /* Info structure for FindFirstFile handle */
87 /* Directory info for DOSFS_ReadDir */
91 #ifdef VFAT_IOCTL_READDIR_BOTH
94 KERNEL_DIRENT dirent
[2];
99 /***********************************************************************
102 * Return 1 if Unix file 'name' is also a valid MS-DOS name
103 * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
104 * File name can be terminated by '\0', '\\' or '/'.
106 static int DOSFS_ValidDOSName( const char *name
, int ignore_case
)
108 static const char invalid_chars
[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS
;
109 const char *p
= name
;
110 const char *invalid
= ignore_case
? (invalid_chars
+ 26) : invalid_chars
;
115 /* Check for "." and ".." */
118 /* All other names beginning with '.' are invalid */
119 return (IS_END_OF_NAME(*p
));
121 while (!IS_END_OF_NAME(*p
))
123 if (strchr( invalid
, *p
)) return 0; /* Invalid char */
124 if (*p
== '.') break; /* Start of the extension */
125 if (++len
> 8) return 0; /* Name too long */
128 if (*p
!= '.') return 1; /* End of name */
130 if (IS_END_OF_NAME(*p
)) return 0; /* Empty extension not allowed */
132 while (!IS_END_OF_NAME(*p
))
134 if (strchr( invalid
, *p
)) return 0; /* Invalid char */
135 if (*p
== '.') return 0; /* Second extension not allowed */
136 if (++len
> 3) return 0; /* Extension too long */
143 /***********************************************************************
144 * DOSFS_ToDosFCBFormat
146 * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
147 * expanding wild cards and converting to upper-case in the process.
148 * File name can be terminated by '\0', '\\' or '/'.
149 * Return FALSE if the name is not a valid DOS name.
150 * 'buffer' must be at least 12 characters long.
152 BOOL32
DOSFS_ToDosFCBFormat( LPCSTR name
, LPSTR buffer
)
154 static const char invalid_chars
[] = INVALID_DOS_CHARS
;
155 const char *p
= name
;
158 /* Check for "." and ".." */
162 strcpy( buffer
, ". " );
168 return (!*p
|| (*p
== '/') || (*p
== '\\'));
171 for (i
= 0; i
< 8; i
++)
188 if (strchr( invalid_chars
, *p
)) return FALSE
;
189 buffer
[i
] = toupper(*p
);
197 /* Skip all chars after wildcard up to first dot */
198 while (*p
&& (*p
!= '/') && (*p
!= '\\') && (*p
!= '.')) p
++;
202 /* Check if name too long */
203 if (*p
&& (*p
!= '/') && (*p
!= '\\') && (*p
!= '.')) return FALSE
;
205 if (*p
== '.') p
++; /* Skip dot */
207 for (i
= 8; i
< 11; i
++)
217 return FALSE
; /* Second extension not allowed */
225 if (strchr( invalid_chars
, *p
)) return FALSE
;
226 buffer
[i
] = toupper(*p
);
236 /***********************************************************************
237 * DOSFS_ToDosDTAFormat
239 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
240 * converting to upper-case in the process.
241 * File name can be terminated by '\0', '\\' or '/'.
242 * 'buffer' must be at least 13 characters long.
244 static void DOSFS_ToDosDTAFormat( LPCSTR name
, LPSTR buffer
)
248 memcpy( buffer
, name
, 8 );
249 for (p
= buffer
+ 8; (p
> buffer
) && (p
[-1] == ' '); p
--);
251 memcpy( p
, name
+ 8, 3 );
252 for (p
+= 3; p
[-1] == ' '; p
--);
253 if (p
[-1] == '.') p
--;
258 /***********************************************************************
261 * Check a DOS file name against a mask (both in FCB format).
263 static int DOSFS_MatchShort( const char *mask
, const char *name
)
266 for (i
= 11; i
> 0; i
--, mask
++, name
++)
267 if ((*mask
!= '?') && (*mask
!= *name
)) return 0;
272 /***********************************************************************
275 * Check a long file name against a mask.
277 static int DOSFS_MatchLong( const char *mask
, const char *name
,
280 if (!strcmp( mask
, "*.*" )) return 1;
281 while (*name
&& *mask
)
286 while (*mask
== '*') mask
++; /* Skip consecutive '*' */
287 if (!*mask
) return 1;
288 if (case_sensitive
) while (*name
&& (*name
!= *mask
)) name
++;
289 else while (*name
&& (toupper(*name
) != toupper(*mask
))) name
++;
290 if (!*name
) return 0;
292 else if (*mask
!= '?')
296 if (*mask
!= *name
) return 0;
298 else if (toupper(*mask
) != toupper(*name
)) return 0;
303 return (!*name
&& !*mask
);
307 /***********************************************************************
310 static DOS_DIR
*DOSFS_OpenDir( LPCSTR path
)
312 DOS_DIR
*dir
= HeapAlloc( SystemHeap
, 0, sizeof(*dir
) );
315 DOS_ERROR( ER_OutOfMemory
, EC_OutOfResource
, SA_Abort
, EL_Memory
);
319 #ifdef VFAT_IOCTL_READDIR_BOTH
321 /* Check if the VFAT ioctl is supported on this directory */
323 if ((dir
->fd
= open( path
, O_RDONLY
)) != -1)
325 if (ioctl( dir
->fd
, VFAT_IOCTL_READDIR_BOTH
, (long)dir
->dirent
) == -1)
332 /* Set the file pointer back at the start of the directory */
333 lseek( dir
->fd
, 0, SEEK_SET
);
338 #endif /* VFAT_IOCTL_READDIR_BOTH */
340 /* Now use the standard opendir/readdir interface */
342 if (!(dir
->dir
= opendir( path
)))
344 HeapFree( SystemHeap
, 0, dir
);
351 /***********************************************************************
354 static void DOSFS_CloseDir( DOS_DIR
*dir
)
356 #ifdef VFAT_IOCTL_READDIR_BOTH
357 if (dir
->fd
!= -1) close( dir
->fd
);
358 #endif /* VFAT_IOCTL_READDIR_BOTH */
359 if (dir
->dir
) closedir( dir
->dir
);
360 HeapFree( SystemHeap
, 0, dir
);
364 /***********************************************************************
367 static BOOL32
DOSFS_ReadDir( DOS_DIR
*dir
, LPCSTR
*long_name
,
370 struct dirent
*dirent
;
372 #ifdef VFAT_IOCTL_READDIR_BOTH
375 if (ioctl( dir
->fd
, VFAT_IOCTL_READDIR_BOTH
, (long)dir
->dirent
) != -1) {
376 if (!dir
->dirent
[0].d_reclen
) return FALSE
;
377 if (!DOSFS_ToDosFCBFormat( dir
->dirent
[0].d_name
, dir
->short_name
))
378 dir
->short_name
[0] = '\0';
379 *short_name
= dir
->short_name
;
380 if (dir
->dirent
[1].d_name
[0]) *long_name
= dir
->dirent
[1].d_name
;
381 else *long_name
= dir
->dirent
[0].d_name
;
385 #endif /* VFAT_IOCTL_READDIR_BOTH */
387 if (!(dirent
= readdir( dir
->dir
))) return FALSE
;
388 *long_name
= dirent
->d_name
;
394 /***********************************************************************
397 * Transform a Unix file name into a hashed DOS name. If the name is a valid
398 * DOS name, it is converted to upper-case; otherwise it is replaced by a
399 * hashed version that fits in 8.3 format.
400 * File name can be terminated by '\0', '\\' or '/'.
401 * 'buffer' must be at least 13 characters long.
403 static void DOSFS_Hash( LPCSTR name
, LPSTR buffer
, BOOL32 dir_format
,
406 static const char invalid_chars
[] = INVALID_DOS_CHARS
"~.";
407 static const char hash_chars
[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
414 if (dir_format
) strcpy( buffer
, " " );
416 if (DOSFS_ValidDOSName( name
, ignore_case
))
418 /* Check for '.' and '..' */
422 if (!dir_format
) buffer
[1] = buffer
[2] = '\0';
423 if (name
[1] == '.') buffer
[1] = '.';
427 /* Simply copy the name, converting to uppercase */
429 for (dst
= buffer
; !IS_END_OF_NAME(*name
) && (*name
!= '.'); name
++)
430 *dst
++ = toupper(*name
);
433 if (dir_format
) dst
= buffer
+ 8;
435 for (name
++; !IS_END_OF_NAME(*name
); name
++)
436 *dst
++ = toupper(*name
);
438 if (!dir_format
) *dst
= '\0';
442 /* Compute the hash code of the file name */
443 /* If you know something about hash functions, feel free to */
444 /* insert a better algorithm here... */
447 for (p
= name
, hash
= 0xbeef; !IS_END_OF_NAME(p
[1]); p
++)
448 hash
= (hash
<<3) ^ (hash
>>5) ^ tolower(*p
) ^ (tolower(p
[1]) << 8);
449 hash
= (hash
<<3) ^ (hash
>>5) ^ tolower(*p
); /* Last character*/
453 for (p
= name
, hash
= 0xbeef; !IS_END_OF_NAME(p
[1]); p
++)
454 hash
= (hash
<< 3) ^ (hash
>> 5) ^ *p
^ (p
[1] << 8);
455 hash
= (hash
<< 3) ^ (hash
>> 5) ^ *p
; /* Last character */
458 /* Find last dot for start of the extension */
459 for (p
= name
+1, ext
= NULL
; !IS_END_OF_NAME(*p
); p
++)
460 if (*p
== '.') ext
= p
;
461 if (ext
&& IS_END_OF_NAME(ext
[1]))
462 ext
= NULL
; /* Empty extension ignored */
464 /* Copy first 4 chars, replacing invalid chars with '_' */
465 for (i
= 4, p
= name
, dst
= buffer
; i
> 0; i
--, p
++)
467 if (IS_END_OF_NAME(*p
) || (p
== ext
)) break;
468 *dst
++ = strchr( invalid_chars
, *p
) ? '_' : toupper(*p
);
470 /* Pad to 5 chars with '~' */
471 while (i
-- >= 0) *dst
++ = '~';
473 /* Insert hash code converted to 3 ASCII chars */
474 *dst
++ = hash_chars
[(hash
>> 10) & 0x1f];
475 *dst
++ = hash_chars
[(hash
>> 5) & 0x1f];
476 *dst
++ = hash_chars
[hash
& 0x1f];
478 /* Copy the first 3 chars of the extension (if any) */
481 if (!dir_format
) *dst
++ = '.';
482 for (i
= 3, ext
++; (i
> 0) && !IS_END_OF_NAME(*ext
); i
--, ext
++)
483 *dst
++ = strchr( invalid_chars
, *ext
) ? '_' : toupper(*ext
);
485 if (!dir_format
) *dst
= '\0';
489 /***********************************************************************
492 * Find the Unix file name in a given directory that corresponds to
493 * a file name (either in Unix or DOS format).
494 * File name can be terminated by '\0', '\\' or '/'.
495 * Return TRUE if OK, FALSE if no file name matches.
497 * 'long_buf' must be at least 'long_len' characters long. If the long name
498 * turns out to be larger than that, the function returns FALSE.
499 * 'short_buf' must be at least 13 characters long.
501 BOOL32
DOSFS_FindUnixName( LPCSTR path
, LPCSTR name
, LPSTR long_buf
,
502 INT32 long_len
, LPSTR short_buf
, BOOL32 ignore_case
)
505 LPCSTR long_name
, short_name
;
506 char dos_name
[12], tmp_buf
[13];
509 const char *p
= strchr( name
, '/' );
510 int len
= p
? (int)(p
- name
) : strlen(name
);
511 if ((p
= strchr( name
, '\\' ))) len
= MIN( (int)(p
- name
), len
);
512 if (long_len
< len
+ 1) return FALSE
;
514 TRACE(dosfs
, "%s,%s\n", path
, name
);
516 if (!DOSFS_ToDosFCBFormat( name
, dos_name
)) dos_name
[0] = '\0';
518 if (!(dir
= DOSFS_OpenDir( path
)))
520 WARN(dosfs
, "(%s,%s): can't open dir: %s\n",
521 path
, name
, strerror(errno
) );
525 while ((ret
= DOSFS_ReadDir( dir
, &long_name
, &short_name
)))
527 /* Check against Unix name */
528 if (len
== strlen(long_name
))
532 if (!lstrncmp32A( long_name
, name
, len
)) break;
536 if (!lstrncmpi32A( long_name
, name
, len
)) break;
541 /* Check against hashed DOS name */
544 DOSFS_Hash( long_name
, tmp_buf
, TRUE
, ignore_case
);
545 short_name
= tmp_buf
;
547 if (!strcmp( dos_name
, short_name
)) break;
552 if (long_buf
) strcpy( long_buf
, long_name
);
556 DOSFS_ToDosDTAFormat( short_name
, short_buf
);
558 DOSFS_Hash( long_name
, short_buf
, FALSE
, ignore_case
);
560 TRACE(dosfs
, "(%s,%s) -> %s (%s)\n",
561 path
, name
, long_name
, short_buf
? short_buf
: "***");
564 WARN(dosfs
, "'%s' not found in '%s'\n", name
, path
);
565 DOSFS_CloseDir( dir
);
570 /***********************************************************************
573 * Check if a DOS file name represents a DOS device and return the device.
575 const DOS_DEVICE
*DOSFS_GetDevice( const char *name
)
580 if (name
[0] && (name
[1] == ':')) name
+= 2;
581 if ((p
= strrchr( name
, '/' ))) name
= p
+ 1;
582 if ((p
= strrchr( name
, '\\' ))) name
= p
+ 1;
583 for (i
= 0; i
< sizeof(DOSFS_Devices
)/sizeof(DOSFS_Devices
[0]); i
++)
585 const char *dev
= DOSFS_Devices
[i
].name
;
586 if (!lstrncmpi32A( dev
, name
, strlen(dev
) ))
588 p
= name
+ strlen( dev
);
589 if (!*p
|| (*p
== '.')) return &DOSFS_Devices
[i
];
595 /***********************************************************************
598 * Open a DOS device. This might not map 1:1 into the UNIX device concept.
600 HFILE32
DOSFS_OpenDevice( const char *name
, int unixmode
)
607 if (name
[0] && (name
[1] == ':')) name
+= 2;
608 if ((p
= strrchr( name
, '/' ))) name
= p
+ 1;
609 if ((p
= strrchr( name
, '\\' ))) name
= p
+ 1;
610 for (i
= 0; i
< sizeof(DOSFS_Devices
)/sizeof(DOSFS_Devices
[0]); i
++)
612 const char *dev
= DOSFS_Devices
[i
].name
;
613 if (!lstrncmpi32A( dev
, name
, strlen(dev
) ))
615 p
= name
+ strlen( dev
);
616 if (!*p
|| (*p
== '.')) {
618 if (!strcmp(DOSFS_Devices
[i
].name
,"NUL"))
619 return FILE_OpenUnixFile("/dev/null",unixmode
);
620 if (!strcmp(DOSFS_Devices
[i
].name
,"CON")) {
623 return GetStdHandle( STD_INPUT_HANDLE
);
626 return GetStdHandle( STD_OUTPUT_HANDLE
);
629 FIXME(dosfs
,"can't open CON read/write\n");
630 return HFILE_ERROR32
;
634 if (!strcmp(DOSFS_Devices
[i
].name
,"SCSIMGR$")) {
635 if ((handle
= FILE_Alloc( &file
)) == INVALID_HANDLE_VALUE32
)
636 return HFILE_ERROR32
;
638 file
->unix_name
= HEAP_strdupA( SystemHeap
, 0, name
);
642 if (!strcmp(DOSFS_Devices
[i
].name
,"HPSCAN")) {
643 if ((handle
= FILE_Alloc( &file
)) == INVALID_HANDLE_VALUE32
)
644 return HFILE_ERROR32
;
646 file
->unix_name
= HEAP_strdupA( SystemHeap
, 0, name
);
650 FIXME(dosfs
,"device open %s not supported (yet)\n",DOSFS_Devices
[i
].name
);
651 return HFILE_ERROR32
;
655 return HFILE_ERROR32
;
659 /***********************************************************************
662 * Get the drive specified by a given path name (DOS or Unix format).
664 static int DOSFS_GetPathDrive( const char **name
)
667 const char *p
= *name
;
669 if (*p
&& (p
[1] == ':'))
671 drive
= toupper(*p
) - 'A';
674 else if (*p
== '/') /* Absolute Unix path? */
676 if ((drive
= DRIVE_FindDriveRoot( name
)) == -1)
678 MSG("Warning: %s not accessible from a DOS drive\n", *name
);
679 /* Assume it really was a DOS name */
680 drive
= DRIVE_GetCurrentDrive();
683 else drive
= DRIVE_GetCurrentDrive();
685 if (!DRIVE_IsValid(drive
))
687 DOS_ERROR( ER_InvalidDrive
, EC_MediaError
, SA_Abort
, EL_Disk
);
694 /***********************************************************************
697 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
698 * Unix name / short DOS name pair.
699 * Return FALSE if one of the path components does not exist. The last path
700 * component is only checked if 'check_last' is non-zero.
701 * The buffers pointed to by 'long_buf' and 'short_buf' must be
702 * at least MAX_PATHNAME_LEN long.
704 BOOL32
DOSFS_GetFullName( LPCSTR name
, BOOL32 check_last
, DOS_FULL_NAME
*full
)
708 char *p_l
, *p_s
, *root
;
710 TRACE(dosfs
, "%s (last=%d)\n",
713 if ((full
->drive
= DOSFS_GetPathDrive( &name
)) == -1) return FALSE
;
714 flags
= DRIVE_GetFlags( full
->drive
);
716 lstrcpyn32A( full
->long_name
, DRIVE_GetRoot( full
->drive
),
717 sizeof(full
->long_name
) );
718 if (full
->long_name
[1]) root
= full
->long_name
+ strlen(full
->long_name
);
719 else root
= full
->long_name
; /* root directory */
721 strcpy( full
->short_name
, "A:\\" );
722 full
->short_name
[0] += full
->drive
;
724 if ((*name
== '\\') || (*name
== '/')) /* Absolute path */
726 while ((*name
== '\\') || (*name
== '/')) name
++;
728 else /* Relative path */
730 lstrcpyn32A( root
+ 1, DRIVE_GetUnixCwd( full
->drive
),
731 sizeof(full
->long_name
) - (root
- full
->long_name
) - 1 );
732 if (root
[1]) *root
= '/';
733 lstrcpyn32A( full
->short_name
+ 3, DRIVE_GetDosCwd( full
->drive
),
734 sizeof(full
->short_name
) - 3 );
737 p_l
= full
->long_name
[1] ? full
->long_name
+ strlen(full
->long_name
)
739 p_s
= full
->short_name
[3] ? full
->short_name
+ strlen(full
->short_name
)
740 : full
->short_name
+ 2;
743 while (*name
&& found
)
745 /* Check for '.' and '..' */
749 if (IS_END_OF_NAME(name
[1]))
752 while ((*name
== '\\') || (*name
== '/')) name
++;
755 else if ((name
[1] == '.') && IS_END_OF_NAME(name
[2]))
758 while ((*name
== '\\') || (*name
== '/')) name
++;
759 while ((p_l
> root
) && (*p_l
!= '/')) p_l
--;
760 while ((p_s
> full
->short_name
+ 2) && (*p_s
!= '\\')) p_s
--;
761 *p_l
= *p_s
= '\0'; /* Remove trailing separator */
766 /* Make sure buffers are large enough */
768 if ((p_s
>= full
->short_name
+ sizeof(full
->short_name
) - 14) ||
769 (p_l
>= full
->long_name
+ sizeof(full
->long_name
) - 1))
771 DOS_ERROR( ER_PathNotFound
, EC_NotFound
, SA_Abort
, EL_Disk
);
775 /* Get the long and short name matching the file name */
777 if ((found
= DOSFS_FindUnixName( full
->long_name
, name
, p_l
+ 1,
778 sizeof(full
->long_name
) - (p_l
- full
->long_name
) - 1,
779 p_s
+ 1, !(flags
& DRIVE_CASE_SENSITIVE
) )))
785 while (!IS_END_OF_NAME(*name
)) name
++;
787 else if (!check_last
)
791 while (!IS_END_OF_NAME(*name
) &&
792 (p_s
< full
->short_name
+ sizeof(full
->short_name
) - 1) &&
793 (p_l
< full
->long_name
+ sizeof(full
->long_name
) - 1))
795 *p_s
++ = tolower(*name
);
796 /* If the drive is case-sensitive we want to create new */
797 /* files in lower-case otherwise we can't reopen them */
798 /* under the same short name. */
799 if (flags
& DRIVE_CASE_SENSITIVE
) *p_l
++ = tolower(*name
);
805 while ((*name
== '\\') || (*name
== '/')) name
++;
812 DOS_ERROR( ER_FileNotFound
, EC_NotFound
, SA_Abort
, EL_Disk
);
815 if (*name
) /* Not last */
817 DOS_ERROR( ER_PathNotFound
, EC_NotFound
, SA_Abort
, EL_Disk
);
821 if (!full
->long_name
[0]) strcpy( full
->long_name
, "/" );
822 if (!full
->short_name
[2]) strcpy( full
->short_name
+ 2, "\\" );
823 TRACE(dosfs
, "returning %s = %s\n",
824 full
->long_name
, full
->short_name
);
829 /***********************************************************************
830 * GetShortPathName32A (KERNEL32.271)
832 DWORD WINAPI
GetShortPathName32A( LPCSTR longpath
, LPSTR shortpath
,
835 DOS_FULL_NAME full_name
;
837 /* FIXME: is it correct to always return a fully qualified short path? */
838 if (!DOSFS_GetFullName( longpath
, TRUE
, &full_name
)) return 0;
839 lstrcpyn32A( shortpath
, full_name
.short_name
, shortlen
);
840 return strlen( full_name
.short_name
);
844 /***********************************************************************
845 * GetShortPathName32W (KERNEL32.272)
847 DWORD WINAPI
GetShortPathName32W( LPCWSTR longpath
, LPWSTR shortpath
,
850 DOS_FULL_NAME full_name
;
852 LPSTR longpathA
= HEAP_strdupWtoA( GetProcessHeap(), 0, longpath
);
854 /* FIXME: is it correct to always return a fully qualified short path? */
855 if (DOSFS_GetFullName( longpathA
, TRUE
, &full_name
))
857 ret
= strlen( full_name
.short_name
);
858 lstrcpynAtoW( shortpath
, full_name
.short_name
, shortlen
);
860 HeapFree( GetProcessHeap(), 0, longpathA
);
865 /***********************************************************************
866 * GetLongPathName32A (KERNEL32.xxx)
868 DWORD WINAPI
GetLongPathName32A( LPCSTR shortpath
, LPSTR longpath
,
871 DOS_FULL_NAME full_name
;
873 /* FIXME: Is it correct to return a UNIX style path here? */
874 if (!DOSFS_GetFullName( shortpath
, TRUE
, &full_name
)) return 0;
875 lstrcpyn32A( longpath
, full_name
.long_name
, longlen
);
876 return strlen( full_name
.long_name
);
880 /***********************************************************************
881 * GetLongPathName32W (KERNEL32.269)
883 DWORD WINAPI
GetLongPathName32W( LPCWSTR shortpath
, LPWSTR longpath
,
886 DOS_FULL_NAME full_name
;
888 LPSTR shortpathA
= HEAP_strdupWtoA( GetProcessHeap(), 0, shortpath
);
890 /* FIXME: is it correct to always return a fully qualified short path? */
891 if (DOSFS_GetFullName( shortpathA
, TRUE
, &full_name
))
893 ret
= strlen( full_name
.short_name
);
894 lstrcpynAtoW( longpath
, full_name
.long_name
, longlen
);
896 HeapFree( GetProcessHeap(), 0, shortpathA
);
901 /***********************************************************************
902 * DOSFS_DoGetFullPathName
904 * Implementation of GetFullPathName32A/W.
906 static DWORD
DOSFS_DoGetFullPathName( LPCSTR name
, DWORD len
, LPSTR result
,
909 char buffer
[MAX_PATHNAME_LEN
];
913 TRACE(dosfs
, "converting %s\n", name
);
915 if (!name
|| !result
) return 0;
917 if ((drive
= DOSFS_GetPathDrive( &name
)) == -1) return 0;
921 if (IS_END_OF_NAME(*name
) && (*name
)) /* Absolute path */
923 while ((*name
== '\\') || (*name
== '/')) name
++;
925 else /* Relative path or empty path */
928 lstrcpyn32A( p
, DRIVE_GetDosCwd(drive
), sizeof(buffer
) - 3 );
929 if (*p
) p
+= strlen(p
); else p
--;
931 if (!*name
) /* empty path */
939 if (IS_END_OF_NAME(name
[1]))
942 while ((*name
== '\\') || (*name
== '/')) name
++;
945 else if ((name
[1] == '.') && IS_END_OF_NAME(name
[2]))
948 while ((*name
== '\\') || (*name
== '/')) name
++;
949 while ((p
> buffer
+ 2) && (*p
!= '\\')) p
--;
950 *p
= '\0'; /* Remove trailing separator */
954 if (p
>= buffer
+ sizeof(buffer
) - 1)
956 DOS_ERROR( ER_PathNotFound
, EC_NotFound
, SA_Abort
, EL_Disk
);
960 while (!IS_END_OF_NAME(*name
) && (p
< buffer
+ sizeof(buffer
) - 1))
963 while ((*name
== '\\') || (*name
== '/')) name
++;
971 if (!(DRIVE_GetFlags(drive
) & DRIVE_CASE_PRESERVING
))
972 CharUpper32A( buffer
);
974 if (unicode
) lstrcpynAtoW( (LPWSTR
)result
, buffer
, len
);
975 else lstrcpyn32A( result
, buffer
, len
);
977 TRACE(dosfs
, "returning %s\n", buffer
);
978 return strlen(buffer
);
982 /***********************************************************************
983 * GetFullPathName32A (KERNEL32.272)
985 DWORD WINAPI
GetFullPathName32A( LPCSTR name
, DWORD len
, LPSTR buffer
,
988 DWORD ret
= DOSFS_DoGetFullPathName( name
, len
, buffer
, FALSE
);
991 LPSTR p
= buffer
+ strlen(buffer
);
992 while ((p
> buffer
+ 2) && (*p
!= '\\')) p
--;
999 /***********************************************************************
1000 * GetFullPathName32W (KERNEL32.273)
1002 DWORD WINAPI
GetFullPathName32W( LPCWSTR name
, DWORD len
, LPWSTR buffer
,
1005 LPSTR nameA
= HEAP_strdupWtoA( GetProcessHeap(), 0, name
);
1006 DWORD ret
= DOSFS_DoGetFullPathName( nameA
, len
, (LPSTR
)buffer
, TRUE
);
1007 HeapFree( GetProcessHeap(), 0, nameA
);
1008 if (ret
&& lastpart
)
1010 LPWSTR p
= buffer
+ lstrlen32W(buffer
);
1011 while ((p
> buffer
+ 2) && (*p
!= '\\')) p
--;
1018 /***********************************************************************
1021 * Find the next matching file. Return the number of entries read to find
1022 * the matching one, or 0 if no more entries.
1023 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
1024 * file name mask. Either or both can be NULL.
1026 int DOSFS_FindNext( const char *path
, const char *short_mask
,
1027 const char *long_mask
, int drive
, BYTE attr
,
1028 int skip
, WIN32_FIND_DATA32A
*entry
)
1030 static DOS_DIR
*dir
= NULL
;
1032 static char buffer
[MAX_PATHNAME_LEN
];
1033 static int cur_pos
= 0;
1034 static int drive_root
= 0;
1037 LPCSTR long_name
, short_name
;
1039 BY_HANDLE_FILE_INFORMATION info
;
1041 if ((attr
& ~(FA_UNUSED
| FA_ARCHIVE
| FA_RDONLY
)) == FA_LABEL
)
1044 entry
->dwFileAttributes
= FILE_ATTRIBUTE_LABEL
;
1045 DOSFS_UnixTimeToFileTime( (time_t)0, &entry
->ftCreationTime
, 0 );
1046 DOSFS_UnixTimeToFileTime( (time_t)0, &entry
->ftLastAccessTime
, 0 );
1047 DOSFS_UnixTimeToFileTime( (time_t)0, &entry
->ftLastWriteTime
, 0 );
1048 entry
->nFileSizeHigh
= 0;
1049 entry
->nFileSizeLow
= 0;
1050 entry
->dwReserved0
= 0;
1051 entry
->dwReserved1
= 0;
1052 DOSFS_ToDosDTAFormat( DRIVE_GetLabel( drive
), entry
->cFileName
);
1053 strcpy( entry
->cAlternateFileName
, entry
->cFileName
);
1057 /* Check the cached directory */
1058 if (dir
&& !strcmp( buffer
, path
) && (cur_pos
<= skip
)) skip
-= cur_pos
;
1059 else /* Not in the cache, open it anew */
1061 const char *drive_path
;
1062 TRACE(dosfs
, "cache miss, path=%s skip=%d buf=%s cur=%d\n",
1063 path
, skip
, buffer
, cur_pos
);
1065 if (dir
) DOSFS_CloseDir(dir
);
1066 if (!*path
) path
= "/";
1067 if (!(dir
= DOSFS_OpenDir(path
))) return 0;
1068 drive_path
= path
+ strlen(DRIVE_GetRoot(drive
));
1069 while ((*drive_path
== '/') || (*drive_path
== '\\')) drive_path
++;
1070 drive_root
= !*drive_path
;
1071 TRACE(dosfs
, "drive_root = %d\n", drive_root
);
1072 lstrcpyn32A( buffer
, path
, sizeof(buffer
) - 1 );
1074 strcat( buffer
, "/" );
1075 p
= buffer
+ strlen(buffer
);
1076 attr
|= FA_UNUSED
| FA_ARCHIVE
| FA_RDONLY
;
1077 flags
= DRIVE_GetFlags( drive
);
1079 while (DOSFS_ReadDir( dir
, &long_name
, &short_name
))
1081 if (skip
-- > 0) continue;
1084 /* Don't return '.' and '..' in the root of the drive */
1085 if (drive_root
&& (long_name
[0] == '.') &&
1086 (!long_name
[1] || ((long_name
[1] == '.') && !long_name
[2])))
1089 /* Check the long mask */
1093 if (!DOSFS_MatchLong( long_mask
, long_name
,
1094 flags
& DRIVE_CASE_SENSITIVE
)) continue;
1097 /* Check the short mask */
1103 DOSFS_Hash( long_name
, dos_name
, TRUE
,
1104 !(flags
& DRIVE_CASE_SENSITIVE
) );
1105 short_name
= dos_name
;
1107 if (!DOSFS_MatchShort( short_mask
, short_name
)) continue;
1110 /* Check the file attributes */
1112 lstrcpyn32A( p
, long_name
, sizeof(buffer
) - (int)(p
- buffer
) );
1113 if (!FILE_Stat( buffer
, &info
))
1115 WARN(dosfs
, "can't stat %s\n", buffer
);
1118 if (info
.dwFileAttributes
& ~attr
) continue;
1120 /* We now have a matching entry; fill the result and return */
1122 entry
->dwFileAttributes
= info
.dwFileAttributes
;
1123 entry
->ftCreationTime
= info
.ftCreationTime
;
1124 entry
->ftLastAccessTime
= info
.ftLastAccessTime
;
1125 entry
->ftLastWriteTime
= info
.ftLastWriteTime
;
1126 entry
->nFileSizeHigh
= info
.nFileSizeHigh
;
1127 entry
->nFileSizeLow
= info
.nFileSizeLow
;
1130 DOSFS_ToDosDTAFormat( short_name
, entry
->cAlternateFileName
);
1132 DOSFS_Hash( long_name
, entry
->cAlternateFileName
, FALSE
,
1133 !(flags
& DRIVE_CASE_SENSITIVE
) );
1135 lstrcpyn32A( entry
->cFileName
, long_name
, sizeof(entry
->cFileName
) );
1136 if (!(flags
& DRIVE_CASE_PRESERVING
)) CharLower32A( entry
->cFileName
);
1137 TRACE(dosfs
, "returning %s (%s) %02lx %ld\n",
1138 entry
->cFileName
, entry
->cAlternateFileName
,
1139 entry
->dwFileAttributes
, entry
->nFileSizeLow
);
1141 p
[-1] = '\0'; /* Remove trailing slash in buffer */
1144 DOSFS_CloseDir( dir
);
1146 return 0; /* End of directory */
1150 /*************************************************************************
1151 * FindFirstFile16 (KERNEL.413)
1153 HANDLE16 WINAPI
FindFirstFile16( LPCSTR path
, WIN32_FIND_DATA32A
*data
)
1155 DOS_FULL_NAME full_name
;
1157 FIND_FIRST_INFO
*info
;
1159 if (!path
) return 0;
1160 if (!DOSFS_GetFullName( path
, FALSE
, &full_name
))
1161 return INVALID_HANDLE_VALUE16
;
1162 if (!(handle
= GlobalAlloc16( GMEM_MOVEABLE
, sizeof(FIND_FIRST_INFO
) )))
1163 return INVALID_HANDLE_VALUE16
;
1164 info
= (FIND_FIRST_INFO
*)GlobalLock16( handle
);
1165 info
->path
= HEAP_strdupA( SystemHeap
, 0, full_name
.long_name
);
1166 info
->mask
= strrchr( info
->path
, '/' );
1167 *(info
->mask
++) = '\0';
1168 if (path
[0] && (path
[1] == ':')) info
->drive
= toupper(*path
) - 'A';
1169 else info
->drive
= DRIVE_GetCurrentDrive();
1171 GlobalUnlock16( handle
);
1172 if (!FindNextFile16( handle
, data
))
1174 FindClose16( handle
);
1175 DOS_ERROR( ER_NoMoreFiles
, EC_MediaError
, SA_Abort
, EL_Disk
);
1176 return INVALID_HANDLE_VALUE16
;
1182 /*************************************************************************
1183 * FindFirstFile32A (KERNEL32.123)
1185 HANDLE32 WINAPI
FindFirstFile32A( LPCSTR path
, WIN32_FIND_DATA32A
*data
)
1187 HANDLE32 handle
= FindFirstFile16( path
, data
);
1188 if (handle
== INVALID_HANDLE_VALUE16
) return INVALID_HANDLE_VALUE32
;
1193 /*************************************************************************
1194 * FindFirstFile32W (KERNEL32.124)
1196 HANDLE32 WINAPI
FindFirstFile32W( LPCWSTR path
, WIN32_FIND_DATA32W
*data
)
1198 WIN32_FIND_DATA32A dataA
;
1199 LPSTR pathA
= HEAP_strdupWtoA( GetProcessHeap(), 0, path
);
1200 HANDLE32 handle
= FindFirstFile32A( pathA
, &dataA
);
1201 HeapFree( GetProcessHeap(), 0, pathA
);
1202 if (handle
!= INVALID_HANDLE_VALUE32
)
1204 data
->dwFileAttributes
= dataA
.dwFileAttributes
;
1205 data
->ftCreationTime
= dataA
.ftCreationTime
;
1206 data
->ftLastAccessTime
= dataA
.ftLastAccessTime
;
1207 data
->ftLastWriteTime
= dataA
.ftLastWriteTime
;
1208 data
->nFileSizeHigh
= dataA
.nFileSizeHigh
;
1209 data
->nFileSizeLow
= dataA
.nFileSizeLow
;
1210 lstrcpyAtoW( data
->cFileName
, dataA
.cFileName
);
1211 lstrcpyAtoW( data
->cAlternateFileName
, dataA
.cAlternateFileName
);
1217 /*************************************************************************
1218 * FindNextFile16 (KERNEL.414)
1220 BOOL16 WINAPI
FindNextFile16( HANDLE16 handle
, WIN32_FIND_DATA32A
*data
)
1222 FIND_FIRST_INFO
*info
;
1225 if (!(info
= (FIND_FIRST_INFO
*)GlobalLock16( handle
)))
1227 DOS_ERROR( ER_InvalidHandle
, EC_ProgramError
, SA_Abort
, EL_Disk
);
1230 GlobalUnlock16( handle
);
1233 DOS_ERROR( ER_NoMoreFiles
, EC_MediaError
, SA_Abort
, EL_Disk
);
1236 if (!(count
= DOSFS_FindNext( info
->path
, NULL
, info
->mask
, info
->drive
,
1237 0xff, info
->skip
, data
)))
1239 HeapFree( SystemHeap
, 0, info
->path
);
1240 info
->path
= info
->mask
= NULL
;
1241 DOS_ERROR( ER_NoMoreFiles
, EC_MediaError
, SA_Abort
, EL_Disk
);
1244 info
->skip
+= count
;
1249 /*************************************************************************
1250 * FindNextFile32A (KERNEL32.126)
1252 BOOL32 WINAPI
FindNextFile32A( HANDLE32 handle
, WIN32_FIND_DATA32A
*data
)
1254 return FindNextFile16( handle
, data
);
1258 /*************************************************************************
1259 * FindNextFile32W (KERNEL32.127)
1261 BOOL32 WINAPI
FindNextFile32W( HANDLE32 handle
, WIN32_FIND_DATA32W
*data
)
1263 WIN32_FIND_DATA32A dataA
;
1264 if (!FindNextFile32A( handle
, &dataA
)) return FALSE
;
1265 data
->dwFileAttributes
= dataA
.dwFileAttributes
;
1266 data
->ftCreationTime
= dataA
.ftCreationTime
;
1267 data
->ftLastAccessTime
= dataA
.ftLastAccessTime
;
1268 data
->ftLastWriteTime
= dataA
.ftLastWriteTime
;
1269 data
->nFileSizeHigh
= dataA
.nFileSizeHigh
;
1270 data
->nFileSizeLow
= dataA
.nFileSizeLow
;
1271 lstrcpyAtoW( data
->cFileName
, dataA
.cFileName
);
1272 lstrcpyAtoW( data
->cAlternateFileName
, dataA
.cAlternateFileName
);
1277 /*************************************************************************
1278 * FindClose16 (KERNEL.415)
1280 BOOL16 WINAPI
FindClose16( HANDLE16 handle
)
1282 FIND_FIRST_INFO
*info
;
1284 if ((handle
== INVALID_HANDLE_VALUE16
) ||
1285 !(info
= (FIND_FIRST_INFO
*)GlobalLock16( handle
)))
1287 DOS_ERROR( ER_InvalidHandle
, EC_ProgramError
, SA_Abort
, EL_Disk
);
1290 if (info
->path
) HeapFree( SystemHeap
, 0, info
->path
);
1291 GlobalUnlock16( handle
);
1292 GlobalFree16( handle
);
1297 /*************************************************************************
1298 * FindClose32 (KERNEL32.119)
1300 BOOL32 WINAPI
FindClose32( HANDLE32 handle
)
1302 return FindClose16( (HANDLE16
)handle
);
1306 /***********************************************************************
1307 * DOSFS_UnixTimeToFileTime
1309 * Convert a Unix time to FILETIME format.
1310 * The FILETIME structure is a 64-bit value representing the number of
1311 * 100-nanosecond intervals since January 1, 1601, 0:00.
1312 * 'remainder' is the nonnegative number of 100-ns intervals
1313 * corresponding to the time fraction smaller than 1 second that
1314 * couldn't be stored in the time_t value.
1316 void DOSFS_UnixTimeToFileTime( time_t unix_time
, FILETIME
*filetime
,
1322 The time difference between 1 January 1601, 00:00:00 and
1323 1 January 1970, 00:00:00 is 369 years, plus the leap years
1324 from 1604 to 1968, excluding 1700, 1800, 1900.
1325 This makes (1968 - 1600) / 4 - 3 = 89 leap days, and a total
1328 Any day in that period had 24 * 60 * 60 = 86400 seconds.
1330 The time difference is 134774 * 86400 * 10000000, which can be written
1332 27111902 * 2^32 + 3577643008
1333 413 * 2^48 + 45534 * 2^32 + 54590 * 2^16 + 32768
1335 If you find that these constants are buggy, please change them in all
1336 instances in both conversion functions.
1339 There are two versions, one of them uses long long variables and
1340 is presumably faster but not ISO C. The other one uses standard C
1341 data types and operations but relies on the assumption that negative
1342 numbers are stored as 2's complement (-1 is 0xffff....). If this
1343 assumption is violated, dates before 1970 will not convert correctly.
1344 This should however work on any reasonable architecture where WINE
1349 Take care not to remove the casts. I have tested these functions
1350 (in both versions) for a lot of numbers. I would be interested in
1351 results on other compilers than GCC.
1353 The operations have been designed to account for the possibility
1354 of 64-bit time_t in future UNICES. Even the versions without
1355 internal long long numbers will work if time_t only is 64 bit.
1356 A 32-bit shift, which was necessary for that operation, turned out
1357 not to work correctly in GCC, besides giving the warning. So I
1358 used a double 16-bit shift instead. Numbers are in the ISO version
1359 represented by three limbs, the most significant with 32 bit, the
1360 other two with 16 bit each.
1362 As the modulo-operator % is not well-defined for negative numbers,
1363 negative divisors have been avoided in DOSFS_FileTimeToUnixTime.
1365 There might be quicker ways to do this in C. Certainly so in
1368 Claus Fischer, fischer@iue.tuwien.ac.at
1371 #if (SIZEOF_LONG_LONG >= 8)
1372 # define USE_LONG_LONG 1
1374 # define USE_LONG_LONG 0
1377 #if USE_LONG_LONG /* gcc supports long long type */
1379 long long int t
= unix_time
;
1381 t
+= 116444736000000000LL;
1383 filetime
->dwLowDateTime
= (UINT32
)t
;
1384 filetime
->dwHighDateTime
= (UINT32
)(t
>> 32);
1386 #else /* ISO version */
1388 UINT32 a0
; /* 16 bit, low bits */
1389 UINT32 a1
; /* 16 bit, medium bits */
1390 UINT32 a2
; /* 32 bit, high bits */
1392 /* Copy the unix time to a2/a1/a0 */
1393 a0
= unix_time
& 0xffff;
1394 a1
= (unix_time
>> 16) & 0xffff;
1395 /* This is obsolete if unix_time is only 32 bits, but it does not hurt.
1396 Do not replace this by >> 32, it gives a compiler warning and it does
1398 a2
= (unix_time
>= 0 ? (unix_time
>> 16) >> 16 :
1399 ~((~unix_time
>> 16) >> 16));
1401 /* Multiply a by 10000000 (a = a2/a1/a0)
1402 Split the factor into 10000 * 1000 which are both less than 0xffff. */
1404 a1
= a1
* 10000 + (a0
>> 16);
1405 a2
= a2
* 10000 + (a1
>> 16);
1410 a1
= a1
* 1000 + (a0
>> 16);
1411 a2
= a2
* 1000 + (a1
>> 16);
1415 /* Add the time difference and the remainder */
1416 a0
+= 32768 + (remainder
& 0xffff);
1417 a1
+= 54590 + (remainder
>> 16 ) + (a0
>> 16);
1418 a2
+= 27111902 + (a1
>> 16);
1423 filetime
->dwLowDateTime
= (a1
<< 16) + a0
;
1424 filetime
->dwHighDateTime
= a2
;
1429 /***********************************************************************
1430 * DOSFS_FileTimeToUnixTime
1432 * Convert a FILETIME format to Unix time.
1433 * If not NULL, 'remainder' contains the fractional part of the filetime,
1434 * in the range of [0..9999999] (even if time_t is negative).
1436 time_t DOSFS_FileTimeToUnixTime( const FILETIME
*filetime
, DWORD
*remainder
)
1438 /* Read the comment in the function DOSFS_UnixTimeToFileTime. */
1441 long long int t
= filetime
->dwHighDateTime
;
1443 t
+= (UINT32
)filetime
->dwLowDateTime
;
1444 t
-= 116444736000000000LL;
1447 if (remainder
) *remainder
= 9999999 - (-t
- 1) % 10000000;
1448 return -1 - ((-t
- 1) / 10000000);
1452 if (remainder
) *remainder
= t
% 10000000;
1453 return t
/ 10000000;
1456 #else /* ISO version */
1458 UINT32 a0
; /* 16 bit, low bits */
1459 UINT32 a1
; /* 16 bit, medium bits */
1460 UINT32 a2
; /* 32 bit, high bits */
1461 UINT32 r
; /* remainder of division */
1462 unsigned int carry
; /* carry bit for subtraction */
1463 int negative
; /* whether a represents a negative value */
1465 /* Copy the time values to a2/a1/a0 */
1466 a2
= (UINT32
)filetime
->dwHighDateTime
;
1467 a1
= ((UINT32
)filetime
->dwLowDateTime
) >> 16;
1468 a0
= ((UINT32
)filetime
->dwLowDateTime
) & 0xffff;
1470 /* Subtract the time difference */
1471 if (a0
>= 32768 ) a0
-= 32768 , carry
= 0;
1472 else a0
+= (1 << 16) - 32768 , carry
= 1;
1474 if (a1
>= 54590 + carry
) a1
-= 54590 + carry
, carry
= 0;
1475 else a1
+= (1 << 16) - 54590 - carry
, carry
= 1;
1477 a2
-= 27111902 + carry
;
1479 /* If a is negative, replace a by (-1-a) */
1480 negative
= (a2
>= ((UINT32
)1) << 31);
1483 /* Set a to -a - 1 (a is a2/a1/a0) */
1489 /* Divide a by 10000000 (a = a2/a1/a0), put the rest into r.
1490 Split the divisor into 10000 * 1000 which are both less than 0xffff. */
1491 a1
+= (a2
% 10000) << 16;
1493 a0
+= (a1
% 10000) << 16;
1498 a1
+= (a2
% 1000) << 16;
1500 a0
+= (a1
% 1000) << 16;
1502 r
+= (a0
% 1000) * 10000;
1505 /* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
1508 /* Set a to -a - 1 (a is a2/a1/a0) */
1516 if (remainder
) *remainder
= r
;
1518 /* Do not replace this by << 32, it gives a compiler warning and it does
1520 return ((((time_t)a2
) << 16) << 16) + (a1
<< 16) + a0
;
1525 /***********************************************************************
1526 * DosDateTimeToFileTime (KERNEL32.76)
1528 BOOL32 WINAPI
DosDateTimeToFileTime( WORD fatdate
, WORD fattime
, LPFILETIME ft
)
1532 newtm
.tm_sec
= (fattime
& 0x1f) * 2;
1533 newtm
.tm_min
= (fattime
>> 5) & 0x3f;
1534 newtm
.tm_hour
= (fattime
>> 11);
1535 newtm
.tm_mday
= (fatdate
& 0x1f);
1536 newtm
.tm_mon
= ((fatdate
>> 5) & 0x0f) - 1;
1537 newtm
.tm_year
= (fatdate
>> 9) + 80;
1538 DOSFS_UnixTimeToFileTime( mktime( &newtm
), ft
, 0 );
1543 /***********************************************************************
1544 * FileTimeToDosDateTime (KERNEL32.111)
1546 BOOL32 WINAPI
FileTimeToDosDateTime( const FILETIME
*ft
, LPWORD fatdate
,
1549 time_t unixtime
= DOSFS_FileTimeToUnixTime( ft
, NULL
);
1550 struct tm
*tm
= localtime( &unixtime
);
1552 *fattime
= (tm
->tm_hour
<< 11) + (tm
->tm_min
<< 5) + (tm
->tm_sec
/ 2);
1554 *fatdate
= ((tm
->tm_year
- 80) << 9) + ((tm
->tm_mon
+ 1) << 5)
1560 /***********************************************************************
1561 * LocalFileTimeToFileTime (KERNEL32.373)
1563 BOOL32 WINAPI
LocalFileTimeToFileTime( const FILETIME
*localft
,
1569 /* convert from local to UTC. Perhaps not correct. FIXME */
1570 time_t unixtime
= DOSFS_FileTimeToUnixTime( localft
, &remainder
);
1571 xtm
= gmtime( &unixtime
);
1572 DOSFS_UnixTimeToFileTime( mktime(xtm
), utcft
, remainder
);
1577 /***********************************************************************
1578 * FileTimeToLocalFileTime (KERNEL32.112)
1580 BOOL32 WINAPI
FileTimeToLocalFileTime( const FILETIME
*utcft
,
1581 LPFILETIME localft
)
1584 /* convert from UTC to local. Perhaps not correct. FIXME */
1585 time_t unixtime
= DOSFS_FileTimeToUnixTime( utcft
, &remainder
);
1587 struct tm
*xtm
= localtime( &unixtime
);
1590 localtime
= timegm(xtm
);
1591 DOSFS_UnixTimeToFileTime( localtime
, localft
, remainder
);
1594 struct tm
*xtm
,*gtm
;
1597 xtm
= localtime( &unixtime
);
1598 gtm
= gmtime( &unixtime
);
1599 time1
= mktime(xtm
);
1600 time2
= mktime(gtm
);
1601 DOSFS_UnixTimeToFileTime( 2*time1
-time2
, localft
, remainder
);
1607 /***********************************************************************
1608 * FileTimeToSystemTime (KERNEL32.113)
1610 BOOL32 WINAPI
FileTimeToSystemTime( const FILETIME
*ft
, LPSYSTEMTIME syst
)
1614 time_t xtime
= DOSFS_FileTimeToUnixTime( ft
, &remainder
);
1615 xtm
= gmtime(&xtime
);
1616 syst
->wYear
= xtm
->tm_year
+1900;
1617 syst
->wMonth
= xtm
->tm_mon
+ 1;
1618 syst
->wDayOfWeek
= xtm
->tm_wday
;
1619 syst
->wDay
= xtm
->tm_mday
;
1620 syst
->wHour
= xtm
->tm_hour
;
1621 syst
->wMinute
= xtm
->tm_min
;
1622 syst
->wSecond
= xtm
->tm_sec
;
1623 syst
->wMilliseconds
= remainder
/ 10000;
1627 /***********************************************************************
1628 * QueryDosDeviceA (KERNEL32.413)
1630 * returns array of strings terminated by \0, terminated by \0
1632 DWORD WINAPI
QueryDosDevice32A(LPCSTR devname
,LPSTR target
,DWORD bufsize
)
1637 TRACE(dosfs
,"(%s,...)\n",devname
?devname
:"<null>");
1639 /* return known MSDOS devices */
1640 lstrcpy32A(buffer
,"CON COM1 COM2 LPT1 NUL ");
1641 while ((s
=strchr(buffer
,' ')))
1644 lstrcpyn32A(target
,buffer
,bufsize
);
1645 return strlen(buffer
);
1647 lstrcpy32A(buffer
,"\\DEV\\");
1648 lstrcat32A(buffer
,devname
);
1649 if ((s
=strchr(buffer
,':'))) *s
='\0';
1650 lstrcpyn32A(target
,buffer
,bufsize
);
1651 return strlen(buffer
);
1655 /***********************************************************************
1656 * QueryDosDeviceW (KERNEL32.414)
1658 * returns array of strings terminated by \0, terminated by \0
1660 DWORD WINAPI
QueryDosDevice32W(LPCWSTR devname
,LPWSTR target
,DWORD bufsize
)
1662 LPSTR devnameA
= devname
?HEAP_strdupWtoA(GetProcessHeap(),0,devname
):NULL
;
1663 LPSTR targetA
= (LPSTR
)HEAP_xalloc(GetProcessHeap(),0,bufsize
);
1664 DWORD ret
= QueryDosDevice32A(devnameA
,targetA
,bufsize
);
1666 lstrcpynAtoW(target
,targetA
,bufsize
);
1667 if (devnameA
) HeapFree(GetProcessHeap(),0,devnameA
);
1668 if (targetA
) HeapFree(GetProcessHeap(),0,targetA
);
1673 /***********************************************************************
1674 * SystemTimeToFileTime (KERNEL32.526)
1676 BOOL32 WINAPI
SystemTimeToFileTime( const SYSTEMTIME
*syst
, LPFILETIME ft
)
1682 struct tm xtm
,*local_tm
,*utc_tm
;
1683 time_t localtim
,utctime
;
1686 xtm
.tm_year
= syst
->wYear
-1900;
1687 xtm
.tm_mon
= syst
->wMonth
- 1;
1688 xtm
.tm_wday
= syst
->wDayOfWeek
;
1689 xtm
.tm_mday
= syst
->wDay
;
1690 xtm
.tm_hour
= syst
->wHour
;
1691 xtm
.tm_min
= syst
->wMinute
;
1692 xtm
.tm_sec
= syst
->wSecond
; /* this is UTC */
1695 utctime
= timegm(&xtm
);
1696 DOSFS_UnixTimeToFileTime( utctime
, ft
,
1697 syst
->wMilliseconds
* 10000 );
1699 localtim
= mktime(&xtm
); /* now we've got local time */
1700 local_tm
= localtime(&localtim
);
1701 utc_tm
= gmtime(&localtim
);
1702 utctime
= mktime(utc_tm
);
1703 DOSFS_UnixTimeToFileTime( 2*localtim
-utctime
, ft
,
1704 syst
->wMilliseconds
* 10000 );