Release 980315
[wine/multimedia.git] / files / dos_fs.c
blob8ca3fb9900135bcb2a92de9b713e8712c4101a33
1 /*
2 * DOS file system functions
4 * Copyright 1993 Erik Bos
5 * Copyright 1996 Alexandre Julliard
6 */
8 #include <sys/types.h>
9 #include <ctype.h>
10 #include <dirent.h>
11 #include <errno.h>
12 #include <fcntl.h>
13 #include <string.h>
14 #include <stdlib.h>
15 #include <stdio.h>
16 #include <sys/stat.h>
17 #include <sys/ioctl.h>
18 #include <time.h>
19 #include <unistd.h>
20 #if defined(__svr4__) || defined(_SCO_DS)
21 #include <sys/statfs.h>
22 #endif
24 #include "windows.h"
25 #include "winerror.h"
26 #include "drive.h"
27 #include "file.h"
28 #include "heap.h"
29 #include "msdos.h"
30 #include "debug.h"
32 /* Define the VFAT ioctl to get both short and long file names */
33 /* FIXME: is it possible to get this to work on other systems? */
34 #ifdef linux
35 #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, long)
36 /* We want the real kernel dirent structure, not the libc one */
37 typedef struct
39 long d_ino;
40 long d_off;
41 unsigned short d_reclen;
42 char d_name[256];
43 } KERNEL_DIRENT;
45 #else /* linux */
46 #undef VFAT_IOCTL_READDIR_BOTH /* just in case... */
47 #endif /* linux */
49 /* Chars we don't want to see in DOS file names */
50 #define INVALID_DOS_CHARS "*?<>|\"+=,;[] \345"
52 static const char *DOSFS_Devices[] = {
53 "CON","PRN","NUL","AUX","LPT1","LPT2","LPT3","LPT4","COM1","COM2","COM3","COM4",
57 #define GET_DRIVE(path) \
58 (((path)[1] == ':') ? toupper((path)[0]) - 'A' : DOSFS_CurDrive)
60 /* DOS extended error status */
61 WORD DOS_ExtendedError;
62 BYTE DOS_ErrorClass;
63 BYTE DOS_ErrorAction;
64 BYTE DOS_ErrorLocus;
66 /* Info structure for FindFirstFile handle */
67 typedef struct
69 LPSTR path;
70 LPSTR mask;
71 int drive;
72 int skip;
73 } FIND_FIRST_INFO;
76 /* Directory info for DOSFS_ReadDir */
77 typedef struct
79 DIR *dir;
80 #ifdef VFAT_IOCTL_READDIR_BOTH
81 int fd;
82 char short_name[12];
83 KERNEL_DIRENT dirent[2];
84 #endif
85 } DOS_DIR;
88 /***********************************************************************
89 * DOSFS_ValidDOSName
91 * Return 1 if Unix file 'name' is also a valid MS-DOS name
92 * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
93 * File name can be terminated by '\0', '\\' or '/'.
95 static int DOSFS_ValidDOSName( const char *name, int ignore_case )
97 static const char invalid_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS;
98 const char *p = name;
99 const char *invalid = ignore_case ? (invalid_chars + 26) : invalid_chars;
100 int len = 0;
102 if (*p == '.')
104 /* Check for "." and ".." */
105 p++;
106 if (*p == '.') p++;
107 /* All other names beginning with '.' are invalid */
108 return (IS_END_OF_NAME(*p));
110 while (!IS_END_OF_NAME(*p))
112 if (strchr( invalid, *p )) return 0; /* Invalid char */
113 if (*p == '.') break; /* Start of the extension */
114 if (++len > 8) return 0; /* Name too long */
115 p++;
117 if (*p != '.') return 1; /* End of name */
118 p++;
119 if (IS_END_OF_NAME(*p)) return 0; /* Empty extension not allowed */
120 len = 0;
121 while (!IS_END_OF_NAME(*p))
123 if (strchr( invalid, *p )) return 0; /* Invalid char */
124 if (*p == '.') return 0; /* Second extension not allowed */
125 if (++len > 3) return 0; /* Extension too long */
126 p++;
128 return 1;
132 /***********************************************************************
133 * DOSFS_ToDosFCBFormat
135 * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
136 * expanding wild cards and converting to upper-case in the process.
137 * File name can be terminated by '\0', '\\' or '/'.
138 * Return FALSE if the name is not a valid DOS name.
139 * 'buffer' must be at least 12 characters long.
141 BOOL32 DOSFS_ToDosFCBFormat( LPCSTR name, LPSTR buffer )
143 static const char invalid_chars[] = INVALID_DOS_CHARS;
144 const char *p = name;
145 int i;
147 /* Check for "." and ".." */
148 if (*p == '.')
150 p++;
151 strcpy( buffer, ". " );
152 if (*p == '.')
154 buffer[1] = '.';
155 p++;
157 return (!*p || (*p == '/') || (*p == '\\'));
160 for (i = 0; i < 8; i++)
162 switch(*p)
164 case '\0':
165 case '\\':
166 case '/':
167 case '.':
168 buffer[i] = ' ';
169 break;
170 case '?':
171 p++;
172 /* fall through */
173 case '*':
174 buffer[i] = '?';
175 break;
176 default:
177 if (strchr( invalid_chars, *p )) return FALSE;
178 buffer[i] = toupper(*p);
179 p++;
180 break;
184 if (*p == '*')
186 /* Skip all chars after wildcard up to first dot */
187 while (*p && (*p != '/') && (*p != '\\') && (*p != '.')) p++;
189 else
191 /* Check if name too long */
192 if (*p && (*p != '/') && (*p != '\\') && (*p != '.')) return FALSE;
194 if (*p == '.') p++; /* Skip dot */
196 for (i = 8; i < 11; i++)
198 switch(*p)
200 case '\0':
201 case '\\':
202 case '/':
203 buffer[i] = ' ';
204 break;
205 case '.':
206 return FALSE; /* Second extension not allowed */
207 case '?':
208 p++;
209 /* fall through */
210 case '*':
211 buffer[i] = '?';
212 break;
213 default:
214 if (strchr( invalid_chars, *p )) return FALSE;
215 buffer[i] = toupper(*p);
216 p++;
217 break;
220 buffer[11] = '\0';
221 return TRUE;
225 /***********************************************************************
226 * DOSFS_ToDosDTAFormat
228 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
229 * converting to upper-case in the process.
230 * File name can be terminated by '\0', '\\' or '/'.
231 * 'buffer' must be at least 13 characters long.
233 static void DOSFS_ToDosDTAFormat( LPCSTR name, LPSTR buffer )
235 char *p;
237 memcpy( buffer, name, 8 );
238 for (p = buffer + 8; (p > buffer) && (p[-1] == ' '); p--);
239 *p++ = '.';
240 memcpy( p, name + 8, 3 );
241 for (p += 3; p[-1] == ' '; p--);
242 if (p[-1] == '.') p--;
243 *p = '\0';
247 /***********************************************************************
248 * DOSFS_MatchShort
250 * Check a DOS file name against a mask (both in FCB format).
252 static int DOSFS_MatchShort( const char *mask, const char *name )
254 int i;
255 for (i = 11; i > 0; i--, mask++, name++)
256 if ((*mask != '?') && (*mask != *name)) return 0;
257 return 1;
261 /***********************************************************************
262 * DOSFS_MatchLong
264 * Check a long file name against a mask.
266 static int DOSFS_MatchLong( const char *mask, const char *name,
267 int case_sensitive )
269 if (!strcmp( mask, "*.*" )) return 1;
270 while (*name && *mask)
272 if (*mask == '*')
274 mask++;
275 while (*mask == '*') mask++; /* Skip consecutive '*' */
276 if (!*mask) return 1;
277 if (case_sensitive) while (*name && (*name != *mask)) name++;
278 else while (*name && (toupper(*name) != toupper(*mask))) name++;
279 if (!*name) return 0;
281 else if (*mask != '?')
283 if (case_sensitive)
285 if (*mask != *name) return 0;
287 else if (toupper(*mask) != toupper(*name)) return 0;
289 mask++;
290 name++;
292 return (!*name && !*mask);
296 /***********************************************************************
297 * DOSFS_OpenDir
299 static DOS_DIR *DOSFS_OpenDir( LPCSTR path )
301 DOS_DIR *dir = HeapAlloc( SystemHeap, 0, sizeof(*dir) );
302 if (!dir)
304 DOS_ERROR( ER_OutOfMemory, EC_OutOfResource, SA_Abort, EL_Memory );
305 return NULL;
308 #ifdef VFAT_IOCTL_READDIR_BOTH
310 /* Check if the VFAT ioctl is supported on this directory */
312 if ((dir->fd = open( path, O_RDONLY )) != -1)
314 if (ioctl( dir->fd, VFAT_IOCTL_READDIR_BOTH, (long)dir->dirent ) == -1)
316 close( dir->fd );
317 dir->fd = -1;
319 else
321 /* Set the file pointer back at the start of the directory */
322 lseek( dir->fd, 0, SEEK_SET );
323 dir->dir = NULL;
324 return dir;
327 #endif /* VFAT_IOCTL_READDIR_BOTH */
329 /* Now use the standard opendir/readdir interface */
331 if (!(dir->dir = opendir( path )))
333 HeapFree( SystemHeap, 0, dir );
334 return NULL;
336 return dir;
340 /***********************************************************************
341 * DOSFS_CloseDir
343 static void DOSFS_CloseDir( DOS_DIR *dir )
345 #ifdef VFAT_IOCTL_READDIR_BOTH
346 if (dir->fd != -1) close( dir->fd );
347 #endif /* VFAT_IOCTL_READDIR_BOTH */
348 if (dir->dir) closedir( dir->dir );
349 HeapFree( SystemHeap, 0, dir );
353 /***********************************************************************
354 * DOSFS_ReadDir
356 static BOOL32 DOSFS_ReadDir( DOS_DIR *dir, LPCSTR *long_name,
357 LPCSTR *short_name )
359 struct dirent *dirent;
361 #ifdef VFAT_IOCTL_READDIR_BOTH
362 if (dir->fd != -1)
364 if (ioctl( dir->fd, VFAT_IOCTL_READDIR_BOTH, (long)dir->dirent ) != -1) {
365 if (!dir->dirent[0].d_reclen) return FALSE;
366 if (!DOSFS_ToDosFCBFormat( dir->dirent[0].d_name, dir->short_name ))
367 dir->short_name[0] = '\0';
368 *short_name = dir->short_name;
369 if (dir->dirent[1].d_name[0]) *long_name = dir->dirent[1].d_name;
370 else *long_name = dir->dirent[0].d_name;
371 return TRUE;
374 #endif /* VFAT_IOCTL_READDIR_BOTH */
376 if (!(dirent = readdir( dir->dir ))) return FALSE;
377 *long_name = dirent->d_name;
378 *short_name = NULL;
379 return TRUE;
383 /***********************************************************************
384 * DOSFS_Hash
386 * Transform a Unix file name into a hashed DOS name. If the name is a valid
387 * DOS name, it is converted to upper-case; otherwise it is replaced by a
388 * hashed version that fits in 8.3 format.
389 * File name can be terminated by '\0', '\\' or '/'.
390 * 'buffer' must be at least 13 characters long.
392 static void DOSFS_Hash( LPCSTR name, LPSTR buffer, BOOL32 dir_format,
393 BOOL32 ignore_case )
395 static const char invalid_chars[] = INVALID_DOS_CHARS "~.";
396 static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
398 const char *p, *ext;
399 char *dst;
400 unsigned short hash;
401 int i;
403 if (dir_format) strcpy( buffer, " " );
405 if (DOSFS_ValidDOSName( name, ignore_case ))
407 /* Check for '.' and '..' */
408 if (*name == '.')
410 buffer[0] = '.';
411 if (!dir_format) buffer[1] = buffer[2] = '\0';
412 if (name[1] == '.') buffer[1] = '.';
413 return;
416 /* Simply copy the name, converting to uppercase */
418 for (dst = buffer; !IS_END_OF_NAME(*name) && (*name != '.'); name++)
419 *dst++ = toupper(*name);
420 if (*name == '.')
422 if (dir_format) dst = buffer + 8;
423 else *dst++ = '.';
424 for (name++; !IS_END_OF_NAME(*name); name++)
425 *dst++ = toupper(*name);
427 if (!dir_format) *dst = '\0';
428 return;
431 /* Compute the hash code of the file name */
432 /* If you know something about hash functions, feel free to */
433 /* insert a better algorithm here... */
434 if (ignore_case)
436 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
437 hash = (hash<<3) ^ (hash>>5) ^ tolower(*p) ^ (tolower(p[1]) << 8);
438 hash = (hash<<3) ^ (hash>>5) ^ tolower(*p); /* Last character*/
440 else
442 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
443 hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
444 hash = (hash << 3) ^ (hash >> 5) ^ *p; /* Last character */
447 /* Find last dot for start of the extension */
448 for (p = name+1, ext = NULL; !IS_END_OF_NAME(*p); p++)
449 if (*p == '.') ext = p;
450 if (ext && IS_END_OF_NAME(ext[1]))
451 ext = NULL; /* Empty extension ignored */
453 /* Copy first 4 chars, replacing invalid chars with '_' */
454 for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
456 if (IS_END_OF_NAME(*p) || (p == ext)) break;
457 *dst++ = strchr( invalid_chars, *p ) ? '_' : toupper(*p);
459 /* Pad to 5 chars with '~' */
460 while (i-- >= 0) *dst++ = '~';
462 /* Insert hash code converted to 3 ASCII chars */
463 *dst++ = hash_chars[(hash >> 10) & 0x1f];
464 *dst++ = hash_chars[(hash >> 5) & 0x1f];
465 *dst++ = hash_chars[hash & 0x1f];
467 /* Copy the first 3 chars of the extension (if any) */
468 if (ext)
470 if (!dir_format) *dst++ = '.';
471 for (i = 3, ext++; (i > 0) && !IS_END_OF_NAME(*ext); i--, ext++)
472 *dst++ = strchr( invalid_chars, *ext ) ? '_' : toupper(*ext);
474 if (!dir_format) *dst = '\0';
478 /***********************************************************************
479 * DOSFS_FindUnixName
481 * Find the Unix file name in a given directory that corresponds to
482 * a file name (either in Unix or DOS format).
483 * File name can be terminated by '\0', '\\' or '/'.
484 * Return TRUE if OK, FALSE if no file name matches.
486 * 'long_buf' must be at least 'long_len' characters long. If the long name
487 * turns out to be larger than that, the function returns FALSE.
488 * 'short_buf' must be at least 13 characters long.
490 BOOL32 DOSFS_FindUnixName( LPCSTR path, LPCSTR name, LPSTR long_buf,
491 INT32 long_len, LPSTR short_buf, BOOL32 ignore_case)
493 DOS_DIR *dir;
494 LPCSTR long_name, short_name;
495 char dos_name[12], tmp_buf[13];
496 BOOL32 ret;
498 const char *p = strchr( name, '/' );
499 int len = p ? (int)(p - name) : strlen(name);
500 if ((p = strchr( name, '\\' ))) len = MIN( (int)(p - name), len );
501 if (long_len < len + 1) return FALSE;
503 TRACE(dosfs, "%s,%s\n", path, name );
505 if (!DOSFS_ToDosFCBFormat( name, dos_name )) dos_name[0] = '\0';
507 if (!(dir = DOSFS_OpenDir( path )))
509 WARN(dosfs, "(%s,%s): can't open dir: %s\n",
510 path, name, strerror(errno) );
511 return FALSE;
514 while ((ret = DOSFS_ReadDir( dir, &long_name, &short_name )))
516 /* Check against Unix name */
517 if (len == strlen(long_name))
519 if (!ignore_case)
521 if (!lstrncmp32A( long_name, name, len )) break;
523 else
525 if (!lstrncmpi32A( long_name, name, len )) break;
528 if (dos_name[0])
530 /* Check against hashed DOS name */
531 if (!short_name)
533 DOSFS_Hash( long_name, tmp_buf, TRUE, ignore_case );
534 short_name = tmp_buf;
536 if (!strcmp( dos_name, short_name )) break;
539 if (ret)
541 if (long_buf) strcpy( long_buf, long_name );
542 if (short_buf)
544 if (short_name)
545 DOSFS_ToDosDTAFormat( short_name, short_buf );
546 else
547 DOSFS_Hash( long_name, short_buf, FALSE, ignore_case );
549 TRACE(dosfs, "(%s,%s) -> %s (%s)\n",
550 path, name, long_name, short_buf ? short_buf : "***");
552 else
553 WARN(dosfs, "'%s' not found in '%s'\n", name, path);
554 DOSFS_CloseDir( dir );
555 return ret;
559 /***********************************************************************
560 * DOSFS_IsDevice
562 * Check if a DOS file name represents a DOS device.
564 BOOL32 DOSFS_IsDevice( const char *name )
566 int i;
567 const char *p;
569 if (name[0] && (name[1] == ':')) name += 2;
570 if ((p = strrchr( name, '/' ))) name = p + 1;
571 if ((p = strrchr( name, '\\' ))) name = p + 1;
572 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
574 const char *dev = DOSFS_Devices[i];
575 if (!lstrncmpi32A( dev, name, strlen(dev) ))
577 p = name + strlen( dev );
578 if (!*p || (*p == '.')) return TRUE;
581 return FALSE;
584 /***********************************************************************
585 * DOSFS_OpenDevice
587 * Open a DOS device. This might not map 1:1 into the UNIX device concept.
589 HFILE32 DOSFS_OpenDevice( const char *name, int unixmode )
591 int i;
592 const char *p;
594 if (name[0] && (name[1] == ':')) name += 2;
595 if ((p = strrchr( name, '/' ))) name = p + 1;
596 if ((p = strrchr( name, '\\' ))) name = p + 1;
597 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
599 const char *dev = DOSFS_Devices[i];
600 if (!lstrncmpi32A( dev, name, strlen(dev) ))
602 p = name + strlen( dev );
603 if (!*p || (*p == '.')) {
604 /* got it */
605 if (!strcmp(DOSFS_Devices[i],"NUL"))
606 return FILE_OpenUnixFile("/dev/null",unixmode);
607 if (!strcmp(DOSFS_Devices[i],"CON")) {
608 switch (unixmode) {
609 case O_RDONLY:
610 return GetStdHandle( STD_INPUT_HANDLE );
611 break;
612 case O_WRONLY:
613 return GetStdHandle( STD_OUTPUT_HANDLE );
614 break;
615 default:
616 FIXME(dosfs,"can't open CON read/write\n");
617 return HFILE_ERROR32;
618 break;
621 /* FIXME: the rest of the devices ... lptX, comX et.al. */
622 return HFILE_ERROR32;
626 return HFILE_ERROR32;
630 /***********************************************************************
631 * DOSFS_GetPathDrive
633 * Get the drive specified by a given path name (DOS or Unix format).
635 static int DOSFS_GetPathDrive( const char **name )
637 int drive;
638 const char *p = *name;
640 if (*p && (p[1] == ':'))
642 drive = toupper(*p) - 'A';
643 *name += 2;
645 else if (*p == '/') /* Absolute Unix path? */
647 if ((drive = DRIVE_FindDriveRoot( name )) == -1)
649 fprintf( stderr, "Warning: %s not accessible from a DOS drive\n",
650 *name );
651 /* Assume it really was a DOS name */
652 drive = DRIVE_GetCurrentDrive();
655 else drive = DRIVE_GetCurrentDrive();
657 if (!DRIVE_IsValid(drive))
659 DOS_ERROR( ER_InvalidDrive, EC_MediaError, SA_Abort, EL_Disk );
660 return -1;
662 return drive;
666 /***********************************************************************
667 * DOSFS_GetFullName
669 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
670 * Unix name / short DOS name pair.
671 * Return FALSE if one of the path components does not exist. The last path
672 * component is only checked if 'check_last' is non-zero.
673 * The buffers pointed to by 'long_buf' and 'short_buf' must be
674 * at least MAX_PATHNAME_LEN long.
676 BOOL32 DOSFS_GetFullName( LPCSTR name, BOOL32 check_last, DOS_FULL_NAME *full )
678 BOOL32 found;
679 UINT32 flags;
680 char *p_l, *p_s, *root;
682 TRACE(dosfs, "%s (last=%d)\n",
683 name, check_last );
685 if ((full->drive = DOSFS_GetPathDrive( &name )) == -1) return FALSE;
686 flags = DRIVE_GetFlags( full->drive );
688 lstrcpyn32A( full->long_name, DRIVE_GetRoot( full->drive ),
689 sizeof(full->long_name) );
690 if (full->long_name[1]) root = full->long_name + strlen(full->long_name);
691 else root = full->long_name; /* root directory */
693 strcpy( full->short_name, "A:\\" );
694 full->short_name[0] += full->drive;
696 if ((*name == '\\') || (*name == '/')) /* Absolute path */
698 while ((*name == '\\') || (*name == '/')) name++;
700 else /* Relative path */
702 lstrcpyn32A( root + 1, DRIVE_GetUnixCwd( full->drive ),
703 sizeof(full->long_name) - (root - full->long_name) - 1 );
704 if (root[1]) *root = '/';
705 lstrcpyn32A( full->short_name + 3, DRIVE_GetDosCwd( full->drive ),
706 sizeof(full->short_name) - 3 );
709 p_l = full->long_name[1] ? full->long_name + strlen(full->long_name)
710 : full->long_name;
711 p_s = full->short_name[3] ? full->short_name + strlen(full->short_name)
712 : full->short_name + 2;
713 found = TRUE;
715 while (*name && found)
717 /* Check for '.' and '..' */
719 if (*name == '.')
721 if (IS_END_OF_NAME(name[1]))
723 name++;
724 while ((*name == '\\') || (*name == '/')) name++;
725 continue;
727 else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
729 name += 2;
730 while ((*name == '\\') || (*name == '/')) name++;
731 while ((p_l > root) && (*p_l != '/')) p_l--;
732 while ((p_s > full->short_name + 2) && (*p_s != '\\')) p_s--;
733 *p_l = *p_s = '\0'; /* Remove trailing separator */
734 continue;
738 /* Make sure buffers are large enough */
740 if ((p_s >= full->short_name + sizeof(full->short_name) - 14) ||
741 (p_l >= full->long_name + sizeof(full->long_name) - 1))
743 DOS_ERROR( ER_PathNotFound, EC_NotFound, SA_Abort, EL_Disk );
744 return FALSE;
747 /* Get the long and short name matching the file name */
749 if ((found = DOSFS_FindUnixName( full->long_name, name, p_l + 1,
750 sizeof(full->long_name) - (p_l - full->long_name) - 1,
751 p_s + 1, !(flags & DRIVE_CASE_SENSITIVE) )))
753 *p_l++ = '/';
754 p_l += strlen(p_l);
755 *p_s++ = '\\';
756 p_s += strlen(p_s);
757 while (!IS_END_OF_NAME(*name)) name++;
759 else if (!check_last)
761 *p_l++ = '/';
762 *p_s++ = '\\';
763 while (!IS_END_OF_NAME(*name) &&
764 (p_s < full->short_name + sizeof(full->short_name) - 1) &&
765 (p_l < full->long_name + sizeof(full->long_name) - 1))
767 *p_l++ = *p_s++ = tolower(*name);
768 name++;
770 *p_l = *p_s = '\0';
772 while ((*name == '\\') || (*name == '/')) name++;
775 if (!found)
777 if (check_last)
779 DOS_ERROR( ER_FileNotFound, EC_NotFound, SA_Abort, EL_Disk );
780 return FALSE;
782 if (*name) /* Not last */
784 DOS_ERROR( ER_PathNotFound, EC_NotFound, SA_Abort, EL_Disk );
785 return FALSE;
788 if (!full->long_name[0]) strcpy( full->long_name, "/" );
789 if (!full->short_name[2]) strcpy( full->short_name + 2, "\\" );
790 TRACE(dosfs, "returning %s = %s\n",
791 full->long_name, full->short_name );
792 return TRUE;
796 /***********************************************************************
797 * GetShortPathName32A (KERNEL32.271)
799 DWORD WINAPI GetShortPathName32A( LPCSTR longpath, LPSTR shortpath,
800 DWORD shortlen )
802 DOS_FULL_NAME full_name;
804 /* FIXME: is it correct to always return a fully qualified short path? */
805 if (!DOSFS_GetFullName( longpath, TRUE, &full_name )) return 0;
806 lstrcpyn32A( shortpath, full_name.short_name, shortlen );
807 return strlen( full_name.short_name );
811 /***********************************************************************
812 * GetShortPathName32W (KERNEL32.272)
814 DWORD WINAPI GetShortPathName32W( LPCWSTR longpath, LPWSTR shortpath,
815 DWORD shortlen )
817 DOS_FULL_NAME full_name;
818 DWORD ret = 0;
819 LPSTR longpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, longpath );
821 /* FIXME: is it correct to always return a fully qualified short path? */
822 if (DOSFS_GetFullName( longpathA, TRUE, &full_name ))
824 ret = strlen( full_name.short_name );
825 lstrcpynAtoW( shortpath, full_name.short_name, shortlen );
827 HeapFree( GetProcessHeap(), 0, longpathA );
828 return ret;
832 /***********************************************************************
833 * GetLongPathName32A (KERNEL32.xxx)
835 DWORD WINAPI GetLongPathName32A( LPCSTR shortpath, LPSTR longpath,
836 DWORD longlen )
838 DOS_FULL_NAME full_name;
840 /* FIXME: is it correct to always return a fully qualified short path? */
841 if (!DOSFS_GetFullName( shortpath, TRUE, &full_name )) return 0;
842 lstrcpyn32A( longpath, full_name.long_name, longlen );
843 return strlen( full_name.long_name );
847 /***********************************************************************
848 * GetLongPathName32W (KERNEL32.269)
850 DWORD WINAPI GetLongPathName32W( LPCWSTR shortpath, LPWSTR longpath,
851 DWORD longlen )
853 DOS_FULL_NAME full_name;
854 DWORD ret = 0;
855 LPSTR shortpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, shortpath );
857 /* FIXME: is it correct to always return a fully qualified short path? */
858 if (DOSFS_GetFullName( shortpathA, TRUE, &full_name ))
860 ret = strlen( full_name.short_name );
861 lstrcpynAtoW( longpath, full_name.long_name, longlen );
863 HeapFree( GetProcessHeap(), 0, shortpathA );
864 return ret;
868 /***********************************************************************
869 * DOSFS_DoGetFullPathName
871 * Implementation of GetFullPathName32A/W.
873 static DWORD DOSFS_DoGetFullPathName( LPCSTR name, DWORD len, LPSTR result,
874 BOOL32 unicode )
876 char buffer[MAX_PATHNAME_LEN];
877 int drive;
878 char *p;
880 TRACE(dosfs, "converting %s\n", name );
882 if (!name || !result) return 0;
884 if ((drive = DOSFS_GetPathDrive( &name )) == -1) return 0;
885 p = buffer;
886 *p++ = 'A' + drive;
887 *p++ = ':';
888 if (IS_END_OF_NAME(*name) && (*name)) /* Absolute path */
890 while ((*name == '\\') || (*name == '/')) name++;
892 else /* Relative path or empty path */
894 *p++ = '\\';
895 lstrcpyn32A( p, DRIVE_GetDosCwd(drive), sizeof(buffer) - 3 );
896 if (*p) p += strlen(p); else p--;
898 if (!*name) /* empty path */
899 *p++ = '\\';
900 *p = '\0';
902 while (*name)
904 if (*name == '.')
906 if (IS_END_OF_NAME(name[1]))
908 name++;
909 while ((*name == '\\') || (*name == '/')) name++;
910 continue;
912 else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
914 name += 2;
915 while ((*name == '\\') || (*name == '/')) name++;
916 while ((p > buffer + 2) && (*p != '\\')) p--;
917 *p = '\0'; /* Remove trailing separator */
918 continue;
921 if (p >= buffer + sizeof(buffer) - 1)
923 DOS_ERROR( ER_PathNotFound, EC_NotFound, SA_Abort, EL_Disk );
924 return 0;
926 *p++ = '\\';
927 while (!IS_END_OF_NAME(*name) && (p < buffer + sizeof(buffer) - 1))
928 *p++ = *name++;
929 *p = '\0';
930 while ((*name == '\\') || (*name == '/')) name++;
933 if (!buffer[2])
935 buffer[2] = '\\';
936 buffer[3] = '\0';
938 if (!(DRIVE_GetFlags(drive) & DRIVE_CASE_PRESERVING))
939 CharUpper32A( buffer );
941 if (unicode) lstrcpynAtoW( (LPWSTR)result, buffer, len );
942 else lstrcpyn32A( result, buffer, len );
944 TRACE(dosfs, "returning %s\n", buffer );
945 return strlen(buffer);
949 /***********************************************************************
950 * GetFullPathName32A (KERNEL32.272)
952 DWORD WINAPI GetFullPathName32A( LPCSTR name, DWORD len, LPSTR buffer,
953 LPSTR *lastpart )
955 DWORD ret = DOSFS_DoGetFullPathName( name, len, buffer, FALSE );
956 if (ret && lastpart)
958 LPSTR p = buffer + strlen(buffer);
959 while ((p > buffer + 2) && (*p != '\\')) p--;
960 *lastpart = p + 1;
962 return ret;
966 /***********************************************************************
967 * GetFullPathName32W (KERNEL32.273)
969 DWORD WINAPI GetFullPathName32W( LPCWSTR name, DWORD len, LPWSTR buffer,
970 LPWSTR *lastpart )
972 LPSTR nameA = HEAP_strdupWtoA( GetProcessHeap(), 0, name );
973 DWORD ret = DOSFS_DoGetFullPathName( nameA, len, (LPSTR)buffer, TRUE );
974 HeapFree( GetProcessHeap(), 0, nameA );
975 if (ret && lastpart)
977 LPWSTR p = buffer + lstrlen32W(buffer);
978 while ((p > buffer + 2) && (*p != '\\')) p--;
979 *lastpart = p + 1;
981 return ret;
985 /***********************************************************************
986 * DOSFS_FindNext
988 * Find the next matching file. Return the number of entries read to find
989 * the matching one, or 0 if no more entries.
990 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
991 * file name mask. Either or both can be NULL.
993 int DOSFS_FindNext( const char *path, const char *short_mask,
994 const char *long_mask, int drive, BYTE attr,
995 int skip, WIN32_FIND_DATA32A *entry )
997 static DOS_DIR *dir = NULL;
998 int count = 0;
999 static char buffer[MAX_PATHNAME_LEN];
1000 static int cur_pos = 0;
1001 static int drive_root = 0;
1002 char *p;
1003 char dos_name[13];
1004 LPCSTR long_name, short_name;
1005 UINT32 flags;
1006 BY_HANDLE_FILE_INFORMATION info;
1008 if ((attr & ~(FA_UNUSED | FA_ARCHIVE | FA_RDONLY)) == FA_LABEL)
1010 if (skip) return 0;
1011 entry->dwFileAttributes = FILE_ATTRIBUTE_LABEL;
1012 DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftCreationTime, 0 );
1013 DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftLastAccessTime, 0 );
1014 DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftLastWriteTime, 0 );
1015 entry->nFileSizeHigh = 0;
1016 entry->nFileSizeLow = 0;
1017 entry->dwReserved0 = 0;
1018 entry->dwReserved1 = 0;
1019 DOSFS_ToDosDTAFormat( DRIVE_GetLabel( drive ), entry->cFileName );
1020 strcpy( entry->cAlternateFileName, entry->cFileName );
1021 return 1;
1024 /* Check the cached directory */
1025 if (dir && !strcmp( buffer, path ) && (cur_pos <= skip)) skip -= cur_pos;
1026 else /* Not in the cache, open it anew */
1028 const char *drive_path;
1029 TRACE(dosfs, "cache miss, path=%s skip=%d buf=%s cur=%d\n",
1030 path, skip, buffer, cur_pos );
1031 cur_pos = skip;
1032 if (dir) DOSFS_CloseDir(dir);
1033 if (!*path) path = "/";
1034 if (!(dir = DOSFS_OpenDir(path))) return 0;
1035 drive_path = path + strlen(DRIVE_GetRoot(drive));
1036 while ((*drive_path == '/') || (*drive_path == '\\')) drive_path++;
1037 drive_root = !*drive_path;
1038 TRACE(dosfs, "drive_root = %d\n", drive_root);
1039 lstrcpyn32A( buffer, path, sizeof(buffer) - 1 );
1041 strcat( buffer, "/" );
1042 p = buffer + strlen(buffer);
1043 attr |= FA_UNUSED | FA_ARCHIVE | FA_RDONLY;
1044 flags = DRIVE_GetFlags( drive );
1046 while (DOSFS_ReadDir( dir, &long_name, &short_name ))
1048 if (skip-- > 0) continue;
1049 count++;
1051 /* Don't return '.' and '..' in the root of the drive */
1052 if (drive_root && (long_name[0] == '.') &&
1053 (!long_name[1] || ((long_name[1] == '.') && !long_name[2])))
1054 continue;
1056 /* Check the long mask */
1058 if (long_mask)
1060 if (!DOSFS_MatchLong( long_mask, long_name,
1061 flags & DRIVE_CASE_SENSITIVE )) continue;
1064 /* Check the short mask */
1066 if (short_mask)
1068 if (!short_name)
1070 DOSFS_Hash( long_name, dos_name, TRUE,
1071 !(flags & DRIVE_CASE_SENSITIVE) );
1072 short_name = dos_name;
1074 if (!DOSFS_MatchShort( short_mask, short_name )) continue;
1077 /* Check the file attributes */
1079 lstrcpyn32A( p, long_name, sizeof(buffer) - (int)(p - buffer) );
1080 if (!FILE_Stat( buffer, &info ))
1082 WARN(dosfs, "can't stat %s\n", buffer);
1083 continue;
1085 if (info.dwFileAttributes & ~attr) continue;
1087 /* We now have a matching entry; fill the result and return */
1089 entry->dwFileAttributes = info.dwFileAttributes;
1090 entry->ftCreationTime = info.ftCreationTime;
1091 entry->ftLastAccessTime = info.ftLastAccessTime;
1092 entry->ftLastWriteTime = info.ftLastWriteTime;
1093 entry->nFileSizeHigh = info.nFileSizeHigh;
1094 entry->nFileSizeLow = info.nFileSizeLow;
1096 if (short_name)
1097 DOSFS_ToDosDTAFormat( short_name, entry->cAlternateFileName );
1098 else
1099 DOSFS_Hash( long_name, entry->cAlternateFileName, FALSE,
1100 !(flags & DRIVE_CASE_SENSITIVE) );
1102 lstrcpyn32A( entry->cFileName, long_name, sizeof(entry->cFileName) );
1103 if (!(flags & DRIVE_CASE_PRESERVING)) CharLower32A( entry->cFileName );
1104 TRACE(dosfs, "returning %s (%s) %02lx %ld\n",
1105 entry->cFileName, entry->cAlternateFileName,
1106 entry->dwFileAttributes, entry->nFileSizeLow );
1107 cur_pos += count;
1108 p[-1] = '\0'; /* Remove trailing slash in buffer */
1109 return count;
1111 DOSFS_CloseDir( dir );
1112 dir = NULL;
1113 return 0; /* End of directory */
1117 /*************************************************************************
1118 * FindFirstFile16 (KERNEL.413)
1120 HANDLE16 WINAPI FindFirstFile16( LPCSTR path, WIN32_FIND_DATA32A *data )
1122 DOS_FULL_NAME full_name;
1123 HGLOBAL16 handle;
1124 FIND_FIRST_INFO *info;
1126 if (!path) return 0;
1127 if (!DOSFS_GetFullName( path, FALSE, &full_name ))
1128 return INVALID_HANDLE_VALUE16;
1129 if (!(handle = GlobalAlloc16( GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO) )))
1130 return INVALID_HANDLE_VALUE16;
1131 info = (FIND_FIRST_INFO *)GlobalLock16( handle );
1132 info->path = HEAP_strdupA( SystemHeap, 0, full_name.long_name );
1133 info->mask = strrchr( info->path, '/' );
1134 *(info->mask++) = '\0';
1135 if (path[0] && (path[1] == ':')) info->drive = toupper(*path) - 'A';
1136 else info->drive = DRIVE_GetCurrentDrive();
1137 info->skip = 0;
1138 GlobalUnlock16( handle );
1139 if (!FindNextFile16( handle, data ))
1141 FindClose16( handle );
1142 DOS_ERROR( ER_NoMoreFiles, EC_MediaError, SA_Abort, EL_Disk );
1143 return INVALID_HANDLE_VALUE16;
1145 return handle;
1149 /*************************************************************************
1150 * FindFirstFile32A (KERNEL32.123)
1152 HANDLE32 WINAPI FindFirstFile32A( LPCSTR path, WIN32_FIND_DATA32A *data )
1154 HANDLE32 handle = FindFirstFile16( path, data );
1155 if (handle == INVALID_HANDLE_VALUE16) return INVALID_HANDLE_VALUE32;
1156 return handle;
1160 /*************************************************************************
1161 * FindFirstFile32W (KERNEL32.124)
1163 HANDLE32 WINAPI FindFirstFile32W( LPCWSTR path, WIN32_FIND_DATA32W *data )
1165 WIN32_FIND_DATA32A dataA;
1166 LPSTR pathA = HEAP_strdupWtoA( GetProcessHeap(), 0, path );
1167 HANDLE32 handle = FindFirstFile32A( pathA, &dataA );
1168 HeapFree( GetProcessHeap(), 0, pathA );
1169 if (handle != INVALID_HANDLE_VALUE32)
1171 data->dwFileAttributes = dataA.dwFileAttributes;
1172 data->ftCreationTime = dataA.ftCreationTime;
1173 data->ftLastAccessTime = dataA.ftLastAccessTime;
1174 data->ftLastWriteTime = dataA.ftLastWriteTime;
1175 data->nFileSizeHigh = dataA.nFileSizeHigh;
1176 data->nFileSizeLow = dataA.nFileSizeLow;
1177 lstrcpyAtoW( data->cFileName, dataA.cFileName );
1178 lstrcpyAtoW( data->cAlternateFileName, dataA.cAlternateFileName );
1180 return handle;
1184 /*************************************************************************
1185 * FindNextFile16 (KERNEL.414)
1187 BOOL16 WINAPI FindNextFile16( HANDLE16 handle, WIN32_FIND_DATA32A *data )
1189 FIND_FIRST_INFO *info;
1190 int count;
1192 if (!(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
1194 DOS_ERROR( ER_InvalidHandle, EC_ProgramError, SA_Abort, EL_Disk );
1195 return FALSE;
1197 GlobalUnlock16( handle );
1198 if (!info->path)
1200 DOS_ERROR( ER_NoMoreFiles, EC_MediaError, SA_Abort, EL_Disk );
1201 return FALSE;
1203 if (!(count = DOSFS_FindNext( info->path, NULL, info->mask, info->drive,
1204 0xff, info->skip, data )))
1206 HeapFree( SystemHeap, 0, info->path );
1207 info->path = info->mask = NULL;
1208 DOS_ERROR( ER_NoMoreFiles, EC_MediaError, SA_Abort, EL_Disk );
1209 return FALSE;
1211 info->skip += count;
1212 return TRUE;
1216 /*************************************************************************
1217 * FindNextFile32A (KERNEL32.126)
1219 BOOL32 WINAPI FindNextFile32A( HANDLE32 handle, WIN32_FIND_DATA32A *data )
1221 return FindNextFile16( handle, data );
1225 /*************************************************************************
1226 * FindNextFile32W (KERNEL32.127)
1228 BOOL32 WINAPI FindNextFile32W( HANDLE32 handle, WIN32_FIND_DATA32W *data )
1230 WIN32_FIND_DATA32A dataA;
1231 if (!FindNextFile32A( handle, &dataA )) return FALSE;
1232 data->dwFileAttributes = dataA.dwFileAttributes;
1233 data->ftCreationTime = dataA.ftCreationTime;
1234 data->ftLastAccessTime = dataA.ftLastAccessTime;
1235 data->ftLastWriteTime = dataA.ftLastWriteTime;
1236 data->nFileSizeHigh = dataA.nFileSizeHigh;
1237 data->nFileSizeLow = dataA.nFileSizeLow;
1238 lstrcpyAtoW( data->cFileName, dataA.cFileName );
1239 lstrcpyAtoW( data->cAlternateFileName, dataA.cAlternateFileName );
1240 return TRUE;
1244 /*************************************************************************
1245 * FindClose16 (KERNEL.415)
1247 BOOL16 WINAPI FindClose16( HANDLE16 handle )
1249 FIND_FIRST_INFO *info;
1251 if ((handle == INVALID_HANDLE_VALUE16) ||
1252 !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
1254 DOS_ERROR( ER_InvalidHandle, EC_ProgramError, SA_Abort, EL_Disk );
1255 return FALSE;
1257 if (info->path) HeapFree( SystemHeap, 0, info->path );
1258 GlobalUnlock16( handle );
1259 GlobalFree16( handle );
1260 return TRUE;
1264 /*************************************************************************
1265 * FindClose32 (KERNEL32.119)
1267 BOOL32 WINAPI FindClose32( HANDLE32 handle )
1269 return FindClose16( (HANDLE16)handle );
1273 /***********************************************************************
1274 * DOSFS_UnixTimeToFileTime
1276 * Convert a Unix time to FILETIME format.
1277 * The FILETIME structure is a 64-bit value representing the number of
1278 * 100-nanosecond intervals since January 1, 1601, 0:00.
1279 * 'remainder' is the nonnegative number of 100-ns intervals
1280 * corresponding to the time fraction smaller than 1 second that
1281 * couldn't be stored in the time_t value.
1283 void DOSFS_UnixTimeToFileTime( time_t unix_time, FILETIME *filetime,
1284 DWORD remainder )
1286 /* NOTES:
1288 CONSTANTS:
1289 The time difference between 1 January 1601, 00:00:00 and
1290 1 January 1970, 00:00:00 is 369 years, plus the leap years
1291 from 1604 to 1968, excluding 1700, 1800, 1900.
1292 This makes (1968 - 1600) / 4 - 3 = 89 leap days, and a total
1293 of 134774 days.
1295 Any day in that period had 24 * 60 * 60 = 86400 seconds.
1297 The time difference is 134774 * 86400 * 10000000, which can be written
1298 116444736000000000
1299 27111902 * 2^32 + 3577643008
1300 413 * 2^48 + 45534 * 2^32 + 54590 * 2^16 + 32768
1302 If you find that these constants are buggy, please change them in all
1303 instances in both conversion functions.
1305 VERSIONS:
1306 There are two versions, one of them uses long long variables and
1307 is presumably faster but not ISO C. The other one uses standard C
1308 data types and operations but relies on the assumption that negative
1309 numbers are stored as 2's complement (-1 is 0xffff....). If this
1310 assumption is violated, dates before 1970 will not convert correctly.
1311 This should however work on any reasonable architecture where WINE
1312 will run.
1314 DETAILS:
1316 Take care not to remove the casts. I have tested these functions
1317 (in both versions) for a lot of numbers. I would be interested in
1318 results on other compilers than GCC.
1320 The operations have been designed to account for the possibility
1321 of 64-bit time_t in future UNICES. Even the versions without
1322 internal long long numbers will work if time_t only is 64 bit.
1323 A 32-bit shift, which was necessary for that operation, turned out
1324 not to work correctly in GCC, besides giving the warning. So I
1325 used a double 16-bit shift instead. Numbers are in the ISO version
1326 represented by three limbs, the most significant with 32 bit, the
1327 other two with 16 bit each.
1329 As the modulo-operator % is not well-defined for negative numbers,
1330 negative divisors have been avoided in DOSFS_FileTimeToUnixTime.
1332 There might be quicker ways to do this in C. Certainly so in
1333 assembler.
1335 Claus Fischer, fischer@iue.tuwien.ac.at
1338 #if __GNUC__
1339 # define USE_LONG_LONG 1
1340 #else
1341 # define USE_LONG_LONG 0
1342 #endif
1344 #if USE_LONG_LONG /* gcc supports long long type */
1346 long long int t = unix_time;
1347 t *= 10000000;
1348 t += 116444736000000000LL;
1349 t += remainder;
1350 filetime->dwLowDateTime = (UINT32)t;
1351 filetime->dwHighDateTime = (UINT32)(t >> 32);
1353 #else /* ISO version */
1355 UINT32 a0; /* 16 bit, low bits */
1356 UINT32 a1; /* 16 bit, medium bits */
1357 UINT32 a2; /* 32 bit, high bits */
1359 /* Copy the unix time to a2/a1/a0 */
1360 a0 = unix_time & 0xffff;
1361 a1 = (unix_time >> 16) & 0xffff;
1362 /* This is obsolete if unix_time is only 32 bits, but it does not hurt.
1363 Do not replace this by >> 32, it gives a compiler warning and it does
1364 not work. */
1365 a2 = (unix_time >= 0 ? (unix_time >> 16) >> 16 :
1366 ~((~unix_time >> 16) >> 16));
1368 /* Multiply a by 10000000 (a = a2/a1/a0)
1369 Split the factor into 10000 * 1000 which are both less than 0xffff. */
1370 a0 *= 10000;
1371 a1 = a1 * 10000 + (a0 >> 16);
1372 a2 = a2 * 10000 + (a1 >> 16);
1373 a0 &= 0xffff;
1374 a1 &= 0xffff;
1376 a0 *= 1000;
1377 a1 = a1 * 1000 + (a0 >> 16);
1378 a2 = a2 * 1000 + (a1 >> 16);
1379 a0 &= 0xffff;
1380 a1 &= 0xffff;
1382 /* Add the time difference and the remainder */
1383 a0 += 32768 + (remainder & 0xffff);
1384 a1 += 54590 + (remainder >> 16 ) + (a0 >> 16);
1385 a2 += 27111902 + (a1 >> 16);
1386 a0 &= 0xffff;
1387 a1 &= 0xffff;
1389 /* Set filetime */
1390 filetime->dwLowDateTime = (a1 << 16) + a0;
1391 filetime->dwHighDateTime = a2;
1392 #endif
1396 /***********************************************************************
1397 * DOSFS_FileTimeToUnixTime
1399 * Convert a FILETIME format to Unix time.
1400 * If not NULL, 'remainder' contains the fractional part of the filetime,
1401 * in the range of [0..9999999] (even if time_t is negative).
1403 time_t DOSFS_FileTimeToUnixTime( const FILETIME *filetime, DWORD *remainder )
1405 /* Read the comment in the function DOSFS_UnixTimeToFileTime. */
1406 #if USE_LONG_LONG
1408 long long int t = filetime->dwHighDateTime;
1409 t <<= 32;
1410 t += (UINT32)filetime->dwLowDateTime;
1411 t -= 116444736000000000LL;
1412 if (t < 0)
1414 if (remainder) *remainder = 9999999 - (-t - 1) % 10000000;
1415 return -1 - ((-t - 1) / 10000000);
1417 else
1419 if (remainder) *remainder = t % 10000000;
1420 return t / 10000000;
1423 #else /* ISO version */
1425 UINT32 a0; /* 16 bit, low bits */
1426 UINT32 a1; /* 16 bit, medium bits */
1427 UINT32 a2; /* 32 bit, high bits */
1428 UINT32 r; /* remainder of division */
1429 unsigned int carry; /* carry bit for subtraction */
1430 int negative; /* whether a represents a negative value */
1432 /* Copy the time values to a2/a1/a0 */
1433 a2 = (UINT32)filetime->dwHighDateTime;
1434 a1 = ((UINT32)filetime->dwLowDateTime ) >> 16;
1435 a0 = ((UINT32)filetime->dwLowDateTime ) & 0xffff;
1437 /* Subtract the time difference */
1438 if (a0 >= 32768 ) a0 -= 32768 , carry = 0;
1439 else a0 += (1 << 16) - 32768 , carry = 1;
1441 if (a1 >= 54590 + carry) a1 -= 54590 + carry, carry = 0;
1442 else a1 += (1 << 16) - 54590 - carry, carry = 1;
1444 a2 -= 27111902 + carry;
1446 /* If a is negative, replace a by (-1-a) */
1447 negative = (a2 >= ((UINT32)1) << 31);
1448 if (negative)
1450 /* Set a to -a - 1 (a is a2/a1/a0) */
1451 a0 = 0xffff - a0;
1452 a1 = 0xffff - a1;
1453 a2 = ~a2;
1456 /* Divide a by 10000000 (a = a2/a1/a0), put the rest into r.
1457 Split the divisor into 10000 * 1000 which are both less than 0xffff. */
1458 a1 += (a2 % 10000) << 16;
1459 a2 /= 10000;
1460 a0 += (a1 % 10000) << 16;
1461 a1 /= 10000;
1462 r = a0 % 10000;
1463 a0 /= 10000;
1465 a1 += (a2 % 1000) << 16;
1466 a2 /= 1000;
1467 a0 += (a1 % 1000) << 16;
1468 a1 /= 1000;
1469 r += (a0 % 1000) * 10000;
1470 a0 /= 1000;
1472 /* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
1473 if (negative)
1475 /* Set a to -a - 1 (a is a2/a1/a0) */
1476 a0 = 0xffff - a0;
1477 a1 = 0xffff - a1;
1478 a2 = ~a2;
1480 r = 9999999 - r;
1483 if (remainder) *remainder = r;
1485 /* Do not replace this by << 32, it gives a compiler warning and it does
1486 not work. */
1487 return ((((time_t)a2) << 16) << 16) + (a1 << 16) + a0;
1488 #endif
1492 /***********************************************************************
1493 * DosDateTimeToFileTime (KERNEL32.76)
1495 BOOL32 WINAPI DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft)
1497 struct tm newtm;
1499 newtm.tm_sec = (fattime & 0x1f) * 2;
1500 newtm.tm_min = (fattime >> 5) & 0x3f;
1501 newtm.tm_hour = (fattime >> 11);
1502 newtm.tm_mday = (fatdate & 0x1f);
1503 newtm.tm_mon = ((fatdate >> 5) & 0x0f) - 1;
1504 newtm.tm_year = (fatdate >> 9) + 80;
1505 DOSFS_UnixTimeToFileTime( mktime( &newtm ), ft, 0 );
1506 return TRUE;
1510 /***********************************************************************
1511 * FileTimeToDosDateTime (KERNEL32.111)
1513 BOOL32 WINAPI FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate,
1514 LPWORD fattime )
1516 time_t unixtime = DOSFS_FileTimeToUnixTime( ft, NULL );
1517 struct tm *tm = localtime( &unixtime );
1518 if (fattime)
1519 *fattime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
1520 if (fatdate)
1521 *fatdate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
1522 + tm->tm_mday;
1523 return TRUE;
1527 /***********************************************************************
1528 * LocalFileTimeToFileTime (KERNEL32.373)
1530 BOOL32 WINAPI LocalFileTimeToFileTime( const FILETIME *localft,
1531 LPFILETIME utcft )
1533 struct tm *xtm;
1534 DWORD remainder;
1536 /* convert from local to UTC. Perhaps not correct. FIXME */
1537 time_t unixtime = DOSFS_FileTimeToUnixTime( localft, &remainder );
1538 xtm = gmtime( &unixtime );
1539 DOSFS_UnixTimeToFileTime( mktime(xtm), utcft, remainder );
1540 return TRUE;
1544 /***********************************************************************
1545 * FileTimeToLocalFileTime (KERNEL32.112)
1547 BOOL32 WINAPI FileTimeToLocalFileTime( const FILETIME *utcft,
1548 LPFILETIME localft )
1550 DWORD remainder;
1551 /* convert from UTC to local. Perhaps not correct. FIXME */
1552 time_t unixtime = DOSFS_FileTimeToUnixTime( utcft, &remainder );
1553 #ifdef HAVE_TIMEGM
1554 struct tm *xtm = localtime( &unixtime );
1555 time_t localtime;
1557 localtime = timegm(xtm);
1558 DOSFS_UnixTimeToFileTime( localtime, localft, remainder );
1560 #else
1561 struct tm *xtm,*gtm;
1562 time_t time1,time2;
1564 xtm = localtime( &unixtime );
1565 gtm = gmtime( &unixtime );
1566 time1 = mktime(xtm);
1567 time2 = mktime(gtm);
1568 DOSFS_UnixTimeToFileTime( 2*time1-time2, localft, remainder );
1569 #endif
1570 return TRUE;
1574 /***********************************************************************
1575 * FileTimeToSystemTime (KERNEL32.113)
1577 BOOL32 WINAPI FileTimeToSystemTime( const FILETIME *ft, LPSYSTEMTIME syst )
1579 struct tm *xtm;
1580 DWORD remainder;
1581 time_t xtime = DOSFS_FileTimeToUnixTime( ft, &remainder );
1582 xtm = gmtime(&xtime);
1583 syst->wYear = xtm->tm_year+1900;
1584 syst->wMonth = xtm->tm_mon + 1;
1585 syst->wDayOfWeek = xtm->tm_wday;
1586 syst->wDay = xtm->tm_mday;
1587 syst->wHour = xtm->tm_hour;
1588 syst->wMinute = xtm->tm_min;
1589 syst->wSecond = xtm->tm_sec;
1590 syst->wMilliseconds = remainder / 10000;
1591 return TRUE;
1594 /***********************************************************************
1595 * QueryDosDeviceA (KERNEL32.413)
1597 * returns array of strings terminated by \0, terminated by \0
1599 DWORD WINAPI QueryDosDevice32A(LPCSTR devname,LPSTR target,DWORD bufsize)
1601 LPSTR s;
1602 char buffer[200];
1604 TRACE(dosfs,"(%s,...)\n",devname?devname:"<null>");
1605 if (!devname) {
1606 /* return known MSDOS devices */
1607 lstrcpy32A(buffer,"CON COM1 COM2 LPT1 NUL ");
1608 while ((s=strchr(buffer,' ')))
1609 *s='\0';
1611 lstrcpyn32A(target,buffer,bufsize);
1612 return strlen(buffer);
1614 lstrcpy32A(buffer,"\\DEV\\");
1615 lstrcat32A(buffer,devname);
1616 if ((s=strchr(buffer,':'))) *s='\0';
1617 lstrcpyn32A(target,buffer,bufsize);
1618 return strlen(buffer);
1622 /***********************************************************************
1623 * QueryDosDeviceW (KERNEL32.414)
1625 * returns array of strings terminated by \0, terminated by \0
1627 DWORD WINAPI QueryDosDevice32W(LPCWSTR devname,LPWSTR target,DWORD bufsize)
1629 LPSTR devnameA = devname?HEAP_strdupWtoA(GetProcessHeap(),0,devname):NULL;
1630 LPSTR targetA = (LPSTR)HEAP_xalloc(GetProcessHeap(),0,bufsize);
1631 DWORD ret = QueryDosDevice32A(devnameA,targetA,bufsize);
1633 lstrcpynAtoW(target,targetA,bufsize);
1634 if (devnameA) HeapFree(GetProcessHeap(),0,devnameA);
1635 if (targetA) HeapFree(GetProcessHeap(),0,targetA);
1636 return ret;
1640 /***********************************************************************
1641 * SystemTimeToFileTime (KERNEL32.526)
1643 BOOL32 WINAPI SystemTimeToFileTime( const SYSTEMTIME *syst, LPFILETIME ft )
1645 #ifdef HAVE_TIMEGM
1646 struct tm xtm;
1647 time_t utctime;
1648 #else
1649 struct tm xtm,*local_tm,*utc_tm;
1650 time_t localtim,utctime;
1651 #endif
1653 xtm.tm_year = syst->wYear-1900;
1654 xtm.tm_mon = syst->wMonth - 1;
1655 xtm.tm_wday = syst->wDayOfWeek;
1656 xtm.tm_mday = syst->wDay;
1657 xtm.tm_hour = syst->wHour;
1658 xtm.tm_min = syst->wMinute;
1659 xtm.tm_sec = syst->wSecond; /* this is UTC */
1660 xtm.tm_isdst = -1;
1661 #ifdef HAVE_TIMEGM
1662 utctime = timegm(&xtm);
1663 DOSFS_UnixTimeToFileTime( utctime, ft,
1664 syst->wMilliseconds * 10000 );
1665 #else
1666 localtim = mktime(&xtm); /* now we've got local time */
1667 local_tm = localtime(&localtim);
1668 utc_tm = gmtime(&localtim);
1669 utctime = mktime(utc_tm);
1670 DOSFS_UnixTimeToFileTime( 2*localtim -utctime, ft,
1671 syst->wMilliseconds * 10000 );
1672 #endif
1673 return TRUE;