Release 980726
[wine/multimedia.git] / files / dos_fs.c
blob1204d09def153c712725e89641d7a4c4a07b1176
1 /*
2 * DOS file system functions
4 * Copyright 1993 Erik Bos
5 * Copyright 1996 Alexandre Julliard
6 */
8 #include "config.h"
9 #include <sys/types.h>
10 #include <ctype.h>
11 #include <dirent.h>
12 #include <errno.h>
13 #include <fcntl.h>
14 #include <string.h>
15 #include <stdlib.h>
16 #include <sys/stat.h>
17 #include <sys/ioctl.h>
18 #include <time.h>
19 #include <unistd.h>
21 #include "windows.h"
22 #include "winerror.h"
23 #include "drive.h"
24 #include "file.h"
25 #include "heap.h"
26 #include "msdos.h"
27 #include "debug.h"
29 /* Define the VFAT ioctl to get both short and long file names */
30 /* FIXME: is it possible to get this to work on other systems? */
31 #ifdef linux
32 #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, long)
33 /* We want the real kernel dirent structure, not the libc one */
34 typedef struct
36 long d_ino;
37 long d_off;
38 unsigned short d_reclen;
39 char d_name[256];
40 } KERNEL_DIRENT;
42 #else /* linux */
43 #undef VFAT_IOCTL_READDIR_BOTH /* just in case... */
44 #endif /* linux */
46 /* Chars we don't want to see in DOS file names */
47 #define INVALID_DOS_CHARS "*?<>|\"+=,;[] \345"
49 static const DOS_DEVICE DOSFS_Devices[] =
50 /* name, device flags (see Int 21/AX=0x4400) */
52 { "CON", 0xc0d3 },
53 { "PRN", 0xa0c0 },
54 { "NUL", 0x80c4 },
55 { "AUX", 0x80c0 },
56 { "LPT1", 0xa0c0 },
57 { "LPT2", 0xa0c0 },
58 { "LPT3", 0xa0c0 },
59 { "LPT4", 0xc0d3 },
60 { "COM1", 0x80c0 },
61 { "COM2", 0x80c0 },
62 { "COM3", 0x80c0 },
63 { "COM4", 0x80c0 },
64 { "SCSIMGR$", 0xc0c0 },
65 { "HPSCAN", 0xc0c0 }
68 #define GET_DRIVE(path) \
69 (((path)[1] == ':') ? toupper((path)[0]) - 'A' : DOSFS_CurDrive)
71 /* DOS extended error status */
72 WORD DOS_ExtendedError;
73 BYTE DOS_ErrorClass;
74 BYTE DOS_ErrorAction;
75 BYTE DOS_ErrorLocus;
77 /* Info structure for FindFirstFile handle */
78 typedef struct
80 LPSTR path;
81 LPSTR mask;
82 int drive;
83 int skip;
84 } FIND_FIRST_INFO;
87 /* Directory info for DOSFS_ReadDir */
88 typedef struct
90 DIR *dir;
91 #ifdef VFAT_IOCTL_READDIR_BOTH
92 int fd;
93 char short_name[12];
94 KERNEL_DIRENT dirent[2];
95 #endif
96 } DOS_DIR;
99 /***********************************************************************
100 * DOSFS_ValidDOSName
102 * Return 1 if Unix file 'name' is also a valid MS-DOS name
103 * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
104 * File name can be terminated by '\0', '\\' or '/'.
106 static int DOSFS_ValidDOSName( const char *name, int ignore_case )
108 static const char invalid_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS;
109 const char *p = name;
110 const char *invalid = ignore_case ? (invalid_chars + 26) : invalid_chars;
111 int len = 0;
113 if (*p == '.')
115 /* Check for "." and ".." */
116 p++;
117 if (*p == '.') p++;
118 /* All other names beginning with '.' are invalid */
119 return (IS_END_OF_NAME(*p));
121 while (!IS_END_OF_NAME(*p))
123 if (strchr( invalid, *p )) return 0; /* Invalid char */
124 if (*p == '.') break; /* Start of the extension */
125 if (++len > 8) return 0; /* Name too long */
126 p++;
128 if (*p != '.') return 1; /* End of name */
129 p++;
130 if (IS_END_OF_NAME(*p)) return 0; /* Empty extension not allowed */
131 len = 0;
132 while (!IS_END_OF_NAME(*p))
134 if (strchr( invalid, *p )) return 0; /* Invalid char */
135 if (*p == '.') return 0; /* Second extension not allowed */
136 if (++len > 3) return 0; /* Extension too long */
137 p++;
139 return 1;
143 /***********************************************************************
144 * DOSFS_ToDosFCBFormat
146 * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
147 * expanding wild cards and converting to upper-case in the process.
148 * File name can be terminated by '\0', '\\' or '/'.
149 * Return FALSE if the name is not a valid DOS name.
150 * 'buffer' must be at least 12 characters long.
152 BOOL32 DOSFS_ToDosFCBFormat( LPCSTR name, LPSTR buffer )
154 static const char invalid_chars[] = INVALID_DOS_CHARS;
155 const char *p = name;
156 int i;
158 /* Check for "." and ".." */
159 if (*p == '.')
161 p++;
162 strcpy( buffer, ". " );
163 if (*p == '.')
165 buffer[1] = '.';
166 p++;
168 return (!*p || (*p == '/') || (*p == '\\'));
171 for (i = 0; i < 8; i++)
173 switch(*p)
175 case '\0':
176 case '\\':
177 case '/':
178 case '.':
179 buffer[i] = ' ';
180 break;
181 case '?':
182 p++;
183 /* fall through */
184 case '*':
185 buffer[i] = '?';
186 break;
187 default:
188 if (strchr( invalid_chars, *p )) return FALSE;
189 buffer[i] = toupper(*p);
190 p++;
191 break;
195 if (*p == '*')
197 /* Skip all chars after wildcard up to first dot */
198 while (*p && (*p != '/') && (*p != '\\') && (*p != '.')) p++;
200 else
202 /* Check if name too long */
203 if (*p && (*p != '/') && (*p != '\\') && (*p != '.')) return FALSE;
205 if (*p == '.') p++; /* Skip dot */
207 for (i = 8; i < 11; i++)
209 switch(*p)
211 case '\0':
212 case '\\':
213 case '/':
214 buffer[i] = ' ';
215 break;
216 case '.':
217 return FALSE; /* Second extension not allowed */
218 case '?':
219 p++;
220 /* fall through */
221 case '*':
222 buffer[i] = '?';
223 break;
224 default:
225 if (strchr( invalid_chars, *p )) return FALSE;
226 buffer[i] = toupper(*p);
227 p++;
228 break;
231 buffer[11] = '\0';
232 return TRUE;
236 /***********************************************************************
237 * DOSFS_ToDosDTAFormat
239 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
240 * converting to upper-case in the process.
241 * File name can be terminated by '\0', '\\' or '/'.
242 * 'buffer' must be at least 13 characters long.
244 static void DOSFS_ToDosDTAFormat( LPCSTR name, LPSTR buffer )
246 char *p;
248 memcpy( buffer, name, 8 );
249 for (p = buffer + 8; (p > buffer) && (p[-1] == ' '); p--);
250 *p++ = '.';
251 memcpy( p, name + 8, 3 );
252 for (p += 3; p[-1] == ' '; p--);
253 if (p[-1] == '.') p--;
254 *p = '\0';
258 /***********************************************************************
259 * DOSFS_MatchShort
261 * Check a DOS file name against a mask (both in FCB format).
263 static int DOSFS_MatchShort( const char *mask, const char *name )
265 int i;
266 for (i = 11; i > 0; i--, mask++, name++)
267 if ((*mask != '?') && (*mask != *name)) return 0;
268 return 1;
272 /***********************************************************************
273 * DOSFS_MatchLong
275 * Check a long file name against a mask.
277 static int DOSFS_MatchLong( const char *mask, const char *name,
278 int case_sensitive )
280 if (!strcmp( mask, "*.*" )) return 1;
281 while (*name && *mask)
283 if (*mask == '*')
285 mask++;
286 while (*mask == '*') mask++; /* Skip consecutive '*' */
287 if (!*mask) return 1;
288 if (case_sensitive) while (*name && (*name != *mask)) name++;
289 else while (*name && (toupper(*name) != toupper(*mask))) name++;
290 if (!*name) return 0;
292 else if (*mask != '?')
294 if (case_sensitive)
296 if (*mask != *name) return 0;
298 else if (toupper(*mask) != toupper(*name)) return 0;
300 mask++;
301 name++;
303 return (!*name && !*mask);
307 /***********************************************************************
308 * DOSFS_OpenDir
310 static DOS_DIR *DOSFS_OpenDir( LPCSTR path )
312 DOS_DIR *dir = HeapAlloc( SystemHeap, 0, sizeof(*dir) );
313 if (!dir)
315 DOS_ERROR( ER_OutOfMemory, EC_OutOfResource, SA_Abort, EL_Memory );
316 return NULL;
319 #ifdef VFAT_IOCTL_READDIR_BOTH
321 /* Check if the VFAT ioctl is supported on this directory */
323 if ((dir->fd = open( path, O_RDONLY )) != -1)
325 if (ioctl( dir->fd, VFAT_IOCTL_READDIR_BOTH, (long)dir->dirent ) == -1)
327 close( dir->fd );
328 dir->fd = -1;
330 else
332 /* Set the file pointer back at the start of the directory */
333 lseek( dir->fd, 0, SEEK_SET );
334 dir->dir = NULL;
335 return dir;
338 #endif /* VFAT_IOCTL_READDIR_BOTH */
340 /* Now use the standard opendir/readdir interface */
342 if (!(dir->dir = opendir( path )))
344 HeapFree( SystemHeap, 0, dir );
345 return NULL;
347 return dir;
351 /***********************************************************************
352 * DOSFS_CloseDir
354 static void DOSFS_CloseDir( DOS_DIR *dir )
356 #ifdef VFAT_IOCTL_READDIR_BOTH
357 if (dir->fd != -1) close( dir->fd );
358 #endif /* VFAT_IOCTL_READDIR_BOTH */
359 if (dir->dir) closedir( dir->dir );
360 HeapFree( SystemHeap, 0, dir );
364 /***********************************************************************
365 * DOSFS_ReadDir
367 static BOOL32 DOSFS_ReadDir( DOS_DIR *dir, LPCSTR *long_name,
368 LPCSTR *short_name )
370 struct dirent *dirent;
372 #ifdef VFAT_IOCTL_READDIR_BOTH
373 if (dir->fd != -1)
375 if (ioctl( dir->fd, VFAT_IOCTL_READDIR_BOTH, (long)dir->dirent ) != -1) {
376 if (!dir->dirent[0].d_reclen) return FALSE;
377 if (!DOSFS_ToDosFCBFormat( dir->dirent[0].d_name, dir->short_name ))
378 dir->short_name[0] = '\0';
379 *short_name = dir->short_name;
380 if (dir->dirent[1].d_name[0]) *long_name = dir->dirent[1].d_name;
381 else *long_name = dir->dirent[0].d_name;
382 return TRUE;
385 #endif /* VFAT_IOCTL_READDIR_BOTH */
387 if (!(dirent = readdir( dir->dir ))) return FALSE;
388 *long_name = dirent->d_name;
389 *short_name = NULL;
390 return TRUE;
394 /***********************************************************************
395 * DOSFS_Hash
397 * Transform a Unix file name into a hashed DOS name. If the name is a valid
398 * DOS name, it is converted to upper-case; otherwise it is replaced by a
399 * hashed version that fits in 8.3 format.
400 * File name can be terminated by '\0', '\\' or '/'.
401 * 'buffer' must be at least 13 characters long.
403 static void DOSFS_Hash( LPCSTR name, LPSTR buffer, BOOL32 dir_format,
404 BOOL32 ignore_case )
406 static const char invalid_chars[] = INVALID_DOS_CHARS "~.";
407 static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
409 const char *p, *ext;
410 char *dst;
411 unsigned short hash;
412 int i;
414 if (dir_format) strcpy( buffer, " " );
416 if (DOSFS_ValidDOSName( name, ignore_case ))
418 /* Check for '.' and '..' */
419 if (*name == '.')
421 buffer[0] = '.';
422 if (!dir_format) buffer[1] = buffer[2] = '\0';
423 if (name[1] == '.') buffer[1] = '.';
424 return;
427 /* Simply copy the name, converting to uppercase */
429 for (dst = buffer; !IS_END_OF_NAME(*name) && (*name != '.'); name++)
430 *dst++ = toupper(*name);
431 if (*name == '.')
433 if (dir_format) dst = buffer + 8;
434 else *dst++ = '.';
435 for (name++; !IS_END_OF_NAME(*name); name++)
436 *dst++ = toupper(*name);
438 if (!dir_format) *dst = '\0';
439 return;
442 /* Compute the hash code of the file name */
443 /* If you know something about hash functions, feel free to */
444 /* insert a better algorithm here... */
445 if (ignore_case)
447 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
448 hash = (hash<<3) ^ (hash>>5) ^ tolower(*p) ^ (tolower(p[1]) << 8);
449 hash = (hash<<3) ^ (hash>>5) ^ tolower(*p); /* Last character*/
451 else
453 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
454 hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
455 hash = (hash << 3) ^ (hash >> 5) ^ *p; /* Last character */
458 /* Find last dot for start of the extension */
459 for (p = name+1, ext = NULL; !IS_END_OF_NAME(*p); p++)
460 if (*p == '.') ext = p;
461 if (ext && IS_END_OF_NAME(ext[1]))
462 ext = NULL; /* Empty extension ignored */
464 /* Copy first 4 chars, replacing invalid chars with '_' */
465 for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
467 if (IS_END_OF_NAME(*p) || (p == ext)) break;
468 *dst++ = strchr( invalid_chars, *p ) ? '_' : toupper(*p);
470 /* Pad to 5 chars with '~' */
471 while (i-- >= 0) *dst++ = '~';
473 /* Insert hash code converted to 3 ASCII chars */
474 *dst++ = hash_chars[(hash >> 10) & 0x1f];
475 *dst++ = hash_chars[(hash >> 5) & 0x1f];
476 *dst++ = hash_chars[hash & 0x1f];
478 /* Copy the first 3 chars of the extension (if any) */
479 if (ext)
481 if (!dir_format) *dst++ = '.';
482 for (i = 3, ext++; (i > 0) && !IS_END_OF_NAME(*ext); i--, ext++)
483 *dst++ = strchr( invalid_chars, *ext ) ? '_' : toupper(*ext);
485 if (!dir_format) *dst = '\0';
489 /***********************************************************************
490 * DOSFS_FindUnixName
492 * Find the Unix file name in a given directory that corresponds to
493 * a file name (either in Unix or DOS format).
494 * File name can be terminated by '\0', '\\' or '/'.
495 * Return TRUE if OK, FALSE if no file name matches.
497 * 'long_buf' must be at least 'long_len' characters long. If the long name
498 * turns out to be larger than that, the function returns FALSE.
499 * 'short_buf' must be at least 13 characters long.
501 BOOL32 DOSFS_FindUnixName( LPCSTR path, LPCSTR name, LPSTR long_buf,
502 INT32 long_len, LPSTR short_buf, BOOL32 ignore_case)
504 DOS_DIR *dir;
505 LPCSTR long_name, short_name;
506 char dos_name[12], tmp_buf[13];
507 BOOL32 ret;
509 const char *p = strchr( name, '/' );
510 int len = p ? (int)(p - name) : strlen(name);
511 if ((p = strchr( name, '\\' ))) len = MIN( (int)(p - name), len );
512 if (long_len < len + 1) return FALSE;
514 TRACE(dosfs, "%s,%s\n", path, name );
516 if (!DOSFS_ToDosFCBFormat( name, dos_name )) dos_name[0] = '\0';
518 if (!(dir = DOSFS_OpenDir( path )))
520 WARN(dosfs, "(%s,%s): can't open dir: %s\n",
521 path, name, strerror(errno) );
522 return FALSE;
525 while ((ret = DOSFS_ReadDir( dir, &long_name, &short_name )))
527 /* Check against Unix name */
528 if (len == strlen(long_name))
530 if (!ignore_case)
532 if (!lstrncmp32A( long_name, name, len )) break;
534 else
536 if (!lstrncmpi32A( long_name, name, len )) break;
539 if (dos_name[0])
541 /* Check against hashed DOS name */
542 if (!short_name)
544 DOSFS_Hash( long_name, tmp_buf, TRUE, ignore_case );
545 short_name = tmp_buf;
547 if (!strcmp( dos_name, short_name )) break;
550 if (ret)
552 if (long_buf) strcpy( long_buf, long_name );
553 if (short_buf)
555 if (short_name)
556 DOSFS_ToDosDTAFormat( short_name, short_buf );
557 else
558 DOSFS_Hash( long_name, short_buf, FALSE, ignore_case );
560 TRACE(dosfs, "(%s,%s) -> %s (%s)\n",
561 path, name, long_name, short_buf ? short_buf : "***");
563 else
564 WARN(dosfs, "'%s' not found in '%s'\n", name, path);
565 DOSFS_CloseDir( dir );
566 return ret;
570 /***********************************************************************
571 * DOSFS_GetDevice
573 * Check if a DOS file name represents a DOS device and return the device.
575 const DOS_DEVICE *DOSFS_GetDevice( const char *name )
577 int i;
578 const char *p;
580 if (name[0] && (name[1] == ':')) name += 2;
581 if ((p = strrchr( name, '/' ))) name = p + 1;
582 if ((p = strrchr( name, '\\' ))) name = p + 1;
583 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
585 const char *dev = DOSFS_Devices[i].name;
586 if (!lstrncmpi32A( dev, name, strlen(dev) ))
588 p = name + strlen( dev );
589 if (!*p || (*p == '.')) return &DOSFS_Devices[i];
592 return NULL;
595 /***********************************************************************
596 * DOSFS_OpenDevice
598 * Open a DOS device. This might not map 1:1 into the UNIX device concept.
600 HFILE32 DOSFS_OpenDevice( const char *name, int unixmode )
602 int i;
603 const char *p;
604 FILE_OBJECT *file;
605 HFILE32 handle;
607 if (name[0] && (name[1] == ':')) name += 2;
608 if ((p = strrchr( name, '/' ))) name = p + 1;
609 if ((p = strrchr( name, '\\' ))) name = p + 1;
610 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
612 const char *dev = DOSFS_Devices[i].name;
613 if (!lstrncmpi32A( dev, name, strlen(dev) ))
615 p = name + strlen( dev );
616 if (!*p || (*p == '.')) {
617 /* got it */
618 if (!strcmp(DOSFS_Devices[i].name,"NUL"))
619 return FILE_OpenUnixFile("/dev/null",unixmode);
620 if (!strcmp(DOSFS_Devices[i].name,"CON")) {
621 switch (unixmode) {
622 case O_RDONLY:
623 return GetStdHandle( STD_INPUT_HANDLE );
624 break;
625 case O_WRONLY:
626 return GetStdHandle( STD_OUTPUT_HANDLE );
627 break;
628 default:
629 FIXME(dosfs,"can't open CON read/write\n");
630 return HFILE_ERROR32;
631 break;
634 if (!strcmp(DOSFS_Devices[i].name,"SCSIMGR$")) {
635 if ((handle = FILE_Alloc( &file )) == INVALID_HANDLE_VALUE32)
636 return HFILE_ERROR32;
637 else {
638 file->unix_name = HEAP_strdupA( SystemHeap, 0, name );
639 return handle;
642 if (!strcmp(DOSFS_Devices[i].name,"HPSCAN")) {
643 if ((handle = FILE_Alloc( &file )) == INVALID_HANDLE_VALUE32)
644 return HFILE_ERROR32;
645 else {
646 file->unix_name = HEAP_strdupA( SystemHeap, 0, name );
647 return handle;
650 FIXME(dosfs,"device open %s not supported (yet)\n",DOSFS_Devices[i].name);
651 return HFILE_ERROR32;
655 return HFILE_ERROR32;
659 /***********************************************************************
660 * DOSFS_GetPathDrive
662 * Get the drive specified by a given path name (DOS or Unix format).
664 static int DOSFS_GetPathDrive( const char **name )
666 int drive;
667 const char *p = *name;
669 if (*p && (p[1] == ':'))
671 drive = toupper(*p) - 'A';
672 *name += 2;
674 else if (*p == '/') /* Absolute Unix path? */
676 if ((drive = DRIVE_FindDriveRoot( name )) == -1)
678 MSG("Warning: %s not accessible from a DOS drive\n", *name );
679 /* Assume it really was a DOS name */
680 drive = DRIVE_GetCurrentDrive();
683 else drive = DRIVE_GetCurrentDrive();
685 if (!DRIVE_IsValid(drive))
687 DOS_ERROR( ER_InvalidDrive, EC_MediaError, SA_Abort, EL_Disk );
688 return -1;
690 return drive;
694 /***********************************************************************
695 * DOSFS_GetFullName
697 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
698 * Unix name / short DOS name pair.
699 * Return FALSE if one of the path components does not exist. The last path
700 * component is only checked if 'check_last' is non-zero.
701 * The buffers pointed to by 'long_buf' and 'short_buf' must be
702 * at least MAX_PATHNAME_LEN long.
704 BOOL32 DOSFS_GetFullName( LPCSTR name, BOOL32 check_last, DOS_FULL_NAME *full )
706 BOOL32 found;
707 UINT32 flags;
708 char *p_l, *p_s, *root;
710 TRACE(dosfs, "%s (last=%d)\n",
711 name, check_last );
713 if ((full->drive = DOSFS_GetPathDrive( &name )) == -1) return FALSE;
714 flags = DRIVE_GetFlags( full->drive );
716 lstrcpyn32A( full->long_name, DRIVE_GetRoot( full->drive ),
717 sizeof(full->long_name) );
718 if (full->long_name[1]) root = full->long_name + strlen(full->long_name);
719 else root = full->long_name; /* root directory */
721 strcpy( full->short_name, "A:\\" );
722 full->short_name[0] += full->drive;
724 if ((*name == '\\') || (*name == '/')) /* Absolute path */
726 while ((*name == '\\') || (*name == '/')) name++;
728 else /* Relative path */
730 lstrcpyn32A( root + 1, DRIVE_GetUnixCwd( full->drive ),
731 sizeof(full->long_name) - (root - full->long_name) - 1 );
732 if (root[1]) *root = '/';
733 lstrcpyn32A( full->short_name + 3, DRIVE_GetDosCwd( full->drive ),
734 sizeof(full->short_name) - 3 );
737 p_l = full->long_name[1] ? full->long_name + strlen(full->long_name)
738 : full->long_name;
739 p_s = full->short_name[3] ? full->short_name + strlen(full->short_name)
740 : full->short_name + 2;
741 found = TRUE;
743 while (*name && found)
745 /* Check for '.' and '..' */
747 if (*name == '.')
749 if (IS_END_OF_NAME(name[1]))
751 name++;
752 while ((*name == '\\') || (*name == '/')) name++;
753 continue;
755 else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
757 name += 2;
758 while ((*name == '\\') || (*name == '/')) name++;
759 while ((p_l > root) && (*p_l != '/')) p_l--;
760 while ((p_s > full->short_name + 2) && (*p_s != '\\')) p_s--;
761 *p_l = *p_s = '\0'; /* Remove trailing separator */
762 continue;
766 /* Make sure buffers are large enough */
768 if ((p_s >= full->short_name + sizeof(full->short_name) - 14) ||
769 (p_l >= full->long_name + sizeof(full->long_name) - 1))
771 DOS_ERROR( ER_PathNotFound, EC_NotFound, SA_Abort, EL_Disk );
772 return FALSE;
775 /* Get the long and short name matching the file name */
777 if ((found = DOSFS_FindUnixName( full->long_name, name, p_l + 1,
778 sizeof(full->long_name) - (p_l - full->long_name) - 1,
779 p_s + 1, !(flags & DRIVE_CASE_SENSITIVE) )))
781 *p_l++ = '/';
782 p_l += strlen(p_l);
783 *p_s++ = '\\';
784 p_s += strlen(p_s);
785 while (!IS_END_OF_NAME(*name)) name++;
787 else if (!check_last)
789 *p_l++ = '/';
790 *p_s++ = '\\';
791 while (!IS_END_OF_NAME(*name) &&
792 (p_s < full->short_name + sizeof(full->short_name) - 1) &&
793 (p_l < full->long_name + sizeof(full->long_name) - 1))
795 *p_s++ = tolower(*name);
796 /* If the drive is case-sensitive we want to create new */
797 /* files in lower-case otherwise we can't reopen them */
798 /* under the same short name. */
799 if (flags & DRIVE_CASE_SENSITIVE) *p_l++ = tolower(*name);
800 else *p_l++ = *name;
801 name++;
803 *p_l = *p_s = '\0';
805 while ((*name == '\\') || (*name == '/')) name++;
808 if (!found)
810 if (check_last)
812 DOS_ERROR( ER_FileNotFound, EC_NotFound, SA_Abort, EL_Disk );
813 return FALSE;
815 if (*name) /* Not last */
817 DOS_ERROR( ER_PathNotFound, EC_NotFound, SA_Abort, EL_Disk );
818 return FALSE;
821 if (!full->long_name[0]) strcpy( full->long_name, "/" );
822 if (!full->short_name[2]) strcpy( full->short_name + 2, "\\" );
823 TRACE(dosfs, "returning %s = %s\n",
824 full->long_name, full->short_name );
825 return TRUE;
829 /***********************************************************************
830 * GetShortPathName32A (KERNEL32.271)
832 DWORD WINAPI GetShortPathName32A( LPCSTR longpath, LPSTR shortpath,
833 DWORD shortlen )
835 DOS_FULL_NAME full_name;
837 /* FIXME: is it correct to always return a fully qualified short path? */
838 if (!DOSFS_GetFullName( longpath, TRUE, &full_name )) return 0;
839 lstrcpyn32A( shortpath, full_name.short_name, shortlen );
840 return strlen( full_name.short_name );
844 /***********************************************************************
845 * GetShortPathName32W (KERNEL32.272)
847 DWORD WINAPI GetShortPathName32W( LPCWSTR longpath, LPWSTR shortpath,
848 DWORD shortlen )
850 DOS_FULL_NAME full_name;
851 DWORD ret = 0;
852 LPSTR longpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, longpath );
854 /* FIXME: is it correct to always return a fully qualified short path? */
855 if (DOSFS_GetFullName( longpathA, TRUE, &full_name ))
857 ret = strlen( full_name.short_name );
858 lstrcpynAtoW( shortpath, full_name.short_name, shortlen );
860 HeapFree( GetProcessHeap(), 0, longpathA );
861 return ret;
865 /***********************************************************************
866 * GetLongPathName32A (KERNEL32.xxx)
868 DWORD WINAPI GetLongPathName32A( LPCSTR shortpath, LPSTR longpath,
869 DWORD longlen )
871 DOS_FULL_NAME full_name;
873 /* FIXME: Is it correct to return a UNIX style path here? */
874 if (!DOSFS_GetFullName( shortpath, TRUE, &full_name )) return 0;
875 lstrcpyn32A( longpath, full_name.long_name, longlen );
876 return strlen( full_name.long_name );
880 /***********************************************************************
881 * GetLongPathName32W (KERNEL32.269)
883 DWORD WINAPI GetLongPathName32W( LPCWSTR shortpath, LPWSTR longpath,
884 DWORD longlen )
886 DOS_FULL_NAME full_name;
887 DWORD ret = 0;
888 LPSTR shortpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, shortpath );
890 /* FIXME: is it correct to always return a fully qualified short path? */
891 if (DOSFS_GetFullName( shortpathA, TRUE, &full_name ))
893 ret = strlen( full_name.short_name );
894 lstrcpynAtoW( longpath, full_name.long_name, longlen );
896 HeapFree( GetProcessHeap(), 0, shortpathA );
897 return ret;
901 /***********************************************************************
902 * DOSFS_DoGetFullPathName
904 * Implementation of GetFullPathName32A/W.
906 static DWORD DOSFS_DoGetFullPathName( LPCSTR name, DWORD len, LPSTR result,
907 BOOL32 unicode )
909 char buffer[MAX_PATHNAME_LEN];
910 int drive;
911 char *p;
913 TRACE(dosfs, "converting %s\n", name );
915 if (!name || !result) return 0;
917 if ((drive = DOSFS_GetPathDrive( &name )) == -1) return 0;
918 p = buffer;
919 *p++ = 'A' + drive;
920 *p++ = ':';
921 if (IS_END_OF_NAME(*name) && (*name)) /* Absolute path */
923 while ((*name == '\\') || (*name == '/')) name++;
925 else /* Relative path or empty path */
927 *p++ = '\\';
928 lstrcpyn32A( p, DRIVE_GetDosCwd(drive), sizeof(buffer) - 3 );
929 if (*p) p += strlen(p); else p--;
931 if (!*name) /* empty path */
932 *p++ = '\\';
933 *p = '\0';
935 while (*name)
937 if (*name == '.')
939 if (IS_END_OF_NAME(name[1]))
941 name++;
942 while ((*name == '\\') || (*name == '/')) name++;
943 continue;
945 else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
947 name += 2;
948 while ((*name == '\\') || (*name == '/')) name++;
949 while ((p > buffer + 2) && (*p != '\\')) p--;
950 *p = '\0'; /* Remove trailing separator */
951 continue;
954 if (p >= buffer + sizeof(buffer) - 1)
956 DOS_ERROR( ER_PathNotFound, EC_NotFound, SA_Abort, EL_Disk );
957 return 0;
959 *p++ = '\\';
960 while (!IS_END_OF_NAME(*name) && (p < buffer + sizeof(buffer) - 1))
961 *p++ = *name++;
962 *p = '\0';
963 while ((*name == '\\') || (*name == '/')) name++;
966 if (!buffer[2])
968 buffer[2] = '\\';
969 buffer[3] = '\0';
971 if (!(DRIVE_GetFlags(drive) & DRIVE_CASE_PRESERVING))
972 CharUpper32A( buffer );
974 if (unicode) lstrcpynAtoW( (LPWSTR)result, buffer, len );
975 else lstrcpyn32A( result, buffer, len );
977 TRACE(dosfs, "returning %s\n", buffer );
978 return strlen(buffer);
982 /***********************************************************************
983 * GetFullPathName32A (KERNEL32.272)
985 DWORD WINAPI GetFullPathName32A( LPCSTR name, DWORD len, LPSTR buffer,
986 LPSTR *lastpart )
988 DWORD ret = DOSFS_DoGetFullPathName( name, len, buffer, FALSE );
989 if (ret && lastpart)
991 LPSTR p = buffer + strlen(buffer);
992 while ((p > buffer + 2) && (*p != '\\')) p--;
993 *lastpart = p + 1;
995 return ret;
999 /***********************************************************************
1000 * GetFullPathName32W (KERNEL32.273)
1002 DWORD WINAPI GetFullPathName32W( LPCWSTR name, DWORD len, LPWSTR buffer,
1003 LPWSTR *lastpart )
1005 LPSTR nameA = HEAP_strdupWtoA( GetProcessHeap(), 0, name );
1006 DWORD ret = DOSFS_DoGetFullPathName( nameA, len, (LPSTR)buffer, TRUE );
1007 HeapFree( GetProcessHeap(), 0, nameA );
1008 if (ret && lastpart)
1010 LPWSTR p = buffer + lstrlen32W(buffer);
1011 while ((p > buffer + 2) && (*p != '\\')) p--;
1012 *lastpart = p + 1;
1014 return ret;
1018 /***********************************************************************
1019 * DOSFS_FindNext
1021 * Find the next matching file. Return the number of entries read to find
1022 * the matching one, or 0 if no more entries.
1023 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
1024 * file name mask. Either or both can be NULL.
1026 int DOSFS_FindNext( const char *path, const char *short_mask,
1027 const char *long_mask, int drive, BYTE attr,
1028 int skip, WIN32_FIND_DATA32A *entry )
1030 static DOS_DIR *dir = NULL;
1031 int count = 0;
1032 static char buffer[MAX_PATHNAME_LEN];
1033 static int cur_pos = 0;
1034 static int drive_root = 0;
1035 char *p;
1036 char dos_name[13];
1037 LPCSTR long_name, short_name;
1038 UINT32 flags;
1039 BY_HANDLE_FILE_INFORMATION info;
1041 if ((attr & ~(FA_UNUSED | FA_ARCHIVE | FA_RDONLY)) == FA_LABEL)
1043 if (skip) return 0;
1044 entry->dwFileAttributes = FILE_ATTRIBUTE_LABEL;
1045 DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftCreationTime, 0 );
1046 DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftLastAccessTime, 0 );
1047 DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftLastWriteTime, 0 );
1048 entry->nFileSizeHigh = 0;
1049 entry->nFileSizeLow = 0;
1050 entry->dwReserved0 = 0;
1051 entry->dwReserved1 = 0;
1052 DOSFS_ToDosDTAFormat( DRIVE_GetLabel( drive ), entry->cFileName );
1053 strcpy( entry->cAlternateFileName, entry->cFileName );
1054 return 1;
1057 /* Check the cached directory */
1058 if (dir && !strcmp( buffer, path ) && (cur_pos <= skip)) skip -= cur_pos;
1059 else /* Not in the cache, open it anew */
1061 const char *drive_path;
1062 TRACE(dosfs, "cache miss, path=%s skip=%d buf=%s cur=%d\n",
1063 path, skip, buffer, cur_pos );
1064 cur_pos = skip;
1065 if (dir) DOSFS_CloseDir(dir);
1066 if (!*path) path = "/";
1067 if (!(dir = DOSFS_OpenDir(path))) return 0;
1068 drive_path = path + strlen(DRIVE_GetRoot(drive));
1069 while ((*drive_path == '/') || (*drive_path == '\\')) drive_path++;
1070 drive_root = !*drive_path;
1071 TRACE(dosfs, "drive_root = %d\n", drive_root);
1072 lstrcpyn32A( buffer, path, sizeof(buffer) - 1 );
1074 strcat( buffer, "/" );
1075 p = buffer + strlen(buffer);
1076 attr |= FA_UNUSED | FA_ARCHIVE | FA_RDONLY;
1077 flags = DRIVE_GetFlags( drive );
1079 while (DOSFS_ReadDir( dir, &long_name, &short_name ))
1081 if (skip-- > 0) continue;
1082 count++;
1084 /* Don't return '.' and '..' in the root of the drive */
1085 if (drive_root && (long_name[0] == '.') &&
1086 (!long_name[1] || ((long_name[1] == '.') && !long_name[2])))
1087 continue;
1089 /* Check the long mask */
1091 if (long_mask)
1093 if (!DOSFS_MatchLong( long_mask, long_name,
1094 flags & DRIVE_CASE_SENSITIVE )) continue;
1097 /* Check the short mask */
1099 if (short_mask)
1101 if (!short_name)
1103 DOSFS_Hash( long_name, dos_name, TRUE,
1104 !(flags & DRIVE_CASE_SENSITIVE) );
1105 short_name = dos_name;
1107 if (!DOSFS_MatchShort( short_mask, short_name )) continue;
1110 /* Check the file attributes */
1112 lstrcpyn32A( p, long_name, sizeof(buffer) - (int)(p - buffer) );
1113 if (!FILE_Stat( buffer, &info ))
1115 WARN(dosfs, "can't stat %s\n", buffer);
1116 continue;
1118 if (info.dwFileAttributes & ~attr) continue;
1120 /* We now have a matching entry; fill the result and return */
1122 entry->dwFileAttributes = info.dwFileAttributes;
1123 entry->ftCreationTime = info.ftCreationTime;
1124 entry->ftLastAccessTime = info.ftLastAccessTime;
1125 entry->ftLastWriteTime = info.ftLastWriteTime;
1126 entry->nFileSizeHigh = info.nFileSizeHigh;
1127 entry->nFileSizeLow = info.nFileSizeLow;
1129 if (short_name)
1130 DOSFS_ToDosDTAFormat( short_name, entry->cAlternateFileName );
1131 else
1132 DOSFS_Hash( long_name, entry->cAlternateFileName, FALSE,
1133 !(flags & DRIVE_CASE_SENSITIVE) );
1135 lstrcpyn32A( entry->cFileName, long_name, sizeof(entry->cFileName) );
1136 if (!(flags & DRIVE_CASE_PRESERVING)) CharLower32A( entry->cFileName );
1137 TRACE(dosfs, "returning %s (%s) %02lx %ld\n",
1138 entry->cFileName, entry->cAlternateFileName,
1139 entry->dwFileAttributes, entry->nFileSizeLow );
1140 cur_pos += count;
1141 p[-1] = '\0'; /* Remove trailing slash in buffer */
1142 return count;
1144 DOSFS_CloseDir( dir );
1145 dir = NULL;
1146 return 0; /* End of directory */
1150 /*************************************************************************
1151 * FindFirstFile16 (KERNEL.413)
1153 HANDLE16 WINAPI FindFirstFile16( LPCSTR path, WIN32_FIND_DATA32A *data )
1155 DOS_FULL_NAME full_name;
1156 HGLOBAL16 handle;
1157 FIND_FIRST_INFO *info;
1159 if (!path) return 0;
1160 if (!DOSFS_GetFullName( path, FALSE, &full_name ))
1161 return INVALID_HANDLE_VALUE16;
1162 if (!(handle = GlobalAlloc16( GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO) )))
1163 return INVALID_HANDLE_VALUE16;
1164 info = (FIND_FIRST_INFO *)GlobalLock16( handle );
1165 info->path = HEAP_strdupA( SystemHeap, 0, full_name.long_name );
1166 info->mask = strrchr( info->path, '/' );
1167 *(info->mask++) = '\0';
1168 if (path[0] && (path[1] == ':')) info->drive = toupper(*path) - 'A';
1169 else info->drive = DRIVE_GetCurrentDrive();
1170 info->skip = 0;
1171 GlobalUnlock16( handle );
1172 if (!FindNextFile16( handle, data ))
1174 FindClose16( handle );
1175 DOS_ERROR( ER_NoMoreFiles, EC_MediaError, SA_Abort, EL_Disk );
1176 return INVALID_HANDLE_VALUE16;
1178 return handle;
1182 /*************************************************************************
1183 * FindFirstFile32A (KERNEL32.123)
1185 HANDLE32 WINAPI FindFirstFile32A( LPCSTR path, WIN32_FIND_DATA32A *data )
1187 HANDLE32 handle = FindFirstFile16( path, data );
1188 if (handle == INVALID_HANDLE_VALUE16) return INVALID_HANDLE_VALUE32;
1189 return handle;
1193 /*************************************************************************
1194 * FindFirstFile32W (KERNEL32.124)
1196 HANDLE32 WINAPI FindFirstFile32W( LPCWSTR path, WIN32_FIND_DATA32W *data )
1198 WIN32_FIND_DATA32A dataA;
1199 LPSTR pathA = HEAP_strdupWtoA( GetProcessHeap(), 0, path );
1200 HANDLE32 handle = FindFirstFile32A( pathA, &dataA );
1201 HeapFree( GetProcessHeap(), 0, pathA );
1202 if (handle != INVALID_HANDLE_VALUE32)
1204 data->dwFileAttributes = dataA.dwFileAttributes;
1205 data->ftCreationTime = dataA.ftCreationTime;
1206 data->ftLastAccessTime = dataA.ftLastAccessTime;
1207 data->ftLastWriteTime = dataA.ftLastWriteTime;
1208 data->nFileSizeHigh = dataA.nFileSizeHigh;
1209 data->nFileSizeLow = dataA.nFileSizeLow;
1210 lstrcpyAtoW( data->cFileName, dataA.cFileName );
1211 lstrcpyAtoW( data->cAlternateFileName, dataA.cAlternateFileName );
1213 return handle;
1217 /*************************************************************************
1218 * FindNextFile16 (KERNEL.414)
1220 BOOL16 WINAPI FindNextFile16( HANDLE16 handle, WIN32_FIND_DATA32A *data )
1222 FIND_FIRST_INFO *info;
1223 int count;
1225 if (!(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
1227 DOS_ERROR( ER_InvalidHandle, EC_ProgramError, SA_Abort, EL_Disk );
1228 return FALSE;
1230 GlobalUnlock16( handle );
1231 if (!info->path)
1233 DOS_ERROR( ER_NoMoreFiles, EC_MediaError, SA_Abort, EL_Disk );
1234 return FALSE;
1236 if (!(count = DOSFS_FindNext( info->path, NULL, info->mask, info->drive,
1237 0xff, info->skip, data )))
1239 HeapFree( SystemHeap, 0, info->path );
1240 info->path = info->mask = NULL;
1241 DOS_ERROR( ER_NoMoreFiles, EC_MediaError, SA_Abort, EL_Disk );
1242 return FALSE;
1244 info->skip += count;
1245 return TRUE;
1249 /*************************************************************************
1250 * FindNextFile32A (KERNEL32.126)
1252 BOOL32 WINAPI FindNextFile32A( HANDLE32 handle, WIN32_FIND_DATA32A *data )
1254 return FindNextFile16( handle, data );
1258 /*************************************************************************
1259 * FindNextFile32W (KERNEL32.127)
1261 BOOL32 WINAPI FindNextFile32W( HANDLE32 handle, WIN32_FIND_DATA32W *data )
1263 WIN32_FIND_DATA32A dataA;
1264 if (!FindNextFile32A( handle, &dataA )) return FALSE;
1265 data->dwFileAttributes = dataA.dwFileAttributes;
1266 data->ftCreationTime = dataA.ftCreationTime;
1267 data->ftLastAccessTime = dataA.ftLastAccessTime;
1268 data->ftLastWriteTime = dataA.ftLastWriteTime;
1269 data->nFileSizeHigh = dataA.nFileSizeHigh;
1270 data->nFileSizeLow = dataA.nFileSizeLow;
1271 lstrcpyAtoW( data->cFileName, dataA.cFileName );
1272 lstrcpyAtoW( data->cAlternateFileName, dataA.cAlternateFileName );
1273 return TRUE;
1277 /*************************************************************************
1278 * FindClose16 (KERNEL.415)
1280 BOOL16 WINAPI FindClose16( HANDLE16 handle )
1282 FIND_FIRST_INFO *info;
1284 if ((handle == INVALID_HANDLE_VALUE16) ||
1285 !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
1287 DOS_ERROR( ER_InvalidHandle, EC_ProgramError, SA_Abort, EL_Disk );
1288 return FALSE;
1290 if (info->path) HeapFree( SystemHeap, 0, info->path );
1291 GlobalUnlock16( handle );
1292 GlobalFree16( handle );
1293 return TRUE;
1297 /*************************************************************************
1298 * FindClose32 (KERNEL32.119)
1300 BOOL32 WINAPI FindClose32( HANDLE32 handle )
1302 return FindClose16( (HANDLE16)handle );
1306 /***********************************************************************
1307 * DOSFS_UnixTimeToFileTime
1309 * Convert a Unix time to FILETIME format.
1310 * The FILETIME structure is a 64-bit value representing the number of
1311 * 100-nanosecond intervals since January 1, 1601, 0:00.
1312 * 'remainder' is the nonnegative number of 100-ns intervals
1313 * corresponding to the time fraction smaller than 1 second that
1314 * couldn't be stored in the time_t value.
1316 void DOSFS_UnixTimeToFileTime( time_t unix_time, FILETIME *filetime,
1317 DWORD remainder )
1319 /* NOTES:
1321 CONSTANTS:
1322 The time difference between 1 January 1601, 00:00:00 and
1323 1 January 1970, 00:00:00 is 369 years, plus the leap years
1324 from 1604 to 1968, excluding 1700, 1800, 1900.
1325 This makes (1968 - 1600) / 4 - 3 = 89 leap days, and a total
1326 of 134774 days.
1328 Any day in that period had 24 * 60 * 60 = 86400 seconds.
1330 The time difference is 134774 * 86400 * 10000000, which can be written
1331 116444736000000000
1332 27111902 * 2^32 + 3577643008
1333 413 * 2^48 + 45534 * 2^32 + 54590 * 2^16 + 32768
1335 If you find that these constants are buggy, please change them in all
1336 instances in both conversion functions.
1338 VERSIONS:
1339 There are two versions, one of them uses long long variables and
1340 is presumably faster but not ISO C. The other one uses standard C
1341 data types and operations but relies on the assumption that negative
1342 numbers are stored as 2's complement (-1 is 0xffff....). If this
1343 assumption is violated, dates before 1970 will not convert correctly.
1344 This should however work on any reasonable architecture where WINE
1345 will run.
1347 DETAILS:
1349 Take care not to remove the casts. I have tested these functions
1350 (in both versions) for a lot of numbers. I would be interested in
1351 results on other compilers than GCC.
1353 The operations have been designed to account for the possibility
1354 of 64-bit time_t in future UNICES. Even the versions without
1355 internal long long numbers will work if time_t only is 64 bit.
1356 A 32-bit shift, which was necessary for that operation, turned out
1357 not to work correctly in GCC, besides giving the warning. So I
1358 used a double 16-bit shift instead. Numbers are in the ISO version
1359 represented by three limbs, the most significant with 32 bit, the
1360 other two with 16 bit each.
1362 As the modulo-operator % is not well-defined for negative numbers,
1363 negative divisors have been avoided in DOSFS_FileTimeToUnixTime.
1365 There might be quicker ways to do this in C. Certainly so in
1366 assembler.
1368 Claus Fischer, fischer@iue.tuwien.ac.at
1371 #if (SIZEOF_LONG_LONG >= 8)
1372 # define USE_LONG_LONG 1
1373 #else
1374 # define USE_LONG_LONG 0
1375 #endif
1377 #if USE_LONG_LONG /* gcc supports long long type */
1379 long long int t = unix_time;
1380 t *= 10000000;
1381 t += 116444736000000000LL;
1382 t += remainder;
1383 filetime->dwLowDateTime = (UINT32)t;
1384 filetime->dwHighDateTime = (UINT32)(t >> 32);
1386 #else /* ISO version */
1388 UINT32 a0; /* 16 bit, low bits */
1389 UINT32 a1; /* 16 bit, medium bits */
1390 UINT32 a2; /* 32 bit, high bits */
1392 /* Copy the unix time to a2/a1/a0 */
1393 a0 = unix_time & 0xffff;
1394 a1 = (unix_time >> 16) & 0xffff;
1395 /* This is obsolete if unix_time is only 32 bits, but it does not hurt.
1396 Do not replace this by >> 32, it gives a compiler warning and it does
1397 not work. */
1398 a2 = (unix_time >= 0 ? (unix_time >> 16) >> 16 :
1399 ~((~unix_time >> 16) >> 16));
1401 /* Multiply a by 10000000 (a = a2/a1/a0)
1402 Split the factor into 10000 * 1000 which are both less than 0xffff. */
1403 a0 *= 10000;
1404 a1 = a1 * 10000 + (a0 >> 16);
1405 a2 = a2 * 10000 + (a1 >> 16);
1406 a0 &= 0xffff;
1407 a1 &= 0xffff;
1409 a0 *= 1000;
1410 a1 = a1 * 1000 + (a0 >> 16);
1411 a2 = a2 * 1000 + (a1 >> 16);
1412 a0 &= 0xffff;
1413 a1 &= 0xffff;
1415 /* Add the time difference and the remainder */
1416 a0 += 32768 + (remainder & 0xffff);
1417 a1 += 54590 + (remainder >> 16 ) + (a0 >> 16);
1418 a2 += 27111902 + (a1 >> 16);
1419 a0 &= 0xffff;
1420 a1 &= 0xffff;
1422 /* Set filetime */
1423 filetime->dwLowDateTime = (a1 << 16) + a0;
1424 filetime->dwHighDateTime = a2;
1425 #endif
1429 /***********************************************************************
1430 * DOSFS_FileTimeToUnixTime
1432 * Convert a FILETIME format to Unix time.
1433 * If not NULL, 'remainder' contains the fractional part of the filetime,
1434 * in the range of [0..9999999] (even if time_t is negative).
1436 time_t DOSFS_FileTimeToUnixTime( const FILETIME *filetime, DWORD *remainder )
1438 /* Read the comment in the function DOSFS_UnixTimeToFileTime. */
1439 #if USE_LONG_LONG
1441 long long int t = filetime->dwHighDateTime;
1442 t <<= 32;
1443 t += (UINT32)filetime->dwLowDateTime;
1444 t -= 116444736000000000LL;
1445 if (t < 0)
1447 if (remainder) *remainder = 9999999 - (-t - 1) % 10000000;
1448 return -1 - ((-t - 1) / 10000000);
1450 else
1452 if (remainder) *remainder = t % 10000000;
1453 return t / 10000000;
1456 #else /* ISO version */
1458 UINT32 a0; /* 16 bit, low bits */
1459 UINT32 a1; /* 16 bit, medium bits */
1460 UINT32 a2; /* 32 bit, high bits */
1461 UINT32 r; /* remainder of division */
1462 unsigned int carry; /* carry bit for subtraction */
1463 int negative; /* whether a represents a negative value */
1465 /* Copy the time values to a2/a1/a0 */
1466 a2 = (UINT32)filetime->dwHighDateTime;
1467 a1 = ((UINT32)filetime->dwLowDateTime ) >> 16;
1468 a0 = ((UINT32)filetime->dwLowDateTime ) & 0xffff;
1470 /* Subtract the time difference */
1471 if (a0 >= 32768 ) a0 -= 32768 , carry = 0;
1472 else a0 += (1 << 16) - 32768 , carry = 1;
1474 if (a1 >= 54590 + carry) a1 -= 54590 + carry, carry = 0;
1475 else a1 += (1 << 16) - 54590 - carry, carry = 1;
1477 a2 -= 27111902 + carry;
1479 /* If a is negative, replace a by (-1-a) */
1480 negative = (a2 >= ((UINT32)1) << 31);
1481 if (negative)
1483 /* Set a to -a - 1 (a is a2/a1/a0) */
1484 a0 = 0xffff - a0;
1485 a1 = 0xffff - a1;
1486 a2 = ~a2;
1489 /* Divide a by 10000000 (a = a2/a1/a0), put the rest into r.
1490 Split the divisor into 10000 * 1000 which are both less than 0xffff. */
1491 a1 += (a2 % 10000) << 16;
1492 a2 /= 10000;
1493 a0 += (a1 % 10000) << 16;
1494 a1 /= 10000;
1495 r = a0 % 10000;
1496 a0 /= 10000;
1498 a1 += (a2 % 1000) << 16;
1499 a2 /= 1000;
1500 a0 += (a1 % 1000) << 16;
1501 a1 /= 1000;
1502 r += (a0 % 1000) * 10000;
1503 a0 /= 1000;
1505 /* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
1506 if (negative)
1508 /* Set a to -a - 1 (a is a2/a1/a0) */
1509 a0 = 0xffff - a0;
1510 a1 = 0xffff - a1;
1511 a2 = ~a2;
1513 r = 9999999 - r;
1516 if (remainder) *remainder = r;
1518 /* Do not replace this by << 32, it gives a compiler warning and it does
1519 not work. */
1520 return ((((time_t)a2) << 16) << 16) + (a1 << 16) + a0;
1521 #endif
1525 /***********************************************************************
1526 * DosDateTimeToFileTime (KERNEL32.76)
1528 BOOL32 WINAPI DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft)
1530 struct tm newtm;
1532 newtm.tm_sec = (fattime & 0x1f) * 2;
1533 newtm.tm_min = (fattime >> 5) & 0x3f;
1534 newtm.tm_hour = (fattime >> 11);
1535 newtm.tm_mday = (fatdate & 0x1f);
1536 newtm.tm_mon = ((fatdate >> 5) & 0x0f) - 1;
1537 newtm.tm_year = (fatdate >> 9) + 80;
1538 DOSFS_UnixTimeToFileTime( mktime( &newtm ), ft, 0 );
1539 return TRUE;
1543 /***********************************************************************
1544 * FileTimeToDosDateTime (KERNEL32.111)
1546 BOOL32 WINAPI FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate,
1547 LPWORD fattime )
1549 time_t unixtime = DOSFS_FileTimeToUnixTime( ft, NULL );
1550 struct tm *tm = localtime( &unixtime );
1551 if (fattime)
1552 *fattime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
1553 if (fatdate)
1554 *fatdate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
1555 + tm->tm_mday;
1556 return TRUE;
1560 /***********************************************************************
1561 * LocalFileTimeToFileTime (KERNEL32.373)
1563 BOOL32 WINAPI LocalFileTimeToFileTime( const FILETIME *localft,
1564 LPFILETIME utcft )
1566 struct tm *xtm;
1567 DWORD remainder;
1569 /* convert from local to UTC. Perhaps not correct. FIXME */
1570 time_t unixtime = DOSFS_FileTimeToUnixTime( localft, &remainder );
1571 xtm = gmtime( &unixtime );
1572 DOSFS_UnixTimeToFileTime( mktime(xtm), utcft, remainder );
1573 return TRUE;
1577 /***********************************************************************
1578 * FileTimeToLocalFileTime (KERNEL32.112)
1580 BOOL32 WINAPI FileTimeToLocalFileTime( const FILETIME *utcft,
1581 LPFILETIME localft )
1583 DWORD remainder;
1584 /* convert from UTC to local. Perhaps not correct. FIXME */
1585 time_t unixtime = DOSFS_FileTimeToUnixTime( utcft, &remainder );
1586 #ifdef HAVE_TIMEGM
1587 struct tm *xtm = localtime( &unixtime );
1588 time_t localtime;
1590 localtime = timegm(xtm);
1591 DOSFS_UnixTimeToFileTime( localtime, localft, remainder );
1593 #else
1594 struct tm *xtm,*gtm;
1595 time_t time1,time2;
1597 xtm = localtime( &unixtime );
1598 gtm = gmtime( &unixtime );
1599 time1 = mktime(xtm);
1600 time2 = mktime(gtm);
1601 DOSFS_UnixTimeToFileTime( 2*time1-time2, localft, remainder );
1602 #endif
1603 return TRUE;
1607 /***********************************************************************
1608 * FileTimeToSystemTime (KERNEL32.113)
1610 BOOL32 WINAPI FileTimeToSystemTime( const FILETIME *ft, LPSYSTEMTIME syst )
1612 struct tm *xtm;
1613 DWORD remainder;
1614 time_t xtime = DOSFS_FileTimeToUnixTime( ft, &remainder );
1615 xtm = gmtime(&xtime);
1616 syst->wYear = xtm->tm_year+1900;
1617 syst->wMonth = xtm->tm_mon + 1;
1618 syst->wDayOfWeek = xtm->tm_wday;
1619 syst->wDay = xtm->tm_mday;
1620 syst->wHour = xtm->tm_hour;
1621 syst->wMinute = xtm->tm_min;
1622 syst->wSecond = xtm->tm_sec;
1623 syst->wMilliseconds = remainder / 10000;
1624 return TRUE;
1627 /***********************************************************************
1628 * QueryDosDeviceA (KERNEL32.413)
1630 * returns array of strings terminated by \0, terminated by \0
1632 DWORD WINAPI QueryDosDevice32A(LPCSTR devname,LPSTR target,DWORD bufsize)
1634 LPSTR s;
1635 char buffer[200];
1637 TRACE(dosfs,"(%s,...)\n",devname?devname:"<null>");
1638 if (!devname) {
1639 /* return known MSDOS devices */
1640 lstrcpy32A(buffer,"CON COM1 COM2 LPT1 NUL ");
1641 while ((s=strchr(buffer,' ')))
1642 *s='\0';
1644 lstrcpyn32A(target,buffer,bufsize);
1645 return strlen(buffer);
1647 lstrcpy32A(buffer,"\\DEV\\");
1648 lstrcat32A(buffer,devname);
1649 if ((s=strchr(buffer,':'))) *s='\0';
1650 lstrcpyn32A(target,buffer,bufsize);
1651 return strlen(buffer);
1655 /***********************************************************************
1656 * QueryDosDeviceW (KERNEL32.414)
1658 * returns array of strings terminated by \0, terminated by \0
1660 DWORD WINAPI QueryDosDevice32W(LPCWSTR devname,LPWSTR target,DWORD bufsize)
1662 LPSTR devnameA = devname?HEAP_strdupWtoA(GetProcessHeap(),0,devname):NULL;
1663 LPSTR targetA = (LPSTR)HEAP_xalloc(GetProcessHeap(),0,bufsize);
1664 DWORD ret = QueryDosDevice32A(devnameA,targetA,bufsize);
1666 lstrcpynAtoW(target,targetA,bufsize);
1667 if (devnameA) HeapFree(GetProcessHeap(),0,devnameA);
1668 if (targetA) HeapFree(GetProcessHeap(),0,targetA);
1669 return ret;
1673 /***********************************************************************
1674 * SystemTimeToFileTime (KERNEL32.526)
1676 BOOL32 WINAPI SystemTimeToFileTime( const SYSTEMTIME *syst, LPFILETIME ft )
1678 #ifdef HAVE_TIMEGM
1679 struct tm xtm;
1680 time_t utctime;
1681 #else
1682 struct tm xtm,*local_tm,*utc_tm;
1683 time_t localtim,utctime;
1684 #endif
1686 xtm.tm_year = syst->wYear-1900;
1687 xtm.tm_mon = syst->wMonth - 1;
1688 xtm.tm_wday = syst->wDayOfWeek;
1689 xtm.tm_mday = syst->wDay;
1690 xtm.tm_hour = syst->wHour;
1691 xtm.tm_min = syst->wMinute;
1692 xtm.tm_sec = syst->wSecond; /* this is UTC */
1693 xtm.tm_isdst = -1;
1694 #ifdef HAVE_TIMEGM
1695 utctime = timegm(&xtm);
1696 DOSFS_UnixTimeToFileTime( utctime, ft,
1697 syst->wMilliseconds * 10000 );
1698 #else
1699 localtim = mktime(&xtm); /* now we've got local time */
1700 local_tm = localtime(&localtim);
1701 utc_tm = gmtime(&localtim);
1702 utctime = mktime(utc_tm);
1703 DOSFS_UnixTimeToFileTime( 2*localtim -utctime, ft,
1704 syst->wMilliseconds * 10000 );
1705 #endif
1706 return TRUE;