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>
36 #ifdef HAVE_SYS_IOCTL_H
37 #include <sys/ioctl.h>
44 #define NONAMELESSUNION
45 #define NONAMELESSSTRUCT
52 #include "wine/unicode.h"
53 #include "wine/winbase16.h"
59 #include "wine/server.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 */
159 static WINE_EXCEPTION_FILTER(page_fault
)
161 if (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION
)
162 return EXCEPTION_EXECUTE_HANDLER
;
163 return EXCEPTION_CONTINUE_SEARCH
;
167 /***********************************************************************
170 * Return 1 if Unix file 'name' is also a valid MS-DOS name
171 * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
172 * File name can be terminated by '\0', '\\' or '/'.
174 static int DOSFS_ValidDOSName( LPCWSTR name
, int ignore_case
)
176 static const char invalid_chars
[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS
;
177 const WCHAR
*p
= name
;
178 const char *invalid
= ignore_case
? (invalid_chars
+ 26) : invalid_chars
;
183 /* Check for "." and ".." */
186 /* All other names beginning with '.' are invalid */
187 return (IS_END_OF_NAME(*p
));
189 while (!IS_END_OF_NAME(*p
))
191 if (*p
< 256 && strchr( invalid
, (char)*p
)) return 0; /* Invalid char */
192 if (*p
== '.') break; /* Start of the extension */
193 if (++len
> 8) return 0; /* Name too long */
196 if (*p
!= '.') return 1; /* End of name */
198 if (IS_END_OF_NAME(*p
)) return 0; /* Empty extension not allowed */
200 while (!IS_END_OF_NAME(*p
))
202 if (*p
< 256 && strchr( invalid
, (char)*p
)) return 0; /* Invalid char */
203 if (*p
== '.') return 0; /* Second extension not allowed */
204 if (++len
> 3) return 0; /* Extension too long */
211 /***********************************************************************
212 * DOSFS_ToDosFCBFormat
214 * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
215 * expanding wild cards and converting to upper-case in the process.
216 * File name can be terminated by '\0', '\\' or '/'.
217 * Return FALSE if the name is not a valid DOS name.
218 * 'buffer' must be at least 12 characters long.
220 BOOL
DOSFS_ToDosFCBFormat( LPCWSTR name
, LPWSTR buffer
)
222 static const char invalid_chars
[] = INVALID_DOS_CHARS
;
226 /* Check for "." and ".." */
231 for(i
= 1; i
< 11; i
++) buffer
[i
] = ' ';
238 return (!*p
|| (*p
== '/') || (*p
== '\\'));
241 for (i
= 0; i
< 8; i
++)
258 if (*p
< 256 && strchr( invalid_chars
, (char)*p
)) return FALSE
;
259 buffer
[i
] = toupperW(*p
);
267 /* Skip all chars after wildcard up to first dot */
268 while (*p
&& (*p
!= '/') && (*p
!= '\\') && (*p
!= '.')) p
++;
272 /* Check if name too long */
273 if (*p
&& (*p
!= '/') && (*p
!= '\\') && (*p
!= '.')) return FALSE
;
275 if (*p
== '.') p
++; /* Skip dot */
277 for (i
= 8; i
< 11; i
++)
287 return FALSE
; /* Second extension not allowed */
295 if (*p
< 256 && strchr( invalid_chars
, (char)*p
)) return FALSE
;
296 buffer
[i
] = toupperW(*p
);
303 /* at most 3 character of the extension are processed
304 * is something behind this ?
306 while (*p
== '*' || *p
== ' ') p
++; /* skip wildcards and spaces */
307 return IS_END_OF_NAME(*p
);
311 /***********************************************************************
312 * DOSFS_ToDosDTAFormat
314 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
315 * converting to upper-case in the process.
316 * File name can be terminated by '\0', '\\' or '/'.
317 * 'buffer' must be at least 13 characters long.
319 static void DOSFS_ToDosDTAFormat( LPCWSTR name
, LPWSTR buffer
)
323 memcpy( buffer
, name
, 8 * sizeof(WCHAR
) );
325 while ((p
> buffer
) && (p
[-1] == ' ')) p
--;
327 memcpy( p
, name
+ 8, 3 * sizeof(WCHAR
) );
329 while (p
[-1] == ' ') p
--;
330 if (p
[-1] == '.') p
--;
335 /***********************************************************************
338 * Check a DOS file name against a mask (both in FCB format).
340 static int DOSFS_MatchShort( LPCWSTR mask
, LPCWSTR name
)
343 for (i
= 11; i
> 0; i
--, mask
++, name
++)
344 if ((*mask
!= '?') && (*mask
!= *name
)) return 0;
349 /***********************************************************************
352 * Check a long file name against a mask.
354 * Tests (done in W95 DOS shell - case insensitive):
355 * *.txt test1.test.txt *
357 * *.t??????.t* test1.ta.tornado.txt *
358 * *tornado* test1.ta.tornado.txt *
359 * t*t test1.ta.tornado.txt *
361 * ?est??? test1.txt -
362 * *test1.txt* test1.txt *
363 * h?l?o*t.dat hellothisisatest.dat *
365 static int DOSFS_MatchLong( LPCWSTR mask
, LPCWSTR name
, int case_sensitive
)
367 LPCWSTR lastjoker
= NULL
;
368 LPCWSTR next_to_retry
= NULL
;
369 static const WCHAR asterisk_dot_asterisk
[] = {'*','.','*',0};
371 TRACE("(%s, %s, %x)\n", debugstr_w(mask
), debugstr_w(name
), case_sensitive
);
373 if (!strcmpW( mask
, asterisk_dot_asterisk
)) return 1;
374 while (*name
&& *mask
)
379 while (*mask
== '*') mask
++; /* Skip consecutive '*' */
381 if (!*mask
) return 1; /* end of mask is all '*', so match */
383 /* skip to the next match after the joker(s) */
384 if (case_sensitive
) while (*name
&& (*name
!= *mask
)) name
++;
385 else while (*name
&& (toupperW(*name
) != toupperW(*mask
))) name
++;
388 next_to_retry
= name
;
390 else if (*mask
!= '?')
395 if (*mask
!= *name
) mismatch
= 1;
399 if (toupperW(*mask
) != toupperW(*name
)) mismatch
= 1;
413 else /* mismatch ! */
415 if (lastjoker
) /* we had an '*', so we can try unlimitedly */
419 /* this scan sequence was a mismatch, so restart
420 * 1 char after the first char we checked last time */
422 name
= next_to_retry
;
425 return 0; /* bad luck */
434 while ((*mask
== '.') || (*mask
== '*'))
435 mask
++; /* Ignore trailing '.' or '*' in mask */
436 return (!*name
&& !*mask
);
440 /***********************************************************************
443 * Used to construct an array of filenames in DOSFS_OpenDir
445 static BOOL
DOSFS_AddDirEntry(DOS_DIR
**dir
, LPCWSTR name
, LPCWSTR dosname
)
447 int extra1
= strlenW(name
) + 1;
448 int extra2
= strlenW(dosname
) + 1;
450 /* if we need more, at minimum double the size */
451 if( (extra1
+ extra2
+ (*dir
)->used
) > (*dir
)->size
)
453 int more
= (*dir
)->size
;
456 if(more
<(extra1
+extra2
))
457 more
= extra1
+extra2
;
459 t
= HeapReAlloc(GetProcessHeap(), 0, *dir
, sizeof(**dir
) +
460 ((*dir
)->size
+ more
)*sizeof(WCHAR
) );
463 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
464 ERR("Out of memory caching directory structure %d %d %d\n",
465 (*dir
)->size
, more
, (*dir
)->used
);
469 (*dir
)->size
+= more
;
472 /* at this point, the dir structure is big enough to hold these names */
473 strcpyW(&(*dir
)->names
[(*dir
)->used
], name
);
474 (*dir
)->used
+= extra1
;
475 strcpyW(&(*dir
)->names
[(*dir
)->used
], dosname
);
476 (*dir
)->used
+= extra2
;
482 /***********************************************************************
485 static BOOL
DOSFS_OpenDir_VFAT(UINT codepage
, DOS_DIR
**dir
, const char *unix_path
)
487 #ifdef VFAT_IOCTL_READDIR_BOTH
489 int fd
= open( unix_path
, O_RDONLY
|O_DIRECTORY
);
492 /* Check if the VFAT ioctl is supported on this directory */
499 WCHAR long_name
[MAX_PATH
];
500 WCHAR short_name
[12];
502 r
= (ioctl( fd
, VFAT_IOCTL_READDIR_BOTH
, (long)de
) != -1);
507 MultiByteToWideChar(codepage
, 0, de
[0].d_name
, -1, long_name
, MAX_PATH
);
508 if (!DOSFS_ToDosFCBFormat( long_name
, short_name
))
509 short_name
[0] = '\0';
511 MultiByteToWideChar(codepage
, 0, de
[1].d_name
, -1, long_name
, MAX_PATH
);
513 MultiByteToWideChar(codepage
, 0, de
[0].d_name
, -1, long_name
, MAX_PATH
);
514 r
= DOSFS_AddDirEntry(dir
, long_name
, short_name
);
520 static const WCHAR empty_strW
[] = { 0 };
521 DOSFS_AddDirEntry(dir
, empty_strW
, empty_strW
);
527 #endif /* VFAT_IOCTL_READDIR_BOTH */
531 /***********************************************************************
532 * DOSFS_OpenDir_Normal
534 * Now use the standard opendir/readdir interface
536 static BOOL
DOSFS_OpenDir_Normal( UINT codepage
, DOS_DIR
**dir
, const char *unix_path
)
538 DIR *unixdir
= opendir( unix_path
);
540 static const WCHAR empty_strW
[] = { 0 };
546 WCHAR long_name
[MAX_PATH
];
547 struct dirent
*de
= readdir(unixdir
);
551 MultiByteToWideChar(codepage
, 0, de
->d_name
, -1, long_name
, MAX_PATH
);
552 r
= DOSFS_AddDirEntry(dir
, long_name
, empty_strW
);
557 DOSFS_AddDirEntry(dir
, empty_strW
, empty_strW
);
562 /***********************************************************************
565 static DOS_DIR
*DOSFS_OpenDir( UINT codepage
, const char *unix_path
)
567 const int init_size
= 0x100;
568 DOS_DIR
*dir
= HeapAlloc( GetProcessHeap(), 0, sizeof(*dir
) + init_size
*sizeof (WCHAR
));
571 TRACE("%s\n",debugstr_a(unix_path
));
575 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
579 dir
->size
= init_size
;
581 /* Treat empty path as root directory. This simplifies path split into
582 directory and mask in several other places */
583 if (!*unix_path
) unix_path
= "/";
585 r
= DOSFS_OpenDir_VFAT( codepage
, &dir
, unix_path
);
588 r
= DOSFS_OpenDir_Normal( codepage
, &dir
, unix_path
);
592 HeapFree(GetProcessHeap(), 0, dir
);
601 /***********************************************************************
604 static void DOSFS_CloseDir( DOS_DIR
*dir
)
606 HeapFree( GetProcessHeap(), 0, dir
);
610 /***********************************************************************
613 static BOOL
DOSFS_ReadDir( DOS_DIR
*dir
, LPCWSTR
*long_name
,
614 LPCWSTR
*short_name
)
621 /* the long pathname is first */
622 ln
= &dir
->names
[dir
->used
];
627 dir
->used
+= (strlenW(ln
) + 1);
629 /* followed by the short path name */
630 sn
= &dir
->names
[dir
->used
];
635 dir
->used
+= (strlenW(sn
) + 1);
641 /***********************************************************************
644 * Transform a Unix file name into a hashed DOS name. If the name is a valid
645 * DOS name, it is converted to upper-case; otherwise it is replaced by a
646 * hashed version that fits in 8.3 format.
647 * File name can be terminated by '\0', '\\' or '/'.
648 * 'buffer' must be at least 13 characters long.
650 static void DOSFS_Hash( LPCWSTR name
, LPWSTR buffer
, BOOL dir_format
,
653 static const char invalid_chars
[] = INVALID_DOS_CHARS
"~.";
654 static const char hash_chars
[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
663 for(i
= 0; i
< 11; i
++) buffer
[i
] = ' ';
667 if (DOSFS_ValidDOSName( name
, ignore_case
))
669 /* Check for '.' and '..' */
673 if (!dir_format
) buffer
[1] = buffer
[2] = '\0';
674 if (name
[1] == '.') buffer
[1] = '.';
678 /* Simply copy the name, converting to uppercase */
680 for (dst
= buffer
; !IS_END_OF_NAME(*name
) && (*name
!= '.'); name
++)
681 *dst
++ = toupperW(*name
);
684 if (dir_format
) dst
= buffer
+ 8;
686 for (name
++; !IS_END_OF_NAME(*name
); name
++)
687 *dst
++ = toupperW(*name
);
689 if (!dir_format
) *dst
= '\0';
693 /* Compute the hash code of the file name */
694 /* If you know something about hash functions, feel free to */
695 /* insert a better algorithm here... */
698 for (p
= name
, hash
= 0xbeef; !IS_END_OF_NAME(p
[1]); p
++)
699 hash
= (hash
<<3) ^ (hash
>>5) ^ tolowerW(*p
) ^ (tolowerW(p
[1]) << 8);
700 hash
= (hash
<<3) ^ (hash
>>5) ^ tolowerW(*p
); /* Last character */
704 for (p
= name
, hash
= 0xbeef; !IS_END_OF_NAME(p
[1]); p
++)
705 hash
= (hash
<< 3) ^ (hash
>> 5) ^ *p
^ (p
[1] << 8);
706 hash
= (hash
<< 3) ^ (hash
>> 5) ^ *p
; /* Last character */
709 /* Find last dot for start of the extension */
710 for (p
= name
+1, ext
= NULL
; !IS_END_OF_NAME(*p
); p
++)
711 if (*p
== '.') ext
= p
;
712 if (ext
&& IS_END_OF_NAME(ext
[1]))
713 ext
= NULL
; /* Empty extension ignored */
715 /* Copy first 4 chars, replacing invalid chars with '_' */
716 for (i
= 4, p
= name
, dst
= buffer
; i
> 0; i
--, p
++)
718 if (IS_END_OF_NAME(*p
) || (p
== ext
)) break;
719 *dst
++ = (*p
< 256 && strchr( invalid_chars
, (char)*p
)) ? '_' : toupperW(*p
);
721 /* Pad to 5 chars with '~' */
722 while (i
-- >= 0) *dst
++ = '~';
724 /* Insert hash code converted to 3 ASCII chars */
725 *dst
++ = hash_chars
[(hash
>> 10) & 0x1f];
726 *dst
++ = hash_chars
[(hash
>> 5) & 0x1f];
727 *dst
++ = hash_chars
[hash
& 0x1f];
729 /* Copy the first 3 chars of the extension (if any) */
732 if (!dir_format
) *dst
++ = '.';
733 for (i
= 3, ext
++; (i
> 0) && !IS_END_OF_NAME(*ext
); i
--, ext
++)
734 *dst
++ = (*ext
< 256 && strchr( invalid_chars
, (char)*ext
)) ? '_' : toupperW(*ext
);
736 if (!dir_format
) *dst
= '\0';
740 /***********************************************************************
743 * Find the Unix file name in a given directory that corresponds to
744 * a file name (either in Unix or DOS format).
745 * File name can be terminated by '\0', '\\' or '/'.
746 * Return TRUE if OK, FALSE if no file name matches.
748 * 'long_buf' must be at least 'long_len' characters long. If the long name
749 * turns out to be larger than that, the function returns FALSE.
750 * 'short_buf' must be at least 13 characters long.
752 BOOL
DOSFS_FindUnixName( const DOS_FULL_NAME
*path
, LPCWSTR name
, char *long_buf
,
753 INT long_len
, LPWSTR short_buf
, BOOL ignore_case
)
756 LPCWSTR long_name
, short_name
;
757 WCHAR dos_name
[12], tmp_buf
[13];
760 LPCWSTR p
= strchrW( name
, '/' );
761 int len
= p
? (int)(p
- name
) : strlenW(name
);
762 if ((p
= strchrW( name
, '\\' ))) len
= min( (int)(p
- name
), len
);
763 /* Ignore trailing dots and spaces */
764 while (len
> 1 && (name
[len
-1] == '.' || name
[len
-1] == ' ')) len
--;
765 if (long_len
< len
+ 1) return FALSE
;
767 TRACE("%s,%s\n", path
->long_name
, debugstr_w(name
) );
769 if (!DOSFS_ToDosFCBFormat( name
, dos_name
)) dos_name
[0] = '\0';
771 if (!(dir
= DOSFS_OpenDir( DRIVE_GetCodepage(path
->drive
), path
->long_name
)))
773 WARN("(%s,%s): can't open dir: %s\n",
774 path
->long_name
, debugstr_w(name
), strerror(errno
) );
778 while ((ret
= DOSFS_ReadDir( dir
, &long_name
, &short_name
)))
780 /* Check against Unix name */
781 if (len
== strlenW(long_name
))
785 if (!strncmpW( long_name
, name
, len
)) break;
789 if (!strncmpiW( long_name
, name
, len
)) break;
794 /* Check against hashed DOS name */
797 DOSFS_Hash( long_name
, tmp_buf
, TRUE
, ignore_case
);
798 short_name
= tmp_buf
;
800 if (!strcmpW( dos_name
, short_name
)) break;
805 if (long_buf
) WideCharToMultiByte(DRIVE_GetCodepage(path
->drive
), 0,
806 long_name
, -1, long_buf
, long_len
, NULL
, NULL
);
810 DOSFS_ToDosDTAFormat( short_name
, short_buf
);
812 DOSFS_Hash( long_name
, short_buf
, FALSE
, ignore_case
);
814 TRACE("(%s,%s) -> %s (%s)\n", path
->long_name
, debugstr_w(name
),
815 debugstr_w(long_name
), short_buf
? debugstr_w(short_buf
) : "***");
818 WARN("%s not found in '%s'\n", debugstr_w(name
), path
->long_name
);
819 DOSFS_CloseDir( dir
);
824 /***********************************************************************
827 * Check if a DOS file name represents a DOS device and return the device.
829 const DOS_DEVICE
*DOSFS_GetDevice( LPCWSTR name
)
834 if (!name
) return NULL
; /* if wine_server_handle_to_fd was used */
835 if (name
[0] && (name
[1] == ':')) name
+= 2;
836 if ((p
= strrchrW( name
, '/' ))) name
= p
+ 1;
837 if ((p
= strrchrW( name
, '\\' ))) name
= p
+ 1;
838 for (i
= 0; i
< sizeof(DOSFS_Devices
)/sizeof(DOSFS_Devices
[0]); i
++)
840 const WCHAR
*dev
= DOSFS_Devices
[i
].name
;
841 if (!strncmpiW( dev
, name
, strlenW(dev
) ))
843 p
= name
+ strlenW( dev
);
844 if (!*p
|| (*p
== '.') || (*p
== ':')) return &DOSFS_Devices
[i
];
851 /***********************************************************************
852 * DOSFS_GetDeviceByHandle
854 const DOS_DEVICE
*DOSFS_GetDeviceByHandle( HANDLE hFile
)
856 const DOS_DEVICE
*ret
= NULL
;
857 SERVER_START_REQ( get_device_id
)
860 if (!wine_server_call( req
))
862 if ((reply
->id
>= 0) &&
863 (reply
->id
< sizeof(DOSFS_Devices
)/sizeof(DOSFS_Devices
[0])))
864 ret
= &DOSFS_Devices
[reply
->id
];
872 /**************************************************************************
873 * DOSFS_CreateCommPort
875 static HANDLE
DOSFS_CreateCommPort(LPCWSTR name
, DWORD access
, DWORD attributes
, LPSECURITY_ATTRIBUTES sa
)
880 OBJECT_ATTRIBUTES attr
;
881 UNICODE_STRING nameW
;
886 static const WCHAR serialportsW
[] = {'M','a','c','h','i','n','e','\\',
887 'S','o','f','t','w','a','r','e','\\',
888 'W','i','n','e','\\','W','i','n','e','\\',
889 'C','o','n','f','i','g','\\',
890 'S','e','r','i','a','l','P','o','r','t','s',0};
892 TRACE_(file
)("%s %lx %lx\n", debugstr_w(name
), access
, attributes
);
894 attr
.Length
= sizeof(attr
);
895 attr
.RootDirectory
= 0;
896 attr
.ObjectName
= &nameW
;
898 attr
.SecurityDescriptor
= NULL
;
899 attr
.SecurityQualityOfService
= NULL
;
900 RtlInitUnicodeString( &nameW
, serialportsW
);
902 if (NtOpenKey( &hkey
, KEY_ALL_ACCESS
, &attr
)) return 0;
904 RtlInitUnicodeString( &nameW
, name
);
905 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
906 devnameW
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
912 if (!devnameW
) return 0;
913 WideCharToMultiByte(CP_ACP
, 0, devnameW
, -1, devname
, sizeof(devname
), NULL
, NULL
);
915 TRACE("opening %s as %s\n", devname
, debugstr_w(name
));
917 SERVER_START_REQ( create_serial
)
919 req
->access
= access
;
920 req
->inherit
= (sa
&& (sa
->nLength
>=sizeof(*sa
)) && sa
->bInheritHandle
);
921 req
->attributes
= attributes
;
922 req
->sharing
= FILE_SHARE_READ
|FILE_SHARE_WRITE
;
923 wine_server_add_data( req
, devname
, strlen(devname
) );
925 wine_server_call_err( req
);
931 ERR("Couldn't open device '%s' ! (check permissions)\n",devname
);
933 TRACE("return %p\n", ret
);
937 /***********************************************************************
940 * Open a DOS device. This might not map 1:1 into the UNIX device concept.
941 * Returns 0 on failure.
943 HANDLE
DOSFS_OpenDevice( LPCWSTR name
, DWORD access
, DWORD attributes
, LPSECURITY_ATTRIBUTES sa
)
949 if (name
[0] && (name
[1] == ':')) name
+= 2;
950 if ((p
= strrchrW( name
, '/' ))) name
= p
+ 1;
951 if ((p
= strrchrW( name
, '\\' ))) name
= p
+ 1;
952 for (i
= 0; i
< sizeof(DOSFS_Devices
)/sizeof(DOSFS_Devices
[0]); i
++)
954 const WCHAR
*dev
= DOSFS_Devices
[i
].name
;
955 if (!strncmpiW( dev
, name
, strlenW(dev
) ))
957 p
= name
+ strlenW( dev
);
958 if (!*p
|| (*p
== '.') || (*p
== ':')) {
959 static const WCHAR nulW
[] = {'N','U','L',0};
960 static const WCHAR conW
[] = {'C','O','N',0};
961 static const WCHAR scsimgrW
[] = {'S','C','S','I','M','G','R','$',0};
962 static const WCHAR hpscanW
[] = {'H','P','S','C','A','N',0};
963 static const WCHAR emmxxxx0W
[] = {'E','M','M','X','X','X','X','0',0};
965 if (!strcmpiW(DOSFS_Devices
[i
].name
, nulW
))
966 return FILE_CreateFile( "/dev/null", access
,
967 FILE_SHARE_READ
|FILE_SHARE_WRITE
, sa
,
968 OPEN_EXISTING
, 0, 0, TRUE
, DRIVE_UNKNOWN
);
969 if (!strcmpiW(DOSFS_Devices
[i
].name
, conW
)) {
971 switch (access
& (GENERIC_READ
|GENERIC_WRITE
)) {
973 to_dup
= GetStdHandle( STD_INPUT_HANDLE
);
976 to_dup
= GetStdHandle( STD_OUTPUT_HANDLE
);
979 FIXME("can't open CON read/write\n");
982 if (!DuplicateHandle( GetCurrentProcess(), to_dup
, GetCurrentProcess(),
984 sa
&& (sa
->nLength
>=sizeof(*sa
)) && sa
->bInheritHandle
,
985 DUPLICATE_SAME_ACCESS
))
989 if (!strcmpiW(DOSFS_Devices
[i
].name
, scsimgrW
) ||
990 !strcmpiW(DOSFS_Devices
[i
].name
, hpscanW
) ||
991 !strcmpiW(DOSFS_Devices
[i
].name
, emmxxxx0W
))
993 return FILE_CreateDevice( i
, access
, sa
);
996 if( (handle
=DOSFS_CreateCommPort(DOSFS_Devices
[i
].name
,access
,attributes
,sa
)) )
998 FIXME("device open %s not supported (yet)\n", debugstr_w(DOSFS_Devices
[i
].name
));
1007 /***********************************************************************
1008 * DOSFS_GetPathDrive
1010 * Get the drive specified by a given path name (DOS or Unix format).
1012 static int DOSFS_GetPathDrive( LPCWSTR
*name
)
1017 if (*p
&& (p
[1] == ':'))
1019 drive
= toupperW(*p
) - 'A';
1022 else if (*p
== '/') /* Absolute Unix path? */
1024 if ((drive
= DRIVE_FindDriveRootW( name
)) == -1)
1026 MESSAGE("Warning: %s not accessible from a configured DOS drive\n", debugstr_w(*name
) );
1027 /* Assume it really was a DOS name */
1028 drive
= DRIVE_GetCurrentDrive();
1031 else drive
= DRIVE_GetCurrentDrive();
1033 if (!DRIVE_IsValid(drive
))
1035 SetLastError( ERROR_INVALID_DRIVE
);
1042 /***********************************************************************
1045 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
1046 * Unix name / short DOS name pair.
1047 * Return FALSE if one of the path components does not exist. The last path
1048 * component is only checked if 'check_last' is non-zero.
1049 * The buffers pointed to by 'long_buf' and 'short_buf' must be
1050 * at least MAX_PATHNAME_LEN long.
1052 BOOL
DOSFS_GetFullName( LPCWSTR name
, BOOL check_last
, DOS_FULL_NAME
*full
)
1055 UINT flags
, codepage
;
1058 static const WCHAR driveA_rootW
[] = {'A',':','\\',0};
1059 static const WCHAR dos_rootW
[] = {'\\',0};
1061 TRACE("%s (last=%d)\n", debugstr_w(name
), check_last
);
1063 if ((!*name
) || (*name
=='\n'))
1064 { /* error code for Win98 */
1065 SetLastError(ERROR_BAD_PATHNAME
);
1069 if ((full
->drive
= DOSFS_GetPathDrive( &name
)) == -1) return FALSE
;
1070 flags
= DRIVE_GetFlags( full
->drive
);
1071 codepage
= DRIVE_GetCodepage(full
->drive
);
1073 lstrcpynA( full
->long_name
, DRIVE_GetRoot( full
->drive
),
1074 sizeof(full
->long_name
) );
1075 if (full
->long_name
[1]) root
= full
->long_name
+ strlen(full
->long_name
);
1076 else root
= full
->long_name
; /* root directory */
1078 strcpyW( full
->short_name
, driveA_rootW
);
1079 full
->short_name
[0] += full
->drive
;
1081 if ((*name
== '\\') || (*name
== '/')) /* Absolute path */
1083 while ((*name
== '\\') || (*name
== '/')) name
++;
1085 else /* Relative path */
1087 lstrcpynA( root
+ 1, DRIVE_GetUnixCwd( full
->drive
),
1088 sizeof(full
->long_name
) - (root
- full
->long_name
) - 1 );
1089 if (root
[1]) *root
= '/';
1090 lstrcpynW( full
->short_name
+ 3, DRIVE_GetDosCwd( full
->drive
),
1091 sizeof(full
->short_name
)/sizeof(full
->short_name
[0]) - 3 );
1094 p_l
= full
->long_name
[1] ? full
->long_name
+ strlen(full
->long_name
)
1096 p_s
= full
->short_name
[3] ? full
->short_name
+ strlenW(full
->short_name
)
1097 : full
->short_name
+ 2;
1100 while (*name
&& found
)
1102 /* Check for '.' and '..' */
1106 if (IS_END_OF_NAME(name
[1]))
1109 while ((*name
== '\\') || (*name
== '/')) name
++;
1112 else if ((name
[1] == '.') && IS_END_OF_NAME(name
[2]))
1115 while ((*name
== '\\') || (*name
== '/')) name
++;
1116 while ((p_l
> root
) && (*p_l
!= '/')) p_l
--;
1117 while ((p_s
> full
->short_name
+ 2) && (*p_s
!= '\\')) p_s
--;
1118 *p_l
= *p_s
= '\0'; /* Remove trailing separator */
1123 /* Make sure buffers are large enough */
1125 if ((p_s
>= full
->short_name
+ sizeof(full
->short_name
)/sizeof(full
->short_name
[0]) - 14) ||
1126 (p_l
>= full
->long_name
+ sizeof(full
->long_name
) - 1))
1128 SetLastError( ERROR_PATH_NOT_FOUND
);
1132 /* Get the long and short name matching the file name */
1134 if ((found
= DOSFS_FindUnixName( full
, name
, p_l
+ 1,
1135 sizeof(full
->long_name
) - (p_l
- full
->long_name
) - 1,
1136 p_s
+ 1, !(flags
& DRIVE_CASE_SENSITIVE
) )))
1141 p_s
+= strlenW(p_s
);
1142 while (!IS_END_OF_NAME(*name
)) name
++;
1144 else if (!check_last
)
1148 while (!IS_END_OF_NAME(*name
) &&
1149 (p_s
< full
->short_name
+ sizeof(full
->short_name
)/sizeof(full
->short_name
[0]) - 1) &&
1150 (p_l
< full
->long_name
+ sizeof(full
->long_name
) - 1))
1153 *p_s
++ = tolowerW(*name
);
1154 /* If the drive is case-sensitive we want to create new */
1155 /* files in lower-case otherwise we can't reopen them */
1156 /* under the same short name. */
1157 if (flags
& DRIVE_CASE_SENSITIVE
) wch
= tolowerW(*name
);
1159 p_l
+= WideCharToMultiByte(codepage
, 0, &wch
, 1, p_l
, 2, NULL
, NULL
);
1162 /* Ignore trailing dots and spaces */
1163 while(p_l
[-1] == '.' || p_l
[-1] == ' ') {
1170 while ((*name
== '\\') || (*name
== '/')) name
++;
1177 SetLastError( ERROR_FILE_NOT_FOUND
);
1180 if (*name
) /* Not last */
1182 SetLastError( ERROR_PATH_NOT_FOUND
);
1186 if (!full
->long_name
[0]) strcpy( full
->long_name
, "/" );
1187 if (!full
->short_name
[2]) strcpyW( full
->short_name
+ 2, dos_rootW
);
1188 TRACE("returning %s = %s\n", full
->long_name
, debugstr_w(full
->short_name
) );
1193 /***********************************************************************
1194 * GetShortPathNameW (KERNEL32.@)
1198 * longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1199 * longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0
1201 * more observations ( with NT 3.51 (WinDD) ):
1202 * longpath <= 8.3 -> just copy longpath to shortpath
1204 * a) file does not exist -> return 0, LastError = ERROR_FILE_NOT_FOUND
1205 * b) file does exist -> set the short filename.
1206 * - trailing slashes are reproduced in the short name, even if the
1207 * file is not a directory
1208 * - the absolute/relative path of the short name is reproduced like found
1210 * - longpath and shortpath may have the same address
1211 * Peter Ganten, 1999
1213 DWORD WINAPI
GetShortPathNameW( LPCWSTR longpath
, LPWSTR shortpath
, DWORD shortlen
)
1215 DOS_FULL_NAME full_name
;
1216 WCHAR tmpshortpath
[MAX_PATHNAME_LEN
];
1218 DWORD sp
= 0, lp
= 0;
1222 BOOL unixabsolute
= *longpath
== '/';
1224 TRACE("%s\n", debugstr_w(longpath
));
1227 SetLastError(ERROR_INVALID_PARAMETER
);
1231 SetLastError(ERROR_BAD_PATHNAME
);
1235 /* check for drive letter */
1236 if (!unixabsolute
&& longpath
[1] == ':' ) {
1237 tmpshortpath
[0] = longpath
[0];
1238 tmpshortpath
[1] = ':';
1242 if ( ( drive
= DOSFS_GetPathDrive ( &longpath
)) == -1 ) return 0;
1243 flags
= DRIVE_GetFlags ( drive
);
1245 if (unixabsolute
&& drive
!= DRIVE_GetCurrentDrive()) {
1246 tmpshortpath
[0] = drive
+ 'A';
1247 tmpshortpath
[1] = ':';
1251 while ( longpath
[lp
] ) {
1253 /* check for path delimiters and reproduce them */
1254 if ( longpath
[lp
] == '\\' || longpath
[lp
] == '/' ) {
1255 if (!sp
|| tmpshortpath
[sp
-1]!= '\\')
1257 /* strip double "\\" */
1258 tmpshortpath
[sp
] = '\\';
1261 tmpshortpath
[sp
]=0;/*terminate string*/
1267 for(p
= longpath
+ lp
; *p
&& *p
!= '/' && *p
!= '\\'; p
++)
1269 lstrcpynW(tmpshortpath
+ sp
, longpath
+ lp
, tmplen
+ 1);
1271 /* Check, if the current element is a valid dos name */
1272 if ( DOSFS_ValidDOSName ( longpath
+ lp
, !(flags
& DRIVE_CASE_SENSITIVE
) ) ) {
1278 /* Check if the file exists and use the existing file name */
1279 if ( DOSFS_GetFullName ( tmpshortpath
, TRUE
, &full_name
) ) {
1280 strcpyW(tmpshortpath
+ sp
, strrchrW(full_name
.short_name
, '\\') + 1);
1281 sp
+= strlenW(tmpshortpath
+ sp
);
1286 TRACE("not found!\n" );
1287 SetLastError ( ERROR_FILE_NOT_FOUND
);
1290 tmpshortpath
[sp
] = 0;
1292 tmplen
= strlenW(tmpshortpath
) + 1;
1293 if (tmplen
<= shortlen
)
1295 strcpyW(shortpath
, tmpshortpath
);
1296 TRACE("returning %s\n", debugstr_w(shortpath
));
1297 tmplen
--; /* length without 0 */
1304 /***********************************************************************
1305 * GetShortPathNameA (KERNEL32.@)
1307 DWORD WINAPI
GetShortPathNameA( LPCSTR longpath
, LPSTR shortpath
, DWORD shortlen
)
1309 UNICODE_STRING longpathW
;
1310 WCHAR shortpathW
[MAX_PATH
];
1315 SetLastError(ERROR_INVALID_PARAMETER
);
1319 TRACE("%s\n", debugstr_a(longpath
));
1321 if (!RtlCreateUnicodeStringFromAsciiz(&longpathW
, longpath
))
1323 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1327 retW
= GetShortPathNameW(longpathW
.Buffer
, shortpathW
, MAX_PATH
);
1331 else if (retW
> MAX_PATH
)
1333 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
1338 ret
= WideCharToMultiByte(CP_ACP
, 0, shortpathW
, -1, NULL
, 0, NULL
, NULL
);
1339 if (ret
<= shortlen
)
1341 WideCharToMultiByte(CP_ACP
, 0, shortpathW
, -1, shortpath
, shortlen
, NULL
, NULL
);
1342 ret
--; /* length without 0 */
1346 RtlFreeUnicodeString(&longpathW
);
1351 /***********************************************************************
1352 * GetLongPathNameW (KERNEL32.@)
1355 * observed (Win2000):
1356 * shortpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1357 * shortpath="": LastError=ERROR_PATH_NOT_FOUND, ret=0
1359 DWORD WINAPI
GetLongPathNameW( LPCWSTR shortpath
, LPWSTR longpath
, DWORD longlen
)
1361 DOS_FULL_NAME full_name
;
1369 SetLastError(ERROR_INVALID_PARAMETER
);
1372 if (!shortpath
[0]) {
1373 SetLastError(ERROR_PATH_NOT_FOUND
);
1377 TRACE("%s,%p,%ld\n", debugstr_w(shortpath
), longpath
, longlen
);
1379 if(shortpath
[0]=='\\' && shortpath
[1]=='\\')
1381 ERR("UNC pathname %s\n",debugstr_w(shortpath
));
1382 lstrcpynW( longpath
, full_name
.short_name
, longlen
);
1383 return strlenW(longpath
);
1386 if (!DOSFS_GetFullName( shortpath
, TRUE
, &full_name
)) return 0;
1388 root
= full_name
.long_name
;
1389 drive
= DRIVE_FindDriveRoot(&root
);
1390 codepage
= DRIVE_GetCodepage(drive
);
1392 ret
= MultiByteToWideChar(codepage
, 0, root
, -1, NULL
, 0);
1394 /* reproduce terminating slash */
1395 if (ret
> 4) /* if not drive root */
1397 len
= strlenW(shortpath
);
1398 if (shortpath
[len
- 1] == '\\' || shortpath
[len
- 1] == '/')
1404 longpath
[0] = 'A' + drive
;
1406 MultiByteToWideChar(codepage
, 0, root
, -1, longpath
+ 2, longlen
- 2);
1407 for (p
= longpath
; *p
; p
++) if (*p
== '/') *p
= '\\';
1410 longpath
[ret
- 2] = '\\';
1411 longpath
[ret
- 1] = 0;
1413 TRACE("returning %s\n", debugstr_w(longpath
));
1414 ret
--; /* length without 0 */
1420 /***********************************************************************
1421 * GetLongPathNameA (KERNEL32.@)
1423 DWORD WINAPI
GetLongPathNameA( LPCSTR shortpath
, LPSTR longpath
, DWORD longlen
)
1425 UNICODE_STRING shortpathW
;
1426 WCHAR longpathW
[MAX_PATH
];
1431 SetLastError(ERROR_INVALID_PARAMETER
);
1435 TRACE("%s\n", debugstr_a(shortpath
));
1437 if (!RtlCreateUnicodeStringFromAsciiz(&shortpathW
, shortpath
))
1439 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1443 retW
= GetLongPathNameW(shortpathW
.Buffer
, longpathW
, MAX_PATH
);
1447 else if (retW
> MAX_PATH
)
1449 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
1454 ret
= WideCharToMultiByte(CP_ACP
, 0, longpathW
, -1, NULL
, 0, NULL
, NULL
);
1457 WideCharToMultiByte(CP_ACP
, 0, longpathW
, -1, longpath
, longlen
, NULL
, NULL
);
1458 ret
--; /* length without 0 */
1462 RtlFreeUnicodeString(&shortpathW
);
1467 /***********************************************************************
1468 * DOSFS_DoGetFullPathName
1470 * Implementation of GetFullPathNameA/W.
1472 * bon@elektron 000331:
1473 * A test for GetFullPathName with many pathological cases
1474 * now gives identical output for Wine and OSR2
1476 static DWORD
DOSFS_DoGetFullPathName( LPCWSTR name
, DWORD len
, LPWSTR result
)
1479 DOS_FULL_NAME full_name
;
1483 WCHAR drivecur
[] = {'C',':','.',0};
1484 WCHAR driveletter
=0;
1485 int namelen
,drive
=0;
1486 static const WCHAR bkslashW
[] = {'\\',0};
1487 static const WCHAR dotW
[] = {'.',0};
1488 static const WCHAR updir_slashW
[] = {'\\','.','.','\\',0};
1489 static const WCHAR curdirW
[] = {'\\','.','\\',0};
1490 static const WCHAR updirW
[] = {'\\','.','.',0};
1494 SetLastError(ERROR_BAD_PATHNAME
);
1498 TRACE("passed %s\n", debugstr_w(name
));
1501 /*drive letter given */
1503 driveletter
= name
[0];
1505 if ((name
[1]==':') && ((name
[2]=='\\') || (name
[2]=='/')))
1506 /*absolute path given */
1508 strncpyW(full_name
.short_name
, name
, MAX_PATHNAME_LEN
);
1509 full_name
.short_name
[MAX_PATHNAME_LEN
- 1] = 0; /* ensure 0 termination */
1510 drive
= toupperW(name
[0]) - 'A';
1515 drivecur
[0]=driveletter
;
1516 else if ((name
[0]=='\\') || (name
[0]=='/'))
1517 strcpyW(drivecur
, bkslashW
);
1519 strcpyW(drivecur
, dotW
);
1521 if (!DOSFS_GetFullName( drivecur
, FALSE
, &full_name
))
1523 FIXME("internal: error getting drive/path\n");
1526 /* find path that drive letter substitutes*/
1527 drive
= toupperW(full_name
.short_name
[0]) - 'A';
1528 root
= DRIVE_GetRoot(drive
);
1531 FIXME("internal: error getting DOS Drive Root\n");
1534 if (!strcmp(root
,"/"))
1536 /* we have just the last / and we need it. */
1537 p_l
= full_name
.long_name
;
1541 p_l
= full_name
.long_name
+ strlen(root
);
1543 /* append long name (= unix name) to drive */
1544 MultiByteToWideChar(DRIVE_GetCodepage(drive
), 0, p_l
, -1,
1545 full_name
.short_name
+ 2, MAX_PATHNAME_LEN
- 3);
1546 /* append name to treat */
1547 namelen
= strlenW(full_name
.short_name
);
1550 p
+= 2; /* skip drive name when appending */
1551 if (namelen
+ 2 + strlenW(p
) > MAX_PATHNAME_LEN
)
1553 FIXME("internal error: buffer too small\n");
1556 full_name
.short_name
[namelen
++] ='\\';
1557 full_name
.short_name
[namelen
] = 0;
1558 strncpyW(full_name
.short_name
+ namelen
, p
, MAX_PATHNAME_LEN
- namelen
);
1559 full_name
.short_name
[MAX_PATHNAME_LEN
- 1] = 0; /* ensure 0 termination */
1561 /* reverse all slashes */
1562 for (p
=full_name
.short_name
;
1563 p
< full_name
.short_name
+ strlenW(full_name
.short_name
);
1569 /* Use memmove, as areas overlap */
1571 while ((p
= strstrW(full_name
.short_name
, updir_slashW
)))
1573 if (p
> full_name
.short_name
+2)
1576 q
= strrchrW(full_name
.short_name
, '\\');
1577 memmove(q
+1, p
+4, (strlenW(p
+4)+1) * sizeof(WCHAR
));
1581 memmove(full_name
.short_name
+3, p
+4, (strlenW(p
+4)+1) * sizeof(WCHAR
));
1584 if ((full_name
.short_name
[2]=='.')&&(full_name
.short_name
[3]=='.'))
1586 /* This case istn't treated yet : c:..\test */
1587 memmove(full_name
.short_name
+2,full_name
.short_name
+4,
1588 (strlenW(full_name
.short_name
+4)+1) * sizeof(WCHAR
));
1591 while ((p
= strstrW(full_name
.short_name
, curdirW
)))
1594 memmove(p
+1, p
+3, (strlenW(p
+3)+1) * sizeof(WCHAR
));
1596 if (!(DRIVE_GetFlags(drive
) & DRIVE_CASE_PRESERVING
))
1597 for (p
= full_name
.short_name
; *p
; p
++) *p
= toupperW(*p
);
1598 namelen
= strlenW(full_name
.short_name
);
1599 if (!strcmpW(full_name
.short_name
+namelen
-3, updirW
))
1601 /* one more strange case: "c:\test\test1\.."
1603 *(full_name
.short_name
+namelen
-3)=0;
1604 q
= strrchrW(full_name
.short_name
, '\\');
1607 if (full_name
.short_name
[namelen
-1]=='.')
1608 full_name
.short_name
[(namelen
--)-1] =0;
1610 if (full_name
.short_name
[namelen
-1]=='\\')
1611 full_name
.short_name
[(namelen
--)-1] =0;
1612 TRACE("got %s\n", debugstr_w(full_name
.short_name
));
1614 /* If the lpBuffer buffer is too small, the return value is the
1615 size of the buffer, in characters, required to hold the path
1616 plus the terminating \0 (tested against win95osr2, bon 001118)
1618 ret
= strlenW(full_name
.short_name
);
1621 /* don't touch anything when the buffer is not large enough */
1622 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
1627 strncpyW( result
, full_name
.short_name
, len
);
1628 result
[len
- 1] = 0; /* ensure 0 termination */
1631 TRACE("returning %s\n", debugstr_w(full_name
.short_name
) );
1636 /***********************************************************************
1637 * GetFullPathNameA (KERNEL32.@)
1639 * if the path closed with '\', *lastpart is 0
1641 DWORD WINAPI
GetFullPathNameA( LPCSTR name
, DWORD len
, LPSTR buffer
,
1644 UNICODE_STRING nameW
;
1645 WCHAR bufferW
[MAX_PATH
];
1650 SetLastError(ERROR_INVALID_PARAMETER
);
1654 if (!RtlCreateUnicodeStringFromAsciiz(&nameW
, name
))
1656 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1660 retW
= GetFullPathNameW( nameW
.Buffer
, MAX_PATH
, bufferW
, NULL
);
1664 else if (retW
> MAX_PATH
)
1666 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
1671 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, -1, NULL
, 0, NULL
, NULL
);
1674 WideCharToMultiByte(CP_ACP
, 0, bufferW
, -1, buffer
, len
, NULL
, NULL
);
1675 ret
--; /* length without 0 */
1679 LPSTR p
= buffer
+ strlen(buffer
);
1683 while ((p
> buffer
+ 2) && (*p
!= '\\')) p
--;
1686 else *lastpart
= NULL
;
1691 RtlFreeUnicodeString(&nameW
);
1696 /***********************************************************************
1697 * GetFullPathNameW (KERNEL32.@)
1699 DWORD WINAPI
GetFullPathNameW( LPCWSTR name
, DWORD len
, LPWSTR buffer
,
1702 DWORD ret
= DOSFS_DoGetFullPathName( name
, len
, buffer
);
1703 if (ret
&& (ret
<=len
) && buffer
&& lastpart
)
1705 LPWSTR p
= buffer
+ strlenW(buffer
);
1706 if (*p
!= (WCHAR
)'\\')
1708 while ((p
> buffer
+ 2) && (*p
!= (WCHAR
)'\\')) p
--;
1711 else *lastpart
= NULL
;
1717 /***********************************************************************
1718 * wine_get_unix_file_name (KERNEL32.@) Not a Windows API
1720 * Return the full Unix file name for a given path.
1721 * FIXME: convert dos file name to unicode
1723 BOOL WINAPI
wine_get_unix_file_name( LPCSTR dos
, LPSTR buffer
, DWORD len
)
1727 WCHAR dosW
[MAX_PATHNAME_LEN
];
1729 MultiByteToWideChar(CP_ACP
, 0, dos
, -1, dosW
, MAX_PATHNAME_LEN
);
1730 ret
= DOSFS_GetFullName( dosW
, FALSE
, &path
);
1733 strncpy( buffer
, path
.long_name
, len
);
1734 buffer
[len
- 1] = 0; /* ensure 0 termination */
1740 /***********************************************************************
1741 * get_show_dir_symlinks_option
1743 static BOOL
get_show_dir_symlinks_option(void)
1745 static const WCHAR WineW
[] = {'M','a','c','h','i','n','e','\\',
1746 'S','o','f','t','w','a','r','e','\\',
1747 'W','i','n','e','\\','W','i','n','e','\\',
1748 'C','o','n','f','i','g','\\','W','i','n','e',0};
1749 static const WCHAR ShowDirSymlinksW
[] = {'S','h','o','w','D','i','r','S','y','m','l','i','n','k','s',0};
1754 OBJECT_ATTRIBUTES attr
;
1755 UNICODE_STRING nameW
;
1758 attr
.Length
= sizeof(attr
);
1759 attr
.RootDirectory
= 0;
1760 attr
.ObjectName
= &nameW
;
1761 attr
.Attributes
= 0;
1762 attr
.SecurityDescriptor
= NULL
;
1763 attr
.SecurityQualityOfService
= NULL
;
1764 RtlInitUnicodeString( &nameW
, WineW
);
1766 if (!NtOpenKey( &hkey
, KEY_ALL_ACCESS
, &attr
))
1768 RtlInitUnicodeString( &nameW
, ShowDirSymlinksW
);
1769 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
1771 WCHAR
*str
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
1772 ret
= IS_OPTION_TRUE( str
[0] );
1780 /***********************************************************************
1783 static int DOSFS_FindNextEx( FIND_FIRST_INFO
*info
, WIN32_FIND_DATAW
*entry
)
1785 DWORD attr
= info
->attr
| FA_UNUSED
| FA_ARCHIVE
| FA_RDONLY
;
1786 UINT flags
= DRIVE_GetFlags( info
->drive
);
1787 char *p
, buffer
[MAX_PATHNAME_LEN
];
1788 const char *drive_path
;
1790 LPCWSTR long_name
, short_name
;
1791 BY_HANDLE_FILE_INFORMATION fileinfo
;
1795 if ((info
->attr
& ~(FA_UNUSED
| FA_ARCHIVE
| FA_RDONLY
)) == FA_LABEL
)
1797 if (info
->cur_pos
) return 0;
1798 entry
->dwFileAttributes
= FILE_ATTRIBUTE_LABEL
;
1799 RtlSecondsSince1970ToTime( (time_t)0, (LARGE_INTEGER
*)&entry
->ftCreationTime
);
1800 RtlSecondsSince1970ToTime( (time_t)0, (LARGE_INTEGER
*)&entry
->ftLastAccessTime
);
1801 RtlSecondsSince1970ToTime( (time_t)0, (LARGE_INTEGER
*)&entry
->ftLastWriteTime
);
1802 entry
->nFileSizeHigh
= 0;
1803 entry
->nFileSizeLow
= 0;
1804 entry
->dwReserved0
= 0;
1805 entry
->dwReserved1
= 0;
1806 DOSFS_ToDosDTAFormat( DRIVE_GetLabel( info
->drive
), entry
->cFileName
);
1807 strcpyW( entry
->cAlternateFileName
, entry
->cFileName
);
1809 TRACE("returning %s (%s) as label\n",
1810 debugstr_w(entry
->cFileName
), debugstr_w(entry
->cAlternateFileName
));
1814 drive_path
= info
->path
+ strlen(DRIVE_GetRoot( info
->drive
));
1815 while ((*drive_path
== '/') || (*drive_path
== '\\')) drive_path
++;
1816 drive_root
= !*drive_path
;
1818 lstrcpynA( buffer
, info
->path
, sizeof(buffer
) - 1 );
1819 strcat( buffer
, "/" );
1820 p
= buffer
+ strlen(buffer
);
1822 while (DOSFS_ReadDir( info
->u
.dos_dir
, &long_name
, &short_name
))
1826 /* Don't return '.' and '..' in the root of the drive */
1827 if (drive_root
&& (long_name
[0] == '.') &&
1828 (!long_name
[1] || ((long_name
[1] == '.') && !long_name
[2])))
1831 /* Check the long mask */
1833 if (info
->long_mask
&& *info
->long_mask
)
1835 if (!DOSFS_MatchLong( info
->long_mask
, long_name
,
1836 flags
& DRIVE_CASE_SENSITIVE
)) continue;
1839 /* Check the short mask */
1841 if (info
->short_mask
)
1845 DOSFS_Hash( long_name
, dos_name
, TRUE
,
1846 !(flags
& DRIVE_CASE_SENSITIVE
) );
1847 short_name
= dos_name
;
1849 if (!DOSFS_MatchShort( info
->short_mask
, short_name
)) continue;
1852 /* Check the file attributes */
1853 WideCharToMultiByte(DRIVE_GetCodepage(info
->drive
), 0, long_name
, -1,
1854 p
, sizeof(buffer
) - (int)(p
- buffer
), NULL
, NULL
);
1855 if (!FILE_Stat( buffer
, &fileinfo
, &is_symlink
))
1857 WARN("can't stat %s\n", buffer
);
1860 if (is_symlink
&& (fileinfo
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
))
1862 static int show_dir_symlinks
= -1;
1863 if (show_dir_symlinks
== -1)
1864 show_dir_symlinks
= get_show_dir_symlinks_option();
1865 if (!show_dir_symlinks
) continue;
1868 if (fileinfo
.dwFileAttributes
& ~attr
) continue;
1870 /* We now have a matching entry; fill the result and return */
1872 entry
->dwFileAttributes
= fileinfo
.dwFileAttributes
;
1873 entry
->ftCreationTime
= fileinfo
.ftCreationTime
;
1874 entry
->ftLastAccessTime
= fileinfo
.ftLastAccessTime
;
1875 entry
->ftLastWriteTime
= fileinfo
.ftLastWriteTime
;
1876 entry
->nFileSizeHigh
= fileinfo
.nFileSizeHigh
;
1877 entry
->nFileSizeLow
= fileinfo
.nFileSizeLow
;
1880 DOSFS_ToDosDTAFormat( short_name
, entry
->cAlternateFileName
);
1882 DOSFS_Hash( long_name
, entry
->cAlternateFileName
, FALSE
,
1883 !(flags
& DRIVE_CASE_SENSITIVE
) );
1885 lstrcpynW( entry
->cFileName
, long_name
, sizeof(entry
->cFileName
)/sizeof(entry
->cFileName
[0]) );
1886 if (!(flags
& DRIVE_CASE_PRESERVING
)) strlwrW( entry
->cFileName
);
1887 TRACE("returning %s (%s) %02lx %ld\n",
1888 debugstr_w(entry
->cFileName
), debugstr_w(entry
->cAlternateFileName
),
1889 entry
->dwFileAttributes
, entry
->nFileSizeLow
);
1892 return 0; /* End of directory */
1895 /***********************************************************************
1898 * Find the next matching file. Return the number of entries read to find
1899 * the matching one, or 0 if no more entries.
1900 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
1901 * file name mask. Either or both can be NULL.
1903 * NOTE: This is supposed to be only called by the int21 emulation
1904 * routines, and so assumes that the Win16Mutex is held to
1905 * protect the static directory cache.
1907 int DOSFS_FindNext( const char *path
, const char *short_mask
,
1908 const char *long_mask
, int drive
, BYTE attr
,
1909 int skip
, WIN32_FIND_DATAA
*entry
)
1911 static FIND_FIRST_INFO info
;
1912 LPCWSTR short_name
, long_name
;
1914 UNICODE_STRING short_maskW
, long_maskW
;
1915 WIN32_FIND_DATAW entryW
;
1917 TRACE("(%s, %s, %s, %x, %x, %x, %p)\n", debugstr_a(path
),
1918 debugstr_a(short_mask
), debugstr_a(long_mask
), drive
, attr
, skip
,
1921 RtlCreateUnicodeStringFromAsciiz(&short_maskW
, short_mask
);
1922 RtlCreateUnicodeStringFromAsciiz(&long_maskW
, long_mask
);
1924 /* Check the cached directory */
1925 if (!(info
.u
.dos_dir
&& info
.path
== path
&& !strcmpW(info
.short_mask
, short_maskW
.Buffer
)
1926 && !strcmpW(info
.long_mask
, long_maskW
.Buffer
) && info
.drive
== drive
1927 && info
.attr
== attr
&& info
.cur_pos
<= skip
))
1929 /* Not in the cache, open it anew */
1930 if (info
.u
.dos_dir
) DOSFS_CloseDir( info
.u
.dos_dir
);
1932 info
.path
= (LPSTR
)path
;
1933 RtlFreeHeap(GetProcessHeap(), 0, info
.long_mask
);
1934 RtlFreeHeap(GetProcessHeap(), 0, info
.short_mask
);
1935 info
.long_mask
= long_maskW
.Buffer
;
1936 info
.short_mask
= short_maskW
.Buffer
;
1940 info
.u
.dos_dir
= DOSFS_OpenDir( DRIVE_GetCodepage(drive
), info
.path
);
1944 RtlFreeUnicodeString(&short_maskW
);
1945 RtlFreeUnicodeString(&long_maskW
);
1948 /* Skip to desired position */
1949 while (info
.cur_pos
< skip
)
1950 if (info
.u
.dos_dir
&& DOSFS_ReadDir( info
.u
.dos_dir
, &long_name
, &short_name
))
1955 if (info
.u
.dos_dir
&& info
.cur_pos
== skip
&& DOSFS_FindNextEx( &info
, &entryW
))
1957 WideCharToMultiByte(CP_ACP
, 0, entryW
.cFileName
, -1,
1958 entry
->cFileName
, sizeof(entry
->cFileName
), NULL
, NULL
);
1959 WideCharToMultiByte(CP_ACP
, 0, entryW
.cAlternateFileName
, -1,
1960 entry
->cAlternateFileName
, sizeof(entry
->cAlternateFileName
), NULL
, NULL
);
1961 count
= info
.cur_pos
- skip
;
1963 entry
->dwFileAttributes
= entryW
.dwFileAttributes
;
1964 entry
->nFileSizeHigh
= entryW
.nFileSizeHigh
;
1965 entry
->nFileSizeLow
= entryW
.nFileSizeLow
;
1966 entry
->ftCreationTime
= entryW
.ftCreationTime
;
1967 entry
->ftLastAccessTime
= entryW
.ftLastAccessTime
;
1968 entry
->ftLastWriteTime
= entryW
.ftLastWriteTime
;
1976 if (info
.u
.dos_dir
) DOSFS_CloseDir( info
.u
.dos_dir
);
1977 memset( &info
, '\0', sizeof(info
) );
1983 /*************************************************************************
1984 * FindFirstFileExW (KERNEL32.@)
1986 HANDLE WINAPI
FindFirstFileExW(
1988 FINDEX_INFO_LEVELS fInfoLevelId
,
1989 LPVOID lpFindFileData
,
1990 FINDEX_SEARCH_OPS fSearchOp
,
1991 LPVOID lpSearchFilter
,
1992 DWORD dwAdditionalFlags
)
1994 FIND_FIRST_INFO
*info
;
1998 SetLastError(ERROR_PATH_NOT_FOUND
);
1999 return INVALID_HANDLE_VALUE
;
2002 if ((fSearchOp
!= FindExSearchNameMatch
) || (dwAdditionalFlags
!= 0))
2004 FIXME("options not implemented 0x%08x 0x%08lx\n", fSearchOp
, dwAdditionalFlags
);
2005 return INVALID_HANDLE_VALUE
;
2008 switch(fInfoLevelId
)
2010 case FindExInfoStandard
:
2012 WIN32_FIND_DATAW
* data
= (WIN32_FIND_DATAW
*) lpFindFileData
;
2017 data
->dwReserved0
= data
->dwReserved1
= 0x0;
2018 if (lpFileName
[0] == '\\' && lpFileName
[1] == '\\')
2020 ERR("UNC path name\n");
2021 if (!(info
= HeapAlloc( GetProcessHeap(), 0, sizeof(FIND_FIRST_INFO
)))) break;
2022 info
->u
.smb_dir
= SMB_FindFirst(lpFileName
);
2023 if(!info
->u
.smb_dir
)
2025 HeapFree(GetProcessHeap(), 0, info
);
2029 RtlInitializeCriticalSection( &info
->cs
);
2033 DOS_FULL_NAME full_name
;
2035 if (lpFileName
[0] && lpFileName
[1] == ':')
2037 /* don't allow root directories */
2038 if (!lpFileName
[2] ||
2039 ((lpFileName
[2] == '/' || lpFileName
[2] == '\\') && !lpFileName
[3]))
2041 SetLastError(ERROR_FILE_NOT_FOUND
);
2042 return INVALID_HANDLE_VALUE
;
2045 if (!DOSFS_GetFullName( lpFileName
, FALSE
, &full_name
)) break;
2046 if (!(info
= HeapAlloc( GetProcessHeap(), 0, sizeof(FIND_FIRST_INFO
)))) break;
2047 RtlInitializeCriticalSection( &info
->cs
);
2048 info
->path
= HeapAlloc( GetProcessHeap(), 0, strlen(full_name
.long_name
)+1 );
2049 strcpy( info
->path
, full_name
.long_name
);
2051 codepage
= DRIVE_GetCodepage(full_name
.drive
);
2052 p
= strrchr( info
->path
, '/' );
2054 long_mask_len
= MultiByteToWideChar(codepage
, 0, p
, -1, NULL
, 0);
2055 info
->long_mask
= HeapAlloc( GetProcessHeap(), 0, long_mask_len
* sizeof(WCHAR
) );
2056 MultiByteToWideChar(codepage
, 0, p
, -1, info
->long_mask
, long_mask_len
);
2058 info
->short_mask
= NULL
;
2060 info
->drive
= full_name
.drive
;
2063 info
->u
.dos_dir
= DOSFS_OpenDir( codepage
, info
->path
);
2065 if (!FindNextFileW( (HANDLE
) info
, data
))
2067 FindClose( (HANDLE
) info
);
2068 SetLastError( ERROR_FILE_NOT_FOUND
);
2071 return (HANDLE
) info
;
2075 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId
);
2077 return INVALID_HANDLE_VALUE
;
2080 /*************************************************************************
2081 * FindFirstFileA (KERNEL32.@)
2083 HANDLE WINAPI
FindFirstFileA(
2085 WIN32_FIND_DATAA
*lpFindData
)
2087 return FindFirstFileExA(lpFileName
, FindExInfoStandard
, lpFindData
,
2088 FindExSearchNameMatch
, NULL
, 0);
2091 /*************************************************************************
2092 * FindFirstFileExA (KERNEL32.@)
2094 HANDLE WINAPI
FindFirstFileExA(
2096 FINDEX_INFO_LEVELS fInfoLevelId
,
2097 LPVOID lpFindFileData
,
2098 FINDEX_SEARCH_OPS fSearchOp
,
2099 LPVOID lpSearchFilter
,
2100 DWORD dwAdditionalFlags
)
2103 WIN32_FIND_DATAA
*dataA
;
2104 WIN32_FIND_DATAW dataW
;
2105 UNICODE_STRING pathW
;
2109 SetLastError(ERROR_PATH_NOT_FOUND
);
2110 return INVALID_HANDLE_VALUE
;
2113 if (!RtlCreateUnicodeStringFromAsciiz(&pathW
, lpFileName
))
2115 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
2116 return INVALID_HANDLE_VALUE
;
2119 handle
= FindFirstFileExW(pathW
.Buffer
, fInfoLevelId
, &dataW
, fSearchOp
, lpSearchFilter
, dwAdditionalFlags
);
2120 RtlFreeUnicodeString(&pathW
);
2121 if (handle
== INVALID_HANDLE_VALUE
) return handle
;
2123 dataA
= (WIN32_FIND_DATAA
*) lpFindFileData
;
2124 dataA
->dwFileAttributes
= dataW
.dwFileAttributes
;
2125 dataA
->ftCreationTime
= dataW
.ftCreationTime
;
2126 dataA
->ftLastAccessTime
= dataW
.ftLastAccessTime
;
2127 dataA
->ftLastWriteTime
= dataW
.ftLastWriteTime
;
2128 dataA
->nFileSizeHigh
= dataW
.nFileSizeHigh
;
2129 dataA
->nFileSizeLow
= dataW
.nFileSizeLow
;
2130 WideCharToMultiByte( CP_ACP
, 0, dataW
.cFileName
, -1,
2131 dataA
->cFileName
, sizeof(dataA
->cFileName
), NULL
, NULL
);
2132 WideCharToMultiByte( CP_ACP
, 0, dataW
.cAlternateFileName
, -1,
2133 dataA
->cAlternateFileName
, sizeof(dataA
->cAlternateFileName
), NULL
, NULL
);
2137 /*************************************************************************
2138 * FindFirstFileW (KERNEL32.@)
2140 HANDLE WINAPI
FindFirstFileW( LPCWSTR lpFileName
, WIN32_FIND_DATAW
*lpFindData
)
2142 return FindFirstFileExW(lpFileName
, FindExInfoStandard
, lpFindData
,
2143 FindExSearchNameMatch
, NULL
, 0);
2146 /*************************************************************************
2147 * FindNextFileW (KERNEL32.@)
2149 BOOL WINAPI
FindNextFileW( HANDLE handle
, WIN32_FIND_DATAW
*data
)
2151 FIND_FIRST_INFO
*info
;
2153 DWORD gle
= ERROR_NO_MORE_FILES
;
2155 if (handle
== INVALID_HANDLE_VALUE
)
2157 SetLastError( ERROR_INVALID_HANDLE
);
2160 info
= (FIND_FIRST_INFO
*) handle
;
2161 RtlEnterCriticalSection( &info
->cs
);
2162 if (info
->drive
== -1)
2164 ret
= SMB_FindNext( info
->u
.smb_dir
, data
);
2167 SMB_CloseDir( info
->u
.smb_dir
);
2168 HeapFree( GetProcessHeap(), 0, info
->path
);
2172 else if (!info
->path
|| !info
->u
.dos_dir
)
2176 else if (!DOSFS_FindNextEx( info
, data
))
2178 DOSFS_CloseDir( info
->u
.dos_dir
); info
->u
.dos_dir
= NULL
;
2179 HeapFree( GetProcessHeap(), 0, info
->path
);
2181 HeapFree( GetProcessHeap(), 0, info
->long_mask
);
2182 info
->long_mask
= NULL
;
2187 RtlLeaveCriticalSection( &info
->cs
);
2188 if( !ret
) SetLastError( gle
);
2193 /*************************************************************************
2194 * FindNextFileA (KERNEL32.@)
2196 BOOL WINAPI
FindNextFileA( HANDLE handle
, WIN32_FIND_DATAA
*data
)
2198 WIN32_FIND_DATAW dataW
;
2199 if (!FindNextFileW( handle
, &dataW
)) return FALSE
;
2200 data
->dwFileAttributes
= dataW
.dwFileAttributes
;
2201 data
->ftCreationTime
= dataW
.ftCreationTime
;
2202 data
->ftLastAccessTime
= dataW
.ftLastAccessTime
;
2203 data
->ftLastWriteTime
= dataW
.ftLastWriteTime
;
2204 data
->nFileSizeHigh
= dataW
.nFileSizeHigh
;
2205 data
->nFileSizeLow
= dataW
.nFileSizeLow
;
2206 WideCharToMultiByte( CP_ACP
, 0, dataW
.cFileName
, -1,
2207 data
->cFileName
, sizeof(data
->cFileName
), NULL
, NULL
);
2208 WideCharToMultiByte( CP_ACP
, 0, dataW
.cAlternateFileName
, -1,
2209 data
->cAlternateFileName
,
2210 sizeof(data
->cAlternateFileName
), NULL
, NULL
);
2214 /*************************************************************************
2215 * FindClose (KERNEL32.@)
2217 BOOL WINAPI
FindClose( HANDLE handle
)
2219 FIND_FIRST_INFO
*info
= (FIND_FIRST_INFO
*) handle
;
2221 if (handle
== INVALID_HANDLE_VALUE
) goto error
;
2225 RtlEnterCriticalSection( &info
->cs
);
2228 if (info
->u
.dos_dir
) DOSFS_CloseDir( info
->u
.dos_dir
);
2229 if (info
->path
) HeapFree( GetProcessHeap(), 0, info
->path
);
2230 if (info
->long_mask
) HeapFree( GetProcessHeap(), 0, info
->long_mask
);
2233 __EXCEPT(page_fault
)
2235 WARN("Illegal handle %p\n", handle
);
2236 SetLastError( ERROR_INVALID_HANDLE
);
2240 if (!info
) goto error
;
2241 RtlLeaveCriticalSection( &info
->cs
);
2242 RtlDeleteCriticalSection( &info
->cs
);
2243 HeapFree(GetProcessHeap(), 0, info
);
2247 SetLastError( ERROR_INVALID_HANDLE
);
2251 /***********************************************************************
2252 * MulDiv (KERNEL32.@)
2254 * Result of multiplication and division
2255 * -1: Overflow occurred or Divisor was 0
2262 #if SIZEOF_LONG_LONG >= 8
2265 if (!nDivisor
) return -1;
2267 /* We want to deal with a positive divisor to simplify the logic. */
2270 nMultiplicand
= - nMultiplicand
;
2271 nDivisor
= -nDivisor
;
2274 /* If the result is positive, we "add" to round. else, we subtract to round. */
2275 if ( ( (nMultiplicand
< 0) && (nMultiplier
< 0) ) ||
2276 ( (nMultiplicand
>= 0) && (nMultiplier
>= 0) ) )
2277 ret
= (((long long)nMultiplicand
* nMultiplier
) + (nDivisor
/2)) / nDivisor
;
2279 ret
= (((long long)nMultiplicand
* nMultiplier
) - (nDivisor
/2)) / nDivisor
;
2281 if ((ret
> 2147483647) || (ret
< -2147483647)) return -1;
2284 if (!nDivisor
) return -1;
2286 /* We want to deal with a positive divisor to simplify the logic. */
2289 nMultiplicand
= - nMultiplicand
;
2290 nDivisor
= -nDivisor
;
2293 /* If the result is positive, we "add" to round. else, we subtract to round. */
2294 if ( ( (nMultiplicand
< 0) && (nMultiplier
< 0) ) ||
2295 ( (nMultiplicand
>= 0) && (nMultiplier
>= 0) ) )
2296 return ((nMultiplicand
* nMultiplier
) + (nDivisor
/2)) / nDivisor
;
2298 return ((nMultiplicand
* nMultiplier
) - (nDivisor
/2)) / nDivisor
;
2304 /***********************************************************************
2305 * DosDateTimeToFileTime (KERNEL32.@)
2307 BOOL WINAPI
DosDateTimeToFileTime( WORD fatdate
, WORD fattime
, LPFILETIME ft
)
2312 time_t time1
, time2
;
2315 newtm
.tm_sec
= (fattime
& 0x1f) * 2;
2316 newtm
.tm_min
= (fattime
>> 5) & 0x3f;
2317 newtm
.tm_hour
= (fattime
>> 11);
2318 newtm
.tm_mday
= (fatdate
& 0x1f);
2319 newtm
.tm_mon
= ((fatdate
>> 5) & 0x0f) - 1;
2320 newtm
.tm_year
= (fatdate
>> 9) + 80;
2322 RtlSecondsSince1970ToTime( timegm(&newtm
), (LARGE_INTEGER
*)ft
);
2324 time1
= mktime(&newtm
);
2325 gtm
= gmtime(&time1
);
2326 time2
= mktime(gtm
);
2327 RtlSecondsSince1970ToTime( 2*time1
-time2
, (LARGE_INTEGER
*)ft
);
2333 /***********************************************************************
2334 * FileTimeToDosDateTime (KERNEL32.@)
2336 BOOL WINAPI
FileTimeToDosDateTime( const FILETIME
*ft
, LPWORD fatdate
,
2344 li
.s
.LowPart
= ft
->dwLowDateTime
;
2345 li
.s
.HighPart
= ft
->dwHighDateTime
;
2346 RtlTimeToSecondsSince1970( &li
, &t
);
2348 tm
= gmtime( &unixtime
);
2350 *fattime
= (tm
->tm_hour
<< 11) + (tm
->tm_min
<< 5) + (tm
->tm_sec
/ 2);
2352 *fatdate
= ((tm
->tm_year
- 80) << 9) + ((tm
->tm_mon
+ 1) << 5)
2358 /***********************************************************************
2359 * QueryDosDeviceA (KERNEL32.@)
2361 * returns array of strings terminated by \0, terminated by \0
2363 DWORD WINAPI
QueryDosDeviceA(LPCSTR devname
,LPSTR target
,DWORD bufsize
)
2365 DWORD ret
= 0, retW
;
2366 LPWSTR targetW
= (LPWSTR
)HeapAlloc(GetProcessHeap(),0,
2367 bufsize
* sizeof(WCHAR
));
2368 UNICODE_STRING devnameW
;
2370 if(devname
) RtlCreateUnicodeStringFromAsciiz(&devnameW
, devname
);
2371 else devnameW
.Buffer
= NULL
;
2373 retW
= QueryDosDeviceW(devnameW
.Buffer
, targetW
, bufsize
);
2375 ret
= WideCharToMultiByte(CP_ACP
, 0, targetW
, retW
, target
,
2376 bufsize
, NULL
, NULL
);
2378 RtlFreeUnicodeString(&devnameW
);
2379 if (targetW
) HeapFree(GetProcessHeap(),0,targetW
);
2384 /***********************************************************************
2385 * QueryDosDeviceW (KERNEL32.@)
2387 * returns array of strings terminated by \0, terminated by \0
2390 * - Win9x returns for all calls ERROR_INVALID_PARAMETER
2391 * - the returned devices for devname == NULL is far from complete
2392 * - its not checked that the returned device exist
2394 DWORD WINAPI
QueryDosDeviceW(LPCWSTR devname
,LPWSTR target
,DWORD bufsize
)
2396 const WCHAR
*pDev
, *pName
, *pNum
= NULL
;
2400 TRACE("(%s,...)\n", debugstr_w(devname
));
2402 /* return known MSDOS devices */
2405 static const WCHAR devices
[][5] = {{'A','U','X',0},
2406 {'C','O','M','1',0},
2407 {'C','O','M','2',0},
2408 {'L','P','T','1',0},
2410 for(i
=0; (i
< (sizeof(devices
)/sizeof(devices
[0]))); i
++) {
2411 DWORD len
= strlenW(devices
[i
]);
2412 if(target
&& (bufsize
>= ret
+ len
+ 2)) {
2413 strcpyW(target
+ret
, devices
[i
]);
2416 /* in this case WinXP returns 0 */
2417 FIXME("function return is wrong for WinXP!\n");
2418 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
2422 /* append drives here */
2423 if(target
&& bufsize
> 0) target
[ret
++] = 0;
2424 FIXME("Returned list is not complete\n");
2427 /* In theory all that are possible and have been defined.
2428 * Now just those below, since mirc uses it to check for special files.
2430 * (It is more complex, and supports netmounted stuff, and \\.\ stuff,
2431 * but currently we just ignore that.)
2433 if (!strcmpiW(devname
, auxW
)) {
2438 } else if (!strcmpiW(devname
, nulW
)) {
2441 } else if (!strncmpiW(devname
, comW
, strlenW(comW
))) {
2444 pNum
= devname
+ strlenW(comW
);
2445 for(numsiz
=0; isdigitW(*(pNum
+numsiz
)); numsiz
++);
2446 if(*(pNum
+ numsiz
)) {
2447 SetLastError(ERROR_FILE_NOT_FOUND
);
2450 } else if (!strncmpiW(devname
, lptW
, strlenW(lptW
))) {
2453 pNum
= devname
+ strlenW(lptW
);
2454 for(numsiz
=0; isdigitW(*(pNum
+numsiz
)); numsiz
++);
2455 if(*(pNum
+ numsiz
)) {
2456 SetLastError(ERROR_FILE_NOT_FOUND
);
2460 /* This might be a DOS device we do not handle yet ... */
2461 FIXME("(%s) not detected as DOS device!\n",debugstr_w(devname
));
2463 /* Win9x set the error ERROR_INVALID_PARAMETER */
2464 SetLastError(ERROR_FILE_NOT_FOUND
);
2467 FIXME("device %s may not exist on this computer\n", debugstr_w(devname
));
2469 ret
= strlenW(pDev
) + strlenW(pName
) + numsiz
+ 2;
2470 if (ret
> bufsize
) ret
= 0;
2471 if (target
&& ret
) {
2472 strcpyW(target
,pDev
);
2473 strcatW(target
,pName
);
2474 if (pNum
) strcatW(target
,pNum
);
2481 /***********************************************************************
2482 * DefineDosDeviceA (KERNEL32.@)
2484 BOOL WINAPI
DefineDosDeviceA(DWORD flags
,LPCSTR devname
,LPCSTR targetpath
) {
2485 FIXME("(0x%08lx,%s,%s),stub!\n",flags
,devname
,targetpath
);
2486 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);