2 * DOS file system functions
4 * Copyright 1993 Erik Bos
5 * Copyright 1996 Alexandre Julliard
13 #include <sys/errno.h>
18 #include <sys/ioctl.h>
24 #include "wine/winbase16.h"
35 /* Define the VFAT ioctl to get both short and long file names */
36 /* FIXME: is it possible to get this to work on other systems? */
38 #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, long)
39 /* We want the real kernel dirent structure, not the libc one */
44 unsigned short d_reclen
;
49 #undef VFAT_IOCTL_READDIR_BOTH /* just in case... */
52 /* Chars we don't want to see in DOS file names */
53 #define INVALID_DOS_CHARS "*?<>|\"+=,;[] \345"
55 static const DOS_DEVICE DOSFS_Devices
[] =
56 /* name, device flags (see Int 21/AX=0x4400) */
70 { "SCSIMGR$", 0xc0c0 },
74 #define GET_DRIVE(path) \
75 (((path)[1] == ':') ? toupper((path)[0]) - 'A' : DOSFS_CurDrive)
77 /* Directory info for DOSFS_ReadDir */
81 #ifdef VFAT_IOCTL_READDIR_BOTH
84 KERNEL_DIRENT dirent
[2];
88 /* Info structure for FindFirstFile handle */
102 /***********************************************************************
105 * Return 1 if Unix file 'name' is also a valid MS-DOS name
106 * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
107 * File name can be terminated by '\0', '\\' or '/'.
109 static int DOSFS_ValidDOSName( const char *name
, int ignore_case
)
111 static const char invalid_chars
[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS
;
112 const char *p
= name
;
113 const char *invalid
= ignore_case
? (invalid_chars
+ 26) : invalid_chars
;
118 /* Check for "." and ".." */
121 /* All other names beginning with '.' are invalid */
122 return (IS_END_OF_NAME(*p
));
124 while (!IS_END_OF_NAME(*p
))
126 if (strchr( invalid
, *p
)) return 0; /* Invalid char */
127 if (*p
== '.') break; /* Start of the extension */
128 if (++len
> 8) return 0; /* Name too long */
131 if (*p
!= '.') return 1; /* End of name */
133 if (IS_END_OF_NAME(*p
)) return 0; /* Empty extension not allowed */
135 while (!IS_END_OF_NAME(*p
))
137 if (strchr( invalid
, *p
)) return 0; /* Invalid char */
138 if (*p
== '.') return 0; /* Second extension not allowed */
139 if (++len
> 3) return 0; /* Extension too long */
146 /***********************************************************************
147 * DOSFS_ToDosFCBFormat
149 * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
150 * expanding wild cards and converting to upper-case in the process.
151 * File name can be terminated by '\0', '\\' or '/'.
152 * Return FALSE if the name is not a valid DOS name.
153 * 'buffer' must be at least 12 characters long.
155 BOOL
DOSFS_ToDosFCBFormat( LPCSTR name
, LPSTR buffer
)
157 static const char invalid_chars
[] = INVALID_DOS_CHARS
;
158 const char *p
= name
;
161 /* Check for "." and ".." */
165 strcpy( buffer
, ". " );
171 return (!*p
|| (*p
== '/') || (*p
== '\\'));
174 for (i
= 0; i
< 8; i
++)
191 if (strchr( invalid_chars
, *p
)) return FALSE
;
192 buffer
[i
] = toupper(*p
);
200 /* Skip all chars after wildcard up to first dot */
201 while (*p
&& (*p
!= '/') && (*p
!= '\\') && (*p
!= '.')) p
++;
205 /* Check if name too long */
206 if (*p
&& (*p
!= '/') && (*p
!= '\\') && (*p
!= '.')) return FALSE
;
208 if (*p
== '.') p
++; /* Skip dot */
210 for (i
= 8; i
< 11; i
++)
220 return FALSE
; /* Second extension not allowed */
228 if (strchr( invalid_chars
, *p
)) return FALSE
;
229 buffer
[i
] = toupper(*p
);
239 /***********************************************************************
240 * DOSFS_ToDosDTAFormat
242 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
243 * converting to upper-case in the process.
244 * File name can be terminated by '\0', '\\' or '/'.
245 * 'buffer' must be at least 13 characters long.
247 static void DOSFS_ToDosDTAFormat( LPCSTR name
, LPSTR buffer
)
251 memcpy( buffer
, name
, 8 );
252 for (p
= buffer
+ 8; (p
> buffer
) && (p
[-1] == ' '); p
--);
254 memcpy( p
, name
+ 8, 3 );
255 for (p
+= 3; p
[-1] == ' '; p
--);
256 if (p
[-1] == '.') p
--;
261 /***********************************************************************
264 * Check a DOS file name against a mask (both in FCB format).
266 static int DOSFS_MatchShort( const char *mask
, const char *name
)
269 for (i
= 11; i
> 0; i
--, mask
++, name
++)
270 if ((*mask
!= '?') && (*mask
!= *name
)) return 0;
275 /***********************************************************************
278 * Check a long file name against a mask.
280 static int DOSFS_MatchLong( const char *mask
, const char *name
,
283 if (!strcmp( mask
, "*.*" )) return 1;
284 while (*name
&& *mask
)
289 while (*mask
== '*') mask
++; /* Skip consecutive '*' */
290 if (!*mask
) return 1;
291 if (case_sensitive
) while (*name
&& (*name
!= *mask
)) name
++;
292 else while (*name
&& (toupper(*name
) != toupper(*mask
))) name
++;
293 if (!*name
) return 0;
295 else if (*mask
!= '?')
299 if (*mask
!= *name
) return 0;
301 else if (toupper(*mask
) != toupper(*name
)) return 0;
306 return (!*name
&& !*mask
);
310 /***********************************************************************
313 static DOS_DIR
*DOSFS_OpenDir( LPCSTR path
)
315 DOS_DIR
*dir
= HeapAlloc( SystemHeap
, 0, sizeof(*dir
) );
318 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
322 /* Treat empty path as root directory. This simplifies path split into
323 directory and mask in several other places */
324 if (!*path
) path
= "/";
326 #ifdef VFAT_IOCTL_READDIR_BOTH
328 /* Check if the VFAT ioctl is supported on this directory */
330 if ((dir
->fd
= open( path
, O_RDONLY
)) != -1)
332 if (ioctl( dir
->fd
, VFAT_IOCTL_READDIR_BOTH
, (long)dir
->dirent
) == -1)
339 /* Set the file pointer back at the start of the directory */
340 lseek( dir
->fd
, 0, SEEK_SET
);
345 #endif /* VFAT_IOCTL_READDIR_BOTH */
347 /* Now use the standard opendir/readdir interface */
349 if (!(dir
->dir
= opendir( path
)))
351 HeapFree( SystemHeap
, 0, dir
);
358 /***********************************************************************
361 static void DOSFS_CloseDir( DOS_DIR
*dir
)
363 #ifdef VFAT_IOCTL_READDIR_BOTH
364 if (dir
->fd
!= -1) close( dir
->fd
);
365 #endif /* VFAT_IOCTL_READDIR_BOTH */
366 if (dir
->dir
) closedir( dir
->dir
);
367 HeapFree( SystemHeap
, 0, dir
);
371 /***********************************************************************
374 static BOOL
DOSFS_ReadDir( DOS_DIR
*dir
, LPCSTR
*long_name
,
377 struct dirent
*dirent
;
379 #ifdef VFAT_IOCTL_READDIR_BOTH
382 if (ioctl( dir
->fd
, VFAT_IOCTL_READDIR_BOTH
, (long)dir
->dirent
) != -1) {
383 if (!dir
->dirent
[0].d_reclen
) return FALSE
;
384 if (!DOSFS_ToDosFCBFormat( dir
->dirent
[0].d_name
, dir
->short_name
))
385 dir
->short_name
[0] = '\0';
386 *short_name
= dir
->short_name
;
387 if (dir
->dirent
[1].d_name
[0]) *long_name
= dir
->dirent
[1].d_name
;
388 else *long_name
= dir
->dirent
[0].d_name
;
392 #endif /* VFAT_IOCTL_READDIR_BOTH */
394 if (!(dirent
= readdir( dir
->dir
))) return FALSE
;
395 *long_name
= dirent
->d_name
;
401 /***********************************************************************
404 * Transform a Unix file name into a hashed DOS name. If the name is a valid
405 * DOS name, it is converted to upper-case; otherwise it is replaced by a
406 * hashed version that fits in 8.3 format.
407 * File name can be terminated by '\0', '\\' or '/'.
408 * 'buffer' must be at least 13 characters long.
410 static void DOSFS_Hash( LPCSTR name
, LPSTR buffer
, BOOL dir_format
,
413 static const char invalid_chars
[] = INVALID_DOS_CHARS
"~.";
414 static const char hash_chars
[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
421 if (dir_format
) strcpy( buffer
, " " );
423 if (DOSFS_ValidDOSName( name
, ignore_case
))
425 /* Check for '.' and '..' */
429 if (!dir_format
) buffer
[1] = buffer
[2] = '\0';
430 if (name
[1] == '.') buffer
[1] = '.';
434 /* Simply copy the name, converting to uppercase */
436 for (dst
= buffer
; !IS_END_OF_NAME(*name
) && (*name
!= '.'); name
++)
437 *dst
++ = toupper(*name
);
440 if (dir_format
) dst
= buffer
+ 8;
442 for (name
++; !IS_END_OF_NAME(*name
); name
++)
443 *dst
++ = toupper(*name
);
445 if (!dir_format
) *dst
= '\0';
449 /* Compute the hash code of the file name */
450 /* If you know something about hash functions, feel free to */
451 /* insert a better algorithm here... */
454 for (p
= name
, hash
= 0xbeef; !IS_END_OF_NAME(p
[1]); p
++)
455 hash
= (hash
<<3) ^ (hash
>>5) ^ tolower(*p
) ^ (tolower(p
[1]) << 8);
456 hash
= (hash
<<3) ^ (hash
>>5) ^ tolower(*p
); /* Last character*/
460 for (p
= name
, hash
= 0xbeef; !IS_END_OF_NAME(p
[1]); p
++)
461 hash
= (hash
<< 3) ^ (hash
>> 5) ^ *p
^ (p
[1] << 8);
462 hash
= (hash
<< 3) ^ (hash
>> 5) ^ *p
; /* Last character */
465 /* Find last dot for start of the extension */
466 for (p
= name
+1, ext
= NULL
; !IS_END_OF_NAME(*p
); p
++)
467 if (*p
== '.') ext
= p
;
468 if (ext
&& IS_END_OF_NAME(ext
[1]))
469 ext
= NULL
; /* Empty extension ignored */
471 /* Copy first 4 chars, replacing invalid chars with '_' */
472 for (i
= 4, p
= name
, dst
= buffer
; i
> 0; i
--, p
++)
474 if (IS_END_OF_NAME(*p
) || (p
== ext
)) break;
475 *dst
++ = strchr( invalid_chars
, *p
) ? '_' : toupper(*p
);
477 /* Pad to 5 chars with '~' */
478 while (i
-- >= 0) *dst
++ = '~';
480 /* Insert hash code converted to 3 ASCII chars */
481 *dst
++ = hash_chars
[(hash
>> 10) & 0x1f];
482 *dst
++ = hash_chars
[(hash
>> 5) & 0x1f];
483 *dst
++ = hash_chars
[hash
& 0x1f];
485 /* Copy the first 3 chars of the extension (if any) */
488 if (!dir_format
) *dst
++ = '.';
489 for (i
= 3, ext
++; (i
> 0) && !IS_END_OF_NAME(*ext
); i
--, ext
++)
490 *dst
++ = strchr( invalid_chars
, *ext
) ? '_' : toupper(*ext
);
492 if (!dir_format
) *dst
= '\0';
496 /***********************************************************************
499 * Find the Unix file name in a given directory that corresponds to
500 * a file name (either in Unix or DOS format).
501 * File name can be terminated by '\0', '\\' or '/'.
502 * Return TRUE if OK, FALSE if no file name matches.
504 * 'long_buf' must be at least 'long_len' characters long. If the long name
505 * turns out to be larger than that, the function returns FALSE.
506 * 'short_buf' must be at least 13 characters long.
508 BOOL
DOSFS_FindUnixName( LPCSTR path
, LPCSTR name
, LPSTR long_buf
,
509 INT long_len
, LPSTR short_buf
, BOOL ignore_case
)
512 LPCSTR long_name
, short_name
;
513 char dos_name
[12], tmp_buf
[13];
516 const char *p
= strchr( name
, '/' );
517 int len
= p
? (int)(p
- name
) : strlen(name
);
518 if ((p
= strchr( name
, '\\' ))) len
= MIN( (int)(p
- name
), len
);
519 if (long_len
< len
+ 1) return FALSE
;
521 TRACE(dosfs
, "%s,%s\n", path
, name
);
523 if (!DOSFS_ToDosFCBFormat( name
, dos_name
)) dos_name
[0] = '\0';
525 if (!(dir
= DOSFS_OpenDir( path
)))
527 WARN(dosfs
, "(%s,%s): can't open dir: %s\n",
528 path
, name
, strerror(errno
) );
532 while ((ret
= DOSFS_ReadDir( dir
, &long_name
, &short_name
)))
534 /* Check against Unix name */
535 if (len
== strlen(long_name
))
539 if (!lstrncmpA( long_name
, name
, len
)) break;
543 if (!lstrncmpiA( long_name
, name
, len
)) break;
548 /* Check against hashed DOS name */
551 DOSFS_Hash( long_name
, tmp_buf
, TRUE
, ignore_case
);
552 short_name
= tmp_buf
;
554 if (!strcmp( dos_name
, short_name
)) break;
559 if (long_buf
) strcpy( long_buf
, long_name
);
563 DOSFS_ToDosDTAFormat( short_name
, short_buf
);
565 DOSFS_Hash( long_name
, short_buf
, FALSE
, ignore_case
);
567 TRACE(dosfs
, "(%s,%s) -> %s (%s)\n",
568 path
, name
, long_name
, short_buf
? short_buf
: "***");
571 WARN(dosfs
, "'%s' not found in '%s'\n", name
, path
);
572 DOSFS_CloseDir( dir
);
577 /***********************************************************************
580 * Check if a DOS file name represents a DOS device and return the device.
582 const DOS_DEVICE
*DOSFS_GetDevice( const char *name
)
587 if (!name
) return NULL
; /* if FILE_DupUnixHandle was used */
588 if (name
[0] && (name
[1] == ':')) name
+= 2;
589 if ((p
= strrchr( name
, '/' ))) name
= p
+ 1;
590 if ((p
= strrchr( name
, '\\' ))) name
= p
+ 1;
591 for (i
= 0; i
< sizeof(DOSFS_Devices
)/sizeof(DOSFS_Devices
[0]); i
++)
593 const char *dev
= DOSFS_Devices
[i
].name
;
594 if (!lstrncmpiA( dev
, name
, strlen(dev
) ))
596 p
= name
+ strlen( dev
);
597 if (!*p
|| (*p
== '.')) return &DOSFS_Devices
[i
];
604 /***********************************************************************
605 * DOSFS_GetDeviceByHandle
607 const DOS_DEVICE
*DOSFS_GetDeviceByHandle( HFILE hFile
)
609 struct get_file_info_request req
;
610 struct get_file_info_reply reply
;
613 CLIENT_SendRequest( REQ_GET_FILE_INFO
, -1, 1, &req
, sizeof(req
) );
614 if (!CLIENT_WaitSimpleReply( &reply
, sizeof(reply
), NULL
) &&
615 (reply
.type
== FILE_TYPE_UNKNOWN
))
617 if ((reply
.attr
>= 0) &&
618 (reply
.attr
< sizeof(DOSFS_Devices
)/sizeof(DOSFS_Devices
[0])))
619 return &DOSFS_Devices
[reply
.attr
];
625 /***********************************************************************
628 * Open a DOS device. This might not map 1:1 into the UNIX device concept.
630 HFILE
DOSFS_OpenDevice( const char *name
, DWORD access
)
635 if (!name
) return (HFILE
)NULL
; /* if FILE_DupUnixHandle was used */
636 if (name
[0] && (name
[1] == ':')) name
+= 2;
637 if ((p
= strrchr( name
, '/' ))) name
= p
+ 1;
638 if ((p
= strrchr( name
, '\\' ))) name
= p
+ 1;
639 for (i
= 0; i
< sizeof(DOSFS_Devices
)/sizeof(DOSFS_Devices
[0]); i
++)
641 const char *dev
= DOSFS_Devices
[i
].name
;
642 if (!lstrncmpiA( dev
, name
, strlen(dev
) ))
644 p
= name
+ strlen( dev
);
645 if (!*p
|| (*p
== '.')) {
647 if (!strcmp(DOSFS_Devices
[i
].name
,"NUL"))
648 return FILE_CreateFile( "/dev/null", access
,
649 FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
,
650 OPEN_EXISTING
, 0, -1 );
651 if (!strcmp(DOSFS_Devices
[i
].name
,"CON")) {
654 switch (access
& (GENERIC_READ
|GENERIC_WRITE
)) {
656 to_dup
= GetStdHandle( STD_INPUT_HANDLE
);
659 to_dup
= GetStdHandle( STD_OUTPUT_HANDLE
);
662 FIXME(dosfs
,"can't open CON read/write\n");
666 if (!DuplicateHandle( GetCurrentProcess(), to_dup
, GetCurrentProcess(),
667 &handle
, 0, FALSE
, DUPLICATE_SAME_ACCESS
))
668 handle
= HFILE_ERROR
;
671 if (!strcmp(DOSFS_Devices
[i
].name
,"SCSIMGR$") ||
672 !strcmp(DOSFS_Devices
[i
].name
,"HPSCAN"))
674 return FILE_CreateDevice( i
, access
, NULL
);
676 FIXME(dosfs
,"device open %s not supported (yet)\n",DOSFS_Devices
[i
].name
);
685 /***********************************************************************
688 * Get the drive specified by a given path name (DOS or Unix format).
690 static int DOSFS_GetPathDrive( const char **name
)
693 const char *p
= *name
;
695 if (*p
&& (p
[1] == ':'))
697 drive
= toupper(*p
) - 'A';
700 else if (*p
== '/') /* Absolute Unix path? */
702 if ((drive
= DRIVE_FindDriveRoot( name
)) == -1)
704 MSG("Warning: %s not accessible from a DOS drive\n", *name
);
705 /* Assume it really was a DOS name */
706 drive
= DRIVE_GetCurrentDrive();
709 else drive
= DRIVE_GetCurrentDrive();
711 if (!DRIVE_IsValid(drive
))
713 SetLastError( ERROR_INVALID_DRIVE
);
720 /***********************************************************************
723 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
724 * Unix name / short DOS name pair.
725 * Return FALSE if one of the path components does not exist. The last path
726 * component is only checked if 'check_last' is non-zero.
727 * The buffers pointed to by 'long_buf' and 'short_buf' must be
728 * at least MAX_PATHNAME_LEN long.
730 BOOL
DOSFS_GetFullName( LPCSTR name
, BOOL check_last
, DOS_FULL_NAME
*full
)
734 char *p_l
, *p_s
, *root
;
736 TRACE(dosfs
, "%s (last=%d)\n",
739 if ((full
->drive
= DOSFS_GetPathDrive( &name
)) == -1) return FALSE
;
740 flags
= DRIVE_GetFlags( full
->drive
);
742 lstrcpynA( full
->long_name
, DRIVE_GetRoot( full
->drive
),
743 sizeof(full
->long_name
) );
744 if (full
->long_name
[1]) root
= full
->long_name
+ strlen(full
->long_name
);
745 else root
= full
->long_name
; /* root directory */
747 strcpy( full
->short_name
, "A:\\" );
748 full
->short_name
[0] += full
->drive
;
750 if ((*name
== '\\') || (*name
== '/')) /* Absolute path */
752 while ((*name
== '\\') || (*name
== '/')) name
++;
754 else /* Relative path */
756 lstrcpynA( root
+ 1, DRIVE_GetUnixCwd( full
->drive
),
757 sizeof(full
->long_name
) - (root
- full
->long_name
) - 1 );
758 if (root
[1]) *root
= '/';
759 lstrcpynA( full
->short_name
+ 3, DRIVE_GetDosCwd( full
->drive
),
760 sizeof(full
->short_name
) - 3 );
763 p_l
= full
->long_name
[1] ? full
->long_name
+ strlen(full
->long_name
)
765 p_s
= full
->short_name
[3] ? full
->short_name
+ strlen(full
->short_name
)
766 : full
->short_name
+ 2;
769 while (*name
&& found
)
771 /* Check for '.' and '..' */
775 if (IS_END_OF_NAME(name
[1]))
778 while ((*name
== '\\') || (*name
== '/')) name
++;
781 else if ((name
[1] == '.') && IS_END_OF_NAME(name
[2]))
784 while ((*name
== '\\') || (*name
== '/')) name
++;
785 while ((p_l
> root
) && (*p_l
!= '/')) p_l
--;
786 while ((p_s
> full
->short_name
+ 2) && (*p_s
!= '\\')) p_s
--;
787 *p_l
= *p_s
= '\0'; /* Remove trailing separator */
792 /* Make sure buffers are large enough */
794 if ((p_s
>= full
->short_name
+ sizeof(full
->short_name
) - 14) ||
795 (p_l
>= full
->long_name
+ sizeof(full
->long_name
) - 1))
797 SetLastError( ERROR_PATH_NOT_FOUND
);
801 /* Get the long and short name matching the file name */
803 if ((found
= DOSFS_FindUnixName( full
->long_name
, name
, p_l
+ 1,
804 sizeof(full
->long_name
) - (p_l
- full
->long_name
) - 1,
805 p_s
+ 1, !(flags
& DRIVE_CASE_SENSITIVE
) )))
811 while (!IS_END_OF_NAME(*name
)) name
++;
813 else if (!check_last
)
817 while (!IS_END_OF_NAME(*name
) &&
818 (p_s
< full
->short_name
+ sizeof(full
->short_name
) - 1) &&
819 (p_l
< full
->long_name
+ sizeof(full
->long_name
) - 1))
821 *p_s
++ = tolower(*name
);
822 /* If the drive is case-sensitive we want to create new */
823 /* files in lower-case otherwise we can't reopen them */
824 /* under the same short name. */
825 if (flags
& DRIVE_CASE_SENSITIVE
) *p_l
++ = tolower(*name
);
831 while ((*name
== '\\') || (*name
== '/')) name
++;
838 SetLastError( ERROR_FILE_NOT_FOUND
);
841 if (*name
) /* Not last */
843 SetLastError( ERROR_PATH_NOT_FOUND
);
847 if (!full
->long_name
[0]) strcpy( full
->long_name
, "/" );
848 if (!full
->short_name
[2]) strcpy( full
->short_name
+ 2, "\\" );
849 TRACE(dosfs
, "returning %s = %s\n",
850 full
->long_name
, full
->short_name
);
855 /***********************************************************************
856 * GetShortPathName32A (KERNEL32.271)
860 * longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
861 * *longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0
863 DWORD WINAPI
GetShortPathNameA( LPCSTR longpath
, LPSTR shortpath
,
866 DOS_FULL_NAME full_name
;
870 SetLastError(ERROR_INVALID_PARAMETER
);
876 SetLastError(ERROR_BAD_PATHNAME
);
880 /* FIXME: is it correct to always return a fully qualified short path? */
881 if (!DOSFS_GetFullName( longpath
, TRUE
, &full_name
))
883 SetLastError(ERROR_BAD_PATHNAME
);
886 lstrcpynA( shortpath
, full_name
.short_name
, shortlen
);
887 return strlen( full_name
.short_name
);
891 /***********************************************************************
892 * GetShortPathName32W (KERNEL32.272)
894 DWORD WINAPI
GetShortPathNameW( LPCWSTR longpath
, LPWSTR shortpath
,
897 DOS_FULL_NAME full_name
;
902 { SetLastError(ERROR_INVALID_PARAMETER
);
907 { SetLastError(ERROR_BAD_PATHNAME
);
912 longpathA
= HEAP_strdupWtoA( GetProcessHeap(), 0, longpath
);
914 /* FIXME: is it correct to always return a fully qualified short path? */
915 if (DOSFS_GetFullName( longpathA
, TRUE
, &full_name
))
917 ret
= strlen( full_name
.short_name
);
918 lstrcpynAtoW( shortpath
, full_name
.short_name
, shortlen
);
921 SetLastError(ERROR_BAD_PATHNAME
);
922 HeapFree( GetProcessHeap(), 0, longpathA
);
927 /***********************************************************************
928 * GetLongPathName32A (KERNEL32.xxx)
930 DWORD WINAPI
GetLongPathNameA( LPCSTR shortpath
, LPSTR longpath
,
933 DOS_FULL_NAME full_name
;
938 if (!DOSFS_GetFullName( shortpath
, TRUE
, &full_name
)) return 0;
939 lstrcpynA( longpath
, full_name
.short_name
, longlen
);
940 /* Do some hackery to get the long filename.
941 * FIXME: Would be better if it returned the
942 * long version of the directories too
944 longfilename
= strrchr(full_name
.long_name
, '/')+1;
945 if (longpath
!= NULL
) {
946 if ((p
= strrchr( longpath
, '\\' )) != NULL
) {
948 longlen
-= (p
-longpath
);
949 lstrcpynA( p
, longfilename
, longlen
);
953 ((strrchr( full_name
.short_name
, '\\' ) - full_name
.short_name
) + 1);
954 return shortpathlen
+ strlen( longfilename
);
958 /***********************************************************************
959 * GetLongPathName32W (KERNEL32.269)
961 DWORD WINAPI
GetLongPathNameW( LPCWSTR shortpath
, LPWSTR longpath
,
964 DOS_FULL_NAME full_name
;
966 LPSTR shortpathA
= HEAP_strdupWtoA( GetProcessHeap(), 0, shortpath
);
968 /* FIXME: is it correct to always return a fully qualified short path? */
969 if (DOSFS_GetFullName( shortpathA
, TRUE
, &full_name
))
971 ret
= strlen( full_name
.short_name
);
972 lstrcpynAtoW( longpath
, full_name
.long_name
, longlen
);
974 HeapFree( GetProcessHeap(), 0, shortpathA
);
979 /***********************************************************************
980 * DOSFS_DoGetFullPathName
982 * Implementation of GetFullPathName32A/W.
984 static DWORD
DOSFS_DoGetFullPathName( LPCSTR name
, DWORD len
, LPSTR result
,
987 char buffer
[MAX_PATHNAME_LEN
];
992 /* last possible position for a char != 0 */
993 char *endchar
= buffer
+ sizeof(buffer
) - 2;
996 TRACE(dosfs
, "converting '%s'\n", name
);
998 if (!name
|| !result
|| ((drive
= DOSFS_GetPathDrive( &name
)) == -1) )
999 { SetLastError( ERROR_INVALID_PARAMETER
);
1006 if (IS_END_OF_NAME(*name
) && (*name
)) /* Absolute path */
1008 while (((*name
== '\\') || (*name
== '/')) && (!*endchar
) )
1011 else /* Relative path or empty path */
1014 lstrcpynA( p
, DRIVE_GetDosCwd(drive
), sizeof(buffer
) - 4 );
1027 if (IS_END_OF_NAME(name
[1]))
1030 while ((*name
== '\\') || (*name
== '/')) name
++;
1033 else if ((name
[1] == '.') && IS_END_OF_NAME(name
[2]))
1036 while ((*name
== '\\') || (*name
== '/')) name
++;
1038 if (p
< buffer
+ 3) /* no previous dir component */
1040 p
--; /* skip previously added '\\' */
1041 while ((*p
== '\\') || (*p
== '/')) p
--;
1042 /* skip previous dir component */
1043 while ((*p
!= '\\') && (*p
!= '/')) p
--;
1049 { SetLastError( ERROR_PATH_NOT_FOUND
);
1052 while (!IS_END_OF_NAME(*name
) && (!*endchar
) )
1054 while (((*name
== '\\') || (*name
== '/')) && (!*endchar
) )
1059 if (!(DRIVE_GetFlags(drive
) & DRIVE_CASE_PRESERVING
))
1060 CharUpperA( buffer
);
1063 lstrcpynAtoW( (LPWSTR
)result
, buffer
, len
);
1065 lstrcpynA( result
, buffer
, len
);
1067 TRACE(dosfs
, "returning '%s'\n", buffer
);
1069 /* If the lpBuffer buffer is too small, the return value is the
1070 size of the buffer, in characters, required to hold the path. */
1072 ret
= strlen(buffer
);
1075 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
1081 /***********************************************************************
1082 * GetFullPathName32A (KERNEL32.272)
1084 * if the path closed with '\', *lastpart is 0
1086 DWORD WINAPI
GetFullPathNameA( LPCSTR name
, DWORD len
, LPSTR buffer
,
1089 DWORD ret
= DOSFS_DoGetFullPathName( name
, len
, buffer
, FALSE
);
1090 if (ret
&& lastpart
)
1092 LPSTR p
= buffer
+ strlen(buffer
);
1096 while ((p
> buffer
+ 2) && (*p
!= '\\')) p
--;
1099 else *lastpart
= NULL
;
1105 /***********************************************************************
1106 * GetFullPathName32W (KERNEL32.273)
1108 DWORD WINAPI
GetFullPathNameW( LPCWSTR name
, DWORD len
, LPWSTR buffer
,
1111 LPSTR nameA
= HEAP_strdupWtoA( GetProcessHeap(), 0, name
);
1112 DWORD ret
= DOSFS_DoGetFullPathName( nameA
, len
, (LPSTR
)buffer
, TRUE
);
1113 HeapFree( GetProcessHeap(), 0, nameA
);
1114 if (ret
&& lastpart
)
1116 LPWSTR p
= buffer
+ lstrlenW(buffer
);
1117 if (*p
!= (WCHAR
)'\\')
1119 while ((p
> buffer
+ 2) && (*p
!= (WCHAR
)'\\')) p
--;
1122 else *lastpart
= NULL
;
1127 /***********************************************************************
1130 static int DOSFS_FindNextEx( FIND_FIRST_INFO
*info
, WIN32_FIND_DATAA
*entry
)
1132 BYTE attr
= info
->attr
| FA_UNUSED
| FA_ARCHIVE
| FA_RDONLY
;
1133 UINT flags
= DRIVE_GetFlags( info
->drive
);
1134 char *p
, buffer
[MAX_PATHNAME_LEN
];
1135 const char *drive_path
;
1137 LPCSTR long_name
, short_name
;
1138 BY_HANDLE_FILE_INFORMATION fileinfo
;
1141 if ((info
->attr
& ~(FA_UNUSED
| FA_ARCHIVE
| FA_RDONLY
)) == FA_LABEL
)
1143 if (info
->cur_pos
) return 0;
1144 entry
->dwFileAttributes
= FILE_ATTRIBUTE_LABEL
;
1145 DOSFS_UnixTimeToFileTime( (time_t)0, &entry
->ftCreationTime
, 0 );
1146 DOSFS_UnixTimeToFileTime( (time_t)0, &entry
->ftLastAccessTime
, 0 );
1147 DOSFS_UnixTimeToFileTime( (time_t)0, &entry
->ftLastWriteTime
, 0 );
1148 entry
->nFileSizeHigh
= 0;
1149 entry
->nFileSizeLow
= 0;
1150 entry
->dwReserved0
= 0;
1151 entry
->dwReserved1
= 0;
1152 DOSFS_ToDosDTAFormat( DRIVE_GetLabel( info
->drive
), entry
->cFileName
);
1153 strcpy( entry
->cAlternateFileName
, entry
->cFileName
);
1158 drive_path
= info
->path
+ strlen(DRIVE_GetRoot( info
->drive
));
1159 while ((*drive_path
== '/') || (*drive_path
== '\\')) drive_path
++;
1160 drive_root
= !*drive_path
;
1162 lstrcpynA( buffer
, info
->path
, sizeof(buffer
) - 1 );
1163 strcat( buffer
, "/" );
1164 p
= buffer
+ strlen(buffer
);
1166 while (DOSFS_ReadDir( info
->dir
, &long_name
, &short_name
))
1170 /* Don't return '.' and '..' in the root of the drive */
1171 if (drive_root
&& (long_name
[0] == '.') &&
1172 (!long_name
[1] || ((long_name
[1] == '.') && !long_name
[2])))
1175 /* Check the long mask */
1177 if (info
->long_mask
)
1179 if (!DOSFS_MatchLong( info
->long_mask
, long_name
,
1180 flags
& DRIVE_CASE_SENSITIVE
)) continue;
1183 /* Check the short mask */
1185 if (info
->short_mask
)
1189 DOSFS_Hash( long_name
, dos_name
, TRUE
,
1190 !(flags
& DRIVE_CASE_SENSITIVE
) );
1191 short_name
= dos_name
;
1193 if (!DOSFS_MatchShort( info
->short_mask
, short_name
)) continue;
1196 /* Check the file attributes */
1198 lstrcpynA( p
, long_name
, sizeof(buffer
) - (int)(p
- buffer
) );
1199 if (!FILE_Stat( buffer
, &fileinfo
))
1201 WARN(dosfs
, "can't stat %s\n", buffer
);
1204 if (fileinfo
.dwFileAttributes
& ~attr
) continue;
1206 /* We now have a matching entry; fill the result and return */
1208 entry
->dwFileAttributes
= fileinfo
.dwFileAttributes
;
1209 entry
->ftCreationTime
= fileinfo
.ftCreationTime
;
1210 entry
->ftLastAccessTime
= fileinfo
.ftLastAccessTime
;
1211 entry
->ftLastWriteTime
= fileinfo
.ftLastWriteTime
;
1212 entry
->nFileSizeHigh
= fileinfo
.nFileSizeHigh
;
1213 entry
->nFileSizeLow
= fileinfo
.nFileSizeLow
;
1216 DOSFS_ToDosDTAFormat( short_name
, entry
->cAlternateFileName
);
1218 DOSFS_Hash( long_name
, entry
->cAlternateFileName
, FALSE
,
1219 !(flags
& DRIVE_CASE_SENSITIVE
) );
1221 lstrcpynA( entry
->cFileName
, long_name
, sizeof(entry
->cFileName
) );
1222 if (!(flags
& DRIVE_CASE_PRESERVING
)) CharLowerA( entry
->cFileName
);
1223 TRACE(dosfs
, "returning %s (%s) %02lx %ld\n",
1224 entry
->cFileName
, entry
->cAlternateFileName
,
1225 entry
->dwFileAttributes
, entry
->nFileSizeLow
);
1228 return 0; /* End of directory */
1231 /***********************************************************************
1234 * Find the next matching file. Return the number of entries read to find
1235 * the matching one, or 0 if no more entries.
1236 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
1237 * file name mask. Either or both can be NULL.
1239 * NOTE: This is supposed to be only called by the int21 emulation
1240 * routines. Thus, we should own the Win16Mutex anyway.
1241 * Nevertheless, we explicitly enter it to ensure the static
1242 * directory cache is protected.
1244 int DOSFS_FindNext( const char *path
, const char *short_mask
,
1245 const char *long_mask
, int drive
, BYTE attr
,
1246 int skip
, WIN32_FIND_DATAA
*entry
)
1248 static FIND_FIRST_INFO info
= { NULL
};
1249 LPCSTR short_name
, long_name
;
1252 SYSLEVEL_EnterWin16Lock();
1254 /* Check the cached directory */
1255 if (!(info
.dir
&& info
.path
== path
&& info
.short_mask
== short_mask
1256 && info
.long_mask
== long_mask
&& info
.drive
== drive
1257 && info
.attr
== attr
&& info
.cur_pos
<= skip
))
1259 /* Not in the cache, open it anew */
1260 if (info
.dir
) DOSFS_CloseDir( info
.dir
);
1262 info
.path
= (LPSTR
)path
;
1263 info
.long_mask
= (LPSTR
)long_mask
;
1264 info
.short_mask
= (LPSTR
)short_mask
;
1268 info
.dir
= DOSFS_OpenDir( info
.path
);
1271 /* Skip to desired position */
1272 while (info
.cur_pos
< skip
)
1273 if (info
.dir
&& DOSFS_ReadDir( info
.dir
, &long_name
, &short_name
))
1278 if (info
.dir
&& info
.cur_pos
== skip
&& DOSFS_FindNextEx( &info
, entry
))
1279 count
= info
.cur_pos
- skip
;
1285 if (info
.dir
) DOSFS_CloseDir( info
.dir
);
1286 memset( &info
, '\0', sizeof(info
) );
1289 SYSLEVEL_LeaveWin16Lock();
1296 /*************************************************************************
1297 * FindFirstFile16 (KERNEL.413)
1299 HANDLE16 WINAPI
FindFirstFile16( LPCSTR path
, WIN32_FIND_DATAA
*data
)
1301 DOS_FULL_NAME full_name
;
1303 FIND_FIRST_INFO
*info
;
1305 data
->dwReserved0
= data
->dwReserved1
= 0x0;
1306 if (!path
) return 0;
1307 if (!DOSFS_GetFullName( path
, FALSE
, &full_name
))
1308 return INVALID_HANDLE_VALUE16
;
1309 if (!(handle
= GlobalAlloc16( GMEM_MOVEABLE
, sizeof(FIND_FIRST_INFO
) )))
1310 return INVALID_HANDLE_VALUE16
;
1311 info
= (FIND_FIRST_INFO
*)GlobalLock16( handle
);
1312 info
->path
= HEAP_strdupA( SystemHeap
, 0, full_name
.long_name
);
1313 info
->long_mask
= strrchr( info
->path
, '/' );
1314 *(info
->long_mask
++) = '\0';
1315 info
->short_mask
= NULL
;
1317 if (path
[0] && (path
[1] == ':')) info
->drive
= toupper(*path
) - 'A';
1318 else info
->drive
= DRIVE_GetCurrentDrive();
1321 info
->dir
= DOSFS_OpenDir( info
->path
);
1323 GlobalUnlock16( handle
);
1324 if (!FindNextFile16( handle
, data
))
1326 FindClose16( handle
);
1327 SetLastError( ERROR_NO_MORE_FILES
);
1328 return INVALID_HANDLE_VALUE16
;
1334 /*************************************************************************
1335 * FindFirstFile32A (KERNEL32.123)
1337 HANDLE WINAPI
FindFirstFileA( LPCSTR path
, WIN32_FIND_DATAA
*data
)
1339 HANDLE handle
= FindFirstFile16( path
, data
);
1340 if (handle
== INVALID_HANDLE_VALUE16
) return INVALID_HANDLE_VALUE
;
1345 /*************************************************************************
1346 * FindFirstFile32W (KERNEL32.124)
1348 HANDLE WINAPI
FindFirstFileW( LPCWSTR path
, WIN32_FIND_DATAW
*data
)
1350 WIN32_FIND_DATAA dataA
;
1351 LPSTR pathA
= HEAP_strdupWtoA( GetProcessHeap(), 0, path
);
1352 HANDLE handle
= FindFirstFileA( pathA
, &dataA
);
1353 HeapFree( GetProcessHeap(), 0, pathA
);
1354 if (handle
!= INVALID_HANDLE_VALUE
)
1356 data
->dwFileAttributes
= dataA
.dwFileAttributes
;
1357 data
->ftCreationTime
= dataA
.ftCreationTime
;
1358 data
->ftLastAccessTime
= dataA
.ftLastAccessTime
;
1359 data
->ftLastWriteTime
= dataA
.ftLastWriteTime
;
1360 data
->nFileSizeHigh
= dataA
.nFileSizeHigh
;
1361 data
->nFileSizeLow
= dataA
.nFileSizeLow
;
1362 lstrcpyAtoW( data
->cFileName
, dataA
.cFileName
);
1363 lstrcpyAtoW( data
->cAlternateFileName
, dataA
.cAlternateFileName
);
1369 /*************************************************************************
1370 * FindNextFile16 (KERNEL.414)
1372 BOOL16 WINAPI
FindNextFile16( HANDLE16 handle
, WIN32_FIND_DATAA
*data
)
1374 FIND_FIRST_INFO
*info
;
1376 if (!(info
= (FIND_FIRST_INFO
*)GlobalLock16( handle
)))
1378 SetLastError( ERROR_INVALID_HANDLE
);
1381 GlobalUnlock16( handle
);
1382 if (!info
->path
|| !info
->dir
)
1384 SetLastError( ERROR_NO_MORE_FILES
);
1387 if (!DOSFS_FindNextEx( info
, data
))
1389 DOSFS_CloseDir( info
->dir
); info
->dir
= NULL
;
1390 HeapFree( SystemHeap
, 0, info
->path
);
1391 info
->path
= info
->long_mask
= NULL
;
1392 SetLastError( ERROR_NO_MORE_FILES
);
1399 /*************************************************************************
1400 * FindNextFile32A (KERNEL32.126)
1402 BOOL WINAPI
FindNextFileA( HANDLE handle
, WIN32_FIND_DATAA
*data
)
1404 return FindNextFile16( handle
, data
);
1408 /*************************************************************************
1409 * FindNextFile32W (KERNEL32.127)
1411 BOOL WINAPI
FindNextFileW( HANDLE handle
, WIN32_FIND_DATAW
*data
)
1413 WIN32_FIND_DATAA dataA
;
1414 if (!FindNextFileA( handle
, &dataA
)) return FALSE
;
1415 data
->dwFileAttributes
= dataA
.dwFileAttributes
;
1416 data
->ftCreationTime
= dataA
.ftCreationTime
;
1417 data
->ftLastAccessTime
= dataA
.ftLastAccessTime
;
1418 data
->ftLastWriteTime
= dataA
.ftLastWriteTime
;
1419 data
->nFileSizeHigh
= dataA
.nFileSizeHigh
;
1420 data
->nFileSizeLow
= dataA
.nFileSizeLow
;
1421 lstrcpyAtoW( data
->cFileName
, dataA
.cFileName
);
1422 lstrcpyAtoW( data
->cAlternateFileName
, dataA
.cAlternateFileName
);
1427 /*************************************************************************
1428 * FindClose16 (KERNEL.415)
1430 BOOL16 WINAPI
FindClose16( HANDLE16 handle
)
1432 FIND_FIRST_INFO
*info
;
1434 if ((handle
== INVALID_HANDLE_VALUE16
) ||
1435 !(info
= (FIND_FIRST_INFO
*)GlobalLock16( handle
)))
1437 SetLastError( ERROR_INVALID_HANDLE
);
1440 if (info
->dir
) DOSFS_CloseDir( info
->dir
);
1441 if (info
->path
) HeapFree( SystemHeap
, 0, info
->path
);
1442 GlobalUnlock16( handle
);
1443 GlobalFree16( handle
);
1448 /*************************************************************************
1449 * FindClose32 (KERNEL32.119)
1451 BOOL WINAPI
FindClose( HANDLE handle
)
1453 return FindClose16( (HANDLE16
)handle
);
1457 /***********************************************************************
1458 * DOSFS_UnixTimeToFileTime
1460 * Convert a Unix time to FILETIME format.
1461 * The FILETIME structure is a 64-bit value representing the number of
1462 * 100-nanosecond intervals since January 1, 1601, 0:00.
1463 * 'remainder' is the nonnegative number of 100-ns intervals
1464 * corresponding to the time fraction smaller than 1 second that
1465 * couldn't be stored in the time_t value.
1467 void DOSFS_UnixTimeToFileTime( time_t unix_time
, FILETIME
*filetime
,
1473 The time difference between 1 January 1601, 00:00:00 and
1474 1 January 1970, 00:00:00 is 369 years, plus the leap years
1475 from 1604 to 1968, excluding 1700, 1800, 1900.
1476 This makes (1968 - 1600) / 4 - 3 = 89 leap days, and a total
1479 Any day in that period had 24 * 60 * 60 = 86400 seconds.
1481 The time difference is 134774 * 86400 * 10000000, which can be written
1483 27111902 * 2^32 + 3577643008
1484 413 * 2^48 + 45534 * 2^32 + 54590 * 2^16 + 32768
1486 If you find that these constants are buggy, please change them in all
1487 instances in both conversion functions.
1490 There are two versions, one of them uses long long variables and
1491 is presumably faster but not ISO C. The other one uses standard C
1492 data types and operations but relies on the assumption that negative
1493 numbers are stored as 2's complement (-1 is 0xffff....). If this
1494 assumption is violated, dates before 1970 will not convert correctly.
1495 This should however work on any reasonable architecture where WINE
1500 Take care not to remove the casts. I have tested these functions
1501 (in both versions) for a lot of numbers. I would be interested in
1502 results on other compilers than GCC.
1504 The operations have been designed to account for the possibility
1505 of 64-bit time_t in future UNICES. Even the versions without
1506 internal long long numbers will work if time_t only is 64 bit.
1507 A 32-bit shift, which was necessary for that operation, turned out
1508 not to work correctly in GCC, besides giving the warning. So I
1509 used a double 16-bit shift instead. Numbers are in the ISO version
1510 represented by three limbs, the most significant with 32 bit, the
1511 other two with 16 bit each.
1513 As the modulo-operator % is not well-defined for negative numbers,
1514 negative divisors have been avoided in DOSFS_FileTimeToUnixTime.
1516 There might be quicker ways to do this in C. Certainly so in
1519 Claus Fischer, fischer@iue.tuwien.ac.at
1522 #if (SIZEOF_LONG_LONG >= 8)
1523 # define USE_LONG_LONG 1
1525 # define USE_LONG_LONG 0
1528 #if USE_LONG_LONG /* gcc supports long long type */
1530 long long int t
= unix_time
;
1532 t
+= 116444736000000000LL;
1534 filetime
->dwLowDateTime
= (UINT
)t
;
1535 filetime
->dwHighDateTime
= (UINT
)(t
>> 32);
1537 #else /* ISO version */
1539 UINT a0
; /* 16 bit, low bits */
1540 UINT a1
; /* 16 bit, medium bits */
1541 UINT a2
; /* 32 bit, high bits */
1543 /* Copy the unix time to a2/a1/a0 */
1544 a0
= unix_time
& 0xffff;
1545 a1
= (unix_time
>> 16) & 0xffff;
1546 /* This is obsolete if unix_time is only 32 bits, but it does not hurt.
1547 Do not replace this by >> 32, it gives a compiler warning and it does
1549 a2
= (unix_time
>= 0 ? (unix_time
>> 16) >> 16 :
1550 ~((~unix_time
>> 16) >> 16));
1552 /* Multiply a by 10000000 (a = a2/a1/a0)
1553 Split the factor into 10000 * 1000 which are both less than 0xffff. */
1555 a1
= a1
* 10000 + (a0
>> 16);
1556 a2
= a2
* 10000 + (a1
>> 16);
1561 a1
= a1
* 1000 + (a0
>> 16);
1562 a2
= a2
* 1000 + (a1
>> 16);
1566 /* Add the time difference and the remainder */
1567 a0
+= 32768 + (remainder
& 0xffff);
1568 a1
+= 54590 + (remainder
>> 16 ) + (a0
>> 16);
1569 a2
+= 27111902 + (a1
>> 16);
1574 filetime
->dwLowDateTime
= (a1
<< 16) + a0
;
1575 filetime
->dwHighDateTime
= a2
;
1580 /***********************************************************************
1581 * DOSFS_FileTimeToUnixTime
1583 * Convert a FILETIME format to Unix time.
1584 * If not NULL, 'remainder' contains the fractional part of the filetime,
1585 * in the range of [0..9999999] (even if time_t is negative).
1587 time_t DOSFS_FileTimeToUnixTime( const FILETIME
*filetime
, DWORD
*remainder
)
1589 /* Read the comment in the function DOSFS_UnixTimeToFileTime. */
1592 long long int t
= filetime
->dwHighDateTime
;
1594 t
+= (UINT
)filetime
->dwLowDateTime
;
1595 t
-= 116444736000000000LL;
1598 if (remainder
) *remainder
= 9999999 - (-t
- 1) % 10000000;
1599 return -1 - ((-t
- 1) / 10000000);
1603 if (remainder
) *remainder
= t
% 10000000;
1604 return t
/ 10000000;
1607 #else /* ISO version */
1609 UINT a0
; /* 16 bit, low bits */
1610 UINT a1
; /* 16 bit, medium bits */
1611 UINT a2
; /* 32 bit, high bits */
1612 UINT r
; /* remainder of division */
1613 unsigned int carry
; /* carry bit for subtraction */
1614 int negative
; /* whether a represents a negative value */
1616 /* Copy the time values to a2/a1/a0 */
1617 a2
= (UINT
)filetime
->dwHighDateTime
;
1618 a1
= ((UINT
)filetime
->dwLowDateTime
) >> 16;
1619 a0
= ((UINT
)filetime
->dwLowDateTime
) & 0xffff;
1621 /* Subtract the time difference */
1622 if (a0
>= 32768 ) a0
-= 32768 , carry
= 0;
1623 else a0
+= (1 << 16) - 32768 , carry
= 1;
1625 if (a1
>= 54590 + carry
) a1
-= 54590 + carry
, carry
= 0;
1626 else a1
+= (1 << 16) - 54590 - carry
, carry
= 1;
1628 a2
-= 27111902 + carry
;
1630 /* If a is negative, replace a by (-1-a) */
1631 negative
= (a2
>= ((UINT
)1) << 31);
1634 /* Set a to -a - 1 (a is a2/a1/a0) */
1640 /* Divide a by 10000000 (a = a2/a1/a0), put the rest into r.
1641 Split the divisor into 10000 * 1000 which are both less than 0xffff. */
1642 a1
+= (a2
% 10000) << 16;
1644 a0
+= (a1
% 10000) << 16;
1649 a1
+= (a2
% 1000) << 16;
1651 a0
+= (a1
% 1000) << 16;
1653 r
+= (a0
% 1000) * 10000;
1656 /* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
1659 /* Set a to -a - 1 (a is a2/a1/a0) */
1667 if (remainder
) *remainder
= r
;
1669 /* Do not replace this by << 32, it gives a compiler warning and it does
1671 return ((((time_t)a2
) << 16) << 16) + (a1
<< 16) + a0
;
1676 /***********************************************************************
1677 * DosDateTimeToFileTime (KERNEL32.76)
1679 BOOL WINAPI
DosDateTimeToFileTime( WORD fatdate
, WORD fattime
, LPFILETIME ft
)
1683 newtm
.tm_sec
= (fattime
& 0x1f) * 2;
1684 newtm
.tm_min
= (fattime
>> 5) & 0x3f;
1685 newtm
.tm_hour
= (fattime
>> 11);
1686 newtm
.tm_mday
= (fatdate
& 0x1f);
1687 newtm
.tm_mon
= ((fatdate
>> 5) & 0x0f) - 1;
1688 newtm
.tm_year
= (fatdate
>> 9) + 80;
1689 DOSFS_UnixTimeToFileTime( mktime( &newtm
), ft
, 0 );
1694 /***********************************************************************
1695 * FileTimeToDosDateTime (KERNEL32.111)
1697 BOOL WINAPI
FileTimeToDosDateTime( const FILETIME
*ft
, LPWORD fatdate
,
1700 time_t unixtime
= DOSFS_FileTimeToUnixTime( ft
, NULL
);
1701 struct tm
*tm
= localtime( &unixtime
);
1703 *fattime
= (tm
->tm_hour
<< 11) + (tm
->tm_min
<< 5) + (tm
->tm_sec
/ 2);
1705 *fatdate
= ((tm
->tm_year
- 80) << 9) + ((tm
->tm_mon
+ 1) << 5)
1711 /***********************************************************************
1712 * LocalFileTimeToFileTime (KERNEL32.373)
1714 BOOL WINAPI
LocalFileTimeToFileTime( const FILETIME
*localft
,
1720 /* convert from local to UTC. Perhaps not correct. FIXME */
1721 time_t unixtime
= DOSFS_FileTimeToUnixTime( localft
, &remainder
);
1722 xtm
= gmtime( &unixtime
);
1723 DOSFS_UnixTimeToFileTime( mktime(xtm
), utcft
, remainder
);
1728 /***********************************************************************
1729 * FileTimeToLocalFileTime (KERNEL32.112)
1731 BOOL WINAPI
FileTimeToLocalFileTime( const FILETIME
*utcft
,
1732 LPFILETIME localft
)
1735 /* convert from UTC to local. Perhaps not correct. FIXME */
1736 time_t unixtime
= DOSFS_FileTimeToUnixTime( utcft
, &remainder
);
1738 struct tm
*xtm
= localtime( &unixtime
);
1741 localtime
= timegm(xtm
);
1742 DOSFS_UnixTimeToFileTime( localtime
, localft
, remainder
);
1745 struct tm
*xtm
,*gtm
;
1748 xtm
= localtime( &unixtime
);
1749 gtm
= gmtime( &unixtime
);
1750 time1
= mktime(xtm
);
1751 time2
= mktime(gtm
);
1752 DOSFS_UnixTimeToFileTime( 2*time1
-time2
, localft
, remainder
);
1758 /***********************************************************************
1759 * FileTimeToSystemTime (KERNEL32.113)
1761 BOOL WINAPI
FileTimeToSystemTime( const FILETIME
*ft
, LPSYSTEMTIME syst
)
1765 time_t xtime
= DOSFS_FileTimeToUnixTime( ft
, &remainder
);
1766 xtm
= gmtime(&xtime
);
1767 syst
->wYear
= xtm
->tm_year
+1900;
1768 syst
->wMonth
= xtm
->tm_mon
+ 1;
1769 syst
->wDayOfWeek
= xtm
->tm_wday
;
1770 syst
->wDay
= xtm
->tm_mday
;
1771 syst
->wHour
= xtm
->tm_hour
;
1772 syst
->wMinute
= xtm
->tm_min
;
1773 syst
->wSecond
= xtm
->tm_sec
;
1774 syst
->wMilliseconds
= remainder
/ 10000;
1778 /***********************************************************************
1779 * QueryDosDeviceA (KERNEL32.413)
1781 * returns array of strings terminated by \0, terminated by \0
1783 DWORD WINAPI
QueryDosDeviceA(LPCSTR devname
,LPSTR target
,DWORD bufsize
)
1788 TRACE(dosfs
,"(%s,...)\n",devname
?devname
:"<null>");
1790 /* return known MSDOS devices */
1791 lstrcpyA(buffer
,"CON COM1 COM2 LPT1 NUL ");
1792 while ((s
=strchr(buffer
,' ')))
1795 lstrcpynA(target
,buffer
,bufsize
);
1796 return strlen(buffer
);
1798 lstrcpyA(buffer
,"\\DEV\\");
1799 lstrcatA(buffer
,devname
);
1800 if ((s
=strchr(buffer
,':'))) *s
='\0';
1801 lstrcpynA(target
,buffer
,bufsize
);
1802 return strlen(buffer
);
1806 /***********************************************************************
1807 * QueryDosDeviceW (KERNEL32.414)
1809 * returns array of strings terminated by \0, terminated by \0
1811 DWORD WINAPI
QueryDosDeviceW(LPCWSTR devname
,LPWSTR target
,DWORD bufsize
)
1813 LPSTR devnameA
= devname
?HEAP_strdupWtoA(GetProcessHeap(),0,devname
):NULL
;
1814 LPSTR targetA
= (LPSTR
)HEAP_xalloc(GetProcessHeap(),0,bufsize
);
1815 DWORD ret
= QueryDosDeviceA(devnameA
,targetA
,bufsize
);
1817 lstrcpynAtoW(target
,targetA
,bufsize
);
1818 if (devnameA
) HeapFree(GetProcessHeap(),0,devnameA
);
1819 if (targetA
) HeapFree(GetProcessHeap(),0,targetA
);
1824 /***********************************************************************
1825 * SystemTimeToFileTime (KERNEL32.526)
1827 BOOL WINAPI
SystemTimeToFileTime( const SYSTEMTIME
*syst
, LPFILETIME ft
)
1833 struct tm xtm
,*local_tm
,*utc_tm
;
1834 time_t localtim
,utctime
;
1837 xtm
.tm_year
= syst
->wYear
-1900;
1838 xtm
.tm_mon
= syst
->wMonth
- 1;
1839 xtm
.tm_wday
= syst
->wDayOfWeek
;
1840 xtm
.tm_mday
= syst
->wDay
;
1841 xtm
.tm_hour
= syst
->wHour
;
1842 xtm
.tm_min
= syst
->wMinute
;
1843 xtm
.tm_sec
= syst
->wSecond
; /* this is UTC */
1846 utctime
= timegm(&xtm
);
1847 DOSFS_UnixTimeToFileTime( utctime
, ft
,
1848 syst
->wMilliseconds
* 10000 );
1850 localtim
= mktime(&xtm
); /* now we've got local time */
1851 local_tm
= localtime(&localtim
);
1852 utc_tm
= gmtime(&localtim
);
1853 utctime
= mktime(utc_tm
);
1854 DOSFS_UnixTimeToFileTime( 2*localtim
-utctime
, ft
,
1855 syst
->wMilliseconds
* 10000 );
1860 BOOL WINAPI
DefineDosDeviceA(DWORD flags
,LPCSTR devname
,LPCSTR targetpath
) {
1861 FIXME(dosfs
,"(0x%08lx,%s,%s),stub!\n",flags
,devname
,targetpath
);
1862 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);