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 BOOL32 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 BOOL32 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, BOOL32 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 BOOL32 DOSFS_FindUnixName( LPCSTR path, LPCSTR name, LPSTR long_buf,
509 INT32 long_len, LPSTR short_buf, BOOL32 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 (!lstrncmp32A( long_name, name, len )) break;
543 if (!lstrncmpi32A( 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 (!lstrncmpi32A( 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( HFILE32 hFile )
609 struct get_file_info_request req;
610 struct get_file_info_reply reply;
612 if ((req.handle = HANDLE_GetServerHandle( PROCESS_Current(), hFile,
613 K32OBJ_FILE, 0 )) == -1)
615 CLIENT_SendRequest( REQ_GET_FILE_INFO, -1, 1, &req, sizeof(req) );
616 if (!CLIENT_WaitSimpleReply( &reply, sizeof(reply), NULL ) &&
617 (reply.type == FILE_TYPE_UNKNOWN))
619 if ((reply.attr >= 0) &&
620 (reply.attr < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0])))
621 return &DOSFS_Devices[reply.attr];
627 /***********************************************************************
630 * Open a DOS device. This might not map 1:1 into the UNIX device concept.
632 HFILE32 DOSFS_OpenDevice( const char *name, DWORD access )
637 if (!name) return (HFILE32)NULL; /* if FILE_DupUnixHandle was used */
638 if (name[0] && (name[1] == ':')) name += 2;
639 if ((p = strrchr( name, '/' ))) name = p + 1;
640 if ((p = strrchr( name, '\\' ))) name = p + 1;
641 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
643 const char *dev = DOSFS_Devices[i].name;
644 if (!lstrncmpi32A( dev, name, strlen(dev) ))
646 p = name + strlen( dev );
647 if (!*p || (*p == '.')) {
649 if (!strcmp(DOSFS_Devices[i].name,"NUL"))
650 return FILE_CreateFile( "/dev/null", access,
651 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
652 OPEN_EXISTING, 0, -1 );
653 if (!strcmp(DOSFS_Devices[i].name,"CON")) {
656 switch (access & (GENERIC_READ|GENERIC_WRITE)) {
658 to_dup = GetStdHandle( STD_INPUT_HANDLE );
661 to_dup = GetStdHandle( STD_OUTPUT_HANDLE );
664 FIXME(dosfs,"can't open CON read/write\n");
665 return HFILE_ERROR32;
668 if (!DuplicateHandle( GetCurrentProcess(), to_dup, GetCurrentProcess(),
669 &handle, 0, FALSE, DUPLICATE_SAME_ACCESS ))
670 handle = HFILE_ERROR32;
673 if (!strcmp(DOSFS_Devices[i].name,"SCSIMGR$") ||
674 !strcmp(DOSFS_Devices[i].name,"HPSCAN"))
676 return FILE_CreateDevice( i, access, NULL );
678 FIXME(dosfs,"device open %s not supported (yet)\n",DOSFS_Devices[i].name);
679 return HFILE_ERROR32;
683 return HFILE_ERROR32;
687 /***********************************************************************
690 * Get the drive specified by a given path name (DOS or Unix format).
692 static int DOSFS_GetPathDrive( const char **name )
695 const char *p = *name;
697 if (*p && (p[1] == ':'))
699 drive = toupper(*p) - 'A';
702 else if (*p == '/') /* Absolute Unix path? */
704 if ((drive = DRIVE_FindDriveRoot( name )) == -1)
706 MSG("Warning: %s not accessible from a DOS drive\n", *name );
707 /* Assume it really was a DOS name */
708 drive = DRIVE_GetCurrentDrive();
711 else drive = DRIVE_GetCurrentDrive();
713 if (!DRIVE_IsValid(drive))
715 SetLastError( ERROR_INVALID_DRIVE );
722 /***********************************************************************
725 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
726 * Unix name / short DOS name pair.
727 * Return FALSE if one of the path components does not exist. The last path
728 * component is only checked if 'check_last' is non-zero.
729 * The buffers pointed to by 'long_buf' and 'short_buf' must be
730 * at least MAX_PATHNAME_LEN long.
732 BOOL32 DOSFS_GetFullName( LPCSTR name, BOOL32 check_last, DOS_FULL_NAME *full )
736 char *p_l, *p_s, *root;
738 TRACE(dosfs, "%s (last=%d)\n",
741 if ((full->drive = DOSFS_GetPathDrive( &name )) == -1) return FALSE;
742 flags = DRIVE_GetFlags( full->drive );
744 lstrcpyn32A( full->long_name, DRIVE_GetRoot( full->drive ),
745 sizeof(full->long_name) );
746 if (full->long_name[1]) root = full->long_name + strlen(full->long_name);
747 else root = full->long_name; /* root directory */
749 strcpy( full->short_name, "A:\\" );
750 full->short_name[0] += full->drive;
752 if ((*name == '\\') || (*name == '/')) /* Absolute path */
754 while ((*name == '\\') || (*name == '/')) name++;
756 else /* Relative path */
758 lstrcpyn32A( root + 1, DRIVE_GetUnixCwd( full->drive ),
759 sizeof(full->long_name) - (root - full->long_name) - 1 );
760 if (root[1]) *root = '/';
761 lstrcpyn32A( full->short_name + 3, DRIVE_GetDosCwd( full->drive ),
762 sizeof(full->short_name) - 3 );
765 p_l = full->long_name[1] ? full->long_name + strlen(full->long_name)
767 p_s = full->short_name[3] ? full->short_name + strlen(full->short_name)
768 : full->short_name + 2;
771 while (*name && found)
773 /* Check for '.' and '..' */
777 if (IS_END_OF_NAME(name[1]))
780 while ((*name == '\\') || (*name == '/')) name++;
783 else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
786 while ((*name == '\\') || (*name == '/')) name++;
787 while ((p_l > root) && (*p_l != '/')) p_l--;
788 while ((p_s > full->short_name + 2) && (*p_s != '\\')) p_s--;
789 *p_l = *p_s = '\0'; /* Remove trailing separator */
794 /* Make sure buffers are large enough */
796 if ((p_s >= full->short_name + sizeof(full->short_name) - 14) ||
797 (p_l >= full->long_name + sizeof(full->long_name) - 1))
799 SetLastError( ERROR_PATH_NOT_FOUND );
803 /* Get the long and short name matching the file name */
805 if ((found = DOSFS_FindUnixName( full->long_name, name, p_l + 1,
806 sizeof(full->long_name) - (p_l - full->long_name) - 1,
807 p_s + 1, !(flags & DRIVE_CASE_SENSITIVE) )))
813 while (!IS_END_OF_NAME(*name)) name++;
815 else if (!check_last)
819 while (!IS_END_OF_NAME(*name) &&
820 (p_s < full->short_name + sizeof(full->short_name) - 1) &&
821 (p_l < full->long_name + sizeof(full->long_name) - 1))
823 *p_s++ = tolower(*name);
824 /* If the drive is case-sensitive we want to create new */
825 /* files in lower-case otherwise we can't reopen them */
826 /* under the same short name. */
827 if (flags & DRIVE_CASE_SENSITIVE) *p_l++ = tolower(*name);
833 while ((*name == '\\') || (*name == '/')) name++;
840 SetLastError( ERROR_FILE_NOT_FOUND );
843 if (*name) /* Not last */
845 SetLastError( ERROR_PATH_NOT_FOUND );
849 if (!full->long_name[0]) strcpy( full->long_name, "/" );
850 if (!full->short_name[2]) strcpy( full->short_name + 2, "\\" );
851 TRACE(dosfs, "returning %s = %s\n",
852 full->long_name, full->short_name );
857 /***********************************************************************
858 * GetShortPathName32A (KERNEL32.271)
862 * longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
863 * *longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0
865 DWORD WINAPI GetShortPathName32A( LPCSTR longpath, LPSTR shortpath,
868 DOS_FULL_NAME full_name;
872 SetLastError(ERROR_INVALID_PARAMETER);
878 SetLastError(ERROR_BAD_PATHNAME);
882 /* FIXME: is it correct to always return a fully qualified short path? */
883 if (!DOSFS_GetFullName( longpath, TRUE, &full_name ))
885 SetLastError(ERROR_BAD_PATHNAME);
888 lstrcpyn32A( shortpath, full_name.short_name, shortlen );
889 return strlen( full_name.short_name );
893 /***********************************************************************
894 * GetShortPathName32W (KERNEL32.272)
896 DWORD WINAPI GetShortPathName32W( LPCWSTR longpath, LPWSTR shortpath,
899 DOS_FULL_NAME full_name;
904 { SetLastError(ERROR_INVALID_PARAMETER);
909 { SetLastError(ERROR_BAD_PATHNAME);
914 longpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, longpath );
916 /* FIXME: is it correct to always return a fully qualified short path? */
917 if (DOSFS_GetFullName( longpathA, TRUE, &full_name ))
919 ret = strlen( full_name.short_name );
920 lstrcpynAtoW( shortpath, full_name.short_name, shortlen );
923 SetLastError(ERROR_BAD_PATHNAME);
924 HeapFree( GetProcessHeap(), 0, longpathA );
929 /***********************************************************************
930 * GetLongPathName32A (KERNEL32.xxx)
932 DWORD WINAPI GetLongPathName32A( LPCSTR shortpath, LPSTR longpath,
935 DOS_FULL_NAME full_name;
940 if (!DOSFS_GetFullName( shortpath, TRUE, &full_name )) return 0;
941 lstrcpyn32A( longpath, full_name.short_name, longlen );
942 /* Do some hackery to get the long filename.
943 * FIXME: Would be better if it returned the
944 * long version of the directories too
946 longfilename = strrchr(full_name.long_name, '/')+1;
947 if (longpath != NULL) {
948 if ((p = strrchr( longpath, '\\' )) != NULL) {
950 longlen -= (p-longpath);
951 lstrcpyn32A( p, longfilename , longlen);
955 ((strrchr( full_name.short_name, '\\' ) - full_name.short_name) + 1);
956 return shortpathlen + strlen( longfilename );
960 /***********************************************************************
961 * GetLongPathName32W (KERNEL32.269)
963 DWORD WINAPI GetLongPathName32W( LPCWSTR shortpath, LPWSTR longpath,
966 DOS_FULL_NAME full_name;
968 LPSTR shortpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, shortpath );
970 /* FIXME: is it correct to always return a fully qualified short path? */
971 if (DOSFS_GetFullName( shortpathA, TRUE, &full_name ))
973 ret = strlen( full_name.short_name );
974 lstrcpynAtoW( longpath, full_name.long_name, longlen );
976 HeapFree( GetProcessHeap(), 0, shortpathA );
981 /***********************************************************************
982 * DOSFS_DoGetFullPathName
984 * Implementation of GetFullPathName32A/W.
986 static DWORD DOSFS_DoGetFullPathName( LPCSTR name, DWORD len, LPSTR result,
989 char buffer[MAX_PATHNAME_LEN];
994 /* last possible position for a char != 0 */
995 char *endchar = buffer + sizeof(buffer) - 2;
998 TRACE(dosfs, "converting '%s'\n", name );
1000 if (!name || !result || ((drive = DOSFS_GetPathDrive( &name )) == -1) )
1001 { SetLastError( ERROR_INVALID_PARAMETER );
1008 if (IS_END_OF_NAME(*name) && (*name)) /* Absolute path */
1010 while (((*name == '\\') || (*name == '/')) && (!*endchar) )
1013 else /* Relative path or empty path */
1016 lstrcpyn32A( p, DRIVE_GetDosCwd(drive), sizeof(buffer) - 4 );
1029 if (IS_END_OF_NAME(name[1]))
1032 while ((*name == '\\') || (*name == '/')) name++;
1035 else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
1038 while ((*name == '\\') || (*name == '/')) name++;
1040 if (p < buffer + 3) /* no previous dir component */
1042 p--; /* skip previously added '\\' */
1043 while ((*p == '\\') || (*p == '/')) p--;
1044 /* skip previous dir component */
1045 while ((*p != '\\') && (*p != '/')) p--;
1051 { SetLastError( ERROR_PATH_NOT_FOUND );
1054 while (!IS_END_OF_NAME(*name) && (!*endchar) )
1056 while (((*name == '\\') || (*name == '/')) && (!*endchar) )
1061 if (!(DRIVE_GetFlags(drive) & DRIVE_CASE_PRESERVING))
1062 CharUpper32A( buffer );
1065 lstrcpynAtoW( (LPWSTR)result, buffer, len );
1067 lstrcpyn32A( result, buffer, len );
1069 TRACE(dosfs, "returning '%s'\n", buffer );
1071 /* If the lpBuffer buffer is too small, the return value is the
1072 size of the buffer, in characters, required to hold the path. */
1074 ret = strlen(buffer);
1077 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1083 /***********************************************************************
1084 * GetFullPathName32A (KERNEL32.272)
1086 * if the path closed with '\', *lastpart is 0
1088 DWORD WINAPI GetFullPathName32A( LPCSTR name, DWORD len, LPSTR buffer,
1091 DWORD ret = DOSFS_DoGetFullPathName( name, len, buffer, FALSE );
1092 if (ret && lastpart)
1094 LPSTR p = buffer + strlen(buffer);
1098 while ((p > buffer + 2) && (*p != '\\')) p--;
1101 else *lastpart = NULL;
1107 /***********************************************************************
1108 * GetFullPathName32W (KERNEL32.273)
1110 DWORD WINAPI GetFullPathName32W( LPCWSTR name, DWORD len, LPWSTR buffer,
1113 LPSTR nameA = HEAP_strdupWtoA( GetProcessHeap(), 0, name );
1114 DWORD ret = DOSFS_DoGetFullPathName( nameA, len, (LPSTR)buffer, TRUE );
1115 HeapFree( GetProcessHeap(), 0, nameA );
1116 if (ret && lastpart)
1118 LPWSTR p = buffer + lstrlen32W(buffer);
1119 if (*p != (WCHAR)'\\')
1121 while ((p > buffer + 2) && (*p != (WCHAR)'\\')) p--;
1124 else *lastpart = NULL;
1129 /***********************************************************************
1132 static int DOSFS_FindNextEx( FIND_FIRST_INFO *info, WIN32_FIND_DATA32A *entry )
1134 BYTE attr = info->attr | FA_UNUSED | FA_ARCHIVE | FA_RDONLY;
1135 UINT32 flags = DRIVE_GetFlags( info->drive );
1136 char *p, buffer[MAX_PATHNAME_LEN];
1137 const char *drive_path;
1139 LPCSTR long_name, short_name;
1140 BY_HANDLE_FILE_INFORMATION fileinfo;
1143 if ((info->attr & ~(FA_UNUSED | FA_ARCHIVE | FA_RDONLY)) == FA_LABEL)
1145 if (info->cur_pos) return 0;
1146 entry->dwFileAttributes = FILE_ATTRIBUTE_LABEL;
1147 DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftCreationTime, 0 );
1148 DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftLastAccessTime, 0 );
1149 DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftLastWriteTime, 0 );
1150 entry->nFileSizeHigh = 0;
1151 entry->nFileSizeLow = 0;
1152 entry->dwReserved0 = 0;
1153 entry->dwReserved1 = 0;
1154 DOSFS_ToDosDTAFormat( DRIVE_GetLabel( info->drive ), entry->cFileName );
1155 strcpy( entry->cAlternateFileName, entry->cFileName );
1160 drive_path = info->path + strlen(DRIVE_GetRoot( info->drive ));
1161 while ((*drive_path == '/') || (*drive_path == '\\')) drive_path++;
1162 drive_root = !*drive_path;
1164 lstrcpyn32A( buffer, info->path, sizeof(buffer) - 1 );
1165 strcat( buffer, "/" );
1166 p = buffer + strlen(buffer);
1168 while (DOSFS_ReadDir( info->dir, &long_name, &short_name ))
1172 /* Don't return '.' and '..' in the root of the drive */
1173 if (drive_root && (long_name[0] == '.') &&
1174 (!long_name[1] || ((long_name[1] == '.') && !long_name[2])))
1177 /* Check the long mask */
1179 if (info->long_mask)
1181 if (!DOSFS_MatchLong( info->long_mask, long_name,
1182 flags & DRIVE_CASE_SENSITIVE )) continue;
1185 /* Check the short mask */
1187 if (info->short_mask)
1191 DOSFS_Hash( long_name, dos_name, TRUE,
1192 !(flags & DRIVE_CASE_SENSITIVE) );
1193 short_name = dos_name;
1195 if (!DOSFS_MatchShort( info->short_mask, short_name )) continue;
1198 /* Check the file attributes */
1200 lstrcpyn32A( p, long_name, sizeof(buffer) - (int)(p - buffer) );
1201 if (!FILE_Stat( buffer, &fileinfo ))
1203 WARN(dosfs, "can't stat %s\n", buffer);
1206 if (fileinfo.dwFileAttributes & ~attr) continue;
1208 /* We now have a matching entry; fill the result and return */
1210 entry->dwFileAttributes = fileinfo.dwFileAttributes;
1211 entry->ftCreationTime = fileinfo.ftCreationTime;
1212 entry->ftLastAccessTime = fileinfo.ftLastAccessTime;
1213 entry->ftLastWriteTime = fileinfo.ftLastWriteTime;
1214 entry->nFileSizeHigh = fileinfo.nFileSizeHigh;
1215 entry->nFileSizeLow = fileinfo.nFileSizeLow;
1218 DOSFS_ToDosDTAFormat( short_name, entry->cAlternateFileName );
1220 DOSFS_Hash( long_name, entry->cAlternateFileName, FALSE,
1221 !(flags & DRIVE_CASE_SENSITIVE) );
1223 lstrcpyn32A( entry->cFileName, long_name, sizeof(entry->cFileName) );
1224 if (!(flags & DRIVE_CASE_PRESERVING)) CharLower32A( entry->cFileName );
1225 TRACE(dosfs, "returning %s (%s) %02lx %ld\n",
1226 entry->cFileName, entry->cAlternateFileName,
1227 entry->dwFileAttributes, entry->nFileSizeLow );
1230 return 0; /* End of directory */
1233 /***********************************************************************
1236 * Find the next matching file. Return the number of entries read to find
1237 * the matching one, or 0 if no more entries.
1238 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
1239 * file name mask. Either or both can be NULL.
1241 * NOTE: This is supposed to be only called by the int21 emulation
1242 * routines. Thus, we should own the Win16Mutex anyway.
1243 * Nevertheless, we explicitly enter it to ensure the static
1244 * directory cache is protected.
1246 int DOSFS_FindNext( const char *path, const char *short_mask,
1247 const char *long_mask, int drive, BYTE attr,
1248 int skip, WIN32_FIND_DATA32A *entry )
1250 static FIND_FIRST_INFO info = { NULL };
1251 LPCSTR short_name, long_name;
1254 SYSLEVEL_EnterWin16Lock();
1256 /* Check the cached directory */
1257 if (!(info.dir && info.path == path && info.short_mask == short_mask
1258 && info.long_mask == long_mask && info.drive == drive
1259 && info.attr == attr && info.cur_pos <= skip))
1261 /* Not in the cache, open it anew */
1262 if (info.dir) DOSFS_CloseDir( info.dir );
1264 info.path = (LPSTR)path;
1265 info.long_mask = (LPSTR)long_mask;
1266 info.short_mask = (LPSTR)short_mask;
1270 info.dir = DOSFS_OpenDir( info.path );
1273 /* Skip to desired position */
1274 while (info.cur_pos < skip)
1275 if (info.dir && DOSFS_ReadDir( info.dir, &long_name, &short_name ))
1280 if (info.dir && info.cur_pos == skip && DOSFS_FindNextEx( &info, entry ))
1281 count = info.cur_pos - skip;
1287 if (info.dir) DOSFS_CloseDir( info.dir );
1288 memset( &info, '\0', sizeof(info) );
1291 SYSLEVEL_LeaveWin16Lock();
1298 /*************************************************************************
1299 * FindFirstFile16 (KERNEL.413)
1301 HANDLE16 WINAPI FindFirstFile16( LPCSTR path, WIN32_FIND_DATA32A *data )
1303 DOS_FULL_NAME full_name;
1305 FIND_FIRST_INFO *info;
1307 data->dwReserved0 = data->dwReserved1 = 0x0;
1308 if (!path) return 0;
1309 if (!DOSFS_GetFullName( path, FALSE, &full_name ))
1310 return INVALID_HANDLE_VALUE16;
1311 if (!(handle = GlobalAlloc16( GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO) )))
1312 return INVALID_HANDLE_VALUE16;
1313 info = (FIND_FIRST_INFO *)GlobalLock16( handle );
1314 info->path = HEAP_strdupA( SystemHeap, 0, full_name.long_name );
1315 info->long_mask = strrchr( info->path, '/' );
1316 *(info->long_mask++) = '\0';
1317 info->short_mask = NULL;
1319 if (path[0] && (path[1] == ':')) info->drive = toupper(*path) - 'A';
1320 else info->drive = DRIVE_GetCurrentDrive();
1323 info->dir = DOSFS_OpenDir( info->path );
1325 GlobalUnlock16( handle );
1326 if (!FindNextFile16( handle, data ))
1328 FindClose16( handle );
1329 SetLastError( ERROR_NO_MORE_FILES );
1330 return INVALID_HANDLE_VALUE16;
1336 /*************************************************************************
1337 * FindFirstFile32A (KERNEL32.123)
1339 HANDLE32 WINAPI FindFirstFile32A( LPCSTR path, WIN32_FIND_DATA32A *data )
1341 HANDLE32 handle = FindFirstFile16( path, data );
1342 if (handle == INVALID_HANDLE_VALUE16) return INVALID_HANDLE_VALUE32;
1347 /*************************************************************************
1348 * FindFirstFile32W (KERNEL32.124)
1350 HANDLE32 WINAPI FindFirstFile32W( LPCWSTR path, WIN32_FIND_DATA32W *data )
1352 WIN32_FIND_DATA32A dataA;
1353 LPSTR pathA = HEAP_strdupWtoA( GetProcessHeap(), 0, path );
1354 HANDLE32 handle = FindFirstFile32A( pathA, &dataA );
1355 HeapFree( GetProcessHeap(), 0, pathA );
1356 if (handle != INVALID_HANDLE_VALUE32)
1358 data->dwFileAttributes = dataA.dwFileAttributes;
1359 data->ftCreationTime = dataA.ftCreationTime;
1360 data->ftLastAccessTime = dataA.ftLastAccessTime;
1361 data->ftLastWriteTime = dataA.ftLastWriteTime;
1362 data->nFileSizeHigh = dataA.nFileSizeHigh;
1363 data->nFileSizeLow = dataA.nFileSizeLow;
1364 lstrcpyAtoW( data->cFileName, dataA.cFileName );
1365 lstrcpyAtoW( data->cAlternateFileName, dataA.cAlternateFileName );
1371 /*************************************************************************
1372 * FindNextFile16 (KERNEL.414)
1374 BOOL16 WINAPI FindNextFile16( HANDLE16 handle, WIN32_FIND_DATA32A *data )
1376 FIND_FIRST_INFO *info;
1378 if (!(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
1380 SetLastError( ERROR_INVALID_HANDLE );
1383 GlobalUnlock16( handle );
1384 if (!info->path || !info->dir)
1386 SetLastError( ERROR_NO_MORE_FILES );
1389 if (!DOSFS_FindNextEx( info, data ))
1391 DOSFS_CloseDir( info->dir ); info->dir = NULL;
1392 HeapFree( SystemHeap, 0, info->path );
1393 info->path = info->long_mask = NULL;
1394 SetLastError( ERROR_NO_MORE_FILES );
1401 /*************************************************************************
1402 * FindNextFile32A (KERNEL32.126)
1404 BOOL32 WINAPI FindNextFile32A( HANDLE32 handle, WIN32_FIND_DATA32A *data )
1406 return FindNextFile16( handle, data );
1410 /*************************************************************************
1411 * FindNextFile32W (KERNEL32.127)
1413 BOOL32 WINAPI FindNextFile32W( HANDLE32 handle, WIN32_FIND_DATA32W *data )
1415 WIN32_FIND_DATA32A dataA;
1416 if (!FindNextFile32A( handle, &dataA )) return FALSE;
1417 data->dwFileAttributes = dataA.dwFileAttributes;
1418 data->ftCreationTime = dataA.ftCreationTime;
1419 data->ftLastAccessTime = dataA.ftLastAccessTime;
1420 data->ftLastWriteTime = dataA.ftLastWriteTime;
1421 data->nFileSizeHigh = dataA.nFileSizeHigh;
1422 data->nFileSizeLow = dataA.nFileSizeLow;
1423 lstrcpyAtoW( data->cFileName, dataA.cFileName );
1424 lstrcpyAtoW( data->cAlternateFileName, dataA.cAlternateFileName );
1429 /*************************************************************************
1430 * FindClose16 (KERNEL.415)
1432 BOOL16 WINAPI FindClose16( HANDLE16 handle )
1434 FIND_FIRST_INFO *info;
1436 if ((handle == INVALID_HANDLE_VALUE16) ||
1437 !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
1439 SetLastError( ERROR_INVALID_HANDLE );
1442 if (info->dir) DOSFS_CloseDir( info->dir );
1443 if (info->path) HeapFree( SystemHeap, 0, info->path );
1444 GlobalUnlock16( handle );
1445 GlobalFree16( handle );
1450 /*************************************************************************
1451 * FindClose32 (KERNEL32.119)
1453 BOOL32 WINAPI FindClose32( HANDLE32 handle )
1455 return FindClose16( (HANDLE16)handle );
1459 /***********************************************************************
1460 * DOSFS_UnixTimeToFileTime
1462 * Convert a Unix time to FILETIME format.
1463 * The FILETIME structure is a 64-bit value representing the number of
1464 * 100-nanosecond intervals since January 1, 1601, 0:00.
1465 * 'remainder' is the nonnegative number of 100-ns intervals
1466 * corresponding to the time fraction smaller than 1 second that
1467 * couldn't be stored in the time_t value.
1469 void DOSFS_UnixTimeToFileTime( time_t unix_time, FILETIME *filetime,
1475 The time difference between 1 January 1601, 00:00:00 and
1476 1 January 1970, 00:00:00 is 369 years, plus the leap years
1477 from 1604 to 1968, excluding 1700, 1800, 1900.
1478 This makes (1968 - 1600) / 4 - 3 = 89 leap days, and a total
1481 Any day in that period had 24 * 60 * 60 = 86400 seconds.
1483 The time difference is 134774 * 86400 * 10000000, which can be written
1485 27111902 * 2^32 + 3577643008
1486 413 * 2^48 + 45534 * 2^32 + 54590 * 2^16 + 32768
1488 If you find that these constants are buggy, please change them in all
1489 instances in both conversion functions.
1492 There are two versions, one of them uses long long variables and
1493 is presumably faster but not ISO C. The other one uses standard C
1494 data types and operations but relies on the assumption that negative
1495 numbers are stored as 2's complement (-1 is 0xffff....). If this
1496 assumption is violated, dates before 1970 will not convert correctly.
1497 This should however work on any reasonable architecture where WINE
1502 Take care not to remove the casts. I have tested these functions
1503 (in both versions) for a lot of numbers. I would be interested in
1504 results on other compilers than GCC.
1506 The operations have been designed to account for the possibility
1507 of 64-bit time_t in future UNICES. Even the versions without
1508 internal long long numbers will work if time_t only is 64 bit.
1509 A 32-bit shift, which was necessary for that operation, turned out
1510 not to work correctly in GCC, besides giving the warning. So I
1511 used a double 16-bit shift instead. Numbers are in the ISO version
1512 represented by three limbs, the most significant with 32 bit, the
1513 other two with 16 bit each.
1515 As the modulo-operator % is not well-defined for negative numbers,
1516 negative divisors have been avoided in DOSFS_FileTimeToUnixTime.
1518 There might be quicker ways to do this in C. Certainly so in
1521 Claus Fischer, fischer@iue.tuwien.ac.at
1524 #if (SIZEOF_LONG_LONG >= 8)
1525 # define USE_LONG_LONG 1
1527 # define USE_LONG_LONG 0
1530 #if USE_LONG_LONG /* gcc supports long long type */
1532 long long int t = unix_time;
1534 t += 116444736000000000LL;
1536 filetime->dwLowDateTime = (UINT32)t;
1537 filetime->dwHighDateTime = (UINT32)(t >> 32);
1539 #else /* ISO version */
1541 UINT32 a0; /* 16 bit, low bits */
1542 UINT32 a1; /* 16 bit, medium bits */
1543 UINT32 a2; /* 32 bit, high bits */
1545 /* Copy the unix time to a2/a1/a0 */
1546 a0 = unix_time & 0xffff;
1547 a1 = (unix_time >> 16) & 0xffff;
1548 /* This is obsolete if unix_time is only 32 bits, but it does not hurt.
1549 Do not replace this by >> 32, it gives a compiler warning and it does
1551 a2 = (unix_time >= 0 ? (unix_time >> 16) >> 16 :
1552 ~((~unix_time >> 16) >> 16));
1554 /* Multiply a by 10000000 (a = a2/a1/a0)
1555 Split the factor into 10000 * 1000 which are both less than 0xffff. */
1557 a1 = a1 * 10000 + (a0 >> 16);
1558 a2 = a2 * 10000 + (a1 >> 16);
1563 a1 = a1 * 1000 + (a0 >> 16);
1564 a2 = a2 * 1000 + (a1 >> 16);
1568 /* Add the time difference and the remainder */
1569 a0 += 32768 + (remainder & 0xffff);
1570 a1 += 54590 + (remainder >> 16 ) + (a0 >> 16);
1571 a2 += 27111902 + (a1 >> 16);
1576 filetime->dwLowDateTime = (a1 << 16) + a0;
1577 filetime->dwHighDateTime = a2;
1582 /***********************************************************************
1583 * DOSFS_FileTimeToUnixTime
1585 * Convert a FILETIME format to Unix time.
1586 * If not NULL, 'remainder' contains the fractional part of the filetime,
1587 * in the range of [0..9999999] (even if time_t is negative).
1589 time_t DOSFS_FileTimeToUnixTime( const FILETIME *filetime, DWORD *remainder )
1591 /* Read the comment in the function DOSFS_UnixTimeToFileTime. */
1594 long long int t = filetime->dwHighDateTime;
1596 t += (UINT32)filetime->dwLowDateTime;
1597 t -= 116444736000000000LL;
1600 if (remainder) *remainder = 9999999 - (-t - 1) % 10000000;
1601 return -1 - ((-t - 1) / 10000000);
1605 if (remainder) *remainder = t % 10000000;
1606 return t / 10000000;
1609 #else /* ISO version */
1611 UINT32 a0; /* 16 bit, low bits */
1612 UINT32 a1; /* 16 bit, medium bits */
1613 UINT32 a2; /* 32 bit, high bits */
1614 UINT32 r; /* remainder of division */
1615 unsigned int carry; /* carry bit for subtraction */
1616 int negative; /* whether a represents a negative value */
1618 /* Copy the time values to a2/a1/a0 */
1619 a2 = (UINT32)filetime->dwHighDateTime;
1620 a1 = ((UINT32)filetime->dwLowDateTime ) >> 16;
1621 a0 = ((UINT32)filetime->dwLowDateTime ) & 0xffff;
1623 /* Subtract the time difference */
1624 if (a0 >= 32768 ) a0 -= 32768 , carry = 0;
1625 else a0 += (1 << 16) - 32768 , carry = 1;
1627 if (a1 >= 54590 + carry) a1 -= 54590 + carry, carry = 0;
1628 else a1 += (1 << 16) - 54590 - carry, carry = 1;
1630 a2 -= 27111902 + carry;
1632 /* If a is negative, replace a by (-1-a) */
1633 negative = (a2 >= ((UINT32)1) << 31);
1636 /* Set a to -a - 1 (a is a2/a1/a0) */
1642 /* Divide a by 10000000 (a = a2/a1/a0), put the rest into r.
1643 Split the divisor into 10000 * 1000 which are both less than 0xffff. */
1644 a1 += (a2 % 10000) << 16;
1646 a0 += (a1 % 10000) << 16;
1651 a1 += (a2 % 1000) << 16;
1653 a0 += (a1 % 1000) << 16;
1655 r += (a0 % 1000) * 10000;
1658 /* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
1661 /* Set a to -a - 1 (a is a2/a1/a0) */
1669 if (remainder) *remainder = r;
1671 /* Do not replace this by << 32, it gives a compiler warning and it does
1673 return ((((time_t)a2) << 16) << 16) + (a1 << 16) + a0;
1678 /***********************************************************************
1679 * DosDateTimeToFileTime (KERNEL32.76)
1681 BOOL32 WINAPI DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft)
1685 newtm.tm_sec = (fattime & 0x1f) * 2;
1686 newtm.tm_min = (fattime >> 5) & 0x3f;
1687 newtm.tm_hour = (fattime >> 11);
1688 newtm.tm_mday = (fatdate & 0x1f);
1689 newtm.tm_mon = ((fatdate >> 5) & 0x0f) - 1;
1690 newtm.tm_year = (fatdate >> 9) + 80;
1691 DOSFS_UnixTimeToFileTime( mktime( &newtm ), ft, 0 );
1696 /***********************************************************************
1697 * FileTimeToDosDateTime (KERNEL32.111)
1699 BOOL32 WINAPI FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate,
1702 time_t unixtime = DOSFS_FileTimeToUnixTime( ft, NULL );
1703 struct tm *tm = localtime( &unixtime );
1705 *fattime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
1707 *fatdate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
1713 /***********************************************************************
1714 * LocalFileTimeToFileTime (KERNEL32.373)
1716 BOOL32 WINAPI LocalFileTimeToFileTime( const FILETIME *localft,
1722 /* convert from local to UTC. Perhaps not correct. FIXME */
1723 time_t unixtime = DOSFS_FileTimeToUnixTime( localft, &remainder );
1724 xtm = gmtime( &unixtime );
1725 DOSFS_UnixTimeToFileTime( mktime(xtm), utcft, remainder );
1730 /***********************************************************************
1731 * FileTimeToLocalFileTime (KERNEL32.112)
1733 BOOL32 WINAPI FileTimeToLocalFileTime( const FILETIME *utcft,
1734 LPFILETIME localft )
1737 /* convert from UTC to local. Perhaps not correct. FIXME */
1738 time_t unixtime = DOSFS_FileTimeToUnixTime( utcft, &remainder );
1740 struct tm *xtm = localtime( &unixtime );
1743 localtime = timegm(xtm);
1744 DOSFS_UnixTimeToFileTime( localtime, localft, remainder );
1747 struct tm *xtm,*gtm;
1750 xtm = localtime( &unixtime );
1751 gtm = gmtime( &unixtime );
1752 time1 = mktime(xtm);
1753 time2 = mktime(gtm);
1754 DOSFS_UnixTimeToFileTime( 2*time1-time2, localft, remainder );
1760 /***********************************************************************
1761 * FileTimeToSystemTime (KERNEL32.113)
1763 BOOL32 WINAPI FileTimeToSystemTime( const FILETIME *ft, LPSYSTEMTIME syst )
1767 time_t xtime = DOSFS_FileTimeToUnixTime( ft, &remainder );
1768 xtm = gmtime(&xtime);
1769 syst->wYear = xtm->tm_year+1900;
1770 syst->wMonth = xtm->tm_mon + 1;
1771 syst->wDayOfWeek = xtm->tm_wday;
1772 syst->wDay = xtm->tm_mday;
1773 syst->wHour = xtm->tm_hour;
1774 syst->wMinute = xtm->tm_min;
1775 syst->wSecond = xtm->tm_sec;
1776 syst->wMilliseconds = remainder / 10000;
1780 /***********************************************************************
1781 * QueryDosDeviceA (KERNEL32.413)
1783 * returns array of strings terminated by \0, terminated by \0
1785 DWORD WINAPI QueryDosDevice32A(LPCSTR devname,LPSTR target,DWORD bufsize)
1790 TRACE(dosfs,"(%s,...)\n",devname?devname:"<null>");
1792 /* return known MSDOS devices */
1793 lstrcpy32A(buffer,"CON COM1 COM2 LPT1 NUL ");
1794 while ((s=strchr(buffer,' ')))
1797 lstrcpyn32A(target,buffer,bufsize);
1798 return strlen(buffer);
1800 lstrcpy32A(buffer,"\\DEV\\");
1801 lstrcat32A(buffer,devname);
1802 if ((s=strchr(buffer,':'))) *s='\0';
1803 lstrcpyn32A(target,buffer,bufsize);
1804 return strlen(buffer);
1808 /***********************************************************************
1809 * QueryDosDeviceW (KERNEL32.414)
1811 * returns array of strings terminated by \0, terminated by \0
1813 DWORD WINAPI QueryDosDevice32W(LPCWSTR devname,LPWSTR target,DWORD bufsize)
1815 LPSTR devnameA = devname?HEAP_strdupWtoA(GetProcessHeap(),0,devname):NULL;
1816 LPSTR targetA = (LPSTR)HEAP_xalloc(GetProcessHeap(),0,bufsize);
1817 DWORD ret = QueryDosDevice32A(devnameA,targetA,bufsize);
1819 lstrcpynAtoW(target,targetA,bufsize);
1820 if (devnameA) HeapFree(GetProcessHeap(),0,devnameA);
1821 if (targetA) HeapFree(GetProcessHeap(),0,targetA);
1826 /***********************************************************************
1827 * SystemTimeToFileTime (KERNEL32.526)
1829 BOOL32 WINAPI SystemTimeToFileTime( const SYSTEMTIME *syst, LPFILETIME ft )
1835 struct tm xtm,*local_tm,*utc_tm;
1836 time_t localtim,utctime;
1839 xtm.tm_year = syst->wYear-1900;
1840 xtm.tm_mon = syst->wMonth - 1;
1841 xtm.tm_wday = syst->wDayOfWeek;
1842 xtm.tm_mday = syst->wDay;
1843 xtm.tm_hour = syst->wHour;
1844 xtm.tm_min = syst->wMinute;
1845 xtm.tm_sec = syst->wSecond; /* this is UTC */
1848 utctime = timegm(&xtm);
1849 DOSFS_UnixTimeToFileTime( utctime, ft,
1850 syst->wMilliseconds * 10000 );
1852 localtim = mktime(&xtm); /* now we've got local time */
1853 local_tm = localtime(&localtim);
1854 utc_tm = gmtime(&localtim);
1855 utctime = mktime(utc_tm);
1856 DOSFS_UnixTimeToFileTime( 2*localtim -utctime, ft,
1857 syst->wMilliseconds * 10000 );
1862 BOOL32 WINAPI DefineDosDevice32A(DWORD flags,LPCSTR devname,LPCSTR targetpath) {
1863 FIXME(dosfs,"(0x%08lx,%s,%s),stub!\n",flags,devname,targetpath);
1864 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);