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
, DWORD access
)
615 if (!name
) return (HFILE32
)NULL
; /* if FILE_DupUnixHandle was used */
616 if (name
[0] && (name
[1] == ':')) name
+= 2;
617 if ((p
= strrchr( name
, '/' ))) name
= p
+ 1;
618 if ((p
= strrchr( name
, '\\' ))) name
= p
+ 1;
619 for (i
= 0; i
< sizeof(DOSFS_Devices
)/sizeof(DOSFS_Devices
[0]); i
++)
621 const char *dev
= DOSFS_Devices
[i
].name
;
622 if (!lstrncmpi32A( dev
, name
, strlen(dev
) ))
624 p
= name
+ strlen( dev
);
625 if (!*p
|| (*p
== '.')) {
627 if (!strcmp(DOSFS_Devices
[i
].name
,"NUL"))
628 return FILE_CreateFile( "/dev/null", access
,
629 FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
,
630 OPEN_EXISTING
, 0, -1 );
631 if (!strcmp(DOSFS_Devices
[i
].name
,"CON")) {
634 switch (access
& (GENERIC_READ
|GENERIC_WRITE
)) {
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 !strcmp(DOSFS_Devices
[i
].name
,"HPSCAN"))
654 /* FIXME: should keep the name somewhere */
655 return FILE_CreateFile( "/dev/null", access
,
656 FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
,
657 OPEN_EXISTING
, 0, -1 );
659 FIXME(dosfs
,"device open %s not supported (yet)\n",DOSFS_Devices
[i
].name
);
660 return HFILE_ERROR32
;
664 return HFILE_ERROR32
;
668 /***********************************************************************
671 * Get the drive specified by a given path name (DOS or Unix format).
673 static int DOSFS_GetPathDrive( const char **name
)
676 const char *p
= *name
;
678 if (*p
&& (p
[1] == ':'))
680 drive
= toupper(*p
) - 'A';
683 else if (*p
== '/') /* Absolute Unix path? */
685 if ((drive
= DRIVE_FindDriveRoot( name
)) == -1)
687 MSG("Warning: %s not accessible from a DOS drive\n", *name
);
688 /* Assume it really was a DOS name */
689 drive
= DRIVE_GetCurrentDrive();
692 else drive
= DRIVE_GetCurrentDrive();
694 if (!DRIVE_IsValid(drive
))
696 DOS_ERROR( ER_InvalidDrive
, EC_MediaError
, SA_Abort
, EL_Disk
);
703 /***********************************************************************
706 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
707 * Unix name / short DOS name pair.
708 * Return FALSE if one of the path components does not exist. The last path
709 * component is only checked if 'check_last' is non-zero.
710 * The buffers pointed to by 'long_buf' and 'short_buf' must be
711 * at least MAX_PATHNAME_LEN long.
713 BOOL32
DOSFS_GetFullName( LPCSTR name
, BOOL32 check_last
, DOS_FULL_NAME
*full
)
717 char *p_l
, *p_s
, *root
;
719 TRACE(dosfs
, "%s (last=%d)\n",
722 if ((full
->drive
= DOSFS_GetPathDrive( &name
)) == -1) return FALSE
;
723 flags
= DRIVE_GetFlags( full
->drive
);
725 lstrcpyn32A( full
->long_name
, DRIVE_GetRoot( full
->drive
),
726 sizeof(full
->long_name
) );
727 if (full
->long_name
[1]) root
= full
->long_name
+ strlen(full
->long_name
);
728 else root
= full
->long_name
; /* root directory */
730 strcpy( full
->short_name
, "A:\\" );
731 full
->short_name
[0] += full
->drive
;
733 if ((*name
== '\\') || (*name
== '/')) /* Absolute path */
735 while ((*name
== '\\') || (*name
== '/')) name
++;
737 else /* Relative path */
739 lstrcpyn32A( root
+ 1, DRIVE_GetUnixCwd( full
->drive
),
740 sizeof(full
->long_name
) - (root
- full
->long_name
) - 1 );
741 if (root
[1]) *root
= '/';
742 lstrcpyn32A( full
->short_name
+ 3, DRIVE_GetDosCwd( full
->drive
),
743 sizeof(full
->short_name
) - 3 );
746 p_l
= full
->long_name
[1] ? full
->long_name
+ strlen(full
->long_name
)
748 p_s
= full
->short_name
[3] ? full
->short_name
+ strlen(full
->short_name
)
749 : full
->short_name
+ 2;
752 while (*name
&& found
)
754 /* Check for '.' and '..' */
758 if (IS_END_OF_NAME(name
[1]))
761 while ((*name
== '\\') || (*name
== '/')) name
++;
764 else if ((name
[1] == '.') && IS_END_OF_NAME(name
[2]))
767 while ((*name
== '\\') || (*name
== '/')) name
++;
768 while ((p_l
> root
) && (*p_l
!= '/')) p_l
--;
769 while ((p_s
> full
->short_name
+ 2) && (*p_s
!= '\\')) p_s
--;
770 *p_l
= *p_s
= '\0'; /* Remove trailing separator */
775 /* Make sure buffers are large enough */
777 if ((p_s
>= full
->short_name
+ sizeof(full
->short_name
) - 14) ||
778 (p_l
>= full
->long_name
+ sizeof(full
->long_name
) - 1))
780 DOS_ERROR( ER_PathNotFound
, EC_NotFound
, SA_Abort
, EL_Disk
);
784 /* Get the long and short name matching the file name */
786 if ((found
= DOSFS_FindUnixName( full
->long_name
, name
, p_l
+ 1,
787 sizeof(full
->long_name
) - (p_l
- full
->long_name
) - 1,
788 p_s
+ 1, !(flags
& DRIVE_CASE_SENSITIVE
) )))
794 while (!IS_END_OF_NAME(*name
)) name
++;
796 else if (!check_last
)
800 while (!IS_END_OF_NAME(*name
) &&
801 (p_s
< full
->short_name
+ sizeof(full
->short_name
) - 1) &&
802 (p_l
< full
->long_name
+ sizeof(full
->long_name
) - 1))
804 *p_s
++ = tolower(*name
);
805 /* If the drive is case-sensitive we want to create new */
806 /* files in lower-case otherwise we can't reopen them */
807 /* under the same short name. */
808 if (flags
& DRIVE_CASE_SENSITIVE
) *p_l
++ = tolower(*name
);
814 while ((*name
== '\\') || (*name
== '/')) name
++;
821 DOS_ERROR( ER_FileNotFound
, EC_NotFound
, SA_Abort
, EL_Disk
);
824 if (*name
) /* Not last */
826 DOS_ERROR( ER_PathNotFound
, EC_NotFound
, SA_Abort
, EL_Disk
);
830 if (!full
->long_name
[0]) strcpy( full
->long_name
, "/" );
831 if (!full
->short_name
[2]) strcpy( full
->short_name
+ 2, "\\" );
832 TRACE(dosfs
, "returning %s = %s\n",
833 full
->long_name
, full
->short_name
);
838 /***********************************************************************
839 * GetShortPathName32A (KERNEL32.271)
843 * longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
844 * *longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0
846 DWORD WINAPI
GetShortPathName32A( LPCSTR longpath
, LPSTR shortpath
,
849 DOS_FULL_NAME full_name
;
853 SetLastError(ERROR_INVALID_PARAMETER
);
859 SetLastError(ERROR_BAD_PATHNAME
);
863 /* FIXME: is it correct to always return a fully qualified short path? */
864 if (!DOSFS_GetFullName( longpath
, TRUE
, &full_name
))
866 SetLastError(ERROR_BAD_PATHNAME
);
869 lstrcpyn32A( shortpath
, full_name
.short_name
, shortlen
);
870 return strlen( full_name
.short_name
);
874 /***********************************************************************
875 * GetShortPathName32W (KERNEL32.272)
877 DWORD WINAPI
GetShortPathName32W( LPCWSTR longpath
, LPWSTR shortpath
,
880 DOS_FULL_NAME full_name
;
885 { SetLastError(ERROR_INVALID_PARAMETER
);
890 { SetLastError(ERROR_BAD_PATHNAME
);
895 longpathA
= HEAP_strdupWtoA( GetProcessHeap(), 0, longpath
);
897 /* FIXME: is it correct to always return a fully qualified short path? */
898 if (DOSFS_GetFullName( longpathA
, TRUE
, &full_name
))
900 ret
= strlen( full_name
.short_name
);
901 lstrcpynAtoW( shortpath
, full_name
.short_name
, shortlen
);
904 SetLastError(ERROR_BAD_PATHNAME
);
905 HeapFree( GetProcessHeap(), 0, longpathA
);
910 /***********************************************************************
911 * GetLongPathName32A (KERNEL32.xxx)
913 DWORD WINAPI
GetLongPathName32A( LPCSTR shortpath
, LPSTR longpath
,
916 DOS_FULL_NAME full_name
;
921 if (!DOSFS_GetFullName( shortpath
, TRUE
, &full_name
)) return 0;
922 lstrcpyn32A( longpath
, full_name
.short_name
, longlen
);
923 /* Do some hackery to get the long filename.
924 * FIXME: Would be better if it returned the
925 * long version of the directories too
927 longfilename
= strrchr(full_name
.long_name
, '/')+1;
928 if (longpath
!= NULL
) {
929 if ((p
= strrchr( longpath
, '\\' )) != NULL
) {
931 longlen
-= (p
-longpath
);
932 lstrcpyn32A( p
, longfilename
, longlen
);
936 ((strrchr( full_name
.short_name
, '\\' ) - full_name
.short_name
) + 1);
937 return shortpathlen
+ strlen( longfilename
);
941 /***********************************************************************
942 * GetLongPathName32W (KERNEL32.269)
944 DWORD WINAPI
GetLongPathName32W( LPCWSTR shortpath
, LPWSTR longpath
,
947 DOS_FULL_NAME full_name
;
949 LPSTR shortpathA
= HEAP_strdupWtoA( GetProcessHeap(), 0, shortpath
);
951 /* FIXME: is it correct to always return a fully qualified short path? */
952 if (DOSFS_GetFullName( shortpathA
, TRUE
, &full_name
))
954 ret
= strlen( full_name
.short_name
);
955 lstrcpynAtoW( longpath
, full_name
.long_name
, longlen
);
957 HeapFree( GetProcessHeap(), 0, shortpathA
);
962 /***********************************************************************
963 * DOSFS_DoGetFullPathName
965 * Implementation of GetFullPathName32A/W.
967 static DWORD
DOSFS_DoGetFullPathName( LPCSTR name
, DWORD len
, LPSTR result
,
970 char buffer
[MAX_PATHNAME_LEN
];
974 TRACE(dosfs
, "converting %s\n", name
);
976 if (!name
|| !result
) return 0;
978 if ((drive
= DOSFS_GetPathDrive( &name
)) == -1) return 0;
982 if (IS_END_OF_NAME(*name
) && (*name
)) /* Absolute path */
984 while ((*name
== '\\') || (*name
== '/')) name
++;
986 else /* Relative path or empty path */
989 lstrcpyn32A( p
, DRIVE_GetDosCwd(drive
), sizeof(buffer
) - 3 );
990 if (*p
) p
+= strlen(p
); else p
--;
992 if (!*name
) /* empty path */
1000 if (IS_END_OF_NAME(name
[1]))
1003 while ((*name
== '\\') || (*name
== '/')) name
++;
1006 else if ((name
[1] == '.') && IS_END_OF_NAME(name
[2]))
1009 while ((*name
== '\\') || (*name
== '/')) name
++;
1010 while ((p
> buffer
+ 2) && (*p
!= '\\')) p
--;
1011 *p
= '\0'; /* Remove trailing separator */
1015 if (p
>= buffer
+ sizeof(buffer
) - 1)
1017 DOS_ERROR( ER_PathNotFound
, EC_NotFound
, SA_Abort
, EL_Disk
);
1021 while (!IS_END_OF_NAME(*name
) && (p
< buffer
+ sizeof(buffer
) - 1))
1024 while ((*name
== '\\') || (*name
== '/')) name
++;
1032 if (!(DRIVE_GetFlags(drive
) & DRIVE_CASE_PRESERVING
))
1033 CharUpper32A( buffer
);
1035 if (unicode
) lstrcpynAtoW( (LPWSTR
)result
, buffer
, len
);
1036 else lstrcpyn32A( result
, buffer
, len
);
1038 TRACE(dosfs
, "returning %s\n", buffer
);
1039 return strlen(buffer
);
1043 /***********************************************************************
1044 * GetFullPathName32A (KERNEL32.272)
1046 DWORD WINAPI
GetFullPathName32A( LPCSTR name
, DWORD len
, LPSTR buffer
,
1049 DWORD ret
= DOSFS_DoGetFullPathName( name
, len
, buffer
, FALSE
);
1050 if (ret
&& lastpart
)
1052 LPSTR p
= buffer
+ strlen(buffer
);
1054 /* if the path closed with '\', *lastpart is 0 */
1057 while ((p
> buffer
+ 2) && (*p
!= '\\')) p
--;
1060 else *lastpart
= NULL
;
1066 /***********************************************************************
1067 * GetFullPathName32W (KERNEL32.273)
1069 DWORD WINAPI
GetFullPathName32W( LPCWSTR name
, DWORD len
, LPWSTR buffer
,
1072 LPSTR nameA
= HEAP_strdupWtoA( GetProcessHeap(), 0, name
);
1073 DWORD ret
= DOSFS_DoGetFullPathName( nameA
, len
, (LPSTR
)buffer
, TRUE
);
1074 HeapFree( GetProcessHeap(), 0, nameA
);
1075 if (ret
&& lastpart
)
1077 LPWSTR p
= buffer
+ lstrlen32W(buffer
);
1078 while ((p
> buffer
+ 2) && (*p
!= '\\')) p
--;
1084 /***********************************************************************
1087 static int DOSFS_FindNextEx( FIND_FIRST_INFO
*info
, WIN32_FIND_DATA32A
*entry
)
1089 BYTE attr
= info
->attr
| FA_UNUSED
| FA_ARCHIVE
| FA_RDONLY
;
1090 UINT32 flags
= DRIVE_GetFlags( info
->drive
);
1091 char *p
, buffer
[MAX_PATHNAME_LEN
];
1092 const char *drive_path
;
1094 LPCSTR long_name
, short_name
;
1095 BY_HANDLE_FILE_INFORMATION fileinfo
;
1098 if ((info
->attr
& ~(FA_UNUSED
| FA_ARCHIVE
| FA_RDONLY
)) == FA_LABEL
)
1100 if (info
->cur_pos
) return 0;
1101 entry
->dwFileAttributes
= FILE_ATTRIBUTE_LABEL
;
1102 DOSFS_UnixTimeToFileTime( (time_t)0, &entry
->ftCreationTime
, 0 );
1103 DOSFS_UnixTimeToFileTime( (time_t)0, &entry
->ftLastAccessTime
, 0 );
1104 DOSFS_UnixTimeToFileTime( (time_t)0, &entry
->ftLastWriteTime
, 0 );
1105 entry
->nFileSizeHigh
= 0;
1106 entry
->nFileSizeLow
= 0;
1107 entry
->dwReserved0
= 0;
1108 entry
->dwReserved1
= 0;
1109 DOSFS_ToDosDTAFormat( DRIVE_GetLabel( info
->drive
), entry
->cFileName
);
1110 strcpy( entry
->cAlternateFileName
, entry
->cFileName
);
1115 drive_path
= info
->path
+ strlen(DRIVE_GetRoot( info
->drive
));
1116 while ((*drive_path
== '/') || (*drive_path
== '\\')) drive_path
++;
1117 drive_root
= !*drive_path
;
1119 lstrcpyn32A( buffer
, info
->path
, sizeof(buffer
) - 1 );
1120 strcat( buffer
, "/" );
1121 p
= buffer
+ strlen(buffer
);
1123 while (DOSFS_ReadDir( info
->dir
, &long_name
, &short_name
))
1127 /* Don't return '.' and '..' in the root of the drive */
1128 if (drive_root
&& (long_name
[0] == '.') &&
1129 (!long_name
[1] || ((long_name
[1] == '.') && !long_name
[2])))
1132 /* Check the long mask */
1134 if (info
->long_mask
)
1136 if (!DOSFS_MatchLong( info
->long_mask
, long_name
,
1137 flags
& DRIVE_CASE_SENSITIVE
)) continue;
1140 /* Check the short mask */
1142 if (info
->short_mask
)
1146 DOSFS_Hash( long_name
, dos_name
, TRUE
,
1147 !(flags
& DRIVE_CASE_SENSITIVE
) );
1148 short_name
= dos_name
;
1150 if (!DOSFS_MatchShort( info
->short_mask
, short_name
)) continue;
1153 /* Check the file attributes */
1155 lstrcpyn32A( p
, long_name
, sizeof(buffer
) - (int)(p
- buffer
) );
1156 if (!FILE_Stat( buffer
, &fileinfo
))
1158 WARN(dosfs
, "can't stat %s\n", buffer
);
1161 if (fileinfo
.dwFileAttributes
& ~attr
) continue;
1163 /* We now have a matching entry; fill the result and return */
1165 entry
->dwFileAttributes
= fileinfo
.dwFileAttributes
;
1166 entry
->ftCreationTime
= fileinfo
.ftCreationTime
;
1167 entry
->ftLastAccessTime
= fileinfo
.ftLastAccessTime
;
1168 entry
->ftLastWriteTime
= fileinfo
.ftLastWriteTime
;
1169 entry
->nFileSizeHigh
= fileinfo
.nFileSizeHigh
;
1170 entry
->nFileSizeLow
= fileinfo
.nFileSizeLow
;
1173 DOSFS_ToDosDTAFormat( short_name
, entry
->cAlternateFileName
);
1175 DOSFS_Hash( long_name
, entry
->cAlternateFileName
, FALSE
,
1176 !(flags
& DRIVE_CASE_SENSITIVE
) );
1178 lstrcpyn32A( entry
->cFileName
, long_name
, sizeof(entry
->cFileName
) );
1179 if (!(flags
& DRIVE_CASE_PRESERVING
)) CharLower32A( entry
->cFileName
);
1180 TRACE(dosfs
, "returning %s (%s) %02lx %ld\n",
1181 entry
->cFileName
, entry
->cAlternateFileName
,
1182 entry
->dwFileAttributes
, entry
->nFileSizeLow
);
1185 return 0; /* End of directory */
1188 /***********************************************************************
1191 * Find the next matching file. Return the number of entries read to find
1192 * the matching one, or 0 if no more entries.
1193 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
1194 * file name mask. Either or both can be NULL.
1196 * NOTE: This is supposed to be only called by the int21 emulation
1197 * routines. Thus, we should own the Win16Mutex anyway.
1198 * Nevertheless, we explicitly enter it to ensure the static
1199 * directory cache is protected.
1201 int DOSFS_FindNext( const char *path
, const char *short_mask
,
1202 const char *long_mask
, int drive
, BYTE attr
,
1203 int skip
, WIN32_FIND_DATA32A
*entry
)
1205 static FIND_FIRST_INFO info
= { NULL
};
1206 LPCSTR short_name
, long_name
;
1209 SYSLEVEL_EnterWin16Lock();
1211 /* Check the cached directory */
1212 if (!(info
.dir
&& info
.path
== path
&& info
.short_mask
== short_mask
1213 && info
.long_mask
== long_mask
&& info
.drive
== drive
1214 && info
.attr
== attr
&& info
.cur_pos
<= skip
))
1216 /* Not in the cache, open it anew */
1217 if (info
.dir
) DOSFS_CloseDir( info
.dir
);
1219 info
.path
= (LPSTR
)path
;
1220 info
.long_mask
= (LPSTR
)long_mask
;
1221 info
.short_mask
= (LPSTR
)short_mask
;
1225 info
.dir
= DOSFS_OpenDir( info
.path
);
1228 /* Skip to desired position */
1229 while (info
.cur_pos
< skip
)
1230 if (info
.dir
&& DOSFS_ReadDir( info
.dir
, &long_name
, &short_name
))
1235 if (info
.dir
&& info
.cur_pos
== skip
&& DOSFS_FindNextEx( &info
, entry
))
1236 count
= info
.cur_pos
- skip
;
1242 if (info
.dir
) DOSFS_CloseDir( info
.dir
);
1243 memset( &info
, '\0', sizeof(info
) );
1246 SYSLEVEL_LeaveWin16Lock();
1253 /*************************************************************************
1254 * FindFirstFile16 (KERNEL.413)
1256 HANDLE16 WINAPI
FindFirstFile16( LPCSTR path
, WIN32_FIND_DATA32A
*data
)
1258 DOS_FULL_NAME full_name
;
1260 FIND_FIRST_INFO
*info
;
1262 data
->dwReserved0
= data
->dwReserved1
= 0x0;
1263 if (!path
) return 0;
1264 if (!DOSFS_GetFullName( path
, FALSE
, &full_name
))
1265 return INVALID_HANDLE_VALUE16
;
1266 if (!(handle
= GlobalAlloc16( GMEM_MOVEABLE
, sizeof(FIND_FIRST_INFO
) )))
1267 return INVALID_HANDLE_VALUE16
;
1268 info
= (FIND_FIRST_INFO
*)GlobalLock16( handle
);
1269 info
->path
= HEAP_strdupA( SystemHeap
, 0, full_name
.long_name
);
1270 info
->long_mask
= strrchr( info
->path
, '/' );
1271 *(info
->long_mask
++) = '\0';
1272 info
->short_mask
= NULL
;
1274 if (path
[0] && (path
[1] == ':')) info
->drive
= toupper(*path
) - 'A';
1275 else info
->drive
= DRIVE_GetCurrentDrive();
1278 info
->dir
= DOSFS_OpenDir( info
->path
);
1280 GlobalUnlock16( handle
);
1281 if (!FindNextFile16( handle
, data
))
1283 FindClose16( handle
);
1284 DOS_ERROR( ER_NoMoreFiles
, EC_MediaError
, SA_Abort
, EL_Disk
);
1285 return INVALID_HANDLE_VALUE16
;
1291 /*************************************************************************
1292 * FindFirstFile32A (KERNEL32.123)
1294 HANDLE32 WINAPI
FindFirstFile32A( LPCSTR path
, WIN32_FIND_DATA32A
*data
)
1296 HANDLE32 handle
= FindFirstFile16( path
, data
);
1297 if (handle
== INVALID_HANDLE_VALUE16
) return INVALID_HANDLE_VALUE32
;
1302 /*************************************************************************
1303 * FindFirstFile32W (KERNEL32.124)
1305 HANDLE32 WINAPI
FindFirstFile32W( LPCWSTR path
, WIN32_FIND_DATA32W
*data
)
1307 WIN32_FIND_DATA32A dataA
;
1308 LPSTR pathA
= HEAP_strdupWtoA( GetProcessHeap(), 0, path
);
1309 HANDLE32 handle
= FindFirstFile32A( pathA
, &dataA
);
1310 HeapFree( GetProcessHeap(), 0, pathA
);
1311 if (handle
!= INVALID_HANDLE_VALUE32
)
1313 data
->dwFileAttributes
= dataA
.dwFileAttributes
;
1314 data
->ftCreationTime
= dataA
.ftCreationTime
;
1315 data
->ftLastAccessTime
= dataA
.ftLastAccessTime
;
1316 data
->ftLastWriteTime
= dataA
.ftLastWriteTime
;
1317 data
->nFileSizeHigh
= dataA
.nFileSizeHigh
;
1318 data
->nFileSizeLow
= dataA
.nFileSizeLow
;
1319 lstrcpyAtoW( data
->cFileName
, dataA
.cFileName
);
1320 lstrcpyAtoW( data
->cAlternateFileName
, dataA
.cAlternateFileName
);
1326 /*************************************************************************
1327 * FindNextFile16 (KERNEL.414)
1329 BOOL16 WINAPI
FindNextFile16( HANDLE16 handle
, WIN32_FIND_DATA32A
*data
)
1331 FIND_FIRST_INFO
*info
;
1333 if (!(info
= (FIND_FIRST_INFO
*)GlobalLock16( handle
)))
1335 DOS_ERROR( ER_InvalidHandle
, EC_ProgramError
, SA_Abort
, EL_Disk
);
1338 GlobalUnlock16( handle
);
1339 if (!info
->path
|| !info
->dir
)
1341 DOS_ERROR( ER_NoMoreFiles
, EC_MediaError
, SA_Abort
, EL_Disk
);
1344 if (!DOSFS_FindNextEx( info
, data
))
1346 DOSFS_CloseDir( info
->dir
); info
->dir
= NULL
;
1347 HeapFree( SystemHeap
, 0, info
->path
);
1348 info
->path
= info
->long_mask
= NULL
;
1349 DOS_ERROR( ER_NoMoreFiles
, EC_MediaError
, SA_Abort
, EL_Disk
);
1356 /*************************************************************************
1357 * FindNextFile32A (KERNEL32.126)
1359 BOOL32 WINAPI
FindNextFile32A( HANDLE32 handle
, WIN32_FIND_DATA32A
*data
)
1361 return FindNextFile16( handle
, data
);
1365 /*************************************************************************
1366 * FindNextFile32W (KERNEL32.127)
1368 BOOL32 WINAPI
FindNextFile32W( HANDLE32 handle
, WIN32_FIND_DATA32W
*data
)
1370 WIN32_FIND_DATA32A dataA
;
1371 if (!FindNextFile32A( handle
, &dataA
)) return FALSE
;
1372 data
->dwFileAttributes
= dataA
.dwFileAttributes
;
1373 data
->ftCreationTime
= dataA
.ftCreationTime
;
1374 data
->ftLastAccessTime
= dataA
.ftLastAccessTime
;
1375 data
->ftLastWriteTime
= dataA
.ftLastWriteTime
;
1376 data
->nFileSizeHigh
= dataA
.nFileSizeHigh
;
1377 data
->nFileSizeLow
= dataA
.nFileSizeLow
;
1378 lstrcpyAtoW( data
->cFileName
, dataA
.cFileName
);
1379 lstrcpyAtoW( data
->cAlternateFileName
, dataA
.cAlternateFileName
);
1384 /*************************************************************************
1385 * FindClose16 (KERNEL.415)
1387 BOOL16 WINAPI
FindClose16( HANDLE16 handle
)
1389 FIND_FIRST_INFO
*info
;
1391 if ((handle
== INVALID_HANDLE_VALUE16
) ||
1392 !(info
= (FIND_FIRST_INFO
*)GlobalLock16( handle
)))
1394 DOS_ERROR( ER_InvalidHandle
, EC_ProgramError
, SA_Abort
, EL_Disk
);
1397 if (info
->dir
) DOSFS_CloseDir( info
->dir
);
1398 if (info
->path
) HeapFree( SystemHeap
, 0, info
->path
);
1399 GlobalUnlock16( handle
);
1400 GlobalFree16( handle
);
1405 /*************************************************************************
1406 * FindClose32 (KERNEL32.119)
1408 BOOL32 WINAPI
FindClose32( HANDLE32 handle
)
1410 return FindClose16( (HANDLE16
)handle
);
1414 /***********************************************************************
1415 * DOSFS_UnixTimeToFileTime
1417 * Convert a Unix time to FILETIME format.
1418 * The FILETIME structure is a 64-bit value representing the number of
1419 * 100-nanosecond intervals since January 1, 1601, 0:00.
1420 * 'remainder' is the nonnegative number of 100-ns intervals
1421 * corresponding to the time fraction smaller than 1 second that
1422 * couldn't be stored in the time_t value.
1424 void DOSFS_UnixTimeToFileTime( time_t unix_time
, FILETIME
*filetime
,
1430 The time difference between 1 January 1601, 00:00:00 and
1431 1 January 1970, 00:00:00 is 369 years, plus the leap years
1432 from 1604 to 1968, excluding 1700, 1800, 1900.
1433 This makes (1968 - 1600) / 4 - 3 = 89 leap days, and a total
1436 Any day in that period had 24 * 60 * 60 = 86400 seconds.
1438 The time difference is 134774 * 86400 * 10000000, which can be written
1440 27111902 * 2^32 + 3577643008
1441 413 * 2^48 + 45534 * 2^32 + 54590 * 2^16 + 32768
1443 If you find that these constants are buggy, please change them in all
1444 instances in both conversion functions.
1447 There are two versions, one of them uses long long variables and
1448 is presumably faster but not ISO C. The other one uses standard C
1449 data types and operations but relies on the assumption that negative
1450 numbers are stored as 2's complement (-1 is 0xffff....). If this
1451 assumption is violated, dates before 1970 will not convert correctly.
1452 This should however work on any reasonable architecture where WINE
1457 Take care not to remove the casts. I have tested these functions
1458 (in both versions) for a lot of numbers. I would be interested in
1459 results on other compilers than GCC.
1461 The operations have been designed to account for the possibility
1462 of 64-bit time_t in future UNICES. Even the versions without
1463 internal long long numbers will work if time_t only is 64 bit.
1464 A 32-bit shift, which was necessary for that operation, turned out
1465 not to work correctly in GCC, besides giving the warning. So I
1466 used a double 16-bit shift instead. Numbers are in the ISO version
1467 represented by three limbs, the most significant with 32 bit, the
1468 other two with 16 bit each.
1470 As the modulo-operator % is not well-defined for negative numbers,
1471 negative divisors have been avoided in DOSFS_FileTimeToUnixTime.
1473 There might be quicker ways to do this in C. Certainly so in
1476 Claus Fischer, fischer@iue.tuwien.ac.at
1479 #if (SIZEOF_LONG_LONG >= 8)
1480 # define USE_LONG_LONG 1
1482 # define USE_LONG_LONG 0
1485 #if USE_LONG_LONG /* gcc supports long long type */
1487 long long int t
= unix_time
;
1489 t
+= 116444736000000000LL;
1491 filetime
->dwLowDateTime
= (UINT32
)t
;
1492 filetime
->dwHighDateTime
= (UINT32
)(t
>> 32);
1494 #else /* ISO version */
1496 UINT32 a0
; /* 16 bit, low bits */
1497 UINT32 a1
; /* 16 bit, medium bits */
1498 UINT32 a2
; /* 32 bit, high bits */
1500 /* Copy the unix time to a2/a1/a0 */
1501 a0
= unix_time
& 0xffff;
1502 a1
= (unix_time
>> 16) & 0xffff;
1503 /* This is obsolete if unix_time is only 32 bits, but it does not hurt.
1504 Do not replace this by >> 32, it gives a compiler warning and it does
1506 a2
= (unix_time
>= 0 ? (unix_time
>> 16) >> 16 :
1507 ~((~unix_time
>> 16) >> 16));
1509 /* Multiply a by 10000000 (a = a2/a1/a0)
1510 Split the factor into 10000 * 1000 which are both less than 0xffff. */
1512 a1
= a1
* 10000 + (a0
>> 16);
1513 a2
= a2
* 10000 + (a1
>> 16);
1518 a1
= a1
* 1000 + (a0
>> 16);
1519 a2
= a2
* 1000 + (a1
>> 16);
1523 /* Add the time difference and the remainder */
1524 a0
+= 32768 + (remainder
& 0xffff);
1525 a1
+= 54590 + (remainder
>> 16 ) + (a0
>> 16);
1526 a2
+= 27111902 + (a1
>> 16);
1531 filetime
->dwLowDateTime
= (a1
<< 16) + a0
;
1532 filetime
->dwHighDateTime
= a2
;
1537 /***********************************************************************
1538 * DOSFS_FileTimeToUnixTime
1540 * Convert a FILETIME format to Unix time.
1541 * If not NULL, 'remainder' contains the fractional part of the filetime,
1542 * in the range of [0..9999999] (even if time_t is negative).
1544 time_t DOSFS_FileTimeToUnixTime( const FILETIME
*filetime
, DWORD
*remainder
)
1546 /* Read the comment in the function DOSFS_UnixTimeToFileTime. */
1549 long long int t
= filetime
->dwHighDateTime
;
1551 t
+= (UINT32
)filetime
->dwLowDateTime
;
1552 t
-= 116444736000000000LL;
1555 if (remainder
) *remainder
= 9999999 - (-t
- 1) % 10000000;
1556 return -1 - ((-t
- 1) / 10000000);
1560 if (remainder
) *remainder
= t
% 10000000;
1561 return t
/ 10000000;
1564 #else /* ISO version */
1566 UINT32 a0
; /* 16 bit, low bits */
1567 UINT32 a1
; /* 16 bit, medium bits */
1568 UINT32 a2
; /* 32 bit, high bits */
1569 UINT32 r
; /* remainder of division */
1570 unsigned int carry
; /* carry bit for subtraction */
1571 int negative
; /* whether a represents a negative value */
1573 /* Copy the time values to a2/a1/a0 */
1574 a2
= (UINT32
)filetime
->dwHighDateTime
;
1575 a1
= ((UINT32
)filetime
->dwLowDateTime
) >> 16;
1576 a0
= ((UINT32
)filetime
->dwLowDateTime
) & 0xffff;
1578 /* Subtract the time difference */
1579 if (a0
>= 32768 ) a0
-= 32768 , carry
= 0;
1580 else a0
+= (1 << 16) - 32768 , carry
= 1;
1582 if (a1
>= 54590 + carry
) a1
-= 54590 + carry
, carry
= 0;
1583 else a1
+= (1 << 16) - 54590 - carry
, carry
= 1;
1585 a2
-= 27111902 + carry
;
1587 /* If a is negative, replace a by (-1-a) */
1588 negative
= (a2
>= ((UINT32
)1) << 31);
1591 /* Set a to -a - 1 (a is a2/a1/a0) */
1597 /* Divide a by 10000000 (a = a2/a1/a0), put the rest into r.
1598 Split the divisor into 10000 * 1000 which are both less than 0xffff. */
1599 a1
+= (a2
% 10000) << 16;
1601 a0
+= (a1
% 10000) << 16;
1606 a1
+= (a2
% 1000) << 16;
1608 a0
+= (a1
% 1000) << 16;
1610 r
+= (a0
% 1000) * 10000;
1613 /* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
1616 /* Set a to -a - 1 (a is a2/a1/a0) */
1624 if (remainder
) *remainder
= r
;
1626 /* Do not replace this by << 32, it gives a compiler warning and it does
1628 return ((((time_t)a2
) << 16) << 16) + (a1
<< 16) + a0
;
1633 /***********************************************************************
1634 * DosDateTimeToFileTime (KERNEL32.76)
1636 BOOL32 WINAPI
DosDateTimeToFileTime( WORD fatdate
, WORD fattime
, LPFILETIME ft
)
1640 newtm
.tm_sec
= (fattime
& 0x1f) * 2;
1641 newtm
.tm_min
= (fattime
>> 5) & 0x3f;
1642 newtm
.tm_hour
= (fattime
>> 11);
1643 newtm
.tm_mday
= (fatdate
& 0x1f);
1644 newtm
.tm_mon
= ((fatdate
>> 5) & 0x0f) - 1;
1645 newtm
.tm_year
= (fatdate
>> 9) + 80;
1646 DOSFS_UnixTimeToFileTime( mktime( &newtm
), ft
, 0 );
1651 /***********************************************************************
1652 * FileTimeToDosDateTime (KERNEL32.111)
1654 BOOL32 WINAPI
FileTimeToDosDateTime( const FILETIME
*ft
, LPWORD fatdate
,
1657 time_t unixtime
= DOSFS_FileTimeToUnixTime( ft
, NULL
);
1658 struct tm
*tm
= localtime( &unixtime
);
1660 *fattime
= (tm
->tm_hour
<< 11) + (tm
->tm_min
<< 5) + (tm
->tm_sec
/ 2);
1662 *fatdate
= ((tm
->tm_year
- 80) << 9) + ((tm
->tm_mon
+ 1) << 5)
1668 /***********************************************************************
1669 * LocalFileTimeToFileTime (KERNEL32.373)
1671 BOOL32 WINAPI
LocalFileTimeToFileTime( const FILETIME
*localft
,
1677 /* convert from local to UTC. Perhaps not correct. FIXME */
1678 time_t unixtime
= DOSFS_FileTimeToUnixTime( localft
, &remainder
);
1679 xtm
= gmtime( &unixtime
);
1680 DOSFS_UnixTimeToFileTime( mktime(xtm
), utcft
, remainder
);
1685 /***********************************************************************
1686 * FileTimeToLocalFileTime (KERNEL32.112)
1688 BOOL32 WINAPI
FileTimeToLocalFileTime( const FILETIME
*utcft
,
1689 LPFILETIME localft
)
1692 /* convert from UTC to local. Perhaps not correct. FIXME */
1693 time_t unixtime
= DOSFS_FileTimeToUnixTime( utcft
, &remainder
);
1695 struct tm
*xtm
= localtime( &unixtime
);
1698 localtime
= timegm(xtm
);
1699 DOSFS_UnixTimeToFileTime( localtime
, localft
, remainder
);
1702 struct tm
*xtm
,*gtm
;
1705 xtm
= localtime( &unixtime
);
1706 gtm
= gmtime( &unixtime
);
1707 time1
= mktime(xtm
);
1708 time2
= mktime(gtm
);
1709 DOSFS_UnixTimeToFileTime( 2*time1
-time2
, localft
, remainder
);
1715 /***********************************************************************
1716 * FileTimeToSystemTime (KERNEL32.113)
1718 BOOL32 WINAPI
FileTimeToSystemTime( const FILETIME
*ft
, LPSYSTEMTIME syst
)
1722 time_t xtime
= DOSFS_FileTimeToUnixTime( ft
, &remainder
);
1723 xtm
= gmtime(&xtime
);
1724 syst
->wYear
= xtm
->tm_year
+1900;
1725 syst
->wMonth
= xtm
->tm_mon
+ 1;
1726 syst
->wDayOfWeek
= xtm
->tm_wday
;
1727 syst
->wDay
= xtm
->tm_mday
;
1728 syst
->wHour
= xtm
->tm_hour
;
1729 syst
->wMinute
= xtm
->tm_min
;
1730 syst
->wSecond
= xtm
->tm_sec
;
1731 syst
->wMilliseconds
= remainder
/ 10000;
1735 /***********************************************************************
1736 * QueryDosDeviceA (KERNEL32.413)
1738 * returns array of strings terminated by \0, terminated by \0
1740 DWORD WINAPI
QueryDosDevice32A(LPCSTR devname
,LPSTR target
,DWORD bufsize
)
1745 TRACE(dosfs
,"(%s,...)\n",devname
?devname
:"<null>");
1747 /* return known MSDOS devices */
1748 lstrcpy32A(buffer
,"CON COM1 COM2 LPT1 NUL ");
1749 while ((s
=strchr(buffer
,' ')))
1752 lstrcpyn32A(target
,buffer
,bufsize
);
1753 return strlen(buffer
);
1755 lstrcpy32A(buffer
,"\\DEV\\");
1756 lstrcat32A(buffer
,devname
);
1757 if ((s
=strchr(buffer
,':'))) *s
='\0';
1758 lstrcpyn32A(target
,buffer
,bufsize
);
1759 return strlen(buffer
);
1763 /***********************************************************************
1764 * QueryDosDeviceW (KERNEL32.414)
1766 * returns array of strings terminated by \0, terminated by \0
1768 DWORD WINAPI
QueryDosDevice32W(LPCWSTR devname
,LPWSTR target
,DWORD bufsize
)
1770 LPSTR devnameA
= devname
?HEAP_strdupWtoA(GetProcessHeap(),0,devname
):NULL
;
1771 LPSTR targetA
= (LPSTR
)HEAP_xalloc(GetProcessHeap(),0,bufsize
);
1772 DWORD ret
= QueryDosDevice32A(devnameA
,targetA
,bufsize
);
1774 lstrcpynAtoW(target
,targetA
,bufsize
);
1775 if (devnameA
) HeapFree(GetProcessHeap(),0,devnameA
);
1776 if (targetA
) HeapFree(GetProcessHeap(),0,targetA
);
1781 /***********************************************************************
1782 * SystemTimeToFileTime (KERNEL32.526)
1784 BOOL32 WINAPI
SystemTimeToFileTime( const SYSTEMTIME
*syst
, LPFILETIME ft
)
1790 struct tm xtm
,*local_tm
,*utc_tm
;
1791 time_t localtim
,utctime
;
1794 xtm
.tm_year
= syst
->wYear
-1900;
1795 xtm
.tm_mon
= syst
->wMonth
- 1;
1796 xtm
.tm_wday
= syst
->wDayOfWeek
;
1797 xtm
.tm_mday
= syst
->wDay
;
1798 xtm
.tm_hour
= syst
->wHour
;
1799 xtm
.tm_min
= syst
->wMinute
;
1800 xtm
.tm_sec
= syst
->wSecond
; /* this is UTC */
1803 utctime
= timegm(&xtm
);
1804 DOSFS_UnixTimeToFileTime( utctime
, ft
,
1805 syst
->wMilliseconds
* 10000 );
1807 localtim
= mktime(&xtm
); /* now we've got local time */
1808 local_tm
= localtime(&localtim
);
1809 utc_tm
= gmtime(&localtim
);
1810 utctime
= mktime(utc_tm
);
1811 DOSFS_UnixTimeToFileTime( 2*localtim
-utctime
, ft
,
1812 syst
->wMilliseconds
* 10000 );
1817 BOOL32 WINAPI
DefineDosDevice32A(DWORD flags
,LPCSTR devname
,LPCSTR targetpath
) {
1818 FIXME(dosfs
,"(0x%08lx,%s,%s),stub!\n",flags
,devname
,targetpath
);
1819 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);