Stubs for EnumServiceStatus32, small changes.
[wine/multimedia.git] / files / dos_fs.c
blob95414528e4b9c40c1fec15ca1852d9a021762b38
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 if ((handle = FILE_Alloc( &file )) == INVALID_HANDLE_VALUE32)
653 return HFILE_ERROR32;
654 else {
655 file->unix_name = HEAP_strdupA( SystemHeap, 0, name );
656 return handle;
659 if (!strcmp(DOSFS_Devices[i].name,"HPSCAN")) {
660 if ((handle = FILE_Alloc( &file )) == INVALID_HANDLE_VALUE32)
661 return HFILE_ERROR32;
662 else {
663 file->unix_name = HEAP_strdupA( SystemHeap, 0, name );
664 return handle;
667 FIXME(dosfs,"device open %s not supported (yet)\n",DOSFS_Devices[i].name);
668 return HFILE_ERROR32;
672 return HFILE_ERROR32;
676 /***********************************************************************
677 * DOSFS_GetPathDrive
679 * Get the drive specified by a given path name (DOS or Unix format).
681 static int DOSFS_GetPathDrive( const char **name )
683 int drive;
684 const char *p = *name;
686 if (*p && (p[1] == ':'))
688 drive = toupper(*p) - 'A';
689 *name += 2;
691 else if (*p == '/') /* Absolute Unix path? */
693 if ((drive = DRIVE_FindDriveRoot( name )) == -1)
695 MSG("Warning: %s not accessible from a DOS drive\n", *name );
696 /* Assume it really was a DOS name */
697 drive = DRIVE_GetCurrentDrive();
700 else drive = DRIVE_GetCurrentDrive();
702 if (!DRIVE_IsValid(drive))
704 DOS_ERROR( ER_InvalidDrive, EC_MediaError, SA_Abort, EL_Disk );
705 return -1;
707 return drive;
711 /***********************************************************************
712 * DOSFS_GetFullName
714 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
715 * Unix name / short DOS name pair.
716 * Return FALSE if one of the path components does not exist. The last path
717 * component is only checked if 'check_last' is non-zero.
718 * The buffers pointed to by 'long_buf' and 'short_buf' must be
719 * at least MAX_PATHNAME_LEN long.
721 BOOL32 DOSFS_GetFullName( LPCSTR name, BOOL32 check_last, DOS_FULL_NAME *full )
723 BOOL32 found;
724 UINT32 flags;
725 char *p_l, *p_s, *root;
727 TRACE(dosfs, "%s (last=%d)\n",
728 name, check_last );
730 if ((full->drive = DOSFS_GetPathDrive( &name )) == -1) return FALSE;
731 flags = DRIVE_GetFlags( full->drive );
733 lstrcpyn32A( full->long_name, DRIVE_GetRoot( full->drive ),
734 sizeof(full->long_name) );
735 if (full->long_name[1]) root = full->long_name + strlen(full->long_name);
736 else root = full->long_name; /* root directory */
738 strcpy( full->short_name, "A:\\" );
739 full->short_name[0] += full->drive;
741 if ((*name == '\\') || (*name == '/')) /* Absolute path */
743 while ((*name == '\\') || (*name == '/')) name++;
745 else /* Relative path */
747 lstrcpyn32A( root + 1, DRIVE_GetUnixCwd( full->drive ),
748 sizeof(full->long_name) - (root - full->long_name) - 1 );
749 if (root[1]) *root = '/';
750 lstrcpyn32A( full->short_name + 3, DRIVE_GetDosCwd( full->drive ),
751 sizeof(full->short_name) - 3 );
754 p_l = full->long_name[1] ? full->long_name + strlen(full->long_name)
755 : full->long_name;
756 p_s = full->short_name[3] ? full->short_name + strlen(full->short_name)
757 : full->short_name + 2;
758 found = TRUE;
760 while (*name && found)
762 /* Check for '.' and '..' */
764 if (*name == '.')
766 if (IS_END_OF_NAME(name[1]))
768 name++;
769 while ((*name == '\\') || (*name == '/')) name++;
770 continue;
772 else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
774 name += 2;
775 while ((*name == '\\') || (*name == '/')) name++;
776 while ((p_l > root) && (*p_l != '/')) p_l--;
777 while ((p_s > full->short_name + 2) && (*p_s != '\\')) p_s--;
778 *p_l = *p_s = '\0'; /* Remove trailing separator */
779 continue;
783 /* Make sure buffers are large enough */
785 if ((p_s >= full->short_name + sizeof(full->short_name) - 14) ||
786 (p_l >= full->long_name + sizeof(full->long_name) - 1))
788 DOS_ERROR( ER_PathNotFound, EC_NotFound, SA_Abort, EL_Disk );
789 return FALSE;
792 /* Get the long and short name matching the file name */
794 if ((found = DOSFS_FindUnixName( full->long_name, name, p_l + 1,
795 sizeof(full->long_name) - (p_l - full->long_name) - 1,
796 p_s + 1, !(flags & DRIVE_CASE_SENSITIVE) )))
798 *p_l++ = '/';
799 p_l += strlen(p_l);
800 *p_s++ = '\\';
801 p_s += strlen(p_s);
802 while (!IS_END_OF_NAME(*name)) name++;
804 else if (!check_last)
806 *p_l++ = '/';
807 *p_s++ = '\\';
808 while (!IS_END_OF_NAME(*name) &&
809 (p_s < full->short_name + sizeof(full->short_name) - 1) &&
810 (p_l < full->long_name + sizeof(full->long_name) - 1))
812 *p_s++ = tolower(*name);
813 /* If the drive is case-sensitive we want to create new */
814 /* files in lower-case otherwise we can't reopen them */
815 /* under the same short name. */
816 if (flags & DRIVE_CASE_SENSITIVE) *p_l++ = tolower(*name);
817 else *p_l++ = *name;
818 name++;
820 *p_l = *p_s = '\0';
822 while ((*name == '\\') || (*name == '/')) name++;
825 if (!found)
827 if (check_last)
829 DOS_ERROR( ER_FileNotFound, EC_NotFound, SA_Abort, EL_Disk );
830 return FALSE;
832 if (*name) /* Not last */
834 DOS_ERROR( ER_PathNotFound, EC_NotFound, SA_Abort, EL_Disk );
835 return FALSE;
838 if (!full->long_name[0]) strcpy( full->long_name, "/" );
839 if (!full->short_name[2]) strcpy( full->short_name + 2, "\\" );
840 TRACE(dosfs, "returning %s = %s\n",
841 full->long_name, full->short_name );
842 return TRUE;
846 /***********************************************************************
847 * GetShortPathName32A (KERNEL32.271)
849 * NOTES
850 * observed:
851 * longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
852 * *longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0
854 DWORD WINAPI GetShortPathName32A( LPCSTR longpath, LPSTR shortpath,
855 DWORD shortlen )
857 DOS_FULL_NAME full_name;
859 if (!longpath)
861 SetLastError(ERROR_INVALID_PARAMETER);
862 return 0;
865 if (!longpath[0])
867 SetLastError(ERROR_BAD_PATHNAME);
868 return 0;
871 /* FIXME: is it correct to always return a fully qualified short path? */
872 if (!DOSFS_GetFullName( longpath, TRUE, &full_name ))
874 SetLastError(ERROR_BAD_PATHNAME);
875 return 0;
877 lstrcpyn32A( shortpath, full_name.short_name, shortlen );
878 return strlen( full_name.short_name );
882 /***********************************************************************
883 * GetShortPathName32W (KERNEL32.272)
885 DWORD WINAPI GetShortPathName32W( LPCWSTR longpath, LPWSTR shortpath,
886 DWORD shortlen )
888 DOS_FULL_NAME full_name;
889 LPSTR longpathA ;
890 DWORD ret = 0;
892 if (!longpath)
893 { SetLastError(ERROR_INVALID_PARAMETER);
894 return 0;
897 if (!longpath[0])
898 { SetLastError(ERROR_BAD_PATHNAME);
899 return 0;
903 longpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, longpath );
905 /* FIXME: is it correct to always return a fully qualified short path? */
906 if (DOSFS_GetFullName( longpathA, TRUE, &full_name ))
908 ret = strlen( full_name.short_name );
909 lstrcpynAtoW( shortpath, full_name.short_name, shortlen );
912 SetLastError(ERROR_BAD_PATHNAME);
913 HeapFree( GetProcessHeap(), 0, longpathA );
914 return 0;
918 /***********************************************************************
919 * GetLongPathName32A (KERNEL32.xxx)
921 DWORD WINAPI GetLongPathName32A( LPCSTR shortpath, LPSTR longpath,
922 DWORD longlen )
924 DOS_FULL_NAME full_name;
925 char *p;
926 char *longfilename;
927 DWORD shortpathlen;
929 if (!DOSFS_GetFullName( shortpath, TRUE, &full_name )) return 0;
930 lstrcpyn32A( longpath, full_name.short_name, longlen );
931 /* Do some hackery to get the long filename.
932 * FIXME: Would be better if it returned the
933 * long version of the directories too
935 longfilename = strrchr(full_name.long_name, '/')+1;
936 if (longpath != NULL) {
937 if ((p = strrchr( longpath, '\\' )) != NULL) {
938 p++;
939 longlen -= (p-longpath);
940 lstrcpyn32A( p, longfilename , longlen);
943 shortpathlen =
944 ((strrchr( full_name.short_name, '\\' ) - full_name.short_name) + 1);
945 return shortpathlen + strlen( longfilename );
949 /***********************************************************************
950 * GetLongPathName32W (KERNEL32.269)
952 DWORD WINAPI GetLongPathName32W( LPCWSTR shortpath, LPWSTR longpath,
953 DWORD longlen )
955 DOS_FULL_NAME full_name;
956 DWORD ret = 0;
957 LPSTR shortpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, shortpath );
959 /* FIXME: is it correct to always return a fully qualified short path? */
960 if (DOSFS_GetFullName( shortpathA, TRUE, &full_name ))
962 ret = strlen( full_name.short_name );
963 lstrcpynAtoW( longpath, full_name.long_name, longlen );
965 HeapFree( GetProcessHeap(), 0, shortpathA );
966 return ret;
970 /***********************************************************************
971 * DOSFS_DoGetFullPathName
973 * Implementation of GetFullPathName32A/W.
975 static DWORD DOSFS_DoGetFullPathName( LPCSTR name, DWORD len, LPSTR result,
976 BOOL32 unicode )
978 char buffer[MAX_PATHNAME_LEN];
979 int drive;
980 char *p;
982 TRACE(dosfs, "converting %s\n", name );
984 if (!name || !result) return 0;
986 if ((drive = DOSFS_GetPathDrive( &name )) == -1) return 0;
987 p = buffer;
988 *p++ = 'A' + drive;
989 *p++ = ':';
990 if (IS_END_OF_NAME(*name) && (*name)) /* Absolute path */
992 while ((*name == '\\') || (*name == '/')) name++;
994 else /* Relative path or empty path */
996 *p++ = '\\';
997 lstrcpyn32A( p, DRIVE_GetDosCwd(drive), sizeof(buffer) - 3 );
998 if (*p) p += strlen(p); else p--;
1000 if (!*name) /* empty path */
1001 *p++ = '\\';
1002 *p = '\0';
1004 while (*name)
1006 if (*name == '.')
1008 if (IS_END_OF_NAME(name[1]))
1010 name++;
1011 while ((*name == '\\') || (*name == '/')) name++;
1012 continue;
1014 else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
1016 name += 2;
1017 while ((*name == '\\') || (*name == '/')) name++;
1018 while ((p > buffer + 2) && (*p != '\\')) p--;
1019 *p = '\0'; /* Remove trailing separator */
1020 continue;
1023 if (p >= buffer + sizeof(buffer) - 1)
1025 DOS_ERROR( ER_PathNotFound, EC_NotFound, SA_Abort, EL_Disk );
1026 return 0;
1028 *p++ = '\\';
1029 while (!IS_END_OF_NAME(*name) && (p < buffer + sizeof(buffer) - 1))
1030 *p++ = *name++;
1031 *p = '\0';
1032 while ((*name == '\\') || (*name == '/')) name++;
1035 if (!buffer[2])
1037 buffer[2] = '\\';
1038 buffer[3] = '\0';
1040 if (!(DRIVE_GetFlags(drive) & DRIVE_CASE_PRESERVING))
1041 CharUpper32A( buffer );
1043 if (unicode) lstrcpynAtoW( (LPWSTR)result, buffer, len );
1044 else lstrcpyn32A( result, buffer, len );
1046 TRACE(dosfs, "returning %s\n", buffer );
1047 return strlen(buffer);
1051 /***********************************************************************
1052 * GetFullPathName32A (KERNEL32.272)
1054 DWORD WINAPI GetFullPathName32A( LPCSTR name, DWORD len, LPSTR buffer,
1055 LPSTR *lastpart )
1057 DWORD ret = DOSFS_DoGetFullPathName( name, len, buffer, FALSE );
1058 if (ret && lastpart)
1060 LPSTR p = buffer + strlen(buffer);
1061 while ((p > buffer + 2) && (*p != '\\')) p--;
1062 *lastpart = p + 1;
1064 return ret;
1068 /***********************************************************************
1069 * GetFullPathName32W (KERNEL32.273)
1071 DWORD WINAPI GetFullPathName32W( LPCWSTR name, DWORD len, LPWSTR buffer,
1072 LPWSTR *lastpart )
1074 LPSTR nameA = HEAP_strdupWtoA( GetProcessHeap(), 0, name );
1075 DWORD ret = DOSFS_DoGetFullPathName( nameA, len, (LPSTR)buffer, TRUE );
1076 HeapFree( GetProcessHeap(), 0, nameA );
1077 if (ret && lastpart)
1079 LPWSTR p = buffer + lstrlen32W(buffer);
1080 while ((p > buffer + 2) && (*p != '\\')) p--;
1081 *lastpart = p + 1;
1083 return ret;
1086 /***********************************************************************
1087 * DOSFS_FindNextEx
1089 static int DOSFS_FindNextEx( FIND_FIRST_INFO *info, WIN32_FIND_DATA32A *entry )
1091 BYTE attr = info->attr | FA_UNUSED | FA_ARCHIVE | FA_RDONLY;
1092 UINT32 flags = DRIVE_GetFlags( info->drive );
1093 char *p, buffer[MAX_PATHNAME_LEN];
1094 const char *drive_path;
1095 int drive_root;
1096 LPCSTR long_name, short_name;
1097 BY_HANDLE_FILE_INFORMATION fileinfo;
1098 char dos_name[13];
1100 if ((info->attr & ~(FA_UNUSED | FA_ARCHIVE | FA_RDONLY)) == FA_LABEL)
1102 if (info->cur_pos) return 0;
1103 entry->dwFileAttributes = FILE_ATTRIBUTE_LABEL;
1104 DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftCreationTime, 0 );
1105 DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftLastAccessTime, 0 );
1106 DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftLastWriteTime, 0 );
1107 entry->nFileSizeHigh = 0;
1108 entry->nFileSizeLow = 0;
1109 entry->dwReserved0 = 0;
1110 entry->dwReserved1 = 0;
1111 DOSFS_ToDosDTAFormat( DRIVE_GetLabel( info->drive ), entry->cFileName );
1112 strcpy( entry->cAlternateFileName, entry->cFileName );
1113 info->cur_pos++;
1114 return 1;
1117 drive_path = info->path + strlen(DRIVE_GetRoot( info->drive ));
1118 while ((*drive_path == '/') || (*drive_path == '\\')) drive_path++;
1119 drive_root = !*drive_path;
1121 lstrcpyn32A( buffer, info->path, sizeof(buffer) - 1 );
1122 strcat( buffer, "/" );
1123 p = buffer + strlen(buffer);
1125 while (DOSFS_ReadDir( info->dir, &long_name, &short_name ))
1127 info->cur_pos++;
1129 /* Don't return '.' and '..' in the root of the drive */
1130 if (drive_root && (long_name[0] == '.') &&
1131 (!long_name[1] || ((long_name[1] == '.') && !long_name[2])))
1132 continue;
1134 /* Check the long mask */
1136 if (info->long_mask)
1138 if (!DOSFS_MatchLong( info->long_mask, long_name,
1139 flags & DRIVE_CASE_SENSITIVE )) continue;
1142 /* Check the short mask */
1144 if (info->short_mask)
1146 if (!short_name)
1148 DOSFS_Hash( long_name, dos_name, TRUE,
1149 !(flags & DRIVE_CASE_SENSITIVE) );
1150 short_name = dos_name;
1152 if (!DOSFS_MatchShort( info->short_mask, short_name )) continue;
1155 /* Check the file attributes */
1157 lstrcpyn32A( p, long_name, sizeof(buffer) - (int)(p - buffer) );
1158 if (!FILE_Stat( buffer, &fileinfo ))
1160 WARN(dosfs, "can't stat %s\n", buffer);
1161 continue;
1163 if (fileinfo.dwFileAttributes & ~attr) continue;
1165 /* We now have a matching entry; fill the result and return */
1167 entry->dwFileAttributes = fileinfo.dwFileAttributes;
1168 entry->ftCreationTime = fileinfo.ftCreationTime;
1169 entry->ftLastAccessTime = fileinfo.ftLastAccessTime;
1170 entry->ftLastWriteTime = fileinfo.ftLastWriteTime;
1171 entry->nFileSizeHigh = fileinfo.nFileSizeHigh;
1172 entry->nFileSizeLow = fileinfo.nFileSizeLow;
1174 if (short_name)
1175 DOSFS_ToDosDTAFormat( short_name, entry->cAlternateFileName );
1176 else
1177 DOSFS_Hash( long_name, entry->cAlternateFileName, FALSE,
1178 !(flags & DRIVE_CASE_SENSITIVE) );
1180 lstrcpyn32A( entry->cFileName, long_name, sizeof(entry->cFileName) );
1181 if (!(flags & DRIVE_CASE_PRESERVING)) CharLower32A( entry->cFileName );
1182 TRACE(dosfs, "returning %s (%s) %02lx %ld\n",
1183 entry->cFileName, entry->cAlternateFileName,
1184 entry->dwFileAttributes, entry->nFileSizeLow );
1185 return 1;
1187 return 0; /* End of directory */
1190 /***********************************************************************
1191 * DOSFS_FindNext
1193 * Find the next matching file. Return the number of entries read to find
1194 * the matching one, or 0 if no more entries.
1195 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
1196 * file name mask. Either or both can be NULL.
1198 * NOTE: This is supposed to be only called by the int21 emulation
1199 * routines. Thus, we should own the Win16Mutex anyway.
1200 * Nevertheless, we explicitly enter it to ensure the static
1201 * directory cache is protected.
1203 int DOSFS_FindNext( const char *path, const char *short_mask,
1204 const char *long_mask, int drive, BYTE attr,
1205 int skip, WIN32_FIND_DATA32A *entry )
1207 static FIND_FIRST_INFO info = { NULL };
1208 LPCSTR short_name, long_name;
1209 int count;
1211 SYSLEVEL_EnterWin16Lock();
1213 /* Check the cached directory */
1214 if (!(info.dir && info.path == path && info.short_mask == short_mask
1215 && info.long_mask == long_mask && info.drive == drive
1216 && info.attr == attr && info.cur_pos <= skip))
1218 /* Not in the cache, open it anew */
1219 if (info.dir) DOSFS_CloseDir( info.dir );
1221 info.path = (LPSTR)path;
1222 info.long_mask = (LPSTR)long_mask;
1223 info.short_mask = (LPSTR)short_mask;
1224 info.attr = attr;
1225 info.drive = drive;
1226 info.cur_pos = 0;
1227 info.dir = DOSFS_OpenDir( info.path );
1230 /* Skip to desired position */
1231 while (info.cur_pos < skip)
1232 if (info.dir && DOSFS_ReadDir( info.dir, &long_name, &short_name ))
1233 info.cur_pos++;
1234 else
1235 break;
1237 if (info.dir && info.cur_pos == skip && DOSFS_FindNextEx( &info, entry ))
1238 count = info.cur_pos - skip;
1239 else
1240 count = 0;
1242 if (!count)
1244 if (info.dir) DOSFS_CloseDir( info.dir );
1245 memset( &info, '\0', sizeof(info) );
1248 SYSLEVEL_LeaveWin16Lock();
1250 return count;
1255 /*************************************************************************
1256 * FindFirstFile16 (KERNEL.413)
1258 HANDLE16 WINAPI FindFirstFile16( LPCSTR path, WIN32_FIND_DATA32A *data )
1260 DOS_FULL_NAME full_name;
1261 HGLOBAL16 handle;
1262 FIND_FIRST_INFO *info;
1264 data->dwReserved0 = data->dwReserved1 = 0x0;
1265 if (!path) return 0;
1266 if (!DOSFS_GetFullName( path, FALSE, &full_name ))
1267 return INVALID_HANDLE_VALUE16;
1268 if (!(handle = GlobalAlloc16( GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO) )))
1269 return INVALID_HANDLE_VALUE16;
1270 info = (FIND_FIRST_INFO *)GlobalLock16( handle );
1271 info->path = HEAP_strdupA( SystemHeap, 0, full_name.long_name );
1272 info->long_mask = strrchr( info->path, '/' );
1273 *(info->long_mask++) = '\0';
1274 info->short_mask = NULL;
1275 info->attr = 0xff;
1276 if (path[0] && (path[1] == ':')) info->drive = toupper(*path) - 'A';
1277 else info->drive = DRIVE_GetCurrentDrive();
1278 info->cur_pos = 0;
1280 info->dir = DOSFS_OpenDir( info->path );
1282 GlobalUnlock16( handle );
1283 if (!FindNextFile16( handle, data ))
1285 FindClose16( handle );
1286 DOS_ERROR( ER_NoMoreFiles, EC_MediaError, SA_Abort, EL_Disk );
1287 return INVALID_HANDLE_VALUE16;
1289 return handle;
1293 /*************************************************************************
1294 * FindFirstFile32A (KERNEL32.123)
1296 HANDLE32 WINAPI FindFirstFile32A( LPCSTR path, WIN32_FIND_DATA32A *data )
1298 HANDLE32 handle = FindFirstFile16( path, data );
1299 if (handle == INVALID_HANDLE_VALUE16) return INVALID_HANDLE_VALUE32;
1300 return handle;
1304 /*************************************************************************
1305 * FindFirstFile32W (KERNEL32.124)
1307 HANDLE32 WINAPI FindFirstFile32W( LPCWSTR path, WIN32_FIND_DATA32W *data )
1309 WIN32_FIND_DATA32A dataA;
1310 LPSTR pathA = HEAP_strdupWtoA( GetProcessHeap(), 0, path );
1311 HANDLE32 handle = FindFirstFile32A( pathA, &dataA );
1312 HeapFree( GetProcessHeap(), 0, pathA );
1313 if (handle != INVALID_HANDLE_VALUE32)
1315 data->dwFileAttributes = dataA.dwFileAttributes;
1316 data->ftCreationTime = dataA.ftCreationTime;
1317 data->ftLastAccessTime = dataA.ftLastAccessTime;
1318 data->ftLastWriteTime = dataA.ftLastWriteTime;
1319 data->nFileSizeHigh = dataA.nFileSizeHigh;
1320 data->nFileSizeLow = dataA.nFileSizeLow;
1321 lstrcpyAtoW( data->cFileName, dataA.cFileName );
1322 lstrcpyAtoW( data->cAlternateFileName, dataA.cAlternateFileName );
1324 return handle;
1328 /*************************************************************************
1329 * FindNextFile16 (KERNEL.414)
1331 BOOL16 WINAPI FindNextFile16( HANDLE16 handle, WIN32_FIND_DATA32A *data )
1333 FIND_FIRST_INFO *info;
1335 if (!(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
1337 DOS_ERROR( ER_InvalidHandle, EC_ProgramError, SA_Abort, EL_Disk );
1338 return FALSE;
1340 GlobalUnlock16( handle );
1341 if (!info->path || !info->dir)
1343 DOS_ERROR( ER_NoMoreFiles, EC_MediaError, SA_Abort, EL_Disk );
1344 return FALSE;
1346 if (!DOSFS_FindNextEx( info, data ))
1348 DOSFS_CloseDir( info->dir ); info->dir = NULL;
1349 HeapFree( SystemHeap, 0, info->path );
1350 info->path = info->long_mask = NULL;
1351 DOS_ERROR( ER_NoMoreFiles, EC_MediaError, SA_Abort, EL_Disk );
1352 return FALSE;
1354 return TRUE;
1358 /*************************************************************************
1359 * FindNextFile32A (KERNEL32.126)
1361 BOOL32 WINAPI FindNextFile32A( HANDLE32 handle, WIN32_FIND_DATA32A *data )
1363 return FindNextFile16( handle, data );
1367 /*************************************************************************
1368 * FindNextFile32W (KERNEL32.127)
1370 BOOL32 WINAPI FindNextFile32W( HANDLE32 handle, WIN32_FIND_DATA32W *data )
1372 WIN32_FIND_DATA32A dataA;
1373 if (!FindNextFile32A( handle, &dataA )) return FALSE;
1374 data->dwFileAttributes = dataA.dwFileAttributes;
1375 data->ftCreationTime = dataA.ftCreationTime;
1376 data->ftLastAccessTime = dataA.ftLastAccessTime;
1377 data->ftLastWriteTime = dataA.ftLastWriteTime;
1378 data->nFileSizeHigh = dataA.nFileSizeHigh;
1379 data->nFileSizeLow = dataA.nFileSizeLow;
1380 lstrcpyAtoW( data->cFileName, dataA.cFileName );
1381 lstrcpyAtoW( data->cAlternateFileName, dataA.cAlternateFileName );
1382 return TRUE;
1386 /*************************************************************************
1387 * FindClose16 (KERNEL.415)
1389 BOOL16 WINAPI FindClose16( HANDLE16 handle )
1391 FIND_FIRST_INFO *info;
1393 if ((handle == INVALID_HANDLE_VALUE16) ||
1394 !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
1396 DOS_ERROR( ER_InvalidHandle, EC_ProgramError, SA_Abort, EL_Disk );
1397 return FALSE;
1399 if (info->dir) DOSFS_CloseDir( info->dir );
1400 if (info->path) HeapFree( SystemHeap, 0, info->path );
1401 GlobalUnlock16( handle );
1402 GlobalFree16( handle );
1403 return TRUE;
1407 /*************************************************************************
1408 * FindClose32 (KERNEL32.119)
1410 BOOL32 WINAPI FindClose32( HANDLE32 handle )
1412 return FindClose16( (HANDLE16)handle );
1416 /***********************************************************************
1417 * DOSFS_UnixTimeToFileTime
1419 * Convert a Unix time to FILETIME format.
1420 * The FILETIME structure is a 64-bit value representing the number of
1421 * 100-nanosecond intervals since January 1, 1601, 0:00.
1422 * 'remainder' is the nonnegative number of 100-ns intervals
1423 * corresponding to the time fraction smaller than 1 second that
1424 * couldn't be stored in the time_t value.
1426 void DOSFS_UnixTimeToFileTime( time_t unix_time, FILETIME *filetime,
1427 DWORD remainder )
1429 /* NOTES:
1431 CONSTANTS:
1432 The time difference between 1 January 1601, 00:00:00 and
1433 1 January 1970, 00:00:00 is 369 years, plus the leap years
1434 from 1604 to 1968, excluding 1700, 1800, 1900.
1435 This makes (1968 - 1600) / 4 - 3 = 89 leap days, and a total
1436 of 134774 days.
1438 Any day in that period had 24 * 60 * 60 = 86400 seconds.
1440 The time difference is 134774 * 86400 * 10000000, which can be written
1441 116444736000000000
1442 27111902 * 2^32 + 3577643008
1443 413 * 2^48 + 45534 * 2^32 + 54590 * 2^16 + 32768
1445 If you find that these constants are buggy, please change them in all
1446 instances in both conversion functions.
1448 VERSIONS:
1449 There are two versions, one of them uses long long variables and
1450 is presumably faster but not ISO C. The other one uses standard C
1451 data types and operations but relies on the assumption that negative
1452 numbers are stored as 2's complement (-1 is 0xffff....). If this
1453 assumption is violated, dates before 1970 will not convert correctly.
1454 This should however work on any reasonable architecture where WINE
1455 will run.
1457 DETAILS:
1459 Take care not to remove the casts. I have tested these functions
1460 (in both versions) for a lot of numbers. I would be interested in
1461 results on other compilers than GCC.
1463 The operations have been designed to account for the possibility
1464 of 64-bit time_t in future UNICES. Even the versions without
1465 internal long long numbers will work if time_t only is 64 bit.
1466 A 32-bit shift, which was necessary for that operation, turned out
1467 not to work correctly in GCC, besides giving the warning. So I
1468 used a double 16-bit shift instead. Numbers are in the ISO version
1469 represented by three limbs, the most significant with 32 bit, the
1470 other two with 16 bit each.
1472 As the modulo-operator % is not well-defined for negative numbers,
1473 negative divisors have been avoided in DOSFS_FileTimeToUnixTime.
1475 There might be quicker ways to do this in C. Certainly so in
1476 assembler.
1478 Claus Fischer, fischer@iue.tuwien.ac.at
1481 #if (SIZEOF_LONG_LONG >= 8)
1482 # define USE_LONG_LONG 1
1483 #else
1484 # define USE_LONG_LONG 0
1485 #endif
1487 #if USE_LONG_LONG /* gcc supports long long type */
1489 long long int t = unix_time;
1490 t *= 10000000;
1491 t += 116444736000000000LL;
1492 t += remainder;
1493 filetime->dwLowDateTime = (UINT32)t;
1494 filetime->dwHighDateTime = (UINT32)(t >> 32);
1496 #else /* ISO version */
1498 UINT32 a0; /* 16 bit, low bits */
1499 UINT32 a1; /* 16 bit, medium bits */
1500 UINT32 a2; /* 32 bit, high bits */
1502 /* Copy the unix time to a2/a1/a0 */
1503 a0 = unix_time & 0xffff;
1504 a1 = (unix_time >> 16) & 0xffff;
1505 /* This is obsolete if unix_time is only 32 bits, but it does not hurt.
1506 Do not replace this by >> 32, it gives a compiler warning and it does
1507 not work. */
1508 a2 = (unix_time >= 0 ? (unix_time >> 16) >> 16 :
1509 ~((~unix_time >> 16) >> 16));
1511 /* Multiply a by 10000000 (a = a2/a1/a0)
1512 Split the factor into 10000 * 1000 which are both less than 0xffff. */
1513 a0 *= 10000;
1514 a1 = a1 * 10000 + (a0 >> 16);
1515 a2 = a2 * 10000 + (a1 >> 16);
1516 a0 &= 0xffff;
1517 a1 &= 0xffff;
1519 a0 *= 1000;
1520 a1 = a1 * 1000 + (a0 >> 16);
1521 a2 = a2 * 1000 + (a1 >> 16);
1522 a0 &= 0xffff;
1523 a1 &= 0xffff;
1525 /* Add the time difference and the remainder */
1526 a0 += 32768 + (remainder & 0xffff);
1527 a1 += 54590 + (remainder >> 16 ) + (a0 >> 16);
1528 a2 += 27111902 + (a1 >> 16);
1529 a0 &= 0xffff;
1530 a1 &= 0xffff;
1532 /* Set filetime */
1533 filetime->dwLowDateTime = (a1 << 16) + a0;
1534 filetime->dwHighDateTime = a2;
1535 #endif
1539 /***********************************************************************
1540 * DOSFS_FileTimeToUnixTime
1542 * Convert a FILETIME format to Unix time.
1543 * If not NULL, 'remainder' contains the fractional part of the filetime,
1544 * in the range of [0..9999999] (even if time_t is negative).
1546 time_t DOSFS_FileTimeToUnixTime( const FILETIME *filetime, DWORD *remainder )
1548 /* Read the comment in the function DOSFS_UnixTimeToFileTime. */
1549 #if USE_LONG_LONG
1551 long long int t = filetime->dwHighDateTime;
1552 t <<= 32;
1553 t += (UINT32)filetime->dwLowDateTime;
1554 t -= 116444736000000000LL;
1555 if (t < 0)
1557 if (remainder) *remainder = 9999999 - (-t - 1) % 10000000;
1558 return -1 - ((-t - 1) / 10000000);
1560 else
1562 if (remainder) *remainder = t % 10000000;
1563 return t / 10000000;
1566 #else /* ISO version */
1568 UINT32 a0; /* 16 bit, low bits */
1569 UINT32 a1; /* 16 bit, medium bits */
1570 UINT32 a2; /* 32 bit, high bits */
1571 UINT32 r; /* remainder of division */
1572 unsigned int carry; /* carry bit for subtraction */
1573 int negative; /* whether a represents a negative value */
1575 /* Copy the time values to a2/a1/a0 */
1576 a2 = (UINT32)filetime->dwHighDateTime;
1577 a1 = ((UINT32)filetime->dwLowDateTime ) >> 16;
1578 a0 = ((UINT32)filetime->dwLowDateTime ) & 0xffff;
1580 /* Subtract the time difference */
1581 if (a0 >= 32768 ) a0 -= 32768 , carry = 0;
1582 else a0 += (1 << 16) - 32768 , carry = 1;
1584 if (a1 >= 54590 + carry) a1 -= 54590 + carry, carry = 0;
1585 else a1 += (1 << 16) - 54590 - carry, carry = 1;
1587 a2 -= 27111902 + carry;
1589 /* If a is negative, replace a by (-1-a) */
1590 negative = (a2 >= ((UINT32)1) << 31);
1591 if (negative)
1593 /* Set a to -a - 1 (a is a2/a1/a0) */
1594 a0 = 0xffff - a0;
1595 a1 = 0xffff - a1;
1596 a2 = ~a2;
1599 /* Divide a by 10000000 (a = a2/a1/a0), put the rest into r.
1600 Split the divisor into 10000 * 1000 which are both less than 0xffff. */
1601 a1 += (a2 % 10000) << 16;
1602 a2 /= 10000;
1603 a0 += (a1 % 10000) << 16;
1604 a1 /= 10000;
1605 r = a0 % 10000;
1606 a0 /= 10000;
1608 a1 += (a2 % 1000) << 16;
1609 a2 /= 1000;
1610 a0 += (a1 % 1000) << 16;
1611 a1 /= 1000;
1612 r += (a0 % 1000) * 10000;
1613 a0 /= 1000;
1615 /* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
1616 if (negative)
1618 /* Set a to -a - 1 (a is a2/a1/a0) */
1619 a0 = 0xffff - a0;
1620 a1 = 0xffff - a1;
1621 a2 = ~a2;
1623 r = 9999999 - r;
1626 if (remainder) *remainder = r;
1628 /* Do not replace this by << 32, it gives a compiler warning and it does
1629 not work. */
1630 return ((((time_t)a2) << 16) << 16) + (a1 << 16) + a0;
1631 #endif
1635 /***********************************************************************
1636 * DosDateTimeToFileTime (KERNEL32.76)
1638 BOOL32 WINAPI DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft)
1640 struct tm newtm;
1642 newtm.tm_sec = (fattime & 0x1f) * 2;
1643 newtm.tm_min = (fattime >> 5) & 0x3f;
1644 newtm.tm_hour = (fattime >> 11);
1645 newtm.tm_mday = (fatdate & 0x1f);
1646 newtm.tm_mon = ((fatdate >> 5) & 0x0f) - 1;
1647 newtm.tm_year = (fatdate >> 9) + 80;
1648 DOSFS_UnixTimeToFileTime( mktime( &newtm ), ft, 0 );
1649 return TRUE;
1653 /***********************************************************************
1654 * FileTimeToDosDateTime (KERNEL32.111)
1656 BOOL32 WINAPI FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate,
1657 LPWORD fattime )
1659 time_t unixtime = DOSFS_FileTimeToUnixTime( ft, NULL );
1660 struct tm *tm = localtime( &unixtime );
1661 if (fattime)
1662 *fattime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
1663 if (fatdate)
1664 *fatdate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
1665 + tm->tm_mday;
1666 return TRUE;
1670 /***********************************************************************
1671 * LocalFileTimeToFileTime (KERNEL32.373)
1673 BOOL32 WINAPI LocalFileTimeToFileTime( const FILETIME *localft,
1674 LPFILETIME utcft )
1676 struct tm *xtm;
1677 DWORD remainder;
1679 /* convert from local to UTC. Perhaps not correct. FIXME */
1680 time_t unixtime = DOSFS_FileTimeToUnixTime( localft, &remainder );
1681 xtm = gmtime( &unixtime );
1682 DOSFS_UnixTimeToFileTime( mktime(xtm), utcft, remainder );
1683 return TRUE;
1687 /***********************************************************************
1688 * FileTimeToLocalFileTime (KERNEL32.112)
1690 BOOL32 WINAPI FileTimeToLocalFileTime( const FILETIME *utcft,
1691 LPFILETIME localft )
1693 DWORD remainder;
1694 /* convert from UTC to local. Perhaps not correct. FIXME */
1695 time_t unixtime = DOSFS_FileTimeToUnixTime( utcft, &remainder );
1696 #ifdef HAVE_TIMEGM
1697 struct tm *xtm = localtime( &unixtime );
1698 time_t localtime;
1700 localtime = timegm(xtm);
1701 DOSFS_UnixTimeToFileTime( localtime, localft, remainder );
1703 #else
1704 struct tm *xtm,*gtm;
1705 time_t time1,time2;
1707 xtm = localtime( &unixtime );
1708 gtm = gmtime( &unixtime );
1709 time1 = mktime(xtm);
1710 time2 = mktime(gtm);
1711 DOSFS_UnixTimeToFileTime( 2*time1-time2, localft, remainder );
1712 #endif
1713 return TRUE;
1717 /***********************************************************************
1718 * FileTimeToSystemTime (KERNEL32.113)
1720 BOOL32 WINAPI FileTimeToSystemTime( const FILETIME *ft, LPSYSTEMTIME syst )
1722 struct tm *xtm;
1723 DWORD remainder;
1724 time_t xtime = DOSFS_FileTimeToUnixTime( ft, &remainder );
1725 xtm = gmtime(&xtime);
1726 syst->wYear = xtm->tm_year+1900;
1727 syst->wMonth = xtm->tm_mon + 1;
1728 syst->wDayOfWeek = xtm->tm_wday;
1729 syst->wDay = xtm->tm_mday;
1730 syst->wHour = xtm->tm_hour;
1731 syst->wMinute = xtm->tm_min;
1732 syst->wSecond = xtm->tm_sec;
1733 syst->wMilliseconds = remainder / 10000;
1734 return TRUE;
1737 /***********************************************************************
1738 * QueryDosDeviceA (KERNEL32.413)
1740 * returns array of strings terminated by \0, terminated by \0
1742 DWORD WINAPI QueryDosDevice32A(LPCSTR devname,LPSTR target,DWORD bufsize)
1744 LPSTR s;
1745 char buffer[200];
1747 TRACE(dosfs,"(%s,...)\n",devname?devname:"<null>");
1748 if (!devname) {
1749 /* return known MSDOS devices */
1750 lstrcpy32A(buffer,"CON COM1 COM2 LPT1 NUL ");
1751 while ((s=strchr(buffer,' ')))
1752 *s='\0';
1754 lstrcpyn32A(target,buffer,bufsize);
1755 return strlen(buffer);
1757 lstrcpy32A(buffer,"\\DEV\\");
1758 lstrcat32A(buffer,devname);
1759 if ((s=strchr(buffer,':'))) *s='\0';
1760 lstrcpyn32A(target,buffer,bufsize);
1761 return strlen(buffer);
1765 /***********************************************************************
1766 * QueryDosDeviceW (KERNEL32.414)
1768 * returns array of strings terminated by \0, terminated by \0
1770 DWORD WINAPI QueryDosDevice32W(LPCWSTR devname,LPWSTR target,DWORD bufsize)
1772 LPSTR devnameA = devname?HEAP_strdupWtoA(GetProcessHeap(),0,devname):NULL;
1773 LPSTR targetA = (LPSTR)HEAP_xalloc(GetProcessHeap(),0,bufsize);
1774 DWORD ret = QueryDosDevice32A(devnameA,targetA,bufsize);
1776 lstrcpynAtoW(target,targetA,bufsize);
1777 if (devnameA) HeapFree(GetProcessHeap(),0,devnameA);
1778 if (targetA) HeapFree(GetProcessHeap(),0,targetA);
1779 return ret;
1783 /***********************************************************************
1784 * SystemTimeToFileTime (KERNEL32.526)
1786 BOOL32 WINAPI SystemTimeToFileTime( const SYSTEMTIME *syst, LPFILETIME ft )
1788 #ifdef HAVE_TIMEGM
1789 struct tm xtm;
1790 time_t utctime;
1791 #else
1792 struct tm xtm,*local_tm,*utc_tm;
1793 time_t localtim,utctime;
1794 #endif
1796 xtm.tm_year = syst->wYear-1900;
1797 xtm.tm_mon = syst->wMonth - 1;
1798 xtm.tm_wday = syst->wDayOfWeek;
1799 xtm.tm_mday = syst->wDay;
1800 xtm.tm_hour = syst->wHour;
1801 xtm.tm_min = syst->wMinute;
1802 xtm.tm_sec = syst->wSecond; /* this is UTC */
1803 xtm.tm_isdst = -1;
1804 #ifdef HAVE_TIMEGM
1805 utctime = timegm(&xtm);
1806 DOSFS_UnixTimeToFileTime( utctime, ft,
1807 syst->wMilliseconds * 10000 );
1808 #else
1809 localtim = mktime(&xtm); /* now we've got local time */
1810 local_tm = localtime(&localtim);
1811 utc_tm = gmtime(&localtim);
1812 utctime = mktime(utc_tm);
1813 DOSFS_UnixTimeToFileTime( 2*localtim -utctime, ft,
1814 syst->wMilliseconds * 10000 );
1815 #endif
1816 return TRUE;
1819 BOOL32 WINAPI DefineDosDevice32A(DWORD flags,LPCSTR devname,LPCSTR targetpath) {
1820 FIXME(dosfs,"(0x%08lx,%s,%s),stub!\n",flags,devname,targetpath);
1821 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1822 return FALSE;