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
23 #include "wine/port.h"
25 #include <sys/types.h>
29 #ifdef HAVE_SYS_ERRNO_H
30 #include <sys/errno.h>
37 #ifdef HAVE_SYS_IOCTL_H
38 #include <sys/ioctl.h>
40 #ifdef HAVE_LINUX_IOCTL_H
41 #include <linux/ioctl.h>
48 #define NONAMELESSUNION
49 #define NONAMELESSSTRUCT
56 #include "wine/unicode.h"
57 #include "wine/winbase16.h"
61 #include "wine/server.h"
62 #include "wine/exception.h"
67 #include "wine/debug.h"
69 WINE_DEFAULT_DEBUG_CHANNEL(dosfs
);
70 WINE_DECLARE_DEBUG_CHANNEL(file
);
72 /* Define the VFAT ioctl to get both short and long file names */
73 /* FIXME: is it possible to get this to work on other systems? */
75 /* We want the real kernel dirent structure, not the libc one */
80 unsigned short d_reclen
;
84 #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, KERNEL_DIRENT [2] )
86 /* To avoid blocking on non-directories in DOSFS_OpenDir_VFAT*/
88 # define O_DIRECTORY 0200000 /* must be directory */
92 #undef VFAT_IOCTL_READDIR_BOTH /* just in case... */
95 /* Chars we don't want to see in DOS file names */
96 #define INVALID_DOS_CHARS "*?<>|\"+=,;[] \345"
98 /* DOS device descriptor */
104 static const DOS_DEVICE DOSFS_Devices
[] =
105 /* name, device flags (see Int 21/AX=0x4400) */
111 { {'L','P','T','1',0} },
112 { {'L','P','T','2',0} },
113 { {'L','P','T','3',0} },
114 { {'L','P','T','4',0} },
115 { {'C','O','M','1',0} },
116 { {'C','O','M','2',0} },
117 { {'C','O','M','3',0} },
118 { {'C','O','M','4',0} }
121 static const WCHAR devW
[] = {'\\','D','e','v','i','c','e','\\',0};
122 static const WCHAR dosW
[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\',0};
124 static const WCHAR auxW
[] = {'A','U','X',0};
125 static const WCHAR comW
[] = {'C','O','M',0};
126 static const WCHAR lptW
[] = {'L','P','T',0};
127 static const WCHAR nulW
[] = {'N','U','L',0};
129 static const WCHAR nullW
[] = {'N','u','l','l',0};
130 static const WCHAR parW
[] = {'P','a','r','a','l','l','e','l',0};
131 static const WCHAR serW
[] = {'S','e','r','i','a','l',0};
132 static const WCHAR oneW
[] = {'1',0};
134 /* at some point we may want to allow Winelib apps to set this */
135 static const BOOL is_case_sensitive
= FALSE
;
138 * Directory info for DOSFS_ReadDir
139 * contains the names of *all* the files in the directory
149 /* return non-zero if c is the end of a directory name */
150 static inline int is_end_of_name(WCHAR c
)
152 return !c
|| (c
== '/') || (c
== '\\');
155 /***********************************************************************
158 * Return 1 if Unix file 'name' is also a valid MS-DOS name
159 * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
160 * File name can be terminated by '\0', '\\' or '/'.
162 static int DOSFS_ValidDOSName( LPCWSTR name
)
164 static const char invalid_chars
[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS
;
165 const WCHAR
*p
= name
;
166 const char *invalid
= !is_case_sensitive
? (invalid_chars
+ 26) : invalid_chars
;
171 /* Check for "." and ".." */
174 /* All other names beginning with '.' are invalid */
175 return (is_end_of_name(*p
));
177 while (!is_end_of_name(*p
))
179 if (*p
< 256 && strchr( invalid
, (char)*p
)) return 0; /* Invalid char */
180 if (*p
== '.') break; /* Start of the extension */
181 if (++len
> 8) return 0; /* Name too long */
184 if (*p
!= '.') return 1; /* End of name */
186 if (is_end_of_name(*p
)) return 0; /* Empty extension not allowed */
188 while (!is_end_of_name(*p
))
190 if (*p
< 256 && strchr( invalid
, (char)*p
)) return 0; /* Invalid char */
191 if (*p
== '.') return 0; /* Second extension not allowed */
192 if (++len
> 3) return 0; /* Extension too long */
199 /***********************************************************************
200 * DOSFS_ToDosFCBFormat
202 * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
203 * expanding wild cards and converting to upper-case in the process.
204 * File name can be terminated by '\0', '\\' or '/'.
205 * Return FALSE if the name is not a valid DOS name.
206 * 'buffer' must be at least 12 characters long.
208 static BOOL
DOSFS_ToDosFCBFormat( LPCWSTR name
, LPWSTR buffer
)
210 static const char invalid_chars
[] = INVALID_DOS_CHARS
;
214 /* Check for "." and ".." */
219 for(i
= 1; i
< 11; i
++) buffer
[i
] = ' ';
226 return (!*p
|| (*p
== '/') || (*p
== '\\'));
229 for (i
= 0; i
< 8; i
++)
246 if (*p
< 256 && strchr( invalid_chars
, (char)*p
)) return FALSE
;
247 buffer
[i
] = toupperW(*p
);
255 /* Skip all chars after wildcard up to first dot */
256 while (*p
&& (*p
!= '/') && (*p
!= '\\') && (*p
!= '.')) p
++;
260 /* Check if name too long */
261 if (*p
&& (*p
!= '/') && (*p
!= '\\') && (*p
!= '.')) return FALSE
;
263 if (*p
== '.') p
++; /* Skip dot */
265 for (i
= 8; i
< 11; i
++)
275 return FALSE
; /* Second extension not allowed */
283 if (*p
< 256 && strchr( invalid_chars
, (char)*p
)) return FALSE
;
284 buffer
[i
] = toupperW(*p
);
291 /* at most 3 character of the extension are processed
292 * is something behind this ?
294 while (*p
== '*' || *p
== ' ') p
++; /* skip wildcards and spaces */
295 return is_end_of_name(*p
);
299 /***********************************************************************
300 * DOSFS_ToDosDTAFormat
302 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
303 * converting to upper-case in the process.
304 * File name can be terminated by '\0', '\\' or '/'.
305 * 'buffer' must be at least 13 characters long.
307 static void DOSFS_ToDosDTAFormat( LPCWSTR name
, LPWSTR buffer
)
311 memcpy( buffer
, name
, 8 * sizeof(WCHAR
) );
313 while ((p
> buffer
) && (p
[-1] == ' ')) p
--;
315 memcpy( p
, name
+ 8, 3 * sizeof(WCHAR
) );
317 while (p
[-1] == ' ') p
--;
318 if (p
[-1] == '.') p
--;
323 /***********************************************************************
326 * Used to construct an array of filenames in DOSFS_OpenDir
328 static BOOL
DOSFS_AddDirEntry(DOS_DIR
**dir
, LPCWSTR name
, LPCWSTR dosname
)
330 int extra1
= strlenW(name
) + 1;
331 int extra2
= strlenW(dosname
) + 1;
333 /* if we need more, at minimum double the size */
334 if( (extra1
+ extra2
+ (*dir
)->used
) > (*dir
)->size
)
336 int more
= (*dir
)->size
;
339 if(more
<(extra1
+extra2
))
340 more
= extra1
+extra2
;
342 t
= HeapReAlloc(GetProcessHeap(), 0, *dir
, sizeof(**dir
) +
343 ((*dir
)->size
+ more
)*sizeof(WCHAR
) );
346 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
347 ERR("Out of memory caching directory structure %d %d %d\n",
348 (*dir
)->size
, more
, (*dir
)->used
);
352 (*dir
)->size
+= more
;
355 /* at this point, the dir structure is big enough to hold these names */
356 strcpyW(&(*dir
)->names
[(*dir
)->used
], name
);
357 (*dir
)->used
+= extra1
;
358 strcpyW(&(*dir
)->names
[(*dir
)->used
], dosname
);
359 (*dir
)->used
+= extra2
;
365 /***********************************************************************
368 static BOOL
DOSFS_OpenDir_VFAT(DOS_DIR
**dir
, const char *unix_path
)
370 #ifdef VFAT_IOCTL_READDIR_BOTH
372 int fd
= open( unix_path
, O_RDONLY
|O_DIRECTORY
);
375 /* Check if the VFAT ioctl is supported on this directory */
382 WCHAR long_name
[MAX_PATH
];
383 WCHAR short_name
[12];
385 r
= (ioctl( fd
, VFAT_IOCTL_READDIR_BOTH
, (long)de
) != -1);
390 MultiByteToWideChar(CP_UNIXCP
, 0, de
[0].d_name
, -1, long_name
, MAX_PATH
);
391 if (!DOSFS_ToDosFCBFormat( long_name
, short_name
))
392 short_name
[0] = '\0';
394 MultiByteToWideChar(CP_UNIXCP
, 0, de
[1].d_name
, -1, long_name
, MAX_PATH
);
396 MultiByteToWideChar(CP_UNIXCP
, 0, de
[0].d_name
, -1, long_name
, MAX_PATH
);
397 r
= DOSFS_AddDirEntry(dir
, long_name
, short_name
);
403 static const WCHAR empty_strW
[] = { 0 };
404 DOSFS_AddDirEntry(dir
, empty_strW
, empty_strW
);
410 #endif /* VFAT_IOCTL_READDIR_BOTH */
414 /***********************************************************************
415 * DOSFS_OpenDir_Normal
417 * Now use the standard opendir/readdir interface
419 static BOOL
DOSFS_OpenDir_Normal( DOS_DIR
**dir
, const char *unix_path
)
421 DIR *unixdir
= opendir( unix_path
);
423 static const WCHAR empty_strW
[] = { 0 };
429 WCHAR long_name
[MAX_PATH
];
430 struct dirent
*de
= readdir(unixdir
);
434 MultiByteToWideChar(CP_UNIXCP
, 0, de
->d_name
, -1, long_name
, MAX_PATH
);
435 r
= DOSFS_AddDirEntry(dir
, long_name
, empty_strW
);
440 DOSFS_AddDirEntry(dir
, empty_strW
, empty_strW
);
445 /***********************************************************************
448 static DOS_DIR
*DOSFS_OpenDir( const char *unix_path
)
450 const int init_size
= 0x100;
451 DOS_DIR
*dir
= HeapAlloc( GetProcessHeap(), 0, sizeof(*dir
) + init_size
*sizeof (WCHAR
));
454 TRACE("%s\n",debugstr_a(unix_path
));
458 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
462 dir
->size
= init_size
;
464 /* Treat empty path as root directory. This simplifies path split into
465 directory and mask in several other places */
466 if (!*unix_path
) unix_path
= "/";
468 r
= DOSFS_OpenDir_VFAT( &dir
, unix_path
);
471 r
= DOSFS_OpenDir_Normal( &dir
, unix_path
);
475 HeapFree(GetProcessHeap(), 0, dir
);
484 /***********************************************************************
487 static void DOSFS_CloseDir( DOS_DIR
*dir
)
489 HeapFree( GetProcessHeap(), 0, dir
);
493 /***********************************************************************
496 static BOOL
DOSFS_ReadDir( DOS_DIR
*dir
, LPCWSTR
*long_name
,
497 LPCWSTR
*short_name
)
504 /* the long pathname is first */
505 ln
= &dir
->names
[dir
->used
];
510 dir
->used
+= (strlenW(ln
) + 1);
512 /* followed by the short path name */
513 sn
= &dir
->names
[dir
->used
];
518 dir
->used
+= (strlenW(sn
) + 1);
524 /***********************************************************************
527 * Transform a Unix file name into a hashed DOS name. If the name is a valid
528 * DOS name, it is converted to upper-case; otherwise it is replaced by a
529 * hashed version that fits in 8.3 format.
530 * File name can be terminated by '\0', '\\' or '/'.
531 * 'buffer' must be at least 13 characters long.
533 static void DOSFS_Hash( LPCWSTR name
, LPWSTR buffer
, BOOL dir_format
)
535 static const char invalid_chars
[] = INVALID_DOS_CHARS
"~.";
536 static const char hash_chars
[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
545 for(i
= 0; i
< 11; i
++) buffer
[i
] = ' ';
549 if (DOSFS_ValidDOSName( name
))
551 /* Check for '.' and '..' */
555 if (!dir_format
) buffer
[1] = buffer
[2] = '\0';
556 if (name
[1] == '.') buffer
[1] = '.';
560 /* Simply copy the name, converting to uppercase */
562 for (dst
= buffer
; !is_end_of_name(*name
) && (*name
!= '.'); name
++)
563 *dst
++ = toupperW(*name
);
566 if (dir_format
) dst
= buffer
+ 8;
568 for (name
++; !is_end_of_name(*name
); name
++)
569 *dst
++ = toupperW(*name
);
571 if (!dir_format
) *dst
= '\0';
575 /* Compute the hash code of the file name */
576 /* If you know something about hash functions, feel free to */
577 /* insert a better algorithm here... */
578 if (!is_case_sensitive
)
580 for (p
= name
, hash
= 0xbeef; !is_end_of_name(p
[1]); p
++)
581 hash
= (hash
<<3) ^ (hash
>>5) ^ tolowerW(*p
) ^ (tolowerW(p
[1]) << 8);
582 hash
= (hash
<<3) ^ (hash
>>5) ^ tolowerW(*p
); /* Last character */
586 for (p
= name
, hash
= 0xbeef; !is_end_of_name(p
[1]); p
++)
587 hash
= (hash
<< 3) ^ (hash
>> 5) ^ *p
^ (p
[1] << 8);
588 hash
= (hash
<< 3) ^ (hash
>> 5) ^ *p
; /* Last character */
591 /* Find last dot for start of the extension */
592 for (p
= name
+1, ext
= NULL
; !is_end_of_name(*p
); p
++)
593 if (*p
== '.') ext
= p
;
594 if (ext
&& is_end_of_name(ext
[1]))
595 ext
= NULL
; /* Empty extension ignored */
597 /* Copy first 4 chars, replacing invalid chars with '_' */
598 for (i
= 4, p
= name
, dst
= buffer
; i
> 0; i
--, p
++)
600 if (is_end_of_name(*p
) || (p
== ext
)) break;
601 *dst
++ = (*p
< 256 && strchr( invalid_chars
, (char)*p
)) ? '_' : toupperW(*p
);
603 /* Pad to 5 chars with '~' */
604 while (i
-- >= 0) *dst
++ = '~';
606 /* Insert hash code converted to 3 ASCII chars */
607 *dst
++ = hash_chars
[(hash
>> 10) & 0x1f];
608 *dst
++ = hash_chars
[(hash
>> 5) & 0x1f];
609 *dst
++ = hash_chars
[hash
& 0x1f];
611 /* Copy the first 3 chars of the extension (if any) */
614 if (!dir_format
) *dst
++ = '.';
615 for (i
= 3, ext
++; (i
> 0) && !is_end_of_name(*ext
); i
--, ext
++)
616 *dst
++ = (*ext
< 256 && strchr( invalid_chars
, (char)*ext
)) ? '_' : toupperW(*ext
);
618 if (!dir_format
) *dst
= '\0';
622 /***********************************************************************
625 * Find the Unix file name in a given directory that corresponds to
626 * a file name (either in Unix or DOS format).
627 * File name can be terminated by '\0', '\\' or '/'.
628 * Return TRUE if OK, FALSE if no file name matches.
630 * 'long_buf' must be at least 'long_len' characters long. If the long name
631 * turns out to be larger than that, the function returns FALSE.
632 * 'short_buf' must be at least 13 characters long.
634 BOOL
DOSFS_FindUnixName( const DOS_FULL_NAME
*path
, LPCWSTR name
, char *long_buf
,
635 INT long_len
, LPWSTR short_buf
)
638 LPCWSTR long_name
, short_name
;
639 WCHAR dos_name
[12], tmp_buf
[13];
642 LPCWSTR p
= strchrW( name
, '/' );
643 int len
= p
? (int)(p
- name
) : strlenW(name
);
644 if ((p
= strchrW( name
, '\\' ))) len
= min( (int)(p
- name
), len
);
645 /* Ignore trailing dots and spaces */
646 while (len
> 1 && (name
[len
-1] == '.' || name
[len
-1] == ' ')) len
--;
647 if (long_len
< len
+ 1) return FALSE
;
649 TRACE("%s,%s\n", path
->long_name
, debugstr_w(name
) );
651 if (!DOSFS_ToDosFCBFormat( name
, dos_name
)) dos_name
[0] = '\0';
653 if (!(dir
= DOSFS_OpenDir( path
->long_name
)))
655 WARN("(%s,%s): can't open dir: %s\n",
656 path
->long_name
, debugstr_w(name
), strerror(errno
) );
660 while ((ret
= DOSFS_ReadDir( dir
, &long_name
, &short_name
)))
662 /* Check against Unix name */
663 if (len
== strlenW(long_name
))
665 if (is_case_sensitive
)
667 if (!strncmpW( long_name
, name
, len
)) break;
671 if (!strncmpiW( long_name
, name
, len
)) break;
676 /* Check against hashed DOS name */
679 DOSFS_Hash( long_name
, tmp_buf
, TRUE
);
680 short_name
= tmp_buf
;
682 if (!strcmpW( dos_name
, short_name
)) break;
687 if (long_buf
) WideCharToMultiByte(CP_UNIXCP
, 0, long_name
, -1, long_buf
, long_len
, NULL
, NULL
);
691 DOSFS_ToDosDTAFormat( short_name
, short_buf
);
693 DOSFS_Hash( long_name
, short_buf
, FALSE
);
695 TRACE("(%s,%s) -> %s (%s)\n", path
->long_name
, debugstr_w(name
),
696 debugstr_w(long_name
), short_buf
? debugstr_w(short_buf
) : "***");
699 WARN("%s not found in '%s'\n", debugstr_w(name
), path
->long_name
);
700 DOSFS_CloseDir( dir
);
705 /**************************************************************************
706 * DOSFS_CreateCommPort
708 static HANDLE
DOSFS_CreateCommPort(LPCWSTR name
, DWORD access
, DWORD attributes
, LPSECURITY_ATTRIBUTES sa
)
713 OBJECT_ATTRIBUTES attr
;
714 UNICODE_STRING nameW
;
719 static const WCHAR serialportsW
[] = {'M','a','c','h','i','n','e','\\',
720 'S','o','f','t','w','a','r','e','\\',
721 'W','i','n','e','\\','W','i','n','e','\\',
722 'C','o','n','f','i','g','\\',
723 'S','e','r','i','a','l','P','o','r','t','s',0};
725 TRACE_(file
)("%s %lx %lx\n", debugstr_w(name
), access
, attributes
);
727 attr
.Length
= sizeof(attr
);
728 attr
.RootDirectory
= 0;
729 attr
.ObjectName
= &nameW
;
731 attr
.SecurityDescriptor
= NULL
;
732 attr
.SecurityQualityOfService
= NULL
;
733 RtlInitUnicodeString( &nameW
, serialportsW
);
735 if (NtOpenKey( &hkey
, KEY_ALL_ACCESS
, &attr
)) return 0;
737 RtlInitUnicodeString( &nameW
, name
);
738 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
739 devnameW
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
745 if (!devnameW
) return 0;
746 WideCharToMultiByte(CP_ACP
, 0, devnameW
, -1, devname
, sizeof(devname
), NULL
, NULL
);
748 TRACE("opening %s as %s\n", devname
, debugstr_w(name
));
750 ret
= FILE_CreateFile( devname
, access
, FILE_SHARE_READ
|FILE_SHARE_WRITE
,
751 sa
, OPEN_EXISTING
, attributes
, NULL
, FALSE
, DRIVE_FIXED
);
754 ERR("Couldn't open device '%s' ! (check permissions)\n",devname
);
756 TRACE("return %p\n", ret
);
760 /***********************************************************************
763 * Open a DOS device. This might not map 1:1 into the UNIX device concept.
764 * Returns 0 on failure.
766 HANDLE
DOSFS_OpenDevice( LPCWSTR name
, DWORD access
, DWORD attributes
, LPSECURITY_ATTRIBUTES sa
)
772 if (name
[0] && (name
[1] == ':')) name
+= 2;
773 if ((p
= strrchrW( name
, '/' ))) name
= p
+ 1;
774 if ((p
= strrchrW( name
, '\\' ))) name
= p
+ 1;
775 for (i
= 0; i
< sizeof(DOSFS_Devices
)/sizeof(DOSFS_Devices
[0]); i
++)
777 const WCHAR
*dev
= DOSFS_Devices
[i
].name
;
778 if (!strncmpiW( dev
, name
, strlenW(dev
) ))
780 p
= name
+ strlenW( dev
);
781 if (!*p
|| (*p
== '.') || (*p
== ':')) {
782 static const WCHAR nulW
[] = {'N','U','L',0};
783 static const WCHAR conW
[] = {'C','O','N',0};
785 if (!strcmpiW(DOSFS_Devices
[i
].name
, nulW
))
786 return FILE_CreateFile( "/dev/null", access
,
787 FILE_SHARE_READ
|FILE_SHARE_WRITE
, sa
,
788 OPEN_EXISTING
, 0, 0, TRUE
, DRIVE_UNKNOWN
);
789 if (!strcmpiW(DOSFS_Devices
[i
].name
, conW
)) {
791 switch (access
& (GENERIC_READ
|GENERIC_WRITE
)) {
793 to_dup
= GetStdHandle( STD_INPUT_HANDLE
);
796 to_dup
= GetStdHandle( STD_OUTPUT_HANDLE
);
799 FIXME("can't open CON read/write\n");
802 if (!DuplicateHandle( GetCurrentProcess(), to_dup
, GetCurrentProcess(),
804 sa
&& (sa
->nLength
>=sizeof(*sa
)) && sa
->bInheritHandle
,
805 DUPLICATE_SAME_ACCESS
))
810 if( (handle
=DOSFS_CreateCommPort(DOSFS_Devices
[i
].name
,access
,attributes
,sa
)) )
812 FIXME("device open %s not supported (yet)\n", debugstr_w(DOSFS_Devices
[i
].name
));
821 /***********************************************************************
824 * Get the drive specified by a given path name (DOS or Unix format).
826 static int DOSFS_GetPathDrive( LPCWSTR
*name
)
831 if (*p
&& (p
[1] == ':'))
833 drive
= toupperW(*p
) - 'A';
836 else if (*p
== '/') /* Absolute Unix path? */
838 if ((drive
= DRIVE_FindDriveRootW( name
)) == -1)
840 MESSAGE("Warning: %s not accessible from a configured DOS drive\n", debugstr_w(*name
) );
841 /* Assume it really was a DOS name */
842 drive
= DRIVE_GetCurrentDrive();
845 else drive
= DRIVE_GetCurrentDrive();
847 if (!DRIVE_IsValid(drive
))
849 SetLastError( ERROR_INVALID_DRIVE
);
856 /***********************************************************************
859 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
860 * Unix name / short DOS name pair.
861 * Return FALSE if one of the path components does not exist. The last path
862 * component is only checked if 'check_last' is non-zero.
863 * The buffers pointed to by 'long_buf' and 'short_buf' must be
864 * at least MAX_PATHNAME_LEN long.
866 BOOL
DOSFS_GetFullName( LPCWSTR name
, BOOL check_last
, DOS_FULL_NAME
*full
)
871 static const WCHAR driveA_rootW
[] = {'A',':','\\',0};
872 static const WCHAR dos_rootW
[] = {'\\',0};
874 TRACE("%s (last=%d)\n", debugstr_w(name
), check_last
);
876 if ((!*name
) || (*name
=='\n'))
877 { /* error code for Win98 */
878 SetLastError(ERROR_BAD_PATHNAME
);
882 if ((full
->drive
= DOSFS_GetPathDrive( &name
)) == -1) return FALSE
;
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 strcpyW( full
->short_name
, driveA_rootW
);
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 lstrcpynW( full
->short_name
+ 3, DRIVE_GetDosCwd( full
->drive
),
902 sizeof(full
->short_name
)/sizeof(full
->short_name
[0]) - 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
+ strlenW(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
)/sizeof(full
->short_name
[0]) - 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
, name
, p_l
+ 1,
946 sizeof(full
->long_name
) - (p_l
- full
->long_name
) - 1, p_s
+ 1 )))
952 while (!is_end_of_name(*name
)) name
++;
954 else if (!check_last
)
958 while (!is_end_of_name(*name
) &&
959 (p_s
< full
->short_name
+ sizeof(full
->short_name
)/sizeof(full
->short_name
[0]) - 1) &&
960 (p_l
< full
->long_name
+ sizeof(full
->long_name
) - 1))
963 *p_s
++ = tolowerW(*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 (is_case_sensitive
) wch
= tolowerW(*name
);
969 p_l
+= WideCharToMultiByte(CP_UNIXCP
, 0, &wch
, 1, p_l
, 2, NULL
, NULL
);
972 /* Ignore trailing dots and spaces */
973 while(p_l
[-1] == '.' || p_l
[-1] == ' ') {
980 while ((*name
== '\\') || (*name
== '/')) name
++;
987 SetLastError( ERROR_FILE_NOT_FOUND
);
990 if (*name
) /* Not last */
992 SetLastError( ERROR_PATH_NOT_FOUND
);
996 if (!full
->long_name
[0]) strcpy( full
->long_name
, "/" );
997 if (!full
->short_name
[2]) strcpyW( full
->short_name
+ 2, dos_rootW
);
998 TRACE("returning %s = %s\n", full
->long_name
, debugstr_w(full
->short_name
) );
1003 /***********************************************************************
1004 * wine_get_unix_file_name (KERNEL32.@) Not a Windows API
1006 * Return the full Unix file name for a given path.
1008 BOOL WINAPI
wine_get_unix_file_name( LPCWSTR dosW
, LPSTR buffer
, DWORD len
)
1013 ret
= DOSFS_GetFullName( dosW
, FALSE
, &path
);
1016 strncpy( buffer
, path
.long_name
, len
);
1017 buffer
[len
- 1] = 0; /* ensure 0 termination */
1023 /***********************************************************************
1024 * MulDiv (KERNEL32.@)
1026 * Result of multiplication and division
1027 * -1: Overflow occurred or Divisor was 0
1036 if (!nDivisor
) return -1;
1038 /* We want to deal with a positive divisor to simplify the logic. */
1041 nMultiplicand
= - nMultiplicand
;
1042 nDivisor
= -nDivisor
;
1045 /* If the result is positive, we "add" to round. else, we subtract to round. */
1046 if ( ( (nMultiplicand
< 0) && (nMultiplier
< 0) ) ||
1047 ( (nMultiplicand
>= 0) && (nMultiplier
>= 0) ) )
1048 ret
= (((LONGLONG
)nMultiplicand
* nMultiplier
) + (nDivisor
/2)) / nDivisor
;
1050 ret
= (((LONGLONG
)nMultiplicand
* nMultiplier
) - (nDivisor
/2)) / nDivisor
;
1052 if ((ret
> 2147483647) || (ret
< -2147483647)) return -1;
1057 /***********************************************************************
1058 * DosDateTimeToFileTime (KERNEL32.@)
1060 BOOL WINAPI
DosDateTimeToFileTime( WORD fatdate
, WORD fattime
, LPFILETIME ft
)
1065 time_t time1
, time2
;
1068 newtm
.tm_sec
= (fattime
& 0x1f) * 2;
1069 newtm
.tm_min
= (fattime
>> 5) & 0x3f;
1070 newtm
.tm_hour
= (fattime
>> 11);
1071 newtm
.tm_mday
= (fatdate
& 0x1f);
1072 newtm
.tm_mon
= ((fatdate
>> 5) & 0x0f) - 1;
1073 newtm
.tm_year
= (fatdate
>> 9) + 80;
1075 RtlSecondsSince1970ToTime( timegm(&newtm
), (LARGE_INTEGER
*)ft
);
1077 time1
= mktime(&newtm
);
1078 gtm
= gmtime(&time1
);
1079 time2
= mktime(gtm
);
1080 RtlSecondsSince1970ToTime( 2*time1
-time2
, (LARGE_INTEGER
*)ft
);
1086 /***********************************************************************
1087 * FileTimeToDosDateTime (KERNEL32.@)
1089 BOOL WINAPI
FileTimeToDosDateTime( const FILETIME
*ft
, LPWORD fatdate
,
1097 li
.u
.LowPart
= ft
->dwLowDateTime
;
1098 li
.u
.HighPart
= ft
->dwHighDateTime
;
1099 RtlTimeToSecondsSince1970( &li
, &t
);
1101 tm
= gmtime( &unixtime
);
1103 *fattime
= (tm
->tm_hour
<< 11) + (tm
->tm_min
<< 5) + (tm
->tm_sec
/ 2);
1105 *fatdate
= ((tm
->tm_year
- 80) << 9) + ((tm
->tm_mon
+ 1) << 5)
1111 /***********************************************************************
1112 * QueryDosDeviceA (KERNEL32.@)
1114 * returns array of strings terminated by \0, terminated by \0
1116 DWORD WINAPI
QueryDosDeviceA(LPCSTR devname
,LPSTR target
,DWORD bufsize
)
1118 DWORD ret
= 0, retW
;
1119 LPWSTR targetW
= (LPWSTR
)HeapAlloc(GetProcessHeap(),0,
1120 bufsize
* sizeof(WCHAR
));
1121 UNICODE_STRING devnameW
;
1123 if(devname
) RtlCreateUnicodeStringFromAsciiz(&devnameW
, devname
);
1124 else devnameW
.Buffer
= NULL
;
1126 retW
= QueryDosDeviceW(devnameW
.Buffer
, targetW
, bufsize
);
1128 ret
= WideCharToMultiByte(CP_ACP
, 0, targetW
, retW
, target
,
1129 bufsize
, NULL
, NULL
);
1131 RtlFreeUnicodeString(&devnameW
);
1132 if (targetW
) HeapFree(GetProcessHeap(),0,targetW
);
1137 /***********************************************************************
1138 * QueryDosDeviceW (KERNEL32.@)
1140 * returns array of strings terminated by \0, terminated by \0
1143 * - Win9x returns for all calls ERROR_INVALID_PARAMETER
1144 * - the returned devices for devname == NULL is far from complete
1145 * - its not checked that the returned device exist
1147 DWORD WINAPI
QueryDosDeviceW(LPCWSTR devname
,LPWSTR target
,DWORD bufsize
)
1149 const WCHAR
*pDev
, *pName
, *pNum
= NULL
;
1153 TRACE("(%s,...)\n", debugstr_w(devname
));
1155 /* return known MSDOS devices */
1158 static const WCHAR devices
[][5] = {{'A','U','X',0},
1159 {'C','O','M','1',0},
1160 {'C','O','M','2',0},
1161 {'L','P','T','1',0},
1163 for(i
=0; (i
< (sizeof(devices
)/sizeof(devices
[0]))); i
++) {
1164 DWORD len
= strlenW(devices
[i
]);
1165 if(target
&& (bufsize
>= ret
+ len
+ 2)) {
1166 strcpyW(target
+ret
, devices
[i
]);
1169 /* in this case WinXP returns 0 */
1170 FIXME("function return is wrong for WinXP!\n");
1171 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1175 /* append drives here */
1176 if(target
&& bufsize
> 0) target
[ret
++] = 0;
1177 FIXME("Returned list is not complete\n");
1180 /* In theory all that are possible and have been defined.
1181 * Now just those below, since mirc uses it to check for special files.
1183 * (It is more complex, and supports netmounted stuff, and \\.\ stuff,
1184 * but currently we just ignore that.)
1186 if (!strcmpiW(devname
, auxW
)) {
1191 } else if (!strcmpiW(devname
, nulW
)) {
1194 } else if (!strncmpiW(devname
, comW
, strlenW(comW
))) {
1197 pNum
= devname
+ strlenW(comW
);
1198 for(numsiz
=0; isdigitW(*(pNum
+numsiz
)); numsiz
++);
1199 if(*(pNum
+ numsiz
)) {
1200 SetLastError(ERROR_FILE_NOT_FOUND
);
1203 } else if (!strncmpiW(devname
, lptW
, strlenW(lptW
))) {
1206 pNum
= devname
+ strlenW(lptW
);
1207 for(numsiz
=0; isdigitW(*(pNum
+numsiz
)); numsiz
++);
1208 if(*(pNum
+ numsiz
)) {
1209 SetLastError(ERROR_FILE_NOT_FOUND
);
1213 /* This might be a DOS device we do not handle yet ... */
1214 FIXME("(%s) not detected as DOS device!\n",debugstr_w(devname
));
1216 /* Win9x set the error ERROR_INVALID_PARAMETER */
1217 SetLastError(ERROR_FILE_NOT_FOUND
);
1220 FIXME("device %s may not exist on this computer\n", debugstr_w(devname
));
1222 ret
= strlenW(pDev
) + strlenW(pName
) + numsiz
+ 2;
1223 if (ret
> bufsize
) ret
= 0;
1224 if (target
&& ret
) {
1225 strcpyW(target
,pDev
);
1226 strcatW(target
,pName
);
1227 if (pNum
) strcatW(target
,pNum
);