Strip .drv extensions to MODULE_GetLoadOrder.
[wine/hacks.git] / files / dos_fs.c
blob40170ea0c855eb1ad94f08c6ba542639024efb1f
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 #ifdef HAVE_SYS_ERRNO_H
14 #include <sys/errno.h>
15 #endif
16 #include <fcntl.h>
17 #include <string.h>
18 #include <stdlib.h>
19 #include <sys/stat.h>
20 #include <sys/ioctl.h>
21 #include <time.h>
22 #include <unistd.h>
24 #include "windef.h"
25 #include "winuser.h"
26 #include "wine/winbase16.h"
27 #include "winerror.h"
28 #include "drive.h"
29 #include "file.h"
30 #include "heap.h"
31 #include "msdos.h"
32 #include "syslevel.h"
33 #include "server.h"
34 #include "process.h"
35 #include "options.h"
36 #include "debugtools.h"
38 DECLARE_DEBUG_CHANNEL(dosfs)
39 DECLARE_DEBUG_CHANNEL(file)
41 /* Define the VFAT ioctl to get both short and long file names */
42 /* FIXME: is it possible to get this to work on other systems? */
43 #ifdef linux
44 #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, long)
45 /* We want the real kernel dirent structure, not the libc one */
46 typedef struct
48 long d_ino;
49 long d_off;
50 unsigned short d_reclen;
51 char d_name[256];
52 } KERNEL_DIRENT;
54 #else /* linux */
55 #undef VFAT_IOCTL_READDIR_BOTH /* just in case... */
56 #endif /* linux */
58 /* Chars we don't want to see in DOS file names */
59 #define INVALID_DOS_CHARS "*?<>|\"+=,;[] \345"
61 static const DOS_DEVICE DOSFS_Devices[] =
62 /* name, device flags (see Int 21/AX=0x4400) */
64 { "CON", 0xc0d3 },
65 { "PRN", 0xa0c0 },
66 { "NUL", 0x80c4 },
67 { "AUX", 0x80c0 },
68 { "LPT1", 0xa0c0 },
69 { "LPT2", 0xa0c0 },
70 { "LPT3", 0xa0c0 },
71 { "LPT4", 0xc0d3 },
72 { "COM1", 0x80c0 },
73 { "COM2", 0x80c0 },
74 { "COM3", 0x80c0 },
75 { "COM4", 0x80c0 },
76 { "SCSIMGR$", 0xc0c0 },
77 { "HPSCAN", 0xc0c0 }
80 #define GET_DRIVE(path) \
81 (((path)[1] == ':') ? toupper((path)[0]) - 'A' : DOSFS_CurDrive)
83 /* Directory info for DOSFS_ReadDir */
84 typedef struct
86 DIR *dir;
87 #ifdef VFAT_IOCTL_READDIR_BOTH
88 int fd;
89 char short_name[12];
90 KERNEL_DIRENT dirent[2];
91 #endif
92 } DOS_DIR;
94 /* Info structure for FindFirstFile handle */
95 typedef struct
97 LPSTR path;
98 LPSTR long_mask;
99 LPSTR short_mask;
100 BYTE attr;
101 int drive;
102 int cur_pos;
103 DOS_DIR *dir;
104 } FIND_FIRST_INFO;
108 /***********************************************************************
109 * DOSFS_ValidDOSName
111 * Return 1 if Unix file 'name' is also a valid MS-DOS name
112 * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
113 * File name can be terminated by '\0', '\\' or '/'.
115 static int DOSFS_ValidDOSName( const char *name, int ignore_case )
117 static const char invalid_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS;
118 const char *p = name;
119 const char *invalid = ignore_case ? (invalid_chars + 26) : invalid_chars;
120 int len = 0;
122 if (*p == '.')
124 /* Check for "." and ".." */
125 p++;
126 if (*p == '.') p++;
127 /* All other names beginning with '.' are invalid */
128 return (IS_END_OF_NAME(*p));
130 while (!IS_END_OF_NAME(*p))
132 if (strchr( invalid, *p )) return 0; /* Invalid char */
133 if (*p == '.') break; /* Start of the extension */
134 if (++len > 8) return 0; /* Name too long */
135 p++;
137 if (*p != '.') return 1; /* End of name */
138 p++;
139 if (IS_END_OF_NAME(*p)) return 0; /* Empty extension not allowed */
140 len = 0;
141 while (!IS_END_OF_NAME(*p))
143 if (strchr( invalid, *p )) return 0; /* Invalid char */
144 if (*p == '.') return 0; /* Second extension not allowed */
145 if (++len > 3) return 0; /* Extension too long */
146 p++;
148 return 1;
152 /***********************************************************************
153 * DOSFS_ToDosFCBFormat
155 * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
156 * expanding wild cards and converting to upper-case in the process.
157 * File name can be terminated by '\0', '\\' or '/'.
158 * Return FALSE if the name is not a valid DOS name.
159 * 'buffer' must be at least 12 characters long.
161 BOOL DOSFS_ToDosFCBFormat( LPCSTR name, LPSTR buffer )
163 static const char invalid_chars[] = INVALID_DOS_CHARS;
164 const char *p = name;
165 int i;
167 /* Check for "." and ".." */
168 if (*p == '.')
170 p++;
171 strcpy( buffer, ". " );
172 if (*p == '.')
174 buffer[1] = '.';
175 p++;
177 return (!*p || (*p == '/') || (*p == '\\'));
180 for (i = 0; i < 8; i++)
182 switch(*p)
184 case '\0':
185 case '\\':
186 case '/':
187 case '.':
188 buffer[i] = ' ';
189 break;
190 case '?':
191 p++;
192 /* fall through */
193 case '*':
194 buffer[i] = '?';
195 break;
196 default:
197 if (strchr( invalid_chars, *p )) return FALSE;
198 buffer[i] = toupper(*p);
199 p++;
200 break;
204 if (*p == '*')
206 /* Skip all chars after wildcard up to first dot */
207 while (*p && (*p != '/') && (*p != '\\') && (*p != '.')) p++;
209 else
211 /* Check if name too long */
212 if (*p && (*p != '/') && (*p != '\\') && (*p != '.')) return FALSE;
214 if (*p == '.') p++; /* Skip dot */
216 for (i = 8; i < 11; i++)
218 switch(*p)
220 case '\0':
221 case '\\':
222 case '/':
223 buffer[i] = ' ';
224 break;
225 case '.':
226 return FALSE; /* Second extension not allowed */
227 case '?':
228 p++;
229 /* fall through */
230 case '*':
231 buffer[i] = '?';
232 break;
233 default:
234 if (strchr( invalid_chars, *p )) return FALSE;
235 buffer[i] = toupper(*p);
236 p++;
237 break;
240 buffer[11] = '\0';
241 return TRUE;
245 /***********************************************************************
246 * DOSFS_ToDosDTAFormat
248 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
249 * converting to upper-case in the process.
250 * File name can be terminated by '\0', '\\' or '/'.
251 * 'buffer' must be at least 13 characters long.
253 static void DOSFS_ToDosDTAFormat( LPCSTR name, LPSTR buffer )
255 char *p;
257 memcpy( buffer, name, 8 );
258 for (p = buffer + 8; (p > buffer) && (p[-1] == ' '); p--);
259 *p++ = '.';
260 memcpy( p, name + 8, 3 );
261 for (p += 3; p[-1] == ' '; p--);
262 if (p[-1] == '.') p--;
263 *p = '\0';
267 /***********************************************************************
268 * DOSFS_MatchShort
270 * Check a DOS file name against a mask (both in FCB format).
272 static int DOSFS_MatchShort( const char *mask, const char *name )
274 int i;
275 for (i = 11; i > 0; i--, mask++, name++)
276 if ((*mask != '?') && (*mask != *name)) return 0;
277 return 1;
281 /***********************************************************************
282 * DOSFS_MatchLong
284 * Check a long file name against a mask.
286 static int DOSFS_MatchLong( const char *mask, const char *name,
287 int case_sensitive )
289 if (!strcmp( mask, "*.*" )) return 1;
290 while (*name && *mask)
292 if (*mask == '*')
294 mask++;
295 while (*mask == '*') mask++; /* Skip consecutive '*' */
296 if (!*mask) return 1;
297 if (case_sensitive) while (*name && (*name != *mask)) name++;
298 else while (*name && (toupper(*name) != toupper(*mask))) name++;
299 if (!*name) break;
301 else if (*mask != '?')
303 if (case_sensitive)
305 if (*mask != *name) return 0;
307 else if (toupper(*mask) != toupper(*name)) return 0;
309 mask++;
310 name++;
312 if (*mask == '.') mask++; /* Ignore trailing '.' in mask */
313 return (!*name && !*mask);
317 /***********************************************************************
318 * DOSFS_OpenDir
320 static DOS_DIR *DOSFS_OpenDir( LPCSTR path )
322 DOS_DIR *dir = HeapAlloc( SystemHeap, 0, sizeof(*dir) );
323 if (!dir)
325 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
326 return NULL;
329 /* Treat empty path as root directory. This simplifies path split into
330 directory and mask in several other places */
331 if (!*path) path = "/";
333 #ifdef VFAT_IOCTL_READDIR_BOTH
335 /* Check if the VFAT ioctl is supported on this directory */
337 if ((dir->fd = open( path, O_RDONLY )) != -1)
339 if (ioctl( dir->fd, VFAT_IOCTL_READDIR_BOTH, (long)dir->dirent ) == -1)
341 close( dir->fd );
342 dir->fd = -1;
344 else
346 /* Set the file pointer back at the start of the directory */
347 lseek( dir->fd, 0, SEEK_SET );
348 dir->dir = NULL;
349 return dir;
352 #endif /* VFAT_IOCTL_READDIR_BOTH */
354 /* Now use the standard opendir/readdir interface */
356 if (!(dir->dir = opendir( path )))
358 HeapFree( SystemHeap, 0, dir );
359 return NULL;
361 return dir;
365 /***********************************************************************
366 * DOSFS_CloseDir
368 static void DOSFS_CloseDir( DOS_DIR *dir )
370 #ifdef VFAT_IOCTL_READDIR_BOTH
371 if (dir->fd != -1) close( dir->fd );
372 #endif /* VFAT_IOCTL_READDIR_BOTH */
373 if (dir->dir) closedir( dir->dir );
374 HeapFree( SystemHeap, 0, dir );
378 /***********************************************************************
379 * DOSFS_ReadDir
381 static BOOL DOSFS_ReadDir( DOS_DIR *dir, LPCSTR *long_name,
382 LPCSTR *short_name )
384 struct dirent *dirent;
386 #ifdef VFAT_IOCTL_READDIR_BOTH
387 if (dir->fd != -1)
389 if (ioctl( dir->fd, VFAT_IOCTL_READDIR_BOTH, (long)dir->dirent ) != -1) {
390 if (!dir->dirent[0].d_reclen) return FALSE;
391 if (!DOSFS_ToDosFCBFormat( dir->dirent[0].d_name, dir->short_name ))
392 dir->short_name[0] = '\0';
393 *short_name = dir->short_name;
394 if (dir->dirent[1].d_name[0]) *long_name = dir->dirent[1].d_name;
395 else *long_name = dir->dirent[0].d_name;
396 return TRUE;
399 #endif /* VFAT_IOCTL_READDIR_BOTH */
401 if (!(dirent = readdir( dir->dir ))) return FALSE;
402 *long_name = dirent->d_name;
403 *short_name = NULL;
404 return TRUE;
408 /***********************************************************************
409 * DOSFS_Hash
411 * Transform a Unix file name into a hashed DOS name. If the name is a valid
412 * DOS name, it is converted to upper-case; otherwise it is replaced by a
413 * hashed version that fits in 8.3 format.
414 * File name can be terminated by '\0', '\\' or '/'.
415 * 'buffer' must be at least 13 characters long.
417 static void DOSFS_Hash( LPCSTR name, LPSTR buffer, BOOL dir_format,
418 BOOL ignore_case )
420 static const char invalid_chars[] = INVALID_DOS_CHARS "~.";
421 static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
423 const char *p, *ext;
424 char *dst;
425 unsigned short hash;
426 int i;
428 if (dir_format) strcpy( buffer, " " );
430 if (DOSFS_ValidDOSName( name, ignore_case ))
432 /* Check for '.' and '..' */
433 if (*name == '.')
435 buffer[0] = '.';
436 if (!dir_format) buffer[1] = buffer[2] = '\0';
437 if (name[1] == '.') buffer[1] = '.';
438 return;
441 /* Simply copy the name, converting to uppercase */
443 for (dst = buffer; !IS_END_OF_NAME(*name) && (*name != '.'); name++)
444 *dst++ = toupper(*name);
445 if (*name == '.')
447 if (dir_format) dst = buffer + 8;
448 else *dst++ = '.';
449 for (name++; !IS_END_OF_NAME(*name); name++)
450 *dst++ = toupper(*name);
452 if (!dir_format) *dst = '\0';
453 return;
456 /* Compute the hash code of the file name */
457 /* If you know something about hash functions, feel free to */
458 /* insert a better algorithm here... */
459 if (ignore_case)
461 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
462 hash = (hash<<3) ^ (hash>>5) ^ tolower(*p) ^ (tolower(p[1]) << 8);
463 hash = (hash<<3) ^ (hash>>5) ^ tolower(*p); /* Last character*/
465 else
467 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
468 hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
469 hash = (hash << 3) ^ (hash >> 5) ^ *p; /* Last character */
472 /* Find last dot for start of the extension */
473 for (p = name+1, ext = NULL; !IS_END_OF_NAME(*p); p++)
474 if (*p == '.') ext = p;
475 if (ext && IS_END_OF_NAME(ext[1]))
476 ext = NULL; /* Empty extension ignored */
478 /* Copy first 4 chars, replacing invalid chars with '_' */
479 for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
481 if (IS_END_OF_NAME(*p) || (p == ext)) break;
482 *dst++ = strchr( invalid_chars, *p ) ? '_' : toupper(*p);
484 /* Pad to 5 chars with '~' */
485 while (i-- >= 0) *dst++ = '~';
487 /* Insert hash code converted to 3 ASCII chars */
488 *dst++ = hash_chars[(hash >> 10) & 0x1f];
489 *dst++ = hash_chars[(hash >> 5) & 0x1f];
490 *dst++ = hash_chars[hash & 0x1f];
492 /* Copy the first 3 chars of the extension (if any) */
493 if (ext)
495 if (!dir_format) *dst++ = '.';
496 for (i = 3, ext++; (i > 0) && !IS_END_OF_NAME(*ext); i--, ext++)
497 *dst++ = strchr( invalid_chars, *ext ) ? '_' : toupper(*ext);
499 if (!dir_format) *dst = '\0';
503 /***********************************************************************
504 * DOSFS_FindUnixName
506 * Find the Unix file name in a given directory that corresponds to
507 * a file name (either in Unix or DOS format).
508 * File name can be terminated by '\0', '\\' or '/'.
509 * Return TRUE if OK, FALSE if no file name matches.
511 * 'long_buf' must be at least 'long_len' characters long. If the long name
512 * turns out to be larger than that, the function returns FALSE.
513 * 'short_buf' must be at least 13 characters long.
515 BOOL DOSFS_FindUnixName( LPCSTR path, LPCSTR name, LPSTR long_buf,
516 INT long_len, LPSTR short_buf, BOOL ignore_case)
518 DOS_DIR *dir;
519 LPCSTR long_name, short_name;
520 char dos_name[12], tmp_buf[13];
521 BOOL ret;
523 const char *p = strchr( name, '/' );
524 int len = p ? (int)(p - name) : strlen(name);
525 if ((p = strchr( name, '\\' ))) len = MIN( (int)(p - name), len );
526 /* Ignore trailing dots */
527 while (len > 1 && name[len-1] == '.') len--;
528 if (long_len < len + 1) return FALSE;
530 TRACE_(dosfs)("%s,%s\n", path, name );
532 if (!DOSFS_ToDosFCBFormat( name, dos_name )) dos_name[0] = '\0';
534 if (!(dir = DOSFS_OpenDir( path )))
536 WARN_(dosfs)("(%s,%s): can't open dir: %s\n",
537 path, name, strerror(errno) );
538 return FALSE;
541 while ((ret = DOSFS_ReadDir( dir, &long_name, &short_name )))
543 /* Check against Unix name */
544 if (len == strlen(long_name))
546 if (!ignore_case)
548 if (!lstrncmpA( long_name, name, len )) break;
550 else
552 if (!lstrncmpiA( long_name, name, len )) break;
555 if (dos_name[0])
557 /* Check against hashed DOS name */
558 if (!short_name)
560 DOSFS_Hash( long_name, tmp_buf, TRUE, ignore_case );
561 short_name = tmp_buf;
563 if (!strcmp( dos_name, short_name )) break;
566 if (ret)
568 if (long_buf) strcpy( long_buf, long_name );
569 if (short_buf)
571 if (short_name)
572 DOSFS_ToDosDTAFormat( short_name, short_buf );
573 else
574 DOSFS_Hash( long_name, short_buf, FALSE, ignore_case );
576 TRACE_(dosfs)("(%s,%s) -> %s (%s)\n",
577 path, name, long_name, short_buf ? short_buf : "***");
579 else
580 WARN_(dosfs)("'%s' not found in '%s'\n", name, path);
581 DOSFS_CloseDir( dir );
582 return ret;
586 /***********************************************************************
587 * DOSFS_GetDevice
589 * Check if a DOS file name represents a DOS device and return the device.
591 const DOS_DEVICE *DOSFS_GetDevice( const char *name )
593 int i;
594 const char *p;
596 if (!name) return NULL; /* if FILE_DupUnixHandle was used */
597 if (name[0] && (name[1] == ':')) name += 2;
598 if ((p = strrchr( name, '/' ))) name = p + 1;
599 if ((p = strrchr( name, '\\' ))) name = p + 1;
600 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
602 const char *dev = DOSFS_Devices[i].name;
603 if (!lstrncmpiA( dev, name, strlen(dev) ))
605 p = name + strlen( dev );
606 if (!*p || (*p == '.')) return &DOSFS_Devices[i];
609 return NULL;
613 /***********************************************************************
614 * DOSFS_GetDeviceByHandle
616 const DOS_DEVICE *DOSFS_GetDeviceByHandle( HFILE hFile )
618 struct get_file_info_request *req = get_req_buffer();
620 req->handle = hFile;
621 if (!server_call( REQ_GET_FILE_INFO ) && (req->type == FILE_TYPE_UNKNOWN))
623 if ((req->attr >= 0) &&
624 (req->attr < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0])))
625 return &DOSFS_Devices[req->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 HFILE DOSFS_OpenDevice( const char *name, DWORD access )
638 int i;
639 const char *p;
641 if (!name) return (HFILE)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 (!lstrncmpiA( 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 HFILE to_dup;
659 HFILE 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_ERROR;
670 break;
672 if (!DuplicateHandle( GetCurrentProcess(), to_dup, GetCurrentProcess(),
673 &handle, 0, FALSE, DUPLICATE_SAME_ACCESS ))
674 handle = HFILE_ERROR;
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 );
683 HFILE r;
684 char devname[40];
685 PROFILE_GetWineIniString("serialports",name,"",devname,sizeof devname);
687 if(devname[0])
689 TRACE_(file)("DOSFS_OpenDevice %s is %s\n",
690 DOSFS_Devices[i].name,devname);
691 r = FILE_CreateFile( devname, access,
692 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
693 OPEN_EXISTING, 0, -1 );
694 TRACE_(file)("Create_File return %08X\n",r);
695 return r;
699 FIXME_(dosfs)("device open %s not supported (yet)\n",DOSFS_Devices[i].name);
700 return HFILE_ERROR;
704 return HFILE_ERROR;
708 /***********************************************************************
709 * DOSFS_GetPathDrive
711 * Get the drive specified by a given path name (DOS or Unix format).
713 static int DOSFS_GetPathDrive( const char **name )
715 int drive;
716 const char *p = *name;
718 if (*p && (p[1] == ':'))
720 drive = toupper(*p) - 'A';
721 *name += 2;
723 else if (*p == '/') /* Absolute Unix path? */
725 if ((drive = DRIVE_FindDriveRoot( name )) == -1)
727 MESSAGE("Warning: %s not accessible from a DOS drive\n", *name );
728 /* Assume it really was a DOS name */
729 drive = DRIVE_GetCurrentDrive();
732 else drive = DRIVE_GetCurrentDrive();
734 if (!DRIVE_IsValid(drive))
736 SetLastError( ERROR_INVALID_DRIVE );
737 return -1;
739 return drive;
743 /***********************************************************************
744 * DOSFS_GetFullName
746 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
747 * Unix name / short DOS name pair.
748 * Return FALSE if one of the path components does not exist. The last path
749 * component is only checked if 'check_last' is non-zero.
750 * The buffers pointed to by 'long_buf' and 'short_buf' must be
751 * at least MAX_PATHNAME_LEN long.
753 BOOL DOSFS_GetFullName( LPCSTR name, BOOL check_last, DOS_FULL_NAME *full )
755 BOOL found;
756 UINT flags;
757 char *p_l, *p_s, *root;
759 TRACE_(dosfs)("%s (last=%d)\n",
760 name, check_last );
762 if ((full->drive = DOSFS_GetPathDrive( &name )) == -1) return FALSE;
763 flags = DRIVE_GetFlags( full->drive );
765 lstrcpynA( full->long_name, DRIVE_GetRoot( full->drive ),
766 sizeof(full->long_name) );
767 if (full->long_name[1]) root = full->long_name + strlen(full->long_name);
768 else root = full->long_name; /* root directory */
770 strcpy( full->short_name, "A:\\" );
771 full->short_name[0] += full->drive;
773 if ((*name == '\\') || (*name == '/')) /* Absolute path */
775 while ((*name == '\\') || (*name == '/')) name++;
777 else /* Relative path */
779 lstrcpynA( root + 1, DRIVE_GetUnixCwd( full->drive ),
780 sizeof(full->long_name) - (root - full->long_name) - 1 );
781 if (root[1]) *root = '/';
782 lstrcpynA( full->short_name + 3, DRIVE_GetDosCwd( full->drive ),
783 sizeof(full->short_name) - 3 );
786 p_l = full->long_name[1] ? full->long_name + strlen(full->long_name)
787 : full->long_name;
788 p_s = full->short_name[3] ? full->short_name + strlen(full->short_name)
789 : full->short_name + 2;
790 found = TRUE;
792 while (*name && found)
794 /* Check for '.' and '..' */
796 if (*name == '.')
798 if (IS_END_OF_NAME(name[1]))
800 name++;
801 while ((*name == '\\') || (*name == '/')) name++;
802 continue;
804 else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
806 name += 2;
807 while ((*name == '\\') || (*name == '/')) name++;
808 while ((p_l > root) && (*p_l != '/')) p_l--;
809 while ((p_s > full->short_name + 2) && (*p_s != '\\')) p_s--;
810 *p_l = *p_s = '\0'; /* Remove trailing separator */
811 continue;
815 /* Make sure buffers are large enough */
817 if ((p_s >= full->short_name + sizeof(full->short_name) - 14) ||
818 (p_l >= full->long_name + sizeof(full->long_name) - 1))
820 SetLastError( ERROR_PATH_NOT_FOUND );
821 return FALSE;
824 /* Get the long and short name matching the file name */
826 if ((found = DOSFS_FindUnixName( full->long_name, name, p_l + 1,
827 sizeof(full->long_name) - (p_l - full->long_name) - 1,
828 p_s + 1, !(flags & DRIVE_CASE_SENSITIVE) )))
830 *p_l++ = '/';
831 p_l += strlen(p_l);
832 *p_s++ = '\\';
833 p_s += strlen(p_s);
834 while (!IS_END_OF_NAME(*name)) name++;
836 else if (!check_last)
838 *p_l++ = '/';
839 *p_s++ = '\\';
840 while (!IS_END_OF_NAME(*name) &&
841 (p_s < full->short_name + sizeof(full->short_name) - 1) &&
842 (p_l < full->long_name + sizeof(full->long_name) - 1))
844 *p_s++ = tolower(*name);
845 /* If the drive is case-sensitive we want to create new */
846 /* files in lower-case otherwise we can't reopen them */
847 /* under the same short name. */
848 if (flags & DRIVE_CASE_SENSITIVE) *p_l++ = tolower(*name);
849 else *p_l++ = *name;
850 name++;
852 *p_l = *p_s = '\0';
854 while ((*name == '\\') || (*name == '/')) name++;
857 if (!found)
859 if (check_last)
861 SetLastError( ERROR_FILE_NOT_FOUND );
862 return FALSE;
864 if (*name) /* Not last */
866 SetLastError( ERROR_PATH_NOT_FOUND );
867 return FALSE;
870 if (!full->long_name[0]) strcpy( full->long_name, "/" );
871 if (!full->short_name[2]) strcpy( full->short_name + 2, "\\" );
872 TRACE_(dosfs)("returning %s = %s\n",
873 full->long_name, full->short_name );
874 return TRUE;
878 /***********************************************************************
879 * GetShortPathNameA (KERNEL32.271)
881 * NOTES
882 * observed:
883 * longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
884 * *longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0
886 * more observations ( with NT 3.51 (WinDD) ):
887 * longpath <= 8.3 -> just copy longpath to shortpath
888 * longpath > 8.3 ->
889 * a) file does not exist -> return 0, LastError = ERROR_FILE_NOT_FOUND
890 * b) file does exist -> set the short filename.
891 * - trailing slashes are reproduced in the short name, even if the
892 * file is not a directory
893 * - the absolute/relative path of the short name is reproduced in the
894 * same way, like the long name
895 * - longpath and shortpath may have the same adress
896 * Peter Ganten, 1999
898 DWORD WINAPI GetShortPathNameA( LPCSTR longpath, LPSTR shortpath,
899 DWORD shortlen )
901 DOS_FULL_NAME full_name;
902 LPSTR tmpshortpath;
903 DWORD length = 0, pos = 0;
904 INT start=-1, end=-1, tmplen;
906 if (!longpath) {
907 SetLastError(ERROR_INVALID_PARAMETER);
908 return 0;
910 if (!longpath[0]) {
911 SetLastError(ERROR_BAD_PATHNAME);
912 return 0;
915 tmpshortpath = HeapAlloc( GetProcessHeap(), 0, MAX_PATHNAME_LEN );
916 if ( !tmpshortpath ) {
917 SetLastError ( ERROR_NOT_ENOUGH_MEMORY );
918 return 0;
921 /* Check for Drive-Letter */
922 if ( longpath[1] == ':' ) {
923 lstrcpynA ( tmpshortpath, longpath, 3 );
924 length = 2;
925 pos = 2;
928 /* loop over each part of the name */
929 while ( longpath[pos] ) {
931 if (( longpath[pos] == '\\' ) ||
932 ( longpath[pos+1] == '\0' ) ||
933 ( longpath[pos] == '/')) {
935 if ( start != -1 ) {
936 if ( DOSFS_ValidDOSName ( longpath + start, TRUE )) {
937 tmplen = end - start + ( (( longpath[pos] == '\\' ) || ( longpath[pos] == '/' )) ? 1 : 2 );
938 lstrcpynA ( tmpshortpath+length, longpath+start, tmplen );
939 length += tmplen - 1;
941 else {
942 DOSFS_Hash ( longpath + start, tmpshortpath+length, FALSE, FALSE );
943 length = lstrlenA ( tmpshortpath );
945 /* Check if the path, up to this point exists */
946 if ( !DOSFS_GetFullName ( tmpshortpath, TRUE, &full_name ) ) {
947 SetLastError ( ERROR_FILE_NOT_FOUND );
948 return 0;
954 if (( longpath[pos] == '\\' ) || ( longpath[pos] == '/' )) {
955 tmpshortpath[length] = '\\';
956 tmpshortpath[length+1]='\0';
957 length++;
959 pos++;
961 start = -1;
962 end = -1;
963 continue;
966 if ( start == -1 ) {
967 start = pos;
969 pos++;
970 end = pos;
973 lstrcpynA ( shortpath, tmpshortpath, shortlen );
974 length = lstrlenA ( tmpshortpath );
975 HeapFree ( GetProcessHeap(), 0, tmpshortpath );
977 return length;
981 /***********************************************************************
982 * GetShortPathName32W (KERNEL32.272)
984 DWORD WINAPI GetShortPathNameW( LPCWSTR longpath, LPWSTR shortpath,
985 DWORD shortlen )
987 LPSTR longpathA, shortpathA;
988 DWORD ret = 0;
990 longpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, longpath );
991 shortpathA = HEAP_xalloc ( GetProcessHeap(), 0, shortlen );
993 ret = GetShortPathNameA ( longpathA, shortpathA, shortlen );
994 lstrcpynAtoW ( shortpath, shortpathA, shortlen );
996 HeapFree( GetProcessHeap(), 0, longpathA );
997 HeapFree( GetProcessHeap(), 0, shortpathA );
999 return ret;
1003 /***********************************************************************
1004 * GetLongPathName32A (KERNEL32.xxx)
1006 DWORD WINAPI GetLongPathNameA( LPCSTR shortpath, LPSTR longpath,
1007 DWORD longlen )
1009 DOS_FULL_NAME full_name;
1010 char *p;
1011 char *longfilename;
1012 DWORD shortpathlen;
1014 if (!DOSFS_GetFullName( shortpath, TRUE, &full_name )) return 0;
1015 lstrcpynA( longpath, full_name.short_name, longlen );
1016 /* Do some hackery to get the long filename.
1017 * FIXME: Would be better if it returned the
1018 * long version of the directories too
1020 longfilename = strrchr(full_name.long_name, '/')+1;
1021 if (longpath != NULL) {
1022 if ((p = strrchr( longpath, '\\' )) != NULL) {
1023 p++;
1024 longlen -= (p-longpath);
1025 lstrcpynA( p, longfilename , longlen);
1028 shortpathlen =
1029 ((strrchr( full_name.short_name, '\\' ) - full_name.short_name) + 1);
1030 return shortpathlen + strlen( longfilename );
1034 /***********************************************************************
1035 * GetLongPathName32W (KERNEL32.269)
1037 DWORD WINAPI GetLongPathNameW( LPCWSTR shortpath, LPWSTR longpath,
1038 DWORD longlen )
1040 DOS_FULL_NAME full_name;
1041 DWORD ret = 0;
1042 LPSTR shortpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, shortpath );
1044 /* FIXME: is it correct to always return a fully qualified short path? */
1045 if (DOSFS_GetFullName( shortpathA, TRUE, &full_name ))
1047 ret = strlen( full_name.short_name );
1048 lstrcpynAtoW( longpath, full_name.long_name, longlen );
1050 HeapFree( GetProcessHeap(), 0, shortpathA );
1051 return ret;
1055 /***********************************************************************
1056 * DOSFS_DoGetFullPathName
1058 * Implementation of GetFullPathName32A/W.
1060 static DWORD DOSFS_DoGetFullPathName( LPCSTR name, DWORD len, LPSTR result,
1061 BOOL unicode )
1063 char buffer[MAX_PATHNAME_LEN];
1064 int drive;
1065 char *p;
1066 DWORD ret;
1068 /* last possible position for a char != 0 */
1069 char *endchar = buffer + sizeof(buffer) - 2;
1070 *endchar = '\0';
1072 TRACE_(dosfs)("converting '%s'\n", name );
1074 if (!name || ((drive = DOSFS_GetPathDrive( &name )) == -1) )
1075 { SetLastError( ERROR_INVALID_PARAMETER );
1076 return 0;
1079 p = buffer;
1080 *p++ = 'A' + drive;
1081 *p++ = ':';
1082 if (IS_END_OF_NAME(*name) && (*name)) /* Absolute path */
1084 while (((*name == '\\') || (*name == '/')) && (!*endchar) )
1085 *p++ = *name++;
1087 else /* Relative path or empty path */
1089 *p++ = '\\';
1090 lstrcpynA( p, DRIVE_GetDosCwd(drive), sizeof(buffer) - 4 );
1091 if ( *p )
1093 p += strlen(p);
1094 *p++ = '\\';
1097 *p = '\0';
1099 while (*name)
1101 if (*name == '.')
1103 if (IS_END_OF_NAME(name[1]))
1105 name++;
1106 while ((*name == '\\') || (*name == '/')) name++;
1107 continue;
1109 else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
1111 name += 2;
1112 while ((*name == '\\') || (*name == '/')) name++;
1114 if (p < buffer + 3) /* no previous dir component */
1115 continue;
1116 p--; /* skip previously added '\\' */
1117 while ((*p == '\\') || (*p == '/')) p--;
1118 /* skip previous dir component */
1119 while ((*p != '\\') && (*p != '/')) p--;
1120 p++;
1121 continue;
1124 if ( *endchar )
1125 { SetLastError( ERROR_PATH_NOT_FOUND );
1126 return 0;
1128 while (!IS_END_OF_NAME(*name) && (!*endchar) )
1129 *p++ = *name++;
1130 while (((*name == '\\') || (*name == '/')) && (!*endchar) )
1131 *p++ = *name++;
1133 *p = '\0';
1135 if (!(DRIVE_GetFlags(drive) & DRIVE_CASE_PRESERVING))
1136 CharUpperA( buffer );
1138 if (result)
1140 if (unicode)
1141 lstrcpynAtoW( (LPWSTR)result, buffer, len );
1142 else
1143 lstrcpynA( result, buffer, len );
1146 TRACE_(dosfs)("returning '%s'\n", buffer );
1148 /* If the lpBuffer buffer is too small, the return value is the
1149 size of the buffer, in characters, required to hold the path. */
1151 ret = strlen(buffer);
1153 if (ret >= len )
1154 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1156 return ret;
1160 /***********************************************************************
1161 * GetFullPathName32A (KERNEL32.272)
1162 * NOTES
1163 * if the path closed with '\', *lastpart is 0
1165 DWORD WINAPI GetFullPathNameA( LPCSTR name, DWORD len, LPSTR buffer,
1166 LPSTR *lastpart )
1168 DWORD ret = DOSFS_DoGetFullPathName( name, len, buffer, FALSE );
1169 if (ret && buffer && lastpart)
1171 LPSTR p = buffer + strlen(buffer);
1173 if (*p != '\\')
1175 while ((p > buffer + 2) && (*p != '\\')) p--;
1176 *lastpart = p + 1;
1178 else *lastpart = NULL;
1180 return ret;
1184 /***********************************************************************
1185 * GetFullPathName32W (KERNEL32.273)
1187 DWORD WINAPI GetFullPathNameW( LPCWSTR name, DWORD len, LPWSTR buffer,
1188 LPWSTR *lastpart )
1190 LPSTR nameA = HEAP_strdupWtoA( GetProcessHeap(), 0, name );
1191 DWORD ret = DOSFS_DoGetFullPathName( nameA, len, (LPSTR)buffer, TRUE );
1192 HeapFree( GetProcessHeap(), 0, nameA );
1193 if (ret && buffer && lastpart)
1195 LPWSTR p = buffer + lstrlenW(buffer);
1196 if (*p != (WCHAR)'\\')
1198 while ((p > buffer + 2) && (*p != (WCHAR)'\\')) p--;
1199 *lastpart = p + 1;
1201 else *lastpart = NULL;
1203 return ret;
1206 /***********************************************************************
1207 * DOSFS_FindNextEx
1209 static int DOSFS_FindNextEx( FIND_FIRST_INFO *info, WIN32_FIND_DATAA *entry )
1211 BYTE attr = info->attr | FA_UNUSED | FA_ARCHIVE | FA_RDONLY;
1212 UINT flags = DRIVE_GetFlags( info->drive );
1213 char *p, buffer[MAX_PATHNAME_LEN];
1214 const char *drive_path;
1215 int drive_root;
1216 LPCSTR long_name, short_name;
1217 BY_HANDLE_FILE_INFORMATION fileinfo;
1218 char dos_name[13];
1220 if ((info->attr & ~(FA_UNUSED | FA_ARCHIVE | FA_RDONLY)) == FA_LABEL)
1222 if (info->cur_pos) return 0;
1223 entry->dwFileAttributes = FILE_ATTRIBUTE_LABEL;
1224 DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftCreationTime, 0 );
1225 DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftLastAccessTime, 0 );
1226 DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftLastWriteTime, 0 );
1227 entry->nFileSizeHigh = 0;
1228 entry->nFileSizeLow = 0;
1229 entry->dwReserved0 = 0;
1230 entry->dwReserved1 = 0;
1231 DOSFS_ToDosDTAFormat( DRIVE_GetLabel( info->drive ), entry->cFileName );
1232 strcpy( entry->cAlternateFileName, entry->cFileName );
1233 info->cur_pos++;
1234 return 1;
1237 drive_path = info->path + strlen(DRIVE_GetRoot( info->drive ));
1238 while ((*drive_path == '/') || (*drive_path == '\\')) drive_path++;
1239 drive_root = !*drive_path;
1241 lstrcpynA( buffer, info->path, sizeof(buffer) - 1 );
1242 strcat( buffer, "/" );
1243 p = buffer + strlen(buffer);
1245 while (DOSFS_ReadDir( info->dir, &long_name, &short_name ))
1247 info->cur_pos++;
1249 /* Don't return '.' and '..' in the root of the drive */
1250 if (drive_root && (long_name[0] == '.') &&
1251 (!long_name[1] || ((long_name[1] == '.') && !long_name[2])))
1252 continue;
1254 /* Check the long mask */
1256 if (info->long_mask)
1258 if (!DOSFS_MatchLong( info->long_mask, long_name,
1259 flags & DRIVE_CASE_SENSITIVE )) continue;
1262 /* Check the short mask */
1264 if (info->short_mask)
1266 if (!short_name)
1268 DOSFS_Hash( long_name, dos_name, TRUE,
1269 !(flags & DRIVE_CASE_SENSITIVE) );
1270 short_name = dos_name;
1272 if (!DOSFS_MatchShort( info->short_mask, short_name )) continue;
1275 /* Check the file attributes */
1277 lstrcpynA( p, long_name, sizeof(buffer) - (int)(p - buffer) );
1278 if (!FILE_Stat( buffer, &fileinfo ))
1280 WARN_(dosfs)("can't stat %s\n", buffer);
1281 continue;
1283 if (fileinfo.dwFileAttributes & ~attr) continue;
1285 /* We now have a matching entry; fill the result and return */
1287 entry->dwFileAttributes = fileinfo.dwFileAttributes;
1288 entry->ftCreationTime = fileinfo.ftCreationTime;
1289 entry->ftLastAccessTime = fileinfo.ftLastAccessTime;
1290 entry->ftLastWriteTime = fileinfo.ftLastWriteTime;
1291 entry->nFileSizeHigh = fileinfo.nFileSizeHigh;
1292 entry->nFileSizeLow = fileinfo.nFileSizeLow;
1294 if (short_name)
1295 DOSFS_ToDosDTAFormat( short_name, entry->cAlternateFileName );
1296 else
1297 DOSFS_Hash( long_name, entry->cAlternateFileName, FALSE,
1298 !(flags & DRIVE_CASE_SENSITIVE) );
1300 lstrcpynA( entry->cFileName, long_name, sizeof(entry->cFileName) );
1301 if (!(flags & DRIVE_CASE_PRESERVING)) CharLowerA( entry->cFileName );
1302 TRACE_(dosfs)("returning %s (%s) %02lx %ld\n",
1303 entry->cFileName, entry->cAlternateFileName,
1304 entry->dwFileAttributes, entry->nFileSizeLow );
1305 return 1;
1307 return 0; /* End of directory */
1310 /***********************************************************************
1311 * DOSFS_FindNext
1313 * Find the next matching file. Return the number of entries read to find
1314 * the matching one, or 0 if no more entries.
1315 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
1316 * file name mask. Either or both can be NULL.
1318 * NOTE: This is supposed to be only called by the int21 emulation
1319 * routines. Thus, we should own the Win16Mutex anyway.
1320 * Nevertheless, we explicitly enter it to ensure the static
1321 * directory cache is protected.
1323 int DOSFS_FindNext( const char *path, const char *short_mask,
1324 const char *long_mask, int drive, BYTE attr,
1325 int skip, WIN32_FIND_DATAA *entry )
1327 static FIND_FIRST_INFO info = { NULL };
1328 LPCSTR short_name, long_name;
1329 int count;
1331 SYSLEVEL_EnterWin16Lock();
1333 /* Check the cached directory */
1334 if (!(info.dir && info.path == path && info.short_mask == short_mask
1335 && info.long_mask == long_mask && info.drive == drive
1336 && info.attr == attr && info.cur_pos <= skip))
1338 /* Not in the cache, open it anew */
1339 if (info.dir) DOSFS_CloseDir( info.dir );
1341 info.path = (LPSTR)path;
1342 info.long_mask = (LPSTR)long_mask;
1343 info.short_mask = (LPSTR)short_mask;
1344 info.attr = attr;
1345 info.drive = drive;
1346 info.cur_pos = 0;
1347 info.dir = DOSFS_OpenDir( info.path );
1350 /* Skip to desired position */
1351 while (info.cur_pos < skip)
1352 if (info.dir && DOSFS_ReadDir( info.dir, &long_name, &short_name ))
1353 info.cur_pos++;
1354 else
1355 break;
1357 if (info.dir && info.cur_pos == skip && DOSFS_FindNextEx( &info, entry ))
1358 count = info.cur_pos - skip;
1359 else
1360 count = 0;
1362 if (!count)
1364 if (info.dir) DOSFS_CloseDir( info.dir );
1365 memset( &info, '\0', sizeof(info) );
1368 SYSLEVEL_LeaveWin16Lock();
1370 return count;
1375 /*************************************************************************
1376 * FindFirstFile16 (KERNEL.413)
1378 HANDLE16 WINAPI FindFirstFile16( LPCSTR path, WIN32_FIND_DATAA *data )
1380 DOS_FULL_NAME full_name;
1381 HGLOBAL16 handle;
1382 FIND_FIRST_INFO *info;
1384 data->dwReserved0 = data->dwReserved1 = 0x0;
1385 if (!path) return 0;
1386 if (!DOSFS_GetFullName( path, FALSE, &full_name ))
1387 return INVALID_HANDLE_VALUE16;
1388 if (!(handle = GlobalAlloc16( GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO) )))
1389 return INVALID_HANDLE_VALUE16;
1390 info = (FIND_FIRST_INFO *)GlobalLock16( handle );
1391 info->path = HEAP_strdupA( SystemHeap, 0, full_name.long_name );
1392 info->long_mask = strrchr( info->path, '/' );
1393 *(info->long_mask++) = '\0';
1394 info->short_mask = NULL;
1395 info->attr = 0xff;
1396 if (path[0] && (path[1] == ':')) info->drive = toupper(*path) - 'A';
1397 else info->drive = DRIVE_GetCurrentDrive();
1398 info->cur_pos = 0;
1400 info->dir = DOSFS_OpenDir( info->path );
1402 GlobalUnlock16( handle );
1403 if (!FindNextFile16( handle, data ))
1405 FindClose16( handle );
1406 SetLastError( ERROR_NO_MORE_FILES );
1407 return INVALID_HANDLE_VALUE16;
1409 return handle;
1413 /*************************************************************************
1414 * FindFirstFile32A (KERNEL32.123)
1416 HANDLE WINAPI FindFirstFileA( LPCSTR path, WIN32_FIND_DATAA *data )
1418 HANDLE handle = FindFirstFile16( path, data );
1419 if (handle == INVALID_HANDLE_VALUE16) return INVALID_HANDLE_VALUE;
1420 return handle;
1424 /*************************************************************************
1425 * FindFirstFile32W (KERNEL32.124)
1427 HANDLE WINAPI FindFirstFileW( LPCWSTR path, WIN32_FIND_DATAW *data )
1429 WIN32_FIND_DATAA dataA;
1430 LPSTR pathA = HEAP_strdupWtoA( GetProcessHeap(), 0, path );
1431 HANDLE handle = FindFirstFileA( pathA, &dataA );
1432 HeapFree( GetProcessHeap(), 0, pathA );
1433 if (handle != INVALID_HANDLE_VALUE)
1435 data->dwFileAttributes = dataA.dwFileAttributes;
1436 data->ftCreationTime = dataA.ftCreationTime;
1437 data->ftLastAccessTime = dataA.ftLastAccessTime;
1438 data->ftLastWriteTime = dataA.ftLastWriteTime;
1439 data->nFileSizeHigh = dataA.nFileSizeHigh;
1440 data->nFileSizeLow = dataA.nFileSizeLow;
1441 lstrcpyAtoW( data->cFileName, dataA.cFileName );
1442 lstrcpyAtoW( data->cAlternateFileName, dataA.cAlternateFileName );
1444 return handle;
1448 /*************************************************************************
1449 * FindNextFile16 (KERNEL.414)
1451 BOOL16 WINAPI FindNextFile16( HANDLE16 handle, WIN32_FIND_DATAA *data )
1453 FIND_FIRST_INFO *info;
1455 if (!(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
1457 SetLastError( ERROR_INVALID_HANDLE );
1458 return FALSE;
1460 GlobalUnlock16( handle );
1461 if (!info->path || !info->dir)
1463 SetLastError( ERROR_NO_MORE_FILES );
1464 return FALSE;
1466 if (!DOSFS_FindNextEx( info, data ))
1468 DOSFS_CloseDir( info->dir ); info->dir = NULL;
1469 HeapFree( SystemHeap, 0, info->path );
1470 info->path = info->long_mask = NULL;
1471 SetLastError( ERROR_NO_MORE_FILES );
1472 return FALSE;
1474 return TRUE;
1478 /*************************************************************************
1479 * FindNextFile32A (KERNEL32.126)
1481 BOOL WINAPI FindNextFileA( HANDLE handle, WIN32_FIND_DATAA *data )
1483 return FindNextFile16( handle, data );
1487 /*************************************************************************
1488 * FindNextFile32W (KERNEL32.127)
1490 BOOL WINAPI FindNextFileW( HANDLE handle, WIN32_FIND_DATAW *data )
1492 WIN32_FIND_DATAA dataA;
1493 if (!FindNextFileA( handle, &dataA )) return FALSE;
1494 data->dwFileAttributes = dataA.dwFileAttributes;
1495 data->ftCreationTime = dataA.ftCreationTime;
1496 data->ftLastAccessTime = dataA.ftLastAccessTime;
1497 data->ftLastWriteTime = dataA.ftLastWriteTime;
1498 data->nFileSizeHigh = dataA.nFileSizeHigh;
1499 data->nFileSizeLow = dataA.nFileSizeLow;
1500 lstrcpyAtoW( data->cFileName, dataA.cFileName );
1501 lstrcpyAtoW( data->cAlternateFileName, dataA.cAlternateFileName );
1502 return TRUE;
1506 /*************************************************************************
1507 * FindClose16 (KERNEL.415)
1509 BOOL16 WINAPI FindClose16( HANDLE16 handle )
1511 FIND_FIRST_INFO *info;
1513 if ((handle == INVALID_HANDLE_VALUE16) ||
1514 !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
1516 SetLastError( ERROR_INVALID_HANDLE );
1517 return FALSE;
1519 if (info->dir) DOSFS_CloseDir( info->dir );
1520 if (info->path) HeapFree( SystemHeap, 0, info->path );
1521 GlobalUnlock16( handle );
1522 GlobalFree16( handle );
1523 return TRUE;
1527 /*************************************************************************
1528 * FindClose32 (KERNEL32.119)
1530 BOOL WINAPI FindClose( HANDLE handle )
1532 return FindClose16( (HANDLE16)handle );
1536 /***********************************************************************
1537 * DOSFS_UnixTimeToFileTime
1539 * Convert a Unix time to FILETIME format.
1540 * The FILETIME structure is a 64-bit value representing the number of
1541 * 100-nanosecond intervals since January 1, 1601, 0:00.
1542 * 'remainder' is the nonnegative number of 100-ns intervals
1543 * corresponding to the time fraction smaller than 1 second that
1544 * couldn't be stored in the time_t value.
1546 void DOSFS_UnixTimeToFileTime( time_t unix_time, FILETIME *filetime,
1547 DWORD remainder )
1549 /* NOTES:
1551 CONSTANTS:
1552 The time difference between 1 January 1601, 00:00:00 and
1553 1 January 1970, 00:00:00 is 369 years, plus the leap years
1554 from 1604 to 1968, excluding 1700, 1800, 1900.
1555 This makes (1968 - 1600) / 4 - 3 = 89 leap days, and a total
1556 of 134774 days.
1558 Any day in that period had 24 * 60 * 60 = 86400 seconds.
1560 The time difference is 134774 * 86400 * 10000000, which can be written
1561 116444736000000000
1562 27111902 * 2^32 + 3577643008
1563 413 * 2^48 + 45534 * 2^32 + 54590 * 2^16 + 32768
1565 If you find that these constants are buggy, please change them in all
1566 instances in both conversion functions.
1568 VERSIONS:
1569 There are two versions, one of them uses long long variables and
1570 is presumably faster but not ISO C. The other one uses standard C
1571 data types and operations but relies on the assumption that negative
1572 numbers are stored as 2's complement (-1 is 0xffff....). If this
1573 assumption is violated, dates before 1970 will not convert correctly.
1574 This should however work on any reasonable architecture where WINE
1575 will run.
1577 DETAILS:
1579 Take care not to remove the casts. I have tested these functions
1580 (in both versions) for a lot of numbers. I would be interested in
1581 results on other compilers than GCC.
1583 The operations have been designed to account for the possibility
1584 of 64-bit time_t in future UNICES. Even the versions without
1585 internal long long numbers will work if time_t only is 64 bit.
1586 A 32-bit shift, which was necessary for that operation, turned out
1587 not to work correctly in GCC, besides giving the warning. So I
1588 used a double 16-bit shift instead. Numbers are in the ISO version
1589 represented by three limbs, the most significant with 32 bit, the
1590 other two with 16 bit each.
1592 As the modulo-operator % is not well-defined for negative numbers,
1593 negative divisors have been avoided in DOSFS_FileTimeToUnixTime.
1595 There might be quicker ways to do this in C. Certainly so in
1596 assembler.
1598 Claus Fischer, fischer@iue.tuwien.ac.at
1601 #if (SIZEOF_LONG_LONG >= 8)
1602 # define USE_LONG_LONG 1
1603 #else
1604 # define USE_LONG_LONG 0
1605 #endif
1607 #if USE_LONG_LONG /* gcc supports long long type */
1609 long long int t = unix_time;
1610 t *= 10000000;
1611 t += 116444736000000000LL;
1612 t += remainder;
1613 filetime->dwLowDateTime = (UINT)t;
1614 filetime->dwHighDateTime = (UINT)(t >> 32);
1616 #else /* ISO version */
1618 UINT a0; /* 16 bit, low bits */
1619 UINT a1; /* 16 bit, medium bits */
1620 UINT a2; /* 32 bit, high bits */
1622 /* Copy the unix time to a2/a1/a0 */
1623 a0 = unix_time & 0xffff;
1624 a1 = (unix_time >> 16) & 0xffff;
1625 /* This is obsolete if unix_time is only 32 bits, but it does not hurt.
1626 Do not replace this by >> 32, it gives a compiler warning and it does
1627 not work. */
1628 a2 = (unix_time >= 0 ? (unix_time >> 16) >> 16 :
1629 ~((~unix_time >> 16) >> 16));
1631 /* Multiply a by 10000000 (a = a2/a1/a0)
1632 Split the factor into 10000 * 1000 which are both less than 0xffff. */
1633 a0 *= 10000;
1634 a1 = a1 * 10000 + (a0 >> 16);
1635 a2 = a2 * 10000 + (a1 >> 16);
1636 a0 &= 0xffff;
1637 a1 &= 0xffff;
1639 a0 *= 1000;
1640 a1 = a1 * 1000 + (a0 >> 16);
1641 a2 = a2 * 1000 + (a1 >> 16);
1642 a0 &= 0xffff;
1643 a1 &= 0xffff;
1645 /* Add the time difference and the remainder */
1646 a0 += 32768 + (remainder & 0xffff);
1647 a1 += 54590 + (remainder >> 16 ) + (a0 >> 16);
1648 a2 += 27111902 + (a1 >> 16);
1649 a0 &= 0xffff;
1650 a1 &= 0xffff;
1652 /* Set filetime */
1653 filetime->dwLowDateTime = (a1 << 16) + a0;
1654 filetime->dwHighDateTime = a2;
1655 #endif
1659 /***********************************************************************
1660 * DOSFS_FileTimeToUnixTime
1662 * Convert a FILETIME format to Unix time.
1663 * If not NULL, 'remainder' contains the fractional part of the filetime,
1664 * in the range of [0..9999999] (even if time_t is negative).
1666 time_t DOSFS_FileTimeToUnixTime( const FILETIME *filetime, DWORD *remainder )
1668 /* Read the comment in the function DOSFS_UnixTimeToFileTime. */
1669 #if USE_LONG_LONG
1671 long long int t = filetime->dwHighDateTime;
1672 t <<= 32;
1673 t += (UINT)filetime->dwLowDateTime;
1674 t -= 116444736000000000LL;
1675 if (t < 0)
1677 if (remainder) *remainder = 9999999 - (-t - 1) % 10000000;
1678 return -1 - ((-t - 1) / 10000000);
1680 else
1682 if (remainder) *remainder = t % 10000000;
1683 return t / 10000000;
1686 #else /* ISO version */
1688 UINT a0; /* 16 bit, low bits */
1689 UINT a1; /* 16 bit, medium bits */
1690 UINT a2; /* 32 bit, high bits */
1691 UINT r; /* remainder of division */
1692 unsigned int carry; /* carry bit for subtraction */
1693 int negative; /* whether a represents a negative value */
1695 /* Copy the time values to a2/a1/a0 */
1696 a2 = (UINT)filetime->dwHighDateTime;
1697 a1 = ((UINT)filetime->dwLowDateTime ) >> 16;
1698 a0 = ((UINT)filetime->dwLowDateTime ) & 0xffff;
1700 /* Subtract the time difference */
1701 if (a0 >= 32768 ) a0 -= 32768 , carry = 0;
1702 else a0 += (1 << 16) - 32768 , carry = 1;
1704 if (a1 >= 54590 + carry) a1 -= 54590 + carry, carry = 0;
1705 else a1 += (1 << 16) - 54590 - carry, carry = 1;
1707 a2 -= 27111902 + carry;
1709 /* If a is negative, replace a by (-1-a) */
1710 negative = (a2 >= ((UINT)1) << 31);
1711 if (negative)
1713 /* Set a to -a - 1 (a is a2/a1/a0) */
1714 a0 = 0xffff - a0;
1715 a1 = 0xffff - a1;
1716 a2 = ~a2;
1719 /* Divide a by 10000000 (a = a2/a1/a0), put the rest into r.
1720 Split the divisor into 10000 * 1000 which are both less than 0xffff. */
1721 a1 += (a2 % 10000) << 16;
1722 a2 /= 10000;
1723 a0 += (a1 % 10000) << 16;
1724 a1 /= 10000;
1725 r = a0 % 10000;
1726 a0 /= 10000;
1728 a1 += (a2 % 1000) << 16;
1729 a2 /= 1000;
1730 a0 += (a1 % 1000) << 16;
1731 a1 /= 1000;
1732 r += (a0 % 1000) * 10000;
1733 a0 /= 1000;
1735 /* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
1736 if (negative)
1738 /* Set a to -a - 1 (a is a2/a1/a0) */
1739 a0 = 0xffff - a0;
1740 a1 = 0xffff - a1;
1741 a2 = ~a2;
1743 r = 9999999 - r;
1746 if (remainder) *remainder = r;
1748 /* Do not replace this by << 32, it gives a compiler warning and it does
1749 not work. */
1750 return ((((time_t)a2) << 16) << 16) + (a1 << 16) + a0;
1751 #endif
1755 /***********************************************************************
1756 * DosDateTimeToFileTime (KERNEL32.76)
1758 BOOL WINAPI DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft)
1760 struct tm newtm;
1762 newtm.tm_sec = (fattime & 0x1f) * 2;
1763 newtm.tm_min = (fattime >> 5) & 0x3f;
1764 newtm.tm_hour = (fattime >> 11);
1765 newtm.tm_mday = (fatdate & 0x1f);
1766 newtm.tm_mon = ((fatdate >> 5) & 0x0f) - 1;
1767 newtm.tm_year = (fatdate >> 9) + 80;
1768 DOSFS_UnixTimeToFileTime( mktime( &newtm ), ft, 0 );
1769 return TRUE;
1773 /***********************************************************************
1774 * FileTimeToDosDateTime (KERNEL32.111)
1776 BOOL WINAPI FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate,
1777 LPWORD fattime )
1779 time_t unixtime = DOSFS_FileTimeToUnixTime( ft, NULL );
1780 struct tm *tm = localtime( &unixtime );
1781 if (fattime)
1782 *fattime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
1783 if (fatdate)
1784 *fatdate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
1785 + tm->tm_mday;
1786 return TRUE;
1790 /***********************************************************************
1791 * LocalFileTimeToFileTime (KERNEL32.373)
1793 BOOL WINAPI LocalFileTimeToFileTime( const FILETIME *localft,
1794 LPFILETIME utcft )
1796 struct tm *xtm;
1797 DWORD remainder;
1799 /* convert from local to UTC. Perhaps not correct. FIXME */
1800 time_t unixtime = DOSFS_FileTimeToUnixTime( localft, &remainder );
1801 xtm = gmtime( &unixtime );
1802 DOSFS_UnixTimeToFileTime( mktime(xtm), utcft, remainder );
1803 return TRUE;
1807 /***********************************************************************
1808 * FileTimeToLocalFileTime (KERNEL32.112)
1810 BOOL WINAPI FileTimeToLocalFileTime( const FILETIME *utcft,
1811 LPFILETIME localft )
1813 DWORD remainder;
1814 /* convert from UTC to local. Perhaps not correct. FIXME */
1815 time_t unixtime = DOSFS_FileTimeToUnixTime( utcft, &remainder );
1816 #ifdef HAVE_TIMEGM
1817 struct tm *xtm = localtime( &unixtime );
1818 time_t localtime;
1820 localtime = timegm(xtm);
1821 DOSFS_UnixTimeToFileTime( localtime, localft, remainder );
1823 #else
1824 struct tm *xtm,*gtm;
1825 time_t time1,time2;
1827 xtm = localtime( &unixtime );
1828 gtm = gmtime( &unixtime );
1829 time1 = mktime(xtm);
1830 time2 = mktime(gtm);
1831 DOSFS_UnixTimeToFileTime( 2*time1-time2, localft, remainder );
1832 #endif
1833 return TRUE;
1837 /***********************************************************************
1838 * FileTimeToSystemTime (KERNEL32.113)
1840 BOOL WINAPI FileTimeToSystemTime( const FILETIME *ft, LPSYSTEMTIME syst )
1842 struct tm *xtm;
1843 DWORD remainder;
1844 time_t xtime = DOSFS_FileTimeToUnixTime( ft, &remainder );
1845 xtm = gmtime(&xtime);
1846 syst->wYear = xtm->tm_year+1900;
1847 syst->wMonth = xtm->tm_mon + 1;
1848 syst->wDayOfWeek = xtm->tm_wday;
1849 syst->wDay = xtm->tm_mday;
1850 syst->wHour = xtm->tm_hour;
1851 syst->wMinute = xtm->tm_min;
1852 syst->wSecond = xtm->tm_sec;
1853 syst->wMilliseconds = remainder / 10000;
1854 return TRUE;
1857 /***********************************************************************
1858 * QueryDosDeviceA (KERNEL32.413)
1860 * returns array of strings terminated by \0, terminated by \0
1862 DWORD WINAPI QueryDosDeviceA(LPCSTR devname,LPSTR target,DWORD bufsize)
1864 LPSTR s;
1865 char buffer[200];
1867 TRACE_(dosfs)("(%s,...)\n",devname?devname:"<null>");
1868 if (!devname) {
1869 /* return known MSDOS devices */
1870 lstrcpyA(buffer,"CON COM1 COM2 LPT1 NUL ");
1871 while ((s=strchr(buffer,' ')))
1872 *s='\0';
1874 lstrcpynA(target,buffer,bufsize);
1875 return strlen(buffer);
1877 lstrcpyA(buffer,"\\DEV\\");
1878 lstrcatA(buffer,devname);
1879 if ((s=strchr(buffer,':'))) *s='\0';
1880 lstrcpynA(target,buffer,bufsize);
1881 return strlen(buffer);
1885 /***********************************************************************
1886 * QueryDosDeviceW (KERNEL32.414)
1888 * returns array of strings terminated by \0, terminated by \0
1890 DWORD WINAPI QueryDosDeviceW(LPCWSTR devname,LPWSTR target,DWORD bufsize)
1892 LPSTR devnameA = devname?HEAP_strdupWtoA(GetProcessHeap(),0,devname):NULL;
1893 LPSTR targetA = (LPSTR)HEAP_xalloc(GetProcessHeap(),0,bufsize);
1894 DWORD ret = QueryDosDeviceA(devnameA,targetA,bufsize);
1896 lstrcpynAtoW(target,targetA,bufsize);
1897 if (devnameA) HeapFree(GetProcessHeap(),0,devnameA);
1898 if (targetA) HeapFree(GetProcessHeap(),0,targetA);
1899 return ret;
1903 /***********************************************************************
1904 * SystemTimeToFileTime (KERNEL32.526)
1906 BOOL WINAPI SystemTimeToFileTime( const SYSTEMTIME *syst, LPFILETIME ft )
1908 #ifdef HAVE_TIMEGM
1909 struct tm xtm;
1910 time_t utctime;
1911 #else
1912 struct tm xtm,*local_tm,*utc_tm;
1913 time_t localtim,utctime;
1914 #endif
1916 xtm.tm_year = syst->wYear-1900;
1917 xtm.tm_mon = syst->wMonth - 1;
1918 xtm.tm_wday = syst->wDayOfWeek;
1919 xtm.tm_mday = syst->wDay;
1920 xtm.tm_hour = syst->wHour;
1921 xtm.tm_min = syst->wMinute;
1922 xtm.tm_sec = syst->wSecond; /* this is UTC */
1923 xtm.tm_isdst = -1;
1924 #ifdef HAVE_TIMEGM
1925 utctime = timegm(&xtm);
1926 DOSFS_UnixTimeToFileTime( utctime, ft,
1927 syst->wMilliseconds * 10000 );
1928 #else
1929 localtim = mktime(&xtm); /* now we've got local time */
1930 local_tm = localtime(&localtim);
1931 utc_tm = gmtime(&localtim);
1932 utctime = mktime(utc_tm);
1933 DOSFS_UnixTimeToFileTime( 2*localtim -utctime, ft,
1934 syst->wMilliseconds * 10000 );
1935 #endif
1936 return TRUE;
1939 BOOL WINAPI DefineDosDeviceA(DWORD flags,LPCSTR devname,LPCSTR targetpath) {
1940 FIXME_(dosfs)("(0x%08lx,%s,%s),stub!\n",flags,devname,targetpath);
1941 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1942 return FALSE;