2 * DOS file system functions
4 * Copyright 1993 Erik Bos
5 * Copyright 1996 Alexandre Julliard
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 #include <sys/types.h>
28 #ifdef HAVE_SYS_ERRNO_H
29 #include <sys/errno.h>
35 #include <sys/ioctl.h>
43 #include "wine/unicode.h"
44 #include "wine/winbase16.h"
51 #include "wine/server.h"
52 #include "msvcrt/excpt.h"
54 #include "wine/debug.h"
56 WINE_DEFAULT_DEBUG_CHANNEL(dosfs);
57 WINE_DECLARE_DEBUG_CHANNEL(file);
59 /* Define the VFAT ioctl to get both short and long file names */
60 /* FIXME: is it possible to get this to work on other systems? */
62 /* We want the real kernel dirent structure, not the libc one */
67 unsigned short d_reclen;
71 #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, KERNEL_DIRENT [2] )
74 #undef VFAT_IOCTL_READDIR_BOTH /* just in case... */
77 /* Chars we don't want to see in DOS file names */
78 #define INVALID_DOS_CHARS "*?<>|\"+=,;[] \345"
80 static const DOS_DEVICE DOSFS_Devices[] =
81 /* name, device flags (see Int 21/AX=0x4400) */
95 { "SCSIMGR$", 0xc0c0 },
97 { "EMMXXXX0", 0x0000 }
100 #define GET_DRIVE(path) \
101 (((path)[1] == ':') ? FILE_toupper((path)[0]) - 'A' : DOSFS_CurDrive)
103 /* Directory info for DOSFS_ReadDir */
107 #ifdef VFAT_IOCTL_READDIR_BOTH
110 KERNEL_DIRENT dirent[2];
114 /* Info structure for FindFirstFile handle */
127 static WINE_EXCEPTION_FILTER(page_fault)
129 if (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION)
130 return EXCEPTION_EXECUTE_HANDLER;
131 return EXCEPTION_CONTINUE_SEARCH;
135 /***********************************************************************
138 * Return 1 if Unix file 'name' is also a valid MS-DOS name
139 * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
140 * File name can be terminated by '\0', '\\' or '/'.
142 static int DOSFS_ValidDOSName( const char *name, int ignore_case )
144 static const char invalid_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS;
145 const char *p = name;
146 const char *invalid = ignore_case ? (invalid_chars + 26) : invalid_chars;
151 /* Check for "." and ".." */
154 /* All other names beginning with '.' are invalid */
155 return (IS_END_OF_NAME(*p));
157 while (!IS_END_OF_NAME(*p))
159 if (strchr( invalid, *p )) return 0; /* Invalid char */
160 if (*p == '.') break; /* Start of the extension */
161 if (++len > 8) return 0; /* Name too long */
164 if (*p != '.') return 1; /* End of name */
166 if (IS_END_OF_NAME(*p)) return 0; /* Empty extension not allowed */
168 while (!IS_END_OF_NAME(*p))
170 if (strchr( invalid, *p )) return 0; /* Invalid char */
171 if (*p == '.') return 0; /* Second extension not allowed */
172 if (++len > 3) return 0; /* Extension too long */
179 /***********************************************************************
180 * DOSFS_ToDosFCBFormat
182 * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
183 * expanding wild cards and converting to upper-case in the process.
184 * File name can be terminated by '\0', '\\' or '/'.
185 * Return FALSE if the name is not a valid DOS name.
186 * 'buffer' must be at least 12 characters long.
188 BOOL DOSFS_ToDosFCBFormat( LPCSTR name, LPSTR buffer )
190 static const char invalid_chars[] = INVALID_DOS_CHARS;
191 const char *p = name;
194 /* Check for "." and ".." */
198 strcpy( buffer, ". " );
204 return (!*p || (*p == '/') || (*p == '\\'));
207 for (i = 0; i < 8; i++)
224 if (strchr( invalid_chars, *p )) return FALSE;
225 buffer[i] = FILE_toupper(*p);
233 /* Skip all chars after wildcard up to first dot */
234 while (*p && (*p != '/') && (*p != '\\') && (*p != '.')) p++;
238 /* Check if name too long */
239 if (*p && (*p != '/') && (*p != '\\') && (*p != '.')) return FALSE;
241 if (*p == '.') p++; /* Skip dot */
243 for (i = 8; i < 11; i++)
253 return FALSE; /* Second extension not allowed */
261 if (strchr( invalid_chars, *p )) return FALSE;
262 buffer[i] = FILE_toupper(*p);
269 /* at most 3 character of the extension are processed
270 * is something behind this ?
272 while (*p == '*' || *p == ' ') p++; /* skip wildcards and spaces */
273 return IS_END_OF_NAME(*p);
277 /***********************************************************************
278 * DOSFS_ToDosDTAFormat
280 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
281 * converting to upper-case in the process.
282 * File name can be terminated by '\0', '\\' or '/'.
283 * 'buffer' must be at least 13 characters long.
285 static void DOSFS_ToDosDTAFormat( LPCSTR name, LPSTR buffer )
289 memcpy( buffer, name, 8 );
291 while ((p > buffer) && (p[-1] == ' ')) p--;
293 memcpy( p, name + 8, 3 );
295 while (p[-1] == ' ') p--;
296 if (p[-1] == '.') p--;
301 /***********************************************************************
304 * Check a DOS file name against a mask (both in FCB format).
306 static int DOSFS_MatchShort( const char *mask, const char *name )
309 for (i = 11; i > 0; i--, mask++, name++)
310 if ((*mask != '?') && (*mask != *name)) return 0;
315 /***********************************************************************
318 * Check a long file name against a mask.
320 * Tests (done in W95 DOS shell - case insensitive):
321 * *.txt test1.test.txt *
323 * *.t??????.t* test1.ta.tornado.txt *
324 * *tornado* test1.ta.tornado.txt *
325 * t*t test1.ta.tornado.txt *
327 * ?est??? test1.txt -
328 * *test1.txt* test1.txt *
329 * h?l?o*t.dat hellothisisatest.dat *
331 static int DOSFS_MatchLong( const char *mask, const char *name,
334 const char *lastjoker = NULL;
335 const char *next_to_retry = NULL;
337 if (!strcmp( mask, "*.*" )) return 1;
338 while (*name && *mask)
343 while (*mask == '*') mask++; /* Skip consecutive '*' */
345 if (!*mask) return 1; /* end of mask is all '*', so match */
347 /* skip to the next match after the joker(s) */
348 if (case_sensitive) while (*name && (*name != *mask)) name++;
349 else while (*name && (FILE_toupper(*name) != FILE_toupper(*mask))) name++;
352 next_to_retry = name;
354 else if (*mask != '?')
359 if (*mask != *name) mismatch = 1;
363 if (FILE_toupper(*mask) != FILE_toupper(*name)) mismatch = 1;
377 else /* mismatch ! */
379 if (lastjoker) /* we had an '*', so we can try unlimitedly */
383 /* this scan sequence was a mismatch, so restart
384 * 1 char after the first char we checked last time */
386 name = next_to_retry;
389 return 0; /* bad luck */
398 while ((*mask == '.') || (*mask == '*'))
399 mask++; /* Ignore trailing '.' or '*' in mask */
400 return (!*name && !*mask);
404 /***********************************************************************
407 static DOS_DIR *DOSFS_OpenDir( LPCSTR path )
409 DOS_DIR *dir = HeapAlloc( GetProcessHeap(), 0, sizeof(*dir) );
412 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
416 /* Treat empty path as root directory. This simplifies path split into
417 directory and mask in several other places */
418 if (!*path) path = "/";
420 #ifdef VFAT_IOCTL_READDIR_BOTH
422 /* Check if the VFAT ioctl is supported on this directory */
424 if ((dir->fd = open( path, O_RDONLY )) != -1)
426 if (ioctl( dir->fd, VFAT_IOCTL_READDIR_BOTH, (long)dir->dirent ) == -1)
433 /* Set the file pointer back at the start of the directory */
434 lseek( dir->fd, 0, SEEK_SET );
439 #endif /* VFAT_IOCTL_READDIR_BOTH */
441 /* Now use the standard opendir/readdir interface */
443 if (!(dir->dir = opendir( path )))
445 HeapFree( GetProcessHeap(), 0, dir );
452 /***********************************************************************
455 static void DOSFS_CloseDir( DOS_DIR *dir )
457 #ifdef VFAT_IOCTL_READDIR_BOTH
458 if (dir->fd != -1) close( dir->fd );
459 #endif /* VFAT_IOCTL_READDIR_BOTH */
460 if (dir->dir) closedir( dir->dir );
461 HeapFree( GetProcessHeap(), 0, dir );
465 /***********************************************************************
468 static BOOL DOSFS_ReadDir( DOS_DIR *dir, LPCSTR *long_name,
471 struct dirent *dirent;
473 #ifdef VFAT_IOCTL_READDIR_BOTH
476 if (ioctl( dir->fd, VFAT_IOCTL_READDIR_BOTH, (long)dir->dirent ) != -1) {
477 if (!dir->dirent[0].d_reclen) return FALSE;
478 if (!DOSFS_ToDosFCBFormat( dir->dirent[0].d_name, dir->short_name ))
479 dir->short_name[0] = '\0';
480 *short_name = dir->short_name;
481 if (dir->dirent[1].d_name[0]) *long_name = dir->dirent[1].d_name;
482 else *long_name = dir->dirent[0].d_name;
486 #endif /* VFAT_IOCTL_READDIR_BOTH */
488 if (!(dirent = readdir( dir->dir ))) return FALSE;
489 *long_name = dirent->d_name;
495 /***********************************************************************
498 * Transform a Unix file name into a hashed DOS name. If the name is a valid
499 * DOS name, it is converted to upper-case; otherwise it is replaced by a
500 * hashed version that fits in 8.3 format.
501 * File name can be terminated by '\0', '\\' or '/'.
502 * 'buffer' must be at least 13 characters long.
504 static void DOSFS_Hash( LPCSTR name, LPSTR buffer, BOOL dir_format,
507 static const char invalid_chars[] = INVALID_DOS_CHARS "~.";
508 static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
515 if (dir_format) strcpy( buffer, " " );
517 if (DOSFS_ValidDOSName( name, ignore_case ))
519 /* Check for '.' and '..' */
523 if (!dir_format) buffer[1] = buffer[2] = '\0';
524 if (name[1] == '.') buffer[1] = '.';
528 /* Simply copy the name, converting to uppercase */
530 for (dst = buffer; !IS_END_OF_NAME(*name) && (*name != '.'); name++)
531 *dst++ = FILE_toupper(*name);
534 if (dir_format) dst = buffer + 8;
536 for (name++; !IS_END_OF_NAME(*name); name++)
537 *dst++ = FILE_toupper(*name);
539 if (!dir_format) *dst = '\0';
543 /* Compute the hash code of the file name */
544 /* If you know something about hash functions, feel free to */
545 /* insert a better algorithm here... */
548 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
549 hash = (hash<<3) ^ (hash>>5) ^ FILE_tolower(*p) ^ (FILE_tolower(p[1]) << 8);
550 hash = (hash<<3) ^ (hash>>5) ^ FILE_tolower(*p); /* Last character*/
554 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
555 hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
556 hash = (hash << 3) ^ (hash >> 5) ^ *p; /* Last character */
559 /* Find last dot for start of the extension */
560 for (p = name+1, ext = NULL; !IS_END_OF_NAME(*p); p++)
561 if (*p == '.') ext = p;
562 if (ext && IS_END_OF_NAME(ext[1]))
563 ext = NULL; /* Empty extension ignored */
565 /* Copy first 4 chars, replacing invalid chars with '_' */
566 for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
568 if (IS_END_OF_NAME(*p) || (p == ext)) break;
569 *dst++ = strchr( invalid_chars, *p ) ? '_' : FILE_toupper(*p);
571 /* Pad to 5 chars with '~' */
572 while (i-- >= 0) *dst++ = '~';
574 /* Insert hash code converted to 3 ASCII chars */
575 *dst++ = hash_chars[(hash >> 10) & 0x1f];
576 *dst++ = hash_chars[(hash >> 5) & 0x1f];
577 *dst++ = hash_chars[hash & 0x1f];
579 /* Copy the first 3 chars of the extension (if any) */
582 if (!dir_format) *dst++ = '.';
583 for (i = 3, ext++; (i > 0) && !IS_END_OF_NAME(*ext); i--, ext++)
584 *dst++ = strchr( invalid_chars, *ext ) ? '_' : FILE_toupper(*ext);
586 if (!dir_format) *dst = '\0';
590 /***********************************************************************
593 * Find the Unix file name in a given directory that corresponds to
594 * a file name (either in Unix or DOS format).
595 * File name can be terminated by '\0', '\\' or '/'.
596 * Return TRUE if OK, FALSE if no file name matches.
598 * 'long_buf' must be at least 'long_len' characters long. If the long name
599 * turns out to be larger than that, the function returns FALSE.
600 * 'short_buf' must be at least 13 characters long.
602 BOOL DOSFS_FindUnixName( LPCSTR path, LPCSTR name, LPSTR long_buf,
603 INT long_len, LPSTR short_buf, BOOL ignore_case)
606 LPCSTR long_name, short_name;
607 char dos_name[12], tmp_buf[13];
610 const char *p = strchr( name, '/' );
611 int len = p ? (int)(p - name) : strlen(name);
612 if ((p = strchr( name, '\\' ))) len = min( (int)(p - name), len );
613 /* Ignore trailing dots and spaces */
614 while (len > 1 && (name[len-1] == '.' || name[len-1] == ' ')) len--;
615 if (long_len < len + 1) return FALSE;
617 TRACE("%s,%s\n", path, name );
619 if (!DOSFS_ToDosFCBFormat( name, dos_name )) dos_name[0] = '\0';
621 if (!(dir = DOSFS_OpenDir( path )))
623 WARN("(%s,%s): can't open dir: %s\n",
624 path, name, strerror(errno) );
628 while ((ret = DOSFS_ReadDir( dir, &long_name, &short_name )))
630 /* Check against Unix name */
631 if (len == strlen(long_name))
635 if (!strncmp( long_name, name, len )) break;
639 if (!FILE_strncasecmp( long_name, name, len )) break;
644 /* Check against hashed DOS name */
647 DOSFS_Hash( long_name, tmp_buf, TRUE, ignore_case );
648 short_name = tmp_buf;
650 if (!strcmp( dos_name, short_name )) break;
655 if (long_buf) strcpy( long_buf, long_name );
659 DOSFS_ToDosDTAFormat( short_name, short_buf );
661 DOSFS_Hash( long_name, short_buf, FALSE, ignore_case );
663 TRACE("(%s,%s) -> %s (%s)\n",
664 path, name, long_name, short_buf ? short_buf : "***");
667 WARN("'%s' not found in '%s'\n", name, path);
668 DOSFS_CloseDir( dir );
673 /***********************************************************************
676 * Check if a DOS file name represents a DOS device and return the device.
678 const DOS_DEVICE *DOSFS_GetDevice( const char *name )
683 if (!name) return NULL; /* if FILE_DupUnixHandle was used */
684 if (name[0] && (name[1] == ':')) name += 2;
685 if ((p = strrchr( name, '/' ))) name = p + 1;
686 if ((p = strrchr( name, '\\' ))) name = p + 1;
687 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
689 const char *dev = DOSFS_Devices[i].name;
690 if (!FILE_strncasecmp( dev, name, strlen(dev) ))
692 p = name + strlen( dev );
693 if (!*p || (*p == '.') || (*p == ':')) return &DOSFS_Devices[i];
700 /***********************************************************************
701 * DOSFS_GetDeviceByHandle
703 const DOS_DEVICE *DOSFS_GetDeviceByHandle( HFILE hFile )
705 const DOS_DEVICE *ret = NULL;
706 SERVER_START_REQ( get_file_info )
709 if (!wine_server_call( req ) && (reply->type == FILE_TYPE_UNKNOWN))
711 if ((reply->attr >= 0) &&
712 (reply->attr < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0])))
713 ret = &DOSFS_Devices[reply->attr];
721 /**************************************************************************
722 * DOSFS_CreateCommPort
724 static HANDLE DOSFS_CreateCommPort(LPCSTR name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa)
729 TRACE_(file)("%s %lx %lx\n", name, access, attributes);
731 PROFILE_GetWineIniString("serialports",name,"",devname,sizeof devname);
735 TRACE("opening %s as %s\n", devname, name);
737 SERVER_START_REQ( create_serial )
739 req->access = access;
740 req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
741 req->attributes = attributes;
742 req->sharing = FILE_SHARE_READ|FILE_SHARE_WRITE;
743 wine_server_add_data( req, devname, strlen(devname) );
745 wine_server_call_err( req );
751 ERR("Couldn't open device '%s' ! (check permissions)\n",devname);
753 TRACE("return %08X\n", ret );
757 /***********************************************************************
760 * Open a DOS device. This might not map 1:1 into the UNIX device concept.
761 * Returns 0 on failure.
763 HANDLE DOSFS_OpenDevice( const char *name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa )
769 if (name[0] && (name[1] == ':')) name += 2;
770 if ((p = strrchr( name, '/' ))) name = p + 1;
771 if ((p = strrchr( name, '\\' ))) name = p + 1;
772 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
774 const char *dev = DOSFS_Devices[i].name;
775 if (!FILE_strncasecmp( dev, name, strlen(dev) ))
777 p = name + strlen( dev );
778 if (!*p || (*p == '.') || (*p == ':')) {
780 if (!strcmp(DOSFS_Devices[i].name,"NUL"))
781 return FILE_CreateFile( "/dev/null", access,
782 FILE_SHARE_READ|FILE_SHARE_WRITE, sa,
783 OPEN_EXISTING, 0, 0, TRUE, DRIVE_UNKNOWN );
784 if (!strcmp(DOSFS_Devices[i].name,"CON")) {
786 switch (access & (GENERIC_READ|GENERIC_WRITE)) {
788 to_dup = GetStdHandle( STD_INPUT_HANDLE );
791 to_dup = GetStdHandle( STD_OUTPUT_HANDLE );
794 FIXME("can't open CON read/write\n");
797 if (!DuplicateHandle( GetCurrentProcess(), to_dup, GetCurrentProcess(),
799 sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle,
800 DUPLICATE_SAME_ACCESS ))
804 if (!strcmp(DOSFS_Devices[i].name,"SCSIMGR$") ||
805 !strcmp(DOSFS_Devices[i].name,"HPSCAN") ||
806 !strcmp(DOSFS_Devices[i].name,"EMMXXXX0"))
808 return FILE_CreateDevice( i, access, sa );
811 if( (handle=DOSFS_CreateCommPort(DOSFS_Devices[i].name,access,attributes,sa)) )
813 FIXME("device open %s not supported (yet)\n",DOSFS_Devices[i].name);
822 /***********************************************************************
825 * Get the drive specified by a given path name (DOS or Unix format).
827 static int DOSFS_GetPathDrive( const char **name )
830 const char *p = *name;
832 if (*p && (p[1] == ':'))
834 drive = FILE_toupper(*p) - 'A';
837 else if (*p == '/') /* Absolute Unix path? */
839 if ((drive = DRIVE_FindDriveRoot( name )) == -1)
841 MESSAGE("Warning: %s not accessible from a configured DOS drive\n", *name );
842 /* Assume it really was a DOS name */
843 drive = DRIVE_GetCurrentDrive();
846 else drive = DRIVE_GetCurrentDrive();
848 if (!DRIVE_IsValid(drive))
850 SetLastError( ERROR_INVALID_DRIVE );
857 /***********************************************************************
860 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
861 * Unix name / short DOS name pair.
862 * Return FALSE if one of the path components does not exist. The last path
863 * component is only checked if 'check_last' is non-zero.
864 * The buffers pointed to by 'long_buf' and 'short_buf' must be
865 * at least MAX_PATHNAME_LEN long.
867 BOOL DOSFS_GetFullName( LPCSTR name, BOOL check_last, DOS_FULL_NAME *full )
871 char *p_l, *p_s, *root;
873 TRACE("%s (last=%d)\n", name, check_last );
875 if ((!*name) || (*name=='\n'))
876 { /* error code for Win98 */
877 SetLastError(ERROR_BAD_PATHNAME);
881 if ((full->drive = DOSFS_GetPathDrive( &name )) == -1) return FALSE;
882 flags = DRIVE_GetFlags( full->drive );
884 lstrcpynA( full->long_name, DRIVE_GetRoot( full->drive ),
885 sizeof(full->long_name) );
886 if (full->long_name[1]) root = full->long_name + strlen(full->long_name);
887 else root = full->long_name; /* root directory */
889 strcpy( full->short_name, "A:\\" );
890 full->short_name[0] += full->drive;
892 if ((*name == '\\') || (*name == '/')) /* Absolute path */
894 while ((*name == '\\') || (*name == '/')) name++;
896 else /* Relative path */
898 lstrcpynA( root + 1, DRIVE_GetUnixCwd( full->drive ),
899 sizeof(full->long_name) - (root - full->long_name) - 1 );
900 if (root[1]) *root = '/';
901 lstrcpynA( full->short_name + 3, DRIVE_GetDosCwd( full->drive ),
902 sizeof(full->short_name) - 3 );
905 p_l = full->long_name[1] ? full->long_name + strlen(full->long_name)
907 p_s = full->short_name[3] ? full->short_name + strlen(full->short_name)
908 : full->short_name + 2;
911 while (*name && found)
913 /* Check for '.' and '..' */
917 if (IS_END_OF_NAME(name[1]))
920 while ((*name == '\\') || (*name == '/')) name++;
923 else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
926 while ((*name == '\\') || (*name == '/')) name++;
927 while ((p_l > root) && (*p_l != '/')) p_l--;
928 while ((p_s > full->short_name + 2) && (*p_s != '\\')) p_s--;
929 *p_l = *p_s = '\0'; /* Remove trailing separator */
934 /* Make sure buffers are large enough */
936 if ((p_s >= full->short_name + sizeof(full->short_name) - 14) ||
937 (p_l >= full->long_name + sizeof(full->long_name) - 1))
939 SetLastError( ERROR_PATH_NOT_FOUND );
943 /* Get the long and short name matching the file name */
945 if ((found = DOSFS_FindUnixName( full->long_name, name, p_l + 1,
946 sizeof(full->long_name) - (p_l - full->long_name) - 1,
947 p_s + 1, !(flags & DRIVE_CASE_SENSITIVE) )))
953 while (!IS_END_OF_NAME(*name)) name++;
955 else if (!check_last)
959 while (!IS_END_OF_NAME(*name) &&
960 (p_s < full->short_name + sizeof(full->short_name) - 1) &&
961 (p_l < full->long_name + sizeof(full->long_name) - 1))
963 *p_s++ = FILE_tolower(*name);
964 /* If the drive is case-sensitive we want to create new */
965 /* files in lower-case otherwise we can't reopen them */
966 /* under the same short name. */
967 if (flags & DRIVE_CASE_SENSITIVE) *p_l++ = FILE_tolower(*name);
971 /* Ignore trailing dots and spaces */
972 while(p_l[-1] == '.' || p_l[-1] == ' ') {
978 while ((*name == '\\') || (*name == '/')) name++;
985 SetLastError( ERROR_FILE_NOT_FOUND );
988 if (*name) /* Not last */
990 SetLastError( ERROR_PATH_NOT_FOUND );
994 if (!full->long_name[0]) strcpy( full->long_name, "/" );
995 if (!full->short_name[2]) strcpy( full->short_name + 2, "\\" );
996 TRACE("returning %s = %s\n", full->long_name, full->short_name );
1001 /***********************************************************************
1002 * GetShortPathNameA (KERNEL32.@)
1006 * longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1007 * longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0
1009 * more observations ( with NT 3.51 (WinDD) ):
1010 * longpath <= 8.3 -> just copy longpath to shortpath
1012 * a) file does not exist -> return 0, LastError = ERROR_FILE_NOT_FOUND
1013 * b) file does exist -> set the short filename.
1014 * - trailing slashes are reproduced in the short name, even if the
1015 * file is not a directory
1016 * - the absolute/relative path of the short name is reproduced like found
1018 * - longpath and shortpath may have the same address
1019 * Peter Ganten, 1999
1021 DWORD WINAPI GetShortPathNameA( LPCSTR longpath, LPSTR shortpath,
1024 DOS_FULL_NAME full_name;
1026 DWORD sp = 0, lp = 0;
1030 TRACE("%s\n", debugstr_a(longpath));
1033 SetLastError(ERROR_INVALID_PARAMETER);
1037 SetLastError(ERROR_BAD_PATHNAME);
1041 if ( ( tmpshortpath = HeapAlloc ( GetProcessHeap(), 0, MAX_PATHNAME_LEN ) ) == NULL ) {
1042 SetLastError ( ERROR_NOT_ENOUGH_MEMORY );
1046 /* check for drive letter */
1047 if ( longpath[1] == ':' ) {
1048 tmpshortpath[0] = longpath[0];
1049 tmpshortpath[1] = ':';
1053 if ( ( drive = DOSFS_GetPathDrive ( &longpath )) == -1 ) return 0;
1054 flags = DRIVE_GetFlags ( drive );
1056 while ( longpath[lp] ) {
1058 /* check for path delimiters and reproduce them */
1059 if ( longpath[lp] == '\\' || longpath[lp] == '/' ) {
1060 if (!sp || tmpshortpath[sp-1]!= '\\')
1062 /* strip double "\\" */
1063 tmpshortpath[sp] = '\\';
1066 tmpshortpath[sp]=0;/*terminate string*/
1071 tmplen = strcspn ( longpath + lp, "\\/" );
1072 lstrcpynA ( tmpshortpath+sp, longpath + lp, tmplen+1 );
1074 /* Check, if the current element is a valid dos name */
1075 if ( DOSFS_ValidDOSName ( longpath + lp, !(flags & DRIVE_CASE_SENSITIVE) ) ) {
1081 /* Check if the file exists and use the existing file name */
1082 if ( DOSFS_GetFullName ( tmpshortpath, TRUE, &full_name ) ) {
1083 strcpy( tmpshortpath+sp, strrchr ( full_name.short_name, '\\' ) + 1 );
1084 sp += strlen ( tmpshortpath+sp );
1089 TRACE("not found!\n" );
1090 SetLastError ( ERROR_FILE_NOT_FOUND );
1093 tmpshortpath[sp] = 0;
1095 lstrcpynA ( shortpath, tmpshortpath, shortlen );
1096 TRACE("returning %s\n", debugstr_a(shortpath) );
1097 tmplen = strlen ( tmpshortpath );
1098 HeapFree ( GetProcessHeap(), 0, tmpshortpath );
1104 /***********************************************************************
1105 * GetShortPathNameW (KERNEL32.@)
1107 DWORD WINAPI GetShortPathNameW( LPCWSTR longpath, LPWSTR shortpath,
1110 LPSTR longpathA, shortpathA;
1113 longpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, longpath );
1114 shortpathA = HeapAlloc ( GetProcessHeap(), 0, shortlen );
1116 ret = GetShortPathNameA ( longpathA, shortpathA, shortlen );
1117 if (shortlen > 0 && !MultiByteToWideChar( CP_ACP, 0, shortpathA, -1, shortpath, shortlen ))
1118 shortpath[shortlen-1] = 0;
1119 HeapFree( GetProcessHeap(), 0, longpathA );
1120 HeapFree( GetProcessHeap(), 0, shortpathA );
1126 /***********************************************************************
1127 * GetLongPathNameA (KERNEL32.@)
1130 * observed (Win2000):
1131 * shortpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1132 * shortpath="": LastError=ERROR_PATH_NOT_FOUND, ret=0
1134 DWORD WINAPI GetLongPathNameA( LPCSTR shortpath, LPSTR longpath,
1137 DOS_FULL_NAME full_name;
1138 char *p, *r, *ll, *ss;
1141 SetLastError(ERROR_INVALID_PARAMETER);
1144 if (!shortpath[0]) {
1145 SetLastError(ERROR_PATH_NOT_FOUND);
1149 if (!DOSFS_GetFullName( shortpath, TRUE, &full_name )) return 0;
1150 lstrcpynA( longpath, full_name.short_name, longlen );
1152 /* Do some hackery to get the long filename. */
1155 ss=longpath+strlen(longpath);
1156 ll=full_name.long_name+strlen(full_name.long_name);
1158 while (ss>=longpath)
1160 /* FIXME: aren't we more paranoid, than needed? */
1161 while ((ss[0]=='\\') && (ss>=longpath)) ss--;
1163 while ((ss[0]!='\\') && (ss>=longpath)) ss--;
1166 /* FIXME: aren't we more paranoid, than needed? */
1167 while ((ll[0]=='/') && (ll>=full_name.long_name)) ll--;
1168 while ((ll[0]!='/') && (ll>=full_name.long_name)) ll--;
1169 if (ll<full_name.long_name)
1171 ERR("Bad longname! (ss=%s ll=%s)\n This should never happen !\n"
1178 /* FIXME: fix for names like "C:\\" (ie. with more '\'s) */
1182 if ((p-longpath)>0) longlen -= (p-longpath);
1183 lstrcpynA( p, ll , longlen);
1185 /* Now, change all '/' to '\' */
1186 for (r=p; r<(p+longlen); r++ )
1187 if (r[0]=='/') r[0]='\\';
1188 return strlen(longpath) - strlen(p) + longlen;
1192 return strlen(longpath);
1196 /***********************************************************************
1197 * GetLongPathNameW (KERNEL32.@)
1199 DWORD WINAPI GetLongPathNameW( LPCWSTR shortpath, LPWSTR longpath,
1202 DOS_FULL_NAME full_name;
1204 LPSTR shortpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, shortpath );
1206 /* FIXME: is it correct to always return a fully qualified short path? */
1207 if (DOSFS_GetFullName( shortpathA, TRUE, &full_name ))
1209 ret = strlen( full_name.short_name );
1210 if (longlen > 0 && !MultiByteToWideChar( CP_ACP, 0, full_name.long_name, -1,
1211 longpath, longlen ))
1212 longpath[longlen-1] = 0;
1214 HeapFree( GetProcessHeap(), 0, shortpathA );
1219 /***********************************************************************
1220 * DOSFS_DoGetFullPathName
1222 * Implementation of GetFullPathNameA/W.
1224 * bon@elektron 000331:
1225 * A test for GetFullPathName with many pathological cases
1226 * now gives identical output for Wine and OSR2
1228 static DWORD DOSFS_DoGetFullPathName( LPCSTR name, DWORD len, LPSTR result,
1232 DOS_FULL_NAME full_name;
1235 char drivecur[]="c:.";
1237 int namelen,drive=0;
1239 if ((strlen(name) >1)&& (name[1]==':'))
1240 /* drive letter given */
1242 driveletter = name[0];
1244 if ((strlen(name) >2)&& (name[1]==':') &&
1245 ((name[2]=='\\') || (name[2]=='/')))
1246 /* absolute path given */
1248 lstrcpynA(full_name.short_name,name,MAX_PATHNAME_LEN);
1249 drive = (int)FILE_toupper(name[0]) - 'A';
1254 drivecur[0]=driveletter;
1256 strcpy(drivecur,".");
1257 if (!DOSFS_GetFullName( drivecur, FALSE, &full_name ))
1259 FIXME("internal: error getting drive/path\n");
1262 /* find path that drive letter substitutes*/
1263 drive = (int)FILE_toupper(full_name.short_name[0]) -0x41;
1264 root= DRIVE_GetRoot(drive);
1267 FIXME("internal: error getting DOS Drive Root\n");
1270 if (!strcmp(root,"/"))
1272 /* we have just the last / and we need it. */
1273 p= full_name.long_name;
1277 p= full_name.long_name +strlen(root);
1279 /* append long name (= unix name) to drive */
1280 lstrcpynA(full_name.short_name+2,p,MAX_PATHNAME_LEN-3);
1281 /* append name to treat */
1282 namelen= strlen(full_name.short_name);
1285 p += +2; /* skip drive name when appending */
1286 if (namelen +2 + strlen(p) > MAX_PATHNAME_LEN)
1288 FIXME("internal error: buffer too small\n");
1291 full_name.short_name[namelen++] ='\\';
1292 full_name.short_name[namelen] = 0;
1293 lstrcpynA(full_name.short_name +namelen,p,MAX_PATHNAME_LEN-namelen);
1295 /* reverse all slashes */
1296 for (p=full_name.short_name;
1297 p < full_name.short_name+strlen(full_name.short_name);
1303 /* Use memmove, as areas overlap */
1305 while ((p = strstr(full_name.short_name,"\\..\\")))
1307 if (p > full_name.short_name+2)
1310 q = strrchr(full_name.short_name,'\\');
1311 memmove(q+1,p+4,strlen(p+4)+1);
1315 memmove(full_name.short_name+3,p+4,strlen(p+4)+1);
1318 if ((full_name.short_name[2]=='.')&&(full_name.short_name[3]=='.'))
1320 /* This case istn't treated yet : c:..\test */
1321 memmove(full_name.short_name+2,full_name.short_name+4,
1322 strlen(full_name.short_name+4)+1);
1325 while ((p = strstr(full_name.short_name,"\\.\\")))
1328 memmove(p+1,p+3,strlen(p+3)+1);
1330 if (!(DRIVE_GetFlags(drive) & DRIVE_CASE_PRESERVING))
1331 for (p = full_name.short_name; *p; p++) *p = FILE_toupper(*p);
1332 namelen=strlen(full_name.short_name);
1333 if (!strcmp(full_name.short_name+namelen-3,"\\.."))
1335 /* one more strange case: "c:\test\test1\.."
1337 *(full_name.short_name+namelen-3)=0;
1338 q = strrchr(full_name.short_name,'\\');
1341 if (full_name.short_name[namelen-1]=='.')
1342 full_name.short_name[(namelen--)-1] =0;
1344 if (full_name.short_name[namelen-1]=='\\')
1345 full_name.short_name[(namelen--)-1] =0;
1346 TRACE("got %s\n",full_name.short_name);
1348 /* If the lpBuffer buffer is too small, the return value is the
1349 size of the buffer, in characters, required to hold the path
1350 plus the terminating \0 (tested against win95osr2, bon 001118)
1352 ret = strlen(full_name.short_name);
1355 /* don't touch anything when the buffer is not large enough */
1356 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1362 MultiByteToWideChar( CP_ACP, 0, full_name.short_name, -1, (LPWSTR)result, len );
1364 lstrcpynA( result, full_name.short_name, len );
1367 TRACE("returning '%s'\n", full_name.short_name );
1372 /***********************************************************************
1373 * GetFullPathNameA (KERNEL32.@)
1375 * if the path closed with '\', *lastpart is 0
1377 DWORD WINAPI GetFullPathNameA( LPCSTR name, DWORD len, LPSTR buffer,
1380 DWORD ret = DOSFS_DoGetFullPathName( name, len, buffer, FALSE );
1381 if (ret && (ret<=len) && buffer && lastpart)
1383 LPSTR p = buffer + strlen(buffer);
1387 while ((p > buffer + 2) && (*p != '\\')) p--;
1390 else *lastpart = NULL;
1396 /***********************************************************************
1397 * GetFullPathNameW (KERNEL32.@)
1399 DWORD WINAPI GetFullPathNameW( LPCWSTR name, DWORD len, LPWSTR buffer,
1402 LPSTR nameA = HEAP_strdupWtoA( GetProcessHeap(), 0, name );
1403 DWORD ret = DOSFS_DoGetFullPathName( nameA, len, (LPSTR)buffer, TRUE );
1404 HeapFree( GetProcessHeap(), 0, nameA );
1405 if (ret && (ret<=len) && buffer && lastpart)
1407 LPWSTR p = buffer + strlenW(buffer);
1408 if (*p != (WCHAR)'\\')
1410 while ((p > buffer + 2) && (*p != (WCHAR)'\\')) p--;
1413 else *lastpart = NULL;
1419 /***********************************************************************
1420 * wine_get_unix_file_name (KERNEL32.@) Not a Windows API
1422 * Return the full Unix file name for a given path.
1424 BOOL WINAPI wine_get_unix_file_name( LPCSTR dos, LPSTR buffer, DWORD len )
1428 if ((ret = DOSFS_GetFullName( dos, FALSE, &path ))) lstrcpynA( buffer, path.long_name, len );
1433 /***********************************************************************
1436 static int DOSFS_FindNextEx( FIND_FIRST_INFO *info, WIN32_FIND_DATAA *entry )
1438 DWORD attr = info->attr | FA_UNUSED | FA_ARCHIVE | FA_RDONLY | FILE_ATTRIBUTE_SYMLINK;
1439 UINT flags = DRIVE_GetFlags( info->drive );
1440 char *p, buffer[MAX_PATHNAME_LEN];
1441 const char *drive_path;
1443 LPCSTR long_name, short_name;
1444 BY_HANDLE_FILE_INFORMATION fileinfo;
1447 if ((info->attr & ~(FA_UNUSED | FA_ARCHIVE | FA_RDONLY)) == FA_LABEL)
1449 if (info->cur_pos) return 0;
1450 entry->dwFileAttributes = FILE_ATTRIBUTE_LABEL;
1451 RtlSecondsSince1970ToTime( (time_t)0, &entry->ftCreationTime );
1452 RtlSecondsSince1970ToTime( (time_t)0, &entry->ftLastAccessTime );
1453 RtlSecondsSince1970ToTime( (time_t)0, &entry->ftLastWriteTime );
1454 entry->nFileSizeHigh = 0;
1455 entry->nFileSizeLow = 0;
1456 entry->dwReserved0 = 0;
1457 entry->dwReserved1 = 0;
1458 DOSFS_ToDosDTAFormat( DRIVE_GetLabel( info->drive ), entry->cFileName );
1459 strcpy( entry->cAlternateFileName, entry->cFileName );
1461 TRACE("returning %s (%s) as label\n",
1462 entry->cFileName, entry->cAlternateFileName);
1466 drive_path = info->path + strlen(DRIVE_GetRoot( info->drive ));
1467 while ((*drive_path == '/') || (*drive_path == '\\')) drive_path++;
1468 drive_root = !*drive_path;
1470 lstrcpynA( buffer, info->path, sizeof(buffer) - 1 );
1471 strcat( buffer, "/" );
1472 p = buffer + strlen(buffer);
1474 while (DOSFS_ReadDir( info->dir, &long_name, &short_name ))
1478 /* Don't return '.' and '..' in the root of the drive */
1479 if (drive_root && (long_name[0] == '.') &&
1480 (!long_name[1] || ((long_name[1] == '.') && !long_name[2])))
1483 /* Check the long mask */
1485 if (info->long_mask)
1487 if (!DOSFS_MatchLong( info->long_mask, long_name,
1488 flags & DRIVE_CASE_SENSITIVE )) continue;
1491 /* Check the short mask */
1493 if (info->short_mask)
1497 DOSFS_Hash( long_name, dos_name, TRUE,
1498 !(flags & DRIVE_CASE_SENSITIVE) );
1499 short_name = dos_name;
1501 if (!DOSFS_MatchShort( info->short_mask, short_name )) continue;
1504 /* Check the file attributes */
1506 lstrcpynA( p, long_name, sizeof(buffer) - (int)(p - buffer) );
1507 if (!FILE_Stat( buffer, &fileinfo ))
1509 WARN("can't stat %s\n", buffer);
1512 if ((fileinfo.dwFileAttributes & FILE_ATTRIBUTE_SYMLINK) &&
1513 (fileinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
1515 static int show_dir_symlinks = -1;
1516 if (show_dir_symlinks == -1)
1517 show_dir_symlinks = PROFILE_GetWineIniBool("wine", "ShowDirSymlinks", 0);
1518 if (!show_dir_symlinks) continue;
1521 if (fileinfo.dwFileAttributes & ~attr) continue;
1523 /* We now have a matching entry; fill the result and return */
1525 entry->dwFileAttributes = fileinfo.dwFileAttributes;
1526 entry->ftCreationTime = fileinfo.ftCreationTime;
1527 entry->ftLastAccessTime = fileinfo.ftLastAccessTime;
1528 entry->ftLastWriteTime = fileinfo.ftLastWriteTime;
1529 entry->nFileSizeHigh = fileinfo.nFileSizeHigh;
1530 entry->nFileSizeLow = fileinfo.nFileSizeLow;
1533 DOSFS_ToDosDTAFormat( short_name, entry->cAlternateFileName );
1535 DOSFS_Hash( long_name, entry->cAlternateFileName, FALSE,
1536 !(flags & DRIVE_CASE_SENSITIVE) );
1538 lstrcpynA( entry->cFileName, long_name, sizeof(entry->cFileName) );
1539 if (!(flags & DRIVE_CASE_PRESERVING)) _strlwr( entry->cFileName );
1540 TRACE("returning %s (%s) %02lx %ld\n",
1541 entry->cFileName, entry->cAlternateFileName,
1542 entry->dwFileAttributes, entry->nFileSizeLow );
1545 return 0; /* End of directory */
1548 /***********************************************************************
1551 * Find the next matching file. Return the number of entries read to find
1552 * the matching one, or 0 if no more entries.
1553 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
1554 * file name mask. Either or both can be NULL.
1556 * NOTE: This is supposed to be only called by the int21 emulation
1557 * routines. Thus, we should own the Win16Mutex anyway.
1558 * Nevertheless, we explicitly enter it to ensure the static
1559 * directory cache is protected.
1561 int DOSFS_FindNext( const char *path, const char *short_mask,
1562 const char *long_mask, int drive, BYTE attr,
1563 int skip, WIN32_FIND_DATAA *entry )
1565 static FIND_FIRST_INFO info;
1566 LPCSTR short_name, long_name;
1571 /* Check the cached directory */
1572 if (!(info.dir && info.path == path && info.short_mask == short_mask
1573 && info.long_mask == long_mask && info.drive == drive
1574 && info.attr == attr && info.cur_pos <= skip))
1576 /* Not in the cache, open it anew */
1577 if (info.dir) DOSFS_CloseDir( info.dir );
1579 info.path = (LPSTR)path;
1580 info.long_mask = (LPSTR)long_mask;
1581 info.short_mask = (LPSTR)short_mask;
1585 info.dir = DOSFS_OpenDir( info.path );
1588 /* Skip to desired position */
1589 while (info.cur_pos < skip)
1590 if (info.dir && DOSFS_ReadDir( info.dir, &long_name, &short_name ))
1595 if (info.dir && info.cur_pos == skip && DOSFS_FindNextEx( &info, entry ))
1596 count = info.cur_pos - skip;
1602 if (info.dir) DOSFS_CloseDir( info.dir );
1603 memset( &info, '\0', sizeof(info) );
1611 /*************************************************************************
1612 * FindFirstFileExA (KERNEL32.@)
1614 HANDLE WINAPI FindFirstFileExA(
1616 FINDEX_INFO_LEVELS fInfoLevelId,
1617 LPVOID lpFindFileData,
1618 FINDEX_SEARCH_OPS fSearchOp,
1619 LPVOID lpSearchFilter,
1620 DWORD dwAdditionalFlags)
1622 DOS_FULL_NAME full_name;
1624 FIND_FIRST_INFO *info;
1626 if ((fSearchOp != FindExSearchNameMatch) || (dwAdditionalFlags != 0))
1628 FIXME("options not implemented 0x%08x 0x%08lx\n", fSearchOp, dwAdditionalFlags );
1629 return INVALID_HANDLE_VALUE;
1632 switch(fInfoLevelId)
1634 case FindExInfoStandard:
1636 WIN32_FIND_DATAA * data = (WIN32_FIND_DATAA *) lpFindFileData;
1637 data->dwReserved0 = data->dwReserved1 = 0x0;
1638 if (!lpFileName) return 0;
1639 if (!DOSFS_GetFullName( lpFileName, FALSE, &full_name )) break;
1640 if (!(handle = GlobalAlloc(GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO)))) break;
1641 info = (FIND_FIRST_INFO *)GlobalLock( handle );
1642 info->path = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
1643 strcpy( info->path, full_name.long_name );
1644 info->long_mask = strrchr( info->path, '/' );
1645 *(info->long_mask++) = '\0';
1646 info->short_mask = NULL;
1648 if (lpFileName[0] && (lpFileName[1] == ':'))
1649 info->drive = FILE_toupper(*lpFileName) - 'A';
1650 else info->drive = DRIVE_GetCurrentDrive();
1653 info->dir = DOSFS_OpenDir( info->path );
1655 GlobalUnlock( handle );
1656 if (!FindNextFileA( handle, data ))
1658 FindClose( handle );
1659 SetLastError( ERROR_NO_MORE_FILES );
1666 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
1668 return INVALID_HANDLE_VALUE;
1671 /*************************************************************************
1672 * FindFirstFileA (KERNEL32.@)
1674 HANDLE WINAPI FindFirstFileA(
1676 WIN32_FIND_DATAA *lpFindData )
1678 return FindFirstFileExA(lpFileName, FindExInfoStandard, lpFindData,
1679 FindExSearchNameMatch, NULL, 0);
1682 /*************************************************************************
1683 * FindFirstFileExW (KERNEL32.@)
1685 HANDLE WINAPI FindFirstFileExW(
1687 FINDEX_INFO_LEVELS fInfoLevelId,
1688 LPVOID lpFindFileData,
1689 FINDEX_SEARCH_OPS fSearchOp,
1690 LPVOID lpSearchFilter,
1691 DWORD dwAdditionalFlags)
1694 WIN32_FIND_DATAA dataA;
1695 LPVOID _lpFindFileData;
1698 switch(fInfoLevelId)
1700 case FindExInfoStandard:
1702 _lpFindFileData = &dataA;
1706 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
1707 return INVALID_HANDLE_VALUE;
1710 pathA = HEAP_strdupWtoA( GetProcessHeap(), 0, lpFileName );
1711 handle = FindFirstFileExA(pathA, fInfoLevelId, _lpFindFileData, fSearchOp, lpSearchFilter, dwAdditionalFlags);
1712 HeapFree( GetProcessHeap(), 0, pathA );
1713 if (handle == INVALID_HANDLE_VALUE) return handle;
1715 switch(fInfoLevelId)
1717 case FindExInfoStandard:
1719 WIN32_FIND_DATAW *dataW = (WIN32_FIND_DATAW*) lpFindFileData;
1720 dataW->dwFileAttributes = dataA.dwFileAttributes;
1721 dataW->ftCreationTime = dataA.ftCreationTime;
1722 dataW->ftLastAccessTime = dataA.ftLastAccessTime;
1723 dataW->ftLastWriteTime = dataA.ftLastWriteTime;
1724 dataW->nFileSizeHigh = dataA.nFileSizeHigh;
1725 dataW->nFileSizeLow = dataA.nFileSizeLow;
1726 MultiByteToWideChar( CP_ACP, 0, dataA.cFileName, -1,
1727 dataW->cFileName, sizeof(dataW->cFileName)/sizeof(WCHAR) );
1728 MultiByteToWideChar( CP_ACP, 0, dataA.cAlternateFileName, -1,
1729 dataW->cAlternateFileName,
1730 sizeof(dataW->cAlternateFileName)/sizeof(WCHAR) );
1734 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
1735 return INVALID_HANDLE_VALUE;
1740 /*************************************************************************
1741 * FindFirstFileW (KERNEL32.@)
1743 HANDLE WINAPI FindFirstFileW( LPCWSTR lpFileName, WIN32_FIND_DATAW *lpFindData )
1745 return FindFirstFileExW(lpFileName, FindExInfoStandard, lpFindData,
1746 FindExSearchNameMatch, NULL, 0);
1749 /*************************************************************************
1750 * FindNextFileA (KERNEL32.@)
1752 BOOL WINAPI FindNextFileA( HANDLE handle, WIN32_FIND_DATAA *data )
1754 FIND_FIRST_INFO *info;
1756 if ((handle == INVALID_HANDLE_VALUE) ||
1757 !(info = (FIND_FIRST_INFO *)GlobalLock( handle )))
1759 SetLastError( ERROR_INVALID_HANDLE );
1762 GlobalUnlock( handle );
1763 if (!info->path || !info->dir)
1765 SetLastError( ERROR_NO_MORE_FILES );
1768 if (!DOSFS_FindNextEx( info, data ))
1770 DOSFS_CloseDir( info->dir ); info->dir = NULL;
1771 HeapFree( GetProcessHeap(), 0, info->path );
1772 info->path = info->long_mask = NULL;
1773 SetLastError( ERROR_NO_MORE_FILES );
1780 /*************************************************************************
1781 * FindNextFileW (KERNEL32.@)
1783 BOOL WINAPI FindNextFileW( HANDLE handle, WIN32_FIND_DATAW *data )
1785 WIN32_FIND_DATAA dataA;
1786 if (!FindNextFileA( handle, &dataA )) return FALSE;
1787 data->dwFileAttributes = dataA.dwFileAttributes;
1788 data->ftCreationTime = dataA.ftCreationTime;
1789 data->ftLastAccessTime = dataA.ftLastAccessTime;
1790 data->ftLastWriteTime = dataA.ftLastWriteTime;
1791 data->nFileSizeHigh = dataA.nFileSizeHigh;
1792 data->nFileSizeLow = dataA.nFileSizeLow;
1793 MultiByteToWideChar( CP_ACP, 0, dataA.cFileName, -1,
1794 data->cFileName, sizeof(data->cFileName)/sizeof(WCHAR) );
1795 MultiByteToWideChar( CP_ACP, 0, dataA.cAlternateFileName, -1,
1796 data->cAlternateFileName,
1797 sizeof(data->cAlternateFileName)/sizeof(WCHAR) );
1801 /*************************************************************************
1802 * FindClose (KERNEL32.@)
1804 BOOL WINAPI FindClose( HANDLE handle )
1806 FIND_FIRST_INFO *info;
1808 if ((handle == INVALID_HANDLE_VALUE) ||
1809 !(info = (FIND_FIRST_INFO *)GlobalLock( handle )))
1811 SetLastError( ERROR_INVALID_HANDLE );
1816 if (info->dir) DOSFS_CloseDir( info->dir );
1817 if (info->path) HeapFree( GetProcessHeap(), 0, info->path );
1819 __EXCEPT(page_fault)
1821 WARN("Illegal handle %x\n", handle);
1822 SetLastError( ERROR_INVALID_HANDLE );
1826 GlobalUnlock( handle );
1827 GlobalFree( handle );
1831 /***********************************************************************
1832 * DOSFS_UnixTimeToFileTime
1834 * Convert a Unix time to FILETIME format.
1835 * The FILETIME structure is a 64-bit value representing the number of
1836 * 100-nanosecond intervals since January 1, 1601, 0:00.
1837 * 'remainder' is the nonnegative number of 100-ns intervals
1838 * corresponding to the time fraction smaller than 1 second that
1839 * couldn't be stored in the time_t value.
1841 void DOSFS_UnixTimeToFileTime( time_t unix_time, FILETIME *filetime,
1847 The time difference between 1 January 1601, 00:00:00 and
1848 1 January 1970, 00:00:00 is 369 years, plus the leap years
1849 from 1604 to 1968, excluding 1700, 1800, 1900.
1850 This makes (1968 - 1600) / 4 - 3 = 89 leap days, and a total
1853 Any day in that period had 24 * 60 * 60 = 86400 seconds.
1855 The time difference is 134774 * 86400 * 10000000, which can be written
1857 27111902 * 2^32 + 3577643008
1858 413 * 2^48 + 45534 * 2^32 + 54590 * 2^16 + 32768
1860 If you find that these constants are buggy, please change them in all
1861 instances in both conversion functions.
1864 There are two versions, one of them uses long long variables and
1865 is presumably faster but not ISO C. The other one uses standard C
1866 data types and operations but relies on the assumption that negative
1867 numbers are stored as 2's complement (-1 is 0xffff....). If this
1868 assumption is violated, dates before 1970 will not convert correctly.
1869 This should however work on any reasonable architecture where WINE
1874 Take care not to remove the casts. I have tested these functions
1875 (in both versions) for a lot of numbers. I would be interested in
1876 results on other compilers than GCC.
1878 The operations have been designed to account for the possibility
1879 of 64-bit time_t in future UNICES. Even the versions without
1880 internal long long numbers will work if time_t only is 64 bit.
1881 A 32-bit shift, which was necessary for that operation, turned out
1882 not to work correctly in GCC, besides giving the warning. So I
1883 used a double 16-bit shift instead. Numbers are in the ISO version
1884 represented by three limbs, the most significant with 32 bit, the
1885 other two with 16 bit each.
1887 As the modulo-operator % is not well-defined for negative numbers,
1888 negative divisors have been avoided in DOSFS_FileTimeToUnixTime.
1890 There might be quicker ways to do this in C. Certainly so in
1893 Claus Fischer, fischer@iue.tuwien.ac.at
1896 #if SIZEOF_LONG_LONG >= 8
1897 # define USE_LONG_LONG 1
1899 # define USE_LONG_LONG 0
1902 #if USE_LONG_LONG /* gcc supports long long type */
1904 long long int t = unix_time;
1906 t += 116444736000000000LL;
1908 filetime->dwLowDateTime = (UINT)t;
1909 filetime->dwHighDateTime = (UINT)(t >> 32);
1911 #else /* ISO version */
1913 UINT a0; /* 16 bit, low bits */
1914 UINT a1; /* 16 bit, medium bits */
1915 UINT a2; /* 32 bit, high bits */
1917 /* Copy the unix time to a2/a1/a0 */
1918 a0 = unix_time & 0xffff;
1919 a1 = (unix_time >> 16) & 0xffff;
1920 /* This is obsolete if unix_time is only 32 bits, but it does not hurt.
1921 Do not replace this by >> 32, it gives a compiler warning and it does
1923 a2 = (unix_time >= 0 ? (unix_time >> 16) >> 16 :
1924 ~((~unix_time >> 16) >> 16));
1926 /* Multiply a by 10000000 (a = a2/a1/a0)
1927 Split the factor into 10000 * 1000 which are both less than 0xffff. */
1929 a1 = a1 * 10000 + (a0 >> 16);
1930 a2 = a2 * 10000 + (a1 >> 16);
1935 a1 = a1 * 1000 + (a0 >> 16);
1936 a2 = a2 * 1000 + (a1 >> 16);
1940 /* Add the time difference and the remainder */
1941 a0 += 32768 + (remainder & 0xffff);
1942 a1 += 54590 + (remainder >> 16 ) + (a0 >> 16);
1943 a2 += 27111902 + (a1 >> 16);
1948 filetime->dwLowDateTime = (a1 << 16) + a0;
1949 filetime->dwHighDateTime = a2;
1954 /***********************************************************************
1955 * DOSFS_FileTimeToUnixTime
1957 * Convert a FILETIME format to Unix time.
1958 * If not NULL, 'remainder' contains the fractional part of the filetime,
1959 * in the range of [0..9999999] (even if time_t is negative).
1961 time_t DOSFS_FileTimeToUnixTime( const FILETIME *filetime, DWORD *remainder )
1963 /* Read the comment in the function DOSFS_UnixTimeToFileTime. */
1966 long long int t = filetime->dwHighDateTime;
1968 t += (UINT)filetime->dwLowDateTime;
1969 t -= 116444736000000000LL;
1972 if (remainder) *remainder = 9999999 - (-t - 1) % 10000000;
1973 return -1 - ((-t - 1) / 10000000);
1977 if (remainder) *remainder = t % 10000000;
1978 return t / 10000000;
1981 #else /* ISO version */
1983 UINT a0; /* 16 bit, low bits */
1984 UINT a1; /* 16 bit, medium bits */
1985 UINT a2; /* 32 bit, high bits */
1986 UINT r; /* remainder of division */
1987 unsigned int carry; /* carry bit for subtraction */
1988 int negative; /* whether a represents a negative value */
1990 /* Copy the time values to a2/a1/a0 */
1991 a2 = (UINT)filetime->dwHighDateTime;
1992 a1 = ((UINT)filetime->dwLowDateTime ) >> 16;
1993 a0 = ((UINT)filetime->dwLowDateTime ) & 0xffff;
1995 /* Subtract the time difference */
1996 if (a0 >= 32768 ) a0 -= 32768 , carry = 0;
1997 else a0 += (1 << 16) - 32768 , carry = 1;
1999 if (a1 >= 54590 + carry) a1 -= 54590 + carry, carry = 0;
2000 else a1 += (1 << 16) - 54590 - carry, carry = 1;
2002 a2 -= 27111902 + carry;
2004 /* If a is negative, replace a by (-1-a) */
2005 negative = (a2 >= ((UINT)1) << 31);
2008 /* Set a to -a - 1 (a is a2/a1/a0) */
2014 /* Divide a by 10000000 (a = a2/a1/a0), put the rest into r.
2015 Split the divisor into 10000 * 1000 which are both less than 0xffff. */
2016 a1 += (a2 % 10000) << 16;
2018 a0 += (a1 % 10000) << 16;
2023 a1 += (a2 % 1000) << 16;
2025 a0 += (a1 % 1000) << 16;
2027 r += (a0 % 1000) * 10000;
2030 /* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
2033 /* Set a to -a - 1 (a is a2/a1/a0) */
2041 if (remainder) *remainder = r;
2043 /* Do not replace this by << 32, it gives a compiler warning and it does
2045 return ((((time_t)a2) << 16) << 16) + (a1 << 16) + a0;
2050 /***********************************************************************
2051 * MulDiv (KERNEL32.@)
2053 * Result of multiplication and division
2054 * -1: Overflow occurred or Divisor was 0
2061 #if SIZEOF_LONG_LONG >= 8
2064 if (!nDivisor) return -1;
2066 /* We want to deal with a positive divisor to simplify the logic. */
2069 nMultiplicand = - nMultiplicand;
2070 nDivisor = -nDivisor;
2073 /* If the result is positive, we "add" to round. else, we subtract to round. */
2074 if ( ( (nMultiplicand < 0) && (nMultiplier < 0) ) ||
2075 ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
2076 ret = (((long long)nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
2078 ret = (((long long)nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
2080 if ((ret > 2147483647) || (ret < -2147483647)) return -1;
2083 if (!nDivisor) return -1;
2085 /* We want to deal with a positive divisor to simplify the logic. */
2088 nMultiplicand = - nMultiplicand;
2089 nDivisor = -nDivisor;
2092 /* If the result is positive, we "add" to round. else, we subtract to round. */
2093 if ( ( (nMultiplicand < 0) && (nMultiplier < 0) ) ||
2094 ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
2095 return ((nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
2097 return ((nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
2103 /***********************************************************************
2104 * DosDateTimeToFileTime (KERNEL32.@)
2106 BOOL WINAPI DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft)
2110 newtm.tm_sec = (fattime & 0x1f) * 2;
2111 newtm.tm_min = (fattime >> 5) & 0x3f;
2112 newtm.tm_hour = (fattime >> 11);
2113 newtm.tm_mday = (fatdate & 0x1f);
2114 newtm.tm_mon = ((fatdate >> 5) & 0x0f) - 1;
2115 newtm.tm_year = (fatdate >> 9) + 80;
2116 RtlSecondsSince1970ToTime( mktime( &newtm ), ft );
2121 /***********************************************************************
2122 * FileTimeToDosDateTime (KERNEL32.@)
2124 BOOL WINAPI FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate,
2127 time_t unixtime = DOSFS_FileTimeToUnixTime( ft, NULL );
2128 struct tm *tm = localtime( &unixtime );
2130 *fattime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
2132 *fatdate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
2138 /***********************************************************************
2139 * LocalFileTimeToFileTime (KERNEL32.@)
2141 BOOL WINAPI LocalFileTimeToFileTime( const FILETIME *localft,
2147 /* convert from local to UTC. Perhaps not correct. FIXME */
2148 time_t unixtime = DOSFS_FileTimeToUnixTime( localft, &remainder );
2149 xtm = gmtime( &unixtime );
2150 DOSFS_UnixTimeToFileTime( mktime(xtm), utcft, remainder );
2155 /***********************************************************************
2156 * FileTimeToLocalFileTime (KERNEL32.@)
2158 BOOL WINAPI FileTimeToLocalFileTime( const FILETIME *utcft,
2159 LPFILETIME localft )
2162 /* convert from UTC to local. Perhaps not correct. FIXME */
2163 time_t unixtime = DOSFS_FileTimeToUnixTime( utcft, &remainder );
2165 struct tm *xtm = localtime( &unixtime );
2168 localtime = timegm(xtm);
2169 DOSFS_UnixTimeToFileTime( localtime, localft, remainder );
2172 struct tm *xtm,*gtm;
2175 xtm = localtime( &unixtime );
2176 gtm = gmtime( &unixtime );
2177 time1 = mktime(xtm);
2178 time2 = mktime(gtm);
2179 DOSFS_UnixTimeToFileTime( 2*time1-time2, localft, remainder );
2185 /***********************************************************************
2186 * FileTimeToSystemTime (KERNEL32.@)
2188 BOOL WINAPI FileTimeToSystemTime( const FILETIME *ft, LPSYSTEMTIME syst )
2192 time_t xtime = DOSFS_FileTimeToUnixTime( ft, &remainder );
2193 xtm = gmtime(&xtime);
2194 syst->wYear = xtm->tm_year+1900;
2195 syst->wMonth = xtm->tm_mon + 1;
2196 syst->wDayOfWeek = xtm->tm_wday;
2197 syst->wDay = xtm->tm_mday;
2198 syst->wHour = xtm->tm_hour;
2199 syst->wMinute = xtm->tm_min;
2200 syst->wSecond = xtm->tm_sec;
2201 syst->wMilliseconds = remainder / 10000;
2205 /***********************************************************************
2206 * QueryDosDeviceA (KERNEL32.@)
2208 * returns array of strings terminated by \0, terminated by \0
2210 DWORD WINAPI QueryDosDeviceA(LPCSTR devname,LPSTR target,DWORD bufsize)
2215 TRACE("(%s,...)\n", devname ? devname : "<null>");
2217 /* return known MSDOS devices */
2218 static const char devices[24] = "CON\0COM1\0COM2\0LPT1\0NUL\0\0";
2219 memcpy( target, devices, min(bufsize,sizeof(devices)) );
2220 return min(bufsize,sizeof(devices));
2222 /* In theory all that are possible and have been defined.
2223 * Now just those below, since mirc uses it to check for special files.
2225 * (It is more complex, and supports netmounted stuff, and \\.\ stuff,
2226 * but currently we just ignore that.)
2228 #define CHECK(x) (strstr(devname,#x)==devname)
2229 if (CHECK(con) || CHECK(com) || CHECK(lpt) || CHECK(nul)) {
2230 strcpy(buffer,"\\DEV\\");
2231 strcat(buffer,devname);
2232 if ((s=strchr(buffer,':'))) *s='\0';
2233 lstrcpynA(target,buffer,bufsize);
2234 return strlen(buffer)+1;
2236 if (strchr(devname,':') || devname[0]=='\\') {
2237 /* This might be a DOS device we do not handle yet ... */
2238 FIXME("(%s) not detected as DOS device!\n",devname);
2240 SetLastError(ERROR_DEV_NOT_EXIST);
2247 /***********************************************************************
2248 * QueryDosDeviceW (KERNEL32.@)
2250 * returns array of strings terminated by \0, terminated by \0
2252 DWORD WINAPI QueryDosDeviceW(LPCWSTR devname,LPWSTR target,DWORD bufsize)
2254 LPSTR devnameA = devname?HEAP_strdupWtoA(GetProcessHeap(),0,devname):NULL;
2255 LPSTR targetA = (LPSTR)HeapAlloc(GetProcessHeap(),0,bufsize);
2256 DWORD ret = QueryDosDeviceA(devnameA,targetA,bufsize);
2258 ret = MultiByteToWideChar( CP_ACP, 0, targetA, ret, target, bufsize );
2259 if (devnameA) HeapFree(GetProcessHeap(),0,devnameA);
2260 if (targetA) HeapFree(GetProcessHeap(),0,targetA);
2265 /***********************************************************************
2266 * SystemTimeToFileTime (KERNEL32.@)
2268 BOOL WINAPI SystemTimeToFileTime( const SYSTEMTIME *syst, LPFILETIME ft )
2274 struct tm xtm,*local_tm,*utc_tm;
2275 time_t localtim,utctime;
2278 xtm.tm_year = syst->wYear-1900;
2279 xtm.tm_mon = syst->wMonth - 1;
2280 xtm.tm_wday = syst->wDayOfWeek;
2281 xtm.tm_mday = syst->wDay;
2282 xtm.tm_hour = syst->wHour;
2283 xtm.tm_min = syst->wMinute;
2284 xtm.tm_sec = syst->wSecond; /* this is UTC */
2287 utctime = timegm(&xtm);
2288 DOSFS_UnixTimeToFileTime( utctime, ft,
2289 syst->wMilliseconds * 10000 );
2291 localtim = mktime(&xtm); /* now we've got local time */
2292 local_tm = localtime(&localtim);
2293 utc_tm = gmtime(&localtim);
2294 utctime = mktime(utc_tm);
2295 DOSFS_UnixTimeToFileTime( 2*localtim -utctime, ft,
2296 syst->wMilliseconds * 10000 );
2301 /***********************************************************************
2302 * DefineDosDeviceA (KERNEL32.@)
2304 BOOL WINAPI DefineDosDeviceA(DWORD flags,LPCSTR devname,LPCSTR targetpath) {
2305 FIXME("(0x%08lx,%s,%s),stub!\n",flags,devname,targetpath);
2306 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2311 --- 16 bit functions ---
2314 /*************************************************************************
2315 * FindFirstFile (KERNEL.413)
2317 HANDLE16 WINAPI FindFirstFile16( LPCSTR path, WIN32_FIND_DATAA *data )
2319 DOS_FULL_NAME full_name;
2321 FIND_FIRST_INFO *info;
2323 data->dwReserved0 = data->dwReserved1 = 0x0;
2324 if (!path) return 0;
2325 if (!DOSFS_GetFullName( path, FALSE, &full_name ))
2326 return INVALID_HANDLE_VALUE16;
2327 if (!(handle = GlobalAlloc16( GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO) )))
2328 return INVALID_HANDLE_VALUE16;
2329 info = (FIND_FIRST_INFO *)GlobalLock16( handle );
2330 info->path = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
2331 strcpy( info->path, full_name.long_name );
2332 info->long_mask = strrchr( info->path, '/' );
2333 if (info->long_mask )
2334 *(info->long_mask++) = '\0';
2335 info->short_mask = NULL;
2337 if (path[0] && (path[1] == ':')) info->drive = FILE_toupper(*path) - 'A';
2338 else info->drive = DRIVE_GetCurrentDrive();
2341 info->dir = DOSFS_OpenDir( info->path );
2343 GlobalUnlock16( handle );
2344 if (!FindNextFile16( handle, data ))
2346 FindClose16( handle );
2347 SetLastError( ERROR_NO_MORE_FILES );
2348 return INVALID_HANDLE_VALUE16;
2353 /*************************************************************************
2354 * FindNextFile (KERNEL.414)
2356 BOOL16 WINAPI FindNextFile16( HANDLE16 handle, WIN32_FIND_DATAA *data )
2358 FIND_FIRST_INFO *info;
2360 if ((handle == INVALID_HANDLE_VALUE16) ||
2361 !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
2363 SetLastError( ERROR_INVALID_HANDLE );
2366 GlobalUnlock16( handle );
2367 if (!info->path || !info->dir)
2369 SetLastError( ERROR_NO_MORE_FILES );
2372 if (!DOSFS_FindNextEx( info, data ))
2374 DOSFS_CloseDir( info->dir ); info->dir = NULL;
2375 HeapFree( GetProcessHeap(), 0, info->path );
2376 info->path = info->long_mask = NULL;
2377 SetLastError( ERROR_NO_MORE_FILES );
2383 /*************************************************************************
2384 * FindClose (KERNEL.415)
2386 BOOL16 WINAPI FindClose16( HANDLE16 handle )
2388 FIND_FIRST_INFO *info;
2390 if ((handle == INVALID_HANDLE_VALUE16) ||
2391 !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
2393 SetLastError( ERROR_INVALID_HANDLE );
2396 if (info->dir) DOSFS_CloseDir( info->dir );
2397 if (info->path) HeapFree( GetProcessHeap(), 0, info->path );
2398 GlobalUnlock16( handle );
2399 GlobalFree16( handle );