2 * DOS file system functions
4 * Copyright 1993 Erik Bos
5 * Copyright 1996 Alexandre Julliard
13 #ifdef HAVE_SYS_ERRNO_H
14 #include <sys/errno.h>
20 #include <sys/ioctl.h>
26 #include "wine/winbase16.h"
36 #include "debugtools.h"
38 DECLARE_DEBUG_CHANNEL(dosfs
)
39 DECLARE_DEBUG_CHANNEL(file
)
41 /* Define the VFAT ioctl to get both short and long file names */
42 /* FIXME: is it possible to get this to work on other systems? */
44 #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, long)
45 /* We want the real kernel dirent structure, not the libc one */
50 unsigned short d_reclen
;
55 #undef VFAT_IOCTL_READDIR_BOTH /* just in case... */
58 /* Chars we don't want to see in DOS file names */
59 #define INVALID_DOS_CHARS "*?<>|\"+=,;[] \345"
61 static const DOS_DEVICE DOSFS_Devices
[] =
62 /* name, device flags (see Int 21/AX=0x4400) */
76 { "SCSIMGR$", 0xc0c0 },
80 #define GET_DRIVE(path) \
81 (((path)[1] == ':') ? toupper((path)[0]) - 'A' : DOSFS_CurDrive)
83 /* Directory info for DOSFS_ReadDir */
87 #ifdef VFAT_IOCTL_READDIR_BOTH
90 KERNEL_DIRENT dirent
[2];
94 /* Info structure for FindFirstFile handle */
108 /***********************************************************************
111 * Return 1 if Unix file 'name' is also a valid MS-DOS name
112 * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
113 * File name can be terminated by '\0', '\\' or '/'.
115 static int DOSFS_ValidDOSName( const char *name
, int ignore_case
)
117 static const char invalid_chars
[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS
;
118 const char *p
= name
;
119 const char *invalid
= ignore_case
? (invalid_chars
+ 26) : invalid_chars
;
124 /* Check for "." and ".." */
127 /* All other names beginning with '.' are invalid */
128 return (IS_END_OF_NAME(*p
));
130 while (!IS_END_OF_NAME(*p
))
132 if (strchr( invalid
, *p
)) return 0; /* Invalid char */
133 if (*p
== '.') break; /* Start of the extension */
134 if (++len
> 8) return 0; /* Name too long */
137 if (*p
!= '.') return 1; /* End of name */
139 if (IS_END_OF_NAME(*p
)) return 0; /* Empty extension not allowed */
141 while (!IS_END_OF_NAME(*p
))
143 if (strchr( invalid
, *p
)) return 0; /* Invalid char */
144 if (*p
== '.') return 0; /* Second extension not allowed */
145 if (++len
> 3) return 0; /* Extension too long */
152 /***********************************************************************
153 * DOSFS_ToDosFCBFormat
155 * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
156 * expanding wild cards and converting to upper-case in the process.
157 * File name can be terminated by '\0', '\\' or '/'.
158 * Return FALSE if the name is not a valid DOS name.
159 * 'buffer' must be at least 12 characters long.
161 BOOL
DOSFS_ToDosFCBFormat( LPCSTR name
, LPSTR buffer
)
163 static const char invalid_chars
[] = INVALID_DOS_CHARS
;
164 const char *p
= name
;
167 /* Check for "." and ".." */
171 strcpy( buffer
, ". " );
177 return (!*p
|| (*p
== '/') || (*p
== '\\'));
180 for (i
= 0; i
< 8; i
++)
197 if (strchr( invalid_chars
, *p
)) return FALSE
;
198 buffer
[i
] = toupper(*p
);
206 /* Skip all chars after wildcard up to first dot */
207 while (*p
&& (*p
!= '/') && (*p
!= '\\') && (*p
!= '.')) p
++;
211 /* Check if name too long */
212 if (*p
&& (*p
!= '/') && (*p
!= '\\') && (*p
!= '.')) return FALSE
;
214 if (*p
== '.') p
++; /* Skip dot */
216 for (i
= 8; i
< 11; i
++)
226 return FALSE
; /* Second extension not allowed */
234 if (strchr( invalid_chars
, *p
)) return FALSE
;
235 buffer
[i
] = toupper(*p
);
245 /***********************************************************************
246 * DOSFS_ToDosDTAFormat
248 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
249 * converting to upper-case in the process.
250 * File name can be terminated by '\0', '\\' or '/'.
251 * 'buffer' must be at least 13 characters long.
253 static void DOSFS_ToDosDTAFormat( LPCSTR name
, LPSTR buffer
)
257 memcpy( buffer
, name
, 8 );
258 for (p
= buffer
+ 8; (p
> buffer
) && (p
[-1] == ' '); p
--);
260 memcpy( p
, name
+ 8, 3 );
261 for (p
+= 3; p
[-1] == ' '; p
--);
262 if (p
[-1] == '.') p
--;
267 /***********************************************************************
270 * Check a DOS file name against a mask (both in FCB format).
272 static int DOSFS_MatchShort( const char *mask
, const char *name
)
275 for (i
= 11; i
> 0; i
--, mask
++, name
++)
276 if ((*mask
!= '?') && (*mask
!= *name
)) return 0;
281 /***********************************************************************
284 * Check a long file name against a mask.
286 static int DOSFS_MatchLong( const char *mask
, const char *name
,
289 if (!strcmp( mask
, "*.*" )) return 1;
290 while (*name
&& *mask
)
295 while (*mask
== '*') mask
++; /* Skip consecutive '*' */
296 if (!*mask
) return 1;
297 if (case_sensitive
) while (*name
&& (*name
!= *mask
)) name
++;
298 else while (*name
&& (toupper(*name
) != toupper(*mask
))) name
++;
301 else if (*mask
!= '?')
305 if (*mask
!= *name
) return 0;
307 else if (toupper(*mask
) != toupper(*name
)) return 0;
312 if (*mask
== '.') mask
++; /* Ignore trailing '.' in mask */
313 return (!*name
&& !*mask
);
317 /***********************************************************************
320 static DOS_DIR
*DOSFS_OpenDir( LPCSTR path
)
322 DOS_DIR
*dir
= HeapAlloc( SystemHeap
, 0, sizeof(*dir
) );
325 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
329 /* Treat empty path as root directory. This simplifies path split into
330 directory and mask in several other places */
331 if (!*path
) path
= "/";
333 #ifdef VFAT_IOCTL_READDIR_BOTH
335 /* Check if the VFAT ioctl is supported on this directory */
337 if ((dir
->fd
= open( path
, O_RDONLY
)) != -1)
339 if (ioctl( dir
->fd
, VFAT_IOCTL_READDIR_BOTH
, (long)dir
->dirent
) == -1)
346 /* Set the file pointer back at the start of the directory */
347 lseek( dir
->fd
, 0, SEEK_SET
);
352 #endif /* VFAT_IOCTL_READDIR_BOTH */
354 /* Now use the standard opendir/readdir interface */
356 if (!(dir
->dir
= opendir( path
)))
358 HeapFree( SystemHeap
, 0, dir
);
365 /***********************************************************************
368 static void DOSFS_CloseDir( DOS_DIR
*dir
)
370 #ifdef VFAT_IOCTL_READDIR_BOTH
371 if (dir
->fd
!= -1) close( dir
->fd
);
372 #endif /* VFAT_IOCTL_READDIR_BOTH */
373 if (dir
->dir
) closedir( dir
->dir
);
374 HeapFree( SystemHeap
, 0, dir
);
378 /***********************************************************************
381 static BOOL
DOSFS_ReadDir( DOS_DIR
*dir
, LPCSTR
*long_name
,
384 struct dirent
*dirent
;
386 #ifdef VFAT_IOCTL_READDIR_BOTH
389 if (ioctl( dir
->fd
, VFAT_IOCTL_READDIR_BOTH
, (long)dir
->dirent
) != -1) {
390 if (!dir
->dirent
[0].d_reclen
) return FALSE
;
391 if (!DOSFS_ToDosFCBFormat( dir
->dirent
[0].d_name
, dir
->short_name
))
392 dir
->short_name
[0] = '\0';
393 *short_name
= dir
->short_name
;
394 if (dir
->dirent
[1].d_name
[0]) *long_name
= dir
->dirent
[1].d_name
;
395 else *long_name
= dir
->dirent
[0].d_name
;
399 #endif /* VFAT_IOCTL_READDIR_BOTH */
401 if (!(dirent
= readdir( dir
->dir
))) return FALSE
;
402 *long_name
= dirent
->d_name
;
408 /***********************************************************************
411 * Transform a Unix file name into a hashed DOS name. If the name is a valid
412 * DOS name, it is converted to upper-case; otherwise it is replaced by a
413 * hashed version that fits in 8.3 format.
414 * File name can be terminated by '\0', '\\' or '/'.
415 * 'buffer' must be at least 13 characters long.
417 static void DOSFS_Hash( LPCSTR name
, LPSTR buffer
, BOOL dir_format
,
420 static const char invalid_chars
[] = INVALID_DOS_CHARS
"~.";
421 static const char hash_chars
[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
428 if (dir_format
) strcpy( buffer
, " " );
430 if (DOSFS_ValidDOSName( name
, ignore_case
))
432 /* Check for '.' and '..' */
436 if (!dir_format
) buffer
[1] = buffer
[2] = '\0';
437 if (name
[1] == '.') buffer
[1] = '.';
441 /* Simply copy the name, converting to uppercase */
443 for (dst
= buffer
; !IS_END_OF_NAME(*name
) && (*name
!= '.'); name
++)
444 *dst
++ = toupper(*name
);
447 if (dir_format
) dst
= buffer
+ 8;
449 for (name
++; !IS_END_OF_NAME(*name
); name
++)
450 *dst
++ = toupper(*name
);
452 if (!dir_format
) *dst
= '\0';
456 /* Compute the hash code of the file name */
457 /* If you know something about hash functions, feel free to */
458 /* insert a better algorithm here... */
461 for (p
= name
, hash
= 0xbeef; !IS_END_OF_NAME(p
[1]); p
++)
462 hash
= (hash
<<3) ^ (hash
>>5) ^ tolower(*p
) ^ (tolower(p
[1]) << 8);
463 hash
= (hash
<<3) ^ (hash
>>5) ^ tolower(*p
); /* Last character*/
467 for (p
= name
, hash
= 0xbeef; !IS_END_OF_NAME(p
[1]); p
++)
468 hash
= (hash
<< 3) ^ (hash
>> 5) ^ *p
^ (p
[1] << 8);
469 hash
= (hash
<< 3) ^ (hash
>> 5) ^ *p
; /* Last character */
472 /* Find last dot for start of the extension */
473 for (p
= name
+1, ext
= NULL
; !IS_END_OF_NAME(*p
); p
++)
474 if (*p
== '.') ext
= p
;
475 if (ext
&& IS_END_OF_NAME(ext
[1]))
476 ext
= NULL
; /* Empty extension ignored */
478 /* Copy first 4 chars, replacing invalid chars with '_' */
479 for (i
= 4, p
= name
, dst
= buffer
; i
> 0; i
--, p
++)
481 if (IS_END_OF_NAME(*p
) || (p
== ext
)) break;
482 *dst
++ = strchr( invalid_chars
, *p
) ? '_' : toupper(*p
);
484 /* Pad to 5 chars with '~' */
485 while (i
-- >= 0) *dst
++ = '~';
487 /* Insert hash code converted to 3 ASCII chars */
488 *dst
++ = hash_chars
[(hash
>> 10) & 0x1f];
489 *dst
++ = hash_chars
[(hash
>> 5) & 0x1f];
490 *dst
++ = hash_chars
[hash
& 0x1f];
492 /* Copy the first 3 chars of the extension (if any) */
495 if (!dir_format
) *dst
++ = '.';
496 for (i
= 3, ext
++; (i
> 0) && !IS_END_OF_NAME(*ext
); i
--, ext
++)
497 *dst
++ = strchr( invalid_chars
, *ext
) ? '_' : toupper(*ext
);
499 if (!dir_format
) *dst
= '\0';
503 /***********************************************************************
506 * Find the Unix file name in a given directory that corresponds to
507 * a file name (either in Unix or DOS format).
508 * File name can be terminated by '\0', '\\' or '/'.
509 * Return TRUE if OK, FALSE if no file name matches.
511 * 'long_buf' must be at least 'long_len' characters long. If the long name
512 * turns out to be larger than that, the function returns FALSE.
513 * 'short_buf' must be at least 13 characters long.
515 BOOL
DOSFS_FindUnixName( LPCSTR path
, LPCSTR name
, LPSTR long_buf
,
516 INT long_len
, LPSTR short_buf
, BOOL ignore_case
)
519 LPCSTR long_name
, short_name
;
520 char dos_name
[12], tmp_buf
[13];
523 const char *p
= strchr( name
, '/' );
524 int len
= p
? (int)(p
- name
) : strlen(name
);
525 if ((p
= strchr( name
, '\\' ))) len
= MIN( (int)(p
- name
), len
);
526 /* Ignore trailing dots */
527 while (len
> 1 && name
[len
-1] == '.') len
--;
528 if (long_len
< len
+ 1) return FALSE
;
530 TRACE_(dosfs
)("%s,%s\n", path
, name
);
532 if (!DOSFS_ToDosFCBFormat( name
, dos_name
)) dos_name
[0] = '\0';
534 if (!(dir
= DOSFS_OpenDir( path
)))
536 WARN_(dosfs
)("(%s,%s): can't open dir: %s\n",
537 path
, name
, strerror(errno
) );
541 while ((ret
= DOSFS_ReadDir( dir
, &long_name
, &short_name
)))
543 /* Check against Unix name */
544 if (len
== strlen(long_name
))
548 if (!strncmp( long_name
, name
, len
)) break;
552 if (!lstrncmpiA( long_name
, name
, len
)) break;
557 /* Check against hashed DOS name */
560 DOSFS_Hash( long_name
, tmp_buf
, TRUE
, ignore_case
);
561 short_name
= tmp_buf
;
563 if (!strcmp( dos_name
, short_name
)) break;
568 if (long_buf
) strcpy( long_buf
, long_name
);
572 DOSFS_ToDosDTAFormat( short_name
, short_buf
);
574 DOSFS_Hash( long_name
, short_buf
, FALSE
, ignore_case
);
576 TRACE_(dosfs
)("(%s,%s) -> %s (%s)\n",
577 path
, name
, long_name
, short_buf
? short_buf
: "***");
580 WARN_(dosfs
)("'%s' not found in '%s'\n", name
, path
);
581 DOSFS_CloseDir( dir
);
586 /***********************************************************************
589 * Check if a DOS file name represents a DOS device and return the device.
591 const DOS_DEVICE
*DOSFS_GetDevice( const char *name
)
596 if (!name
) return NULL
; /* if FILE_DupUnixHandle was used */
597 if (name
[0] && (name
[1] == ':')) name
+= 2;
598 if ((p
= strrchr( name
, '/' ))) name
= p
+ 1;
599 if ((p
= strrchr( name
, '\\' ))) name
= p
+ 1;
600 for (i
= 0; i
< sizeof(DOSFS_Devices
)/sizeof(DOSFS_Devices
[0]); i
++)
602 const char *dev
= DOSFS_Devices
[i
].name
;
603 if (!lstrncmpiA( dev
, name
, strlen(dev
) ))
605 p
= name
+ strlen( dev
);
606 if (!*p
|| (*p
== '.')) return &DOSFS_Devices
[i
];
613 /***********************************************************************
614 * DOSFS_GetDeviceByHandle
616 const DOS_DEVICE
*DOSFS_GetDeviceByHandle( HFILE hFile
)
618 struct get_file_info_request
*req
= get_req_buffer();
621 if (!server_call( REQ_GET_FILE_INFO
) && (req
->type
== FILE_TYPE_UNKNOWN
))
623 if ((req
->attr
>= 0) &&
624 (req
->attr
< sizeof(DOSFS_Devices
)/sizeof(DOSFS_Devices
[0])))
625 return &DOSFS_Devices
[req
->attr
];
631 /***********************************************************************
634 * Open a DOS device. This might not map 1:1 into the UNIX device concept.
636 HFILE
DOSFS_OpenDevice( const char *name
, DWORD access
)
641 if (!name
) return (HFILE
)NULL
; /* if FILE_DupUnixHandle was used */
642 if (name
[0] && (name
[1] == ':')) name
+= 2;
643 if ((p
= strrchr( name
, '/' ))) name
= p
+ 1;
644 if ((p
= strrchr( name
, '\\' ))) name
= p
+ 1;
645 for (i
= 0; i
< sizeof(DOSFS_Devices
)/sizeof(DOSFS_Devices
[0]); i
++)
647 const char *dev
= DOSFS_Devices
[i
].name
;
648 if (!lstrncmpiA( dev
, name
, strlen(dev
) ))
650 p
= name
+ strlen( dev
);
651 if (!*p
|| (*p
== '.')) {
653 if (!strcmp(DOSFS_Devices
[i
].name
,"NUL"))
654 return FILE_CreateFile( "/dev/null", access
,
655 FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
,
656 OPEN_EXISTING
, 0, -1 );
657 if (!strcmp(DOSFS_Devices
[i
].name
,"CON")) {
660 switch (access
& (GENERIC_READ
|GENERIC_WRITE
)) {
662 to_dup
= GetStdHandle( STD_INPUT_HANDLE
);
665 to_dup
= GetStdHandle( STD_OUTPUT_HANDLE
);
668 FIXME_(dosfs
)("can't open CON read/write\n");
672 if (!DuplicateHandle( GetCurrentProcess(), to_dup
, GetCurrentProcess(),
673 &handle
, 0, FALSE
, DUPLICATE_SAME_ACCESS
))
674 handle
= HFILE_ERROR
;
677 if (!strcmp(DOSFS_Devices
[i
].name
,"SCSIMGR$") ||
678 !strcmp(DOSFS_Devices
[i
].name
,"HPSCAN"))
680 return FILE_CreateDevice( i
, access
, NULL
);
685 PROFILE_GetWineIniString("serialports",name
,"",devname
,sizeof devname
);
689 TRACE_(file
)("DOSFS_OpenDevice %s is %s\n",
690 DOSFS_Devices
[i
].name
,devname
);
691 r
= FILE_CreateFile( devname
, access
,
692 FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
,
693 OPEN_EXISTING
, 0, -1 );
694 TRACE_(file
)("Create_File return %08X\n",r
);
699 FIXME_(dosfs
)("device open %s not supported (yet)\n",DOSFS_Devices
[i
].name
);
708 /***********************************************************************
711 * Get the drive specified by a given path name (DOS or Unix format).
713 static int DOSFS_GetPathDrive( const char **name
)
716 const char *p
= *name
;
718 if (*p
&& (p
[1] == ':'))
720 drive
= toupper(*p
) - 'A';
723 else if (*p
== '/') /* Absolute Unix path? */
725 if ((drive
= DRIVE_FindDriveRoot( name
)) == -1)
727 MESSAGE("Warning: %s not accessible from a DOS drive\n", *name
);
728 /* Assume it really was a DOS name */
729 drive
= DRIVE_GetCurrentDrive();
732 else drive
= DRIVE_GetCurrentDrive();
734 if (!DRIVE_IsValid(drive
))
736 SetLastError( ERROR_INVALID_DRIVE
);
743 /***********************************************************************
746 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
747 * Unix name / short DOS name pair.
748 * Return FALSE if one of the path components does not exist. The last path
749 * component is only checked if 'check_last' is non-zero.
750 * The buffers pointed to by 'long_buf' and 'short_buf' must be
751 * at least MAX_PATHNAME_LEN long.
753 BOOL
DOSFS_GetFullName( LPCSTR name
, BOOL check_last
, DOS_FULL_NAME
*full
)
757 char *p_l
, *p_s
, *root
;
759 TRACE_(dosfs
)("%s (last=%d)\n",
762 if ((full
->drive
= DOSFS_GetPathDrive( &name
)) == -1) return FALSE
;
763 flags
= DRIVE_GetFlags( full
->drive
);
765 lstrcpynA( full
->long_name
, DRIVE_GetRoot( full
->drive
),
766 sizeof(full
->long_name
) );
767 if (full
->long_name
[1]) root
= full
->long_name
+ strlen(full
->long_name
);
768 else root
= full
->long_name
; /* root directory */
770 strcpy( full
->short_name
, "A:\\" );
771 full
->short_name
[0] += full
->drive
;
773 if ((*name
== '\\') || (*name
== '/')) /* Absolute path */
775 while ((*name
== '\\') || (*name
== '/')) name
++;
777 else /* Relative path */
779 lstrcpynA( root
+ 1, DRIVE_GetUnixCwd( full
->drive
),
780 sizeof(full
->long_name
) - (root
- full
->long_name
) - 1 );
781 if (root
[1]) *root
= '/';
782 lstrcpynA( full
->short_name
+ 3, DRIVE_GetDosCwd( full
->drive
),
783 sizeof(full
->short_name
) - 3 );
786 p_l
= full
->long_name
[1] ? full
->long_name
+ strlen(full
->long_name
)
788 p_s
= full
->short_name
[3] ? full
->short_name
+ strlen(full
->short_name
)
789 : full
->short_name
+ 2;
792 while (*name
&& found
)
794 /* Check for '.' and '..' */
798 if (IS_END_OF_NAME(name
[1]))
801 while ((*name
== '\\') || (*name
== '/')) name
++;
804 else if ((name
[1] == '.') && IS_END_OF_NAME(name
[2]))
807 while ((*name
== '\\') || (*name
== '/')) name
++;
808 while ((p_l
> root
) && (*p_l
!= '/')) p_l
--;
809 while ((p_s
> full
->short_name
+ 2) && (*p_s
!= '\\')) p_s
--;
810 *p_l
= *p_s
= '\0'; /* Remove trailing separator */
815 /* Make sure buffers are large enough */
817 if ((p_s
>= full
->short_name
+ sizeof(full
->short_name
) - 14) ||
818 (p_l
>= full
->long_name
+ sizeof(full
->long_name
) - 1))
820 SetLastError( ERROR_PATH_NOT_FOUND
);
824 /* Get the long and short name matching the file name */
826 if ((found
= DOSFS_FindUnixName( full
->long_name
, name
, p_l
+ 1,
827 sizeof(full
->long_name
) - (p_l
- full
->long_name
) - 1,
828 p_s
+ 1, !(flags
& DRIVE_CASE_SENSITIVE
) )))
834 while (!IS_END_OF_NAME(*name
)) name
++;
836 else if (!check_last
)
840 while (!IS_END_OF_NAME(*name
) &&
841 (p_s
< full
->short_name
+ sizeof(full
->short_name
) - 1) &&
842 (p_l
< full
->long_name
+ sizeof(full
->long_name
) - 1))
844 *p_s
++ = tolower(*name
);
845 /* If the drive is case-sensitive we want to create new */
846 /* files in lower-case otherwise we can't reopen them */
847 /* under the same short name. */
848 if (flags
& DRIVE_CASE_SENSITIVE
) *p_l
++ = tolower(*name
);
854 while ((*name
== '\\') || (*name
== '/')) name
++;
861 SetLastError( ERROR_FILE_NOT_FOUND
);
864 if (*name
) /* Not last */
866 SetLastError( ERROR_PATH_NOT_FOUND
);
870 if (!full
->long_name
[0]) strcpy( full
->long_name
, "/" );
871 if (!full
->short_name
[2]) strcpy( full
->short_name
+ 2, "\\" );
872 TRACE_(dosfs
)("returning %s = %s\n",
873 full
->long_name
, full
->short_name
);
878 /***********************************************************************
879 * GetShortPathNameA (KERNEL32.271)
883 * longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
884 * *longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0
886 * more observations ( with NT 3.51 (WinDD) ):
887 * longpath <= 8.3 -> just copy longpath to shortpath
889 * a) file does not exist -> return 0, LastError = ERROR_FILE_NOT_FOUND
890 * b) file does exist -> set the short filename.
891 * - trailing slashes are reproduced in the short name, even if the
892 * file is not a directory
893 * - the absolute/relative path of the short name is reproduced in the
894 * same way, like the long name
895 * - longpath and shortpath may have the same adress
898 DWORD WINAPI
GetShortPathNameA( LPCSTR longpath
, LPSTR shortpath
,
901 DOS_FULL_NAME full_name
;
903 DWORD length
= 0, pos
= 0;
904 INT start
=-1, end
=-1, tmplen
;
907 SetLastError(ERROR_INVALID_PARAMETER
);
911 SetLastError(ERROR_BAD_PATHNAME
);
915 tmpshortpath
= HeapAlloc( GetProcessHeap(), 0, MAX_PATHNAME_LEN
);
916 if ( !tmpshortpath
) {
917 SetLastError ( ERROR_NOT_ENOUGH_MEMORY
);
921 /* Check for Drive-Letter */
922 if ( longpath
[1] == ':' ) {
923 lstrcpynA ( tmpshortpath
, longpath
, 3 );
928 /* loop over each part of the name */
929 while ( longpath
[pos
] ) {
931 if (( longpath
[pos
] == '\\' ) ||
932 ( longpath
[pos
+1] == '\0' ) ||
933 ( longpath
[pos
] == '/')) {
936 if ( DOSFS_ValidDOSName ( longpath
+ start
, TRUE
)) {
937 tmplen
= end
- start
+ ( (( longpath
[pos
] == '\\' ) || ( longpath
[pos
] == '/' )) ? 1 : 2 );
938 lstrcpynA ( tmpshortpath
+length
, longpath
+start
, tmplen
);
939 length
+= tmplen
- 1;
942 DOSFS_Hash ( longpath
+ start
, tmpshortpath
+length
, FALSE
, FALSE
);
943 length
= lstrlenA ( tmpshortpath
);
945 /* Check if the path, up to this point exists */
946 if ( !DOSFS_GetFullName ( tmpshortpath
, TRUE
, &full_name
) ) {
947 SetLastError ( ERROR_FILE_NOT_FOUND
);
954 if (( longpath
[pos
] == '\\' ) || ( longpath
[pos
] == '/' )) {
955 tmpshortpath
[length
] = '\\';
956 tmpshortpath
[length
+1]='\0';
973 lstrcpynA ( shortpath
, tmpshortpath
, shortlen
);
974 length
= lstrlenA ( tmpshortpath
);
975 HeapFree ( GetProcessHeap(), 0, tmpshortpath
);
981 /***********************************************************************
982 * GetShortPathName32W (KERNEL32.272)
984 DWORD WINAPI
GetShortPathNameW( LPCWSTR longpath
, LPWSTR shortpath
,
987 LPSTR longpathA
, shortpathA
;
990 longpathA
= HEAP_strdupWtoA( GetProcessHeap(), 0, longpath
);
991 shortpathA
= HEAP_xalloc ( GetProcessHeap(), 0, shortlen
);
993 ret
= GetShortPathNameA ( longpathA
, shortpathA
, shortlen
);
994 lstrcpynAtoW ( shortpath
, shortpathA
, shortlen
);
996 HeapFree( GetProcessHeap(), 0, longpathA
);
997 HeapFree( GetProcessHeap(), 0, shortpathA
);
1003 /***********************************************************************
1004 * GetLongPathName32A (KERNEL32.xxx)
1006 DWORD WINAPI
GetLongPathNameA( LPCSTR shortpath
, LPSTR longpath
,
1009 DOS_FULL_NAME full_name
;
1014 if (!DOSFS_GetFullName( shortpath
, TRUE
, &full_name
)) return 0;
1015 lstrcpynA( longpath
, full_name
.short_name
, longlen
);
1016 /* Do some hackery to get the long filename.
1017 * FIXME: Would be better if it returned the
1018 * long version of the directories too
1020 longfilename
= strrchr(full_name
.long_name
, '/')+1;
1021 if (longpath
!= NULL
) {
1022 if ((p
= strrchr( longpath
, '\\' )) != NULL
) {
1024 longlen
-= (p
-longpath
);
1025 lstrcpynA( p
, longfilename
, longlen
);
1029 ((strrchr( full_name
.short_name
, '\\' ) - full_name
.short_name
) + 1);
1030 return shortpathlen
+ strlen( longfilename
);
1034 /***********************************************************************
1035 * GetLongPathName32W (KERNEL32.269)
1037 DWORD WINAPI
GetLongPathNameW( LPCWSTR shortpath
, LPWSTR longpath
,
1040 DOS_FULL_NAME full_name
;
1042 LPSTR shortpathA
= HEAP_strdupWtoA( GetProcessHeap(), 0, shortpath
);
1044 /* FIXME: is it correct to always return a fully qualified short path? */
1045 if (DOSFS_GetFullName( shortpathA
, TRUE
, &full_name
))
1047 ret
= strlen( full_name
.short_name
);
1048 lstrcpynAtoW( longpath
, full_name
.long_name
, longlen
);
1050 HeapFree( GetProcessHeap(), 0, shortpathA
);
1055 /***********************************************************************
1056 * DOSFS_DoGetFullPathName
1058 * Implementation of GetFullPathName32A/W.
1060 static DWORD
DOSFS_DoGetFullPathName( LPCSTR name
, DWORD len
, LPSTR result
,
1063 char buffer
[MAX_PATHNAME_LEN
];
1068 /* last possible position for a char != 0 */
1069 char *endchar
= buffer
+ sizeof(buffer
) - 2;
1072 TRACE_(dosfs
)("converting '%s'\n", name
);
1074 if (!name
|| ((drive
= DOSFS_GetPathDrive( &name
)) == -1) )
1075 { SetLastError( ERROR_INVALID_PARAMETER
);
1082 if (IS_END_OF_NAME(*name
) && (*name
)) /* Absolute path */
1084 while (((*name
== '\\') || (*name
== '/')) && (!*endchar
) )
1087 else /* Relative path or empty path */
1090 lstrcpynA( p
, DRIVE_GetDosCwd(drive
), sizeof(buffer
) - 4 );
1103 if (IS_END_OF_NAME(name
[1]))
1106 while ((*name
== '\\') || (*name
== '/')) name
++;
1109 else if ((name
[1] == '.') && IS_END_OF_NAME(name
[2]))
1112 while ((*name
== '\\') || (*name
== '/')) name
++;
1114 if (p
< buffer
+ 3) /* no previous dir component */
1116 p
--; /* skip previously added '\\' */
1117 while ((*p
== '\\') || (*p
== '/')) p
--;
1118 /* skip previous dir component */
1119 while ((*p
!= '\\') && (*p
!= '/')) p
--;
1125 { SetLastError( ERROR_PATH_NOT_FOUND
);
1128 while (!IS_END_OF_NAME(*name
) && (!*endchar
) )
1130 while (((*name
== '\\') || (*name
== '/')) && (!*endchar
) )
1135 if (!(DRIVE_GetFlags(drive
) & DRIVE_CASE_PRESERVING
))
1136 CharUpperA( buffer
);
1141 lstrcpynAtoW( (LPWSTR
)result
, buffer
, len
);
1143 lstrcpynA( result
, buffer
, len
);
1146 TRACE_(dosfs
)("returning '%s'\n", buffer
);
1148 /* If the lpBuffer buffer is too small, the return value is the
1149 size of the buffer, in characters, required to hold the path. */
1151 ret
= strlen(buffer
);
1154 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
1160 /***********************************************************************
1161 * GetFullPathName32A (KERNEL32.272)
1163 * if the path closed with '\', *lastpart is 0
1165 DWORD WINAPI
GetFullPathNameA( LPCSTR name
, DWORD len
, LPSTR buffer
,
1168 DWORD ret
= DOSFS_DoGetFullPathName( name
, len
, buffer
, FALSE
);
1169 if (ret
&& buffer
&& lastpart
)
1171 LPSTR p
= buffer
+ strlen(buffer
);
1175 while ((p
> buffer
+ 2) && (*p
!= '\\')) p
--;
1178 else *lastpart
= NULL
;
1184 /***********************************************************************
1185 * GetFullPathName32W (KERNEL32.273)
1187 DWORD WINAPI
GetFullPathNameW( LPCWSTR name
, DWORD len
, LPWSTR buffer
,
1190 LPSTR nameA
= HEAP_strdupWtoA( GetProcessHeap(), 0, name
);
1191 DWORD ret
= DOSFS_DoGetFullPathName( nameA
, len
, (LPSTR
)buffer
, TRUE
);
1192 HeapFree( GetProcessHeap(), 0, nameA
);
1193 if (ret
&& buffer
&& lastpart
)
1195 LPWSTR p
= buffer
+ lstrlenW(buffer
);
1196 if (*p
!= (WCHAR
)'\\')
1198 while ((p
> buffer
+ 2) && (*p
!= (WCHAR
)'\\')) p
--;
1201 else *lastpart
= NULL
;
1206 /***********************************************************************
1209 static int DOSFS_FindNextEx( FIND_FIRST_INFO
*info
, WIN32_FIND_DATAA
*entry
)
1211 BYTE attr
= info
->attr
| FA_UNUSED
| FA_ARCHIVE
| FA_RDONLY
;
1212 UINT flags
= DRIVE_GetFlags( info
->drive
);
1213 char *p
, buffer
[MAX_PATHNAME_LEN
];
1214 const char *drive_path
;
1216 LPCSTR long_name
, short_name
;
1217 BY_HANDLE_FILE_INFORMATION fileinfo
;
1220 if ((info
->attr
& ~(FA_UNUSED
| FA_ARCHIVE
| FA_RDONLY
)) == FA_LABEL
)
1222 if (info
->cur_pos
) return 0;
1223 entry
->dwFileAttributes
= FILE_ATTRIBUTE_LABEL
;
1224 DOSFS_UnixTimeToFileTime( (time_t)0, &entry
->ftCreationTime
, 0 );
1225 DOSFS_UnixTimeToFileTime( (time_t)0, &entry
->ftLastAccessTime
, 0 );
1226 DOSFS_UnixTimeToFileTime( (time_t)0, &entry
->ftLastWriteTime
, 0 );
1227 entry
->nFileSizeHigh
= 0;
1228 entry
->nFileSizeLow
= 0;
1229 entry
->dwReserved0
= 0;
1230 entry
->dwReserved1
= 0;
1231 DOSFS_ToDosDTAFormat( DRIVE_GetLabel( info
->drive
), entry
->cFileName
);
1232 strcpy( entry
->cAlternateFileName
, entry
->cFileName
);
1237 drive_path
= info
->path
+ strlen(DRIVE_GetRoot( info
->drive
));
1238 while ((*drive_path
== '/') || (*drive_path
== '\\')) drive_path
++;
1239 drive_root
= !*drive_path
;
1241 lstrcpynA( buffer
, info
->path
, sizeof(buffer
) - 1 );
1242 strcat( buffer
, "/" );
1243 p
= buffer
+ strlen(buffer
);
1245 while (DOSFS_ReadDir( info
->dir
, &long_name
, &short_name
))
1249 /* Don't return '.' and '..' in the root of the drive */
1250 if (drive_root
&& (long_name
[0] == '.') &&
1251 (!long_name
[1] || ((long_name
[1] == '.') && !long_name
[2])))
1254 /* Check the long mask */
1256 if (info
->long_mask
)
1258 if (!DOSFS_MatchLong( info
->long_mask
, long_name
,
1259 flags
& DRIVE_CASE_SENSITIVE
)) continue;
1262 /* Check the short mask */
1264 if (info
->short_mask
)
1268 DOSFS_Hash( long_name
, dos_name
, TRUE
,
1269 !(flags
& DRIVE_CASE_SENSITIVE
) );
1270 short_name
= dos_name
;
1272 if (!DOSFS_MatchShort( info
->short_mask
, short_name
)) continue;
1275 /* Check the file attributes */
1277 lstrcpynA( p
, long_name
, sizeof(buffer
) - (int)(p
- buffer
) );
1278 if (!FILE_Stat( buffer
, &fileinfo
))
1280 WARN_(dosfs
)("can't stat %s\n", buffer
);
1283 if (fileinfo
.dwFileAttributes
& ~attr
) continue;
1285 /* We now have a matching entry; fill the result and return */
1287 entry
->dwFileAttributes
= fileinfo
.dwFileAttributes
;
1288 entry
->ftCreationTime
= fileinfo
.ftCreationTime
;
1289 entry
->ftLastAccessTime
= fileinfo
.ftLastAccessTime
;
1290 entry
->ftLastWriteTime
= fileinfo
.ftLastWriteTime
;
1291 entry
->nFileSizeHigh
= fileinfo
.nFileSizeHigh
;
1292 entry
->nFileSizeLow
= fileinfo
.nFileSizeLow
;
1295 DOSFS_ToDosDTAFormat( short_name
, entry
->cAlternateFileName
);
1297 DOSFS_Hash( long_name
, entry
->cAlternateFileName
, FALSE
,
1298 !(flags
& DRIVE_CASE_SENSITIVE
) );
1300 lstrcpynA( entry
->cFileName
, long_name
, sizeof(entry
->cFileName
) );
1301 if (!(flags
& DRIVE_CASE_PRESERVING
)) CharLowerA( entry
->cFileName
);
1302 TRACE_(dosfs
)("returning %s (%s) %02lx %ld\n",
1303 entry
->cFileName
, entry
->cAlternateFileName
,
1304 entry
->dwFileAttributes
, entry
->nFileSizeLow
);
1307 return 0; /* End of directory */
1310 /***********************************************************************
1313 * Find the next matching file. Return the number of entries read to find
1314 * the matching one, or 0 if no more entries.
1315 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
1316 * file name mask. Either or both can be NULL.
1318 * NOTE: This is supposed to be only called by the int21 emulation
1319 * routines. Thus, we should own the Win16Mutex anyway.
1320 * Nevertheless, we explicitly enter it to ensure the static
1321 * directory cache is protected.
1323 int DOSFS_FindNext( const char *path
, const char *short_mask
,
1324 const char *long_mask
, int drive
, BYTE attr
,
1325 int skip
, WIN32_FIND_DATAA
*entry
)
1327 static FIND_FIRST_INFO info
= { NULL
};
1328 LPCSTR short_name
, long_name
;
1331 SYSLEVEL_EnterWin16Lock();
1333 /* Check the cached directory */
1334 if (!(info
.dir
&& info
.path
== path
&& info
.short_mask
== short_mask
1335 && info
.long_mask
== long_mask
&& info
.drive
== drive
1336 && info
.attr
== attr
&& info
.cur_pos
<= skip
))
1338 /* Not in the cache, open it anew */
1339 if (info
.dir
) DOSFS_CloseDir( info
.dir
);
1341 info
.path
= (LPSTR
)path
;
1342 info
.long_mask
= (LPSTR
)long_mask
;
1343 info
.short_mask
= (LPSTR
)short_mask
;
1347 info
.dir
= DOSFS_OpenDir( info
.path
);
1350 /* Skip to desired position */
1351 while (info
.cur_pos
< skip
)
1352 if (info
.dir
&& DOSFS_ReadDir( info
.dir
, &long_name
, &short_name
))
1357 if (info
.dir
&& info
.cur_pos
== skip
&& DOSFS_FindNextEx( &info
, entry
))
1358 count
= info
.cur_pos
- skip
;
1364 if (info
.dir
) DOSFS_CloseDir( info
.dir
);
1365 memset( &info
, '\0', sizeof(info
) );
1368 SYSLEVEL_LeaveWin16Lock();
1375 /*************************************************************************
1376 * FindFirstFile16 (KERNEL.413)
1378 HANDLE16 WINAPI
FindFirstFile16( LPCSTR path
, WIN32_FIND_DATAA
*data
)
1380 DOS_FULL_NAME full_name
;
1382 FIND_FIRST_INFO
*info
;
1384 data
->dwReserved0
= data
->dwReserved1
= 0x0;
1385 if (!path
) return 0;
1386 if (!DOSFS_GetFullName( path
, FALSE
, &full_name
))
1387 return INVALID_HANDLE_VALUE16
;
1388 if (!(handle
= GlobalAlloc16( GMEM_MOVEABLE
, sizeof(FIND_FIRST_INFO
) )))
1389 return INVALID_HANDLE_VALUE16
;
1390 info
= (FIND_FIRST_INFO
*)GlobalLock16( handle
);
1391 info
->path
= HEAP_strdupA( SystemHeap
, 0, full_name
.long_name
);
1392 info
->long_mask
= strrchr( info
->path
, '/' );
1393 *(info
->long_mask
++) = '\0';
1394 info
->short_mask
= NULL
;
1396 if (path
[0] && (path
[1] == ':')) info
->drive
= toupper(*path
) - 'A';
1397 else info
->drive
= DRIVE_GetCurrentDrive();
1400 info
->dir
= DOSFS_OpenDir( info
->path
);
1402 GlobalUnlock16( handle
);
1403 if (!FindNextFile16( handle
, data
))
1405 FindClose16( handle
);
1406 SetLastError( ERROR_NO_MORE_FILES
);
1407 return INVALID_HANDLE_VALUE16
;
1413 /*************************************************************************
1414 * FindFirstFile32A (KERNEL32.123)
1416 HANDLE WINAPI
FindFirstFileA( LPCSTR path
, WIN32_FIND_DATAA
*data
)
1418 HANDLE handle
= FindFirstFile16( path
, data
);
1419 if (handle
== INVALID_HANDLE_VALUE16
) return INVALID_HANDLE_VALUE
;
1424 /*************************************************************************
1425 * FindFirstFile32W (KERNEL32.124)
1427 HANDLE WINAPI
FindFirstFileW( LPCWSTR path
, WIN32_FIND_DATAW
*data
)
1429 WIN32_FIND_DATAA dataA
;
1430 LPSTR pathA
= HEAP_strdupWtoA( GetProcessHeap(), 0, path
);
1431 HANDLE handle
= FindFirstFileA( pathA
, &dataA
);
1432 HeapFree( GetProcessHeap(), 0, pathA
);
1433 if (handle
!= INVALID_HANDLE_VALUE
)
1435 data
->dwFileAttributes
= dataA
.dwFileAttributes
;
1436 data
->ftCreationTime
= dataA
.ftCreationTime
;
1437 data
->ftLastAccessTime
= dataA
.ftLastAccessTime
;
1438 data
->ftLastWriteTime
= dataA
.ftLastWriteTime
;
1439 data
->nFileSizeHigh
= dataA
.nFileSizeHigh
;
1440 data
->nFileSizeLow
= dataA
.nFileSizeLow
;
1441 lstrcpyAtoW( data
->cFileName
, dataA
.cFileName
);
1442 lstrcpyAtoW( data
->cAlternateFileName
, dataA
.cAlternateFileName
);
1448 /*************************************************************************
1449 * FindNextFile16 (KERNEL.414)
1451 BOOL16 WINAPI
FindNextFile16( HANDLE16 handle
, WIN32_FIND_DATAA
*data
)
1453 FIND_FIRST_INFO
*info
;
1455 if (!(info
= (FIND_FIRST_INFO
*)GlobalLock16( handle
)))
1457 SetLastError( ERROR_INVALID_HANDLE
);
1460 GlobalUnlock16( handle
);
1461 if (!info
->path
|| !info
->dir
)
1463 SetLastError( ERROR_NO_MORE_FILES
);
1466 if (!DOSFS_FindNextEx( info
, data
))
1468 DOSFS_CloseDir( info
->dir
); info
->dir
= NULL
;
1469 HeapFree( SystemHeap
, 0, info
->path
);
1470 info
->path
= info
->long_mask
= NULL
;
1471 SetLastError( ERROR_NO_MORE_FILES
);
1478 /*************************************************************************
1479 * FindNextFile32A (KERNEL32.126)
1481 BOOL WINAPI
FindNextFileA( HANDLE handle
, WIN32_FIND_DATAA
*data
)
1483 return FindNextFile16( handle
, data
);
1487 /*************************************************************************
1488 * FindNextFile32W (KERNEL32.127)
1490 BOOL WINAPI
FindNextFileW( HANDLE handle
, WIN32_FIND_DATAW
*data
)
1492 WIN32_FIND_DATAA dataA
;
1493 if (!FindNextFileA( handle
, &dataA
)) return FALSE
;
1494 data
->dwFileAttributes
= dataA
.dwFileAttributes
;
1495 data
->ftCreationTime
= dataA
.ftCreationTime
;
1496 data
->ftLastAccessTime
= dataA
.ftLastAccessTime
;
1497 data
->ftLastWriteTime
= dataA
.ftLastWriteTime
;
1498 data
->nFileSizeHigh
= dataA
.nFileSizeHigh
;
1499 data
->nFileSizeLow
= dataA
.nFileSizeLow
;
1500 lstrcpyAtoW( data
->cFileName
, dataA
.cFileName
);
1501 lstrcpyAtoW( data
->cAlternateFileName
, dataA
.cAlternateFileName
);
1506 /*************************************************************************
1507 * FindClose16 (KERNEL.415)
1509 BOOL16 WINAPI
FindClose16( HANDLE16 handle
)
1511 FIND_FIRST_INFO
*info
;
1513 if ((handle
== INVALID_HANDLE_VALUE16
) ||
1514 !(info
= (FIND_FIRST_INFO
*)GlobalLock16( handle
)))
1516 SetLastError( ERROR_INVALID_HANDLE
);
1519 if (info
->dir
) DOSFS_CloseDir( info
->dir
);
1520 if (info
->path
) HeapFree( SystemHeap
, 0, info
->path
);
1521 GlobalUnlock16( handle
);
1522 GlobalFree16( handle
);
1527 /*************************************************************************
1528 * FindClose32 (KERNEL32.119)
1530 BOOL WINAPI
FindClose( HANDLE handle
)
1532 return FindClose16( (HANDLE16
)handle
);
1536 /***********************************************************************
1537 * DOSFS_UnixTimeToFileTime
1539 * Convert a Unix time to FILETIME format.
1540 * The FILETIME structure is a 64-bit value representing the number of
1541 * 100-nanosecond intervals since January 1, 1601, 0:00.
1542 * 'remainder' is the nonnegative number of 100-ns intervals
1543 * corresponding to the time fraction smaller than 1 second that
1544 * couldn't be stored in the time_t value.
1546 void DOSFS_UnixTimeToFileTime( time_t unix_time
, FILETIME
*filetime
,
1552 The time difference between 1 January 1601, 00:00:00 and
1553 1 January 1970, 00:00:00 is 369 years, plus the leap years
1554 from 1604 to 1968, excluding 1700, 1800, 1900.
1555 This makes (1968 - 1600) / 4 - 3 = 89 leap days, and a total
1558 Any day in that period had 24 * 60 * 60 = 86400 seconds.
1560 The time difference is 134774 * 86400 * 10000000, which can be written
1562 27111902 * 2^32 + 3577643008
1563 413 * 2^48 + 45534 * 2^32 + 54590 * 2^16 + 32768
1565 If you find that these constants are buggy, please change them in all
1566 instances in both conversion functions.
1569 There are two versions, one of them uses long long variables and
1570 is presumably faster but not ISO C. The other one uses standard C
1571 data types and operations but relies on the assumption that negative
1572 numbers are stored as 2's complement (-1 is 0xffff....). If this
1573 assumption is violated, dates before 1970 will not convert correctly.
1574 This should however work on any reasonable architecture where WINE
1579 Take care not to remove the casts. I have tested these functions
1580 (in both versions) for a lot of numbers. I would be interested in
1581 results on other compilers than GCC.
1583 The operations have been designed to account for the possibility
1584 of 64-bit time_t in future UNICES. Even the versions without
1585 internal long long numbers will work if time_t only is 64 bit.
1586 A 32-bit shift, which was necessary for that operation, turned out
1587 not to work correctly in GCC, besides giving the warning. So I
1588 used a double 16-bit shift instead. Numbers are in the ISO version
1589 represented by three limbs, the most significant with 32 bit, the
1590 other two with 16 bit each.
1592 As the modulo-operator % is not well-defined for negative numbers,
1593 negative divisors have been avoided in DOSFS_FileTimeToUnixTime.
1595 There might be quicker ways to do this in C. Certainly so in
1598 Claus Fischer, fischer@iue.tuwien.ac.at
1601 #if (SIZEOF_LONG_LONG >= 8)
1602 # define USE_LONG_LONG 1
1604 # define USE_LONG_LONG 0
1607 #if USE_LONG_LONG /* gcc supports long long type */
1609 long long int t
= unix_time
;
1611 t
+= 116444736000000000LL;
1613 filetime
->dwLowDateTime
= (UINT
)t
;
1614 filetime
->dwHighDateTime
= (UINT
)(t
>> 32);
1616 #else /* ISO version */
1618 UINT a0
; /* 16 bit, low bits */
1619 UINT a1
; /* 16 bit, medium bits */
1620 UINT a2
; /* 32 bit, high bits */
1622 /* Copy the unix time to a2/a1/a0 */
1623 a0
= unix_time
& 0xffff;
1624 a1
= (unix_time
>> 16) & 0xffff;
1625 /* This is obsolete if unix_time is only 32 bits, but it does not hurt.
1626 Do not replace this by >> 32, it gives a compiler warning and it does
1628 a2
= (unix_time
>= 0 ? (unix_time
>> 16) >> 16 :
1629 ~((~unix_time
>> 16) >> 16));
1631 /* Multiply a by 10000000 (a = a2/a1/a0)
1632 Split the factor into 10000 * 1000 which are both less than 0xffff. */
1634 a1
= a1
* 10000 + (a0
>> 16);
1635 a2
= a2
* 10000 + (a1
>> 16);
1640 a1
= a1
* 1000 + (a0
>> 16);
1641 a2
= a2
* 1000 + (a1
>> 16);
1645 /* Add the time difference and the remainder */
1646 a0
+= 32768 + (remainder
& 0xffff);
1647 a1
+= 54590 + (remainder
>> 16 ) + (a0
>> 16);
1648 a2
+= 27111902 + (a1
>> 16);
1653 filetime
->dwLowDateTime
= (a1
<< 16) + a0
;
1654 filetime
->dwHighDateTime
= a2
;
1659 /***********************************************************************
1660 * DOSFS_FileTimeToUnixTime
1662 * Convert a FILETIME format to Unix time.
1663 * If not NULL, 'remainder' contains the fractional part of the filetime,
1664 * in the range of [0..9999999] (even if time_t is negative).
1666 time_t DOSFS_FileTimeToUnixTime( const FILETIME
*filetime
, DWORD
*remainder
)
1668 /* Read the comment in the function DOSFS_UnixTimeToFileTime. */
1671 long long int t
= filetime
->dwHighDateTime
;
1673 t
+= (UINT
)filetime
->dwLowDateTime
;
1674 t
-= 116444736000000000LL;
1677 if (remainder
) *remainder
= 9999999 - (-t
- 1) % 10000000;
1678 return -1 - ((-t
- 1) / 10000000);
1682 if (remainder
) *remainder
= t
% 10000000;
1683 return t
/ 10000000;
1686 #else /* ISO version */
1688 UINT a0
; /* 16 bit, low bits */
1689 UINT a1
; /* 16 bit, medium bits */
1690 UINT a2
; /* 32 bit, high bits */
1691 UINT r
; /* remainder of division */
1692 unsigned int carry
; /* carry bit for subtraction */
1693 int negative
; /* whether a represents a negative value */
1695 /* Copy the time values to a2/a1/a0 */
1696 a2
= (UINT
)filetime
->dwHighDateTime
;
1697 a1
= ((UINT
)filetime
->dwLowDateTime
) >> 16;
1698 a0
= ((UINT
)filetime
->dwLowDateTime
) & 0xffff;
1700 /* Subtract the time difference */
1701 if (a0
>= 32768 ) a0
-= 32768 , carry
= 0;
1702 else a0
+= (1 << 16) - 32768 , carry
= 1;
1704 if (a1
>= 54590 + carry
) a1
-= 54590 + carry
, carry
= 0;
1705 else a1
+= (1 << 16) - 54590 - carry
, carry
= 1;
1707 a2
-= 27111902 + carry
;
1709 /* If a is negative, replace a by (-1-a) */
1710 negative
= (a2
>= ((UINT
)1) << 31);
1713 /* Set a to -a - 1 (a is a2/a1/a0) */
1719 /* Divide a by 10000000 (a = a2/a1/a0), put the rest into r.
1720 Split the divisor into 10000 * 1000 which are both less than 0xffff. */
1721 a1
+= (a2
% 10000) << 16;
1723 a0
+= (a1
% 10000) << 16;
1728 a1
+= (a2
% 1000) << 16;
1730 a0
+= (a1
% 1000) << 16;
1732 r
+= (a0
% 1000) * 10000;
1735 /* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
1738 /* Set a to -a - 1 (a is a2/a1/a0) */
1746 if (remainder
) *remainder
= r
;
1748 /* Do not replace this by << 32, it gives a compiler warning and it does
1750 return ((((time_t)a2
) << 16) << 16) + (a1
<< 16) + a0
;
1755 /***********************************************************************
1756 * DosDateTimeToFileTime (KERNEL32.76)
1758 BOOL WINAPI
DosDateTimeToFileTime( WORD fatdate
, WORD fattime
, LPFILETIME ft
)
1762 newtm
.tm_sec
= (fattime
& 0x1f) * 2;
1763 newtm
.tm_min
= (fattime
>> 5) & 0x3f;
1764 newtm
.tm_hour
= (fattime
>> 11);
1765 newtm
.tm_mday
= (fatdate
& 0x1f);
1766 newtm
.tm_mon
= ((fatdate
>> 5) & 0x0f) - 1;
1767 newtm
.tm_year
= (fatdate
>> 9) + 80;
1768 DOSFS_UnixTimeToFileTime( mktime( &newtm
), ft
, 0 );
1773 /***********************************************************************
1774 * FileTimeToDosDateTime (KERNEL32.111)
1776 BOOL WINAPI
FileTimeToDosDateTime( const FILETIME
*ft
, LPWORD fatdate
,
1779 time_t unixtime
= DOSFS_FileTimeToUnixTime( ft
, NULL
);
1780 struct tm
*tm
= localtime( &unixtime
);
1782 *fattime
= (tm
->tm_hour
<< 11) + (tm
->tm_min
<< 5) + (tm
->tm_sec
/ 2);
1784 *fatdate
= ((tm
->tm_year
- 80) << 9) + ((tm
->tm_mon
+ 1) << 5)
1790 /***********************************************************************
1791 * LocalFileTimeToFileTime (KERNEL32.373)
1793 BOOL WINAPI
LocalFileTimeToFileTime( const FILETIME
*localft
,
1799 /* convert from local to UTC. Perhaps not correct. FIXME */
1800 time_t unixtime
= DOSFS_FileTimeToUnixTime( localft
, &remainder
);
1801 xtm
= gmtime( &unixtime
);
1802 DOSFS_UnixTimeToFileTime( mktime(xtm
), utcft
, remainder
);
1807 /***********************************************************************
1808 * FileTimeToLocalFileTime (KERNEL32.112)
1810 BOOL WINAPI
FileTimeToLocalFileTime( const FILETIME
*utcft
,
1811 LPFILETIME localft
)
1814 /* convert from UTC to local. Perhaps not correct. FIXME */
1815 time_t unixtime
= DOSFS_FileTimeToUnixTime( utcft
, &remainder
);
1817 struct tm
*xtm
= localtime( &unixtime
);
1820 localtime
= timegm(xtm
);
1821 DOSFS_UnixTimeToFileTime( localtime
, localft
, remainder
);
1824 struct tm
*xtm
,*gtm
;
1827 xtm
= localtime( &unixtime
);
1828 gtm
= gmtime( &unixtime
);
1829 time1
= mktime(xtm
);
1830 time2
= mktime(gtm
);
1831 DOSFS_UnixTimeToFileTime( 2*time1
-time2
, localft
, remainder
);
1837 /***********************************************************************
1838 * FileTimeToSystemTime (KERNEL32.113)
1840 BOOL WINAPI
FileTimeToSystemTime( const FILETIME
*ft
, LPSYSTEMTIME syst
)
1844 time_t xtime
= DOSFS_FileTimeToUnixTime( ft
, &remainder
);
1845 xtm
= gmtime(&xtime
);
1846 syst
->wYear
= xtm
->tm_year
+1900;
1847 syst
->wMonth
= xtm
->tm_mon
+ 1;
1848 syst
->wDayOfWeek
= xtm
->tm_wday
;
1849 syst
->wDay
= xtm
->tm_mday
;
1850 syst
->wHour
= xtm
->tm_hour
;
1851 syst
->wMinute
= xtm
->tm_min
;
1852 syst
->wSecond
= xtm
->tm_sec
;
1853 syst
->wMilliseconds
= remainder
/ 10000;
1857 /***********************************************************************
1858 * QueryDosDeviceA (KERNEL32.413)
1860 * returns array of strings terminated by \0, terminated by \0
1862 DWORD WINAPI
QueryDosDeviceA(LPCSTR devname
,LPSTR target
,DWORD bufsize
)
1867 TRACE_(dosfs
)("(%s,...)\n",devname
?devname
:"<null>");
1869 /* return known MSDOS devices */
1870 strcpy(buffer
,"CON COM1 COM2 LPT1 NUL ");
1871 while ((s
=strchr(buffer
,' ')))
1874 lstrcpynA(target
,buffer
,bufsize
);
1875 return strlen(buffer
);
1877 strcpy(buffer
,"\\DEV\\");
1878 strcat(buffer
,devname
);
1879 if ((s
=strchr(buffer
,':'))) *s
='\0';
1880 lstrcpynA(target
,buffer
,bufsize
);
1881 return strlen(buffer
);
1885 /***********************************************************************
1886 * QueryDosDeviceW (KERNEL32.414)
1888 * returns array of strings terminated by \0, terminated by \0
1890 DWORD WINAPI
QueryDosDeviceW(LPCWSTR devname
,LPWSTR target
,DWORD bufsize
)
1892 LPSTR devnameA
= devname
?HEAP_strdupWtoA(GetProcessHeap(),0,devname
):NULL
;
1893 LPSTR targetA
= (LPSTR
)HEAP_xalloc(GetProcessHeap(),0,bufsize
);
1894 DWORD ret
= QueryDosDeviceA(devnameA
,targetA
,bufsize
);
1896 lstrcpynAtoW(target
,targetA
,bufsize
);
1897 if (devnameA
) HeapFree(GetProcessHeap(),0,devnameA
);
1898 if (targetA
) HeapFree(GetProcessHeap(),0,targetA
);
1903 /***********************************************************************
1904 * SystemTimeToFileTime (KERNEL32.526)
1906 BOOL WINAPI
SystemTimeToFileTime( const SYSTEMTIME
*syst
, LPFILETIME ft
)
1912 struct tm xtm
,*local_tm
,*utc_tm
;
1913 time_t localtim
,utctime
;
1916 xtm
.tm_year
= syst
->wYear
-1900;
1917 xtm
.tm_mon
= syst
->wMonth
- 1;
1918 xtm
.tm_wday
= syst
->wDayOfWeek
;
1919 xtm
.tm_mday
= syst
->wDay
;
1920 xtm
.tm_hour
= syst
->wHour
;
1921 xtm
.tm_min
= syst
->wMinute
;
1922 xtm
.tm_sec
= syst
->wSecond
; /* this is UTC */
1925 utctime
= timegm(&xtm
);
1926 DOSFS_UnixTimeToFileTime( utctime
, ft
,
1927 syst
->wMilliseconds
* 10000 );
1929 localtim
= mktime(&xtm
); /* now we've got local time */
1930 local_tm
= localtime(&localtim
);
1931 utc_tm
= gmtime(&localtim
);
1932 utctime
= mktime(utc_tm
);
1933 DOSFS_UnixTimeToFileTime( 2*localtim
-utctime
, ft
,
1934 syst
->wMilliseconds
* 10000 );
1939 BOOL WINAPI
DefineDosDeviceA(DWORD flags
,LPCSTR devname
,LPCSTR targetpath
) {
1940 FIXME_(dosfs
)("(0x%08lx,%s,%s),stub!\n",flags
,devname
,targetpath
);
1941 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);