Fixed prototypes of a couple of comm routines.
[wine/gsoc_dplay.git] / files / dos_fs.c
blobdf2495468d67173af1e8894e07202a8e796b2488
1 /*
2 * DOS file system functions
4 * Copyright 1993 Erik Bos
5 * Copyright 1996 Alexandre Julliard
6 */
8 #include "config.h"
9 #include <sys/types.h>
10 #include <ctype.h>
11 #include <dirent.h>
12 #include <errno.h>
13 #include <sys/errno.h>
14 #include <fcntl.h>
15 #include <string.h>
16 #include <stdlib.h>
17 #include <sys/stat.h>
18 #include <sys/ioctl.h>
19 #include <time.h>
20 #include <unistd.h>
22 #include "windef.h"
23 #include "winuser.h"
24 #include "wine/winbase16.h"
25 #include "winerror.h"
26 #include "drive.h"
27 #include "file.h"
28 #include "heap.h"
29 #include "msdos.h"
30 #include "syslevel.h"
31 #include "server.h"
32 #include "process.h"
33 #include "options.h"
34 #include "debug.h"
36 /* Define the VFAT ioctl to get both short and long file names */
37 /* FIXME: is it possible to get this to work on other systems? */
38 #ifdef linux
39 #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, long)
40 /* We want the real kernel dirent structure, not the libc one */
41 typedef struct
43 long d_ino;
44 long d_off;
45 unsigned short d_reclen;
46 char d_name[256];
47 } KERNEL_DIRENT;
49 #else /* linux */
50 #undef VFAT_IOCTL_READDIR_BOTH /* just in case... */
51 #endif /* linux */
53 /* Chars we don't want to see in DOS file names */
54 #define INVALID_DOS_CHARS "*?<>|\"+=,;[] \345"
56 static const DOS_DEVICE DOSFS_Devices[] =
57 /* name, device flags (see Int 21/AX=0x4400) */
59 { "CON", 0xc0d3 },
60 { "PRN", 0xa0c0 },
61 { "NUL", 0x80c4 },
62 { "AUX", 0x80c0 },
63 { "LPT1", 0xa0c0 },
64 { "LPT2", 0xa0c0 },
65 { "LPT3", 0xa0c0 },
66 { "LPT4", 0xc0d3 },
67 { "COM1", 0x80c0 },
68 { "COM2", 0x80c0 },
69 { "COM3", 0x80c0 },
70 { "COM4", 0x80c0 },
71 { "SCSIMGR$", 0xc0c0 },
72 { "HPSCAN", 0xc0c0 }
75 #define GET_DRIVE(path) \
76 (((path)[1] == ':') ? toupper((path)[0]) - 'A' : DOSFS_CurDrive)
78 /* Directory info for DOSFS_ReadDir */
79 typedef struct
81 DIR *dir;
82 #ifdef VFAT_IOCTL_READDIR_BOTH
83 int fd;
84 char short_name[12];
85 KERNEL_DIRENT dirent[2];
86 #endif
87 } DOS_DIR;
89 /* Info structure for FindFirstFile handle */
90 typedef struct
92 LPSTR path;
93 LPSTR long_mask;
94 LPSTR short_mask;
95 BYTE attr;
96 int drive;
97 int cur_pos;
98 DOS_DIR *dir;
99 } FIND_FIRST_INFO;
103 /***********************************************************************
104 * DOSFS_ValidDOSName
106 * Return 1 if Unix file 'name' is also a valid MS-DOS name
107 * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
108 * File name can be terminated by '\0', '\\' or '/'.
110 static int DOSFS_ValidDOSName( const char *name, int ignore_case )
112 static const char invalid_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS;
113 const char *p = name;
114 const char *invalid = ignore_case ? (invalid_chars + 26) : invalid_chars;
115 int len = 0;
117 if (*p == '.')
119 /* Check for "." and ".." */
120 p++;
121 if (*p == '.') p++;
122 /* All other names beginning with '.' are invalid */
123 return (IS_END_OF_NAME(*p));
125 while (!IS_END_OF_NAME(*p))
127 if (strchr( invalid, *p )) return 0; /* Invalid char */
128 if (*p == '.') break; /* Start of the extension */
129 if (++len > 8) return 0; /* Name too long */
130 p++;
132 if (*p != '.') return 1; /* End of name */
133 p++;
134 if (IS_END_OF_NAME(*p)) return 0; /* Empty extension not allowed */
135 len = 0;
136 while (!IS_END_OF_NAME(*p))
138 if (strchr( invalid, *p )) return 0; /* Invalid char */
139 if (*p == '.') return 0; /* Second extension not allowed */
140 if (++len > 3) return 0; /* Extension too long */
141 p++;
143 return 1;
147 /***********************************************************************
148 * DOSFS_ToDosFCBFormat
150 * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
151 * expanding wild cards and converting to upper-case in the process.
152 * File name can be terminated by '\0', '\\' or '/'.
153 * Return FALSE if the name is not a valid DOS name.
154 * 'buffer' must be at least 12 characters long.
156 BOOL DOSFS_ToDosFCBFormat( LPCSTR name, LPSTR buffer )
158 static const char invalid_chars[] = INVALID_DOS_CHARS;
159 const char *p = name;
160 int i;
162 /* Check for "." and ".." */
163 if (*p == '.')
165 p++;
166 strcpy( buffer, ". " );
167 if (*p == '.')
169 buffer[1] = '.';
170 p++;
172 return (!*p || (*p == '/') || (*p == '\\'));
175 for (i = 0; i < 8; i++)
177 switch(*p)
179 case '\0':
180 case '\\':
181 case '/':
182 case '.':
183 buffer[i] = ' ';
184 break;
185 case '?':
186 p++;
187 /* fall through */
188 case '*':
189 buffer[i] = '?';
190 break;
191 default:
192 if (strchr( invalid_chars, *p )) return FALSE;
193 buffer[i] = toupper(*p);
194 p++;
195 break;
199 if (*p == '*')
201 /* Skip all chars after wildcard up to first dot */
202 while (*p && (*p != '/') && (*p != '\\') && (*p != '.')) p++;
204 else
206 /* Check if name too long */
207 if (*p && (*p != '/') && (*p != '\\') && (*p != '.')) return FALSE;
209 if (*p == '.') p++; /* Skip dot */
211 for (i = 8; i < 11; i++)
213 switch(*p)
215 case '\0':
216 case '\\':
217 case '/':
218 buffer[i] = ' ';
219 break;
220 case '.':
221 return FALSE; /* Second extension not allowed */
222 case '?':
223 p++;
224 /* fall through */
225 case '*':
226 buffer[i] = '?';
227 break;
228 default:
229 if (strchr( invalid_chars, *p )) return FALSE;
230 buffer[i] = toupper(*p);
231 p++;
232 break;
235 buffer[11] = '\0';
236 return TRUE;
240 /***********************************************************************
241 * DOSFS_ToDosDTAFormat
243 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
244 * converting to upper-case in the process.
245 * File name can be terminated by '\0', '\\' or '/'.
246 * 'buffer' must be at least 13 characters long.
248 static void DOSFS_ToDosDTAFormat( LPCSTR name, LPSTR buffer )
250 char *p;
252 memcpy( buffer, name, 8 );
253 for (p = buffer + 8; (p > buffer) && (p[-1] == ' '); p--);
254 *p++ = '.';
255 memcpy( p, name + 8, 3 );
256 for (p += 3; p[-1] == ' '; p--);
257 if (p[-1] == '.') p--;
258 *p = '\0';
262 /***********************************************************************
263 * DOSFS_MatchShort
265 * Check a DOS file name against a mask (both in FCB format).
267 static int DOSFS_MatchShort( const char *mask, const char *name )
269 int i;
270 for (i = 11; i > 0; i--, mask++, name++)
271 if ((*mask != '?') && (*mask != *name)) return 0;
272 return 1;
276 /***********************************************************************
277 * DOSFS_MatchLong
279 * Check a long file name against a mask.
281 static int DOSFS_MatchLong( const char *mask, const char *name,
282 int case_sensitive )
284 if (!strcmp( mask, "*.*" )) return 1;
285 while (*name && *mask)
287 if (*mask == '*')
289 mask++;
290 while (*mask == '*') mask++; /* Skip consecutive '*' */
291 if (!*mask) return 1;
292 if (case_sensitive) while (*name && (*name != *mask)) name++;
293 else while (*name && (toupper(*name) != toupper(*mask))) name++;
294 if (!*name) return 0;
296 else if (*mask != '?')
298 if (case_sensitive)
300 if (*mask != *name) return 0;
302 else if (toupper(*mask) != toupper(*name)) return 0;
304 mask++;
305 name++;
307 return (!*name && !*mask);
311 /***********************************************************************
312 * DOSFS_OpenDir
314 static DOS_DIR *DOSFS_OpenDir( LPCSTR path )
316 DOS_DIR *dir = HeapAlloc( SystemHeap, 0, sizeof(*dir) );
317 if (!dir)
319 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
320 return NULL;
323 /* Treat empty path as root directory. This simplifies path split into
324 directory and mask in several other places */
325 if (!*path) path = "/";
327 #ifdef VFAT_IOCTL_READDIR_BOTH
329 /* Check if the VFAT ioctl is supported on this directory */
331 if ((dir->fd = open( path, O_RDONLY )) != -1)
333 if (ioctl( dir->fd, VFAT_IOCTL_READDIR_BOTH, (long)dir->dirent ) == -1)
335 close( dir->fd );
336 dir->fd = -1;
338 else
340 /* Set the file pointer back at the start of the directory */
341 lseek( dir->fd, 0, SEEK_SET );
342 dir->dir = NULL;
343 return dir;
346 #endif /* VFAT_IOCTL_READDIR_BOTH */
348 /* Now use the standard opendir/readdir interface */
350 if (!(dir->dir = opendir( path )))
352 HeapFree( SystemHeap, 0, dir );
353 return NULL;
355 return dir;
359 /***********************************************************************
360 * DOSFS_CloseDir
362 static void DOSFS_CloseDir( DOS_DIR *dir )
364 #ifdef VFAT_IOCTL_READDIR_BOTH
365 if (dir->fd != -1) close( dir->fd );
366 #endif /* VFAT_IOCTL_READDIR_BOTH */
367 if (dir->dir) closedir( dir->dir );
368 HeapFree( SystemHeap, 0, dir );
372 /***********************************************************************
373 * DOSFS_ReadDir
375 static BOOL DOSFS_ReadDir( DOS_DIR *dir, LPCSTR *long_name,
376 LPCSTR *short_name )
378 struct dirent *dirent;
380 #ifdef VFAT_IOCTL_READDIR_BOTH
381 if (dir->fd != -1)
383 if (ioctl( dir->fd, VFAT_IOCTL_READDIR_BOTH, (long)dir->dirent ) != -1) {
384 if (!dir->dirent[0].d_reclen) return FALSE;
385 if (!DOSFS_ToDosFCBFormat( dir->dirent[0].d_name, dir->short_name ))
386 dir->short_name[0] = '\0';
387 *short_name = dir->short_name;
388 if (dir->dirent[1].d_name[0]) *long_name = dir->dirent[1].d_name;
389 else *long_name = dir->dirent[0].d_name;
390 return TRUE;
393 #endif /* VFAT_IOCTL_READDIR_BOTH */
395 if (!(dirent = readdir( dir->dir ))) return FALSE;
396 *long_name = dirent->d_name;
397 *short_name = NULL;
398 return TRUE;
402 /***********************************************************************
403 * DOSFS_Hash
405 * Transform a Unix file name into a hashed DOS name. If the name is a valid
406 * DOS name, it is converted to upper-case; otherwise it is replaced by a
407 * hashed version that fits in 8.3 format.
408 * File name can be terminated by '\0', '\\' or '/'.
409 * 'buffer' must be at least 13 characters long.
411 static void DOSFS_Hash( LPCSTR name, LPSTR buffer, BOOL dir_format,
412 BOOL ignore_case )
414 static const char invalid_chars[] = INVALID_DOS_CHARS "~.";
415 static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
417 const char *p, *ext;
418 char *dst;
419 unsigned short hash;
420 int i;
422 if (dir_format) strcpy( buffer, " " );
424 if (DOSFS_ValidDOSName( name, ignore_case ))
426 /* Check for '.' and '..' */
427 if (*name == '.')
429 buffer[0] = '.';
430 if (!dir_format) buffer[1] = buffer[2] = '\0';
431 if (name[1] == '.') buffer[1] = '.';
432 return;
435 /* Simply copy the name, converting to uppercase */
437 for (dst = buffer; !IS_END_OF_NAME(*name) && (*name != '.'); name++)
438 *dst++ = toupper(*name);
439 if (*name == '.')
441 if (dir_format) dst = buffer + 8;
442 else *dst++ = '.';
443 for (name++; !IS_END_OF_NAME(*name); name++)
444 *dst++ = toupper(*name);
446 if (!dir_format) *dst = '\0';
447 return;
450 /* Compute the hash code of the file name */
451 /* If you know something about hash functions, feel free to */
452 /* insert a better algorithm here... */
453 if (ignore_case)
455 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
456 hash = (hash<<3) ^ (hash>>5) ^ tolower(*p) ^ (tolower(p[1]) << 8);
457 hash = (hash<<3) ^ (hash>>5) ^ tolower(*p); /* Last character*/
459 else
461 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
462 hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
463 hash = (hash << 3) ^ (hash >> 5) ^ *p; /* Last character */
466 /* Find last dot for start of the extension */
467 for (p = name+1, ext = NULL; !IS_END_OF_NAME(*p); p++)
468 if (*p == '.') ext = p;
469 if (ext && IS_END_OF_NAME(ext[1]))
470 ext = NULL; /* Empty extension ignored */
472 /* Copy first 4 chars, replacing invalid chars with '_' */
473 for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
475 if (IS_END_OF_NAME(*p) || (p == ext)) break;
476 *dst++ = strchr( invalid_chars, *p ) ? '_' : toupper(*p);
478 /* Pad to 5 chars with '~' */
479 while (i-- >= 0) *dst++ = '~';
481 /* Insert hash code converted to 3 ASCII chars */
482 *dst++ = hash_chars[(hash >> 10) & 0x1f];
483 *dst++ = hash_chars[(hash >> 5) & 0x1f];
484 *dst++ = hash_chars[hash & 0x1f];
486 /* Copy the first 3 chars of the extension (if any) */
487 if (ext)
489 if (!dir_format) *dst++ = '.';
490 for (i = 3, ext++; (i > 0) && !IS_END_OF_NAME(*ext); i--, ext++)
491 *dst++ = strchr( invalid_chars, *ext ) ? '_' : toupper(*ext);
493 if (!dir_format) *dst = '\0';
497 /***********************************************************************
498 * DOSFS_FindUnixName
500 * Find the Unix file name in a given directory that corresponds to
501 * a file name (either in Unix or DOS format).
502 * File name can be terminated by '\0', '\\' or '/'.
503 * Return TRUE if OK, FALSE if no file name matches.
505 * 'long_buf' must be at least 'long_len' characters long. If the long name
506 * turns out to be larger than that, the function returns FALSE.
507 * 'short_buf' must be at least 13 characters long.
509 BOOL DOSFS_FindUnixName( LPCSTR path, LPCSTR name, LPSTR long_buf,
510 INT long_len, LPSTR short_buf, BOOL ignore_case)
512 DOS_DIR *dir;
513 LPCSTR long_name, short_name;
514 char dos_name[12], tmp_buf[13];
515 BOOL ret;
517 const char *p = strchr( name, '/' );
518 int len = p ? (int)(p - name) : strlen(name);
519 if ((p = strchr( name, '\\' ))) len = MIN( (int)(p - name), len );
520 if (long_len < len + 1) return FALSE;
522 TRACE(dosfs, "%s,%s\n", path, name );
524 if (!DOSFS_ToDosFCBFormat( name, dos_name )) dos_name[0] = '\0';
526 if (!(dir = DOSFS_OpenDir( path )))
528 WARN(dosfs, "(%s,%s): can't open dir: %s\n",
529 path, name, strerror(errno) );
530 return FALSE;
533 while ((ret = DOSFS_ReadDir( dir, &long_name, &short_name )))
535 /* Check against Unix name */
536 if (len == strlen(long_name))
538 if (!ignore_case)
540 if (!lstrncmpA( long_name, name, len )) break;
542 else
544 if (!lstrncmpiA( long_name, name, len )) break;
547 if (dos_name[0])
549 /* Check against hashed DOS name */
550 if (!short_name)
552 DOSFS_Hash( long_name, tmp_buf, TRUE, ignore_case );
553 short_name = tmp_buf;
555 if (!strcmp( dos_name, short_name )) break;
558 if (ret)
560 if (long_buf) strcpy( long_buf, long_name );
561 if (short_buf)
563 if (short_name)
564 DOSFS_ToDosDTAFormat( short_name, short_buf );
565 else
566 DOSFS_Hash( long_name, short_buf, FALSE, ignore_case );
568 TRACE(dosfs, "(%s,%s) -> %s (%s)\n",
569 path, name, long_name, short_buf ? short_buf : "***");
571 else
572 WARN(dosfs, "'%s' not found in '%s'\n", name, path);
573 DOSFS_CloseDir( dir );
574 return ret;
578 /***********************************************************************
579 * DOSFS_GetDevice
581 * Check if a DOS file name represents a DOS device and return the device.
583 const DOS_DEVICE *DOSFS_GetDevice( const char *name )
585 int i;
586 const char *p;
588 if (!name) return NULL; /* if FILE_DupUnixHandle was used */
589 if (name[0] && (name[1] == ':')) name += 2;
590 if ((p = strrchr( name, '/' ))) name = p + 1;
591 if ((p = strrchr( name, '\\' ))) name = p + 1;
592 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
594 const char *dev = DOSFS_Devices[i].name;
595 if (!lstrncmpiA( dev, name, strlen(dev) ))
597 p = name + strlen( dev );
598 if (!*p || (*p == '.')) return &DOSFS_Devices[i];
601 return NULL;
605 /***********************************************************************
606 * DOSFS_GetDeviceByHandle
608 const DOS_DEVICE *DOSFS_GetDeviceByHandle( HFILE hFile )
610 struct get_file_info_request req;
611 struct get_file_info_reply reply;
613 req.handle = hFile;
614 CLIENT_SendRequest( REQ_GET_FILE_INFO, -1, 1, &req, sizeof(req) );
615 if (!CLIENT_WaitSimpleReply( &reply, sizeof(reply), NULL ) &&
616 (reply.type == FILE_TYPE_UNKNOWN))
618 if ((reply.attr >= 0) &&
619 (reply.attr < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0])))
620 return &DOSFS_Devices[reply.attr];
622 return NULL;
626 /***********************************************************************
627 * DOSFS_OpenDevice
629 * Open a DOS device. This might not map 1:1 into the UNIX device concept.
631 HFILE DOSFS_OpenDevice( const char *name, DWORD access )
633 int i;
634 const char *p;
636 if (!name) return (HFILE)NULL; /* if FILE_DupUnixHandle was used */
637 if (name[0] && (name[1] == ':')) name += 2;
638 if ((p = strrchr( name, '/' ))) name = p + 1;
639 if ((p = strrchr( name, '\\' ))) name = p + 1;
640 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
642 const char *dev = DOSFS_Devices[i].name;
643 if (!lstrncmpiA( dev, name, strlen(dev) ))
645 p = name + strlen( dev );
646 if (!*p || (*p == '.')) {
647 /* got it */
648 if (!strcmp(DOSFS_Devices[i].name,"NUL"))
649 return FILE_CreateFile( "/dev/null", access,
650 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
651 OPEN_EXISTING, 0, -1 );
652 if (!strcmp(DOSFS_Devices[i].name,"CON")) {
653 HFILE to_dup;
654 HFILE handle;
655 switch (access & (GENERIC_READ|GENERIC_WRITE)) {
656 case GENERIC_READ:
657 to_dup = GetStdHandle( STD_INPUT_HANDLE );
658 break;
659 case GENERIC_WRITE:
660 to_dup = GetStdHandle( STD_OUTPUT_HANDLE );
661 break;
662 default:
663 FIXME(dosfs,"can't open CON read/write\n");
664 return HFILE_ERROR;
665 break;
667 if (!DuplicateHandle( GetCurrentProcess(), to_dup, GetCurrentProcess(),
668 &handle, 0, FALSE, DUPLICATE_SAME_ACCESS ))
669 handle = HFILE_ERROR;
670 return handle;
672 if (!strcmp(DOSFS_Devices[i].name,"SCSIMGR$") ||
673 !strcmp(DOSFS_Devices[i].name,"HPSCAN"))
675 return FILE_CreateDevice( i, access, NULL );
678 HFILE r;
679 char devname[40];
680 PROFILE_GetWineIniString("serialports",name,"",devname,sizeof devname);
682 if(devname[0])
684 TRACE(file,"DOSFS_OpenDevice %s is %s\n",
685 DOSFS_Devices[i].name,devname);
686 r = FILE_CreateFile( devname, access,
687 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
688 OPEN_EXISTING, 0, -1 );
689 TRACE(file,"Create_File return %08X\n",r);
690 return r;
694 FIXME(dosfs,"device open %s not supported (yet)\n",DOSFS_Devices[i].name);
695 return HFILE_ERROR;
699 return HFILE_ERROR;
703 /***********************************************************************
704 * DOSFS_GetPathDrive
706 * Get the drive specified by a given path name (DOS or Unix format).
708 static int DOSFS_GetPathDrive( const char **name )
710 int drive;
711 const char *p = *name;
713 if (*p && (p[1] == ':'))
715 drive = toupper(*p) - 'A';
716 *name += 2;
718 else if (*p == '/') /* Absolute Unix path? */
720 if ((drive = DRIVE_FindDriveRoot( name )) == -1)
722 MSG("Warning: %s not accessible from a DOS drive\n", *name );
723 /* Assume it really was a DOS name */
724 drive = DRIVE_GetCurrentDrive();
727 else drive = DRIVE_GetCurrentDrive();
729 if (!DRIVE_IsValid(drive))
731 SetLastError( ERROR_INVALID_DRIVE );
732 return -1;
734 return drive;
738 /***********************************************************************
739 * DOSFS_GetFullName
741 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
742 * Unix name / short DOS name pair.
743 * Return FALSE if one of the path components does not exist. The last path
744 * component is only checked if 'check_last' is non-zero.
745 * The buffers pointed to by 'long_buf' and 'short_buf' must be
746 * at least MAX_PATHNAME_LEN long.
748 BOOL DOSFS_GetFullName( LPCSTR name, BOOL check_last, DOS_FULL_NAME *full )
750 BOOL found;
751 UINT flags;
752 char *p_l, *p_s, *root;
754 TRACE(dosfs, "%s (last=%d)\n",
755 name, check_last );
757 if ((full->drive = DOSFS_GetPathDrive( &name )) == -1) return FALSE;
758 flags = DRIVE_GetFlags( full->drive );
760 lstrcpynA( full->long_name, DRIVE_GetRoot( full->drive ),
761 sizeof(full->long_name) );
762 if (full->long_name[1]) root = full->long_name + strlen(full->long_name);
763 else root = full->long_name; /* root directory */
765 strcpy( full->short_name, "A:\\" );
766 full->short_name[0] += full->drive;
768 if ((*name == '\\') || (*name == '/')) /* Absolute path */
770 while ((*name == '\\') || (*name == '/')) name++;
772 else /* Relative path */
774 lstrcpynA( root + 1, DRIVE_GetUnixCwd( full->drive ),
775 sizeof(full->long_name) - (root - full->long_name) - 1 );
776 if (root[1]) *root = '/';
777 lstrcpynA( full->short_name + 3, DRIVE_GetDosCwd( full->drive ),
778 sizeof(full->short_name) - 3 );
781 p_l = full->long_name[1] ? full->long_name + strlen(full->long_name)
782 : full->long_name;
783 p_s = full->short_name[3] ? full->short_name + strlen(full->short_name)
784 : full->short_name + 2;
785 found = TRUE;
787 while (*name && found)
789 /* Check for '.' and '..' */
791 if (*name == '.')
793 if (IS_END_OF_NAME(name[1]))
795 name++;
796 while ((*name == '\\') || (*name == '/')) name++;
797 continue;
799 else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
801 name += 2;
802 while ((*name == '\\') || (*name == '/')) name++;
803 while ((p_l > root) && (*p_l != '/')) p_l--;
804 while ((p_s > full->short_name + 2) && (*p_s != '\\')) p_s--;
805 *p_l = *p_s = '\0'; /* Remove trailing separator */
806 continue;
810 /* Make sure buffers are large enough */
812 if ((p_s >= full->short_name + sizeof(full->short_name) - 14) ||
813 (p_l >= full->long_name + sizeof(full->long_name) - 1))
815 SetLastError( ERROR_PATH_NOT_FOUND );
816 return FALSE;
819 /* Get the long and short name matching the file name */
821 if ((found = DOSFS_FindUnixName( full->long_name, name, p_l + 1,
822 sizeof(full->long_name) - (p_l - full->long_name) - 1,
823 p_s + 1, !(flags & DRIVE_CASE_SENSITIVE) )))
825 *p_l++ = '/';
826 p_l += strlen(p_l);
827 *p_s++ = '\\';
828 p_s += strlen(p_s);
829 while (!IS_END_OF_NAME(*name)) name++;
831 else if (!check_last)
833 *p_l++ = '/';
834 *p_s++ = '\\';
835 while (!IS_END_OF_NAME(*name) &&
836 (p_s < full->short_name + sizeof(full->short_name) - 1) &&
837 (p_l < full->long_name + sizeof(full->long_name) - 1))
839 *p_s++ = tolower(*name);
840 /* If the drive is case-sensitive we want to create new */
841 /* files in lower-case otherwise we can't reopen them */
842 /* under the same short name. */
843 if (flags & DRIVE_CASE_SENSITIVE) *p_l++ = tolower(*name);
844 else *p_l++ = *name;
845 name++;
847 *p_l = *p_s = '\0';
849 while ((*name == '\\') || (*name == '/')) name++;
852 if (!found)
854 if (check_last)
856 SetLastError( ERROR_FILE_NOT_FOUND );
857 return FALSE;
859 if (*name) /* Not last */
861 SetLastError( ERROR_PATH_NOT_FOUND );
862 return FALSE;
865 if (!full->long_name[0]) strcpy( full->long_name, "/" );
866 if (!full->short_name[2]) strcpy( full->short_name + 2, "\\" );
867 TRACE(dosfs, "returning %s = %s\n",
868 full->long_name, full->short_name );
869 return TRUE;
873 /***********************************************************************
874 * GetShortPathNameA (KERNEL32.271)
876 * NOTES
877 * observed:
878 * longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
879 * *longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0
881 * more observations ( with NT 3.51 (WinDD) ):
882 * longpath <= 8.3 -> just copy longpath to shortpath
883 * longpath > 8.3 ->
884 * a) file does not exist -> return 0, LastError = ERROR_FILE_NOT_FOUND
885 * b) file does exist -> set the short filename.
886 * - trailing slashes are reproduced in the short name, even if the
887 * file is not a directory
888 * - the absolute/relative path of the short name is reproduced in the
889 * same way, like the long name
890 * - longpath and shortpath may have the same adress
891 * Peter Ganten, 1999
893 DWORD WINAPI GetShortPathNameA( LPCSTR longpath, LPSTR shortpath,
894 DWORD shortlen )
896 DOS_FULL_NAME full_name;
897 LPSTR tmpshortpath;
898 DWORD length = 0, pos = 0;
899 INT start=-1, end=-1, tmplen;
901 if (!longpath) {
902 SetLastError(ERROR_INVALID_PARAMETER);
903 return 0;
905 if (!longpath[0]) {
906 SetLastError(ERROR_BAD_PATHNAME);
907 return 0;
910 tmpshortpath = HeapAlloc( GetProcessHeap(), 0, MAX_PATHNAME_LEN );
911 if ( !tmpshortpath ) {
912 SetLastError ( ERROR_NOT_ENOUGH_MEMORY );
913 return 0;
916 /* Check for Drive-Letter */
917 if ( longpath[1] == ':' ) {
918 lstrcpynA ( tmpshortpath, longpath, 3 );
919 length = 2;
920 pos = 2;
923 /* loop over each part of the name */
924 while ( longpath[pos] ) {
926 if (( longpath[pos] == '\\' ) ||
927 ( longpath[pos+1] == '\0' ) ||
928 ( longpath[pos] == '/')) {
930 if ( start != -1 ) {
931 if ( DOSFS_ValidDOSName ( longpath + start, TRUE )) {
932 tmplen = end - start + ( (( longpath[pos] == '\\' ) || ( longpath[pos] == '/' )) ? 1 : 2 );
933 lstrcpynA ( tmpshortpath+length, longpath+start, tmplen );
934 length += tmplen - 1;
936 else {
937 DOSFS_Hash ( longpath + start, tmpshortpath+length, FALSE, FALSE );
938 length = lstrlenA ( tmpshortpath );
940 /* Check if the path, up to this point exists */
941 if ( !DOSFS_GetFullName ( tmpshortpath, TRUE, &full_name ) ) {
942 SetLastError ( ERROR_FILE_NOT_FOUND );
943 return 0;
949 if (( longpath[pos] == '\\' ) || ( longpath[pos] == '/' )) {
950 tmpshortpath[length] = '\\';
951 tmpshortpath[length+1]='\0';
952 length++;
954 pos++;
956 start = -1;
957 end = -1;
958 continue;
961 if ( start == -1 ) {
962 start = pos;
964 pos++;
965 end = pos;
968 lstrcpynA ( shortpath, tmpshortpath, shortlen );
969 length = lstrlenA ( tmpshortpath );
970 HeapFree ( GetProcessHeap(), 0, tmpshortpath );
972 return length;
976 /***********************************************************************
977 * GetShortPathName32W (KERNEL32.272)
979 DWORD WINAPI GetShortPathNameW( LPCWSTR longpath, LPWSTR shortpath,
980 DWORD shortlen )
982 LPSTR longpathA, shortpathA;
983 DWORD ret = 0;
985 longpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, longpath );
986 shortpathA = HEAP_xalloc ( GetProcessHeap(), 0, shortlen );
988 ret = GetShortPathNameA ( longpathA, shortpathA, shortlen );
989 lstrcpynAtoW ( shortpath, shortpathA, shortlen );
991 HeapFree( GetProcessHeap(), 0, longpathA );
992 HeapFree( GetProcessHeap(), 0, shortpathA );
994 return ret;
998 /***********************************************************************
999 * GetLongPathName32A (KERNEL32.xxx)
1001 DWORD WINAPI GetLongPathNameA( LPCSTR shortpath, LPSTR longpath,
1002 DWORD longlen )
1004 DOS_FULL_NAME full_name;
1005 char *p;
1006 char *longfilename;
1007 DWORD shortpathlen;
1009 if (!DOSFS_GetFullName( shortpath, TRUE, &full_name )) return 0;
1010 lstrcpynA( longpath, full_name.short_name, longlen );
1011 /* Do some hackery to get the long filename.
1012 * FIXME: Would be better if it returned the
1013 * long version of the directories too
1015 longfilename = strrchr(full_name.long_name, '/')+1;
1016 if (longpath != NULL) {
1017 if ((p = strrchr( longpath, '\\' )) != NULL) {
1018 p++;
1019 longlen -= (p-longpath);
1020 lstrcpynA( p, longfilename , longlen);
1023 shortpathlen =
1024 ((strrchr( full_name.short_name, '\\' ) - full_name.short_name) + 1);
1025 return shortpathlen + strlen( longfilename );
1029 /***********************************************************************
1030 * GetLongPathName32W (KERNEL32.269)
1032 DWORD WINAPI GetLongPathNameW( LPCWSTR shortpath, LPWSTR longpath,
1033 DWORD longlen )
1035 DOS_FULL_NAME full_name;
1036 DWORD ret = 0;
1037 LPSTR shortpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, shortpath );
1039 /* FIXME: is it correct to always return a fully qualified short path? */
1040 if (DOSFS_GetFullName( shortpathA, TRUE, &full_name ))
1042 ret = strlen( full_name.short_name );
1043 lstrcpynAtoW( longpath, full_name.long_name, longlen );
1045 HeapFree( GetProcessHeap(), 0, shortpathA );
1046 return ret;
1050 /***********************************************************************
1051 * DOSFS_DoGetFullPathName
1053 * Implementation of GetFullPathName32A/W.
1055 static DWORD DOSFS_DoGetFullPathName( LPCSTR name, DWORD len, LPSTR result,
1056 BOOL unicode )
1058 char buffer[MAX_PATHNAME_LEN];
1059 int drive;
1060 char *p;
1061 DWORD ret;
1063 /* last possible position for a char != 0 */
1064 char *endchar = buffer + sizeof(buffer) - 2;
1065 *endchar = '\0';
1067 TRACE(dosfs, "converting '%s'\n", name );
1069 if (!name || !result || ((drive = DOSFS_GetPathDrive( &name )) == -1) )
1070 { SetLastError( ERROR_INVALID_PARAMETER );
1071 return 0;
1074 p = buffer;
1075 *p++ = 'A' + drive;
1076 *p++ = ':';
1077 if (IS_END_OF_NAME(*name) && (*name)) /* Absolute path */
1079 while (((*name == '\\') || (*name == '/')) && (!*endchar) )
1080 *p++ = *name++;
1082 else /* Relative path or empty path */
1084 *p++ = '\\';
1085 lstrcpynA( p, DRIVE_GetDosCwd(drive), sizeof(buffer) - 4 );
1086 if ( *p )
1088 p += strlen(p);
1089 *p++ = '\\';
1092 *p = '\0';
1094 while (*name)
1096 if (*name == '.')
1098 if (IS_END_OF_NAME(name[1]))
1100 name++;
1101 while ((*name == '\\') || (*name == '/')) name++;
1102 continue;
1104 else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
1106 name += 2;
1107 while ((*name == '\\') || (*name == '/')) name++;
1109 if (p < buffer + 3) /* no previous dir component */
1110 continue;
1111 p--; /* skip previously added '\\' */
1112 while ((*p == '\\') || (*p == '/')) p--;
1113 /* skip previous dir component */
1114 while ((*p != '\\') && (*p != '/')) p--;
1115 p++;
1116 continue;
1119 if ( *endchar )
1120 { SetLastError( ERROR_PATH_NOT_FOUND );
1121 return 0;
1123 while (!IS_END_OF_NAME(*name) && (!*endchar) )
1124 *p++ = *name++;
1125 while (((*name == '\\') || (*name == '/')) && (!*endchar) )
1126 *p++ = *name++;
1128 *p = '\0';
1130 if (!(DRIVE_GetFlags(drive) & DRIVE_CASE_PRESERVING))
1131 CharUpperA( buffer );
1133 if (unicode)
1134 lstrcpynAtoW( (LPWSTR)result, buffer, len );
1135 else
1136 lstrcpynA( result, buffer, len );
1138 TRACE(dosfs, "returning '%s'\n", buffer );
1140 /* If the lpBuffer buffer is too small, the return value is the
1141 size of the buffer, in characters, required to hold the path. */
1143 ret = strlen(buffer);
1145 if (ret >= len )
1146 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1148 return ret;
1152 /***********************************************************************
1153 * GetFullPathName32A (KERNEL32.272)
1154 * NOTES
1155 * if the path closed with '\', *lastpart is 0
1157 DWORD WINAPI GetFullPathNameA( LPCSTR name, DWORD len, LPSTR buffer,
1158 LPSTR *lastpart )
1160 DWORD ret = DOSFS_DoGetFullPathName( name, len, buffer, FALSE );
1161 if (ret && lastpart)
1163 LPSTR p = buffer + strlen(buffer);
1165 if (*p != '\\')
1167 while ((p > buffer + 2) && (*p != '\\')) p--;
1168 *lastpart = p + 1;
1170 else *lastpart = NULL;
1172 return ret;
1176 /***********************************************************************
1177 * GetFullPathName32W (KERNEL32.273)
1179 DWORD WINAPI GetFullPathNameW( LPCWSTR name, DWORD len, LPWSTR buffer,
1180 LPWSTR *lastpart )
1182 LPSTR nameA = HEAP_strdupWtoA( GetProcessHeap(), 0, name );
1183 DWORD ret = DOSFS_DoGetFullPathName( nameA, len, (LPSTR)buffer, TRUE );
1184 HeapFree( GetProcessHeap(), 0, nameA );
1185 if (ret && lastpart)
1187 LPWSTR p = buffer + lstrlenW(buffer);
1188 if (*p != (WCHAR)'\\')
1190 while ((p > buffer + 2) && (*p != (WCHAR)'\\')) p--;
1191 *lastpart = p + 1;
1193 else *lastpart = NULL;
1195 return ret;
1198 /***********************************************************************
1199 * DOSFS_FindNextEx
1201 static int DOSFS_FindNextEx( FIND_FIRST_INFO *info, WIN32_FIND_DATAA *entry )
1203 BYTE attr = info->attr | FA_UNUSED | FA_ARCHIVE | FA_RDONLY;
1204 UINT flags = DRIVE_GetFlags( info->drive );
1205 char *p, buffer[MAX_PATHNAME_LEN];
1206 const char *drive_path;
1207 int drive_root;
1208 LPCSTR long_name, short_name;
1209 BY_HANDLE_FILE_INFORMATION fileinfo;
1210 char dos_name[13];
1212 if ((info->attr & ~(FA_UNUSED | FA_ARCHIVE | FA_RDONLY)) == FA_LABEL)
1214 if (info->cur_pos) return 0;
1215 entry->dwFileAttributes = FILE_ATTRIBUTE_LABEL;
1216 DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftCreationTime, 0 );
1217 DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftLastAccessTime, 0 );
1218 DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftLastWriteTime, 0 );
1219 entry->nFileSizeHigh = 0;
1220 entry->nFileSizeLow = 0;
1221 entry->dwReserved0 = 0;
1222 entry->dwReserved1 = 0;
1223 DOSFS_ToDosDTAFormat( DRIVE_GetLabel( info->drive ), entry->cFileName );
1224 strcpy( entry->cAlternateFileName, entry->cFileName );
1225 info->cur_pos++;
1226 return 1;
1229 drive_path = info->path + strlen(DRIVE_GetRoot( info->drive ));
1230 while ((*drive_path == '/') || (*drive_path == '\\')) drive_path++;
1231 drive_root = !*drive_path;
1233 lstrcpynA( buffer, info->path, sizeof(buffer) - 1 );
1234 strcat( buffer, "/" );
1235 p = buffer + strlen(buffer);
1237 while (DOSFS_ReadDir( info->dir, &long_name, &short_name ))
1239 info->cur_pos++;
1241 /* Don't return '.' and '..' in the root of the drive */
1242 if (drive_root && (long_name[0] == '.') &&
1243 (!long_name[1] || ((long_name[1] == '.') && !long_name[2])))
1244 continue;
1246 /* Check the long mask */
1248 if (info->long_mask)
1250 if (!DOSFS_MatchLong( info->long_mask, long_name,
1251 flags & DRIVE_CASE_SENSITIVE )) continue;
1254 /* Check the short mask */
1256 if (info->short_mask)
1258 if (!short_name)
1260 DOSFS_Hash( long_name, dos_name, TRUE,
1261 !(flags & DRIVE_CASE_SENSITIVE) );
1262 short_name = dos_name;
1264 if (!DOSFS_MatchShort( info->short_mask, short_name )) continue;
1267 /* Check the file attributes */
1269 lstrcpynA( p, long_name, sizeof(buffer) - (int)(p - buffer) );
1270 if (!FILE_Stat( buffer, &fileinfo ))
1272 WARN(dosfs, "can't stat %s\n", buffer);
1273 continue;
1275 if (fileinfo.dwFileAttributes & ~attr) continue;
1277 /* We now have a matching entry; fill the result and return */
1279 entry->dwFileAttributes = fileinfo.dwFileAttributes;
1280 entry->ftCreationTime = fileinfo.ftCreationTime;
1281 entry->ftLastAccessTime = fileinfo.ftLastAccessTime;
1282 entry->ftLastWriteTime = fileinfo.ftLastWriteTime;
1283 entry->nFileSizeHigh = fileinfo.nFileSizeHigh;
1284 entry->nFileSizeLow = fileinfo.nFileSizeLow;
1286 if (short_name)
1287 DOSFS_ToDosDTAFormat( short_name, entry->cAlternateFileName );
1288 else
1289 DOSFS_Hash( long_name, entry->cAlternateFileName, FALSE,
1290 !(flags & DRIVE_CASE_SENSITIVE) );
1292 lstrcpynA( entry->cFileName, long_name, sizeof(entry->cFileName) );
1293 if (!(flags & DRIVE_CASE_PRESERVING)) CharLowerA( entry->cFileName );
1294 TRACE(dosfs, "returning %s (%s) %02lx %ld\n",
1295 entry->cFileName, entry->cAlternateFileName,
1296 entry->dwFileAttributes, entry->nFileSizeLow );
1297 return 1;
1299 return 0; /* End of directory */
1302 /***********************************************************************
1303 * DOSFS_FindNext
1305 * Find the next matching file. Return the number of entries read to find
1306 * the matching one, or 0 if no more entries.
1307 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
1308 * file name mask. Either or both can be NULL.
1310 * NOTE: This is supposed to be only called by the int21 emulation
1311 * routines. Thus, we should own the Win16Mutex anyway.
1312 * Nevertheless, we explicitly enter it to ensure the static
1313 * directory cache is protected.
1315 int DOSFS_FindNext( const char *path, const char *short_mask,
1316 const char *long_mask, int drive, BYTE attr,
1317 int skip, WIN32_FIND_DATAA *entry )
1319 static FIND_FIRST_INFO info = { NULL };
1320 LPCSTR short_name, long_name;
1321 int count;
1323 SYSLEVEL_EnterWin16Lock();
1325 /* Check the cached directory */
1326 if (!(info.dir && info.path == path && info.short_mask == short_mask
1327 && info.long_mask == long_mask && info.drive == drive
1328 && info.attr == attr && info.cur_pos <= skip))
1330 /* Not in the cache, open it anew */
1331 if (info.dir) DOSFS_CloseDir( info.dir );
1333 info.path = (LPSTR)path;
1334 info.long_mask = (LPSTR)long_mask;
1335 info.short_mask = (LPSTR)short_mask;
1336 info.attr = attr;
1337 info.drive = drive;
1338 info.cur_pos = 0;
1339 info.dir = DOSFS_OpenDir( info.path );
1342 /* Skip to desired position */
1343 while (info.cur_pos < skip)
1344 if (info.dir && DOSFS_ReadDir( info.dir, &long_name, &short_name ))
1345 info.cur_pos++;
1346 else
1347 break;
1349 if (info.dir && info.cur_pos == skip && DOSFS_FindNextEx( &info, entry ))
1350 count = info.cur_pos - skip;
1351 else
1352 count = 0;
1354 if (!count)
1356 if (info.dir) DOSFS_CloseDir( info.dir );
1357 memset( &info, '\0', sizeof(info) );
1360 SYSLEVEL_LeaveWin16Lock();
1362 return count;
1367 /*************************************************************************
1368 * FindFirstFile16 (KERNEL.413)
1370 HANDLE16 WINAPI FindFirstFile16( LPCSTR path, WIN32_FIND_DATAA *data )
1372 DOS_FULL_NAME full_name;
1373 HGLOBAL16 handle;
1374 FIND_FIRST_INFO *info;
1376 data->dwReserved0 = data->dwReserved1 = 0x0;
1377 if (!path) return 0;
1378 if (!DOSFS_GetFullName( path, FALSE, &full_name ))
1379 return INVALID_HANDLE_VALUE16;
1380 if (!(handle = GlobalAlloc16( GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO) )))
1381 return INVALID_HANDLE_VALUE16;
1382 info = (FIND_FIRST_INFO *)GlobalLock16( handle );
1383 info->path = HEAP_strdupA( SystemHeap, 0, full_name.long_name );
1384 info->long_mask = strrchr( info->path, '/' );
1385 *(info->long_mask++) = '\0';
1386 info->short_mask = NULL;
1387 info->attr = 0xff;
1388 if (path[0] && (path[1] == ':')) info->drive = toupper(*path) - 'A';
1389 else info->drive = DRIVE_GetCurrentDrive();
1390 info->cur_pos = 0;
1392 info->dir = DOSFS_OpenDir( info->path );
1394 GlobalUnlock16( handle );
1395 if (!FindNextFile16( handle, data ))
1397 FindClose16( handle );
1398 SetLastError( ERROR_NO_MORE_FILES );
1399 return INVALID_HANDLE_VALUE16;
1401 return handle;
1405 /*************************************************************************
1406 * FindFirstFile32A (KERNEL32.123)
1408 HANDLE WINAPI FindFirstFileA( LPCSTR path, WIN32_FIND_DATAA *data )
1410 HANDLE handle = FindFirstFile16( path, data );
1411 if (handle == INVALID_HANDLE_VALUE16) return INVALID_HANDLE_VALUE;
1412 return handle;
1416 /*************************************************************************
1417 * FindFirstFile32W (KERNEL32.124)
1419 HANDLE WINAPI FindFirstFileW( LPCWSTR path, WIN32_FIND_DATAW *data )
1421 WIN32_FIND_DATAA dataA;
1422 LPSTR pathA = HEAP_strdupWtoA( GetProcessHeap(), 0, path );
1423 HANDLE handle = FindFirstFileA( pathA, &dataA );
1424 HeapFree( GetProcessHeap(), 0, pathA );
1425 if (handle != INVALID_HANDLE_VALUE)
1427 data->dwFileAttributes = dataA.dwFileAttributes;
1428 data->ftCreationTime = dataA.ftCreationTime;
1429 data->ftLastAccessTime = dataA.ftLastAccessTime;
1430 data->ftLastWriteTime = dataA.ftLastWriteTime;
1431 data->nFileSizeHigh = dataA.nFileSizeHigh;
1432 data->nFileSizeLow = dataA.nFileSizeLow;
1433 lstrcpyAtoW( data->cFileName, dataA.cFileName );
1434 lstrcpyAtoW( data->cAlternateFileName, dataA.cAlternateFileName );
1436 return handle;
1440 /*************************************************************************
1441 * FindNextFile16 (KERNEL.414)
1443 BOOL16 WINAPI FindNextFile16( HANDLE16 handle, WIN32_FIND_DATAA *data )
1445 FIND_FIRST_INFO *info;
1447 if (!(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
1449 SetLastError( ERROR_INVALID_HANDLE );
1450 return FALSE;
1452 GlobalUnlock16( handle );
1453 if (!info->path || !info->dir)
1455 SetLastError( ERROR_NO_MORE_FILES );
1456 return FALSE;
1458 if (!DOSFS_FindNextEx( info, data ))
1460 DOSFS_CloseDir( info->dir ); info->dir = NULL;
1461 HeapFree( SystemHeap, 0, info->path );
1462 info->path = info->long_mask = NULL;
1463 SetLastError( ERROR_NO_MORE_FILES );
1464 return FALSE;
1466 return TRUE;
1470 /*************************************************************************
1471 * FindNextFile32A (KERNEL32.126)
1473 BOOL WINAPI FindNextFileA( HANDLE handle, WIN32_FIND_DATAA *data )
1475 return FindNextFile16( handle, data );
1479 /*************************************************************************
1480 * FindNextFile32W (KERNEL32.127)
1482 BOOL WINAPI FindNextFileW( HANDLE handle, WIN32_FIND_DATAW *data )
1484 WIN32_FIND_DATAA dataA;
1485 if (!FindNextFileA( handle, &dataA )) return FALSE;
1486 data->dwFileAttributes = dataA.dwFileAttributes;
1487 data->ftCreationTime = dataA.ftCreationTime;
1488 data->ftLastAccessTime = dataA.ftLastAccessTime;
1489 data->ftLastWriteTime = dataA.ftLastWriteTime;
1490 data->nFileSizeHigh = dataA.nFileSizeHigh;
1491 data->nFileSizeLow = dataA.nFileSizeLow;
1492 lstrcpyAtoW( data->cFileName, dataA.cFileName );
1493 lstrcpyAtoW( data->cAlternateFileName, dataA.cAlternateFileName );
1494 return TRUE;
1498 /*************************************************************************
1499 * FindClose16 (KERNEL.415)
1501 BOOL16 WINAPI FindClose16( HANDLE16 handle )
1503 FIND_FIRST_INFO *info;
1505 if ((handle == INVALID_HANDLE_VALUE16) ||
1506 !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
1508 SetLastError( ERROR_INVALID_HANDLE );
1509 return FALSE;
1511 if (info->dir) DOSFS_CloseDir( info->dir );
1512 if (info->path) HeapFree( SystemHeap, 0, info->path );
1513 GlobalUnlock16( handle );
1514 GlobalFree16( handle );
1515 return TRUE;
1519 /*************************************************************************
1520 * FindClose32 (KERNEL32.119)
1522 BOOL WINAPI FindClose( HANDLE handle )
1524 return FindClose16( (HANDLE16)handle );
1528 /***********************************************************************
1529 * DOSFS_UnixTimeToFileTime
1531 * Convert a Unix time to FILETIME format.
1532 * The FILETIME structure is a 64-bit value representing the number of
1533 * 100-nanosecond intervals since January 1, 1601, 0:00.
1534 * 'remainder' is the nonnegative number of 100-ns intervals
1535 * corresponding to the time fraction smaller than 1 second that
1536 * couldn't be stored in the time_t value.
1538 void DOSFS_UnixTimeToFileTime( time_t unix_time, FILETIME *filetime,
1539 DWORD remainder )
1541 /* NOTES:
1543 CONSTANTS:
1544 The time difference between 1 January 1601, 00:00:00 and
1545 1 January 1970, 00:00:00 is 369 years, plus the leap years
1546 from 1604 to 1968, excluding 1700, 1800, 1900.
1547 This makes (1968 - 1600) / 4 - 3 = 89 leap days, and a total
1548 of 134774 days.
1550 Any day in that period had 24 * 60 * 60 = 86400 seconds.
1552 The time difference is 134774 * 86400 * 10000000, which can be written
1553 116444736000000000
1554 27111902 * 2^32 + 3577643008
1555 413 * 2^48 + 45534 * 2^32 + 54590 * 2^16 + 32768
1557 If you find that these constants are buggy, please change them in all
1558 instances in both conversion functions.
1560 VERSIONS:
1561 There are two versions, one of them uses long long variables and
1562 is presumably faster but not ISO C. The other one uses standard C
1563 data types and operations but relies on the assumption that negative
1564 numbers are stored as 2's complement (-1 is 0xffff....). If this
1565 assumption is violated, dates before 1970 will not convert correctly.
1566 This should however work on any reasonable architecture where WINE
1567 will run.
1569 DETAILS:
1571 Take care not to remove the casts. I have tested these functions
1572 (in both versions) for a lot of numbers. I would be interested in
1573 results on other compilers than GCC.
1575 The operations have been designed to account for the possibility
1576 of 64-bit time_t in future UNICES. Even the versions without
1577 internal long long numbers will work if time_t only is 64 bit.
1578 A 32-bit shift, which was necessary for that operation, turned out
1579 not to work correctly in GCC, besides giving the warning. So I
1580 used a double 16-bit shift instead. Numbers are in the ISO version
1581 represented by three limbs, the most significant with 32 bit, the
1582 other two with 16 bit each.
1584 As the modulo-operator % is not well-defined for negative numbers,
1585 negative divisors have been avoided in DOSFS_FileTimeToUnixTime.
1587 There might be quicker ways to do this in C. Certainly so in
1588 assembler.
1590 Claus Fischer, fischer@iue.tuwien.ac.at
1593 #if (SIZEOF_LONG_LONG >= 8)
1594 # define USE_LONG_LONG 1
1595 #else
1596 # define USE_LONG_LONG 0
1597 #endif
1599 #if USE_LONG_LONG /* gcc supports long long type */
1601 long long int t = unix_time;
1602 t *= 10000000;
1603 t += 116444736000000000LL;
1604 t += remainder;
1605 filetime->dwLowDateTime = (UINT)t;
1606 filetime->dwHighDateTime = (UINT)(t >> 32);
1608 #else /* ISO version */
1610 UINT a0; /* 16 bit, low bits */
1611 UINT a1; /* 16 bit, medium bits */
1612 UINT a2; /* 32 bit, high bits */
1614 /* Copy the unix time to a2/a1/a0 */
1615 a0 = unix_time & 0xffff;
1616 a1 = (unix_time >> 16) & 0xffff;
1617 /* This is obsolete if unix_time is only 32 bits, but it does not hurt.
1618 Do not replace this by >> 32, it gives a compiler warning and it does
1619 not work. */
1620 a2 = (unix_time >= 0 ? (unix_time >> 16) >> 16 :
1621 ~((~unix_time >> 16) >> 16));
1623 /* Multiply a by 10000000 (a = a2/a1/a0)
1624 Split the factor into 10000 * 1000 which are both less than 0xffff. */
1625 a0 *= 10000;
1626 a1 = a1 * 10000 + (a0 >> 16);
1627 a2 = a2 * 10000 + (a1 >> 16);
1628 a0 &= 0xffff;
1629 a1 &= 0xffff;
1631 a0 *= 1000;
1632 a1 = a1 * 1000 + (a0 >> 16);
1633 a2 = a2 * 1000 + (a1 >> 16);
1634 a0 &= 0xffff;
1635 a1 &= 0xffff;
1637 /* Add the time difference and the remainder */
1638 a0 += 32768 + (remainder & 0xffff);
1639 a1 += 54590 + (remainder >> 16 ) + (a0 >> 16);
1640 a2 += 27111902 + (a1 >> 16);
1641 a0 &= 0xffff;
1642 a1 &= 0xffff;
1644 /* Set filetime */
1645 filetime->dwLowDateTime = (a1 << 16) + a0;
1646 filetime->dwHighDateTime = a2;
1647 #endif
1651 /***********************************************************************
1652 * DOSFS_FileTimeToUnixTime
1654 * Convert a FILETIME format to Unix time.
1655 * If not NULL, 'remainder' contains the fractional part of the filetime,
1656 * in the range of [0..9999999] (even if time_t is negative).
1658 time_t DOSFS_FileTimeToUnixTime( const FILETIME *filetime, DWORD *remainder )
1660 /* Read the comment in the function DOSFS_UnixTimeToFileTime. */
1661 #if USE_LONG_LONG
1663 long long int t = filetime->dwHighDateTime;
1664 t <<= 32;
1665 t += (UINT)filetime->dwLowDateTime;
1666 t -= 116444736000000000LL;
1667 if (t < 0)
1669 if (remainder) *remainder = 9999999 - (-t - 1) % 10000000;
1670 return -1 - ((-t - 1) / 10000000);
1672 else
1674 if (remainder) *remainder = t % 10000000;
1675 return t / 10000000;
1678 #else /* ISO version */
1680 UINT a0; /* 16 bit, low bits */
1681 UINT a1; /* 16 bit, medium bits */
1682 UINT a2; /* 32 bit, high bits */
1683 UINT r; /* remainder of division */
1684 unsigned int carry; /* carry bit for subtraction */
1685 int negative; /* whether a represents a negative value */
1687 /* Copy the time values to a2/a1/a0 */
1688 a2 = (UINT)filetime->dwHighDateTime;
1689 a1 = ((UINT)filetime->dwLowDateTime ) >> 16;
1690 a0 = ((UINT)filetime->dwLowDateTime ) & 0xffff;
1692 /* Subtract the time difference */
1693 if (a0 >= 32768 ) a0 -= 32768 , carry = 0;
1694 else a0 += (1 << 16) - 32768 , carry = 1;
1696 if (a1 >= 54590 + carry) a1 -= 54590 + carry, carry = 0;
1697 else a1 += (1 << 16) - 54590 - carry, carry = 1;
1699 a2 -= 27111902 + carry;
1701 /* If a is negative, replace a by (-1-a) */
1702 negative = (a2 >= ((UINT)1) << 31);
1703 if (negative)
1705 /* Set a to -a - 1 (a is a2/a1/a0) */
1706 a0 = 0xffff - a0;
1707 a1 = 0xffff - a1;
1708 a2 = ~a2;
1711 /* Divide a by 10000000 (a = a2/a1/a0), put the rest into r.
1712 Split the divisor into 10000 * 1000 which are both less than 0xffff. */
1713 a1 += (a2 % 10000) << 16;
1714 a2 /= 10000;
1715 a0 += (a1 % 10000) << 16;
1716 a1 /= 10000;
1717 r = a0 % 10000;
1718 a0 /= 10000;
1720 a1 += (a2 % 1000) << 16;
1721 a2 /= 1000;
1722 a0 += (a1 % 1000) << 16;
1723 a1 /= 1000;
1724 r += (a0 % 1000) * 10000;
1725 a0 /= 1000;
1727 /* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
1728 if (negative)
1730 /* Set a to -a - 1 (a is a2/a1/a0) */
1731 a0 = 0xffff - a0;
1732 a1 = 0xffff - a1;
1733 a2 = ~a2;
1735 r = 9999999 - r;
1738 if (remainder) *remainder = r;
1740 /* Do not replace this by << 32, it gives a compiler warning and it does
1741 not work. */
1742 return ((((time_t)a2) << 16) << 16) + (a1 << 16) + a0;
1743 #endif
1747 /***********************************************************************
1748 * DosDateTimeToFileTime (KERNEL32.76)
1750 BOOL WINAPI DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft)
1752 struct tm newtm;
1754 newtm.tm_sec = (fattime & 0x1f) * 2;
1755 newtm.tm_min = (fattime >> 5) & 0x3f;
1756 newtm.tm_hour = (fattime >> 11);
1757 newtm.tm_mday = (fatdate & 0x1f);
1758 newtm.tm_mon = ((fatdate >> 5) & 0x0f) - 1;
1759 newtm.tm_year = (fatdate >> 9) + 80;
1760 DOSFS_UnixTimeToFileTime( mktime( &newtm ), ft, 0 );
1761 return TRUE;
1765 /***********************************************************************
1766 * FileTimeToDosDateTime (KERNEL32.111)
1768 BOOL WINAPI FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate,
1769 LPWORD fattime )
1771 time_t unixtime = DOSFS_FileTimeToUnixTime( ft, NULL );
1772 struct tm *tm = localtime( &unixtime );
1773 if (fattime)
1774 *fattime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
1775 if (fatdate)
1776 *fatdate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
1777 + tm->tm_mday;
1778 return TRUE;
1782 /***********************************************************************
1783 * LocalFileTimeToFileTime (KERNEL32.373)
1785 BOOL WINAPI LocalFileTimeToFileTime( const FILETIME *localft,
1786 LPFILETIME utcft )
1788 struct tm *xtm;
1789 DWORD remainder;
1791 /* convert from local to UTC. Perhaps not correct. FIXME */
1792 time_t unixtime = DOSFS_FileTimeToUnixTime( localft, &remainder );
1793 xtm = gmtime( &unixtime );
1794 DOSFS_UnixTimeToFileTime( mktime(xtm), utcft, remainder );
1795 return TRUE;
1799 /***********************************************************************
1800 * FileTimeToLocalFileTime (KERNEL32.112)
1802 BOOL WINAPI FileTimeToLocalFileTime( const FILETIME *utcft,
1803 LPFILETIME localft )
1805 DWORD remainder;
1806 /* convert from UTC to local. Perhaps not correct. FIXME */
1807 time_t unixtime = DOSFS_FileTimeToUnixTime( utcft, &remainder );
1808 #ifdef HAVE_TIMEGM
1809 struct tm *xtm = localtime( &unixtime );
1810 time_t localtime;
1812 localtime = timegm(xtm);
1813 DOSFS_UnixTimeToFileTime( localtime, localft, remainder );
1815 #else
1816 struct tm *xtm,*gtm;
1817 time_t time1,time2;
1819 xtm = localtime( &unixtime );
1820 gtm = gmtime( &unixtime );
1821 time1 = mktime(xtm);
1822 time2 = mktime(gtm);
1823 DOSFS_UnixTimeToFileTime( 2*time1-time2, localft, remainder );
1824 #endif
1825 return TRUE;
1829 /***********************************************************************
1830 * FileTimeToSystemTime (KERNEL32.113)
1832 BOOL WINAPI FileTimeToSystemTime( const FILETIME *ft, LPSYSTEMTIME syst )
1834 struct tm *xtm;
1835 DWORD remainder;
1836 time_t xtime = DOSFS_FileTimeToUnixTime( ft, &remainder );
1837 xtm = gmtime(&xtime);
1838 syst->wYear = xtm->tm_year+1900;
1839 syst->wMonth = xtm->tm_mon + 1;
1840 syst->wDayOfWeek = xtm->tm_wday;
1841 syst->wDay = xtm->tm_mday;
1842 syst->wHour = xtm->tm_hour;
1843 syst->wMinute = xtm->tm_min;
1844 syst->wSecond = xtm->tm_sec;
1845 syst->wMilliseconds = remainder / 10000;
1846 return TRUE;
1849 /***********************************************************************
1850 * QueryDosDeviceA (KERNEL32.413)
1852 * returns array of strings terminated by \0, terminated by \0
1854 DWORD WINAPI QueryDosDeviceA(LPCSTR devname,LPSTR target,DWORD bufsize)
1856 LPSTR s;
1857 char buffer[200];
1859 TRACE(dosfs,"(%s,...)\n",devname?devname:"<null>");
1860 if (!devname) {
1861 /* return known MSDOS devices */
1862 lstrcpyA(buffer,"CON COM1 COM2 LPT1 NUL ");
1863 while ((s=strchr(buffer,' ')))
1864 *s='\0';
1866 lstrcpynA(target,buffer,bufsize);
1867 return strlen(buffer);
1869 lstrcpyA(buffer,"\\DEV\\");
1870 lstrcatA(buffer,devname);
1871 if ((s=strchr(buffer,':'))) *s='\0';
1872 lstrcpynA(target,buffer,bufsize);
1873 return strlen(buffer);
1877 /***********************************************************************
1878 * QueryDosDeviceW (KERNEL32.414)
1880 * returns array of strings terminated by \0, terminated by \0
1882 DWORD WINAPI QueryDosDeviceW(LPCWSTR devname,LPWSTR target,DWORD bufsize)
1884 LPSTR devnameA = devname?HEAP_strdupWtoA(GetProcessHeap(),0,devname):NULL;
1885 LPSTR targetA = (LPSTR)HEAP_xalloc(GetProcessHeap(),0,bufsize);
1886 DWORD ret = QueryDosDeviceA(devnameA,targetA,bufsize);
1888 lstrcpynAtoW(target,targetA,bufsize);
1889 if (devnameA) HeapFree(GetProcessHeap(),0,devnameA);
1890 if (targetA) HeapFree(GetProcessHeap(),0,targetA);
1891 return ret;
1895 /***********************************************************************
1896 * SystemTimeToFileTime (KERNEL32.526)
1898 BOOL WINAPI SystemTimeToFileTime( const SYSTEMTIME *syst, LPFILETIME ft )
1900 #ifdef HAVE_TIMEGM
1901 struct tm xtm;
1902 time_t utctime;
1903 #else
1904 struct tm xtm,*local_tm,*utc_tm;
1905 time_t localtim,utctime;
1906 #endif
1908 xtm.tm_year = syst->wYear-1900;
1909 xtm.tm_mon = syst->wMonth - 1;
1910 xtm.tm_wday = syst->wDayOfWeek;
1911 xtm.tm_mday = syst->wDay;
1912 xtm.tm_hour = syst->wHour;
1913 xtm.tm_min = syst->wMinute;
1914 xtm.tm_sec = syst->wSecond; /* this is UTC */
1915 xtm.tm_isdst = -1;
1916 #ifdef HAVE_TIMEGM
1917 utctime = timegm(&xtm);
1918 DOSFS_UnixTimeToFileTime( utctime, ft,
1919 syst->wMilliseconds * 10000 );
1920 #else
1921 localtim = mktime(&xtm); /* now we've got local time */
1922 local_tm = localtime(&localtim);
1923 utc_tm = gmtime(&localtim);
1924 utctime = mktime(utc_tm);
1925 DOSFS_UnixTimeToFileTime( 2*localtim -utctime, ft,
1926 syst->wMilliseconds * 10000 );
1927 #endif
1928 return TRUE;
1931 BOOL WINAPI DefineDosDeviceA(DWORD flags,LPCSTR devname,LPCSTR targetpath) {
1932 FIXME(dosfs,"(0x%08lx,%s,%s),stub!\n",flags,devname,targetpath);
1933 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1934 return FALSE;