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>
27 #include "wine/winbase16.h"
37 #include "debugtools.h"
39 DEFAULT_DEBUG_CHANNEL(dosfs
)
40 DECLARE_DEBUG_CHANNEL(file
)
42 /* Define the VFAT ioctl to get both short and long file names */
43 /* FIXME: is it possible to get this to work on other systems? */
45 /* We want the real kernel dirent structure, not the libc one */
50 unsigned short d_reclen
;
54 #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, KERNEL_DIRENT [2] )
57 #undef VFAT_IOCTL_READDIR_BOTH /* just in case... */
60 /* Chars we don't want to see in DOS file names */
61 #define INVALID_DOS_CHARS "*?<>|\"+=,;[] \345"
63 static const DOS_DEVICE DOSFS_Devices
[] =
64 /* name, device flags (see Int 21/AX=0x4400) */
78 { "SCSIMGR$", 0xc0c0 },
82 #define GET_DRIVE(path) \
83 (((path)[1] == ':') ? toupper((path)[0]) - 'A' : DOSFS_CurDrive)
85 /* Directory info for DOSFS_ReadDir */
89 #ifdef VFAT_IOCTL_READDIR_BOTH
92 KERNEL_DIRENT dirent
[2];
96 /* Info structure for FindFirstFile handle */
110 /***********************************************************************
113 * Return 1 if Unix file 'name' is also a valid MS-DOS name
114 * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
115 * File name can be terminated by '\0', '\\' or '/'.
117 static int DOSFS_ValidDOSName( const char *name
, int ignore_case
)
119 static const char invalid_chars
[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS
;
120 const char *p
= name
;
121 const char *invalid
= ignore_case
? (invalid_chars
+ 26) : invalid_chars
;
126 /* Check for "." and ".." */
129 /* All other names beginning with '.' are invalid */
130 return (IS_END_OF_NAME(*p
));
132 while (!IS_END_OF_NAME(*p
))
134 if (strchr( invalid
, *p
)) return 0; /* Invalid char */
135 if (*p
== '.') break; /* Start of the extension */
136 if (++len
> 8) return 0; /* Name too long */
139 if (*p
!= '.') return 1; /* End of name */
141 if (IS_END_OF_NAME(*p
)) return 0; /* Empty extension not allowed */
143 while (!IS_END_OF_NAME(*p
))
145 if (strchr( invalid
, *p
)) return 0; /* Invalid char */
146 if (*p
== '.') return 0; /* Second extension not allowed */
147 if (++len
> 3) return 0; /* Extension too long */
154 /***********************************************************************
155 * DOSFS_ToDosFCBFormat
157 * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
158 * expanding wild cards and converting to upper-case in the process.
159 * File name can be terminated by '\0', '\\' or '/'.
160 * Return FALSE if the name is not a valid DOS name.
161 * 'buffer' must be at least 12 characters long.
163 BOOL
DOSFS_ToDosFCBFormat( LPCSTR name
, LPSTR buffer
)
165 static const char invalid_chars
[] = INVALID_DOS_CHARS
;
166 const char *p
= name
;
169 /* Check for "." and ".." */
173 strcpy( buffer
, ". " );
179 return (!*p
|| (*p
== '/') || (*p
== '\\'));
182 for (i
= 0; i
< 8; i
++)
199 if (strchr( invalid_chars
, *p
)) return FALSE
;
200 buffer
[i
] = toupper(*p
);
208 /* Skip all chars after wildcard up to first dot */
209 while (*p
&& (*p
!= '/') && (*p
!= '\\') && (*p
!= '.')) p
++;
213 /* Check if name too long */
214 if (*p
&& (*p
!= '/') && (*p
!= '\\') && (*p
!= '.')) return FALSE
;
216 if (*p
== '.') p
++; /* Skip dot */
218 for (i
= 8; i
< 11; i
++)
228 return FALSE
; /* Second extension not allowed */
236 if (strchr( invalid_chars
, *p
)) return FALSE
;
237 buffer
[i
] = toupper(*p
);
247 /***********************************************************************
248 * DOSFS_ToDosDTAFormat
250 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
251 * converting to upper-case in the process.
252 * File name can be terminated by '\0', '\\' or '/'.
253 * 'buffer' must be at least 13 characters long.
255 static void DOSFS_ToDosDTAFormat( LPCSTR name
, LPSTR buffer
)
259 memcpy( buffer
, name
, 8 );
260 for (p
= buffer
+ 8; (p
> buffer
) && (p
[-1] == ' '); p
--);
262 memcpy( p
, name
+ 8, 3 );
263 for (p
+= 3; p
[-1] == ' '; p
--);
264 if (p
[-1] == '.') p
--;
269 /***********************************************************************
272 * Check a DOS file name against a mask (both in FCB format).
274 static int DOSFS_MatchShort( const char *mask
, const char *name
)
277 for (i
= 11; i
> 0; i
--, mask
++, name
++)
278 if ((*mask
!= '?') && (*mask
!= *name
)) return 0;
283 /***********************************************************************
286 * Check a long file name against a mask.
288 static int DOSFS_MatchLong( const char *mask
, const char *name
,
291 if (!strcmp( mask
, "*.*" )) return 1;
292 while (*name
&& *mask
)
297 while (*mask
== '*') mask
++; /* Skip consecutive '*' */
298 if (!*mask
) return 1;
299 if (case_sensitive
) while (*name
&& (*name
!= *mask
)) name
++;
300 else while (*name
&& (toupper(*name
) != toupper(*mask
))) name
++;
303 else if (*mask
!= '?')
307 if (*mask
!= *name
) return 0;
309 else if (toupper(*mask
) != toupper(*name
)) return 0;
314 if (*mask
== '.') mask
++; /* Ignore trailing '.' in mask */
315 return (!*name
&& !*mask
);
319 /***********************************************************************
322 static DOS_DIR
*DOSFS_OpenDir( LPCSTR path
)
324 DOS_DIR
*dir
= HeapAlloc( GetProcessHeap(), 0, sizeof(*dir
) );
327 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
331 /* Treat empty path as root directory. This simplifies path split into
332 directory and mask in several other places */
333 if (!*path
) path
= "/";
335 #ifdef VFAT_IOCTL_READDIR_BOTH
337 /* Check if the VFAT ioctl is supported on this directory */
339 if ((dir
->fd
= open( path
, O_RDONLY
)) != -1)
341 if (ioctl( dir
->fd
, VFAT_IOCTL_READDIR_BOTH
, (long)dir
->dirent
) == -1)
348 /* Set the file pointer back at the start of the directory */
349 lseek( dir
->fd
, 0, SEEK_SET
);
354 #endif /* VFAT_IOCTL_READDIR_BOTH */
356 /* Now use the standard opendir/readdir interface */
358 if (!(dir
->dir
= opendir( path
)))
360 HeapFree( GetProcessHeap(), 0, dir
);
367 /***********************************************************************
370 static void DOSFS_CloseDir( DOS_DIR
*dir
)
372 #ifdef VFAT_IOCTL_READDIR_BOTH
373 if (dir
->fd
!= -1) close( dir
->fd
);
374 #endif /* VFAT_IOCTL_READDIR_BOTH */
375 if (dir
->dir
) closedir( dir
->dir
);
376 HeapFree( GetProcessHeap(), 0, dir
);
380 /***********************************************************************
383 static BOOL
DOSFS_ReadDir( DOS_DIR
*dir
, LPCSTR
*long_name
,
386 struct dirent
*dirent
;
388 #ifdef VFAT_IOCTL_READDIR_BOTH
391 if (ioctl( dir
->fd
, VFAT_IOCTL_READDIR_BOTH
, (long)dir
->dirent
) != -1) {
392 if (!dir
->dirent
[0].d_reclen
) return FALSE
;
393 if (!DOSFS_ToDosFCBFormat( dir
->dirent
[0].d_name
, dir
->short_name
))
394 dir
->short_name
[0] = '\0';
395 *short_name
= dir
->short_name
;
396 if (dir
->dirent
[1].d_name
[0]) *long_name
= dir
->dirent
[1].d_name
;
397 else *long_name
= dir
->dirent
[0].d_name
;
401 #endif /* VFAT_IOCTL_READDIR_BOTH */
403 if (!(dirent
= readdir( dir
->dir
))) return FALSE
;
404 *long_name
= dirent
->d_name
;
410 /***********************************************************************
413 * Transform a Unix file name into a hashed DOS name. If the name is a valid
414 * DOS name, it is converted to upper-case; otherwise it is replaced by a
415 * hashed version that fits in 8.3 format.
416 * File name can be terminated by '\0', '\\' or '/'.
417 * 'buffer' must be at least 13 characters long.
419 static void DOSFS_Hash( LPCSTR name
, LPSTR buffer
, BOOL dir_format
,
422 static const char invalid_chars
[] = INVALID_DOS_CHARS
"~.";
423 static const char hash_chars
[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
430 if (dir_format
) strcpy( buffer
, " " );
432 if (DOSFS_ValidDOSName( name
, ignore_case
))
434 /* Check for '.' and '..' */
438 if (!dir_format
) buffer
[1] = buffer
[2] = '\0';
439 if (name
[1] == '.') buffer
[1] = '.';
443 /* Simply copy the name, converting to uppercase */
445 for (dst
= buffer
; !IS_END_OF_NAME(*name
) && (*name
!= '.'); name
++)
446 *dst
++ = toupper(*name
);
449 if (dir_format
) dst
= buffer
+ 8;
451 for (name
++; !IS_END_OF_NAME(*name
); name
++)
452 *dst
++ = toupper(*name
);
454 if (!dir_format
) *dst
= '\0';
458 /* Compute the hash code of the file name */
459 /* If you know something about hash functions, feel free to */
460 /* insert a better algorithm here... */
463 for (p
= name
, hash
= 0xbeef; !IS_END_OF_NAME(p
[1]); p
++)
464 hash
= (hash
<<3) ^ (hash
>>5) ^ tolower(*p
) ^ (tolower(p
[1]) << 8);
465 hash
= (hash
<<3) ^ (hash
>>5) ^ tolower(*p
); /* Last character*/
469 for (p
= name
, hash
= 0xbeef; !IS_END_OF_NAME(p
[1]); p
++)
470 hash
= (hash
<< 3) ^ (hash
>> 5) ^ *p
^ (p
[1] << 8);
471 hash
= (hash
<< 3) ^ (hash
>> 5) ^ *p
; /* Last character */
474 /* Find last dot for start of the extension */
475 for (p
= name
+1, ext
= NULL
; !IS_END_OF_NAME(*p
); p
++)
476 if (*p
== '.') ext
= p
;
477 if (ext
&& IS_END_OF_NAME(ext
[1]))
478 ext
= NULL
; /* Empty extension ignored */
480 /* Copy first 4 chars, replacing invalid chars with '_' */
481 for (i
= 4, p
= name
, dst
= buffer
; i
> 0; i
--, p
++)
483 if (IS_END_OF_NAME(*p
) || (p
== ext
)) break;
484 *dst
++ = strchr( invalid_chars
, *p
) ? '_' : toupper(*p
);
486 /* Pad to 5 chars with '~' */
487 while (i
-- >= 0) *dst
++ = '~';
489 /* Insert hash code converted to 3 ASCII chars */
490 *dst
++ = hash_chars
[(hash
>> 10) & 0x1f];
491 *dst
++ = hash_chars
[(hash
>> 5) & 0x1f];
492 *dst
++ = hash_chars
[hash
& 0x1f];
494 /* Copy the first 3 chars of the extension (if any) */
497 if (!dir_format
) *dst
++ = '.';
498 for (i
= 3, ext
++; (i
> 0) && !IS_END_OF_NAME(*ext
); i
--, ext
++)
499 *dst
++ = strchr( invalid_chars
, *ext
) ? '_' : toupper(*ext
);
501 if (!dir_format
) *dst
= '\0';
505 /***********************************************************************
508 * Find the Unix file name in a given directory that corresponds to
509 * a file name (either in Unix or DOS format).
510 * File name can be terminated by '\0', '\\' or '/'.
511 * Return TRUE if OK, FALSE if no file name matches.
513 * 'long_buf' must be at least 'long_len' characters long. If the long name
514 * turns out to be larger than that, the function returns FALSE.
515 * 'short_buf' must be at least 13 characters long.
517 BOOL
DOSFS_FindUnixName( LPCSTR path
, LPCSTR name
, LPSTR long_buf
,
518 INT long_len
, LPSTR short_buf
, BOOL ignore_case
)
521 LPCSTR long_name
, short_name
;
522 char dos_name
[12], tmp_buf
[13];
525 const char *p
= strchr( name
, '/' );
526 int len
= p
? (int)(p
- name
) : strlen(name
);
527 if ((p
= strchr( name
, '\\' ))) len
= MIN( (int)(p
- name
), len
);
528 /* Ignore trailing dots */
529 while (len
> 1 && name
[len
-1] == '.') len
--;
530 if (long_len
< len
+ 1) return FALSE
;
532 TRACE("%s,%s\n", path
, name
);
534 if (!DOSFS_ToDosFCBFormat( name
, dos_name
)) dos_name
[0] = '\0';
536 if (!(dir
= DOSFS_OpenDir( path
)))
538 WARN("(%s,%s): can't open dir: %s\n",
539 path
, name
, strerror(errno
) );
543 while ((ret
= DOSFS_ReadDir( dir
, &long_name
, &short_name
)))
545 /* Check against Unix name */
546 if (len
== strlen(long_name
))
550 if (!strncmp( long_name
, name
, len
)) break;
554 if (!lstrncmpiA( long_name
, name
, len
)) break;
559 /* Check against hashed DOS name */
562 DOSFS_Hash( long_name
, tmp_buf
, TRUE
, ignore_case
);
563 short_name
= tmp_buf
;
565 if (!strcmp( dos_name
, short_name
)) break;
570 if (long_buf
) strcpy( long_buf
, long_name
);
574 DOSFS_ToDosDTAFormat( short_name
, short_buf
);
576 DOSFS_Hash( long_name
, short_buf
, FALSE
, ignore_case
);
578 TRACE("(%s,%s) -> %s (%s)\n",
579 path
, name
, long_name
, short_buf
? short_buf
: "***");
582 WARN("'%s' not found in '%s'\n", name
, path
);
583 DOSFS_CloseDir( dir
);
588 /***********************************************************************
591 * Check if a DOS file name represents a DOS device and return the device.
593 const DOS_DEVICE
*DOSFS_GetDevice( const char *name
)
598 if (!name
) return NULL
; /* if FILE_DupUnixHandle was used */
599 if (name
[0] && (name
[1] == ':')) name
+= 2;
600 if ((p
= strrchr( name
, '/' ))) name
= p
+ 1;
601 if ((p
= strrchr( name
, '\\' ))) name
= p
+ 1;
602 for (i
= 0; i
< sizeof(DOSFS_Devices
)/sizeof(DOSFS_Devices
[0]); i
++)
604 const char *dev
= DOSFS_Devices
[i
].name
;
605 if (!lstrncmpiA( dev
, name
, strlen(dev
) ))
607 p
= name
+ strlen( dev
);
608 if (!*p
|| (*p
== '.')) return &DOSFS_Devices
[i
];
615 /***********************************************************************
616 * DOSFS_GetDeviceByHandle
618 const DOS_DEVICE
*DOSFS_GetDeviceByHandle( HFILE hFile
)
620 struct get_file_info_request
*req
= get_req_buffer();
623 if (!server_call( REQ_GET_FILE_INFO
) && (req
->type
== FILE_TYPE_UNKNOWN
))
625 if ((req
->attr
>= 0) &&
626 (req
->attr
< sizeof(DOSFS_Devices
)/sizeof(DOSFS_Devices
[0])))
627 return &DOSFS_Devices
[req
->attr
];
633 /***********************************************************************
636 * Open a DOS device. This might not map 1:1 into the UNIX device concept.
638 HFILE
DOSFS_OpenDevice( const char *name
, DWORD access
)
643 if (!name
) return (HFILE
)NULL
; /* if FILE_DupUnixHandle was used */
644 if (name
[0] && (name
[1] == ':')) name
+= 2;
645 if ((p
= strrchr( name
, '/' ))) name
= p
+ 1;
646 if ((p
= strrchr( name
, '\\' ))) name
= p
+ 1;
647 for (i
= 0; i
< sizeof(DOSFS_Devices
)/sizeof(DOSFS_Devices
[0]); i
++)
649 const char *dev
= DOSFS_Devices
[i
].name
;
650 if (!lstrncmpiA( dev
, name
, strlen(dev
) ))
652 p
= name
+ strlen( dev
);
653 if (!*p
|| (*p
== '.')) {
655 if (!strcmp(DOSFS_Devices
[i
].name
,"NUL"))
656 return FILE_CreateFile( "/dev/null", access
,
657 FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
,
658 OPEN_EXISTING
, 0, -1 );
659 if (!strcmp(DOSFS_Devices
[i
].name
,"CON")) {
662 switch (access
& (GENERIC_READ
|GENERIC_WRITE
)) {
664 to_dup
= GetStdHandle( STD_INPUT_HANDLE
);
667 to_dup
= GetStdHandle( STD_OUTPUT_HANDLE
);
670 FIXME("can't open CON read/write\n");
674 if (!DuplicateHandle( GetCurrentProcess(), to_dup
, GetCurrentProcess(),
675 &handle
, 0, FALSE
, DUPLICATE_SAME_ACCESS
))
676 handle
= HFILE_ERROR
;
679 if (!strcmp(DOSFS_Devices
[i
].name
,"SCSIMGR$") ||
680 !strcmp(DOSFS_Devices
[i
].name
,"HPSCAN"))
682 return FILE_CreateDevice( i
, access
, NULL
);
687 PROFILE_GetWineIniString("serialports",name
,"",devname
,sizeof devname
);
691 TRACE_(file
)("DOSFS_OpenDevice %s is %s\n",
692 DOSFS_Devices
[i
].name
,devname
);
693 r
= FILE_CreateFile( devname
, access
,
694 FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
,
695 OPEN_EXISTING
, 0, -1 );
696 TRACE_(file
)("Create_File return %08X\n",r
);
701 FIXME("device open %s not supported (yet)\n",DOSFS_Devices
[i
].name
);
710 /***********************************************************************
713 * Get the drive specified by a given path name (DOS or Unix format).
715 static int DOSFS_GetPathDrive( const char **name
)
718 const char *p
= *name
;
720 if (*p
&& (p
[1] == ':'))
722 drive
= toupper(*p
) - 'A';
725 else if (*p
== '/') /* Absolute Unix path? */
727 if ((drive
= DRIVE_FindDriveRoot( name
)) == -1)
729 MESSAGE("Warning: %s not accessible from a DOS drive\n", *name
);
730 /* Assume it really was a DOS name */
731 drive
= DRIVE_GetCurrentDrive();
734 else drive
= DRIVE_GetCurrentDrive();
736 if (!DRIVE_IsValid(drive
))
738 SetLastError( ERROR_INVALID_DRIVE
);
745 /***********************************************************************
748 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
749 * Unix name / short DOS name pair.
750 * Return FALSE if one of the path components does not exist. The last path
751 * component is only checked if 'check_last' is non-zero.
752 * The buffers pointed to by 'long_buf' and 'short_buf' must be
753 * at least MAX_PATHNAME_LEN long.
755 BOOL
DOSFS_GetFullName( LPCSTR name
, BOOL check_last
, DOS_FULL_NAME
*full
)
759 char *p_l
, *p_s
, *root
;
761 TRACE("%s (last=%d)\n", name
, check_last
);
763 if ((full
->drive
= DOSFS_GetPathDrive( &name
)) == -1) return FALSE
;
764 flags
= DRIVE_GetFlags( full
->drive
);
766 lstrcpynA( full
->long_name
, DRIVE_GetRoot( full
->drive
),
767 sizeof(full
->long_name
) );
768 if (full
->long_name
[1]) root
= full
->long_name
+ strlen(full
->long_name
);
769 else root
= full
->long_name
; /* root directory */
771 strcpy( full
->short_name
, "A:\\" );
772 full
->short_name
[0] += full
->drive
;
774 if ((*name
== '\\') || (*name
== '/')) /* Absolute path */
776 while ((*name
== '\\') || (*name
== '/')) name
++;
778 else /* Relative path */
780 lstrcpynA( root
+ 1, DRIVE_GetUnixCwd( full
->drive
),
781 sizeof(full
->long_name
) - (root
- full
->long_name
) - 1 );
782 if (root
[1]) *root
= '/';
783 lstrcpynA( full
->short_name
+ 3, DRIVE_GetDosCwd( full
->drive
),
784 sizeof(full
->short_name
) - 3 );
787 p_l
= full
->long_name
[1] ? full
->long_name
+ strlen(full
->long_name
)
789 p_s
= full
->short_name
[3] ? full
->short_name
+ strlen(full
->short_name
)
790 : full
->short_name
+ 2;
793 while (*name
&& found
)
795 /* Check for '.' and '..' */
799 if (IS_END_OF_NAME(name
[1]))
802 while ((*name
== '\\') || (*name
== '/')) name
++;
805 else if ((name
[1] == '.') && IS_END_OF_NAME(name
[2]))
808 while ((*name
== '\\') || (*name
== '/')) name
++;
809 while ((p_l
> root
) && (*p_l
!= '/')) p_l
--;
810 while ((p_s
> full
->short_name
+ 2) && (*p_s
!= '\\')) p_s
--;
811 *p_l
= *p_s
= '\0'; /* Remove trailing separator */
816 /* Make sure buffers are large enough */
818 if ((p_s
>= full
->short_name
+ sizeof(full
->short_name
) - 14) ||
819 (p_l
>= full
->long_name
+ sizeof(full
->long_name
) - 1))
821 SetLastError( ERROR_PATH_NOT_FOUND
);
825 /* Get the long and short name matching the file name */
827 if ((found
= DOSFS_FindUnixName( full
->long_name
, name
, p_l
+ 1,
828 sizeof(full
->long_name
) - (p_l
- full
->long_name
) - 1,
829 p_s
+ 1, !(flags
& DRIVE_CASE_SENSITIVE
) )))
835 while (!IS_END_OF_NAME(*name
)) name
++;
837 else if (!check_last
)
841 while (!IS_END_OF_NAME(*name
) &&
842 (p_s
< full
->short_name
+ sizeof(full
->short_name
) - 1) &&
843 (p_l
< full
->long_name
+ sizeof(full
->long_name
) - 1))
845 *p_s
++ = tolower(*name
);
846 /* If the drive is case-sensitive we want to create new */
847 /* files in lower-case otherwise we can't reopen them */
848 /* under the same short name. */
849 if (flags
& DRIVE_CASE_SENSITIVE
) *p_l
++ = tolower(*name
);
855 while ((*name
== '\\') || (*name
== '/')) name
++;
862 SetLastError( ERROR_FILE_NOT_FOUND
);
865 if (*name
) /* Not last */
867 SetLastError( ERROR_PATH_NOT_FOUND
);
871 if (!full
->long_name
[0]) strcpy( full
->long_name
, "/" );
872 if (!full
->short_name
[2]) strcpy( full
->short_name
+ 2, "\\" );
873 TRACE("returning %s = %s\n", 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 like found
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 sp
= 0, lp
= 0;
907 TRACE("%s\n", debugstr_a(longpath
));
910 SetLastError(ERROR_INVALID_PARAMETER
);
914 SetLastError(ERROR_BAD_PATHNAME
);
918 if ( ( tmpshortpath
= HeapAlloc ( GetProcessHeap(), 0, MAX_PATHNAME_LEN
) ) == NULL
) {
919 SetLastError ( ERROR_NOT_ENOUGH_MEMORY
);
923 /* check for drive letter */
924 if ( longpath
[1] == ':' ) {
925 tmpshortpath
[0] = longpath
[0];
926 tmpshortpath
[1] = ':';
930 if ( ( drive
= DOSFS_GetPathDrive ( &longpath
)) == -1 ) return 0;
931 flags
= DRIVE_GetFlags ( drive
);
933 while ( longpath
[lp
] ) {
935 /* check for path delimiters and reproduce them */
936 if ( longpath
[lp
] == '\\' || longpath
[lp
] == '/' ) {
937 tmpshortpath
[sp
] = longpath
[lp
];
943 tmplen
= strcspn ( longpath
+ lp
, "\\/" );
944 lstrcpynA ( tmpshortpath
+sp
, longpath
+ lp
, tmplen
+1 );
946 /* Check, if the current element is a valid dos name */
947 if ( DOSFS_ValidDOSName ( longpath
+ lp
, !(flags
& DRIVE_CASE_SENSITIVE
) ) ) {
953 /* Check if the file exists and use the existing file name */
954 if ( DOSFS_GetFullName ( tmpshortpath
, TRUE
, &full_name
) ) {
955 lstrcpyA ( tmpshortpath
+sp
, strrchr ( full_name
.short_name
, '\\' ) + 1 );
956 sp
+= lstrlenA ( tmpshortpath
+sp
);
961 TRACE("not found!\n" );
962 SetLastError ( ERROR_FILE_NOT_FOUND
);
966 lstrcpynA ( shortpath
, tmpshortpath
, shortlen
);
967 TRACE("returning %s\n", debugstr_a(shortpath
) );
968 tmplen
= lstrlenA ( tmpshortpath
);
969 HeapFree ( GetProcessHeap(), 0, tmpshortpath
);
975 /***********************************************************************
976 * GetShortPathName32W (KERNEL32.272)
978 DWORD WINAPI
GetShortPathNameW( LPCWSTR longpath
, LPWSTR shortpath
,
981 LPSTR longpathA
, shortpathA
;
984 longpathA
= HEAP_strdupWtoA( GetProcessHeap(), 0, longpath
);
985 shortpathA
= HEAP_xalloc ( GetProcessHeap(), 0, shortlen
);
987 ret
= GetShortPathNameA ( longpathA
, shortpathA
, shortlen
);
988 lstrcpynAtoW ( shortpath
, shortpathA
, shortlen
);
990 HeapFree( GetProcessHeap(), 0, longpathA
);
991 HeapFree( GetProcessHeap(), 0, shortpathA
);
997 /***********************************************************************
998 * GetLongPathName32A (KERNEL32.xxx)
1000 DWORD WINAPI
GetLongPathNameA( LPCSTR shortpath
, LPSTR longpath
,
1003 DOS_FULL_NAME full_name
;
1004 char *p
, *r
, *ll
, *ss
;
1006 if (!DOSFS_GetFullName( shortpath
, TRUE
, &full_name
)) return 0;
1007 lstrcpynA( longpath
, full_name
.short_name
, longlen
);
1009 /* Do some hackery to get the long filename. */
1012 ss
=longpath
+strlen(longpath
);
1013 ll
=full_name
.long_name
+strlen(full_name
.long_name
);
1015 while (ss
>=longpath
)
1017 /* FIXME: aren't we more paranoid, than needed? */
1018 while ((ss
[0]=='\\') && (ss
>=longpath
)) ss
--;
1020 while ((ss
[0]!='\\') && (ss
>=longpath
)) ss
--;
1023 /* FIXME: aren't we more paranoid, than needed? */
1024 while ((ll
[0]=='/') && (ll
>=full_name
.long_name
)) ll
--;
1025 while ((ll
[0]!='/') && (ll
>=full_name
.long_name
)) ll
--;
1026 if (ll
<full_name
.long_name
)
1028 ERR("Bad longname! (ss=%s ll=%s)\n This should never happen !\n"
1035 /* FIXME: fix for names like "C:\\" (ie. with more '\'s) */
1039 if ((p
-longpath
)>0) longlen
-= (p
-longpath
);
1040 lstrcpynA( p
, ll
, longlen
);
1042 /* Now, change all '/' to '\' */
1043 for (r
=p
; r
<(p
+longlen
); r
++ )
1044 if (r
[0]=='/') r
[0]='\\';
1045 return strlen(longpath
) - strlen(p
) + longlen
;
1049 return strlen(longpath
);
1053 /***********************************************************************
1054 * GetLongPathName32W (KERNEL32.269)
1056 DWORD WINAPI
GetLongPathNameW( LPCWSTR shortpath
, LPWSTR longpath
,
1059 DOS_FULL_NAME full_name
;
1061 LPSTR shortpathA
= HEAP_strdupWtoA( GetProcessHeap(), 0, shortpath
);
1063 /* FIXME: is it correct to always return a fully qualified short path? */
1064 if (DOSFS_GetFullName( shortpathA
, TRUE
, &full_name
))
1066 ret
= strlen( full_name
.short_name
);
1067 lstrcpynAtoW( longpath
, full_name
.long_name
, longlen
);
1069 HeapFree( GetProcessHeap(), 0, shortpathA
);
1074 /***********************************************************************
1075 * DOSFS_DoGetFullPathName
1077 * Implementation of GetFullPathName32A/W.
1079 static DWORD
DOSFS_DoGetFullPathName( LPCSTR name
, DWORD len
, LPSTR result
,
1082 char buffer
[MAX_PATHNAME_LEN
];
1087 /* Address of the last byte in the buffer */
1088 char *endbuf
= buffer
+ sizeof(buffer
) - 1;
1090 TRACE("converting '%s'\n", name
);
1092 if (!name
|| ((drive
= DOSFS_GetPathDrive( &name
)) == -1) )
1094 SetLastError( ERROR_INVALID_PARAMETER
);
1102 if ((*name
!='/') && (*name
!='\\'))
1104 /* Relative path or empty path */
1105 lstrcpynA( p
, DRIVE_GetDosCwd(drive
), sizeof(buffer
) - 4 );
1115 while ((*name
== '\\') || (*name
== '/'))
1119 if (IS_END_OF_NAME(name
[1]))
1124 else if ((name
[1] == '.') && IS_END_OF_NAME(name
[2]))
1126 if (p
== buffer
+ 3) {
1127 /* no previous dir component */
1128 SetLastError( ERROR_PATH_NOT_FOUND
);
1131 /* skip previously added '\\' */
1133 /* skip previous dir component */
1142 while (!IS_END_OF_NAME(*name
) && (p
<endbuf
) )
1144 if ((p
<endbuf
) && ((*name
== '\\') || (*name
== '/'))) {
1148 if ( p
==endbuf
&& *name
)
1150 SetLastError( ERROR_PATH_NOT_FOUND
);
1156 if (!(DRIVE_GetFlags(drive
) & DRIVE_CASE_PRESERVING
))
1157 CharUpperA( buffer
);
1162 lstrcpynAtoW( (LPWSTR
)result
, buffer
, len
);
1164 lstrcpynA( result
, buffer
, len
);
1167 TRACE("returning '%s'\n", buffer
);
1169 /* If the lpBuffer buffer is too small, the return value is the
1170 size of the buffer, in characters, required to hold the path. */
1172 ret
= strlen(buffer
);
1175 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
1181 /***********************************************************************
1182 * GetFullPathName32A (KERNEL32.272)
1184 * if the path closed with '\', *lastpart is 0
1186 DWORD WINAPI
GetFullPathNameA( LPCSTR name
, DWORD len
, LPSTR buffer
,
1189 DWORD ret
= DOSFS_DoGetFullPathName( name
, len
, buffer
, FALSE
);
1190 if (ret
&& buffer
&& lastpart
)
1192 LPSTR p
= buffer
+ strlen(buffer
);
1196 while ((p
> buffer
+ 2) && (*p
!= '\\')) p
--;
1199 else *lastpart
= NULL
;
1205 /***********************************************************************
1206 * GetFullPathName32W (KERNEL32.273)
1208 DWORD WINAPI
GetFullPathNameW( LPCWSTR name
, DWORD len
, LPWSTR buffer
,
1211 LPSTR nameA
= HEAP_strdupWtoA( GetProcessHeap(), 0, name
);
1212 DWORD ret
= DOSFS_DoGetFullPathName( nameA
, len
, (LPSTR
)buffer
, TRUE
);
1213 HeapFree( GetProcessHeap(), 0, nameA
);
1214 if (ret
&& buffer
&& lastpart
)
1216 LPWSTR p
= buffer
+ lstrlenW(buffer
);
1217 if (*p
!= (WCHAR
)'\\')
1219 while ((p
> buffer
+ 2) && (*p
!= (WCHAR
)'\\')) p
--;
1222 else *lastpart
= NULL
;
1227 /***********************************************************************
1230 static int DOSFS_FindNextEx( FIND_FIRST_INFO
*info
, WIN32_FIND_DATAA
*entry
)
1232 BYTE attr
= info
->attr
| FA_UNUSED
| FA_ARCHIVE
| FA_RDONLY
;
1233 UINT flags
= DRIVE_GetFlags( info
->drive
);
1234 char *p
, buffer
[MAX_PATHNAME_LEN
];
1235 const char *drive_path
;
1237 LPCSTR long_name
, short_name
;
1238 BY_HANDLE_FILE_INFORMATION fileinfo
;
1241 if ((info
->attr
& ~(FA_UNUSED
| FA_ARCHIVE
| FA_RDONLY
)) == FA_LABEL
)
1243 if (info
->cur_pos
) return 0;
1244 entry
->dwFileAttributes
= FILE_ATTRIBUTE_LABEL
;
1245 DOSFS_UnixTimeToFileTime( (time_t)0, &entry
->ftCreationTime
, 0 );
1246 DOSFS_UnixTimeToFileTime( (time_t)0, &entry
->ftLastAccessTime
, 0 );
1247 DOSFS_UnixTimeToFileTime( (time_t)0, &entry
->ftLastWriteTime
, 0 );
1248 entry
->nFileSizeHigh
= 0;
1249 entry
->nFileSizeLow
= 0;
1250 entry
->dwReserved0
= 0;
1251 entry
->dwReserved1
= 0;
1252 DOSFS_ToDosDTAFormat( DRIVE_GetLabel( info
->drive
), entry
->cFileName
);
1253 strcpy( entry
->cAlternateFileName
, entry
->cFileName
);
1258 drive_path
= info
->path
+ strlen(DRIVE_GetRoot( info
->drive
));
1259 while ((*drive_path
== '/') || (*drive_path
== '\\')) drive_path
++;
1260 drive_root
= !*drive_path
;
1262 lstrcpynA( buffer
, info
->path
, sizeof(buffer
) - 1 );
1263 strcat( buffer
, "/" );
1264 p
= buffer
+ strlen(buffer
);
1266 while (DOSFS_ReadDir( info
->dir
, &long_name
, &short_name
))
1270 /* Don't return '.' and '..' in the root of the drive */
1271 if (drive_root
&& (long_name
[0] == '.') &&
1272 (!long_name
[1] || ((long_name
[1] == '.') && !long_name
[2])))
1275 /* Check the long mask */
1277 if (info
->long_mask
)
1279 if (!DOSFS_MatchLong( info
->long_mask
, long_name
,
1280 flags
& DRIVE_CASE_SENSITIVE
)) continue;
1283 /* Check the short mask */
1285 if (info
->short_mask
)
1289 DOSFS_Hash( long_name
, dos_name
, TRUE
,
1290 !(flags
& DRIVE_CASE_SENSITIVE
) );
1291 short_name
= dos_name
;
1293 if (!DOSFS_MatchShort( info
->short_mask
, short_name
)) continue;
1296 /* Check the file attributes */
1298 lstrcpynA( p
, long_name
, sizeof(buffer
) - (int)(p
- buffer
) );
1299 if (!FILE_Stat( buffer
, &fileinfo
))
1301 WARN("can't stat %s\n", buffer
);
1304 if (fileinfo
.dwFileAttributes
& ~attr
) continue;
1306 /* We now have a matching entry; fill the result and return */
1308 entry
->dwFileAttributes
= fileinfo
.dwFileAttributes
;
1309 entry
->ftCreationTime
= fileinfo
.ftCreationTime
;
1310 entry
->ftLastAccessTime
= fileinfo
.ftLastAccessTime
;
1311 entry
->ftLastWriteTime
= fileinfo
.ftLastWriteTime
;
1312 entry
->nFileSizeHigh
= fileinfo
.nFileSizeHigh
;
1313 entry
->nFileSizeLow
= fileinfo
.nFileSizeLow
;
1316 DOSFS_ToDosDTAFormat( short_name
, entry
->cAlternateFileName
);
1318 DOSFS_Hash( long_name
, entry
->cAlternateFileName
, FALSE
,
1319 !(flags
& DRIVE_CASE_SENSITIVE
) );
1321 lstrcpynA( entry
->cFileName
, long_name
, sizeof(entry
->cFileName
) );
1322 if (!(flags
& DRIVE_CASE_PRESERVING
)) CharLowerA( entry
->cFileName
);
1323 TRACE("returning %s (%s) %02lx %ld\n",
1324 entry
->cFileName
, entry
->cAlternateFileName
,
1325 entry
->dwFileAttributes
, entry
->nFileSizeLow
);
1328 return 0; /* End of directory */
1331 /***********************************************************************
1334 * Find the next matching file. Return the number of entries read to find
1335 * the matching one, or 0 if no more entries.
1336 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
1337 * file name mask. Either or both can be NULL.
1339 * NOTE: This is supposed to be only called by the int21 emulation
1340 * routines. Thus, we should own the Win16Mutex anyway.
1341 * Nevertheless, we explicitly enter it to ensure the static
1342 * directory cache is protected.
1344 int DOSFS_FindNext( const char *path
, const char *short_mask
,
1345 const char *long_mask
, int drive
, BYTE attr
,
1346 int skip
, WIN32_FIND_DATAA
*entry
)
1348 static FIND_FIRST_INFO info
= { NULL
};
1349 LPCSTR short_name
, long_name
;
1352 SYSLEVEL_EnterWin16Lock();
1354 /* Check the cached directory */
1355 if (!(info
.dir
&& info
.path
== path
&& info
.short_mask
== short_mask
1356 && info
.long_mask
== long_mask
&& info
.drive
== drive
1357 && info
.attr
== attr
&& info
.cur_pos
<= skip
))
1359 /* Not in the cache, open it anew */
1360 if (info
.dir
) DOSFS_CloseDir( info
.dir
);
1362 info
.path
= (LPSTR
)path
;
1363 info
.long_mask
= (LPSTR
)long_mask
;
1364 info
.short_mask
= (LPSTR
)short_mask
;
1368 info
.dir
= DOSFS_OpenDir( info
.path
);
1371 /* Skip to desired position */
1372 while (info
.cur_pos
< skip
)
1373 if (info
.dir
&& DOSFS_ReadDir( info
.dir
, &long_name
, &short_name
))
1378 if (info
.dir
&& info
.cur_pos
== skip
&& DOSFS_FindNextEx( &info
, entry
))
1379 count
= info
.cur_pos
- skip
;
1385 if (info
.dir
) DOSFS_CloseDir( info
.dir
);
1386 memset( &info
, '\0', sizeof(info
) );
1389 SYSLEVEL_LeaveWin16Lock();
1396 /*************************************************************************
1397 * FindFirstFile16 (KERNEL.413)
1399 HANDLE16 WINAPI
FindFirstFile16( LPCSTR path
, WIN32_FIND_DATAA
*data
)
1401 DOS_FULL_NAME full_name
;
1403 FIND_FIRST_INFO
*info
;
1405 data
->dwReserved0
= data
->dwReserved1
= 0x0;
1406 if (!path
) return 0;
1407 if (!DOSFS_GetFullName( path
, FALSE
, &full_name
))
1408 return INVALID_HANDLE_VALUE16
;
1409 if (!(handle
= GlobalAlloc16( GMEM_MOVEABLE
, sizeof(FIND_FIRST_INFO
) )))
1410 return INVALID_HANDLE_VALUE16
;
1411 info
= (FIND_FIRST_INFO
*)GlobalLock16( handle
);
1412 info
->path
= HEAP_strdupA( GetProcessHeap(), 0, full_name
.long_name
);
1413 info
->long_mask
= strrchr( info
->path
, '/' );
1414 *(info
->long_mask
++) = '\0';
1415 info
->short_mask
= NULL
;
1417 if (path
[0] && (path
[1] == ':')) info
->drive
= toupper(*path
) - 'A';
1418 else info
->drive
= DRIVE_GetCurrentDrive();
1421 info
->dir
= DOSFS_OpenDir( info
->path
);
1423 GlobalUnlock16( handle
);
1424 if (!FindNextFile16( handle
, data
))
1426 FindClose16( handle
);
1427 SetLastError( ERROR_NO_MORE_FILES
);
1428 return INVALID_HANDLE_VALUE16
;
1434 /*************************************************************************
1435 * FindFirstFile32A (KERNEL32.123)
1437 HANDLE WINAPI
FindFirstFileA( LPCSTR path
, WIN32_FIND_DATAA
*data
)
1439 HANDLE handle
= FindFirstFile16( path
, data
);
1440 if (handle
== INVALID_HANDLE_VALUE16
) return INVALID_HANDLE_VALUE
;
1445 /*************************************************************************
1446 * FindFirstFile32W (KERNEL32.124)
1448 HANDLE WINAPI
FindFirstFileW( LPCWSTR path
, WIN32_FIND_DATAW
*data
)
1450 WIN32_FIND_DATAA dataA
;
1451 LPSTR pathA
= HEAP_strdupWtoA( GetProcessHeap(), 0, path
);
1452 HANDLE handle
= FindFirstFileA( pathA
, &dataA
);
1453 HeapFree( GetProcessHeap(), 0, pathA
);
1454 if (handle
!= INVALID_HANDLE_VALUE
)
1456 data
->dwFileAttributes
= dataA
.dwFileAttributes
;
1457 data
->ftCreationTime
= dataA
.ftCreationTime
;
1458 data
->ftLastAccessTime
= dataA
.ftLastAccessTime
;
1459 data
->ftLastWriteTime
= dataA
.ftLastWriteTime
;
1460 data
->nFileSizeHigh
= dataA
.nFileSizeHigh
;
1461 data
->nFileSizeLow
= dataA
.nFileSizeLow
;
1462 lstrcpyAtoW( data
->cFileName
, dataA
.cFileName
);
1463 lstrcpyAtoW( data
->cAlternateFileName
, dataA
.cAlternateFileName
);
1469 /*************************************************************************
1470 * FindNextFile16 (KERNEL.414)
1472 BOOL16 WINAPI
FindNextFile16( HANDLE16 handle
, WIN32_FIND_DATAA
*data
)
1474 FIND_FIRST_INFO
*info
;
1476 if (!(info
= (FIND_FIRST_INFO
*)GlobalLock16( handle
)))
1478 SetLastError( ERROR_INVALID_HANDLE
);
1481 GlobalUnlock16( handle
);
1482 if (!info
->path
|| !info
->dir
)
1484 SetLastError( ERROR_NO_MORE_FILES
);
1487 if (!DOSFS_FindNextEx( info
, data
))
1489 DOSFS_CloseDir( info
->dir
); info
->dir
= NULL
;
1490 HeapFree( GetProcessHeap(), 0, info
->path
);
1491 info
->path
= info
->long_mask
= NULL
;
1492 SetLastError( ERROR_NO_MORE_FILES
);
1499 /*************************************************************************
1500 * FindNextFile32A (KERNEL32.126)
1502 BOOL WINAPI
FindNextFileA( HANDLE handle
, WIN32_FIND_DATAA
*data
)
1504 return FindNextFile16( handle
, data
);
1508 /*************************************************************************
1509 * FindNextFile32W (KERNEL32.127)
1511 BOOL WINAPI
FindNextFileW( HANDLE handle
, WIN32_FIND_DATAW
*data
)
1513 WIN32_FIND_DATAA dataA
;
1514 if (!FindNextFileA( handle
, &dataA
)) return FALSE
;
1515 data
->dwFileAttributes
= dataA
.dwFileAttributes
;
1516 data
->ftCreationTime
= dataA
.ftCreationTime
;
1517 data
->ftLastAccessTime
= dataA
.ftLastAccessTime
;
1518 data
->ftLastWriteTime
= dataA
.ftLastWriteTime
;
1519 data
->nFileSizeHigh
= dataA
.nFileSizeHigh
;
1520 data
->nFileSizeLow
= dataA
.nFileSizeLow
;
1521 lstrcpyAtoW( data
->cFileName
, dataA
.cFileName
);
1522 lstrcpyAtoW( data
->cAlternateFileName
, dataA
.cAlternateFileName
);
1527 /*************************************************************************
1528 * FindClose16 (KERNEL.415)
1530 BOOL16 WINAPI
FindClose16( HANDLE16 handle
)
1532 FIND_FIRST_INFO
*info
;
1534 if ((handle
== INVALID_HANDLE_VALUE16
) ||
1535 !(info
= (FIND_FIRST_INFO
*)GlobalLock16( handle
)))
1537 SetLastError( ERROR_INVALID_HANDLE
);
1540 if (info
->dir
) DOSFS_CloseDir( info
->dir
);
1541 if (info
->path
) HeapFree( GetProcessHeap(), 0, info
->path
);
1542 GlobalUnlock16( handle
);
1543 GlobalFree16( handle
);
1548 /*************************************************************************
1549 * FindClose32 (KERNEL32.119)
1551 BOOL WINAPI
FindClose( HANDLE handle
)
1553 return FindClose16( (HANDLE16
)handle
);
1557 /***********************************************************************
1558 * DOSFS_UnixTimeToFileTime
1560 * Convert a Unix time to FILETIME format.
1561 * The FILETIME structure is a 64-bit value representing the number of
1562 * 100-nanosecond intervals since January 1, 1601, 0:00.
1563 * 'remainder' is the nonnegative number of 100-ns intervals
1564 * corresponding to the time fraction smaller than 1 second that
1565 * couldn't be stored in the time_t value.
1567 void DOSFS_UnixTimeToFileTime( time_t unix_time
, FILETIME
*filetime
,
1573 The time difference between 1 January 1601, 00:00:00 and
1574 1 January 1970, 00:00:00 is 369 years, plus the leap years
1575 from 1604 to 1968, excluding 1700, 1800, 1900.
1576 This makes (1968 - 1600) / 4 - 3 = 89 leap days, and a total
1579 Any day in that period had 24 * 60 * 60 = 86400 seconds.
1581 The time difference is 134774 * 86400 * 10000000, which can be written
1583 27111902 * 2^32 + 3577643008
1584 413 * 2^48 + 45534 * 2^32 + 54590 * 2^16 + 32768
1586 If you find that these constants are buggy, please change them in all
1587 instances in both conversion functions.
1590 There are two versions, one of them uses long long variables and
1591 is presumably faster but not ISO C. The other one uses standard C
1592 data types and operations but relies on the assumption that negative
1593 numbers are stored as 2's complement (-1 is 0xffff....). If this
1594 assumption is violated, dates before 1970 will not convert correctly.
1595 This should however work on any reasonable architecture where WINE
1600 Take care not to remove the casts. I have tested these functions
1601 (in both versions) for a lot of numbers. I would be interested in
1602 results on other compilers than GCC.
1604 The operations have been designed to account for the possibility
1605 of 64-bit time_t in future UNICES. Even the versions without
1606 internal long long numbers will work if time_t only is 64 bit.
1607 A 32-bit shift, which was necessary for that operation, turned out
1608 not to work correctly in GCC, besides giving the warning. So I
1609 used a double 16-bit shift instead. Numbers are in the ISO version
1610 represented by three limbs, the most significant with 32 bit, the
1611 other two with 16 bit each.
1613 As the modulo-operator % is not well-defined for negative numbers,
1614 negative divisors have been avoided in DOSFS_FileTimeToUnixTime.
1616 There might be quicker ways to do this in C. Certainly so in
1619 Claus Fischer, fischer@iue.tuwien.ac.at
1622 #if SIZEOF_LONG_LONG >= 8
1623 # define USE_LONG_LONG 1
1625 # define USE_LONG_LONG 0
1628 #if USE_LONG_LONG /* gcc supports long long type */
1630 long long int t
= unix_time
;
1632 t
+= 116444736000000000LL;
1634 filetime
->dwLowDateTime
= (UINT
)t
;
1635 filetime
->dwHighDateTime
= (UINT
)(t
>> 32);
1637 #else /* ISO version */
1639 UINT a0
; /* 16 bit, low bits */
1640 UINT a1
; /* 16 bit, medium bits */
1641 UINT a2
; /* 32 bit, high bits */
1643 /* Copy the unix time to a2/a1/a0 */
1644 a0
= unix_time
& 0xffff;
1645 a1
= (unix_time
>> 16) & 0xffff;
1646 /* This is obsolete if unix_time is only 32 bits, but it does not hurt.
1647 Do not replace this by >> 32, it gives a compiler warning and it does
1649 a2
= (unix_time
>= 0 ? (unix_time
>> 16) >> 16 :
1650 ~((~unix_time
>> 16) >> 16));
1652 /* Multiply a by 10000000 (a = a2/a1/a0)
1653 Split the factor into 10000 * 1000 which are both less than 0xffff. */
1655 a1
= a1
* 10000 + (a0
>> 16);
1656 a2
= a2
* 10000 + (a1
>> 16);
1661 a1
= a1
* 1000 + (a0
>> 16);
1662 a2
= a2
* 1000 + (a1
>> 16);
1666 /* Add the time difference and the remainder */
1667 a0
+= 32768 + (remainder
& 0xffff);
1668 a1
+= 54590 + (remainder
>> 16 ) + (a0
>> 16);
1669 a2
+= 27111902 + (a1
>> 16);
1674 filetime
->dwLowDateTime
= (a1
<< 16) + a0
;
1675 filetime
->dwHighDateTime
= a2
;
1680 /***********************************************************************
1681 * DOSFS_FileTimeToUnixTime
1683 * Convert a FILETIME format to Unix time.
1684 * If not NULL, 'remainder' contains the fractional part of the filetime,
1685 * in the range of [0..9999999] (even if time_t is negative).
1687 time_t DOSFS_FileTimeToUnixTime( const FILETIME
*filetime
, DWORD
*remainder
)
1689 /* Read the comment in the function DOSFS_UnixTimeToFileTime. */
1692 long long int t
= filetime
->dwHighDateTime
;
1694 t
+= (UINT
)filetime
->dwLowDateTime
;
1695 t
-= 116444736000000000LL;
1698 if (remainder
) *remainder
= 9999999 - (-t
- 1) % 10000000;
1699 return -1 - ((-t
- 1) / 10000000);
1703 if (remainder
) *remainder
= t
% 10000000;
1704 return t
/ 10000000;
1707 #else /* ISO version */
1709 UINT a0
; /* 16 bit, low bits */
1710 UINT a1
; /* 16 bit, medium bits */
1711 UINT a2
; /* 32 bit, high bits */
1712 UINT r
; /* remainder of division */
1713 unsigned int carry
; /* carry bit for subtraction */
1714 int negative
; /* whether a represents a negative value */
1716 /* Copy the time values to a2/a1/a0 */
1717 a2
= (UINT
)filetime
->dwHighDateTime
;
1718 a1
= ((UINT
)filetime
->dwLowDateTime
) >> 16;
1719 a0
= ((UINT
)filetime
->dwLowDateTime
) & 0xffff;
1721 /* Subtract the time difference */
1722 if (a0
>= 32768 ) a0
-= 32768 , carry
= 0;
1723 else a0
+= (1 << 16) - 32768 , carry
= 1;
1725 if (a1
>= 54590 + carry
) a1
-= 54590 + carry
, carry
= 0;
1726 else a1
+= (1 << 16) - 54590 - carry
, carry
= 1;
1728 a2
-= 27111902 + carry
;
1730 /* If a is negative, replace a by (-1-a) */
1731 negative
= (a2
>= ((UINT
)1) << 31);
1734 /* Set a to -a - 1 (a is a2/a1/a0) */
1740 /* Divide a by 10000000 (a = a2/a1/a0), put the rest into r.
1741 Split the divisor into 10000 * 1000 which are both less than 0xffff. */
1742 a1
+= (a2
% 10000) << 16;
1744 a0
+= (a1
% 10000) << 16;
1749 a1
+= (a2
% 1000) << 16;
1751 a0
+= (a1
% 1000) << 16;
1753 r
+= (a0
% 1000) * 10000;
1756 /* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
1759 /* Set a to -a - 1 (a is a2/a1/a0) */
1767 if (remainder
) *remainder
= r
;
1769 /* Do not replace this by << 32, it gives a compiler warning and it does
1771 return ((((time_t)a2
) << 16) << 16) + (a1
<< 16) + a0
;
1776 /***********************************************************************
1777 * DosDateTimeToFileTime (KERNEL32.76)
1779 BOOL WINAPI
DosDateTimeToFileTime( WORD fatdate
, WORD fattime
, LPFILETIME ft
)
1783 newtm
.tm_sec
= (fattime
& 0x1f) * 2;
1784 newtm
.tm_min
= (fattime
>> 5) & 0x3f;
1785 newtm
.tm_hour
= (fattime
>> 11);
1786 newtm
.tm_mday
= (fatdate
& 0x1f);
1787 newtm
.tm_mon
= ((fatdate
>> 5) & 0x0f) - 1;
1788 newtm
.tm_year
= (fatdate
>> 9) + 80;
1789 DOSFS_UnixTimeToFileTime( mktime( &newtm
), ft
, 0 );
1794 /***********************************************************************
1795 * FileTimeToDosDateTime (KERNEL32.111)
1797 BOOL WINAPI
FileTimeToDosDateTime( const FILETIME
*ft
, LPWORD fatdate
,
1800 time_t unixtime
= DOSFS_FileTimeToUnixTime( ft
, NULL
);
1801 struct tm
*tm
= localtime( &unixtime
);
1803 *fattime
= (tm
->tm_hour
<< 11) + (tm
->tm_min
<< 5) + (tm
->tm_sec
/ 2);
1805 *fatdate
= ((tm
->tm_year
- 80) << 9) + ((tm
->tm_mon
+ 1) << 5)
1811 /***********************************************************************
1812 * LocalFileTimeToFileTime (KERNEL32.373)
1814 BOOL WINAPI
LocalFileTimeToFileTime( const FILETIME
*localft
,
1820 /* convert from local to UTC. Perhaps not correct. FIXME */
1821 time_t unixtime
= DOSFS_FileTimeToUnixTime( localft
, &remainder
);
1822 xtm
= gmtime( &unixtime
);
1823 DOSFS_UnixTimeToFileTime( mktime(xtm
), utcft
, remainder
);
1828 /***********************************************************************
1829 * FileTimeToLocalFileTime (KERNEL32.112)
1831 BOOL WINAPI
FileTimeToLocalFileTime( const FILETIME
*utcft
,
1832 LPFILETIME localft
)
1835 /* convert from UTC to local. Perhaps not correct. FIXME */
1836 time_t unixtime
= DOSFS_FileTimeToUnixTime( utcft
, &remainder
);
1838 struct tm
*xtm
= localtime( &unixtime
);
1841 localtime
= timegm(xtm
);
1842 DOSFS_UnixTimeToFileTime( localtime
, localft
, remainder
);
1845 struct tm
*xtm
,*gtm
;
1848 xtm
= localtime( &unixtime
);
1849 gtm
= gmtime( &unixtime
);
1850 time1
= mktime(xtm
);
1851 time2
= mktime(gtm
);
1852 DOSFS_UnixTimeToFileTime( 2*time1
-time2
, localft
, remainder
);
1858 /***********************************************************************
1859 * FileTimeToSystemTime (KERNEL32.113)
1861 BOOL WINAPI
FileTimeToSystemTime( const FILETIME
*ft
, LPSYSTEMTIME syst
)
1865 time_t xtime
= DOSFS_FileTimeToUnixTime( ft
, &remainder
);
1866 xtm
= gmtime(&xtime
);
1867 syst
->wYear
= xtm
->tm_year
+1900;
1868 syst
->wMonth
= xtm
->tm_mon
+ 1;
1869 syst
->wDayOfWeek
= xtm
->tm_wday
;
1870 syst
->wDay
= xtm
->tm_mday
;
1871 syst
->wHour
= xtm
->tm_hour
;
1872 syst
->wMinute
= xtm
->tm_min
;
1873 syst
->wSecond
= xtm
->tm_sec
;
1874 syst
->wMilliseconds
= remainder
/ 10000;
1878 /***********************************************************************
1879 * QueryDosDeviceA (KERNEL32.413)
1881 * returns array of strings terminated by \0, terminated by \0
1883 DWORD WINAPI
QueryDosDeviceA(LPCSTR devname
,LPSTR target
,DWORD bufsize
)
1888 TRACE("(%s,...)\n", devname
? devname
: "<null>");
1890 /* return known MSDOS devices */
1891 strcpy(buffer
,"CON COM1 COM2 LPT1 NUL ");
1892 while ((s
=strchr(buffer
,' ')))
1895 lstrcpynA(target
,buffer
,bufsize
);
1896 return strlen(buffer
);
1898 strcpy(buffer
,"\\DEV\\");
1899 strcat(buffer
,devname
);
1900 if ((s
=strchr(buffer
,':'))) *s
='\0';
1901 lstrcpynA(target
,buffer
,bufsize
);
1902 return strlen(buffer
);
1906 /***********************************************************************
1907 * QueryDosDeviceW (KERNEL32.414)
1909 * returns array of strings terminated by \0, terminated by \0
1911 DWORD WINAPI
QueryDosDeviceW(LPCWSTR devname
,LPWSTR target
,DWORD bufsize
)
1913 LPSTR devnameA
= devname
?HEAP_strdupWtoA(GetProcessHeap(),0,devname
):NULL
;
1914 LPSTR targetA
= (LPSTR
)HEAP_xalloc(GetProcessHeap(),0,bufsize
);
1915 DWORD ret
= QueryDosDeviceA(devnameA
,targetA
,bufsize
);
1917 lstrcpynAtoW(target
,targetA
,bufsize
);
1918 if (devnameA
) HeapFree(GetProcessHeap(),0,devnameA
);
1919 if (targetA
) HeapFree(GetProcessHeap(),0,targetA
);
1924 /***********************************************************************
1925 * SystemTimeToFileTime (KERNEL32.526)
1927 BOOL WINAPI
SystemTimeToFileTime( const SYSTEMTIME
*syst
, LPFILETIME ft
)
1933 struct tm xtm
,*local_tm
,*utc_tm
;
1934 time_t localtim
,utctime
;
1937 xtm
.tm_year
= syst
->wYear
-1900;
1938 xtm
.tm_mon
= syst
->wMonth
- 1;
1939 xtm
.tm_wday
= syst
->wDayOfWeek
;
1940 xtm
.tm_mday
= syst
->wDay
;
1941 xtm
.tm_hour
= syst
->wHour
;
1942 xtm
.tm_min
= syst
->wMinute
;
1943 xtm
.tm_sec
= syst
->wSecond
; /* this is UTC */
1946 utctime
= timegm(&xtm
);
1947 DOSFS_UnixTimeToFileTime( utctime
, ft
,
1948 syst
->wMilliseconds
* 10000 );
1950 localtim
= mktime(&xtm
); /* now we've got local time */
1951 local_tm
= localtime(&localtim
);
1952 utc_tm
= gmtime(&localtim
);
1953 utctime
= mktime(utc_tm
);
1954 DOSFS_UnixTimeToFileTime( 2*localtim
-utctime
, ft
,
1955 syst
->wMilliseconds
* 10000 );
1960 /***********************************************************************
1961 * DefineDosDeviceA (KERNEL32.182)
1963 BOOL WINAPI
DefineDosDeviceA(DWORD flags
,LPCSTR devname
,LPCSTR targetpath
) {
1964 FIXME("(0x%08lx,%s,%s),stub!\n",flags
,devname
,targetpath
);
1965 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);