2 * DOS file system functions
4 * Copyright 1993 Erik Bos
5 * Copyright 1996 Alexandre Julliard
13 #include <sys/errno.h>
18 #include <sys/ioctl.h>
31 /* Define the VFAT ioctl to get both short and long file names */
32 /* FIXME: is it possible to get this to work on other systems? */
34 #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, long)
35 /* We want the real kernel dirent structure, not the libc one */
40 unsigned short d_reclen
;
45 #undef VFAT_IOCTL_READDIR_BOTH /* just in case... */
48 /* Chars we don't want to see in DOS file names */
49 #define INVALID_DOS_CHARS "*?<>|\"+=,;[] \345"
51 static const DOS_DEVICE DOSFS_Devices
[] =
52 /* name, device flags (see Int 21/AX=0x4400) */
66 { "SCSIMGR$", 0xc0c0 },
70 #define GET_DRIVE(path) \
71 (((path)[1] == ':') ? toupper((path)[0]) - 'A' : DOSFS_CurDrive)
73 /* DOS extended error status */
74 WORD DOS_ExtendedError
;
79 /* Directory info for DOSFS_ReadDir */
83 #ifdef VFAT_IOCTL_READDIR_BOTH
86 KERNEL_DIRENT dirent
[2];
90 /* Info structure for FindFirstFile handle */
104 /***********************************************************************
107 * Return 1 if Unix file 'name' is also a valid MS-DOS name
108 * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
109 * File name can be terminated by '\0', '\\' or '/'.
111 static int DOSFS_ValidDOSName( const char *name
, int ignore_case
)
113 static const char invalid_chars
[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS
;
114 const char *p
= name
;
115 const char *invalid
= ignore_case
? (invalid_chars
+ 26) : invalid_chars
;
120 /* Check for "." and ".." */
123 /* All other names beginning with '.' are invalid */
124 return (IS_END_OF_NAME(*p
));
126 while (!IS_END_OF_NAME(*p
))
128 if (strchr( invalid
, *p
)) return 0; /* Invalid char */
129 if (*p
== '.') break; /* Start of the extension */
130 if (++len
> 8) return 0; /* Name too long */
133 if (*p
!= '.') return 1; /* End of name */
135 if (IS_END_OF_NAME(*p
)) return 0; /* Empty extension not allowed */
137 while (!IS_END_OF_NAME(*p
))
139 if (strchr( invalid
, *p
)) return 0; /* Invalid char */
140 if (*p
== '.') return 0; /* Second extension not allowed */
141 if (++len
> 3) return 0; /* Extension too long */
148 /***********************************************************************
149 * DOSFS_ToDosFCBFormat
151 * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
152 * expanding wild cards and converting to upper-case in the process.
153 * File name can be terminated by '\0', '\\' or '/'.
154 * Return FALSE if the name is not a valid DOS name.
155 * 'buffer' must be at least 12 characters long.
157 BOOL32
DOSFS_ToDosFCBFormat( LPCSTR name
, LPSTR buffer
)
159 static const char invalid_chars
[] = INVALID_DOS_CHARS
;
160 const char *p
= name
;
163 /* Check for "." and ".." */
167 strcpy( buffer
, ". " );
173 return (!*p
|| (*p
== '/') || (*p
== '\\'));
176 for (i
= 0; i
< 8; i
++)
193 if (strchr( invalid_chars
, *p
)) return FALSE
;
194 buffer
[i
] = toupper(*p
);
202 /* Skip all chars after wildcard up to first dot */
203 while (*p
&& (*p
!= '/') && (*p
!= '\\') && (*p
!= '.')) p
++;
207 /* Check if name too long */
208 if (*p
&& (*p
!= '/') && (*p
!= '\\') && (*p
!= '.')) return FALSE
;
210 if (*p
== '.') p
++; /* Skip dot */
212 for (i
= 8; i
< 11; i
++)
222 return FALSE
; /* Second extension not allowed */
230 if (strchr( invalid_chars
, *p
)) return FALSE
;
231 buffer
[i
] = toupper(*p
);
241 /***********************************************************************
242 * DOSFS_ToDosDTAFormat
244 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
245 * converting to upper-case in the process.
246 * File name can be terminated by '\0', '\\' or '/'.
247 * 'buffer' must be at least 13 characters long.
249 static void DOSFS_ToDosDTAFormat( LPCSTR name
, LPSTR buffer
)
253 memcpy( buffer
, name
, 8 );
254 for (p
= buffer
+ 8; (p
> buffer
) && (p
[-1] == ' '); p
--);
256 memcpy( p
, name
+ 8, 3 );
257 for (p
+= 3; p
[-1] == ' '; p
--);
258 if (p
[-1] == '.') p
--;
263 /***********************************************************************
266 * Check a DOS file name against a mask (both in FCB format).
268 static int DOSFS_MatchShort( const char *mask
, const char *name
)
271 for (i
= 11; i
> 0; i
--, mask
++, name
++)
272 if ((*mask
!= '?') && (*mask
!= *name
)) return 0;
277 /***********************************************************************
280 * Check a long file name against a mask.
282 static int DOSFS_MatchLong( const char *mask
, const char *name
,
285 if (!strcmp( mask
, "*.*" )) return 1;
286 while (*name
&& *mask
)
291 while (*mask
== '*') mask
++; /* Skip consecutive '*' */
292 if (!*mask
) return 1;
293 if (case_sensitive
) while (*name
&& (*name
!= *mask
)) name
++;
294 else while (*name
&& (toupper(*name
) != toupper(*mask
))) name
++;
295 if (!*name
) return 0;
297 else if (*mask
!= '?')
301 if (*mask
!= *name
) return 0;
303 else if (toupper(*mask
) != toupper(*name
)) return 0;
308 return (!*name
&& !*mask
);
312 /***********************************************************************
315 static DOS_DIR
*DOSFS_OpenDir( LPCSTR path
)
317 DOS_DIR
*dir
= HeapAlloc( SystemHeap
, 0, sizeof(*dir
) );
320 DOS_ERROR( ER_OutOfMemory
, EC_OutOfResource
, SA_Abort
, EL_Memory
);
324 /* Treat empty path as root directory. This simplifies path split into
325 directory and mask in several other places */
326 if (!*path
) path
= "/";
328 #ifdef VFAT_IOCTL_READDIR_BOTH
330 /* Check if the VFAT ioctl is supported on this directory */
332 if ((dir
->fd
= open( path
, O_RDONLY
)) != -1)
334 if (ioctl( dir
->fd
, VFAT_IOCTL_READDIR_BOTH
, (long)dir
->dirent
) == -1)
341 /* Set the file pointer back at the start of the directory */
342 lseek( dir
->fd
, 0, SEEK_SET
);
347 #endif /* VFAT_IOCTL_READDIR_BOTH */
349 /* Now use the standard opendir/readdir interface */
351 if (!(dir
->dir
= opendir( path
)))
353 HeapFree( SystemHeap
, 0, dir
);
360 /***********************************************************************
363 static void DOSFS_CloseDir( DOS_DIR
*dir
)
365 #ifdef VFAT_IOCTL_READDIR_BOTH
366 if (dir
->fd
!= -1) close( dir
->fd
);
367 #endif /* VFAT_IOCTL_READDIR_BOTH */
368 if (dir
->dir
) closedir( dir
->dir
);
369 HeapFree( SystemHeap
, 0, dir
);
373 /***********************************************************************
376 static BOOL32
DOSFS_ReadDir( DOS_DIR
*dir
, LPCSTR
*long_name
,
379 struct dirent
*dirent
;
381 #ifdef VFAT_IOCTL_READDIR_BOTH
384 if (ioctl( dir
->fd
, VFAT_IOCTL_READDIR_BOTH
, (long)dir
->dirent
) != -1) {
385 if (!dir
->dirent
[0].d_reclen
) return FALSE
;
386 if (!DOSFS_ToDosFCBFormat( dir
->dirent
[0].d_name
, dir
->short_name
))
387 dir
->short_name
[0] = '\0';
388 *short_name
= dir
->short_name
;
389 if (dir
->dirent
[1].d_name
[0]) *long_name
= dir
->dirent
[1].d_name
;
390 else *long_name
= dir
->dirent
[0].d_name
;
394 #endif /* VFAT_IOCTL_READDIR_BOTH */
396 if (!(dirent
= readdir( dir
->dir
))) return FALSE
;
397 *long_name
= dirent
->d_name
;
403 /***********************************************************************
406 * Transform a Unix file name into a hashed DOS name. If the name is a valid
407 * DOS name, it is converted to upper-case; otherwise it is replaced by a
408 * hashed version that fits in 8.3 format.
409 * File name can be terminated by '\0', '\\' or '/'.
410 * 'buffer' must be at least 13 characters long.
412 static void DOSFS_Hash( LPCSTR name
, LPSTR buffer
, BOOL32 dir_format
,
415 static const char invalid_chars
[] = INVALID_DOS_CHARS
"~.";
416 static const char hash_chars
[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
423 if (dir_format
) strcpy( buffer
, " " );
425 if (DOSFS_ValidDOSName( name
, ignore_case
))
427 /* Check for '.' and '..' */
431 if (!dir_format
) buffer
[1] = buffer
[2] = '\0';
432 if (name
[1] == '.') buffer
[1] = '.';
436 /* Simply copy the name, converting to uppercase */
438 for (dst
= buffer
; !IS_END_OF_NAME(*name
) && (*name
!= '.'); name
++)
439 *dst
++ = toupper(*name
);
442 if (dir_format
) dst
= buffer
+ 8;
444 for (name
++; !IS_END_OF_NAME(*name
); name
++)
445 *dst
++ = toupper(*name
);
447 if (!dir_format
) *dst
= '\0';
451 /* Compute the hash code of the file name */
452 /* If you know something about hash functions, feel free to */
453 /* insert a better algorithm here... */
456 for (p
= name
, hash
= 0xbeef; !IS_END_OF_NAME(p
[1]); p
++)
457 hash
= (hash
<<3) ^ (hash
>>5) ^ tolower(*p
) ^ (tolower(p
[1]) << 8);
458 hash
= (hash
<<3) ^ (hash
>>5) ^ tolower(*p
); /* Last character*/
462 for (p
= name
, hash
= 0xbeef; !IS_END_OF_NAME(p
[1]); p
++)
463 hash
= (hash
<< 3) ^ (hash
>> 5) ^ *p
^ (p
[1] << 8);
464 hash
= (hash
<< 3) ^ (hash
>> 5) ^ *p
; /* Last character */
467 /* Find last dot for start of the extension */
468 for (p
= name
+1, ext
= NULL
; !IS_END_OF_NAME(*p
); p
++)
469 if (*p
== '.') ext
= p
;
470 if (ext
&& IS_END_OF_NAME(ext
[1]))
471 ext
= NULL
; /* Empty extension ignored */
473 /* Copy first 4 chars, replacing invalid chars with '_' */
474 for (i
= 4, p
= name
, dst
= buffer
; i
> 0; i
--, p
++)
476 if (IS_END_OF_NAME(*p
) || (p
== ext
)) break;
477 *dst
++ = strchr( invalid_chars
, *p
) ? '_' : toupper(*p
);
479 /* Pad to 5 chars with '~' */
480 while (i
-- >= 0) *dst
++ = '~';
482 /* Insert hash code converted to 3 ASCII chars */
483 *dst
++ = hash_chars
[(hash
>> 10) & 0x1f];
484 *dst
++ = hash_chars
[(hash
>> 5) & 0x1f];
485 *dst
++ = hash_chars
[hash
& 0x1f];
487 /* Copy the first 3 chars of the extension (if any) */
490 if (!dir_format
) *dst
++ = '.';
491 for (i
= 3, ext
++; (i
> 0) && !IS_END_OF_NAME(*ext
); i
--, ext
++)
492 *dst
++ = strchr( invalid_chars
, *ext
) ? '_' : toupper(*ext
);
494 if (!dir_format
) *dst
= '\0';
498 /***********************************************************************
501 * Find the Unix file name in a given directory that corresponds to
502 * a file name (either in Unix or DOS format).
503 * File name can be terminated by '\0', '\\' or '/'.
504 * Return TRUE if OK, FALSE if no file name matches.
506 * 'long_buf' must be at least 'long_len' characters long. If the long name
507 * turns out to be larger than that, the function returns FALSE.
508 * 'short_buf' must be at least 13 characters long.
510 BOOL32
DOSFS_FindUnixName( LPCSTR path
, LPCSTR name
, LPSTR long_buf
,
511 INT32 long_len
, LPSTR short_buf
, BOOL32 ignore_case
)
514 LPCSTR long_name
, short_name
;
515 char dos_name
[12], tmp_buf
[13];
518 const char *p
= strchr( name
, '/' );
519 int len
= p
? (int)(p
- name
) : strlen(name
);
520 if ((p
= strchr( name
, '\\' ))) len
= MIN( (int)(p
- name
), len
);
521 if (long_len
< len
+ 1) return FALSE
;
523 TRACE(dosfs
, "%s,%s\n", path
, name
);
525 if (!DOSFS_ToDosFCBFormat( name
, dos_name
)) dos_name
[0] = '\0';
527 if (!(dir
= DOSFS_OpenDir( path
)))
529 WARN(dosfs
, "(%s,%s): can't open dir: %s\n",
530 path
, name
, strerror(errno
) );
534 while ((ret
= DOSFS_ReadDir( dir
, &long_name
, &short_name
)))
536 /* Check against Unix name */
537 if (len
== strlen(long_name
))
541 if (!lstrncmp32A( long_name
, name
, len
)) break;
545 if (!lstrncmpi32A( long_name
, name
, len
)) break;
550 /* Check against hashed DOS name */
553 DOSFS_Hash( long_name
, tmp_buf
, TRUE
, ignore_case
);
554 short_name
= tmp_buf
;
556 if (!strcmp( dos_name
, short_name
)) break;
561 if (long_buf
) strcpy( long_buf
, long_name
);
565 DOSFS_ToDosDTAFormat( short_name
, short_buf
);
567 DOSFS_Hash( long_name
, short_buf
, FALSE
, ignore_case
);
569 TRACE(dosfs
, "(%s,%s) -> %s (%s)\n",
570 path
, name
, long_name
, short_buf
? short_buf
: "***");
573 WARN(dosfs
, "'%s' not found in '%s'\n", name
, path
);
574 DOSFS_CloseDir( dir
);
579 /***********************************************************************
582 * Check if a DOS file name represents a DOS device and return the device.
584 const DOS_DEVICE
*DOSFS_GetDevice( const char *name
)
589 if (!name
) return NULL
; /* if FILE_DupUnixHandle was used */
590 if (name
[0] && (name
[1] == ':')) name
+= 2;
591 if ((p
= strrchr( name
, '/' ))) name
= p
+ 1;
592 if ((p
= strrchr( name
, '\\' ))) name
= p
+ 1;
593 for (i
= 0; i
< sizeof(DOSFS_Devices
)/sizeof(DOSFS_Devices
[0]); i
++)
595 const char *dev
= DOSFS_Devices
[i
].name
;
596 if (!lstrncmpi32A( dev
, name
, strlen(dev
) ))
598 p
= name
+ strlen( dev
);
599 if (!*p
|| (*p
== '.')) return &DOSFS_Devices
[i
];
605 /***********************************************************************
608 * Open a DOS device. This might not map 1:1 into the UNIX device concept.
610 HFILE32
DOSFS_OpenDevice( const char *name
, int unixmode
)
617 if (!name
) return (HFILE32
)NULL
; /* if FILE_DupUnixHandle was used */
618 if (name
[0] && (name
[1] == ':')) name
+= 2;
619 if ((p
= strrchr( name
, '/' ))) name
= p
+ 1;
620 if ((p
= strrchr( name
, '\\' ))) name
= p
+ 1;
621 for (i
= 0; i
< sizeof(DOSFS_Devices
)/sizeof(DOSFS_Devices
[0]); i
++)
623 const char *dev
= DOSFS_Devices
[i
].name
;
624 if (!lstrncmpi32A( dev
, name
, strlen(dev
) ))
626 p
= name
+ strlen( dev
);
627 if (!*p
|| (*p
== '.')) {
629 if (!strcmp(DOSFS_Devices
[i
].name
,"NUL"))
630 return FILE_OpenUnixFile("/dev/null",unixmode
);
631 if (!strcmp(DOSFS_Devices
[i
].name
,"CON")) {
636 to_dup
= GetStdHandle( STD_INPUT_HANDLE
);
639 to_dup
= GetStdHandle( STD_OUTPUT_HANDLE
);
642 FIXME(dosfs
,"can't open CON read/write\n");
643 return HFILE_ERROR32
;
646 if (!DuplicateHandle( GetCurrentProcess(), to_dup
, GetCurrentProcess(),
647 &handle
, 0, FALSE
, DUPLICATE_SAME_ACCESS
))
648 handle
= HFILE_ERROR32
;
651 if (!strcmp(DOSFS_Devices
[i
].name
,"SCSIMGR$")) {
652 if ((handle
= FILE_Alloc( &file
)) == INVALID_HANDLE_VALUE32
)
653 return HFILE_ERROR32
;
655 file
->unix_name
= HEAP_strdupA( SystemHeap
, 0, name
);
659 if (!strcmp(DOSFS_Devices
[i
].name
,"HPSCAN")) {
660 if ((handle
= FILE_Alloc( &file
)) == INVALID_HANDLE_VALUE32
)
661 return HFILE_ERROR32
;
663 file
->unix_name
= HEAP_strdupA( SystemHeap
, 0, name
);
667 FIXME(dosfs
,"device open %s not supported (yet)\n",DOSFS_Devices
[i
].name
);
668 return HFILE_ERROR32
;
672 return HFILE_ERROR32
;
676 /***********************************************************************
679 * Get the drive specified by a given path name (DOS or Unix format).
681 static int DOSFS_GetPathDrive( const char **name
)
684 const char *p
= *name
;
686 if (*p
&& (p
[1] == ':'))
688 drive
= toupper(*p
) - 'A';
691 else if (*p
== '/') /* Absolute Unix path? */
693 if ((drive
= DRIVE_FindDriveRoot( name
)) == -1)
695 MSG("Warning: %s not accessible from a DOS drive\n", *name
);
696 /* Assume it really was a DOS name */
697 drive
= DRIVE_GetCurrentDrive();
700 else drive
= DRIVE_GetCurrentDrive();
702 if (!DRIVE_IsValid(drive
))
704 DOS_ERROR( ER_InvalidDrive
, EC_MediaError
, SA_Abort
, EL_Disk
);
711 /***********************************************************************
714 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
715 * Unix name / short DOS name pair.
716 * Return FALSE if one of the path components does not exist. The last path
717 * component is only checked if 'check_last' is non-zero.
718 * The buffers pointed to by 'long_buf' and 'short_buf' must be
719 * at least MAX_PATHNAME_LEN long.
721 BOOL32
DOSFS_GetFullName( LPCSTR name
, BOOL32 check_last
, DOS_FULL_NAME
*full
)
725 char *p_l
, *p_s
, *root
;
727 TRACE(dosfs
, "%s (last=%d)\n",
730 if ((full
->drive
= DOSFS_GetPathDrive( &name
)) == -1) return FALSE
;
731 flags
= DRIVE_GetFlags( full
->drive
);
733 lstrcpyn32A( full
->long_name
, DRIVE_GetRoot( full
->drive
),
734 sizeof(full
->long_name
) );
735 if (full
->long_name
[1]) root
= full
->long_name
+ strlen(full
->long_name
);
736 else root
= full
->long_name
; /* root directory */
738 strcpy( full
->short_name
, "A:\\" );
739 full
->short_name
[0] += full
->drive
;
741 if ((*name
== '\\') || (*name
== '/')) /* Absolute path */
743 while ((*name
== '\\') || (*name
== '/')) name
++;
745 else /* Relative path */
747 lstrcpyn32A( root
+ 1, DRIVE_GetUnixCwd( full
->drive
),
748 sizeof(full
->long_name
) - (root
- full
->long_name
) - 1 );
749 if (root
[1]) *root
= '/';
750 lstrcpyn32A( full
->short_name
+ 3, DRIVE_GetDosCwd( full
->drive
),
751 sizeof(full
->short_name
) - 3 );
754 p_l
= full
->long_name
[1] ? full
->long_name
+ strlen(full
->long_name
)
756 p_s
= full
->short_name
[3] ? full
->short_name
+ strlen(full
->short_name
)
757 : full
->short_name
+ 2;
760 while (*name
&& found
)
762 /* Check for '.' and '..' */
766 if (IS_END_OF_NAME(name
[1]))
769 while ((*name
== '\\') || (*name
== '/')) name
++;
772 else if ((name
[1] == '.') && IS_END_OF_NAME(name
[2]))
775 while ((*name
== '\\') || (*name
== '/')) name
++;
776 while ((p_l
> root
) && (*p_l
!= '/')) p_l
--;
777 while ((p_s
> full
->short_name
+ 2) && (*p_s
!= '\\')) p_s
--;
778 *p_l
= *p_s
= '\0'; /* Remove trailing separator */
783 /* Make sure buffers are large enough */
785 if ((p_s
>= full
->short_name
+ sizeof(full
->short_name
) - 14) ||
786 (p_l
>= full
->long_name
+ sizeof(full
->long_name
) - 1))
788 DOS_ERROR( ER_PathNotFound
, EC_NotFound
, SA_Abort
, EL_Disk
);
792 /* Get the long and short name matching the file name */
794 if ((found
= DOSFS_FindUnixName( full
->long_name
, name
, p_l
+ 1,
795 sizeof(full
->long_name
) - (p_l
- full
->long_name
) - 1,
796 p_s
+ 1, !(flags
& DRIVE_CASE_SENSITIVE
) )))
802 while (!IS_END_OF_NAME(*name
)) name
++;
804 else if (!check_last
)
808 while (!IS_END_OF_NAME(*name
) &&
809 (p_s
< full
->short_name
+ sizeof(full
->short_name
) - 1) &&
810 (p_l
< full
->long_name
+ sizeof(full
->long_name
) - 1))
812 *p_s
++ = tolower(*name
);
813 /* If the drive is case-sensitive we want to create new */
814 /* files in lower-case otherwise we can't reopen them */
815 /* under the same short name. */
816 if (flags
& DRIVE_CASE_SENSITIVE
) *p_l
++ = tolower(*name
);
822 while ((*name
== '\\') || (*name
== '/')) name
++;
829 DOS_ERROR( ER_FileNotFound
, EC_NotFound
, SA_Abort
, EL_Disk
);
832 if (*name
) /* Not last */
834 DOS_ERROR( ER_PathNotFound
, EC_NotFound
, SA_Abort
, EL_Disk
);
838 if (!full
->long_name
[0]) strcpy( full
->long_name
, "/" );
839 if (!full
->short_name
[2]) strcpy( full
->short_name
+ 2, "\\" );
840 TRACE(dosfs
, "returning %s = %s\n",
841 full
->long_name
, full
->short_name
);
846 /***********************************************************************
847 * GetShortPathName32A (KERNEL32.271)
851 * longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
852 * *longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0
854 DWORD WINAPI
GetShortPathName32A( LPCSTR longpath
, LPSTR shortpath
,
857 DOS_FULL_NAME full_name
;
861 SetLastError(ERROR_INVALID_PARAMETER
);
867 SetLastError(ERROR_BAD_PATHNAME
);
871 /* FIXME: is it correct to always return a fully qualified short path? */
872 if (!DOSFS_GetFullName( longpath
, TRUE
, &full_name
))
874 SetLastError(ERROR_BAD_PATHNAME
);
877 lstrcpyn32A( shortpath
, full_name
.short_name
, shortlen
);
878 return strlen( full_name
.short_name
);
882 /***********************************************************************
883 * GetShortPathName32W (KERNEL32.272)
885 DWORD WINAPI
GetShortPathName32W( LPCWSTR longpath
, LPWSTR shortpath
,
888 DOS_FULL_NAME full_name
;
893 { SetLastError(ERROR_INVALID_PARAMETER
);
898 { SetLastError(ERROR_BAD_PATHNAME
);
903 longpathA
= HEAP_strdupWtoA( GetProcessHeap(), 0, longpath
);
905 /* FIXME: is it correct to always return a fully qualified short path? */
906 if (DOSFS_GetFullName( longpathA
, TRUE
, &full_name
))
908 ret
= strlen( full_name
.short_name
);
909 lstrcpynAtoW( shortpath
, full_name
.short_name
, shortlen
);
912 SetLastError(ERROR_BAD_PATHNAME
);
913 HeapFree( GetProcessHeap(), 0, longpathA
);
918 /***********************************************************************
919 * GetLongPathName32A (KERNEL32.xxx)
921 DWORD WINAPI
GetLongPathName32A( LPCSTR shortpath
, LPSTR longpath
,
924 DOS_FULL_NAME full_name
;
929 if (!DOSFS_GetFullName( shortpath
, TRUE
, &full_name
)) return 0;
930 lstrcpyn32A( longpath
, full_name
.short_name
, longlen
);
931 /* Do some hackery to get the long filename.
932 * FIXME: Would be better if it returned the
933 * long version of the directories too
935 longfilename
= strrchr(full_name
.long_name
, '/')+1;
936 if (longpath
!= NULL
) {
937 if ((p
= strrchr( longpath
, '\\' )) != NULL
) {
939 longlen
-= (p
-longpath
);
940 lstrcpyn32A( p
, longfilename
, longlen
);
944 ((strrchr( full_name
.short_name
, '\\' ) - full_name
.short_name
) + 1);
945 return shortpathlen
+ strlen( longfilename
);
949 /***********************************************************************
950 * GetLongPathName32W (KERNEL32.269)
952 DWORD WINAPI
GetLongPathName32W( LPCWSTR shortpath
, LPWSTR longpath
,
955 DOS_FULL_NAME full_name
;
957 LPSTR shortpathA
= HEAP_strdupWtoA( GetProcessHeap(), 0, shortpath
);
959 /* FIXME: is it correct to always return a fully qualified short path? */
960 if (DOSFS_GetFullName( shortpathA
, TRUE
, &full_name
))
962 ret
= strlen( full_name
.short_name
);
963 lstrcpynAtoW( longpath
, full_name
.long_name
, longlen
);
965 HeapFree( GetProcessHeap(), 0, shortpathA
);
970 /***********************************************************************
971 * DOSFS_DoGetFullPathName
973 * Implementation of GetFullPathName32A/W.
975 static DWORD
DOSFS_DoGetFullPathName( LPCSTR name
, DWORD len
, LPSTR result
,
978 char buffer
[MAX_PATHNAME_LEN
];
982 TRACE(dosfs
, "converting %s\n", name
);
984 if (!name
|| !result
) return 0;
986 if ((drive
= DOSFS_GetPathDrive( &name
)) == -1) return 0;
990 if (IS_END_OF_NAME(*name
) && (*name
)) /* Absolute path */
992 while ((*name
== '\\') || (*name
== '/')) name
++;
994 else /* Relative path or empty path */
997 lstrcpyn32A( p
, DRIVE_GetDosCwd(drive
), sizeof(buffer
) - 3 );
998 if (*p
) p
+= strlen(p
); else p
--;
1000 if (!*name
) /* empty path */
1008 if (IS_END_OF_NAME(name
[1]))
1011 while ((*name
== '\\') || (*name
== '/')) name
++;
1014 else if ((name
[1] == '.') && IS_END_OF_NAME(name
[2]))
1017 while ((*name
== '\\') || (*name
== '/')) name
++;
1018 while ((p
> buffer
+ 2) && (*p
!= '\\')) p
--;
1019 *p
= '\0'; /* Remove trailing separator */
1023 if (p
>= buffer
+ sizeof(buffer
) - 1)
1025 DOS_ERROR( ER_PathNotFound
, EC_NotFound
, SA_Abort
, EL_Disk
);
1029 while (!IS_END_OF_NAME(*name
) && (p
< buffer
+ sizeof(buffer
) - 1))
1032 while ((*name
== '\\') || (*name
== '/')) name
++;
1040 if (!(DRIVE_GetFlags(drive
) & DRIVE_CASE_PRESERVING
))
1041 CharUpper32A( buffer
);
1043 if (unicode
) lstrcpynAtoW( (LPWSTR
)result
, buffer
, len
);
1044 else lstrcpyn32A( result
, buffer
, len
);
1046 TRACE(dosfs
, "returning %s\n", buffer
);
1047 return strlen(buffer
);
1051 /***********************************************************************
1052 * GetFullPathName32A (KERNEL32.272)
1054 DWORD WINAPI
GetFullPathName32A( LPCSTR name
, DWORD len
, LPSTR buffer
,
1057 DWORD ret
= DOSFS_DoGetFullPathName( name
, len
, buffer
, FALSE
);
1058 if (ret
&& lastpart
)
1060 LPSTR p
= buffer
+ strlen(buffer
);
1062 /* if the path closed with '\', *lastpart is 0 */
1065 while ((p
> buffer
+ 2) && (*p
!= '\\')) p
--;
1068 else *lastpart
= NULL
;
1074 /***********************************************************************
1075 * GetFullPathName32W (KERNEL32.273)
1077 DWORD WINAPI
GetFullPathName32W( LPCWSTR name
, DWORD len
, LPWSTR buffer
,
1080 LPSTR nameA
= HEAP_strdupWtoA( GetProcessHeap(), 0, name
);
1081 DWORD ret
= DOSFS_DoGetFullPathName( nameA
, len
, (LPSTR
)buffer
, TRUE
);
1082 HeapFree( GetProcessHeap(), 0, nameA
);
1083 if (ret
&& lastpart
)
1085 LPWSTR p
= buffer
+ lstrlen32W(buffer
);
1086 while ((p
> buffer
+ 2) && (*p
!= '\\')) p
--;
1092 /***********************************************************************
1095 static int DOSFS_FindNextEx( FIND_FIRST_INFO
*info
, WIN32_FIND_DATA32A
*entry
)
1097 BYTE attr
= info
->attr
| FA_UNUSED
| FA_ARCHIVE
| FA_RDONLY
;
1098 UINT32 flags
= DRIVE_GetFlags( info
->drive
);
1099 char *p
, buffer
[MAX_PATHNAME_LEN
];
1100 const char *drive_path
;
1102 LPCSTR long_name
, short_name
;
1103 BY_HANDLE_FILE_INFORMATION fileinfo
;
1106 if ((info
->attr
& ~(FA_UNUSED
| FA_ARCHIVE
| FA_RDONLY
)) == FA_LABEL
)
1108 if (info
->cur_pos
) return 0;
1109 entry
->dwFileAttributes
= FILE_ATTRIBUTE_LABEL
;
1110 DOSFS_UnixTimeToFileTime( (time_t)0, &entry
->ftCreationTime
, 0 );
1111 DOSFS_UnixTimeToFileTime( (time_t)0, &entry
->ftLastAccessTime
, 0 );
1112 DOSFS_UnixTimeToFileTime( (time_t)0, &entry
->ftLastWriteTime
, 0 );
1113 entry
->nFileSizeHigh
= 0;
1114 entry
->nFileSizeLow
= 0;
1115 entry
->dwReserved0
= 0;
1116 entry
->dwReserved1
= 0;
1117 DOSFS_ToDosDTAFormat( DRIVE_GetLabel( info
->drive
), entry
->cFileName
);
1118 strcpy( entry
->cAlternateFileName
, entry
->cFileName
);
1123 drive_path
= info
->path
+ strlen(DRIVE_GetRoot( info
->drive
));
1124 while ((*drive_path
== '/') || (*drive_path
== '\\')) drive_path
++;
1125 drive_root
= !*drive_path
;
1127 lstrcpyn32A( buffer
, info
->path
, sizeof(buffer
) - 1 );
1128 strcat( buffer
, "/" );
1129 p
= buffer
+ strlen(buffer
);
1131 while (DOSFS_ReadDir( info
->dir
, &long_name
, &short_name
))
1135 /* Don't return '.' and '..' in the root of the drive */
1136 if (drive_root
&& (long_name
[0] == '.') &&
1137 (!long_name
[1] || ((long_name
[1] == '.') && !long_name
[2])))
1140 /* Check the long mask */
1142 if (info
->long_mask
)
1144 if (!DOSFS_MatchLong( info
->long_mask
, long_name
,
1145 flags
& DRIVE_CASE_SENSITIVE
)) continue;
1148 /* Check the short mask */
1150 if (info
->short_mask
)
1154 DOSFS_Hash( long_name
, dos_name
, TRUE
,
1155 !(flags
& DRIVE_CASE_SENSITIVE
) );
1156 short_name
= dos_name
;
1158 if (!DOSFS_MatchShort( info
->short_mask
, short_name
)) continue;
1161 /* Check the file attributes */
1163 lstrcpyn32A( p
, long_name
, sizeof(buffer
) - (int)(p
- buffer
) );
1164 if (!FILE_Stat( buffer
, &fileinfo
))
1166 WARN(dosfs
, "can't stat %s\n", buffer
);
1169 if (fileinfo
.dwFileAttributes
& ~attr
) continue;
1171 /* We now have a matching entry; fill the result and return */
1173 entry
->dwFileAttributes
= fileinfo
.dwFileAttributes
;
1174 entry
->ftCreationTime
= fileinfo
.ftCreationTime
;
1175 entry
->ftLastAccessTime
= fileinfo
.ftLastAccessTime
;
1176 entry
->ftLastWriteTime
= fileinfo
.ftLastWriteTime
;
1177 entry
->nFileSizeHigh
= fileinfo
.nFileSizeHigh
;
1178 entry
->nFileSizeLow
= fileinfo
.nFileSizeLow
;
1181 DOSFS_ToDosDTAFormat( short_name
, entry
->cAlternateFileName
);
1183 DOSFS_Hash( long_name
, entry
->cAlternateFileName
, FALSE
,
1184 !(flags
& DRIVE_CASE_SENSITIVE
) );
1186 lstrcpyn32A( entry
->cFileName
, long_name
, sizeof(entry
->cFileName
) );
1187 if (!(flags
& DRIVE_CASE_PRESERVING
)) CharLower32A( entry
->cFileName
);
1188 TRACE(dosfs
, "returning %s (%s) %02lx %ld\n",
1189 entry
->cFileName
, entry
->cAlternateFileName
,
1190 entry
->dwFileAttributes
, entry
->nFileSizeLow
);
1193 return 0; /* End of directory */
1196 /***********************************************************************
1199 * Find the next matching file. Return the number of entries read to find
1200 * the matching one, or 0 if no more entries.
1201 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
1202 * file name mask. Either or both can be NULL.
1204 * NOTE: This is supposed to be only called by the int21 emulation
1205 * routines. Thus, we should own the Win16Mutex anyway.
1206 * Nevertheless, we explicitly enter it to ensure the static
1207 * directory cache is protected.
1209 int DOSFS_FindNext( const char *path
, const char *short_mask
,
1210 const char *long_mask
, int drive
, BYTE attr
,
1211 int skip
, WIN32_FIND_DATA32A
*entry
)
1213 static FIND_FIRST_INFO info
= { NULL
};
1214 LPCSTR short_name
, long_name
;
1217 SYSLEVEL_EnterWin16Lock();
1219 /* Check the cached directory */
1220 if (!(info
.dir
&& info
.path
== path
&& info
.short_mask
== short_mask
1221 && info
.long_mask
== long_mask
&& info
.drive
== drive
1222 && info
.attr
== attr
&& info
.cur_pos
<= skip
))
1224 /* Not in the cache, open it anew */
1225 if (info
.dir
) DOSFS_CloseDir( info
.dir
);
1227 info
.path
= (LPSTR
)path
;
1228 info
.long_mask
= (LPSTR
)long_mask
;
1229 info
.short_mask
= (LPSTR
)short_mask
;
1233 info
.dir
= DOSFS_OpenDir( info
.path
);
1236 /* Skip to desired position */
1237 while (info
.cur_pos
< skip
)
1238 if (info
.dir
&& DOSFS_ReadDir( info
.dir
, &long_name
, &short_name
))
1243 if (info
.dir
&& info
.cur_pos
== skip
&& DOSFS_FindNextEx( &info
, entry
))
1244 count
= info
.cur_pos
- skip
;
1250 if (info
.dir
) DOSFS_CloseDir( info
.dir
);
1251 memset( &info
, '\0', sizeof(info
) );
1254 SYSLEVEL_LeaveWin16Lock();
1261 /*************************************************************************
1262 * FindFirstFile16 (KERNEL.413)
1264 HANDLE16 WINAPI
FindFirstFile16( LPCSTR path
, WIN32_FIND_DATA32A
*data
)
1266 DOS_FULL_NAME full_name
;
1268 FIND_FIRST_INFO
*info
;
1270 data
->dwReserved0
= data
->dwReserved1
= 0x0;
1271 if (!path
) return 0;
1272 if (!DOSFS_GetFullName( path
, FALSE
, &full_name
))
1273 return INVALID_HANDLE_VALUE16
;
1274 if (!(handle
= GlobalAlloc16( GMEM_MOVEABLE
, sizeof(FIND_FIRST_INFO
) )))
1275 return INVALID_HANDLE_VALUE16
;
1276 info
= (FIND_FIRST_INFO
*)GlobalLock16( handle
);
1277 info
->path
= HEAP_strdupA( SystemHeap
, 0, full_name
.long_name
);
1278 info
->long_mask
= strrchr( info
->path
, '/' );
1279 *(info
->long_mask
++) = '\0';
1280 info
->short_mask
= NULL
;
1282 if (path
[0] && (path
[1] == ':')) info
->drive
= toupper(*path
) - 'A';
1283 else info
->drive
= DRIVE_GetCurrentDrive();
1286 info
->dir
= DOSFS_OpenDir( info
->path
);
1288 GlobalUnlock16( handle
);
1289 if (!FindNextFile16( handle
, data
))
1291 FindClose16( handle
);
1292 DOS_ERROR( ER_NoMoreFiles
, EC_MediaError
, SA_Abort
, EL_Disk
);
1293 return INVALID_HANDLE_VALUE16
;
1299 /*************************************************************************
1300 * FindFirstFile32A (KERNEL32.123)
1302 HANDLE32 WINAPI
FindFirstFile32A( LPCSTR path
, WIN32_FIND_DATA32A
*data
)
1304 HANDLE32 handle
= FindFirstFile16( path
, data
);
1305 if (handle
== INVALID_HANDLE_VALUE16
) return INVALID_HANDLE_VALUE32
;
1310 /*************************************************************************
1311 * FindFirstFile32W (KERNEL32.124)
1313 HANDLE32 WINAPI
FindFirstFile32W( LPCWSTR path
, WIN32_FIND_DATA32W
*data
)
1315 WIN32_FIND_DATA32A dataA
;
1316 LPSTR pathA
= HEAP_strdupWtoA( GetProcessHeap(), 0, path
);
1317 HANDLE32 handle
= FindFirstFile32A( pathA
, &dataA
);
1318 HeapFree( GetProcessHeap(), 0, pathA
);
1319 if (handle
!= INVALID_HANDLE_VALUE32
)
1321 data
->dwFileAttributes
= dataA
.dwFileAttributes
;
1322 data
->ftCreationTime
= dataA
.ftCreationTime
;
1323 data
->ftLastAccessTime
= dataA
.ftLastAccessTime
;
1324 data
->ftLastWriteTime
= dataA
.ftLastWriteTime
;
1325 data
->nFileSizeHigh
= dataA
.nFileSizeHigh
;
1326 data
->nFileSizeLow
= dataA
.nFileSizeLow
;
1327 lstrcpyAtoW( data
->cFileName
, dataA
.cFileName
);
1328 lstrcpyAtoW( data
->cAlternateFileName
, dataA
.cAlternateFileName
);
1334 /*************************************************************************
1335 * FindNextFile16 (KERNEL.414)
1337 BOOL16 WINAPI
FindNextFile16( HANDLE16 handle
, WIN32_FIND_DATA32A
*data
)
1339 FIND_FIRST_INFO
*info
;
1341 if (!(info
= (FIND_FIRST_INFO
*)GlobalLock16( handle
)))
1343 DOS_ERROR( ER_InvalidHandle
, EC_ProgramError
, SA_Abort
, EL_Disk
);
1346 GlobalUnlock16( handle
);
1347 if (!info
->path
|| !info
->dir
)
1349 DOS_ERROR( ER_NoMoreFiles
, EC_MediaError
, SA_Abort
, EL_Disk
);
1352 if (!DOSFS_FindNextEx( info
, data
))
1354 DOSFS_CloseDir( info
->dir
); info
->dir
= NULL
;
1355 HeapFree( SystemHeap
, 0, info
->path
);
1356 info
->path
= info
->long_mask
= NULL
;
1357 DOS_ERROR( ER_NoMoreFiles
, EC_MediaError
, SA_Abort
, EL_Disk
);
1364 /*************************************************************************
1365 * FindNextFile32A (KERNEL32.126)
1367 BOOL32 WINAPI
FindNextFile32A( HANDLE32 handle
, WIN32_FIND_DATA32A
*data
)
1369 return FindNextFile16( handle
, data
);
1373 /*************************************************************************
1374 * FindNextFile32W (KERNEL32.127)
1376 BOOL32 WINAPI
FindNextFile32W( HANDLE32 handle
, WIN32_FIND_DATA32W
*data
)
1378 WIN32_FIND_DATA32A dataA
;
1379 if (!FindNextFile32A( handle
, &dataA
)) return FALSE
;
1380 data
->dwFileAttributes
= dataA
.dwFileAttributes
;
1381 data
->ftCreationTime
= dataA
.ftCreationTime
;
1382 data
->ftLastAccessTime
= dataA
.ftLastAccessTime
;
1383 data
->ftLastWriteTime
= dataA
.ftLastWriteTime
;
1384 data
->nFileSizeHigh
= dataA
.nFileSizeHigh
;
1385 data
->nFileSizeLow
= dataA
.nFileSizeLow
;
1386 lstrcpyAtoW( data
->cFileName
, dataA
.cFileName
);
1387 lstrcpyAtoW( data
->cAlternateFileName
, dataA
.cAlternateFileName
);
1392 /*************************************************************************
1393 * FindClose16 (KERNEL.415)
1395 BOOL16 WINAPI
FindClose16( HANDLE16 handle
)
1397 FIND_FIRST_INFO
*info
;
1399 if ((handle
== INVALID_HANDLE_VALUE16
) ||
1400 !(info
= (FIND_FIRST_INFO
*)GlobalLock16( handle
)))
1402 DOS_ERROR( ER_InvalidHandle
, EC_ProgramError
, SA_Abort
, EL_Disk
);
1405 if (info
->dir
) DOSFS_CloseDir( info
->dir
);
1406 if (info
->path
) HeapFree( SystemHeap
, 0, info
->path
);
1407 GlobalUnlock16( handle
);
1408 GlobalFree16( handle
);
1413 /*************************************************************************
1414 * FindClose32 (KERNEL32.119)
1416 BOOL32 WINAPI
FindClose32( HANDLE32 handle
)
1418 return FindClose16( (HANDLE16
)handle
);
1422 /***********************************************************************
1423 * DOSFS_UnixTimeToFileTime
1425 * Convert a Unix time to FILETIME format.
1426 * The FILETIME structure is a 64-bit value representing the number of
1427 * 100-nanosecond intervals since January 1, 1601, 0:00.
1428 * 'remainder' is the nonnegative number of 100-ns intervals
1429 * corresponding to the time fraction smaller than 1 second that
1430 * couldn't be stored in the time_t value.
1432 void DOSFS_UnixTimeToFileTime( time_t unix_time
, FILETIME
*filetime
,
1438 The time difference between 1 January 1601, 00:00:00 and
1439 1 January 1970, 00:00:00 is 369 years, plus the leap years
1440 from 1604 to 1968, excluding 1700, 1800, 1900.
1441 This makes (1968 - 1600) / 4 - 3 = 89 leap days, and a total
1444 Any day in that period had 24 * 60 * 60 = 86400 seconds.
1446 The time difference is 134774 * 86400 * 10000000, which can be written
1448 27111902 * 2^32 + 3577643008
1449 413 * 2^48 + 45534 * 2^32 + 54590 * 2^16 + 32768
1451 If you find that these constants are buggy, please change them in all
1452 instances in both conversion functions.
1455 There are two versions, one of them uses long long variables and
1456 is presumably faster but not ISO C. The other one uses standard C
1457 data types and operations but relies on the assumption that negative
1458 numbers are stored as 2's complement (-1 is 0xffff....). If this
1459 assumption is violated, dates before 1970 will not convert correctly.
1460 This should however work on any reasonable architecture where WINE
1465 Take care not to remove the casts. I have tested these functions
1466 (in both versions) for a lot of numbers. I would be interested in
1467 results on other compilers than GCC.
1469 The operations have been designed to account for the possibility
1470 of 64-bit time_t in future UNICES. Even the versions without
1471 internal long long numbers will work if time_t only is 64 bit.
1472 A 32-bit shift, which was necessary for that operation, turned out
1473 not to work correctly in GCC, besides giving the warning. So I
1474 used a double 16-bit shift instead. Numbers are in the ISO version
1475 represented by three limbs, the most significant with 32 bit, the
1476 other two with 16 bit each.
1478 As the modulo-operator % is not well-defined for negative numbers,
1479 negative divisors have been avoided in DOSFS_FileTimeToUnixTime.
1481 There might be quicker ways to do this in C. Certainly so in
1484 Claus Fischer, fischer@iue.tuwien.ac.at
1487 #if (SIZEOF_LONG_LONG >= 8)
1488 # define USE_LONG_LONG 1
1490 # define USE_LONG_LONG 0
1493 #if USE_LONG_LONG /* gcc supports long long type */
1495 long long int t
= unix_time
;
1497 t
+= 116444736000000000LL;
1499 filetime
->dwLowDateTime
= (UINT32
)t
;
1500 filetime
->dwHighDateTime
= (UINT32
)(t
>> 32);
1502 #else /* ISO version */
1504 UINT32 a0
; /* 16 bit, low bits */
1505 UINT32 a1
; /* 16 bit, medium bits */
1506 UINT32 a2
; /* 32 bit, high bits */
1508 /* Copy the unix time to a2/a1/a0 */
1509 a0
= unix_time
& 0xffff;
1510 a1
= (unix_time
>> 16) & 0xffff;
1511 /* This is obsolete if unix_time is only 32 bits, but it does not hurt.
1512 Do not replace this by >> 32, it gives a compiler warning and it does
1514 a2
= (unix_time
>= 0 ? (unix_time
>> 16) >> 16 :
1515 ~((~unix_time
>> 16) >> 16));
1517 /* Multiply a by 10000000 (a = a2/a1/a0)
1518 Split the factor into 10000 * 1000 which are both less than 0xffff. */
1520 a1
= a1
* 10000 + (a0
>> 16);
1521 a2
= a2
* 10000 + (a1
>> 16);
1526 a1
= a1
* 1000 + (a0
>> 16);
1527 a2
= a2
* 1000 + (a1
>> 16);
1531 /* Add the time difference and the remainder */
1532 a0
+= 32768 + (remainder
& 0xffff);
1533 a1
+= 54590 + (remainder
>> 16 ) + (a0
>> 16);
1534 a2
+= 27111902 + (a1
>> 16);
1539 filetime
->dwLowDateTime
= (a1
<< 16) + a0
;
1540 filetime
->dwHighDateTime
= a2
;
1545 /***********************************************************************
1546 * DOSFS_FileTimeToUnixTime
1548 * Convert a FILETIME format to Unix time.
1549 * If not NULL, 'remainder' contains the fractional part of the filetime,
1550 * in the range of [0..9999999] (even if time_t is negative).
1552 time_t DOSFS_FileTimeToUnixTime( const FILETIME
*filetime
, DWORD
*remainder
)
1554 /* Read the comment in the function DOSFS_UnixTimeToFileTime. */
1557 long long int t
= filetime
->dwHighDateTime
;
1559 t
+= (UINT32
)filetime
->dwLowDateTime
;
1560 t
-= 116444736000000000LL;
1563 if (remainder
) *remainder
= 9999999 - (-t
- 1) % 10000000;
1564 return -1 - ((-t
- 1) / 10000000);
1568 if (remainder
) *remainder
= t
% 10000000;
1569 return t
/ 10000000;
1572 #else /* ISO version */
1574 UINT32 a0
; /* 16 bit, low bits */
1575 UINT32 a1
; /* 16 bit, medium bits */
1576 UINT32 a2
; /* 32 bit, high bits */
1577 UINT32 r
; /* remainder of division */
1578 unsigned int carry
; /* carry bit for subtraction */
1579 int negative
; /* whether a represents a negative value */
1581 /* Copy the time values to a2/a1/a0 */
1582 a2
= (UINT32
)filetime
->dwHighDateTime
;
1583 a1
= ((UINT32
)filetime
->dwLowDateTime
) >> 16;
1584 a0
= ((UINT32
)filetime
->dwLowDateTime
) & 0xffff;
1586 /* Subtract the time difference */
1587 if (a0
>= 32768 ) a0
-= 32768 , carry
= 0;
1588 else a0
+= (1 << 16) - 32768 , carry
= 1;
1590 if (a1
>= 54590 + carry
) a1
-= 54590 + carry
, carry
= 0;
1591 else a1
+= (1 << 16) - 54590 - carry
, carry
= 1;
1593 a2
-= 27111902 + carry
;
1595 /* If a is negative, replace a by (-1-a) */
1596 negative
= (a2
>= ((UINT32
)1) << 31);
1599 /* Set a to -a - 1 (a is a2/a1/a0) */
1605 /* Divide a by 10000000 (a = a2/a1/a0), put the rest into r.
1606 Split the divisor into 10000 * 1000 which are both less than 0xffff. */
1607 a1
+= (a2
% 10000) << 16;
1609 a0
+= (a1
% 10000) << 16;
1614 a1
+= (a2
% 1000) << 16;
1616 a0
+= (a1
% 1000) << 16;
1618 r
+= (a0
% 1000) * 10000;
1621 /* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
1624 /* Set a to -a - 1 (a is a2/a1/a0) */
1632 if (remainder
) *remainder
= r
;
1634 /* Do not replace this by << 32, it gives a compiler warning and it does
1636 return ((((time_t)a2
) << 16) << 16) + (a1
<< 16) + a0
;
1641 /***********************************************************************
1642 * DosDateTimeToFileTime (KERNEL32.76)
1644 BOOL32 WINAPI
DosDateTimeToFileTime( WORD fatdate
, WORD fattime
, LPFILETIME ft
)
1648 newtm
.tm_sec
= (fattime
& 0x1f) * 2;
1649 newtm
.tm_min
= (fattime
>> 5) & 0x3f;
1650 newtm
.tm_hour
= (fattime
>> 11);
1651 newtm
.tm_mday
= (fatdate
& 0x1f);
1652 newtm
.tm_mon
= ((fatdate
>> 5) & 0x0f) - 1;
1653 newtm
.tm_year
= (fatdate
>> 9) + 80;
1654 DOSFS_UnixTimeToFileTime( mktime( &newtm
), ft
, 0 );
1659 /***********************************************************************
1660 * FileTimeToDosDateTime (KERNEL32.111)
1662 BOOL32 WINAPI
FileTimeToDosDateTime( const FILETIME
*ft
, LPWORD fatdate
,
1665 time_t unixtime
= DOSFS_FileTimeToUnixTime( ft
, NULL
);
1666 struct tm
*tm
= localtime( &unixtime
);
1668 *fattime
= (tm
->tm_hour
<< 11) + (tm
->tm_min
<< 5) + (tm
->tm_sec
/ 2);
1670 *fatdate
= ((tm
->tm_year
- 80) << 9) + ((tm
->tm_mon
+ 1) << 5)
1676 /***********************************************************************
1677 * LocalFileTimeToFileTime (KERNEL32.373)
1679 BOOL32 WINAPI
LocalFileTimeToFileTime( const FILETIME
*localft
,
1685 /* convert from local to UTC. Perhaps not correct. FIXME */
1686 time_t unixtime
= DOSFS_FileTimeToUnixTime( localft
, &remainder
);
1687 xtm
= gmtime( &unixtime
);
1688 DOSFS_UnixTimeToFileTime( mktime(xtm
), utcft
, remainder
);
1693 /***********************************************************************
1694 * FileTimeToLocalFileTime (KERNEL32.112)
1696 BOOL32 WINAPI
FileTimeToLocalFileTime( const FILETIME
*utcft
,
1697 LPFILETIME localft
)
1700 /* convert from UTC to local. Perhaps not correct. FIXME */
1701 time_t unixtime
= DOSFS_FileTimeToUnixTime( utcft
, &remainder
);
1703 struct tm
*xtm
= localtime( &unixtime
);
1706 localtime
= timegm(xtm
);
1707 DOSFS_UnixTimeToFileTime( localtime
, localft
, remainder
);
1710 struct tm
*xtm
,*gtm
;
1713 xtm
= localtime( &unixtime
);
1714 gtm
= gmtime( &unixtime
);
1715 time1
= mktime(xtm
);
1716 time2
= mktime(gtm
);
1717 DOSFS_UnixTimeToFileTime( 2*time1
-time2
, localft
, remainder
);
1723 /***********************************************************************
1724 * FileTimeToSystemTime (KERNEL32.113)
1726 BOOL32 WINAPI
FileTimeToSystemTime( const FILETIME
*ft
, LPSYSTEMTIME syst
)
1730 time_t xtime
= DOSFS_FileTimeToUnixTime( ft
, &remainder
);
1731 xtm
= gmtime(&xtime
);
1732 syst
->wYear
= xtm
->tm_year
+1900;
1733 syst
->wMonth
= xtm
->tm_mon
+ 1;
1734 syst
->wDayOfWeek
= xtm
->tm_wday
;
1735 syst
->wDay
= xtm
->tm_mday
;
1736 syst
->wHour
= xtm
->tm_hour
;
1737 syst
->wMinute
= xtm
->tm_min
;
1738 syst
->wSecond
= xtm
->tm_sec
;
1739 syst
->wMilliseconds
= remainder
/ 10000;
1743 /***********************************************************************
1744 * QueryDosDeviceA (KERNEL32.413)
1746 * returns array of strings terminated by \0, terminated by \0
1748 DWORD WINAPI
QueryDosDevice32A(LPCSTR devname
,LPSTR target
,DWORD bufsize
)
1753 TRACE(dosfs
,"(%s,...)\n",devname
?devname
:"<null>");
1755 /* return known MSDOS devices */
1756 lstrcpy32A(buffer
,"CON COM1 COM2 LPT1 NUL ");
1757 while ((s
=strchr(buffer
,' ')))
1760 lstrcpyn32A(target
,buffer
,bufsize
);
1761 return strlen(buffer
);
1763 lstrcpy32A(buffer
,"\\DEV\\");
1764 lstrcat32A(buffer
,devname
);
1765 if ((s
=strchr(buffer
,':'))) *s
='\0';
1766 lstrcpyn32A(target
,buffer
,bufsize
);
1767 return strlen(buffer
);
1771 /***********************************************************************
1772 * QueryDosDeviceW (KERNEL32.414)
1774 * returns array of strings terminated by \0, terminated by \0
1776 DWORD WINAPI
QueryDosDevice32W(LPCWSTR devname
,LPWSTR target
,DWORD bufsize
)
1778 LPSTR devnameA
= devname
?HEAP_strdupWtoA(GetProcessHeap(),0,devname
):NULL
;
1779 LPSTR targetA
= (LPSTR
)HEAP_xalloc(GetProcessHeap(),0,bufsize
);
1780 DWORD ret
= QueryDosDevice32A(devnameA
,targetA
,bufsize
);
1782 lstrcpynAtoW(target
,targetA
,bufsize
);
1783 if (devnameA
) HeapFree(GetProcessHeap(),0,devnameA
);
1784 if (targetA
) HeapFree(GetProcessHeap(),0,targetA
);
1789 /***********************************************************************
1790 * SystemTimeToFileTime (KERNEL32.526)
1792 BOOL32 WINAPI
SystemTimeToFileTime( const SYSTEMTIME
*syst
, LPFILETIME ft
)
1798 struct tm xtm
,*local_tm
,*utc_tm
;
1799 time_t localtim
,utctime
;
1802 xtm
.tm_year
= syst
->wYear
-1900;
1803 xtm
.tm_mon
= syst
->wMonth
- 1;
1804 xtm
.tm_wday
= syst
->wDayOfWeek
;
1805 xtm
.tm_mday
= syst
->wDay
;
1806 xtm
.tm_hour
= syst
->wHour
;
1807 xtm
.tm_min
= syst
->wMinute
;
1808 xtm
.tm_sec
= syst
->wSecond
; /* this is UTC */
1811 utctime
= timegm(&xtm
);
1812 DOSFS_UnixTimeToFileTime( utctime
, ft
,
1813 syst
->wMilliseconds
* 10000 );
1815 localtim
= mktime(&xtm
); /* now we've got local time */
1816 local_tm
= localtime(&localtim
);
1817 utc_tm
= gmtime(&localtim
);
1818 utctime
= mktime(utc_tm
);
1819 DOSFS_UnixTimeToFileTime( 2*localtim
-utctime
, ft
,
1820 syst
->wMilliseconds
* 10000 );
1825 BOOL32 WINAPI
DefineDosDevice32A(DWORD flags
,LPCSTR devname
,LPCSTR targetpath
) {
1826 FIXME(dosfs
,"(0x%08lx,%s,%s),stub!\n",flags
,devname
,targetpath
);
1827 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);