2 * DOS file system functions
4 * Copyright 1993 Erik Bos
5 * Copyright 1996 Alexandre Julliard
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 #include "wine/port.h"
25 #include <sys/types.h>
29 #ifdef HAVE_SYS_ERRNO_H
30 #include <sys/errno.h>
37 #ifdef HAVE_SYS_IOCTL_H
38 #include <sys/ioctl.h>
40 #ifdef HAVE_LINUX_IOCTL_H
41 #include <linux/ioctl.h>
48 #define NONAMELESSUNION
49 #define NONAMELESSSTRUCT
56 #include "wine/unicode.h"
57 #include "wine/winbase16.h"
61 #include "wine/server.h"
62 #include "wine/exception.h"
67 #include "wine/debug.h"
69 WINE_DEFAULT_DEBUG_CHANNEL(dosfs
);
70 WINE_DECLARE_DEBUG_CHANNEL(file
);
72 /* Define the VFAT ioctl to get both short and long file names */
73 /* FIXME: is it possible to get this to work on other systems? */
75 /* We want the real kernel dirent structure, not the libc one */
80 unsigned short d_reclen
;
84 #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, KERNEL_DIRENT [2] )
86 /* To avoid blocking on non-directories in DOSFS_OpenDir_VFAT*/
88 # define O_DIRECTORY 0200000 /* must be directory */
92 #undef VFAT_IOCTL_READDIR_BOTH /* just in case... */
95 /* Chars we don't want to see in DOS file names */
96 #define INVALID_DOS_CHARS "*?<>|\"+=,;[] \345"
98 /* at some point we may want to allow Winelib apps to set this */
99 static const BOOL is_case_sensitive
= FALSE
;
102 * Directory info for DOSFS_ReadDir
103 * contains the names of *all* the files in the directory
113 /* return non-zero if c is the end of a directory name */
114 static inline int is_end_of_name(WCHAR c
)
116 return !c
|| (c
== '/') || (c
== '\\');
119 /***********************************************************************
122 * Return 1 if Unix file 'name' is also a valid MS-DOS name
123 * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
124 * File name can be terminated by '\0', '\\' or '/'.
126 static int DOSFS_ValidDOSName( LPCWSTR name
)
128 static const char invalid_chars
[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS
;
129 const WCHAR
*p
= name
;
130 const char *invalid
= !is_case_sensitive
? (invalid_chars
+ 26) : invalid_chars
;
135 /* Check for "." and ".." */
138 /* All other names beginning with '.' are invalid */
139 return (is_end_of_name(*p
));
141 while (!is_end_of_name(*p
))
143 if (*p
< 256 && strchr( invalid
, (char)*p
)) return 0; /* Invalid char */
144 if (*p
== '.') break; /* Start of the extension */
145 if (++len
> 8) return 0; /* Name too long */
148 if (*p
!= '.') return 1; /* End of name */
150 if (is_end_of_name(*p
)) return 0; /* Empty extension not allowed */
152 while (!is_end_of_name(*p
))
154 if (*p
< 256 && strchr( invalid
, (char)*p
)) return 0; /* Invalid char */
155 if (*p
== '.') return 0; /* Second extension not allowed */
156 if (++len
> 3) return 0; /* Extension too long */
163 /***********************************************************************
164 * DOSFS_ToDosFCBFormat
166 * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
167 * expanding wild cards and converting to upper-case in the process.
168 * File name can be terminated by '\0', '\\' or '/'.
169 * Return FALSE if the name is not a valid DOS name.
170 * 'buffer' must be at least 12 characters long.
172 static BOOL
DOSFS_ToDosFCBFormat( LPCWSTR name
, LPWSTR buffer
)
174 static const char invalid_chars
[] = INVALID_DOS_CHARS
;
178 /* Check for "." and ".." */
183 for(i
= 1; i
< 11; i
++) buffer
[i
] = ' ';
190 return (!*p
|| (*p
== '/') || (*p
== '\\'));
193 for (i
= 0; i
< 8; i
++)
210 if (*p
< 256 && strchr( invalid_chars
, (char)*p
)) return FALSE
;
211 buffer
[i
] = toupperW(*p
);
219 /* Skip all chars after wildcard up to first dot */
220 while (*p
&& (*p
!= '/') && (*p
!= '\\') && (*p
!= '.')) p
++;
224 /* Check if name too long */
225 if (*p
&& (*p
!= '/') && (*p
!= '\\') && (*p
!= '.')) return FALSE
;
227 if (*p
== '.') p
++; /* Skip dot */
229 for (i
= 8; i
< 11; i
++)
239 return FALSE
; /* Second extension not allowed */
247 if (*p
< 256 && strchr( invalid_chars
, (char)*p
)) return FALSE
;
248 buffer
[i
] = toupperW(*p
);
255 /* at most 3 character of the extension are processed
256 * is something behind this ?
258 while (*p
== '*' || *p
== ' ') p
++; /* skip wildcards and spaces */
259 return is_end_of_name(*p
);
263 /***********************************************************************
264 * DOSFS_ToDosDTAFormat
266 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
267 * converting to upper-case in the process.
268 * File name can be terminated by '\0', '\\' or '/'.
269 * 'buffer' must be at least 13 characters long.
271 static void DOSFS_ToDosDTAFormat( LPCWSTR name
, LPWSTR buffer
)
275 memcpy( buffer
, name
, 8 * sizeof(WCHAR
) );
277 while ((p
> buffer
) && (p
[-1] == ' ')) p
--;
279 memcpy( p
, name
+ 8, 3 * sizeof(WCHAR
) );
281 while (p
[-1] == ' ') p
--;
282 if (p
[-1] == '.') p
--;
287 /***********************************************************************
290 * Used to construct an array of filenames in DOSFS_OpenDir
292 static BOOL
DOSFS_AddDirEntry(DOS_DIR
**dir
, LPCWSTR name
, LPCWSTR dosname
)
294 int extra1
= strlenW(name
) + 1;
295 int extra2
= strlenW(dosname
) + 1;
297 /* if we need more, at minimum double the size */
298 if( (extra1
+ extra2
+ (*dir
)->used
) > (*dir
)->size
)
300 int more
= (*dir
)->size
;
303 if(more
<(extra1
+extra2
))
304 more
= extra1
+extra2
;
306 t
= HeapReAlloc(GetProcessHeap(), 0, *dir
, sizeof(**dir
) +
307 ((*dir
)->size
+ more
)*sizeof(WCHAR
) );
310 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
311 ERR("Out of memory caching directory structure %d %d %d\n",
312 (*dir
)->size
, more
, (*dir
)->used
);
316 (*dir
)->size
+= more
;
319 /* at this point, the dir structure is big enough to hold these names */
320 strcpyW(&(*dir
)->names
[(*dir
)->used
], name
);
321 (*dir
)->used
+= extra1
;
322 strcpyW(&(*dir
)->names
[(*dir
)->used
], dosname
);
323 (*dir
)->used
+= extra2
;
329 /***********************************************************************
332 static BOOL
DOSFS_OpenDir_VFAT(DOS_DIR
**dir
, const char *unix_path
)
334 #ifdef VFAT_IOCTL_READDIR_BOTH
336 int fd
= open( unix_path
, O_RDONLY
|O_DIRECTORY
);
339 /* Check if the VFAT ioctl is supported on this directory */
346 WCHAR long_name
[MAX_PATH
];
347 WCHAR short_name
[12];
349 r
= (ioctl( fd
, VFAT_IOCTL_READDIR_BOTH
, (long)de
) != -1);
354 MultiByteToWideChar(CP_UNIXCP
, 0, de
[0].d_name
, -1, long_name
, MAX_PATH
);
355 if (!DOSFS_ToDosFCBFormat( long_name
, short_name
))
356 short_name
[0] = '\0';
358 MultiByteToWideChar(CP_UNIXCP
, 0, de
[1].d_name
, -1, long_name
, MAX_PATH
);
360 MultiByteToWideChar(CP_UNIXCP
, 0, de
[0].d_name
, -1, long_name
, MAX_PATH
);
361 r
= DOSFS_AddDirEntry(dir
, long_name
, short_name
);
367 static const WCHAR empty_strW
[] = { 0 };
368 DOSFS_AddDirEntry(dir
, empty_strW
, empty_strW
);
374 #endif /* VFAT_IOCTL_READDIR_BOTH */
378 /***********************************************************************
379 * DOSFS_OpenDir_Normal
381 * Now use the standard opendir/readdir interface
383 static BOOL
DOSFS_OpenDir_Normal( DOS_DIR
**dir
, const char *unix_path
)
385 DIR *unixdir
= opendir( unix_path
);
387 static const WCHAR empty_strW
[] = { 0 };
393 WCHAR long_name
[MAX_PATH
];
394 struct dirent
*de
= readdir(unixdir
);
398 MultiByteToWideChar(CP_UNIXCP
, 0, de
->d_name
, -1, long_name
, MAX_PATH
);
399 r
= DOSFS_AddDirEntry(dir
, long_name
, empty_strW
);
404 DOSFS_AddDirEntry(dir
, empty_strW
, empty_strW
);
409 /***********************************************************************
412 static DOS_DIR
*DOSFS_OpenDir( const char *unix_path
)
414 const int init_size
= 0x100;
415 DOS_DIR
*dir
= HeapAlloc( GetProcessHeap(), 0, sizeof(*dir
) + init_size
*sizeof (WCHAR
));
418 TRACE("%s\n",debugstr_a(unix_path
));
422 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
426 dir
->size
= init_size
;
428 /* Treat empty path as root directory. This simplifies path split into
429 directory and mask in several other places */
430 if (!*unix_path
) unix_path
= "/";
432 r
= DOSFS_OpenDir_VFAT( &dir
, unix_path
);
435 r
= DOSFS_OpenDir_Normal( &dir
, unix_path
);
439 HeapFree(GetProcessHeap(), 0, dir
);
448 /***********************************************************************
451 static void DOSFS_CloseDir( DOS_DIR
*dir
)
453 HeapFree( GetProcessHeap(), 0, dir
);
457 /***********************************************************************
460 static BOOL
DOSFS_ReadDir( DOS_DIR
*dir
, LPCWSTR
*long_name
,
461 LPCWSTR
*short_name
)
468 /* the long pathname is first */
469 ln
= &dir
->names
[dir
->used
];
474 dir
->used
+= (strlenW(ln
) + 1);
476 /* followed by the short path name */
477 sn
= &dir
->names
[dir
->used
];
482 dir
->used
+= (strlenW(sn
) + 1);
488 /***********************************************************************
491 * Transform a Unix file name into a hashed DOS name. If the name is a valid
492 * DOS name, it is converted to upper-case; otherwise it is replaced by a
493 * hashed version that fits in 8.3 format.
494 * File name can be terminated by '\0', '\\' or '/'.
495 * 'buffer' must be at least 13 characters long.
497 static void DOSFS_Hash( LPCWSTR name
, LPWSTR buffer
, BOOL dir_format
)
499 static const char invalid_chars
[] = INVALID_DOS_CHARS
"~.";
500 static const char hash_chars
[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
509 for(i
= 0; i
< 11; i
++) buffer
[i
] = ' ';
513 if (DOSFS_ValidDOSName( name
))
515 /* Check for '.' and '..' */
519 if (!dir_format
) buffer
[1] = buffer
[2] = '\0';
520 if (name
[1] == '.') buffer
[1] = '.';
524 /* Simply copy the name, converting to uppercase */
526 for (dst
= buffer
; !is_end_of_name(*name
) && (*name
!= '.'); name
++)
527 *dst
++ = toupperW(*name
);
530 if (dir_format
) dst
= buffer
+ 8;
532 for (name
++; !is_end_of_name(*name
); name
++)
533 *dst
++ = toupperW(*name
);
535 if (!dir_format
) *dst
= '\0';
539 /* Compute the hash code of the file name */
540 /* If you know something about hash functions, feel free to */
541 /* insert a better algorithm here... */
542 if (!is_case_sensitive
)
544 for (p
= name
, hash
= 0xbeef; !is_end_of_name(p
[1]); p
++)
545 hash
= (hash
<<3) ^ (hash
>>5) ^ tolowerW(*p
) ^ (tolowerW(p
[1]) << 8);
546 hash
= (hash
<<3) ^ (hash
>>5) ^ tolowerW(*p
); /* Last character */
550 for (p
= name
, hash
= 0xbeef; !is_end_of_name(p
[1]); p
++)
551 hash
= (hash
<< 3) ^ (hash
>> 5) ^ *p
^ (p
[1] << 8);
552 hash
= (hash
<< 3) ^ (hash
>> 5) ^ *p
; /* Last character */
555 /* Find last dot for start of the extension */
556 for (p
= name
+1, ext
= NULL
; !is_end_of_name(*p
); p
++)
557 if (*p
== '.') ext
= p
;
558 if (ext
&& is_end_of_name(ext
[1]))
559 ext
= NULL
; /* Empty extension ignored */
561 /* Copy first 4 chars, replacing invalid chars with '_' */
562 for (i
= 4, p
= name
, dst
= buffer
; i
> 0; i
--, p
++)
564 if (is_end_of_name(*p
) || (p
== ext
)) break;
565 *dst
++ = (*p
< 256 && strchr( invalid_chars
, (char)*p
)) ? '_' : toupperW(*p
);
567 /* Pad to 5 chars with '~' */
568 while (i
-- >= 0) *dst
++ = '~';
570 /* Insert hash code converted to 3 ASCII chars */
571 *dst
++ = hash_chars
[(hash
>> 10) & 0x1f];
572 *dst
++ = hash_chars
[(hash
>> 5) & 0x1f];
573 *dst
++ = hash_chars
[hash
& 0x1f];
575 /* Copy the first 3 chars of the extension (if any) */
578 if (!dir_format
) *dst
++ = '.';
579 for (i
= 3, ext
++; (i
> 0) && !is_end_of_name(*ext
); i
--, ext
++)
580 *dst
++ = (*ext
< 256 && strchr( invalid_chars
, (char)*ext
)) ? '_' : toupperW(*ext
);
582 if (!dir_format
) *dst
= '\0';
586 /***********************************************************************
589 * Find the Unix file name in a given directory that corresponds to
590 * a file name (either in Unix or DOS format).
591 * File name can be terminated by '\0', '\\' or '/'.
592 * Return TRUE if OK, FALSE if no file name matches.
594 * 'long_buf' must be at least 'long_len' characters long. If the long name
595 * turns out to be larger than that, the function returns FALSE.
596 * 'short_buf' must be at least 13 characters long.
598 BOOL
DOSFS_FindUnixName( const DOS_FULL_NAME
*path
, LPCWSTR name
, char *long_buf
,
599 INT long_len
, LPWSTR short_buf
)
602 LPCWSTR long_name
, short_name
;
603 WCHAR dos_name
[12], tmp_buf
[13];
606 LPCWSTR p
= strchrW( name
, '/' );
607 int len
= p
? (int)(p
- name
) : strlenW(name
);
608 if ((p
= strchrW( name
, '\\' ))) len
= min( (int)(p
- name
), len
);
609 /* Ignore trailing dots and spaces */
610 while (len
> 1 && (name
[len
-1] == '.' || name
[len
-1] == ' ')) len
--;
611 if (long_len
< len
+ 1) return FALSE
;
613 TRACE("%s,%s\n", path
->long_name
, debugstr_w(name
) );
615 if (!DOSFS_ToDosFCBFormat( name
, dos_name
)) dos_name
[0] = '\0';
617 if (!(dir
= DOSFS_OpenDir( path
->long_name
)))
619 WARN("(%s,%s): can't open dir: %s\n",
620 path
->long_name
, debugstr_w(name
), strerror(errno
) );
624 while ((ret
= DOSFS_ReadDir( dir
, &long_name
, &short_name
)))
626 /* Check against Unix name */
627 if (len
== strlenW(long_name
))
629 if (is_case_sensitive
)
631 if (!strncmpW( long_name
, name
, len
)) break;
635 if (!strncmpiW( long_name
, name
, len
)) break;
640 /* Check against hashed DOS name */
643 DOSFS_Hash( long_name
, tmp_buf
, TRUE
);
644 short_name
= tmp_buf
;
646 if (!strcmpW( dos_name
, short_name
)) break;
651 if (long_buf
) WideCharToMultiByte(CP_UNIXCP
, 0, long_name
, -1, long_buf
, long_len
, NULL
, NULL
);
655 DOSFS_ToDosDTAFormat( short_name
, short_buf
);
657 DOSFS_Hash( long_name
, short_buf
, FALSE
);
659 TRACE("(%s,%s) -> %s (%s)\n", path
->long_name
, debugstr_w(name
),
660 debugstr_w(long_name
), short_buf
? debugstr_w(short_buf
) : "***");
663 WARN("%s not found in '%s'\n", debugstr_w(name
), path
->long_name
);
664 DOSFS_CloseDir( dir
);
669 /***********************************************************************
672 * Get the drive specified by a given path name (DOS or Unix format).
674 static int DOSFS_GetPathDrive( LPCWSTR
*name
)
679 if (*p
&& (p
[1] == ':'))
681 drive
= toupperW(*p
) - 'A';
684 else if (*p
== '/') /* Absolute Unix path? */
686 if ((drive
= DRIVE_FindDriveRootW( name
)) == -1)
688 MESSAGE("Warning: %s not accessible from a configured DOS drive\n", debugstr_w(*name
) );
689 /* Assume it really was a DOS name */
690 drive
= DRIVE_GetCurrentDrive();
693 else drive
= DRIVE_GetCurrentDrive();
695 if (!DRIVE_IsValid(drive
))
697 SetLastError( ERROR_INVALID_DRIVE
);
704 /***********************************************************************
707 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
708 * Unix name / short DOS name pair.
709 * Return FALSE if one of the path components does not exist. The last path
710 * component is only checked if 'check_last' is non-zero.
711 * The buffers pointed to by 'long_buf' and 'short_buf' must be
712 * at least MAX_PATHNAME_LEN long.
714 BOOL
DOSFS_GetFullName( LPCWSTR name
, BOOL check_last
, DOS_FULL_NAME
*full
)
719 static const WCHAR driveA_rootW
[] = {'A',':','\\',0};
720 static const WCHAR dos_rootW
[] = {'\\',0};
722 TRACE("%s (last=%d)\n", debugstr_w(name
), check_last
);
724 if ((!*name
) || (*name
=='\n'))
725 { /* error code for Win98 */
726 SetLastError(ERROR_BAD_PATHNAME
);
730 if ((full
->drive
= DOSFS_GetPathDrive( &name
)) == -1) return FALSE
;
732 lstrcpynA( full
->long_name
, DRIVE_GetRoot( full
->drive
),
733 sizeof(full
->long_name
) );
734 if (full
->long_name
[1]) root
= full
->long_name
+ strlen(full
->long_name
);
735 else root
= full
->long_name
; /* root directory */
737 strcpyW( full
->short_name
, driveA_rootW
);
738 full
->short_name
[0] += full
->drive
;
740 if ((*name
== '\\') || (*name
== '/')) /* Absolute path */
742 while ((*name
== '\\') || (*name
== '/')) name
++;
744 else /* Relative path */
746 lstrcpynA( root
+ 1, DRIVE_GetUnixCwd( full
->drive
),
747 sizeof(full
->long_name
) - (root
- full
->long_name
) - 1 );
748 if (root
[1]) *root
= '/';
749 lstrcpynW( full
->short_name
+ 3, DRIVE_GetDosCwd( full
->drive
),
750 sizeof(full
->short_name
)/sizeof(full
->short_name
[0]) - 3 );
753 p_l
= full
->long_name
[1] ? full
->long_name
+ strlen(full
->long_name
)
755 p_s
= full
->short_name
[3] ? full
->short_name
+ strlenW(full
->short_name
)
756 : full
->short_name
+ 2;
759 while (*name
&& found
)
761 /* Check for '.' and '..' */
765 if (is_end_of_name(name
[1]))
768 while ((*name
== '\\') || (*name
== '/')) name
++;
771 else if ((name
[1] == '.') && is_end_of_name(name
[2]))
774 while ((*name
== '\\') || (*name
== '/')) name
++;
775 while ((p_l
> root
) && (*p_l
!= '/')) p_l
--;
776 while ((p_s
> full
->short_name
+ 2) && (*p_s
!= '\\')) p_s
--;
777 *p_l
= *p_s
= '\0'; /* Remove trailing separator */
782 /* Make sure buffers are large enough */
784 if ((p_s
>= full
->short_name
+ sizeof(full
->short_name
)/sizeof(full
->short_name
[0]) - 14) ||
785 (p_l
>= full
->long_name
+ sizeof(full
->long_name
) - 1))
787 SetLastError( ERROR_PATH_NOT_FOUND
);
791 /* Get the long and short name matching the file name */
793 if ((found
= DOSFS_FindUnixName( full
, name
, p_l
+ 1,
794 sizeof(full
->long_name
) - (p_l
- full
->long_name
) - 1, p_s
+ 1 )))
800 while (!is_end_of_name(*name
)) name
++;
802 else if (!check_last
)
806 while (!is_end_of_name(*name
) &&
807 (p_s
< full
->short_name
+ sizeof(full
->short_name
)/sizeof(full
->short_name
[0]) - 1) &&
808 (p_l
< full
->long_name
+ sizeof(full
->long_name
) - 1))
811 *p_s
++ = tolowerW(*name
);
812 /* If the drive is case-sensitive we want to create new */
813 /* files in lower-case otherwise we can't reopen them */
814 /* under the same short name. */
815 if (is_case_sensitive
) wch
= tolowerW(*name
);
817 p_l
+= WideCharToMultiByte(CP_UNIXCP
, 0, &wch
, 1, p_l
, 2, NULL
, NULL
);
820 /* Ignore trailing dots and spaces */
821 while(p_l
[-1] == '.' || p_l
[-1] == ' ') {
828 while ((*name
== '\\') || (*name
== '/')) name
++;
835 SetLastError( ERROR_FILE_NOT_FOUND
);
838 if (*name
) /* Not last */
840 SetLastError( ERROR_PATH_NOT_FOUND
);
844 if (!full
->long_name
[0]) strcpy( full
->long_name
, "/" );
845 if (!full
->short_name
[2]) strcpyW( full
->short_name
+ 2, dos_rootW
);
846 TRACE("returning %s = %s\n", full
->long_name
, debugstr_w(full
->short_name
) );
851 /***********************************************************************
852 * MulDiv (KERNEL32.@)
854 * Result of multiplication and division
855 * -1: Overflow occurred or Divisor was 0
864 if (!nDivisor
) return -1;
866 /* We want to deal with a positive divisor to simplify the logic. */
869 nMultiplicand
= - nMultiplicand
;
870 nDivisor
= -nDivisor
;
873 /* If the result is positive, we "add" to round. else, we subtract to round. */
874 if ( ( (nMultiplicand
< 0) && (nMultiplier
< 0) ) ||
875 ( (nMultiplicand
>= 0) && (nMultiplier
>= 0) ) )
876 ret
= (((LONGLONG
)nMultiplicand
* nMultiplier
) + (nDivisor
/2)) / nDivisor
;
878 ret
= (((LONGLONG
)nMultiplicand
* nMultiplier
) - (nDivisor
/2)) / nDivisor
;
880 if ((ret
> 2147483647) || (ret
< -2147483647)) return -1;
885 /***********************************************************************
886 * DosDateTimeToFileTime (KERNEL32.@)
888 BOOL WINAPI
DosDateTimeToFileTime( WORD fatdate
, WORD fattime
, LPFILETIME ft
)
896 newtm
.tm_sec
= (fattime
& 0x1f) * 2;
897 newtm
.tm_min
= (fattime
>> 5) & 0x3f;
898 newtm
.tm_hour
= (fattime
>> 11);
899 newtm
.tm_mday
= (fatdate
& 0x1f);
900 newtm
.tm_mon
= ((fatdate
>> 5) & 0x0f) - 1;
901 newtm
.tm_year
= (fatdate
>> 9) + 80;
903 RtlSecondsSince1970ToTime( timegm(&newtm
), (LARGE_INTEGER
*)ft
);
905 time1
= mktime(&newtm
);
906 gtm
= gmtime(&time1
);
908 RtlSecondsSince1970ToTime( 2*time1
-time2
, (LARGE_INTEGER
*)ft
);
914 /***********************************************************************
915 * FileTimeToDosDateTime (KERNEL32.@)
917 BOOL WINAPI
FileTimeToDosDateTime( const FILETIME
*ft
, LPWORD fatdate
,
925 li
.u
.LowPart
= ft
->dwLowDateTime
;
926 li
.u
.HighPart
= ft
->dwHighDateTime
;
927 RtlTimeToSecondsSince1970( &li
, &t
);
929 tm
= gmtime( &unixtime
);
931 *fattime
= (tm
->tm_hour
<< 11) + (tm
->tm_min
<< 5) + (tm
->tm_sec
/ 2);
933 *fatdate
= ((tm
->tm_year
- 80) << 9) + ((tm
->tm_mon
+ 1) << 5)