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