2 * DOS file system functions
4 * Copyright 1993 Erik Bos
5 * Copyright 1996 Alexandre Julliard
17 #include <sys/ioctl.h>
30 /* Define the VFAT ioctl to get both short and long file names */
31 /* FIXME: is it possible to get this to work on other systems? */
33 #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, long)
34 /* We want the real kernel dirent structure, not the libc one */
39 unsigned short d_reclen;
44 #undef VFAT_IOCTL_READDIR_BOTH /* just in case... */
47 /* Chars we don't want to see in DOS file names */
48 #define INVALID_DOS_CHARS "*?<>|\"+=,;[] \345"
50 static const DOS_DEVICE DOSFS_Devices[] =
51 /* name, device flags (see Int 21/AX=0x4400) */
65 { "SCSIMGR$", 0xc0c0 },
69 #define GET_DRIVE(path) \
70 (((path)[1] == ':') ? toupper((path)[0]) - 'A' : DOSFS_CurDrive)
72 /* DOS extended error status */
73 WORD DOS_ExtendedError;
78 /* Directory info for DOSFS_ReadDir */
82 #ifdef VFAT_IOCTL_READDIR_BOTH
85 KERNEL_DIRENT dirent[2];
89 /* Info structure for FindFirstFile handle */
103 /***********************************************************************
106 * Return 1 if Unix file 'name' is also a valid MS-DOS name
107 * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
108 * File name can be terminated by '\0', '\\' or '/'.
110 static int DOSFS_ValidDOSName( const char *name, int ignore_case )
112 static const char invalid_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS;
113 const char *p = name;
114 const char *invalid = ignore_case ? (invalid_chars + 26) : invalid_chars;
119 /* Check for "." and ".." */
122 /* All other names beginning with '.' are invalid */
123 return (IS_END_OF_NAME(*p));
125 while (!IS_END_OF_NAME(*p))
127 if (strchr( invalid, *p )) return 0; /* Invalid char */
128 if (*p == '.') break; /* Start of the extension */
129 if (++len > 8) return 0; /* Name too long */
132 if (*p != '.') return 1; /* End of name */
134 if (IS_END_OF_NAME(*p)) return 0; /* Empty extension not allowed */
136 while (!IS_END_OF_NAME(*p))
138 if (strchr( invalid, *p )) return 0; /* Invalid char */
139 if (*p == '.') return 0; /* Second extension not allowed */
140 if (++len > 3) return 0; /* Extension too long */
147 /***********************************************************************
148 * DOSFS_ToDosFCBFormat
150 * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
151 * expanding wild cards and converting to upper-case in the process.
152 * File name can be terminated by '\0', '\\' or '/'.
153 * Return FALSE if the name is not a valid DOS name.
154 * 'buffer' must be at least 12 characters long.
156 BOOL32 DOSFS_ToDosFCBFormat( LPCSTR name, LPSTR buffer )
158 static const char invalid_chars[] = INVALID_DOS_CHARS;
159 const char *p = name;
162 /* Check for "." and ".." */
166 strcpy( buffer, ". " );
172 return (!*p || (*p == '/') || (*p == '\\'));
175 for (i = 0; i < 8; i++)
192 if (strchr( invalid_chars, *p )) return FALSE;
193 buffer[i] = toupper(*p);
201 /* Skip all chars after wildcard up to first dot */
202 while (*p && (*p != '/') && (*p != '\\') && (*p != '.')) p++;
206 /* Check if name too long */
207 if (*p && (*p != '/') && (*p != '\\') && (*p != '.')) return FALSE;
209 if (*p == '.') p++; /* Skip dot */
211 for (i = 8; i < 11; i++)
221 return FALSE; /* Second extension not allowed */
229 if (strchr( invalid_chars, *p )) return FALSE;
230 buffer[i] = toupper(*p);
240 /***********************************************************************
241 * DOSFS_ToDosDTAFormat
243 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
244 * converting to upper-case in the process.
245 * File name can be terminated by '\0', '\\' or '/'.
246 * 'buffer' must be at least 13 characters long.
248 static void DOSFS_ToDosDTAFormat( LPCSTR name, LPSTR buffer )
252 memcpy( buffer, name, 8 );
253 for (p = buffer + 8; (p > buffer) && (p[-1] == ' '); p--);
255 memcpy( p, name + 8, 3 );
256 for (p += 3; p[-1] == ' '; p--);
257 if (p[-1] == '.') p--;
262 /***********************************************************************
265 * Check a DOS file name against a mask (both in FCB format).
267 static int DOSFS_MatchShort( const char *mask, const char *name )
270 for (i = 11; i > 0; i--, mask++, name++)
271 if ((*mask != '?') && (*mask != *name)) return 0;
276 /***********************************************************************
279 * Check a long file name against a mask.
281 static int DOSFS_MatchLong( const char *mask, const char *name,
284 if (!strcmp( mask, "*.*" )) return 1;
285 while (*name && *mask)
290 while (*mask == '*') mask++; /* Skip consecutive '*' */
291 if (!*mask) return 1;
292 if (case_sensitive) while (*name && (*name != *mask)) name++;
293 else while (*name && (toupper(*name) != toupper(*mask))) name++;
294 if (!*name) return 0;
296 else if (*mask != '?')
300 if (*mask != *name) return 0;
302 else if (toupper(*mask) != toupper(*name)) return 0;
307 return (!*name && !*mask);
311 /***********************************************************************
314 static DOS_DIR *DOSFS_OpenDir( LPCSTR path )
316 DOS_DIR *dir = HeapAlloc( SystemHeap, 0, sizeof(*dir) );
319 DOS_ERROR( ER_OutOfMemory, EC_OutOfResource, SA_Abort, EL_Memory );
323 /* Treat empty path as root directory. This simplifies path split into
324 directory and mask in several other places */
325 if (!*path) path = "/";
327 #ifdef VFAT_IOCTL_READDIR_BOTH
329 /* Check if the VFAT ioctl is supported on this directory */
331 if ((dir->fd = open( path, O_RDONLY )) != -1)
333 if (ioctl( dir->fd, VFAT_IOCTL_READDIR_BOTH, (long)dir->dirent ) == -1)
340 /* Set the file pointer back at the start of the directory */
341 lseek( dir->fd, 0, SEEK_SET );
346 #endif /* VFAT_IOCTL_READDIR_BOTH */
348 /* Now use the standard opendir/readdir interface */
350 if (!(dir->dir = opendir( path )))
352 HeapFree( SystemHeap, 0, dir );
359 /***********************************************************************
362 static void DOSFS_CloseDir( DOS_DIR *dir )
364 #ifdef VFAT_IOCTL_READDIR_BOTH
365 if (dir->fd != -1) close( dir->fd );
366 #endif /* VFAT_IOCTL_READDIR_BOTH */
367 if (dir->dir) closedir( dir->dir );
368 HeapFree( SystemHeap, 0, dir );
372 /***********************************************************************
375 static BOOL32 DOSFS_ReadDir( DOS_DIR *dir, LPCSTR *long_name,
378 struct dirent *dirent;
380 #ifdef VFAT_IOCTL_READDIR_BOTH
383 if (ioctl( dir->fd, VFAT_IOCTL_READDIR_BOTH, (long)dir->dirent ) != -1) {
384 if (!dir->dirent[0].d_reclen) return FALSE;
385 if (!DOSFS_ToDosFCBFormat( dir->dirent[0].d_name, dir->short_name ))
386 dir->short_name[0] = '\0';
387 *short_name = dir->short_name;
388 if (dir->dirent[1].d_name[0]) *long_name = dir->dirent[1].d_name;
389 else *long_name = dir->dirent[0].d_name;
393 #endif /* VFAT_IOCTL_READDIR_BOTH */
395 if (!(dirent = readdir( dir->dir ))) return FALSE;
396 *long_name = dirent->d_name;
402 /***********************************************************************
405 * Transform a Unix file name into a hashed DOS name. If the name is a valid
406 * DOS name, it is converted to upper-case; otherwise it is replaced by a
407 * hashed version that fits in 8.3 format.
408 * File name can be terminated by '\0', '\\' or '/'.
409 * 'buffer' must be at least 13 characters long.
411 static void DOSFS_Hash( LPCSTR name, LPSTR buffer, BOOL32 dir_format,
414 static const char invalid_chars[] = INVALID_DOS_CHARS "~.";
415 static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
422 if (dir_format) strcpy( buffer, " " );
424 if (DOSFS_ValidDOSName( name, ignore_case ))
426 /* Check for '.' and '..' */
430 if (!dir_format) buffer[1] = buffer[2] = '\0';
431 if (name[1] == '.') buffer[1] = '.';
435 /* Simply copy the name, converting to uppercase */
437 for (dst = buffer; !IS_END_OF_NAME(*name) && (*name != '.'); name++)
438 *dst++ = toupper(*name);
441 if (dir_format) dst = buffer + 8;
443 for (name++; !IS_END_OF_NAME(*name); name++)
444 *dst++ = toupper(*name);
446 if (!dir_format) *dst = '\0';
450 /* Compute the hash code of the file name */
451 /* If you know something about hash functions, feel free to */
452 /* insert a better algorithm here... */
455 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
456 hash = (hash<<3) ^ (hash>>5) ^ tolower(*p) ^ (tolower(p[1]) << 8);
457 hash = (hash<<3) ^ (hash>>5) ^ tolower(*p); /* Last character*/
461 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
462 hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
463 hash = (hash << 3) ^ (hash >> 5) ^ *p; /* Last character */
466 /* Find last dot for start of the extension */
467 for (p = name+1, ext = NULL; !IS_END_OF_NAME(*p); p++)
468 if (*p == '.') ext = p;
469 if (ext && IS_END_OF_NAME(ext[1]))
470 ext = NULL; /* Empty extension ignored */
472 /* Copy first 4 chars, replacing invalid chars with '_' */
473 for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
475 if (IS_END_OF_NAME(*p) || (p == ext)) break;
476 *dst++ = strchr( invalid_chars, *p ) ? '_' : toupper(*p);
478 /* Pad to 5 chars with '~' */
479 while (i-- >= 0) *dst++ = '~';
481 /* Insert hash code converted to 3 ASCII chars */
482 *dst++ = hash_chars[(hash >> 10) & 0x1f];
483 *dst++ = hash_chars[(hash >> 5) & 0x1f];
484 *dst++ = hash_chars[hash & 0x1f];
486 /* Copy the first 3 chars of the extension (if any) */
489 if (!dir_format) *dst++ = '.';
490 for (i = 3, ext++; (i > 0) && !IS_END_OF_NAME(*ext); i--, ext++)
491 *dst++ = strchr( invalid_chars, *ext ) ? '_' : toupper(*ext);
493 if (!dir_format) *dst = '\0';
497 /***********************************************************************
500 * Find the Unix file name in a given directory that corresponds to
501 * a file name (either in Unix or DOS format).
502 * File name can be terminated by '\0', '\\' or '/'.
503 * Return TRUE if OK, FALSE if no file name matches.
505 * 'long_buf' must be at least 'long_len' characters long. If the long name
506 * turns out to be larger than that, the function returns FALSE.
507 * 'short_buf' must be at least 13 characters long.
509 BOOL32 DOSFS_FindUnixName( LPCSTR path, LPCSTR name, LPSTR long_buf,
510 INT32 long_len, LPSTR short_buf, BOOL32 ignore_case)
513 LPCSTR long_name, short_name;
514 char dos_name[12], tmp_buf[13];
517 const char *p = strchr( name, '/' );
518 int len = p ? (int)(p - name) : strlen(name);
519 if ((p = strchr( name, '\\' ))) len = MIN( (int)(p - name), len );
520 if (long_len < len + 1) return FALSE;
522 TRACE(dosfs, "%s,%s\n", path, name );
524 if (!DOSFS_ToDosFCBFormat( name, dos_name )) dos_name[0] = '\0';
526 if (!(dir = DOSFS_OpenDir( path )))
528 WARN(dosfs, "(%s,%s): can't open dir: %s\n",
529 path, name, strerror(errno) );
533 while ((ret = DOSFS_ReadDir( dir, &long_name, &short_name )))
535 /* Check against Unix name */
536 if (len == strlen(long_name))
540 if (!lstrncmp32A( long_name, name, len )) break;
544 if (!lstrncmpi32A( long_name, name, len )) break;
549 /* Check against hashed DOS name */
552 DOSFS_Hash( long_name, tmp_buf, TRUE, ignore_case );
553 short_name = tmp_buf;
555 if (!strcmp( dos_name, short_name )) break;
560 if (long_buf) strcpy( long_buf, long_name );
564 DOSFS_ToDosDTAFormat( short_name, short_buf );
566 DOSFS_Hash( long_name, short_buf, FALSE, ignore_case );
568 TRACE(dosfs, "(%s,%s) -> %s (%s)\n",
569 path, name, long_name, short_buf ? short_buf : "***");
572 WARN(dosfs, "'%s' not found in '%s'\n", name, path);
573 DOSFS_CloseDir( dir );
578 /***********************************************************************
581 * Check if a DOS file name represents a DOS device and return the device.
583 const DOS_DEVICE *DOSFS_GetDevice( const char *name )
588 if (!name) return NULL; /* if FILE_DupUnixHandle was used */
589 if (name[0] && (name[1] == ':')) name += 2;
590 if ((p = strrchr( name, '/' ))) name = p + 1;
591 if ((p = strrchr( name, '\\' ))) name = p + 1;
592 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
594 const char *dev = DOSFS_Devices[i].name;
595 if (!lstrncmpi32A( dev, name, strlen(dev) ))
597 p = name + strlen( dev );
598 if (!*p || (*p == '.')) return &DOSFS_Devices[i];
604 /***********************************************************************
607 * Open a DOS device. This might not map 1:1 into the UNIX device concept.
609 HFILE32 DOSFS_OpenDevice( const char *name, int unixmode )
616 if (!name) return (HFILE32)NULL; /* if FILE_DupUnixHandle was used */
617 if (name[0] && (name[1] == ':')) name += 2;
618 if ((p = strrchr( name, '/' ))) name = p + 1;
619 if ((p = strrchr( name, '\\' ))) name = p + 1;
620 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
622 const char *dev = DOSFS_Devices[i].name;
623 if (!lstrncmpi32A( dev, name, strlen(dev) ))
625 p = name + strlen( dev );
626 if (!*p || (*p == '.')) {
628 if (!strcmp(DOSFS_Devices[i].name,"NUL"))
629 return FILE_OpenUnixFile("/dev/null",unixmode);
630 if (!strcmp(DOSFS_Devices[i].name,"CON")) {
635 to_dup = GetStdHandle( STD_INPUT_HANDLE );
638 to_dup = GetStdHandle( STD_OUTPUT_HANDLE );
641 FIXME(dosfs,"can't open CON read/write\n");
642 return HFILE_ERROR32;
645 if (!DuplicateHandle( GetCurrentProcess(), to_dup, GetCurrentProcess(),
646 &handle, 0, FALSE, DUPLICATE_SAME_ACCESS ))
647 handle = HFILE_ERROR32;
650 if (!strcmp(DOSFS_Devices[i].name,"SCSIMGR$")) {
651 if ((handle = FILE_Alloc( &file )) == INVALID_HANDLE_VALUE32)
652 return HFILE_ERROR32;
654 file->unix_name = HEAP_strdupA( SystemHeap, 0, name );
658 if (!strcmp(DOSFS_Devices[i].name,"HPSCAN")) {
659 if ((handle = FILE_Alloc( &file )) == INVALID_HANDLE_VALUE32)
660 return HFILE_ERROR32;
662 file->unix_name = HEAP_strdupA( SystemHeap, 0, name );
666 FIXME(dosfs,"device open %s not supported (yet)\n",DOSFS_Devices[i].name);
667 return HFILE_ERROR32;
671 return HFILE_ERROR32;
675 /***********************************************************************
678 * Get the drive specified by a given path name (DOS or Unix format).
680 static int DOSFS_GetPathDrive( const char **name )
683 const char *p = *name;
685 if (*p && (p[1] == ':'))
687 drive = toupper(*p) - 'A';
690 else if (*p == '/') /* Absolute Unix path? */
692 if ((drive = DRIVE_FindDriveRoot( name )) == -1)
694 MSG("Warning: %s not accessible from a DOS drive\n", *name );
695 /* Assume it really was a DOS name */
696 drive = DRIVE_GetCurrentDrive();
699 else drive = DRIVE_GetCurrentDrive();
701 if (!DRIVE_IsValid(drive))
703 DOS_ERROR( ER_InvalidDrive, EC_MediaError, SA_Abort, EL_Disk );
710 /***********************************************************************
713 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
714 * Unix name / short DOS name pair.
715 * Return FALSE if one of the path components does not exist. The last path
716 * component is only checked if 'check_last' is non-zero.
717 * The buffers pointed to by 'long_buf' and 'short_buf' must be
718 * at least MAX_PATHNAME_LEN long.
720 BOOL32 DOSFS_GetFullName( LPCSTR name, BOOL32 check_last, DOS_FULL_NAME *full )
724 char *p_l, *p_s, *root;
726 TRACE(dosfs, "%s (last=%d)\n",
729 if ((full->drive = DOSFS_GetPathDrive( &name )) == -1) return FALSE;
730 flags = DRIVE_GetFlags( full->drive );
732 lstrcpyn32A( 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 strcpy( full->short_name, "A:\\" );
738 full->short_name[0] += full->drive;
740 if ((*name == '\\') || (*name == '/')) /* Absolute path */
742 while ((*name == '\\') || (*name == '/')) name++;
744 else /* Relative path */
746 lstrcpyn32A( root + 1, DRIVE_GetUnixCwd( full->drive ),
747 sizeof(full->long_name) - (root - full->long_name) - 1 );
748 if (root[1]) *root = '/';
749 lstrcpyn32A( full->short_name + 3, DRIVE_GetDosCwd( full->drive ),
750 sizeof(full->short_name) - 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 + strlen(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) - 14) ||
785 (p_l >= full->long_name + sizeof(full->long_name) - 1))
787 DOS_ERROR( ER_PathNotFound, EC_NotFound, SA_Abort, EL_Disk );
791 /* Get the long and short name matching the file name */
793 if ((found = DOSFS_FindUnixName( full->long_name, name, p_l + 1,
794 sizeof(full->long_name) - (p_l - full->long_name) - 1,
795 p_s + 1, !(flags & DRIVE_CASE_SENSITIVE) )))
801 while (!IS_END_OF_NAME(*name)) name++;
803 else if (!check_last)
807 while (!IS_END_OF_NAME(*name) &&
808 (p_s < full->short_name + sizeof(full->short_name) - 1) &&
809 (p_l < full->long_name + sizeof(full->long_name) - 1))
811 *p_s++ = tolower(*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 (flags & DRIVE_CASE_SENSITIVE) *p_l++ = tolower(*name);
821 while ((*name == '\\') || (*name == '/')) name++;
828 DOS_ERROR( ER_FileNotFound, EC_NotFound, SA_Abort, EL_Disk );
831 if (*name) /* Not last */
833 DOS_ERROR( ER_PathNotFound, EC_NotFound, SA_Abort, EL_Disk );
837 if (!full->long_name[0]) strcpy( full->long_name, "/" );
838 if (!full->short_name[2]) strcpy( full->short_name + 2, "\\" );
839 TRACE(dosfs, "returning %s = %s\n",
840 full->long_name, full->short_name );
845 /***********************************************************************
846 * GetShortPathName32A (KERNEL32.271)
848 DWORD WINAPI GetShortPathName32A( LPCSTR longpath, LPSTR shortpath,
851 DOS_FULL_NAME full_name;
853 /* FIXME: is it correct to always return a fully qualified short path? */
854 if (!DOSFS_GetFullName( longpath, TRUE, &full_name )) return 0;
855 lstrcpyn32A( shortpath, full_name.short_name, shortlen );
856 return strlen( full_name.short_name );
860 /***********************************************************************
861 * GetShortPathName32W (KERNEL32.272)
863 DWORD WINAPI GetShortPathName32W( LPCWSTR longpath, LPWSTR shortpath,
866 DOS_FULL_NAME full_name;
868 LPSTR longpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, longpath );
870 /* FIXME: is it correct to always return a fully qualified short path? */
871 if (DOSFS_GetFullName( longpathA, TRUE, &full_name ))
873 ret = strlen( full_name.short_name );
874 lstrcpynAtoW( shortpath, full_name.short_name, shortlen );
876 HeapFree( GetProcessHeap(), 0, longpathA );
881 /***********************************************************************
882 * GetLongPathName32A (KERNEL32.xxx)
884 DWORD WINAPI GetLongPathName32A( LPCSTR shortpath, LPSTR longpath,
887 DOS_FULL_NAME full_name;
889 /* FIXME: Is it correct to return a UNIX style path here? */
890 if (!DOSFS_GetFullName( shortpath, TRUE, &full_name )) return 0;
891 lstrcpyn32A( longpath, full_name.long_name, longlen );
892 return strlen( full_name.long_name );
896 /***********************************************************************
897 * GetLongPathName32W (KERNEL32.269)
899 DWORD WINAPI GetLongPathName32W( LPCWSTR shortpath, LPWSTR longpath,
902 DOS_FULL_NAME full_name;
904 LPSTR shortpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, shortpath );
906 /* FIXME: is it correct to always return a fully qualified short path? */
907 if (DOSFS_GetFullName( shortpathA, TRUE, &full_name ))
909 ret = strlen( full_name.short_name );
910 lstrcpynAtoW( longpath, full_name.long_name, longlen );
912 HeapFree( GetProcessHeap(), 0, shortpathA );
917 /***********************************************************************
918 * DOSFS_DoGetFullPathName
920 * Implementation of GetFullPathName32A/W.
922 static DWORD DOSFS_DoGetFullPathName( LPCSTR name, DWORD len, LPSTR result,
925 char buffer[MAX_PATHNAME_LEN];
929 TRACE(dosfs, "converting %s\n", name );
931 if (!name || !result) return 0;
933 if ((drive = DOSFS_GetPathDrive( &name )) == -1) return 0;
937 if (IS_END_OF_NAME(*name) && (*name)) /* Absolute path */
939 while ((*name == '\\') || (*name == '/')) name++;
941 else /* Relative path or empty path */
944 lstrcpyn32A( p, DRIVE_GetDosCwd(drive), sizeof(buffer) - 3 );
945 if (*p) p += strlen(p); else p--;
947 if (!*name) /* empty path */
955 if (IS_END_OF_NAME(name[1]))
958 while ((*name == '\\') || (*name == '/')) name++;
961 else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
964 while ((*name == '\\') || (*name == '/')) name++;
965 while ((p > buffer + 2) && (*p != '\\')) p--;
966 *p = '\0'; /* Remove trailing separator */
970 if (p >= buffer + sizeof(buffer) - 1)
972 DOS_ERROR( ER_PathNotFound, EC_NotFound, SA_Abort, EL_Disk );
976 while (!IS_END_OF_NAME(*name) && (p < buffer + sizeof(buffer) - 1))
979 while ((*name == '\\') || (*name == '/')) name++;
987 if (!(DRIVE_GetFlags(drive) & DRIVE_CASE_PRESERVING))
988 CharUpper32A( buffer );
990 if (unicode) lstrcpynAtoW( (LPWSTR)result, buffer, len );
991 else lstrcpyn32A( result, buffer, len );
993 TRACE(dosfs, "returning %s\n", buffer );
994 return strlen(buffer);
998 /***********************************************************************
999 * GetFullPathName32A (KERNEL32.272)
1001 DWORD WINAPI GetFullPathName32A( LPCSTR name, DWORD len, LPSTR buffer,
1004 DWORD ret = DOSFS_DoGetFullPathName( name, len, buffer, FALSE );
1005 if (ret && lastpart)
1007 LPSTR p = buffer + strlen(buffer);
1008 while ((p > buffer + 2) && (*p != '\\')) p--;
1015 /***********************************************************************
1016 * GetFullPathName32W (KERNEL32.273)
1018 DWORD WINAPI GetFullPathName32W( LPCWSTR name, DWORD len, LPWSTR buffer,
1021 LPSTR nameA = HEAP_strdupWtoA( GetProcessHeap(), 0, name );
1022 DWORD ret = DOSFS_DoGetFullPathName( nameA, len, (LPSTR)buffer, TRUE );
1023 HeapFree( GetProcessHeap(), 0, nameA );
1024 if (ret && lastpart)
1026 LPWSTR p = buffer + lstrlen32W(buffer);
1027 while ((p > buffer + 2) && (*p != '\\')) p--;
1033 /***********************************************************************
1036 static int DOSFS_FindNextEx( FIND_FIRST_INFO *info, WIN32_FIND_DATA32A *entry )
1038 BYTE attr = info->attr | FA_UNUSED | FA_ARCHIVE | FA_RDONLY;
1039 UINT32 flags = DRIVE_GetFlags( info->drive );
1040 char *p, buffer[MAX_PATHNAME_LEN];
1041 const char *drive_path;
1043 LPCSTR long_name, short_name;
1044 BY_HANDLE_FILE_INFORMATION fileinfo;
1047 if ((info->attr & ~(FA_UNUSED | FA_ARCHIVE | FA_RDONLY)) == FA_LABEL)
1049 if (info->cur_pos) return 0;
1050 entry->dwFileAttributes = FILE_ATTRIBUTE_LABEL;
1051 DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftCreationTime, 0 );
1052 DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftLastAccessTime, 0 );
1053 DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftLastWriteTime, 0 );
1054 entry->nFileSizeHigh = 0;
1055 entry->nFileSizeLow = 0;
1056 entry->dwReserved0 = 0;
1057 entry->dwReserved1 = 0;
1058 DOSFS_ToDosDTAFormat( DRIVE_GetLabel( info->drive ), entry->cFileName );
1059 strcpy( entry->cAlternateFileName, entry->cFileName );
1064 drive_path = info->path + strlen(DRIVE_GetRoot( info->drive ));
1065 while ((*drive_path == '/') || (*drive_path == '\\')) drive_path++;
1066 drive_root = !*drive_path;
1068 lstrcpyn32A( buffer, info->path, sizeof(buffer) - 1 );
1069 strcat( buffer, "/" );
1070 p = buffer + strlen(buffer);
1072 while (DOSFS_ReadDir( info->dir, &long_name, &short_name ))
1076 /* Don't return '.' and '..' in the root of the drive */
1077 if (drive_root && (long_name[0] == '.') &&
1078 (!long_name[1] || ((long_name[1] == '.') && !long_name[2])))
1081 /* Check the long mask */
1083 if (info->long_mask)
1085 if (!DOSFS_MatchLong( info->long_mask, long_name,
1086 flags & DRIVE_CASE_SENSITIVE )) continue;
1089 /* Check the short mask */
1091 if (info->short_mask)
1095 DOSFS_Hash( long_name, dos_name, TRUE,
1096 !(flags & DRIVE_CASE_SENSITIVE) );
1097 short_name = dos_name;
1099 if (!DOSFS_MatchShort( info->short_mask, short_name )) continue;
1102 /* Check the file attributes */
1104 lstrcpyn32A( p, long_name, sizeof(buffer) - (int)(p - buffer) );
1105 if (!FILE_Stat( buffer, &fileinfo ))
1107 WARN(dosfs, "can't stat %s\n", buffer);
1110 if (fileinfo.dwFileAttributes & ~attr) continue;
1112 /* We now have a matching entry; fill the result and return */
1114 entry->dwFileAttributes = fileinfo.dwFileAttributes;
1115 entry->ftCreationTime = fileinfo.ftCreationTime;
1116 entry->ftLastAccessTime = fileinfo.ftLastAccessTime;
1117 entry->ftLastWriteTime = fileinfo.ftLastWriteTime;
1118 entry->nFileSizeHigh = fileinfo.nFileSizeHigh;
1119 entry->nFileSizeLow = fileinfo.nFileSizeLow;
1122 DOSFS_ToDosDTAFormat( short_name, entry->cAlternateFileName );
1124 DOSFS_Hash( long_name, entry->cAlternateFileName, FALSE,
1125 !(flags & DRIVE_CASE_SENSITIVE) );
1127 lstrcpyn32A( entry->cFileName, long_name, sizeof(entry->cFileName) );
1128 if (!(flags & DRIVE_CASE_PRESERVING)) CharLower32A( entry->cFileName );
1129 TRACE(dosfs, "returning %s (%s) %02lx %ld\n",
1130 entry->cFileName, entry->cAlternateFileName,
1131 entry->dwFileAttributes, entry->nFileSizeLow );
1134 return 0; /* End of directory */
1137 /***********************************************************************
1140 * Find the next matching file. Return the number of entries read to find
1141 * the matching one, or 0 if no more entries.
1142 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
1143 * file name mask. Either or both can be NULL.
1145 * NOTE: This is supposed to be only called by the int21 emulation
1146 * routines. Thus, we should own the Win16Mutex anyway.
1147 * Nevertheless, we explicitly enter it to ensure the static
1148 * directory cache is protected.
1150 int DOSFS_FindNext( const char *path, const char *short_mask,
1151 const char *long_mask, int drive, BYTE attr,
1152 int skip, WIN32_FIND_DATA32A *entry )
1154 static FIND_FIRST_INFO info = { NULL };
1155 LPCSTR short_name, long_name;
1158 SYSLEVEL_EnterWin16Lock();
1160 /* Check the cached directory */
1161 if (!(info.dir && info.path == path && info.short_mask == short_mask
1162 && info.long_mask == long_mask && info.drive == drive
1163 && info.attr == attr && info.cur_pos <= skip))
1165 /* Not in the cache, open it anew */
1166 if (info.dir) DOSFS_CloseDir( info.dir );
1168 info.path = (LPSTR)path;
1169 info.long_mask = (LPSTR)long_mask;
1170 info.short_mask = (LPSTR)short_mask;
1174 info.dir = DOSFS_OpenDir( info.path );
1177 /* Skip to desired position */
1178 while (info.cur_pos < skip)
1179 if (info.dir && DOSFS_ReadDir( info.dir, &long_name, &short_name ))
1184 if (info.dir && info.cur_pos == skip && DOSFS_FindNextEx( &info, entry ))
1185 count = info.cur_pos - skip;
1191 if (info.dir) DOSFS_CloseDir( info.dir );
1192 memset( &info, '\0', sizeof(info) );
1195 SYSLEVEL_LeaveWin16Lock();
1202 /*************************************************************************
1203 * FindFirstFile16 (KERNEL.413)
1205 HANDLE16 WINAPI FindFirstFile16( LPCSTR path, WIN32_FIND_DATA32A *data )
1207 DOS_FULL_NAME full_name;
1209 FIND_FIRST_INFO *info;
1211 if (!path) return 0;
1212 if (!DOSFS_GetFullName( path, FALSE, &full_name ))
1213 return INVALID_HANDLE_VALUE16;
1214 if (!(handle = GlobalAlloc16( GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO) )))
1215 return INVALID_HANDLE_VALUE16;
1216 info = (FIND_FIRST_INFO *)GlobalLock16( handle );
1217 info->path = HEAP_strdupA( SystemHeap, 0, full_name.long_name );
1218 info->long_mask = strrchr( info->path, '/' );
1219 *(info->long_mask++) = '\0';
1220 info->short_mask = NULL;
1222 if (path[0] && (path[1] == ':')) info->drive = toupper(*path) - 'A';
1223 else info->drive = DRIVE_GetCurrentDrive();
1226 info->dir = DOSFS_OpenDir( info->path );
1228 GlobalUnlock16( handle );
1229 if (!FindNextFile16( handle, data ))
1231 FindClose16( handle );
1232 DOS_ERROR( ER_NoMoreFiles, EC_MediaError, SA_Abort, EL_Disk );
1233 return INVALID_HANDLE_VALUE16;
1239 /*************************************************************************
1240 * FindFirstFile32A (KERNEL32.123)
1242 HANDLE32 WINAPI FindFirstFile32A( LPCSTR path, WIN32_FIND_DATA32A *data )
1244 HANDLE32 handle = FindFirstFile16( path, data );
1245 if (handle == INVALID_HANDLE_VALUE16) return INVALID_HANDLE_VALUE32;
1250 /*************************************************************************
1251 * FindFirstFile32W (KERNEL32.124)
1253 HANDLE32 WINAPI FindFirstFile32W( LPCWSTR path, WIN32_FIND_DATA32W *data )
1255 WIN32_FIND_DATA32A dataA;
1256 LPSTR pathA = HEAP_strdupWtoA( GetProcessHeap(), 0, path );
1257 HANDLE32 handle = FindFirstFile32A( pathA, &dataA );
1258 HeapFree( GetProcessHeap(), 0, pathA );
1259 if (handle != INVALID_HANDLE_VALUE32)
1261 data->dwFileAttributes = dataA.dwFileAttributes;
1262 data->ftCreationTime = dataA.ftCreationTime;
1263 data->ftLastAccessTime = dataA.ftLastAccessTime;
1264 data->ftLastWriteTime = dataA.ftLastWriteTime;
1265 data->nFileSizeHigh = dataA.nFileSizeHigh;
1266 data->nFileSizeLow = dataA.nFileSizeLow;
1267 lstrcpyAtoW( data->cFileName, dataA.cFileName );
1268 lstrcpyAtoW( data->cAlternateFileName, dataA.cAlternateFileName );
1274 /*************************************************************************
1275 * FindNextFile16 (KERNEL.414)
1277 BOOL16 WINAPI FindNextFile16( HANDLE16 handle, WIN32_FIND_DATA32A *data )
1279 FIND_FIRST_INFO *info;
1281 if (!(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
1283 DOS_ERROR( ER_InvalidHandle, EC_ProgramError, SA_Abort, EL_Disk );
1286 GlobalUnlock16( handle );
1287 if (!info->path || !info->dir)
1289 DOS_ERROR( ER_NoMoreFiles, EC_MediaError, SA_Abort, EL_Disk );
1292 if (!DOSFS_FindNextEx( info, data ))
1294 DOSFS_CloseDir( info->dir ); info->dir = NULL;
1295 HeapFree( SystemHeap, 0, info->path );
1296 info->path = info->long_mask = NULL;
1297 DOS_ERROR( ER_NoMoreFiles, EC_MediaError, SA_Abort, EL_Disk );
1304 /*************************************************************************
1305 * FindNextFile32A (KERNEL32.126)
1307 BOOL32 WINAPI FindNextFile32A( HANDLE32 handle, WIN32_FIND_DATA32A *data )
1309 return FindNextFile16( handle, data );
1313 /*************************************************************************
1314 * FindNextFile32W (KERNEL32.127)
1316 BOOL32 WINAPI FindNextFile32W( HANDLE32 handle, WIN32_FIND_DATA32W *data )
1318 WIN32_FIND_DATA32A dataA;
1319 if (!FindNextFile32A( handle, &dataA )) return FALSE;
1320 data->dwFileAttributes = dataA.dwFileAttributes;
1321 data->ftCreationTime = dataA.ftCreationTime;
1322 data->ftLastAccessTime = dataA.ftLastAccessTime;
1323 data->ftLastWriteTime = dataA.ftLastWriteTime;
1324 data->nFileSizeHigh = dataA.nFileSizeHigh;
1325 data->nFileSizeLow = dataA.nFileSizeLow;
1326 lstrcpyAtoW( data->cFileName, dataA.cFileName );
1327 lstrcpyAtoW( data->cAlternateFileName, dataA.cAlternateFileName );
1332 /*************************************************************************
1333 * FindClose16 (KERNEL.415)
1335 BOOL16 WINAPI FindClose16( HANDLE16 handle )
1337 FIND_FIRST_INFO *info;
1339 if ((handle == INVALID_HANDLE_VALUE16) ||
1340 !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
1342 DOS_ERROR( ER_InvalidHandle, EC_ProgramError, SA_Abort, EL_Disk );
1345 if (info->dir) DOSFS_CloseDir( info->dir );
1346 if (info->path) HeapFree( SystemHeap, 0, info->path );
1347 GlobalUnlock16( handle );
1348 GlobalFree16( handle );
1353 /*************************************************************************
1354 * FindClose32 (KERNEL32.119)
1356 BOOL32 WINAPI FindClose32( HANDLE32 handle )
1358 return FindClose16( (HANDLE16)handle );
1362 /***********************************************************************
1363 * DOSFS_UnixTimeToFileTime
1365 * Convert a Unix time to FILETIME format.
1366 * The FILETIME structure is a 64-bit value representing the number of
1367 * 100-nanosecond intervals since January 1, 1601, 0:00.
1368 * 'remainder' is the nonnegative number of 100-ns intervals
1369 * corresponding to the time fraction smaller than 1 second that
1370 * couldn't be stored in the time_t value.
1372 void DOSFS_UnixTimeToFileTime( time_t unix_time, FILETIME *filetime,
1378 The time difference between 1 January 1601, 00:00:00 and
1379 1 January 1970, 00:00:00 is 369 years, plus the leap years
1380 from 1604 to 1968, excluding 1700, 1800, 1900.
1381 This makes (1968 - 1600) / 4 - 3 = 89 leap days, and a total
1384 Any day in that period had 24 * 60 * 60 = 86400 seconds.
1386 The time difference is 134774 * 86400 * 10000000, which can be written
1388 27111902 * 2^32 + 3577643008
1389 413 * 2^48 + 45534 * 2^32 + 54590 * 2^16 + 32768
1391 If you find that these constants are buggy, please change them in all
1392 instances in both conversion functions.
1395 There are two versions, one of them uses long long variables and
1396 is presumably faster but not ISO C. The other one uses standard C
1397 data types and operations but relies on the assumption that negative
1398 numbers are stored as 2's complement (-1 is 0xffff....). If this
1399 assumption is violated, dates before 1970 will not convert correctly.
1400 This should however work on any reasonable architecture where WINE
1405 Take care not to remove the casts. I have tested these functions
1406 (in both versions) for a lot of numbers. I would be interested in
1407 results on other compilers than GCC.
1409 The operations have been designed to account for the possibility
1410 of 64-bit time_t in future UNICES. Even the versions without
1411 internal long long numbers will work if time_t only is 64 bit.
1412 A 32-bit shift, which was necessary for that operation, turned out
1413 not to work correctly in GCC, besides giving the warning. So I
1414 used a double 16-bit shift instead. Numbers are in the ISO version
1415 represented by three limbs, the most significant with 32 bit, the
1416 other two with 16 bit each.
1418 As the modulo-operator % is not well-defined for negative numbers,
1419 negative divisors have been avoided in DOSFS_FileTimeToUnixTime.
1421 There might be quicker ways to do this in C. Certainly so in
1424 Claus Fischer, fischer@iue.tuwien.ac.at
1427 #if (SIZEOF_LONG_LONG >= 8)
1428 # define USE_LONG_LONG 1
1430 # define USE_LONG_LONG 0
1433 #if USE_LONG_LONG /* gcc supports long long type */
1435 long long int t = unix_time;
1437 t += 116444736000000000LL;
1439 filetime->dwLowDateTime = (UINT32)t;
1440 filetime->dwHighDateTime = (UINT32)(t >> 32);
1442 #else /* ISO version */
1444 UINT32 a0; /* 16 bit, low bits */
1445 UINT32 a1; /* 16 bit, medium bits */
1446 UINT32 a2; /* 32 bit, high bits */
1448 /* Copy the unix time to a2/a1/a0 */
1449 a0 = unix_time & 0xffff;
1450 a1 = (unix_time >> 16) & 0xffff;
1451 /* This is obsolete if unix_time is only 32 bits, but it does not hurt.
1452 Do not replace this by >> 32, it gives a compiler warning and it does
1454 a2 = (unix_time >= 0 ? (unix_time >> 16) >> 16 :
1455 ~((~unix_time >> 16) >> 16));
1457 /* Multiply a by 10000000 (a = a2/a1/a0)
1458 Split the factor into 10000 * 1000 which are both less than 0xffff. */
1460 a1 = a1 * 10000 + (a0 >> 16);
1461 a2 = a2 * 10000 + (a1 >> 16);
1466 a1 = a1 * 1000 + (a0 >> 16);
1467 a2 = a2 * 1000 + (a1 >> 16);
1471 /* Add the time difference and the remainder */
1472 a0 += 32768 + (remainder & 0xffff);
1473 a1 += 54590 + (remainder >> 16 ) + (a0 >> 16);
1474 a2 += 27111902 + (a1 >> 16);
1479 filetime->dwLowDateTime = (a1 << 16) + a0;
1480 filetime->dwHighDateTime = a2;
1485 /***********************************************************************
1486 * DOSFS_FileTimeToUnixTime
1488 * Convert a FILETIME format to Unix time.
1489 * If not NULL, 'remainder' contains the fractional part of the filetime,
1490 * in the range of [0..9999999] (even if time_t is negative).
1492 time_t DOSFS_FileTimeToUnixTime( const FILETIME *filetime, DWORD *remainder )
1494 /* Read the comment in the function DOSFS_UnixTimeToFileTime. */
1497 long long int t = filetime->dwHighDateTime;
1499 t += (UINT32)filetime->dwLowDateTime;
1500 t -= 116444736000000000LL;
1503 if (remainder) *remainder = 9999999 - (-t - 1) % 10000000;
1504 return -1 - ((-t - 1) / 10000000);
1508 if (remainder) *remainder = t % 10000000;
1509 return t / 10000000;
1512 #else /* ISO version */
1514 UINT32 a0; /* 16 bit, low bits */
1515 UINT32 a1; /* 16 bit, medium bits */
1516 UINT32 a2; /* 32 bit, high bits */
1517 UINT32 r; /* remainder of division */
1518 unsigned int carry; /* carry bit for subtraction */
1519 int negative; /* whether a represents a negative value */
1521 /* Copy the time values to a2/a1/a0 */
1522 a2 = (UINT32)filetime->dwHighDateTime;
1523 a1 = ((UINT32)filetime->dwLowDateTime ) >> 16;
1524 a0 = ((UINT32)filetime->dwLowDateTime ) & 0xffff;
1526 /* Subtract the time difference */
1527 if (a0 >= 32768 ) a0 -= 32768 , carry = 0;
1528 else a0 += (1 << 16) - 32768 , carry = 1;
1530 if (a1 >= 54590 + carry) a1 -= 54590 + carry, carry = 0;
1531 else a1 += (1 << 16) - 54590 - carry, carry = 1;
1533 a2 -= 27111902 + carry;
1535 /* If a is negative, replace a by (-1-a) */
1536 negative = (a2 >= ((UINT32)1) << 31);
1539 /* Set a to -a - 1 (a is a2/a1/a0) */
1545 /* Divide a by 10000000 (a = a2/a1/a0), put the rest into r.
1546 Split the divisor into 10000 * 1000 which are both less than 0xffff. */
1547 a1 += (a2 % 10000) << 16;
1549 a0 += (a1 % 10000) << 16;
1554 a1 += (a2 % 1000) << 16;
1556 a0 += (a1 % 1000) << 16;
1558 r += (a0 % 1000) * 10000;
1561 /* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
1564 /* Set a to -a - 1 (a is a2/a1/a0) */
1572 if (remainder) *remainder = r;
1574 /* Do not replace this by << 32, it gives a compiler warning and it does
1576 return ((((time_t)a2) << 16) << 16) + (a1 << 16) + a0;
1581 /***********************************************************************
1582 * DosDateTimeToFileTime (KERNEL32.76)
1584 BOOL32 WINAPI DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft)
1588 newtm.tm_sec = (fattime & 0x1f) * 2;
1589 newtm.tm_min = (fattime >> 5) & 0x3f;
1590 newtm.tm_hour = (fattime >> 11);
1591 newtm.tm_mday = (fatdate & 0x1f);
1592 newtm.tm_mon = ((fatdate >> 5) & 0x0f) - 1;
1593 newtm.tm_year = (fatdate >> 9) + 80;
1594 DOSFS_UnixTimeToFileTime( mktime( &newtm ), ft, 0 );
1599 /***********************************************************************
1600 * FileTimeToDosDateTime (KERNEL32.111)
1602 BOOL32 WINAPI FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate,
1605 time_t unixtime = DOSFS_FileTimeToUnixTime( ft, NULL );
1606 struct tm *tm = localtime( &unixtime );
1608 *fattime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
1610 *fatdate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
1616 /***********************************************************************
1617 * LocalFileTimeToFileTime (KERNEL32.373)
1619 BOOL32 WINAPI LocalFileTimeToFileTime( const FILETIME *localft,
1625 /* convert from local to UTC. Perhaps not correct. FIXME */
1626 time_t unixtime = DOSFS_FileTimeToUnixTime( localft, &remainder );
1627 xtm = gmtime( &unixtime );
1628 DOSFS_UnixTimeToFileTime( mktime(xtm), utcft, remainder );
1633 /***********************************************************************
1634 * FileTimeToLocalFileTime (KERNEL32.112)
1636 BOOL32 WINAPI FileTimeToLocalFileTime( const FILETIME *utcft,
1637 LPFILETIME localft )
1640 /* convert from UTC to local. Perhaps not correct. FIXME */
1641 time_t unixtime = DOSFS_FileTimeToUnixTime( utcft, &remainder );
1643 struct tm *xtm = localtime( &unixtime );
1646 localtime = timegm(xtm);
1647 DOSFS_UnixTimeToFileTime( localtime, localft, remainder );
1650 struct tm *xtm,*gtm;
1653 xtm = localtime( &unixtime );
1654 gtm = gmtime( &unixtime );
1655 time1 = mktime(xtm);
1656 time2 = mktime(gtm);
1657 DOSFS_UnixTimeToFileTime( 2*time1-time2, localft, remainder );
1663 /***********************************************************************
1664 * FileTimeToSystemTime (KERNEL32.113)
1666 BOOL32 WINAPI FileTimeToSystemTime( const FILETIME *ft, LPSYSTEMTIME syst )
1670 time_t xtime = DOSFS_FileTimeToUnixTime( ft, &remainder );
1671 xtm = gmtime(&xtime);
1672 syst->wYear = xtm->tm_year+1900;
1673 syst->wMonth = xtm->tm_mon + 1;
1674 syst->wDayOfWeek = xtm->tm_wday;
1675 syst->wDay = xtm->tm_mday;
1676 syst->wHour = xtm->tm_hour;
1677 syst->wMinute = xtm->tm_min;
1678 syst->wSecond = xtm->tm_sec;
1679 syst->wMilliseconds = remainder / 10000;
1683 /***********************************************************************
1684 * QueryDosDeviceA (KERNEL32.413)
1686 * returns array of strings terminated by \0, terminated by \0
1688 DWORD WINAPI QueryDosDevice32A(LPCSTR devname,LPSTR target,DWORD bufsize)
1693 TRACE(dosfs,"(%s,...)\n",devname?devname:"<null>");
1695 /* return known MSDOS devices */
1696 lstrcpy32A(buffer,"CON COM1 COM2 LPT1 NUL ");
1697 while ((s=strchr(buffer,' ')))
1700 lstrcpyn32A(target,buffer,bufsize);
1701 return strlen(buffer);
1703 lstrcpy32A(buffer,"\\DEV\\");
1704 lstrcat32A(buffer,devname);
1705 if ((s=strchr(buffer,':'))) *s='\0';
1706 lstrcpyn32A(target,buffer,bufsize);
1707 return strlen(buffer);
1711 /***********************************************************************
1712 * QueryDosDeviceW (KERNEL32.414)
1714 * returns array of strings terminated by \0, terminated by \0
1716 DWORD WINAPI QueryDosDevice32W(LPCWSTR devname,LPWSTR target,DWORD bufsize)
1718 LPSTR devnameA = devname?HEAP_strdupWtoA(GetProcessHeap(),0,devname):NULL;
1719 LPSTR targetA = (LPSTR)HEAP_xalloc(GetProcessHeap(),0,bufsize);
1720 DWORD ret = QueryDosDevice32A(devnameA,targetA,bufsize);
1722 lstrcpynAtoW(target,targetA,bufsize);
1723 if (devnameA) HeapFree(GetProcessHeap(),0,devnameA);
1724 if (targetA) HeapFree(GetProcessHeap(),0,targetA);
1729 /***********************************************************************
1730 * SystemTimeToFileTime (KERNEL32.526)
1732 BOOL32 WINAPI SystemTimeToFileTime( const SYSTEMTIME *syst, LPFILETIME ft )
1738 struct tm xtm,*local_tm,*utc_tm;
1739 time_t localtim,utctime;
1742 xtm.tm_year = syst->wYear-1900;
1743 xtm.tm_mon = syst->wMonth - 1;
1744 xtm.tm_wday = syst->wDayOfWeek;
1745 xtm.tm_mday = syst->wDay;
1746 xtm.tm_hour = syst->wHour;
1747 xtm.tm_min = syst->wMinute;
1748 xtm.tm_sec = syst->wSecond; /* this is UTC */
1751 utctime = timegm(&xtm);
1752 DOSFS_UnixTimeToFileTime( utctime, ft,
1753 syst->wMilliseconds * 10000 );
1755 localtim = mktime(&xtm); /* now we've got local time */
1756 local_tm = localtime(&localtim);
1757 utc_tm = gmtime(&localtim);
1758 utctime = mktime(utc_tm);
1759 DOSFS_UnixTimeToFileTime( 2*localtim -utctime, ft,
1760 syst->wMilliseconds * 10000 );