2 * DOS file system functions
4 * Copyright 1993 Erik Bos
5 * Copyright 1996 Alexandre Julliard
13 #ifdef HAVE_SYS_ERRNO_H
14 #include <sys/errno.h>
20 #include <sys/ioctl.h>
26 #include "wine/winbase16.h"
36 #include "debugtools.h"
38 DEFAULT_DEBUG_CHANNEL(dosfs)
39 DECLARE_DEBUG_CHANNEL(file)
41 /* Define the VFAT ioctl to get both short and long file names */
42 /* FIXME: is it possible to get this to work on other systems? */
44 /* We want the real kernel dirent structure, not the libc one */
49 unsigned short d_reclen;
53 #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, KERNEL_DIRENT [2] )
56 #undef VFAT_IOCTL_READDIR_BOTH /* just in case... */
59 /* Chars we don't want to see in DOS file names */
60 #define INVALID_DOS_CHARS "*?<>|\"+=,;[] \345"
62 static const DOS_DEVICE DOSFS_Devices[] =
63 /* name, device flags (see Int 21/AX=0x4400) */
77 { "SCSIMGR$", 0xc0c0 },
81 #define GET_DRIVE(path) \
82 (((path)[1] == ':') ? toupper((path)[0]) - 'A' : DOSFS_CurDrive)
84 /* Directory info for DOSFS_ReadDir */
88 #ifdef VFAT_IOCTL_READDIR_BOTH
91 KERNEL_DIRENT dirent[2];
95 /* Info structure for FindFirstFile handle */
109 /***********************************************************************
112 * Return 1 if Unix file 'name' is also a valid MS-DOS name
113 * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
114 * File name can be terminated by '\0', '\\' or '/'.
116 static int DOSFS_ValidDOSName( const char *name, int ignore_case )
118 static const char invalid_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS;
119 const char *p = name;
120 const char *invalid = ignore_case ? (invalid_chars + 26) : invalid_chars;
125 /* Check for "." and ".." */
128 /* All other names beginning with '.' are invalid */
129 return (IS_END_OF_NAME(*p));
131 while (!IS_END_OF_NAME(*p))
133 if (strchr( invalid, *p )) return 0; /* Invalid char */
134 if (*p == '.') break; /* Start of the extension */
135 if (++len > 8) return 0; /* Name too long */
138 if (*p != '.') return 1; /* End of name */
140 if (IS_END_OF_NAME(*p)) return 0; /* Empty extension not allowed */
142 while (!IS_END_OF_NAME(*p))
144 if (strchr( invalid, *p )) return 0; /* Invalid char */
145 if (*p == '.') return 0; /* Second extension not allowed */
146 if (++len > 3) return 0; /* Extension too long */
153 /***********************************************************************
154 * DOSFS_ToDosFCBFormat
156 * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
157 * expanding wild cards and converting to upper-case in the process.
158 * File name can be terminated by '\0', '\\' or '/'.
159 * Return FALSE if the name is not a valid DOS name.
160 * 'buffer' must be at least 12 characters long.
162 BOOL DOSFS_ToDosFCBFormat( LPCSTR name, LPSTR buffer )
164 static const char invalid_chars[] = INVALID_DOS_CHARS;
165 const char *p = name;
168 /* Check for "." and ".." */
172 strcpy( buffer, ". " );
178 return (!*p || (*p == '/') || (*p == '\\'));
181 for (i = 0; i < 8; i++)
198 if (strchr( invalid_chars, *p )) return FALSE;
199 buffer[i] = toupper(*p);
207 /* Skip all chars after wildcard up to first dot */
208 while (*p && (*p != '/') && (*p != '\\') && (*p != '.')) p++;
212 /* Check if name too long */
213 if (*p && (*p != '/') && (*p != '\\') && (*p != '.')) return FALSE;
215 if (*p == '.') p++; /* Skip dot */
217 for (i = 8; i < 11; i++)
227 return FALSE; /* Second extension not allowed */
235 if (strchr( invalid_chars, *p )) return FALSE;
236 buffer[i] = toupper(*p);
246 /***********************************************************************
247 * DOSFS_ToDosDTAFormat
249 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
250 * converting to upper-case in the process.
251 * File name can be terminated by '\0', '\\' or '/'.
252 * 'buffer' must be at least 13 characters long.
254 static void DOSFS_ToDosDTAFormat( LPCSTR name, LPSTR buffer )
258 memcpy( buffer, name, 8 );
259 for (p = buffer + 8; (p > buffer) && (p[-1] == ' '); p--);
261 memcpy( p, name + 8, 3 );
262 for (p += 3; p[-1] == ' '; p--);
263 if (p[-1] == '.') p--;
268 /***********************************************************************
271 * Check a DOS file name against a mask (both in FCB format).
273 static int DOSFS_MatchShort( const char *mask, const char *name )
276 for (i = 11; i > 0; i--, mask++, name++)
277 if ((*mask != '?') && (*mask != *name)) return 0;
282 /***********************************************************************
285 * Check a long file name against a mask.
287 static int DOSFS_MatchLong( const char *mask, const char *name,
290 if (!strcmp( mask, "*.*" )) return 1;
291 while (*name && *mask)
296 while (*mask == '*') mask++; /* Skip consecutive '*' */
297 if (!*mask) return 1;
298 if (case_sensitive) while (*name && (*name != *mask)) name++;
299 else while (*name && (toupper(*name) != toupper(*mask))) name++;
302 else if (*mask != '?')
306 if (*mask != *name) return 0;
308 else if (toupper(*mask) != toupper(*name)) return 0;
313 if (*mask == '.') mask++; /* Ignore trailing '.' in mask */
314 return (!*name && !*mask);
318 /***********************************************************************
321 static DOS_DIR *DOSFS_OpenDir( LPCSTR path )
323 DOS_DIR *dir = HeapAlloc( SystemHeap, 0, sizeof(*dir) );
326 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
330 /* Treat empty path as root directory. This simplifies path split into
331 directory and mask in several other places */
332 if (!*path) path = "/";
334 #ifdef VFAT_IOCTL_READDIR_BOTH
336 /* Check if the VFAT ioctl is supported on this directory */
338 if ((dir->fd = open( path, O_RDONLY )) != -1)
340 if (ioctl( dir->fd, VFAT_IOCTL_READDIR_BOTH, (long)dir->dirent ) == -1)
347 /* Set the file pointer back at the start of the directory */
348 lseek( dir->fd, 0, SEEK_SET );
353 #endif /* VFAT_IOCTL_READDIR_BOTH */
355 /* Now use the standard opendir/readdir interface */
357 if (!(dir->dir = opendir( path )))
359 HeapFree( SystemHeap, 0, dir );
366 /***********************************************************************
369 static void DOSFS_CloseDir( DOS_DIR *dir )
371 #ifdef VFAT_IOCTL_READDIR_BOTH
372 if (dir->fd != -1) close( dir->fd );
373 #endif /* VFAT_IOCTL_READDIR_BOTH */
374 if (dir->dir) closedir( dir->dir );
375 HeapFree( SystemHeap, 0, dir );
379 /***********************************************************************
382 static BOOL DOSFS_ReadDir( DOS_DIR *dir, LPCSTR *long_name,
385 struct dirent *dirent;
387 #ifdef VFAT_IOCTL_READDIR_BOTH
390 if (ioctl( dir->fd, VFAT_IOCTL_READDIR_BOTH, (long)dir->dirent ) != -1) {
391 if (!dir->dirent[0].d_reclen) return FALSE;
392 if (!DOSFS_ToDosFCBFormat( dir->dirent[0].d_name, dir->short_name ))
393 dir->short_name[0] = '\0';
394 *short_name = dir->short_name;
395 if (dir->dirent[1].d_name[0]) *long_name = dir->dirent[1].d_name;
396 else *long_name = dir->dirent[0].d_name;
400 #endif /* VFAT_IOCTL_READDIR_BOTH */
402 if (!(dirent = readdir( dir->dir ))) return FALSE;
403 *long_name = dirent->d_name;
409 /***********************************************************************
412 * Transform a Unix file name into a hashed DOS name. If the name is a valid
413 * DOS name, it is converted to upper-case; otherwise it is replaced by a
414 * hashed version that fits in 8.3 format.
415 * File name can be terminated by '\0', '\\' or '/'.
416 * 'buffer' must be at least 13 characters long.
418 static void DOSFS_Hash( LPCSTR name, LPSTR buffer, BOOL dir_format,
421 static const char invalid_chars[] = INVALID_DOS_CHARS "~.";
422 static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
429 if (dir_format) strcpy( buffer, " " );
431 if (DOSFS_ValidDOSName( name, ignore_case ))
433 /* Check for '.' and '..' */
437 if (!dir_format) buffer[1] = buffer[2] = '\0';
438 if (name[1] == '.') buffer[1] = '.';
442 /* Simply copy the name, converting to uppercase */
444 for (dst = buffer; !IS_END_OF_NAME(*name) && (*name != '.'); name++)
445 *dst++ = toupper(*name);
448 if (dir_format) dst = buffer + 8;
450 for (name++; !IS_END_OF_NAME(*name); name++)
451 *dst++ = toupper(*name);
453 if (!dir_format) *dst = '\0';
457 /* Compute the hash code of the file name */
458 /* If you know something about hash functions, feel free to */
459 /* insert a better algorithm here... */
462 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
463 hash = (hash<<3) ^ (hash>>5) ^ tolower(*p) ^ (tolower(p[1]) << 8);
464 hash = (hash<<3) ^ (hash>>5) ^ tolower(*p); /* Last character*/
468 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
469 hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
470 hash = (hash << 3) ^ (hash >> 5) ^ *p; /* Last character */
473 /* Find last dot for start of the extension */
474 for (p = name+1, ext = NULL; !IS_END_OF_NAME(*p); p++)
475 if (*p == '.') ext = p;
476 if (ext && IS_END_OF_NAME(ext[1]))
477 ext = NULL; /* Empty extension ignored */
479 /* Copy first 4 chars, replacing invalid chars with '_' */
480 for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
482 if (IS_END_OF_NAME(*p) || (p == ext)) break;
483 *dst++ = strchr( invalid_chars, *p ) ? '_' : toupper(*p);
485 /* Pad to 5 chars with '~' */
486 while (i-- >= 0) *dst++ = '~';
488 /* Insert hash code converted to 3 ASCII chars */
489 *dst++ = hash_chars[(hash >> 10) & 0x1f];
490 *dst++ = hash_chars[(hash >> 5) & 0x1f];
491 *dst++ = hash_chars[hash & 0x1f];
493 /* Copy the first 3 chars of the extension (if any) */
496 if (!dir_format) *dst++ = '.';
497 for (i = 3, ext++; (i > 0) && !IS_END_OF_NAME(*ext); i--, ext++)
498 *dst++ = strchr( invalid_chars, *ext ) ? '_' : toupper(*ext);
500 if (!dir_format) *dst = '\0';
504 /***********************************************************************
507 * Find the Unix file name in a given directory that corresponds to
508 * a file name (either in Unix or DOS format).
509 * File name can be terminated by '\0', '\\' or '/'.
510 * Return TRUE if OK, FALSE if no file name matches.
512 * 'long_buf' must be at least 'long_len' characters long. If the long name
513 * turns out to be larger than that, the function returns FALSE.
514 * 'short_buf' must be at least 13 characters long.
516 BOOL DOSFS_FindUnixName( LPCSTR path, LPCSTR name, LPSTR long_buf,
517 INT long_len, LPSTR short_buf, BOOL ignore_case)
520 LPCSTR long_name, short_name;
521 char dos_name[12], tmp_buf[13];
524 const char *p = strchr( name, '/' );
525 int len = p ? (int)(p - name) : strlen(name);
526 if ((p = strchr( name, '\\' ))) len = MIN( (int)(p - name), len );
527 /* Ignore trailing dots */
528 while (len > 1 && name[len-1] == '.') len--;
529 if (long_len < len + 1) return FALSE;
531 TRACE("%s,%s\n", path, name );
533 if (!DOSFS_ToDosFCBFormat( name, dos_name )) dos_name[0] = '\0';
535 if (!(dir = DOSFS_OpenDir( path )))
537 WARN("(%s,%s): can't open dir: %s\n",
538 path, name, strerror(errno) );
542 while ((ret = DOSFS_ReadDir( dir, &long_name, &short_name )))
544 /* Check against Unix name */
545 if (len == strlen(long_name))
549 if (!strncmp( long_name, name, len )) break;
553 if (!lstrncmpiA( long_name, name, len )) break;
558 /* Check against hashed DOS name */
561 DOSFS_Hash( long_name, tmp_buf, TRUE, ignore_case );
562 short_name = tmp_buf;
564 if (!strcmp( dos_name, short_name )) break;
569 if (long_buf) strcpy( long_buf, long_name );
573 DOSFS_ToDosDTAFormat( short_name, short_buf );
575 DOSFS_Hash( long_name, short_buf, FALSE, ignore_case );
577 TRACE("(%s,%s) -> %s (%s)\n",
578 path, name, long_name, short_buf ? short_buf : "***");
581 WARN("'%s' not found in '%s'\n", name, path);
582 DOSFS_CloseDir( dir );
587 /***********************************************************************
590 * Check if a DOS file name represents a DOS device and return the device.
592 const DOS_DEVICE *DOSFS_GetDevice( const char *name )
597 if (!name) return NULL; /* if FILE_DupUnixHandle was used */
598 if (name[0] && (name[1] == ':')) name += 2;
599 if ((p = strrchr( name, '/' ))) name = p + 1;
600 if ((p = strrchr( name, '\\' ))) name = p + 1;
601 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
603 const char *dev = DOSFS_Devices[i].name;
604 if (!lstrncmpiA( dev, name, strlen(dev) ))
606 p = name + strlen( dev );
607 if (!*p || (*p == '.')) return &DOSFS_Devices[i];
614 /***********************************************************************
615 * DOSFS_GetDeviceByHandle
617 const DOS_DEVICE *DOSFS_GetDeviceByHandle( HFILE hFile )
619 struct get_file_info_request *req = get_req_buffer();
622 if (!server_call( REQ_GET_FILE_INFO ) && (req->type == FILE_TYPE_UNKNOWN))
624 if ((req->attr >= 0) &&
625 (req->attr < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0])))
626 return &DOSFS_Devices[req->attr];
632 /***********************************************************************
635 * Open a DOS device. This might not map 1:1 into the UNIX device concept.
637 HFILE DOSFS_OpenDevice( const char *name, DWORD access )
642 if (!name) return (HFILE)NULL; /* if FILE_DupUnixHandle was used */
643 if (name[0] && (name[1] == ':')) name += 2;
644 if ((p = strrchr( name, '/' ))) name = p + 1;
645 if ((p = strrchr( name, '\\' ))) name = p + 1;
646 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
648 const char *dev = DOSFS_Devices[i].name;
649 if (!lstrncmpiA( dev, name, strlen(dev) ))
651 p = name + strlen( dev );
652 if (!*p || (*p == '.')) {
654 if (!strcmp(DOSFS_Devices[i].name,"NUL"))
655 return FILE_CreateFile( "/dev/null", access,
656 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
657 OPEN_EXISTING, 0, -1 );
658 if (!strcmp(DOSFS_Devices[i].name,"CON")) {
661 switch (access & (GENERIC_READ|GENERIC_WRITE)) {
663 to_dup = GetStdHandle( STD_INPUT_HANDLE );
666 to_dup = GetStdHandle( STD_OUTPUT_HANDLE );
669 FIXME("can't open CON read/write\n");
673 if (!DuplicateHandle( GetCurrentProcess(), to_dup, GetCurrentProcess(),
674 &handle, 0, FALSE, DUPLICATE_SAME_ACCESS ))
675 handle = HFILE_ERROR;
678 if (!strcmp(DOSFS_Devices[i].name,"SCSIMGR$") ||
679 !strcmp(DOSFS_Devices[i].name,"HPSCAN"))
681 return FILE_CreateDevice( i, access, NULL );
686 PROFILE_GetWineIniString("serialports",name,"",devname,sizeof devname);
690 TRACE_(file)("DOSFS_OpenDevice %s is %s\n",
691 DOSFS_Devices[i].name,devname);
692 r = FILE_CreateFile( devname, access,
693 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
694 OPEN_EXISTING, 0, -1 );
695 TRACE_(file)("Create_File return %08X\n",r);
700 FIXME("device open %s not supported (yet)\n",DOSFS_Devices[i].name);
709 /***********************************************************************
712 * Get the drive specified by a given path name (DOS or Unix format).
714 static int DOSFS_GetPathDrive( const char **name )
717 const char *p = *name;
719 if (*p && (p[1] == ':'))
721 drive = toupper(*p) - 'A';
724 else if (*p == '/') /* Absolute Unix path? */
726 if ((drive = DRIVE_FindDriveRoot( name )) == -1)
728 MESSAGE("Warning: %s not accessible from a DOS drive\n", *name );
729 /* Assume it really was a DOS name */
730 drive = DRIVE_GetCurrentDrive();
733 else drive = DRIVE_GetCurrentDrive();
735 if (!DRIVE_IsValid(drive))
737 SetLastError( ERROR_INVALID_DRIVE );
744 /***********************************************************************
747 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
748 * Unix name / short DOS name pair.
749 * Return FALSE if one of the path components does not exist. The last path
750 * component is only checked if 'check_last' is non-zero.
751 * The buffers pointed to by 'long_buf' and 'short_buf' must be
752 * at least MAX_PATHNAME_LEN long.
754 BOOL DOSFS_GetFullName( LPCSTR name, BOOL check_last, DOS_FULL_NAME *full )
758 char *p_l, *p_s, *root;
760 TRACE("%s (last=%d)\n", name, check_last );
762 if ((full->drive = DOSFS_GetPathDrive( &name )) == -1) return FALSE;
763 flags = DRIVE_GetFlags( full->drive );
765 lstrcpynA( full->long_name, DRIVE_GetRoot( full->drive ),
766 sizeof(full->long_name) );
767 if (full->long_name[1]) root = full->long_name + strlen(full->long_name);
768 else root = full->long_name; /* root directory */
770 strcpy( full->short_name, "A:\\" );
771 full->short_name[0] += full->drive;
773 if ((*name == '\\') || (*name == '/')) /* Absolute path */
775 while ((*name == '\\') || (*name == '/')) name++;
777 else /* Relative path */
779 lstrcpynA( root + 1, DRIVE_GetUnixCwd( full->drive ),
780 sizeof(full->long_name) - (root - full->long_name) - 1 );
781 if (root[1]) *root = '/';
782 lstrcpynA( full->short_name + 3, DRIVE_GetDosCwd( full->drive ),
783 sizeof(full->short_name) - 3 );
786 p_l = full->long_name[1] ? full->long_name + strlen(full->long_name)
788 p_s = full->short_name[3] ? full->short_name + strlen(full->short_name)
789 : full->short_name + 2;
792 while (*name && found)
794 /* Check for '.' and '..' */
798 if (IS_END_OF_NAME(name[1]))
801 while ((*name == '\\') || (*name == '/')) name++;
804 else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
807 while ((*name == '\\') || (*name == '/')) name++;
808 while ((p_l > root) && (*p_l != '/')) p_l--;
809 while ((p_s > full->short_name + 2) && (*p_s != '\\')) p_s--;
810 *p_l = *p_s = '\0'; /* Remove trailing separator */
815 /* Make sure buffers are large enough */
817 if ((p_s >= full->short_name + sizeof(full->short_name) - 14) ||
818 (p_l >= full->long_name + sizeof(full->long_name) - 1))
820 SetLastError( ERROR_PATH_NOT_FOUND );
824 /* Get the long and short name matching the file name */
826 if ((found = DOSFS_FindUnixName( full->long_name, name, p_l + 1,
827 sizeof(full->long_name) - (p_l - full->long_name) - 1,
828 p_s + 1, !(flags & DRIVE_CASE_SENSITIVE) )))
834 while (!IS_END_OF_NAME(*name)) name++;
836 else if (!check_last)
840 while (!IS_END_OF_NAME(*name) &&
841 (p_s < full->short_name + sizeof(full->short_name) - 1) &&
842 (p_l < full->long_name + sizeof(full->long_name) - 1))
844 *p_s++ = tolower(*name);
845 /* If the drive is case-sensitive we want to create new */
846 /* files in lower-case otherwise we can't reopen them */
847 /* under the same short name. */
848 if (flags & DRIVE_CASE_SENSITIVE) *p_l++ = tolower(*name);
854 while ((*name == '\\') || (*name == '/')) name++;
861 SetLastError( ERROR_FILE_NOT_FOUND );
864 if (*name) /* Not last */
866 SetLastError( ERROR_PATH_NOT_FOUND );
870 if (!full->long_name[0]) strcpy( full->long_name, "/" );
871 if (!full->short_name[2]) strcpy( full->short_name + 2, "\\" );
872 TRACE("returning %s = %s\n", full->long_name, full->short_name );
877 /***********************************************************************
878 * GetShortPathNameA (KERNEL32.271)
882 * longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
883 * *longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0
885 * more observations ( with NT 3.51 (WinDD) ):
886 * longpath <= 8.3 -> just copy longpath to shortpath
888 * a) file does not exist -> return 0, LastError = ERROR_FILE_NOT_FOUND
889 * b) file does exist -> set the short filename.
890 * - trailing slashes are reproduced in the short name, even if the
891 * file is not a directory
892 * - the absolute/relative path of the short name is reproduced like found
894 * - longpath and shortpath may have the same adress
897 DWORD WINAPI GetShortPathNameA( LPCSTR longpath, LPSTR shortpath,
900 DOS_FULL_NAME full_name;
902 DWORD sp = 0, lp = 0;
906 TRACE("%s\n", longpath);
909 SetLastError(ERROR_INVALID_PARAMETER);
913 SetLastError(ERROR_BAD_PATHNAME);
917 if ( ( tmpshortpath = HeapAlloc ( GetProcessHeap(), 0, MAX_PATHNAME_LEN ) ) == NULL ) {
918 SetLastError ( ERROR_NOT_ENOUGH_MEMORY );
922 /* check for drive letter */
923 if ( longpath[1] == ':' ) {
924 tmpshortpath[0] = longpath[0];
925 tmpshortpath[1] = ':';
929 if ( ( drive = DOSFS_GetPathDrive ( &longpath )) == -1 ) return 0;
930 flags = DRIVE_GetFlags ( drive );
932 while ( longpath[lp] ) {
934 /* check for path delimiters and reproduce them */
935 if ( longpath[lp] == '\\' || longpath[lp] == '/' ) {
936 tmpshortpath[sp] = longpath[lp];
942 tmplen = strcspn ( longpath + lp, "\\/" );
943 lstrcpynA ( tmpshortpath+sp, longpath + lp, tmplen+1 );
945 /* Check, if the current element is a valid dos name */
946 if ( DOSFS_ValidDOSName ( longpath + lp, !(flags & DRIVE_CASE_SENSITIVE) ) ) {
952 /* Check if the file exists and use the existing file name */
953 if ( DOSFS_GetFullName ( tmpshortpath, TRUE, &full_name ) ) {
954 lstrcpyA ( tmpshortpath+sp, strrchr ( full_name.short_name, '\\' ) + 1 );
955 sp += lstrlenA ( tmpshortpath+sp );
960 TRACE("not found!\n" );
961 SetLastError ( ERROR_FILE_NOT_FOUND );
965 lstrcpynA ( shortpath, tmpshortpath, shortlen );
966 TRACE("returning %s\n", shortpath );
967 tmplen = lstrlenA ( tmpshortpath );
968 HeapFree ( GetProcessHeap(), 0, tmpshortpath );
974 /***********************************************************************
975 * GetShortPathName32W (KERNEL32.272)
977 DWORD WINAPI GetShortPathNameW( LPCWSTR longpath, LPWSTR shortpath,
980 LPSTR longpathA, shortpathA;
983 longpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, longpath );
984 shortpathA = HEAP_xalloc ( GetProcessHeap(), 0, shortlen );
986 ret = GetShortPathNameA ( longpathA, shortpathA, shortlen );
987 lstrcpynAtoW ( shortpath, shortpathA, shortlen );
989 HeapFree( GetProcessHeap(), 0, longpathA );
990 HeapFree( GetProcessHeap(), 0, shortpathA );
996 /***********************************************************************
997 * GetLongPathName32A (KERNEL32.xxx)
999 DWORD WINAPI GetLongPathNameA( LPCSTR shortpath, LPSTR longpath,
1002 DOS_FULL_NAME full_name;
1007 if (!DOSFS_GetFullName( shortpath, TRUE, &full_name )) return 0;
1008 lstrcpynA( longpath, full_name.short_name, longlen );
1009 /* Do some hackery to get the long filename.
1010 * FIXME: Would be better if it returned the
1011 * long version of the directories too
1013 longfilename = strrchr(full_name.long_name, '/')+1;
1014 if (longpath != NULL) {
1015 if ((p = strrchr( longpath, '\\' )) != NULL) {
1017 longlen -= (p-longpath);
1018 lstrcpynA( p, longfilename , longlen);
1022 ((strrchr( full_name.short_name, '\\' ) - full_name.short_name) + 1);
1023 return shortpathlen + strlen( longfilename );
1027 /***********************************************************************
1028 * GetLongPathName32W (KERNEL32.269)
1030 DWORD WINAPI GetLongPathNameW( LPCWSTR shortpath, LPWSTR longpath,
1033 DOS_FULL_NAME full_name;
1035 LPSTR shortpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, shortpath );
1037 /* FIXME: is it correct to always return a fully qualified short path? */
1038 if (DOSFS_GetFullName( shortpathA, TRUE, &full_name ))
1040 ret = strlen( full_name.short_name );
1041 lstrcpynAtoW( longpath, full_name.long_name, longlen );
1043 HeapFree( GetProcessHeap(), 0, shortpathA );
1048 /***********************************************************************
1049 * DOSFS_DoGetFullPathName
1051 * Implementation of GetFullPathName32A/W.
1053 static DWORD DOSFS_DoGetFullPathName( LPCSTR name, DWORD len, LPSTR result,
1056 char buffer[MAX_PATHNAME_LEN];
1061 /* last possible position for a char != 0 */
1062 char *endchar = buffer + sizeof(buffer) - 2;
1065 TRACE("converting '%s'\n", name );
1067 if (!name || ((drive = DOSFS_GetPathDrive( &name )) == -1) )
1068 { SetLastError( ERROR_INVALID_PARAMETER );
1075 if (IS_END_OF_NAME(*name) && (*name)) /* Absolute path */
1077 while (((*name == '\\') || (*name == '/')) && (!*endchar) )
1080 else /* Relative path or empty path */
1083 lstrcpynA( p, DRIVE_GetDosCwd(drive), sizeof(buffer) - 4 );
1096 if (IS_END_OF_NAME(name[1]))
1099 while ((*name == '\\') || (*name == '/')) name++;
1102 else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
1105 while ((*name == '\\') || (*name == '/')) name++;
1107 if (p < buffer + 3) /* no previous dir component */
1109 p--; /* skip previously added '\\' */
1110 while ((*p == '\\') || (*p == '/')) p--;
1111 /* skip previous dir component */
1112 while ((*p != '\\') && (*p != '/')) p--;
1118 { SetLastError( ERROR_PATH_NOT_FOUND );
1121 while (!IS_END_OF_NAME(*name) && (!*endchar) )
1123 while (((*name == '\\') || (*name == '/')) && (!*endchar) )
1128 if (!(DRIVE_GetFlags(drive) & DRIVE_CASE_PRESERVING))
1129 CharUpperA( buffer );
1134 lstrcpynAtoW( (LPWSTR)result, buffer, len );
1136 lstrcpynA( result, buffer, len );
1139 TRACE("returning '%s'\n", buffer );
1141 /* If the lpBuffer buffer is too small, the return value is the
1142 size of the buffer, in characters, required to hold the path. */
1144 ret = strlen(buffer);
1147 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1153 /***********************************************************************
1154 * GetFullPathName32A (KERNEL32.272)
1156 * if the path closed with '\', *lastpart is 0
1158 DWORD WINAPI GetFullPathNameA( LPCSTR name, DWORD len, LPSTR buffer,
1161 DWORD ret = DOSFS_DoGetFullPathName( name, len, buffer, FALSE );
1162 if (ret && buffer && lastpart)
1164 LPSTR p = buffer + strlen(buffer);
1168 while ((p > buffer + 2) && (*p != '\\')) p--;
1171 else *lastpart = NULL;
1177 /***********************************************************************
1178 * GetFullPathName32W (KERNEL32.273)
1180 DWORD WINAPI GetFullPathNameW( LPCWSTR name, DWORD len, LPWSTR buffer,
1183 LPSTR nameA = HEAP_strdupWtoA( GetProcessHeap(), 0, name );
1184 DWORD ret = DOSFS_DoGetFullPathName( nameA, len, (LPSTR)buffer, TRUE );
1185 HeapFree( GetProcessHeap(), 0, nameA );
1186 if (ret && buffer && lastpart)
1188 LPWSTR p = buffer + lstrlenW(buffer);
1189 if (*p != (WCHAR)'\\')
1191 while ((p > buffer + 2) && (*p != (WCHAR)'\\')) p--;
1194 else *lastpart = NULL;
1199 /***********************************************************************
1202 static int DOSFS_FindNextEx( FIND_FIRST_INFO *info, WIN32_FIND_DATAA *entry )
1204 BYTE attr = info->attr | FA_UNUSED | FA_ARCHIVE | FA_RDONLY;
1205 UINT flags = DRIVE_GetFlags( info->drive );
1206 char *p, buffer[MAX_PATHNAME_LEN];
1207 const char *drive_path;
1209 LPCSTR long_name, short_name;
1210 BY_HANDLE_FILE_INFORMATION fileinfo;
1213 if ((info->attr & ~(FA_UNUSED | FA_ARCHIVE | FA_RDONLY)) == FA_LABEL)
1215 if (info->cur_pos) return 0;
1216 entry->dwFileAttributes = FILE_ATTRIBUTE_LABEL;
1217 DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftCreationTime, 0 );
1218 DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftLastAccessTime, 0 );
1219 DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftLastWriteTime, 0 );
1220 entry->nFileSizeHigh = 0;
1221 entry->nFileSizeLow = 0;
1222 entry->dwReserved0 = 0;
1223 entry->dwReserved1 = 0;
1224 DOSFS_ToDosDTAFormat( DRIVE_GetLabel( info->drive ), entry->cFileName );
1225 strcpy( entry->cAlternateFileName, entry->cFileName );
1230 drive_path = info->path + strlen(DRIVE_GetRoot( info->drive ));
1231 while ((*drive_path == '/') || (*drive_path == '\\')) drive_path++;
1232 drive_root = !*drive_path;
1234 lstrcpynA( buffer, info->path, sizeof(buffer) - 1 );
1235 strcat( buffer, "/" );
1236 p = buffer + strlen(buffer);
1238 while (DOSFS_ReadDir( info->dir, &long_name, &short_name ))
1242 /* Don't return '.' and '..' in the root of the drive */
1243 if (drive_root && (long_name[0] == '.') &&
1244 (!long_name[1] || ((long_name[1] == '.') && !long_name[2])))
1247 /* Check the long mask */
1249 if (info->long_mask)
1251 if (!DOSFS_MatchLong( info->long_mask, long_name,
1252 flags & DRIVE_CASE_SENSITIVE )) continue;
1255 /* Check the short mask */
1257 if (info->short_mask)
1261 DOSFS_Hash( long_name, dos_name, TRUE,
1262 !(flags & DRIVE_CASE_SENSITIVE) );
1263 short_name = dos_name;
1265 if (!DOSFS_MatchShort( info->short_mask, short_name )) continue;
1268 /* Check the file attributes */
1270 lstrcpynA( p, long_name, sizeof(buffer) - (int)(p - buffer) );
1271 if (!FILE_Stat( buffer, &fileinfo ))
1273 WARN("can't stat %s\n", buffer);
1276 if (fileinfo.dwFileAttributes & ~attr) continue;
1278 /* We now have a matching entry; fill the result and return */
1280 entry->dwFileAttributes = fileinfo.dwFileAttributes;
1281 entry->ftCreationTime = fileinfo.ftCreationTime;
1282 entry->ftLastAccessTime = fileinfo.ftLastAccessTime;
1283 entry->ftLastWriteTime = fileinfo.ftLastWriteTime;
1284 entry->nFileSizeHigh = fileinfo.nFileSizeHigh;
1285 entry->nFileSizeLow = fileinfo.nFileSizeLow;
1288 DOSFS_ToDosDTAFormat( short_name, entry->cAlternateFileName );
1290 DOSFS_Hash( long_name, entry->cAlternateFileName, FALSE,
1291 !(flags & DRIVE_CASE_SENSITIVE) );
1293 lstrcpynA( entry->cFileName, long_name, sizeof(entry->cFileName) );
1294 if (!(flags & DRIVE_CASE_PRESERVING)) CharLowerA( entry->cFileName );
1295 TRACE("returning %s (%s) %02lx %ld\n",
1296 entry->cFileName, entry->cAlternateFileName,
1297 entry->dwFileAttributes, entry->nFileSizeLow );
1300 return 0; /* End of directory */
1303 /***********************************************************************
1306 * Find the next matching file. Return the number of entries read to find
1307 * the matching one, or 0 if no more entries.
1308 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
1309 * file name mask. Either or both can be NULL.
1311 * NOTE: This is supposed to be only called by the int21 emulation
1312 * routines. Thus, we should own the Win16Mutex anyway.
1313 * Nevertheless, we explicitly enter it to ensure the static
1314 * directory cache is protected.
1316 int DOSFS_FindNext( const char *path, const char *short_mask,
1317 const char *long_mask, int drive, BYTE attr,
1318 int skip, WIN32_FIND_DATAA *entry )
1320 static FIND_FIRST_INFO info = { NULL };
1321 LPCSTR short_name, long_name;
1324 SYSLEVEL_EnterWin16Lock();
1326 /* Check the cached directory */
1327 if (!(info.dir && info.path == path && info.short_mask == short_mask
1328 && info.long_mask == long_mask && info.drive == drive
1329 && info.attr == attr && info.cur_pos <= skip))
1331 /* Not in the cache, open it anew */
1332 if (info.dir) DOSFS_CloseDir( info.dir );
1334 info.path = (LPSTR)path;
1335 info.long_mask = (LPSTR)long_mask;
1336 info.short_mask = (LPSTR)short_mask;
1340 info.dir = DOSFS_OpenDir( info.path );
1343 /* Skip to desired position */
1344 while (info.cur_pos < skip)
1345 if (info.dir && DOSFS_ReadDir( info.dir, &long_name, &short_name ))
1350 if (info.dir && info.cur_pos == skip && DOSFS_FindNextEx( &info, entry ))
1351 count = info.cur_pos - skip;
1357 if (info.dir) DOSFS_CloseDir( info.dir );
1358 memset( &info, '\0', sizeof(info) );
1361 SYSLEVEL_LeaveWin16Lock();
1368 /*************************************************************************
1369 * FindFirstFile16 (KERNEL.413)
1371 HANDLE16 WINAPI FindFirstFile16( LPCSTR path, WIN32_FIND_DATAA *data )
1373 DOS_FULL_NAME full_name;
1375 FIND_FIRST_INFO *info;
1377 data->dwReserved0 = data->dwReserved1 = 0x0;
1378 if (!path) return 0;
1379 if (!DOSFS_GetFullName( path, FALSE, &full_name ))
1380 return INVALID_HANDLE_VALUE16;
1381 if (!(handle = GlobalAlloc16( GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO) )))
1382 return INVALID_HANDLE_VALUE16;
1383 info = (FIND_FIRST_INFO *)GlobalLock16( handle );
1384 info->path = HEAP_strdupA( SystemHeap, 0, full_name.long_name );
1385 info->long_mask = strrchr( info->path, '/' );
1386 *(info->long_mask++) = '\0';
1387 info->short_mask = NULL;
1389 if (path[0] && (path[1] == ':')) info->drive = toupper(*path) - 'A';
1390 else info->drive = DRIVE_GetCurrentDrive();
1393 info->dir = DOSFS_OpenDir( info->path );
1395 GlobalUnlock16( handle );
1396 if (!FindNextFile16( handle, data ))
1398 FindClose16( handle );
1399 SetLastError( ERROR_NO_MORE_FILES );
1400 return INVALID_HANDLE_VALUE16;
1406 /*************************************************************************
1407 * FindFirstFile32A (KERNEL32.123)
1409 HANDLE WINAPI FindFirstFileA( LPCSTR path, WIN32_FIND_DATAA *data )
1411 HANDLE handle = FindFirstFile16( path, data );
1412 if (handle == INVALID_HANDLE_VALUE16) return INVALID_HANDLE_VALUE;
1417 /*************************************************************************
1418 * FindFirstFile32W (KERNEL32.124)
1420 HANDLE WINAPI FindFirstFileW( LPCWSTR path, WIN32_FIND_DATAW *data )
1422 WIN32_FIND_DATAA dataA;
1423 LPSTR pathA = HEAP_strdupWtoA( GetProcessHeap(), 0, path );
1424 HANDLE handle = FindFirstFileA( pathA, &dataA );
1425 HeapFree( GetProcessHeap(), 0, pathA );
1426 if (handle != INVALID_HANDLE_VALUE)
1428 data->dwFileAttributes = dataA.dwFileAttributes;
1429 data->ftCreationTime = dataA.ftCreationTime;
1430 data->ftLastAccessTime = dataA.ftLastAccessTime;
1431 data->ftLastWriteTime = dataA.ftLastWriteTime;
1432 data->nFileSizeHigh = dataA.nFileSizeHigh;
1433 data->nFileSizeLow = dataA.nFileSizeLow;
1434 lstrcpyAtoW( data->cFileName, dataA.cFileName );
1435 lstrcpyAtoW( data->cAlternateFileName, dataA.cAlternateFileName );
1441 /*************************************************************************
1442 * FindNextFile16 (KERNEL.414)
1444 BOOL16 WINAPI FindNextFile16( HANDLE16 handle, WIN32_FIND_DATAA *data )
1446 FIND_FIRST_INFO *info;
1448 if (!(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
1450 SetLastError( ERROR_INVALID_HANDLE );
1453 GlobalUnlock16( handle );
1454 if (!info->path || !info->dir)
1456 SetLastError( ERROR_NO_MORE_FILES );
1459 if (!DOSFS_FindNextEx( info, data ))
1461 DOSFS_CloseDir( info->dir ); info->dir = NULL;
1462 HeapFree( SystemHeap, 0, info->path );
1463 info->path = info->long_mask = NULL;
1464 SetLastError( ERROR_NO_MORE_FILES );
1471 /*************************************************************************
1472 * FindNextFile32A (KERNEL32.126)
1474 BOOL WINAPI FindNextFileA( HANDLE handle, WIN32_FIND_DATAA *data )
1476 return FindNextFile16( handle, data );
1480 /*************************************************************************
1481 * FindNextFile32W (KERNEL32.127)
1483 BOOL WINAPI FindNextFileW( HANDLE handle, WIN32_FIND_DATAW *data )
1485 WIN32_FIND_DATAA dataA;
1486 if (!FindNextFileA( handle, &dataA )) return FALSE;
1487 data->dwFileAttributes = dataA.dwFileAttributes;
1488 data->ftCreationTime = dataA.ftCreationTime;
1489 data->ftLastAccessTime = dataA.ftLastAccessTime;
1490 data->ftLastWriteTime = dataA.ftLastWriteTime;
1491 data->nFileSizeHigh = dataA.nFileSizeHigh;
1492 data->nFileSizeLow = dataA.nFileSizeLow;
1493 lstrcpyAtoW( data->cFileName, dataA.cFileName );
1494 lstrcpyAtoW( data->cAlternateFileName, dataA.cAlternateFileName );
1499 /*************************************************************************
1500 * FindClose16 (KERNEL.415)
1502 BOOL16 WINAPI FindClose16( HANDLE16 handle )
1504 FIND_FIRST_INFO *info;
1506 if ((handle == INVALID_HANDLE_VALUE16) ||
1507 !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
1509 SetLastError( ERROR_INVALID_HANDLE );
1512 if (info->dir) DOSFS_CloseDir( info->dir );
1513 if (info->path) HeapFree( SystemHeap, 0, info->path );
1514 GlobalUnlock16( handle );
1515 GlobalFree16( handle );
1520 /*************************************************************************
1521 * FindClose32 (KERNEL32.119)
1523 BOOL WINAPI FindClose( HANDLE handle )
1525 return FindClose16( (HANDLE16)handle );
1529 /***********************************************************************
1530 * DOSFS_UnixTimeToFileTime
1532 * Convert a Unix time to FILETIME format.
1533 * The FILETIME structure is a 64-bit value representing the number of
1534 * 100-nanosecond intervals since January 1, 1601, 0:00.
1535 * 'remainder' is the nonnegative number of 100-ns intervals
1536 * corresponding to the time fraction smaller than 1 second that
1537 * couldn't be stored in the time_t value.
1539 void DOSFS_UnixTimeToFileTime( time_t unix_time, FILETIME *filetime,
1545 The time difference between 1 January 1601, 00:00:00 and
1546 1 January 1970, 00:00:00 is 369 years, plus the leap years
1547 from 1604 to 1968, excluding 1700, 1800, 1900.
1548 This makes (1968 - 1600) / 4 - 3 = 89 leap days, and a total
1551 Any day in that period had 24 * 60 * 60 = 86400 seconds.
1553 The time difference is 134774 * 86400 * 10000000, which can be written
1555 27111902 * 2^32 + 3577643008
1556 413 * 2^48 + 45534 * 2^32 + 54590 * 2^16 + 32768
1558 If you find that these constants are buggy, please change them in all
1559 instances in both conversion functions.
1562 There are two versions, one of them uses long long variables and
1563 is presumably faster but not ISO C. The other one uses standard C
1564 data types and operations but relies on the assumption that negative
1565 numbers are stored as 2's complement (-1 is 0xffff....). If this
1566 assumption is violated, dates before 1970 will not convert correctly.
1567 This should however work on any reasonable architecture where WINE
1572 Take care not to remove the casts. I have tested these functions
1573 (in both versions) for a lot of numbers. I would be interested in
1574 results on other compilers than GCC.
1576 The operations have been designed to account for the possibility
1577 of 64-bit time_t in future UNICES. Even the versions without
1578 internal long long numbers will work if time_t only is 64 bit.
1579 A 32-bit shift, which was necessary for that operation, turned out
1580 not to work correctly in GCC, besides giving the warning. So I
1581 used a double 16-bit shift instead. Numbers are in the ISO version
1582 represented by three limbs, the most significant with 32 bit, the
1583 other two with 16 bit each.
1585 As the modulo-operator % is not well-defined for negative numbers,
1586 negative divisors have been avoided in DOSFS_FileTimeToUnixTime.
1588 There might be quicker ways to do this in C. Certainly so in
1591 Claus Fischer, fischer@iue.tuwien.ac.at
1594 #if SIZEOF_LONG_LONG >= 8
1595 # define USE_LONG_LONG 1
1597 # define USE_LONG_LONG 0
1600 #if USE_LONG_LONG /* gcc supports long long type */
1602 long long int t = unix_time;
1604 t += 116444736000000000LL;
1606 filetime->dwLowDateTime = (UINT)t;
1607 filetime->dwHighDateTime = (UINT)(t >> 32);
1609 #else /* ISO version */
1611 UINT a0; /* 16 bit, low bits */
1612 UINT a1; /* 16 bit, medium bits */
1613 UINT a2; /* 32 bit, high bits */
1615 /* Copy the unix time to a2/a1/a0 */
1616 a0 = unix_time & 0xffff;
1617 a1 = (unix_time >> 16) & 0xffff;
1618 /* This is obsolete if unix_time is only 32 bits, but it does not hurt.
1619 Do not replace this by >> 32, it gives a compiler warning and it does
1621 a2 = (unix_time >= 0 ? (unix_time >> 16) >> 16 :
1622 ~((~unix_time >> 16) >> 16));
1624 /* Multiply a by 10000000 (a = a2/a1/a0)
1625 Split the factor into 10000 * 1000 which are both less than 0xffff. */
1627 a1 = a1 * 10000 + (a0 >> 16);
1628 a2 = a2 * 10000 + (a1 >> 16);
1633 a1 = a1 * 1000 + (a0 >> 16);
1634 a2 = a2 * 1000 + (a1 >> 16);
1638 /* Add the time difference and the remainder */
1639 a0 += 32768 + (remainder & 0xffff);
1640 a1 += 54590 + (remainder >> 16 ) + (a0 >> 16);
1641 a2 += 27111902 + (a1 >> 16);
1646 filetime->dwLowDateTime = (a1 << 16) + a0;
1647 filetime->dwHighDateTime = a2;
1652 /***********************************************************************
1653 * DOSFS_FileTimeToUnixTime
1655 * Convert a FILETIME format to Unix time.
1656 * If not NULL, 'remainder' contains the fractional part of the filetime,
1657 * in the range of [0..9999999] (even if time_t is negative).
1659 time_t DOSFS_FileTimeToUnixTime( const FILETIME *filetime, DWORD *remainder )
1661 /* Read the comment in the function DOSFS_UnixTimeToFileTime. */
1664 long long int t = filetime->dwHighDateTime;
1666 t += (UINT)filetime->dwLowDateTime;
1667 t -= 116444736000000000LL;
1670 if (remainder) *remainder = 9999999 - (-t - 1) % 10000000;
1671 return -1 - ((-t - 1) / 10000000);
1675 if (remainder) *remainder = t % 10000000;
1676 return t / 10000000;
1679 #else /* ISO version */
1681 UINT a0; /* 16 bit, low bits */
1682 UINT a1; /* 16 bit, medium bits */
1683 UINT a2; /* 32 bit, high bits */
1684 UINT r; /* remainder of division */
1685 unsigned int carry; /* carry bit for subtraction */
1686 int negative; /* whether a represents a negative value */
1688 /* Copy the time values to a2/a1/a0 */
1689 a2 = (UINT)filetime->dwHighDateTime;
1690 a1 = ((UINT)filetime->dwLowDateTime ) >> 16;
1691 a0 = ((UINT)filetime->dwLowDateTime ) & 0xffff;
1693 /* Subtract the time difference */
1694 if (a0 >= 32768 ) a0 -= 32768 , carry = 0;
1695 else a0 += (1 << 16) - 32768 , carry = 1;
1697 if (a1 >= 54590 + carry) a1 -= 54590 + carry, carry = 0;
1698 else a1 += (1 << 16) - 54590 - carry, carry = 1;
1700 a2 -= 27111902 + carry;
1702 /* If a is negative, replace a by (-1-a) */
1703 negative = (a2 >= ((UINT)1) << 31);
1706 /* Set a to -a - 1 (a is a2/a1/a0) */
1712 /* Divide a by 10000000 (a = a2/a1/a0), put the rest into r.
1713 Split the divisor into 10000 * 1000 which are both less than 0xffff. */
1714 a1 += (a2 % 10000) << 16;
1716 a0 += (a1 % 10000) << 16;
1721 a1 += (a2 % 1000) << 16;
1723 a0 += (a1 % 1000) << 16;
1725 r += (a0 % 1000) * 10000;
1728 /* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
1731 /* Set a to -a - 1 (a is a2/a1/a0) */
1739 if (remainder) *remainder = r;
1741 /* Do not replace this by << 32, it gives a compiler warning and it does
1743 return ((((time_t)a2) << 16) << 16) + (a1 << 16) + a0;
1748 /***********************************************************************
1749 * DosDateTimeToFileTime (KERNEL32.76)
1751 BOOL WINAPI DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft)
1755 newtm.tm_sec = (fattime & 0x1f) * 2;
1756 newtm.tm_min = (fattime >> 5) & 0x3f;
1757 newtm.tm_hour = (fattime >> 11);
1758 newtm.tm_mday = (fatdate & 0x1f);
1759 newtm.tm_mon = ((fatdate >> 5) & 0x0f) - 1;
1760 newtm.tm_year = (fatdate >> 9) + 80;
1761 DOSFS_UnixTimeToFileTime( mktime( &newtm ), ft, 0 );
1766 /***********************************************************************
1767 * FileTimeToDosDateTime (KERNEL32.111)
1769 BOOL WINAPI FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate,
1772 time_t unixtime = DOSFS_FileTimeToUnixTime( ft, NULL );
1773 struct tm *tm = localtime( &unixtime );
1775 *fattime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
1777 *fatdate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
1783 /***********************************************************************
1784 * LocalFileTimeToFileTime (KERNEL32.373)
1786 BOOL WINAPI LocalFileTimeToFileTime( const FILETIME *localft,
1792 /* convert from local to UTC. Perhaps not correct. FIXME */
1793 time_t unixtime = DOSFS_FileTimeToUnixTime( localft, &remainder );
1794 xtm = gmtime( &unixtime );
1795 DOSFS_UnixTimeToFileTime( mktime(xtm), utcft, remainder );
1800 /***********************************************************************
1801 * FileTimeToLocalFileTime (KERNEL32.112)
1803 BOOL WINAPI FileTimeToLocalFileTime( const FILETIME *utcft,
1804 LPFILETIME localft )
1807 /* convert from UTC to local. Perhaps not correct. FIXME */
1808 time_t unixtime = DOSFS_FileTimeToUnixTime( utcft, &remainder );
1810 struct tm *xtm = localtime( &unixtime );
1813 localtime = timegm(xtm);
1814 DOSFS_UnixTimeToFileTime( localtime, localft, remainder );
1817 struct tm *xtm,*gtm;
1820 xtm = localtime( &unixtime );
1821 gtm = gmtime( &unixtime );
1822 time1 = mktime(xtm);
1823 time2 = mktime(gtm);
1824 DOSFS_UnixTimeToFileTime( 2*time1-time2, localft, remainder );
1830 /***********************************************************************
1831 * FileTimeToSystemTime (KERNEL32.113)
1833 BOOL WINAPI FileTimeToSystemTime( const FILETIME *ft, LPSYSTEMTIME syst )
1837 time_t xtime = DOSFS_FileTimeToUnixTime( ft, &remainder );
1838 xtm = gmtime(&xtime);
1839 syst->wYear = xtm->tm_year+1900;
1840 syst->wMonth = xtm->tm_mon + 1;
1841 syst->wDayOfWeek = xtm->tm_wday;
1842 syst->wDay = xtm->tm_mday;
1843 syst->wHour = xtm->tm_hour;
1844 syst->wMinute = xtm->tm_min;
1845 syst->wSecond = xtm->tm_sec;
1846 syst->wMilliseconds = remainder / 10000;
1850 /***********************************************************************
1851 * QueryDosDeviceA (KERNEL32.413)
1853 * returns array of strings terminated by \0, terminated by \0
1855 DWORD WINAPI QueryDosDeviceA(LPCSTR devname,LPSTR target,DWORD bufsize)
1860 TRACE("(%s,...)\n", devname ? devname : "<null>");
1862 /* return known MSDOS devices */
1863 strcpy(buffer,"CON COM1 COM2 LPT1 NUL ");
1864 while ((s=strchr(buffer,' ')))
1867 lstrcpynA(target,buffer,bufsize);
1868 return strlen(buffer);
1870 strcpy(buffer,"\\DEV\\");
1871 strcat(buffer,devname);
1872 if ((s=strchr(buffer,':'))) *s='\0';
1873 lstrcpynA(target,buffer,bufsize);
1874 return strlen(buffer);
1878 /***********************************************************************
1879 * QueryDosDeviceW (KERNEL32.414)
1881 * returns array of strings terminated by \0, terminated by \0
1883 DWORD WINAPI QueryDosDeviceW(LPCWSTR devname,LPWSTR target,DWORD bufsize)
1885 LPSTR devnameA = devname?HEAP_strdupWtoA(GetProcessHeap(),0,devname):NULL;
1886 LPSTR targetA = (LPSTR)HEAP_xalloc(GetProcessHeap(),0,bufsize);
1887 DWORD ret = QueryDosDeviceA(devnameA,targetA,bufsize);
1889 lstrcpynAtoW(target,targetA,bufsize);
1890 if (devnameA) HeapFree(GetProcessHeap(),0,devnameA);
1891 if (targetA) HeapFree(GetProcessHeap(),0,targetA);
1896 /***********************************************************************
1897 * SystemTimeToFileTime (KERNEL32.526)
1899 BOOL WINAPI SystemTimeToFileTime( const SYSTEMTIME *syst, LPFILETIME ft )
1905 struct tm xtm,*local_tm,*utc_tm;
1906 time_t localtim,utctime;
1909 xtm.tm_year = syst->wYear-1900;
1910 xtm.tm_mon = syst->wMonth - 1;
1911 xtm.tm_wday = syst->wDayOfWeek;
1912 xtm.tm_mday = syst->wDay;
1913 xtm.tm_hour = syst->wHour;
1914 xtm.tm_min = syst->wMinute;
1915 xtm.tm_sec = syst->wSecond; /* this is UTC */
1918 utctime = timegm(&xtm);
1919 DOSFS_UnixTimeToFileTime( utctime, ft,
1920 syst->wMilliseconds * 10000 );
1922 localtim = mktime(&xtm); /* now we've got local time */
1923 local_tm = localtime(&localtim);
1924 utc_tm = gmtime(&localtim);
1925 utctime = mktime(utc_tm);
1926 DOSFS_UnixTimeToFileTime( 2*localtim -utctime, ft,
1927 syst->wMilliseconds * 10000 );
1932 /***********************************************************************
1933 * DefineDosDeviceA (KERNEL32.182)
1935 BOOL WINAPI DefineDosDeviceA(DWORD flags,LPCSTR devname,LPCSTR targetpath) {
1936 FIXME("(0x%08lx,%s,%s),stub!\n",flags,devname,targetpath);
1937 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);