Fixed error messages.
[wine/dcerpc.git] / files / dos_fs.c
blob47d86e2655563bae0cb621e626afcb0f9c8a9006
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 "server.h"
30 #include "process.h"
31 #include "debug.h"
33 /* Define the VFAT ioctl to get both short and long file names */
34 /* FIXME: is it possible to get this to work on other systems? */
35 #ifdef linux
36 #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, long)
37 /* We want the real kernel dirent structure, not the libc one */
38 typedef struct
40 long d_ino;
41 long d_off;
42 unsigned short d_reclen;
43 char d_name[256];
44 } KERNEL_DIRENT;
46 #else /* linux */
47 #undef VFAT_IOCTL_READDIR_BOTH /* just in case... */
48 #endif /* linux */
50 /* Chars we don't want to see in DOS file names */
51 #define INVALID_DOS_CHARS "*?<>|\"+=,;[] \345"
53 static const DOS_DEVICE DOSFS_Devices[] =
54 /* name, device flags (see Int 21/AX=0x4400) */
56 { "CON", 0xc0d3 },
57 { "PRN", 0xa0c0 },
58 { "NUL", 0x80c4 },
59 { "AUX", 0x80c0 },
60 { "LPT1", 0xa0c0 },
61 { "LPT2", 0xa0c0 },
62 { "LPT3", 0xa0c0 },
63 { "LPT4", 0xc0d3 },
64 { "COM1", 0x80c0 },
65 { "COM2", 0x80c0 },
66 { "COM3", 0x80c0 },
67 { "COM4", 0x80c0 },
68 { "SCSIMGR$", 0xc0c0 },
69 { "HPSCAN", 0xc0c0 }
72 #define GET_DRIVE(path) \
73 (((path)[1] == ':') ? toupper((path)[0]) - 'A' : DOSFS_CurDrive)
75 /* DOS extended error status */
76 WORD DOS_ExtendedError;
77 BYTE DOS_ErrorClass;
78 BYTE DOS_ErrorAction;
79 BYTE DOS_ErrorLocus;
81 /* Directory info for DOSFS_ReadDir */
82 typedef struct
84 DIR *dir;
85 #ifdef VFAT_IOCTL_READDIR_BOTH
86 int fd;
87 char short_name[12];
88 KERNEL_DIRENT dirent[2];
89 #endif
90 } DOS_DIR;
92 /* Info structure for FindFirstFile handle */
93 typedef struct
95 LPSTR path;
96 LPSTR long_mask;
97 LPSTR short_mask;
98 BYTE attr;
99 int drive;
100 int cur_pos;
101 DOS_DIR *dir;
102 } FIND_FIRST_INFO;
106 /***********************************************************************
107 * DOSFS_ValidDOSName
109 * Return 1 if Unix file 'name' is also a valid MS-DOS name
110 * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
111 * File name can be terminated by '\0', '\\' or '/'.
113 static int DOSFS_ValidDOSName( const char *name, int ignore_case )
115 static const char invalid_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS;
116 const char *p = name;
117 const char *invalid = ignore_case ? (invalid_chars + 26) : invalid_chars;
118 int len = 0;
120 if (*p == '.')
122 /* Check for "." and ".." */
123 p++;
124 if (*p == '.') p++;
125 /* All other names beginning with '.' are invalid */
126 return (IS_END_OF_NAME(*p));
128 while (!IS_END_OF_NAME(*p))
130 if (strchr( invalid, *p )) return 0; /* Invalid char */
131 if (*p == '.') break; /* Start of the extension */
132 if (++len > 8) return 0; /* Name too long */
133 p++;
135 if (*p != '.') return 1; /* End of name */
136 p++;
137 if (IS_END_OF_NAME(*p)) return 0; /* Empty extension not allowed */
138 len = 0;
139 while (!IS_END_OF_NAME(*p))
141 if (strchr( invalid, *p )) return 0; /* Invalid char */
142 if (*p == '.') return 0; /* Second extension not allowed */
143 if (++len > 3) return 0; /* Extension too long */
144 p++;
146 return 1;
150 /***********************************************************************
151 * DOSFS_ToDosFCBFormat
153 * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
154 * expanding wild cards and converting to upper-case in the process.
155 * File name can be terminated by '\0', '\\' or '/'.
156 * Return FALSE if the name is not a valid DOS name.
157 * 'buffer' must be at least 12 characters long.
159 BOOL32 DOSFS_ToDosFCBFormat( LPCSTR name, LPSTR buffer )
161 static const char invalid_chars[] = INVALID_DOS_CHARS;
162 const char *p = name;
163 int i;
165 /* Check for "." and ".." */
166 if (*p == '.')
168 p++;
169 strcpy( buffer, ". " );
170 if (*p == '.')
172 buffer[1] = '.';
173 p++;
175 return (!*p || (*p == '/') || (*p == '\\'));
178 for (i = 0; i < 8; i++)
180 switch(*p)
182 case '\0':
183 case '\\':
184 case '/':
185 case '.':
186 buffer[i] = ' ';
187 break;
188 case '?':
189 p++;
190 /* fall through */
191 case '*':
192 buffer[i] = '?';
193 break;
194 default:
195 if (strchr( invalid_chars, *p )) return FALSE;
196 buffer[i] = toupper(*p);
197 p++;
198 break;
202 if (*p == '*')
204 /* Skip all chars after wildcard up to first dot */
205 while (*p && (*p != '/') && (*p != '\\') && (*p != '.')) p++;
207 else
209 /* Check if name too long */
210 if (*p && (*p != '/') && (*p != '\\') && (*p != '.')) return FALSE;
212 if (*p == '.') p++; /* Skip dot */
214 for (i = 8; i < 11; i++)
216 switch(*p)
218 case '\0':
219 case '\\':
220 case '/':
221 buffer[i] = ' ';
222 break;
223 case '.':
224 return FALSE; /* Second extension not allowed */
225 case '?':
226 p++;
227 /* fall through */
228 case '*':
229 buffer[i] = '?';
230 break;
231 default:
232 if (strchr( invalid_chars, *p )) return FALSE;
233 buffer[i] = toupper(*p);
234 p++;
235 break;
238 buffer[11] = '\0';
239 return TRUE;
243 /***********************************************************************
244 * DOSFS_ToDosDTAFormat
246 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
247 * converting to upper-case in the process.
248 * File name can be terminated by '\0', '\\' or '/'.
249 * 'buffer' must be at least 13 characters long.
251 static void DOSFS_ToDosDTAFormat( LPCSTR name, LPSTR buffer )
253 char *p;
255 memcpy( buffer, name, 8 );
256 for (p = buffer + 8; (p > buffer) && (p[-1] == ' '); p--);
257 *p++ = '.';
258 memcpy( p, name + 8, 3 );
259 for (p += 3; p[-1] == ' '; p--);
260 if (p[-1] == '.') p--;
261 *p = '\0';
265 /***********************************************************************
266 * DOSFS_MatchShort
268 * Check a DOS file name against a mask (both in FCB format).
270 static int DOSFS_MatchShort( const char *mask, const char *name )
272 int i;
273 for (i = 11; i > 0; i--, mask++, name++)
274 if ((*mask != '?') && (*mask != *name)) return 0;
275 return 1;
279 /***********************************************************************
280 * DOSFS_MatchLong
282 * Check a long file name against a mask.
284 static int DOSFS_MatchLong( const char *mask, const char *name,
285 int case_sensitive )
287 if (!strcmp( mask, "*.*" )) return 1;
288 while (*name && *mask)
290 if (*mask == '*')
292 mask++;
293 while (*mask == '*') mask++; /* Skip consecutive '*' */
294 if (!*mask) return 1;
295 if (case_sensitive) while (*name && (*name != *mask)) name++;
296 else while (*name && (toupper(*name) != toupper(*mask))) name++;
297 if (!*name) return 0;
299 else if (*mask != '?')
301 if (case_sensitive)
303 if (*mask != *name) return 0;
305 else if (toupper(*mask) != toupper(*name)) return 0;
307 mask++;
308 name++;
310 return (!*name && !*mask);
314 /***********************************************************************
315 * DOSFS_OpenDir
317 static DOS_DIR *DOSFS_OpenDir( LPCSTR path )
319 DOS_DIR *dir = HeapAlloc( SystemHeap, 0, sizeof(*dir) );
320 if (!dir)
322 DOS_ERROR( ER_OutOfMemory, EC_OutOfResource, SA_Abort, EL_Memory );
323 return NULL;
326 /* Treat empty path as root directory. This simplifies path split into
327 directory and mask in several other places */
328 if (!*path) path = "/";
330 #ifdef VFAT_IOCTL_READDIR_BOTH
332 /* Check if the VFAT ioctl is supported on this directory */
334 if ((dir->fd = open( path, O_RDONLY )) != -1)
336 if (ioctl( dir->fd, VFAT_IOCTL_READDIR_BOTH, (long)dir->dirent ) == -1)
338 close( dir->fd );
339 dir->fd = -1;
341 else
343 /* Set the file pointer back at the start of the directory */
344 lseek( dir->fd, 0, SEEK_SET );
345 dir->dir = NULL;
346 return dir;
349 #endif /* VFAT_IOCTL_READDIR_BOTH */
351 /* Now use the standard opendir/readdir interface */
353 if (!(dir->dir = opendir( path )))
355 HeapFree( SystemHeap, 0, dir );
356 return NULL;
358 return dir;
362 /***********************************************************************
363 * DOSFS_CloseDir
365 static void DOSFS_CloseDir( DOS_DIR *dir )
367 #ifdef VFAT_IOCTL_READDIR_BOTH
368 if (dir->fd != -1) close( dir->fd );
369 #endif /* VFAT_IOCTL_READDIR_BOTH */
370 if (dir->dir) closedir( dir->dir );
371 HeapFree( SystemHeap, 0, dir );
375 /***********************************************************************
376 * DOSFS_ReadDir
378 static BOOL32 DOSFS_ReadDir( DOS_DIR *dir, LPCSTR *long_name,
379 LPCSTR *short_name )
381 struct dirent *dirent;
383 #ifdef VFAT_IOCTL_READDIR_BOTH
384 if (dir->fd != -1)
386 if (ioctl( dir->fd, VFAT_IOCTL_READDIR_BOTH, (long)dir->dirent ) != -1) {
387 if (!dir->dirent[0].d_reclen) return FALSE;
388 if (!DOSFS_ToDosFCBFormat( dir->dirent[0].d_name, dir->short_name ))
389 dir->short_name[0] = '\0';
390 *short_name = dir->short_name;
391 if (dir->dirent[1].d_name[0]) *long_name = dir->dirent[1].d_name;
392 else *long_name = dir->dirent[0].d_name;
393 return TRUE;
396 #endif /* VFAT_IOCTL_READDIR_BOTH */
398 if (!(dirent = readdir( dir->dir ))) return FALSE;
399 *long_name = dirent->d_name;
400 *short_name = NULL;
401 return TRUE;
405 /***********************************************************************
406 * DOSFS_Hash
408 * Transform a Unix file name into a hashed DOS name. If the name is a valid
409 * DOS name, it is converted to upper-case; otherwise it is replaced by a
410 * hashed version that fits in 8.3 format.
411 * File name can be terminated by '\0', '\\' or '/'.
412 * 'buffer' must be at least 13 characters long.
414 static void DOSFS_Hash( LPCSTR name, LPSTR buffer, BOOL32 dir_format,
415 BOOL32 ignore_case )
417 static const char invalid_chars[] = INVALID_DOS_CHARS "~.";
418 static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
420 const char *p, *ext;
421 char *dst;
422 unsigned short hash;
423 int i;
425 if (dir_format) strcpy( buffer, " " );
427 if (DOSFS_ValidDOSName( name, ignore_case ))
429 /* Check for '.' and '..' */
430 if (*name == '.')
432 buffer[0] = '.';
433 if (!dir_format) buffer[1] = buffer[2] = '\0';
434 if (name[1] == '.') buffer[1] = '.';
435 return;
438 /* Simply copy the name, converting to uppercase */
440 for (dst = buffer; !IS_END_OF_NAME(*name) && (*name != '.'); name++)
441 *dst++ = toupper(*name);
442 if (*name == '.')
444 if (dir_format) dst = buffer + 8;
445 else *dst++ = '.';
446 for (name++; !IS_END_OF_NAME(*name); name++)
447 *dst++ = toupper(*name);
449 if (!dir_format) *dst = '\0';
450 return;
453 /* Compute the hash code of the file name */
454 /* If you know something about hash functions, feel free to */
455 /* insert a better algorithm here... */
456 if (ignore_case)
458 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
459 hash = (hash<<3) ^ (hash>>5) ^ tolower(*p) ^ (tolower(p[1]) << 8);
460 hash = (hash<<3) ^ (hash>>5) ^ tolower(*p); /* Last character*/
462 else
464 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
465 hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
466 hash = (hash << 3) ^ (hash >> 5) ^ *p; /* Last character */
469 /* Find last dot for start of the extension */
470 for (p = name+1, ext = NULL; !IS_END_OF_NAME(*p); p++)
471 if (*p == '.') ext = p;
472 if (ext && IS_END_OF_NAME(ext[1]))
473 ext = NULL; /* Empty extension ignored */
475 /* Copy first 4 chars, replacing invalid chars with '_' */
476 for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
478 if (IS_END_OF_NAME(*p) || (p == ext)) break;
479 *dst++ = strchr( invalid_chars, *p ) ? '_' : toupper(*p);
481 /* Pad to 5 chars with '~' */
482 while (i-- >= 0) *dst++ = '~';
484 /* Insert hash code converted to 3 ASCII chars */
485 *dst++ = hash_chars[(hash >> 10) & 0x1f];
486 *dst++ = hash_chars[(hash >> 5) & 0x1f];
487 *dst++ = hash_chars[hash & 0x1f];
489 /* Copy the first 3 chars of the extension (if any) */
490 if (ext)
492 if (!dir_format) *dst++ = '.';
493 for (i = 3, ext++; (i > 0) && !IS_END_OF_NAME(*ext); i--, ext++)
494 *dst++ = strchr( invalid_chars, *ext ) ? '_' : toupper(*ext);
496 if (!dir_format) *dst = '\0';
500 /***********************************************************************
501 * DOSFS_FindUnixName
503 * Find the Unix file name in a given directory that corresponds to
504 * a file name (either in Unix or DOS format).
505 * File name can be terminated by '\0', '\\' or '/'.
506 * Return TRUE if OK, FALSE if no file name matches.
508 * 'long_buf' must be at least 'long_len' characters long. If the long name
509 * turns out to be larger than that, the function returns FALSE.
510 * 'short_buf' must be at least 13 characters long.
512 BOOL32 DOSFS_FindUnixName( LPCSTR path, LPCSTR name, LPSTR long_buf,
513 INT32 long_len, LPSTR short_buf, BOOL32 ignore_case)
515 DOS_DIR *dir;
516 LPCSTR long_name, short_name;
517 char dos_name[12], tmp_buf[13];
518 BOOL32 ret;
520 const char *p = strchr( name, '/' );
521 int len = p ? (int)(p - name) : strlen(name);
522 if ((p = strchr( name, '\\' ))) len = MIN( (int)(p - name), len );
523 if (long_len < len + 1) return FALSE;
525 TRACE(dosfs, "%s,%s\n", path, name );
527 if (!DOSFS_ToDosFCBFormat( name, dos_name )) dos_name[0] = '\0';
529 if (!(dir = DOSFS_OpenDir( path )))
531 WARN(dosfs, "(%s,%s): can't open dir: %s\n",
532 path, name, strerror(errno) );
533 return FALSE;
536 while ((ret = DOSFS_ReadDir( dir, &long_name, &short_name )))
538 /* Check against Unix name */
539 if (len == strlen(long_name))
541 if (!ignore_case)
543 if (!lstrncmp32A( long_name, name, len )) break;
545 else
547 if (!lstrncmpi32A( long_name, name, len )) break;
550 if (dos_name[0])
552 /* Check against hashed DOS name */
553 if (!short_name)
555 DOSFS_Hash( long_name, tmp_buf, TRUE, ignore_case );
556 short_name = tmp_buf;
558 if (!strcmp( dos_name, short_name )) break;
561 if (ret)
563 if (long_buf) strcpy( long_buf, long_name );
564 if (short_buf)
566 if (short_name)
567 DOSFS_ToDosDTAFormat( short_name, short_buf );
568 else
569 DOSFS_Hash( long_name, short_buf, FALSE, ignore_case );
571 TRACE(dosfs, "(%s,%s) -> %s (%s)\n",
572 path, name, long_name, short_buf ? short_buf : "***");
574 else
575 WARN(dosfs, "'%s' not found in '%s'\n", name, path);
576 DOSFS_CloseDir( dir );
577 return ret;
581 /***********************************************************************
582 * DOSFS_GetDevice
584 * Check if a DOS file name represents a DOS device and return the device.
586 const DOS_DEVICE *DOSFS_GetDevice( const char *name )
588 int i;
589 const char *p;
591 if (!name) return NULL; /* if FILE_DupUnixHandle was used */
592 if (name[0] && (name[1] == ':')) name += 2;
593 if ((p = strrchr( name, '/' ))) name = p + 1;
594 if ((p = strrchr( name, '\\' ))) name = p + 1;
595 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
597 const char *dev = DOSFS_Devices[i].name;
598 if (!lstrncmpi32A( dev, name, strlen(dev) ))
600 p = name + strlen( dev );
601 if (!*p || (*p == '.')) return &DOSFS_Devices[i];
604 return NULL;
608 /***********************************************************************
609 * DOSFS_GetDeviceByHandle
611 const DOS_DEVICE *DOSFS_GetDeviceByHandle( HFILE32 hFile )
613 struct get_file_info_request req;
614 struct get_file_info_reply reply;
616 if ((req.handle = HANDLE_GetServerHandle( PROCESS_Current(), hFile,
617 K32OBJ_FILE, 0 )) == -1)
618 return NULL;
619 CLIENT_SendRequest( REQ_GET_FILE_INFO, -1, 1, &req, sizeof(req) );
620 if (!CLIENT_WaitSimpleReply( &reply, sizeof(reply), NULL ) &&
621 (reply.type == FILE_TYPE_UNKNOWN))
623 if ((reply.attr >= 0) &&
624 (reply.attr < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0])))
625 return &DOSFS_Devices[reply.attr];
627 return NULL;
631 /***********************************************************************
632 * DOSFS_OpenDevice
634 * Open a DOS device. This might not map 1:1 into the UNIX device concept.
636 HFILE32 DOSFS_OpenDevice( const char *name, DWORD access )
638 int i;
639 const char *p;
641 if (!name) return (HFILE32)NULL; /* if FILE_DupUnixHandle was used */
642 if (name[0] && (name[1] == ':')) name += 2;
643 if ((p = strrchr( name, '/' ))) name = p + 1;
644 if ((p = strrchr( name, '\\' ))) name = p + 1;
645 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
647 const char *dev = DOSFS_Devices[i].name;
648 if (!lstrncmpi32A( dev, name, strlen(dev) ))
650 p = name + strlen( dev );
651 if (!*p || (*p == '.')) {
652 /* got it */
653 if (!strcmp(DOSFS_Devices[i].name,"NUL"))
654 return FILE_CreateFile( "/dev/null", access,
655 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
656 OPEN_EXISTING, 0, -1 );
657 if (!strcmp(DOSFS_Devices[i].name,"CON")) {
658 HFILE32 to_dup;
659 HFILE32 handle;
660 switch (access & (GENERIC_READ|GENERIC_WRITE)) {
661 case GENERIC_READ:
662 to_dup = GetStdHandle( STD_INPUT_HANDLE );
663 break;
664 case GENERIC_WRITE:
665 to_dup = GetStdHandle( STD_OUTPUT_HANDLE );
666 break;
667 default:
668 FIXME(dosfs,"can't open CON read/write\n");
669 return HFILE_ERROR32;
670 break;
672 if (!DuplicateHandle( GetCurrentProcess(), to_dup, GetCurrentProcess(),
673 &handle, 0, FALSE, DUPLICATE_SAME_ACCESS ))
674 handle = HFILE_ERROR32;
675 return handle;
677 if (!strcmp(DOSFS_Devices[i].name,"SCSIMGR$") ||
678 !strcmp(DOSFS_Devices[i].name,"HPSCAN"))
680 return FILE_CreateDevice( i, access, NULL );
682 FIXME(dosfs,"device open %s not supported (yet)\n",DOSFS_Devices[i].name);
683 return HFILE_ERROR32;
687 return HFILE_ERROR32;
691 /***********************************************************************
692 * DOSFS_GetPathDrive
694 * Get the drive specified by a given path name (DOS or Unix format).
696 static int DOSFS_GetPathDrive( const char **name )
698 int drive;
699 const char *p = *name;
701 if (*p && (p[1] == ':'))
703 drive = toupper(*p) - 'A';
704 *name += 2;
706 else if (*p == '/') /* Absolute Unix path? */
708 if ((drive = DRIVE_FindDriveRoot( name )) == -1)
710 MSG("Warning: %s not accessible from a DOS drive\n", *name );
711 /* Assume it really was a DOS name */
712 drive = DRIVE_GetCurrentDrive();
715 else drive = DRIVE_GetCurrentDrive();
717 if (!DRIVE_IsValid(drive))
719 DOS_ERROR( ER_InvalidDrive, EC_MediaError, SA_Abort, EL_Disk );
720 return -1;
722 return drive;
726 /***********************************************************************
727 * DOSFS_GetFullName
729 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
730 * Unix name / short DOS name pair.
731 * Return FALSE if one of the path components does not exist. The last path
732 * component is only checked if 'check_last' is non-zero.
733 * The buffers pointed to by 'long_buf' and 'short_buf' must be
734 * at least MAX_PATHNAME_LEN long.
736 BOOL32 DOSFS_GetFullName( LPCSTR name, BOOL32 check_last, DOS_FULL_NAME *full )
738 BOOL32 found;
739 UINT32 flags;
740 char *p_l, *p_s, *root;
742 TRACE(dosfs, "%s (last=%d)\n",
743 name, check_last );
745 if ((full->drive = DOSFS_GetPathDrive( &name )) == -1) return FALSE;
746 flags = DRIVE_GetFlags( full->drive );
748 lstrcpyn32A( full->long_name, DRIVE_GetRoot( full->drive ),
749 sizeof(full->long_name) );
750 if (full->long_name[1]) root = full->long_name + strlen(full->long_name);
751 else root = full->long_name; /* root directory */
753 strcpy( full->short_name, "A:\\" );
754 full->short_name[0] += full->drive;
756 if ((*name == '\\') || (*name == '/')) /* Absolute path */
758 while ((*name == '\\') || (*name == '/')) name++;
760 else /* Relative path */
762 lstrcpyn32A( root + 1, DRIVE_GetUnixCwd( full->drive ),
763 sizeof(full->long_name) - (root - full->long_name) - 1 );
764 if (root[1]) *root = '/';
765 lstrcpyn32A( full->short_name + 3, DRIVE_GetDosCwd( full->drive ),
766 sizeof(full->short_name) - 3 );
769 p_l = full->long_name[1] ? full->long_name + strlen(full->long_name)
770 : full->long_name;
771 p_s = full->short_name[3] ? full->short_name + strlen(full->short_name)
772 : full->short_name + 2;
773 found = TRUE;
775 while (*name && found)
777 /* Check for '.' and '..' */
779 if (*name == '.')
781 if (IS_END_OF_NAME(name[1]))
783 name++;
784 while ((*name == '\\') || (*name == '/')) name++;
785 continue;
787 else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
789 name += 2;
790 while ((*name == '\\') || (*name == '/')) name++;
791 while ((p_l > root) && (*p_l != '/')) p_l--;
792 while ((p_s > full->short_name + 2) && (*p_s != '\\')) p_s--;
793 *p_l = *p_s = '\0'; /* Remove trailing separator */
794 continue;
798 /* Make sure buffers are large enough */
800 if ((p_s >= full->short_name + sizeof(full->short_name) - 14) ||
801 (p_l >= full->long_name + sizeof(full->long_name) - 1))
803 DOS_ERROR( ER_PathNotFound, EC_NotFound, SA_Abort, EL_Disk );
804 return FALSE;
807 /* Get the long and short name matching the file name */
809 if ((found = DOSFS_FindUnixName( full->long_name, name, p_l + 1,
810 sizeof(full->long_name) - (p_l - full->long_name) - 1,
811 p_s + 1, !(flags & DRIVE_CASE_SENSITIVE) )))
813 *p_l++ = '/';
814 p_l += strlen(p_l);
815 *p_s++ = '\\';
816 p_s += strlen(p_s);
817 while (!IS_END_OF_NAME(*name)) name++;
819 else if (!check_last)
821 *p_l++ = '/';
822 *p_s++ = '\\';
823 while (!IS_END_OF_NAME(*name) &&
824 (p_s < full->short_name + sizeof(full->short_name) - 1) &&
825 (p_l < full->long_name + sizeof(full->long_name) - 1))
827 *p_s++ = tolower(*name);
828 /* If the drive is case-sensitive we want to create new */
829 /* files in lower-case otherwise we can't reopen them */
830 /* under the same short name. */
831 if (flags & DRIVE_CASE_SENSITIVE) *p_l++ = tolower(*name);
832 else *p_l++ = *name;
833 name++;
835 *p_l = *p_s = '\0';
837 while ((*name == '\\') || (*name == '/')) name++;
840 if (!found)
842 if (check_last)
844 DOS_ERROR( ER_FileNotFound, EC_NotFound, SA_Abort, EL_Disk );
845 return FALSE;
847 if (*name) /* Not last */
849 DOS_ERROR( ER_PathNotFound, EC_NotFound, SA_Abort, EL_Disk );
850 return FALSE;
853 if (!full->long_name[0]) strcpy( full->long_name, "/" );
854 if (!full->short_name[2]) strcpy( full->short_name + 2, "\\" );
855 TRACE(dosfs, "returning %s = %s\n",
856 full->long_name, full->short_name );
857 return TRUE;
861 /***********************************************************************
862 * GetShortPathName32A (KERNEL32.271)
864 * NOTES
865 * observed:
866 * longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
867 * *longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0
869 DWORD WINAPI GetShortPathName32A( LPCSTR longpath, LPSTR shortpath,
870 DWORD shortlen )
872 DOS_FULL_NAME full_name;
874 if (!longpath)
876 SetLastError(ERROR_INVALID_PARAMETER);
877 return 0;
880 if (!longpath[0])
882 SetLastError(ERROR_BAD_PATHNAME);
883 return 0;
886 /* FIXME: is it correct to always return a fully qualified short path? */
887 if (!DOSFS_GetFullName( longpath, TRUE, &full_name ))
889 SetLastError(ERROR_BAD_PATHNAME);
890 return 0;
892 lstrcpyn32A( shortpath, full_name.short_name, shortlen );
893 return strlen( full_name.short_name );
897 /***********************************************************************
898 * GetShortPathName32W (KERNEL32.272)
900 DWORD WINAPI GetShortPathName32W( LPCWSTR longpath, LPWSTR shortpath,
901 DWORD shortlen )
903 DOS_FULL_NAME full_name;
904 LPSTR longpathA ;
905 DWORD ret = 0;
907 if (!longpath)
908 { SetLastError(ERROR_INVALID_PARAMETER);
909 return 0;
912 if (!longpath[0])
913 { SetLastError(ERROR_BAD_PATHNAME);
914 return 0;
918 longpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, longpath );
920 /* FIXME: is it correct to always return a fully qualified short path? */
921 if (DOSFS_GetFullName( longpathA, TRUE, &full_name ))
923 ret = strlen( full_name.short_name );
924 lstrcpynAtoW( shortpath, full_name.short_name, shortlen );
927 SetLastError(ERROR_BAD_PATHNAME);
928 HeapFree( GetProcessHeap(), 0, longpathA );
929 return 0;
933 /***********************************************************************
934 * GetLongPathName32A (KERNEL32.xxx)
936 DWORD WINAPI GetLongPathName32A( LPCSTR shortpath, LPSTR longpath,
937 DWORD longlen )
939 DOS_FULL_NAME full_name;
940 char *p;
941 char *longfilename;
942 DWORD shortpathlen;
944 if (!DOSFS_GetFullName( shortpath, TRUE, &full_name )) return 0;
945 lstrcpyn32A( longpath, full_name.short_name, longlen );
946 /* Do some hackery to get the long filename.
947 * FIXME: Would be better if it returned the
948 * long version of the directories too
950 longfilename = strrchr(full_name.long_name, '/')+1;
951 if (longpath != NULL) {
952 if ((p = strrchr( longpath, '\\' )) != NULL) {
953 p++;
954 longlen -= (p-longpath);
955 lstrcpyn32A( p, longfilename , longlen);
958 shortpathlen =
959 ((strrchr( full_name.short_name, '\\' ) - full_name.short_name) + 1);
960 return shortpathlen + strlen( longfilename );
964 /***********************************************************************
965 * GetLongPathName32W (KERNEL32.269)
967 DWORD WINAPI GetLongPathName32W( LPCWSTR shortpath, LPWSTR longpath,
968 DWORD longlen )
970 DOS_FULL_NAME full_name;
971 DWORD ret = 0;
972 LPSTR shortpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, shortpath );
974 /* FIXME: is it correct to always return a fully qualified short path? */
975 if (DOSFS_GetFullName( shortpathA, TRUE, &full_name ))
977 ret = strlen( full_name.short_name );
978 lstrcpynAtoW( longpath, full_name.long_name, longlen );
980 HeapFree( GetProcessHeap(), 0, shortpathA );
981 return ret;
985 /***********************************************************************
986 * DOSFS_DoGetFullPathName
988 * Implementation of GetFullPathName32A/W.
990 static DWORD DOSFS_DoGetFullPathName( LPCSTR name, DWORD len, LPSTR result,
991 BOOL32 unicode )
993 char buffer[MAX_PATHNAME_LEN];
994 int drive;
995 char *p;
996 DWORD ret;
998 /* last possible position for a char != 0 */
999 char *endchar = buffer + sizeof(buffer) - 2;
1000 *endchar = '\0';
1002 TRACE(dosfs, "converting '%s'\n", name );
1004 if (!name || !result || ((drive = DOSFS_GetPathDrive( &name )) == -1) )
1005 { SetLastError( ERROR_INVALID_PARAMETER );
1006 return 0;
1009 p = buffer;
1010 *p++ = 'A' + drive;
1011 *p++ = ':';
1012 if (IS_END_OF_NAME(*name) && (*name)) /* Absolute path */
1014 while (((*name == '\\') || (*name == '/')) && (!*endchar) )
1015 *p++ = *name++;
1017 else /* Relative path or empty path */
1019 *p++ = '\\';
1020 lstrcpyn32A( p, DRIVE_GetDosCwd(drive), sizeof(buffer) - 4 );
1021 if ( *p )
1023 p += strlen(p);
1024 *p++ = '\\';
1027 *p = '\0';
1029 while (*name)
1031 if (*name == '.')
1033 if (IS_END_OF_NAME(name[1]))
1035 name++;
1036 while ((*name == '\\') || (*name == '/')) name++;
1037 continue;
1039 else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
1041 name += 2;
1042 while ((*name == '\\') || (*name == '/')) name++;
1044 if (p < buffer + 3) /* no previous dir component */
1045 continue;
1046 p--; /* skip previously added '\\' */
1047 while ((*p == '\\') || (*p == '/')) p--;
1048 /* skip previous dir component */
1049 while ((*p != '\\') && (*p != '/')) p--;
1050 p++;
1051 continue;
1054 if ( *endchar )
1055 { DOS_ERROR( ER_PathNotFound, EC_NotFound, SA_Abort, EL_Disk );
1056 return 0;
1058 while (!IS_END_OF_NAME(*name) && (!*endchar) )
1059 *p++ = *name++;
1060 while (((*name == '\\') || (*name == '/')) && (!*endchar) )
1061 *p++ = *name++;
1063 *p = '\0';
1065 if (!(DRIVE_GetFlags(drive) & DRIVE_CASE_PRESERVING))
1066 CharUpper32A( buffer );
1068 if (unicode)
1069 lstrcpynAtoW( (LPWSTR)result, buffer, len );
1070 else
1071 lstrcpyn32A( result, buffer, len );
1073 TRACE(dosfs, "returning '%s'\n", buffer );
1075 /* If the lpBuffer buffer is too small, the return value is the
1076 size of the buffer, in characters, required to hold the path. */
1078 ret = strlen(buffer);
1080 if (ret >= len )
1081 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1083 return ret;
1087 /***********************************************************************
1088 * GetFullPathName32A (KERNEL32.272)
1089 * NOTES
1090 * if the path closed with '\', *lastpart is 0
1092 DWORD WINAPI GetFullPathName32A( LPCSTR name, DWORD len, LPSTR buffer,
1093 LPSTR *lastpart )
1095 DWORD ret = DOSFS_DoGetFullPathName( name, len, buffer, FALSE );
1096 if (ret && lastpart)
1098 LPSTR p = buffer + strlen(buffer);
1100 if (*p != '\\')
1102 while ((p > buffer + 2) && (*p != '\\')) p--;
1103 *lastpart = p + 1;
1105 else *lastpart = NULL;
1107 return ret;
1111 /***********************************************************************
1112 * GetFullPathName32W (KERNEL32.273)
1114 DWORD WINAPI GetFullPathName32W( LPCWSTR name, DWORD len, LPWSTR buffer,
1115 LPWSTR *lastpart )
1117 LPSTR nameA = HEAP_strdupWtoA( GetProcessHeap(), 0, name );
1118 DWORD ret = DOSFS_DoGetFullPathName( nameA, len, (LPSTR)buffer, TRUE );
1119 HeapFree( GetProcessHeap(), 0, nameA );
1120 if (ret && lastpart)
1122 LPWSTR p = buffer + lstrlen32W(buffer);
1123 if (*p != (WCHAR)'\\')
1125 while ((p > buffer + 2) && (*p != (WCHAR)'\\')) p--;
1126 *lastpart = p + 1;
1128 else *lastpart = NULL;
1130 return ret;
1133 /***********************************************************************
1134 * DOSFS_FindNextEx
1136 static int DOSFS_FindNextEx( FIND_FIRST_INFO *info, WIN32_FIND_DATA32A *entry )
1138 BYTE attr = info->attr | FA_UNUSED | FA_ARCHIVE | FA_RDONLY;
1139 UINT32 flags = DRIVE_GetFlags( info->drive );
1140 char *p, buffer[MAX_PATHNAME_LEN];
1141 const char *drive_path;
1142 int drive_root;
1143 LPCSTR long_name, short_name;
1144 BY_HANDLE_FILE_INFORMATION fileinfo;
1145 char dos_name[13];
1147 if ((info->attr & ~(FA_UNUSED | FA_ARCHIVE | FA_RDONLY)) == FA_LABEL)
1149 if (info->cur_pos) return 0;
1150 entry->dwFileAttributes = FILE_ATTRIBUTE_LABEL;
1151 DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftCreationTime, 0 );
1152 DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftLastAccessTime, 0 );
1153 DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftLastWriteTime, 0 );
1154 entry->nFileSizeHigh = 0;
1155 entry->nFileSizeLow = 0;
1156 entry->dwReserved0 = 0;
1157 entry->dwReserved1 = 0;
1158 DOSFS_ToDosDTAFormat( DRIVE_GetLabel( info->drive ), entry->cFileName );
1159 strcpy( entry->cAlternateFileName, entry->cFileName );
1160 info->cur_pos++;
1161 return 1;
1164 drive_path = info->path + strlen(DRIVE_GetRoot( info->drive ));
1165 while ((*drive_path == '/') || (*drive_path == '\\')) drive_path++;
1166 drive_root = !*drive_path;
1168 lstrcpyn32A( buffer, info->path, sizeof(buffer) - 1 );
1169 strcat( buffer, "/" );
1170 p = buffer + strlen(buffer);
1172 while (DOSFS_ReadDir( info->dir, &long_name, &short_name ))
1174 info->cur_pos++;
1176 /* Don't return '.' and '..' in the root of the drive */
1177 if (drive_root && (long_name[0] == '.') &&
1178 (!long_name[1] || ((long_name[1] == '.') && !long_name[2])))
1179 continue;
1181 /* Check the long mask */
1183 if (info->long_mask)
1185 if (!DOSFS_MatchLong( info->long_mask, long_name,
1186 flags & DRIVE_CASE_SENSITIVE )) continue;
1189 /* Check the short mask */
1191 if (info->short_mask)
1193 if (!short_name)
1195 DOSFS_Hash( long_name, dos_name, TRUE,
1196 !(flags & DRIVE_CASE_SENSITIVE) );
1197 short_name = dos_name;
1199 if (!DOSFS_MatchShort( info->short_mask, short_name )) continue;
1202 /* Check the file attributes */
1204 lstrcpyn32A( p, long_name, sizeof(buffer) - (int)(p - buffer) );
1205 if (!FILE_Stat( buffer, &fileinfo ))
1207 WARN(dosfs, "can't stat %s\n", buffer);
1208 continue;
1210 if (fileinfo.dwFileAttributes & ~attr) continue;
1212 /* We now have a matching entry; fill the result and return */
1214 entry->dwFileAttributes = fileinfo.dwFileAttributes;
1215 entry->ftCreationTime = fileinfo.ftCreationTime;
1216 entry->ftLastAccessTime = fileinfo.ftLastAccessTime;
1217 entry->ftLastWriteTime = fileinfo.ftLastWriteTime;
1218 entry->nFileSizeHigh = fileinfo.nFileSizeHigh;
1219 entry->nFileSizeLow = fileinfo.nFileSizeLow;
1221 if (short_name)
1222 DOSFS_ToDosDTAFormat( short_name, entry->cAlternateFileName );
1223 else
1224 DOSFS_Hash( long_name, entry->cAlternateFileName, FALSE,
1225 !(flags & DRIVE_CASE_SENSITIVE) );
1227 lstrcpyn32A( entry->cFileName, long_name, sizeof(entry->cFileName) );
1228 if (!(flags & DRIVE_CASE_PRESERVING)) CharLower32A( entry->cFileName );
1229 TRACE(dosfs, "returning %s (%s) %02lx %ld\n",
1230 entry->cFileName, entry->cAlternateFileName,
1231 entry->dwFileAttributes, entry->nFileSizeLow );
1232 return 1;
1234 return 0; /* End of directory */
1237 /***********************************************************************
1238 * DOSFS_FindNext
1240 * Find the next matching file. Return the number of entries read to find
1241 * the matching one, or 0 if no more entries.
1242 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
1243 * file name mask. Either or both can be NULL.
1245 * NOTE: This is supposed to be only called by the int21 emulation
1246 * routines. Thus, we should own the Win16Mutex anyway.
1247 * Nevertheless, we explicitly enter it to ensure the static
1248 * directory cache is protected.
1250 int DOSFS_FindNext( const char *path, const char *short_mask,
1251 const char *long_mask, int drive, BYTE attr,
1252 int skip, WIN32_FIND_DATA32A *entry )
1254 static FIND_FIRST_INFO info = { NULL };
1255 LPCSTR short_name, long_name;
1256 int count;
1258 SYSLEVEL_EnterWin16Lock();
1260 /* Check the cached directory */
1261 if (!(info.dir && info.path == path && info.short_mask == short_mask
1262 && info.long_mask == long_mask && info.drive == drive
1263 && info.attr == attr && info.cur_pos <= skip))
1265 /* Not in the cache, open it anew */
1266 if (info.dir) DOSFS_CloseDir( info.dir );
1268 info.path = (LPSTR)path;
1269 info.long_mask = (LPSTR)long_mask;
1270 info.short_mask = (LPSTR)short_mask;
1271 info.attr = attr;
1272 info.drive = drive;
1273 info.cur_pos = 0;
1274 info.dir = DOSFS_OpenDir( info.path );
1277 /* Skip to desired position */
1278 while (info.cur_pos < skip)
1279 if (info.dir && DOSFS_ReadDir( info.dir, &long_name, &short_name ))
1280 info.cur_pos++;
1281 else
1282 break;
1284 if (info.dir && info.cur_pos == skip && DOSFS_FindNextEx( &info, entry ))
1285 count = info.cur_pos - skip;
1286 else
1287 count = 0;
1289 if (!count)
1291 if (info.dir) DOSFS_CloseDir( info.dir );
1292 memset( &info, '\0', sizeof(info) );
1295 SYSLEVEL_LeaveWin16Lock();
1297 return count;
1302 /*************************************************************************
1303 * FindFirstFile16 (KERNEL.413)
1305 HANDLE16 WINAPI FindFirstFile16( LPCSTR path, WIN32_FIND_DATA32A *data )
1307 DOS_FULL_NAME full_name;
1308 HGLOBAL16 handle;
1309 FIND_FIRST_INFO *info;
1311 data->dwReserved0 = data->dwReserved1 = 0x0;
1312 if (!path) return 0;
1313 if (!DOSFS_GetFullName( path, FALSE, &full_name ))
1314 return INVALID_HANDLE_VALUE16;
1315 if (!(handle = GlobalAlloc16( GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO) )))
1316 return INVALID_HANDLE_VALUE16;
1317 info = (FIND_FIRST_INFO *)GlobalLock16( handle );
1318 info->path = HEAP_strdupA( SystemHeap, 0, full_name.long_name );
1319 info->long_mask = strrchr( info->path, '/' );
1320 *(info->long_mask++) = '\0';
1321 info->short_mask = NULL;
1322 info->attr = 0xff;
1323 if (path[0] && (path[1] == ':')) info->drive = toupper(*path) - 'A';
1324 else info->drive = DRIVE_GetCurrentDrive();
1325 info->cur_pos = 0;
1327 info->dir = DOSFS_OpenDir( info->path );
1329 GlobalUnlock16( handle );
1330 if (!FindNextFile16( handle, data ))
1332 FindClose16( handle );
1333 DOS_ERROR( ER_NoMoreFiles, EC_MediaError, SA_Abort, EL_Disk );
1334 return INVALID_HANDLE_VALUE16;
1336 return handle;
1340 /*************************************************************************
1341 * FindFirstFile32A (KERNEL32.123)
1343 HANDLE32 WINAPI FindFirstFile32A( LPCSTR path, WIN32_FIND_DATA32A *data )
1345 HANDLE32 handle = FindFirstFile16( path, data );
1346 if (handle == INVALID_HANDLE_VALUE16) return INVALID_HANDLE_VALUE32;
1347 return handle;
1351 /*************************************************************************
1352 * FindFirstFile32W (KERNEL32.124)
1354 HANDLE32 WINAPI FindFirstFile32W( LPCWSTR path, WIN32_FIND_DATA32W *data )
1356 WIN32_FIND_DATA32A dataA;
1357 LPSTR pathA = HEAP_strdupWtoA( GetProcessHeap(), 0, path );
1358 HANDLE32 handle = FindFirstFile32A( pathA, &dataA );
1359 HeapFree( GetProcessHeap(), 0, pathA );
1360 if (handle != INVALID_HANDLE_VALUE32)
1362 data->dwFileAttributes = dataA.dwFileAttributes;
1363 data->ftCreationTime = dataA.ftCreationTime;
1364 data->ftLastAccessTime = dataA.ftLastAccessTime;
1365 data->ftLastWriteTime = dataA.ftLastWriteTime;
1366 data->nFileSizeHigh = dataA.nFileSizeHigh;
1367 data->nFileSizeLow = dataA.nFileSizeLow;
1368 lstrcpyAtoW( data->cFileName, dataA.cFileName );
1369 lstrcpyAtoW( data->cAlternateFileName, dataA.cAlternateFileName );
1371 return handle;
1375 /*************************************************************************
1376 * FindNextFile16 (KERNEL.414)
1378 BOOL16 WINAPI FindNextFile16( HANDLE16 handle, WIN32_FIND_DATA32A *data )
1380 FIND_FIRST_INFO *info;
1382 if (!(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
1384 DOS_ERROR( ER_InvalidHandle, EC_ProgramError, SA_Abort, EL_Disk );
1385 return FALSE;
1387 GlobalUnlock16( handle );
1388 if (!info->path || !info->dir)
1390 DOS_ERROR( ER_NoMoreFiles, EC_MediaError, SA_Abort, EL_Disk );
1391 return FALSE;
1393 if (!DOSFS_FindNextEx( info, data ))
1395 DOSFS_CloseDir( info->dir ); info->dir = NULL;
1396 HeapFree( SystemHeap, 0, info->path );
1397 info->path = info->long_mask = NULL;
1398 DOS_ERROR( ER_NoMoreFiles, EC_MediaError, SA_Abort, EL_Disk );
1399 return FALSE;
1401 return TRUE;
1405 /*************************************************************************
1406 * FindNextFile32A (KERNEL32.126)
1408 BOOL32 WINAPI FindNextFile32A( HANDLE32 handle, WIN32_FIND_DATA32A *data )
1410 return FindNextFile16( handle, data );
1414 /*************************************************************************
1415 * FindNextFile32W (KERNEL32.127)
1417 BOOL32 WINAPI FindNextFile32W( HANDLE32 handle, WIN32_FIND_DATA32W *data )
1419 WIN32_FIND_DATA32A dataA;
1420 if (!FindNextFile32A( handle, &dataA )) return FALSE;
1421 data->dwFileAttributes = dataA.dwFileAttributes;
1422 data->ftCreationTime = dataA.ftCreationTime;
1423 data->ftLastAccessTime = dataA.ftLastAccessTime;
1424 data->ftLastWriteTime = dataA.ftLastWriteTime;
1425 data->nFileSizeHigh = dataA.nFileSizeHigh;
1426 data->nFileSizeLow = dataA.nFileSizeLow;
1427 lstrcpyAtoW( data->cFileName, dataA.cFileName );
1428 lstrcpyAtoW( data->cAlternateFileName, dataA.cAlternateFileName );
1429 return TRUE;
1433 /*************************************************************************
1434 * FindClose16 (KERNEL.415)
1436 BOOL16 WINAPI FindClose16( HANDLE16 handle )
1438 FIND_FIRST_INFO *info;
1440 if ((handle == INVALID_HANDLE_VALUE16) ||
1441 !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
1443 DOS_ERROR( ER_InvalidHandle, EC_ProgramError, SA_Abort, EL_Disk );
1444 return FALSE;
1446 if (info->dir) DOSFS_CloseDir( info->dir );
1447 if (info->path) HeapFree( SystemHeap, 0, info->path );
1448 GlobalUnlock16( handle );
1449 GlobalFree16( handle );
1450 return TRUE;
1454 /*************************************************************************
1455 * FindClose32 (KERNEL32.119)
1457 BOOL32 WINAPI FindClose32( HANDLE32 handle )
1459 return FindClose16( (HANDLE16)handle );
1463 /***********************************************************************
1464 * DOSFS_UnixTimeToFileTime
1466 * Convert a Unix time to FILETIME format.
1467 * The FILETIME structure is a 64-bit value representing the number of
1468 * 100-nanosecond intervals since January 1, 1601, 0:00.
1469 * 'remainder' is the nonnegative number of 100-ns intervals
1470 * corresponding to the time fraction smaller than 1 second that
1471 * couldn't be stored in the time_t value.
1473 void DOSFS_UnixTimeToFileTime( time_t unix_time, FILETIME *filetime,
1474 DWORD remainder )
1476 /* NOTES:
1478 CONSTANTS:
1479 The time difference between 1 January 1601, 00:00:00 and
1480 1 January 1970, 00:00:00 is 369 years, plus the leap years
1481 from 1604 to 1968, excluding 1700, 1800, 1900.
1482 This makes (1968 - 1600) / 4 - 3 = 89 leap days, and a total
1483 of 134774 days.
1485 Any day in that period had 24 * 60 * 60 = 86400 seconds.
1487 The time difference is 134774 * 86400 * 10000000, which can be written
1488 116444736000000000
1489 27111902 * 2^32 + 3577643008
1490 413 * 2^48 + 45534 * 2^32 + 54590 * 2^16 + 32768
1492 If you find that these constants are buggy, please change them in all
1493 instances in both conversion functions.
1495 VERSIONS:
1496 There are two versions, one of them uses long long variables and
1497 is presumably faster but not ISO C. The other one uses standard C
1498 data types and operations but relies on the assumption that negative
1499 numbers are stored as 2's complement (-1 is 0xffff....). If this
1500 assumption is violated, dates before 1970 will not convert correctly.
1501 This should however work on any reasonable architecture where WINE
1502 will run.
1504 DETAILS:
1506 Take care not to remove the casts. I have tested these functions
1507 (in both versions) for a lot of numbers. I would be interested in
1508 results on other compilers than GCC.
1510 The operations have been designed to account for the possibility
1511 of 64-bit time_t in future UNICES. Even the versions without
1512 internal long long numbers will work if time_t only is 64 bit.
1513 A 32-bit shift, which was necessary for that operation, turned out
1514 not to work correctly in GCC, besides giving the warning. So I
1515 used a double 16-bit shift instead. Numbers are in the ISO version
1516 represented by three limbs, the most significant with 32 bit, the
1517 other two with 16 bit each.
1519 As the modulo-operator % is not well-defined for negative numbers,
1520 negative divisors have been avoided in DOSFS_FileTimeToUnixTime.
1522 There might be quicker ways to do this in C. Certainly so in
1523 assembler.
1525 Claus Fischer, fischer@iue.tuwien.ac.at
1528 #if (SIZEOF_LONG_LONG >= 8)
1529 # define USE_LONG_LONG 1
1530 #else
1531 # define USE_LONG_LONG 0
1532 #endif
1534 #if USE_LONG_LONG /* gcc supports long long type */
1536 long long int t = unix_time;
1537 t *= 10000000;
1538 t += 116444736000000000LL;
1539 t += remainder;
1540 filetime->dwLowDateTime = (UINT32)t;
1541 filetime->dwHighDateTime = (UINT32)(t >> 32);
1543 #else /* ISO version */
1545 UINT32 a0; /* 16 bit, low bits */
1546 UINT32 a1; /* 16 bit, medium bits */
1547 UINT32 a2; /* 32 bit, high bits */
1549 /* Copy the unix time to a2/a1/a0 */
1550 a0 = unix_time & 0xffff;
1551 a1 = (unix_time >> 16) & 0xffff;
1552 /* This is obsolete if unix_time is only 32 bits, but it does not hurt.
1553 Do not replace this by >> 32, it gives a compiler warning and it does
1554 not work. */
1555 a2 = (unix_time >= 0 ? (unix_time >> 16) >> 16 :
1556 ~((~unix_time >> 16) >> 16));
1558 /* Multiply a by 10000000 (a = a2/a1/a0)
1559 Split the factor into 10000 * 1000 which are both less than 0xffff. */
1560 a0 *= 10000;
1561 a1 = a1 * 10000 + (a0 >> 16);
1562 a2 = a2 * 10000 + (a1 >> 16);
1563 a0 &= 0xffff;
1564 a1 &= 0xffff;
1566 a0 *= 1000;
1567 a1 = a1 * 1000 + (a0 >> 16);
1568 a2 = a2 * 1000 + (a1 >> 16);
1569 a0 &= 0xffff;
1570 a1 &= 0xffff;
1572 /* Add the time difference and the remainder */
1573 a0 += 32768 + (remainder & 0xffff);
1574 a1 += 54590 + (remainder >> 16 ) + (a0 >> 16);
1575 a2 += 27111902 + (a1 >> 16);
1576 a0 &= 0xffff;
1577 a1 &= 0xffff;
1579 /* Set filetime */
1580 filetime->dwLowDateTime = (a1 << 16) + a0;
1581 filetime->dwHighDateTime = a2;
1582 #endif
1586 /***********************************************************************
1587 * DOSFS_FileTimeToUnixTime
1589 * Convert a FILETIME format to Unix time.
1590 * If not NULL, 'remainder' contains the fractional part of the filetime,
1591 * in the range of [0..9999999] (even if time_t is negative).
1593 time_t DOSFS_FileTimeToUnixTime( const FILETIME *filetime, DWORD *remainder )
1595 /* Read the comment in the function DOSFS_UnixTimeToFileTime. */
1596 #if USE_LONG_LONG
1598 long long int t = filetime->dwHighDateTime;
1599 t <<= 32;
1600 t += (UINT32)filetime->dwLowDateTime;
1601 t -= 116444736000000000LL;
1602 if (t < 0)
1604 if (remainder) *remainder = 9999999 - (-t - 1) % 10000000;
1605 return -1 - ((-t - 1) / 10000000);
1607 else
1609 if (remainder) *remainder = t % 10000000;
1610 return t / 10000000;
1613 #else /* ISO version */
1615 UINT32 a0; /* 16 bit, low bits */
1616 UINT32 a1; /* 16 bit, medium bits */
1617 UINT32 a2; /* 32 bit, high bits */
1618 UINT32 r; /* remainder of division */
1619 unsigned int carry; /* carry bit for subtraction */
1620 int negative; /* whether a represents a negative value */
1622 /* Copy the time values to a2/a1/a0 */
1623 a2 = (UINT32)filetime->dwHighDateTime;
1624 a1 = ((UINT32)filetime->dwLowDateTime ) >> 16;
1625 a0 = ((UINT32)filetime->dwLowDateTime ) & 0xffff;
1627 /* Subtract the time difference */
1628 if (a0 >= 32768 ) a0 -= 32768 , carry = 0;
1629 else a0 += (1 << 16) - 32768 , carry = 1;
1631 if (a1 >= 54590 + carry) a1 -= 54590 + carry, carry = 0;
1632 else a1 += (1 << 16) - 54590 - carry, carry = 1;
1634 a2 -= 27111902 + carry;
1636 /* If a is negative, replace a by (-1-a) */
1637 negative = (a2 >= ((UINT32)1) << 31);
1638 if (negative)
1640 /* Set a to -a - 1 (a is a2/a1/a0) */
1641 a0 = 0xffff - a0;
1642 a1 = 0xffff - a1;
1643 a2 = ~a2;
1646 /* Divide a by 10000000 (a = a2/a1/a0), put the rest into r.
1647 Split the divisor into 10000 * 1000 which are both less than 0xffff. */
1648 a1 += (a2 % 10000) << 16;
1649 a2 /= 10000;
1650 a0 += (a1 % 10000) << 16;
1651 a1 /= 10000;
1652 r = a0 % 10000;
1653 a0 /= 10000;
1655 a1 += (a2 % 1000) << 16;
1656 a2 /= 1000;
1657 a0 += (a1 % 1000) << 16;
1658 a1 /= 1000;
1659 r += (a0 % 1000) * 10000;
1660 a0 /= 1000;
1662 /* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
1663 if (negative)
1665 /* Set a to -a - 1 (a is a2/a1/a0) */
1666 a0 = 0xffff - a0;
1667 a1 = 0xffff - a1;
1668 a2 = ~a2;
1670 r = 9999999 - r;
1673 if (remainder) *remainder = r;
1675 /* Do not replace this by << 32, it gives a compiler warning and it does
1676 not work. */
1677 return ((((time_t)a2) << 16) << 16) + (a1 << 16) + a0;
1678 #endif
1682 /***********************************************************************
1683 * DosDateTimeToFileTime (KERNEL32.76)
1685 BOOL32 WINAPI DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft)
1687 struct tm newtm;
1689 newtm.tm_sec = (fattime & 0x1f) * 2;
1690 newtm.tm_min = (fattime >> 5) & 0x3f;
1691 newtm.tm_hour = (fattime >> 11);
1692 newtm.tm_mday = (fatdate & 0x1f);
1693 newtm.tm_mon = ((fatdate >> 5) & 0x0f) - 1;
1694 newtm.tm_year = (fatdate >> 9) + 80;
1695 DOSFS_UnixTimeToFileTime( mktime( &newtm ), ft, 0 );
1696 return TRUE;
1700 /***********************************************************************
1701 * FileTimeToDosDateTime (KERNEL32.111)
1703 BOOL32 WINAPI FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate,
1704 LPWORD fattime )
1706 time_t unixtime = DOSFS_FileTimeToUnixTime( ft, NULL );
1707 struct tm *tm = localtime( &unixtime );
1708 if (fattime)
1709 *fattime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
1710 if (fatdate)
1711 *fatdate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
1712 + tm->tm_mday;
1713 return TRUE;
1717 /***********************************************************************
1718 * LocalFileTimeToFileTime (KERNEL32.373)
1720 BOOL32 WINAPI LocalFileTimeToFileTime( const FILETIME *localft,
1721 LPFILETIME utcft )
1723 struct tm *xtm;
1724 DWORD remainder;
1726 /* convert from local to UTC. Perhaps not correct. FIXME */
1727 time_t unixtime = DOSFS_FileTimeToUnixTime( localft, &remainder );
1728 xtm = gmtime( &unixtime );
1729 DOSFS_UnixTimeToFileTime( mktime(xtm), utcft, remainder );
1730 return TRUE;
1734 /***********************************************************************
1735 * FileTimeToLocalFileTime (KERNEL32.112)
1737 BOOL32 WINAPI FileTimeToLocalFileTime( const FILETIME *utcft,
1738 LPFILETIME localft )
1740 DWORD remainder;
1741 /* convert from UTC to local. Perhaps not correct. FIXME */
1742 time_t unixtime = DOSFS_FileTimeToUnixTime( utcft, &remainder );
1743 #ifdef HAVE_TIMEGM
1744 struct tm *xtm = localtime( &unixtime );
1745 time_t localtime;
1747 localtime = timegm(xtm);
1748 DOSFS_UnixTimeToFileTime( localtime, localft, remainder );
1750 #else
1751 struct tm *xtm,*gtm;
1752 time_t time1,time2;
1754 xtm = localtime( &unixtime );
1755 gtm = gmtime( &unixtime );
1756 time1 = mktime(xtm);
1757 time2 = mktime(gtm);
1758 DOSFS_UnixTimeToFileTime( 2*time1-time2, localft, remainder );
1759 #endif
1760 return TRUE;
1764 /***********************************************************************
1765 * FileTimeToSystemTime (KERNEL32.113)
1767 BOOL32 WINAPI FileTimeToSystemTime( const FILETIME *ft, LPSYSTEMTIME syst )
1769 struct tm *xtm;
1770 DWORD remainder;
1771 time_t xtime = DOSFS_FileTimeToUnixTime( ft, &remainder );
1772 xtm = gmtime(&xtime);
1773 syst->wYear = xtm->tm_year+1900;
1774 syst->wMonth = xtm->tm_mon + 1;
1775 syst->wDayOfWeek = xtm->tm_wday;
1776 syst->wDay = xtm->tm_mday;
1777 syst->wHour = xtm->tm_hour;
1778 syst->wMinute = xtm->tm_min;
1779 syst->wSecond = xtm->tm_sec;
1780 syst->wMilliseconds = remainder / 10000;
1781 return TRUE;
1784 /***********************************************************************
1785 * QueryDosDeviceA (KERNEL32.413)
1787 * returns array of strings terminated by \0, terminated by \0
1789 DWORD WINAPI QueryDosDevice32A(LPCSTR devname,LPSTR target,DWORD bufsize)
1791 LPSTR s;
1792 char buffer[200];
1794 TRACE(dosfs,"(%s,...)\n",devname?devname:"<null>");
1795 if (!devname) {
1796 /* return known MSDOS devices */
1797 lstrcpy32A(buffer,"CON COM1 COM2 LPT1 NUL ");
1798 while ((s=strchr(buffer,' ')))
1799 *s='\0';
1801 lstrcpyn32A(target,buffer,bufsize);
1802 return strlen(buffer);
1804 lstrcpy32A(buffer,"\\DEV\\");
1805 lstrcat32A(buffer,devname);
1806 if ((s=strchr(buffer,':'))) *s='\0';
1807 lstrcpyn32A(target,buffer,bufsize);
1808 return strlen(buffer);
1812 /***********************************************************************
1813 * QueryDosDeviceW (KERNEL32.414)
1815 * returns array of strings terminated by \0, terminated by \0
1817 DWORD WINAPI QueryDosDevice32W(LPCWSTR devname,LPWSTR target,DWORD bufsize)
1819 LPSTR devnameA = devname?HEAP_strdupWtoA(GetProcessHeap(),0,devname):NULL;
1820 LPSTR targetA = (LPSTR)HEAP_xalloc(GetProcessHeap(),0,bufsize);
1821 DWORD ret = QueryDosDevice32A(devnameA,targetA,bufsize);
1823 lstrcpynAtoW(target,targetA,bufsize);
1824 if (devnameA) HeapFree(GetProcessHeap(),0,devnameA);
1825 if (targetA) HeapFree(GetProcessHeap(),0,targetA);
1826 return ret;
1830 /***********************************************************************
1831 * SystemTimeToFileTime (KERNEL32.526)
1833 BOOL32 WINAPI SystemTimeToFileTime( const SYSTEMTIME *syst, LPFILETIME ft )
1835 #ifdef HAVE_TIMEGM
1836 struct tm xtm;
1837 time_t utctime;
1838 #else
1839 struct tm xtm,*local_tm,*utc_tm;
1840 time_t localtim,utctime;
1841 #endif
1843 xtm.tm_year = syst->wYear-1900;
1844 xtm.tm_mon = syst->wMonth - 1;
1845 xtm.tm_wday = syst->wDayOfWeek;
1846 xtm.tm_mday = syst->wDay;
1847 xtm.tm_hour = syst->wHour;
1848 xtm.tm_min = syst->wMinute;
1849 xtm.tm_sec = syst->wSecond; /* this is UTC */
1850 xtm.tm_isdst = -1;
1851 #ifdef HAVE_TIMEGM
1852 utctime = timegm(&xtm);
1853 DOSFS_UnixTimeToFileTime( utctime, ft,
1854 syst->wMilliseconds * 10000 );
1855 #else
1856 localtim = mktime(&xtm); /* now we've got local time */
1857 local_tm = localtime(&localtim);
1858 utc_tm = gmtime(&localtim);
1859 utctime = mktime(utc_tm);
1860 DOSFS_UnixTimeToFileTime( 2*localtim -utctime, ft,
1861 syst->wMilliseconds * 10000 );
1862 #endif
1863 return TRUE;
1866 BOOL32 WINAPI DefineDosDevice32A(DWORD flags,LPCSTR devname,LPCSTR targetpath) {
1867 FIXME(dosfs,"(0x%08lx,%s,%s),stub!\n",flags,devname,targetpath);
1868 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1869 return FALSE;