Murali Pattathe
[wine/multimedia.git] / files / dos_fs.c
blobd1eb7b574c337809bc67bce09416bf86519ecaf3
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 in the
893 * same way, like 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 length = 0, pos = 0;
903 INT start=-1, end=-1, tmplen;
905 if (!longpath) {
906 SetLastError(ERROR_INVALID_PARAMETER);
907 return 0;
909 if (!longpath[0]) {
910 SetLastError(ERROR_BAD_PATHNAME);
911 return 0;
914 tmpshortpath = HeapAlloc( GetProcessHeap(), 0, MAX_PATHNAME_LEN );
915 if ( !tmpshortpath ) {
916 SetLastError ( ERROR_NOT_ENOUGH_MEMORY );
917 return 0;
920 /* Check for Drive-Letter */
921 if ( longpath[1] == ':' ) {
922 lstrcpynA ( tmpshortpath, longpath, 3 );
923 length = 2;
924 pos = 2;
927 /* loop over each part of the name */
928 while ( longpath[pos] ) {
930 if (( longpath[pos] == '\\' ) ||
931 ( longpath[pos+1] == '\0' ) ||
932 ( longpath[pos] == '/')) {
934 if ( start != -1 ) {
935 if ( DOSFS_ValidDOSName ( longpath + start, TRUE )) {
936 tmplen = end - start + ( (( longpath[pos] == '\\' ) || ( longpath[pos] == '/' )) ? 1 : 2 );
937 lstrcpynA ( tmpshortpath+length, longpath+start, tmplen );
938 length += tmplen - 1;
940 else {
941 DOSFS_Hash ( longpath + start, tmpshortpath+length, FALSE, FALSE );
942 length = lstrlenA ( tmpshortpath );
944 /* Check if the path, up to this point exists */
945 if ( !DOSFS_GetFullName ( tmpshortpath, TRUE, &full_name ) ) {
946 SetLastError ( ERROR_FILE_NOT_FOUND );
947 return 0;
953 if (( longpath[pos] == '\\' ) || ( longpath[pos] == '/' )) {
954 tmpshortpath[length] = '\\';
955 tmpshortpath[length+1]='\0';
956 length++;
958 pos++;
960 start = -1;
961 end = -1;
962 continue;
965 if ( start == -1 ) {
966 start = pos;
968 pos++;
969 end = pos;
972 lstrcpynA ( shortpath, tmpshortpath, shortlen );
973 length = lstrlenA ( tmpshortpath );
974 HeapFree ( GetProcessHeap(), 0, tmpshortpath );
976 return length;
980 /***********************************************************************
981 * GetShortPathName32W (KERNEL32.272)
983 DWORD WINAPI GetShortPathNameW( LPCWSTR longpath, LPWSTR shortpath,
984 DWORD shortlen )
986 LPSTR longpathA, shortpathA;
987 DWORD ret = 0;
989 longpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, longpath );
990 shortpathA = HEAP_xalloc ( GetProcessHeap(), 0, shortlen );
992 ret = GetShortPathNameA ( longpathA, shortpathA, shortlen );
993 lstrcpynAtoW ( shortpath, shortpathA, shortlen );
995 HeapFree( GetProcessHeap(), 0, longpathA );
996 HeapFree( GetProcessHeap(), 0, shortpathA );
998 return ret;
1002 /***********************************************************************
1003 * GetLongPathName32A (KERNEL32.xxx)
1005 DWORD WINAPI GetLongPathNameA( LPCSTR shortpath, LPSTR longpath,
1006 DWORD longlen )
1008 DOS_FULL_NAME full_name;
1009 char *p;
1010 char *longfilename;
1011 DWORD shortpathlen;
1013 if (!DOSFS_GetFullName( shortpath, TRUE, &full_name )) return 0;
1014 lstrcpynA( longpath, full_name.short_name, longlen );
1015 /* Do some hackery to get the long filename.
1016 * FIXME: Would be better if it returned the
1017 * long version of the directories too
1019 longfilename = strrchr(full_name.long_name, '/')+1;
1020 if (longpath != NULL) {
1021 if ((p = strrchr( longpath, '\\' )) != NULL) {
1022 p++;
1023 longlen -= (p-longpath);
1024 lstrcpynA( p, longfilename , longlen);
1027 shortpathlen =
1028 ((strrchr( full_name.short_name, '\\' ) - full_name.short_name) + 1);
1029 return shortpathlen + strlen( longfilename );
1033 /***********************************************************************
1034 * GetLongPathName32W (KERNEL32.269)
1036 DWORD WINAPI GetLongPathNameW( LPCWSTR shortpath, LPWSTR longpath,
1037 DWORD longlen )
1039 DOS_FULL_NAME full_name;
1040 DWORD ret = 0;
1041 LPSTR shortpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, shortpath );
1043 /* FIXME: is it correct to always return a fully qualified short path? */
1044 if (DOSFS_GetFullName( shortpathA, TRUE, &full_name ))
1046 ret = strlen( full_name.short_name );
1047 lstrcpynAtoW( longpath, full_name.long_name, longlen );
1049 HeapFree( GetProcessHeap(), 0, shortpathA );
1050 return ret;
1054 /***********************************************************************
1055 * DOSFS_DoGetFullPathName
1057 * Implementation of GetFullPathName32A/W.
1059 static DWORD DOSFS_DoGetFullPathName( LPCSTR name, DWORD len, LPSTR result,
1060 BOOL unicode )
1062 char buffer[MAX_PATHNAME_LEN];
1063 int drive;
1064 char *p;
1065 DWORD ret;
1067 /* last possible position for a char != 0 */
1068 char *endchar = buffer + sizeof(buffer) - 2;
1069 *endchar = '\0';
1071 TRACE("converting '%s'\n", name );
1073 if (!name || ((drive = DOSFS_GetPathDrive( &name )) == -1) )
1074 { SetLastError( ERROR_INVALID_PARAMETER );
1075 return 0;
1078 p = buffer;
1079 *p++ = 'A' + drive;
1080 *p++ = ':';
1081 if (IS_END_OF_NAME(*name) && (*name)) /* Absolute path */
1083 while (((*name == '\\') || (*name == '/')) && (!*endchar) )
1084 *p++ = *name++;
1086 else /* Relative path or empty path */
1088 *p++ = '\\';
1089 lstrcpynA( p, DRIVE_GetDosCwd(drive), sizeof(buffer) - 4 );
1090 if ( *p )
1092 p += strlen(p);
1093 *p++ = '\\';
1096 *p = '\0';
1098 while (*name)
1100 if (*name == '.')
1102 if (IS_END_OF_NAME(name[1]))
1104 name++;
1105 while ((*name == '\\') || (*name == '/')) name++;
1106 continue;
1108 else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
1110 name += 2;
1111 while ((*name == '\\') || (*name == '/')) name++;
1113 if (p < buffer + 3) /* no previous dir component */
1114 continue;
1115 p--; /* skip previously added '\\' */
1116 while ((*p == '\\') || (*p == '/')) p--;
1117 /* skip previous dir component */
1118 while ((*p != '\\') && (*p != '/')) p--;
1119 p++;
1120 continue;
1123 if ( *endchar )
1124 { SetLastError( ERROR_PATH_NOT_FOUND );
1125 return 0;
1127 while (!IS_END_OF_NAME(*name) && (!*endchar) )
1128 *p++ = *name++;
1129 while (((*name == '\\') || (*name == '/')) && (!*endchar) )
1130 *p++ = *name++;
1132 *p = '\0';
1134 if (!(DRIVE_GetFlags(drive) & DRIVE_CASE_PRESERVING))
1135 CharUpperA( buffer );
1137 if (result)
1139 if (unicode)
1140 lstrcpynAtoW( (LPWSTR)result, buffer, len );
1141 else
1142 lstrcpynA( result, buffer, len );
1145 TRACE("returning '%s'\n", buffer );
1147 /* If the lpBuffer buffer is too small, the return value is the
1148 size of the buffer, in characters, required to hold the path. */
1150 ret = strlen(buffer);
1152 if (ret >= len )
1153 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1155 return ret;
1159 /***********************************************************************
1160 * GetFullPathName32A (KERNEL32.272)
1161 * NOTES
1162 * if the path closed with '\', *lastpart is 0
1164 DWORD WINAPI GetFullPathNameA( LPCSTR name, DWORD len, LPSTR buffer,
1165 LPSTR *lastpart )
1167 DWORD ret = DOSFS_DoGetFullPathName( name, len, buffer, FALSE );
1168 if (ret && buffer && lastpart)
1170 LPSTR p = buffer + strlen(buffer);
1172 if (*p != '\\')
1174 while ((p > buffer + 2) && (*p != '\\')) p--;
1175 *lastpart = p + 1;
1177 else *lastpart = NULL;
1179 return ret;
1183 /***********************************************************************
1184 * GetFullPathName32W (KERNEL32.273)
1186 DWORD WINAPI GetFullPathNameW( LPCWSTR name, DWORD len, LPWSTR buffer,
1187 LPWSTR *lastpart )
1189 LPSTR nameA = HEAP_strdupWtoA( GetProcessHeap(), 0, name );
1190 DWORD ret = DOSFS_DoGetFullPathName( nameA, len, (LPSTR)buffer, TRUE );
1191 HeapFree( GetProcessHeap(), 0, nameA );
1192 if (ret && buffer && lastpart)
1194 LPWSTR p = buffer + lstrlenW(buffer);
1195 if (*p != (WCHAR)'\\')
1197 while ((p > buffer + 2) && (*p != (WCHAR)'\\')) p--;
1198 *lastpart = p + 1;
1200 else *lastpart = NULL;
1202 return ret;
1205 /***********************************************************************
1206 * DOSFS_FindNextEx
1208 static int DOSFS_FindNextEx( FIND_FIRST_INFO *info, WIN32_FIND_DATAA *entry )
1210 BYTE attr = info->attr | FA_UNUSED | FA_ARCHIVE | FA_RDONLY;
1211 UINT flags = DRIVE_GetFlags( info->drive );
1212 char *p, buffer[MAX_PATHNAME_LEN];
1213 const char *drive_path;
1214 int drive_root;
1215 LPCSTR long_name, short_name;
1216 BY_HANDLE_FILE_INFORMATION fileinfo;
1217 char dos_name[13];
1219 if ((info->attr & ~(FA_UNUSED | FA_ARCHIVE | FA_RDONLY)) == FA_LABEL)
1221 if (info->cur_pos) return 0;
1222 entry->dwFileAttributes = FILE_ATTRIBUTE_LABEL;
1223 DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftCreationTime, 0 );
1224 DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftLastAccessTime, 0 );
1225 DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftLastWriteTime, 0 );
1226 entry->nFileSizeHigh = 0;
1227 entry->nFileSizeLow = 0;
1228 entry->dwReserved0 = 0;
1229 entry->dwReserved1 = 0;
1230 DOSFS_ToDosDTAFormat( DRIVE_GetLabel( info->drive ), entry->cFileName );
1231 strcpy( entry->cAlternateFileName, entry->cFileName );
1232 info->cur_pos++;
1233 return 1;
1236 drive_path = info->path + strlen(DRIVE_GetRoot( info->drive ));
1237 while ((*drive_path == '/') || (*drive_path == '\\')) drive_path++;
1238 drive_root = !*drive_path;
1240 lstrcpynA( buffer, info->path, sizeof(buffer) - 1 );
1241 strcat( buffer, "/" );
1242 p = buffer + strlen(buffer);
1244 while (DOSFS_ReadDir( info->dir, &long_name, &short_name ))
1246 info->cur_pos++;
1248 /* Don't return '.' and '..' in the root of the drive */
1249 if (drive_root && (long_name[0] == '.') &&
1250 (!long_name[1] || ((long_name[1] == '.') && !long_name[2])))
1251 continue;
1253 /* Check the long mask */
1255 if (info->long_mask)
1257 if (!DOSFS_MatchLong( info->long_mask, long_name,
1258 flags & DRIVE_CASE_SENSITIVE )) continue;
1261 /* Check the short mask */
1263 if (info->short_mask)
1265 if (!short_name)
1267 DOSFS_Hash( long_name, dos_name, TRUE,
1268 !(flags & DRIVE_CASE_SENSITIVE) );
1269 short_name = dos_name;
1271 if (!DOSFS_MatchShort( info->short_mask, short_name )) continue;
1274 /* Check the file attributes */
1276 lstrcpynA( p, long_name, sizeof(buffer) - (int)(p - buffer) );
1277 if (!FILE_Stat( buffer, &fileinfo ))
1279 WARN("can't stat %s\n", buffer);
1280 continue;
1282 if (fileinfo.dwFileAttributes & ~attr) continue;
1284 /* We now have a matching entry; fill the result and return */
1286 entry->dwFileAttributes = fileinfo.dwFileAttributes;
1287 entry->ftCreationTime = fileinfo.ftCreationTime;
1288 entry->ftLastAccessTime = fileinfo.ftLastAccessTime;
1289 entry->ftLastWriteTime = fileinfo.ftLastWriteTime;
1290 entry->nFileSizeHigh = fileinfo.nFileSizeHigh;
1291 entry->nFileSizeLow = fileinfo.nFileSizeLow;
1293 if (short_name)
1294 DOSFS_ToDosDTAFormat( short_name, entry->cAlternateFileName );
1295 else
1296 DOSFS_Hash( long_name, entry->cAlternateFileName, FALSE,
1297 !(flags & DRIVE_CASE_SENSITIVE) );
1299 lstrcpynA( entry->cFileName, long_name, sizeof(entry->cFileName) );
1300 if (!(flags & DRIVE_CASE_PRESERVING)) CharLowerA( entry->cFileName );
1301 TRACE("returning %s (%s) %02lx %ld\n",
1302 entry->cFileName, entry->cAlternateFileName,
1303 entry->dwFileAttributes, entry->nFileSizeLow );
1304 return 1;
1306 return 0; /* End of directory */
1309 /***********************************************************************
1310 * DOSFS_FindNext
1312 * Find the next matching file. Return the number of entries read to find
1313 * the matching one, or 0 if no more entries.
1314 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
1315 * file name mask. Either or both can be NULL.
1317 * NOTE: This is supposed to be only called by the int21 emulation
1318 * routines. Thus, we should own the Win16Mutex anyway.
1319 * Nevertheless, we explicitly enter it to ensure the static
1320 * directory cache is protected.
1322 int DOSFS_FindNext( const char *path, const char *short_mask,
1323 const char *long_mask, int drive, BYTE attr,
1324 int skip, WIN32_FIND_DATAA *entry )
1326 static FIND_FIRST_INFO info = { NULL };
1327 LPCSTR short_name, long_name;
1328 int count;
1330 SYSLEVEL_EnterWin16Lock();
1332 /* Check the cached directory */
1333 if (!(info.dir && info.path == path && info.short_mask == short_mask
1334 && info.long_mask == long_mask && info.drive == drive
1335 && info.attr == attr && info.cur_pos <= skip))
1337 /* Not in the cache, open it anew */
1338 if (info.dir) DOSFS_CloseDir( info.dir );
1340 info.path = (LPSTR)path;
1341 info.long_mask = (LPSTR)long_mask;
1342 info.short_mask = (LPSTR)short_mask;
1343 info.attr = attr;
1344 info.drive = drive;
1345 info.cur_pos = 0;
1346 info.dir = DOSFS_OpenDir( info.path );
1349 /* Skip to desired position */
1350 while (info.cur_pos < skip)
1351 if (info.dir && DOSFS_ReadDir( info.dir, &long_name, &short_name ))
1352 info.cur_pos++;
1353 else
1354 break;
1356 if (info.dir && info.cur_pos == skip && DOSFS_FindNextEx( &info, entry ))
1357 count = info.cur_pos - skip;
1358 else
1359 count = 0;
1361 if (!count)
1363 if (info.dir) DOSFS_CloseDir( info.dir );
1364 memset( &info, '\0', sizeof(info) );
1367 SYSLEVEL_LeaveWin16Lock();
1369 return count;
1374 /*************************************************************************
1375 * FindFirstFile16 (KERNEL.413)
1377 HANDLE16 WINAPI FindFirstFile16( LPCSTR path, WIN32_FIND_DATAA *data )
1379 DOS_FULL_NAME full_name;
1380 HGLOBAL16 handle;
1381 FIND_FIRST_INFO *info;
1383 data->dwReserved0 = data->dwReserved1 = 0x0;
1384 if (!path) return 0;
1385 if (!DOSFS_GetFullName( path, FALSE, &full_name ))
1386 return INVALID_HANDLE_VALUE16;
1387 if (!(handle = GlobalAlloc16( GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO) )))
1388 return INVALID_HANDLE_VALUE16;
1389 info = (FIND_FIRST_INFO *)GlobalLock16( handle );
1390 info->path = HEAP_strdupA( SystemHeap, 0, full_name.long_name );
1391 info->long_mask = strrchr( info->path, '/' );
1392 *(info->long_mask++) = '\0';
1393 info->short_mask = NULL;
1394 info->attr = 0xff;
1395 if (path[0] && (path[1] == ':')) info->drive = toupper(*path) - 'A';
1396 else info->drive = DRIVE_GetCurrentDrive();
1397 info->cur_pos = 0;
1399 info->dir = DOSFS_OpenDir( info->path );
1401 GlobalUnlock16( handle );
1402 if (!FindNextFile16( handle, data ))
1404 FindClose16( handle );
1405 SetLastError( ERROR_NO_MORE_FILES );
1406 return INVALID_HANDLE_VALUE16;
1408 return handle;
1412 /*************************************************************************
1413 * FindFirstFile32A (KERNEL32.123)
1415 HANDLE WINAPI FindFirstFileA( LPCSTR path, WIN32_FIND_DATAA *data )
1417 HANDLE handle = FindFirstFile16( path, data );
1418 if (handle == INVALID_HANDLE_VALUE16) return INVALID_HANDLE_VALUE;
1419 return handle;
1423 /*************************************************************************
1424 * FindFirstFile32W (KERNEL32.124)
1426 HANDLE WINAPI FindFirstFileW( LPCWSTR path, WIN32_FIND_DATAW *data )
1428 WIN32_FIND_DATAA dataA;
1429 LPSTR pathA = HEAP_strdupWtoA( GetProcessHeap(), 0, path );
1430 HANDLE handle = FindFirstFileA( pathA, &dataA );
1431 HeapFree( GetProcessHeap(), 0, pathA );
1432 if (handle != INVALID_HANDLE_VALUE)
1434 data->dwFileAttributes = dataA.dwFileAttributes;
1435 data->ftCreationTime = dataA.ftCreationTime;
1436 data->ftLastAccessTime = dataA.ftLastAccessTime;
1437 data->ftLastWriteTime = dataA.ftLastWriteTime;
1438 data->nFileSizeHigh = dataA.nFileSizeHigh;
1439 data->nFileSizeLow = dataA.nFileSizeLow;
1440 lstrcpyAtoW( data->cFileName, dataA.cFileName );
1441 lstrcpyAtoW( data->cAlternateFileName, dataA.cAlternateFileName );
1443 return handle;
1447 /*************************************************************************
1448 * FindNextFile16 (KERNEL.414)
1450 BOOL16 WINAPI FindNextFile16( HANDLE16 handle, WIN32_FIND_DATAA *data )
1452 FIND_FIRST_INFO *info;
1454 if (!(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
1456 SetLastError( ERROR_INVALID_HANDLE );
1457 return FALSE;
1459 GlobalUnlock16( handle );
1460 if (!info->path || !info->dir)
1462 SetLastError( ERROR_NO_MORE_FILES );
1463 return FALSE;
1465 if (!DOSFS_FindNextEx( info, data ))
1467 DOSFS_CloseDir( info->dir ); info->dir = NULL;
1468 HeapFree( SystemHeap, 0, info->path );
1469 info->path = info->long_mask = NULL;
1470 SetLastError( ERROR_NO_MORE_FILES );
1471 return FALSE;
1473 return TRUE;
1477 /*************************************************************************
1478 * FindNextFile32A (KERNEL32.126)
1480 BOOL WINAPI FindNextFileA( HANDLE handle, WIN32_FIND_DATAA *data )
1482 return FindNextFile16( handle, data );
1486 /*************************************************************************
1487 * FindNextFile32W (KERNEL32.127)
1489 BOOL WINAPI FindNextFileW( HANDLE handle, WIN32_FIND_DATAW *data )
1491 WIN32_FIND_DATAA dataA;
1492 if (!FindNextFileA( handle, &dataA )) return FALSE;
1493 data->dwFileAttributes = dataA.dwFileAttributes;
1494 data->ftCreationTime = dataA.ftCreationTime;
1495 data->ftLastAccessTime = dataA.ftLastAccessTime;
1496 data->ftLastWriteTime = dataA.ftLastWriteTime;
1497 data->nFileSizeHigh = dataA.nFileSizeHigh;
1498 data->nFileSizeLow = dataA.nFileSizeLow;
1499 lstrcpyAtoW( data->cFileName, dataA.cFileName );
1500 lstrcpyAtoW( data->cAlternateFileName, dataA.cAlternateFileName );
1501 return TRUE;
1505 /*************************************************************************
1506 * FindClose16 (KERNEL.415)
1508 BOOL16 WINAPI FindClose16( HANDLE16 handle )
1510 FIND_FIRST_INFO *info;
1512 if ((handle == INVALID_HANDLE_VALUE16) ||
1513 !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
1515 SetLastError( ERROR_INVALID_HANDLE );
1516 return FALSE;
1518 if (info->dir) DOSFS_CloseDir( info->dir );
1519 if (info->path) HeapFree( SystemHeap, 0, info->path );
1520 GlobalUnlock16( handle );
1521 GlobalFree16( handle );
1522 return TRUE;
1526 /*************************************************************************
1527 * FindClose32 (KERNEL32.119)
1529 BOOL WINAPI FindClose( HANDLE handle )
1531 return FindClose16( (HANDLE16)handle );
1535 /***********************************************************************
1536 * DOSFS_UnixTimeToFileTime
1538 * Convert a Unix time to FILETIME format.
1539 * The FILETIME structure is a 64-bit value representing the number of
1540 * 100-nanosecond intervals since January 1, 1601, 0:00.
1541 * 'remainder' is the nonnegative number of 100-ns intervals
1542 * corresponding to the time fraction smaller than 1 second that
1543 * couldn't be stored in the time_t value.
1545 void DOSFS_UnixTimeToFileTime( time_t unix_time, FILETIME *filetime,
1546 DWORD remainder )
1548 /* NOTES:
1550 CONSTANTS:
1551 The time difference between 1 January 1601, 00:00:00 and
1552 1 January 1970, 00:00:00 is 369 years, plus the leap years
1553 from 1604 to 1968, excluding 1700, 1800, 1900.
1554 This makes (1968 - 1600) / 4 - 3 = 89 leap days, and a total
1555 of 134774 days.
1557 Any day in that period had 24 * 60 * 60 = 86400 seconds.
1559 The time difference is 134774 * 86400 * 10000000, which can be written
1560 116444736000000000
1561 27111902 * 2^32 + 3577643008
1562 413 * 2^48 + 45534 * 2^32 + 54590 * 2^16 + 32768
1564 If you find that these constants are buggy, please change them in all
1565 instances in both conversion functions.
1567 VERSIONS:
1568 There are two versions, one of them uses long long variables and
1569 is presumably faster but not ISO C. The other one uses standard C
1570 data types and operations but relies on the assumption that negative
1571 numbers are stored as 2's complement (-1 is 0xffff....). If this
1572 assumption is violated, dates before 1970 will not convert correctly.
1573 This should however work on any reasonable architecture where WINE
1574 will run.
1576 DETAILS:
1578 Take care not to remove the casts. I have tested these functions
1579 (in both versions) for a lot of numbers. I would be interested in
1580 results on other compilers than GCC.
1582 The operations have been designed to account for the possibility
1583 of 64-bit time_t in future UNICES. Even the versions without
1584 internal long long numbers will work if time_t only is 64 bit.
1585 A 32-bit shift, which was necessary for that operation, turned out
1586 not to work correctly in GCC, besides giving the warning. So I
1587 used a double 16-bit shift instead. Numbers are in the ISO version
1588 represented by three limbs, the most significant with 32 bit, the
1589 other two with 16 bit each.
1591 As the modulo-operator % is not well-defined for negative numbers,
1592 negative divisors have been avoided in DOSFS_FileTimeToUnixTime.
1594 There might be quicker ways to do this in C. Certainly so in
1595 assembler.
1597 Claus Fischer, fischer@iue.tuwien.ac.at
1600 #if SIZEOF_LONG_LONG >= 8
1601 # define USE_LONG_LONG 1
1602 #else
1603 # define USE_LONG_LONG 0
1604 #endif
1606 #if USE_LONG_LONG /* gcc supports long long type */
1608 long long int t = unix_time;
1609 t *= 10000000;
1610 t += 116444736000000000LL;
1611 t += remainder;
1612 filetime->dwLowDateTime = (UINT)t;
1613 filetime->dwHighDateTime = (UINT)(t >> 32);
1615 #else /* ISO version */
1617 UINT a0; /* 16 bit, low bits */
1618 UINT a1; /* 16 bit, medium bits */
1619 UINT a2; /* 32 bit, high bits */
1621 /* Copy the unix time to a2/a1/a0 */
1622 a0 = unix_time & 0xffff;
1623 a1 = (unix_time >> 16) & 0xffff;
1624 /* This is obsolete if unix_time is only 32 bits, but it does not hurt.
1625 Do not replace this by >> 32, it gives a compiler warning and it does
1626 not work. */
1627 a2 = (unix_time >= 0 ? (unix_time >> 16) >> 16 :
1628 ~((~unix_time >> 16) >> 16));
1630 /* Multiply a by 10000000 (a = a2/a1/a0)
1631 Split the factor into 10000 * 1000 which are both less than 0xffff. */
1632 a0 *= 10000;
1633 a1 = a1 * 10000 + (a0 >> 16);
1634 a2 = a2 * 10000 + (a1 >> 16);
1635 a0 &= 0xffff;
1636 a1 &= 0xffff;
1638 a0 *= 1000;
1639 a1 = a1 * 1000 + (a0 >> 16);
1640 a2 = a2 * 1000 + (a1 >> 16);
1641 a0 &= 0xffff;
1642 a1 &= 0xffff;
1644 /* Add the time difference and the remainder */
1645 a0 += 32768 + (remainder & 0xffff);
1646 a1 += 54590 + (remainder >> 16 ) + (a0 >> 16);
1647 a2 += 27111902 + (a1 >> 16);
1648 a0 &= 0xffff;
1649 a1 &= 0xffff;
1651 /* Set filetime */
1652 filetime->dwLowDateTime = (a1 << 16) + a0;
1653 filetime->dwHighDateTime = a2;
1654 #endif
1658 /***********************************************************************
1659 * DOSFS_FileTimeToUnixTime
1661 * Convert a FILETIME format to Unix time.
1662 * If not NULL, 'remainder' contains the fractional part of the filetime,
1663 * in the range of [0..9999999] (even if time_t is negative).
1665 time_t DOSFS_FileTimeToUnixTime( const FILETIME *filetime, DWORD *remainder )
1667 /* Read the comment in the function DOSFS_UnixTimeToFileTime. */
1668 #if USE_LONG_LONG
1670 long long int t = filetime->dwHighDateTime;
1671 t <<= 32;
1672 t += (UINT)filetime->dwLowDateTime;
1673 t -= 116444736000000000LL;
1674 if (t < 0)
1676 if (remainder) *remainder = 9999999 - (-t - 1) % 10000000;
1677 return -1 - ((-t - 1) / 10000000);
1679 else
1681 if (remainder) *remainder = t % 10000000;
1682 return t / 10000000;
1685 #else /* ISO version */
1687 UINT a0; /* 16 bit, low bits */
1688 UINT a1; /* 16 bit, medium bits */
1689 UINT a2; /* 32 bit, high bits */
1690 UINT r; /* remainder of division */
1691 unsigned int carry; /* carry bit for subtraction */
1692 int negative; /* whether a represents a negative value */
1694 /* Copy the time values to a2/a1/a0 */
1695 a2 = (UINT)filetime->dwHighDateTime;
1696 a1 = ((UINT)filetime->dwLowDateTime ) >> 16;
1697 a0 = ((UINT)filetime->dwLowDateTime ) & 0xffff;
1699 /* Subtract the time difference */
1700 if (a0 >= 32768 ) a0 -= 32768 , carry = 0;
1701 else a0 += (1 << 16) - 32768 , carry = 1;
1703 if (a1 >= 54590 + carry) a1 -= 54590 + carry, carry = 0;
1704 else a1 += (1 << 16) - 54590 - carry, carry = 1;
1706 a2 -= 27111902 + carry;
1708 /* If a is negative, replace a by (-1-a) */
1709 negative = (a2 >= ((UINT)1) << 31);
1710 if (negative)
1712 /* Set a to -a - 1 (a is a2/a1/a0) */
1713 a0 = 0xffff - a0;
1714 a1 = 0xffff - a1;
1715 a2 = ~a2;
1718 /* Divide a by 10000000 (a = a2/a1/a0), put the rest into r.
1719 Split the divisor into 10000 * 1000 which are both less than 0xffff. */
1720 a1 += (a2 % 10000) << 16;
1721 a2 /= 10000;
1722 a0 += (a1 % 10000) << 16;
1723 a1 /= 10000;
1724 r = a0 % 10000;
1725 a0 /= 10000;
1727 a1 += (a2 % 1000) << 16;
1728 a2 /= 1000;
1729 a0 += (a1 % 1000) << 16;
1730 a1 /= 1000;
1731 r += (a0 % 1000) * 10000;
1732 a0 /= 1000;
1734 /* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
1735 if (negative)
1737 /* Set a to -a - 1 (a is a2/a1/a0) */
1738 a0 = 0xffff - a0;
1739 a1 = 0xffff - a1;
1740 a2 = ~a2;
1742 r = 9999999 - r;
1745 if (remainder) *remainder = r;
1747 /* Do not replace this by << 32, it gives a compiler warning and it does
1748 not work. */
1749 return ((((time_t)a2) << 16) << 16) + (a1 << 16) + a0;
1750 #endif
1754 /***********************************************************************
1755 * DosDateTimeToFileTime (KERNEL32.76)
1757 BOOL WINAPI DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft)
1759 struct tm newtm;
1761 newtm.tm_sec = (fattime & 0x1f) * 2;
1762 newtm.tm_min = (fattime >> 5) & 0x3f;
1763 newtm.tm_hour = (fattime >> 11);
1764 newtm.tm_mday = (fatdate & 0x1f);
1765 newtm.tm_mon = ((fatdate >> 5) & 0x0f) - 1;
1766 newtm.tm_year = (fatdate >> 9) + 80;
1767 DOSFS_UnixTimeToFileTime( mktime( &newtm ), ft, 0 );
1768 return TRUE;
1772 /***********************************************************************
1773 * FileTimeToDosDateTime (KERNEL32.111)
1775 BOOL WINAPI FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate,
1776 LPWORD fattime )
1778 time_t unixtime = DOSFS_FileTimeToUnixTime( ft, NULL );
1779 struct tm *tm = localtime( &unixtime );
1780 if (fattime)
1781 *fattime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
1782 if (fatdate)
1783 *fatdate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
1784 + tm->tm_mday;
1785 return TRUE;
1789 /***********************************************************************
1790 * LocalFileTimeToFileTime (KERNEL32.373)
1792 BOOL WINAPI LocalFileTimeToFileTime( const FILETIME *localft,
1793 LPFILETIME utcft )
1795 struct tm *xtm;
1796 DWORD remainder;
1798 /* convert from local to UTC. Perhaps not correct. FIXME */
1799 time_t unixtime = DOSFS_FileTimeToUnixTime( localft, &remainder );
1800 xtm = gmtime( &unixtime );
1801 DOSFS_UnixTimeToFileTime( mktime(xtm), utcft, remainder );
1802 return TRUE;
1806 /***********************************************************************
1807 * FileTimeToLocalFileTime (KERNEL32.112)
1809 BOOL WINAPI FileTimeToLocalFileTime( const FILETIME *utcft,
1810 LPFILETIME localft )
1812 DWORD remainder;
1813 /* convert from UTC to local. Perhaps not correct. FIXME */
1814 time_t unixtime = DOSFS_FileTimeToUnixTime( utcft, &remainder );
1815 #ifdef HAVE_TIMEGM
1816 struct tm *xtm = localtime( &unixtime );
1817 time_t localtime;
1819 localtime = timegm(xtm);
1820 DOSFS_UnixTimeToFileTime( localtime, localft, remainder );
1822 #else
1823 struct tm *xtm,*gtm;
1824 time_t time1,time2;
1826 xtm = localtime( &unixtime );
1827 gtm = gmtime( &unixtime );
1828 time1 = mktime(xtm);
1829 time2 = mktime(gtm);
1830 DOSFS_UnixTimeToFileTime( 2*time1-time2, localft, remainder );
1831 #endif
1832 return TRUE;
1836 /***********************************************************************
1837 * FileTimeToSystemTime (KERNEL32.113)
1839 BOOL WINAPI FileTimeToSystemTime( const FILETIME *ft, LPSYSTEMTIME syst )
1841 struct tm *xtm;
1842 DWORD remainder;
1843 time_t xtime = DOSFS_FileTimeToUnixTime( ft, &remainder );
1844 xtm = gmtime(&xtime);
1845 syst->wYear = xtm->tm_year+1900;
1846 syst->wMonth = xtm->tm_mon + 1;
1847 syst->wDayOfWeek = xtm->tm_wday;
1848 syst->wDay = xtm->tm_mday;
1849 syst->wHour = xtm->tm_hour;
1850 syst->wMinute = xtm->tm_min;
1851 syst->wSecond = xtm->tm_sec;
1852 syst->wMilliseconds = remainder / 10000;
1853 return TRUE;
1856 /***********************************************************************
1857 * QueryDosDeviceA (KERNEL32.413)
1859 * returns array of strings terminated by \0, terminated by \0
1861 DWORD WINAPI QueryDosDeviceA(LPCSTR devname,LPSTR target,DWORD bufsize)
1863 LPSTR s;
1864 char buffer[200];
1866 TRACE("(%s,...)\n", devname ? devname : "<null>");
1867 if (!devname) {
1868 /* return known MSDOS devices */
1869 strcpy(buffer,"CON COM1 COM2 LPT1 NUL ");
1870 while ((s=strchr(buffer,' ')))
1871 *s='\0';
1873 lstrcpynA(target,buffer,bufsize);
1874 return strlen(buffer);
1876 strcpy(buffer,"\\DEV\\");
1877 strcat(buffer,devname);
1878 if ((s=strchr(buffer,':'))) *s='\0';
1879 lstrcpynA(target,buffer,bufsize);
1880 return strlen(buffer);
1884 /***********************************************************************
1885 * QueryDosDeviceW (KERNEL32.414)
1887 * returns array of strings terminated by \0, terminated by \0
1889 DWORD WINAPI QueryDosDeviceW(LPCWSTR devname,LPWSTR target,DWORD bufsize)
1891 LPSTR devnameA = devname?HEAP_strdupWtoA(GetProcessHeap(),0,devname):NULL;
1892 LPSTR targetA = (LPSTR)HEAP_xalloc(GetProcessHeap(),0,bufsize);
1893 DWORD ret = QueryDosDeviceA(devnameA,targetA,bufsize);
1895 lstrcpynAtoW(target,targetA,bufsize);
1896 if (devnameA) HeapFree(GetProcessHeap(),0,devnameA);
1897 if (targetA) HeapFree(GetProcessHeap(),0,targetA);
1898 return ret;
1902 /***********************************************************************
1903 * SystemTimeToFileTime (KERNEL32.526)
1905 BOOL WINAPI SystemTimeToFileTime( const SYSTEMTIME *syst, LPFILETIME ft )
1907 #ifdef HAVE_TIMEGM
1908 struct tm xtm;
1909 time_t utctime;
1910 #else
1911 struct tm xtm,*local_tm,*utc_tm;
1912 time_t localtim,utctime;
1913 #endif
1915 xtm.tm_year = syst->wYear-1900;
1916 xtm.tm_mon = syst->wMonth - 1;
1917 xtm.tm_wday = syst->wDayOfWeek;
1918 xtm.tm_mday = syst->wDay;
1919 xtm.tm_hour = syst->wHour;
1920 xtm.tm_min = syst->wMinute;
1921 xtm.tm_sec = syst->wSecond; /* this is UTC */
1922 xtm.tm_isdst = -1;
1923 #ifdef HAVE_TIMEGM
1924 utctime = timegm(&xtm);
1925 DOSFS_UnixTimeToFileTime( utctime, ft,
1926 syst->wMilliseconds * 10000 );
1927 #else
1928 localtim = mktime(&xtm); /* now we've got local time */
1929 local_tm = localtime(&localtim);
1930 utc_tm = gmtime(&localtim);
1931 utctime = mktime(utc_tm);
1932 DOSFS_UnixTimeToFileTime( 2*localtim -utctime, ft,
1933 syst->wMilliseconds * 10000 );
1934 #endif
1935 return TRUE;
1938 /***********************************************************************
1939 * DefineDosDeviceA (KERNEL32.182)
1941 BOOL WINAPI DefineDosDeviceA(DWORD flags,LPCSTR devname,LPCSTR targetpath) {
1942 FIXME("(0x%08lx,%s,%s),stub!\n",flags,devname,targetpath);
1943 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1944 return FALSE;