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 }
115 * Directory info for DOSFS_ReadDir
116 * contains the names of *all* the files in the directory
125 /* Info structure for FindFirstFile handle */
128 char *path
; /* unix path */
142 static WINE_EXCEPTION_FILTER(page_fault
)
144 if (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION
)
145 return EXCEPTION_EXECUTE_HANDLER
;
146 return EXCEPTION_CONTINUE_SEARCH
;
150 /***********************************************************************
153 * Return 1 if Unix file 'name' is also a valid MS-DOS name
154 * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
155 * File name can be terminated by '\0', '\\' or '/'.
157 static int DOSFS_ValidDOSName( LPCWSTR name
, int ignore_case
)
159 static const char invalid_chars
[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS
;
160 const WCHAR
*p
= name
;
161 const char *invalid
= ignore_case
? (invalid_chars
+ 26) : invalid_chars
;
166 /* Check for "." and ".." */
169 /* All other names beginning with '.' are invalid */
170 return (IS_END_OF_NAME(*p
));
172 while (!IS_END_OF_NAME(*p
))
174 if (*p
< 256 && strchr( invalid
, (char)*p
)) return 0; /* Invalid char */
175 if (*p
== '.') break; /* Start of the extension */
176 if (++len
> 8) return 0; /* Name too long */
179 if (*p
!= '.') return 1; /* End of name */
181 if (IS_END_OF_NAME(*p
)) return 0; /* Empty extension not allowed */
183 while (!IS_END_OF_NAME(*p
))
185 if (*p
< 256 && strchr( invalid
, (char)*p
)) return 0; /* Invalid char */
186 if (*p
== '.') return 0; /* Second extension not allowed */
187 if (++len
> 3) return 0; /* Extension too long */
194 /***********************************************************************
195 * DOSFS_ToDosFCBFormat
197 * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
198 * expanding wild cards and converting to upper-case in the process.
199 * File name can be terminated by '\0', '\\' or '/'.
200 * Return FALSE if the name is not a valid DOS name.
201 * 'buffer' must be at least 12 characters long.
203 BOOL
DOSFS_ToDosFCBFormat( LPCWSTR name
, LPWSTR buffer
)
205 static const char invalid_chars
[] = INVALID_DOS_CHARS
;
209 /* Check for "." and ".." */
214 for(i
= 1; i
< 11; i
++) buffer
[i
] = ' ';
221 return (!*p
|| (*p
== '/') || (*p
== '\\'));
224 for (i
= 0; i
< 8; i
++)
241 if (*p
< 256 && strchr( invalid_chars
, (char)*p
)) return FALSE
;
242 buffer
[i
] = toupperW(*p
);
250 /* Skip all chars after wildcard up to first dot */
251 while (*p
&& (*p
!= '/') && (*p
!= '\\') && (*p
!= '.')) p
++;
255 /* Check if name too long */
256 if (*p
&& (*p
!= '/') && (*p
!= '\\') && (*p
!= '.')) return FALSE
;
258 if (*p
== '.') p
++; /* Skip dot */
260 for (i
= 8; i
< 11; i
++)
270 return FALSE
; /* Second extension not allowed */
278 if (*p
< 256 && strchr( invalid_chars
, (char)*p
)) return FALSE
;
279 buffer
[i
] = toupperW(*p
);
286 /* at most 3 character of the extension are processed
287 * is something behind this ?
289 while (*p
== '*' || *p
== ' ') p
++; /* skip wildcards and spaces */
290 return IS_END_OF_NAME(*p
);
294 /***********************************************************************
295 * DOSFS_ToDosDTAFormat
297 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
298 * converting to upper-case in the process.
299 * File name can be terminated by '\0', '\\' or '/'.
300 * 'buffer' must be at least 13 characters long.
302 static void DOSFS_ToDosDTAFormat( LPCWSTR name
, LPWSTR buffer
)
306 memcpy( buffer
, name
, 8 * sizeof(WCHAR
) );
308 while ((p
> buffer
) && (p
[-1] == ' ')) p
--;
310 memcpy( p
, name
+ 8, 3 * sizeof(WCHAR
) );
312 while (p
[-1] == ' ') p
--;
313 if (p
[-1] == '.') p
--;
318 /***********************************************************************
321 * Check a DOS file name against a mask (both in FCB format).
323 static int DOSFS_MatchShort( LPCWSTR mask
, LPCWSTR name
)
326 for (i
= 11; i
> 0; i
--, mask
++, name
++)
327 if ((*mask
!= '?') && (*mask
!= *name
)) return 0;
332 /***********************************************************************
335 * Check a long file name against a mask.
337 * Tests (done in W95 DOS shell - case insensitive):
338 * *.txt test1.test.txt *
340 * *.t??????.t* test1.ta.tornado.txt *
341 * *tornado* test1.ta.tornado.txt *
342 * t*t test1.ta.tornado.txt *
344 * ?est??? test1.txt -
345 * *test1.txt* test1.txt *
346 * h?l?o*t.dat hellothisisatest.dat *
348 static int DOSFS_MatchLong( LPCWSTR mask
, LPCWSTR name
, int case_sensitive
)
350 LPCWSTR lastjoker
= NULL
;
351 LPCWSTR next_to_retry
= NULL
;
352 static const WCHAR asterisk_dot_asterisk
[] = {'*','.','*',0};
354 TRACE("(%s, %s, %x)\n", debugstr_w(mask
), debugstr_w(name
), case_sensitive
);
356 if (!strcmpW( mask
, asterisk_dot_asterisk
)) return 1;
357 while (*name
&& *mask
)
362 while (*mask
== '*') mask
++; /* Skip consecutive '*' */
364 if (!*mask
) return 1; /* end of mask is all '*', so match */
366 /* skip to the next match after the joker(s) */
367 if (case_sensitive
) while (*name
&& (*name
!= *mask
)) name
++;
368 else while (*name
&& (toupperW(*name
) != toupperW(*mask
))) name
++;
371 next_to_retry
= name
;
373 else if (*mask
!= '?')
378 if (*mask
!= *name
) mismatch
= 1;
382 if (toupperW(*mask
) != toupperW(*name
)) mismatch
= 1;
396 else /* mismatch ! */
398 if (lastjoker
) /* we had an '*', so we can try unlimitedly */
402 /* this scan sequence was a mismatch, so restart
403 * 1 char after the first char we checked last time */
405 name
= next_to_retry
;
408 return 0; /* bad luck */
417 while ((*mask
== '.') || (*mask
== '*'))
418 mask
++; /* Ignore trailing '.' or '*' in mask */
419 return (!*name
&& !*mask
);
423 /***********************************************************************
426 * Used to construct an array of filenames in DOSFS_OpenDir
428 static BOOL
DOSFS_AddDirEntry(DOS_DIR
**dir
, LPCWSTR name
, LPCWSTR dosname
)
430 int extra1
= strlenW(name
) + 1;
431 int extra2
= strlenW(dosname
) + 1;
433 /* if we need more, at minimum double the size */
434 if( (extra1
+ extra2
+ (*dir
)->used
) > (*dir
)->size
)
436 int more
= (*dir
)->size
;
439 if(more
<(extra1
+extra2
))
440 more
= extra1
+extra2
;
442 t
= HeapReAlloc(GetProcessHeap(), 0, *dir
, sizeof(**dir
) +
443 ((*dir
)->size
+ more
)*sizeof(WCHAR
) );
446 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
447 ERR("Out of memory caching directory structure %d %d %d\n",
448 (*dir
)->size
, more
, (*dir
)->used
);
452 (*dir
)->size
+= more
;
455 /* at this point, the dir structure is big enough to hold these names */
456 strcpyW(&(*dir
)->names
[(*dir
)->used
], name
);
457 (*dir
)->used
+= extra1
;
458 strcpyW(&(*dir
)->names
[(*dir
)->used
], dosname
);
459 (*dir
)->used
+= extra2
;
465 /***********************************************************************
468 static BOOL
DOSFS_OpenDir_VFAT(UINT codepage
, DOS_DIR
**dir
, const char *unix_path
)
470 #ifdef VFAT_IOCTL_READDIR_BOTH
472 int fd
= open( unix_path
, O_RDONLY
|O_DIRECTORY
);
475 /* Check if the VFAT ioctl is supported on this directory */
482 WCHAR long_name
[MAX_PATH
];
483 WCHAR short_name
[12];
485 r
= (ioctl( fd
, VFAT_IOCTL_READDIR_BOTH
, (long)de
) != -1);
490 MultiByteToWideChar(codepage
, 0, de
[0].d_name
, -1, long_name
, MAX_PATH
);
491 if (!DOSFS_ToDosFCBFormat( long_name
, short_name
))
492 short_name
[0] = '\0';
494 MultiByteToWideChar(codepage
, 0, de
[1].d_name
, -1, long_name
, MAX_PATH
);
496 MultiByteToWideChar(codepage
, 0, de
[0].d_name
, -1, long_name
, MAX_PATH
);
497 r
= DOSFS_AddDirEntry(dir
, long_name
, short_name
);
503 static const WCHAR empty_strW
[] = { 0 };
504 DOSFS_AddDirEntry(dir
, empty_strW
, empty_strW
);
510 #endif /* VFAT_IOCTL_READDIR_BOTH */
514 /***********************************************************************
515 * DOSFS_OpenDir_Normal
517 * Now use the standard opendir/readdir interface
519 static BOOL
DOSFS_OpenDir_Normal( UINT codepage
, DOS_DIR
**dir
, const char *unix_path
)
521 DIR *unixdir
= opendir( unix_path
);
523 static const WCHAR empty_strW
[] = { 0 };
529 WCHAR long_name
[MAX_PATH
];
530 struct dirent
*de
= readdir(unixdir
);
534 MultiByteToWideChar(codepage
, 0, de
->d_name
, -1, long_name
, MAX_PATH
);
535 r
= DOSFS_AddDirEntry(dir
, long_name
, empty_strW
);
540 DOSFS_AddDirEntry(dir
, empty_strW
, empty_strW
);
545 /***********************************************************************
548 static DOS_DIR
*DOSFS_OpenDir( UINT codepage
, const char *unix_path
)
550 const int init_size
= 0x100;
551 DOS_DIR
*dir
= HeapAlloc( GetProcessHeap(), 0, sizeof(*dir
) + init_size
*sizeof (WCHAR
));
554 TRACE("%s\n",debugstr_a(unix_path
));
558 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
562 dir
->size
= init_size
;
564 /* Treat empty path as root directory. This simplifies path split into
565 directory and mask in several other places */
566 if (!*unix_path
) unix_path
= "/";
568 r
= DOSFS_OpenDir_VFAT( codepage
, &dir
, unix_path
);
571 r
= DOSFS_OpenDir_Normal( codepage
, &dir
, unix_path
);
575 HeapFree(GetProcessHeap(), 0, dir
);
584 /***********************************************************************
587 static void DOSFS_CloseDir( DOS_DIR
*dir
)
589 HeapFree( GetProcessHeap(), 0, dir
);
593 /***********************************************************************
596 static BOOL
DOSFS_ReadDir( DOS_DIR
*dir
, LPCWSTR
*long_name
,
597 LPCWSTR
*short_name
)
604 /* the long pathname is first */
605 ln
= &dir
->names
[dir
->used
];
610 dir
->used
+= (strlenW(ln
) + 1);
612 /* followed by the short path name */
613 sn
= &dir
->names
[dir
->used
];
618 dir
->used
+= (strlenW(sn
) + 1);
624 /***********************************************************************
627 * Transform a Unix file name into a hashed DOS name. If the name is a valid
628 * DOS name, it is converted to upper-case; otherwise it is replaced by a
629 * hashed version that fits in 8.3 format.
630 * File name can be terminated by '\0', '\\' or '/'.
631 * 'buffer' must be at least 13 characters long.
633 static void DOSFS_Hash( LPCWSTR name
, LPWSTR buffer
, BOOL dir_format
,
636 static const char invalid_chars
[] = INVALID_DOS_CHARS
"~.";
637 static const char hash_chars
[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
646 for(i
= 0; i
< 11; i
++) buffer
[i
] = ' ';
650 if (DOSFS_ValidDOSName( name
, ignore_case
))
652 /* Check for '.' and '..' */
656 if (!dir_format
) buffer
[1] = buffer
[2] = '\0';
657 if (name
[1] == '.') buffer
[1] = '.';
661 /* Simply copy the name, converting to uppercase */
663 for (dst
= buffer
; !IS_END_OF_NAME(*name
) && (*name
!= '.'); name
++)
664 *dst
++ = toupperW(*name
);
667 if (dir_format
) dst
= buffer
+ 8;
669 for (name
++; !IS_END_OF_NAME(*name
); name
++)
670 *dst
++ = toupperW(*name
);
672 if (!dir_format
) *dst
= '\0';
676 /* Compute the hash code of the file name */
677 /* If you know something about hash functions, feel free to */
678 /* insert a better algorithm here... */
681 for (p
= name
, hash
= 0xbeef; !IS_END_OF_NAME(p
[1]); p
++)
682 hash
= (hash
<<3) ^ (hash
>>5) ^ tolowerW(*p
) ^ (tolowerW(p
[1]) << 8);
683 hash
= (hash
<<3) ^ (hash
>>5) ^ tolowerW(*p
); /* Last character */
687 for (p
= name
, hash
= 0xbeef; !IS_END_OF_NAME(p
[1]); p
++)
688 hash
= (hash
<< 3) ^ (hash
>> 5) ^ *p
^ (p
[1] << 8);
689 hash
= (hash
<< 3) ^ (hash
>> 5) ^ *p
; /* Last character */
692 /* Find last dot for start of the extension */
693 for (p
= name
+1, ext
= NULL
; !IS_END_OF_NAME(*p
); p
++)
694 if (*p
== '.') ext
= p
;
695 if (ext
&& IS_END_OF_NAME(ext
[1]))
696 ext
= NULL
; /* Empty extension ignored */
698 /* Copy first 4 chars, replacing invalid chars with '_' */
699 for (i
= 4, p
= name
, dst
= buffer
; i
> 0; i
--, p
++)
701 if (IS_END_OF_NAME(*p
) || (p
== ext
)) break;
702 *dst
++ = (*p
< 256 && strchr( invalid_chars
, (char)*p
)) ? '_' : toupperW(*p
);
704 /* Pad to 5 chars with '~' */
705 while (i
-- >= 0) *dst
++ = '~';
707 /* Insert hash code converted to 3 ASCII chars */
708 *dst
++ = hash_chars
[(hash
>> 10) & 0x1f];
709 *dst
++ = hash_chars
[(hash
>> 5) & 0x1f];
710 *dst
++ = hash_chars
[hash
& 0x1f];
712 /* Copy the first 3 chars of the extension (if any) */
715 if (!dir_format
) *dst
++ = '.';
716 for (i
= 3, ext
++; (i
> 0) && !IS_END_OF_NAME(*ext
); i
--, ext
++)
717 *dst
++ = (*ext
< 256 && strchr( invalid_chars
, (char)*ext
)) ? '_' : toupperW(*ext
);
719 if (!dir_format
) *dst
= '\0';
723 /***********************************************************************
726 * Find the Unix file name in a given directory that corresponds to
727 * a file name (either in Unix or DOS format).
728 * File name can be terminated by '\0', '\\' or '/'.
729 * Return TRUE if OK, FALSE if no file name matches.
731 * 'long_buf' must be at least 'long_len' characters long. If the long name
732 * turns out to be larger than that, the function returns FALSE.
733 * 'short_buf' must be at least 13 characters long.
735 BOOL
DOSFS_FindUnixName( const DOS_FULL_NAME
*path
, LPCWSTR name
, char *long_buf
,
736 INT long_len
, LPWSTR short_buf
, BOOL ignore_case
)
739 LPCWSTR long_name
, short_name
;
740 WCHAR dos_name
[12], tmp_buf
[13];
743 LPCWSTR p
= strchrW( name
, '/' );
744 int len
= p
? (int)(p
- name
) : strlenW(name
);
745 if ((p
= strchrW( name
, '\\' ))) len
= min( (int)(p
- name
), len
);
746 /* Ignore trailing dots and spaces */
747 while (len
> 1 && (name
[len
-1] == '.' || name
[len
-1] == ' ')) len
--;
748 if (long_len
< len
+ 1) return FALSE
;
750 TRACE("%s,%s\n", path
->long_name
, debugstr_w(name
) );
752 if (!DOSFS_ToDosFCBFormat( name
, dos_name
)) dos_name
[0] = '\0';
754 if (!(dir
= DOSFS_OpenDir( DRIVE_GetCodepage(path
->drive
), path
->long_name
)))
756 WARN("(%s,%s): can't open dir: %s\n",
757 path
->long_name
, debugstr_w(name
), strerror(errno
) );
761 while ((ret
= DOSFS_ReadDir( dir
, &long_name
, &short_name
)))
763 /* Check against Unix name */
764 if (len
== strlenW(long_name
))
768 if (!strncmpW( long_name
, name
, len
)) break;
772 if (!strncmpiW( long_name
, name
, len
)) break;
777 /* Check against hashed DOS name */
780 DOSFS_Hash( long_name
, tmp_buf
, TRUE
, ignore_case
);
781 short_name
= tmp_buf
;
783 if (!strcmpW( dos_name
, short_name
)) break;
788 if (long_buf
) WideCharToMultiByte(DRIVE_GetCodepage(path
->drive
), 0,
789 long_name
, -1, long_buf
, long_len
, NULL
, NULL
);
793 DOSFS_ToDosDTAFormat( short_name
, short_buf
);
795 DOSFS_Hash( long_name
, short_buf
, FALSE
, ignore_case
);
797 TRACE("(%s,%s) -> %s (%s)\n", path
->long_name
, debugstr_w(name
),
798 debugstr_w(long_name
), short_buf
? debugstr_w(short_buf
) : "***");
801 WARN("%s not found in '%s'\n", debugstr_w(name
), path
->long_name
);
802 DOSFS_CloseDir( dir
);
807 /***********************************************************************
810 * Check if a DOS file name represents a DOS device and return the device.
812 const DOS_DEVICE
*DOSFS_GetDevice( LPCWSTR name
)
817 if (!name
) return NULL
; /* if wine_server_handle_to_fd was used */
818 if (name
[0] && (name
[1] == ':')) name
+= 2;
819 if ((p
= strrchrW( name
, '/' ))) name
= p
+ 1;
820 if ((p
= strrchrW( name
, '\\' ))) name
= p
+ 1;
821 for (i
= 0; i
< sizeof(DOSFS_Devices
)/sizeof(DOSFS_Devices
[0]); i
++)
823 const WCHAR
*dev
= DOSFS_Devices
[i
].name
;
824 if (!strncmpiW( dev
, name
, strlenW(dev
) ))
826 p
= name
+ strlenW( dev
);
827 if (!*p
|| (*p
== '.') || (*p
== ':')) return &DOSFS_Devices
[i
];
834 /***********************************************************************
835 * DOSFS_GetDeviceByHandle
837 const DOS_DEVICE
*DOSFS_GetDeviceByHandle( HANDLE hFile
)
839 const DOS_DEVICE
*ret
= NULL
;
840 SERVER_START_REQ( get_device_id
)
843 if (!wine_server_call( req
))
845 if ((reply
->id
>= 0) &&
846 (reply
->id
< sizeof(DOSFS_Devices
)/sizeof(DOSFS_Devices
[0])))
847 ret
= &DOSFS_Devices
[reply
->id
];
855 /**************************************************************************
856 * DOSFS_CreateCommPort
858 static HANDLE
DOSFS_CreateCommPort(LPCWSTR name
, DWORD access
, DWORD attributes
, LPSECURITY_ATTRIBUTES sa
)
863 OBJECT_ATTRIBUTES attr
;
864 UNICODE_STRING nameW
;
869 static const WCHAR serialportsW
[] = {'M','a','c','h','i','n','e','\\',
870 'S','o','f','t','w','a','r','e','\\',
871 'W','i','n','e','\\','W','i','n','e','\\',
872 'C','o','n','f','i','g','\\',
873 'S','e','r','i','a','l','P','o','r','t','s',0};
875 TRACE_(file
)("%s %lx %lx\n", debugstr_w(name
), access
, attributes
);
877 attr
.Length
= sizeof(attr
);
878 attr
.RootDirectory
= 0;
879 attr
.ObjectName
= &nameW
;
881 attr
.SecurityDescriptor
= NULL
;
882 attr
.SecurityQualityOfService
= NULL
;
883 RtlInitUnicodeString( &nameW
, serialportsW
);
885 if (NtOpenKey( &hkey
, KEY_ALL_ACCESS
, &attr
)) return 0;
887 RtlInitUnicodeString( &nameW
, name
);
888 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
889 devnameW
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
895 if (!devnameW
) return 0;
896 WideCharToMultiByte(CP_ACP
, 0, devnameW
, -1, devname
, sizeof(devname
), NULL
, NULL
);
898 TRACE("opening %s as %s\n", devname
, debugstr_w(name
));
900 SERVER_START_REQ( create_serial
)
902 req
->access
= access
;
903 req
->inherit
= (sa
&& (sa
->nLength
>=sizeof(*sa
)) && sa
->bInheritHandle
);
904 req
->attributes
= attributes
;
905 req
->sharing
= FILE_SHARE_READ
|FILE_SHARE_WRITE
;
906 wine_server_add_data( req
, devname
, strlen(devname
) );
908 wine_server_call_err( req
);
914 ERR("Couldn't open device '%s' ! (check permissions)\n",devname
);
916 TRACE("return %p\n", ret
);
920 /***********************************************************************
923 * Open a DOS device. This might not map 1:1 into the UNIX device concept.
924 * Returns 0 on failure.
926 HANDLE
DOSFS_OpenDevice( LPCWSTR name
, DWORD access
, DWORD attributes
, LPSECURITY_ATTRIBUTES sa
)
932 if (name
[0] && (name
[1] == ':')) name
+= 2;
933 if ((p
= strrchrW( name
, '/' ))) name
= p
+ 1;
934 if ((p
= strrchrW( name
, '\\' ))) name
= p
+ 1;
935 for (i
= 0; i
< sizeof(DOSFS_Devices
)/sizeof(DOSFS_Devices
[0]); i
++)
937 const WCHAR
*dev
= DOSFS_Devices
[i
].name
;
938 if (!strncmpiW( dev
, name
, strlenW(dev
) ))
940 p
= name
+ strlenW( dev
);
941 if (!*p
|| (*p
== '.') || (*p
== ':')) {
942 static const WCHAR nulW
[] = {'N','U','L',0};
943 static const WCHAR conW
[] = {'C','O','N',0};
944 static const WCHAR scsimgrW
[] = {'S','C','S','I','M','G','R','$',0};
945 static const WCHAR hpscanW
[] = {'H','P','S','C','A','N',0};
946 static const WCHAR emmxxxx0W
[] = {'E','M','M','X','X','X','X','0',0};
948 if (!strcmpiW(DOSFS_Devices
[i
].name
, nulW
))
949 return FILE_CreateFile( "/dev/null", access
,
950 FILE_SHARE_READ
|FILE_SHARE_WRITE
, sa
,
951 OPEN_EXISTING
, 0, 0, TRUE
, DRIVE_UNKNOWN
);
952 if (!strcmpiW(DOSFS_Devices
[i
].name
, conW
)) {
954 switch (access
& (GENERIC_READ
|GENERIC_WRITE
)) {
956 to_dup
= GetStdHandle( STD_INPUT_HANDLE
);
959 to_dup
= GetStdHandle( STD_OUTPUT_HANDLE
);
962 FIXME("can't open CON read/write\n");
965 if (!DuplicateHandle( GetCurrentProcess(), to_dup
, GetCurrentProcess(),
967 sa
&& (sa
->nLength
>=sizeof(*sa
)) && sa
->bInheritHandle
,
968 DUPLICATE_SAME_ACCESS
))
972 if (!strcmpiW(DOSFS_Devices
[i
].name
, scsimgrW
) ||
973 !strcmpiW(DOSFS_Devices
[i
].name
, hpscanW
) ||
974 !strcmpiW(DOSFS_Devices
[i
].name
, emmxxxx0W
))
976 return FILE_CreateDevice( i
, access
, sa
);
979 if( (handle
=DOSFS_CreateCommPort(DOSFS_Devices
[i
].name
,access
,attributes
,sa
)) )
981 FIXME("device open %s not supported (yet)\n", debugstr_w(DOSFS_Devices
[i
].name
));
990 /***********************************************************************
993 * Get the drive specified by a given path name (DOS or Unix format).
995 static int DOSFS_GetPathDrive( LPCWSTR
*name
)
1000 if (*p
&& (p
[1] == ':'))
1002 drive
= toupperW(*p
) - 'A';
1005 else if (*p
== '/') /* Absolute Unix path? */
1007 if ((drive
= DRIVE_FindDriveRootW( name
)) == -1)
1009 MESSAGE("Warning: %s not accessible from a configured DOS drive\n", debugstr_w(*name
) );
1010 /* Assume it really was a DOS name */
1011 drive
= DRIVE_GetCurrentDrive();
1014 else drive
= DRIVE_GetCurrentDrive();
1016 if (!DRIVE_IsValid(drive
))
1018 SetLastError( ERROR_INVALID_DRIVE
);
1025 /***********************************************************************
1028 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
1029 * Unix name / short DOS name pair.
1030 * Return FALSE if one of the path components does not exist. The last path
1031 * component is only checked if 'check_last' is non-zero.
1032 * The buffers pointed to by 'long_buf' and 'short_buf' must be
1033 * at least MAX_PATHNAME_LEN long.
1035 BOOL
DOSFS_GetFullName( LPCWSTR name
, BOOL check_last
, DOS_FULL_NAME
*full
)
1038 UINT flags
, codepage
;
1041 static const WCHAR driveA_rootW
[] = {'A',':','\\',0};
1042 static const WCHAR dos_rootW
[] = {'\\',0};
1044 TRACE("%s (last=%d)\n", debugstr_w(name
), check_last
);
1046 if ((!*name
) || (*name
=='\n'))
1047 { /* error code for Win98 */
1048 SetLastError(ERROR_BAD_PATHNAME
);
1052 if ((full
->drive
= DOSFS_GetPathDrive( &name
)) == -1) return FALSE
;
1053 flags
= DRIVE_GetFlags( full
->drive
);
1054 codepage
= DRIVE_GetCodepage(full
->drive
);
1056 lstrcpynA( full
->long_name
, DRIVE_GetRoot( full
->drive
),
1057 sizeof(full
->long_name
) );
1058 if (full
->long_name
[1]) root
= full
->long_name
+ strlen(full
->long_name
);
1059 else root
= full
->long_name
; /* root directory */
1061 strcpyW( full
->short_name
, driveA_rootW
);
1062 full
->short_name
[0] += full
->drive
;
1064 if ((*name
== '\\') || (*name
== '/')) /* Absolute path */
1066 while ((*name
== '\\') || (*name
== '/')) name
++;
1068 else /* Relative path */
1070 lstrcpynA( root
+ 1, DRIVE_GetUnixCwd( full
->drive
),
1071 sizeof(full
->long_name
) - (root
- full
->long_name
) - 1 );
1072 if (root
[1]) *root
= '/';
1073 lstrcpynW( full
->short_name
+ 3, DRIVE_GetDosCwd( full
->drive
),
1074 sizeof(full
->short_name
)/sizeof(full
->short_name
[0]) - 3 );
1077 p_l
= full
->long_name
[1] ? full
->long_name
+ strlen(full
->long_name
)
1079 p_s
= full
->short_name
[3] ? full
->short_name
+ strlenW(full
->short_name
)
1080 : full
->short_name
+ 2;
1083 while (*name
&& found
)
1085 /* Check for '.' and '..' */
1089 if (IS_END_OF_NAME(name
[1]))
1092 while ((*name
== '\\') || (*name
== '/')) name
++;
1095 else if ((name
[1] == '.') && IS_END_OF_NAME(name
[2]))
1098 while ((*name
== '\\') || (*name
== '/')) name
++;
1099 while ((p_l
> root
) && (*p_l
!= '/')) p_l
--;
1100 while ((p_s
> full
->short_name
+ 2) && (*p_s
!= '\\')) p_s
--;
1101 *p_l
= *p_s
= '\0'; /* Remove trailing separator */
1106 /* Make sure buffers are large enough */
1108 if ((p_s
>= full
->short_name
+ sizeof(full
->short_name
)/sizeof(full
->short_name
[0]) - 14) ||
1109 (p_l
>= full
->long_name
+ sizeof(full
->long_name
) - 1))
1111 SetLastError( ERROR_PATH_NOT_FOUND
);
1115 /* Get the long and short name matching the file name */
1117 if ((found
= DOSFS_FindUnixName( full
, name
, p_l
+ 1,
1118 sizeof(full
->long_name
) - (p_l
- full
->long_name
) - 1,
1119 p_s
+ 1, !(flags
& DRIVE_CASE_SENSITIVE
) )))
1124 p_s
+= strlenW(p_s
);
1125 while (!IS_END_OF_NAME(*name
)) name
++;
1127 else if (!check_last
)
1131 while (!IS_END_OF_NAME(*name
) &&
1132 (p_s
< full
->short_name
+ sizeof(full
->short_name
)/sizeof(full
->short_name
[0]) - 1) &&
1133 (p_l
< full
->long_name
+ sizeof(full
->long_name
) - 1))
1136 *p_s
++ = tolowerW(*name
);
1137 /* If the drive is case-sensitive we want to create new */
1138 /* files in lower-case otherwise we can't reopen them */
1139 /* under the same short name. */
1140 if (flags
& DRIVE_CASE_SENSITIVE
) wch
= tolowerW(*name
);
1142 p_l
+= WideCharToMultiByte(codepage
, 0, &wch
, 1, p_l
, 2, NULL
, NULL
);
1145 /* Ignore trailing dots and spaces */
1146 while(p_l
[-1] == '.' || p_l
[-1] == ' ') {
1153 while ((*name
== '\\') || (*name
== '/')) name
++;
1160 SetLastError( ERROR_FILE_NOT_FOUND
);
1163 if (*name
) /* Not last */
1165 SetLastError( ERROR_PATH_NOT_FOUND
);
1169 if (!full
->long_name
[0]) strcpy( full
->long_name
, "/" );
1170 if (!full
->short_name
[2]) strcpyW( full
->short_name
+ 2, dos_rootW
);
1171 TRACE("returning %s = %s\n", full
->long_name
, debugstr_w(full
->short_name
) );
1176 /***********************************************************************
1177 * GetShortPathNameW (KERNEL32.@)
1181 * longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1182 * longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0
1184 * more observations ( with NT 3.51 (WinDD) ):
1185 * longpath <= 8.3 -> just copy longpath to shortpath
1187 * a) file does not exist -> return 0, LastError = ERROR_FILE_NOT_FOUND
1188 * b) file does exist -> set the short filename.
1189 * - trailing slashes are reproduced in the short name, even if the
1190 * file is not a directory
1191 * - the absolute/relative path of the short name is reproduced like found
1193 * - longpath and shortpath may have the same address
1194 * Peter Ganten, 1999
1196 DWORD WINAPI
GetShortPathNameW( LPCWSTR longpath
, LPWSTR shortpath
, DWORD shortlen
)
1198 DOS_FULL_NAME full_name
;
1199 WCHAR tmpshortpath
[MAX_PATHNAME_LEN
];
1201 DWORD sp
= 0, lp
= 0;
1205 BOOL unixabsolute
= *longpath
== '/';
1207 TRACE("%s\n", debugstr_w(longpath
));
1210 SetLastError(ERROR_INVALID_PARAMETER
);
1214 SetLastError(ERROR_BAD_PATHNAME
);
1218 /* check for drive letter */
1219 if (!unixabsolute
&& longpath
[1] == ':' ) {
1220 tmpshortpath
[0] = longpath
[0];
1221 tmpshortpath
[1] = ':';
1225 if ( ( drive
= DOSFS_GetPathDrive ( &longpath
)) == -1 ) return 0;
1226 flags
= DRIVE_GetFlags ( drive
);
1228 if (unixabsolute
&& drive
!= DRIVE_GetCurrentDrive()) {
1229 tmpshortpath
[0] = drive
+ 'A';
1230 tmpshortpath
[1] = ':';
1234 while ( longpath
[lp
] ) {
1236 /* check for path delimiters and reproduce them */
1237 if ( longpath
[lp
] == '\\' || longpath
[lp
] == '/' ) {
1238 if (!sp
|| tmpshortpath
[sp
-1]!= '\\')
1240 /* strip double "\\" */
1241 tmpshortpath
[sp
] = '\\';
1244 tmpshortpath
[sp
]=0;/*terminate string*/
1250 for(p
= longpath
+ lp
; *p
&& *p
!= '/' && *p
!= '\\'; p
++)
1252 lstrcpynW(tmpshortpath
+ sp
, longpath
+ lp
, tmplen
+ 1);
1254 /* Check, if the current element is a valid dos name */
1255 if ( DOSFS_ValidDOSName ( longpath
+ lp
, !(flags
& DRIVE_CASE_SENSITIVE
) ) ) {
1261 /* Check if the file exists and use the existing file name */
1262 if ( DOSFS_GetFullName ( tmpshortpath
, TRUE
, &full_name
) ) {
1263 strcpyW(tmpshortpath
+ sp
, strrchrW(full_name
.short_name
, '\\') + 1);
1264 sp
+= strlenW(tmpshortpath
+ sp
);
1269 TRACE("not found!\n" );
1270 SetLastError ( ERROR_FILE_NOT_FOUND
);
1273 tmpshortpath
[sp
] = 0;
1275 tmplen
= strlenW(tmpshortpath
) + 1;
1276 if (tmplen
<= shortlen
)
1278 strcpyW(shortpath
, tmpshortpath
);
1279 TRACE("returning %s\n", debugstr_w(shortpath
));
1280 tmplen
--; /* length without 0 */
1287 /***********************************************************************
1288 * GetShortPathNameA (KERNEL32.@)
1290 DWORD WINAPI
GetShortPathNameA( LPCSTR longpath
, LPSTR shortpath
, DWORD shortlen
)
1292 UNICODE_STRING longpathW
;
1293 WCHAR shortpathW
[MAX_PATH
];
1298 SetLastError(ERROR_INVALID_PARAMETER
);
1302 TRACE("%s\n", debugstr_a(longpath
));
1304 if (!RtlCreateUnicodeStringFromAsciiz(&longpathW
, longpath
))
1306 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1310 retW
= GetShortPathNameW(longpathW
.Buffer
, shortpathW
, MAX_PATH
);
1314 else if (retW
> MAX_PATH
)
1316 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
1321 ret
= WideCharToMultiByte(CP_ACP
, 0, shortpathW
, -1, NULL
, 0, NULL
, NULL
);
1322 if (ret
<= shortlen
)
1324 WideCharToMultiByte(CP_ACP
, 0, shortpathW
, -1, shortpath
, shortlen
, NULL
, NULL
);
1325 ret
--; /* length without 0 */
1329 RtlFreeUnicodeString(&longpathW
);
1334 /***********************************************************************
1335 * GetLongPathNameW (KERNEL32.@)
1338 * observed (Win2000):
1339 * shortpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1340 * shortpath="": LastError=ERROR_PATH_NOT_FOUND, ret=0
1342 DWORD WINAPI
GetLongPathNameW( LPCWSTR shortpath
, LPWSTR longpath
, DWORD longlen
)
1344 DOS_FULL_NAME full_name
;
1352 SetLastError(ERROR_INVALID_PARAMETER
);
1355 if (!shortpath
[0]) {
1356 SetLastError(ERROR_PATH_NOT_FOUND
);
1360 TRACE("%s,%p,%ld\n", debugstr_w(shortpath
), longpath
, longlen
);
1362 if(shortpath
[0]=='\\' && shortpath
[1]=='\\')
1364 ERR("UNC pathname %s\n",debugstr_w(shortpath
));
1365 lstrcpynW( longpath
, full_name
.short_name
, longlen
);
1366 return strlenW(longpath
);
1369 if (!DOSFS_GetFullName( shortpath
, TRUE
, &full_name
)) return 0;
1371 root
= full_name
.long_name
;
1372 drive
= DRIVE_FindDriveRoot(&root
);
1373 codepage
= DRIVE_GetCodepage(drive
);
1375 ret
= MultiByteToWideChar(codepage
, 0, root
, -1, NULL
, 0);
1377 /* reproduce terminating slash */
1378 if (ret
> 4) /* if not drive root */
1380 len
= strlenW(shortpath
);
1381 if (shortpath
[len
- 1] == '\\' || shortpath
[len
- 1] == '/')
1387 longpath
[0] = 'A' + drive
;
1389 MultiByteToWideChar(codepage
, 0, root
, -1, longpath
+ 2, longlen
- 2);
1390 for (p
= longpath
; *p
; p
++) if (*p
== '/') *p
= '\\';
1393 longpath
[ret
- 2] = '\\';
1394 longpath
[ret
- 1] = 0;
1396 TRACE("returning %s\n", debugstr_w(longpath
));
1397 ret
--; /* length without 0 */
1403 /***********************************************************************
1404 * GetLongPathNameA (KERNEL32.@)
1406 DWORD WINAPI
GetLongPathNameA( LPCSTR shortpath
, LPSTR longpath
, DWORD longlen
)
1408 UNICODE_STRING shortpathW
;
1409 WCHAR longpathW
[MAX_PATH
];
1414 SetLastError(ERROR_INVALID_PARAMETER
);
1418 TRACE("%s\n", debugstr_a(shortpath
));
1420 if (!RtlCreateUnicodeStringFromAsciiz(&shortpathW
, shortpath
))
1422 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1426 retW
= GetLongPathNameW(shortpathW
.Buffer
, longpathW
, MAX_PATH
);
1430 else if (retW
> MAX_PATH
)
1432 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
1437 ret
= WideCharToMultiByte(CP_ACP
, 0, longpathW
, -1, NULL
, 0, NULL
, NULL
);
1440 WideCharToMultiByte(CP_ACP
, 0, longpathW
, -1, longpath
, longlen
, NULL
, NULL
);
1441 ret
--; /* length without 0 */
1445 RtlFreeUnicodeString(&shortpathW
);
1450 /***********************************************************************
1451 * DOSFS_DoGetFullPathName
1453 * Implementation of GetFullPathNameA/W.
1455 * bon@elektron 000331:
1456 * A test for GetFullPathName with many pathological cases
1457 * now gives identical output for Wine and OSR2
1459 static DWORD
DOSFS_DoGetFullPathName( LPCWSTR name
, DWORD len
, LPWSTR result
)
1462 DOS_FULL_NAME full_name
;
1466 WCHAR drivecur
[] = {'C',':','.',0};
1467 WCHAR driveletter
=0;
1468 int namelen
,drive
=0;
1469 static const WCHAR bkslashW
[] = {'\\',0};
1470 static const WCHAR dotW
[] = {'.',0};
1471 static const WCHAR updir_slashW
[] = {'\\','.','.','\\',0};
1472 static const WCHAR curdirW
[] = {'\\','.','\\',0};
1473 static const WCHAR updirW
[] = {'\\','.','.',0};
1477 SetLastError(ERROR_BAD_PATHNAME
);
1481 TRACE("passed %s\n", debugstr_w(name
));
1484 /*drive letter given */
1486 driveletter
= name
[0];
1488 if ((name
[1]==':') && ((name
[2]=='\\') || (name
[2]=='/')))
1489 /*absolute path given */
1491 strncpyW(full_name
.short_name
, name
, MAX_PATHNAME_LEN
);
1492 full_name
.short_name
[MAX_PATHNAME_LEN
- 1] = 0; /* ensure 0 termination */
1493 drive
= toupperW(name
[0]) - 'A';
1498 drivecur
[0]=driveletter
;
1499 else if ((name
[0]=='\\') || (name
[0]=='/'))
1500 strcpyW(drivecur
, bkslashW
);
1502 strcpyW(drivecur
, dotW
);
1504 if (!DOSFS_GetFullName( drivecur
, FALSE
, &full_name
))
1506 FIXME("internal: error getting drive/path\n");
1509 /* find path that drive letter substitutes*/
1510 drive
= toupperW(full_name
.short_name
[0]) - 'A';
1511 root
= DRIVE_GetRoot(drive
);
1514 FIXME("internal: error getting DOS Drive Root\n");
1517 if (!strcmp(root
,"/"))
1519 /* we have just the last / and we need it. */
1520 p_l
= full_name
.long_name
;
1524 p_l
= full_name
.long_name
+ strlen(root
);
1526 /* append long name (= unix name) to drive */
1527 MultiByteToWideChar(DRIVE_GetCodepage(drive
), 0, p_l
, -1,
1528 full_name
.short_name
+ 2, MAX_PATHNAME_LEN
- 3);
1529 /* append name to treat */
1530 namelen
= strlenW(full_name
.short_name
);
1533 p
+= 2; /* skip drive name when appending */
1534 if (namelen
+ 2 + strlenW(p
) > MAX_PATHNAME_LEN
)
1536 FIXME("internal error: buffer too small\n");
1539 full_name
.short_name
[namelen
++] ='\\';
1540 full_name
.short_name
[namelen
] = 0;
1541 strncpyW(full_name
.short_name
+ namelen
, p
, MAX_PATHNAME_LEN
- namelen
);
1542 full_name
.short_name
[MAX_PATHNAME_LEN
- 1] = 0; /* ensure 0 termination */
1544 /* reverse all slashes */
1545 for (p
=full_name
.short_name
;
1546 p
< full_name
.short_name
+ strlenW(full_name
.short_name
);
1552 /* Use memmove, as areas overlap */
1554 while ((p
= strstrW(full_name
.short_name
, updir_slashW
)))
1556 if (p
> full_name
.short_name
+2)
1559 q
= strrchrW(full_name
.short_name
, '\\');
1560 memmove(q
+1, p
+4, (strlenW(p
+4)+1) * sizeof(WCHAR
));
1564 memmove(full_name
.short_name
+3, p
+4, (strlenW(p
+4)+1) * sizeof(WCHAR
));
1567 if ((full_name
.short_name
[2]=='.')&&(full_name
.short_name
[3]=='.'))
1569 /* This case istn't treated yet : c:..\test */
1570 memmove(full_name
.short_name
+2,full_name
.short_name
+4,
1571 (strlenW(full_name
.short_name
+4)+1) * sizeof(WCHAR
));
1574 while ((p
= strstrW(full_name
.short_name
, curdirW
)))
1577 memmove(p
+1, p
+3, (strlenW(p
+3)+1) * sizeof(WCHAR
));
1579 if (!(DRIVE_GetFlags(drive
) & DRIVE_CASE_PRESERVING
))
1580 for (p
= full_name
.short_name
; *p
; p
++) *p
= toupperW(*p
);
1581 namelen
= strlenW(full_name
.short_name
);
1582 if (!strcmpW(full_name
.short_name
+namelen
-3, updirW
))
1584 /* one more strange case: "c:\test\test1\.."
1586 *(full_name
.short_name
+namelen
-3)=0;
1587 q
= strrchrW(full_name
.short_name
, '\\');
1590 if (full_name
.short_name
[namelen
-1]=='.')
1591 full_name
.short_name
[(namelen
--)-1] =0;
1593 if (full_name
.short_name
[namelen
-1]=='\\')
1594 full_name
.short_name
[(namelen
--)-1] =0;
1595 TRACE("got %s\n", debugstr_w(full_name
.short_name
));
1597 /* If the lpBuffer buffer is too small, the return value is the
1598 size of the buffer, in characters, required to hold the path
1599 plus the terminating \0 (tested against win95osr2, bon 001118)
1601 ret
= strlenW(full_name
.short_name
);
1604 /* don't touch anything when the buffer is not large enough */
1605 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
1610 strncpyW( result
, full_name
.short_name
, len
);
1611 result
[len
- 1] = 0; /* ensure 0 termination */
1614 TRACE("returning %s\n", debugstr_w(full_name
.short_name
) );
1619 /***********************************************************************
1620 * GetFullPathNameA (KERNEL32.@)
1622 * if the path closed with '\', *lastpart is 0
1624 DWORD WINAPI
GetFullPathNameA( LPCSTR name
, DWORD len
, LPSTR buffer
,
1627 UNICODE_STRING nameW
;
1628 WCHAR bufferW
[MAX_PATH
];
1633 SetLastError(ERROR_INVALID_PARAMETER
);
1637 if (!RtlCreateUnicodeStringFromAsciiz(&nameW
, name
))
1639 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1643 retW
= GetFullPathNameW( nameW
.Buffer
, MAX_PATH
, bufferW
, NULL
);
1647 else if (retW
> MAX_PATH
)
1649 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
1654 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, -1, NULL
, 0, NULL
, NULL
);
1657 WideCharToMultiByte(CP_ACP
, 0, bufferW
, -1, buffer
, len
, NULL
, NULL
);
1658 ret
--; /* length without 0 */
1662 LPSTR p
= buffer
+ strlen(buffer
);
1666 while ((p
> buffer
+ 2) && (*p
!= '\\')) p
--;
1669 else *lastpart
= NULL
;
1674 RtlFreeUnicodeString(&nameW
);
1679 /***********************************************************************
1680 * GetFullPathNameW (KERNEL32.@)
1682 DWORD WINAPI
GetFullPathNameW( LPCWSTR name
, DWORD len
, LPWSTR buffer
,
1685 DWORD ret
= DOSFS_DoGetFullPathName( name
, len
, buffer
);
1686 if (ret
&& (ret
<=len
) && buffer
&& lastpart
)
1688 LPWSTR p
= buffer
+ strlenW(buffer
);
1689 if (*p
!= (WCHAR
)'\\')
1691 while ((p
> buffer
+ 2) && (*p
!= (WCHAR
)'\\')) p
--;
1694 else *lastpart
= NULL
;
1700 /***********************************************************************
1701 * wine_get_unix_file_name (KERNEL32.@) Not a Windows API
1703 * Return the full Unix file name for a given path.
1704 * FIXME: convert dos file name to unicode
1706 BOOL WINAPI
wine_get_unix_file_name( LPCSTR dos
, LPSTR buffer
, DWORD len
)
1710 WCHAR dosW
[MAX_PATHNAME_LEN
];
1712 MultiByteToWideChar(CP_ACP
, 0, dos
, -1, dosW
, MAX_PATHNAME_LEN
);
1713 ret
= DOSFS_GetFullName( dosW
, FALSE
, &path
);
1716 strncpy( buffer
, path
.long_name
, len
);
1717 buffer
[len
- 1] = 0; /* ensure 0 termination */
1723 /***********************************************************************
1724 * get_show_dir_symlinks_option
1726 static BOOL
get_show_dir_symlinks_option(void)
1728 static const WCHAR WineW
[] = {'M','a','c','h','i','n','e','\\',
1729 'S','o','f','t','w','a','r','e','\\',
1730 'W','i','n','e','\\','W','i','n','e','\\',
1731 'C','o','n','f','i','g','\\','W','i','n','e',0};
1732 static const WCHAR ShowDirSymlinksW
[] = {'S','h','o','w','D','i','r','S','y','m','l','i','n','k','s',0};
1737 OBJECT_ATTRIBUTES attr
;
1738 UNICODE_STRING nameW
;
1741 attr
.Length
= sizeof(attr
);
1742 attr
.RootDirectory
= 0;
1743 attr
.ObjectName
= &nameW
;
1744 attr
.Attributes
= 0;
1745 attr
.SecurityDescriptor
= NULL
;
1746 attr
.SecurityQualityOfService
= NULL
;
1747 RtlInitUnicodeString( &nameW
, WineW
);
1749 if (!NtOpenKey( &hkey
, KEY_ALL_ACCESS
, &attr
))
1751 RtlInitUnicodeString( &nameW
, ShowDirSymlinksW
);
1752 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
1754 WCHAR
*str
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
1755 ret
= IS_OPTION_TRUE( str
[0] );
1763 /***********************************************************************
1766 static int DOSFS_FindNextEx( FIND_FIRST_INFO
*info
, WIN32_FIND_DATAW
*entry
)
1768 DWORD attr
= info
->attr
| FA_UNUSED
| FA_ARCHIVE
| FA_RDONLY
;
1769 UINT flags
= DRIVE_GetFlags( info
->drive
);
1770 char *p
, buffer
[MAX_PATHNAME_LEN
];
1771 const char *drive_path
;
1773 LPCWSTR long_name
, short_name
;
1774 BY_HANDLE_FILE_INFORMATION fileinfo
;
1778 if ((info
->attr
& ~(FA_UNUSED
| FA_ARCHIVE
| FA_RDONLY
)) == FA_LABEL
)
1780 if (info
->cur_pos
) return 0;
1781 entry
->dwFileAttributes
= FILE_ATTRIBUTE_LABEL
;
1782 RtlSecondsSince1970ToTime( (time_t)0, (LARGE_INTEGER
*)&entry
->ftCreationTime
);
1783 RtlSecondsSince1970ToTime( (time_t)0, (LARGE_INTEGER
*)&entry
->ftLastAccessTime
);
1784 RtlSecondsSince1970ToTime( (time_t)0, (LARGE_INTEGER
*)&entry
->ftLastWriteTime
);
1785 entry
->nFileSizeHigh
= 0;
1786 entry
->nFileSizeLow
= 0;
1787 entry
->dwReserved0
= 0;
1788 entry
->dwReserved1
= 0;
1789 DOSFS_ToDosDTAFormat( DRIVE_GetLabel( info
->drive
), entry
->cFileName
);
1790 strcpyW( entry
->cAlternateFileName
, entry
->cFileName
);
1792 TRACE("returning %s (%s) as label\n",
1793 debugstr_w(entry
->cFileName
), debugstr_w(entry
->cAlternateFileName
));
1797 drive_path
= info
->path
+ strlen(DRIVE_GetRoot( info
->drive
));
1798 while ((*drive_path
== '/') || (*drive_path
== '\\')) drive_path
++;
1799 drive_root
= !*drive_path
;
1801 lstrcpynA( buffer
, info
->path
, sizeof(buffer
) - 1 );
1802 strcat( buffer
, "/" );
1803 p
= buffer
+ strlen(buffer
);
1805 while (DOSFS_ReadDir( info
->u
.dos_dir
, &long_name
, &short_name
))
1809 /* Don't return '.' and '..' in the root of the drive */
1810 if (drive_root
&& (long_name
[0] == '.') &&
1811 (!long_name
[1] || ((long_name
[1] == '.') && !long_name
[2])))
1814 /* Check the long mask */
1816 if (info
->long_mask
&& *info
->long_mask
)
1818 if (!DOSFS_MatchLong( info
->long_mask
, long_name
,
1819 flags
& DRIVE_CASE_SENSITIVE
)) continue;
1822 /* Check the short mask */
1824 if (info
->short_mask
)
1828 DOSFS_Hash( long_name
, dos_name
, TRUE
,
1829 !(flags
& DRIVE_CASE_SENSITIVE
) );
1830 short_name
= dos_name
;
1832 if (!DOSFS_MatchShort( info
->short_mask
, short_name
)) continue;
1835 /* Check the file attributes */
1836 WideCharToMultiByte(DRIVE_GetCodepage(info
->drive
), 0, long_name
, -1,
1837 p
, sizeof(buffer
) - (int)(p
- buffer
), NULL
, NULL
);
1838 if (!FILE_Stat( buffer
, &fileinfo
, &is_symlink
))
1840 WARN("can't stat %s\n", buffer
);
1843 if (is_symlink
&& (fileinfo
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
))
1845 static int show_dir_symlinks
= -1;
1846 if (show_dir_symlinks
== -1)
1847 show_dir_symlinks
= get_show_dir_symlinks_option();
1848 if (!show_dir_symlinks
) continue;
1851 if (fileinfo
.dwFileAttributes
& ~attr
) continue;
1853 /* We now have a matching entry; fill the result and return */
1855 entry
->dwFileAttributes
= fileinfo
.dwFileAttributes
;
1856 entry
->ftCreationTime
= fileinfo
.ftCreationTime
;
1857 entry
->ftLastAccessTime
= fileinfo
.ftLastAccessTime
;
1858 entry
->ftLastWriteTime
= fileinfo
.ftLastWriteTime
;
1859 entry
->nFileSizeHigh
= fileinfo
.nFileSizeHigh
;
1860 entry
->nFileSizeLow
= fileinfo
.nFileSizeLow
;
1863 DOSFS_ToDosDTAFormat( short_name
, entry
->cAlternateFileName
);
1865 DOSFS_Hash( long_name
, entry
->cAlternateFileName
, FALSE
,
1866 !(flags
& DRIVE_CASE_SENSITIVE
) );
1868 lstrcpynW( entry
->cFileName
, long_name
, sizeof(entry
->cFileName
)/sizeof(entry
->cFileName
[0]) );
1869 if (!(flags
& DRIVE_CASE_PRESERVING
)) strlwrW( entry
->cFileName
);
1870 TRACE("returning %s (%s) %02lx %ld\n",
1871 debugstr_w(entry
->cFileName
), debugstr_w(entry
->cAlternateFileName
),
1872 entry
->dwFileAttributes
, entry
->nFileSizeLow
);
1875 return 0; /* End of directory */
1878 /***********************************************************************
1881 * Find the next matching file. Return the number of entries read to find
1882 * the matching one, or 0 if no more entries.
1883 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
1884 * file name mask. Either or both can be NULL.
1886 * NOTE: This is supposed to be only called by the int21 emulation
1887 * routines. Thus, we should own the Win16Mutex anyway.
1888 * Nevertheless, we explicitly enter it to ensure the static
1889 * directory cache is protected.
1891 int DOSFS_FindNext( const char *path
, const char *short_mask
,
1892 const char *long_mask
, int drive
, BYTE attr
,
1893 int skip
, WIN32_FIND_DATAA
*entry
)
1895 static FIND_FIRST_INFO info
;
1896 LPCWSTR short_name
, long_name
;
1898 UNICODE_STRING short_maskW
, long_maskW
;
1899 WIN32_FIND_DATAW entryW
;
1901 TRACE("(%s, %s, %s, %x, %x, %x, %p)\n", debugstr_a(path
),
1902 debugstr_a(short_mask
), debugstr_a(long_mask
), drive
, attr
, skip
,
1907 RtlCreateUnicodeStringFromAsciiz(&short_maskW
, short_mask
);
1908 RtlCreateUnicodeStringFromAsciiz(&long_maskW
, long_mask
);
1910 /* Check the cached directory */
1911 if (!(info
.u
.dos_dir
&& info
.path
== path
&& !strcmpW(info
.short_mask
, short_maskW
.Buffer
)
1912 && !strcmpW(info
.long_mask
, long_maskW
.Buffer
) && info
.drive
== drive
1913 && info
.attr
== attr
&& info
.cur_pos
<= skip
))
1915 /* Not in the cache, open it anew */
1916 if (info
.u
.dos_dir
) DOSFS_CloseDir( info
.u
.dos_dir
);
1918 info
.path
= (LPSTR
)path
;
1919 RtlFreeHeap(GetProcessHeap(), 0, info
.long_mask
);
1920 RtlFreeHeap(GetProcessHeap(), 0, info
.short_mask
);
1921 info
.long_mask
= long_maskW
.Buffer
;
1922 info
.short_mask
= short_maskW
.Buffer
;
1926 info
.u
.dos_dir
= DOSFS_OpenDir( DRIVE_GetCodepage(drive
), info
.path
);
1930 RtlFreeUnicodeString(&short_maskW
);
1931 RtlFreeUnicodeString(&long_maskW
);
1934 /* Skip to desired position */
1935 while (info
.cur_pos
< skip
)
1936 if (info
.u
.dos_dir
&& DOSFS_ReadDir( info
.u
.dos_dir
, &long_name
, &short_name
))
1941 if (info
.u
.dos_dir
&& info
.cur_pos
== skip
&& DOSFS_FindNextEx( &info
, &entryW
))
1943 WideCharToMultiByte(CP_ACP
, 0, entryW
.cFileName
, -1,
1944 entry
->cFileName
, sizeof(entry
->cFileName
), NULL
, NULL
);
1945 WideCharToMultiByte(CP_ACP
, 0, entryW
.cAlternateFileName
, -1,
1946 entry
->cAlternateFileName
, sizeof(entry
->cAlternateFileName
), NULL
, NULL
);
1947 count
= info
.cur_pos
- skip
;
1949 entry
->dwFileAttributes
= entryW
.dwFileAttributes
;
1950 entry
->nFileSizeHigh
= entryW
.nFileSizeHigh
;
1951 entry
->nFileSizeLow
= entryW
.nFileSizeLow
;
1952 entry
->ftCreationTime
= entryW
.ftCreationTime
;
1953 entry
->ftLastAccessTime
= entryW
.ftLastAccessTime
;
1954 entry
->ftLastWriteTime
= entryW
.ftLastWriteTime
;
1962 if (info
.u
.dos_dir
) DOSFS_CloseDir( info
.u
.dos_dir
);
1963 memset( &info
, '\0', sizeof(info
) );
1971 /*************************************************************************
1972 * FindFirstFileExW (KERNEL32.@)
1974 HANDLE WINAPI
FindFirstFileExW(
1976 FINDEX_INFO_LEVELS fInfoLevelId
,
1977 LPVOID lpFindFileData
,
1978 FINDEX_SEARCH_OPS fSearchOp
,
1979 LPVOID lpSearchFilter
,
1980 DWORD dwAdditionalFlags
)
1983 FIND_FIRST_INFO
*info
;
1987 SetLastError(ERROR_PATH_NOT_FOUND
);
1988 return INVALID_HANDLE_VALUE
;
1991 if ((fSearchOp
!= FindExSearchNameMatch
) || (dwAdditionalFlags
!= 0))
1993 FIXME("options not implemented 0x%08x 0x%08lx\n", fSearchOp
, dwAdditionalFlags
);
1994 return INVALID_HANDLE_VALUE
;
1997 switch(fInfoLevelId
)
1999 case FindExInfoStandard
:
2001 WIN32_FIND_DATAW
* data
= (WIN32_FIND_DATAW
*) lpFindFileData
;
2006 data
->dwReserved0
= data
->dwReserved1
= 0x0;
2007 if (lpFileName
[0] == '\\' && lpFileName
[1] == '\\')
2009 ERR("UNC path name\n");
2010 if (!(handle
= GlobalAlloc(GMEM_MOVEABLE
, sizeof(FIND_FIRST_INFO
)))) break;
2012 info
= (FIND_FIRST_INFO
*)GlobalLock( handle
);
2013 info
->u
.smb_dir
= SMB_FindFirst(lpFileName
);
2014 if(!info
->u
.smb_dir
)
2016 GlobalUnlock( handle
);
2023 GlobalUnlock( handle
);
2027 DOS_FULL_NAME full_name
;
2029 if (lpFileName
[0] && lpFileName
[1] == ':')
2031 /* don't allow root directories */
2032 if (!lpFileName
[2] ||
2033 ((lpFileName
[2] == '/' || lpFileName
[2] == '\\') && !lpFileName
[3]))
2035 SetLastError(ERROR_FILE_NOT_FOUND
);
2036 return INVALID_HANDLE_VALUE
;
2039 if (!DOSFS_GetFullName( lpFileName
, FALSE
, &full_name
)) break;
2040 if (!(handle
= GlobalAlloc(GMEM_MOVEABLE
, sizeof(FIND_FIRST_INFO
)))) break;
2041 info
= (FIND_FIRST_INFO
*)GlobalLock( handle
);
2042 info
->path
= HeapAlloc( GetProcessHeap(), 0, strlen(full_name
.long_name
)+1 );
2043 strcpy( info
->path
, full_name
.long_name
);
2045 codepage
= DRIVE_GetCodepage(full_name
.drive
);
2046 p
= strrchr( info
->path
, '/' );
2048 long_mask_len
= MultiByteToWideChar(codepage
, 0, p
, -1, NULL
, 0);
2049 info
->long_mask
= HeapAlloc( GetProcessHeap(), 0, long_mask_len
* sizeof(WCHAR
) );
2050 MultiByteToWideChar(codepage
, 0, p
, -1, info
->long_mask
, long_mask_len
);
2052 info
->short_mask
= NULL
;
2054 info
->drive
= full_name
.drive
;
2057 info
->u
.dos_dir
= DOSFS_OpenDir( codepage
, info
->path
);
2058 GlobalUnlock( handle
);
2060 if (!FindNextFileW( handle
, data
))
2062 FindClose( handle
);
2063 SetLastError( ERROR_FILE_NOT_FOUND
);
2070 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId
);
2072 return INVALID_HANDLE_VALUE
;
2075 /*************************************************************************
2076 * FindFirstFileA (KERNEL32.@)
2078 HANDLE WINAPI
FindFirstFileA(
2080 WIN32_FIND_DATAA
*lpFindData
)
2082 return FindFirstFileExA(lpFileName
, FindExInfoStandard
, lpFindData
,
2083 FindExSearchNameMatch
, NULL
, 0);
2086 /*************************************************************************
2087 * FindFirstFileExA (KERNEL32.@)
2089 HANDLE WINAPI
FindFirstFileExA(
2091 FINDEX_INFO_LEVELS fInfoLevelId
,
2092 LPVOID lpFindFileData
,
2093 FINDEX_SEARCH_OPS fSearchOp
,
2094 LPVOID lpSearchFilter
,
2095 DWORD dwAdditionalFlags
)
2098 WIN32_FIND_DATAA
*dataA
;
2099 WIN32_FIND_DATAW dataW
;
2100 UNICODE_STRING pathW
;
2104 SetLastError(ERROR_PATH_NOT_FOUND
);
2105 return INVALID_HANDLE_VALUE
;
2108 if (!RtlCreateUnicodeStringFromAsciiz(&pathW
, lpFileName
))
2110 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
2111 return INVALID_HANDLE_VALUE
;
2114 handle
= FindFirstFileExW(pathW
.Buffer
, fInfoLevelId
, &dataW
, fSearchOp
, lpSearchFilter
, dwAdditionalFlags
);
2115 RtlFreeUnicodeString(&pathW
);
2116 if (handle
== INVALID_HANDLE_VALUE
) return handle
;
2118 dataA
= (WIN32_FIND_DATAA
*) lpFindFileData
;
2119 dataA
->dwFileAttributes
= dataW
.dwFileAttributes
;
2120 dataA
->ftCreationTime
= dataW
.ftCreationTime
;
2121 dataA
->ftLastAccessTime
= dataW
.ftLastAccessTime
;
2122 dataA
->ftLastWriteTime
= dataW
.ftLastWriteTime
;
2123 dataA
->nFileSizeHigh
= dataW
.nFileSizeHigh
;
2124 dataA
->nFileSizeLow
= dataW
.nFileSizeLow
;
2125 WideCharToMultiByte( CP_ACP
, 0, dataW
.cFileName
, -1,
2126 dataA
->cFileName
, sizeof(dataA
->cFileName
), NULL
, NULL
);
2127 WideCharToMultiByte( CP_ACP
, 0, dataW
.cAlternateFileName
, -1,
2128 dataA
->cAlternateFileName
, sizeof(dataA
->cAlternateFileName
), NULL
, NULL
);
2132 /*************************************************************************
2133 * FindFirstFileW (KERNEL32.@)
2135 HANDLE WINAPI
FindFirstFileW( LPCWSTR lpFileName
, WIN32_FIND_DATAW
*lpFindData
)
2137 return FindFirstFileExW(lpFileName
, FindExInfoStandard
, lpFindData
,
2138 FindExSearchNameMatch
, NULL
, 0);
2141 /*************************************************************************
2142 * FindNextFileW (KERNEL32.@)
2144 BOOL WINAPI
FindNextFileW( HANDLE handle
, WIN32_FIND_DATAW
*data
)
2146 FIND_FIRST_INFO
*info
;
2148 DWORD gle
= ERROR_NO_MORE_FILES
;
2150 if ((handle
== INVALID_HANDLE_VALUE
) ||
2151 !(info
= (FIND_FIRST_INFO
*)GlobalLock( handle
)))
2153 SetLastError( ERROR_INVALID_HANDLE
);
2156 if (info
->drive
== -1)
2158 ret
= SMB_FindNext( info
->u
.smb_dir
, data
);
2161 SMB_CloseDir( info
->u
.smb_dir
);
2162 HeapFree( GetProcessHeap(), 0, info
->path
);
2166 else if (!info
->path
|| !info
->u
.dos_dir
)
2170 else if (!DOSFS_FindNextEx( info
, data
))
2172 DOSFS_CloseDir( info
->u
.dos_dir
); info
->u
.dos_dir
= NULL
;
2173 HeapFree( GetProcessHeap(), 0, info
->path
);
2175 HeapFree( GetProcessHeap(), 0, info
->long_mask
);
2176 info
->long_mask
= NULL
;
2181 GlobalUnlock( handle
);
2182 if( !ret
) SetLastError( gle
);
2187 /*************************************************************************
2188 * FindNextFileA (KERNEL32.@)
2190 BOOL WINAPI
FindNextFileA( HANDLE handle
, WIN32_FIND_DATAA
*data
)
2192 WIN32_FIND_DATAW dataW
;
2193 if (!FindNextFileW( handle
, &dataW
)) return FALSE
;
2194 data
->dwFileAttributes
= dataW
.dwFileAttributes
;
2195 data
->ftCreationTime
= dataW
.ftCreationTime
;
2196 data
->ftLastAccessTime
= dataW
.ftLastAccessTime
;
2197 data
->ftLastWriteTime
= dataW
.ftLastWriteTime
;
2198 data
->nFileSizeHigh
= dataW
.nFileSizeHigh
;
2199 data
->nFileSizeLow
= dataW
.nFileSizeLow
;
2200 WideCharToMultiByte( CP_ACP
, 0, dataW
.cFileName
, -1,
2201 data
->cFileName
, sizeof(data
->cFileName
), NULL
, NULL
);
2202 WideCharToMultiByte( CP_ACP
, 0, dataW
.cAlternateFileName
, -1,
2203 data
->cAlternateFileName
,
2204 sizeof(data
->cAlternateFileName
), NULL
, NULL
);
2208 /*************************************************************************
2209 * FindClose (KERNEL32.@)
2211 BOOL WINAPI
FindClose( HANDLE handle
)
2213 FIND_FIRST_INFO
*info
;
2215 if (handle
== INVALID_HANDLE_VALUE
) goto error
;
2219 if ((info
= (FIND_FIRST_INFO
*)GlobalLock( handle
)))
2221 if (info
->u
.dos_dir
) DOSFS_CloseDir( info
->u
.dos_dir
);
2222 if (info
->path
) HeapFree( GetProcessHeap(), 0, info
->path
);
2223 if (info
->long_mask
) HeapFree( GetProcessHeap(), 0, info
->long_mask
);
2226 __EXCEPT(page_fault
)
2228 WARN("Illegal handle %p\n", handle
);
2229 SetLastError( ERROR_INVALID_HANDLE
);
2233 if (!info
) goto error
;
2234 GlobalUnlock( handle
);
2235 GlobalFree( handle
);
2239 SetLastError( ERROR_INVALID_HANDLE
);
2243 /***********************************************************************
2244 * MulDiv (KERNEL32.@)
2246 * Result of multiplication and division
2247 * -1: Overflow occurred or Divisor was 0
2254 #if SIZEOF_LONG_LONG >= 8
2257 if (!nDivisor
) return -1;
2259 /* We want to deal with a positive divisor to simplify the logic. */
2262 nMultiplicand
= - nMultiplicand
;
2263 nDivisor
= -nDivisor
;
2266 /* If the result is positive, we "add" to round. else, we subtract to round. */
2267 if ( ( (nMultiplicand
< 0) && (nMultiplier
< 0) ) ||
2268 ( (nMultiplicand
>= 0) && (nMultiplier
>= 0) ) )
2269 ret
= (((long long)nMultiplicand
* nMultiplier
) + (nDivisor
/2)) / nDivisor
;
2271 ret
= (((long long)nMultiplicand
* nMultiplier
) - (nDivisor
/2)) / nDivisor
;
2273 if ((ret
> 2147483647) || (ret
< -2147483647)) return -1;
2276 if (!nDivisor
) return -1;
2278 /* We want to deal with a positive divisor to simplify the logic. */
2281 nMultiplicand
= - nMultiplicand
;
2282 nDivisor
= -nDivisor
;
2285 /* If the result is positive, we "add" to round. else, we subtract to round. */
2286 if ( ( (nMultiplicand
< 0) && (nMultiplier
< 0) ) ||
2287 ( (nMultiplicand
>= 0) && (nMultiplier
>= 0) ) )
2288 return ((nMultiplicand
* nMultiplier
) + (nDivisor
/2)) / nDivisor
;
2290 return ((nMultiplicand
* nMultiplier
) - (nDivisor
/2)) / nDivisor
;
2296 /***********************************************************************
2297 * DosDateTimeToFileTime (KERNEL32.@)
2299 BOOL WINAPI
DosDateTimeToFileTime( WORD fatdate
, WORD fattime
, LPFILETIME ft
)
2304 time_t time1
, time2
;
2307 newtm
.tm_sec
= (fattime
& 0x1f) * 2;
2308 newtm
.tm_min
= (fattime
>> 5) & 0x3f;
2309 newtm
.tm_hour
= (fattime
>> 11);
2310 newtm
.tm_mday
= (fatdate
& 0x1f);
2311 newtm
.tm_mon
= ((fatdate
>> 5) & 0x0f) - 1;
2312 newtm
.tm_year
= (fatdate
>> 9) + 80;
2314 RtlSecondsSince1970ToTime( timegm(&newtm
), (LARGE_INTEGER
*)ft
);
2316 time1
= mktime(&newtm
);
2317 gtm
= gmtime(&time1
);
2318 time2
= mktime(gtm
);
2319 RtlSecondsSince1970ToTime( 2*time1
-time2
, (LARGE_INTEGER
*)ft
);
2325 /***********************************************************************
2326 * FileTimeToDosDateTime (KERNEL32.@)
2328 BOOL WINAPI
FileTimeToDosDateTime( const FILETIME
*ft
, LPWORD fatdate
,
2336 li
.s
.LowPart
= ft
->dwLowDateTime
;
2337 li
.s
.HighPart
= ft
->dwHighDateTime
;
2338 RtlTimeToSecondsSince1970( &li
, &t
);
2340 tm
= gmtime( &unixtime
);
2342 *fattime
= (tm
->tm_hour
<< 11) + (tm
->tm_min
<< 5) + (tm
->tm_sec
/ 2);
2344 *fatdate
= ((tm
->tm_year
- 80) << 9) + ((tm
->tm_mon
+ 1) << 5)
2350 /***********************************************************************
2351 * QueryDosDeviceA (KERNEL32.@)
2353 * returns array of strings terminated by \0, terminated by \0
2355 DWORD WINAPI
QueryDosDeviceA(LPCSTR devname
,LPSTR target
,DWORD bufsize
)
2360 TRACE("(%s,...)\n", devname
? devname
: "<null>");
2362 /* return known MSDOS devices */
2363 static const char devices
[24] = "CON\0COM1\0COM2\0LPT1\0NUL\0\0";
2364 memcpy( target
, devices
, min(bufsize
,sizeof(devices
)) );
2365 return min(bufsize
,sizeof(devices
));
2367 /* In theory all that are possible and have been defined.
2368 * Now just those below, since mirc uses it to check for special files.
2370 * (It is more complex, and supports netmounted stuff, and \\.\ stuff,
2371 * but currently we just ignore that.)
2373 #define CHECK(x) (strstr(devname,#x)==devname)
2374 if (CHECK(con
) || CHECK(com
) || CHECK(lpt
) || CHECK(nul
)) {
2375 strcpy(buffer
,"\\DEV\\");
2376 strcat(buffer
,devname
);
2377 if ((s
=strchr(buffer
,':'))) *s
='\0';
2378 lstrcpynA(target
,buffer
,bufsize
);
2379 return strlen(buffer
)+1;
2381 if (strchr(devname
,':') || devname
[0]=='\\') {
2382 /* This might be a DOS device we do not handle yet ... */
2383 FIXME("(%s) not detected as DOS device!\n",devname
);
2385 SetLastError(ERROR_DEV_NOT_EXIST
);
2392 /***********************************************************************
2393 * QueryDosDeviceW (KERNEL32.@)
2395 * returns array of strings terminated by \0, terminated by \0
2397 DWORD WINAPI
QueryDosDeviceW(LPCWSTR devname
,LPWSTR target
,DWORD bufsize
)
2399 LPSTR devnameA
= devname
?HEAP_strdupWtoA(GetProcessHeap(),0,devname
):NULL
;
2400 LPSTR targetA
= (LPSTR
)HeapAlloc(GetProcessHeap(),0,bufsize
);
2401 DWORD ret
= QueryDosDeviceA(devnameA
,targetA
,bufsize
);
2403 ret
= MultiByteToWideChar( CP_ACP
, 0, targetA
, ret
, target
, bufsize
);
2404 if (devnameA
) HeapFree(GetProcessHeap(),0,devnameA
);
2405 if (targetA
) HeapFree(GetProcessHeap(),0,targetA
);
2410 /***********************************************************************
2411 * DefineDosDeviceA (KERNEL32.@)
2413 BOOL WINAPI
DefineDosDeviceA(DWORD flags
,LPCSTR devname
,LPCSTR targetpath
) {
2414 FIXME("(0x%08lx,%s,%s),stub!\n",flags
,devname
,targetpath
);
2415 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
2420 --- 16 bit functions ---
2423 /*************************************************************************
2424 * FindFirstFile (KERNEL.413)
2426 HANDLE16 WINAPI
FindFirstFile16( LPCSTR path
, WIN32_FIND_DATAA
*data
)
2428 DOS_FULL_NAME full_name
;
2430 FIND_FIRST_INFO
*info
;
2431 WCHAR pathW
[MAX_PATH
];
2436 data
->dwReserved0
= data
->dwReserved1
= 0x0;
2437 if (!path
) return INVALID_HANDLE_VALUE16
;
2438 MultiByteToWideChar(CP_ACP
, 0, path
, -1, pathW
, MAX_PATH
);
2439 if (!DOSFS_GetFullName( pathW
, FALSE
, &full_name
))
2440 return INVALID_HANDLE_VALUE16
;
2441 if (!(handle
= GlobalAlloc16( GMEM_MOVEABLE
, sizeof(FIND_FIRST_INFO
) )))
2442 return INVALID_HANDLE_VALUE16
;
2443 info
= (FIND_FIRST_INFO
*)GlobalLock16( handle
);
2444 info
->path
= HeapAlloc( GetProcessHeap(), 0, strlen(full_name
.long_name
)+1 );
2445 strcpy( info
->path
, full_name
.long_name
);
2447 codepage
= DRIVE_GetCodepage(full_name
.drive
);
2448 p
= strrchr( info
->path
, '/' );
2450 long_mask_len
= MultiByteToWideChar(codepage
, 0, p
, -1, NULL
, 0);
2451 info
->long_mask
= HeapAlloc( GetProcessHeap(), 0, long_mask_len
* sizeof(WCHAR
) );
2452 MultiByteToWideChar(codepage
, 0, p
, -1, info
->long_mask
, long_mask_len
);
2454 info
->short_mask
= NULL
;
2456 info
->drive
= full_name
.drive
;
2459 info
->u
.dos_dir
= DOSFS_OpenDir( codepage
, info
->path
);
2461 GlobalUnlock16( handle
);
2462 if (!FindNextFile16( handle
, data
))
2464 FindClose16( handle
);
2465 SetLastError( ERROR_NO_MORE_FILES
);
2466 return INVALID_HANDLE_VALUE16
;
2471 /*************************************************************************
2472 * FindNextFile (KERNEL.414)
2474 BOOL16 WINAPI
FindNextFile16( HANDLE16 handle
, WIN32_FIND_DATAA
*data
)
2476 FIND_FIRST_INFO
*info
;
2477 WIN32_FIND_DATAW dataW
;
2479 DWORD gle
= ERROR_NO_MORE_FILES
;
2481 if ((handle
== INVALID_HANDLE_VALUE16
) ||
2482 !(info
= (FIND_FIRST_INFO
*)GlobalLock16( handle
)))
2484 SetLastError( ERROR_INVALID_HANDLE
);
2487 if (!info
->path
|| !info
->u
.dos_dir
)
2491 if (!DOSFS_FindNextEx( info
, &dataW
))
2493 DOSFS_CloseDir( info
->u
.dos_dir
); info
->u
.dos_dir
= NULL
;
2494 HeapFree( GetProcessHeap(), 0, info
->path
);
2496 HeapFree( GetProcessHeap(), 0, info
->long_mask
);
2497 info
->long_mask
= NULL
;
2503 data
->dwFileAttributes
= dataW
.dwFileAttributes
;
2504 data
->ftCreationTime
= dataW
.ftCreationTime
;
2505 data
->ftLastAccessTime
= dataW
.ftLastAccessTime
;
2506 data
->ftLastWriteTime
= dataW
.ftLastWriteTime
;
2507 data
->nFileSizeHigh
= dataW
.nFileSizeHigh
;
2508 data
->nFileSizeLow
= dataW
.nFileSizeLow
;
2509 WideCharToMultiByte( CP_ACP
, 0, dataW
.cFileName
, -1,
2510 data
->cFileName
, sizeof(data
->cFileName
), NULL
, NULL
);
2511 WideCharToMultiByte( CP_ACP
, 0, dataW
.cAlternateFileName
, -1,
2512 data
->cAlternateFileName
,
2513 sizeof(data
->cAlternateFileName
), NULL
, NULL
);
2515 if( !ret
) SetLastError( gle
);
2516 GlobalUnlock16( handle
);
2521 /*************************************************************************
2522 * FindClose (KERNEL.415)
2524 BOOL16 WINAPI
FindClose16( HANDLE16 handle
)
2526 FIND_FIRST_INFO
*info
;
2528 if ((handle
== INVALID_HANDLE_VALUE16
) ||
2529 !(info
= (FIND_FIRST_INFO
*)GlobalLock16( handle
)))
2531 SetLastError( ERROR_INVALID_HANDLE
);
2534 if (info
->u
.dos_dir
) DOSFS_CloseDir( info
->u
.dos_dir
);
2535 if (info
->path
) HeapFree( GetProcessHeap(), 0, info
->path
);
2536 if (info
->long_mask
) HeapFree( GetProcessHeap(), 0, info
->long_mask
);
2537 GlobalUnlock16( handle
);
2538 GlobalFree16( handle
);