Fixed some debug message crashes.
[wine.git] / files / dos_fs.c
blobda174a7b4bcdd0d1046a5e17aad80958a633deb1
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 "wingdi.h"
26 #include "winuser.h"
27 #include "wine/winbase16.h"
28 #include "winerror.h"
29 #include "drive.h"
30 #include "file.h"
31 #include "heap.h"
32 #include "msdos.h"
33 #include "syslevel.h"
34 #include "server.h"
35 #include "process.h"
36 #include "options.h"
37 #include "debugtools.h"
39 DEFAULT_DEBUG_CHANNEL(dosfs)
40 DECLARE_DEBUG_CHANNEL(file)
42 /* Define the VFAT ioctl to get both short and long file names */
43 /* FIXME: is it possible to get this to work on other systems? */
44 #ifdef linux
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 #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, KERNEL_DIRENT [2] )
56 #else /* linux */
57 #undef VFAT_IOCTL_READDIR_BOTH /* just in case... */
58 #endif /* linux */
60 /* Chars we don't want to see in DOS file names */
61 #define INVALID_DOS_CHARS "*?<>|\"+=,;[] \345"
63 static const DOS_DEVICE DOSFS_Devices[] =
64 /* name, device flags (see Int 21/AX=0x4400) */
66 { "CON", 0xc0d3 },
67 { "PRN", 0xa0c0 },
68 { "NUL", 0x80c4 },
69 { "AUX", 0x80c0 },
70 { "LPT1", 0xa0c0 },
71 { "LPT2", 0xa0c0 },
72 { "LPT3", 0xa0c0 },
73 { "LPT4", 0xc0d3 },
74 { "COM1", 0x80c0 },
75 { "COM2", 0x80c0 },
76 { "COM3", 0x80c0 },
77 { "COM4", 0x80c0 },
78 { "SCSIMGR$", 0xc0c0 },
79 { "HPSCAN", 0xc0c0 }
82 #define GET_DRIVE(path) \
83 (((path)[1] == ':') ? toupper((path)[0]) - 'A' : DOSFS_CurDrive)
85 /* Directory info for DOSFS_ReadDir */
86 typedef struct
88 DIR *dir;
89 #ifdef VFAT_IOCTL_READDIR_BOTH
90 int fd;
91 char short_name[12];
92 KERNEL_DIRENT dirent[2];
93 #endif
94 } DOS_DIR;
96 /* Info structure for FindFirstFile handle */
97 typedef struct
99 LPSTR path;
100 LPSTR long_mask;
101 LPSTR short_mask;
102 BYTE attr;
103 int drive;
104 int cur_pos;
105 DOS_DIR *dir;
106 } FIND_FIRST_INFO;
110 /***********************************************************************
111 * DOSFS_ValidDOSName
113 * Return 1 if Unix file 'name' is also a valid MS-DOS name
114 * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
115 * File name can be terminated by '\0', '\\' or '/'.
117 static int DOSFS_ValidDOSName( const char *name, int ignore_case )
119 static const char invalid_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS;
120 const char *p = name;
121 const char *invalid = ignore_case ? (invalid_chars + 26) : invalid_chars;
122 int len = 0;
124 if (*p == '.')
126 /* Check for "." and ".." */
127 p++;
128 if (*p == '.') p++;
129 /* All other names beginning with '.' are invalid */
130 return (IS_END_OF_NAME(*p));
132 while (!IS_END_OF_NAME(*p))
134 if (strchr( invalid, *p )) return 0; /* Invalid char */
135 if (*p == '.') break; /* Start of the extension */
136 if (++len > 8) return 0; /* Name too long */
137 p++;
139 if (*p != '.') return 1; /* End of name */
140 p++;
141 if (IS_END_OF_NAME(*p)) return 0; /* Empty extension not allowed */
142 len = 0;
143 while (!IS_END_OF_NAME(*p))
145 if (strchr( invalid, *p )) return 0; /* Invalid char */
146 if (*p == '.') return 0; /* Second extension not allowed */
147 if (++len > 3) return 0; /* Extension too long */
148 p++;
150 return 1;
154 /***********************************************************************
155 * DOSFS_ToDosFCBFormat
157 * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
158 * expanding wild cards and converting to upper-case in the process.
159 * File name can be terminated by '\0', '\\' or '/'.
160 * Return FALSE if the name is not a valid DOS name.
161 * 'buffer' must be at least 12 characters long.
163 BOOL DOSFS_ToDosFCBFormat( LPCSTR name, LPSTR buffer )
165 static const char invalid_chars[] = INVALID_DOS_CHARS;
166 const char *p = name;
167 int i;
169 /* Check for "." and ".." */
170 if (*p == '.')
172 p++;
173 strcpy( buffer, ". " );
174 if (*p == '.')
176 buffer[1] = '.';
177 p++;
179 return (!*p || (*p == '/') || (*p == '\\'));
182 for (i = 0; i < 8; i++)
184 switch(*p)
186 case '\0':
187 case '\\':
188 case '/':
189 case '.':
190 buffer[i] = ' ';
191 break;
192 case '?':
193 p++;
194 /* fall through */
195 case '*':
196 buffer[i] = '?';
197 break;
198 default:
199 if (strchr( invalid_chars, *p )) return FALSE;
200 buffer[i] = toupper(*p);
201 p++;
202 break;
206 if (*p == '*')
208 /* Skip all chars after wildcard up to first dot */
209 while (*p && (*p != '/') && (*p != '\\') && (*p != '.')) p++;
211 else
213 /* Check if name too long */
214 if (*p && (*p != '/') && (*p != '\\') && (*p != '.')) return FALSE;
216 if (*p == '.') p++; /* Skip dot */
218 for (i = 8; i < 11; i++)
220 switch(*p)
222 case '\0':
223 case '\\':
224 case '/':
225 buffer[i] = ' ';
226 break;
227 case '.':
228 return FALSE; /* Second extension not allowed */
229 case '?':
230 p++;
231 /* fall through */
232 case '*':
233 buffer[i] = '?';
234 break;
235 default:
236 if (strchr( invalid_chars, *p )) return FALSE;
237 buffer[i] = toupper(*p);
238 p++;
239 break;
242 buffer[11] = '\0';
243 return TRUE;
247 /***********************************************************************
248 * DOSFS_ToDosDTAFormat
250 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
251 * converting to upper-case in the process.
252 * File name can be terminated by '\0', '\\' or '/'.
253 * 'buffer' must be at least 13 characters long.
255 static void DOSFS_ToDosDTAFormat( LPCSTR name, LPSTR buffer )
257 char *p;
259 memcpy( buffer, name, 8 );
260 for (p = buffer + 8; (p > buffer) && (p[-1] == ' '); p--);
261 *p++ = '.';
262 memcpy( p, name + 8, 3 );
263 for (p += 3; p[-1] == ' '; p--);
264 if (p[-1] == '.') p--;
265 *p = '\0';
269 /***********************************************************************
270 * DOSFS_MatchShort
272 * Check a DOS file name against a mask (both in FCB format).
274 static int DOSFS_MatchShort( const char *mask, const char *name )
276 int i;
277 for (i = 11; i > 0; i--, mask++, name++)
278 if ((*mask != '?') && (*mask != *name)) return 0;
279 return 1;
283 /***********************************************************************
284 * DOSFS_MatchLong
286 * Check a long file name against a mask.
288 static int DOSFS_MatchLong( const char *mask, const char *name,
289 int case_sensitive )
291 if (!strcmp( mask, "*.*" )) return 1;
292 while (*name && *mask)
294 if (*mask == '*')
296 mask++;
297 while (*mask == '*') mask++; /* Skip consecutive '*' */
298 if (!*mask) return 1;
299 if (case_sensitive) while (*name && (*name != *mask)) name++;
300 else while (*name && (toupper(*name) != toupper(*mask))) name++;
301 if (!*name) break;
303 else if (*mask != '?')
305 if (case_sensitive)
307 if (*mask != *name) return 0;
309 else if (toupper(*mask) != toupper(*name)) return 0;
311 mask++;
312 name++;
314 if (*mask == '.') mask++; /* Ignore trailing '.' in mask */
315 return (!*name && !*mask);
319 /***********************************************************************
320 * DOSFS_OpenDir
322 static DOS_DIR *DOSFS_OpenDir( LPCSTR path )
324 DOS_DIR *dir = HeapAlloc( GetProcessHeap(), 0, sizeof(*dir) );
325 if (!dir)
327 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
328 return NULL;
331 /* Treat empty path as root directory. This simplifies path split into
332 directory and mask in several other places */
333 if (!*path) path = "/";
335 #ifdef VFAT_IOCTL_READDIR_BOTH
337 /* Check if the VFAT ioctl is supported on this directory */
339 if ((dir->fd = open( path, O_RDONLY )) != -1)
341 if (ioctl( dir->fd, VFAT_IOCTL_READDIR_BOTH, (long)dir->dirent ) == -1)
343 close( dir->fd );
344 dir->fd = -1;
346 else
348 /* Set the file pointer back at the start of the directory */
349 lseek( dir->fd, 0, SEEK_SET );
350 dir->dir = NULL;
351 return dir;
354 #endif /* VFAT_IOCTL_READDIR_BOTH */
356 /* Now use the standard opendir/readdir interface */
358 if (!(dir->dir = opendir( path )))
360 HeapFree( GetProcessHeap(), 0, dir );
361 return NULL;
363 return dir;
367 /***********************************************************************
368 * DOSFS_CloseDir
370 static void DOSFS_CloseDir( DOS_DIR *dir )
372 #ifdef VFAT_IOCTL_READDIR_BOTH
373 if (dir->fd != -1) close( dir->fd );
374 #endif /* VFAT_IOCTL_READDIR_BOTH */
375 if (dir->dir) closedir( dir->dir );
376 HeapFree( GetProcessHeap(), 0, dir );
380 /***********************************************************************
381 * DOSFS_ReadDir
383 static BOOL DOSFS_ReadDir( DOS_DIR *dir, LPCSTR *long_name,
384 LPCSTR *short_name )
386 struct dirent *dirent;
388 #ifdef VFAT_IOCTL_READDIR_BOTH
389 if (dir->fd != -1)
391 if (ioctl( dir->fd, VFAT_IOCTL_READDIR_BOTH, (long)dir->dirent ) != -1) {
392 if (!dir->dirent[0].d_reclen) return FALSE;
393 if (!DOSFS_ToDosFCBFormat( dir->dirent[0].d_name, dir->short_name ))
394 dir->short_name[0] = '\0';
395 *short_name = dir->short_name;
396 if (dir->dirent[1].d_name[0]) *long_name = dir->dirent[1].d_name;
397 else *long_name = dir->dirent[0].d_name;
398 return TRUE;
401 #endif /* VFAT_IOCTL_READDIR_BOTH */
403 if (!(dirent = readdir( dir->dir ))) return FALSE;
404 *long_name = dirent->d_name;
405 *short_name = NULL;
406 return TRUE;
410 /***********************************************************************
411 * DOSFS_Hash
413 * Transform a Unix file name into a hashed DOS name. If the name is a valid
414 * DOS name, it is converted to upper-case; otherwise it is replaced by a
415 * hashed version that fits in 8.3 format.
416 * File name can be terminated by '\0', '\\' or '/'.
417 * 'buffer' must be at least 13 characters long.
419 static void DOSFS_Hash( LPCSTR name, LPSTR buffer, BOOL dir_format,
420 BOOL ignore_case )
422 static const char invalid_chars[] = INVALID_DOS_CHARS "~.";
423 static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
425 const char *p, *ext;
426 char *dst;
427 unsigned short hash;
428 int i;
430 if (dir_format) strcpy( buffer, " " );
432 if (DOSFS_ValidDOSName( name, ignore_case ))
434 /* Check for '.' and '..' */
435 if (*name == '.')
437 buffer[0] = '.';
438 if (!dir_format) buffer[1] = buffer[2] = '\0';
439 if (name[1] == '.') buffer[1] = '.';
440 return;
443 /* Simply copy the name, converting to uppercase */
445 for (dst = buffer; !IS_END_OF_NAME(*name) && (*name != '.'); name++)
446 *dst++ = toupper(*name);
447 if (*name == '.')
449 if (dir_format) dst = buffer + 8;
450 else *dst++ = '.';
451 for (name++; !IS_END_OF_NAME(*name); name++)
452 *dst++ = toupper(*name);
454 if (!dir_format) *dst = '\0';
455 return;
458 /* Compute the hash code of the file name */
459 /* If you know something about hash functions, feel free to */
460 /* insert a better algorithm here... */
461 if (ignore_case)
463 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
464 hash = (hash<<3) ^ (hash>>5) ^ tolower(*p) ^ (tolower(p[1]) << 8);
465 hash = (hash<<3) ^ (hash>>5) ^ tolower(*p); /* Last character*/
467 else
469 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
470 hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
471 hash = (hash << 3) ^ (hash >> 5) ^ *p; /* Last character */
474 /* Find last dot for start of the extension */
475 for (p = name+1, ext = NULL; !IS_END_OF_NAME(*p); p++)
476 if (*p == '.') ext = p;
477 if (ext && IS_END_OF_NAME(ext[1]))
478 ext = NULL; /* Empty extension ignored */
480 /* Copy first 4 chars, replacing invalid chars with '_' */
481 for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
483 if (IS_END_OF_NAME(*p) || (p == ext)) break;
484 *dst++ = strchr( invalid_chars, *p ) ? '_' : toupper(*p);
486 /* Pad to 5 chars with '~' */
487 while (i-- >= 0) *dst++ = '~';
489 /* Insert hash code converted to 3 ASCII chars */
490 *dst++ = hash_chars[(hash >> 10) & 0x1f];
491 *dst++ = hash_chars[(hash >> 5) & 0x1f];
492 *dst++ = hash_chars[hash & 0x1f];
494 /* Copy the first 3 chars of the extension (if any) */
495 if (ext)
497 if (!dir_format) *dst++ = '.';
498 for (i = 3, ext++; (i > 0) && !IS_END_OF_NAME(*ext); i--, ext++)
499 *dst++ = strchr( invalid_chars, *ext ) ? '_' : toupper(*ext);
501 if (!dir_format) *dst = '\0';
505 /***********************************************************************
506 * DOSFS_FindUnixName
508 * Find the Unix file name in a given directory that corresponds to
509 * a file name (either in Unix or DOS format).
510 * File name can be terminated by '\0', '\\' or '/'.
511 * Return TRUE if OK, FALSE if no file name matches.
513 * 'long_buf' must be at least 'long_len' characters long. If the long name
514 * turns out to be larger than that, the function returns FALSE.
515 * 'short_buf' must be at least 13 characters long.
517 BOOL DOSFS_FindUnixName( LPCSTR path, LPCSTR name, LPSTR long_buf,
518 INT long_len, LPSTR short_buf, BOOL ignore_case)
520 DOS_DIR *dir;
521 LPCSTR long_name, short_name;
522 char dos_name[12], tmp_buf[13];
523 BOOL ret;
525 const char *p = strchr( name, '/' );
526 int len = p ? (int)(p - name) : strlen(name);
527 if ((p = strchr( name, '\\' ))) len = MIN( (int)(p - name), len );
528 /* Ignore trailing dots */
529 while (len > 1 && name[len-1] == '.') len--;
530 if (long_len < len + 1) return FALSE;
532 TRACE("%s,%s\n", path, name );
534 if (!DOSFS_ToDosFCBFormat( name, dos_name )) dos_name[0] = '\0';
536 if (!(dir = DOSFS_OpenDir( path )))
538 WARN("(%s,%s): can't open dir: %s\n",
539 path, name, strerror(errno) );
540 return FALSE;
543 while ((ret = DOSFS_ReadDir( dir, &long_name, &short_name )))
545 /* Check against Unix name */
546 if (len == strlen(long_name))
548 if (!ignore_case)
550 if (!strncmp( long_name, name, len )) break;
552 else
554 if (!lstrncmpiA( long_name, name, len )) break;
557 if (dos_name[0])
559 /* Check against hashed DOS name */
560 if (!short_name)
562 DOSFS_Hash( long_name, tmp_buf, TRUE, ignore_case );
563 short_name = tmp_buf;
565 if (!strcmp( dos_name, short_name )) break;
568 if (ret)
570 if (long_buf) strcpy( long_buf, long_name );
571 if (short_buf)
573 if (short_name)
574 DOSFS_ToDosDTAFormat( short_name, short_buf );
575 else
576 DOSFS_Hash( long_name, short_buf, FALSE, ignore_case );
578 TRACE("(%s,%s) -> %s (%s)\n",
579 path, name, long_name, short_buf ? short_buf : "***");
581 else
582 WARN("'%s' not found in '%s'\n", name, path);
583 DOSFS_CloseDir( dir );
584 return ret;
588 /***********************************************************************
589 * DOSFS_GetDevice
591 * Check if a DOS file name represents a DOS device and return the device.
593 const DOS_DEVICE *DOSFS_GetDevice( const char *name )
595 int i;
596 const char *p;
598 if (!name) return NULL; /* if FILE_DupUnixHandle was used */
599 if (name[0] && (name[1] == ':')) name += 2;
600 if ((p = strrchr( name, '/' ))) name = p + 1;
601 if ((p = strrchr( name, '\\' ))) name = p + 1;
602 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
604 const char *dev = DOSFS_Devices[i].name;
605 if (!lstrncmpiA( dev, name, strlen(dev) ))
607 p = name + strlen( dev );
608 if (!*p || (*p == '.')) return &DOSFS_Devices[i];
611 return NULL;
615 /***********************************************************************
616 * DOSFS_GetDeviceByHandle
618 const DOS_DEVICE *DOSFS_GetDeviceByHandle( HFILE hFile )
620 struct get_file_info_request *req = get_req_buffer();
622 req->handle = hFile;
623 if (!server_call( REQ_GET_FILE_INFO ) && (req->type == FILE_TYPE_UNKNOWN))
625 if ((req->attr >= 0) &&
626 (req->attr < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0])))
627 return &DOSFS_Devices[req->attr];
629 return NULL;
633 /***********************************************************************
634 * DOSFS_OpenDevice
636 * Open a DOS device. This might not map 1:1 into the UNIX device concept.
638 HFILE DOSFS_OpenDevice( const char *name, DWORD access )
640 int i;
641 const char *p;
643 if (!name) return (HFILE)NULL; /* if FILE_DupUnixHandle was used */
644 if (name[0] && (name[1] == ':')) name += 2;
645 if ((p = strrchr( name, '/' ))) name = p + 1;
646 if ((p = strrchr( name, '\\' ))) name = p + 1;
647 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
649 const char *dev = DOSFS_Devices[i].name;
650 if (!lstrncmpiA( dev, name, strlen(dev) ))
652 p = name + strlen( dev );
653 if (!*p || (*p == '.')) {
654 /* got it */
655 if (!strcmp(DOSFS_Devices[i].name,"NUL"))
656 return FILE_CreateFile( "/dev/null", access,
657 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
658 OPEN_EXISTING, 0, -1 );
659 if (!strcmp(DOSFS_Devices[i].name,"CON")) {
660 HFILE to_dup;
661 HFILE handle;
662 switch (access & (GENERIC_READ|GENERIC_WRITE)) {
663 case GENERIC_READ:
664 to_dup = GetStdHandle( STD_INPUT_HANDLE );
665 break;
666 case GENERIC_WRITE:
667 to_dup = GetStdHandle( STD_OUTPUT_HANDLE );
668 break;
669 default:
670 FIXME("can't open CON read/write\n");
671 return HFILE_ERROR;
672 break;
674 if (!DuplicateHandle( GetCurrentProcess(), to_dup, GetCurrentProcess(),
675 &handle, 0, FALSE, DUPLICATE_SAME_ACCESS ))
676 handle = HFILE_ERROR;
677 return handle;
679 if (!strcmp(DOSFS_Devices[i].name,"SCSIMGR$") ||
680 !strcmp(DOSFS_Devices[i].name,"HPSCAN"))
682 return FILE_CreateDevice( i, access, NULL );
685 HFILE r;
686 char devname[40];
687 PROFILE_GetWineIniString("serialports",name,"",devname,sizeof devname);
689 if(devname[0])
691 TRACE_(file)("DOSFS_OpenDevice %s is %s\n",
692 DOSFS_Devices[i].name,devname);
693 r = FILE_CreateFile( devname, access,
694 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
695 OPEN_EXISTING, 0, -1 );
696 TRACE_(file)("Create_File return %08X\n",r);
697 return r;
701 FIXME("device open %s not supported (yet)\n",DOSFS_Devices[i].name);
702 return HFILE_ERROR;
706 return HFILE_ERROR;
710 /***********************************************************************
711 * DOSFS_GetPathDrive
713 * Get the drive specified by a given path name (DOS or Unix format).
715 static int DOSFS_GetPathDrive( const char **name )
717 int drive;
718 const char *p = *name;
720 if (*p && (p[1] == ':'))
722 drive = toupper(*p) - 'A';
723 *name += 2;
725 else if (*p == '/') /* Absolute Unix path? */
727 if ((drive = DRIVE_FindDriveRoot( name )) == -1)
729 MESSAGE("Warning: %s not accessible from a DOS drive\n", *name );
730 /* Assume it really was a DOS name */
731 drive = DRIVE_GetCurrentDrive();
734 else drive = DRIVE_GetCurrentDrive();
736 if (!DRIVE_IsValid(drive))
738 SetLastError( ERROR_INVALID_DRIVE );
739 return -1;
741 return drive;
745 /***********************************************************************
746 * DOSFS_GetFullName
748 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
749 * Unix name / short DOS name pair.
750 * Return FALSE if one of the path components does not exist. The last path
751 * component is only checked if 'check_last' is non-zero.
752 * The buffers pointed to by 'long_buf' and 'short_buf' must be
753 * at least MAX_PATHNAME_LEN long.
755 BOOL DOSFS_GetFullName( LPCSTR name, BOOL check_last, DOS_FULL_NAME *full )
757 BOOL found;
758 UINT flags;
759 char *p_l, *p_s, *root;
761 TRACE("%s (last=%d)\n", name, check_last );
763 if ((full->drive = DOSFS_GetPathDrive( &name )) == -1) return FALSE;
764 flags = DRIVE_GetFlags( full->drive );
766 lstrcpynA( full->long_name, DRIVE_GetRoot( full->drive ),
767 sizeof(full->long_name) );
768 if (full->long_name[1]) root = full->long_name + strlen(full->long_name);
769 else root = full->long_name; /* root directory */
771 strcpy( full->short_name, "A:\\" );
772 full->short_name[0] += full->drive;
774 if ((*name == '\\') || (*name == '/')) /* Absolute path */
776 while ((*name == '\\') || (*name == '/')) name++;
778 else /* Relative path */
780 lstrcpynA( root + 1, DRIVE_GetUnixCwd( full->drive ),
781 sizeof(full->long_name) - (root - full->long_name) - 1 );
782 if (root[1]) *root = '/';
783 lstrcpynA( full->short_name + 3, DRIVE_GetDosCwd( full->drive ),
784 sizeof(full->short_name) - 3 );
787 p_l = full->long_name[1] ? full->long_name + strlen(full->long_name)
788 : full->long_name;
789 p_s = full->short_name[3] ? full->short_name + strlen(full->short_name)
790 : full->short_name + 2;
791 found = TRUE;
793 while (*name && found)
795 /* Check for '.' and '..' */
797 if (*name == '.')
799 if (IS_END_OF_NAME(name[1]))
801 name++;
802 while ((*name == '\\') || (*name == '/')) name++;
803 continue;
805 else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
807 name += 2;
808 while ((*name == '\\') || (*name == '/')) name++;
809 while ((p_l > root) && (*p_l != '/')) p_l--;
810 while ((p_s > full->short_name + 2) && (*p_s != '\\')) p_s--;
811 *p_l = *p_s = '\0'; /* Remove trailing separator */
812 continue;
816 /* Make sure buffers are large enough */
818 if ((p_s >= full->short_name + sizeof(full->short_name) - 14) ||
819 (p_l >= full->long_name + sizeof(full->long_name) - 1))
821 SetLastError( ERROR_PATH_NOT_FOUND );
822 return FALSE;
825 /* Get the long and short name matching the file name */
827 if ((found = DOSFS_FindUnixName( full->long_name, name, p_l + 1,
828 sizeof(full->long_name) - (p_l - full->long_name) - 1,
829 p_s + 1, !(flags & DRIVE_CASE_SENSITIVE) )))
831 *p_l++ = '/';
832 p_l += strlen(p_l);
833 *p_s++ = '\\';
834 p_s += strlen(p_s);
835 while (!IS_END_OF_NAME(*name)) name++;
837 else if (!check_last)
839 *p_l++ = '/';
840 *p_s++ = '\\';
841 while (!IS_END_OF_NAME(*name) &&
842 (p_s < full->short_name + sizeof(full->short_name) - 1) &&
843 (p_l < full->long_name + sizeof(full->long_name) - 1))
845 *p_s++ = tolower(*name);
846 /* If the drive is case-sensitive we want to create new */
847 /* files in lower-case otherwise we can't reopen them */
848 /* under the same short name. */
849 if (flags & DRIVE_CASE_SENSITIVE) *p_l++ = tolower(*name);
850 else *p_l++ = *name;
851 name++;
853 *p_l = *p_s = '\0';
855 while ((*name == '\\') || (*name == '/')) name++;
858 if (!found)
860 if (check_last)
862 SetLastError( ERROR_FILE_NOT_FOUND );
863 return FALSE;
865 if (*name) /* Not last */
867 SetLastError( ERROR_PATH_NOT_FOUND );
868 return FALSE;
871 if (!full->long_name[0]) strcpy( full->long_name, "/" );
872 if (!full->short_name[2]) strcpy( full->short_name + 2, "\\" );
873 TRACE("returning %s = %s\n", 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 like found
894 * in 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 sp = 0, lp = 0;
904 int tmplen, drive;
905 UINT flags;
907 TRACE("%s\n", debugstr_a(longpath));
909 if (!longpath) {
910 SetLastError(ERROR_INVALID_PARAMETER);
911 return 0;
913 if (!longpath[0]) {
914 SetLastError(ERROR_BAD_PATHNAME);
915 return 0;
918 if ( ( tmpshortpath = HeapAlloc ( GetProcessHeap(), 0, MAX_PATHNAME_LEN ) ) == NULL ) {
919 SetLastError ( ERROR_NOT_ENOUGH_MEMORY );
920 return 0;
923 /* check for drive letter */
924 if ( longpath[1] == ':' ) {
925 tmpshortpath[0] = longpath[0];
926 tmpshortpath[1] = ':';
927 sp = 2;
930 if ( ( drive = DOSFS_GetPathDrive ( &longpath )) == -1 ) return 0;
931 flags = DRIVE_GetFlags ( drive );
933 while ( longpath[lp] ) {
935 /* check for path delimiters and reproduce them */
936 if ( longpath[lp] == '\\' || longpath[lp] == '/' ) {
937 tmpshortpath[sp] = longpath[lp];
938 sp++;
939 lp++;
940 continue;
943 tmplen = strcspn ( longpath + lp, "\\/" );
944 lstrcpynA ( tmpshortpath+sp, longpath + lp, tmplen+1 );
946 /* Check, if the current element is a valid dos name */
947 if ( DOSFS_ValidDOSName ( longpath + lp, !(flags & DRIVE_CASE_SENSITIVE) ) ) {
948 sp += tmplen;
949 lp += tmplen;
950 continue;
953 /* Check if the file exists and use the existing file name */
954 if ( DOSFS_GetFullName ( tmpshortpath, TRUE, &full_name ) ) {
955 lstrcpyA ( tmpshortpath+sp, strrchr ( full_name.short_name, '\\' ) + 1 );
956 sp += lstrlenA ( tmpshortpath+sp );
957 lp += tmplen;
958 continue;
961 TRACE("not found!\n" );
962 SetLastError ( ERROR_FILE_NOT_FOUND );
963 return 0;
966 lstrcpynA ( shortpath, tmpshortpath, shortlen );
967 TRACE("returning %s\n", debugstr_a(shortpath) );
968 tmplen = lstrlenA ( tmpshortpath );
969 HeapFree ( GetProcessHeap(), 0, tmpshortpath );
971 return tmplen;
975 /***********************************************************************
976 * GetShortPathName32W (KERNEL32.272)
978 DWORD WINAPI GetShortPathNameW( LPCWSTR longpath, LPWSTR shortpath,
979 DWORD shortlen )
981 LPSTR longpathA, shortpathA;
982 DWORD ret = 0;
984 longpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, longpath );
985 shortpathA = HEAP_xalloc ( GetProcessHeap(), 0, shortlen );
987 ret = GetShortPathNameA ( longpathA, shortpathA, shortlen );
988 lstrcpynAtoW ( shortpath, shortpathA, shortlen );
990 HeapFree( GetProcessHeap(), 0, longpathA );
991 HeapFree( GetProcessHeap(), 0, shortpathA );
993 return ret;
997 /***********************************************************************
998 * GetLongPathName32A (KERNEL32.xxx)
1000 DWORD WINAPI GetLongPathNameA( LPCSTR shortpath, LPSTR longpath,
1001 DWORD longlen )
1003 DOS_FULL_NAME full_name;
1004 char *p, *r, *ll, *ss;
1006 if (!DOSFS_GetFullName( shortpath, TRUE, &full_name )) return 0;
1007 lstrcpynA( longpath, full_name.short_name, longlen );
1009 /* Do some hackery to get the long filename. */
1011 if (longpath) {
1012 ss=longpath+strlen(longpath);
1013 ll=full_name.long_name+strlen(full_name.long_name);
1014 p=NULL;
1015 while (ss>=longpath)
1017 /* FIXME: aren't we more paranoid, than needed? */
1018 while ((ss[0]=='\\') && (ss>=longpath)) ss--;
1019 p=ss;
1020 while ((ss[0]!='\\') && (ss>=longpath)) ss--;
1021 if (ss>=longpath)
1023 /* FIXME: aren't we more paranoid, than needed? */
1024 while ((ll[0]=='/') && (ll>=full_name.long_name)) ll--;
1025 while ((ll[0]!='/') && (ll>=full_name.long_name)) ll--;
1026 if (ll<full_name.long_name)
1028 ERR("Bad longname! (ss=%s ll=%s)\n This should never happen !\n"
1029 ,ss ,ll );
1030 return 0;
1035 /* FIXME: fix for names like "C:\\" (ie. with more '\'s) */
1036 if (p && p[2])
1038 p+=1;
1039 if ((p-longpath)>0) longlen -= (p-longpath);
1040 lstrcpynA( p, ll , longlen);
1042 /* Now, change all '/' to '\' */
1043 for (r=p; r<(p+longlen); r++ )
1044 if (r[0]=='/') r[0]='\\';
1045 return strlen(longpath) - strlen(p) + longlen;
1049 return strlen(longpath);
1053 /***********************************************************************
1054 * GetLongPathName32W (KERNEL32.269)
1056 DWORD WINAPI GetLongPathNameW( LPCWSTR shortpath, LPWSTR longpath,
1057 DWORD longlen )
1059 DOS_FULL_NAME full_name;
1060 DWORD ret = 0;
1061 LPSTR shortpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, shortpath );
1063 /* FIXME: is it correct to always return a fully qualified short path? */
1064 if (DOSFS_GetFullName( shortpathA, TRUE, &full_name ))
1066 ret = strlen( full_name.short_name );
1067 lstrcpynAtoW( longpath, full_name.long_name, longlen );
1069 HeapFree( GetProcessHeap(), 0, shortpathA );
1070 return ret;
1074 /***********************************************************************
1075 * DOSFS_DoGetFullPathName
1077 * Implementation of GetFullPathName32A/W.
1079 static DWORD DOSFS_DoGetFullPathName( LPCSTR name, DWORD len, LPSTR result,
1080 BOOL unicode )
1082 char buffer[MAX_PATHNAME_LEN];
1083 int drive;
1084 char *p;
1085 DWORD ret;
1087 /* Address of the last byte in the buffer */
1088 char *endbuf = buffer + sizeof(buffer) - 1;
1090 TRACE("converting '%s'\n", name );
1092 if (!name || ((drive = DOSFS_GetPathDrive( &name )) == -1) )
1094 SetLastError( ERROR_INVALID_PARAMETER );
1095 return 0;
1098 p = buffer;
1099 *p++ = 'A' + drive;
1100 *p++ = ':';
1101 *p++ = '\\';
1102 if ((*name!='/') && (*name!='\\'))
1104 /* Relative path or empty path */
1105 lstrcpynA( p, DRIVE_GetDosCwd(drive), sizeof(buffer) - 4 );
1106 if ( *p )
1108 p += strlen(p);
1109 *p++ = '\\';
1113 while (*name)
1115 while ((*name == '\\') || (*name == '/'))
1116 name++;
1117 if (*name == '.')
1119 if (IS_END_OF_NAME(name[1]))
1121 name++;
1122 continue;
1124 else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
1126 if (p == buffer + 3) {
1127 /* no previous dir component */
1128 SetLastError( ERROR_PATH_NOT_FOUND );
1129 return 0;
1131 /* skip previously added '\\' */
1132 p-=2;
1133 /* skip previous dir component */
1134 while (*p != '\\')
1135 p--;
1136 p++;
1138 name += 2;
1139 continue;
1142 while (!IS_END_OF_NAME(*name) && (p<endbuf) )
1143 *p++ = *name++;
1144 if ((p<endbuf) && ((*name == '\\') || (*name == '/'))) {
1145 *p++='\\';
1146 name++;
1148 if ( p==endbuf && *name )
1150 SetLastError( ERROR_PATH_NOT_FOUND );
1151 return 0;
1154 *p = '\0';
1156 if (!(DRIVE_GetFlags(drive) & DRIVE_CASE_PRESERVING))
1157 CharUpperA( buffer );
1159 if (result)
1161 if (unicode)
1162 lstrcpynAtoW( (LPWSTR)result, buffer, len );
1163 else
1164 lstrcpynA( result, buffer, len );
1167 TRACE("returning '%s'\n", buffer );
1169 /* If the lpBuffer buffer is too small, the return value is the
1170 size of the buffer, in characters, required to hold the path. */
1172 ret = strlen(buffer);
1174 if (ret >= len )
1175 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1177 return ret;
1181 /***********************************************************************
1182 * GetFullPathName32A (KERNEL32.272)
1183 * NOTES
1184 * if the path closed with '\', *lastpart is 0
1186 DWORD WINAPI GetFullPathNameA( LPCSTR name, DWORD len, LPSTR buffer,
1187 LPSTR *lastpart )
1189 DWORD ret = DOSFS_DoGetFullPathName( name, len, buffer, FALSE );
1190 if (ret && buffer && lastpart)
1192 LPSTR p = buffer + strlen(buffer);
1194 if (*p != '\\')
1196 while ((p > buffer + 2) && (*p != '\\')) p--;
1197 *lastpart = p + 1;
1199 else *lastpart = NULL;
1201 return ret;
1205 /***********************************************************************
1206 * GetFullPathName32W (KERNEL32.273)
1208 DWORD WINAPI GetFullPathNameW( LPCWSTR name, DWORD len, LPWSTR buffer,
1209 LPWSTR *lastpart )
1211 LPSTR nameA = HEAP_strdupWtoA( GetProcessHeap(), 0, name );
1212 DWORD ret = DOSFS_DoGetFullPathName( nameA, len, (LPSTR)buffer, TRUE );
1213 HeapFree( GetProcessHeap(), 0, nameA );
1214 if (ret && buffer && lastpart)
1216 LPWSTR p = buffer + lstrlenW(buffer);
1217 if (*p != (WCHAR)'\\')
1219 while ((p > buffer + 2) && (*p != (WCHAR)'\\')) p--;
1220 *lastpart = p + 1;
1222 else *lastpart = NULL;
1224 return ret;
1227 /***********************************************************************
1228 * DOSFS_FindNextEx
1230 static int DOSFS_FindNextEx( FIND_FIRST_INFO *info, WIN32_FIND_DATAA *entry )
1232 BYTE attr = info->attr | FA_UNUSED | FA_ARCHIVE | FA_RDONLY;
1233 UINT flags = DRIVE_GetFlags( info->drive );
1234 char *p, buffer[MAX_PATHNAME_LEN];
1235 const char *drive_path;
1236 int drive_root;
1237 LPCSTR long_name, short_name;
1238 BY_HANDLE_FILE_INFORMATION fileinfo;
1239 char dos_name[13];
1241 if ((info->attr & ~(FA_UNUSED | FA_ARCHIVE | FA_RDONLY)) == FA_LABEL)
1243 if (info->cur_pos) return 0;
1244 entry->dwFileAttributes = FILE_ATTRIBUTE_LABEL;
1245 DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftCreationTime, 0 );
1246 DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftLastAccessTime, 0 );
1247 DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftLastWriteTime, 0 );
1248 entry->nFileSizeHigh = 0;
1249 entry->nFileSizeLow = 0;
1250 entry->dwReserved0 = 0;
1251 entry->dwReserved1 = 0;
1252 DOSFS_ToDosDTAFormat( DRIVE_GetLabel( info->drive ), entry->cFileName );
1253 strcpy( entry->cAlternateFileName, entry->cFileName );
1254 info->cur_pos++;
1255 return 1;
1258 drive_path = info->path + strlen(DRIVE_GetRoot( info->drive ));
1259 while ((*drive_path == '/') || (*drive_path == '\\')) drive_path++;
1260 drive_root = !*drive_path;
1262 lstrcpynA( buffer, info->path, sizeof(buffer) - 1 );
1263 strcat( buffer, "/" );
1264 p = buffer + strlen(buffer);
1266 while (DOSFS_ReadDir( info->dir, &long_name, &short_name ))
1268 info->cur_pos++;
1270 /* Don't return '.' and '..' in the root of the drive */
1271 if (drive_root && (long_name[0] == '.') &&
1272 (!long_name[1] || ((long_name[1] == '.') && !long_name[2])))
1273 continue;
1275 /* Check the long mask */
1277 if (info->long_mask)
1279 if (!DOSFS_MatchLong( info->long_mask, long_name,
1280 flags & DRIVE_CASE_SENSITIVE )) continue;
1283 /* Check the short mask */
1285 if (info->short_mask)
1287 if (!short_name)
1289 DOSFS_Hash( long_name, dos_name, TRUE,
1290 !(flags & DRIVE_CASE_SENSITIVE) );
1291 short_name = dos_name;
1293 if (!DOSFS_MatchShort( info->short_mask, short_name )) continue;
1296 /* Check the file attributes */
1298 lstrcpynA( p, long_name, sizeof(buffer) - (int)(p - buffer) );
1299 if (!FILE_Stat( buffer, &fileinfo ))
1301 WARN("can't stat %s\n", buffer);
1302 continue;
1304 if (fileinfo.dwFileAttributes & ~attr) continue;
1306 /* We now have a matching entry; fill the result and return */
1308 entry->dwFileAttributes = fileinfo.dwFileAttributes;
1309 entry->ftCreationTime = fileinfo.ftCreationTime;
1310 entry->ftLastAccessTime = fileinfo.ftLastAccessTime;
1311 entry->ftLastWriteTime = fileinfo.ftLastWriteTime;
1312 entry->nFileSizeHigh = fileinfo.nFileSizeHigh;
1313 entry->nFileSizeLow = fileinfo.nFileSizeLow;
1315 if (short_name)
1316 DOSFS_ToDosDTAFormat( short_name, entry->cAlternateFileName );
1317 else
1318 DOSFS_Hash( long_name, entry->cAlternateFileName, FALSE,
1319 !(flags & DRIVE_CASE_SENSITIVE) );
1321 lstrcpynA( entry->cFileName, long_name, sizeof(entry->cFileName) );
1322 if (!(flags & DRIVE_CASE_PRESERVING)) CharLowerA( entry->cFileName );
1323 TRACE("returning %s (%s) %02lx %ld\n",
1324 entry->cFileName, entry->cAlternateFileName,
1325 entry->dwFileAttributes, entry->nFileSizeLow );
1326 return 1;
1328 return 0; /* End of directory */
1331 /***********************************************************************
1332 * DOSFS_FindNext
1334 * Find the next matching file. Return the number of entries read to find
1335 * the matching one, or 0 if no more entries.
1336 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
1337 * file name mask. Either or both can be NULL.
1339 * NOTE: This is supposed to be only called by the int21 emulation
1340 * routines. Thus, we should own the Win16Mutex anyway.
1341 * Nevertheless, we explicitly enter it to ensure the static
1342 * directory cache is protected.
1344 int DOSFS_FindNext( const char *path, const char *short_mask,
1345 const char *long_mask, int drive, BYTE attr,
1346 int skip, WIN32_FIND_DATAA *entry )
1348 static FIND_FIRST_INFO info = { NULL };
1349 LPCSTR short_name, long_name;
1350 int count;
1352 SYSLEVEL_EnterWin16Lock();
1354 /* Check the cached directory */
1355 if (!(info.dir && info.path == path && info.short_mask == short_mask
1356 && info.long_mask == long_mask && info.drive == drive
1357 && info.attr == attr && info.cur_pos <= skip))
1359 /* Not in the cache, open it anew */
1360 if (info.dir) DOSFS_CloseDir( info.dir );
1362 info.path = (LPSTR)path;
1363 info.long_mask = (LPSTR)long_mask;
1364 info.short_mask = (LPSTR)short_mask;
1365 info.attr = attr;
1366 info.drive = drive;
1367 info.cur_pos = 0;
1368 info.dir = DOSFS_OpenDir( info.path );
1371 /* Skip to desired position */
1372 while (info.cur_pos < skip)
1373 if (info.dir && DOSFS_ReadDir( info.dir, &long_name, &short_name ))
1374 info.cur_pos++;
1375 else
1376 break;
1378 if (info.dir && info.cur_pos == skip && DOSFS_FindNextEx( &info, entry ))
1379 count = info.cur_pos - skip;
1380 else
1381 count = 0;
1383 if (!count)
1385 if (info.dir) DOSFS_CloseDir( info.dir );
1386 memset( &info, '\0', sizeof(info) );
1389 SYSLEVEL_LeaveWin16Lock();
1391 return count;
1396 /*************************************************************************
1397 * FindFirstFile16 (KERNEL.413)
1399 HANDLE16 WINAPI FindFirstFile16( LPCSTR path, WIN32_FIND_DATAA *data )
1401 DOS_FULL_NAME full_name;
1402 HGLOBAL16 handle;
1403 FIND_FIRST_INFO *info;
1405 data->dwReserved0 = data->dwReserved1 = 0x0;
1406 if (!path) return 0;
1407 if (!DOSFS_GetFullName( path, FALSE, &full_name ))
1408 return INVALID_HANDLE_VALUE16;
1409 if (!(handle = GlobalAlloc16( GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO) )))
1410 return INVALID_HANDLE_VALUE16;
1411 info = (FIND_FIRST_INFO *)GlobalLock16( handle );
1412 info->path = HEAP_strdupA( GetProcessHeap(), 0, full_name.long_name );
1413 info->long_mask = strrchr( info->path, '/' );
1414 *(info->long_mask++) = '\0';
1415 info->short_mask = NULL;
1416 info->attr = 0xff;
1417 if (path[0] && (path[1] == ':')) info->drive = toupper(*path) - 'A';
1418 else info->drive = DRIVE_GetCurrentDrive();
1419 info->cur_pos = 0;
1421 info->dir = DOSFS_OpenDir( info->path );
1423 GlobalUnlock16( handle );
1424 if (!FindNextFile16( handle, data ))
1426 FindClose16( handle );
1427 SetLastError( ERROR_NO_MORE_FILES );
1428 return INVALID_HANDLE_VALUE16;
1430 return handle;
1434 /*************************************************************************
1435 * FindFirstFile32A (KERNEL32.123)
1437 HANDLE WINAPI FindFirstFileA( LPCSTR path, WIN32_FIND_DATAA *data )
1439 HANDLE handle = FindFirstFile16( path, data );
1440 if (handle == INVALID_HANDLE_VALUE16) return INVALID_HANDLE_VALUE;
1441 return handle;
1445 /*************************************************************************
1446 * FindFirstFile32W (KERNEL32.124)
1448 HANDLE WINAPI FindFirstFileW( LPCWSTR path, WIN32_FIND_DATAW *data )
1450 WIN32_FIND_DATAA dataA;
1451 LPSTR pathA = HEAP_strdupWtoA( GetProcessHeap(), 0, path );
1452 HANDLE handle = FindFirstFileA( pathA, &dataA );
1453 HeapFree( GetProcessHeap(), 0, pathA );
1454 if (handle != INVALID_HANDLE_VALUE)
1456 data->dwFileAttributes = dataA.dwFileAttributes;
1457 data->ftCreationTime = dataA.ftCreationTime;
1458 data->ftLastAccessTime = dataA.ftLastAccessTime;
1459 data->ftLastWriteTime = dataA.ftLastWriteTime;
1460 data->nFileSizeHigh = dataA.nFileSizeHigh;
1461 data->nFileSizeLow = dataA.nFileSizeLow;
1462 lstrcpyAtoW( data->cFileName, dataA.cFileName );
1463 lstrcpyAtoW( data->cAlternateFileName, dataA.cAlternateFileName );
1465 return handle;
1469 /*************************************************************************
1470 * FindNextFile16 (KERNEL.414)
1472 BOOL16 WINAPI FindNextFile16( HANDLE16 handle, WIN32_FIND_DATAA *data )
1474 FIND_FIRST_INFO *info;
1476 if (!(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
1478 SetLastError( ERROR_INVALID_HANDLE );
1479 return FALSE;
1481 GlobalUnlock16( handle );
1482 if (!info->path || !info->dir)
1484 SetLastError( ERROR_NO_MORE_FILES );
1485 return FALSE;
1487 if (!DOSFS_FindNextEx( info, data ))
1489 DOSFS_CloseDir( info->dir ); info->dir = NULL;
1490 HeapFree( GetProcessHeap(), 0, info->path );
1491 info->path = info->long_mask = NULL;
1492 SetLastError( ERROR_NO_MORE_FILES );
1493 return FALSE;
1495 return TRUE;
1499 /*************************************************************************
1500 * FindNextFile32A (KERNEL32.126)
1502 BOOL WINAPI FindNextFileA( HANDLE handle, WIN32_FIND_DATAA *data )
1504 return FindNextFile16( handle, data );
1508 /*************************************************************************
1509 * FindNextFile32W (KERNEL32.127)
1511 BOOL WINAPI FindNextFileW( HANDLE handle, WIN32_FIND_DATAW *data )
1513 WIN32_FIND_DATAA dataA;
1514 if (!FindNextFileA( handle, &dataA )) return FALSE;
1515 data->dwFileAttributes = dataA.dwFileAttributes;
1516 data->ftCreationTime = dataA.ftCreationTime;
1517 data->ftLastAccessTime = dataA.ftLastAccessTime;
1518 data->ftLastWriteTime = dataA.ftLastWriteTime;
1519 data->nFileSizeHigh = dataA.nFileSizeHigh;
1520 data->nFileSizeLow = dataA.nFileSizeLow;
1521 lstrcpyAtoW( data->cFileName, dataA.cFileName );
1522 lstrcpyAtoW( data->cAlternateFileName, dataA.cAlternateFileName );
1523 return TRUE;
1527 /*************************************************************************
1528 * FindClose16 (KERNEL.415)
1530 BOOL16 WINAPI FindClose16( HANDLE16 handle )
1532 FIND_FIRST_INFO *info;
1534 if ((handle == INVALID_HANDLE_VALUE16) ||
1535 !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
1537 SetLastError( ERROR_INVALID_HANDLE );
1538 return FALSE;
1540 if (info->dir) DOSFS_CloseDir( info->dir );
1541 if (info->path) HeapFree( GetProcessHeap(), 0, info->path );
1542 GlobalUnlock16( handle );
1543 GlobalFree16( handle );
1544 return TRUE;
1548 /*************************************************************************
1549 * FindClose32 (KERNEL32.119)
1551 BOOL WINAPI FindClose( HANDLE handle )
1553 return FindClose16( (HANDLE16)handle );
1557 /***********************************************************************
1558 * DOSFS_UnixTimeToFileTime
1560 * Convert a Unix time to FILETIME format.
1561 * The FILETIME structure is a 64-bit value representing the number of
1562 * 100-nanosecond intervals since January 1, 1601, 0:00.
1563 * 'remainder' is the nonnegative number of 100-ns intervals
1564 * corresponding to the time fraction smaller than 1 second that
1565 * couldn't be stored in the time_t value.
1567 void DOSFS_UnixTimeToFileTime( time_t unix_time, FILETIME *filetime,
1568 DWORD remainder )
1570 /* NOTES:
1572 CONSTANTS:
1573 The time difference between 1 January 1601, 00:00:00 and
1574 1 January 1970, 00:00:00 is 369 years, plus the leap years
1575 from 1604 to 1968, excluding 1700, 1800, 1900.
1576 This makes (1968 - 1600) / 4 - 3 = 89 leap days, and a total
1577 of 134774 days.
1579 Any day in that period had 24 * 60 * 60 = 86400 seconds.
1581 The time difference is 134774 * 86400 * 10000000, which can be written
1582 116444736000000000
1583 27111902 * 2^32 + 3577643008
1584 413 * 2^48 + 45534 * 2^32 + 54590 * 2^16 + 32768
1586 If you find that these constants are buggy, please change them in all
1587 instances in both conversion functions.
1589 VERSIONS:
1590 There are two versions, one of them uses long long variables and
1591 is presumably faster but not ISO C. The other one uses standard C
1592 data types and operations but relies on the assumption that negative
1593 numbers are stored as 2's complement (-1 is 0xffff....). If this
1594 assumption is violated, dates before 1970 will not convert correctly.
1595 This should however work on any reasonable architecture where WINE
1596 will run.
1598 DETAILS:
1600 Take care not to remove the casts. I have tested these functions
1601 (in both versions) for a lot of numbers. I would be interested in
1602 results on other compilers than GCC.
1604 The operations have been designed to account for the possibility
1605 of 64-bit time_t in future UNICES. Even the versions without
1606 internal long long numbers will work if time_t only is 64 bit.
1607 A 32-bit shift, which was necessary for that operation, turned out
1608 not to work correctly in GCC, besides giving the warning. So I
1609 used a double 16-bit shift instead. Numbers are in the ISO version
1610 represented by three limbs, the most significant with 32 bit, the
1611 other two with 16 bit each.
1613 As the modulo-operator % is not well-defined for negative numbers,
1614 negative divisors have been avoided in DOSFS_FileTimeToUnixTime.
1616 There might be quicker ways to do this in C. Certainly so in
1617 assembler.
1619 Claus Fischer, fischer@iue.tuwien.ac.at
1622 #if SIZEOF_LONG_LONG >= 8
1623 # define USE_LONG_LONG 1
1624 #else
1625 # define USE_LONG_LONG 0
1626 #endif
1628 #if USE_LONG_LONG /* gcc supports long long type */
1630 long long int t = unix_time;
1631 t *= 10000000;
1632 t += 116444736000000000LL;
1633 t += remainder;
1634 filetime->dwLowDateTime = (UINT)t;
1635 filetime->dwHighDateTime = (UINT)(t >> 32);
1637 #else /* ISO version */
1639 UINT a0; /* 16 bit, low bits */
1640 UINT a1; /* 16 bit, medium bits */
1641 UINT a2; /* 32 bit, high bits */
1643 /* Copy the unix time to a2/a1/a0 */
1644 a0 = unix_time & 0xffff;
1645 a1 = (unix_time >> 16) & 0xffff;
1646 /* This is obsolete if unix_time is only 32 bits, but it does not hurt.
1647 Do not replace this by >> 32, it gives a compiler warning and it does
1648 not work. */
1649 a2 = (unix_time >= 0 ? (unix_time >> 16) >> 16 :
1650 ~((~unix_time >> 16) >> 16));
1652 /* Multiply a by 10000000 (a = a2/a1/a0)
1653 Split the factor into 10000 * 1000 which are both less than 0xffff. */
1654 a0 *= 10000;
1655 a1 = a1 * 10000 + (a0 >> 16);
1656 a2 = a2 * 10000 + (a1 >> 16);
1657 a0 &= 0xffff;
1658 a1 &= 0xffff;
1660 a0 *= 1000;
1661 a1 = a1 * 1000 + (a0 >> 16);
1662 a2 = a2 * 1000 + (a1 >> 16);
1663 a0 &= 0xffff;
1664 a1 &= 0xffff;
1666 /* Add the time difference and the remainder */
1667 a0 += 32768 + (remainder & 0xffff);
1668 a1 += 54590 + (remainder >> 16 ) + (a0 >> 16);
1669 a2 += 27111902 + (a1 >> 16);
1670 a0 &= 0xffff;
1671 a1 &= 0xffff;
1673 /* Set filetime */
1674 filetime->dwLowDateTime = (a1 << 16) + a0;
1675 filetime->dwHighDateTime = a2;
1676 #endif
1680 /***********************************************************************
1681 * DOSFS_FileTimeToUnixTime
1683 * Convert a FILETIME format to Unix time.
1684 * If not NULL, 'remainder' contains the fractional part of the filetime,
1685 * in the range of [0..9999999] (even if time_t is negative).
1687 time_t DOSFS_FileTimeToUnixTime( const FILETIME *filetime, DWORD *remainder )
1689 /* Read the comment in the function DOSFS_UnixTimeToFileTime. */
1690 #if USE_LONG_LONG
1692 long long int t = filetime->dwHighDateTime;
1693 t <<= 32;
1694 t += (UINT)filetime->dwLowDateTime;
1695 t -= 116444736000000000LL;
1696 if (t < 0)
1698 if (remainder) *remainder = 9999999 - (-t - 1) % 10000000;
1699 return -1 - ((-t - 1) / 10000000);
1701 else
1703 if (remainder) *remainder = t % 10000000;
1704 return t / 10000000;
1707 #else /* ISO version */
1709 UINT a0; /* 16 bit, low bits */
1710 UINT a1; /* 16 bit, medium bits */
1711 UINT a2; /* 32 bit, high bits */
1712 UINT r; /* remainder of division */
1713 unsigned int carry; /* carry bit for subtraction */
1714 int negative; /* whether a represents a negative value */
1716 /* Copy the time values to a2/a1/a0 */
1717 a2 = (UINT)filetime->dwHighDateTime;
1718 a1 = ((UINT)filetime->dwLowDateTime ) >> 16;
1719 a0 = ((UINT)filetime->dwLowDateTime ) & 0xffff;
1721 /* Subtract the time difference */
1722 if (a0 >= 32768 ) a0 -= 32768 , carry = 0;
1723 else a0 += (1 << 16) - 32768 , carry = 1;
1725 if (a1 >= 54590 + carry) a1 -= 54590 + carry, carry = 0;
1726 else a1 += (1 << 16) - 54590 - carry, carry = 1;
1728 a2 -= 27111902 + carry;
1730 /* If a is negative, replace a by (-1-a) */
1731 negative = (a2 >= ((UINT)1) << 31);
1732 if (negative)
1734 /* Set a to -a - 1 (a is a2/a1/a0) */
1735 a0 = 0xffff - a0;
1736 a1 = 0xffff - a1;
1737 a2 = ~a2;
1740 /* Divide a by 10000000 (a = a2/a1/a0), put the rest into r.
1741 Split the divisor into 10000 * 1000 which are both less than 0xffff. */
1742 a1 += (a2 % 10000) << 16;
1743 a2 /= 10000;
1744 a0 += (a1 % 10000) << 16;
1745 a1 /= 10000;
1746 r = a0 % 10000;
1747 a0 /= 10000;
1749 a1 += (a2 % 1000) << 16;
1750 a2 /= 1000;
1751 a0 += (a1 % 1000) << 16;
1752 a1 /= 1000;
1753 r += (a0 % 1000) * 10000;
1754 a0 /= 1000;
1756 /* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
1757 if (negative)
1759 /* Set a to -a - 1 (a is a2/a1/a0) */
1760 a0 = 0xffff - a0;
1761 a1 = 0xffff - a1;
1762 a2 = ~a2;
1764 r = 9999999 - r;
1767 if (remainder) *remainder = r;
1769 /* Do not replace this by << 32, it gives a compiler warning and it does
1770 not work. */
1771 return ((((time_t)a2) << 16) << 16) + (a1 << 16) + a0;
1772 #endif
1776 /***********************************************************************
1777 * DosDateTimeToFileTime (KERNEL32.76)
1779 BOOL WINAPI DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft)
1781 struct tm newtm;
1783 newtm.tm_sec = (fattime & 0x1f) * 2;
1784 newtm.tm_min = (fattime >> 5) & 0x3f;
1785 newtm.tm_hour = (fattime >> 11);
1786 newtm.tm_mday = (fatdate & 0x1f);
1787 newtm.tm_mon = ((fatdate >> 5) & 0x0f) - 1;
1788 newtm.tm_year = (fatdate >> 9) + 80;
1789 DOSFS_UnixTimeToFileTime( mktime( &newtm ), ft, 0 );
1790 return TRUE;
1794 /***********************************************************************
1795 * FileTimeToDosDateTime (KERNEL32.111)
1797 BOOL WINAPI FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate,
1798 LPWORD fattime )
1800 time_t unixtime = DOSFS_FileTimeToUnixTime( ft, NULL );
1801 struct tm *tm = localtime( &unixtime );
1802 if (fattime)
1803 *fattime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
1804 if (fatdate)
1805 *fatdate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
1806 + tm->tm_mday;
1807 return TRUE;
1811 /***********************************************************************
1812 * LocalFileTimeToFileTime (KERNEL32.373)
1814 BOOL WINAPI LocalFileTimeToFileTime( const FILETIME *localft,
1815 LPFILETIME utcft )
1817 struct tm *xtm;
1818 DWORD remainder;
1820 /* convert from local to UTC. Perhaps not correct. FIXME */
1821 time_t unixtime = DOSFS_FileTimeToUnixTime( localft, &remainder );
1822 xtm = gmtime( &unixtime );
1823 DOSFS_UnixTimeToFileTime( mktime(xtm), utcft, remainder );
1824 return TRUE;
1828 /***********************************************************************
1829 * FileTimeToLocalFileTime (KERNEL32.112)
1831 BOOL WINAPI FileTimeToLocalFileTime( const FILETIME *utcft,
1832 LPFILETIME localft )
1834 DWORD remainder;
1835 /* convert from UTC to local. Perhaps not correct. FIXME */
1836 time_t unixtime = DOSFS_FileTimeToUnixTime( utcft, &remainder );
1837 #ifdef HAVE_TIMEGM
1838 struct tm *xtm = localtime( &unixtime );
1839 time_t localtime;
1841 localtime = timegm(xtm);
1842 DOSFS_UnixTimeToFileTime( localtime, localft, remainder );
1844 #else
1845 struct tm *xtm,*gtm;
1846 time_t time1,time2;
1848 xtm = localtime( &unixtime );
1849 gtm = gmtime( &unixtime );
1850 time1 = mktime(xtm);
1851 time2 = mktime(gtm);
1852 DOSFS_UnixTimeToFileTime( 2*time1-time2, localft, remainder );
1853 #endif
1854 return TRUE;
1858 /***********************************************************************
1859 * FileTimeToSystemTime (KERNEL32.113)
1861 BOOL WINAPI FileTimeToSystemTime( const FILETIME *ft, LPSYSTEMTIME syst )
1863 struct tm *xtm;
1864 DWORD remainder;
1865 time_t xtime = DOSFS_FileTimeToUnixTime( ft, &remainder );
1866 xtm = gmtime(&xtime);
1867 syst->wYear = xtm->tm_year+1900;
1868 syst->wMonth = xtm->tm_mon + 1;
1869 syst->wDayOfWeek = xtm->tm_wday;
1870 syst->wDay = xtm->tm_mday;
1871 syst->wHour = xtm->tm_hour;
1872 syst->wMinute = xtm->tm_min;
1873 syst->wSecond = xtm->tm_sec;
1874 syst->wMilliseconds = remainder / 10000;
1875 return TRUE;
1878 /***********************************************************************
1879 * QueryDosDeviceA (KERNEL32.413)
1881 * returns array of strings terminated by \0, terminated by \0
1883 DWORD WINAPI QueryDosDeviceA(LPCSTR devname,LPSTR target,DWORD bufsize)
1885 LPSTR s;
1886 char buffer[200];
1888 TRACE("(%s,...)\n", devname ? devname : "<null>");
1889 if (!devname) {
1890 /* return known MSDOS devices */
1891 strcpy(buffer,"CON COM1 COM2 LPT1 NUL ");
1892 while ((s=strchr(buffer,' ')))
1893 *s='\0';
1895 lstrcpynA(target,buffer,bufsize);
1896 return strlen(buffer);
1898 strcpy(buffer,"\\DEV\\");
1899 strcat(buffer,devname);
1900 if ((s=strchr(buffer,':'))) *s='\0';
1901 lstrcpynA(target,buffer,bufsize);
1902 return strlen(buffer);
1906 /***********************************************************************
1907 * QueryDosDeviceW (KERNEL32.414)
1909 * returns array of strings terminated by \0, terminated by \0
1911 DWORD WINAPI QueryDosDeviceW(LPCWSTR devname,LPWSTR target,DWORD bufsize)
1913 LPSTR devnameA = devname?HEAP_strdupWtoA(GetProcessHeap(),0,devname):NULL;
1914 LPSTR targetA = (LPSTR)HEAP_xalloc(GetProcessHeap(),0,bufsize);
1915 DWORD ret = QueryDosDeviceA(devnameA,targetA,bufsize);
1917 lstrcpynAtoW(target,targetA,bufsize);
1918 if (devnameA) HeapFree(GetProcessHeap(),0,devnameA);
1919 if (targetA) HeapFree(GetProcessHeap(),0,targetA);
1920 return ret;
1924 /***********************************************************************
1925 * SystemTimeToFileTime (KERNEL32.526)
1927 BOOL WINAPI SystemTimeToFileTime( const SYSTEMTIME *syst, LPFILETIME ft )
1929 #ifdef HAVE_TIMEGM
1930 struct tm xtm;
1931 time_t utctime;
1932 #else
1933 struct tm xtm,*local_tm,*utc_tm;
1934 time_t localtim,utctime;
1935 #endif
1937 xtm.tm_year = syst->wYear-1900;
1938 xtm.tm_mon = syst->wMonth - 1;
1939 xtm.tm_wday = syst->wDayOfWeek;
1940 xtm.tm_mday = syst->wDay;
1941 xtm.tm_hour = syst->wHour;
1942 xtm.tm_min = syst->wMinute;
1943 xtm.tm_sec = syst->wSecond; /* this is UTC */
1944 xtm.tm_isdst = -1;
1945 #ifdef HAVE_TIMEGM
1946 utctime = timegm(&xtm);
1947 DOSFS_UnixTimeToFileTime( utctime, ft,
1948 syst->wMilliseconds * 10000 );
1949 #else
1950 localtim = mktime(&xtm); /* now we've got local time */
1951 local_tm = localtime(&localtim);
1952 utc_tm = gmtime(&localtim);
1953 utctime = mktime(utc_tm);
1954 DOSFS_UnixTimeToFileTime( 2*localtim -utctime, ft,
1955 syst->wMilliseconds * 10000 );
1956 #endif
1957 return TRUE;
1960 /***********************************************************************
1961 * DefineDosDeviceA (KERNEL32.182)
1963 BOOL WINAPI DefineDosDeviceA(DWORD flags,LPCSTR devname,LPCSTR targetpath) {
1964 FIXME("(0x%08lx,%s,%s),stub!\n",flags,devname,targetpath);
1965 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1966 return FALSE;