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 DEFAULT_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 /* We want the real kernel dirent structure, not the libc one */
49 unsigned short d_reclen
;
53 #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, KERNEL_DIRENT [2] )
56 #undef VFAT_IOCTL_READDIR_BOTH /* just in case... */
59 /* Chars we don't want to see in DOS file names */
60 #define INVALID_DOS_CHARS "*?<>|\"+=,;[] \345"
62 static const DOS_DEVICE DOSFS_Devices
[] =
63 /* name, device flags (see Int 21/AX=0x4400) */
77 { "SCSIMGR$", 0xc0c0 },
81 #define GET_DRIVE(path) \
82 (((path)[1] == ':') ? toupper((path)[0]) - 'A' : DOSFS_CurDrive)
84 /* Directory info for DOSFS_ReadDir */
88 #ifdef VFAT_IOCTL_READDIR_BOTH
91 KERNEL_DIRENT dirent
[2];
95 /* Info structure for FindFirstFile handle */
109 /***********************************************************************
112 * Return 1 if Unix file 'name' is also a valid MS-DOS name
113 * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
114 * File name can be terminated by '\0', '\\' or '/'.
116 static int DOSFS_ValidDOSName( const char *name
, int ignore_case
)
118 static const char invalid_chars
[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS
;
119 const char *p
= name
;
120 const char *invalid
= ignore_case
? (invalid_chars
+ 26) : invalid_chars
;
125 /* Check for "." and ".." */
128 /* All other names beginning with '.' are invalid */
129 return (IS_END_OF_NAME(*p
));
131 while (!IS_END_OF_NAME(*p
))
133 if (strchr( invalid
, *p
)) return 0; /* Invalid char */
134 if (*p
== '.') break; /* Start of the extension */
135 if (++len
> 8) return 0; /* Name too long */
138 if (*p
!= '.') return 1; /* End of name */
140 if (IS_END_OF_NAME(*p
)) return 0; /* Empty extension not allowed */
142 while (!IS_END_OF_NAME(*p
))
144 if (strchr( invalid
, *p
)) return 0; /* Invalid char */
145 if (*p
== '.') return 0; /* Second extension not allowed */
146 if (++len
> 3) return 0; /* Extension too long */
153 /***********************************************************************
154 * DOSFS_ToDosFCBFormat
156 * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
157 * expanding wild cards and converting to upper-case in the process.
158 * File name can be terminated by '\0', '\\' or '/'.
159 * Return FALSE if the name is not a valid DOS name.
160 * 'buffer' must be at least 12 characters long.
162 BOOL
DOSFS_ToDosFCBFormat( LPCSTR name
, LPSTR buffer
)
164 static const char invalid_chars
[] = INVALID_DOS_CHARS
;
165 const char *p
= name
;
168 /* Check for "." and ".." */
172 strcpy( buffer
, ". " );
178 return (!*p
|| (*p
== '/') || (*p
== '\\'));
181 for (i
= 0; i
< 8; i
++)
198 if (strchr( invalid_chars
, *p
)) return FALSE
;
199 buffer
[i
] = toupper(*p
);
207 /* Skip all chars after wildcard up to first dot */
208 while (*p
&& (*p
!= '/') && (*p
!= '\\') && (*p
!= '.')) p
++;
212 /* Check if name too long */
213 if (*p
&& (*p
!= '/') && (*p
!= '\\') && (*p
!= '.')) return FALSE
;
215 if (*p
== '.') p
++; /* Skip dot */
217 for (i
= 8; i
< 11; i
++)
227 return FALSE
; /* Second extension not allowed */
235 if (strchr( invalid_chars
, *p
)) return FALSE
;
236 buffer
[i
] = toupper(*p
);
246 /***********************************************************************
247 * DOSFS_ToDosDTAFormat
249 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
250 * converting to upper-case in the process.
251 * File name can be terminated by '\0', '\\' or '/'.
252 * 'buffer' must be at least 13 characters long.
254 static void DOSFS_ToDosDTAFormat( LPCSTR name
, LPSTR buffer
)
258 memcpy( buffer
, name
, 8 );
259 for (p
= buffer
+ 8; (p
> buffer
) && (p
[-1] == ' '); p
--);
261 memcpy( p
, name
+ 8, 3 );
262 for (p
+= 3; p
[-1] == ' '; p
--);
263 if (p
[-1] == '.') p
--;
268 /***********************************************************************
271 * Check a DOS file name against a mask (both in FCB format).
273 static int DOSFS_MatchShort( const char *mask
, const char *name
)
276 for (i
= 11; i
> 0; i
--, mask
++, name
++)
277 if ((*mask
!= '?') && (*mask
!= *name
)) return 0;
282 /***********************************************************************
285 * Check a long file name against a mask.
287 static int DOSFS_MatchLong( const char *mask
, const char *name
,
290 if (!strcmp( mask
, "*.*" )) return 1;
291 while (*name
&& *mask
)
296 while (*mask
== '*') mask
++; /* Skip consecutive '*' */
297 if (!*mask
) return 1;
298 if (case_sensitive
) while (*name
&& (*name
!= *mask
)) name
++;
299 else while (*name
&& (toupper(*name
) != toupper(*mask
))) name
++;
302 else if (*mask
!= '?')
306 if (*mask
!= *name
) return 0;
308 else if (toupper(*mask
) != toupper(*name
)) return 0;
313 if (*mask
== '.') mask
++; /* Ignore trailing '.' in mask */
314 return (!*name
&& !*mask
);
318 /***********************************************************************
321 static DOS_DIR
*DOSFS_OpenDir( LPCSTR path
)
323 DOS_DIR
*dir
= HeapAlloc( SystemHeap
, 0, sizeof(*dir
) );
326 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
330 /* Treat empty path as root directory. This simplifies path split into
331 directory and mask in several other places */
332 if (!*path
) path
= "/";
334 #ifdef VFAT_IOCTL_READDIR_BOTH
336 /* Check if the VFAT ioctl is supported on this directory */
338 if ((dir
->fd
= open( path
, O_RDONLY
)) != -1)
340 if (ioctl( dir
->fd
, VFAT_IOCTL_READDIR_BOTH
, (long)dir
->dirent
) == -1)
347 /* Set the file pointer back at the start of the directory */
348 lseek( dir
->fd
, 0, SEEK_SET
);
353 #endif /* VFAT_IOCTL_READDIR_BOTH */
355 /* Now use the standard opendir/readdir interface */
357 if (!(dir
->dir
= opendir( path
)))
359 HeapFree( SystemHeap
, 0, dir
);
366 /***********************************************************************
369 static void DOSFS_CloseDir( DOS_DIR
*dir
)
371 #ifdef VFAT_IOCTL_READDIR_BOTH
372 if (dir
->fd
!= -1) close( dir
->fd
);
373 #endif /* VFAT_IOCTL_READDIR_BOTH */
374 if (dir
->dir
) closedir( dir
->dir
);
375 HeapFree( SystemHeap
, 0, dir
);
379 /***********************************************************************
382 static BOOL
DOSFS_ReadDir( DOS_DIR
*dir
, LPCSTR
*long_name
,
385 struct dirent
*dirent
;
387 #ifdef VFAT_IOCTL_READDIR_BOTH
390 if (ioctl( dir
->fd
, VFAT_IOCTL_READDIR_BOTH
, (long)dir
->dirent
) != -1) {
391 if (!dir
->dirent
[0].d_reclen
) return FALSE
;
392 if (!DOSFS_ToDosFCBFormat( dir
->dirent
[0].d_name
, dir
->short_name
))
393 dir
->short_name
[0] = '\0';
394 *short_name
= dir
->short_name
;
395 if (dir
->dirent
[1].d_name
[0]) *long_name
= dir
->dirent
[1].d_name
;
396 else *long_name
= dir
->dirent
[0].d_name
;
400 #endif /* VFAT_IOCTL_READDIR_BOTH */
402 if (!(dirent
= readdir( dir
->dir
))) return FALSE
;
403 *long_name
= dirent
->d_name
;
409 /***********************************************************************
412 * Transform a Unix file name into a hashed DOS name. If the name is a valid
413 * DOS name, it is converted to upper-case; otherwise it is replaced by a
414 * hashed version that fits in 8.3 format.
415 * File name can be terminated by '\0', '\\' or '/'.
416 * 'buffer' must be at least 13 characters long.
418 static void DOSFS_Hash( LPCSTR name
, LPSTR buffer
, BOOL dir_format
,
421 static const char invalid_chars
[] = INVALID_DOS_CHARS
"~.";
422 static const char hash_chars
[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
429 if (dir_format
) strcpy( buffer
, " " );
431 if (DOSFS_ValidDOSName( name
, ignore_case
))
433 /* Check for '.' and '..' */
437 if (!dir_format
) buffer
[1] = buffer
[2] = '\0';
438 if (name
[1] == '.') buffer
[1] = '.';
442 /* Simply copy the name, converting to uppercase */
444 for (dst
= buffer
; !IS_END_OF_NAME(*name
) && (*name
!= '.'); name
++)
445 *dst
++ = toupper(*name
);
448 if (dir_format
) dst
= buffer
+ 8;
450 for (name
++; !IS_END_OF_NAME(*name
); name
++)
451 *dst
++ = toupper(*name
);
453 if (!dir_format
) *dst
= '\0';
457 /* Compute the hash code of the file name */
458 /* If you know something about hash functions, feel free to */
459 /* insert a better algorithm here... */
462 for (p
= name
, hash
= 0xbeef; !IS_END_OF_NAME(p
[1]); p
++)
463 hash
= (hash
<<3) ^ (hash
>>5) ^ tolower(*p
) ^ (tolower(p
[1]) << 8);
464 hash
= (hash
<<3) ^ (hash
>>5) ^ tolower(*p
); /* Last character*/
468 for (p
= name
, hash
= 0xbeef; !IS_END_OF_NAME(p
[1]); p
++)
469 hash
= (hash
<< 3) ^ (hash
>> 5) ^ *p
^ (p
[1] << 8);
470 hash
= (hash
<< 3) ^ (hash
>> 5) ^ *p
; /* Last character */
473 /* Find last dot for start of the extension */
474 for (p
= name
+1, ext
= NULL
; !IS_END_OF_NAME(*p
); p
++)
475 if (*p
== '.') ext
= p
;
476 if (ext
&& IS_END_OF_NAME(ext
[1]))
477 ext
= NULL
; /* Empty extension ignored */
479 /* Copy first 4 chars, replacing invalid chars with '_' */
480 for (i
= 4, p
= name
, dst
= buffer
; i
> 0; i
--, p
++)
482 if (IS_END_OF_NAME(*p
) || (p
== ext
)) break;
483 *dst
++ = strchr( invalid_chars
, *p
) ? '_' : toupper(*p
);
485 /* Pad to 5 chars with '~' */
486 while (i
-- >= 0) *dst
++ = '~';
488 /* Insert hash code converted to 3 ASCII chars */
489 *dst
++ = hash_chars
[(hash
>> 10) & 0x1f];
490 *dst
++ = hash_chars
[(hash
>> 5) & 0x1f];
491 *dst
++ = hash_chars
[hash
& 0x1f];
493 /* Copy the first 3 chars of the extension (if any) */
496 if (!dir_format
) *dst
++ = '.';
497 for (i
= 3, ext
++; (i
> 0) && !IS_END_OF_NAME(*ext
); i
--, ext
++)
498 *dst
++ = strchr( invalid_chars
, *ext
) ? '_' : toupper(*ext
);
500 if (!dir_format
) *dst
= '\0';
504 /***********************************************************************
507 * Find the Unix file name in a given directory that corresponds to
508 * a file name (either in Unix or DOS format).
509 * File name can be terminated by '\0', '\\' or '/'.
510 * Return TRUE if OK, FALSE if no file name matches.
512 * 'long_buf' must be at least 'long_len' characters long. If the long name
513 * turns out to be larger than that, the function returns FALSE.
514 * 'short_buf' must be at least 13 characters long.
516 BOOL
DOSFS_FindUnixName( LPCSTR path
, LPCSTR name
, LPSTR long_buf
,
517 INT long_len
, LPSTR short_buf
, BOOL ignore_case
)
520 LPCSTR long_name
, short_name
;
521 char dos_name
[12], tmp_buf
[13];
524 const char *p
= strchr( name
, '/' );
525 int len
= p
? (int)(p
- name
) : strlen(name
);
526 if ((p
= strchr( name
, '\\' ))) len
= MIN( (int)(p
- name
), len
);
527 /* Ignore trailing dots */
528 while (len
> 1 && name
[len
-1] == '.') len
--;
529 if (long_len
< len
+ 1) return FALSE
;
531 TRACE("%s,%s\n", path
, name
);
533 if (!DOSFS_ToDosFCBFormat( name
, dos_name
)) dos_name
[0] = '\0';
535 if (!(dir
= DOSFS_OpenDir( path
)))
537 WARN("(%s,%s): can't open dir: %s\n",
538 path
, name
, strerror(errno
) );
542 while ((ret
= DOSFS_ReadDir( dir
, &long_name
, &short_name
)))
544 /* Check against Unix name */
545 if (len
== strlen(long_name
))
549 if (!strncmp( long_name
, name
, len
)) break;
553 if (!lstrncmpiA( long_name
, name
, len
)) break;
558 /* Check against hashed DOS name */
561 DOSFS_Hash( long_name
, tmp_buf
, TRUE
, ignore_case
);
562 short_name
= tmp_buf
;
564 if (!strcmp( dos_name
, short_name
)) break;
569 if (long_buf
) strcpy( long_buf
, long_name
);
573 DOSFS_ToDosDTAFormat( short_name
, short_buf
);
575 DOSFS_Hash( long_name
, short_buf
, FALSE
, ignore_case
);
577 TRACE("(%s,%s) -> %s (%s)\n",
578 path
, name
, long_name
, short_buf
? short_buf
: "***");
581 WARN("'%s' not found in '%s'\n", name
, path
);
582 DOSFS_CloseDir( dir
);
587 /***********************************************************************
590 * Check if a DOS file name represents a DOS device and return the device.
592 const DOS_DEVICE
*DOSFS_GetDevice( const char *name
)
597 if (!name
) return NULL
; /* if FILE_DupUnixHandle was used */
598 if (name
[0] && (name
[1] == ':')) name
+= 2;
599 if ((p
= strrchr( name
, '/' ))) name
= p
+ 1;
600 if ((p
= strrchr( name
, '\\' ))) name
= p
+ 1;
601 for (i
= 0; i
< sizeof(DOSFS_Devices
)/sizeof(DOSFS_Devices
[0]); i
++)
603 const char *dev
= DOSFS_Devices
[i
].name
;
604 if (!lstrncmpiA( dev
, name
, strlen(dev
) ))
606 p
= name
+ strlen( dev
);
607 if (!*p
|| (*p
== '.')) return &DOSFS_Devices
[i
];
614 /***********************************************************************
615 * DOSFS_GetDeviceByHandle
617 const DOS_DEVICE
*DOSFS_GetDeviceByHandle( HFILE hFile
)
619 struct get_file_info_request
*req
= get_req_buffer();
622 if (!server_call( REQ_GET_FILE_INFO
) && (req
->type
== FILE_TYPE_UNKNOWN
))
624 if ((req
->attr
>= 0) &&
625 (req
->attr
< sizeof(DOSFS_Devices
)/sizeof(DOSFS_Devices
[0])))
626 return &DOSFS_Devices
[req
->attr
];
632 /***********************************************************************
635 * Open a DOS device. This might not map 1:1 into the UNIX device concept.
637 HFILE
DOSFS_OpenDevice( const char *name
, DWORD access
)
642 if (!name
) return (HFILE
)NULL
; /* if FILE_DupUnixHandle was used */
643 if (name
[0] && (name
[1] == ':')) name
+= 2;
644 if ((p
= strrchr( name
, '/' ))) name
= p
+ 1;
645 if ((p
= strrchr( name
, '\\' ))) name
= p
+ 1;
646 for (i
= 0; i
< sizeof(DOSFS_Devices
)/sizeof(DOSFS_Devices
[0]); i
++)
648 const char *dev
= DOSFS_Devices
[i
].name
;
649 if (!lstrncmpiA( dev
, name
, strlen(dev
) ))
651 p
= name
+ strlen( dev
);
652 if (!*p
|| (*p
== '.')) {
654 if (!strcmp(DOSFS_Devices
[i
].name
,"NUL"))
655 return FILE_CreateFile( "/dev/null", access
,
656 FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
,
657 OPEN_EXISTING
, 0, -1 );
658 if (!strcmp(DOSFS_Devices
[i
].name
,"CON")) {
661 switch (access
& (GENERIC_READ
|GENERIC_WRITE
)) {
663 to_dup
= GetStdHandle( STD_INPUT_HANDLE
);
666 to_dup
= GetStdHandle( STD_OUTPUT_HANDLE
);
669 FIXME("can't open CON read/write\n");
673 if (!DuplicateHandle( GetCurrentProcess(), to_dup
, GetCurrentProcess(),
674 &handle
, 0, FALSE
, DUPLICATE_SAME_ACCESS
))
675 handle
= HFILE_ERROR
;
678 if (!strcmp(DOSFS_Devices
[i
].name
,"SCSIMGR$") ||
679 !strcmp(DOSFS_Devices
[i
].name
,"HPSCAN"))
681 return FILE_CreateDevice( i
, access
, NULL
);
686 PROFILE_GetWineIniString("serialports",name
,"",devname
,sizeof devname
);
690 TRACE_(file
)("DOSFS_OpenDevice %s is %s\n",
691 DOSFS_Devices
[i
].name
,devname
);
692 r
= FILE_CreateFile( devname
, access
,
693 FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
,
694 OPEN_EXISTING
, 0, -1 );
695 TRACE_(file
)("Create_File return %08X\n",r
);
700 FIXME("device open %s not supported (yet)\n",DOSFS_Devices
[i
].name
);
709 /***********************************************************************
712 * Get the drive specified by a given path name (DOS or Unix format).
714 static int DOSFS_GetPathDrive( const char **name
)
717 const char *p
= *name
;
719 if (*p
&& (p
[1] == ':'))
721 drive
= toupper(*p
) - 'A';
724 else if (*p
== '/') /* Absolute Unix path? */
726 if ((drive
= DRIVE_FindDriveRoot( name
)) == -1)
728 MESSAGE("Warning: %s not accessible from a DOS drive\n", *name
);
729 /* Assume it really was a DOS name */
730 drive
= DRIVE_GetCurrentDrive();
733 else drive
= DRIVE_GetCurrentDrive();
735 if (!DRIVE_IsValid(drive
))
737 SetLastError( ERROR_INVALID_DRIVE
);
744 /***********************************************************************
747 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
748 * Unix name / short DOS name pair.
749 * Return FALSE if one of the path components does not exist. The last path
750 * component is only checked if 'check_last' is non-zero.
751 * The buffers pointed to by 'long_buf' and 'short_buf' must be
752 * at least MAX_PATHNAME_LEN long.
754 BOOL
DOSFS_GetFullName( LPCSTR name
, BOOL check_last
, DOS_FULL_NAME
*full
)
758 char *p_l
, *p_s
, *root
;
760 TRACE("%s (last=%d)\n", name
, check_last
);
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("returning %s = %s\n", full
->long_name
, full
->short_name
);
877 /***********************************************************************
878 * GetShortPathNameA (KERNEL32.271)
882 * longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
883 * *longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0
885 * more observations ( with NT 3.51 (WinDD) ):
886 * longpath <= 8.3 -> just copy longpath to shortpath
888 * a) file does not exist -> return 0, LastError = ERROR_FILE_NOT_FOUND
889 * b) file does exist -> set the short filename.
890 * - trailing slashes are reproduced in the short name, even if the
891 * file is not a directory
892 * - the absolute/relative path of the short name is reproduced in the
893 * same way, like the long name
894 * - longpath and shortpath may have the same adress
897 DWORD WINAPI
GetShortPathNameA( LPCSTR longpath
, LPSTR shortpath
,
900 DOS_FULL_NAME full_name
;
902 DWORD length
= 0, pos
= 0;
903 INT start
=-1, end
=-1, tmplen
;
906 SetLastError(ERROR_INVALID_PARAMETER
);
910 SetLastError(ERROR_BAD_PATHNAME
);
914 tmpshortpath
= HeapAlloc( GetProcessHeap(), 0, MAX_PATHNAME_LEN
);
915 if ( !tmpshortpath
) {
916 SetLastError ( ERROR_NOT_ENOUGH_MEMORY
);
920 /* Check for Drive-Letter */
921 if ( longpath
[1] == ':' ) {
922 lstrcpynA ( tmpshortpath
, longpath
, 3 );
927 /* loop over each part of the name */
928 while ( longpath
[pos
] ) {
930 if (( longpath
[pos
] == '\\' ) ||
931 ( longpath
[pos
+1] == '\0' ) ||
932 ( longpath
[pos
] == '/')) {
935 if ( DOSFS_ValidDOSName ( longpath
+ start
, TRUE
)) {
936 tmplen
= end
- start
+ ( (( longpath
[pos
] == '\\' ) || ( longpath
[pos
] == '/' )) ? 1 : 2 );
937 lstrcpynA ( tmpshortpath
+length
, longpath
+start
, tmplen
);
938 length
+= tmplen
- 1;
941 DOSFS_Hash ( longpath
+ start
, tmpshortpath
+length
, FALSE
, FALSE
);
942 length
= lstrlenA ( tmpshortpath
);
944 /* Check if the path, up to this point exists */
945 if ( !DOSFS_GetFullName ( tmpshortpath
, TRUE
, &full_name
) ) {
946 SetLastError ( ERROR_FILE_NOT_FOUND
);
953 if (( longpath
[pos
] == '\\' ) || ( longpath
[pos
] == '/' )) {
954 tmpshortpath
[length
] = '\\';
955 tmpshortpath
[length
+1]='\0';
972 lstrcpynA ( shortpath
, tmpshortpath
, shortlen
);
973 length
= lstrlenA ( tmpshortpath
);
974 HeapFree ( GetProcessHeap(), 0, tmpshortpath
);
980 /***********************************************************************
981 * GetShortPathName32W (KERNEL32.272)
983 DWORD WINAPI
GetShortPathNameW( LPCWSTR longpath
, LPWSTR shortpath
,
986 LPSTR longpathA
, shortpathA
;
989 longpathA
= HEAP_strdupWtoA( GetProcessHeap(), 0, longpath
);
990 shortpathA
= HEAP_xalloc ( GetProcessHeap(), 0, shortlen
);
992 ret
= GetShortPathNameA ( longpathA
, shortpathA
, shortlen
);
993 lstrcpynAtoW ( shortpath
, shortpathA
, shortlen
);
995 HeapFree( GetProcessHeap(), 0, longpathA
);
996 HeapFree( GetProcessHeap(), 0, shortpathA
);
1002 /***********************************************************************
1003 * GetLongPathName32A (KERNEL32.xxx)
1005 DWORD WINAPI
GetLongPathNameA( LPCSTR shortpath
, LPSTR longpath
,
1008 DOS_FULL_NAME full_name
;
1013 if (!DOSFS_GetFullName( shortpath
, TRUE
, &full_name
)) return 0;
1014 lstrcpynA( longpath
, full_name
.short_name
, longlen
);
1015 /* Do some hackery to get the long filename.
1016 * FIXME: Would be better if it returned the
1017 * long version of the directories too
1019 longfilename
= strrchr(full_name
.long_name
, '/')+1;
1020 if (longpath
!= NULL
) {
1021 if ((p
= strrchr( longpath
, '\\' )) != NULL
) {
1023 longlen
-= (p
-longpath
);
1024 lstrcpynA( p
, longfilename
, longlen
);
1028 ((strrchr( full_name
.short_name
, '\\' ) - full_name
.short_name
) + 1);
1029 return shortpathlen
+ strlen( longfilename
);
1033 /***********************************************************************
1034 * GetLongPathName32W (KERNEL32.269)
1036 DWORD WINAPI
GetLongPathNameW( LPCWSTR shortpath
, LPWSTR longpath
,
1039 DOS_FULL_NAME full_name
;
1041 LPSTR shortpathA
= HEAP_strdupWtoA( GetProcessHeap(), 0, shortpath
);
1043 /* FIXME: is it correct to always return a fully qualified short path? */
1044 if (DOSFS_GetFullName( shortpathA
, TRUE
, &full_name
))
1046 ret
= strlen( full_name
.short_name
);
1047 lstrcpynAtoW( longpath
, full_name
.long_name
, longlen
);
1049 HeapFree( GetProcessHeap(), 0, shortpathA
);
1054 /***********************************************************************
1055 * DOSFS_DoGetFullPathName
1057 * Implementation of GetFullPathName32A/W.
1059 static DWORD
DOSFS_DoGetFullPathName( LPCSTR name
, DWORD len
, LPSTR result
,
1062 char buffer
[MAX_PATHNAME_LEN
];
1067 /* last possible position for a char != 0 */
1068 char *endchar
= buffer
+ sizeof(buffer
) - 2;
1071 TRACE("converting '%s'\n", name
);
1073 if (!name
|| ((drive
= DOSFS_GetPathDrive( &name
)) == -1) )
1074 { SetLastError( ERROR_INVALID_PARAMETER
);
1081 if (IS_END_OF_NAME(*name
) && (*name
)) /* Absolute path */
1083 while (((*name
== '\\') || (*name
== '/')) && (!*endchar
) )
1086 else /* Relative path or empty path */
1089 lstrcpynA( p
, DRIVE_GetDosCwd(drive
), sizeof(buffer
) - 4 );
1102 if (IS_END_OF_NAME(name
[1]))
1105 while ((*name
== '\\') || (*name
== '/')) name
++;
1108 else if ((name
[1] == '.') && IS_END_OF_NAME(name
[2]))
1111 while ((*name
== '\\') || (*name
== '/')) name
++;
1113 if (p
< buffer
+ 3) /* no previous dir component */
1115 p
--; /* skip previously added '\\' */
1116 while ((*p
== '\\') || (*p
== '/')) p
--;
1117 /* skip previous dir component */
1118 while ((*p
!= '\\') && (*p
!= '/')) p
--;
1124 { SetLastError( ERROR_PATH_NOT_FOUND
);
1127 while (!IS_END_OF_NAME(*name
) && (!*endchar
) )
1129 while (((*name
== '\\') || (*name
== '/')) && (!*endchar
) )
1134 if (!(DRIVE_GetFlags(drive
) & DRIVE_CASE_PRESERVING
))
1135 CharUpperA( buffer
);
1140 lstrcpynAtoW( (LPWSTR
)result
, buffer
, len
);
1142 lstrcpynA( result
, buffer
, len
);
1145 TRACE("returning '%s'\n", buffer
);
1147 /* If the lpBuffer buffer is too small, the return value is the
1148 size of the buffer, in characters, required to hold the path. */
1150 ret
= strlen(buffer
);
1153 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
1159 /***********************************************************************
1160 * GetFullPathName32A (KERNEL32.272)
1162 * if the path closed with '\', *lastpart is 0
1164 DWORD WINAPI
GetFullPathNameA( LPCSTR name
, DWORD len
, LPSTR buffer
,
1167 DWORD ret
= DOSFS_DoGetFullPathName( name
, len
, buffer
, FALSE
);
1168 if (ret
&& buffer
&& lastpart
)
1170 LPSTR p
= buffer
+ strlen(buffer
);
1174 while ((p
> buffer
+ 2) && (*p
!= '\\')) p
--;
1177 else *lastpart
= NULL
;
1183 /***********************************************************************
1184 * GetFullPathName32W (KERNEL32.273)
1186 DWORD WINAPI
GetFullPathNameW( LPCWSTR name
, DWORD len
, LPWSTR buffer
,
1189 LPSTR nameA
= HEAP_strdupWtoA( GetProcessHeap(), 0, name
);
1190 DWORD ret
= DOSFS_DoGetFullPathName( nameA
, len
, (LPSTR
)buffer
, TRUE
);
1191 HeapFree( GetProcessHeap(), 0, nameA
);
1192 if (ret
&& buffer
&& lastpart
)
1194 LPWSTR p
= buffer
+ lstrlenW(buffer
);
1195 if (*p
!= (WCHAR
)'\\')
1197 while ((p
> buffer
+ 2) && (*p
!= (WCHAR
)'\\')) p
--;
1200 else *lastpart
= NULL
;
1205 /***********************************************************************
1208 static int DOSFS_FindNextEx( FIND_FIRST_INFO
*info
, WIN32_FIND_DATAA
*entry
)
1210 BYTE attr
= info
->attr
| FA_UNUSED
| FA_ARCHIVE
| FA_RDONLY
;
1211 UINT flags
= DRIVE_GetFlags( info
->drive
);
1212 char *p
, buffer
[MAX_PATHNAME_LEN
];
1213 const char *drive_path
;
1215 LPCSTR long_name
, short_name
;
1216 BY_HANDLE_FILE_INFORMATION fileinfo
;
1219 if ((info
->attr
& ~(FA_UNUSED
| FA_ARCHIVE
| FA_RDONLY
)) == FA_LABEL
)
1221 if (info
->cur_pos
) return 0;
1222 entry
->dwFileAttributes
= FILE_ATTRIBUTE_LABEL
;
1223 DOSFS_UnixTimeToFileTime( (time_t)0, &entry
->ftCreationTime
, 0 );
1224 DOSFS_UnixTimeToFileTime( (time_t)0, &entry
->ftLastAccessTime
, 0 );
1225 DOSFS_UnixTimeToFileTime( (time_t)0, &entry
->ftLastWriteTime
, 0 );
1226 entry
->nFileSizeHigh
= 0;
1227 entry
->nFileSizeLow
= 0;
1228 entry
->dwReserved0
= 0;
1229 entry
->dwReserved1
= 0;
1230 DOSFS_ToDosDTAFormat( DRIVE_GetLabel( info
->drive
), entry
->cFileName
);
1231 strcpy( entry
->cAlternateFileName
, entry
->cFileName
);
1236 drive_path
= info
->path
+ strlen(DRIVE_GetRoot( info
->drive
));
1237 while ((*drive_path
== '/') || (*drive_path
== '\\')) drive_path
++;
1238 drive_root
= !*drive_path
;
1240 lstrcpynA( buffer
, info
->path
, sizeof(buffer
) - 1 );
1241 strcat( buffer
, "/" );
1242 p
= buffer
+ strlen(buffer
);
1244 while (DOSFS_ReadDir( info
->dir
, &long_name
, &short_name
))
1248 /* Don't return '.' and '..' in the root of the drive */
1249 if (drive_root
&& (long_name
[0] == '.') &&
1250 (!long_name
[1] || ((long_name
[1] == '.') && !long_name
[2])))
1253 /* Check the long mask */
1255 if (info
->long_mask
)
1257 if (!DOSFS_MatchLong( info
->long_mask
, long_name
,
1258 flags
& DRIVE_CASE_SENSITIVE
)) continue;
1261 /* Check the short mask */
1263 if (info
->short_mask
)
1267 DOSFS_Hash( long_name
, dos_name
, TRUE
,
1268 !(flags
& DRIVE_CASE_SENSITIVE
) );
1269 short_name
= dos_name
;
1271 if (!DOSFS_MatchShort( info
->short_mask
, short_name
)) continue;
1274 /* Check the file attributes */
1276 lstrcpynA( p
, long_name
, sizeof(buffer
) - (int)(p
- buffer
) );
1277 if (!FILE_Stat( buffer
, &fileinfo
))
1279 WARN("can't stat %s\n", buffer
);
1282 if (fileinfo
.dwFileAttributes
& ~attr
) continue;
1284 /* We now have a matching entry; fill the result and return */
1286 entry
->dwFileAttributes
= fileinfo
.dwFileAttributes
;
1287 entry
->ftCreationTime
= fileinfo
.ftCreationTime
;
1288 entry
->ftLastAccessTime
= fileinfo
.ftLastAccessTime
;
1289 entry
->ftLastWriteTime
= fileinfo
.ftLastWriteTime
;
1290 entry
->nFileSizeHigh
= fileinfo
.nFileSizeHigh
;
1291 entry
->nFileSizeLow
= fileinfo
.nFileSizeLow
;
1294 DOSFS_ToDosDTAFormat( short_name
, entry
->cAlternateFileName
);
1296 DOSFS_Hash( long_name
, entry
->cAlternateFileName
, FALSE
,
1297 !(flags
& DRIVE_CASE_SENSITIVE
) );
1299 lstrcpynA( entry
->cFileName
, long_name
, sizeof(entry
->cFileName
) );
1300 if (!(flags
& DRIVE_CASE_PRESERVING
)) CharLowerA( entry
->cFileName
);
1301 TRACE("returning %s (%s) %02lx %ld\n",
1302 entry
->cFileName
, entry
->cAlternateFileName
,
1303 entry
->dwFileAttributes
, entry
->nFileSizeLow
);
1306 return 0; /* End of directory */
1309 /***********************************************************************
1312 * Find the next matching file. Return the number of entries read to find
1313 * the matching one, or 0 if no more entries.
1314 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
1315 * file name mask. Either or both can be NULL.
1317 * NOTE: This is supposed to be only called by the int21 emulation
1318 * routines. Thus, we should own the Win16Mutex anyway.
1319 * Nevertheless, we explicitly enter it to ensure the static
1320 * directory cache is protected.
1322 int DOSFS_FindNext( const char *path
, const char *short_mask
,
1323 const char *long_mask
, int drive
, BYTE attr
,
1324 int skip
, WIN32_FIND_DATAA
*entry
)
1326 static FIND_FIRST_INFO info
= { NULL
};
1327 LPCSTR short_name
, long_name
;
1330 SYSLEVEL_EnterWin16Lock();
1332 /* Check the cached directory */
1333 if (!(info
.dir
&& info
.path
== path
&& info
.short_mask
== short_mask
1334 && info
.long_mask
== long_mask
&& info
.drive
== drive
1335 && info
.attr
== attr
&& info
.cur_pos
<= skip
))
1337 /* Not in the cache, open it anew */
1338 if (info
.dir
) DOSFS_CloseDir( info
.dir
);
1340 info
.path
= (LPSTR
)path
;
1341 info
.long_mask
= (LPSTR
)long_mask
;
1342 info
.short_mask
= (LPSTR
)short_mask
;
1346 info
.dir
= DOSFS_OpenDir( info
.path
);
1349 /* Skip to desired position */
1350 while (info
.cur_pos
< skip
)
1351 if (info
.dir
&& DOSFS_ReadDir( info
.dir
, &long_name
, &short_name
))
1356 if (info
.dir
&& info
.cur_pos
== skip
&& DOSFS_FindNextEx( &info
, entry
))
1357 count
= info
.cur_pos
- skip
;
1363 if (info
.dir
) DOSFS_CloseDir( info
.dir
);
1364 memset( &info
, '\0', sizeof(info
) );
1367 SYSLEVEL_LeaveWin16Lock();
1374 /*************************************************************************
1375 * FindFirstFile16 (KERNEL.413)
1377 HANDLE16 WINAPI
FindFirstFile16( LPCSTR path
, WIN32_FIND_DATAA
*data
)
1379 DOS_FULL_NAME full_name
;
1381 FIND_FIRST_INFO
*info
;
1383 data
->dwReserved0
= data
->dwReserved1
= 0x0;
1384 if (!path
) return 0;
1385 if (!DOSFS_GetFullName( path
, FALSE
, &full_name
))
1386 return INVALID_HANDLE_VALUE16
;
1387 if (!(handle
= GlobalAlloc16( GMEM_MOVEABLE
, sizeof(FIND_FIRST_INFO
) )))
1388 return INVALID_HANDLE_VALUE16
;
1389 info
= (FIND_FIRST_INFO
*)GlobalLock16( handle
);
1390 info
->path
= HEAP_strdupA( SystemHeap
, 0, full_name
.long_name
);
1391 info
->long_mask
= strrchr( info
->path
, '/' );
1392 *(info
->long_mask
++) = '\0';
1393 info
->short_mask
= NULL
;
1395 if (path
[0] && (path
[1] == ':')) info
->drive
= toupper(*path
) - 'A';
1396 else info
->drive
= DRIVE_GetCurrentDrive();
1399 info
->dir
= DOSFS_OpenDir( info
->path
);
1401 GlobalUnlock16( handle
);
1402 if (!FindNextFile16( handle
, data
))
1404 FindClose16( handle
);
1405 SetLastError( ERROR_NO_MORE_FILES
);
1406 return INVALID_HANDLE_VALUE16
;
1412 /*************************************************************************
1413 * FindFirstFile32A (KERNEL32.123)
1415 HANDLE WINAPI
FindFirstFileA( LPCSTR path
, WIN32_FIND_DATAA
*data
)
1417 HANDLE handle
= FindFirstFile16( path
, data
);
1418 if (handle
== INVALID_HANDLE_VALUE16
) return INVALID_HANDLE_VALUE
;
1423 /*************************************************************************
1424 * FindFirstFile32W (KERNEL32.124)
1426 HANDLE WINAPI
FindFirstFileW( LPCWSTR path
, WIN32_FIND_DATAW
*data
)
1428 WIN32_FIND_DATAA dataA
;
1429 LPSTR pathA
= HEAP_strdupWtoA( GetProcessHeap(), 0, path
);
1430 HANDLE handle
= FindFirstFileA( pathA
, &dataA
);
1431 HeapFree( GetProcessHeap(), 0, pathA
);
1432 if (handle
!= INVALID_HANDLE_VALUE
)
1434 data
->dwFileAttributes
= dataA
.dwFileAttributes
;
1435 data
->ftCreationTime
= dataA
.ftCreationTime
;
1436 data
->ftLastAccessTime
= dataA
.ftLastAccessTime
;
1437 data
->ftLastWriteTime
= dataA
.ftLastWriteTime
;
1438 data
->nFileSizeHigh
= dataA
.nFileSizeHigh
;
1439 data
->nFileSizeLow
= dataA
.nFileSizeLow
;
1440 lstrcpyAtoW( data
->cFileName
, dataA
.cFileName
);
1441 lstrcpyAtoW( data
->cAlternateFileName
, dataA
.cAlternateFileName
);
1447 /*************************************************************************
1448 * FindNextFile16 (KERNEL.414)
1450 BOOL16 WINAPI
FindNextFile16( HANDLE16 handle
, WIN32_FIND_DATAA
*data
)
1452 FIND_FIRST_INFO
*info
;
1454 if (!(info
= (FIND_FIRST_INFO
*)GlobalLock16( handle
)))
1456 SetLastError( ERROR_INVALID_HANDLE
);
1459 GlobalUnlock16( handle
);
1460 if (!info
->path
|| !info
->dir
)
1462 SetLastError( ERROR_NO_MORE_FILES
);
1465 if (!DOSFS_FindNextEx( info
, data
))
1467 DOSFS_CloseDir( info
->dir
); info
->dir
= NULL
;
1468 HeapFree( SystemHeap
, 0, info
->path
);
1469 info
->path
= info
->long_mask
= NULL
;
1470 SetLastError( ERROR_NO_MORE_FILES
);
1477 /*************************************************************************
1478 * FindNextFile32A (KERNEL32.126)
1480 BOOL WINAPI
FindNextFileA( HANDLE handle
, WIN32_FIND_DATAA
*data
)
1482 return FindNextFile16( handle
, data
);
1486 /*************************************************************************
1487 * FindNextFile32W (KERNEL32.127)
1489 BOOL WINAPI
FindNextFileW( HANDLE handle
, WIN32_FIND_DATAW
*data
)
1491 WIN32_FIND_DATAA dataA
;
1492 if (!FindNextFileA( handle
, &dataA
)) return FALSE
;
1493 data
->dwFileAttributes
= dataA
.dwFileAttributes
;
1494 data
->ftCreationTime
= dataA
.ftCreationTime
;
1495 data
->ftLastAccessTime
= dataA
.ftLastAccessTime
;
1496 data
->ftLastWriteTime
= dataA
.ftLastWriteTime
;
1497 data
->nFileSizeHigh
= dataA
.nFileSizeHigh
;
1498 data
->nFileSizeLow
= dataA
.nFileSizeLow
;
1499 lstrcpyAtoW( data
->cFileName
, dataA
.cFileName
);
1500 lstrcpyAtoW( data
->cAlternateFileName
, dataA
.cAlternateFileName
);
1505 /*************************************************************************
1506 * FindClose16 (KERNEL.415)
1508 BOOL16 WINAPI
FindClose16( HANDLE16 handle
)
1510 FIND_FIRST_INFO
*info
;
1512 if ((handle
== INVALID_HANDLE_VALUE16
) ||
1513 !(info
= (FIND_FIRST_INFO
*)GlobalLock16( handle
)))
1515 SetLastError( ERROR_INVALID_HANDLE
);
1518 if (info
->dir
) DOSFS_CloseDir( info
->dir
);
1519 if (info
->path
) HeapFree( SystemHeap
, 0, info
->path
);
1520 GlobalUnlock16( handle
);
1521 GlobalFree16( handle
);
1526 /*************************************************************************
1527 * FindClose32 (KERNEL32.119)
1529 BOOL WINAPI
FindClose( HANDLE handle
)
1531 return FindClose16( (HANDLE16
)handle
);
1535 /***********************************************************************
1536 * DOSFS_UnixTimeToFileTime
1538 * Convert a Unix time to FILETIME format.
1539 * The FILETIME structure is a 64-bit value representing the number of
1540 * 100-nanosecond intervals since January 1, 1601, 0:00.
1541 * 'remainder' is the nonnegative number of 100-ns intervals
1542 * corresponding to the time fraction smaller than 1 second that
1543 * couldn't be stored in the time_t value.
1545 void DOSFS_UnixTimeToFileTime( time_t unix_time
, FILETIME
*filetime
,
1551 The time difference between 1 January 1601, 00:00:00 and
1552 1 January 1970, 00:00:00 is 369 years, plus the leap years
1553 from 1604 to 1968, excluding 1700, 1800, 1900.
1554 This makes (1968 - 1600) / 4 - 3 = 89 leap days, and a total
1557 Any day in that period had 24 * 60 * 60 = 86400 seconds.
1559 The time difference is 134774 * 86400 * 10000000, which can be written
1561 27111902 * 2^32 + 3577643008
1562 413 * 2^48 + 45534 * 2^32 + 54590 * 2^16 + 32768
1564 If you find that these constants are buggy, please change them in all
1565 instances in both conversion functions.
1568 There are two versions, one of them uses long long variables and
1569 is presumably faster but not ISO C. The other one uses standard C
1570 data types and operations but relies on the assumption that negative
1571 numbers are stored as 2's complement (-1 is 0xffff....). If this
1572 assumption is violated, dates before 1970 will not convert correctly.
1573 This should however work on any reasonable architecture where WINE
1578 Take care not to remove the casts. I have tested these functions
1579 (in both versions) for a lot of numbers. I would be interested in
1580 results on other compilers than GCC.
1582 The operations have been designed to account for the possibility
1583 of 64-bit time_t in future UNICES. Even the versions without
1584 internal long long numbers will work if time_t only is 64 bit.
1585 A 32-bit shift, which was necessary for that operation, turned out
1586 not to work correctly in GCC, besides giving the warning. So I
1587 used a double 16-bit shift instead. Numbers are in the ISO version
1588 represented by three limbs, the most significant with 32 bit, the
1589 other two with 16 bit each.
1591 As the modulo-operator % is not well-defined for negative numbers,
1592 negative divisors have been avoided in DOSFS_FileTimeToUnixTime.
1594 There might be quicker ways to do this in C. Certainly so in
1597 Claus Fischer, fischer@iue.tuwien.ac.at
1600 #if SIZEOF_LONG_LONG >= 8
1601 # define USE_LONG_LONG 1
1603 # define USE_LONG_LONG 0
1606 #if USE_LONG_LONG /* gcc supports long long type */
1608 long long int t
= unix_time
;
1610 t
+= 116444736000000000LL;
1612 filetime
->dwLowDateTime
= (UINT
)t
;
1613 filetime
->dwHighDateTime
= (UINT
)(t
>> 32);
1615 #else /* ISO version */
1617 UINT a0
; /* 16 bit, low bits */
1618 UINT a1
; /* 16 bit, medium bits */
1619 UINT a2
; /* 32 bit, high bits */
1621 /* Copy the unix time to a2/a1/a0 */
1622 a0
= unix_time
& 0xffff;
1623 a1
= (unix_time
>> 16) & 0xffff;
1624 /* This is obsolete if unix_time is only 32 bits, but it does not hurt.
1625 Do not replace this by >> 32, it gives a compiler warning and it does
1627 a2
= (unix_time
>= 0 ? (unix_time
>> 16) >> 16 :
1628 ~((~unix_time
>> 16) >> 16));
1630 /* Multiply a by 10000000 (a = a2/a1/a0)
1631 Split the factor into 10000 * 1000 which are both less than 0xffff. */
1633 a1
= a1
* 10000 + (a0
>> 16);
1634 a2
= a2
* 10000 + (a1
>> 16);
1639 a1
= a1
* 1000 + (a0
>> 16);
1640 a2
= a2
* 1000 + (a1
>> 16);
1644 /* Add the time difference and the remainder */
1645 a0
+= 32768 + (remainder
& 0xffff);
1646 a1
+= 54590 + (remainder
>> 16 ) + (a0
>> 16);
1647 a2
+= 27111902 + (a1
>> 16);
1652 filetime
->dwLowDateTime
= (a1
<< 16) + a0
;
1653 filetime
->dwHighDateTime
= a2
;
1658 /***********************************************************************
1659 * DOSFS_FileTimeToUnixTime
1661 * Convert a FILETIME format to Unix time.
1662 * If not NULL, 'remainder' contains the fractional part of the filetime,
1663 * in the range of [0..9999999] (even if time_t is negative).
1665 time_t DOSFS_FileTimeToUnixTime( const FILETIME
*filetime
, DWORD
*remainder
)
1667 /* Read the comment in the function DOSFS_UnixTimeToFileTime. */
1670 long long int t
= filetime
->dwHighDateTime
;
1672 t
+= (UINT
)filetime
->dwLowDateTime
;
1673 t
-= 116444736000000000LL;
1676 if (remainder
) *remainder
= 9999999 - (-t
- 1) % 10000000;
1677 return -1 - ((-t
- 1) / 10000000);
1681 if (remainder
) *remainder
= t
% 10000000;
1682 return t
/ 10000000;
1685 #else /* ISO version */
1687 UINT a0
; /* 16 bit, low bits */
1688 UINT a1
; /* 16 bit, medium bits */
1689 UINT a2
; /* 32 bit, high bits */
1690 UINT r
; /* remainder of division */
1691 unsigned int carry
; /* carry bit for subtraction */
1692 int negative
; /* whether a represents a negative value */
1694 /* Copy the time values to a2/a1/a0 */
1695 a2
= (UINT
)filetime
->dwHighDateTime
;
1696 a1
= ((UINT
)filetime
->dwLowDateTime
) >> 16;
1697 a0
= ((UINT
)filetime
->dwLowDateTime
) & 0xffff;
1699 /* Subtract the time difference */
1700 if (a0
>= 32768 ) a0
-= 32768 , carry
= 0;
1701 else a0
+= (1 << 16) - 32768 , carry
= 1;
1703 if (a1
>= 54590 + carry
) a1
-= 54590 + carry
, carry
= 0;
1704 else a1
+= (1 << 16) - 54590 - carry
, carry
= 1;
1706 a2
-= 27111902 + carry
;
1708 /* If a is negative, replace a by (-1-a) */
1709 negative
= (a2
>= ((UINT
)1) << 31);
1712 /* Set a to -a - 1 (a is a2/a1/a0) */
1718 /* Divide a by 10000000 (a = a2/a1/a0), put the rest into r.
1719 Split the divisor into 10000 * 1000 which are both less than 0xffff. */
1720 a1
+= (a2
% 10000) << 16;
1722 a0
+= (a1
% 10000) << 16;
1727 a1
+= (a2
% 1000) << 16;
1729 a0
+= (a1
% 1000) << 16;
1731 r
+= (a0
% 1000) * 10000;
1734 /* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
1737 /* Set a to -a - 1 (a is a2/a1/a0) */
1745 if (remainder
) *remainder
= r
;
1747 /* Do not replace this by << 32, it gives a compiler warning and it does
1749 return ((((time_t)a2
) << 16) << 16) + (a1
<< 16) + a0
;
1754 /***********************************************************************
1755 * DosDateTimeToFileTime (KERNEL32.76)
1757 BOOL WINAPI
DosDateTimeToFileTime( WORD fatdate
, WORD fattime
, LPFILETIME ft
)
1761 newtm
.tm_sec
= (fattime
& 0x1f) * 2;
1762 newtm
.tm_min
= (fattime
>> 5) & 0x3f;
1763 newtm
.tm_hour
= (fattime
>> 11);
1764 newtm
.tm_mday
= (fatdate
& 0x1f);
1765 newtm
.tm_mon
= ((fatdate
>> 5) & 0x0f) - 1;
1766 newtm
.tm_year
= (fatdate
>> 9) + 80;
1767 DOSFS_UnixTimeToFileTime( mktime( &newtm
), ft
, 0 );
1772 /***********************************************************************
1773 * FileTimeToDosDateTime (KERNEL32.111)
1775 BOOL WINAPI
FileTimeToDosDateTime( const FILETIME
*ft
, LPWORD fatdate
,
1778 time_t unixtime
= DOSFS_FileTimeToUnixTime( ft
, NULL
);
1779 struct tm
*tm
= localtime( &unixtime
);
1781 *fattime
= (tm
->tm_hour
<< 11) + (tm
->tm_min
<< 5) + (tm
->tm_sec
/ 2);
1783 *fatdate
= ((tm
->tm_year
- 80) << 9) + ((tm
->tm_mon
+ 1) << 5)
1789 /***********************************************************************
1790 * LocalFileTimeToFileTime (KERNEL32.373)
1792 BOOL WINAPI
LocalFileTimeToFileTime( const FILETIME
*localft
,
1798 /* convert from local to UTC. Perhaps not correct. FIXME */
1799 time_t unixtime
= DOSFS_FileTimeToUnixTime( localft
, &remainder
);
1800 xtm
= gmtime( &unixtime
);
1801 DOSFS_UnixTimeToFileTime( mktime(xtm
), utcft
, remainder
);
1806 /***********************************************************************
1807 * FileTimeToLocalFileTime (KERNEL32.112)
1809 BOOL WINAPI
FileTimeToLocalFileTime( const FILETIME
*utcft
,
1810 LPFILETIME localft
)
1813 /* convert from UTC to local. Perhaps not correct. FIXME */
1814 time_t unixtime
= DOSFS_FileTimeToUnixTime( utcft
, &remainder
);
1816 struct tm
*xtm
= localtime( &unixtime
);
1819 localtime
= timegm(xtm
);
1820 DOSFS_UnixTimeToFileTime( localtime
, localft
, remainder
);
1823 struct tm
*xtm
,*gtm
;
1826 xtm
= localtime( &unixtime
);
1827 gtm
= gmtime( &unixtime
);
1828 time1
= mktime(xtm
);
1829 time2
= mktime(gtm
);
1830 DOSFS_UnixTimeToFileTime( 2*time1
-time2
, localft
, remainder
);
1836 /***********************************************************************
1837 * FileTimeToSystemTime (KERNEL32.113)
1839 BOOL WINAPI
FileTimeToSystemTime( const FILETIME
*ft
, LPSYSTEMTIME syst
)
1843 time_t xtime
= DOSFS_FileTimeToUnixTime( ft
, &remainder
);
1844 xtm
= gmtime(&xtime
);
1845 syst
->wYear
= xtm
->tm_year
+1900;
1846 syst
->wMonth
= xtm
->tm_mon
+ 1;
1847 syst
->wDayOfWeek
= xtm
->tm_wday
;
1848 syst
->wDay
= xtm
->tm_mday
;
1849 syst
->wHour
= xtm
->tm_hour
;
1850 syst
->wMinute
= xtm
->tm_min
;
1851 syst
->wSecond
= xtm
->tm_sec
;
1852 syst
->wMilliseconds
= remainder
/ 10000;
1856 /***********************************************************************
1857 * QueryDosDeviceA (KERNEL32.413)
1859 * returns array of strings terminated by \0, terminated by \0
1861 DWORD WINAPI
QueryDosDeviceA(LPCSTR devname
,LPSTR target
,DWORD bufsize
)
1866 TRACE("(%s,...)\n", devname
? devname
: "<null>");
1868 /* return known MSDOS devices */
1869 strcpy(buffer
,"CON COM1 COM2 LPT1 NUL ");
1870 while ((s
=strchr(buffer
,' ')))
1873 lstrcpynA(target
,buffer
,bufsize
);
1874 return strlen(buffer
);
1876 strcpy(buffer
,"\\DEV\\");
1877 strcat(buffer
,devname
);
1878 if ((s
=strchr(buffer
,':'))) *s
='\0';
1879 lstrcpynA(target
,buffer
,bufsize
);
1880 return strlen(buffer
);
1884 /***********************************************************************
1885 * QueryDosDeviceW (KERNEL32.414)
1887 * returns array of strings terminated by \0, terminated by \0
1889 DWORD WINAPI
QueryDosDeviceW(LPCWSTR devname
,LPWSTR target
,DWORD bufsize
)
1891 LPSTR devnameA
= devname
?HEAP_strdupWtoA(GetProcessHeap(),0,devname
):NULL
;
1892 LPSTR targetA
= (LPSTR
)HEAP_xalloc(GetProcessHeap(),0,bufsize
);
1893 DWORD ret
= QueryDosDeviceA(devnameA
,targetA
,bufsize
);
1895 lstrcpynAtoW(target
,targetA
,bufsize
);
1896 if (devnameA
) HeapFree(GetProcessHeap(),0,devnameA
);
1897 if (targetA
) HeapFree(GetProcessHeap(),0,targetA
);
1902 /***********************************************************************
1903 * SystemTimeToFileTime (KERNEL32.526)
1905 BOOL WINAPI
SystemTimeToFileTime( const SYSTEMTIME
*syst
, LPFILETIME ft
)
1911 struct tm xtm
,*local_tm
,*utc_tm
;
1912 time_t localtim
,utctime
;
1915 xtm
.tm_year
= syst
->wYear
-1900;
1916 xtm
.tm_mon
= syst
->wMonth
- 1;
1917 xtm
.tm_wday
= syst
->wDayOfWeek
;
1918 xtm
.tm_mday
= syst
->wDay
;
1919 xtm
.tm_hour
= syst
->wHour
;
1920 xtm
.tm_min
= syst
->wMinute
;
1921 xtm
.tm_sec
= syst
->wSecond
; /* this is UTC */
1924 utctime
= timegm(&xtm
);
1925 DOSFS_UnixTimeToFileTime( utctime
, ft
,
1926 syst
->wMilliseconds
* 10000 );
1928 localtim
= mktime(&xtm
); /* now we've got local time */
1929 local_tm
= localtime(&localtim
);
1930 utc_tm
= gmtime(&localtim
);
1931 utctime
= mktime(utc_tm
);
1932 DOSFS_UnixTimeToFileTime( 2*localtim
-utctime
, ft
,
1933 syst
->wMilliseconds
* 10000 );
1938 /***********************************************************************
1939 * DefineDosDeviceA (KERNEL32.182)
1941 BOOL WINAPI
DefineDosDeviceA(DWORD flags
,LPCSTR devname
,LPCSTR targetpath
) {
1942 FIXME("(0x%08lx,%s,%s),stub!\n",flags
,devname
,targetpath
);
1943 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);