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>
24 #include "wine/winbase16.h"
36 /* Define the VFAT ioctl to get both short and long file names */
37 /* FIXME: is it possible to get this to work on other systems? */
39 #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, long)
40 /* We want the real kernel dirent structure, not the libc one */
45 unsigned short d_reclen
;
50 #undef VFAT_IOCTL_READDIR_BOTH /* just in case... */
53 /* Chars we don't want to see in DOS file names */
54 #define INVALID_DOS_CHARS "*?<>|\"+=,;[] \345"
56 static const DOS_DEVICE DOSFS_Devices
[] =
57 /* name, device flags (see Int 21/AX=0x4400) */
71 { "SCSIMGR$", 0xc0c0 },
75 #define GET_DRIVE(path) \
76 (((path)[1] == ':') ? toupper((path)[0]) - 'A' : DOSFS_CurDrive)
78 /* Directory info for DOSFS_ReadDir */
82 #ifdef VFAT_IOCTL_READDIR_BOTH
85 KERNEL_DIRENT dirent
[2];
89 /* Info structure for FindFirstFile handle */
103 /***********************************************************************
106 * Return 1 if Unix file 'name' is also a valid MS-DOS name
107 * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
108 * File name can be terminated by '\0', '\\' or '/'.
110 static int DOSFS_ValidDOSName( const char *name
, int ignore_case
)
112 static const char invalid_chars
[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS
;
113 const char *p
= name
;
114 const char *invalid
= ignore_case
? (invalid_chars
+ 26) : invalid_chars
;
119 /* Check for "." and ".." */
122 /* All other names beginning with '.' are invalid */
123 return (IS_END_OF_NAME(*p
));
125 while (!IS_END_OF_NAME(*p
))
127 if (strchr( invalid
, *p
)) return 0; /* Invalid char */
128 if (*p
== '.') break; /* Start of the extension */
129 if (++len
> 8) return 0; /* Name too long */
132 if (*p
!= '.') return 1; /* End of name */
134 if (IS_END_OF_NAME(*p
)) return 0; /* Empty extension not allowed */
136 while (!IS_END_OF_NAME(*p
))
138 if (strchr( invalid
, *p
)) return 0; /* Invalid char */
139 if (*p
== '.') return 0; /* Second extension not allowed */
140 if (++len
> 3) return 0; /* Extension too long */
147 /***********************************************************************
148 * DOSFS_ToDosFCBFormat
150 * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
151 * expanding wild cards and converting to upper-case in the process.
152 * File name can be terminated by '\0', '\\' or '/'.
153 * Return FALSE if the name is not a valid DOS name.
154 * 'buffer' must be at least 12 characters long.
156 BOOL
DOSFS_ToDosFCBFormat( LPCSTR name
, LPSTR buffer
)
158 static const char invalid_chars
[] = INVALID_DOS_CHARS
;
159 const char *p
= name
;
162 /* Check for "." and ".." */
166 strcpy( buffer
, ". " );
172 return (!*p
|| (*p
== '/') || (*p
== '\\'));
175 for (i
= 0; i
< 8; i
++)
192 if (strchr( invalid_chars
, *p
)) return FALSE
;
193 buffer
[i
] = toupper(*p
);
201 /* Skip all chars after wildcard up to first dot */
202 while (*p
&& (*p
!= '/') && (*p
!= '\\') && (*p
!= '.')) p
++;
206 /* Check if name too long */
207 if (*p
&& (*p
!= '/') && (*p
!= '\\') && (*p
!= '.')) return FALSE
;
209 if (*p
== '.') p
++; /* Skip dot */
211 for (i
= 8; i
< 11; i
++)
221 return FALSE
; /* Second extension not allowed */
229 if (strchr( invalid_chars
, *p
)) return FALSE
;
230 buffer
[i
] = toupper(*p
);
240 /***********************************************************************
241 * DOSFS_ToDosDTAFormat
243 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
244 * converting to upper-case in the process.
245 * File name can be terminated by '\0', '\\' or '/'.
246 * 'buffer' must be at least 13 characters long.
248 static void DOSFS_ToDosDTAFormat( LPCSTR name
, LPSTR buffer
)
252 memcpy( buffer
, name
, 8 );
253 for (p
= buffer
+ 8; (p
> buffer
) && (p
[-1] == ' '); p
--);
255 memcpy( p
, name
+ 8, 3 );
256 for (p
+= 3; p
[-1] == ' '; p
--);
257 if (p
[-1] == '.') p
--;
262 /***********************************************************************
265 * Check a DOS file name against a mask (both in FCB format).
267 static int DOSFS_MatchShort( const char *mask
, const char *name
)
270 for (i
= 11; i
> 0; i
--, mask
++, name
++)
271 if ((*mask
!= '?') && (*mask
!= *name
)) return 0;
276 /***********************************************************************
279 * Check a long file name against a mask.
281 static int DOSFS_MatchLong( const char *mask
, const char *name
,
284 if (!strcmp( mask
, "*.*" )) return 1;
285 while (*name
&& *mask
)
290 while (*mask
== '*') mask
++; /* Skip consecutive '*' */
291 if (!*mask
) return 1;
292 if (case_sensitive
) while (*name
&& (*name
!= *mask
)) name
++;
293 else while (*name
&& (toupper(*name
) != toupper(*mask
))) name
++;
294 if (!*name
) return 0;
296 else if (*mask
!= '?')
300 if (*mask
!= *name
) return 0;
302 else if (toupper(*mask
) != toupper(*name
)) return 0;
307 return (!*name
&& !*mask
);
311 /***********************************************************************
314 static DOS_DIR
*DOSFS_OpenDir( LPCSTR path
)
316 DOS_DIR
*dir
= HeapAlloc( SystemHeap
, 0, sizeof(*dir
) );
319 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
323 /* Treat empty path as root directory. This simplifies path split into
324 directory and mask in several other places */
325 if (!*path
) path
= "/";
327 #ifdef VFAT_IOCTL_READDIR_BOTH
329 /* Check if the VFAT ioctl is supported on this directory */
331 if ((dir
->fd
= open( path
, O_RDONLY
)) != -1)
333 if (ioctl( dir
->fd
, VFAT_IOCTL_READDIR_BOTH
, (long)dir
->dirent
) == -1)
340 /* Set the file pointer back at the start of the directory */
341 lseek( dir
->fd
, 0, SEEK_SET
);
346 #endif /* VFAT_IOCTL_READDIR_BOTH */
348 /* Now use the standard opendir/readdir interface */
350 if (!(dir
->dir
= opendir( path
)))
352 HeapFree( SystemHeap
, 0, dir
);
359 /***********************************************************************
362 static void DOSFS_CloseDir( DOS_DIR
*dir
)
364 #ifdef VFAT_IOCTL_READDIR_BOTH
365 if (dir
->fd
!= -1) close( dir
->fd
);
366 #endif /* VFAT_IOCTL_READDIR_BOTH */
367 if (dir
->dir
) closedir( dir
->dir
);
368 HeapFree( SystemHeap
, 0, dir
);
372 /***********************************************************************
375 static BOOL
DOSFS_ReadDir( DOS_DIR
*dir
, LPCSTR
*long_name
,
378 struct dirent
*dirent
;
380 #ifdef VFAT_IOCTL_READDIR_BOTH
383 if (ioctl( dir
->fd
, VFAT_IOCTL_READDIR_BOTH
, (long)dir
->dirent
) != -1) {
384 if (!dir
->dirent
[0].d_reclen
) return FALSE
;
385 if (!DOSFS_ToDosFCBFormat( dir
->dirent
[0].d_name
, dir
->short_name
))
386 dir
->short_name
[0] = '\0';
387 *short_name
= dir
->short_name
;
388 if (dir
->dirent
[1].d_name
[0]) *long_name
= dir
->dirent
[1].d_name
;
389 else *long_name
= dir
->dirent
[0].d_name
;
393 #endif /* VFAT_IOCTL_READDIR_BOTH */
395 if (!(dirent
= readdir( dir
->dir
))) return FALSE
;
396 *long_name
= dirent
->d_name
;
402 /***********************************************************************
405 * Transform a Unix file name into a hashed DOS name. If the name is a valid
406 * DOS name, it is converted to upper-case; otherwise it is replaced by a
407 * hashed version that fits in 8.3 format.
408 * File name can be terminated by '\0', '\\' or '/'.
409 * 'buffer' must be at least 13 characters long.
411 static void DOSFS_Hash( LPCSTR name
, LPSTR buffer
, BOOL dir_format
,
414 static const char invalid_chars
[] = INVALID_DOS_CHARS
"~.";
415 static const char hash_chars
[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
422 if (dir_format
) strcpy( buffer
, " " );
424 if (DOSFS_ValidDOSName( name
, ignore_case
))
426 /* Check for '.' and '..' */
430 if (!dir_format
) buffer
[1] = buffer
[2] = '\0';
431 if (name
[1] == '.') buffer
[1] = '.';
435 /* Simply copy the name, converting to uppercase */
437 for (dst
= buffer
; !IS_END_OF_NAME(*name
) && (*name
!= '.'); name
++)
438 *dst
++ = toupper(*name
);
441 if (dir_format
) dst
= buffer
+ 8;
443 for (name
++; !IS_END_OF_NAME(*name
); name
++)
444 *dst
++ = toupper(*name
);
446 if (!dir_format
) *dst
= '\0';
450 /* Compute the hash code of the file name */
451 /* If you know something about hash functions, feel free to */
452 /* insert a better algorithm here... */
455 for (p
= name
, hash
= 0xbeef; !IS_END_OF_NAME(p
[1]); p
++)
456 hash
= (hash
<<3) ^ (hash
>>5) ^ tolower(*p
) ^ (tolower(p
[1]) << 8);
457 hash
= (hash
<<3) ^ (hash
>>5) ^ tolower(*p
); /* Last character*/
461 for (p
= name
, hash
= 0xbeef; !IS_END_OF_NAME(p
[1]); p
++)
462 hash
= (hash
<< 3) ^ (hash
>> 5) ^ *p
^ (p
[1] << 8);
463 hash
= (hash
<< 3) ^ (hash
>> 5) ^ *p
; /* Last character */
466 /* Find last dot for start of the extension */
467 for (p
= name
+1, ext
= NULL
; !IS_END_OF_NAME(*p
); p
++)
468 if (*p
== '.') ext
= p
;
469 if (ext
&& IS_END_OF_NAME(ext
[1]))
470 ext
= NULL
; /* Empty extension ignored */
472 /* Copy first 4 chars, replacing invalid chars with '_' */
473 for (i
= 4, p
= name
, dst
= buffer
; i
> 0; i
--, p
++)
475 if (IS_END_OF_NAME(*p
) || (p
== ext
)) break;
476 *dst
++ = strchr( invalid_chars
, *p
) ? '_' : toupper(*p
);
478 /* Pad to 5 chars with '~' */
479 while (i
-- >= 0) *dst
++ = '~';
481 /* Insert hash code converted to 3 ASCII chars */
482 *dst
++ = hash_chars
[(hash
>> 10) & 0x1f];
483 *dst
++ = hash_chars
[(hash
>> 5) & 0x1f];
484 *dst
++ = hash_chars
[hash
& 0x1f];
486 /* Copy the first 3 chars of the extension (if any) */
489 if (!dir_format
) *dst
++ = '.';
490 for (i
= 3, ext
++; (i
> 0) && !IS_END_OF_NAME(*ext
); i
--, ext
++)
491 *dst
++ = strchr( invalid_chars
, *ext
) ? '_' : toupper(*ext
);
493 if (!dir_format
) *dst
= '\0';
497 /***********************************************************************
500 * Find the Unix file name in a given directory that corresponds to
501 * a file name (either in Unix or DOS format).
502 * File name can be terminated by '\0', '\\' or '/'.
503 * Return TRUE if OK, FALSE if no file name matches.
505 * 'long_buf' must be at least 'long_len' characters long. If the long name
506 * turns out to be larger than that, the function returns FALSE.
507 * 'short_buf' must be at least 13 characters long.
509 BOOL
DOSFS_FindUnixName( LPCSTR path
, LPCSTR name
, LPSTR long_buf
,
510 INT long_len
, LPSTR short_buf
, BOOL ignore_case
)
513 LPCSTR long_name
, short_name
;
514 char dos_name
[12], tmp_buf
[13];
517 const char *p
= strchr( name
, '/' );
518 int len
= p
? (int)(p
- name
) : strlen(name
);
519 if ((p
= strchr( name
, '\\' ))) len
= MIN( (int)(p
- name
), len
);
520 if (long_len
< len
+ 1) return FALSE
;
522 TRACE(dosfs
, "%s,%s\n", path
, name
);
524 if (!DOSFS_ToDosFCBFormat( name
, dos_name
)) dos_name
[0] = '\0';
526 if (!(dir
= DOSFS_OpenDir( path
)))
528 WARN(dosfs
, "(%s,%s): can't open dir: %s\n",
529 path
, name
, strerror(errno
) );
533 while ((ret
= DOSFS_ReadDir( dir
, &long_name
, &short_name
)))
535 /* Check against Unix name */
536 if (len
== strlen(long_name
))
540 if (!lstrncmpA( long_name
, name
, len
)) break;
544 if (!lstrncmpiA( long_name
, name
, len
)) break;
549 /* Check against hashed DOS name */
552 DOSFS_Hash( long_name
, tmp_buf
, TRUE
, ignore_case
);
553 short_name
= tmp_buf
;
555 if (!strcmp( dos_name
, short_name
)) break;
560 if (long_buf
) strcpy( long_buf
, long_name
);
564 DOSFS_ToDosDTAFormat( short_name
, short_buf
);
566 DOSFS_Hash( long_name
, short_buf
, FALSE
, ignore_case
);
568 TRACE(dosfs
, "(%s,%s) -> %s (%s)\n",
569 path
, name
, long_name
, short_buf
? short_buf
: "***");
572 WARN(dosfs
, "'%s' not found in '%s'\n", name
, path
);
573 DOSFS_CloseDir( dir
);
578 /***********************************************************************
581 * Check if a DOS file name represents a DOS device and return the device.
583 const DOS_DEVICE
*DOSFS_GetDevice( const char *name
)
588 if (!name
) return NULL
; /* if FILE_DupUnixHandle was used */
589 if (name
[0] && (name
[1] == ':')) name
+= 2;
590 if ((p
= strrchr( name
, '/' ))) name
= p
+ 1;
591 if ((p
= strrchr( name
, '\\' ))) name
= p
+ 1;
592 for (i
= 0; i
< sizeof(DOSFS_Devices
)/sizeof(DOSFS_Devices
[0]); i
++)
594 const char *dev
= DOSFS_Devices
[i
].name
;
595 if (!lstrncmpiA( dev
, name
, strlen(dev
) ))
597 p
= name
+ strlen( dev
);
598 if (!*p
|| (*p
== '.')) return &DOSFS_Devices
[i
];
605 /***********************************************************************
606 * DOSFS_GetDeviceByHandle
608 const DOS_DEVICE
*DOSFS_GetDeviceByHandle( HFILE hFile
)
610 struct get_file_info_request req
;
611 struct get_file_info_reply reply
;
614 CLIENT_SendRequest( REQ_GET_FILE_INFO
, -1, 1, &req
, sizeof(req
) );
615 if (!CLIENT_WaitSimpleReply( &reply
, sizeof(reply
), NULL
) &&
616 (reply
.type
== FILE_TYPE_UNKNOWN
))
618 if ((reply
.attr
>= 0) &&
619 (reply
.attr
< sizeof(DOSFS_Devices
)/sizeof(DOSFS_Devices
[0])))
620 return &DOSFS_Devices
[reply
.attr
];
626 /***********************************************************************
629 * Open a DOS device. This might not map 1:1 into the UNIX device concept.
631 HFILE
DOSFS_OpenDevice( const char *name
, DWORD access
)
636 if (!name
) return (HFILE
)NULL
; /* if FILE_DupUnixHandle was used */
637 if (name
[0] && (name
[1] == ':')) name
+= 2;
638 if ((p
= strrchr( name
, '/' ))) name
= p
+ 1;
639 if ((p
= strrchr( name
, '\\' ))) name
= p
+ 1;
640 for (i
= 0; i
< sizeof(DOSFS_Devices
)/sizeof(DOSFS_Devices
[0]); i
++)
642 const char *dev
= DOSFS_Devices
[i
].name
;
643 if (!lstrncmpiA( dev
, name
, strlen(dev
) ))
645 p
= name
+ strlen( dev
);
646 if (!*p
|| (*p
== '.')) {
648 if (!strcmp(DOSFS_Devices
[i
].name
,"NUL"))
649 return FILE_CreateFile( "/dev/null", access
,
650 FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
,
651 OPEN_EXISTING
, 0, -1 );
652 if (!strcmp(DOSFS_Devices
[i
].name
,"CON")) {
655 switch (access
& (GENERIC_READ
|GENERIC_WRITE
)) {
657 to_dup
= GetStdHandle( STD_INPUT_HANDLE
);
660 to_dup
= GetStdHandle( STD_OUTPUT_HANDLE
);
663 FIXME(dosfs
,"can't open CON read/write\n");
667 if (!DuplicateHandle( GetCurrentProcess(), to_dup
, GetCurrentProcess(),
668 &handle
, 0, FALSE
, DUPLICATE_SAME_ACCESS
))
669 handle
= HFILE_ERROR
;
672 if (!strcmp(DOSFS_Devices
[i
].name
,"SCSIMGR$") ||
673 !strcmp(DOSFS_Devices
[i
].name
,"HPSCAN"))
675 return FILE_CreateDevice( i
, access
, NULL
);
680 PROFILE_GetWineIniString("serialports",name
,"",devname
,sizeof devname
);
684 TRACE(file
,"DOSFS_OpenDevice %s is %s\n",
685 DOSFS_Devices
[i
].name
,devname
);
686 r
= FILE_CreateFile( devname
, access
,
687 FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
,
688 OPEN_EXISTING
, 0, -1 );
689 TRACE(file
,"Create_File return %08X\n",r
);
694 FIXME(dosfs
,"device open %s not supported (yet)\n",DOSFS_Devices
[i
].name
);
703 /***********************************************************************
706 * Get the drive specified by a given path name (DOS or Unix format).
708 static int DOSFS_GetPathDrive( const char **name
)
711 const char *p
= *name
;
713 if (*p
&& (p
[1] == ':'))
715 drive
= toupper(*p
) - 'A';
718 else if (*p
== '/') /* Absolute Unix path? */
720 if ((drive
= DRIVE_FindDriveRoot( name
)) == -1)
722 MSG("Warning: %s not accessible from a DOS drive\n", *name
);
723 /* Assume it really was a DOS name */
724 drive
= DRIVE_GetCurrentDrive();
727 else drive
= DRIVE_GetCurrentDrive();
729 if (!DRIVE_IsValid(drive
))
731 SetLastError( ERROR_INVALID_DRIVE
);
738 /***********************************************************************
741 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
742 * Unix name / short DOS name pair.
743 * Return FALSE if one of the path components does not exist. The last path
744 * component is only checked if 'check_last' is non-zero.
745 * The buffers pointed to by 'long_buf' and 'short_buf' must be
746 * at least MAX_PATHNAME_LEN long.
748 BOOL
DOSFS_GetFullName( LPCSTR name
, BOOL check_last
, DOS_FULL_NAME
*full
)
752 char *p_l
, *p_s
, *root
;
754 TRACE(dosfs
, "%s (last=%d)\n",
757 if ((full
->drive
= DOSFS_GetPathDrive( &name
)) == -1) return FALSE
;
758 flags
= DRIVE_GetFlags( full
->drive
);
760 lstrcpynA( full
->long_name
, DRIVE_GetRoot( full
->drive
),
761 sizeof(full
->long_name
) );
762 if (full
->long_name
[1]) root
= full
->long_name
+ strlen(full
->long_name
);
763 else root
= full
->long_name
; /* root directory */
765 strcpy( full
->short_name
, "A:\\" );
766 full
->short_name
[0] += full
->drive
;
768 if ((*name
== '\\') || (*name
== '/')) /* Absolute path */
770 while ((*name
== '\\') || (*name
== '/')) name
++;
772 else /* Relative path */
774 lstrcpynA( root
+ 1, DRIVE_GetUnixCwd( full
->drive
),
775 sizeof(full
->long_name
) - (root
- full
->long_name
) - 1 );
776 if (root
[1]) *root
= '/';
777 lstrcpynA( full
->short_name
+ 3, DRIVE_GetDosCwd( full
->drive
),
778 sizeof(full
->short_name
) - 3 );
781 p_l
= full
->long_name
[1] ? full
->long_name
+ strlen(full
->long_name
)
783 p_s
= full
->short_name
[3] ? full
->short_name
+ strlen(full
->short_name
)
784 : full
->short_name
+ 2;
787 while (*name
&& found
)
789 /* Check for '.' and '..' */
793 if (IS_END_OF_NAME(name
[1]))
796 while ((*name
== '\\') || (*name
== '/')) name
++;
799 else if ((name
[1] == '.') && IS_END_OF_NAME(name
[2]))
802 while ((*name
== '\\') || (*name
== '/')) name
++;
803 while ((p_l
> root
) && (*p_l
!= '/')) p_l
--;
804 while ((p_s
> full
->short_name
+ 2) && (*p_s
!= '\\')) p_s
--;
805 *p_l
= *p_s
= '\0'; /* Remove trailing separator */
810 /* Make sure buffers are large enough */
812 if ((p_s
>= full
->short_name
+ sizeof(full
->short_name
) - 14) ||
813 (p_l
>= full
->long_name
+ sizeof(full
->long_name
) - 1))
815 SetLastError( ERROR_PATH_NOT_FOUND
);
819 /* Get the long and short name matching the file name */
821 if ((found
= DOSFS_FindUnixName( full
->long_name
, name
, p_l
+ 1,
822 sizeof(full
->long_name
) - (p_l
- full
->long_name
) - 1,
823 p_s
+ 1, !(flags
& DRIVE_CASE_SENSITIVE
) )))
829 while (!IS_END_OF_NAME(*name
)) name
++;
831 else if (!check_last
)
835 while (!IS_END_OF_NAME(*name
) &&
836 (p_s
< full
->short_name
+ sizeof(full
->short_name
) - 1) &&
837 (p_l
< full
->long_name
+ sizeof(full
->long_name
) - 1))
839 *p_s
++ = tolower(*name
);
840 /* If the drive is case-sensitive we want to create new */
841 /* files in lower-case otherwise we can't reopen them */
842 /* under the same short name. */
843 if (flags
& DRIVE_CASE_SENSITIVE
) *p_l
++ = tolower(*name
);
849 while ((*name
== '\\') || (*name
== '/')) name
++;
856 SetLastError( ERROR_FILE_NOT_FOUND
);
859 if (*name
) /* Not last */
861 SetLastError( ERROR_PATH_NOT_FOUND
);
865 if (!full
->long_name
[0]) strcpy( full
->long_name
, "/" );
866 if (!full
->short_name
[2]) strcpy( full
->short_name
+ 2, "\\" );
867 TRACE(dosfs
, "returning %s = %s\n",
868 full
->long_name
, full
->short_name
);
873 /***********************************************************************
874 * GetShortPathNameA (KERNEL32.271)
878 * longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
879 * *longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0
881 * more observations ( with NT 3.51 (WinDD) ):
882 * longpath <= 8.3 -> just copy longpath to shortpath
884 * a) file does not exist -> return 0, LastError = ERROR_FILE_NOT_FOUND
885 * b) file does exist -> set the short filename.
886 * - trailing slashes are reproduced in the short name, even if the
887 * file is not a directory
888 * - the absolute/relative path of the short name is reproduced in the
889 * same way, like the long name
890 * - longpath and shortpath may have the same adress
893 DWORD WINAPI
GetShortPathNameA( LPCSTR longpath
, LPSTR shortpath
,
896 DOS_FULL_NAME full_name
;
898 DWORD length
= 0, pos
= 0;
899 INT start
=-1, end
=-1, tmplen
;
902 SetLastError(ERROR_INVALID_PARAMETER
);
906 SetLastError(ERROR_BAD_PATHNAME
);
910 tmpshortpath
= HeapAlloc( GetProcessHeap(), 0, MAX_PATHNAME_LEN
);
911 if ( !tmpshortpath
) {
912 SetLastError ( ERROR_NOT_ENOUGH_MEMORY
);
916 /* Check for Drive-Letter */
917 if ( longpath
[1] == ':' ) {
918 lstrcpynA ( tmpshortpath
, longpath
, 3 );
923 /* loop over each part of the name */
924 while ( longpath
[pos
] ) {
926 if (( longpath
[pos
] == '\\' ) ||
927 ( longpath
[pos
+1] == '\0' ) ||
928 ( longpath
[pos
] == '/')) {
931 if ( DOSFS_ValidDOSName ( longpath
+ start
, TRUE
)) {
932 tmplen
= end
- start
+ ( (( longpath
[pos
] == '\\' ) || ( longpath
[pos
] == '/' )) ? 1 : 2 );
933 lstrcpynA ( tmpshortpath
+length
, longpath
+start
, tmplen
);
934 length
+= tmplen
- 1;
937 DOSFS_Hash ( longpath
+ start
, tmpshortpath
+length
, FALSE
, FALSE
);
938 length
= lstrlenA ( tmpshortpath
);
940 /* Check if the path, up to this point exists */
941 if ( !DOSFS_GetFullName ( tmpshortpath
, TRUE
, &full_name
) ) {
942 SetLastError ( ERROR_FILE_NOT_FOUND
);
949 if (( longpath
[pos
] == '\\' ) || ( longpath
[pos
] == '/' )) {
950 tmpshortpath
[length
] = '\\';
951 tmpshortpath
[length
+1]='\0';
968 lstrcpynA ( shortpath
, tmpshortpath
, shortlen
);
969 length
= lstrlenA ( tmpshortpath
);
970 HeapFree ( GetProcessHeap(), 0, tmpshortpath
);
976 /***********************************************************************
977 * GetShortPathName32W (KERNEL32.272)
979 DWORD WINAPI
GetShortPathNameW( LPCWSTR longpath
, LPWSTR shortpath
,
982 LPSTR longpathA
, shortpathA
;
985 longpathA
= HEAP_strdupWtoA( GetProcessHeap(), 0, longpath
);
986 shortpathA
= HEAP_xalloc ( GetProcessHeap(), 0, shortlen
);
988 ret
= GetShortPathNameA ( longpathA
, shortpathA
, shortlen
);
989 lstrcpynAtoW ( shortpath
, shortpathA
, shortlen
);
991 HeapFree( GetProcessHeap(), 0, longpathA
);
992 HeapFree( GetProcessHeap(), 0, shortpathA
);
998 /***********************************************************************
999 * GetLongPathName32A (KERNEL32.xxx)
1001 DWORD WINAPI
GetLongPathNameA( LPCSTR shortpath
, LPSTR longpath
,
1004 DOS_FULL_NAME full_name
;
1009 if (!DOSFS_GetFullName( shortpath
, TRUE
, &full_name
)) return 0;
1010 lstrcpynA( longpath
, full_name
.short_name
, longlen
);
1011 /* Do some hackery to get the long filename.
1012 * FIXME: Would be better if it returned the
1013 * long version of the directories too
1015 longfilename
= strrchr(full_name
.long_name
, '/')+1;
1016 if (longpath
!= NULL
) {
1017 if ((p
= strrchr( longpath
, '\\' )) != NULL
) {
1019 longlen
-= (p
-longpath
);
1020 lstrcpynA( p
, longfilename
, longlen
);
1024 ((strrchr( full_name
.short_name
, '\\' ) - full_name
.short_name
) + 1);
1025 return shortpathlen
+ strlen( longfilename
);
1029 /***********************************************************************
1030 * GetLongPathName32W (KERNEL32.269)
1032 DWORD WINAPI
GetLongPathNameW( LPCWSTR shortpath
, LPWSTR longpath
,
1035 DOS_FULL_NAME full_name
;
1037 LPSTR shortpathA
= HEAP_strdupWtoA( GetProcessHeap(), 0, shortpath
);
1039 /* FIXME: is it correct to always return a fully qualified short path? */
1040 if (DOSFS_GetFullName( shortpathA
, TRUE
, &full_name
))
1042 ret
= strlen( full_name
.short_name
);
1043 lstrcpynAtoW( longpath
, full_name
.long_name
, longlen
);
1045 HeapFree( GetProcessHeap(), 0, shortpathA
);
1050 /***********************************************************************
1051 * DOSFS_DoGetFullPathName
1053 * Implementation of GetFullPathName32A/W.
1055 static DWORD
DOSFS_DoGetFullPathName( LPCSTR name
, DWORD len
, LPSTR result
,
1058 char buffer
[MAX_PATHNAME_LEN
];
1063 /* last possible position for a char != 0 */
1064 char *endchar
= buffer
+ sizeof(buffer
) - 2;
1067 TRACE(dosfs
, "converting '%s'\n", name
);
1069 if (!name
|| !result
|| ((drive
= DOSFS_GetPathDrive( &name
)) == -1) )
1070 { SetLastError( ERROR_INVALID_PARAMETER
);
1077 if (IS_END_OF_NAME(*name
) && (*name
)) /* Absolute path */
1079 while (((*name
== '\\') || (*name
== '/')) && (!*endchar
) )
1082 else /* Relative path or empty path */
1085 lstrcpynA( p
, DRIVE_GetDosCwd(drive
), sizeof(buffer
) - 4 );
1098 if (IS_END_OF_NAME(name
[1]))
1101 while ((*name
== '\\') || (*name
== '/')) name
++;
1104 else if ((name
[1] == '.') && IS_END_OF_NAME(name
[2]))
1107 while ((*name
== '\\') || (*name
== '/')) name
++;
1109 if (p
< buffer
+ 3) /* no previous dir component */
1111 p
--; /* skip previously added '\\' */
1112 while ((*p
== '\\') || (*p
== '/')) p
--;
1113 /* skip previous dir component */
1114 while ((*p
!= '\\') && (*p
!= '/')) p
--;
1120 { SetLastError( ERROR_PATH_NOT_FOUND
);
1123 while (!IS_END_OF_NAME(*name
) && (!*endchar
) )
1125 while (((*name
== '\\') || (*name
== '/')) && (!*endchar
) )
1130 if (!(DRIVE_GetFlags(drive
) & DRIVE_CASE_PRESERVING
))
1131 CharUpperA( buffer
);
1134 lstrcpynAtoW( (LPWSTR
)result
, buffer
, len
);
1136 lstrcpynA( result
, buffer
, len
);
1138 TRACE(dosfs
, "returning '%s'\n", buffer
);
1140 /* If the lpBuffer buffer is too small, the return value is the
1141 size of the buffer, in characters, required to hold the path. */
1143 ret
= strlen(buffer
);
1146 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
1152 /***********************************************************************
1153 * GetFullPathName32A (KERNEL32.272)
1155 * if the path closed with '\', *lastpart is 0
1157 DWORD WINAPI
GetFullPathNameA( LPCSTR name
, DWORD len
, LPSTR buffer
,
1160 DWORD ret
= DOSFS_DoGetFullPathName( name
, len
, buffer
, FALSE
);
1161 if (ret
&& lastpart
)
1163 LPSTR p
= buffer
+ strlen(buffer
);
1167 while ((p
> buffer
+ 2) && (*p
!= '\\')) p
--;
1170 else *lastpart
= NULL
;
1176 /***********************************************************************
1177 * GetFullPathName32W (KERNEL32.273)
1179 DWORD WINAPI
GetFullPathNameW( LPCWSTR name
, DWORD len
, LPWSTR buffer
,
1182 LPSTR nameA
= HEAP_strdupWtoA( GetProcessHeap(), 0, name
);
1183 DWORD ret
= DOSFS_DoGetFullPathName( nameA
, len
, (LPSTR
)buffer
, TRUE
);
1184 HeapFree( GetProcessHeap(), 0, nameA
);
1185 if (ret
&& lastpart
)
1187 LPWSTR p
= buffer
+ lstrlenW(buffer
);
1188 if (*p
!= (WCHAR
)'\\')
1190 while ((p
> buffer
+ 2) && (*p
!= (WCHAR
)'\\')) p
--;
1193 else *lastpart
= NULL
;
1198 /***********************************************************************
1201 static int DOSFS_FindNextEx( FIND_FIRST_INFO
*info
, WIN32_FIND_DATAA
*entry
)
1203 BYTE attr
= info
->attr
| FA_UNUSED
| FA_ARCHIVE
| FA_RDONLY
;
1204 UINT flags
= DRIVE_GetFlags( info
->drive
);
1205 char *p
, buffer
[MAX_PATHNAME_LEN
];
1206 const char *drive_path
;
1208 LPCSTR long_name
, short_name
;
1209 BY_HANDLE_FILE_INFORMATION fileinfo
;
1212 if ((info
->attr
& ~(FA_UNUSED
| FA_ARCHIVE
| FA_RDONLY
)) == FA_LABEL
)
1214 if (info
->cur_pos
) return 0;
1215 entry
->dwFileAttributes
= FILE_ATTRIBUTE_LABEL
;
1216 DOSFS_UnixTimeToFileTime( (time_t)0, &entry
->ftCreationTime
, 0 );
1217 DOSFS_UnixTimeToFileTime( (time_t)0, &entry
->ftLastAccessTime
, 0 );
1218 DOSFS_UnixTimeToFileTime( (time_t)0, &entry
->ftLastWriteTime
, 0 );
1219 entry
->nFileSizeHigh
= 0;
1220 entry
->nFileSizeLow
= 0;
1221 entry
->dwReserved0
= 0;
1222 entry
->dwReserved1
= 0;
1223 DOSFS_ToDosDTAFormat( DRIVE_GetLabel( info
->drive
), entry
->cFileName
);
1224 strcpy( entry
->cAlternateFileName
, entry
->cFileName
);
1229 drive_path
= info
->path
+ strlen(DRIVE_GetRoot( info
->drive
));
1230 while ((*drive_path
== '/') || (*drive_path
== '\\')) drive_path
++;
1231 drive_root
= !*drive_path
;
1233 lstrcpynA( buffer
, info
->path
, sizeof(buffer
) - 1 );
1234 strcat( buffer
, "/" );
1235 p
= buffer
+ strlen(buffer
);
1237 while (DOSFS_ReadDir( info
->dir
, &long_name
, &short_name
))
1241 /* Don't return '.' and '..' in the root of the drive */
1242 if (drive_root
&& (long_name
[0] == '.') &&
1243 (!long_name
[1] || ((long_name
[1] == '.') && !long_name
[2])))
1246 /* Check the long mask */
1248 if (info
->long_mask
)
1250 if (!DOSFS_MatchLong( info
->long_mask
, long_name
,
1251 flags
& DRIVE_CASE_SENSITIVE
)) continue;
1254 /* Check the short mask */
1256 if (info
->short_mask
)
1260 DOSFS_Hash( long_name
, dos_name
, TRUE
,
1261 !(flags
& DRIVE_CASE_SENSITIVE
) );
1262 short_name
= dos_name
;
1264 if (!DOSFS_MatchShort( info
->short_mask
, short_name
)) continue;
1267 /* Check the file attributes */
1269 lstrcpynA( p
, long_name
, sizeof(buffer
) - (int)(p
- buffer
) );
1270 if (!FILE_Stat( buffer
, &fileinfo
))
1272 WARN(dosfs
, "can't stat %s\n", buffer
);
1275 if (fileinfo
.dwFileAttributes
& ~attr
) continue;
1277 /* We now have a matching entry; fill the result and return */
1279 entry
->dwFileAttributes
= fileinfo
.dwFileAttributes
;
1280 entry
->ftCreationTime
= fileinfo
.ftCreationTime
;
1281 entry
->ftLastAccessTime
= fileinfo
.ftLastAccessTime
;
1282 entry
->ftLastWriteTime
= fileinfo
.ftLastWriteTime
;
1283 entry
->nFileSizeHigh
= fileinfo
.nFileSizeHigh
;
1284 entry
->nFileSizeLow
= fileinfo
.nFileSizeLow
;
1287 DOSFS_ToDosDTAFormat( short_name
, entry
->cAlternateFileName
);
1289 DOSFS_Hash( long_name
, entry
->cAlternateFileName
, FALSE
,
1290 !(flags
& DRIVE_CASE_SENSITIVE
) );
1292 lstrcpynA( entry
->cFileName
, long_name
, sizeof(entry
->cFileName
) );
1293 if (!(flags
& DRIVE_CASE_PRESERVING
)) CharLowerA( entry
->cFileName
);
1294 TRACE(dosfs
, "returning %s (%s) %02lx %ld\n",
1295 entry
->cFileName
, entry
->cAlternateFileName
,
1296 entry
->dwFileAttributes
, entry
->nFileSizeLow
);
1299 return 0; /* End of directory */
1302 /***********************************************************************
1305 * Find the next matching file. Return the number of entries read to find
1306 * the matching one, or 0 if no more entries.
1307 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
1308 * file name mask. Either or both can be NULL.
1310 * NOTE: This is supposed to be only called by the int21 emulation
1311 * routines. Thus, we should own the Win16Mutex anyway.
1312 * Nevertheless, we explicitly enter it to ensure the static
1313 * directory cache is protected.
1315 int DOSFS_FindNext( const char *path
, const char *short_mask
,
1316 const char *long_mask
, int drive
, BYTE attr
,
1317 int skip
, WIN32_FIND_DATAA
*entry
)
1319 static FIND_FIRST_INFO info
= { NULL
};
1320 LPCSTR short_name
, long_name
;
1323 SYSLEVEL_EnterWin16Lock();
1325 /* Check the cached directory */
1326 if (!(info
.dir
&& info
.path
== path
&& info
.short_mask
== short_mask
1327 && info
.long_mask
== long_mask
&& info
.drive
== drive
1328 && info
.attr
== attr
&& info
.cur_pos
<= skip
))
1330 /* Not in the cache, open it anew */
1331 if (info
.dir
) DOSFS_CloseDir( info
.dir
);
1333 info
.path
= (LPSTR
)path
;
1334 info
.long_mask
= (LPSTR
)long_mask
;
1335 info
.short_mask
= (LPSTR
)short_mask
;
1339 info
.dir
= DOSFS_OpenDir( info
.path
);
1342 /* Skip to desired position */
1343 while (info
.cur_pos
< skip
)
1344 if (info
.dir
&& DOSFS_ReadDir( info
.dir
, &long_name
, &short_name
))
1349 if (info
.dir
&& info
.cur_pos
== skip
&& DOSFS_FindNextEx( &info
, entry
))
1350 count
= info
.cur_pos
- skip
;
1356 if (info
.dir
) DOSFS_CloseDir( info
.dir
);
1357 memset( &info
, '\0', sizeof(info
) );
1360 SYSLEVEL_LeaveWin16Lock();
1367 /*************************************************************************
1368 * FindFirstFile16 (KERNEL.413)
1370 HANDLE16 WINAPI
FindFirstFile16( LPCSTR path
, WIN32_FIND_DATAA
*data
)
1372 DOS_FULL_NAME full_name
;
1374 FIND_FIRST_INFO
*info
;
1376 data
->dwReserved0
= data
->dwReserved1
= 0x0;
1377 if (!path
) return 0;
1378 if (!DOSFS_GetFullName( path
, FALSE
, &full_name
))
1379 return INVALID_HANDLE_VALUE16
;
1380 if (!(handle
= GlobalAlloc16( GMEM_MOVEABLE
, sizeof(FIND_FIRST_INFO
) )))
1381 return INVALID_HANDLE_VALUE16
;
1382 info
= (FIND_FIRST_INFO
*)GlobalLock16( handle
);
1383 info
->path
= HEAP_strdupA( SystemHeap
, 0, full_name
.long_name
);
1384 info
->long_mask
= strrchr( info
->path
, '/' );
1385 *(info
->long_mask
++) = '\0';
1386 info
->short_mask
= NULL
;
1388 if (path
[0] && (path
[1] == ':')) info
->drive
= toupper(*path
) - 'A';
1389 else info
->drive
= DRIVE_GetCurrentDrive();
1392 info
->dir
= DOSFS_OpenDir( info
->path
);
1394 GlobalUnlock16( handle
);
1395 if (!FindNextFile16( handle
, data
))
1397 FindClose16( handle
);
1398 SetLastError( ERROR_NO_MORE_FILES
);
1399 return INVALID_HANDLE_VALUE16
;
1405 /*************************************************************************
1406 * FindFirstFile32A (KERNEL32.123)
1408 HANDLE WINAPI
FindFirstFileA( LPCSTR path
, WIN32_FIND_DATAA
*data
)
1410 HANDLE handle
= FindFirstFile16( path
, data
);
1411 if (handle
== INVALID_HANDLE_VALUE16
) return INVALID_HANDLE_VALUE
;
1416 /*************************************************************************
1417 * FindFirstFile32W (KERNEL32.124)
1419 HANDLE WINAPI
FindFirstFileW( LPCWSTR path
, WIN32_FIND_DATAW
*data
)
1421 WIN32_FIND_DATAA dataA
;
1422 LPSTR pathA
= HEAP_strdupWtoA( GetProcessHeap(), 0, path
);
1423 HANDLE handle
= FindFirstFileA( pathA
, &dataA
);
1424 HeapFree( GetProcessHeap(), 0, pathA
);
1425 if (handle
!= INVALID_HANDLE_VALUE
)
1427 data
->dwFileAttributes
= dataA
.dwFileAttributes
;
1428 data
->ftCreationTime
= dataA
.ftCreationTime
;
1429 data
->ftLastAccessTime
= dataA
.ftLastAccessTime
;
1430 data
->ftLastWriteTime
= dataA
.ftLastWriteTime
;
1431 data
->nFileSizeHigh
= dataA
.nFileSizeHigh
;
1432 data
->nFileSizeLow
= dataA
.nFileSizeLow
;
1433 lstrcpyAtoW( data
->cFileName
, dataA
.cFileName
);
1434 lstrcpyAtoW( data
->cAlternateFileName
, dataA
.cAlternateFileName
);
1440 /*************************************************************************
1441 * FindNextFile16 (KERNEL.414)
1443 BOOL16 WINAPI
FindNextFile16( HANDLE16 handle
, WIN32_FIND_DATAA
*data
)
1445 FIND_FIRST_INFO
*info
;
1447 if (!(info
= (FIND_FIRST_INFO
*)GlobalLock16( handle
)))
1449 SetLastError( ERROR_INVALID_HANDLE
);
1452 GlobalUnlock16( handle
);
1453 if (!info
->path
|| !info
->dir
)
1455 SetLastError( ERROR_NO_MORE_FILES
);
1458 if (!DOSFS_FindNextEx( info
, data
))
1460 DOSFS_CloseDir( info
->dir
); info
->dir
= NULL
;
1461 HeapFree( SystemHeap
, 0, info
->path
);
1462 info
->path
= info
->long_mask
= NULL
;
1463 SetLastError( ERROR_NO_MORE_FILES
);
1470 /*************************************************************************
1471 * FindNextFile32A (KERNEL32.126)
1473 BOOL WINAPI
FindNextFileA( HANDLE handle
, WIN32_FIND_DATAA
*data
)
1475 return FindNextFile16( handle
, data
);
1479 /*************************************************************************
1480 * FindNextFile32W (KERNEL32.127)
1482 BOOL WINAPI
FindNextFileW( HANDLE handle
, WIN32_FIND_DATAW
*data
)
1484 WIN32_FIND_DATAA dataA
;
1485 if (!FindNextFileA( handle
, &dataA
)) return FALSE
;
1486 data
->dwFileAttributes
= dataA
.dwFileAttributes
;
1487 data
->ftCreationTime
= dataA
.ftCreationTime
;
1488 data
->ftLastAccessTime
= dataA
.ftLastAccessTime
;
1489 data
->ftLastWriteTime
= dataA
.ftLastWriteTime
;
1490 data
->nFileSizeHigh
= dataA
.nFileSizeHigh
;
1491 data
->nFileSizeLow
= dataA
.nFileSizeLow
;
1492 lstrcpyAtoW( data
->cFileName
, dataA
.cFileName
);
1493 lstrcpyAtoW( data
->cAlternateFileName
, dataA
.cAlternateFileName
);
1498 /*************************************************************************
1499 * FindClose16 (KERNEL.415)
1501 BOOL16 WINAPI
FindClose16( HANDLE16 handle
)
1503 FIND_FIRST_INFO
*info
;
1505 if ((handle
== INVALID_HANDLE_VALUE16
) ||
1506 !(info
= (FIND_FIRST_INFO
*)GlobalLock16( handle
)))
1508 SetLastError( ERROR_INVALID_HANDLE
);
1511 if (info
->dir
) DOSFS_CloseDir( info
->dir
);
1512 if (info
->path
) HeapFree( SystemHeap
, 0, info
->path
);
1513 GlobalUnlock16( handle
);
1514 GlobalFree16( handle
);
1519 /*************************************************************************
1520 * FindClose32 (KERNEL32.119)
1522 BOOL WINAPI
FindClose( HANDLE handle
)
1524 return FindClose16( (HANDLE16
)handle
);
1528 /***********************************************************************
1529 * DOSFS_UnixTimeToFileTime
1531 * Convert a Unix time to FILETIME format.
1532 * The FILETIME structure is a 64-bit value representing the number of
1533 * 100-nanosecond intervals since January 1, 1601, 0:00.
1534 * 'remainder' is the nonnegative number of 100-ns intervals
1535 * corresponding to the time fraction smaller than 1 second that
1536 * couldn't be stored in the time_t value.
1538 void DOSFS_UnixTimeToFileTime( time_t unix_time
, FILETIME
*filetime
,
1544 The time difference between 1 January 1601, 00:00:00 and
1545 1 January 1970, 00:00:00 is 369 years, plus the leap years
1546 from 1604 to 1968, excluding 1700, 1800, 1900.
1547 This makes (1968 - 1600) / 4 - 3 = 89 leap days, and a total
1550 Any day in that period had 24 * 60 * 60 = 86400 seconds.
1552 The time difference is 134774 * 86400 * 10000000, which can be written
1554 27111902 * 2^32 + 3577643008
1555 413 * 2^48 + 45534 * 2^32 + 54590 * 2^16 + 32768
1557 If you find that these constants are buggy, please change them in all
1558 instances in both conversion functions.
1561 There are two versions, one of them uses long long variables and
1562 is presumably faster but not ISO C. The other one uses standard C
1563 data types and operations but relies on the assumption that negative
1564 numbers are stored as 2's complement (-1 is 0xffff....). If this
1565 assumption is violated, dates before 1970 will not convert correctly.
1566 This should however work on any reasonable architecture where WINE
1571 Take care not to remove the casts. I have tested these functions
1572 (in both versions) for a lot of numbers. I would be interested in
1573 results on other compilers than GCC.
1575 The operations have been designed to account for the possibility
1576 of 64-bit time_t in future UNICES. Even the versions without
1577 internal long long numbers will work if time_t only is 64 bit.
1578 A 32-bit shift, which was necessary for that operation, turned out
1579 not to work correctly in GCC, besides giving the warning. So I
1580 used a double 16-bit shift instead. Numbers are in the ISO version
1581 represented by three limbs, the most significant with 32 bit, the
1582 other two with 16 bit each.
1584 As the modulo-operator % is not well-defined for negative numbers,
1585 negative divisors have been avoided in DOSFS_FileTimeToUnixTime.
1587 There might be quicker ways to do this in C. Certainly so in
1590 Claus Fischer, fischer@iue.tuwien.ac.at
1593 #if (SIZEOF_LONG_LONG >= 8)
1594 # define USE_LONG_LONG 1
1596 # define USE_LONG_LONG 0
1599 #if USE_LONG_LONG /* gcc supports long long type */
1601 long long int t
= unix_time
;
1603 t
+= 116444736000000000LL;
1605 filetime
->dwLowDateTime
= (UINT
)t
;
1606 filetime
->dwHighDateTime
= (UINT
)(t
>> 32);
1608 #else /* ISO version */
1610 UINT a0
; /* 16 bit, low bits */
1611 UINT a1
; /* 16 bit, medium bits */
1612 UINT a2
; /* 32 bit, high bits */
1614 /* Copy the unix time to a2/a1/a0 */
1615 a0
= unix_time
& 0xffff;
1616 a1
= (unix_time
>> 16) & 0xffff;
1617 /* This is obsolete if unix_time is only 32 bits, but it does not hurt.
1618 Do not replace this by >> 32, it gives a compiler warning and it does
1620 a2
= (unix_time
>= 0 ? (unix_time
>> 16) >> 16 :
1621 ~((~unix_time
>> 16) >> 16));
1623 /* Multiply a by 10000000 (a = a2/a1/a0)
1624 Split the factor into 10000 * 1000 which are both less than 0xffff. */
1626 a1
= a1
* 10000 + (a0
>> 16);
1627 a2
= a2
* 10000 + (a1
>> 16);
1632 a1
= a1
* 1000 + (a0
>> 16);
1633 a2
= a2
* 1000 + (a1
>> 16);
1637 /* Add the time difference and the remainder */
1638 a0
+= 32768 + (remainder
& 0xffff);
1639 a1
+= 54590 + (remainder
>> 16 ) + (a0
>> 16);
1640 a2
+= 27111902 + (a1
>> 16);
1645 filetime
->dwLowDateTime
= (a1
<< 16) + a0
;
1646 filetime
->dwHighDateTime
= a2
;
1651 /***********************************************************************
1652 * DOSFS_FileTimeToUnixTime
1654 * Convert a FILETIME format to Unix time.
1655 * If not NULL, 'remainder' contains the fractional part of the filetime,
1656 * in the range of [0..9999999] (even if time_t is negative).
1658 time_t DOSFS_FileTimeToUnixTime( const FILETIME
*filetime
, DWORD
*remainder
)
1660 /* Read the comment in the function DOSFS_UnixTimeToFileTime. */
1663 long long int t
= filetime
->dwHighDateTime
;
1665 t
+= (UINT
)filetime
->dwLowDateTime
;
1666 t
-= 116444736000000000LL;
1669 if (remainder
) *remainder
= 9999999 - (-t
- 1) % 10000000;
1670 return -1 - ((-t
- 1) / 10000000);
1674 if (remainder
) *remainder
= t
% 10000000;
1675 return t
/ 10000000;
1678 #else /* ISO version */
1680 UINT a0
; /* 16 bit, low bits */
1681 UINT a1
; /* 16 bit, medium bits */
1682 UINT a2
; /* 32 bit, high bits */
1683 UINT r
; /* remainder of division */
1684 unsigned int carry
; /* carry bit for subtraction */
1685 int negative
; /* whether a represents a negative value */
1687 /* Copy the time values to a2/a1/a0 */
1688 a2
= (UINT
)filetime
->dwHighDateTime
;
1689 a1
= ((UINT
)filetime
->dwLowDateTime
) >> 16;
1690 a0
= ((UINT
)filetime
->dwLowDateTime
) & 0xffff;
1692 /* Subtract the time difference */
1693 if (a0
>= 32768 ) a0
-= 32768 , carry
= 0;
1694 else a0
+= (1 << 16) - 32768 , carry
= 1;
1696 if (a1
>= 54590 + carry
) a1
-= 54590 + carry
, carry
= 0;
1697 else a1
+= (1 << 16) - 54590 - carry
, carry
= 1;
1699 a2
-= 27111902 + carry
;
1701 /* If a is negative, replace a by (-1-a) */
1702 negative
= (a2
>= ((UINT
)1) << 31);
1705 /* Set a to -a - 1 (a is a2/a1/a0) */
1711 /* Divide a by 10000000 (a = a2/a1/a0), put the rest into r.
1712 Split the divisor into 10000 * 1000 which are both less than 0xffff. */
1713 a1
+= (a2
% 10000) << 16;
1715 a0
+= (a1
% 10000) << 16;
1720 a1
+= (a2
% 1000) << 16;
1722 a0
+= (a1
% 1000) << 16;
1724 r
+= (a0
% 1000) * 10000;
1727 /* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
1730 /* Set a to -a - 1 (a is a2/a1/a0) */
1738 if (remainder
) *remainder
= r
;
1740 /* Do not replace this by << 32, it gives a compiler warning and it does
1742 return ((((time_t)a2
) << 16) << 16) + (a1
<< 16) + a0
;
1747 /***********************************************************************
1748 * DosDateTimeToFileTime (KERNEL32.76)
1750 BOOL WINAPI
DosDateTimeToFileTime( WORD fatdate
, WORD fattime
, LPFILETIME ft
)
1754 newtm
.tm_sec
= (fattime
& 0x1f) * 2;
1755 newtm
.tm_min
= (fattime
>> 5) & 0x3f;
1756 newtm
.tm_hour
= (fattime
>> 11);
1757 newtm
.tm_mday
= (fatdate
& 0x1f);
1758 newtm
.tm_mon
= ((fatdate
>> 5) & 0x0f) - 1;
1759 newtm
.tm_year
= (fatdate
>> 9) + 80;
1760 DOSFS_UnixTimeToFileTime( mktime( &newtm
), ft
, 0 );
1765 /***********************************************************************
1766 * FileTimeToDosDateTime (KERNEL32.111)
1768 BOOL WINAPI
FileTimeToDosDateTime( const FILETIME
*ft
, LPWORD fatdate
,
1771 time_t unixtime
= DOSFS_FileTimeToUnixTime( ft
, NULL
);
1772 struct tm
*tm
= localtime( &unixtime
);
1774 *fattime
= (tm
->tm_hour
<< 11) + (tm
->tm_min
<< 5) + (tm
->tm_sec
/ 2);
1776 *fatdate
= ((tm
->tm_year
- 80) << 9) + ((tm
->tm_mon
+ 1) << 5)
1782 /***********************************************************************
1783 * LocalFileTimeToFileTime (KERNEL32.373)
1785 BOOL WINAPI
LocalFileTimeToFileTime( const FILETIME
*localft
,
1791 /* convert from local to UTC. Perhaps not correct. FIXME */
1792 time_t unixtime
= DOSFS_FileTimeToUnixTime( localft
, &remainder
);
1793 xtm
= gmtime( &unixtime
);
1794 DOSFS_UnixTimeToFileTime( mktime(xtm
), utcft
, remainder
);
1799 /***********************************************************************
1800 * FileTimeToLocalFileTime (KERNEL32.112)
1802 BOOL WINAPI
FileTimeToLocalFileTime( const FILETIME
*utcft
,
1803 LPFILETIME localft
)
1806 /* convert from UTC to local. Perhaps not correct. FIXME */
1807 time_t unixtime
= DOSFS_FileTimeToUnixTime( utcft
, &remainder
);
1809 struct tm
*xtm
= localtime( &unixtime
);
1812 localtime
= timegm(xtm
);
1813 DOSFS_UnixTimeToFileTime( localtime
, localft
, remainder
);
1816 struct tm
*xtm
,*gtm
;
1819 xtm
= localtime( &unixtime
);
1820 gtm
= gmtime( &unixtime
);
1821 time1
= mktime(xtm
);
1822 time2
= mktime(gtm
);
1823 DOSFS_UnixTimeToFileTime( 2*time1
-time2
, localft
, remainder
);
1829 /***********************************************************************
1830 * FileTimeToSystemTime (KERNEL32.113)
1832 BOOL WINAPI
FileTimeToSystemTime( const FILETIME
*ft
, LPSYSTEMTIME syst
)
1836 time_t xtime
= DOSFS_FileTimeToUnixTime( ft
, &remainder
);
1837 xtm
= gmtime(&xtime
);
1838 syst
->wYear
= xtm
->tm_year
+1900;
1839 syst
->wMonth
= xtm
->tm_mon
+ 1;
1840 syst
->wDayOfWeek
= xtm
->tm_wday
;
1841 syst
->wDay
= xtm
->tm_mday
;
1842 syst
->wHour
= xtm
->tm_hour
;
1843 syst
->wMinute
= xtm
->tm_min
;
1844 syst
->wSecond
= xtm
->tm_sec
;
1845 syst
->wMilliseconds
= remainder
/ 10000;
1849 /***********************************************************************
1850 * QueryDosDeviceA (KERNEL32.413)
1852 * returns array of strings terminated by \0, terminated by \0
1854 DWORD WINAPI
QueryDosDeviceA(LPCSTR devname
,LPSTR target
,DWORD bufsize
)
1859 TRACE(dosfs
,"(%s,...)\n",devname
?devname
:"<null>");
1861 /* return known MSDOS devices */
1862 lstrcpyA(buffer
,"CON COM1 COM2 LPT1 NUL ");
1863 while ((s
=strchr(buffer
,' ')))
1866 lstrcpynA(target
,buffer
,bufsize
);
1867 return strlen(buffer
);
1869 lstrcpyA(buffer
,"\\DEV\\");
1870 lstrcatA(buffer
,devname
);
1871 if ((s
=strchr(buffer
,':'))) *s
='\0';
1872 lstrcpynA(target
,buffer
,bufsize
);
1873 return strlen(buffer
);
1877 /***********************************************************************
1878 * QueryDosDeviceW (KERNEL32.414)
1880 * returns array of strings terminated by \0, terminated by \0
1882 DWORD WINAPI
QueryDosDeviceW(LPCWSTR devname
,LPWSTR target
,DWORD bufsize
)
1884 LPSTR devnameA
= devname
?HEAP_strdupWtoA(GetProcessHeap(),0,devname
):NULL
;
1885 LPSTR targetA
= (LPSTR
)HEAP_xalloc(GetProcessHeap(),0,bufsize
);
1886 DWORD ret
= QueryDosDeviceA(devnameA
,targetA
,bufsize
);
1888 lstrcpynAtoW(target
,targetA
,bufsize
);
1889 if (devnameA
) HeapFree(GetProcessHeap(),0,devnameA
);
1890 if (targetA
) HeapFree(GetProcessHeap(),0,targetA
);
1895 /***********************************************************************
1896 * SystemTimeToFileTime (KERNEL32.526)
1898 BOOL WINAPI
SystemTimeToFileTime( const SYSTEMTIME
*syst
, LPFILETIME ft
)
1904 struct tm xtm
,*local_tm
,*utc_tm
;
1905 time_t localtim
,utctime
;
1908 xtm
.tm_year
= syst
->wYear
-1900;
1909 xtm
.tm_mon
= syst
->wMonth
- 1;
1910 xtm
.tm_wday
= syst
->wDayOfWeek
;
1911 xtm
.tm_mday
= syst
->wDay
;
1912 xtm
.tm_hour
= syst
->wHour
;
1913 xtm
.tm_min
= syst
->wMinute
;
1914 xtm
.tm_sec
= syst
->wSecond
; /* this is UTC */
1917 utctime
= timegm(&xtm
);
1918 DOSFS_UnixTimeToFileTime( utctime
, ft
,
1919 syst
->wMilliseconds
* 10000 );
1921 localtim
= mktime(&xtm
); /* now we've got local time */
1922 local_tm
= localtime(&localtim
);
1923 utc_tm
= gmtime(&localtim
);
1924 utctime
= mktime(utc_tm
);
1925 DOSFS_UnixTimeToFileTime( 2*localtim
-utctime
, ft
,
1926 syst
->wMilliseconds
* 10000 );
1931 BOOL WINAPI
DefineDosDeviceA(DWORD flags
,LPCSTR devname
,LPCSTR targetpath
) {
1932 FIXME(dosfs
,"(0x%08lx,%s,%s),stub!\n",flags
,devname
,targetpath
);
1933 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);