Release 971012
[wine.git] / files / dos_fs.c
blob74f6cf24e3ee9c54f913fe421ea209b3849d5ca1
1 /*
2 * DOS file system functions
4 * Copyright 1993 Erik Bos
5 * Copyright 1996 Alexandre Julliard
6 */
8 #include <sys/types.h>
9 #include <ctype.h>
10 #include <dirent.h>
11 #include <fcntl.h>
12 #include <string.h>
13 #include <stdlib.h>
14 #include <stdio.h>
15 #include <sys/stat.h>
16 #include <sys/ioctl.h>
17 #include <time.h>
18 #include <unistd.h>
19 #if defined(__svr4__) || defined(_SCO_DS)
20 #include <sys/statfs.h>
21 #endif
23 #include "windows.h"
24 #include "winerror.h"
25 #include "drive.h"
26 #include "file.h"
27 #include "heap.h"
28 #include "msdos.h"
29 #include "stddebug.h"
30 #include "debug.h"
32 /* Define the VFAT ioctl to get both short and long file names */
33 /* FIXME: is it possible to get this to work on other systems? */
34 #ifdef linux
35 #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, long)
36 /* We want the real kernel dirent structure, not the libc one */
37 typedef struct
39 long d_ino;
40 long d_off;
41 unsigned short d_reclen;
42 char d_name[256];
43 } KERNEL_DIRENT;
45 #else /* linux */
46 #undef VFAT_IOCTL_READDIR_BOTH /* just in case... */
47 #endif /* linux */
49 /* Chars we don't want to see in DOS file names */
50 #define INVALID_DOS_CHARS "*?<>|\"+=,;[] \345"
52 static const char *DOSFS_Devices[][2] =
54 { "CON", "" },
55 { "PRN", "" },
56 { "NUL", "/dev/null" },
57 { "AUX", "" },
58 { "LPT1", "" },
59 { "LPT2", "" },
60 { "LPT3", "" },
61 { "LPT4", "" },
62 { "COM1", "" },
63 { "COM2", "" },
64 { "COM3", "" },
65 { "COM4", "" }
69 #define GET_DRIVE(path) \
70 (((path)[1] == ':') ? toupper((path)[0]) - 'A' : DOSFS_CurDrive)
72 /* DOS extended error status */
73 WORD DOS_ExtendedError;
74 BYTE DOS_ErrorClass;
75 BYTE DOS_ErrorAction;
76 BYTE DOS_ErrorLocus;
78 /* Info structure for FindFirstFile handle */
79 typedef struct
81 LPSTR path;
82 LPSTR mask;
83 int drive;
84 int skip;
85 } FIND_FIRST_INFO;
88 /* Directory info for DOSFS_ReadDir */
89 typedef struct
91 DIR *dir;
92 #ifdef VFAT_IOCTL_READDIR_BOTH
93 int fd;
94 char short_name[12];
95 KERNEL_DIRENT dirent[2];
96 #endif
97 } DOS_DIR;
100 /***********************************************************************
101 * DOSFS_ValidDOSName
103 * Return 1 if Unix file 'name' is also a valid MS-DOS name
104 * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
105 * File name can be terminated by '\0', '\\' or '/'.
107 static int DOSFS_ValidDOSName( const char *name, int ignore_case )
109 static const char invalid_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS;
110 const char *p = name;
111 const char *invalid = ignore_case ? (invalid_chars + 26) : invalid_chars;
112 int len = 0;
114 if (*p == '.')
116 /* Check for "." and ".." */
117 p++;
118 if (*p == '.') p++;
119 /* All other names beginning with '.' are invalid */
120 return (IS_END_OF_NAME(*p));
122 while (!IS_END_OF_NAME(*p))
124 if (strchr( invalid, *p )) return 0; /* Invalid char */
125 if (*p == '.') break; /* Start of the extension */
126 if (++len > 8) return 0; /* Name too long */
127 p++;
129 if (*p != '.') return 1; /* End of name */
130 p++;
131 if (IS_END_OF_NAME(*p)) return 0; /* Empty extension not allowed */
132 len = 0;
133 while (!IS_END_OF_NAME(*p))
135 if (strchr( invalid, *p )) return 0; /* Invalid char */
136 if (*p == '.') return 0; /* Second extension not allowed */
137 if (++len > 3) return 0; /* Extension too long */
138 p++;
140 return 1;
144 /***********************************************************************
145 * DOSFS_ToDosFCBFormat
147 * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
148 * expanding wild cards and converting to upper-case in the process.
149 * File name can be terminated by '\0', '\\' or '/'.
150 * Return FALSE if the name is not a valid DOS name.
151 * 'buffer' must be at least 12 characters long.
153 BOOL32 DOSFS_ToDosFCBFormat( LPCSTR name, LPSTR buffer )
155 static const char invalid_chars[] = INVALID_DOS_CHARS;
156 const char *p = name;
157 int i;
159 /* Check for "." and ".." */
160 if (*p == '.')
162 p++;
163 strcpy( buffer, ". " );
164 if (*p == '.')
166 buffer[1] = '.';
167 p++;
169 return (!*p || (*p == '/') || (*p == '\\'));
172 for (i = 0; i < 8; i++)
174 switch(*p)
176 case '\0':
177 case '\\':
178 case '/':
179 case '.':
180 buffer[i] = ' ';
181 break;
182 case '?':
183 p++;
184 /* fall through */
185 case '*':
186 buffer[i] = '?';
187 break;
188 default:
189 if (strchr( invalid_chars, *p )) return FALSE;
190 buffer[i] = toupper(*p);
191 p++;
192 break;
196 if (*p == '*')
198 /* Skip all chars after wildcard up to first dot */
199 while (*p && (*p != '/') && (*p != '\\') && (*p != '.')) p++;
201 else
203 /* Check if name too long */
204 if (*p && (*p != '/') && (*p != '\\') && (*p != '.')) return FALSE;
206 if (*p == '.') p++; /* Skip dot */
208 for (i = 8; i < 11; i++)
210 switch(*p)
212 case '\0':
213 case '\\':
214 case '/':
215 buffer[i] = ' ';
216 break;
217 case '.':
218 return FALSE; /* Second extension not allowed */
219 case '?':
220 p++;
221 /* fall through */
222 case '*':
223 buffer[i] = '?';
224 break;
225 default:
226 if (strchr( invalid_chars, *p )) return FALSE;
227 buffer[i] = toupper(*p);
228 p++;
229 break;
232 buffer[11] = '\0';
233 return TRUE;
237 /***********************************************************************
238 * DOSFS_ToDosDTAFormat
240 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
241 * converting to upper-case in the process.
242 * File name can be terminated by '\0', '\\' or '/'.
243 * 'buffer' must be at least 13 characters long.
245 static void DOSFS_ToDosDTAFormat( LPCSTR name, LPSTR buffer )
247 char *p;
249 memcpy( buffer, name, 8 );
250 for (p = buffer + 8; (p > buffer) && (p[-1] == ' '); p--);
251 *p++ = '.';
252 memcpy( p, name + 8, 3 );
253 for (p += 3; p[-1] == ' '; p--);
254 if (p[-1] == '.') p--;
255 *p = '\0';
259 /***********************************************************************
260 * DOSFS_MatchShort
262 * Check a DOS file name against a mask (both in FCB format).
264 static int DOSFS_MatchShort( const char *mask, const char *name )
266 int i;
267 for (i = 11; i > 0; i--, mask++, name++)
268 if ((*mask != '?') && (*mask != *name)) return 0;
269 return 1;
273 /***********************************************************************
274 * DOSFS_MatchLong
276 * Check a long file name against a mask.
278 static int DOSFS_MatchLong( const char *mask, const char *name,
279 int case_sensitive )
281 if (!strcmp( mask, "*.*" )) return 1;
282 while (*name && *mask)
284 if (*mask == '*')
286 mask++;
287 while (*mask == '*') mask++; /* Skip consecutive '*' */
288 if (!*mask) return 1;
289 if (case_sensitive) while (*name && (*name != *mask)) name++;
290 else while (*name && (toupper(*name) != toupper(*mask))) name++;
291 if (!*name) return 0;
293 else if (*mask != '?')
295 if (case_sensitive)
297 if (*mask != *name) return 0;
299 else if (toupper(*mask) != toupper(*name)) return 0;
301 mask++;
302 name++;
304 return (!*name && !*mask);
308 /***********************************************************************
309 * DOSFS_OpenDir
311 static DOS_DIR *DOSFS_OpenDir( LPCSTR path )
313 DOS_DIR *dir = HeapAlloc( SystemHeap, 0, sizeof(*dir) );
314 if (!dir)
316 DOS_ERROR( ER_OutOfMemory, EC_OutOfResource, SA_Abort, EL_Memory );
317 return NULL;
320 #ifdef VFAT_IOCTL_READDIR_BOTH
322 /* Check if the VFAT ioctl is supported on this directory */
324 if ((dir->fd = open( path, O_RDONLY )) != -1)
326 if (ioctl( dir->fd, VFAT_IOCTL_READDIR_BOTH, (long)dir->dirent ) == -1)
328 close( dir->fd );
329 dir->fd = -1;
331 else
333 /* Set the file pointer back at the start of the directory */
334 lseek( dir->fd, 0, SEEK_SET );
335 dir->dir = NULL;
336 return dir;
339 #endif /* VFAT_IOCTL_READDIR_BOTH */
341 /* Now use the standard opendir/readdir interface */
343 if (!(dir->dir = opendir( path )))
345 HeapFree( SystemHeap, 0, dir );
346 return NULL;
348 return dir;
352 /***********************************************************************
353 * DOSFS_CloseDir
355 static void DOSFS_CloseDir( DOS_DIR *dir )
357 #ifdef VFAT_IOCTL_READDIR_BOTH
358 if (dir->fd != -1) close( dir->fd );
359 #endif /* VFAT_IOCTL_READDIR_BOTH */
360 if (dir->dir) closedir( dir->dir );
361 HeapFree( SystemHeap, 0, dir );
365 /***********************************************************************
366 * DOSFS_ReadDir
368 static BOOL32 DOSFS_ReadDir( DOS_DIR *dir, LPCSTR *long_name,
369 LPCSTR *short_name )
371 struct dirent *dirent;
373 #ifdef VFAT_IOCTL_READDIR_BOTH
374 if (dir->fd != -1)
376 if (ioctl( dir->fd, VFAT_IOCTL_READDIR_BOTH, (long)dir->dirent ) == -1)
377 return FALSE;
378 if (!dir->dirent[0].d_reclen) return FALSE;
379 if (!DOSFS_ToDosFCBFormat( dir->dirent[0].d_name, dir->short_name ))
380 dir->short_name[0] = '\0';
381 *short_name = dir->short_name;
382 if (dir->dirent[1].d_name[0]) *long_name = dir->dirent[1].d_name;
383 else *long_name = dir->dirent[0].d_name;
384 return TRUE;
386 #endif /* VFAT_IOCTL_READDIR_BOTH */
388 if (!(dirent = readdir( dir->dir ))) return FALSE;
389 *long_name = dirent->d_name;
390 *short_name = NULL;
391 return TRUE;
395 /***********************************************************************
396 * DOSFS_Hash
398 * Transform a Unix file name into a hashed DOS name. If the name is a valid
399 * DOS name, it is converted to upper-case; otherwise it is replaced by a
400 * hashed version that fits in 8.3 format.
401 * File name can be terminated by '\0', '\\' or '/'.
402 * 'buffer' must be at least 13 characters long.
404 static void DOSFS_Hash( LPCSTR name, LPSTR buffer, BOOL32 dir_format,
405 BOOL32 ignore_case )
407 static const char invalid_chars[] = INVALID_DOS_CHARS "~.";
408 static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
410 const char *p, *ext;
411 char *dst;
412 unsigned short hash;
413 int i;
415 if (dir_format) strcpy( buffer, " " );
417 if (DOSFS_ValidDOSName( name, ignore_case ))
419 /* Check for '.' and '..' */
420 if (*name == '.')
422 buffer[0] = '.';
423 if (!dir_format) buffer[1] = buffer[2] = '\0';
424 if (name[1] == '.') buffer[1] = '.';
425 return;
428 /* Simply copy the name, converting to uppercase */
430 for (dst = buffer; !IS_END_OF_NAME(*name) && (*name != '.'); name++)
431 *dst++ = toupper(*name);
432 if (*name == '.')
434 if (dir_format) dst = buffer + 8;
435 else *dst++ = '.';
436 for (name++; !IS_END_OF_NAME(*name); name++)
437 *dst++ = toupper(*name);
439 if (!dir_format) *dst = '\0';
440 return;
443 /* Compute the hash code of the file name */
444 /* If you know something about hash functions, feel free to */
445 /* insert a better algorithm here... */
446 if (ignore_case)
448 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
449 hash = (hash<<3) ^ (hash>>5) ^ tolower(*p) ^ (tolower(p[1]) << 8);
450 hash = (hash<<3) ^ (hash>>5) ^ tolower(*p); /* Last character*/
452 else
454 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
455 hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
456 hash = (hash << 3) ^ (hash >> 5) ^ *p; /* Last character */
459 /* Find last dot for start of the extension */
460 for (p = name+1, ext = NULL; !IS_END_OF_NAME(*p); p++)
461 if (*p == '.') ext = p;
462 if (ext && IS_END_OF_NAME(ext[1]))
463 ext = NULL; /* Empty extension ignored */
465 /* Copy first 4 chars, replacing invalid chars with '_' */
466 for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
468 if (IS_END_OF_NAME(*p) || (p == ext)) break;
469 *dst++ = strchr( invalid_chars, *p ) ? '_' : toupper(*p);
471 /* Pad to 5 chars with '~' */
472 while (i-- >= 0) *dst++ = '~';
474 /* Insert hash code converted to 3 ASCII chars */
475 *dst++ = hash_chars[(hash >> 10) & 0x1f];
476 *dst++ = hash_chars[(hash >> 5) & 0x1f];
477 *dst++ = hash_chars[hash & 0x1f];
479 /* Copy the first 3 chars of the extension (if any) */
480 if (ext)
482 if (!dir_format) *dst++ = '.';
483 for (i = 3, ext++; (i > 0) && !IS_END_OF_NAME(*ext); i--, ext++)
484 *dst++ = strchr( invalid_chars, *ext ) ? '_' : toupper(*ext);
486 if (!dir_format) *dst = '\0';
490 /***********************************************************************
491 * DOSFS_FindUnixName
493 * Find the Unix file name in a given directory that corresponds to
494 * a file name (either in Unix or DOS format).
495 * File name can be terminated by '\0', '\\' or '/'.
496 * Return TRUE if OK, FALSE if no file name matches.
498 * 'long_buf' must be at least 'long_len' characters long. If the long name
499 * turns out to be larger than that, the function returns FALSE.
500 * 'short_buf' must be at least 13 characters long.
502 BOOL32 DOSFS_FindUnixName( LPCSTR path, LPCSTR name, LPSTR long_buf,
503 INT32 long_len, LPSTR short_buf, BOOL32 ignore_case)
505 DOS_DIR *dir;
506 LPCSTR long_name, short_name;
507 char dos_name[12], tmp_buf[13];
508 BOOL32 ret;
510 const char *p = strchr( name, '/' );
511 int len = p ? (int)(p - name) : strlen(name);
512 if ((p = strchr( name, '\\' ))) len = MIN( (int)(p - name), len );
513 if (long_len < len + 1) return FALSE;
515 dprintf_dosfs( stddeb, "DOSFS_FindUnixName: %s,%s\n", path, name );
517 if (!DOSFS_ToDosFCBFormat( name, dos_name )) dos_name[0] = '\0';
519 if (!(dir = DOSFS_OpenDir( path )))
521 dprintf_dosfs( stddeb, "DOSFS_FindUnixName(%s,%s): can't open dir\n",
522 path, name );
523 return FALSE;
526 while ((ret = DOSFS_ReadDir( dir, &long_name, &short_name )))
528 /* Check against Unix name */
529 if (len == strlen(long_name))
531 if (!ignore_case)
533 if (!lstrncmp32A( long_name, name, len )) break;
535 else
537 if (!lstrncmpi32A( long_name, name, len )) break;
540 if (dos_name[0])
542 /* Check against hashed DOS name */
543 if (!short_name)
545 DOSFS_Hash( long_name, tmp_buf, TRUE, ignore_case );
546 short_name = tmp_buf;
548 if (!strcmp( dos_name, short_name )) break;
551 if (ret)
553 if (long_buf) strcpy( long_buf, long_name );
554 if (short_buf)
556 if (short_name)
557 DOSFS_ToDosDTAFormat( short_name, short_buf );
558 else
559 DOSFS_Hash( long_name, short_buf, FALSE, ignore_case );
561 dprintf_dosfs( stddeb, "DOSFS_FindUnixName(%s,%s) -> %s (%s)\n",
562 path, name, long_name, short_buf ? short_buf : "***");
564 else
565 dprintf_dosfs(stddeb,"DOSFS_FindUnixName(%s,%s) -> ** Not found **\n",
566 path, name );
567 DOSFS_CloseDir( dir );
568 return ret;
572 /***********************************************************************
573 * DOSFS_IsDevice
575 * Check if a DOS file name represents a DOS device. Returns the name
576 * of the associated Unix device, or NULL if not found.
578 const char *DOSFS_IsDevice( const char *name )
580 int i;
581 const char *p;
583 if (name[0] && (name[1] == ':')) name += 2;
584 if ((p = strrchr( name, '/' ))) name = p + 1;
585 if ((p = strrchr( name, '\\' ))) name = p + 1;
586 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
588 const char *dev = DOSFS_Devices[i][0];
589 if (!lstrncmpi32A( dev, name, strlen(dev) ))
591 p = name + strlen( dev );
592 if (!*p || (*p == '.')) return DOSFS_Devices[i][1];
595 return NULL;
598 /***********************************************************************
599 * DOSFS_GetPathDrive
601 * Get the drive specified by a given path name (DOS or Unix format).
603 static int DOSFS_GetPathDrive( const char **name )
605 int drive;
606 const char *p = *name;
608 if (*p && (p[1] == ':'))
610 drive = toupper(*p) - 'A';
611 *name += 2;
613 else if (*p == '/') /* Absolute Unix path? */
615 if ((drive = DRIVE_FindDriveRoot( name )) == -1)
617 fprintf( stderr, "Warning: %s not accessible from a DOS drive\n",
618 *name );
619 /* Assume it really was a DOS name */
620 drive = DRIVE_GetCurrentDrive();
623 else drive = DRIVE_GetCurrentDrive();
625 if (!DRIVE_IsValid(drive))
627 DOS_ERROR( ER_InvalidDrive, EC_MediaError, SA_Abort, EL_Disk );
628 return -1;
630 return drive;
634 /***********************************************************************
635 * DOSFS_GetFullName
637 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
638 * Unix name / short DOS name pair.
639 * Return FALSE if one of the path components does not exist. The last path
640 * component is only checked if 'check_last' is non-zero.
641 * The buffers pointed to by 'long_buf' and 'short_buf' must be
642 * at least MAX_PATHNAME_LEN long.
644 BOOL32 DOSFS_GetFullName( LPCSTR name, BOOL32 check_last, DOS_FULL_NAME *full )
646 BOOL32 found;
647 UINT32 flags;
648 char *p_l, *p_s, *root;
650 dprintf_dosfs( stddeb, "DOSFS_GetFullName: %s (last=%d)\n",
651 name, check_last );
653 if ((full->drive = DOSFS_GetPathDrive( &name )) == -1) return FALSE;
654 flags = DRIVE_GetFlags( full->drive );
656 lstrcpyn32A( full->long_name, DRIVE_GetRoot( full->drive ),
657 sizeof(full->long_name) );
658 if (full->long_name[1]) root = full->long_name + strlen(full->long_name);
659 else root = full->long_name; /* root directory */
661 strcpy( full->short_name, "A:\\" );
662 full->short_name[0] += full->drive;
664 if ((*name == '\\') || (*name == '/')) /* Absolute path */
666 while ((*name == '\\') || (*name == '/')) name++;
668 else /* Relative path */
670 lstrcpyn32A( root + 1, DRIVE_GetUnixCwd( full->drive ),
671 sizeof(full->long_name) - (root - full->long_name) - 1 );
672 if (root[1]) *root = '/';
673 lstrcpyn32A( full->short_name + 3, DRIVE_GetDosCwd( full->drive ),
674 sizeof(full->short_name) - 3 );
677 p_l = full->long_name[1] ? full->long_name + strlen(full->long_name)
678 : full->long_name;
679 p_s = full->short_name[3] ? full->short_name + strlen(full->short_name)
680 : full->short_name + 2;
681 found = TRUE;
683 while (*name && found)
685 /* Check for '.' and '..' */
687 if (*name == '.')
689 if (IS_END_OF_NAME(name[1]))
691 name++;
692 while ((*name == '\\') || (*name == '/')) name++;
693 continue;
695 else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
697 name += 2;
698 while ((*name == '\\') || (*name == '/')) name++;
699 while ((p_l > root) && (*p_l != '/')) p_l--;
700 while ((p_s > full->short_name + 2) && (*p_s != '\\')) p_s--;
701 *p_l = *p_s = '\0'; /* Remove trailing separator */
702 continue;
706 /* Make sure buffers are large enough */
708 if ((p_s >= full->short_name + sizeof(full->short_name) - 14) ||
709 (p_l >= full->long_name + sizeof(full->long_name) - 1))
711 DOS_ERROR( ER_PathNotFound, EC_NotFound, SA_Abort, EL_Disk );
712 return FALSE;
715 /* Get the long and short name matching the file name */
717 if ((found = DOSFS_FindUnixName( full->long_name, name, p_l + 1,
718 sizeof(full->long_name) - (p_l - full->long_name) - 1,
719 p_s + 1, !(flags & DRIVE_CASE_SENSITIVE) )))
721 *p_l++ = '/';
722 p_l += strlen(p_l);
723 *p_s++ = '\\';
724 p_s += strlen(p_s);
725 while (!IS_END_OF_NAME(*name)) name++;
727 else if (!check_last)
729 *p_l++ = '/';
730 *p_s++ = '\\';
731 while (!IS_END_OF_NAME(*name) &&
732 (p_s < full->short_name + sizeof(full->short_name) - 1) &&
733 (p_l < full->long_name + sizeof(full->long_name) - 1))
735 *p_l++ = *p_s++ = tolower(*name);
736 name++;
738 *p_l = *p_s = '\0';
740 while ((*name == '\\') || (*name == '/')) name++;
743 if (!found)
745 if (check_last)
747 DOS_ERROR( ER_FileNotFound, EC_NotFound, SA_Abort, EL_Disk );
748 return FALSE;
750 if (*name) /* Not last */
752 DOS_ERROR( ER_PathNotFound, EC_NotFound, SA_Abort, EL_Disk );
753 return FALSE;
756 if (!full->long_name[0]) strcpy( full->long_name, "/" );
757 if (!full->short_name[2]) strcpy( full->short_name + 2, "\\" );
758 dprintf_dosfs( stddeb, "DOSFS_GetFullName: returning %s = %s\n",
759 full->long_name, full->short_name );
760 return TRUE;
764 /***********************************************************************
765 * GetShortPathName32A (KERNEL32.271)
767 DWORD WINAPI GetShortPathName32A( LPCSTR longpath, LPSTR shortpath,
768 DWORD shortlen )
770 DOS_FULL_NAME full_name;
772 /* FIXME: is it correct to always return a fully qualified short path? */
773 if (!DOSFS_GetFullName( longpath, TRUE, &full_name )) return 0;
774 lstrcpyn32A( shortpath, full_name.short_name, shortlen );
775 return strlen( full_name.short_name );
779 /***********************************************************************
780 * GetShortPathName32W (KERNEL32.272)
782 DWORD WINAPI GetShortPathName32W( LPCWSTR longpath, LPWSTR shortpath,
783 DWORD shortlen )
785 DOS_FULL_NAME full_name;
786 DWORD ret = 0;
787 LPSTR longpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, longpath );
789 /* FIXME: is it correct to always return a fully qualified short path? */
790 if (DOSFS_GetFullName( longpathA, TRUE, &full_name ))
792 ret = strlen( full_name.short_name );
793 lstrcpynAtoW( shortpath, full_name.short_name, shortlen );
795 HeapFree( GetProcessHeap(), 0, longpathA );
796 return ret;
800 /***********************************************************************
801 * DOSFS_DoGetFullPathName
803 * Implementation of GetFullPathName32A/W.
805 static DWORD DOSFS_DoGetFullPathName( LPCSTR name, DWORD len, LPSTR result,
806 BOOL32 unicode )
808 char buffer[MAX_PATHNAME_LEN];
809 int drive;
810 char *p;
812 dprintf_dosfs( stddeb, "GetFullPathName: converting %s\n", name );
814 if (!name || !result) return 0;
816 if ((drive = DOSFS_GetPathDrive( &name )) == -1) return 0;
817 p = buffer;
818 *p++ = 'A' + drive;
819 *p++ = ':';
820 if (IS_END_OF_NAME(*name)) /* Absolute path */
822 while ((*name == '\\') || (*name == '/')) name++;
824 else /* Relative path */
826 *p++ = '\\';
827 lstrcpyn32A( p, DRIVE_GetDosCwd(drive), sizeof(buffer) - 3 );
828 if (*p) p += strlen(p); else p--;
830 *p = '\0';
832 while (*name)
834 if (*name == '.')
836 if (IS_END_OF_NAME(name[1]))
838 name++;
839 while ((*name == '\\') || (*name == '/')) name++;
840 continue;
842 else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
844 name += 2;
845 while ((*name == '\\') || (*name == '/')) name++;
846 while ((p > buffer + 2) && (*p != '\\')) p--;
847 *p = '\0'; /* Remove trailing separator */
848 continue;
851 if (p >= buffer + sizeof(buffer) - 1)
853 DOS_ERROR( ER_PathNotFound, EC_NotFound, SA_Abort, EL_Disk );
854 return 0;
856 *p++ = '\\';
857 while (!IS_END_OF_NAME(*name) && (p < buffer + sizeof(buffer) - 1))
858 *p++ = *name++;
859 *p = '\0';
860 while ((*name == '\\') || (*name == '/')) name++;
863 if (!buffer[2])
865 buffer[2] = '\\';
866 buffer[3] = '\0';
868 if (!(DRIVE_GetFlags(drive) & DRIVE_CASE_PRESERVING))
869 CharUpper32A( buffer );
871 if (unicode) lstrcpynAtoW( (LPWSTR)result, buffer, len );
872 else lstrcpyn32A( result, buffer, len );
874 dprintf_dosfs( stddeb, "GetFullPathName: returning %s\n", buffer );
875 return strlen(buffer);
879 /***********************************************************************
880 * GetFullPathName32A (KERNEL32.272)
882 DWORD WINAPI GetFullPathName32A( LPCSTR name, DWORD len, LPSTR buffer,
883 LPSTR *lastpart )
885 DWORD ret = DOSFS_DoGetFullPathName( name, len, buffer, FALSE );
886 if (ret && lastpart)
888 LPSTR p = buffer + strlen(buffer);
889 while ((p > buffer + 2) && (*p != '\\')) p--;
890 *lastpart = p + 1;
892 return ret;
896 /***********************************************************************
897 * GetFullPathName32W (KERNEL32.273)
899 DWORD WINAPI GetFullPathName32W( LPCWSTR name, DWORD len, LPWSTR buffer,
900 LPWSTR *lastpart )
902 LPSTR nameA = HEAP_strdupWtoA( GetProcessHeap(), 0, name );
903 DWORD ret = DOSFS_DoGetFullPathName( nameA, len, (LPSTR)buffer, TRUE );
904 HeapFree( GetProcessHeap(), 0, nameA );
905 if (ret && lastpart)
907 LPWSTR p = buffer + lstrlen32W(buffer);
908 while ((p > buffer + 2) && (*p != '\\')) p--;
909 *lastpart = p + 1;
911 return ret;
915 /***********************************************************************
916 * DOSFS_FindNext
918 * Find the next matching file. Return the number of entries read to find
919 * the matching one, or 0 if no more entries.
920 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
921 * file name mask. Either or both can be NULL.
923 int DOSFS_FindNext( const char *path, const char *short_mask,
924 const char *long_mask, int drive, BYTE attr,
925 int skip, WIN32_FIND_DATA32A *entry )
927 static DOS_DIR *dir = NULL;
928 int count = 0;
929 static char buffer[MAX_PATHNAME_LEN];
930 static int cur_pos = 0;
931 static int drive_root = 0;
932 char *p;
933 char dos_name[13];
934 LPCSTR long_name, short_name;
935 UINT32 flags;
936 BY_HANDLE_FILE_INFORMATION info;
938 if ((attr & ~(FA_UNUSED | FA_ARCHIVE | FA_RDONLY)) == FA_LABEL)
940 if (skip) return 0;
941 entry->dwFileAttributes = FILE_ATTRIBUTE_LABEL;
942 DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftCreationTime, 0 );
943 DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftLastAccessTime, 0 );
944 DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftLastWriteTime, 0 );
945 entry->nFileSizeHigh = 0;
946 entry->nFileSizeLow = 0;
947 entry->dwReserved0 = 0;
948 entry->dwReserved1 = 0;
949 DOSFS_ToDosDTAFormat( DRIVE_GetLabel( drive ), entry->cFileName );
950 strcpy( entry->cAlternateFileName, entry->cFileName );
951 return 1;
954 /* Check the cached directory */
955 if (dir && !strcmp( buffer, path ) && (cur_pos <= skip)) skip -= cur_pos;
956 else /* Not in the cache, open it anew */
958 const char *drive_path;
959 dprintf_dosfs( stddeb, "DOSFS_FindNext: cache miss, path=%s skip=%d buf=%s cur=%d\n",
960 path, skip, buffer, cur_pos );
961 cur_pos = skip;
962 if (dir) DOSFS_CloseDir(dir);
963 if (!*path) path = "/";
964 if (!(dir = DOSFS_OpenDir(path))) return 0;
965 drive_path = path + strlen(DRIVE_GetRoot(drive));
966 while ((*drive_path == '/') || (*drive_path == '\\')) drive_path++;
967 drive_root = !*drive_path;
968 dprintf_dosfs(stddeb, "DOSFS_FindNext: drive_root = %d\n", drive_root);
969 lstrcpyn32A( buffer, path, sizeof(buffer) - 1 );
971 strcat( buffer, "/" );
972 p = buffer + strlen(buffer);
973 attr |= FA_UNUSED | FA_ARCHIVE | FA_RDONLY;
974 flags = DRIVE_GetFlags( drive );
976 while (DOSFS_ReadDir( dir, &long_name, &short_name ))
978 if (skip-- > 0) continue;
979 count++;
981 /* Don't return '.' and '..' in the root of the drive */
982 if (drive_root && (long_name[0] == '.') &&
983 (!long_name[1] || ((long_name[1] == '.') && !long_name[2])))
984 continue;
986 /* Check the long mask */
988 if (long_mask)
990 if (!DOSFS_MatchLong( long_mask, long_name,
991 flags & DRIVE_CASE_SENSITIVE )) continue;
994 /* Check the short mask */
996 if (short_mask)
998 if (!short_name)
1000 DOSFS_Hash( long_name, dos_name, TRUE,
1001 !(flags & DRIVE_CASE_SENSITIVE) );
1002 short_name = dos_name;
1004 if (!DOSFS_MatchShort( short_mask, short_name )) continue;
1007 /* Check the file attributes */
1009 lstrcpyn32A( p, long_name, sizeof(buffer) - (int)(p - buffer) );
1010 if (!FILE_Stat( buffer, &info ))
1012 fprintf( stderr, "DOSFS_FindNext: can't stat %s\n", buffer );
1013 continue;
1015 if (info.dwFileAttributes & ~attr) continue;
1017 /* We now have a matching entry; fill the result and return */
1019 entry->dwFileAttributes = info.dwFileAttributes;
1020 entry->ftCreationTime = info.ftCreationTime;
1021 entry->ftLastAccessTime = info.ftLastAccessTime;
1022 entry->ftLastWriteTime = info.ftLastWriteTime;
1023 entry->nFileSizeHigh = info.nFileSizeHigh;
1024 entry->nFileSizeLow = info.nFileSizeLow;
1026 if (short_name)
1027 DOSFS_ToDosDTAFormat( short_name, entry->cAlternateFileName );
1028 else
1029 DOSFS_Hash( long_name, entry->cAlternateFileName, FALSE,
1030 !(flags & DRIVE_CASE_SENSITIVE) );
1032 lstrcpyn32A( entry->cFileName, long_name, sizeof(entry->cFileName) );
1033 if (!(flags & DRIVE_CASE_PRESERVING)) CharLower32A( entry->cFileName );
1034 dprintf_dosfs( stddeb, "DOSFS_FindNext: returning %s (%s) %02lx %ld\n",
1035 entry->cFileName, entry->cAlternateFileName,
1036 entry->dwFileAttributes, entry->nFileSizeLow );
1037 cur_pos += count;
1038 p[-1] = '\0'; /* Remove trailing slash in buffer */
1039 return count;
1041 DOSFS_CloseDir( dir );
1042 dir = NULL;
1043 return 0; /* End of directory */
1047 /*************************************************************************
1048 * FindFirstFile16 (KERNEL.413)
1050 HANDLE16 WINAPI FindFirstFile16( LPCSTR path, WIN32_FIND_DATA32A *data )
1052 DOS_FULL_NAME full_name;
1053 HGLOBAL16 handle;
1054 FIND_FIRST_INFO *info;
1056 if (!path) return 0;
1057 if (!DOSFS_GetFullName( path, FALSE, &full_name ))
1058 return INVALID_HANDLE_VALUE16;
1059 if (!(handle = GlobalAlloc16( GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO) )))
1060 return INVALID_HANDLE_VALUE16;
1061 info = (FIND_FIRST_INFO *)GlobalLock16( handle );
1062 info->path = HEAP_strdupA( SystemHeap, 0, full_name.long_name );
1063 info->mask = strrchr( info->path, '/' );
1064 *(info->mask++) = '\0';
1065 if (path[0] && (path[1] == ':')) info->drive = toupper(*path) - 'A';
1066 else info->drive = DRIVE_GetCurrentDrive();
1067 info->skip = 0;
1068 GlobalUnlock16( handle );
1069 if (!FindNextFile16( handle, data ))
1071 FindClose16( handle );
1072 DOS_ERROR( ER_NoMoreFiles, EC_MediaError, SA_Abort, EL_Disk );
1073 return INVALID_HANDLE_VALUE16;
1075 return handle;
1079 /*************************************************************************
1080 * FindFirstFile32A (KERNEL32.123)
1082 HANDLE32 WINAPI FindFirstFile32A( LPCSTR path, WIN32_FIND_DATA32A *data )
1084 HANDLE32 handle = FindFirstFile16( path, data );
1085 if (handle == INVALID_HANDLE_VALUE16) return INVALID_HANDLE_VALUE32;
1086 return handle;
1090 /*************************************************************************
1091 * FindFirstFile32W (KERNEL32.124)
1093 HANDLE32 WINAPI FindFirstFile32W( LPCWSTR path, WIN32_FIND_DATA32W *data )
1095 WIN32_FIND_DATA32A dataA;
1096 LPSTR pathA = HEAP_strdupWtoA( GetProcessHeap(), 0, path );
1097 HANDLE32 handle = FindFirstFile32A( pathA, &dataA );
1098 HeapFree( GetProcessHeap(), 0, pathA );
1099 if (handle != INVALID_HANDLE_VALUE32)
1101 data->dwFileAttributes = dataA.dwFileAttributes;
1102 data->ftCreationTime = dataA.ftCreationTime;
1103 data->ftLastAccessTime = dataA.ftLastAccessTime;
1104 data->ftLastWriteTime = dataA.ftLastWriteTime;
1105 data->nFileSizeHigh = dataA.nFileSizeHigh;
1106 data->nFileSizeLow = dataA.nFileSizeLow;
1107 lstrcpyAtoW( data->cFileName, dataA.cFileName );
1108 lstrcpyAtoW( data->cAlternateFileName, dataA.cAlternateFileName );
1110 return handle;
1114 /*************************************************************************
1115 * FindNextFile16 (KERNEL.414)
1117 BOOL16 WINAPI FindNextFile16( HANDLE16 handle, WIN32_FIND_DATA32A *data )
1119 FIND_FIRST_INFO *info;
1120 int count;
1122 if (!(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
1124 DOS_ERROR( ER_InvalidHandle, EC_ProgramError, SA_Abort, EL_Disk );
1125 return FALSE;
1127 GlobalUnlock16( handle );
1128 if (!info->path)
1130 DOS_ERROR( ER_NoMoreFiles, EC_MediaError, SA_Abort, EL_Disk );
1131 return FALSE;
1133 if (!(count = DOSFS_FindNext( info->path, NULL, info->mask, info->drive,
1134 0xff, info->skip, data )))
1136 HeapFree( SystemHeap, 0, info->path );
1137 info->path = info->mask = NULL;
1138 DOS_ERROR( ER_NoMoreFiles, EC_MediaError, SA_Abort, EL_Disk );
1139 return FALSE;
1141 info->skip += count;
1142 return TRUE;
1146 /*************************************************************************
1147 * FindNextFile32A (KERNEL32.126)
1149 BOOL32 WINAPI FindNextFile32A( HANDLE32 handle, WIN32_FIND_DATA32A *data )
1151 return FindNextFile16( handle, data );
1155 /*************************************************************************
1156 * FindNextFile32W (KERNEL32.127)
1158 BOOL32 WINAPI FindNextFile32W( HANDLE32 handle, WIN32_FIND_DATA32W *data )
1160 WIN32_FIND_DATA32A dataA;
1161 if (!FindNextFile32A( handle, &dataA )) return FALSE;
1162 data->dwFileAttributes = dataA.dwFileAttributes;
1163 data->ftCreationTime = dataA.ftCreationTime;
1164 data->ftLastAccessTime = dataA.ftLastAccessTime;
1165 data->ftLastWriteTime = dataA.ftLastWriteTime;
1166 data->nFileSizeHigh = dataA.nFileSizeHigh;
1167 data->nFileSizeLow = dataA.nFileSizeLow;
1168 lstrcpyAtoW( data->cFileName, dataA.cFileName );
1169 lstrcpyAtoW( data->cAlternateFileName, dataA.cAlternateFileName );
1170 return TRUE;
1174 /*************************************************************************
1175 * FindClose16 (KERNEL.415)
1177 BOOL16 WINAPI FindClose16( HANDLE16 handle )
1179 FIND_FIRST_INFO *info;
1181 if ((handle == INVALID_HANDLE_VALUE16) ||
1182 !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
1184 DOS_ERROR( ER_InvalidHandle, EC_ProgramError, SA_Abort, EL_Disk );
1185 return FALSE;
1187 if (info->path) HeapFree( SystemHeap, 0, info->path );
1188 GlobalUnlock16( handle );
1189 GlobalFree16( handle );
1190 return TRUE;
1194 /*************************************************************************
1195 * FindClose32 (KERNEL32.119)
1197 BOOL32 WINAPI FindClose32( HANDLE32 handle )
1199 return FindClose16( (HANDLE16)handle );
1203 /***********************************************************************
1204 * DOSFS_UnixTimeToFileTime
1206 * Convert a Unix time to FILETIME format.
1207 * The FILETIME structure is a 64-bit value representing the number of
1208 * 100-nanosecond intervals since January 1, 1601, 0:00.
1209 * 'remainder' is the nonnegative number of 100-ns intervals
1210 * corresponding to the time fraction smaller than 1 second that
1211 * couldn't be stored in the time_t value.
1213 void DOSFS_UnixTimeToFileTime( time_t unix_time, FILETIME *filetime,
1214 DWORD remainder )
1216 /* NOTES:
1218 CONSTANTS:
1219 The time difference between 1 January 1601, 00:00:00 and
1220 1 January 1970, 00:00:00 is 369 years, plus the leap years
1221 from 1604 to 1968, excluding 1700, 1800, 1900.
1222 This makes (1968 - 1600) / 4 - 3 = 89 leap days, and a total
1223 of 134774 days.
1225 Any day in that period had 24 * 60 * 60 = 86400 seconds.
1227 The time difference is 134774 * 86400 * 10000000, which can be written
1228 116444736000000000
1229 27111902 * 2^32 + 3577643008
1230 413 * 2^48 + 45534 * 2^32 + 54590 * 2^16 + 32768
1232 If you find that these constants are buggy, please change them in all
1233 instances in both conversion functions.
1235 VERSIONS:
1236 There are two versions, one of them uses long long variables and
1237 is presumably faster but not ISO C. The other one uses standard C
1238 data types and operations but relies on the assumption that negative
1239 numbers are stored as 2's complement (-1 is 0xffff....). If this
1240 assumption is violated, dates before 1970 will not convert correctly.
1241 This should however work on any reasonable architecture where WINE
1242 will run.
1244 DETAILS:
1246 Take care not to remove the casts. I have tested these functions
1247 (in both versions) for a lot of numbers. I would be interested in
1248 results on other compilers than GCC.
1250 The operations have been designed to account for the possibility
1251 of 64-bit time_t in future UNICES. Even the versions without
1252 internal long long numbers will work if time_t only is 64 bit.
1253 A 32-bit shift, which was necessary for that operation, turned out
1254 not to work correctly in GCC, besides giving the warning. So I
1255 used a double 16-bit shift instead. Numbers are in the ISO version
1256 represented by three limbs, the most significant with 32 bit, the
1257 other two with 16 bit each.
1259 As the modulo-operator % is not well-defined for negative numbers,
1260 negative divisors have been avoided in DOSFS_FileTimeToUnixTime.
1262 There might be quicker ways to do this in C. Certainly so in
1263 assembler.
1265 Claus Fischer, fischer@iue.tuwien.ac.at
1268 #if __GNUC__
1269 # define USE_LONG_LONG 1
1270 #else
1271 # define USE_LONG_LONG 0
1272 #endif
1274 #if USE_LONG_LONG /* gcc supports long long type */
1276 long long int t = unix_time;
1277 t *= 10000000;
1278 t += 116444736000000000LL;
1279 t += remainder;
1280 filetime->dwLowDateTime = (UINT32)t;
1281 filetime->dwHighDateTime = (UINT32)(t >> 32);
1283 #else /* ISO version */
1285 UINT32 a0; /* 16 bit, low bits */
1286 UINT32 a1; /* 16 bit, medium bits */
1287 UINT32 a2; /* 32 bit, high bits */
1289 /* Copy the unix time to a2/a1/a0 */
1290 a0 = unix_time & 0xffff;
1291 a1 = (unix_time >> 16) & 0xffff;
1292 /* This is obsolete if unix_time is only 32 bits, but it does not hurt.
1293 Do not replace this by >> 32, it gives a compiler warning and it does
1294 not work. */
1295 a2 = (unix_time >= 0 ? (unix_time >> 16) >> 16 :
1296 ~((~unix_time >> 16) >> 16));
1298 /* Multiply a by 10000000 (a = a2/a1/a0)
1299 Split the factor into 10000 * 1000 which are both less than 0xffff. */
1300 a0 *= 10000;
1301 a1 = a1 * 10000 + (a0 >> 16);
1302 a2 = a2 * 10000 + (a1 >> 16);
1303 a0 &= 0xffff;
1304 a1 &= 0xffff;
1306 a0 *= 1000;
1307 a1 = a1 * 1000 + (a0 >> 16);
1308 a2 = a2 * 1000 + (a1 >> 16);
1309 a0 &= 0xffff;
1310 a1 &= 0xffff;
1312 /* Add the time difference and the remainder */
1313 a0 += 32768 + (remainder & 0xffff);
1314 a1 += 54590 + (remainder >> 16 ) + (a0 >> 16);
1315 a2 += 27111902 + (a1 >> 16);
1316 a0 &= 0xffff;
1317 a1 &= 0xffff;
1319 /* Set filetime */
1320 filetime->dwLowDateTime = (a1 << 16) + a0;
1321 filetime->dwHighDateTime = a2;
1322 #endif
1326 /***********************************************************************
1327 * DOSFS_FileTimeToUnixTime
1329 * Convert a FILETIME format to Unix time.
1330 * If not NULL, 'remainder' contains the fractional part of the filetime,
1331 * in the range of [0..9999999] (even if time_t is negative).
1333 time_t DOSFS_FileTimeToUnixTime( const FILETIME *filetime, DWORD *remainder )
1335 /* Read the comment in the function DOSFS_UnixTimeToFileTime. */
1336 #if USE_LONG_LONG
1338 long long int t = filetime->dwHighDateTime;
1339 t <<= 32;
1340 t += (UINT32)filetime->dwLowDateTime;
1341 t -= 116444736000000000LL;
1342 if (t < 0)
1344 if (remainder) *remainder = 9999999 - (-t - 1) % 10000000;
1345 return -1 - ((-t - 1) / 10000000);
1347 else
1349 if (remainder) *remainder = t % 10000000;
1350 return t / 10000000;
1353 #else /* ISO version */
1355 UINT32 a0; /* 16 bit, low bits */
1356 UINT32 a1; /* 16 bit, medium bits */
1357 UINT32 a2; /* 32 bit, high bits */
1358 UINT32 r; /* remainder of division */
1359 unsigned int carry; /* carry bit for subtraction */
1360 int negative; /* whether a represents a negative value */
1362 /* Copy the time values to a2/a1/a0 */
1363 a2 = (UINT32)filetime->dwHighDateTime;
1364 a1 = ((UINT32)filetime->dwLowDateTime ) >> 16;
1365 a0 = ((UINT32)filetime->dwLowDateTime ) & 0xffff;
1367 /* Subtract the time difference */
1368 if (a0 >= 32768 ) a0 -= 32768 , carry = 0;
1369 else a0 += (1 << 16) - 32768 , carry = 1;
1371 if (a1 >= 54590 + carry) a1 -= 54590 + carry, carry = 0;
1372 else a1 += (1 << 16) - 54590 - carry, carry = 1;
1374 a2 -= 27111902 + carry;
1376 /* If a is negative, replace a by (-1-a) */
1377 negative = (a2 >= ((UINT32)1) << 31);
1378 if (negative)
1380 /* Set a to -a - 1 (a is a2/a1/a0) */
1381 a0 = 0xffff - a0;
1382 a1 = 0xffff - a1;
1383 a2 = ~a2;
1386 /* Divide a by 10000000 (a = a2/a1/a0), put the rest into r.
1387 Split the divisor into 10000 * 1000 which are both less than 0xffff. */
1388 a1 += (a2 % 10000) << 16;
1389 a2 /= 10000;
1390 a0 += (a1 % 10000) << 16;
1391 a1 /= 10000;
1392 r = a0 % 10000;
1393 a0 /= 10000;
1395 a1 += (a2 % 1000) << 16;
1396 a2 /= 1000;
1397 a0 += (a1 % 1000) << 16;
1398 a1 /= 1000;
1399 r += (a0 % 1000) * 10000;
1400 a0 /= 1000;
1402 /* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
1403 if (negative)
1405 /* Set a to -a - 1 (a is a2/a1/a0) */
1406 a0 = 0xffff - a0;
1407 a1 = 0xffff - a1;
1408 a2 = ~a2;
1410 r = 9999999 - r;
1413 if (remainder) *remainder = r;
1415 /* Do not replace this by << 32, it gives a compiler warning and it does
1416 not work. */
1417 return ((((time_t)a2) << 16) << 16) + (a1 << 16) + a0;
1418 #endif
1422 /***********************************************************************
1423 * DosDateTimeToFileTime (KERNEL32.76)
1425 BOOL32 WINAPI DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft)
1427 struct tm newtm;
1429 newtm.tm_sec = (fattime & 0x1f) * 2;
1430 newtm.tm_min = (fattime >> 5) & 0x3f;
1431 newtm.tm_hour = (fattime >> 11);
1432 newtm.tm_mday = (fatdate & 0x1f);
1433 newtm.tm_mon = ((fatdate >> 5) & 0x0f) - 1;
1434 newtm.tm_year = (fatdate >> 9) + 80;
1435 DOSFS_UnixTimeToFileTime( mktime( &newtm ), ft, 0 );
1436 return TRUE;
1440 /***********************************************************************
1441 * FileTimeToDosDateTime (KERNEL32.111)
1443 BOOL32 WINAPI FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate,
1444 LPWORD fattime )
1446 time_t unixtime = DOSFS_FileTimeToUnixTime( ft, NULL );
1447 struct tm *tm = localtime( &unixtime );
1448 if (fattime)
1449 *fattime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
1450 if (fatdate)
1451 *fatdate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
1452 + tm->tm_mday;
1453 return TRUE;
1457 /***********************************************************************
1458 * LocalFileTimeToFileTime (KERNEL32.373)
1460 BOOL32 WINAPI LocalFileTimeToFileTime( const FILETIME *localft,
1461 LPFILETIME utcft )
1463 struct tm *xtm;
1464 DWORD remainder;
1466 /* convert from local to UTC. Perhaps not correct. FIXME */
1467 time_t unixtime = DOSFS_FileTimeToUnixTime( localft, &remainder );
1468 xtm = gmtime( &unixtime );
1469 DOSFS_UnixTimeToFileTime( mktime(xtm), utcft, remainder );
1470 return TRUE;
1474 /***********************************************************************
1475 * FileTimeToLocalFileTime (KERNEL32.112)
1477 BOOL32 WINAPI FileTimeToLocalFileTime( const FILETIME *utcft,
1478 LPFILETIME localft )
1480 struct tm *xtm;
1481 DWORD remainder;
1483 /* convert from UTC to local. Perhaps not correct. FIXME */
1484 time_t unixtime = DOSFS_FileTimeToUnixTime( utcft, &remainder );
1485 xtm = localtime( &unixtime );
1486 DOSFS_UnixTimeToFileTime( mktime(xtm), localft, remainder );
1487 return TRUE;
1491 /***********************************************************************
1492 * FileTimeToSystemTime (KERNEL32.113)
1494 BOOL32 WINAPI FileTimeToSystemTime( const FILETIME *ft, LPSYSTEMTIME syst )
1496 struct tm *xtm;
1497 DWORD remainder;
1498 time_t xtime = DOSFS_FileTimeToUnixTime( ft, &remainder );
1499 xtm = gmtime(&xtime);
1500 syst->wYear = xtm->tm_year;
1501 syst->wMonth = xtm->tm_mon;
1502 syst->wDayOfWeek = xtm->tm_wday;
1503 syst->wDay = xtm->tm_mday;
1504 syst->wHour = xtm->tm_hour;
1505 syst->wMinute = xtm->tm_min;
1506 syst->wSecond = xtm->tm_sec;
1507 syst->wMilliseconds = remainder / 10000;
1508 return TRUE;
1511 /***********************************************************************
1512 * QueryDosDeviceA (KERNEL32.413)
1514 * returns array of strings terminated by \0, terminated by \0
1516 DWORD WINAPI QueryDosDevice32A(LPCSTR devname,LPSTR target,DWORD bufsize)
1518 LPSTR s;
1519 char buffer[200];
1521 dprintf_dosfs(stddeb,"QueryDosDevice(%s,...)\n",devname?devname:"<null>");
1522 if (!devname) {
1523 /* return known MSDOS devices */
1524 lstrcpy32A(buffer,"CON COM1 COM2 LPT1 NUL ");
1525 while ((s=strchr(buffer,' ')))
1526 *s='\0';
1528 lstrcpyn32A(target,buffer,bufsize);
1529 return strlen(buffer);
1531 lstrcpy32A(buffer,"\\DEV\\");
1532 lstrcat32A(buffer,devname);
1533 if ((s=strchr(buffer,':'))) *s='\0';
1534 lstrcpyn32A(target,buffer,bufsize);
1535 return strlen(buffer);
1539 /***********************************************************************
1540 * QueryDosDeviceW (KERNEL32.414)
1542 * returns array of strings terminated by \0, terminated by \0
1544 DWORD WINAPI QueryDosDevice32W(LPCWSTR devname,LPWSTR target,DWORD bufsize)
1546 LPSTR devnameA = devname?HEAP_strdupWtoA(GetProcessHeap(),0,devname):NULL;
1547 LPSTR targetA = (LPSTR)HEAP_xalloc(GetProcessHeap(),0,bufsize);
1548 DWORD ret = QueryDosDevice32A(devnameA,targetA,bufsize);
1550 lstrcpynAtoW(target,targetA,bufsize);
1551 if (devnameA) HeapFree(GetProcessHeap(),0,devnameA);
1552 if (targetA) HeapFree(GetProcessHeap(),0,targetA);
1553 return ret;
1557 /***********************************************************************
1558 * SystemTimeToFileTime (KERNEL32.526)
1560 BOOL32 WINAPI SystemTimeToFileTime( const SYSTEMTIME *syst, LPFILETIME ft )
1562 struct tm xtm;
1564 xtm.tm_year = syst->wYear;
1565 xtm.tm_mon = syst->wMonth;
1566 xtm.tm_wday = syst->wDayOfWeek;
1567 xtm.tm_mday = syst->wDay;
1568 xtm.tm_hour = syst->wHour;
1569 xtm.tm_min = syst->wMinute;
1570 xtm.tm_sec = syst->wSecond;
1571 DOSFS_UnixTimeToFileTime( mktime(&xtm), ft, syst->wMilliseconds * 10000 );
1572 return TRUE;