2 * DOS file system functions
4 * Copyright 1993 Erik Bos
5 * Copyright 1996 Alexandre Julliard
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 #include <sys/types.h>
28 #ifdef HAVE_SYS_ERRNO_H
29 #include <sys/errno.h>
35 #ifdef HAVE_SYS_IOCTL_H
36 #include <sys/ioctl.h>
43 #define NONAMELESSUNION
44 #define NONAMELESSSTRUCT
49 #include "wine/unicode.h"
50 #include "wine/winbase16.h"
56 #include "wine/server.h"
61 #include "wine/debug.h"
63 WINE_DEFAULT_DEBUG_CHANNEL(dosfs
);
64 WINE_DECLARE_DEBUG_CHANNEL(file
);
66 /* Define the VFAT ioctl to get both short and long file names */
67 /* FIXME: is it possible to get this to work on other systems? */
69 /* We want the real kernel dirent structure, not the libc one */
74 unsigned short d_reclen
;
78 #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, KERNEL_DIRENT [2] )
80 /* To avoid blocking on non-directories in DOSFS_OpenDir_VFAT*/
82 # define O_DIRECTORY 0200000 /* must be directory */
86 #undef VFAT_IOCTL_READDIR_BOTH /* just in case... */
89 #define IS_OPTION_TRUE(ch) ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1')
91 /* Chars we don't want to see in DOS file names */
92 #define INVALID_DOS_CHARS "*?<>|\"+=,;[] \345"
94 static const DOS_DEVICE DOSFS_Devices
[] =
95 /* name, device flags (see Int 21/AX=0x4400) */
97 { {'C','O','N',0}, 0xc0d3 },
98 { {'P','R','N',0}, 0xa0c0 },
99 { {'N','U','L',0}, 0x80c4 },
100 { {'A','U','X',0}, 0x80c0 },
101 { {'L','P','T','1',0}, 0xa0c0 },
102 { {'L','P','T','2',0}, 0xa0c0 },
103 { {'L','P','T','3',0}, 0xa0c0 },
104 { {'L','P','T','4',0}, 0xc0d3 },
105 { {'C','O','M','1',0}, 0x80c0 },
106 { {'C','O','M','2',0}, 0x80c0 },
107 { {'C','O','M','3',0}, 0x80c0 },
108 { {'C','O','M','4',0}, 0x80c0 },
109 { {'S','C','S','I','M','G','R','$',0}, 0xc0c0 },
110 { {'H','P','S','C','A','N',0}, 0xc0c0 },
111 { {'E','M','M','X','X','X','X','0',0}, 0x0000 }
114 static const WCHAR devW
[] = {'\\','D','e','v','i','c','e','\\',0};
115 static const WCHAR dosW
[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\',0};
117 static const WCHAR auxW
[] = {'A','U','X',0};
118 static const WCHAR comW
[] = {'C','O','M',0};
119 static const WCHAR lptW
[] = {'L','P','T',0};
120 static const WCHAR nulW
[] = {'N','U','L',0};
122 static const WCHAR nullW
[] = {'N','u','l','l',0};
123 static const WCHAR parW
[] = {'P','a','r','a','l','l','e','l',0};
124 static const WCHAR serW
[] = {'S','e','r','i','a','l',0};
125 static const WCHAR oneW
[] = {'1',0};
128 * Directory info for DOSFS_ReadDir
129 * contains the names of *all* the files in the directory
138 /* Info structure for FindFirstFile handle */
141 char *path
; /* unix path */
155 static WINE_EXCEPTION_FILTER(page_fault
)
157 if (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION
)
158 return EXCEPTION_EXECUTE_HANDLER
;
159 return EXCEPTION_CONTINUE_SEARCH
;
163 /***********************************************************************
166 * Return 1 if Unix file 'name' is also a valid MS-DOS name
167 * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
168 * File name can be terminated by '\0', '\\' or '/'.
170 static int DOSFS_ValidDOSName( LPCWSTR name
, int ignore_case
)
172 static const char invalid_chars
[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS
;
173 const WCHAR
*p
= name
;
174 const char *invalid
= ignore_case
? (invalid_chars
+ 26) : invalid_chars
;
179 /* Check for "." and ".." */
182 /* All other names beginning with '.' are invalid */
183 return (IS_END_OF_NAME(*p
));
185 while (!IS_END_OF_NAME(*p
))
187 if (*p
< 256 && strchr( invalid
, (char)*p
)) return 0; /* Invalid char */
188 if (*p
== '.') break; /* Start of the extension */
189 if (++len
> 8) return 0; /* Name too long */
192 if (*p
!= '.') return 1; /* End of name */
194 if (IS_END_OF_NAME(*p
)) return 0; /* Empty extension not allowed */
196 while (!IS_END_OF_NAME(*p
))
198 if (*p
< 256 && strchr( invalid
, (char)*p
)) return 0; /* Invalid char */
199 if (*p
== '.') return 0; /* Second extension not allowed */
200 if (++len
> 3) return 0; /* Extension too long */
207 /***********************************************************************
208 * DOSFS_ToDosFCBFormat
210 * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
211 * expanding wild cards and converting to upper-case in the process.
212 * File name can be terminated by '\0', '\\' or '/'.
213 * Return FALSE if the name is not a valid DOS name.
214 * 'buffer' must be at least 12 characters long.
216 BOOL
DOSFS_ToDosFCBFormat( LPCWSTR name
, LPWSTR buffer
)
218 static const char invalid_chars
[] = INVALID_DOS_CHARS
;
222 /* Check for "." and ".." */
227 for(i
= 1; i
< 11; i
++) buffer
[i
] = ' ';
234 return (!*p
|| (*p
== '/') || (*p
== '\\'));
237 for (i
= 0; i
< 8; i
++)
254 if (*p
< 256 && strchr( invalid_chars
, (char)*p
)) return FALSE
;
255 buffer
[i
] = toupperW(*p
);
263 /* Skip all chars after wildcard up to first dot */
264 while (*p
&& (*p
!= '/') && (*p
!= '\\') && (*p
!= '.')) p
++;
268 /* Check if name too long */
269 if (*p
&& (*p
!= '/') && (*p
!= '\\') && (*p
!= '.')) return FALSE
;
271 if (*p
== '.') p
++; /* Skip dot */
273 for (i
= 8; i
< 11; i
++)
283 return FALSE
; /* Second extension not allowed */
291 if (*p
< 256 && strchr( invalid_chars
, (char)*p
)) return FALSE
;
292 buffer
[i
] = toupperW(*p
);
299 /* at most 3 character of the extension are processed
300 * is something behind this ?
302 while (*p
== '*' || *p
== ' ') p
++; /* skip wildcards and spaces */
303 return IS_END_OF_NAME(*p
);
307 /***********************************************************************
308 * DOSFS_ToDosDTAFormat
310 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
311 * converting to upper-case in the process.
312 * File name can be terminated by '\0', '\\' or '/'.
313 * 'buffer' must be at least 13 characters long.
315 static void DOSFS_ToDosDTAFormat( LPCWSTR name
, LPWSTR buffer
)
319 memcpy( buffer
, name
, 8 * sizeof(WCHAR
) );
321 while ((p
> buffer
) && (p
[-1] == ' ')) p
--;
323 memcpy( p
, name
+ 8, 3 * sizeof(WCHAR
) );
325 while (p
[-1] == ' ') p
--;
326 if (p
[-1] == '.') p
--;
331 /***********************************************************************
334 * Check a DOS file name against a mask (both in FCB format).
336 static int DOSFS_MatchShort( LPCWSTR mask
, LPCWSTR name
)
339 for (i
= 11; i
> 0; i
--, mask
++, name
++)
340 if ((*mask
!= '?') && (*mask
!= *name
)) return 0;
345 /***********************************************************************
348 * Check a long file name against a mask.
350 * Tests (done in W95 DOS shell - case insensitive):
351 * *.txt test1.test.txt *
353 * *.t??????.t* test1.ta.tornado.txt *
354 * *tornado* test1.ta.tornado.txt *
355 * t*t test1.ta.tornado.txt *
357 * ?est??? test1.txt -
358 * *test1.txt* test1.txt *
359 * h?l?o*t.dat hellothisisatest.dat *
361 static int DOSFS_MatchLong( LPCWSTR mask
, LPCWSTR name
, int case_sensitive
)
363 LPCWSTR lastjoker
= NULL
;
364 LPCWSTR next_to_retry
= NULL
;
365 static const WCHAR asterisk_dot_asterisk
[] = {'*','.','*',0};
367 TRACE("(%s, %s, %x)\n", debugstr_w(mask
), debugstr_w(name
), case_sensitive
);
369 if (!strcmpW( mask
, asterisk_dot_asterisk
)) return 1;
370 while (*name
&& *mask
)
375 while (*mask
== '*') mask
++; /* Skip consecutive '*' */
377 if (!*mask
) return 1; /* end of mask is all '*', so match */
379 /* skip to the next match after the joker(s) */
380 if (case_sensitive
) while (*name
&& (*name
!= *mask
)) name
++;
381 else while (*name
&& (toupperW(*name
) != toupperW(*mask
))) name
++;
384 next_to_retry
= name
;
386 else if (*mask
!= '?')
391 if (*mask
!= *name
) mismatch
= 1;
395 if (toupperW(*mask
) != toupperW(*name
)) mismatch
= 1;
409 else /* mismatch ! */
411 if (lastjoker
) /* we had an '*', so we can try unlimitedly */
415 /* this scan sequence was a mismatch, so restart
416 * 1 char after the first char we checked last time */
418 name
= next_to_retry
;
421 return 0; /* bad luck */
430 while ((*mask
== '.') || (*mask
== '*'))
431 mask
++; /* Ignore trailing '.' or '*' in mask */
432 return (!*name
&& !*mask
);
436 /***********************************************************************
439 * Used to construct an array of filenames in DOSFS_OpenDir
441 static BOOL
DOSFS_AddDirEntry(DOS_DIR
**dir
, LPCWSTR name
, LPCWSTR dosname
)
443 int extra1
= strlenW(name
) + 1;
444 int extra2
= strlenW(dosname
) + 1;
446 /* if we need more, at minimum double the size */
447 if( (extra1
+ extra2
+ (*dir
)->used
) > (*dir
)->size
)
449 int more
= (*dir
)->size
;
452 if(more
<(extra1
+extra2
))
453 more
= extra1
+extra2
;
455 t
= HeapReAlloc(GetProcessHeap(), 0, *dir
, sizeof(**dir
) +
456 ((*dir
)->size
+ more
)*sizeof(WCHAR
) );
459 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
460 ERR("Out of memory caching directory structure %d %d %d\n",
461 (*dir
)->size
, more
, (*dir
)->used
);
465 (*dir
)->size
+= more
;
468 /* at this point, the dir structure is big enough to hold these names */
469 strcpyW(&(*dir
)->names
[(*dir
)->used
], name
);
470 (*dir
)->used
+= extra1
;
471 strcpyW(&(*dir
)->names
[(*dir
)->used
], dosname
);
472 (*dir
)->used
+= extra2
;
478 /***********************************************************************
481 static BOOL
DOSFS_OpenDir_VFAT(UINT codepage
, DOS_DIR
**dir
, const char *unix_path
)
483 #ifdef VFAT_IOCTL_READDIR_BOTH
485 int fd
= open( unix_path
, O_RDONLY
|O_DIRECTORY
);
488 /* Check if the VFAT ioctl is supported on this directory */
495 WCHAR long_name
[MAX_PATH
];
496 WCHAR short_name
[12];
498 r
= (ioctl( fd
, VFAT_IOCTL_READDIR_BOTH
, (long)de
) != -1);
503 MultiByteToWideChar(codepage
, 0, de
[0].d_name
, -1, long_name
, MAX_PATH
);
504 if (!DOSFS_ToDosFCBFormat( long_name
, short_name
))
505 short_name
[0] = '\0';
507 MultiByteToWideChar(codepage
, 0, de
[1].d_name
, -1, long_name
, MAX_PATH
);
509 MultiByteToWideChar(codepage
, 0, de
[0].d_name
, -1, long_name
, MAX_PATH
);
510 r
= DOSFS_AddDirEntry(dir
, long_name
, short_name
);
516 static const WCHAR empty_strW
[] = { 0 };
517 DOSFS_AddDirEntry(dir
, empty_strW
, empty_strW
);
523 #endif /* VFAT_IOCTL_READDIR_BOTH */
527 /***********************************************************************
528 * DOSFS_OpenDir_Normal
530 * Now use the standard opendir/readdir interface
532 static BOOL
DOSFS_OpenDir_Normal( UINT codepage
, DOS_DIR
**dir
, const char *unix_path
)
534 DIR *unixdir
= opendir( unix_path
);
536 static const WCHAR empty_strW
[] = { 0 };
542 WCHAR long_name
[MAX_PATH
];
543 struct dirent
*de
= readdir(unixdir
);
547 MultiByteToWideChar(codepage
, 0, de
->d_name
, -1, long_name
, MAX_PATH
);
548 r
= DOSFS_AddDirEntry(dir
, long_name
, empty_strW
);
553 DOSFS_AddDirEntry(dir
, empty_strW
, empty_strW
);
558 /***********************************************************************
561 static DOS_DIR
*DOSFS_OpenDir( UINT codepage
, const char *unix_path
)
563 const int init_size
= 0x100;
564 DOS_DIR
*dir
= HeapAlloc( GetProcessHeap(), 0, sizeof(*dir
) + init_size
*sizeof (WCHAR
));
567 TRACE("%s\n",debugstr_a(unix_path
));
571 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
575 dir
->size
= init_size
;
577 /* Treat empty path as root directory. This simplifies path split into
578 directory and mask in several other places */
579 if (!*unix_path
) unix_path
= "/";
581 r
= DOSFS_OpenDir_VFAT( codepage
, &dir
, unix_path
);
584 r
= DOSFS_OpenDir_Normal( codepage
, &dir
, unix_path
);
588 HeapFree(GetProcessHeap(), 0, dir
);
597 /***********************************************************************
600 static void DOSFS_CloseDir( DOS_DIR
*dir
)
602 HeapFree( GetProcessHeap(), 0, dir
);
606 /***********************************************************************
609 static BOOL
DOSFS_ReadDir( DOS_DIR
*dir
, LPCWSTR
*long_name
,
610 LPCWSTR
*short_name
)
617 /* the long pathname is first */
618 ln
= &dir
->names
[dir
->used
];
623 dir
->used
+= (strlenW(ln
) + 1);
625 /* followed by the short path name */
626 sn
= &dir
->names
[dir
->used
];
631 dir
->used
+= (strlenW(sn
) + 1);
637 /***********************************************************************
640 * Transform a Unix file name into a hashed DOS name. If the name is a valid
641 * DOS name, it is converted to upper-case; otherwise it is replaced by a
642 * hashed version that fits in 8.3 format.
643 * File name can be terminated by '\0', '\\' or '/'.
644 * 'buffer' must be at least 13 characters long.
646 static void DOSFS_Hash( LPCWSTR name
, LPWSTR buffer
, BOOL dir_format
,
649 static const char invalid_chars
[] = INVALID_DOS_CHARS
"~.";
650 static const char hash_chars
[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
659 for(i
= 0; i
< 11; i
++) buffer
[i
] = ' ';
663 if (DOSFS_ValidDOSName( name
, ignore_case
))
665 /* Check for '.' and '..' */
669 if (!dir_format
) buffer
[1] = buffer
[2] = '\0';
670 if (name
[1] == '.') buffer
[1] = '.';
674 /* Simply copy the name, converting to uppercase */
676 for (dst
= buffer
; !IS_END_OF_NAME(*name
) && (*name
!= '.'); name
++)
677 *dst
++ = toupperW(*name
);
680 if (dir_format
) dst
= buffer
+ 8;
682 for (name
++; !IS_END_OF_NAME(*name
); name
++)
683 *dst
++ = toupperW(*name
);
685 if (!dir_format
) *dst
= '\0';
689 /* Compute the hash code of the file name */
690 /* If you know something about hash functions, feel free to */
691 /* insert a better algorithm here... */
694 for (p
= name
, hash
= 0xbeef; !IS_END_OF_NAME(p
[1]); p
++)
695 hash
= (hash
<<3) ^ (hash
>>5) ^ tolowerW(*p
) ^ (tolowerW(p
[1]) << 8);
696 hash
= (hash
<<3) ^ (hash
>>5) ^ tolowerW(*p
); /* Last character */
700 for (p
= name
, hash
= 0xbeef; !IS_END_OF_NAME(p
[1]); p
++)
701 hash
= (hash
<< 3) ^ (hash
>> 5) ^ *p
^ (p
[1] << 8);
702 hash
= (hash
<< 3) ^ (hash
>> 5) ^ *p
; /* Last character */
705 /* Find last dot for start of the extension */
706 for (p
= name
+1, ext
= NULL
; !IS_END_OF_NAME(*p
); p
++)
707 if (*p
== '.') ext
= p
;
708 if (ext
&& IS_END_OF_NAME(ext
[1]))
709 ext
= NULL
; /* Empty extension ignored */
711 /* Copy first 4 chars, replacing invalid chars with '_' */
712 for (i
= 4, p
= name
, dst
= buffer
; i
> 0; i
--, p
++)
714 if (IS_END_OF_NAME(*p
) || (p
== ext
)) break;
715 *dst
++ = (*p
< 256 && strchr( invalid_chars
, (char)*p
)) ? '_' : toupperW(*p
);
717 /* Pad to 5 chars with '~' */
718 while (i
-- >= 0) *dst
++ = '~';
720 /* Insert hash code converted to 3 ASCII chars */
721 *dst
++ = hash_chars
[(hash
>> 10) & 0x1f];
722 *dst
++ = hash_chars
[(hash
>> 5) & 0x1f];
723 *dst
++ = hash_chars
[hash
& 0x1f];
725 /* Copy the first 3 chars of the extension (if any) */
728 if (!dir_format
) *dst
++ = '.';
729 for (i
= 3, ext
++; (i
> 0) && !IS_END_OF_NAME(*ext
); i
--, ext
++)
730 *dst
++ = (*ext
< 256 && strchr( invalid_chars
, (char)*ext
)) ? '_' : toupperW(*ext
);
732 if (!dir_format
) *dst
= '\0';
736 /***********************************************************************
739 * Find the Unix file name in a given directory that corresponds to
740 * a file name (either in Unix or DOS format).
741 * File name can be terminated by '\0', '\\' or '/'.
742 * Return TRUE if OK, FALSE if no file name matches.
744 * 'long_buf' must be at least 'long_len' characters long. If the long name
745 * turns out to be larger than that, the function returns FALSE.
746 * 'short_buf' must be at least 13 characters long.
748 BOOL
DOSFS_FindUnixName( const DOS_FULL_NAME
*path
, LPCWSTR name
, char *long_buf
,
749 INT long_len
, LPWSTR short_buf
, BOOL ignore_case
)
752 LPCWSTR long_name
, short_name
;
753 WCHAR dos_name
[12], tmp_buf
[13];
756 LPCWSTR p
= strchrW( name
, '/' );
757 int len
= p
? (int)(p
- name
) : strlenW(name
);
758 if ((p
= strchrW( name
, '\\' ))) len
= min( (int)(p
- name
), len
);
759 /* Ignore trailing dots and spaces */
760 while (len
> 1 && (name
[len
-1] == '.' || name
[len
-1] == ' ')) len
--;
761 if (long_len
< len
+ 1) return FALSE
;
763 TRACE("%s,%s\n", path
->long_name
, debugstr_w(name
) );
765 if (!DOSFS_ToDosFCBFormat( name
, dos_name
)) dos_name
[0] = '\0';
767 if (!(dir
= DOSFS_OpenDir( DRIVE_GetCodepage(path
->drive
), path
->long_name
)))
769 WARN("(%s,%s): can't open dir: %s\n",
770 path
->long_name
, debugstr_w(name
), strerror(errno
) );
774 while ((ret
= DOSFS_ReadDir( dir
, &long_name
, &short_name
)))
776 /* Check against Unix name */
777 if (len
== strlenW(long_name
))
781 if (!strncmpW( long_name
, name
, len
)) break;
785 if (!strncmpiW( long_name
, name
, len
)) break;
790 /* Check against hashed DOS name */
793 DOSFS_Hash( long_name
, tmp_buf
, TRUE
, ignore_case
);
794 short_name
= tmp_buf
;
796 if (!strcmpW( dos_name
, short_name
)) break;
801 if (long_buf
) WideCharToMultiByte(DRIVE_GetCodepage(path
->drive
), 0,
802 long_name
, -1, long_buf
, long_len
, NULL
, NULL
);
806 DOSFS_ToDosDTAFormat( short_name
, short_buf
);
808 DOSFS_Hash( long_name
, short_buf
, FALSE
, ignore_case
);
810 TRACE("(%s,%s) -> %s (%s)\n", path
->long_name
, debugstr_w(name
),
811 debugstr_w(long_name
), short_buf
? debugstr_w(short_buf
) : "***");
814 WARN("%s not found in '%s'\n", debugstr_w(name
), path
->long_name
);
815 DOSFS_CloseDir( dir
);
820 /***********************************************************************
823 * Check if a DOS file name represents a DOS device and return the device.
825 const DOS_DEVICE
*DOSFS_GetDevice( LPCWSTR name
)
830 if (!name
) return NULL
; /* if wine_server_handle_to_fd was used */
831 if (name
[0] && (name
[1] == ':')) name
+= 2;
832 if ((p
= strrchrW( name
, '/' ))) name
= p
+ 1;
833 if ((p
= strrchrW( name
, '\\' ))) name
= p
+ 1;
834 for (i
= 0; i
< sizeof(DOSFS_Devices
)/sizeof(DOSFS_Devices
[0]); i
++)
836 const WCHAR
*dev
= DOSFS_Devices
[i
].name
;
837 if (!strncmpiW( dev
, name
, strlenW(dev
) ))
839 p
= name
+ strlenW( dev
);
840 if (!*p
|| (*p
== '.') || (*p
== ':')) return &DOSFS_Devices
[i
];
847 /***********************************************************************
848 * DOSFS_GetDeviceByHandle
850 const DOS_DEVICE
*DOSFS_GetDeviceByHandle( HANDLE hFile
)
852 const DOS_DEVICE
*ret
= NULL
;
853 SERVER_START_REQ( get_device_id
)
856 if (!wine_server_call( req
))
858 if ((reply
->id
>= 0) &&
859 (reply
->id
< sizeof(DOSFS_Devices
)/sizeof(DOSFS_Devices
[0])))
860 ret
= &DOSFS_Devices
[reply
->id
];
868 /**************************************************************************
869 * DOSFS_CreateCommPort
871 static HANDLE
DOSFS_CreateCommPort(LPCWSTR name
, DWORD access
, DWORD attributes
, LPSECURITY_ATTRIBUTES sa
)
876 OBJECT_ATTRIBUTES attr
;
877 UNICODE_STRING nameW
;
882 static const WCHAR serialportsW
[] = {'M','a','c','h','i','n','e','\\',
883 'S','o','f','t','w','a','r','e','\\',
884 'W','i','n','e','\\','W','i','n','e','\\',
885 'C','o','n','f','i','g','\\',
886 'S','e','r','i','a','l','P','o','r','t','s',0};
888 TRACE_(file
)("%s %lx %lx\n", debugstr_w(name
), access
, attributes
);
890 attr
.Length
= sizeof(attr
);
891 attr
.RootDirectory
= 0;
892 attr
.ObjectName
= &nameW
;
894 attr
.SecurityDescriptor
= NULL
;
895 attr
.SecurityQualityOfService
= NULL
;
896 RtlInitUnicodeString( &nameW
, serialportsW
);
898 if (NtOpenKey( &hkey
, KEY_ALL_ACCESS
, &attr
)) return 0;
900 RtlInitUnicodeString( &nameW
, name
);
901 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
902 devnameW
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
908 if (!devnameW
) return 0;
909 WideCharToMultiByte(CP_ACP
, 0, devnameW
, -1, devname
, sizeof(devname
), NULL
, NULL
);
911 TRACE("opening %s as %s\n", devname
, debugstr_w(name
));
913 SERVER_START_REQ( create_serial
)
915 req
->access
= access
;
916 req
->inherit
= (sa
&& (sa
->nLength
>=sizeof(*sa
)) && sa
->bInheritHandle
);
917 req
->attributes
= attributes
;
918 req
->sharing
= FILE_SHARE_READ
|FILE_SHARE_WRITE
;
919 wine_server_add_data( req
, devname
, strlen(devname
) );
921 wine_server_call_err( req
);
927 ERR("Couldn't open device '%s' ! (check permissions)\n",devname
);
929 TRACE("return %p\n", ret
);
933 /***********************************************************************
936 * Open a DOS device. This might not map 1:1 into the UNIX device concept.
937 * Returns 0 on failure.
939 HANDLE
DOSFS_OpenDevice( LPCWSTR name
, DWORD access
, DWORD attributes
, LPSECURITY_ATTRIBUTES sa
)
945 if (name
[0] && (name
[1] == ':')) name
+= 2;
946 if ((p
= strrchrW( name
, '/' ))) name
= p
+ 1;
947 if ((p
= strrchrW( name
, '\\' ))) name
= p
+ 1;
948 for (i
= 0; i
< sizeof(DOSFS_Devices
)/sizeof(DOSFS_Devices
[0]); i
++)
950 const WCHAR
*dev
= DOSFS_Devices
[i
].name
;
951 if (!strncmpiW( dev
, name
, strlenW(dev
) ))
953 p
= name
+ strlenW( dev
);
954 if (!*p
|| (*p
== '.') || (*p
== ':')) {
955 static const WCHAR nulW
[] = {'N','U','L',0};
956 static const WCHAR conW
[] = {'C','O','N',0};
957 static const WCHAR scsimgrW
[] = {'S','C','S','I','M','G','R','$',0};
958 static const WCHAR hpscanW
[] = {'H','P','S','C','A','N',0};
959 static const WCHAR emmxxxx0W
[] = {'E','M','M','X','X','X','X','0',0};
961 if (!strcmpiW(DOSFS_Devices
[i
].name
, nulW
))
962 return FILE_CreateFile( "/dev/null", access
,
963 FILE_SHARE_READ
|FILE_SHARE_WRITE
, sa
,
964 OPEN_EXISTING
, 0, 0, TRUE
, DRIVE_UNKNOWN
);
965 if (!strcmpiW(DOSFS_Devices
[i
].name
, conW
)) {
967 switch (access
& (GENERIC_READ
|GENERIC_WRITE
)) {
969 to_dup
= GetStdHandle( STD_INPUT_HANDLE
);
972 to_dup
= GetStdHandle( STD_OUTPUT_HANDLE
);
975 FIXME("can't open CON read/write\n");
978 if (!DuplicateHandle( GetCurrentProcess(), to_dup
, GetCurrentProcess(),
980 sa
&& (sa
->nLength
>=sizeof(*sa
)) && sa
->bInheritHandle
,
981 DUPLICATE_SAME_ACCESS
))
985 if (!strcmpiW(DOSFS_Devices
[i
].name
, scsimgrW
) ||
986 !strcmpiW(DOSFS_Devices
[i
].name
, hpscanW
) ||
987 !strcmpiW(DOSFS_Devices
[i
].name
, emmxxxx0W
))
989 return FILE_CreateDevice( i
, access
, sa
);
992 if( (handle
=DOSFS_CreateCommPort(DOSFS_Devices
[i
].name
,access
,attributes
,sa
)) )
994 FIXME("device open %s not supported (yet)\n", debugstr_w(DOSFS_Devices
[i
].name
));
1003 /***********************************************************************
1004 * DOSFS_GetPathDrive
1006 * Get the drive specified by a given path name (DOS or Unix format).
1008 static int DOSFS_GetPathDrive( LPCWSTR
*name
)
1013 if (*p
&& (p
[1] == ':'))
1015 drive
= toupperW(*p
) - 'A';
1018 else if (*p
== '/') /* Absolute Unix path? */
1020 if ((drive
= DRIVE_FindDriveRootW( name
)) == -1)
1022 MESSAGE("Warning: %s not accessible from a configured DOS drive\n", debugstr_w(*name
) );
1023 /* Assume it really was a DOS name */
1024 drive
= DRIVE_GetCurrentDrive();
1027 else drive
= DRIVE_GetCurrentDrive();
1029 if (!DRIVE_IsValid(drive
))
1031 SetLastError( ERROR_INVALID_DRIVE
);
1038 /***********************************************************************
1041 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
1042 * Unix name / short DOS name pair.
1043 * Return FALSE if one of the path components does not exist. The last path
1044 * component is only checked if 'check_last' is non-zero.
1045 * The buffers pointed to by 'long_buf' and 'short_buf' must be
1046 * at least MAX_PATHNAME_LEN long.
1048 BOOL
DOSFS_GetFullName( LPCWSTR name
, BOOL check_last
, DOS_FULL_NAME
*full
)
1051 UINT flags
, codepage
;
1054 static const WCHAR driveA_rootW
[] = {'A',':','\\',0};
1055 static const WCHAR dos_rootW
[] = {'\\',0};
1057 TRACE("%s (last=%d)\n", debugstr_w(name
), check_last
);
1059 if ((!*name
) || (*name
=='\n'))
1060 { /* error code for Win98 */
1061 SetLastError(ERROR_BAD_PATHNAME
);
1065 if ((full
->drive
= DOSFS_GetPathDrive( &name
)) == -1) return FALSE
;
1066 flags
= DRIVE_GetFlags( full
->drive
);
1067 codepage
= DRIVE_GetCodepage(full
->drive
);
1069 lstrcpynA( full
->long_name
, DRIVE_GetRoot( full
->drive
),
1070 sizeof(full
->long_name
) );
1071 if (full
->long_name
[1]) root
= full
->long_name
+ strlen(full
->long_name
);
1072 else root
= full
->long_name
; /* root directory */
1074 strcpyW( full
->short_name
, driveA_rootW
);
1075 full
->short_name
[0] += full
->drive
;
1077 if ((*name
== '\\') || (*name
== '/')) /* Absolute path */
1079 while ((*name
== '\\') || (*name
== '/')) name
++;
1081 else /* Relative path */
1083 lstrcpynA( root
+ 1, DRIVE_GetUnixCwd( full
->drive
),
1084 sizeof(full
->long_name
) - (root
- full
->long_name
) - 1 );
1085 if (root
[1]) *root
= '/';
1086 lstrcpynW( full
->short_name
+ 3, DRIVE_GetDosCwd( full
->drive
),
1087 sizeof(full
->short_name
)/sizeof(full
->short_name
[0]) - 3 );
1090 p_l
= full
->long_name
[1] ? full
->long_name
+ strlen(full
->long_name
)
1092 p_s
= full
->short_name
[3] ? full
->short_name
+ strlenW(full
->short_name
)
1093 : full
->short_name
+ 2;
1096 while (*name
&& found
)
1098 /* Check for '.' and '..' */
1102 if (IS_END_OF_NAME(name
[1]))
1105 while ((*name
== '\\') || (*name
== '/')) name
++;
1108 else if ((name
[1] == '.') && IS_END_OF_NAME(name
[2]))
1111 while ((*name
== '\\') || (*name
== '/')) name
++;
1112 while ((p_l
> root
) && (*p_l
!= '/')) p_l
--;
1113 while ((p_s
> full
->short_name
+ 2) && (*p_s
!= '\\')) p_s
--;
1114 *p_l
= *p_s
= '\0'; /* Remove trailing separator */
1119 /* Make sure buffers are large enough */
1121 if ((p_s
>= full
->short_name
+ sizeof(full
->short_name
)/sizeof(full
->short_name
[0]) - 14) ||
1122 (p_l
>= full
->long_name
+ sizeof(full
->long_name
) - 1))
1124 SetLastError( ERROR_PATH_NOT_FOUND
);
1128 /* Get the long and short name matching the file name */
1130 if ((found
= DOSFS_FindUnixName( full
, name
, p_l
+ 1,
1131 sizeof(full
->long_name
) - (p_l
- full
->long_name
) - 1,
1132 p_s
+ 1, !(flags
& DRIVE_CASE_SENSITIVE
) )))
1137 p_s
+= strlenW(p_s
);
1138 while (!IS_END_OF_NAME(*name
)) name
++;
1140 else if (!check_last
)
1144 while (!IS_END_OF_NAME(*name
) &&
1145 (p_s
< full
->short_name
+ sizeof(full
->short_name
)/sizeof(full
->short_name
[0]) - 1) &&
1146 (p_l
< full
->long_name
+ sizeof(full
->long_name
) - 1))
1149 *p_s
++ = tolowerW(*name
);
1150 /* If the drive is case-sensitive we want to create new */
1151 /* files in lower-case otherwise we can't reopen them */
1152 /* under the same short name. */
1153 if (flags
& DRIVE_CASE_SENSITIVE
) wch
= tolowerW(*name
);
1155 p_l
+= WideCharToMultiByte(codepage
, 0, &wch
, 1, p_l
, 2, NULL
, NULL
);
1158 /* Ignore trailing dots and spaces */
1159 while(p_l
[-1] == '.' || p_l
[-1] == ' ') {
1166 while ((*name
== '\\') || (*name
== '/')) name
++;
1173 SetLastError( ERROR_FILE_NOT_FOUND
);
1176 if (*name
) /* Not last */
1178 SetLastError( ERROR_PATH_NOT_FOUND
);
1182 if (!full
->long_name
[0]) strcpy( full
->long_name
, "/" );
1183 if (!full
->short_name
[2]) strcpyW( full
->short_name
+ 2, dos_rootW
);
1184 TRACE("returning %s = %s\n", full
->long_name
, debugstr_w(full
->short_name
) );
1189 /***********************************************************************
1190 * GetShortPathNameW (KERNEL32.@)
1194 * longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1195 * longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0
1197 * more observations ( with NT 3.51 (WinDD) ):
1198 * longpath <= 8.3 -> just copy longpath to shortpath
1200 * a) file does not exist -> return 0, LastError = ERROR_FILE_NOT_FOUND
1201 * b) file does exist -> set the short filename.
1202 * - trailing slashes are reproduced in the short name, even if the
1203 * file is not a directory
1204 * - the absolute/relative path of the short name is reproduced like found
1206 * - longpath and shortpath may have the same address
1207 * Peter Ganten, 1999
1209 DWORD WINAPI
GetShortPathNameW( LPCWSTR longpath
, LPWSTR shortpath
, DWORD shortlen
)
1211 DOS_FULL_NAME full_name
;
1212 WCHAR tmpshortpath
[MAX_PATHNAME_LEN
];
1214 DWORD sp
= 0, lp
= 0;
1218 BOOL unixabsolute
= *longpath
== '/';
1220 TRACE("%s\n", debugstr_w(longpath
));
1223 SetLastError(ERROR_INVALID_PARAMETER
);
1227 SetLastError(ERROR_BAD_PATHNAME
);
1231 /* check for drive letter */
1232 if (!unixabsolute
&& longpath
[1] == ':' ) {
1233 tmpshortpath
[0] = longpath
[0];
1234 tmpshortpath
[1] = ':';
1238 if ( ( drive
= DOSFS_GetPathDrive ( &longpath
)) == -1 ) return 0;
1239 flags
= DRIVE_GetFlags ( drive
);
1241 if (unixabsolute
&& drive
!= DRIVE_GetCurrentDrive()) {
1242 tmpshortpath
[0] = drive
+ 'A';
1243 tmpshortpath
[1] = ':';
1247 while ( longpath
[lp
] ) {
1249 /* check for path delimiters and reproduce them */
1250 if ( longpath
[lp
] == '\\' || longpath
[lp
] == '/' ) {
1251 if (!sp
|| tmpshortpath
[sp
-1]!= '\\')
1253 /* strip double "\\" */
1254 tmpshortpath
[sp
] = '\\';
1257 tmpshortpath
[sp
]=0;/*terminate string*/
1263 for(p
= longpath
+ lp
; *p
&& *p
!= '/' && *p
!= '\\'; p
++)
1265 lstrcpynW(tmpshortpath
+ sp
, longpath
+ lp
, tmplen
+ 1);
1267 /* Check, if the current element is a valid dos name */
1268 if ( DOSFS_ValidDOSName ( longpath
+ lp
, !(flags
& DRIVE_CASE_SENSITIVE
) ) ) {
1274 /* Check if the file exists and use the existing file name */
1275 if ( DOSFS_GetFullName ( tmpshortpath
, TRUE
, &full_name
) ) {
1276 strcpyW(tmpshortpath
+ sp
, strrchrW(full_name
.short_name
, '\\') + 1);
1277 sp
+= strlenW(tmpshortpath
+ sp
);
1282 TRACE("not found!\n" );
1283 SetLastError ( ERROR_FILE_NOT_FOUND
);
1286 tmpshortpath
[sp
] = 0;
1288 tmplen
= strlenW(tmpshortpath
) + 1;
1289 if (tmplen
<= shortlen
)
1291 strcpyW(shortpath
, tmpshortpath
);
1292 TRACE("returning %s\n", debugstr_w(shortpath
));
1293 tmplen
--; /* length without 0 */
1300 /***********************************************************************
1301 * GetShortPathNameA (KERNEL32.@)
1303 DWORD WINAPI
GetShortPathNameA( LPCSTR longpath
, LPSTR shortpath
, DWORD shortlen
)
1305 UNICODE_STRING longpathW
;
1306 WCHAR shortpathW
[MAX_PATH
];
1311 SetLastError(ERROR_INVALID_PARAMETER
);
1315 TRACE("%s\n", debugstr_a(longpath
));
1317 if (!RtlCreateUnicodeStringFromAsciiz(&longpathW
, longpath
))
1319 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1323 retW
= GetShortPathNameW(longpathW
.Buffer
, shortpathW
, MAX_PATH
);
1327 else if (retW
> MAX_PATH
)
1329 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
1334 ret
= WideCharToMultiByte(CP_ACP
, 0, shortpathW
, -1, NULL
, 0, NULL
, NULL
);
1335 if (ret
<= shortlen
)
1337 WideCharToMultiByte(CP_ACP
, 0, shortpathW
, -1, shortpath
, shortlen
, NULL
, NULL
);
1338 ret
--; /* length without 0 */
1342 RtlFreeUnicodeString(&longpathW
);
1347 /***********************************************************************
1348 * GetLongPathNameW (KERNEL32.@)
1351 * observed (Win2000):
1352 * shortpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1353 * shortpath="": LastError=ERROR_PATH_NOT_FOUND, ret=0
1355 DWORD WINAPI
GetLongPathNameW( LPCWSTR shortpath
, LPWSTR longpath
, DWORD longlen
)
1357 DOS_FULL_NAME full_name
;
1365 SetLastError(ERROR_INVALID_PARAMETER
);
1368 if (!shortpath
[0]) {
1369 SetLastError(ERROR_PATH_NOT_FOUND
);
1373 TRACE("%s,%p,%ld\n", debugstr_w(shortpath
), longpath
, longlen
);
1375 if(shortpath
[0]=='\\' && shortpath
[1]=='\\')
1377 ERR("UNC pathname %s\n",debugstr_w(shortpath
));
1378 lstrcpynW( longpath
, full_name
.short_name
, longlen
);
1379 return strlenW(longpath
);
1382 if (!DOSFS_GetFullName( shortpath
, TRUE
, &full_name
)) return 0;
1384 root
= full_name
.long_name
;
1385 drive
= DRIVE_FindDriveRoot(&root
);
1386 codepage
= DRIVE_GetCodepage(drive
);
1388 ret
= MultiByteToWideChar(codepage
, 0, root
, -1, NULL
, 0);
1390 /* reproduce terminating slash */
1391 if (ret
> 4) /* if not drive root */
1393 len
= strlenW(shortpath
);
1394 if (shortpath
[len
- 1] == '\\' || shortpath
[len
- 1] == '/')
1400 longpath
[0] = 'A' + drive
;
1402 MultiByteToWideChar(codepage
, 0, root
, -1, longpath
+ 2, longlen
- 2);
1403 for (p
= longpath
; *p
; p
++) if (*p
== '/') *p
= '\\';
1406 longpath
[ret
- 2] = '\\';
1407 longpath
[ret
- 1] = 0;
1409 TRACE("returning %s\n", debugstr_w(longpath
));
1410 ret
--; /* length without 0 */
1416 /***********************************************************************
1417 * GetLongPathNameA (KERNEL32.@)
1419 DWORD WINAPI
GetLongPathNameA( LPCSTR shortpath
, LPSTR longpath
, DWORD longlen
)
1421 UNICODE_STRING shortpathW
;
1422 WCHAR longpathW
[MAX_PATH
];
1427 SetLastError(ERROR_INVALID_PARAMETER
);
1431 TRACE("%s\n", debugstr_a(shortpath
));
1433 if (!RtlCreateUnicodeStringFromAsciiz(&shortpathW
, shortpath
))
1435 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1439 retW
= GetLongPathNameW(shortpathW
.Buffer
, longpathW
, MAX_PATH
);
1443 else if (retW
> MAX_PATH
)
1445 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
1450 ret
= WideCharToMultiByte(CP_ACP
, 0, longpathW
, -1, NULL
, 0, NULL
, NULL
);
1453 WideCharToMultiByte(CP_ACP
, 0, longpathW
, -1, longpath
, longlen
, NULL
, NULL
);
1454 ret
--; /* length without 0 */
1458 RtlFreeUnicodeString(&shortpathW
);
1463 /***********************************************************************
1464 * DOSFS_DoGetFullPathName
1466 * Implementation of GetFullPathNameA/W.
1468 * bon@elektron 000331:
1469 * A test for GetFullPathName with many pathological cases
1470 * now gives identical output for Wine and OSR2
1472 static DWORD
DOSFS_DoGetFullPathName( LPCWSTR name
, DWORD len
, LPWSTR result
)
1475 DOS_FULL_NAME full_name
;
1479 WCHAR drivecur
[] = {'C',':','.',0};
1480 WCHAR driveletter
=0;
1481 int namelen
,drive
=0;
1482 static const WCHAR bkslashW
[] = {'\\',0};
1483 static const WCHAR dotW
[] = {'.',0};
1484 static const WCHAR updir_slashW
[] = {'\\','.','.','\\',0};
1485 static const WCHAR curdirW
[] = {'\\','.','\\',0};
1486 static const WCHAR updirW
[] = {'\\','.','.',0};
1490 SetLastError(ERROR_BAD_PATHNAME
);
1494 TRACE("passed %s\n", debugstr_w(name
));
1497 /*drive letter given */
1499 driveletter
= name
[0];
1501 if ((name
[1]==':') && ((name
[2]=='\\') || (name
[2]=='/')))
1502 /*absolute path given */
1504 strncpyW(full_name
.short_name
, name
, MAX_PATHNAME_LEN
);
1505 full_name
.short_name
[MAX_PATHNAME_LEN
- 1] = 0; /* ensure 0 termination */
1506 drive
= toupperW(name
[0]) - 'A';
1511 drivecur
[0]=driveletter
;
1512 else if ((name
[0]=='\\') || (name
[0]=='/'))
1513 strcpyW(drivecur
, bkslashW
);
1515 strcpyW(drivecur
, dotW
);
1517 if (!DOSFS_GetFullName( drivecur
, FALSE
, &full_name
))
1519 FIXME("internal: error getting drive/path\n");
1522 /* find path that drive letter substitutes*/
1523 drive
= toupperW(full_name
.short_name
[0]) - 'A';
1524 root
= DRIVE_GetRoot(drive
);
1527 FIXME("internal: error getting DOS Drive Root\n");
1530 if (!strcmp(root
,"/"))
1532 /* we have just the last / and we need it. */
1533 p_l
= full_name
.long_name
;
1537 p_l
= full_name
.long_name
+ strlen(root
);
1539 /* append long name (= unix name) to drive */
1540 MultiByteToWideChar(DRIVE_GetCodepage(drive
), 0, p_l
, -1,
1541 full_name
.short_name
+ 2, MAX_PATHNAME_LEN
- 3);
1542 /* append name to treat */
1543 namelen
= strlenW(full_name
.short_name
);
1546 p
+= 2; /* skip drive name when appending */
1547 if (namelen
+ 2 + strlenW(p
) > MAX_PATHNAME_LEN
)
1549 FIXME("internal error: buffer too small\n");
1552 full_name
.short_name
[namelen
++] ='\\';
1553 full_name
.short_name
[namelen
] = 0;
1554 strncpyW(full_name
.short_name
+ namelen
, p
, MAX_PATHNAME_LEN
- namelen
);
1555 full_name
.short_name
[MAX_PATHNAME_LEN
- 1] = 0; /* ensure 0 termination */
1557 /* reverse all slashes */
1558 for (p
=full_name
.short_name
;
1559 p
< full_name
.short_name
+ strlenW(full_name
.short_name
);
1565 /* Use memmove, as areas overlap */
1567 while ((p
= strstrW(full_name
.short_name
, updir_slashW
)))
1569 if (p
> full_name
.short_name
+2)
1572 q
= strrchrW(full_name
.short_name
, '\\');
1573 memmove(q
+1, p
+4, (strlenW(p
+4)+1) * sizeof(WCHAR
));
1577 memmove(full_name
.short_name
+3, p
+4, (strlenW(p
+4)+1) * sizeof(WCHAR
));
1580 if ((full_name
.short_name
[2]=='.')&&(full_name
.short_name
[3]=='.'))
1582 /* This case istn't treated yet : c:..\test */
1583 memmove(full_name
.short_name
+2,full_name
.short_name
+4,
1584 (strlenW(full_name
.short_name
+4)+1) * sizeof(WCHAR
));
1587 while ((p
= strstrW(full_name
.short_name
, curdirW
)))
1590 memmove(p
+1, p
+3, (strlenW(p
+3)+1) * sizeof(WCHAR
));
1592 if (!(DRIVE_GetFlags(drive
) & DRIVE_CASE_PRESERVING
))
1593 for (p
= full_name
.short_name
; *p
; p
++) *p
= toupperW(*p
);
1594 namelen
= strlenW(full_name
.short_name
);
1595 if (!strcmpW(full_name
.short_name
+namelen
-3, updirW
))
1597 /* one more strange case: "c:\test\test1\.."
1599 *(full_name
.short_name
+namelen
-3)=0;
1600 q
= strrchrW(full_name
.short_name
, '\\');
1603 if (full_name
.short_name
[namelen
-1]=='.')
1604 full_name
.short_name
[(namelen
--)-1] =0;
1606 if (full_name
.short_name
[namelen
-1]=='\\')
1607 full_name
.short_name
[(namelen
--)-1] =0;
1608 TRACE("got %s\n", debugstr_w(full_name
.short_name
));
1610 /* If the lpBuffer buffer is too small, the return value is the
1611 size of the buffer, in characters, required to hold the path
1612 plus the terminating \0 (tested against win95osr2, bon 001118)
1614 ret
= strlenW(full_name
.short_name
);
1617 /* don't touch anything when the buffer is not large enough */
1618 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
1623 strncpyW( result
, full_name
.short_name
, len
);
1624 result
[len
- 1] = 0; /* ensure 0 termination */
1627 TRACE("returning %s\n", debugstr_w(full_name
.short_name
) );
1632 /***********************************************************************
1633 * GetFullPathNameA (KERNEL32.@)
1635 * if the path closed with '\', *lastpart is 0
1637 DWORD WINAPI
GetFullPathNameA( LPCSTR name
, DWORD len
, LPSTR buffer
,
1640 UNICODE_STRING nameW
;
1641 WCHAR bufferW
[MAX_PATH
];
1646 SetLastError(ERROR_INVALID_PARAMETER
);
1650 if (!RtlCreateUnicodeStringFromAsciiz(&nameW
, name
))
1652 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1656 retW
= GetFullPathNameW( nameW
.Buffer
, MAX_PATH
, bufferW
, NULL
);
1660 else if (retW
> MAX_PATH
)
1662 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
1667 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, -1, NULL
, 0, NULL
, NULL
);
1670 WideCharToMultiByte(CP_ACP
, 0, bufferW
, -1, buffer
, len
, NULL
, NULL
);
1671 ret
--; /* length without 0 */
1675 LPSTR p
= buffer
+ strlen(buffer
);
1679 while ((p
> buffer
+ 2) && (*p
!= '\\')) p
--;
1682 else *lastpart
= NULL
;
1687 RtlFreeUnicodeString(&nameW
);
1692 /***********************************************************************
1693 * GetFullPathNameW (KERNEL32.@)
1695 DWORD WINAPI
GetFullPathNameW( LPCWSTR name
, DWORD len
, LPWSTR buffer
,
1698 DWORD ret
= DOSFS_DoGetFullPathName( name
, len
, buffer
);
1699 if (ret
&& (ret
<=len
) && buffer
&& lastpart
)
1701 LPWSTR p
= buffer
+ strlenW(buffer
);
1702 if (*p
!= (WCHAR
)'\\')
1704 while ((p
> buffer
+ 2) && (*p
!= (WCHAR
)'\\')) p
--;
1707 else *lastpart
= NULL
;
1713 /***********************************************************************
1714 * wine_get_unix_file_name (KERNEL32.@) Not a Windows API
1716 * Return the full Unix file name for a given path.
1717 * FIXME: convert dos file name to unicode
1719 BOOL WINAPI
wine_get_unix_file_name( LPCSTR dos
, LPSTR buffer
, DWORD len
)
1723 WCHAR dosW
[MAX_PATHNAME_LEN
];
1725 MultiByteToWideChar(CP_ACP
, 0, dos
, -1, dosW
, MAX_PATHNAME_LEN
);
1726 ret
= DOSFS_GetFullName( dosW
, FALSE
, &path
);
1729 strncpy( buffer
, path
.long_name
, len
);
1730 buffer
[len
- 1] = 0; /* ensure 0 termination */
1736 /***********************************************************************
1737 * get_show_dir_symlinks_option
1739 static BOOL
get_show_dir_symlinks_option(void)
1741 static const WCHAR WineW
[] = {'M','a','c','h','i','n','e','\\',
1742 'S','o','f','t','w','a','r','e','\\',
1743 'W','i','n','e','\\','W','i','n','e','\\',
1744 'C','o','n','f','i','g','\\','W','i','n','e',0};
1745 static const WCHAR ShowDirSymlinksW
[] = {'S','h','o','w','D','i','r','S','y','m','l','i','n','k','s',0};
1750 OBJECT_ATTRIBUTES attr
;
1751 UNICODE_STRING nameW
;
1754 attr
.Length
= sizeof(attr
);
1755 attr
.RootDirectory
= 0;
1756 attr
.ObjectName
= &nameW
;
1757 attr
.Attributes
= 0;
1758 attr
.SecurityDescriptor
= NULL
;
1759 attr
.SecurityQualityOfService
= NULL
;
1760 RtlInitUnicodeString( &nameW
, WineW
);
1762 if (!NtOpenKey( &hkey
, KEY_ALL_ACCESS
, &attr
))
1764 RtlInitUnicodeString( &nameW
, ShowDirSymlinksW
);
1765 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
1767 WCHAR
*str
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
1768 ret
= IS_OPTION_TRUE( str
[0] );
1776 /***********************************************************************
1779 static int DOSFS_FindNextEx( FIND_FIRST_INFO
*info
, WIN32_FIND_DATAW
*entry
)
1781 DWORD attr
= info
->attr
| FA_UNUSED
| FA_ARCHIVE
| FA_RDONLY
;
1782 UINT flags
= DRIVE_GetFlags( info
->drive
);
1783 char *p
, buffer
[MAX_PATHNAME_LEN
];
1784 const char *drive_path
;
1786 LPCWSTR long_name
, short_name
;
1787 BY_HANDLE_FILE_INFORMATION fileinfo
;
1791 if ((info
->attr
& ~(FA_UNUSED
| FA_ARCHIVE
| FA_RDONLY
)) == FA_LABEL
)
1793 if (info
->cur_pos
) return 0;
1794 entry
->dwFileAttributes
= FILE_ATTRIBUTE_LABEL
;
1795 RtlSecondsSince1970ToTime( (time_t)0, (LARGE_INTEGER
*)&entry
->ftCreationTime
);
1796 RtlSecondsSince1970ToTime( (time_t)0, (LARGE_INTEGER
*)&entry
->ftLastAccessTime
);
1797 RtlSecondsSince1970ToTime( (time_t)0, (LARGE_INTEGER
*)&entry
->ftLastWriteTime
);
1798 entry
->nFileSizeHigh
= 0;
1799 entry
->nFileSizeLow
= 0;
1800 entry
->dwReserved0
= 0;
1801 entry
->dwReserved1
= 0;
1802 DOSFS_ToDosDTAFormat( DRIVE_GetLabel( info
->drive
), entry
->cFileName
);
1803 strcpyW( entry
->cAlternateFileName
, entry
->cFileName
);
1805 TRACE("returning %s (%s) as label\n",
1806 debugstr_w(entry
->cFileName
), debugstr_w(entry
->cAlternateFileName
));
1810 drive_path
= info
->path
+ strlen(DRIVE_GetRoot( info
->drive
));
1811 while ((*drive_path
== '/') || (*drive_path
== '\\')) drive_path
++;
1812 drive_root
= !*drive_path
;
1814 lstrcpynA( buffer
, info
->path
, sizeof(buffer
) - 1 );
1815 strcat( buffer
, "/" );
1816 p
= buffer
+ strlen(buffer
);
1818 while (DOSFS_ReadDir( info
->u
.dos_dir
, &long_name
, &short_name
))
1822 /* Don't return '.' and '..' in the root of the drive */
1823 if (drive_root
&& (long_name
[0] == '.') &&
1824 (!long_name
[1] || ((long_name
[1] == '.') && !long_name
[2])))
1827 /* Check the long mask */
1829 if (info
->long_mask
&& *info
->long_mask
)
1831 if (!DOSFS_MatchLong( info
->long_mask
, long_name
,
1832 flags
& DRIVE_CASE_SENSITIVE
)) continue;
1835 /* Check the short mask */
1837 if (info
->short_mask
)
1841 DOSFS_Hash( long_name
, dos_name
, TRUE
,
1842 !(flags
& DRIVE_CASE_SENSITIVE
) );
1843 short_name
= dos_name
;
1845 if (!DOSFS_MatchShort( info
->short_mask
, short_name
)) continue;
1848 /* Check the file attributes */
1849 WideCharToMultiByte(DRIVE_GetCodepage(info
->drive
), 0, long_name
, -1,
1850 p
, sizeof(buffer
) - (int)(p
- buffer
), NULL
, NULL
);
1851 if (!FILE_Stat( buffer
, &fileinfo
, &is_symlink
))
1853 WARN("can't stat %s\n", buffer
);
1856 if (is_symlink
&& (fileinfo
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
))
1858 static int show_dir_symlinks
= -1;
1859 if (show_dir_symlinks
== -1)
1860 show_dir_symlinks
= get_show_dir_symlinks_option();
1861 if (!show_dir_symlinks
) continue;
1864 if (fileinfo
.dwFileAttributes
& ~attr
) continue;
1866 /* We now have a matching entry; fill the result and return */
1868 entry
->dwFileAttributes
= fileinfo
.dwFileAttributes
;
1869 entry
->ftCreationTime
= fileinfo
.ftCreationTime
;
1870 entry
->ftLastAccessTime
= fileinfo
.ftLastAccessTime
;
1871 entry
->ftLastWriteTime
= fileinfo
.ftLastWriteTime
;
1872 entry
->nFileSizeHigh
= fileinfo
.nFileSizeHigh
;
1873 entry
->nFileSizeLow
= fileinfo
.nFileSizeLow
;
1876 DOSFS_ToDosDTAFormat( short_name
, entry
->cAlternateFileName
);
1878 DOSFS_Hash( long_name
, entry
->cAlternateFileName
, FALSE
,
1879 !(flags
& DRIVE_CASE_SENSITIVE
) );
1881 lstrcpynW( entry
->cFileName
, long_name
, sizeof(entry
->cFileName
)/sizeof(entry
->cFileName
[0]) );
1882 if (!(flags
& DRIVE_CASE_PRESERVING
)) strlwrW( entry
->cFileName
);
1883 TRACE("returning %s (%s) %02lx %ld\n",
1884 debugstr_w(entry
->cFileName
), debugstr_w(entry
->cAlternateFileName
),
1885 entry
->dwFileAttributes
, entry
->nFileSizeLow
);
1888 return 0; /* End of directory */
1891 /***********************************************************************
1894 * Find the next matching file. Return the number of entries read to find
1895 * the matching one, or 0 if no more entries.
1896 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
1897 * file name mask. Either or both can be NULL.
1899 * NOTE: This is supposed to be only called by the int21 emulation
1900 * routines. Thus, we should own the Win16Mutex anyway.
1901 * Nevertheless, we explicitly enter it to ensure the static
1902 * directory cache is protected.
1904 int DOSFS_FindNext( const char *path
, const char *short_mask
,
1905 const char *long_mask
, int drive
, BYTE attr
,
1906 int skip
, WIN32_FIND_DATAA
*entry
)
1908 static FIND_FIRST_INFO info
;
1909 LPCWSTR short_name
, long_name
;
1911 UNICODE_STRING short_maskW
, long_maskW
;
1912 WIN32_FIND_DATAW entryW
;
1914 TRACE("(%s, %s, %s, %x, %x, %x, %p)\n", debugstr_a(path
),
1915 debugstr_a(short_mask
), debugstr_a(long_mask
), drive
, attr
, skip
,
1920 RtlCreateUnicodeStringFromAsciiz(&short_maskW
, short_mask
);
1921 RtlCreateUnicodeStringFromAsciiz(&long_maskW
, long_mask
);
1923 /* Check the cached directory */
1924 if (!(info
.u
.dos_dir
&& info
.path
== path
&& !strcmpW(info
.short_mask
, short_maskW
.Buffer
)
1925 && !strcmpW(info
.long_mask
, long_maskW
.Buffer
) && info
.drive
== drive
1926 && info
.attr
== attr
&& info
.cur_pos
<= skip
))
1928 /* Not in the cache, open it anew */
1929 if (info
.u
.dos_dir
) DOSFS_CloseDir( info
.u
.dos_dir
);
1931 info
.path
= (LPSTR
)path
;
1932 RtlFreeHeap(GetProcessHeap(), 0, info
.long_mask
);
1933 RtlFreeHeap(GetProcessHeap(), 0, info
.short_mask
);
1934 info
.long_mask
= long_maskW
.Buffer
;
1935 info
.short_mask
= short_maskW
.Buffer
;
1939 info
.u
.dos_dir
= DOSFS_OpenDir( DRIVE_GetCodepage(drive
), info
.path
);
1943 RtlFreeUnicodeString(&short_maskW
);
1944 RtlFreeUnicodeString(&long_maskW
);
1947 /* Skip to desired position */
1948 while (info
.cur_pos
< skip
)
1949 if (info
.u
.dos_dir
&& DOSFS_ReadDir( info
.u
.dos_dir
, &long_name
, &short_name
))
1954 if (info
.u
.dos_dir
&& info
.cur_pos
== skip
&& DOSFS_FindNextEx( &info
, &entryW
))
1956 WideCharToMultiByte(CP_ACP
, 0, entryW
.cFileName
, -1,
1957 entry
->cFileName
, sizeof(entry
->cFileName
), NULL
, NULL
);
1958 WideCharToMultiByte(CP_ACP
, 0, entryW
.cAlternateFileName
, -1,
1959 entry
->cAlternateFileName
, sizeof(entry
->cAlternateFileName
), NULL
, NULL
);
1960 count
= info
.cur_pos
- skip
;
1962 entry
->dwFileAttributes
= entryW
.dwFileAttributes
;
1963 entry
->nFileSizeHigh
= entryW
.nFileSizeHigh
;
1964 entry
->nFileSizeLow
= entryW
.nFileSizeLow
;
1965 entry
->ftCreationTime
= entryW
.ftCreationTime
;
1966 entry
->ftLastAccessTime
= entryW
.ftLastAccessTime
;
1967 entry
->ftLastWriteTime
= entryW
.ftLastWriteTime
;
1975 if (info
.u
.dos_dir
) DOSFS_CloseDir( info
.u
.dos_dir
);
1976 memset( &info
, '\0', sizeof(info
) );
1984 /*************************************************************************
1985 * FindFirstFileExW (KERNEL32.@)
1987 HANDLE WINAPI
FindFirstFileExW(
1989 FINDEX_INFO_LEVELS fInfoLevelId
,
1990 LPVOID lpFindFileData
,
1991 FINDEX_SEARCH_OPS fSearchOp
,
1992 LPVOID lpSearchFilter
,
1993 DWORD dwAdditionalFlags
)
1996 FIND_FIRST_INFO
*info
;
2000 SetLastError(ERROR_PATH_NOT_FOUND
);
2001 return INVALID_HANDLE_VALUE
;
2004 if ((fSearchOp
!= FindExSearchNameMatch
) || (dwAdditionalFlags
!= 0))
2006 FIXME("options not implemented 0x%08x 0x%08lx\n", fSearchOp
, dwAdditionalFlags
);
2007 return INVALID_HANDLE_VALUE
;
2010 switch(fInfoLevelId
)
2012 case FindExInfoStandard
:
2014 WIN32_FIND_DATAW
* data
= (WIN32_FIND_DATAW
*) lpFindFileData
;
2019 data
->dwReserved0
= data
->dwReserved1
= 0x0;
2020 if (lpFileName
[0] == '\\' && lpFileName
[1] == '\\')
2022 ERR("UNC path name\n");
2023 if (!(handle
= GlobalAlloc(GMEM_MOVEABLE
, sizeof(FIND_FIRST_INFO
)))) break;
2025 info
= (FIND_FIRST_INFO
*)GlobalLock( handle
);
2026 info
->u
.smb_dir
= SMB_FindFirst(lpFileName
);
2027 if(!info
->u
.smb_dir
)
2029 GlobalUnlock( handle
);
2036 GlobalUnlock( handle
);
2040 DOS_FULL_NAME full_name
;
2042 if (lpFileName
[0] && lpFileName
[1] == ':')
2044 /* don't allow root directories */
2045 if (!lpFileName
[2] ||
2046 ((lpFileName
[2] == '/' || lpFileName
[2] == '\\') && !lpFileName
[3]))
2048 SetLastError(ERROR_FILE_NOT_FOUND
);
2049 return INVALID_HANDLE_VALUE
;
2052 if (!DOSFS_GetFullName( lpFileName
, FALSE
, &full_name
)) break;
2053 if (!(handle
= GlobalAlloc(GMEM_MOVEABLE
, sizeof(FIND_FIRST_INFO
)))) break;
2054 info
= (FIND_FIRST_INFO
*)GlobalLock( handle
);
2055 info
->path
= HeapAlloc( GetProcessHeap(), 0, strlen(full_name
.long_name
)+1 );
2056 strcpy( info
->path
, full_name
.long_name
);
2058 codepage
= DRIVE_GetCodepage(full_name
.drive
);
2059 p
= strrchr( info
->path
, '/' );
2061 long_mask_len
= MultiByteToWideChar(codepage
, 0, p
, -1, NULL
, 0);
2062 info
->long_mask
= HeapAlloc( GetProcessHeap(), 0, long_mask_len
* sizeof(WCHAR
) );
2063 MultiByteToWideChar(codepage
, 0, p
, -1, info
->long_mask
, long_mask_len
);
2065 info
->short_mask
= NULL
;
2067 info
->drive
= full_name
.drive
;
2070 info
->u
.dos_dir
= DOSFS_OpenDir( codepage
, info
->path
);
2071 GlobalUnlock( handle
);
2073 if (!FindNextFileW( handle
, data
))
2075 FindClose( handle
);
2076 SetLastError( ERROR_FILE_NOT_FOUND
);
2083 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId
);
2085 return INVALID_HANDLE_VALUE
;
2088 /*************************************************************************
2089 * FindFirstFileA (KERNEL32.@)
2091 HANDLE WINAPI
FindFirstFileA(
2093 WIN32_FIND_DATAA
*lpFindData
)
2095 return FindFirstFileExA(lpFileName
, FindExInfoStandard
, lpFindData
,
2096 FindExSearchNameMatch
, NULL
, 0);
2099 /*************************************************************************
2100 * FindFirstFileExA (KERNEL32.@)
2102 HANDLE WINAPI
FindFirstFileExA(
2104 FINDEX_INFO_LEVELS fInfoLevelId
,
2105 LPVOID lpFindFileData
,
2106 FINDEX_SEARCH_OPS fSearchOp
,
2107 LPVOID lpSearchFilter
,
2108 DWORD dwAdditionalFlags
)
2111 WIN32_FIND_DATAA
*dataA
;
2112 WIN32_FIND_DATAW dataW
;
2113 UNICODE_STRING pathW
;
2117 SetLastError(ERROR_PATH_NOT_FOUND
);
2118 return INVALID_HANDLE_VALUE
;
2121 if (!RtlCreateUnicodeStringFromAsciiz(&pathW
, lpFileName
))
2123 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
2124 return INVALID_HANDLE_VALUE
;
2127 handle
= FindFirstFileExW(pathW
.Buffer
, fInfoLevelId
, &dataW
, fSearchOp
, lpSearchFilter
, dwAdditionalFlags
);
2128 RtlFreeUnicodeString(&pathW
);
2129 if (handle
== INVALID_HANDLE_VALUE
) return handle
;
2131 dataA
= (WIN32_FIND_DATAA
*) lpFindFileData
;
2132 dataA
->dwFileAttributes
= dataW
.dwFileAttributes
;
2133 dataA
->ftCreationTime
= dataW
.ftCreationTime
;
2134 dataA
->ftLastAccessTime
= dataW
.ftLastAccessTime
;
2135 dataA
->ftLastWriteTime
= dataW
.ftLastWriteTime
;
2136 dataA
->nFileSizeHigh
= dataW
.nFileSizeHigh
;
2137 dataA
->nFileSizeLow
= dataW
.nFileSizeLow
;
2138 WideCharToMultiByte( CP_ACP
, 0, dataW
.cFileName
, -1,
2139 dataA
->cFileName
, sizeof(dataA
->cFileName
), NULL
, NULL
);
2140 WideCharToMultiByte( CP_ACP
, 0, dataW
.cAlternateFileName
, -1,
2141 dataA
->cAlternateFileName
, sizeof(dataA
->cAlternateFileName
), NULL
, NULL
);
2145 /*************************************************************************
2146 * FindFirstFileW (KERNEL32.@)
2148 HANDLE WINAPI
FindFirstFileW( LPCWSTR lpFileName
, WIN32_FIND_DATAW
*lpFindData
)
2150 return FindFirstFileExW(lpFileName
, FindExInfoStandard
, lpFindData
,
2151 FindExSearchNameMatch
, NULL
, 0);
2154 /*************************************************************************
2155 * FindNextFileW (KERNEL32.@)
2157 BOOL WINAPI
FindNextFileW( HANDLE handle
, WIN32_FIND_DATAW
*data
)
2159 FIND_FIRST_INFO
*info
;
2161 DWORD gle
= ERROR_NO_MORE_FILES
;
2163 if ((handle
== INVALID_HANDLE_VALUE
) ||
2164 !(info
= (FIND_FIRST_INFO
*)GlobalLock( handle
)))
2166 SetLastError( ERROR_INVALID_HANDLE
);
2169 if (info
->drive
== -1)
2171 ret
= SMB_FindNext( info
->u
.smb_dir
, data
);
2174 SMB_CloseDir( info
->u
.smb_dir
);
2175 HeapFree( GetProcessHeap(), 0, info
->path
);
2179 else if (!info
->path
|| !info
->u
.dos_dir
)
2183 else if (!DOSFS_FindNextEx( info
, data
))
2185 DOSFS_CloseDir( info
->u
.dos_dir
); info
->u
.dos_dir
= NULL
;
2186 HeapFree( GetProcessHeap(), 0, info
->path
);
2188 HeapFree( GetProcessHeap(), 0, info
->long_mask
);
2189 info
->long_mask
= NULL
;
2194 GlobalUnlock( handle
);
2195 if( !ret
) SetLastError( gle
);
2200 /*************************************************************************
2201 * FindNextFileA (KERNEL32.@)
2203 BOOL WINAPI
FindNextFileA( HANDLE handle
, WIN32_FIND_DATAA
*data
)
2205 WIN32_FIND_DATAW dataW
;
2206 if (!FindNextFileW( handle
, &dataW
)) return FALSE
;
2207 data
->dwFileAttributes
= dataW
.dwFileAttributes
;
2208 data
->ftCreationTime
= dataW
.ftCreationTime
;
2209 data
->ftLastAccessTime
= dataW
.ftLastAccessTime
;
2210 data
->ftLastWriteTime
= dataW
.ftLastWriteTime
;
2211 data
->nFileSizeHigh
= dataW
.nFileSizeHigh
;
2212 data
->nFileSizeLow
= dataW
.nFileSizeLow
;
2213 WideCharToMultiByte( CP_ACP
, 0, dataW
.cFileName
, -1,
2214 data
->cFileName
, sizeof(data
->cFileName
), NULL
, NULL
);
2215 WideCharToMultiByte( CP_ACP
, 0, dataW
.cAlternateFileName
, -1,
2216 data
->cAlternateFileName
,
2217 sizeof(data
->cAlternateFileName
), NULL
, NULL
);
2221 /*************************************************************************
2222 * FindClose (KERNEL32.@)
2224 BOOL WINAPI
FindClose( HANDLE handle
)
2226 FIND_FIRST_INFO
*info
;
2228 if (handle
== INVALID_HANDLE_VALUE
) goto error
;
2232 if ((info
= (FIND_FIRST_INFO
*)GlobalLock( handle
)))
2234 if (info
->u
.dos_dir
) DOSFS_CloseDir( info
->u
.dos_dir
);
2235 if (info
->path
) HeapFree( GetProcessHeap(), 0, info
->path
);
2236 if (info
->long_mask
) HeapFree( GetProcessHeap(), 0, info
->long_mask
);
2239 __EXCEPT(page_fault
)
2241 WARN("Illegal handle %p\n", handle
);
2242 SetLastError( ERROR_INVALID_HANDLE
);
2246 if (!info
) goto error
;
2247 GlobalUnlock( handle
);
2248 GlobalFree( handle
);
2252 SetLastError( ERROR_INVALID_HANDLE
);
2256 /***********************************************************************
2257 * MulDiv (KERNEL32.@)
2259 * Result of multiplication and division
2260 * -1: Overflow occurred or Divisor was 0
2267 #if SIZEOF_LONG_LONG >= 8
2270 if (!nDivisor
) return -1;
2272 /* We want to deal with a positive divisor to simplify the logic. */
2275 nMultiplicand
= - nMultiplicand
;
2276 nDivisor
= -nDivisor
;
2279 /* If the result is positive, we "add" to round. else, we subtract to round. */
2280 if ( ( (nMultiplicand
< 0) && (nMultiplier
< 0) ) ||
2281 ( (nMultiplicand
>= 0) && (nMultiplier
>= 0) ) )
2282 ret
= (((long long)nMultiplicand
* nMultiplier
) + (nDivisor
/2)) / nDivisor
;
2284 ret
= (((long long)nMultiplicand
* nMultiplier
) - (nDivisor
/2)) / nDivisor
;
2286 if ((ret
> 2147483647) || (ret
< -2147483647)) return -1;
2289 if (!nDivisor
) return -1;
2291 /* We want to deal with a positive divisor to simplify the logic. */
2294 nMultiplicand
= - nMultiplicand
;
2295 nDivisor
= -nDivisor
;
2298 /* If the result is positive, we "add" to round. else, we subtract to round. */
2299 if ( ( (nMultiplicand
< 0) && (nMultiplier
< 0) ) ||
2300 ( (nMultiplicand
>= 0) && (nMultiplier
>= 0) ) )
2301 return ((nMultiplicand
* nMultiplier
) + (nDivisor
/2)) / nDivisor
;
2303 return ((nMultiplicand
* nMultiplier
) - (nDivisor
/2)) / nDivisor
;
2309 /***********************************************************************
2310 * DosDateTimeToFileTime (KERNEL32.@)
2312 BOOL WINAPI
DosDateTimeToFileTime( WORD fatdate
, WORD fattime
, LPFILETIME ft
)
2317 time_t time1
, time2
;
2320 newtm
.tm_sec
= (fattime
& 0x1f) * 2;
2321 newtm
.tm_min
= (fattime
>> 5) & 0x3f;
2322 newtm
.tm_hour
= (fattime
>> 11);
2323 newtm
.tm_mday
= (fatdate
& 0x1f);
2324 newtm
.tm_mon
= ((fatdate
>> 5) & 0x0f) - 1;
2325 newtm
.tm_year
= (fatdate
>> 9) + 80;
2327 RtlSecondsSince1970ToTime( timegm(&newtm
), (LARGE_INTEGER
*)ft
);
2329 time1
= mktime(&newtm
);
2330 gtm
= gmtime(&time1
);
2331 time2
= mktime(gtm
);
2332 RtlSecondsSince1970ToTime( 2*time1
-time2
, (LARGE_INTEGER
*)ft
);
2338 /***********************************************************************
2339 * FileTimeToDosDateTime (KERNEL32.@)
2341 BOOL WINAPI
FileTimeToDosDateTime( const FILETIME
*ft
, LPWORD fatdate
,
2349 li
.s
.LowPart
= ft
->dwLowDateTime
;
2350 li
.s
.HighPart
= ft
->dwHighDateTime
;
2351 RtlTimeToSecondsSince1970( &li
, &t
);
2353 tm
= gmtime( &unixtime
);
2355 *fattime
= (tm
->tm_hour
<< 11) + (tm
->tm_min
<< 5) + (tm
->tm_sec
/ 2);
2357 *fatdate
= ((tm
->tm_year
- 80) << 9) + ((tm
->tm_mon
+ 1) << 5)
2363 /***********************************************************************
2364 * QueryDosDeviceA (KERNEL32.@)
2366 * returns array of strings terminated by \0, terminated by \0
2368 DWORD WINAPI
QueryDosDeviceA(LPCSTR devname
,LPSTR target
,DWORD bufsize
)
2370 DWORD ret
= 0, retW
;
2371 LPWSTR targetW
= (LPWSTR
)HeapAlloc(GetProcessHeap(),0,
2372 bufsize
* sizeof(WCHAR
));
2373 UNICODE_STRING devnameW
;
2375 if(devname
) RtlCreateUnicodeStringFromAsciiz(&devnameW
, devname
);
2376 else devnameW
.Buffer
= NULL
;
2378 retW
= QueryDosDeviceW(devnameW
.Buffer
, targetW
, bufsize
);
2380 ret
= WideCharToMultiByte(CP_ACP
, 0, targetW
, retW
, target
,
2381 bufsize
, NULL
, NULL
);
2383 RtlFreeUnicodeString(&devnameW
);
2384 if (targetW
) HeapFree(GetProcessHeap(),0,targetW
);
2389 /***********************************************************************
2390 * QueryDosDeviceW (KERNEL32.@)
2392 * returns array of strings terminated by \0, terminated by \0
2395 * - Win9x returns for all calls ERROR_INVALID_PARAMETER
2396 * - the returned devices for devname == NULL is far from complete
2397 * - its not checked that the returned device exist
2399 DWORD WINAPI
QueryDosDeviceW(LPCWSTR devname
,LPWSTR target
,DWORD bufsize
)
2401 const WCHAR
*pDev
, *pName
, *pNum
= NULL
;
2405 TRACE("(%s,...)\n", debugstr_w(devname
));
2407 /* return known MSDOS devices */
2410 static const WCHAR devices
[][5] = {{'A','U','X',0},
2411 {'C','O','M','1',0},
2412 {'C','O','M','2',0},
2413 {'L','P','T','1',0},
2415 for(i
=0; (i
< (sizeof(devices
)/sizeof(devices
[0]))); i
++) {
2416 DWORD len
= strlenW(devices
[i
]);
2417 if(target
&& (bufsize
>= ret
+ len
+ 2)) {
2418 lstrcpyW(target
+ret
, devices
[i
]);
2421 /* in this case WinXP returns 0 */
2422 FIXME("function return is wrong for WinXP!\n");
2423 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
2427 /* append drives here */
2428 if(target
&& bufsize
> 0) target
[ret
++] = 0;
2429 FIXME("Returned list is not complete\n");
2432 /* In theory all that are possible and have been defined.
2433 * Now just those below, since mirc uses it to check for special files.
2435 * (It is more complex, and supports netmounted stuff, and \\.\ stuff,
2436 * but currently we just ignore that.)
2438 if (!strcmpiW(devname
, auxW
)) {
2443 } else if (!strcmpiW(devname
, nulW
)) {
2446 } else if (!strncmpiW(devname
, comW
, strlenW(comW
))) {
2449 pNum
= devname
+ strlenW(comW
);
2450 for(numsiz
=0; isdigitW(*(pNum
+numsiz
)); numsiz
++);
2451 if(*(pNum
+ numsiz
)) {
2452 SetLastError(ERROR_FILE_NOT_FOUND
);
2455 } else if (!strncmpiW(devname
, lptW
, strlenW(lptW
))) {
2458 pNum
= devname
+ strlenW(lptW
);
2459 for(numsiz
=0; isdigitW(*(pNum
+numsiz
)); numsiz
++);
2460 if(*(pNum
+ numsiz
)) {
2461 SetLastError(ERROR_FILE_NOT_FOUND
);
2465 /* This might be a DOS device we do not handle yet ... */
2466 FIXME("(%s) not detected as DOS device!\n",debugstr_w(devname
));
2468 /* Win9x set the error ERROR_INVALID_PARAMETER */
2469 SetLastError(ERROR_FILE_NOT_FOUND
);
2472 FIXME("device %s may not exist on this computer\n", debugstr_w(devname
));
2474 ret
= strlenW(pDev
) + strlenW(pName
) + numsiz
+ 2;
2475 if (ret
> bufsize
) ret
= 0;
2476 if (target
&& ret
) {
2477 lstrcpyW(target
,pDev
);
2478 lstrcatW(target
,pName
);
2479 if (pNum
) lstrcatW(target
,pNum
);
2486 /***********************************************************************
2487 * DefineDosDeviceA (KERNEL32.@)
2489 BOOL WINAPI
DefineDosDeviceA(DWORD flags
,LPCSTR devname
,LPCSTR targetpath
) {
2490 FIXME("(0x%08lx,%s,%s),stub!\n",flags
,devname
,targetpath
);
2491 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
2496 --- 16 bit functions ---
2499 /*************************************************************************
2500 * FindFirstFile (KERNEL.413)
2502 HANDLE16 WINAPI
FindFirstFile16( LPCSTR path
, WIN32_FIND_DATAA
*data
)
2504 DOS_FULL_NAME full_name
;
2506 FIND_FIRST_INFO
*info
;
2507 WCHAR pathW
[MAX_PATH
];
2512 data
->dwReserved0
= data
->dwReserved1
= 0x0;
2513 if (!path
) return INVALID_HANDLE_VALUE16
;
2514 MultiByteToWideChar(CP_ACP
, 0, path
, -1, pathW
, MAX_PATH
);
2515 if (!DOSFS_GetFullName( pathW
, FALSE
, &full_name
))
2516 return INVALID_HANDLE_VALUE16
;
2517 if (!(handle
= GlobalAlloc16( GMEM_MOVEABLE
, sizeof(FIND_FIRST_INFO
) )))
2518 return INVALID_HANDLE_VALUE16
;
2519 info
= (FIND_FIRST_INFO
*)GlobalLock16( handle
);
2520 info
->path
= HeapAlloc( GetProcessHeap(), 0, strlen(full_name
.long_name
)+1 );
2521 strcpy( info
->path
, full_name
.long_name
);
2523 codepage
= DRIVE_GetCodepage(full_name
.drive
);
2524 p
= strrchr( info
->path
, '/' );
2526 long_mask_len
= MultiByteToWideChar(codepage
, 0, p
, -1, NULL
, 0);
2527 info
->long_mask
= HeapAlloc( GetProcessHeap(), 0, long_mask_len
* sizeof(WCHAR
) );
2528 MultiByteToWideChar(codepage
, 0, p
, -1, info
->long_mask
, long_mask_len
);
2530 info
->short_mask
= NULL
;
2532 info
->drive
= full_name
.drive
;
2535 info
->u
.dos_dir
= DOSFS_OpenDir( codepage
, info
->path
);
2537 GlobalUnlock16( handle
);
2538 if (!FindNextFile16( handle
, data
))
2540 FindClose16( handle
);
2541 SetLastError( ERROR_NO_MORE_FILES
);
2542 return INVALID_HANDLE_VALUE16
;
2547 /*************************************************************************
2548 * FindNextFile (KERNEL.414)
2550 BOOL16 WINAPI
FindNextFile16( HANDLE16 handle
, WIN32_FIND_DATAA
*data
)
2552 FIND_FIRST_INFO
*info
;
2553 WIN32_FIND_DATAW dataW
;
2555 DWORD gle
= ERROR_NO_MORE_FILES
;
2557 if ((handle
== INVALID_HANDLE_VALUE16
) ||
2558 !(info
= (FIND_FIRST_INFO
*)GlobalLock16( handle
)))
2560 SetLastError( ERROR_INVALID_HANDLE
);
2563 if (!info
->path
|| !info
->u
.dos_dir
)
2567 if (!DOSFS_FindNextEx( info
, &dataW
))
2569 DOSFS_CloseDir( info
->u
.dos_dir
); info
->u
.dos_dir
= NULL
;
2570 HeapFree( GetProcessHeap(), 0, info
->path
);
2572 HeapFree( GetProcessHeap(), 0, info
->long_mask
);
2573 info
->long_mask
= NULL
;
2579 data
->dwFileAttributes
= dataW
.dwFileAttributes
;
2580 data
->ftCreationTime
= dataW
.ftCreationTime
;
2581 data
->ftLastAccessTime
= dataW
.ftLastAccessTime
;
2582 data
->ftLastWriteTime
= dataW
.ftLastWriteTime
;
2583 data
->nFileSizeHigh
= dataW
.nFileSizeHigh
;
2584 data
->nFileSizeLow
= dataW
.nFileSizeLow
;
2585 WideCharToMultiByte( CP_ACP
, 0, dataW
.cFileName
, -1,
2586 data
->cFileName
, sizeof(data
->cFileName
), NULL
, NULL
);
2587 WideCharToMultiByte( CP_ACP
, 0, dataW
.cAlternateFileName
, -1,
2588 data
->cAlternateFileName
,
2589 sizeof(data
->cAlternateFileName
), NULL
, NULL
);
2591 if( !ret
) SetLastError( gle
);
2592 GlobalUnlock16( handle
);
2597 /*************************************************************************
2598 * FindClose (KERNEL.415)
2600 BOOL16 WINAPI
FindClose16( HANDLE16 handle
)
2602 FIND_FIRST_INFO
*info
;
2604 if ((handle
== INVALID_HANDLE_VALUE16
) ||
2605 !(info
= (FIND_FIRST_INFO
*)GlobalLock16( handle
)))
2607 SetLastError( ERROR_INVALID_HANDLE
);
2610 if (info
->u
.dos_dir
) DOSFS_CloseDir( info
->u
.dos_dir
);
2611 if (info
->path
) HeapFree( GetProcessHeap(), 0, info
->path
);
2612 if (info
->long_mask
) HeapFree( GetProcessHeap(), 0, info
->long_mask
);
2613 GlobalUnlock16( handle
);
2614 GlobalFree16( handle
);