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>
45 #define NONAMELESSUNION
46 #define NONAMELESSSTRUCT
53 #include "wine/unicode.h"
54 #include "wine/winbase16.h"
58 #include "wine/server.h"
59 #include "wine/exception.h"
64 #include "wine/debug.h"
66 WINE_DEFAULT_DEBUG_CHANNEL(dosfs
);
67 WINE_DECLARE_DEBUG_CHANNEL(file
);
69 /* Define the VFAT ioctl to get both short and long file names */
70 /* FIXME: is it possible to get this to work on other systems? */
72 /* We want the real kernel dirent structure, not the libc one */
77 unsigned short d_reclen
;
81 #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, KERNEL_DIRENT [2] )
83 /* To avoid blocking on non-directories in DOSFS_OpenDir_VFAT*/
85 # define O_DIRECTORY 0200000 /* must be directory */
89 #undef VFAT_IOCTL_READDIR_BOTH /* just in case... */
92 #define IS_OPTION_TRUE(ch) ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1')
94 /* Chars we don't want to see in DOS file names */
95 #define INVALID_DOS_CHARS "*?<>|\"+=,;[] \345"
97 static const DOS_DEVICE DOSFS_Devices
[] =
98 /* name, device flags (see Int 21/AX=0x4400) */
100 { {'C','O','N',0}, 0xc0d3 },
101 { {'P','R','N',0}, 0xa0c0 },
102 { {'N','U','L',0}, 0x80c4 },
103 { {'A','U','X',0}, 0x80c0 },
104 { {'L','P','T','1',0}, 0xa0c0 },
105 { {'L','P','T','2',0}, 0xa0c0 },
106 { {'L','P','T','3',0}, 0xa0c0 },
107 { {'L','P','T','4',0}, 0xc0d3 },
108 { {'C','O','M','1',0}, 0x80c0 },
109 { {'C','O','M','2',0}, 0x80c0 },
110 { {'C','O','M','3',0}, 0x80c0 },
111 { {'C','O','M','4',0}, 0x80c0 },
112 { {'S','C','S','I','M','G','R','$',0}, 0xc0c0 },
113 { {'H','P','S','C','A','N',0}, 0xc0c0 },
114 { {'E','M','M','X','X','X','X','0',0}, 0x0000 }
117 static const WCHAR devW
[] = {'\\','D','e','v','i','c','e','\\',0};
118 static const WCHAR dosW
[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\',0};
120 static const WCHAR auxW
[] = {'A','U','X',0};
121 static const WCHAR comW
[] = {'C','O','M',0};
122 static const WCHAR lptW
[] = {'L','P','T',0};
123 static const WCHAR nulW
[] = {'N','U','L',0};
125 static const WCHAR nullW
[] = {'N','u','l','l',0};
126 static const WCHAR parW
[] = {'P','a','r','a','l','l','e','l',0};
127 static const WCHAR serW
[] = {'S','e','r','i','a','l',0};
128 static const WCHAR oneW
[] = {'1',0};
131 * Directory info for DOSFS_ReadDir
132 * contains the names of *all* the files in the directory
141 /* Info structure for FindFirstFile handle */
144 char *path
; /* unix path */
157 static WINE_EXCEPTION_FILTER(page_fault
)
159 if (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION
)
160 return EXCEPTION_EXECUTE_HANDLER
;
161 return EXCEPTION_CONTINUE_SEARCH
;
165 /***********************************************************************
168 * Return 1 if Unix file 'name' is also a valid MS-DOS name
169 * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
170 * File name can be terminated by '\0', '\\' or '/'.
172 static int DOSFS_ValidDOSName( LPCWSTR name
, int ignore_case
)
174 static const char invalid_chars
[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS
;
175 const WCHAR
*p
= name
;
176 const char *invalid
= ignore_case
? (invalid_chars
+ 26) : invalid_chars
;
181 /* Check for "." and ".." */
184 /* All other names beginning with '.' are invalid */
185 return (IS_END_OF_NAME(*p
));
187 while (!IS_END_OF_NAME(*p
))
189 if (*p
< 256 && strchr( invalid
, (char)*p
)) return 0; /* Invalid char */
190 if (*p
== '.') break; /* Start of the extension */
191 if (++len
> 8) return 0; /* Name too long */
194 if (*p
!= '.') return 1; /* End of name */
196 if (IS_END_OF_NAME(*p
)) return 0; /* Empty extension not allowed */
198 while (!IS_END_OF_NAME(*p
))
200 if (*p
< 256 && strchr( invalid
, (char)*p
)) return 0; /* Invalid char */
201 if (*p
== '.') return 0; /* Second extension not allowed */
202 if (++len
> 3) return 0; /* Extension too long */
209 /***********************************************************************
210 * DOSFS_ToDosFCBFormat
212 * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
213 * expanding wild cards and converting to upper-case in the process.
214 * File name can be terminated by '\0', '\\' or '/'.
215 * Return FALSE if the name is not a valid DOS name.
216 * 'buffer' must be at least 12 characters long.
218 BOOL
DOSFS_ToDosFCBFormat( LPCWSTR name
, LPWSTR buffer
)
220 static const char invalid_chars
[] = INVALID_DOS_CHARS
;
224 /* Check for "." and ".." */
229 for(i
= 1; i
< 11; i
++) buffer
[i
] = ' ';
236 return (!*p
|| (*p
== '/') || (*p
== '\\'));
239 for (i
= 0; i
< 8; i
++)
256 if (*p
< 256 && strchr( invalid_chars
, (char)*p
)) return FALSE
;
257 buffer
[i
] = toupperW(*p
);
265 /* Skip all chars after wildcard up to first dot */
266 while (*p
&& (*p
!= '/') && (*p
!= '\\') && (*p
!= '.')) p
++;
270 /* Check if name too long */
271 if (*p
&& (*p
!= '/') && (*p
!= '\\') && (*p
!= '.')) return FALSE
;
273 if (*p
== '.') p
++; /* Skip dot */
275 for (i
= 8; i
< 11; i
++)
285 return FALSE
; /* Second extension not allowed */
293 if (*p
< 256 && strchr( invalid_chars
, (char)*p
)) return FALSE
;
294 buffer
[i
] = toupperW(*p
);
301 /* at most 3 character of the extension are processed
302 * is something behind this ?
304 while (*p
== '*' || *p
== ' ') p
++; /* skip wildcards and spaces */
305 return IS_END_OF_NAME(*p
);
309 /***********************************************************************
310 * DOSFS_ToDosDTAFormat
312 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
313 * converting to upper-case in the process.
314 * File name can be terminated by '\0', '\\' or '/'.
315 * 'buffer' must be at least 13 characters long.
317 static void DOSFS_ToDosDTAFormat( LPCWSTR name
, LPWSTR buffer
)
321 memcpy( buffer
, name
, 8 * sizeof(WCHAR
) );
323 while ((p
> buffer
) && (p
[-1] == ' ')) p
--;
325 memcpy( p
, name
+ 8, 3 * sizeof(WCHAR
) );
327 while (p
[-1] == ' ') p
--;
328 if (p
[-1] == '.') p
--;
333 /***********************************************************************
336 * Check a long file name against a mask.
338 * Tests (done in W95 DOS shell - case insensitive):
339 * *.txt test1.test.txt *
341 * *.t??????.t* test1.ta.tornado.txt *
342 * *tornado* test1.ta.tornado.txt *
343 * t*t test1.ta.tornado.txt *
345 * ?est??? test1.txt -
346 * *test1.txt* test1.txt *
347 * h?l?o*t.dat hellothisisatest.dat *
349 static int DOSFS_MatchLong( LPCWSTR mask
, LPCWSTR name
, int case_sensitive
)
351 LPCWSTR lastjoker
= NULL
;
352 LPCWSTR next_to_retry
= NULL
;
353 static const WCHAR asterisk_dot_asterisk
[] = {'*','.','*',0};
355 TRACE("(%s, %s, %x)\n", debugstr_w(mask
), debugstr_w(name
), case_sensitive
);
357 if (!strcmpW( mask
, asterisk_dot_asterisk
)) return 1;
358 while (*name
&& *mask
)
363 while (*mask
== '*') mask
++; /* Skip consecutive '*' */
365 if (!*mask
) return 1; /* end of mask is all '*', so match */
367 /* skip to the next match after the joker(s) */
368 if (case_sensitive
) while (*name
&& (*name
!= *mask
)) name
++;
369 else while (*name
&& (toupperW(*name
) != toupperW(*mask
))) name
++;
372 next_to_retry
= name
;
374 else if (*mask
!= '?')
379 if (*mask
!= *name
) mismatch
= 1;
383 if (toupperW(*mask
) != toupperW(*name
)) mismatch
= 1;
397 else /* mismatch ! */
399 if (lastjoker
) /* we had an '*', so we can try unlimitedly */
403 /* this scan sequence was a mismatch, so restart
404 * 1 char after the first char we checked last time */
406 name
= next_to_retry
;
409 return 0; /* bad luck */
418 while ((*mask
== '.') || (*mask
== '*'))
419 mask
++; /* Ignore trailing '.' or '*' in mask */
420 return (!*name
&& !*mask
);
424 /***********************************************************************
427 * Used to construct an array of filenames in DOSFS_OpenDir
429 static BOOL
DOSFS_AddDirEntry(DOS_DIR
**dir
, LPCWSTR name
, LPCWSTR dosname
)
431 int extra1
= strlenW(name
) + 1;
432 int extra2
= strlenW(dosname
) + 1;
434 /* if we need more, at minimum double the size */
435 if( (extra1
+ extra2
+ (*dir
)->used
) > (*dir
)->size
)
437 int more
= (*dir
)->size
;
440 if(more
<(extra1
+extra2
))
441 more
= extra1
+extra2
;
443 t
= HeapReAlloc(GetProcessHeap(), 0, *dir
, sizeof(**dir
) +
444 ((*dir
)->size
+ more
)*sizeof(WCHAR
) );
447 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
448 ERR("Out of memory caching directory structure %d %d %d\n",
449 (*dir
)->size
, more
, (*dir
)->used
);
453 (*dir
)->size
+= more
;
456 /* at this point, the dir structure is big enough to hold these names */
457 strcpyW(&(*dir
)->names
[(*dir
)->used
], name
);
458 (*dir
)->used
+= extra1
;
459 strcpyW(&(*dir
)->names
[(*dir
)->used
], dosname
);
460 (*dir
)->used
+= extra2
;
466 /***********************************************************************
469 static BOOL
DOSFS_OpenDir_VFAT(DOS_DIR
**dir
, const char *unix_path
)
471 #ifdef VFAT_IOCTL_READDIR_BOTH
473 int fd
= open( unix_path
, O_RDONLY
|O_DIRECTORY
);
476 /* Check if the VFAT ioctl is supported on this directory */
483 WCHAR long_name
[MAX_PATH
];
484 WCHAR short_name
[12];
486 r
= (ioctl( fd
, VFAT_IOCTL_READDIR_BOTH
, (long)de
) != -1);
491 MultiByteToWideChar(CP_UNIXCP
, 0, de
[0].d_name
, -1, long_name
, MAX_PATH
);
492 if (!DOSFS_ToDosFCBFormat( long_name
, short_name
))
493 short_name
[0] = '\0';
495 MultiByteToWideChar(CP_UNIXCP
, 0, de
[1].d_name
, -1, long_name
, MAX_PATH
);
497 MultiByteToWideChar(CP_UNIXCP
, 0, de
[0].d_name
, -1, long_name
, MAX_PATH
);
498 r
= DOSFS_AddDirEntry(dir
, long_name
, short_name
);
504 static const WCHAR empty_strW
[] = { 0 };
505 DOSFS_AddDirEntry(dir
, empty_strW
, empty_strW
);
511 #endif /* VFAT_IOCTL_READDIR_BOTH */
515 /***********************************************************************
516 * DOSFS_OpenDir_Normal
518 * Now use the standard opendir/readdir interface
520 static BOOL
DOSFS_OpenDir_Normal( DOS_DIR
**dir
, const char *unix_path
)
522 DIR *unixdir
= opendir( unix_path
);
524 static const WCHAR empty_strW
[] = { 0 };
530 WCHAR long_name
[MAX_PATH
];
531 struct dirent
*de
= readdir(unixdir
);
535 MultiByteToWideChar(CP_UNIXCP
, 0, de
->d_name
, -1, long_name
, MAX_PATH
);
536 r
= DOSFS_AddDirEntry(dir
, long_name
, empty_strW
);
541 DOSFS_AddDirEntry(dir
, empty_strW
, empty_strW
);
546 /***********************************************************************
549 static DOS_DIR
*DOSFS_OpenDir( const char *unix_path
)
551 const int init_size
= 0x100;
552 DOS_DIR
*dir
= HeapAlloc( GetProcessHeap(), 0, sizeof(*dir
) + init_size
*sizeof (WCHAR
));
555 TRACE("%s\n",debugstr_a(unix_path
));
559 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
563 dir
->size
= init_size
;
565 /* Treat empty path as root directory. This simplifies path split into
566 directory and mask in several other places */
567 if (!*unix_path
) unix_path
= "/";
569 r
= DOSFS_OpenDir_VFAT( &dir
, unix_path
);
572 r
= DOSFS_OpenDir_Normal( &dir
, unix_path
);
576 HeapFree(GetProcessHeap(), 0, dir
);
585 /***********************************************************************
588 static void DOSFS_CloseDir( DOS_DIR
*dir
)
590 HeapFree( GetProcessHeap(), 0, dir
);
594 /***********************************************************************
597 static BOOL
DOSFS_ReadDir( DOS_DIR
*dir
, LPCWSTR
*long_name
,
598 LPCWSTR
*short_name
)
605 /* the long pathname is first */
606 ln
= &dir
->names
[dir
->used
];
611 dir
->used
+= (strlenW(ln
) + 1);
613 /* followed by the short path name */
614 sn
= &dir
->names
[dir
->used
];
619 dir
->used
+= (strlenW(sn
) + 1);
625 /***********************************************************************
628 * Transform a Unix file name into a hashed DOS name. If the name is a valid
629 * DOS name, it is converted to upper-case; otherwise it is replaced by a
630 * hashed version that fits in 8.3 format.
631 * File name can be terminated by '\0', '\\' or '/'.
632 * 'buffer' must be at least 13 characters long.
634 static void DOSFS_Hash( LPCWSTR name
, LPWSTR buffer
, BOOL dir_format
,
637 static const char invalid_chars
[] = INVALID_DOS_CHARS
"~.";
638 static const char hash_chars
[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
647 for(i
= 0; i
< 11; i
++) buffer
[i
] = ' ';
651 if (DOSFS_ValidDOSName( name
, ignore_case
))
653 /* Check for '.' and '..' */
657 if (!dir_format
) buffer
[1] = buffer
[2] = '\0';
658 if (name
[1] == '.') buffer
[1] = '.';
662 /* Simply copy the name, converting to uppercase */
664 for (dst
= buffer
; !IS_END_OF_NAME(*name
) && (*name
!= '.'); name
++)
665 *dst
++ = toupperW(*name
);
668 if (dir_format
) dst
= buffer
+ 8;
670 for (name
++; !IS_END_OF_NAME(*name
); name
++)
671 *dst
++ = toupperW(*name
);
673 if (!dir_format
) *dst
= '\0';
677 /* Compute the hash code of the file name */
678 /* If you know something about hash functions, feel free to */
679 /* insert a better algorithm here... */
682 for (p
= name
, hash
= 0xbeef; !IS_END_OF_NAME(p
[1]); p
++)
683 hash
= (hash
<<3) ^ (hash
>>5) ^ tolowerW(*p
) ^ (tolowerW(p
[1]) << 8);
684 hash
= (hash
<<3) ^ (hash
>>5) ^ tolowerW(*p
); /* Last character */
688 for (p
= name
, hash
= 0xbeef; !IS_END_OF_NAME(p
[1]); p
++)
689 hash
= (hash
<< 3) ^ (hash
>> 5) ^ *p
^ (p
[1] << 8);
690 hash
= (hash
<< 3) ^ (hash
>> 5) ^ *p
; /* Last character */
693 /* Find last dot for start of the extension */
694 for (p
= name
+1, ext
= NULL
; !IS_END_OF_NAME(*p
); p
++)
695 if (*p
== '.') ext
= p
;
696 if (ext
&& IS_END_OF_NAME(ext
[1]))
697 ext
= NULL
; /* Empty extension ignored */
699 /* Copy first 4 chars, replacing invalid chars with '_' */
700 for (i
= 4, p
= name
, dst
= buffer
; i
> 0; i
--, p
++)
702 if (IS_END_OF_NAME(*p
) || (p
== ext
)) break;
703 *dst
++ = (*p
< 256 && strchr( invalid_chars
, (char)*p
)) ? '_' : toupperW(*p
);
705 /* Pad to 5 chars with '~' */
706 while (i
-- >= 0) *dst
++ = '~';
708 /* Insert hash code converted to 3 ASCII chars */
709 *dst
++ = hash_chars
[(hash
>> 10) & 0x1f];
710 *dst
++ = hash_chars
[(hash
>> 5) & 0x1f];
711 *dst
++ = hash_chars
[hash
& 0x1f];
713 /* Copy the first 3 chars of the extension (if any) */
716 if (!dir_format
) *dst
++ = '.';
717 for (i
= 3, ext
++; (i
> 0) && !IS_END_OF_NAME(*ext
); i
--, ext
++)
718 *dst
++ = (*ext
< 256 && strchr( invalid_chars
, (char)*ext
)) ? '_' : toupperW(*ext
);
720 if (!dir_format
) *dst
= '\0';
724 /***********************************************************************
727 * Find the Unix file name in a given directory that corresponds to
728 * a file name (either in Unix or DOS format).
729 * File name can be terminated by '\0', '\\' or '/'.
730 * Return TRUE if OK, FALSE if no file name matches.
732 * 'long_buf' must be at least 'long_len' characters long. If the long name
733 * turns out to be larger than that, the function returns FALSE.
734 * 'short_buf' must be at least 13 characters long.
736 BOOL
DOSFS_FindUnixName( const DOS_FULL_NAME
*path
, LPCWSTR name
, char *long_buf
,
737 INT long_len
, LPWSTR short_buf
, BOOL ignore_case
)
740 LPCWSTR long_name
, short_name
;
741 WCHAR dos_name
[12], tmp_buf
[13];
744 LPCWSTR p
= strchrW( name
, '/' );
745 int len
= p
? (int)(p
- name
) : strlenW(name
);
746 if ((p
= strchrW( name
, '\\' ))) len
= min( (int)(p
- name
), len
);
747 /* Ignore trailing dots and spaces */
748 while (len
> 1 && (name
[len
-1] == '.' || name
[len
-1] == ' ')) len
--;
749 if (long_len
< len
+ 1) return FALSE
;
751 TRACE("%s,%s\n", path
->long_name
, debugstr_w(name
) );
753 if (!DOSFS_ToDosFCBFormat( name
, dos_name
)) dos_name
[0] = '\0';
755 if (!(dir
= DOSFS_OpenDir( path
->long_name
)))
757 WARN("(%s,%s): can't open dir: %s\n",
758 path
->long_name
, debugstr_w(name
), strerror(errno
) );
762 while ((ret
= DOSFS_ReadDir( dir
, &long_name
, &short_name
)))
764 /* Check against Unix name */
765 if (len
== strlenW(long_name
))
769 if (!strncmpW( long_name
, name
, len
)) break;
773 if (!strncmpiW( long_name
, name
, len
)) break;
778 /* Check against hashed DOS name */
781 DOSFS_Hash( long_name
, tmp_buf
, TRUE
, ignore_case
);
782 short_name
= tmp_buf
;
784 if (!strcmpW( dos_name
, short_name
)) break;
789 if (long_buf
) WideCharToMultiByte(CP_UNIXCP
, 0, long_name
, -1, long_buf
, long_len
, NULL
, NULL
);
793 DOSFS_ToDosDTAFormat( short_name
, short_buf
);
795 DOSFS_Hash( long_name
, short_buf
, FALSE
, ignore_case
);
797 TRACE("(%s,%s) -> %s (%s)\n", path
->long_name
, debugstr_w(name
),
798 debugstr_w(long_name
), short_buf
? debugstr_w(short_buf
) : "***");
801 WARN("%s not found in '%s'\n", debugstr_w(name
), path
->long_name
);
802 DOSFS_CloseDir( dir
);
807 /***********************************************************************
810 * Check if a DOS file name represents a DOS device and return the device.
812 const DOS_DEVICE
*DOSFS_GetDevice( LPCWSTR name
)
817 if (!name
) return NULL
; /* if wine_server_handle_to_fd was used */
818 if (name
[0] && (name
[1] == ':')) name
+= 2;
819 if ((p
= strrchrW( name
, '/' ))) name
= p
+ 1;
820 if ((p
= strrchrW( name
, '\\' ))) name
= p
+ 1;
821 for (i
= 0; i
< sizeof(DOSFS_Devices
)/sizeof(DOSFS_Devices
[0]); i
++)
823 const WCHAR
*dev
= DOSFS_Devices
[i
].name
;
824 if (!strncmpiW( dev
, name
, strlenW(dev
) ))
826 p
= name
+ strlenW( dev
);
827 if (!*p
|| (*p
== '.') || (*p
== ':')) return &DOSFS_Devices
[i
];
834 /***********************************************************************
835 * DOSFS_GetDeviceByHandle
837 const DOS_DEVICE
*DOSFS_GetDeviceByHandle( HANDLE hFile
)
839 const DOS_DEVICE
*ret
= NULL
;
840 SERVER_START_REQ( get_device_id
)
843 if (!wine_server_call( req
))
845 if ((reply
->id
>= 0) &&
846 (reply
->id
< sizeof(DOSFS_Devices
)/sizeof(DOSFS_Devices
[0])))
847 ret
= &DOSFS_Devices
[reply
->id
];
855 /**************************************************************************
856 * DOSFS_CreateCommPort
858 static HANDLE
DOSFS_CreateCommPort(LPCWSTR name
, DWORD access
, DWORD attributes
, LPSECURITY_ATTRIBUTES sa
)
863 OBJECT_ATTRIBUTES attr
;
864 UNICODE_STRING nameW
;
869 static const WCHAR serialportsW
[] = {'M','a','c','h','i','n','e','\\',
870 'S','o','f','t','w','a','r','e','\\',
871 'W','i','n','e','\\','W','i','n','e','\\',
872 'C','o','n','f','i','g','\\',
873 'S','e','r','i','a','l','P','o','r','t','s',0};
875 TRACE_(file
)("%s %lx %lx\n", debugstr_w(name
), access
, attributes
);
877 attr
.Length
= sizeof(attr
);
878 attr
.RootDirectory
= 0;
879 attr
.ObjectName
= &nameW
;
881 attr
.SecurityDescriptor
= NULL
;
882 attr
.SecurityQualityOfService
= NULL
;
883 RtlInitUnicodeString( &nameW
, serialportsW
);
885 if (NtOpenKey( &hkey
, KEY_ALL_ACCESS
, &attr
)) return 0;
887 RtlInitUnicodeString( &nameW
, name
);
888 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
889 devnameW
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
895 if (!devnameW
) return 0;
896 WideCharToMultiByte(CP_ACP
, 0, devnameW
, -1, devname
, sizeof(devname
), NULL
, NULL
);
898 TRACE("opening %s as %s\n", devname
, debugstr_w(name
));
900 SERVER_START_REQ( create_serial
)
902 req
->access
= access
;
903 req
->inherit
= (sa
&& (sa
->nLength
>=sizeof(*sa
)) && sa
->bInheritHandle
);
904 req
->attributes
= attributes
;
905 req
->sharing
= FILE_SHARE_READ
|FILE_SHARE_WRITE
;
906 wine_server_add_data( req
, devname
, strlen(devname
) );
908 wine_server_call_err( req
);
914 ERR("Couldn't open device '%s' ! (check permissions)\n",devname
);
916 TRACE("return %p\n", ret
);
920 /***********************************************************************
923 * Open a DOS device. This might not map 1:1 into the UNIX device concept.
924 * Returns 0 on failure.
926 HANDLE
DOSFS_OpenDevice( LPCWSTR name
, DWORD access
, DWORD attributes
, LPSECURITY_ATTRIBUTES sa
)
932 if (name
[0] && (name
[1] == ':')) name
+= 2;
933 if ((p
= strrchrW( name
, '/' ))) name
= p
+ 1;
934 if ((p
= strrchrW( name
, '\\' ))) name
= p
+ 1;
935 for (i
= 0; i
< sizeof(DOSFS_Devices
)/sizeof(DOSFS_Devices
[0]); i
++)
937 const WCHAR
*dev
= DOSFS_Devices
[i
].name
;
938 if (!strncmpiW( dev
, name
, strlenW(dev
) ))
940 p
= name
+ strlenW( dev
);
941 if (!*p
|| (*p
== '.') || (*p
== ':')) {
942 static const WCHAR nulW
[] = {'N','U','L',0};
943 static const WCHAR conW
[] = {'C','O','N',0};
944 static const WCHAR scsimgrW
[] = {'S','C','S','I','M','G','R','$',0};
945 static const WCHAR hpscanW
[] = {'H','P','S','C','A','N',0};
946 static const WCHAR emmxxxx0W
[] = {'E','M','M','X','X','X','X','0',0};
948 if (!strcmpiW(DOSFS_Devices
[i
].name
, nulW
))
949 return FILE_CreateFile( "/dev/null", access
,
950 FILE_SHARE_READ
|FILE_SHARE_WRITE
, sa
,
951 OPEN_EXISTING
, 0, 0, TRUE
, DRIVE_UNKNOWN
);
952 if (!strcmpiW(DOSFS_Devices
[i
].name
, conW
)) {
954 switch (access
& (GENERIC_READ
|GENERIC_WRITE
)) {
956 to_dup
= GetStdHandle( STD_INPUT_HANDLE
);
959 to_dup
= GetStdHandle( STD_OUTPUT_HANDLE
);
962 FIXME("can't open CON read/write\n");
965 if (!DuplicateHandle( GetCurrentProcess(), to_dup
, GetCurrentProcess(),
967 sa
&& (sa
->nLength
>=sizeof(*sa
)) && sa
->bInheritHandle
,
968 DUPLICATE_SAME_ACCESS
))
972 if (!strcmpiW(DOSFS_Devices
[i
].name
, scsimgrW
) ||
973 !strcmpiW(DOSFS_Devices
[i
].name
, hpscanW
) ||
974 !strcmpiW(DOSFS_Devices
[i
].name
, emmxxxx0W
))
976 return FILE_CreateDevice( i
, access
, sa
);
979 if( (handle
=DOSFS_CreateCommPort(DOSFS_Devices
[i
].name
,access
,attributes
,sa
)) )
981 FIXME("device open %s not supported (yet)\n", debugstr_w(DOSFS_Devices
[i
].name
));
990 /***********************************************************************
993 * Get the drive specified by a given path name (DOS or Unix format).
995 static int DOSFS_GetPathDrive( LPCWSTR
*name
)
1000 if (*p
&& (p
[1] == ':'))
1002 drive
= toupperW(*p
) - 'A';
1005 else if (*p
== '/') /* Absolute Unix path? */
1007 if ((drive
= DRIVE_FindDriveRootW( name
)) == -1)
1009 MESSAGE("Warning: %s not accessible from a configured DOS drive\n", debugstr_w(*name
) );
1010 /* Assume it really was a DOS name */
1011 drive
= DRIVE_GetCurrentDrive();
1014 else drive
= DRIVE_GetCurrentDrive();
1016 if (!DRIVE_IsValid(drive
))
1018 SetLastError( ERROR_INVALID_DRIVE
);
1025 /***********************************************************************
1028 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
1029 * Unix name / short DOS name pair.
1030 * Return FALSE if one of the path components does not exist. The last path
1031 * component is only checked if 'check_last' is non-zero.
1032 * The buffers pointed to by 'long_buf' and 'short_buf' must be
1033 * at least MAX_PATHNAME_LEN long.
1035 BOOL
DOSFS_GetFullName( LPCWSTR name
, BOOL check_last
, DOS_FULL_NAME
*full
)
1041 static const WCHAR driveA_rootW
[] = {'A',':','\\',0};
1042 static const WCHAR dos_rootW
[] = {'\\',0};
1044 TRACE("%s (last=%d)\n", debugstr_w(name
), check_last
);
1046 if ((!*name
) || (*name
=='\n'))
1047 { /* error code for Win98 */
1048 SetLastError(ERROR_BAD_PATHNAME
);
1052 if ((full
->drive
= DOSFS_GetPathDrive( &name
)) == -1) return FALSE
;
1053 flags
= DRIVE_GetFlags( full
->drive
);
1055 lstrcpynA( full
->long_name
, DRIVE_GetRoot( full
->drive
),
1056 sizeof(full
->long_name
) );
1057 if (full
->long_name
[1]) root
= full
->long_name
+ strlen(full
->long_name
);
1058 else root
= full
->long_name
; /* root directory */
1060 strcpyW( full
->short_name
, driveA_rootW
);
1061 full
->short_name
[0] += full
->drive
;
1063 if ((*name
== '\\') || (*name
== '/')) /* Absolute path */
1065 while ((*name
== '\\') || (*name
== '/')) name
++;
1067 else /* Relative path */
1069 lstrcpynA( root
+ 1, DRIVE_GetUnixCwd( full
->drive
),
1070 sizeof(full
->long_name
) - (root
- full
->long_name
) - 1 );
1071 if (root
[1]) *root
= '/';
1072 lstrcpynW( full
->short_name
+ 3, DRIVE_GetDosCwd( full
->drive
),
1073 sizeof(full
->short_name
)/sizeof(full
->short_name
[0]) - 3 );
1076 p_l
= full
->long_name
[1] ? full
->long_name
+ strlen(full
->long_name
)
1078 p_s
= full
->short_name
[3] ? full
->short_name
+ strlenW(full
->short_name
)
1079 : full
->short_name
+ 2;
1082 while (*name
&& found
)
1084 /* Check for '.' and '..' */
1088 if (IS_END_OF_NAME(name
[1]))
1091 while ((*name
== '\\') || (*name
== '/')) name
++;
1094 else if ((name
[1] == '.') && IS_END_OF_NAME(name
[2]))
1097 while ((*name
== '\\') || (*name
== '/')) name
++;
1098 while ((p_l
> root
) && (*p_l
!= '/')) p_l
--;
1099 while ((p_s
> full
->short_name
+ 2) && (*p_s
!= '\\')) p_s
--;
1100 *p_l
= *p_s
= '\0'; /* Remove trailing separator */
1105 /* Make sure buffers are large enough */
1107 if ((p_s
>= full
->short_name
+ sizeof(full
->short_name
)/sizeof(full
->short_name
[0]) - 14) ||
1108 (p_l
>= full
->long_name
+ sizeof(full
->long_name
) - 1))
1110 SetLastError( ERROR_PATH_NOT_FOUND
);
1114 /* Get the long and short name matching the file name */
1116 if ((found
= DOSFS_FindUnixName( full
, name
, p_l
+ 1,
1117 sizeof(full
->long_name
) - (p_l
- full
->long_name
) - 1,
1118 p_s
+ 1, !(flags
& DRIVE_CASE_SENSITIVE
) )))
1123 p_s
+= strlenW(p_s
);
1124 while (!IS_END_OF_NAME(*name
)) name
++;
1126 else if (!check_last
)
1130 while (!IS_END_OF_NAME(*name
) &&
1131 (p_s
< full
->short_name
+ sizeof(full
->short_name
)/sizeof(full
->short_name
[0]) - 1) &&
1132 (p_l
< full
->long_name
+ sizeof(full
->long_name
) - 1))
1135 *p_s
++ = tolowerW(*name
);
1136 /* If the drive is case-sensitive we want to create new */
1137 /* files in lower-case otherwise we can't reopen them */
1138 /* under the same short name. */
1139 if (flags
& DRIVE_CASE_SENSITIVE
) wch
= tolowerW(*name
);
1141 p_l
+= WideCharToMultiByte(CP_UNIXCP
, 0, &wch
, 1, p_l
, 2, NULL
, NULL
);
1144 /* Ignore trailing dots and spaces */
1145 while(p_l
[-1] == '.' || p_l
[-1] == ' ') {
1152 while ((*name
== '\\') || (*name
== '/')) name
++;
1159 SetLastError( ERROR_FILE_NOT_FOUND
);
1162 if (*name
) /* Not last */
1164 SetLastError( ERROR_PATH_NOT_FOUND
);
1168 if (!full
->long_name
[0]) strcpy( full
->long_name
, "/" );
1169 if (!full
->short_name
[2]) strcpyW( full
->short_name
+ 2, dos_rootW
);
1170 TRACE("returning %s = %s\n", full
->long_name
, debugstr_w(full
->short_name
) );
1175 /***********************************************************************
1176 * wine_get_unix_file_name (KERNEL32.@) Not a Windows API
1178 * Return the full Unix file name for a given path.
1180 BOOL WINAPI
wine_get_unix_file_name( LPCWSTR dosW
, LPSTR buffer
, DWORD len
)
1185 ret
= DOSFS_GetFullName( dosW
, FALSE
, &path
);
1188 strncpy( buffer
, path
.long_name
, len
);
1189 buffer
[len
- 1] = 0; /* ensure 0 termination */
1195 /***********************************************************************
1196 * get_show_dir_symlinks_option
1198 static BOOL
get_show_dir_symlinks_option(void)
1200 static const WCHAR WineW
[] = {'M','a','c','h','i','n','e','\\',
1201 'S','o','f','t','w','a','r','e','\\',
1202 'W','i','n','e','\\','W','i','n','e','\\',
1203 'C','o','n','f','i','g','\\','W','i','n','e',0};
1204 static const WCHAR ShowDirSymlinksW
[] = {'S','h','o','w','D','i','r','S','y','m','l','i','n','k','s',0};
1209 OBJECT_ATTRIBUTES attr
;
1210 UNICODE_STRING nameW
;
1213 attr
.Length
= sizeof(attr
);
1214 attr
.RootDirectory
= 0;
1215 attr
.ObjectName
= &nameW
;
1216 attr
.Attributes
= 0;
1217 attr
.SecurityDescriptor
= NULL
;
1218 attr
.SecurityQualityOfService
= NULL
;
1219 RtlInitUnicodeString( &nameW
, WineW
);
1221 if (!NtOpenKey( &hkey
, KEY_ALL_ACCESS
, &attr
))
1223 RtlInitUnicodeString( &nameW
, ShowDirSymlinksW
);
1224 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
1226 WCHAR
*str
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
1227 ret
= IS_OPTION_TRUE( str
[0] );
1235 /***********************************************************************
1238 static int DOSFS_FindNextEx( FIND_FIRST_INFO
*info
, WIN32_FIND_DATAW
*entry
)
1240 UINT flags
= DRIVE_GetFlags( info
->drive
);
1241 char *p
, buffer
[MAX_PATHNAME_LEN
];
1242 const char *drive_path
;
1244 LPCWSTR long_name
, short_name
;
1245 BY_HANDLE_FILE_INFORMATION fileinfo
;
1248 drive_path
= info
->path
+ strlen(DRIVE_GetRoot( info
->drive
));
1249 while ((*drive_path
== '/') || (*drive_path
== '\\')) drive_path
++;
1250 drive_root
= !*drive_path
;
1252 lstrcpynA( buffer
, info
->path
, sizeof(buffer
) - 1 );
1253 strcat( buffer
, "/" );
1254 p
= buffer
+ strlen(buffer
);
1256 while (DOSFS_ReadDir( info
->u
.dos_dir
, &long_name
, &short_name
))
1260 /* Don't return '.' and '..' in the root of the drive */
1261 if (drive_root
&& (long_name
[0] == '.') &&
1262 (!long_name
[1] || ((long_name
[1] == '.') && !long_name
[2])))
1265 /* Check the long mask */
1267 if (info
->long_mask
&& *info
->long_mask
)
1269 if (!DOSFS_MatchLong( info
->long_mask
, long_name
,
1270 flags
& DRIVE_CASE_SENSITIVE
)) continue;
1273 /* Check the file attributes */
1274 WideCharToMultiByte(CP_UNIXCP
, 0, long_name
, -1,
1275 p
, sizeof(buffer
) - (int)(p
- buffer
), NULL
, NULL
);
1276 if (!FILE_Stat( buffer
, &fileinfo
, &is_symlink
))
1278 WARN("can't stat %s\n", buffer
);
1281 if (is_symlink
&& (fileinfo
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
))
1283 static int show_dir_symlinks
= -1;
1284 if (show_dir_symlinks
== -1)
1285 show_dir_symlinks
= get_show_dir_symlinks_option();
1286 if (!show_dir_symlinks
) continue;
1289 /* We now have a matching entry; fill the result and return */
1291 entry
->dwFileAttributes
= fileinfo
.dwFileAttributes
;
1292 entry
->ftCreationTime
= fileinfo
.ftCreationTime
;
1293 entry
->ftLastAccessTime
= fileinfo
.ftLastAccessTime
;
1294 entry
->ftLastWriteTime
= fileinfo
.ftLastWriteTime
;
1295 entry
->nFileSizeHigh
= fileinfo
.nFileSizeHigh
;
1296 entry
->nFileSizeLow
= fileinfo
.nFileSizeLow
;
1299 DOSFS_ToDosDTAFormat( short_name
, entry
->cAlternateFileName
);
1301 DOSFS_Hash( long_name
, entry
->cAlternateFileName
, FALSE
,
1302 !(flags
& DRIVE_CASE_SENSITIVE
) );
1304 lstrcpynW( entry
->cFileName
, long_name
, sizeof(entry
->cFileName
)/sizeof(entry
->cFileName
[0]) );
1305 if (!(flags
& DRIVE_CASE_PRESERVING
)) strlwrW( entry
->cFileName
);
1306 TRACE("returning %s (%s) %02lx %ld\n",
1307 debugstr_w(entry
->cFileName
), debugstr_w(entry
->cAlternateFileName
),
1308 entry
->dwFileAttributes
, entry
->nFileSizeLow
);
1311 return 0; /* End of directory */
1314 /*************************************************************************
1315 * FindFirstFileExW (KERNEL32.@)
1317 HANDLE WINAPI
FindFirstFileExW(
1319 FINDEX_INFO_LEVELS fInfoLevelId
,
1320 LPVOID lpFindFileData
,
1321 FINDEX_SEARCH_OPS fSearchOp
,
1322 LPVOID lpSearchFilter
,
1323 DWORD dwAdditionalFlags
)
1325 FIND_FIRST_INFO
*info
;
1329 SetLastError(ERROR_PATH_NOT_FOUND
);
1330 return INVALID_HANDLE_VALUE
;
1333 if ((fSearchOp
!= FindExSearchNameMatch
) || (dwAdditionalFlags
!= 0))
1335 FIXME("options not implemented 0x%08x 0x%08lx\n", fSearchOp
, dwAdditionalFlags
);
1336 return INVALID_HANDLE_VALUE
;
1339 switch(fInfoLevelId
)
1341 case FindExInfoStandard
:
1343 WIN32_FIND_DATAW
* data
= (WIN32_FIND_DATAW
*) lpFindFileData
;
1347 data
->dwReserved0
= data
->dwReserved1
= 0x0;
1348 if (lpFileName
[0] == '\\' && lpFileName
[1] == '\\')
1350 ERR("UNC path name\n");
1351 if (!(info
= HeapAlloc( GetProcessHeap(), 0, sizeof(FIND_FIRST_INFO
)))) break;
1352 info
->u
.smb_dir
= SMB_FindFirst(lpFileName
);
1353 if(!info
->u
.smb_dir
)
1355 HeapFree(GetProcessHeap(), 0, info
);
1359 RtlInitializeCriticalSection( &info
->cs
);
1363 DOS_FULL_NAME full_name
;
1365 if (lpFileName
[0] && lpFileName
[1] == ':')
1367 /* don't allow root directories */
1368 if (!lpFileName
[2] ||
1369 ((lpFileName
[2] == '/' || lpFileName
[2] == '\\') && !lpFileName
[3]))
1371 SetLastError(ERROR_FILE_NOT_FOUND
);
1372 return INVALID_HANDLE_VALUE
;
1375 if (!DOSFS_GetFullName( lpFileName
, FALSE
, &full_name
)) break;
1376 if (!(info
= HeapAlloc( GetProcessHeap(), 0, sizeof(FIND_FIRST_INFO
)))) break;
1377 RtlInitializeCriticalSection( &info
->cs
);
1378 info
->path
= HeapAlloc( GetProcessHeap(), 0, strlen(full_name
.long_name
)+1 );
1379 strcpy( info
->path
, full_name
.long_name
);
1381 p
= strrchr( info
->path
, '/' );
1383 long_mask_len
= MultiByteToWideChar(CP_UNIXCP
, 0, p
, -1, NULL
, 0);
1384 info
->long_mask
= HeapAlloc( GetProcessHeap(), 0, long_mask_len
* sizeof(WCHAR
) );
1385 MultiByteToWideChar(CP_UNIXCP
, 0, p
, -1, info
->long_mask
, long_mask_len
);
1387 info
->drive
= full_name
.drive
;
1390 info
->u
.dos_dir
= DOSFS_OpenDir( info
->path
);
1392 if (!FindNextFileW( (HANDLE
) info
, data
))
1394 FindClose( (HANDLE
) info
);
1395 SetLastError( ERROR_FILE_NOT_FOUND
);
1398 return (HANDLE
) info
;
1402 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId
);
1404 return INVALID_HANDLE_VALUE
;
1407 /*************************************************************************
1408 * FindFirstFileA (KERNEL32.@)
1410 HANDLE WINAPI
FindFirstFileA(
1412 WIN32_FIND_DATAA
*lpFindData
)
1414 return FindFirstFileExA(lpFileName
, FindExInfoStandard
, lpFindData
,
1415 FindExSearchNameMatch
, NULL
, 0);
1418 /*************************************************************************
1419 * FindFirstFileExA (KERNEL32.@)
1421 HANDLE WINAPI
FindFirstFileExA(
1423 FINDEX_INFO_LEVELS fInfoLevelId
,
1424 LPVOID lpFindFileData
,
1425 FINDEX_SEARCH_OPS fSearchOp
,
1426 LPVOID lpSearchFilter
,
1427 DWORD dwAdditionalFlags
)
1430 WIN32_FIND_DATAA
*dataA
;
1431 WIN32_FIND_DATAW dataW
;
1432 UNICODE_STRING pathW
;
1436 SetLastError(ERROR_PATH_NOT_FOUND
);
1437 return INVALID_HANDLE_VALUE
;
1440 if (!RtlCreateUnicodeStringFromAsciiz(&pathW
, lpFileName
))
1442 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1443 return INVALID_HANDLE_VALUE
;
1446 handle
= FindFirstFileExW(pathW
.Buffer
, fInfoLevelId
, &dataW
, fSearchOp
, lpSearchFilter
, dwAdditionalFlags
);
1447 RtlFreeUnicodeString(&pathW
);
1448 if (handle
== INVALID_HANDLE_VALUE
) return handle
;
1450 dataA
= (WIN32_FIND_DATAA
*) lpFindFileData
;
1451 dataA
->dwFileAttributes
= dataW
.dwFileAttributes
;
1452 dataA
->ftCreationTime
= dataW
.ftCreationTime
;
1453 dataA
->ftLastAccessTime
= dataW
.ftLastAccessTime
;
1454 dataA
->ftLastWriteTime
= dataW
.ftLastWriteTime
;
1455 dataA
->nFileSizeHigh
= dataW
.nFileSizeHigh
;
1456 dataA
->nFileSizeLow
= dataW
.nFileSizeLow
;
1457 WideCharToMultiByte( CP_ACP
, 0, dataW
.cFileName
, -1,
1458 dataA
->cFileName
, sizeof(dataA
->cFileName
), NULL
, NULL
);
1459 WideCharToMultiByte( CP_ACP
, 0, dataW
.cAlternateFileName
, -1,
1460 dataA
->cAlternateFileName
, sizeof(dataA
->cAlternateFileName
), NULL
, NULL
);
1464 /*************************************************************************
1465 * FindFirstFileW (KERNEL32.@)
1467 HANDLE WINAPI
FindFirstFileW( LPCWSTR lpFileName
, WIN32_FIND_DATAW
*lpFindData
)
1469 return FindFirstFileExW(lpFileName
, FindExInfoStandard
, lpFindData
,
1470 FindExSearchNameMatch
, NULL
, 0);
1473 /*************************************************************************
1474 * FindNextFileW (KERNEL32.@)
1476 BOOL WINAPI
FindNextFileW( HANDLE handle
, WIN32_FIND_DATAW
*data
)
1478 FIND_FIRST_INFO
*info
;
1480 DWORD gle
= ERROR_NO_MORE_FILES
;
1482 if (handle
== INVALID_HANDLE_VALUE
)
1484 SetLastError( ERROR_INVALID_HANDLE
);
1487 info
= (FIND_FIRST_INFO
*) handle
;
1488 RtlEnterCriticalSection( &info
->cs
);
1489 if (info
->drive
== -1)
1491 ret
= SMB_FindNext( info
->u
.smb_dir
, data
);
1494 SMB_CloseDir( info
->u
.smb_dir
);
1495 HeapFree( GetProcessHeap(), 0, info
->path
);
1499 else if (!info
->path
|| !info
->u
.dos_dir
)
1503 else if (!DOSFS_FindNextEx( info
, data
))
1505 DOSFS_CloseDir( info
->u
.dos_dir
); info
->u
.dos_dir
= NULL
;
1506 HeapFree( GetProcessHeap(), 0, info
->path
);
1508 HeapFree( GetProcessHeap(), 0, info
->long_mask
);
1509 info
->long_mask
= NULL
;
1514 RtlLeaveCriticalSection( &info
->cs
);
1515 if( !ret
) SetLastError( gle
);
1520 /*************************************************************************
1521 * FindNextFileA (KERNEL32.@)
1523 BOOL WINAPI
FindNextFileA( HANDLE handle
, WIN32_FIND_DATAA
*data
)
1525 WIN32_FIND_DATAW dataW
;
1526 if (!FindNextFileW( handle
, &dataW
)) return FALSE
;
1527 data
->dwFileAttributes
= dataW
.dwFileAttributes
;
1528 data
->ftCreationTime
= dataW
.ftCreationTime
;
1529 data
->ftLastAccessTime
= dataW
.ftLastAccessTime
;
1530 data
->ftLastWriteTime
= dataW
.ftLastWriteTime
;
1531 data
->nFileSizeHigh
= dataW
.nFileSizeHigh
;
1532 data
->nFileSizeLow
= dataW
.nFileSizeLow
;
1533 WideCharToMultiByte( CP_ACP
, 0, dataW
.cFileName
, -1,
1534 data
->cFileName
, sizeof(data
->cFileName
), NULL
, NULL
);
1535 WideCharToMultiByte( CP_ACP
, 0, dataW
.cAlternateFileName
, -1,
1536 data
->cAlternateFileName
,
1537 sizeof(data
->cAlternateFileName
), NULL
, NULL
);
1541 /*************************************************************************
1542 * FindClose (KERNEL32.@)
1544 BOOL WINAPI
FindClose( HANDLE handle
)
1546 FIND_FIRST_INFO
*info
= (FIND_FIRST_INFO
*) handle
;
1548 if (handle
== INVALID_HANDLE_VALUE
) goto error
;
1552 RtlEnterCriticalSection( &info
->cs
);
1555 if (info
->u
.dos_dir
) DOSFS_CloseDir( info
->u
.dos_dir
);
1556 if (info
->path
) HeapFree( GetProcessHeap(), 0, info
->path
);
1557 if (info
->long_mask
) HeapFree( GetProcessHeap(), 0, info
->long_mask
);
1560 __EXCEPT(page_fault
)
1562 WARN("Illegal handle %p\n", handle
);
1563 SetLastError( ERROR_INVALID_HANDLE
);
1567 if (!info
) goto error
;
1568 RtlLeaveCriticalSection( &info
->cs
);
1569 RtlDeleteCriticalSection( &info
->cs
);
1570 HeapFree(GetProcessHeap(), 0, info
);
1574 SetLastError( ERROR_INVALID_HANDLE
);
1578 /***********************************************************************
1579 * MulDiv (KERNEL32.@)
1581 * Result of multiplication and division
1582 * -1: Overflow occurred or Divisor was 0
1589 #if SIZEOF_LONG_LONG >= 8
1592 if (!nDivisor
) return -1;
1594 /* We want to deal with a positive divisor to simplify the logic. */
1597 nMultiplicand
= - nMultiplicand
;
1598 nDivisor
= -nDivisor
;
1601 /* If the result is positive, we "add" to round. else, we subtract to round. */
1602 if ( ( (nMultiplicand
< 0) && (nMultiplier
< 0) ) ||
1603 ( (nMultiplicand
>= 0) && (nMultiplier
>= 0) ) )
1604 ret
= (((long long)nMultiplicand
* nMultiplier
) + (nDivisor
/2)) / nDivisor
;
1606 ret
= (((long long)nMultiplicand
* nMultiplier
) - (nDivisor
/2)) / nDivisor
;
1608 if ((ret
> 2147483647) || (ret
< -2147483647)) return -1;
1611 if (!nDivisor
) return -1;
1613 /* We want to deal with a positive divisor to simplify the logic. */
1616 nMultiplicand
= - nMultiplicand
;
1617 nDivisor
= -nDivisor
;
1620 /* If the result is positive, we "add" to round. else, we subtract to round. */
1621 if ( ( (nMultiplicand
< 0) && (nMultiplier
< 0) ) ||
1622 ( (nMultiplicand
>= 0) && (nMultiplier
>= 0) ) )
1623 return ((nMultiplicand
* nMultiplier
) + (nDivisor
/2)) / nDivisor
;
1625 return ((nMultiplicand
* nMultiplier
) - (nDivisor
/2)) / nDivisor
;
1631 /***********************************************************************
1632 * DosDateTimeToFileTime (KERNEL32.@)
1634 BOOL WINAPI
DosDateTimeToFileTime( WORD fatdate
, WORD fattime
, LPFILETIME ft
)
1639 time_t time1
, time2
;
1642 newtm
.tm_sec
= (fattime
& 0x1f) * 2;
1643 newtm
.tm_min
= (fattime
>> 5) & 0x3f;
1644 newtm
.tm_hour
= (fattime
>> 11);
1645 newtm
.tm_mday
= (fatdate
& 0x1f);
1646 newtm
.tm_mon
= ((fatdate
>> 5) & 0x0f) - 1;
1647 newtm
.tm_year
= (fatdate
>> 9) + 80;
1649 RtlSecondsSince1970ToTime( timegm(&newtm
), (LARGE_INTEGER
*)ft
);
1651 time1
= mktime(&newtm
);
1652 gtm
= gmtime(&time1
);
1653 time2
= mktime(gtm
);
1654 RtlSecondsSince1970ToTime( 2*time1
-time2
, (LARGE_INTEGER
*)ft
);
1660 /***********************************************************************
1661 * FileTimeToDosDateTime (KERNEL32.@)
1663 BOOL WINAPI
FileTimeToDosDateTime( const FILETIME
*ft
, LPWORD fatdate
,
1671 li
.u
.LowPart
= ft
->dwLowDateTime
;
1672 li
.u
.HighPart
= ft
->dwHighDateTime
;
1673 RtlTimeToSecondsSince1970( &li
, &t
);
1675 tm
= gmtime( &unixtime
);
1677 *fattime
= (tm
->tm_hour
<< 11) + (tm
->tm_min
<< 5) + (tm
->tm_sec
/ 2);
1679 *fatdate
= ((tm
->tm_year
- 80) << 9) + ((tm
->tm_mon
+ 1) << 5)
1685 /***********************************************************************
1686 * QueryDosDeviceA (KERNEL32.@)
1688 * returns array of strings terminated by \0, terminated by \0
1690 DWORD WINAPI
QueryDosDeviceA(LPCSTR devname
,LPSTR target
,DWORD bufsize
)
1692 DWORD ret
= 0, retW
;
1693 LPWSTR targetW
= (LPWSTR
)HeapAlloc(GetProcessHeap(),0,
1694 bufsize
* sizeof(WCHAR
));
1695 UNICODE_STRING devnameW
;
1697 if(devname
) RtlCreateUnicodeStringFromAsciiz(&devnameW
, devname
);
1698 else devnameW
.Buffer
= NULL
;
1700 retW
= QueryDosDeviceW(devnameW
.Buffer
, targetW
, bufsize
);
1702 ret
= WideCharToMultiByte(CP_ACP
, 0, targetW
, retW
, target
,
1703 bufsize
, NULL
, NULL
);
1705 RtlFreeUnicodeString(&devnameW
);
1706 if (targetW
) HeapFree(GetProcessHeap(),0,targetW
);
1711 /***********************************************************************
1712 * QueryDosDeviceW (KERNEL32.@)
1714 * returns array of strings terminated by \0, terminated by \0
1717 * - Win9x returns for all calls ERROR_INVALID_PARAMETER
1718 * - the returned devices for devname == NULL is far from complete
1719 * - its not checked that the returned device exist
1721 DWORD WINAPI
QueryDosDeviceW(LPCWSTR devname
,LPWSTR target
,DWORD bufsize
)
1723 const WCHAR
*pDev
, *pName
, *pNum
= NULL
;
1727 TRACE("(%s,...)\n", debugstr_w(devname
));
1729 /* return known MSDOS devices */
1732 static const WCHAR devices
[][5] = {{'A','U','X',0},
1733 {'C','O','M','1',0},
1734 {'C','O','M','2',0},
1735 {'L','P','T','1',0},
1737 for(i
=0; (i
< (sizeof(devices
)/sizeof(devices
[0]))); i
++) {
1738 DWORD len
= strlenW(devices
[i
]);
1739 if(target
&& (bufsize
>= ret
+ len
+ 2)) {
1740 strcpyW(target
+ret
, devices
[i
]);
1743 /* in this case WinXP returns 0 */
1744 FIXME("function return is wrong for WinXP!\n");
1745 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1749 /* append drives here */
1750 if(target
&& bufsize
> 0) target
[ret
++] = 0;
1751 FIXME("Returned list is not complete\n");
1754 /* In theory all that are possible and have been defined.
1755 * Now just those below, since mirc uses it to check for special files.
1757 * (It is more complex, and supports netmounted stuff, and \\.\ stuff,
1758 * but currently we just ignore that.)
1760 if (!strcmpiW(devname
, auxW
)) {
1765 } else if (!strcmpiW(devname
, nulW
)) {
1768 } else if (!strncmpiW(devname
, comW
, strlenW(comW
))) {
1771 pNum
= devname
+ strlenW(comW
);
1772 for(numsiz
=0; isdigitW(*(pNum
+numsiz
)); numsiz
++);
1773 if(*(pNum
+ numsiz
)) {
1774 SetLastError(ERROR_FILE_NOT_FOUND
);
1777 } else if (!strncmpiW(devname
, lptW
, strlenW(lptW
))) {
1780 pNum
= devname
+ strlenW(lptW
);
1781 for(numsiz
=0; isdigitW(*(pNum
+numsiz
)); numsiz
++);
1782 if(*(pNum
+ numsiz
)) {
1783 SetLastError(ERROR_FILE_NOT_FOUND
);
1787 /* This might be a DOS device we do not handle yet ... */
1788 FIXME("(%s) not detected as DOS device!\n",debugstr_w(devname
));
1790 /* Win9x set the error ERROR_INVALID_PARAMETER */
1791 SetLastError(ERROR_FILE_NOT_FOUND
);
1794 FIXME("device %s may not exist on this computer\n", debugstr_w(devname
));
1796 ret
= strlenW(pDev
) + strlenW(pName
) + numsiz
+ 2;
1797 if (ret
> bufsize
) ret
= 0;
1798 if (target
&& ret
) {
1799 strcpyW(target
,pDev
);
1800 strcatW(target
,pName
);
1801 if (pNum
) strcatW(target
,pNum
);