Fixed GetShortPathNameA.
[wine/hacks.git] / files / dos_fs.c
blobac263750af994407fd7e971cd9119600d988380c
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 DEFAULT_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 /* We want the real kernel dirent structure, not the libc one */
45 typedef struct
47 long d_ino;
48 long d_off;
49 unsigned short d_reclen;
50 char d_name[256];
51 } KERNEL_DIRENT;
53 #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, KERNEL_DIRENT [2] )
55 #else /* linux */
56 #undef VFAT_IOCTL_READDIR_BOTH /* just in case... */
57 #endif /* linux */
59 /* Chars we don't want to see in DOS file names */
60 #define INVALID_DOS_CHARS "*?<>|\"+=,;[] \345"
62 static const DOS_DEVICE DOSFS_Devices[] =
63 /* name, device flags (see Int 21/AX=0x4400) */
65 { "CON", 0xc0d3 },
66 { "PRN", 0xa0c0 },
67 { "NUL", 0x80c4 },
68 { "AUX", 0x80c0 },
69 { "LPT1", 0xa0c0 },
70 { "LPT2", 0xa0c0 },
71 { "LPT3", 0xa0c0 },
72 { "LPT4", 0xc0d3 },
73 { "COM1", 0x80c0 },
74 { "COM2", 0x80c0 },
75 { "COM3", 0x80c0 },
76 { "COM4", 0x80c0 },
77 { "SCSIMGR$", 0xc0c0 },
78 { "HPSCAN", 0xc0c0 }
81 #define GET_DRIVE(path) \
82 (((path)[1] == ':') ? toupper((path)[0]) - 'A' : DOSFS_CurDrive)
84 /* Directory info for DOSFS_ReadDir */
85 typedef struct
87 DIR *dir;
88 #ifdef VFAT_IOCTL_READDIR_BOTH
89 int fd;
90 char short_name[12];
91 KERNEL_DIRENT dirent[2];
92 #endif
93 } DOS_DIR;
95 /* Info structure for FindFirstFile handle */
96 typedef struct
98 LPSTR path;
99 LPSTR long_mask;
100 LPSTR short_mask;
101 BYTE attr;
102 int drive;
103 int cur_pos;
104 DOS_DIR *dir;
105 } FIND_FIRST_INFO;
109 /***********************************************************************
110 * DOSFS_ValidDOSName
112 * Return 1 if Unix file 'name' is also a valid MS-DOS name
113 * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
114 * File name can be terminated by '\0', '\\' or '/'.
116 static int DOSFS_ValidDOSName( const char *name, int ignore_case )
118 static const char invalid_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS;
119 const char *p = name;
120 const char *invalid = ignore_case ? (invalid_chars + 26) : invalid_chars;
121 int len = 0;
123 if (*p == '.')
125 /* Check for "." and ".." */
126 p++;
127 if (*p == '.') p++;
128 /* All other names beginning with '.' are invalid */
129 return (IS_END_OF_NAME(*p));
131 while (!IS_END_OF_NAME(*p))
133 if (strchr( invalid, *p )) return 0; /* Invalid char */
134 if (*p == '.') break; /* Start of the extension */
135 if (++len > 8) return 0; /* Name too long */
136 p++;
138 if (*p != '.') return 1; /* End of name */
139 p++;
140 if (IS_END_OF_NAME(*p)) return 0; /* Empty extension not allowed */
141 len = 0;
142 while (!IS_END_OF_NAME(*p))
144 if (strchr( invalid, *p )) return 0; /* Invalid char */
145 if (*p == '.') return 0; /* Second extension not allowed */
146 if (++len > 3) return 0; /* Extension too long */
147 p++;
149 return 1;
153 /***********************************************************************
154 * DOSFS_ToDosFCBFormat
156 * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
157 * expanding wild cards and converting to upper-case in the process.
158 * File name can be terminated by '\0', '\\' or '/'.
159 * Return FALSE if the name is not a valid DOS name.
160 * 'buffer' must be at least 12 characters long.
162 BOOL DOSFS_ToDosFCBFormat( LPCSTR name, LPSTR buffer )
164 static const char invalid_chars[] = INVALID_DOS_CHARS;
165 const char *p = name;
166 int i;
168 /* Check for "." and ".." */
169 if (*p == '.')
171 p++;
172 strcpy( buffer, ". " );
173 if (*p == '.')
175 buffer[1] = '.';
176 p++;
178 return (!*p || (*p == '/') || (*p == '\\'));
181 for (i = 0; i < 8; i++)
183 switch(*p)
185 case '\0':
186 case '\\':
187 case '/':
188 case '.':
189 buffer[i] = ' ';
190 break;
191 case '?':
192 p++;
193 /* fall through */
194 case '*':
195 buffer[i] = '?';
196 break;
197 default:
198 if (strchr( invalid_chars, *p )) return FALSE;
199 buffer[i] = toupper(*p);
200 p++;
201 break;
205 if (*p == '*')
207 /* Skip all chars after wildcard up to first dot */
208 while (*p && (*p != '/') && (*p != '\\') && (*p != '.')) p++;
210 else
212 /* Check if name too long */
213 if (*p && (*p != '/') && (*p != '\\') && (*p != '.')) return FALSE;
215 if (*p == '.') p++; /* Skip dot */
217 for (i = 8; i < 11; i++)
219 switch(*p)
221 case '\0':
222 case '\\':
223 case '/':
224 buffer[i] = ' ';
225 break;
226 case '.':
227 return FALSE; /* Second extension not allowed */
228 case '?':
229 p++;
230 /* fall through */
231 case '*':
232 buffer[i] = '?';
233 break;
234 default:
235 if (strchr( invalid_chars, *p )) return FALSE;
236 buffer[i] = toupper(*p);
237 p++;
238 break;
241 buffer[11] = '\0';
242 return TRUE;
246 /***********************************************************************
247 * DOSFS_ToDosDTAFormat
249 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
250 * converting to upper-case in the process.
251 * File name can be terminated by '\0', '\\' or '/'.
252 * 'buffer' must be at least 13 characters long.
254 static void DOSFS_ToDosDTAFormat( LPCSTR name, LPSTR buffer )
256 char *p;
258 memcpy( buffer, name, 8 );
259 for (p = buffer + 8; (p > buffer) && (p[-1] == ' '); p--);
260 *p++ = '.';
261 memcpy( p, name + 8, 3 );
262 for (p += 3; p[-1] == ' '; p--);
263 if (p[-1] == '.') p--;
264 *p = '\0';
268 /***********************************************************************
269 * DOSFS_MatchShort
271 * Check a DOS file name against a mask (both in FCB format).
273 static int DOSFS_MatchShort( const char *mask, const char *name )
275 int i;
276 for (i = 11; i > 0; i--, mask++, name++)
277 if ((*mask != '?') && (*mask != *name)) return 0;
278 return 1;
282 /***********************************************************************
283 * DOSFS_MatchLong
285 * Check a long file name against a mask.
287 static int DOSFS_MatchLong( const char *mask, const char *name,
288 int case_sensitive )
290 if (!strcmp( mask, "*.*" )) return 1;
291 while (*name && *mask)
293 if (*mask == '*')
295 mask++;
296 while (*mask == '*') mask++; /* Skip consecutive '*' */
297 if (!*mask) return 1;
298 if (case_sensitive) while (*name && (*name != *mask)) name++;
299 else while (*name && (toupper(*name) != toupper(*mask))) name++;
300 if (!*name) break;
302 else if (*mask != '?')
304 if (case_sensitive)
306 if (*mask != *name) return 0;
308 else if (toupper(*mask) != toupper(*name)) return 0;
310 mask++;
311 name++;
313 if (*mask == '.') mask++; /* Ignore trailing '.' in mask */
314 return (!*name && !*mask);
318 /***********************************************************************
319 * DOSFS_OpenDir
321 static DOS_DIR *DOSFS_OpenDir( LPCSTR path )
323 DOS_DIR *dir = HeapAlloc( SystemHeap, 0, sizeof(*dir) );
324 if (!dir)
326 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
327 return NULL;
330 /* Treat empty path as root directory. This simplifies path split into
331 directory and mask in several other places */
332 if (!*path) path = "/";
334 #ifdef VFAT_IOCTL_READDIR_BOTH
336 /* Check if the VFAT ioctl is supported on this directory */
338 if ((dir->fd = open( path, O_RDONLY )) != -1)
340 if (ioctl( dir->fd, VFAT_IOCTL_READDIR_BOTH, (long)dir->dirent ) == -1)
342 close( dir->fd );
343 dir->fd = -1;
345 else
347 /* Set the file pointer back at the start of the directory */
348 lseek( dir->fd, 0, SEEK_SET );
349 dir->dir = NULL;
350 return dir;
353 #endif /* VFAT_IOCTL_READDIR_BOTH */
355 /* Now use the standard opendir/readdir interface */
357 if (!(dir->dir = opendir( path )))
359 HeapFree( SystemHeap, 0, dir );
360 return NULL;
362 return dir;
366 /***********************************************************************
367 * DOSFS_CloseDir
369 static void DOSFS_CloseDir( DOS_DIR *dir )
371 #ifdef VFAT_IOCTL_READDIR_BOTH
372 if (dir->fd != -1) close( dir->fd );
373 #endif /* VFAT_IOCTL_READDIR_BOTH */
374 if (dir->dir) closedir( dir->dir );
375 HeapFree( SystemHeap, 0, dir );
379 /***********************************************************************
380 * DOSFS_ReadDir
382 static BOOL DOSFS_ReadDir( DOS_DIR *dir, LPCSTR *long_name,
383 LPCSTR *short_name )
385 struct dirent *dirent;
387 #ifdef VFAT_IOCTL_READDIR_BOTH
388 if (dir->fd != -1)
390 if (ioctl( dir->fd, VFAT_IOCTL_READDIR_BOTH, (long)dir->dirent ) != -1) {
391 if (!dir->dirent[0].d_reclen) return FALSE;
392 if (!DOSFS_ToDosFCBFormat( dir->dirent[0].d_name, dir->short_name ))
393 dir->short_name[0] = '\0';
394 *short_name = dir->short_name;
395 if (dir->dirent[1].d_name[0]) *long_name = dir->dirent[1].d_name;
396 else *long_name = dir->dirent[0].d_name;
397 return TRUE;
400 #endif /* VFAT_IOCTL_READDIR_BOTH */
402 if (!(dirent = readdir( dir->dir ))) return FALSE;
403 *long_name = dirent->d_name;
404 *short_name = NULL;
405 return TRUE;
409 /***********************************************************************
410 * DOSFS_Hash
412 * Transform a Unix file name into a hashed DOS name. If the name is a valid
413 * DOS name, it is converted to upper-case; otherwise it is replaced by a
414 * hashed version that fits in 8.3 format.
415 * File name can be terminated by '\0', '\\' or '/'.
416 * 'buffer' must be at least 13 characters long.
418 static void DOSFS_Hash( LPCSTR name, LPSTR buffer, BOOL dir_format,
419 BOOL ignore_case )
421 static const char invalid_chars[] = INVALID_DOS_CHARS "~.";
422 static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
424 const char *p, *ext;
425 char *dst;
426 unsigned short hash;
427 int i;
429 if (dir_format) strcpy( buffer, " " );
431 if (DOSFS_ValidDOSName( name, ignore_case ))
433 /* Check for '.' and '..' */
434 if (*name == '.')
436 buffer[0] = '.';
437 if (!dir_format) buffer[1] = buffer[2] = '\0';
438 if (name[1] == '.') buffer[1] = '.';
439 return;
442 /* Simply copy the name, converting to uppercase */
444 for (dst = buffer; !IS_END_OF_NAME(*name) && (*name != '.'); name++)
445 *dst++ = toupper(*name);
446 if (*name == '.')
448 if (dir_format) dst = buffer + 8;
449 else *dst++ = '.';
450 for (name++; !IS_END_OF_NAME(*name); name++)
451 *dst++ = toupper(*name);
453 if (!dir_format) *dst = '\0';
454 return;
457 /* Compute the hash code of the file name */
458 /* If you know something about hash functions, feel free to */
459 /* insert a better algorithm here... */
460 if (ignore_case)
462 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
463 hash = (hash<<3) ^ (hash>>5) ^ tolower(*p) ^ (tolower(p[1]) << 8);
464 hash = (hash<<3) ^ (hash>>5) ^ tolower(*p); /* Last character*/
466 else
468 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
469 hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
470 hash = (hash << 3) ^ (hash >> 5) ^ *p; /* Last character */
473 /* Find last dot for start of the extension */
474 for (p = name+1, ext = NULL; !IS_END_OF_NAME(*p); p++)
475 if (*p == '.') ext = p;
476 if (ext && IS_END_OF_NAME(ext[1]))
477 ext = NULL; /* Empty extension ignored */
479 /* Copy first 4 chars, replacing invalid chars with '_' */
480 for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
482 if (IS_END_OF_NAME(*p) || (p == ext)) break;
483 *dst++ = strchr( invalid_chars, *p ) ? '_' : toupper(*p);
485 /* Pad to 5 chars with '~' */
486 while (i-- >= 0) *dst++ = '~';
488 /* Insert hash code converted to 3 ASCII chars */
489 *dst++ = hash_chars[(hash >> 10) & 0x1f];
490 *dst++ = hash_chars[(hash >> 5) & 0x1f];
491 *dst++ = hash_chars[hash & 0x1f];
493 /* Copy the first 3 chars of the extension (if any) */
494 if (ext)
496 if (!dir_format) *dst++ = '.';
497 for (i = 3, ext++; (i > 0) && !IS_END_OF_NAME(*ext); i--, ext++)
498 *dst++ = strchr( invalid_chars, *ext ) ? '_' : toupper(*ext);
500 if (!dir_format) *dst = '\0';
504 /***********************************************************************
505 * DOSFS_FindUnixName
507 * Find the Unix file name in a given directory that corresponds to
508 * a file name (either in Unix or DOS format).
509 * File name can be terminated by '\0', '\\' or '/'.
510 * Return TRUE if OK, FALSE if no file name matches.
512 * 'long_buf' must be at least 'long_len' characters long. If the long name
513 * turns out to be larger than that, the function returns FALSE.
514 * 'short_buf' must be at least 13 characters long.
516 BOOL DOSFS_FindUnixName( LPCSTR path, LPCSTR name, LPSTR long_buf,
517 INT long_len, LPSTR short_buf, BOOL ignore_case)
519 DOS_DIR *dir;
520 LPCSTR long_name, short_name;
521 char dos_name[12], tmp_buf[13];
522 BOOL ret;
524 const char *p = strchr( name, '/' );
525 int len = p ? (int)(p - name) : strlen(name);
526 if ((p = strchr( name, '\\' ))) len = MIN( (int)(p - name), len );
527 /* Ignore trailing dots */
528 while (len > 1 && name[len-1] == '.') len--;
529 if (long_len < len + 1) return FALSE;
531 TRACE("%s,%s\n", path, name );
533 if (!DOSFS_ToDosFCBFormat( name, dos_name )) dos_name[0] = '\0';
535 if (!(dir = DOSFS_OpenDir( path )))
537 WARN("(%s,%s): can't open dir: %s\n",
538 path, name, strerror(errno) );
539 return FALSE;
542 while ((ret = DOSFS_ReadDir( dir, &long_name, &short_name )))
544 /* Check against Unix name */
545 if (len == strlen(long_name))
547 if (!ignore_case)
549 if (!strncmp( long_name, name, len )) break;
551 else
553 if (!lstrncmpiA( long_name, name, len )) break;
556 if (dos_name[0])
558 /* Check against hashed DOS name */
559 if (!short_name)
561 DOSFS_Hash( long_name, tmp_buf, TRUE, ignore_case );
562 short_name = tmp_buf;
564 if (!strcmp( dos_name, short_name )) break;
567 if (ret)
569 if (long_buf) strcpy( long_buf, long_name );
570 if (short_buf)
572 if (short_name)
573 DOSFS_ToDosDTAFormat( short_name, short_buf );
574 else
575 DOSFS_Hash( long_name, short_buf, FALSE, ignore_case );
577 TRACE("(%s,%s) -> %s (%s)\n",
578 path, name, long_name, short_buf ? short_buf : "***");
580 else
581 WARN("'%s' not found in '%s'\n", name, path);
582 DOSFS_CloseDir( dir );
583 return ret;
587 /***********************************************************************
588 * DOSFS_GetDevice
590 * Check if a DOS file name represents a DOS device and return the device.
592 const DOS_DEVICE *DOSFS_GetDevice( const char *name )
594 int i;
595 const char *p;
597 if (!name) return NULL; /* if FILE_DupUnixHandle was used */
598 if (name[0] && (name[1] == ':')) name += 2;
599 if ((p = strrchr( name, '/' ))) name = p + 1;
600 if ((p = strrchr( name, '\\' ))) name = p + 1;
601 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
603 const char *dev = DOSFS_Devices[i].name;
604 if (!lstrncmpiA( dev, name, strlen(dev) ))
606 p = name + strlen( dev );
607 if (!*p || (*p == '.')) return &DOSFS_Devices[i];
610 return NULL;
614 /***********************************************************************
615 * DOSFS_GetDeviceByHandle
617 const DOS_DEVICE *DOSFS_GetDeviceByHandle( HFILE hFile )
619 struct get_file_info_request *req = get_req_buffer();
621 req->handle = hFile;
622 if (!server_call( REQ_GET_FILE_INFO ) && (req->type == FILE_TYPE_UNKNOWN))
624 if ((req->attr >= 0) &&
625 (req->attr < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0])))
626 return &DOSFS_Devices[req->attr];
628 return NULL;
632 /***********************************************************************
633 * DOSFS_OpenDevice
635 * Open a DOS device. This might not map 1:1 into the UNIX device concept.
637 HFILE DOSFS_OpenDevice( const char *name, DWORD access )
639 int i;
640 const char *p;
642 if (!name) return (HFILE)NULL; /* if FILE_DupUnixHandle was used */
643 if (name[0] && (name[1] == ':')) name += 2;
644 if ((p = strrchr( name, '/' ))) name = p + 1;
645 if ((p = strrchr( name, '\\' ))) name = p + 1;
646 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
648 const char *dev = DOSFS_Devices[i].name;
649 if (!lstrncmpiA( dev, name, strlen(dev) ))
651 p = name + strlen( dev );
652 if (!*p || (*p == '.')) {
653 /* got it */
654 if (!strcmp(DOSFS_Devices[i].name,"NUL"))
655 return FILE_CreateFile( "/dev/null", access,
656 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
657 OPEN_EXISTING, 0, -1 );
658 if (!strcmp(DOSFS_Devices[i].name,"CON")) {
659 HFILE to_dup;
660 HFILE handle;
661 switch (access & (GENERIC_READ|GENERIC_WRITE)) {
662 case GENERIC_READ:
663 to_dup = GetStdHandle( STD_INPUT_HANDLE );
664 break;
665 case GENERIC_WRITE:
666 to_dup = GetStdHandle( STD_OUTPUT_HANDLE );
667 break;
668 default:
669 FIXME("can't open CON read/write\n");
670 return HFILE_ERROR;
671 break;
673 if (!DuplicateHandle( GetCurrentProcess(), to_dup, GetCurrentProcess(),
674 &handle, 0, FALSE, DUPLICATE_SAME_ACCESS ))
675 handle = HFILE_ERROR;
676 return handle;
678 if (!strcmp(DOSFS_Devices[i].name,"SCSIMGR$") ||
679 !strcmp(DOSFS_Devices[i].name,"HPSCAN"))
681 return FILE_CreateDevice( i, access, NULL );
684 HFILE r;
685 char devname[40];
686 PROFILE_GetWineIniString("serialports",name,"",devname,sizeof devname);
688 if(devname[0])
690 TRACE_(file)("DOSFS_OpenDevice %s is %s\n",
691 DOSFS_Devices[i].name,devname);
692 r = FILE_CreateFile( devname, access,
693 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
694 OPEN_EXISTING, 0, -1 );
695 TRACE_(file)("Create_File return %08X\n",r);
696 return r;
700 FIXME("device open %s not supported (yet)\n",DOSFS_Devices[i].name);
701 return HFILE_ERROR;
705 return HFILE_ERROR;
709 /***********************************************************************
710 * DOSFS_GetPathDrive
712 * Get the drive specified by a given path name (DOS or Unix format).
714 static int DOSFS_GetPathDrive( const char **name )
716 int drive;
717 const char *p = *name;
719 if (*p && (p[1] == ':'))
721 drive = toupper(*p) - 'A';
722 *name += 2;
724 else if (*p == '/') /* Absolute Unix path? */
726 if ((drive = DRIVE_FindDriveRoot( name )) == -1)
728 MESSAGE("Warning: %s not accessible from a DOS drive\n", *name );
729 /* Assume it really was a DOS name */
730 drive = DRIVE_GetCurrentDrive();
733 else drive = DRIVE_GetCurrentDrive();
735 if (!DRIVE_IsValid(drive))
737 SetLastError( ERROR_INVALID_DRIVE );
738 return -1;
740 return drive;
744 /***********************************************************************
745 * DOSFS_GetFullName
747 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
748 * Unix name / short DOS name pair.
749 * Return FALSE if one of the path components does not exist. The last path
750 * component is only checked if 'check_last' is non-zero.
751 * The buffers pointed to by 'long_buf' and 'short_buf' must be
752 * at least MAX_PATHNAME_LEN long.
754 BOOL DOSFS_GetFullName( LPCSTR name, BOOL check_last, DOS_FULL_NAME *full )
756 BOOL found;
757 UINT flags;
758 char *p_l, *p_s, *root;
760 TRACE("%s (last=%d)\n", 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("returning %s = %s\n", full->long_name, full->short_name );
873 return TRUE;
877 /***********************************************************************
878 * GetShortPathNameA (KERNEL32.271)
880 * NOTES
881 * observed:
882 * longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
883 * *longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0
885 * more observations ( with NT 3.51 (WinDD) ):
886 * longpath <= 8.3 -> just copy longpath to shortpath
887 * longpath > 8.3 ->
888 * a) file does not exist -> return 0, LastError = ERROR_FILE_NOT_FOUND
889 * b) file does exist -> set the short filename.
890 * - trailing slashes are reproduced in the short name, even if the
891 * file is not a directory
892 * - the absolute/relative path of the short name is reproduced like found
893 * in the long name
894 * - longpath and shortpath may have the same adress
895 * Peter Ganten, 1999
897 DWORD WINAPI GetShortPathNameA( LPCSTR longpath, LPSTR shortpath,
898 DWORD shortlen )
900 DOS_FULL_NAME full_name;
901 LPSTR tmpshortpath;
902 DWORD sp = 0, lp = 0;
903 int tmplen, drive;
904 UINT flags;
906 TRACE("%s\n", longpath);
908 if (!longpath) {
909 SetLastError(ERROR_INVALID_PARAMETER);
910 return 0;
912 if (!longpath[0]) {
913 SetLastError(ERROR_BAD_PATHNAME);
914 return 0;
917 if ( ( tmpshortpath = HeapAlloc ( GetProcessHeap(), 0, MAX_PATHNAME_LEN ) ) == NULL ) {
918 SetLastError ( ERROR_NOT_ENOUGH_MEMORY );
919 return 0;
922 /* check for drive letter */
923 if ( longpath[1] == ':' ) {
924 tmpshortpath[0] = longpath[0];
925 tmpshortpath[1] = ':';
926 sp = 2;
929 if ( ( drive = DOSFS_GetPathDrive ( &longpath )) == -1 ) return 0;
930 flags = DRIVE_GetFlags ( drive );
932 while ( longpath[lp] ) {
934 /* check for path delimiters and reproduce them */
935 if ( longpath[lp] == '\\' || longpath[lp] == '/' ) {
936 tmpshortpath[sp] = longpath[lp];
937 sp++;
938 lp++;
939 continue;
942 tmplen = strcspn ( longpath + lp, "\\/" );
943 lstrcpynA ( tmpshortpath+sp, longpath + lp, tmplen+1 );
945 /* Check, if the current element is a valid dos name */
946 if ( DOSFS_ValidDOSName ( longpath + lp, !(flags & DRIVE_CASE_SENSITIVE) ) ) {
947 sp += tmplen;
948 lp += tmplen;
949 continue;
952 /* Check if the file exists and use the existing file name */
953 if ( DOSFS_GetFullName ( tmpshortpath, TRUE, &full_name ) ) {
954 lstrcpyA ( tmpshortpath+sp, strrchr ( full_name.short_name, '\\' ) + 1 );
955 sp += lstrlenA ( tmpshortpath+sp );
956 lp += tmplen;
957 continue;
960 TRACE("not found!\n" );
961 SetLastError ( ERROR_FILE_NOT_FOUND );
962 return 0;
965 lstrcpynA ( shortpath, tmpshortpath, shortlen );
966 TRACE("returning %s\n", shortpath );
967 tmplen = lstrlenA ( tmpshortpath );
968 HeapFree ( GetProcessHeap(), 0, tmpshortpath );
970 return tmplen;
974 /***********************************************************************
975 * GetShortPathName32W (KERNEL32.272)
977 DWORD WINAPI GetShortPathNameW( LPCWSTR longpath, LPWSTR shortpath,
978 DWORD shortlen )
980 LPSTR longpathA, shortpathA;
981 DWORD ret = 0;
983 longpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, longpath );
984 shortpathA = HEAP_xalloc ( GetProcessHeap(), 0, shortlen );
986 ret = GetShortPathNameA ( longpathA, shortpathA, shortlen );
987 lstrcpynAtoW ( shortpath, shortpathA, shortlen );
989 HeapFree( GetProcessHeap(), 0, longpathA );
990 HeapFree( GetProcessHeap(), 0, shortpathA );
992 return ret;
996 /***********************************************************************
997 * GetLongPathName32A (KERNEL32.xxx)
999 DWORD WINAPI GetLongPathNameA( LPCSTR shortpath, LPSTR longpath,
1000 DWORD longlen )
1002 DOS_FULL_NAME full_name;
1003 char *p;
1004 char *longfilename;
1005 DWORD shortpathlen;
1007 if (!DOSFS_GetFullName( shortpath, TRUE, &full_name )) return 0;
1008 lstrcpynA( longpath, full_name.short_name, longlen );
1009 /* Do some hackery to get the long filename.
1010 * FIXME: Would be better if it returned the
1011 * long version of the directories too
1013 longfilename = strrchr(full_name.long_name, '/')+1;
1014 if (longpath != NULL) {
1015 if ((p = strrchr( longpath, '\\' )) != NULL) {
1016 p++;
1017 longlen -= (p-longpath);
1018 lstrcpynA( p, longfilename , longlen);
1021 shortpathlen =
1022 ((strrchr( full_name.short_name, '\\' ) - full_name.short_name) + 1);
1023 return shortpathlen + strlen( longfilename );
1027 /***********************************************************************
1028 * GetLongPathName32W (KERNEL32.269)
1030 DWORD WINAPI GetLongPathNameW( LPCWSTR shortpath, LPWSTR longpath,
1031 DWORD longlen )
1033 DOS_FULL_NAME full_name;
1034 DWORD ret = 0;
1035 LPSTR shortpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, shortpath );
1037 /* FIXME: is it correct to always return a fully qualified short path? */
1038 if (DOSFS_GetFullName( shortpathA, TRUE, &full_name ))
1040 ret = strlen( full_name.short_name );
1041 lstrcpynAtoW( longpath, full_name.long_name, longlen );
1043 HeapFree( GetProcessHeap(), 0, shortpathA );
1044 return ret;
1048 /***********************************************************************
1049 * DOSFS_DoGetFullPathName
1051 * Implementation of GetFullPathName32A/W.
1053 static DWORD DOSFS_DoGetFullPathName( LPCSTR name, DWORD len, LPSTR result,
1054 BOOL unicode )
1056 char buffer[MAX_PATHNAME_LEN];
1057 int drive;
1058 char *p;
1059 DWORD ret;
1061 /* last possible position for a char != 0 */
1062 char *endchar = buffer + sizeof(buffer) - 2;
1063 *endchar = '\0';
1065 TRACE("converting '%s'\n", name );
1067 if (!name || ((drive = DOSFS_GetPathDrive( &name )) == -1) )
1068 { SetLastError( ERROR_INVALID_PARAMETER );
1069 return 0;
1072 p = buffer;
1073 *p++ = 'A' + drive;
1074 *p++ = ':';
1075 if (IS_END_OF_NAME(*name) && (*name)) /* Absolute path */
1077 while (((*name == '\\') || (*name == '/')) && (!*endchar) )
1078 *p++ = *name++;
1080 else /* Relative path or empty path */
1082 *p++ = '\\';
1083 lstrcpynA( p, DRIVE_GetDosCwd(drive), sizeof(buffer) - 4 );
1084 if ( *p )
1086 p += strlen(p);
1087 *p++ = '\\';
1090 *p = '\0';
1092 while (*name)
1094 if (*name == '.')
1096 if (IS_END_OF_NAME(name[1]))
1098 name++;
1099 while ((*name == '\\') || (*name == '/')) name++;
1100 continue;
1102 else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
1104 name += 2;
1105 while ((*name == '\\') || (*name == '/')) name++;
1107 if (p < buffer + 3) /* no previous dir component */
1108 continue;
1109 p--; /* skip previously added '\\' */
1110 while ((*p == '\\') || (*p == '/')) p--;
1111 /* skip previous dir component */
1112 while ((*p != '\\') && (*p != '/')) p--;
1113 p++;
1114 continue;
1117 if ( *endchar )
1118 { SetLastError( ERROR_PATH_NOT_FOUND );
1119 return 0;
1121 while (!IS_END_OF_NAME(*name) && (!*endchar) )
1122 *p++ = *name++;
1123 while (((*name == '\\') || (*name == '/')) && (!*endchar) )
1124 *p++ = *name++;
1126 *p = '\0';
1128 if (!(DRIVE_GetFlags(drive) & DRIVE_CASE_PRESERVING))
1129 CharUpperA( buffer );
1131 if (result)
1133 if (unicode)
1134 lstrcpynAtoW( (LPWSTR)result, buffer, len );
1135 else
1136 lstrcpynA( result, buffer, len );
1139 TRACE("returning '%s'\n", buffer );
1141 /* If the lpBuffer buffer is too small, the return value is the
1142 size of the buffer, in characters, required to hold the path. */
1144 ret = strlen(buffer);
1146 if (ret >= len )
1147 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1149 return ret;
1153 /***********************************************************************
1154 * GetFullPathName32A (KERNEL32.272)
1155 * NOTES
1156 * if the path closed with '\', *lastpart is 0
1158 DWORD WINAPI GetFullPathNameA( LPCSTR name, DWORD len, LPSTR buffer,
1159 LPSTR *lastpart )
1161 DWORD ret = DOSFS_DoGetFullPathName( name, len, buffer, FALSE );
1162 if (ret && buffer && lastpart)
1164 LPSTR p = buffer + strlen(buffer);
1166 if (*p != '\\')
1168 while ((p > buffer + 2) && (*p != '\\')) p--;
1169 *lastpart = p + 1;
1171 else *lastpart = NULL;
1173 return ret;
1177 /***********************************************************************
1178 * GetFullPathName32W (KERNEL32.273)
1180 DWORD WINAPI GetFullPathNameW( LPCWSTR name, DWORD len, LPWSTR buffer,
1181 LPWSTR *lastpart )
1183 LPSTR nameA = HEAP_strdupWtoA( GetProcessHeap(), 0, name );
1184 DWORD ret = DOSFS_DoGetFullPathName( nameA, len, (LPSTR)buffer, TRUE );
1185 HeapFree( GetProcessHeap(), 0, nameA );
1186 if (ret && buffer && lastpart)
1188 LPWSTR p = buffer + lstrlenW(buffer);
1189 if (*p != (WCHAR)'\\')
1191 while ((p > buffer + 2) && (*p != (WCHAR)'\\')) p--;
1192 *lastpart = p + 1;
1194 else *lastpart = NULL;
1196 return ret;
1199 /***********************************************************************
1200 * DOSFS_FindNextEx
1202 static int DOSFS_FindNextEx( FIND_FIRST_INFO *info, WIN32_FIND_DATAA *entry )
1204 BYTE attr = info->attr | FA_UNUSED | FA_ARCHIVE | FA_RDONLY;
1205 UINT flags = DRIVE_GetFlags( info->drive );
1206 char *p, buffer[MAX_PATHNAME_LEN];
1207 const char *drive_path;
1208 int drive_root;
1209 LPCSTR long_name, short_name;
1210 BY_HANDLE_FILE_INFORMATION fileinfo;
1211 char dos_name[13];
1213 if ((info->attr & ~(FA_UNUSED | FA_ARCHIVE | FA_RDONLY)) == FA_LABEL)
1215 if (info->cur_pos) return 0;
1216 entry->dwFileAttributes = FILE_ATTRIBUTE_LABEL;
1217 DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftCreationTime, 0 );
1218 DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftLastAccessTime, 0 );
1219 DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftLastWriteTime, 0 );
1220 entry->nFileSizeHigh = 0;
1221 entry->nFileSizeLow = 0;
1222 entry->dwReserved0 = 0;
1223 entry->dwReserved1 = 0;
1224 DOSFS_ToDosDTAFormat( DRIVE_GetLabel( info->drive ), entry->cFileName );
1225 strcpy( entry->cAlternateFileName, entry->cFileName );
1226 info->cur_pos++;
1227 return 1;
1230 drive_path = info->path + strlen(DRIVE_GetRoot( info->drive ));
1231 while ((*drive_path == '/') || (*drive_path == '\\')) drive_path++;
1232 drive_root = !*drive_path;
1234 lstrcpynA( buffer, info->path, sizeof(buffer) - 1 );
1235 strcat( buffer, "/" );
1236 p = buffer + strlen(buffer);
1238 while (DOSFS_ReadDir( info->dir, &long_name, &short_name ))
1240 info->cur_pos++;
1242 /* Don't return '.' and '..' in the root of the drive */
1243 if (drive_root && (long_name[0] == '.') &&
1244 (!long_name[1] || ((long_name[1] == '.') && !long_name[2])))
1245 continue;
1247 /* Check the long mask */
1249 if (info->long_mask)
1251 if (!DOSFS_MatchLong( info->long_mask, long_name,
1252 flags & DRIVE_CASE_SENSITIVE )) continue;
1255 /* Check the short mask */
1257 if (info->short_mask)
1259 if (!short_name)
1261 DOSFS_Hash( long_name, dos_name, TRUE,
1262 !(flags & DRIVE_CASE_SENSITIVE) );
1263 short_name = dos_name;
1265 if (!DOSFS_MatchShort( info->short_mask, short_name )) continue;
1268 /* Check the file attributes */
1270 lstrcpynA( p, long_name, sizeof(buffer) - (int)(p - buffer) );
1271 if (!FILE_Stat( buffer, &fileinfo ))
1273 WARN("can't stat %s\n", buffer);
1274 continue;
1276 if (fileinfo.dwFileAttributes & ~attr) continue;
1278 /* We now have a matching entry; fill the result and return */
1280 entry->dwFileAttributes = fileinfo.dwFileAttributes;
1281 entry->ftCreationTime = fileinfo.ftCreationTime;
1282 entry->ftLastAccessTime = fileinfo.ftLastAccessTime;
1283 entry->ftLastWriteTime = fileinfo.ftLastWriteTime;
1284 entry->nFileSizeHigh = fileinfo.nFileSizeHigh;
1285 entry->nFileSizeLow = fileinfo.nFileSizeLow;
1287 if (short_name)
1288 DOSFS_ToDosDTAFormat( short_name, entry->cAlternateFileName );
1289 else
1290 DOSFS_Hash( long_name, entry->cAlternateFileName, FALSE,
1291 !(flags & DRIVE_CASE_SENSITIVE) );
1293 lstrcpynA( entry->cFileName, long_name, sizeof(entry->cFileName) );
1294 if (!(flags & DRIVE_CASE_PRESERVING)) CharLowerA( entry->cFileName );
1295 TRACE("returning %s (%s) %02lx %ld\n",
1296 entry->cFileName, entry->cAlternateFileName,
1297 entry->dwFileAttributes, entry->nFileSizeLow );
1298 return 1;
1300 return 0; /* End of directory */
1303 /***********************************************************************
1304 * DOSFS_FindNext
1306 * Find the next matching file. Return the number of entries read to find
1307 * the matching one, or 0 if no more entries.
1308 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
1309 * file name mask. Either or both can be NULL.
1311 * NOTE: This is supposed to be only called by the int21 emulation
1312 * routines. Thus, we should own the Win16Mutex anyway.
1313 * Nevertheless, we explicitly enter it to ensure the static
1314 * directory cache is protected.
1316 int DOSFS_FindNext( const char *path, const char *short_mask,
1317 const char *long_mask, int drive, BYTE attr,
1318 int skip, WIN32_FIND_DATAA *entry )
1320 static FIND_FIRST_INFO info = { NULL };
1321 LPCSTR short_name, long_name;
1322 int count;
1324 SYSLEVEL_EnterWin16Lock();
1326 /* Check the cached directory */
1327 if (!(info.dir && info.path == path && info.short_mask == short_mask
1328 && info.long_mask == long_mask && info.drive == drive
1329 && info.attr == attr && info.cur_pos <= skip))
1331 /* Not in the cache, open it anew */
1332 if (info.dir) DOSFS_CloseDir( info.dir );
1334 info.path = (LPSTR)path;
1335 info.long_mask = (LPSTR)long_mask;
1336 info.short_mask = (LPSTR)short_mask;
1337 info.attr = attr;
1338 info.drive = drive;
1339 info.cur_pos = 0;
1340 info.dir = DOSFS_OpenDir( info.path );
1343 /* Skip to desired position */
1344 while (info.cur_pos < skip)
1345 if (info.dir && DOSFS_ReadDir( info.dir, &long_name, &short_name ))
1346 info.cur_pos++;
1347 else
1348 break;
1350 if (info.dir && info.cur_pos == skip && DOSFS_FindNextEx( &info, entry ))
1351 count = info.cur_pos - skip;
1352 else
1353 count = 0;
1355 if (!count)
1357 if (info.dir) DOSFS_CloseDir( info.dir );
1358 memset( &info, '\0', sizeof(info) );
1361 SYSLEVEL_LeaveWin16Lock();
1363 return count;
1368 /*************************************************************************
1369 * FindFirstFile16 (KERNEL.413)
1371 HANDLE16 WINAPI FindFirstFile16( LPCSTR path, WIN32_FIND_DATAA *data )
1373 DOS_FULL_NAME full_name;
1374 HGLOBAL16 handle;
1375 FIND_FIRST_INFO *info;
1377 data->dwReserved0 = data->dwReserved1 = 0x0;
1378 if (!path) return 0;
1379 if (!DOSFS_GetFullName( path, FALSE, &full_name ))
1380 return INVALID_HANDLE_VALUE16;
1381 if (!(handle = GlobalAlloc16( GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO) )))
1382 return INVALID_HANDLE_VALUE16;
1383 info = (FIND_FIRST_INFO *)GlobalLock16( handle );
1384 info->path = HEAP_strdupA( SystemHeap, 0, full_name.long_name );
1385 info->long_mask = strrchr( info->path, '/' );
1386 *(info->long_mask++) = '\0';
1387 info->short_mask = NULL;
1388 info->attr = 0xff;
1389 if (path[0] && (path[1] == ':')) info->drive = toupper(*path) - 'A';
1390 else info->drive = DRIVE_GetCurrentDrive();
1391 info->cur_pos = 0;
1393 info->dir = DOSFS_OpenDir( info->path );
1395 GlobalUnlock16( handle );
1396 if (!FindNextFile16( handle, data ))
1398 FindClose16( handle );
1399 SetLastError( ERROR_NO_MORE_FILES );
1400 return INVALID_HANDLE_VALUE16;
1402 return handle;
1406 /*************************************************************************
1407 * FindFirstFile32A (KERNEL32.123)
1409 HANDLE WINAPI FindFirstFileA( LPCSTR path, WIN32_FIND_DATAA *data )
1411 HANDLE handle = FindFirstFile16( path, data );
1412 if (handle == INVALID_HANDLE_VALUE16) return INVALID_HANDLE_VALUE;
1413 return handle;
1417 /*************************************************************************
1418 * FindFirstFile32W (KERNEL32.124)
1420 HANDLE WINAPI FindFirstFileW( LPCWSTR path, WIN32_FIND_DATAW *data )
1422 WIN32_FIND_DATAA dataA;
1423 LPSTR pathA = HEAP_strdupWtoA( GetProcessHeap(), 0, path );
1424 HANDLE handle = FindFirstFileA( pathA, &dataA );
1425 HeapFree( GetProcessHeap(), 0, pathA );
1426 if (handle != INVALID_HANDLE_VALUE)
1428 data->dwFileAttributes = dataA.dwFileAttributes;
1429 data->ftCreationTime = dataA.ftCreationTime;
1430 data->ftLastAccessTime = dataA.ftLastAccessTime;
1431 data->ftLastWriteTime = dataA.ftLastWriteTime;
1432 data->nFileSizeHigh = dataA.nFileSizeHigh;
1433 data->nFileSizeLow = dataA.nFileSizeLow;
1434 lstrcpyAtoW( data->cFileName, dataA.cFileName );
1435 lstrcpyAtoW( data->cAlternateFileName, dataA.cAlternateFileName );
1437 return handle;
1441 /*************************************************************************
1442 * FindNextFile16 (KERNEL.414)
1444 BOOL16 WINAPI FindNextFile16( HANDLE16 handle, WIN32_FIND_DATAA *data )
1446 FIND_FIRST_INFO *info;
1448 if (!(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
1450 SetLastError( ERROR_INVALID_HANDLE );
1451 return FALSE;
1453 GlobalUnlock16( handle );
1454 if (!info->path || !info->dir)
1456 SetLastError( ERROR_NO_MORE_FILES );
1457 return FALSE;
1459 if (!DOSFS_FindNextEx( info, data ))
1461 DOSFS_CloseDir( info->dir ); info->dir = NULL;
1462 HeapFree( SystemHeap, 0, info->path );
1463 info->path = info->long_mask = NULL;
1464 SetLastError( ERROR_NO_MORE_FILES );
1465 return FALSE;
1467 return TRUE;
1471 /*************************************************************************
1472 * FindNextFile32A (KERNEL32.126)
1474 BOOL WINAPI FindNextFileA( HANDLE handle, WIN32_FIND_DATAA *data )
1476 return FindNextFile16( handle, data );
1480 /*************************************************************************
1481 * FindNextFile32W (KERNEL32.127)
1483 BOOL WINAPI FindNextFileW( HANDLE handle, WIN32_FIND_DATAW *data )
1485 WIN32_FIND_DATAA dataA;
1486 if (!FindNextFileA( handle, &dataA )) return FALSE;
1487 data->dwFileAttributes = dataA.dwFileAttributes;
1488 data->ftCreationTime = dataA.ftCreationTime;
1489 data->ftLastAccessTime = dataA.ftLastAccessTime;
1490 data->ftLastWriteTime = dataA.ftLastWriteTime;
1491 data->nFileSizeHigh = dataA.nFileSizeHigh;
1492 data->nFileSizeLow = dataA.nFileSizeLow;
1493 lstrcpyAtoW( data->cFileName, dataA.cFileName );
1494 lstrcpyAtoW( data->cAlternateFileName, dataA.cAlternateFileName );
1495 return TRUE;
1499 /*************************************************************************
1500 * FindClose16 (KERNEL.415)
1502 BOOL16 WINAPI FindClose16( HANDLE16 handle )
1504 FIND_FIRST_INFO *info;
1506 if ((handle == INVALID_HANDLE_VALUE16) ||
1507 !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
1509 SetLastError( ERROR_INVALID_HANDLE );
1510 return FALSE;
1512 if (info->dir) DOSFS_CloseDir( info->dir );
1513 if (info->path) HeapFree( SystemHeap, 0, info->path );
1514 GlobalUnlock16( handle );
1515 GlobalFree16( handle );
1516 return TRUE;
1520 /*************************************************************************
1521 * FindClose32 (KERNEL32.119)
1523 BOOL WINAPI FindClose( HANDLE handle )
1525 return FindClose16( (HANDLE16)handle );
1529 /***********************************************************************
1530 * DOSFS_UnixTimeToFileTime
1532 * Convert a Unix time to FILETIME format.
1533 * The FILETIME structure is a 64-bit value representing the number of
1534 * 100-nanosecond intervals since January 1, 1601, 0:00.
1535 * 'remainder' is the nonnegative number of 100-ns intervals
1536 * corresponding to the time fraction smaller than 1 second that
1537 * couldn't be stored in the time_t value.
1539 void DOSFS_UnixTimeToFileTime( time_t unix_time, FILETIME *filetime,
1540 DWORD remainder )
1542 /* NOTES:
1544 CONSTANTS:
1545 The time difference between 1 January 1601, 00:00:00 and
1546 1 January 1970, 00:00:00 is 369 years, plus the leap years
1547 from 1604 to 1968, excluding 1700, 1800, 1900.
1548 This makes (1968 - 1600) / 4 - 3 = 89 leap days, and a total
1549 of 134774 days.
1551 Any day in that period had 24 * 60 * 60 = 86400 seconds.
1553 The time difference is 134774 * 86400 * 10000000, which can be written
1554 116444736000000000
1555 27111902 * 2^32 + 3577643008
1556 413 * 2^48 + 45534 * 2^32 + 54590 * 2^16 + 32768
1558 If you find that these constants are buggy, please change them in all
1559 instances in both conversion functions.
1561 VERSIONS:
1562 There are two versions, one of them uses long long variables and
1563 is presumably faster but not ISO C. The other one uses standard C
1564 data types and operations but relies on the assumption that negative
1565 numbers are stored as 2's complement (-1 is 0xffff....). If this
1566 assumption is violated, dates before 1970 will not convert correctly.
1567 This should however work on any reasonable architecture where WINE
1568 will run.
1570 DETAILS:
1572 Take care not to remove the casts. I have tested these functions
1573 (in both versions) for a lot of numbers. I would be interested in
1574 results on other compilers than GCC.
1576 The operations have been designed to account for the possibility
1577 of 64-bit time_t in future UNICES. Even the versions without
1578 internal long long numbers will work if time_t only is 64 bit.
1579 A 32-bit shift, which was necessary for that operation, turned out
1580 not to work correctly in GCC, besides giving the warning. So I
1581 used a double 16-bit shift instead. Numbers are in the ISO version
1582 represented by three limbs, the most significant with 32 bit, the
1583 other two with 16 bit each.
1585 As the modulo-operator % is not well-defined for negative numbers,
1586 negative divisors have been avoided in DOSFS_FileTimeToUnixTime.
1588 There might be quicker ways to do this in C. Certainly so in
1589 assembler.
1591 Claus Fischer, fischer@iue.tuwien.ac.at
1594 #if SIZEOF_LONG_LONG >= 8
1595 # define USE_LONG_LONG 1
1596 #else
1597 # define USE_LONG_LONG 0
1598 #endif
1600 #if USE_LONG_LONG /* gcc supports long long type */
1602 long long int t = unix_time;
1603 t *= 10000000;
1604 t += 116444736000000000LL;
1605 t += remainder;
1606 filetime->dwLowDateTime = (UINT)t;
1607 filetime->dwHighDateTime = (UINT)(t >> 32);
1609 #else /* ISO version */
1611 UINT a0; /* 16 bit, low bits */
1612 UINT a1; /* 16 bit, medium bits */
1613 UINT a2; /* 32 bit, high bits */
1615 /* Copy the unix time to a2/a1/a0 */
1616 a0 = unix_time & 0xffff;
1617 a1 = (unix_time >> 16) & 0xffff;
1618 /* This is obsolete if unix_time is only 32 bits, but it does not hurt.
1619 Do not replace this by >> 32, it gives a compiler warning and it does
1620 not work. */
1621 a2 = (unix_time >= 0 ? (unix_time >> 16) >> 16 :
1622 ~((~unix_time >> 16) >> 16));
1624 /* Multiply a by 10000000 (a = a2/a1/a0)
1625 Split the factor into 10000 * 1000 which are both less than 0xffff. */
1626 a0 *= 10000;
1627 a1 = a1 * 10000 + (a0 >> 16);
1628 a2 = a2 * 10000 + (a1 >> 16);
1629 a0 &= 0xffff;
1630 a1 &= 0xffff;
1632 a0 *= 1000;
1633 a1 = a1 * 1000 + (a0 >> 16);
1634 a2 = a2 * 1000 + (a1 >> 16);
1635 a0 &= 0xffff;
1636 a1 &= 0xffff;
1638 /* Add the time difference and the remainder */
1639 a0 += 32768 + (remainder & 0xffff);
1640 a1 += 54590 + (remainder >> 16 ) + (a0 >> 16);
1641 a2 += 27111902 + (a1 >> 16);
1642 a0 &= 0xffff;
1643 a1 &= 0xffff;
1645 /* Set filetime */
1646 filetime->dwLowDateTime = (a1 << 16) + a0;
1647 filetime->dwHighDateTime = a2;
1648 #endif
1652 /***********************************************************************
1653 * DOSFS_FileTimeToUnixTime
1655 * Convert a FILETIME format to Unix time.
1656 * If not NULL, 'remainder' contains the fractional part of the filetime,
1657 * in the range of [0..9999999] (even if time_t is negative).
1659 time_t DOSFS_FileTimeToUnixTime( const FILETIME *filetime, DWORD *remainder )
1661 /* Read the comment in the function DOSFS_UnixTimeToFileTime. */
1662 #if USE_LONG_LONG
1664 long long int t = filetime->dwHighDateTime;
1665 t <<= 32;
1666 t += (UINT)filetime->dwLowDateTime;
1667 t -= 116444736000000000LL;
1668 if (t < 0)
1670 if (remainder) *remainder = 9999999 - (-t - 1) % 10000000;
1671 return -1 - ((-t - 1) / 10000000);
1673 else
1675 if (remainder) *remainder = t % 10000000;
1676 return t / 10000000;
1679 #else /* ISO version */
1681 UINT a0; /* 16 bit, low bits */
1682 UINT a1; /* 16 bit, medium bits */
1683 UINT a2; /* 32 bit, high bits */
1684 UINT r; /* remainder of division */
1685 unsigned int carry; /* carry bit for subtraction */
1686 int negative; /* whether a represents a negative value */
1688 /* Copy the time values to a2/a1/a0 */
1689 a2 = (UINT)filetime->dwHighDateTime;
1690 a1 = ((UINT)filetime->dwLowDateTime ) >> 16;
1691 a0 = ((UINT)filetime->dwLowDateTime ) & 0xffff;
1693 /* Subtract the time difference */
1694 if (a0 >= 32768 ) a0 -= 32768 , carry = 0;
1695 else a0 += (1 << 16) - 32768 , carry = 1;
1697 if (a1 >= 54590 + carry) a1 -= 54590 + carry, carry = 0;
1698 else a1 += (1 << 16) - 54590 - carry, carry = 1;
1700 a2 -= 27111902 + carry;
1702 /* If a is negative, replace a by (-1-a) */
1703 negative = (a2 >= ((UINT)1) << 31);
1704 if (negative)
1706 /* Set a to -a - 1 (a is a2/a1/a0) */
1707 a0 = 0xffff - a0;
1708 a1 = 0xffff - a1;
1709 a2 = ~a2;
1712 /* Divide a by 10000000 (a = a2/a1/a0), put the rest into r.
1713 Split the divisor into 10000 * 1000 which are both less than 0xffff. */
1714 a1 += (a2 % 10000) << 16;
1715 a2 /= 10000;
1716 a0 += (a1 % 10000) << 16;
1717 a1 /= 10000;
1718 r = a0 % 10000;
1719 a0 /= 10000;
1721 a1 += (a2 % 1000) << 16;
1722 a2 /= 1000;
1723 a0 += (a1 % 1000) << 16;
1724 a1 /= 1000;
1725 r += (a0 % 1000) * 10000;
1726 a0 /= 1000;
1728 /* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
1729 if (negative)
1731 /* Set a to -a - 1 (a is a2/a1/a0) */
1732 a0 = 0xffff - a0;
1733 a1 = 0xffff - a1;
1734 a2 = ~a2;
1736 r = 9999999 - r;
1739 if (remainder) *remainder = r;
1741 /* Do not replace this by << 32, it gives a compiler warning and it does
1742 not work. */
1743 return ((((time_t)a2) << 16) << 16) + (a1 << 16) + a0;
1744 #endif
1748 /***********************************************************************
1749 * DosDateTimeToFileTime (KERNEL32.76)
1751 BOOL WINAPI DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft)
1753 struct tm newtm;
1755 newtm.tm_sec = (fattime & 0x1f) * 2;
1756 newtm.tm_min = (fattime >> 5) & 0x3f;
1757 newtm.tm_hour = (fattime >> 11);
1758 newtm.tm_mday = (fatdate & 0x1f);
1759 newtm.tm_mon = ((fatdate >> 5) & 0x0f) - 1;
1760 newtm.tm_year = (fatdate >> 9) + 80;
1761 DOSFS_UnixTimeToFileTime( mktime( &newtm ), ft, 0 );
1762 return TRUE;
1766 /***********************************************************************
1767 * FileTimeToDosDateTime (KERNEL32.111)
1769 BOOL WINAPI FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate,
1770 LPWORD fattime )
1772 time_t unixtime = DOSFS_FileTimeToUnixTime( ft, NULL );
1773 struct tm *tm = localtime( &unixtime );
1774 if (fattime)
1775 *fattime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
1776 if (fatdate)
1777 *fatdate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
1778 + tm->tm_mday;
1779 return TRUE;
1783 /***********************************************************************
1784 * LocalFileTimeToFileTime (KERNEL32.373)
1786 BOOL WINAPI LocalFileTimeToFileTime( const FILETIME *localft,
1787 LPFILETIME utcft )
1789 struct tm *xtm;
1790 DWORD remainder;
1792 /* convert from local to UTC. Perhaps not correct. FIXME */
1793 time_t unixtime = DOSFS_FileTimeToUnixTime( localft, &remainder );
1794 xtm = gmtime( &unixtime );
1795 DOSFS_UnixTimeToFileTime( mktime(xtm), utcft, remainder );
1796 return TRUE;
1800 /***********************************************************************
1801 * FileTimeToLocalFileTime (KERNEL32.112)
1803 BOOL WINAPI FileTimeToLocalFileTime( const FILETIME *utcft,
1804 LPFILETIME localft )
1806 DWORD remainder;
1807 /* convert from UTC to local. Perhaps not correct. FIXME */
1808 time_t unixtime = DOSFS_FileTimeToUnixTime( utcft, &remainder );
1809 #ifdef HAVE_TIMEGM
1810 struct tm *xtm = localtime( &unixtime );
1811 time_t localtime;
1813 localtime = timegm(xtm);
1814 DOSFS_UnixTimeToFileTime( localtime, localft, remainder );
1816 #else
1817 struct tm *xtm,*gtm;
1818 time_t time1,time2;
1820 xtm = localtime( &unixtime );
1821 gtm = gmtime( &unixtime );
1822 time1 = mktime(xtm);
1823 time2 = mktime(gtm);
1824 DOSFS_UnixTimeToFileTime( 2*time1-time2, localft, remainder );
1825 #endif
1826 return TRUE;
1830 /***********************************************************************
1831 * FileTimeToSystemTime (KERNEL32.113)
1833 BOOL WINAPI FileTimeToSystemTime( const FILETIME *ft, LPSYSTEMTIME syst )
1835 struct tm *xtm;
1836 DWORD remainder;
1837 time_t xtime = DOSFS_FileTimeToUnixTime( ft, &remainder );
1838 xtm = gmtime(&xtime);
1839 syst->wYear = xtm->tm_year+1900;
1840 syst->wMonth = xtm->tm_mon + 1;
1841 syst->wDayOfWeek = xtm->tm_wday;
1842 syst->wDay = xtm->tm_mday;
1843 syst->wHour = xtm->tm_hour;
1844 syst->wMinute = xtm->tm_min;
1845 syst->wSecond = xtm->tm_sec;
1846 syst->wMilliseconds = remainder / 10000;
1847 return TRUE;
1850 /***********************************************************************
1851 * QueryDosDeviceA (KERNEL32.413)
1853 * returns array of strings terminated by \0, terminated by \0
1855 DWORD WINAPI QueryDosDeviceA(LPCSTR devname,LPSTR target,DWORD bufsize)
1857 LPSTR s;
1858 char buffer[200];
1860 TRACE("(%s,...)\n", devname ? devname : "<null>");
1861 if (!devname) {
1862 /* return known MSDOS devices */
1863 strcpy(buffer,"CON COM1 COM2 LPT1 NUL ");
1864 while ((s=strchr(buffer,' ')))
1865 *s='\0';
1867 lstrcpynA(target,buffer,bufsize);
1868 return strlen(buffer);
1870 strcpy(buffer,"\\DEV\\");
1871 strcat(buffer,devname);
1872 if ((s=strchr(buffer,':'))) *s='\0';
1873 lstrcpynA(target,buffer,bufsize);
1874 return strlen(buffer);
1878 /***********************************************************************
1879 * QueryDosDeviceW (KERNEL32.414)
1881 * returns array of strings terminated by \0, terminated by \0
1883 DWORD WINAPI QueryDosDeviceW(LPCWSTR devname,LPWSTR target,DWORD bufsize)
1885 LPSTR devnameA = devname?HEAP_strdupWtoA(GetProcessHeap(),0,devname):NULL;
1886 LPSTR targetA = (LPSTR)HEAP_xalloc(GetProcessHeap(),0,bufsize);
1887 DWORD ret = QueryDosDeviceA(devnameA,targetA,bufsize);
1889 lstrcpynAtoW(target,targetA,bufsize);
1890 if (devnameA) HeapFree(GetProcessHeap(),0,devnameA);
1891 if (targetA) HeapFree(GetProcessHeap(),0,targetA);
1892 return ret;
1896 /***********************************************************************
1897 * SystemTimeToFileTime (KERNEL32.526)
1899 BOOL WINAPI SystemTimeToFileTime( const SYSTEMTIME *syst, LPFILETIME ft )
1901 #ifdef HAVE_TIMEGM
1902 struct tm xtm;
1903 time_t utctime;
1904 #else
1905 struct tm xtm,*local_tm,*utc_tm;
1906 time_t localtim,utctime;
1907 #endif
1909 xtm.tm_year = syst->wYear-1900;
1910 xtm.tm_mon = syst->wMonth - 1;
1911 xtm.tm_wday = syst->wDayOfWeek;
1912 xtm.tm_mday = syst->wDay;
1913 xtm.tm_hour = syst->wHour;
1914 xtm.tm_min = syst->wMinute;
1915 xtm.tm_sec = syst->wSecond; /* this is UTC */
1916 xtm.tm_isdst = -1;
1917 #ifdef HAVE_TIMEGM
1918 utctime = timegm(&xtm);
1919 DOSFS_UnixTimeToFileTime( utctime, ft,
1920 syst->wMilliseconds * 10000 );
1921 #else
1922 localtim = mktime(&xtm); /* now we've got local time */
1923 local_tm = localtime(&localtim);
1924 utc_tm = gmtime(&localtim);
1925 utctime = mktime(utc_tm);
1926 DOSFS_UnixTimeToFileTime( 2*localtim -utctime, ft,
1927 syst->wMilliseconds * 10000 );
1928 #endif
1929 return TRUE;
1932 /***********************************************************************
1933 * DefineDosDeviceA (KERNEL32.182)
1935 BOOL WINAPI DefineDosDeviceA(DWORD flags,LPCSTR devname,LPCSTR targetpath) {
1936 FIXME("(0x%08lx,%s,%s),stub!\n",flags,devname,targetpath);
1937 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1938 return FALSE;