Initialize lpdwNeeded.
[wine/multimedia.git] / files / dos_fs.c
blob7e49caed13541fd58ce0885ee246cd75a11a7bed
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 <sys/errno.h>
14 #include <fcntl.h>
15 #include <string.h>
16 #include <stdlib.h>
17 #include <sys/stat.h>
18 #include <sys/ioctl.h>
19 #include <time.h>
20 #include <unistd.h>
22 #include "windows.h"
23 #include "winerror.h"
24 #include "drive.h"
25 #include "file.h"
26 #include "heap.h"
27 #include "msdos.h"
28 #include "syslevel.h"
29 #include "debug.h"
31 /* Define the VFAT ioctl to get both short and long file names */
32 /* FIXME: is it possible to get this to work on other systems? */
33 #ifdef linux
34 #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, long)
35 /* We want the real kernel dirent structure, not the libc one */
36 typedef struct
38 long d_ino;
39 long d_off;
40 unsigned short d_reclen;
41 char d_name[256];
42 } KERNEL_DIRENT;
44 #else /* linux */
45 #undef VFAT_IOCTL_READDIR_BOTH /* just in case... */
46 #endif /* linux */
48 /* Chars we don't want to see in DOS file names */
49 #define INVALID_DOS_CHARS "*?<>|\"+=,;[] \345"
51 static const DOS_DEVICE DOSFS_Devices[] =
52 /* name, device flags (see Int 21/AX=0x4400) */
54 { "CON", 0xc0d3 },
55 { "PRN", 0xa0c0 },
56 { "NUL", 0x80c4 },
57 { "AUX", 0x80c0 },
58 { "LPT1", 0xa0c0 },
59 { "LPT2", 0xa0c0 },
60 { "LPT3", 0xa0c0 },
61 { "LPT4", 0xc0d3 },
62 { "COM1", 0x80c0 },
63 { "COM2", 0x80c0 },
64 { "COM3", 0x80c0 },
65 { "COM4", 0x80c0 },
66 { "SCSIMGR$", 0xc0c0 },
67 { "HPSCAN", 0xc0c0 }
70 #define GET_DRIVE(path) \
71 (((path)[1] == ':') ? toupper((path)[0]) - 'A' : DOSFS_CurDrive)
73 /* DOS extended error status */
74 WORD DOS_ExtendedError;
75 BYTE DOS_ErrorClass;
76 BYTE DOS_ErrorAction;
77 BYTE DOS_ErrorLocus;
79 /* Directory info for DOSFS_ReadDir */
80 typedef struct
82 DIR *dir;
83 #ifdef VFAT_IOCTL_READDIR_BOTH
84 int fd;
85 char short_name[12];
86 KERNEL_DIRENT dirent[2];
87 #endif
88 } DOS_DIR;
90 /* Info structure for FindFirstFile handle */
91 typedef struct
93 LPSTR path;
94 LPSTR long_mask;
95 LPSTR short_mask;
96 BYTE attr;
97 int drive;
98 int cur_pos;
99 DOS_DIR *dir;
100 } FIND_FIRST_INFO;
104 /***********************************************************************
105 * DOSFS_ValidDOSName
107 * Return 1 if Unix file 'name' is also a valid MS-DOS name
108 * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
109 * File name can be terminated by '\0', '\\' or '/'.
111 static int DOSFS_ValidDOSName( const char *name, int ignore_case )
113 static const char invalid_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS;
114 const char *p = name;
115 const char *invalid = ignore_case ? (invalid_chars + 26) : invalid_chars;
116 int len = 0;
118 if (*p == '.')
120 /* Check for "." and ".." */
121 p++;
122 if (*p == '.') p++;
123 /* All other names beginning with '.' are invalid */
124 return (IS_END_OF_NAME(*p));
126 while (!IS_END_OF_NAME(*p))
128 if (strchr( invalid, *p )) return 0; /* Invalid char */
129 if (*p == '.') break; /* Start of the extension */
130 if (++len > 8) return 0; /* Name too long */
131 p++;
133 if (*p != '.') return 1; /* End of name */
134 p++;
135 if (IS_END_OF_NAME(*p)) return 0; /* Empty extension not allowed */
136 len = 0;
137 while (!IS_END_OF_NAME(*p))
139 if (strchr( invalid, *p )) return 0; /* Invalid char */
140 if (*p == '.') return 0; /* Second extension not allowed */
141 if (++len > 3) return 0; /* Extension too long */
142 p++;
144 return 1;
148 /***********************************************************************
149 * DOSFS_ToDosFCBFormat
151 * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
152 * expanding wild cards and converting to upper-case in the process.
153 * File name can be terminated by '\0', '\\' or '/'.
154 * Return FALSE if the name is not a valid DOS name.
155 * 'buffer' must be at least 12 characters long.
157 BOOL32 DOSFS_ToDosFCBFormat( LPCSTR name, LPSTR buffer )
159 static const char invalid_chars[] = INVALID_DOS_CHARS;
160 const char *p = name;
161 int i;
163 /* Check for "." and ".." */
164 if (*p == '.')
166 p++;
167 strcpy( buffer, ". " );
168 if (*p == '.')
170 buffer[1] = '.';
171 p++;
173 return (!*p || (*p == '/') || (*p == '\\'));
176 for (i = 0; i < 8; i++)
178 switch(*p)
180 case '\0':
181 case '\\':
182 case '/':
183 case '.':
184 buffer[i] = ' ';
185 break;
186 case '?':
187 p++;
188 /* fall through */
189 case '*':
190 buffer[i] = '?';
191 break;
192 default:
193 if (strchr( invalid_chars, *p )) return FALSE;
194 buffer[i] = toupper(*p);
195 p++;
196 break;
200 if (*p == '*')
202 /* Skip all chars after wildcard up to first dot */
203 while (*p && (*p != '/') && (*p != '\\') && (*p != '.')) p++;
205 else
207 /* Check if name too long */
208 if (*p && (*p != '/') && (*p != '\\') && (*p != '.')) return FALSE;
210 if (*p == '.') p++; /* Skip dot */
212 for (i = 8; i < 11; i++)
214 switch(*p)
216 case '\0':
217 case '\\':
218 case '/':
219 buffer[i] = ' ';
220 break;
221 case '.':
222 return FALSE; /* Second extension not allowed */
223 case '?':
224 p++;
225 /* fall through */
226 case '*':
227 buffer[i] = '?';
228 break;
229 default:
230 if (strchr( invalid_chars, *p )) return FALSE;
231 buffer[i] = toupper(*p);
232 p++;
233 break;
236 buffer[11] = '\0';
237 return TRUE;
241 /***********************************************************************
242 * DOSFS_ToDosDTAFormat
244 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
245 * converting to upper-case in the process.
246 * File name can be terminated by '\0', '\\' or '/'.
247 * 'buffer' must be at least 13 characters long.
249 static void DOSFS_ToDosDTAFormat( LPCSTR name, LPSTR buffer )
251 char *p;
253 memcpy( buffer, name, 8 );
254 for (p = buffer + 8; (p > buffer) && (p[-1] == ' '); p--);
255 *p++ = '.';
256 memcpy( p, name + 8, 3 );
257 for (p += 3; p[-1] == ' '; p--);
258 if (p[-1] == '.') p--;
259 *p = '\0';
263 /***********************************************************************
264 * DOSFS_MatchShort
266 * Check a DOS file name against a mask (both in FCB format).
268 static int DOSFS_MatchShort( const char *mask, const char *name )
270 int i;
271 for (i = 11; i > 0; i--, mask++, name++)
272 if ((*mask != '?') && (*mask != *name)) return 0;
273 return 1;
277 /***********************************************************************
278 * DOSFS_MatchLong
280 * Check a long file name against a mask.
282 static int DOSFS_MatchLong( const char *mask, const char *name,
283 int case_sensitive )
285 if (!strcmp( mask, "*.*" )) return 1;
286 while (*name && *mask)
288 if (*mask == '*')
290 mask++;
291 while (*mask == '*') mask++; /* Skip consecutive '*' */
292 if (!*mask) return 1;
293 if (case_sensitive) while (*name && (*name != *mask)) name++;
294 else while (*name && (toupper(*name) != toupper(*mask))) name++;
295 if (!*name) return 0;
297 else if (*mask != '?')
299 if (case_sensitive)
301 if (*mask != *name) return 0;
303 else if (toupper(*mask) != toupper(*name)) return 0;
305 mask++;
306 name++;
308 return (!*name && !*mask);
312 /***********************************************************************
313 * DOSFS_OpenDir
315 static DOS_DIR *DOSFS_OpenDir( LPCSTR path )
317 DOS_DIR *dir = HeapAlloc( SystemHeap, 0, sizeof(*dir) );
318 if (!dir)
320 DOS_ERROR( ER_OutOfMemory, EC_OutOfResource, SA_Abort, EL_Memory );
321 return NULL;
324 /* Treat empty path as root directory. This simplifies path split into
325 directory and mask in several other places */
326 if (!*path) path = "/";
328 #ifdef VFAT_IOCTL_READDIR_BOTH
330 /* Check if the VFAT ioctl is supported on this directory */
332 if ((dir->fd = open( path, O_RDONLY )) != -1)
334 if (ioctl( dir->fd, VFAT_IOCTL_READDIR_BOTH, (long)dir->dirent ) == -1)
336 close( dir->fd );
337 dir->fd = -1;
339 else
341 /* Set the file pointer back at the start of the directory */
342 lseek( dir->fd, 0, SEEK_SET );
343 dir->dir = NULL;
344 return dir;
347 #endif /* VFAT_IOCTL_READDIR_BOTH */
349 /* Now use the standard opendir/readdir interface */
351 if (!(dir->dir = opendir( path )))
353 HeapFree( SystemHeap, 0, dir );
354 return NULL;
356 return dir;
360 /***********************************************************************
361 * DOSFS_CloseDir
363 static void DOSFS_CloseDir( DOS_DIR *dir )
365 #ifdef VFAT_IOCTL_READDIR_BOTH
366 if (dir->fd != -1) close( dir->fd );
367 #endif /* VFAT_IOCTL_READDIR_BOTH */
368 if (dir->dir) closedir( dir->dir );
369 HeapFree( SystemHeap, 0, dir );
373 /***********************************************************************
374 * DOSFS_ReadDir
376 static BOOL32 DOSFS_ReadDir( DOS_DIR *dir, LPCSTR *long_name,
377 LPCSTR *short_name )
379 struct dirent *dirent;
381 #ifdef VFAT_IOCTL_READDIR_BOTH
382 if (dir->fd != -1)
384 if (ioctl( dir->fd, VFAT_IOCTL_READDIR_BOTH, (long)dir->dirent ) != -1) {
385 if (!dir->dirent[0].d_reclen) return FALSE;
386 if (!DOSFS_ToDosFCBFormat( dir->dirent[0].d_name, dir->short_name ))
387 dir->short_name[0] = '\0';
388 *short_name = dir->short_name;
389 if (dir->dirent[1].d_name[0]) *long_name = dir->dirent[1].d_name;
390 else *long_name = dir->dirent[0].d_name;
391 return TRUE;
394 #endif /* VFAT_IOCTL_READDIR_BOTH */
396 if (!(dirent = readdir( dir->dir ))) return FALSE;
397 *long_name = dirent->d_name;
398 *short_name = NULL;
399 return TRUE;
403 /***********************************************************************
404 * DOSFS_Hash
406 * Transform a Unix file name into a hashed DOS name. If the name is a valid
407 * DOS name, it is converted to upper-case; otherwise it is replaced by a
408 * hashed version that fits in 8.3 format.
409 * File name can be terminated by '\0', '\\' or '/'.
410 * 'buffer' must be at least 13 characters long.
412 static void DOSFS_Hash( LPCSTR name, LPSTR buffer, BOOL32 dir_format,
413 BOOL32 ignore_case )
415 static const char invalid_chars[] = INVALID_DOS_CHARS "~.";
416 static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
418 const char *p, *ext;
419 char *dst;
420 unsigned short hash;
421 int i;
423 if (dir_format) strcpy( buffer, " " );
425 if (DOSFS_ValidDOSName( name, ignore_case ))
427 /* Check for '.' and '..' */
428 if (*name == '.')
430 buffer[0] = '.';
431 if (!dir_format) buffer[1] = buffer[2] = '\0';
432 if (name[1] == '.') buffer[1] = '.';
433 return;
436 /* Simply copy the name, converting to uppercase */
438 for (dst = buffer; !IS_END_OF_NAME(*name) && (*name != '.'); name++)
439 *dst++ = toupper(*name);
440 if (*name == '.')
442 if (dir_format) dst = buffer + 8;
443 else *dst++ = '.';
444 for (name++; !IS_END_OF_NAME(*name); name++)
445 *dst++ = toupper(*name);
447 if (!dir_format) *dst = '\0';
448 return;
451 /* Compute the hash code of the file name */
452 /* If you know something about hash functions, feel free to */
453 /* insert a better algorithm here... */
454 if (ignore_case)
456 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
457 hash = (hash<<3) ^ (hash>>5) ^ tolower(*p) ^ (tolower(p[1]) << 8);
458 hash = (hash<<3) ^ (hash>>5) ^ tolower(*p); /* Last character*/
460 else
462 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
463 hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
464 hash = (hash << 3) ^ (hash >> 5) ^ *p; /* Last character */
467 /* Find last dot for start of the extension */
468 for (p = name+1, ext = NULL; !IS_END_OF_NAME(*p); p++)
469 if (*p == '.') ext = p;
470 if (ext && IS_END_OF_NAME(ext[1]))
471 ext = NULL; /* Empty extension ignored */
473 /* Copy first 4 chars, replacing invalid chars with '_' */
474 for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
476 if (IS_END_OF_NAME(*p) || (p == ext)) break;
477 *dst++ = strchr( invalid_chars, *p ) ? '_' : toupper(*p);
479 /* Pad to 5 chars with '~' */
480 while (i-- >= 0) *dst++ = '~';
482 /* Insert hash code converted to 3 ASCII chars */
483 *dst++ = hash_chars[(hash >> 10) & 0x1f];
484 *dst++ = hash_chars[(hash >> 5) & 0x1f];
485 *dst++ = hash_chars[hash & 0x1f];
487 /* Copy the first 3 chars of the extension (if any) */
488 if (ext)
490 if (!dir_format) *dst++ = '.';
491 for (i = 3, ext++; (i > 0) && !IS_END_OF_NAME(*ext); i--, ext++)
492 *dst++ = strchr( invalid_chars, *ext ) ? '_' : toupper(*ext);
494 if (!dir_format) *dst = '\0';
498 /***********************************************************************
499 * DOSFS_FindUnixName
501 * Find the Unix file name in a given directory that corresponds to
502 * a file name (either in Unix or DOS format).
503 * File name can be terminated by '\0', '\\' or '/'.
504 * Return TRUE if OK, FALSE if no file name matches.
506 * 'long_buf' must be at least 'long_len' characters long. If the long name
507 * turns out to be larger than that, the function returns FALSE.
508 * 'short_buf' must be at least 13 characters long.
510 BOOL32 DOSFS_FindUnixName( LPCSTR path, LPCSTR name, LPSTR long_buf,
511 INT32 long_len, LPSTR short_buf, BOOL32 ignore_case)
513 DOS_DIR *dir;
514 LPCSTR long_name, short_name;
515 char dos_name[12], tmp_buf[13];
516 BOOL32 ret;
518 const char *p = strchr( name, '/' );
519 int len = p ? (int)(p - name) : strlen(name);
520 if ((p = strchr( name, '\\' ))) len = MIN( (int)(p - name), len );
521 if (long_len < len + 1) return FALSE;
523 TRACE(dosfs, "%s,%s\n", path, name );
525 if (!DOSFS_ToDosFCBFormat( name, dos_name )) dos_name[0] = '\0';
527 if (!(dir = DOSFS_OpenDir( path )))
529 WARN(dosfs, "(%s,%s): can't open dir: %s\n",
530 path, name, strerror(errno) );
531 return FALSE;
534 while ((ret = DOSFS_ReadDir( dir, &long_name, &short_name )))
536 /* Check against Unix name */
537 if (len == strlen(long_name))
539 if (!ignore_case)
541 if (!lstrncmp32A( long_name, name, len )) break;
543 else
545 if (!lstrncmpi32A( long_name, name, len )) break;
548 if (dos_name[0])
550 /* Check against hashed DOS name */
551 if (!short_name)
553 DOSFS_Hash( long_name, tmp_buf, TRUE, ignore_case );
554 short_name = tmp_buf;
556 if (!strcmp( dos_name, short_name )) break;
559 if (ret)
561 if (long_buf) strcpy( long_buf, long_name );
562 if (short_buf)
564 if (short_name)
565 DOSFS_ToDosDTAFormat( short_name, short_buf );
566 else
567 DOSFS_Hash( long_name, short_buf, FALSE, ignore_case );
569 TRACE(dosfs, "(%s,%s) -> %s (%s)\n",
570 path, name, long_name, short_buf ? short_buf : "***");
572 else
573 WARN(dosfs, "'%s' not found in '%s'\n", name, path);
574 DOSFS_CloseDir( dir );
575 return ret;
579 /***********************************************************************
580 * DOSFS_GetDevice
582 * Check if a DOS file name represents a DOS device and return the device.
584 const DOS_DEVICE *DOSFS_GetDevice( const char *name )
586 int i;
587 const char *p;
589 if (!name) return NULL; /* if FILE_DupUnixHandle was used */
590 if (name[0] && (name[1] == ':')) name += 2;
591 if ((p = strrchr( name, '/' ))) name = p + 1;
592 if ((p = strrchr( name, '\\' ))) name = p + 1;
593 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
595 const char *dev = DOSFS_Devices[i].name;
596 if (!lstrncmpi32A( dev, name, strlen(dev) ))
598 p = name + strlen( dev );
599 if (!*p || (*p == '.')) return &DOSFS_Devices[i];
602 return NULL;
605 /***********************************************************************
606 * DOSFS_OpenDevice
608 * Open a DOS device. This might not map 1:1 into the UNIX device concept.
610 HFILE32 DOSFS_OpenDevice( const char *name, int unixmode )
612 int i;
613 const char *p;
614 FILE_OBJECT *file;
615 HFILE32 handle;
617 if (!name) return (HFILE32)NULL; /* if FILE_DupUnixHandle was used */
618 if (name[0] && (name[1] == ':')) name += 2;
619 if ((p = strrchr( name, '/' ))) name = p + 1;
620 if ((p = strrchr( name, '\\' ))) name = p + 1;
621 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
623 const char *dev = DOSFS_Devices[i].name;
624 if (!lstrncmpi32A( dev, name, strlen(dev) ))
626 p = name + strlen( dev );
627 if (!*p || (*p == '.')) {
628 /* got it */
629 if (!strcmp(DOSFS_Devices[i].name,"NUL"))
630 return FILE_OpenUnixFile("/dev/null",unixmode);
631 if (!strcmp(DOSFS_Devices[i].name,"CON")) {
632 HFILE32 to_dup;
633 HFILE32 handle;
634 switch (unixmode) {
635 case O_RDONLY:
636 to_dup = GetStdHandle( STD_INPUT_HANDLE );
637 break;
638 case O_WRONLY:
639 to_dup = GetStdHandle( STD_OUTPUT_HANDLE );
640 break;
641 default:
642 FIXME(dosfs,"can't open CON read/write\n");
643 return HFILE_ERROR32;
644 break;
646 if (!DuplicateHandle( GetCurrentProcess(), to_dup, GetCurrentProcess(),
647 &handle, 0, FALSE, DUPLICATE_SAME_ACCESS ))
648 handle = HFILE_ERROR32;
649 return handle;
651 if (!strcmp(DOSFS_Devices[i].name,"SCSIMGR$") ||
652 !strcmp(DOSFS_Devices[i].name,"HPSCAN"))
654 int fd = open( "/dev/null", unixmode );
655 return FILE_Alloc( &file, fd, DOSFS_Devices[i].name );
657 FIXME(dosfs,"device open %s not supported (yet)\n",DOSFS_Devices[i].name);
658 return HFILE_ERROR32;
662 return HFILE_ERROR32;
666 /***********************************************************************
667 * DOSFS_GetPathDrive
669 * Get the drive specified by a given path name (DOS or Unix format).
671 static int DOSFS_GetPathDrive( const char **name )
673 int drive;
674 const char *p = *name;
676 if (*p && (p[1] == ':'))
678 drive = toupper(*p) - 'A';
679 *name += 2;
681 else if (*p == '/') /* Absolute Unix path? */
683 if ((drive = DRIVE_FindDriveRoot( name )) == -1)
685 MSG("Warning: %s not accessible from a DOS drive\n", *name );
686 /* Assume it really was a DOS name */
687 drive = DRIVE_GetCurrentDrive();
690 else drive = DRIVE_GetCurrentDrive();
692 if (!DRIVE_IsValid(drive))
694 DOS_ERROR( ER_InvalidDrive, EC_MediaError, SA_Abort, EL_Disk );
695 return -1;
697 return drive;
701 /***********************************************************************
702 * DOSFS_GetFullName
704 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
705 * Unix name / short DOS name pair.
706 * Return FALSE if one of the path components does not exist. The last path
707 * component is only checked if 'check_last' is non-zero.
708 * The buffers pointed to by 'long_buf' and 'short_buf' must be
709 * at least MAX_PATHNAME_LEN long.
711 BOOL32 DOSFS_GetFullName( LPCSTR name, BOOL32 check_last, DOS_FULL_NAME *full )
713 BOOL32 found;
714 UINT32 flags;
715 char *p_l, *p_s, *root;
717 TRACE(dosfs, "%s (last=%d)\n",
718 name, check_last );
720 if ((full->drive = DOSFS_GetPathDrive( &name )) == -1) return FALSE;
721 flags = DRIVE_GetFlags( full->drive );
723 lstrcpyn32A( full->long_name, DRIVE_GetRoot( full->drive ),
724 sizeof(full->long_name) );
725 if (full->long_name[1]) root = full->long_name + strlen(full->long_name);
726 else root = full->long_name; /* root directory */
728 strcpy( full->short_name, "A:\\" );
729 full->short_name[0] += full->drive;
731 if ((*name == '\\') || (*name == '/')) /* Absolute path */
733 while ((*name == '\\') || (*name == '/')) name++;
735 else /* Relative path */
737 lstrcpyn32A( root + 1, DRIVE_GetUnixCwd( full->drive ),
738 sizeof(full->long_name) - (root - full->long_name) - 1 );
739 if (root[1]) *root = '/';
740 lstrcpyn32A( full->short_name + 3, DRIVE_GetDosCwd( full->drive ),
741 sizeof(full->short_name) - 3 );
744 p_l = full->long_name[1] ? full->long_name + strlen(full->long_name)
745 : full->long_name;
746 p_s = full->short_name[3] ? full->short_name + strlen(full->short_name)
747 : full->short_name + 2;
748 found = TRUE;
750 while (*name && found)
752 /* Check for '.' and '..' */
754 if (*name == '.')
756 if (IS_END_OF_NAME(name[1]))
758 name++;
759 while ((*name == '\\') || (*name == '/')) name++;
760 continue;
762 else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
764 name += 2;
765 while ((*name == '\\') || (*name == '/')) name++;
766 while ((p_l > root) && (*p_l != '/')) p_l--;
767 while ((p_s > full->short_name + 2) && (*p_s != '\\')) p_s--;
768 *p_l = *p_s = '\0'; /* Remove trailing separator */
769 continue;
773 /* Make sure buffers are large enough */
775 if ((p_s >= full->short_name + sizeof(full->short_name) - 14) ||
776 (p_l >= full->long_name + sizeof(full->long_name) - 1))
778 DOS_ERROR( ER_PathNotFound, EC_NotFound, SA_Abort, EL_Disk );
779 return FALSE;
782 /* Get the long and short name matching the file name */
784 if ((found = DOSFS_FindUnixName( full->long_name, name, p_l + 1,
785 sizeof(full->long_name) - (p_l - full->long_name) - 1,
786 p_s + 1, !(flags & DRIVE_CASE_SENSITIVE) )))
788 *p_l++ = '/';
789 p_l += strlen(p_l);
790 *p_s++ = '\\';
791 p_s += strlen(p_s);
792 while (!IS_END_OF_NAME(*name)) name++;
794 else if (!check_last)
796 *p_l++ = '/';
797 *p_s++ = '\\';
798 while (!IS_END_OF_NAME(*name) &&
799 (p_s < full->short_name + sizeof(full->short_name) - 1) &&
800 (p_l < full->long_name + sizeof(full->long_name) - 1))
802 *p_s++ = tolower(*name);
803 /* If the drive is case-sensitive we want to create new */
804 /* files in lower-case otherwise we can't reopen them */
805 /* under the same short name. */
806 if (flags & DRIVE_CASE_SENSITIVE) *p_l++ = tolower(*name);
807 else *p_l++ = *name;
808 name++;
810 *p_l = *p_s = '\0';
812 while ((*name == '\\') || (*name == '/')) name++;
815 if (!found)
817 if (check_last)
819 DOS_ERROR( ER_FileNotFound, EC_NotFound, SA_Abort, EL_Disk );
820 return FALSE;
822 if (*name) /* Not last */
824 DOS_ERROR( ER_PathNotFound, EC_NotFound, SA_Abort, EL_Disk );
825 return FALSE;
828 if (!full->long_name[0]) strcpy( full->long_name, "/" );
829 if (!full->short_name[2]) strcpy( full->short_name + 2, "\\" );
830 TRACE(dosfs, "returning %s = %s\n",
831 full->long_name, full->short_name );
832 return TRUE;
836 /***********************************************************************
837 * GetShortPathName32A (KERNEL32.271)
839 * NOTES
840 * observed:
841 * longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
842 * *longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0
844 DWORD WINAPI GetShortPathName32A( LPCSTR longpath, LPSTR shortpath,
845 DWORD shortlen )
847 DOS_FULL_NAME full_name;
849 if (!longpath)
851 SetLastError(ERROR_INVALID_PARAMETER);
852 return 0;
855 if (!longpath[0])
857 SetLastError(ERROR_BAD_PATHNAME);
858 return 0;
861 /* FIXME: is it correct to always return a fully qualified short path? */
862 if (!DOSFS_GetFullName( longpath, TRUE, &full_name ))
864 SetLastError(ERROR_BAD_PATHNAME);
865 return 0;
867 lstrcpyn32A( shortpath, full_name.short_name, shortlen );
868 return strlen( full_name.short_name );
872 /***********************************************************************
873 * GetShortPathName32W (KERNEL32.272)
875 DWORD WINAPI GetShortPathName32W( LPCWSTR longpath, LPWSTR shortpath,
876 DWORD shortlen )
878 DOS_FULL_NAME full_name;
879 LPSTR longpathA ;
880 DWORD ret = 0;
882 if (!longpath)
883 { SetLastError(ERROR_INVALID_PARAMETER);
884 return 0;
887 if (!longpath[0])
888 { SetLastError(ERROR_BAD_PATHNAME);
889 return 0;
893 longpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, longpath );
895 /* FIXME: is it correct to always return a fully qualified short path? */
896 if (DOSFS_GetFullName( longpathA, TRUE, &full_name ))
898 ret = strlen( full_name.short_name );
899 lstrcpynAtoW( shortpath, full_name.short_name, shortlen );
902 SetLastError(ERROR_BAD_PATHNAME);
903 HeapFree( GetProcessHeap(), 0, longpathA );
904 return 0;
908 /***********************************************************************
909 * GetLongPathName32A (KERNEL32.xxx)
911 DWORD WINAPI GetLongPathName32A( LPCSTR shortpath, LPSTR longpath,
912 DWORD longlen )
914 DOS_FULL_NAME full_name;
915 char *p;
916 char *longfilename;
917 DWORD shortpathlen;
919 if (!DOSFS_GetFullName( shortpath, TRUE, &full_name )) return 0;
920 lstrcpyn32A( longpath, full_name.short_name, longlen );
921 /* Do some hackery to get the long filename.
922 * FIXME: Would be better if it returned the
923 * long version of the directories too
925 longfilename = strrchr(full_name.long_name, '/')+1;
926 if (longpath != NULL) {
927 if ((p = strrchr( longpath, '\\' )) != NULL) {
928 p++;
929 longlen -= (p-longpath);
930 lstrcpyn32A( p, longfilename , longlen);
933 shortpathlen =
934 ((strrchr( full_name.short_name, '\\' ) - full_name.short_name) + 1);
935 return shortpathlen + strlen( longfilename );
939 /***********************************************************************
940 * GetLongPathName32W (KERNEL32.269)
942 DWORD WINAPI GetLongPathName32W( LPCWSTR shortpath, LPWSTR longpath,
943 DWORD longlen )
945 DOS_FULL_NAME full_name;
946 DWORD ret = 0;
947 LPSTR shortpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, shortpath );
949 /* FIXME: is it correct to always return a fully qualified short path? */
950 if (DOSFS_GetFullName( shortpathA, TRUE, &full_name ))
952 ret = strlen( full_name.short_name );
953 lstrcpynAtoW( longpath, full_name.long_name, longlen );
955 HeapFree( GetProcessHeap(), 0, shortpathA );
956 return ret;
960 /***********************************************************************
961 * DOSFS_DoGetFullPathName
963 * Implementation of GetFullPathName32A/W.
965 static DWORD DOSFS_DoGetFullPathName( LPCSTR name, DWORD len, LPSTR result,
966 BOOL32 unicode )
968 char buffer[MAX_PATHNAME_LEN];
969 int drive;
970 char *p;
972 TRACE(dosfs, "converting %s\n", name );
974 if (!name || !result) return 0;
976 if ((drive = DOSFS_GetPathDrive( &name )) == -1) return 0;
977 p = buffer;
978 *p++ = 'A' + drive;
979 *p++ = ':';
980 if (IS_END_OF_NAME(*name) && (*name)) /* Absolute path */
982 while ((*name == '\\') || (*name == '/')) name++;
984 else /* Relative path or empty path */
986 *p++ = '\\';
987 lstrcpyn32A( p, DRIVE_GetDosCwd(drive), sizeof(buffer) - 3 );
988 if (*p) p += strlen(p); else p--;
990 if (!*name) /* empty path */
991 *p++ = '\\';
992 *p = '\0';
994 while (*name)
996 if (*name == '.')
998 if (IS_END_OF_NAME(name[1]))
1000 name++;
1001 while ((*name == '\\') || (*name == '/')) name++;
1002 continue;
1004 else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
1006 name += 2;
1007 while ((*name == '\\') || (*name == '/')) name++;
1008 while ((p > buffer + 2) && (*p != '\\')) p--;
1009 *p = '\0'; /* Remove trailing separator */
1010 continue;
1013 if (p >= buffer + sizeof(buffer) - 1)
1015 DOS_ERROR( ER_PathNotFound, EC_NotFound, SA_Abort, EL_Disk );
1016 return 0;
1018 *p++ = '\\';
1019 while (!IS_END_OF_NAME(*name) && (p < buffer + sizeof(buffer) - 1))
1020 *p++ = *name++;
1021 *p = '\0';
1022 while ((*name == '\\') || (*name == '/')) name++;
1025 if (!buffer[2])
1027 buffer[2] = '\\';
1028 buffer[3] = '\0';
1030 if (!(DRIVE_GetFlags(drive) & DRIVE_CASE_PRESERVING))
1031 CharUpper32A( buffer );
1033 if (unicode) lstrcpynAtoW( (LPWSTR)result, buffer, len );
1034 else lstrcpyn32A( result, buffer, len );
1036 TRACE(dosfs, "returning %s\n", buffer );
1037 return strlen(buffer);
1041 /***********************************************************************
1042 * GetFullPathName32A (KERNEL32.272)
1044 DWORD WINAPI GetFullPathName32A( LPCSTR name, DWORD len, LPSTR buffer,
1045 LPSTR *lastpart )
1047 DWORD ret = DOSFS_DoGetFullPathName( name, len, buffer, FALSE );
1048 if (ret && lastpart)
1050 LPSTR p = buffer + strlen(buffer);
1052 /* if the path closed with '\', *lastpart is 0 */
1053 if (*p != '\\')
1055 while ((p > buffer + 2) && (*p != '\\')) p--;
1056 *lastpart = p + 1;
1058 else *lastpart = NULL;
1060 return ret;
1064 /***********************************************************************
1065 * GetFullPathName32W (KERNEL32.273)
1067 DWORD WINAPI GetFullPathName32W( LPCWSTR name, DWORD len, LPWSTR buffer,
1068 LPWSTR *lastpart )
1070 LPSTR nameA = HEAP_strdupWtoA( GetProcessHeap(), 0, name );
1071 DWORD ret = DOSFS_DoGetFullPathName( nameA, len, (LPSTR)buffer, TRUE );
1072 HeapFree( GetProcessHeap(), 0, nameA );
1073 if (ret && lastpart)
1075 LPWSTR p = buffer + lstrlen32W(buffer);
1076 while ((p > buffer + 2) && (*p != '\\')) p--;
1077 *lastpart = p + 1;
1079 return ret;
1082 /***********************************************************************
1083 * DOSFS_FindNextEx
1085 static int DOSFS_FindNextEx( FIND_FIRST_INFO *info, WIN32_FIND_DATA32A *entry )
1087 BYTE attr = info->attr | FA_UNUSED | FA_ARCHIVE | FA_RDONLY;
1088 UINT32 flags = DRIVE_GetFlags( info->drive );
1089 char *p, buffer[MAX_PATHNAME_LEN];
1090 const char *drive_path;
1091 int drive_root;
1092 LPCSTR long_name, short_name;
1093 BY_HANDLE_FILE_INFORMATION fileinfo;
1094 char dos_name[13];
1096 if ((info->attr & ~(FA_UNUSED | FA_ARCHIVE | FA_RDONLY)) == FA_LABEL)
1098 if (info->cur_pos) return 0;
1099 entry->dwFileAttributes = FILE_ATTRIBUTE_LABEL;
1100 DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftCreationTime, 0 );
1101 DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftLastAccessTime, 0 );
1102 DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftLastWriteTime, 0 );
1103 entry->nFileSizeHigh = 0;
1104 entry->nFileSizeLow = 0;
1105 entry->dwReserved0 = 0;
1106 entry->dwReserved1 = 0;
1107 DOSFS_ToDosDTAFormat( DRIVE_GetLabel( info->drive ), entry->cFileName );
1108 strcpy( entry->cAlternateFileName, entry->cFileName );
1109 info->cur_pos++;
1110 return 1;
1113 drive_path = info->path + strlen(DRIVE_GetRoot( info->drive ));
1114 while ((*drive_path == '/') || (*drive_path == '\\')) drive_path++;
1115 drive_root = !*drive_path;
1117 lstrcpyn32A( buffer, info->path, sizeof(buffer) - 1 );
1118 strcat( buffer, "/" );
1119 p = buffer + strlen(buffer);
1121 while (DOSFS_ReadDir( info->dir, &long_name, &short_name ))
1123 info->cur_pos++;
1125 /* Don't return '.' and '..' in the root of the drive */
1126 if (drive_root && (long_name[0] == '.') &&
1127 (!long_name[1] || ((long_name[1] == '.') && !long_name[2])))
1128 continue;
1130 /* Check the long mask */
1132 if (info->long_mask)
1134 if (!DOSFS_MatchLong( info->long_mask, long_name,
1135 flags & DRIVE_CASE_SENSITIVE )) continue;
1138 /* Check the short mask */
1140 if (info->short_mask)
1142 if (!short_name)
1144 DOSFS_Hash( long_name, dos_name, TRUE,
1145 !(flags & DRIVE_CASE_SENSITIVE) );
1146 short_name = dos_name;
1148 if (!DOSFS_MatchShort( info->short_mask, short_name )) continue;
1151 /* Check the file attributes */
1153 lstrcpyn32A( p, long_name, sizeof(buffer) - (int)(p - buffer) );
1154 if (!FILE_Stat( buffer, &fileinfo ))
1156 WARN(dosfs, "can't stat %s\n", buffer);
1157 continue;
1159 if (fileinfo.dwFileAttributes & ~attr) continue;
1161 /* We now have a matching entry; fill the result and return */
1163 entry->dwFileAttributes = fileinfo.dwFileAttributes;
1164 entry->ftCreationTime = fileinfo.ftCreationTime;
1165 entry->ftLastAccessTime = fileinfo.ftLastAccessTime;
1166 entry->ftLastWriteTime = fileinfo.ftLastWriteTime;
1167 entry->nFileSizeHigh = fileinfo.nFileSizeHigh;
1168 entry->nFileSizeLow = fileinfo.nFileSizeLow;
1170 if (short_name)
1171 DOSFS_ToDosDTAFormat( short_name, entry->cAlternateFileName );
1172 else
1173 DOSFS_Hash( long_name, entry->cAlternateFileName, FALSE,
1174 !(flags & DRIVE_CASE_SENSITIVE) );
1176 lstrcpyn32A( entry->cFileName, long_name, sizeof(entry->cFileName) );
1177 if (!(flags & DRIVE_CASE_PRESERVING)) CharLower32A( entry->cFileName );
1178 TRACE(dosfs, "returning %s (%s) %02lx %ld\n",
1179 entry->cFileName, entry->cAlternateFileName,
1180 entry->dwFileAttributes, entry->nFileSizeLow );
1181 return 1;
1183 return 0; /* End of directory */
1186 /***********************************************************************
1187 * DOSFS_FindNext
1189 * Find the next matching file. Return the number of entries read to find
1190 * the matching one, or 0 if no more entries.
1191 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
1192 * file name mask. Either or both can be NULL.
1194 * NOTE: This is supposed to be only called by the int21 emulation
1195 * routines. Thus, we should own the Win16Mutex anyway.
1196 * Nevertheless, we explicitly enter it to ensure the static
1197 * directory cache is protected.
1199 int DOSFS_FindNext( const char *path, const char *short_mask,
1200 const char *long_mask, int drive, BYTE attr,
1201 int skip, WIN32_FIND_DATA32A *entry )
1203 static FIND_FIRST_INFO info = { NULL };
1204 LPCSTR short_name, long_name;
1205 int count;
1207 SYSLEVEL_EnterWin16Lock();
1209 /* Check the cached directory */
1210 if (!(info.dir && info.path == path && info.short_mask == short_mask
1211 && info.long_mask == long_mask && info.drive == drive
1212 && info.attr == attr && info.cur_pos <= skip))
1214 /* Not in the cache, open it anew */
1215 if (info.dir) DOSFS_CloseDir( info.dir );
1217 info.path = (LPSTR)path;
1218 info.long_mask = (LPSTR)long_mask;
1219 info.short_mask = (LPSTR)short_mask;
1220 info.attr = attr;
1221 info.drive = drive;
1222 info.cur_pos = 0;
1223 info.dir = DOSFS_OpenDir( info.path );
1226 /* Skip to desired position */
1227 while (info.cur_pos < skip)
1228 if (info.dir && DOSFS_ReadDir( info.dir, &long_name, &short_name ))
1229 info.cur_pos++;
1230 else
1231 break;
1233 if (info.dir && info.cur_pos == skip && DOSFS_FindNextEx( &info, entry ))
1234 count = info.cur_pos - skip;
1235 else
1236 count = 0;
1238 if (!count)
1240 if (info.dir) DOSFS_CloseDir( info.dir );
1241 memset( &info, '\0', sizeof(info) );
1244 SYSLEVEL_LeaveWin16Lock();
1246 return count;
1251 /*************************************************************************
1252 * FindFirstFile16 (KERNEL.413)
1254 HANDLE16 WINAPI FindFirstFile16( LPCSTR path, WIN32_FIND_DATA32A *data )
1256 DOS_FULL_NAME full_name;
1257 HGLOBAL16 handle;
1258 FIND_FIRST_INFO *info;
1260 data->dwReserved0 = data->dwReserved1 = 0x0;
1261 if (!path) return 0;
1262 if (!DOSFS_GetFullName( path, FALSE, &full_name ))
1263 return INVALID_HANDLE_VALUE16;
1264 if (!(handle = GlobalAlloc16( GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO) )))
1265 return INVALID_HANDLE_VALUE16;
1266 info = (FIND_FIRST_INFO *)GlobalLock16( handle );
1267 info->path = HEAP_strdupA( SystemHeap, 0, full_name.long_name );
1268 info->long_mask = strrchr( info->path, '/' );
1269 *(info->long_mask++) = '\0';
1270 info->short_mask = NULL;
1271 info->attr = 0xff;
1272 if (path[0] && (path[1] == ':')) info->drive = toupper(*path) - 'A';
1273 else info->drive = DRIVE_GetCurrentDrive();
1274 info->cur_pos = 0;
1276 info->dir = DOSFS_OpenDir( info->path );
1278 GlobalUnlock16( handle );
1279 if (!FindNextFile16( handle, data ))
1281 FindClose16( handle );
1282 DOS_ERROR( ER_NoMoreFiles, EC_MediaError, SA_Abort, EL_Disk );
1283 return INVALID_HANDLE_VALUE16;
1285 return handle;
1289 /*************************************************************************
1290 * FindFirstFile32A (KERNEL32.123)
1292 HANDLE32 WINAPI FindFirstFile32A( LPCSTR path, WIN32_FIND_DATA32A *data )
1294 HANDLE32 handle = FindFirstFile16( path, data );
1295 if (handle == INVALID_HANDLE_VALUE16) return INVALID_HANDLE_VALUE32;
1296 return handle;
1300 /*************************************************************************
1301 * FindFirstFile32W (KERNEL32.124)
1303 HANDLE32 WINAPI FindFirstFile32W( LPCWSTR path, WIN32_FIND_DATA32W *data )
1305 WIN32_FIND_DATA32A dataA;
1306 LPSTR pathA = HEAP_strdupWtoA( GetProcessHeap(), 0, path );
1307 HANDLE32 handle = FindFirstFile32A( pathA, &dataA );
1308 HeapFree( GetProcessHeap(), 0, pathA );
1309 if (handle != INVALID_HANDLE_VALUE32)
1311 data->dwFileAttributes = dataA.dwFileAttributes;
1312 data->ftCreationTime = dataA.ftCreationTime;
1313 data->ftLastAccessTime = dataA.ftLastAccessTime;
1314 data->ftLastWriteTime = dataA.ftLastWriteTime;
1315 data->nFileSizeHigh = dataA.nFileSizeHigh;
1316 data->nFileSizeLow = dataA.nFileSizeLow;
1317 lstrcpyAtoW( data->cFileName, dataA.cFileName );
1318 lstrcpyAtoW( data->cAlternateFileName, dataA.cAlternateFileName );
1320 return handle;
1324 /*************************************************************************
1325 * FindNextFile16 (KERNEL.414)
1327 BOOL16 WINAPI FindNextFile16( HANDLE16 handle, WIN32_FIND_DATA32A *data )
1329 FIND_FIRST_INFO *info;
1331 if (!(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
1333 DOS_ERROR( ER_InvalidHandle, EC_ProgramError, SA_Abort, EL_Disk );
1334 return FALSE;
1336 GlobalUnlock16( handle );
1337 if (!info->path || !info->dir)
1339 DOS_ERROR( ER_NoMoreFiles, EC_MediaError, SA_Abort, EL_Disk );
1340 return FALSE;
1342 if (!DOSFS_FindNextEx( info, data ))
1344 DOSFS_CloseDir( info->dir ); info->dir = NULL;
1345 HeapFree( SystemHeap, 0, info->path );
1346 info->path = info->long_mask = NULL;
1347 DOS_ERROR( ER_NoMoreFiles, EC_MediaError, SA_Abort, EL_Disk );
1348 return FALSE;
1350 return TRUE;
1354 /*************************************************************************
1355 * FindNextFile32A (KERNEL32.126)
1357 BOOL32 WINAPI FindNextFile32A( HANDLE32 handle, WIN32_FIND_DATA32A *data )
1359 return FindNextFile16( handle, data );
1363 /*************************************************************************
1364 * FindNextFile32W (KERNEL32.127)
1366 BOOL32 WINAPI FindNextFile32W( HANDLE32 handle, WIN32_FIND_DATA32W *data )
1368 WIN32_FIND_DATA32A dataA;
1369 if (!FindNextFile32A( handle, &dataA )) return FALSE;
1370 data->dwFileAttributes = dataA.dwFileAttributes;
1371 data->ftCreationTime = dataA.ftCreationTime;
1372 data->ftLastAccessTime = dataA.ftLastAccessTime;
1373 data->ftLastWriteTime = dataA.ftLastWriteTime;
1374 data->nFileSizeHigh = dataA.nFileSizeHigh;
1375 data->nFileSizeLow = dataA.nFileSizeLow;
1376 lstrcpyAtoW( data->cFileName, dataA.cFileName );
1377 lstrcpyAtoW( data->cAlternateFileName, dataA.cAlternateFileName );
1378 return TRUE;
1382 /*************************************************************************
1383 * FindClose16 (KERNEL.415)
1385 BOOL16 WINAPI FindClose16( HANDLE16 handle )
1387 FIND_FIRST_INFO *info;
1389 if ((handle == INVALID_HANDLE_VALUE16) ||
1390 !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
1392 DOS_ERROR( ER_InvalidHandle, EC_ProgramError, SA_Abort, EL_Disk );
1393 return FALSE;
1395 if (info->dir) DOSFS_CloseDir( info->dir );
1396 if (info->path) HeapFree( SystemHeap, 0, info->path );
1397 GlobalUnlock16( handle );
1398 GlobalFree16( handle );
1399 return TRUE;
1403 /*************************************************************************
1404 * FindClose32 (KERNEL32.119)
1406 BOOL32 WINAPI FindClose32( HANDLE32 handle )
1408 return FindClose16( (HANDLE16)handle );
1412 /***********************************************************************
1413 * DOSFS_UnixTimeToFileTime
1415 * Convert a Unix time to FILETIME format.
1416 * The FILETIME structure is a 64-bit value representing the number of
1417 * 100-nanosecond intervals since January 1, 1601, 0:00.
1418 * 'remainder' is the nonnegative number of 100-ns intervals
1419 * corresponding to the time fraction smaller than 1 second that
1420 * couldn't be stored in the time_t value.
1422 void DOSFS_UnixTimeToFileTime( time_t unix_time, FILETIME *filetime,
1423 DWORD remainder )
1425 /* NOTES:
1427 CONSTANTS:
1428 The time difference between 1 January 1601, 00:00:00 and
1429 1 January 1970, 00:00:00 is 369 years, plus the leap years
1430 from 1604 to 1968, excluding 1700, 1800, 1900.
1431 This makes (1968 - 1600) / 4 - 3 = 89 leap days, and a total
1432 of 134774 days.
1434 Any day in that period had 24 * 60 * 60 = 86400 seconds.
1436 The time difference is 134774 * 86400 * 10000000, which can be written
1437 116444736000000000
1438 27111902 * 2^32 + 3577643008
1439 413 * 2^48 + 45534 * 2^32 + 54590 * 2^16 + 32768
1441 If you find that these constants are buggy, please change them in all
1442 instances in both conversion functions.
1444 VERSIONS:
1445 There are two versions, one of them uses long long variables and
1446 is presumably faster but not ISO C. The other one uses standard C
1447 data types and operations but relies on the assumption that negative
1448 numbers are stored as 2's complement (-1 is 0xffff....). If this
1449 assumption is violated, dates before 1970 will not convert correctly.
1450 This should however work on any reasonable architecture where WINE
1451 will run.
1453 DETAILS:
1455 Take care not to remove the casts. I have tested these functions
1456 (in both versions) for a lot of numbers. I would be interested in
1457 results on other compilers than GCC.
1459 The operations have been designed to account for the possibility
1460 of 64-bit time_t in future UNICES. Even the versions without
1461 internal long long numbers will work if time_t only is 64 bit.
1462 A 32-bit shift, which was necessary for that operation, turned out
1463 not to work correctly in GCC, besides giving the warning. So I
1464 used a double 16-bit shift instead. Numbers are in the ISO version
1465 represented by three limbs, the most significant with 32 bit, the
1466 other two with 16 bit each.
1468 As the modulo-operator % is not well-defined for negative numbers,
1469 negative divisors have been avoided in DOSFS_FileTimeToUnixTime.
1471 There might be quicker ways to do this in C. Certainly so in
1472 assembler.
1474 Claus Fischer, fischer@iue.tuwien.ac.at
1477 #if (SIZEOF_LONG_LONG >= 8)
1478 # define USE_LONG_LONG 1
1479 #else
1480 # define USE_LONG_LONG 0
1481 #endif
1483 #if USE_LONG_LONG /* gcc supports long long type */
1485 long long int t = unix_time;
1486 t *= 10000000;
1487 t += 116444736000000000LL;
1488 t += remainder;
1489 filetime->dwLowDateTime = (UINT32)t;
1490 filetime->dwHighDateTime = (UINT32)(t >> 32);
1492 #else /* ISO version */
1494 UINT32 a0; /* 16 bit, low bits */
1495 UINT32 a1; /* 16 bit, medium bits */
1496 UINT32 a2; /* 32 bit, high bits */
1498 /* Copy the unix time to a2/a1/a0 */
1499 a0 = unix_time & 0xffff;
1500 a1 = (unix_time >> 16) & 0xffff;
1501 /* This is obsolete if unix_time is only 32 bits, but it does not hurt.
1502 Do not replace this by >> 32, it gives a compiler warning and it does
1503 not work. */
1504 a2 = (unix_time >= 0 ? (unix_time >> 16) >> 16 :
1505 ~((~unix_time >> 16) >> 16));
1507 /* Multiply a by 10000000 (a = a2/a1/a0)
1508 Split the factor into 10000 * 1000 which are both less than 0xffff. */
1509 a0 *= 10000;
1510 a1 = a1 * 10000 + (a0 >> 16);
1511 a2 = a2 * 10000 + (a1 >> 16);
1512 a0 &= 0xffff;
1513 a1 &= 0xffff;
1515 a0 *= 1000;
1516 a1 = a1 * 1000 + (a0 >> 16);
1517 a2 = a2 * 1000 + (a1 >> 16);
1518 a0 &= 0xffff;
1519 a1 &= 0xffff;
1521 /* Add the time difference and the remainder */
1522 a0 += 32768 + (remainder & 0xffff);
1523 a1 += 54590 + (remainder >> 16 ) + (a0 >> 16);
1524 a2 += 27111902 + (a1 >> 16);
1525 a0 &= 0xffff;
1526 a1 &= 0xffff;
1528 /* Set filetime */
1529 filetime->dwLowDateTime = (a1 << 16) + a0;
1530 filetime->dwHighDateTime = a2;
1531 #endif
1535 /***********************************************************************
1536 * DOSFS_FileTimeToUnixTime
1538 * Convert a FILETIME format to Unix time.
1539 * If not NULL, 'remainder' contains the fractional part of the filetime,
1540 * in the range of [0..9999999] (even if time_t is negative).
1542 time_t DOSFS_FileTimeToUnixTime( const FILETIME *filetime, DWORD *remainder )
1544 /* Read the comment in the function DOSFS_UnixTimeToFileTime. */
1545 #if USE_LONG_LONG
1547 long long int t = filetime->dwHighDateTime;
1548 t <<= 32;
1549 t += (UINT32)filetime->dwLowDateTime;
1550 t -= 116444736000000000LL;
1551 if (t < 0)
1553 if (remainder) *remainder = 9999999 - (-t - 1) % 10000000;
1554 return -1 - ((-t - 1) / 10000000);
1556 else
1558 if (remainder) *remainder = t % 10000000;
1559 return t / 10000000;
1562 #else /* ISO version */
1564 UINT32 a0; /* 16 bit, low bits */
1565 UINT32 a1; /* 16 bit, medium bits */
1566 UINT32 a2; /* 32 bit, high bits */
1567 UINT32 r; /* remainder of division */
1568 unsigned int carry; /* carry bit for subtraction */
1569 int negative; /* whether a represents a negative value */
1571 /* Copy the time values to a2/a1/a0 */
1572 a2 = (UINT32)filetime->dwHighDateTime;
1573 a1 = ((UINT32)filetime->dwLowDateTime ) >> 16;
1574 a0 = ((UINT32)filetime->dwLowDateTime ) & 0xffff;
1576 /* Subtract the time difference */
1577 if (a0 >= 32768 ) a0 -= 32768 , carry = 0;
1578 else a0 += (1 << 16) - 32768 , carry = 1;
1580 if (a1 >= 54590 + carry) a1 -= 54590 + carry, carry = 0;
1581 else a1 += (1 << 16) - 54590 - carry, carry = 1;
1583 a2 -= 27111902 + carry;
1585 /* If a is negative, replace a by (-1-a) */
1586 negative = (a2 >= ((UINT32)1) << 31);
1587 if (negative)
1589 /* Set a to -a - 1 (a is a2/a1/a0) */
1590 a0 = 0xffff - a0;
1591 a1 = 0xffff - a1;
1592 a2 = ~a2;
1595 /* Divide a by 10000000 (a = a2/a1/a0), put the rest into r.
1596 Split the divisor into 10000 * 1000 which are both less than 0xffff. */
1597 a1 += (a2 % 10000) << 16;
1598 a2 /= 10000;
1599 a0 += (a1 % 10000) << 16;
1600 a1 /= 10000;
1601 r = a0 % 10000;
1602 a0 /= 10000;
1604 a1 += (a2 % 1000) << 16;
1605 a2 /= 1000;
1606 a0 += (a1 % 1000) << 16;
1607 a1 /= 1000;
1608 r += (a0 % 1000) * 10000;
1609 a0 /= 1000;
1611 /* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
1612 if (negative)
1614 /* Set a to -a - 1 (a is a2/a1/a0) */
1615 a0 = 0xffff - a0;
1616 a1 = 0xffff - a1;
1617 a2 = ~a2;
1619 r = 9999999 - r;
1622 if (remainder) *remainder = r;
1624 /* Do not replace this by << 32, it gives a compiler warning and it does
1625 not work. */
1626 return ((((time_t)a2) << 16) << 16) + (a1 << 16) + a0;
1627 #endif
1631 /***********************************************************************
1632 * DosDateTimeToFileTime (KERNEL32.76)
1634 BOOL32 WINAPI DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft)
1636 struct tm newtm;
1638 newtm.tm_sec = (fattime & 0x1f) * 2;
1639 newtm.tm_min = (fattime >> 5) & 0x3f;
1640 newtm.tm_hour = (fattime >> 11);
1641 newtm.tm_mday = (fatdate & 0x1f);
1642 newtm.tm_mon = ((fatdate >> 5) & 0x0f) - 1;
1643 newtm.tm_year = (fatdate >> 9) + 80;
1644 DOSFS_UnixTimeToFileTime( mktime( &newtm ), ft, 0 );
1645 return TRUE;
1649 /***********************************************************************
1650 * FileTimeToDosDateTime (KERNEL32.111)
1652 BOOL32 WINAPI FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate,
1653 LPWORD fattime )
1655 time_t unixtime = DOSFS_FileTimeToUnixTime( ft, NULL );
1656 struct tm *tm = localtime( &unixtime );
1657 if (fattime)
1658 *fattime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
1659 if (fatdate)
1660 *fatdate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
1661 + tm->tm_mday;
1662 return TRUE;
1666 /***********************************************************************
1667 * LocalFileTimeToFileTime (KERNEL32.373)
1669 BOOL32 WINAPI LocalFileTimeToFileTime( const FILETIME *localft,
1670 LPFILETIME utcft )
1672 struct tm *xtm;
1673 DWORD remainder;
1675 /* convert from local to UTC. Perhaps not correct. FIXME */
1676 time_t unixtime = DOSFS_FileTimeToUnixTime( localft, &remainder );
1677 xtm = gmtime( &unixtime );
1678 DOSFS_UnixTimeToFileTime( mktime(xtm), utcft, remainder );
1679 return TRUE;
1683 /***********************************************************************
1684 * FileTimeToLocalFileTime (KERNEL32.112)
1686 BOOL32 WINAPI FileTimeToLocalFileTime( const FILETIME *utcft,
1687 LPFILETIME localft )
1689 DWORD remainder;
1690 /* convert from UTC to local. Perhaps not correct. FIXME */
1691 time_t unixtime = DOSFS_FileTimeToUnixTime( utcft, &remainder );
1692 #ifdef HAVE_TIMEGM
1693 struct tm *xtm = localtime( &unixtime );
1694 time_t localtime;
1696 localtime = timegm(xtm);
1697 DOSFS_UnixTimeToFileTime( localtime, localft, remainder );
1699 #else
1700 struct tm *xtm,*gtm;
1701 time_t time1,time2;
1703 xtm = localtime( &unixtime );
1704 gtm = gmtime( &unixtime );
1705 time1 = mktime(xtm);
1706 time2 = mktime(gtm);
1707 DOSFS_UnixTimeToFileTime( 2*time1-time2, localft, remainder );
1708 #endif
1709 return TRUE;
1713 /***********************************************************************
1714 * FileTimeToSystemTime (KERNEL32.113)
1716 BOOL32 WINAPI FileTimeToSystemTime( const FILETIME *ft, LPSYSTEMTIME syst )
1718 struct tm *xtm;
1719 DWORD remainder;
1720 time_t xtime = DOSFS_FileTimeToUnixTime( ft, &remainder );
1721 xtm = gmtime(&xtime);
1722 syst->wYear = xtm->tm_year+1900;
1723 syst->wMonth = xtm->tm_mon + 1;
1724 syst->wDayOfWeek = xtm->tm_wday;
1725 syst->wDay = xtm->tm_mday;
1726 syst->wHour = xtm->tm_hour;
1727 syst->wMinute = xtm->tm_min;
1728 syst->wSecond = xtm->tm_sec;
1729 syst->wMilliseconds = remainder / 10000;
1730 return TRUE;
1733 /***********************************************************************
1734 * QueryDosDeviceA (KERNEL32.413)
1736 * returns array of strings terminated by \0, terminated by \0
1738 DWORD WINAPI QueryDosDevice32A(LPCSTR devname,LPSTR target,DWORD bufsize)
1740 LPSTR s;
1741 char buffer[200];
1743 TRACE(dosfs,"(%s,...)\n",devname?devname:"<null>");
1744 if (!devname) {
1745 /* return known MSDOS devices */
1746 lstrcpy32A(buffer,"CON COM1 COM2 LPT1 NUL ");
1747 while ((s=strchr(buffer,' ')))
1748 *s='\0';
1750 lstrcpyn32A(target,buffer,bufsize);
1751 return strlen(buffer);
1753 lstrcpy32A(buffer,"\\DEV\\");
1754 lstrcat32A(buffer,devname);
1755 if ((s=strchr(buffer,':'))) *s='\0';
1756 lstrcpyn32A(target,buffer,bufsize);
1757 return strlen(buffer);
1761 /***********************************************************************
1762 * QueryDosDeviceW (KERNEL32.414)
1764 * returns array of strings terminated by \0, terminated by \0
1766 DWORD WINAPI QueryDosDevice32W(LPCWSTR devname,LPWSTR target,DWORD bufsize)
1768 LPSTR devnameA = devname?HEAP_strdupWtoA(GetProcessHeap(),0,devname):NULL;
1769 LPSTR targetA = (LPSTR)HEAP_xalloc(GetProcessHeap(),0,bufsize);
1770 DWORD ret = QueryDosDevice32A(devnameA,targetA,bufsize);
1772 lstrcpynAtoW(target,targetA,bufsize);
1773 if (devnameA) HeapFree(GetProcessHeap(),0,devnameA);
1774 if (targetA) HeapFree(GetProcessHeap(),0,targetA);
1775 return ret;
1779 /***********************************************************************
1780 * SystemTimeToFileTime (KERNEL32.526)
1782 BOOL32 WINAPI SystemTimeToFileTime( const SYSTEMTIME *syst, LPFILETIME ft )
1784 #ifdef HAVE_TIMEGM
1785 struct tm xtm;
1786 time_t utctime;
1787 #else
1788 struct tm xtm,*local_tm,*utc_tm;
1789 time_t localtim,utctime;
1790 #endif
1792 xtm.tm_year = syst->wYear-1900;
1793 xtm.tm_mon = syst->wMonth - 1;
1794 xtm.tm_wday = syst->wDayOfWeek;
1795 xtm.tm_mday = syst->wDay;
1796 xtm.tm_hour = syst->wHour;
1797 xtm.tm_min = syst->wMinute;
1798 xtm.tm_sec = syst->wSecond; /* this is UTC */
1799 xtm.tm_isdst = -1;
1800 #ifdef HAVE_TIMEGM
1801 utctime = timegm(&xtm);
1802 DOSFS_UnixTimeToFileTime( utctime, ft,
1803 syst->wMilliseconds * 10000 );
1804 #else
1805 localtim = mktime(&xtm); /* now we've got local time */
1806 local_tm = localtime(&localtim);
1807 utc_tm = gmtime(&localtim);
1808 utctime = mktime(utc_tm);
1809 DOSFS_UnixTimeToFileTime( 2*localtim -utctime, ft,
1810 syst->wMilliseconds * 10000 );
1811 #endif
1812 return TRUE;
1815 BOOL32 WINAPI DefineDosDevice32A(DWORD flags,LPCSTR devname,LPCSTR targetpath) {
1816 FIXME(dosfs,"(0x%08lx,%s,%s),stub!\n",flags,devname,targetpath);
1817 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1818 return FALSE;