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>
47 #include "wine/unicode.h"
48 #include "wine/winbase16.h"
54 #include "wine/server.h"
59 #include "wine/debug.h"
61 WINE_DEFAULT_DEBUG_CHANNEL(dosfs
);
62 WINE_DECLARE_DEBUG_CHANNEL(file
);
64 /* Define the VFAT ioctl to get both short and long file names */
65 /* FIXME: is it possible to get this to work on other systems? */
67 /* We want the real kernel dirent structure, not the libc one */
72 unsigned short d_reclen
;
76 #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, KERNEL_DIRENT [2] )
78 /* To avoid blocking on non-directories in DOSFS_OpenDir_VFAT*/
80 # define O_DIRECTORY 0200000 /* must be directory */
84 #undef VFAT_IOCTL_READDIR_BOTH /* just in case... */
87 /* Chars we don't want to see in DOS file names */
88 #define INVALID_DOS_CHARS "*?<>|\"+=,;[] \345"
90 static const DOS_DEVICE DOSFS_Devices
[] =
91 /* name, device flags (see Int 21/AX=0x4400) */
93 { {'C','O','N',0}, 0xc0d3 },
94 { {'P','R','N',0}, 0xa0c0 },
95 { {'N','U','L',0}, 0x80c4 },
96 { {'A','U','X',0}, 0x80c0 },
97 { {'L','P','T','1',0}, 0xa0c0 },
98 { {'L','P','T','2',0}, 0xa0c0 },
99 { {'L','P','T','3',0}, 0xa0c0 },
100 { {'L','P','T','4',0}, 0xc0d3 },
101 { {'C','O','M','1',0}, 0x80c0 },
102 { {'C','O','M','2',0}, 0x80c0 },
103 { {'C','O','M','3',0}, 0x80c0 },
104 { {'C','O','M','4',0}, 0x80c0 },
105 { {'S','C','S','I','M','G','R','$',0}, 0xc0c0 },
106 { {'H','P','S','C','A','N',0}, 0xc0c0 },
107 { {'E','M','M','X','X','X','X','0',0}, 0x0000 }
111 * Directory info for DOSFS_ReadDir
112 * contains the names of *all* the files in the directory
121 /* Info structure for FindFirstFile handle */
124 char *path
; /* unix path */
138 static WINE_EXCEPTION_FILTER(page_fault
)
140 if (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION
)
141 return EXCEPTION_EXECUTE_HANDLER
;
142 return EXCEPTION_CONTINUE_SEARCH
;
146 /***********************************************************************
149 * Return 1 if Unix file 'name' is also a valid MS-DOS name
150 * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
151 * File name can be terminated by '\0', '\\' or '/'.
153 static int DOSFS_ValidDOSName( LPCWSTR name
, int ignore_case
)
155 static const char invalid_chars
[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS
;
156 const WCHAR
*p
= name
;
157 const char *invalid
= ignore_case
? (invalid_chars
+ 26) : invalid_chars
;
162 /* Check for "." and ".." */
165 /* All other names beginning with '.' are invalid */
166 return (IS_END_OF_NAME(*p
));
168 while (!IS_END_OF_NAME(*p
))
170 if (*p
< 256 && strchr( invalid
, (char)*p
)) return 0; /* Invalid char */
171 if (*p
== '.') break; /* Start of the extension */
172 if (++len
> 8) return 0; /* Name too long */
175 if (*p
!= '.') return 1; /* End of name */
177 if (IS_END_OF_NAME(*p
)) return 0; /* Empty extension not allowed */
179 while (!IS_END_OF_NAME(*p
))
181 if (*p
< 256 && strchr( invalid
, (char)*p
)) return 0; /* Invalid char */
182 if (*p
== '.') return 0; /* Second extension not allowed */
183 if (++len
> 3) return 0; /* Extension too long */
190 /***********************************************************************
191 * DOSFS_ToDosFCBFormat
193 * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
194 * expanding wild cards and converting to upper-case in the process.
195 * File name can be terminated by '\0', '\\' or '/'.
196 * Return FALSE if the name is not a valid DOS name.
197 * 'buffer' must be at least 12 characters long.
199 BOOL
DOSFS_ToDosFCBFormat( LPCWSTR name
, LPWSTR buffer
)
201 static const char invalid_chars
[] = INVALID_DOS_CHARS
;
205 TRACE("(%s, %p)\n", debugstr_w(name
), buffer
);
207 /* Check for "." and ".." */
212 for(i
= 1; i
< 11; i
++) buffer
[i
] = ' ';
219 return (!*p
|| (*p
== '/') || (*p
== '\\'));
222 for (i
= 0; i
< 8; i
++)
239 if (*p
< 256 && strchr( invalid_chars
, (char)*p
)) return FALSE
;
240 buffer
[i
] = toupperW(*p
);
248 /* Skip all chars after wildcard up to first dot */
249 while (*p
&& (*p
!= '/') && (*p
!= '\\') && (*p
!= '.')) p
++;
253 /* Check if name too long */
254 if (*p
&& (*p
!= '/') && (*p
!= '\\') && (*p
!= '.')) return FALSE
;
256 if (*p
== '.') p
++; /* Skip dot */
258 for (i
= 8; i
< 11; i
++)
268 return FALSE
; /* Second extension not allowed */
276 if (*p
< 256 && strchr( invalid_chars
, (char)*p
)) return FALSE
;
277 buffer
[i
] = toupperW(*p
);
284 /* at most 3 character of the extension are processed
285 * is something behind this ?
287 while (*p
== '*' || *p
== ' ') p
++; /* skip wildcards and spaces */
288 return IS_END_OF_NAME(*p
);
292 /***********************************************************************
293 * DOSFS_ToDosDTAFormat
295 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
296 * converting to upper-case in the process.
297 * File name can be terminated by '\0', '\\' or '/'.
298 * 'buffer' must be at least 13 characters long.
300 static void DOSFS_ToDosDTAFormat( LPCWSTR name
, LPWSTR buffer
)
304 memcpy( buffer
, name
, 8 * sizeof(WCHAR
) );
306 while ((p
> buffer
) && (p
[-1] == ' ')) p
--;
308 memcpy( p
, name
+ 8, 3 * sizeof(WCHAR
) );
310 while (p
[-1] == ' ') p
--;
311 if (p
[-1] == '.') p
--;
316 /***********************************************************************
319 * Check a DOS file name against a mask (both in FCB format).
321 static int DOSFS_MatchShort( LPCWSTR mask
, LPCWSTR name
)
324 for (i
= 11; i
> 0; i
--, mask
++, name
++)
325 if ((*mask
!= '?') && (*mask
!= *name
)) return 0;
330 /***********************************************************************
333 * Check a long file name against a mask.
335 * Tests (done in W95 DOS shell - case insensitive):
336 * *.txt test1.test.txt *
338 * *.t??????.t* test1.ta.tornado.txt *
339 * *tornado* test1.ta.tornado.txt *
340 * t*t test1.ta.tornado.txt *
342 * ?est??? test1.txt -
343 * *test1.txt* test1.txt *
344 * h?l?o*t.dat hellothisisatest.dat *
346 static int DOSFS_MatchLong( LPCWSTR mask
, LPCWSTR name
, int case_sensitive
)
348 LPCWSTR lastjoker
= NULL
;
349 LPCWSTR next_to_retry
= NULL
;
350 static const WCHAR asterisk_dot_asterisk
[] = {'*','.','*',0};
352 TRACE("(%s, %s, %x)\n", debugstr_w(mask
), debugstr_w(name
), case_sensitive
);
354 if (!strcmpW( mask
, asterisk_dot_asterisk
)) return 1;
355 while (*name
&& *mask
)
360 while (*mask
== '*') mask
++; /* Skip consecutive '*' */
362 if (!*mask
) return 1; /* end of mask is all '*', so match */
364 /* skip to the next match after the joker(s) */
365 if (case_sensitive
) while (*name
&& (*name
!= *mask
)) name
++;
366 else while (*name
&& (toupperW(*name
) != toupperW(*mask
))) name
++;
369 next_to_retry
= name
;
371 else if (*mask
!= '?')
376 if (*mask
!= *name
) mismatch
= 1;
380 if (toupperW(*mask
) != toupperW(*name
)) mismatch
= 1;
394 else /* mismatch ! */
396 if (lastjoker
) /* we had an '*', so we can try unlimitedly */
400 /* this scan sequence was a mismatch, so restart
401 * 1 char after the first char we checked last time */
403 name
= next_to_retry
;
406 return 0; /* bad luck */
415 while ((*mask
== '.') || (*mask
== '*'))
416 mask
++; /* Ignore trailing '.' or '*' in mask */
417 return (!*name
&& !*mask
);
421 /***********************************************************************
424 * Used to construct an array of filenames in DOSFS_OpenDir
426 static BOOL
DOSFS_AddDirEntry(DOS_DIR
**dir
, LPCWSTR name
, LPCWSTR dosname
)
428 int extra1
= (strlenW(name
) + 1) * sizeof(WCHAR
);
429 int extra2
= (strlenW(dosname
) + 1) * sizeof(WCHAR
);
431 /* if we need more, at minimum double the size */
432 if( (extra1
+ extra2
+ (*dir
)->used
) > (*dir
)->size
)
434 int more
= (*dir
)->size
;
437 if(more
<(extra1
+extra2
))
438 more
= extra1
+extra2
;
440 t
= HeapReAlloc(GetProcessHeap(), 0, *dir
, sizeof(**dir
) + (*dir
)->size
+ more
);
443 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
444 ERR("Out of memory caching directory structure %d %d %d\n",
445 (*dir
)->size
, more
, (*dir
)->used
);
449 (*dir
)->size
+= more
;
452 /* at this point, the dir structure is big enough to hold these names */
453 strcpyW((LPWSTR
)&(*dir
)->names
[(*dir
)->used
], name
);
454 (*dir
)->used
+= extra1
;
455 strcpyW((LPWSTR
)&(*dir
)->names
[(*dir
)->used
], dosname
);
456 (*dir
)->used
+= extra2
;
462 /***********************************************************************
465 static BOOL
DOSFS_OpenDir_VFAT(UINT codepage
, DOS_DIR
**dir
, const char *unix_path
)
467 #ifdef VFAT_IOCTL_READDIR_BOTH
469 int fd
= open( unix_path
, O_RDONLY
|O_DIRECTORY
);
472 /* Check if the VFAT ioctl is supported on this directory */
479 WCHAR long_name
[MAX_PATH
];
480 WCHAR short_name
[12];
482 r
= (ioctl( fd
, VFAT_IOCTL_READDIR_BOTH
, (long)de
) != -1);
487 MultiByteToWideChar(codepage
, 0, de
[0].d_name
, -1, long_name
, MAX_PATH
);
488 if (!DOSFS_ToDosFCBFormat( long_name
, short_name
))
489 short_name
[0] = '\0';
491 MultiByteToWideChar(codepage
, 0, de
[1].d_name
, -1, long_name
, MAX_PATH
);
493 MultiByteToWideChar(codepage
, 0, de
[0].d_name
, -1, long_name
, MAX_PATH
);
494 r
= DOSFS_AddDirEntry(dir
, long_name
, short_name
);
500 static const WCHAR empty_strW
[] = { 0 };
501 DOSFS_AddDirEntry(dir
, empty_strW
, empty_strW
);
507 #endif /* VFAT_IOCTL_READDIR_BOTH */
511 /***********************************************************************
512 * DOSFS_OpenDir_Normal
514 * Now use the standard opendir/readdir interface
516 static BOOL
DOSFS_OpenDir_Normal( UINT codepage
, DOS_DIR
**dir
, const char *unix_path
)
518 DIR *unixdir
= opendir( unix_path
);
520 static const WCHAR empty_strW
[] = { 0 };
526 WCHAR long_name
[MAX_PATH
];
527 struct dirent
*de
= readdir(unixdir
);
531 MultiByteToWideChar(codepage
, 0, de
->d_name
, -1, long_name
, MAX_PATH
);
532 r
= DOSFS_AddDirEntry(dir
, long_name
, empty_strW
);
537 DOSFS_AddDirEntry(dir
, empty_strW
, empty_strW
);
542 /***********************************************************************
545 static DOS_DIR
*DOSFS_OpenDir( UINT codepage
, const char *unix_path
)
547 const int init_size
= 0x100;
548 DOS_DIR
*dir
= HeapAlloc( GetProcessHeap(), 0, sizeof(*dir
) + init_size
);
551 TRACE("%s\n",debugstr_a(unix_path
));
555 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
559 dir
->size
= init_size
;
561 /* Treat empty path as root directory. This simplifies path split into
562 directory and mask in several other places */
563 if (!*unix_path
) unix_path
= "/";
565 r
= DOSFS_OpenDir_VFAT( codepage
, &dir
, unix_path
);
568 r
= DOSFS_OpenDir_Normal( codepage
, &dir
, unix_path
);
572 HeapFree(GetProcessHeap(), 0, dir
);
581 /***********************************************************************
584 static void DOSFS_CloseDir( DOS_DIR
*dir
)
586 HeapFree( GetProcessHeap(), 0, dir
);
590 /***********************************************************************
593 static BOOL
DOSFS_ReadDir( DOS_DIR
*dir
, LPCWSTR
*long_name
,
594 LPCWSTR
*short_name
)
601 /* the long pathname is first */
602 ln
= (LPCWSTR
)&dir
->names
[dir
->used
];
607 dir
->used
+= (strlenW(ln
) + 1) * sizeof(WCHAR
);
609 /* followed by the short path name */
610 sn
= (LPCWSTR
)&dir
->names
[dir
->used
];
615 dir
->used
+= (strlenW(sn
) + 1) * sizeof(WCHAR
);
617 TRACE("Read: long_name: %s, short_name: %s\n",
618 debugstr_w(*long_name
), debugstr_w(*short_name
));
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 FILE_DupUnixHandle 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 static const WCHAR serialportsW
[] = {'s','e','r','i','a','l','p','o','r','t','s',0};
864 static const WCHAR empty_strW
[] = { 0 };
866 TRACE_(file
)("%s %lx %lx\n", debugstr_w(name
), access
, attributes
);
868 PROFILE_GetWineIniString(serialportsW
, name
, empty_strW
, devnameW
, 40);
872 WideCharToMultiByte(CP_ACP
, 0, devnameW
, -1, devname
, sizeof(devname
), NULL
, NULL
);
874 TRACE("opening %s as %s\n", devname
, debugstr_w(name
));
876 SERVER_START_REQ( create_serial
)
878 req
->access
= access
;
879 req
->inherit
= (sa
&& (sa
->nLength
>=sizeof(*sa
)) && sa
->bInheritHandle
);
880 req
->attributes
= attributes
;
881 req
->sharing
= FILE_SHARE_READ
|FILE_SHARE_WRITE
;
882 wine_server_add_data( req
, devname
, strlen(devname
) );
884 wine_server_call_err( req
);
890 ERR("Couldn't open device '%s' ! (check permissions)\n",devname
);
892 TRACE("return %p\n", ret
);
896 /***********************************************************************
899 * Open a DOS device. This might not map 1:1 into the UNIX device concept.
900 * Returns 0 on failure.
902 HANDLE
DOSFS_OpenDevice( LPCWSTR name
, DWORD access
, DWORD attributes
, LPSECURITY_ATTRIBUTES sa
)
908 if (name
[0] && (name
[1] == ':')) name
+= 2;
909 if ((p
= strrchrW( name
, '/' ))) name
= p
+ 1;
910 if ((p
= strrchrW( name
, '\\' ))) name
= p
+ 1;
911 for (i
= 0; i
< sizeof(DOSFS_Devices
)/sizeof(DOSFS_Devices
[0]); i
++)
913 const WCHAR
*dev
= DOSFS_Devices
[i
].name
;
914 if (!strncmpiW( dev
, name
, strlenW(dev
) ))
916 p
= name
+ strlenW( dev
);
917 if (!*p
|| (*p
== '.') || (*p
== ':')) {
918 static const WCHAR nulW
[] = {'N','U','L',0};
919 static const WCHAR conW
[] = {'C','O','N',0};
920 static const WCHAR scsimgrW
[] = {'S','C','S','I','M','G','R','$',0};
921 static const WCHAR hpscanW
[] = {'H','P','S','C','A','N',0};
922 static const WCHAR emmxxxx0W
[] = {'E','M','M','X','X','X','X','0',0};
924 if (!strcmpiW(DOSFS_Devices
[i
].name
, nulW
))
925 return FILE_CreateFile( "/dev/null", access
,
926 FILE_SHARE_READ
|FILE_SHARE_WRITE
, sa
,
927 OPEN_EXISTING
, 0, 0, TRUE
, DRIVE_UNKNOWN
);
928 if (!strcmpiW(DOSFS_Devices
[i
].name
, conW
)) {
930 switch (access
& (GENERIC_READ
|GENERIC_WRITE
)) {
932 to_dup
= GetStdHandle( STD_INPUT_HANDLE
);
935 to_dup
= GetStdHandle( STD_OUTPUT_HANDLE
);
938 FIXME("can't open CON read/write\n");
941 if (!DuplicateHandle( GetCurrentProcess(), to_dup
, GetCurrentProcess(),
943 sa
&& (sa
->nLength
>=sizeof(*sa
)) && sa
->bInheritHandle
,
944 DUPLICATE_SAME_ACCESS
))
948 if (!strcmpiW(DOSFS_Devices
[i
].name
, scsimgrW
) ||
949 !strcmpiW(DOSFS_Devices
[i
].name
, hpscanW
) ||
950 !strcmpiW(DOSFS_Devices
[i
].name
, emmxxxx0W
))
952 return FILE_CreateDevice( i
, access
, sa
);
955 if( (handle
=DOSFS_CreateCommPort(DOSFS_Devices
[i
].name
,access
,attributes
,sa
)) )
957 FIXME("device open %s not supported (yet)\n", debugstr_w(DOSFS_Devices
[i
].name
));
966 /***********************************************************************
969 * Get the drive specified by a given path name (DOS or Unix format).
971 static int DOSFS_GetPathDrive( LPCWSTR
*name
)
976 if (*p
&& (p
[1] == ':'))
978 drive
= toupperW(*p
) - 'A';
981 else if (*p
== '/') /* Absolute Unix path? */
983 if ((drive
= DRIVE_FindDriveRootW( name
)) == -1)
985 MESSAGE("Warning: %s not accessible from a configured DOS drive\n", debugstr_w(*name
) );
986 /* Assume it really was a DOS name */
987 drive
= DRIVE_GetCurrentDrive();
990 else drive
= DRIVE_GetCurrentDrive();
992 if (!DRIVE_IsValid(drive
))
994 SetLastError( ERROR_INVALID_DRIVE
);
1001 /***********************************************************************
1004 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
1005 * Unix name / short DOS name pair.
1006 * Return FALSE if one of the path components does not exist. The last path
1007 * component is only checked if 'check_last' is non-zero.
1008 * The buffers pointed to by 'long_buf' and 'short_buf' must be
1009 * at least MAX_PATHNAME_LEN long.
1011 BOOL
DOSFS_GetFullName( LPCWSTR name
, BOOL check_last
, DOS_FULL_NAME
*full
)
1014 UINT flags
, codepage
;
1017 static const WCHAR driveA_rootW
[] = {'A',':','\\',0};
1018 static const WCHAR dos_rootW
[] = {'\\',0};
1020 TRACE("%s (last=%d)\n", debugstr_w(name
), check_last
);
1022 if ((!*name
) || (*name
=='\n'))
1023 { /* error code for Win98 */
1024 SetLastError(ERROR_BAD_PATHNAME
);
1028 if ((full
->drive
= DOSFS_GetPathDrive( &name
)) == -1) return FALSE
;
1029 flags
= DRIVE_GetFlags( full
->drive
);
1030 codepage
= DRIVE_GetCodepage(full
->drive
);
1032 lstrcpynA( full
->long_name
, DRIVE_GetRoot( full
->drive
),
1033 sizeof(full
->long_name
) );
1034 if (full
->long_name
[1]) root
= full
->long_name
+ strlen(full
->long_name
);
1035 else root
= full
->long_name
; /* root directory */
1037 strcpyW( full
->short_name
, driveA_rootW
);
1038 full
->short_name
[0] += full
->drive
;
1040 if ((*name
== '\\') || (*name
== '/')) /* Absolute path */
1042 while ((*name
== '\\') || (*name
== '/')) name
++;
1044 else /* Relative path */
1046 lstrcpynA( root
+ 1, DRIVE_GetUnixCwd( full
->drive
),
1047 sizeof(full
->long_name
) - (root
- full
->long_name
) - 1 );
1048 if (root
[1]) *root
= '/';
1049 lstrcpynW( full
->short_name
+ 3, DRIVE_GetDosCwd( full
->drive
),
1050 sizeof(full
->short_name
)/sizeof(full
->short_name
[0]) - 3 );
1053 p_l
= full
->long_name
[1] ? full
->long_name
+ strlen(full
->long_name
)
1055 p_s
= full
->short_name
[3] ? full
->short_name
+ strlenW(full
->short_name
)
1056 : full
->short_name
+ 2;
1059 while (*name
&& found
)
1061 /* Check for '.' and '..' */
1065 if (IS_END_OF_NAME(name
[1]))
1068 while ((*name
== '\\') || (*name
== '/')) name
++;
1071 else if ((name
[1] == '.') && IS_END_OF_NAME(name
[2]))
1074 while ((*name
== '\\') || (*name
== '/')) name
++;
1075 while ((p_l
> root
) && (*p_l
!= '/')) p_l
--;
1076 while ((p_s
> full
->short_name
+ 2) && (*p_s
!= '\\')) p_s
--;
1077 *p_l
= *p_s
= '\0'; /* Remove trailing separator */
1082 /* Make sure buffers are large enough */
1084 if ((p_s
>= full
->short_name
+ sizeof(full
->short_name
)/sizeof(full
->short_name
[0]) - 14) ||
1085 (p_l
>= full
->long_name
+ sizeof(full
->long_name
) - 1))
1087 SetLastError( ERROR_PATH_NOT_FOUND
);
1091 /* Get the long and short name matching the file name */
1093 if ((found
= DOSFS_FindUnixName( full
, name
, p_l
+ 1,
1094 sizeof(full
->long_name
) - (p_l
- full
->long_name
) - 1,
1095 p_s
+ 1, !(flags
& DRIVE_CASE_SENSITIVE
) )))
1100 p_s
+= strlenW(p_s
);
1101 while (!IS_END_OF_NAME(*name
)) name
++;
1103 else if (!check_last
)
1107 while (!IS_END_OF_NAME(*name
) &&
1108 (p_s
< full
->short_name
+ sizeof(full
->short_name
)/sizeof(full
->short_name
[0]) - 1) &&
1109 (p_l
< full
->long_name
+ sizeof(full
->long_name
) - 1))
1112 *p_s
++ = tolowerW(*name
);
1113 /* If the drive is case-sensitive we want to create new */
1114 /* files in lower-case otherwise we can't reopen them */
1115 /* under the same short name. */
1116 if (flags
& DRIVE_CASE_SENSITIVE
) wch
= tolowerW(*name
);
1118 p_l
+= WideCharToMultiByte(codepage
, 0, &wch
, 1, p_l
, 2, NULL
, NULL
);
1121 /* Ignore trailing dots and spaces */
1122 while(p_l
[-1] == '.' || p_l
[-1] == ' ') {
1129 while ((*name
== '\\') || (*name
== '/')) name
++;
1136 SetLastError( ERROR_FILE_NOT_FOUND
);
1139 if (*name
) /* Not last */
1141 SetLastError( ERROR_PATH_NOT_FOUND
);
1145 if (!full
->long_name
[0]) strcpy( full
->long_name
, "/" );
1146 if (!full
->short_name
[2]) strcpyW( full
->short_name
+ 2, dos_rootW
);
1147 TRACE("returning %s = %s\n", full
->long_name
, debugstr_w(full
->short_name
) );
1152 /***********************************************************************
1153 * GetShortPathNameW (KERNEL32.@)
1157 * longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1158 * longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0
1160 * more observations ( with NT 3.51 (WinDD) ):
1161 * longpath <= 8.3 -> just copy longpath to shortpath
1163 * a) file does not exist -> return 0, LastError = ERROR_FILE_NOT_FOUND
1164 * b) file does exist -> set the short filename.
1165 * - trailing slashes are reproduced in the short name, even if the
1166 * file is not a directory
1167 * - the absolute/relative path of the short name is reproduced like found
1169 * - longpath and shortpath may have the same address
1170 * Peter Ganten, 1999
1172 DWORD WINAPI
GetShortPathNameW( LPCWSTR longpath
, LPWSTR shortpath
, DWORD shortlen
)
1174 DOS_FULL_NAME full_name
;
1175 WCHAR tmpshortpath
[MAX_PATHNAME_LEN
];
1177 DWORD sp
= 0, lp
= 0;
1181 BOOL unixabsolute
= *longpath
== '/';
1183 TRACE("%s\n", debugstr_w(longpath
));
1186 SetLastError(ERROR_INVALID_PARAMETER
);
1190 SetLastError(ERROR_BAD_PATHNAME
);
1194 /* check for drive letter */
1195 if (!unixabsolute
&& longpath
[1] == ':' ) {
1196 tmpshortpath
[0] = longpath
[0];
1197 tmpshortpath
[1] = ':';
1201 if ( ( drive
= DOSFS_GetPathDrive ( &longpath
)) == -1 ) return 0;
1202 flags
= DRIVE_GetFlags ( drive
);
1204 if (unixabsolute
&& drive
!= DRIVE_GetCurrentDrive()) {
1205 tmpshortpath
[0] = drive
+ 'A';
1206 tmpshortpath
[1] = ':';
1210 while ( longpath
[lp
] ) {
1212 /* check for path delimiters and reproduce them */
1213 if ( longpath
[lp
] == '\\' || longpath
[lp
] == '/' ) {
1214 if (!sp
|| tmpshortpath
[sp
-1]!= '\\')
1216 /* strip double "\\" */
1217 tmpshortpath
[sp
] = '\\';
1220 tmpshortpath
[sp
]=0;/*terminate string*/
1226 for(p
= longpath
+ lp
; *p
&& *p
!= '/' && *p
!= '\\'; p
++)
1228 lstrcpynW(tmpshortpath
+ sp
, longpath
+ lp
, tmplen
+ 1);
1230 /* Check, if the current element is a valid dos name */
1231 if ( DOSFS_ValidDOSName ( longpath
+ lp
, !(flags
& DRIVE_CASE_SENSITIVE
) ) ) {
1237 /* Check if the file exists and use the existing file name */
1238 if ( DOSFS_GetFullName ( tmpshortpath
, TRUE
, &full_name
) ) {
1239 strcpyW(tmpshortpath
+ sp
, strrchrW(full_name
.short_name
, '\\') + 1);
1240 sp
+= strlenW(tmpshortpath
+ sp
);
1245 TRACE("not found!\n" );
1246 SetLastError ( ERROR_FILE_NOT_FOUND
);
1249 tmpshortpath
[sp
] = 0;
1251 tmplen
= strlenW(tmpshortpath
) + 1;
1252 if (tmplen
<= shortlen
)
1254 strcpyW(shortpath
, tmpshortpath
);
1255 TRACE("returning %s\n", debugstr_w(shortpath
));
1256 tmplen
--; /* length without 0 */
1263 /***********************************************************************
1264 * GetShortPathNameA (KERNEL32.@)
1266 DWORD WINAPI
GetShortPathNameA( LPCSTR longpath
, LPSTR shortpath
, DWORD shortlen
)
1268 UNICODE_STRING longpathW
;
1269 WCHAR shortpathW
[MAX_PATH
];
1274 SetLastError(ERROR_INVALID_PARAMETER
);
1278 TRACE("%s\n", debugstr_a(longpath
));
1280 if (!RtlCreateUnicodeStringFromAsciiz(&longpathW
, longpath
))
1282 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1286 retW
= GetShortPathNameW(longpathW
.Buffer
, shortpathW
, MAX_PATH
);
1290 else if (retW
> MAX_PATH
)
1292 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
1297 ret
= WideCharToMultiByte(CP_ACP
, 0, shortpathW
, -1, NULL
, 0, NULL
, NULL
);
1298 if (ret
<= shortlen
)
1300 WideCharToMultiByte(CP_ACP
, 0, shortpathW
, -1, shortpath
, shortlen
, NULL
, NULL
);
1301 ret
--; /* length without 0 */
1305 RtlFreeUnicodeString(&longpathW
);
1310 /***********************************************************************
1311 * GetLongPathNameW (KERNEL32.@)
1314 * observed (Win2000):
1315 * shortpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1316 * shortpath="": LastError=ERROR_PATH_NOT_FOUND, ret=0
1318 DWORD WINAPI
GetLongPathNameW( LPCWSTR shortpath
, LPWSTR longpath
, DWORD longlen
)
1320 DOS_FULL_NAME full_name
;
1328 SetLastError(ERROR_INVALID_PARAMETER
);
1331 if (!shortpath
[0]) {
1332 SetLastError(ERROR_PATH_NOT_FOUND
);
1336 TRACE("%s,%p,%ld\n", debugstr_w(shortpath
), longpath
, longlen
);
1338 if(shortpath
[0]=='\\' && shortpath
[1]=='\\')
1340 ERR("UNC pathname %s\n",debugstr_w(shortpath
));
1341 lstrcpynW( longpath
, full_name
.short_name
, longlen
);
1342 return strlenW(longpath
);
1345 if (!DOSFS_GetFullName( shortpath
, TRUE
, &full_name
)) return 0;
1347 root
= full_name
.long_name
;
1348 drive
= DRIVE_FindDriveRoot(&root
);
1349 codepage
= DRIVE_GetCodepage(drive
);
1351 ret
= MultiByteToWideChar(codepage
, 0, root
, -1, NULL
, 0);
1353 /* reproduce terminating slash */
1354 if (ret
> 4) /* if not drive root */
1356 len
= strlenW(shortpath
);
1357 if (shortpath
[len
- 1] == '\\' || shortpath
[len
- 1] == '/')
1363 longpath
[0] = 'A' + drive
;
1365 MultiByteToWideChar(codepage
, 0, root
, -1, longpath
+ 2, longlen
- 2);
1366 for (p
= longpath
; *p
; p
++) if (*p
== '/') *p
= '\\';
1369 longpath
[ret
- 2] = '\\';
1370 longpath
[ret
- 1] = 0;
1372 TRACE("returning %s\n", debugstr_w(longpath
));
1373 ret
--; /* length without 0 */
1379 /***********************************************************************
1380 * GetLongPathNameA (KERNEL32.@)
1382 DWORD WINAPI
GetLongPathNameA( LPCSTR shortpath
, LPSTR longpath
, DWORD longlen
)
1384 UNICODE_STRING shortpathW
;
1385 WCHAR longpathW
[MAX_PATH
];
1390 SetLastError(ERROR_INVALID_PARAMETER
);
1394 TRACE("%s\n", debugstr_a(shortpath
));
1396 if (!RtlCreateUnicodeStringFromAsciiz(&shortpathW
, shortpath
))
1398 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1402 retW
= GetLongPathNameW(shortpathW
.Buffer
, longpathW
, MAX_PATH
);
1406 else if (retW
> MAX_PATH
)
1408 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
1413 ret
= WideCharToMultiByte(CP_ACP
, 0, longpathW
, -1, NULL
, 0, NULL
, NULL
);
1416 WideCharToMultiByte(CP_ACP
, 0, longpathW
, -1, longpath
, longlen
, NULL
, NULL
);
1417 ret
--; /* length without 0 */
1421 RtlFreeUnicodeString(&shortpathW
);
1426 /***********************************************************************
1427 * DOSFS_DoGetFullPathName
1429 * Implementation of GetFullPathNameA/W.
1431 * bon@elektron 000331:
1432 * A test for GetFullPathName with many pathological cases
1433 * now gives identical output for Wine and OSR2
1435 static DWORD
DOSFS_DoGetFullPathName( LPCWSTR name
, DWORD len
, LPWSTR result
)
1438 DOS_FULL_NAME full_name
;
1442 WCHAR drivecur
[] = {'C',':','.',0};
1443 WCHAR driveletter
=0;
1444 int namelen
,drive
=0;
1445 static const WCHAR bkslashW
[] = {'\\',0};
1446 static const WCHAR dotW
[] = {'.',0};
1447 static const WCHAR updir_slashW
[] = {'\\','.','.','\\',0};
1448 static const WCHAR curdirW
[] = {'\\','.','\\',0};
1449 static const WCHAR updirW
[] = {'\\','.','.',0};
1453 SetLastError(ERROR_BAD_PATHNAME
);
1457 TRACE("passed %s\n", debugstr_w(name
));
1460 /*drive letter given */
1462 driveletter
= name
[0];
1464 if ((name
[1]==':') && ((name
[2]=='\\') || (name
[2]=='/')))
1465 /*absolute path given */
1467 strncpyW(full_name
.short_name
, name
, MAX_PATHNAME_LEN
);
1468 full_name
.short_name
[MAX_PATHNAME_LEN
- 1] = 0; /* ensure 0 termination */
1469 drive
= toupperW(name
[0]) - 'A';
1474 drivecur
[0]=driveletter
;
1475 else if ((name
[0]=='\\') || (name
[0]=='/'))
1476 strcpyW(drivecur
, bkslashW
);
1478 strcpyW(drivecur
, dotW
);
1480 if (!DOSFS_GetFullName( drivecur
, FALSE
, &full_name
))
1482 FIXME("internal: error getting drive/path\n");
1485 /* find path that drive letter substitutes*/
1486 drive
= toupperW(full_name
.short_name
[0]) - 'A';
1487 root
= DRIVE_GetRoot(drive
);
1490 FIXME("internal: error getting DOS Drive Root\n");
1493 if (!strcmp(root
,"/"))
1495 /* we have just the last / and we need it. */
1496 p_l
= full_name
.long_name
;
1500 p_l
= full_name
.long_name
+ strlen(root
);
1502 /* append long name (= unix name) to drive */
1503 MultiByteToWideChar(DRIVE_GetCodepage(drive
), 0, p_l
, -1,
1504 full_name
.short_name
+ 2, MAX_PATHNAME_LEN
- 3);
1505 /* append name to treat */
1506 namelen
= strlenW(full_name
.short_name
);
1509 p
+= 2; /* skip drive name when appending */
1510 if (namelen
+ 2 + strlenW(p
) > MAX_PATHNAME_LEN
)
1512 FIXME("internal error: buffer too small\n");
1515 full_name
.short_name
[namelen
++] ='\\';
1516 full_name
.short_name
[namelen
] = 0;
1517 strncpyW(full_name
.short_name
+ namelen
, p
, MAX_PATHNAME_LEN
- namelen
);
1518 full_name
.short_name
[MAX_PATHNAME_LEN
- 1] = 0; /* ensure 0 termination */
1520 /* reverse all slashes */
1521 for (p
=full_name
.short_name
;
1522 p
< full_name
.short_name
+ strlenW(full_name
.short_name
);
1528 /* Use memmove, as areas overlap */
1530 while ((p
= strstrW(full_name
.short_name
, updir_slashW
)))
1532 if (p
> full_name
.short_name
+2)
1535 q
= strrchrW(full_name
.short_name
, '\\');
1536 memmove(q
+1, p
+4, (strlenW(p
+4)+1) * sizeof(WCHAR
));
1540 memmove(full_name
.short_name
+3, p
+4, (strlenW(p
+4)+1) * sizeof(WCHAR
));
1543 if ((full_name
.short_name
[2]=='.')&&(full_name
.short_name
[3]=='.'))
1545 /* This case istn't treated yet : c:..\test */
1546 memmove(full_name
.short_name
+2,full_name
.short_name
+4,
1547 (strlenW(full_name
.short_name
+4)+1) * sizeof(WCHAR
));
1550 while ((p
= strstrW(full_name
.short_name
, curdirW
)))
1553 memmove(p
+1, p
+3, (strlenW(p
+3)+1) * sizeof(WCHAR
));
1555 if (!(DRIVE_GetFlags(drive
) & DRIVE_CASE_PRESERVING
))
1556 for (p
= full_name
.short_name
; *p
; p
++) *p
= toupperW(*p
);
1557 namelen
= strlenW(full_name
.short_name
);
1558 if (!strcmpW(full_name
.short_name
+namelen
-3, updirW
))
1560 /* one more strange case: "c:\test\test1\.."
1562 *(full_name
.short_name
+namelen
-3)=0;
1563 q
= strrchrW(full_name
.short_name
, '\\');
1566 if (full_name
.short_name
[namelen
-1]=='.')
1567 full_name
.short_name
[(namelen
--)-1] =0;
1569 if (full_name
.short_name
[namelen
-1]=='\\')
1570 full_name
.short_name
[(namelen
--)-1] =0;
1571 TRACE("got %s\n", debugstr_w(full_name
.short_name
));
1573 /* If the lpBuffer buffer is too small, the return value is the
1574 size of the buffer, in characters, required to hold the path
1575 plus the terminating \0 (tested against win95osr2, bon 001118)
1577 ret
= strlenW(full_name
.short_name
);
1580 /* don't touch anything when the buffer is not large enough */
1581 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
1586 strncpyW( result
, full_name
.short_name
, len
);
1587 result
[len
- 1] = 0; /* ensure 0 termination */
1590 TRACE("returning %s\n", debugstr_w(full_name
.short_name
) );
1595 /***********************************************************************
1596 * GetFullPathNameA (KERNEL32.@)
1598 * if the path closed with '\', *lastpart is 0
1600 DWORD WINAPI
GetFullPathNameA( LPCSTR name
, DWORD len
, LPSTR buffer
,
1603 UNICODE_STRING nameW
;
1604 WCHAR bufferW
[MAX_PATH
];
1609 SetLastError(ERROR_INVALID_PARAMETER
);
1613 if (!RtlCreateUnicodeStringFromAsciiz(&nameW
, name
))
1615 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1619 retW
= GetFullPathNameW( nameW
.Buffer
, MAX_PATH
, bufferW
, NULL
);
1623 else if (retW
> MAX_PATH
)
1625 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
1630 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, -1, NULL
, 0, NULL
, NULL
);
1633 WideCharToMultiByte(CP_ACP
, 0, bufferW
, -1, buffer
, len
, NULL
, NULL
);
1634 ret
--; /* length without 0 */
1638 LPSTR p
= buffer
+ strlen(buffer
);
1642 while ((p
> buffer
+ 2) && (*p
!= '\\')) p
--;
1645 else *lastpart
= NULL
;
1650 RtlFreeUnicodeString(&nameW
);
1655 /***********************************************************************
1656 * GetFullPathNameW (KERNEL32.@)
1658 DWORD WINAPI
GetFullPathNameW( LPCWSTR name
, DWORD len
, LPWSTR buffer
,
1661 DWORD ret
= DOSFS_DoGetFullPathName( name
, len
, buffer
);
1662 if (ret
&& (ret
<=len
) && buffer
&& lastpart
)
1664 LPWSTR p
= buffer
+ strlenW(buffer
);
1665 if (*p
!= (WCHAR
)'\\')
1667 while ((p
> buffer
+ 2) && (*p
!= (WCHAR
)'\\')) p
--;
1670 else *lastpart
= NULL
;
1676 /***********************************************************************
1677 * wine_get_unix_file_name (KERNEL32.@) Not a Windows API
1679 * Return the full Unix file name for a given path.
1680 * FIXME: convert dos file name to unicode
1682 BOOL WINAPI
wine_get_unix_file_name( LPCSTR dos
, LPSTR buffer
, DWORD len
)
1686 WCHAR dosW
[MAX_PATHNAME_LEN
];
1688 MultiByteToWideChar(CP_ACP
, 0, dos
, -1, dosW
, MAX_PATHNAME_LEN
);
1689 ret
= DOSFS_GetFullName( dosW
, FALSE
, &path
);
1692 strncpy( buffer
, path
.long_name
, len
);
1693 buffer
[len
- 1] = 0; /* ensure 0 termination */
1699 /***********************************************************************
1702 static int DOSFS_FindNextEx( FIND_FIRST_INFO
*info
, WIN32_FIND_DATAW
*entry
)
1704 DWORD attr
= info
->attr
| FA_UNUSED
| FA_ARCHIVE
| FA_RDONLY
;
1705 UINT flags
= DRIVE_GetFlags( info
->drive
);
1706 char *p
, buffer
[MAX_PATHNAME_LEN
];
1707 const char *drive_path
;
1709 LPCWSTR long_name
, short_name
;
1710 BY_HANDLE_FILE_INFORMATION fileinfo
;
1714 if ((info
->attr
& ~(FA_UNUSED
| FA_ARCHIVE
| FA_RDONLY
)) == FA_LABEL
)
1716 if (info
->cur_pos
) return 0;
1717 entry
->dwFileAttributes
= FILE_ATTRIBUTE_LABEL
;
1718 RtlSecondsSince1970ToTime( (time_t)0, (LARGE_INTEGER
*)&entry
->ftCreationTime
);
1719 RtlSecondsSince1970ToTime( (time_t)0, (LARGE_INTEGER
*)&entry
->ftLastAccessTime
);
1720 RtlSecondsSince1970ToTime( (time_t)0, (LARGE_INTEGER
*)&entry
->ftLastWriteTime
);
1721 entry
->nFileSizeHigh
= 0;
1722 entry
->nFileSizeLow
= 0;
1723 entry
->dwReserved0
= 0;
1724 entry
->dwReserved1
= 0;
1725 DOSFS_ToDosDTAFormat( DRIVE_GetLabel( info
->drive
), entry
->cFileName
);
1726 strcpyW( entry
->cAlternateFileName
, entry
->cFileName
);
1728 TRACE("returning %s (%s) as label\n",
1729 debugstr_w(entry
->cFileName
), debugstr_w(entry
->cAlternateFileName
));
1733 drive_path
= info
->path
+ strlen(DRIVE_GetRoot( info
->drive
));
1734 while ((*drive_path
== '/') || (*drive_path
== '\\')) drive_path
++;
1735 drive_root
= !*drive_path
;
1737 lstrcpynA( buffer
, info
->path
, sizeof(buffer
) - 1 );
1738 strcat( buffer
, "/" );
1739 p
= buffer
+ strlen(buffer
);
1741 while (DOSFS_ReadDir( info
->u
.dos_dir
, &long_name
, &short_name
))
1745 /* Don't return '.' and '..' in the root of the drive */
1746 if (drive_root
&& (long_name
[0] == '.') &&
1747 (!long_name
[1] || ((long_name
[1] == '.') && !long_name
[2])))
1750 /* Check the long mask */
1752 if (info
->long_mask
&& *info
->long_mask
)
1754 if (!DOSFS_MatchLong( info
->long_mask
, long_name
,
1755 flags
& DRIVE_CASE_SENSITIVE
)) continue;
1758 /* Check the short mask */
1760 if (info
->short_mask
)
1764 DOSFS_Hash( long_name
, dos_name
, TRUE
,
1765 !(flags
& DRIVE_CASE_SENSITIVE
) );
1766 short_name
= dos_name
;
1768 if (!DOSFS_MatchShort( info
->short_mask
, short_name
)) continue;
1771 /* Check the file attributes */
1772 WideCharToMultiByte(DRIVE_GetCodepage(info
->drive
), 0, long_name
, -1,
1773 p
, sizeof(buffer
) - (int)(p
- buffer
), NULL
, NULL
);
1774 if (!FILE_Stat( buffer
, &fileinfo
, &is_symlink
))
1776 WARN("can't stat %s\n", buffer
);
1779 if (is_symlink
&& (fileinfo
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
))
1781 static const WCHAR wineW
[] = {'w','i','n','e',0};
1782 static const WCHAR ShowDirSymlinksW
[] = {'S','h','o','w','D','i','r','S','y','m','l','i','n','k','s',0};
1783 static int show_dir_symlinks
= -1;
1784 if (show_dir_symlinks
== -1)
1785 show_dir_symlinks
= PROFILE_GetWineIniBool(wineW
, ShowDirSymlinksW
, 0);
1786 if (!show_dir_symlinks
) continue;
1789 if (fileinfo
.dwFileAttributes
& ~attr
) continue;
1791 /* We now have a matching entry; fill the result and return */
1793 entry
->dwFileAttributes
= fileinfo
.dwFileAttributes
;
1794 entry
->ftCreationTime
= fileinfo
.ftCreationTime
;
1795 entry
->ftLastAccessTime
= fileinfo
.ftLastAccessTime
;
1796 entry
->ftLastWriteTime
= fileinfo
.ftLastWriteTime
;
1797 entry
->nFileSizeHigh
= fileinfo
.nFileSizeHigh
;
1798 entry
->nFileSizeLow
= fileinfo
.nFileSizeLow
;
1801 DOSFS_ToDosDTAFormat( short_name
, entry
->cAlternateFileName
);
1803 DOSFS_Hash( long_name
, entry
->cAlternateFileName
, FALSE
,
1804 !(flags
& DRIVE_CASE_SENSITIVE
) );
1806 lstrcpynW( entry
->cFileName
, long_name
, sizeof(entry
->cFileName
)/sizeof(entry
->cFileName
[0]) );
1807 if (!(flags
& DRIVE_CASE_PRESERVING
)) strlwrW( entry
->cFileName
);
1808 TRACE("returning %s (%s) %02lx %ld\n",
1809 debugstr_w(entry
->cFileName
), debugstr_w(entry
->cAlternateFileName
),
1810 entry
->dwFileAttributes
, entry
->nFileSizeLow
);
1813 return 0; /* End of directory */
1816 /***********************************************************************
1819 * Find the next matching file. Return the number of entries read to find
1820 * the matching one, or 0 if no more entries.
1821 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
1822 * file name mask. Either or both can be NULL.
1824 * NOTE: This is supposed to be only called by the int21 emulation
1825 * routines. Thus, we should own the Win16Mutex anyway.
1826 * Nevertheless, we explicitly enter it to ensure the static
1827 * directory cache is protected.
1829 int DOSFS_FindNext( const char *path
, const char *short_mask
,
1830 const char *long_mask
, int drive
, BYTE attr
,
1831 int skip
, WIN32_FIND_DATAA
*entry
)
1833 static FIND_FIRST_INFO info
;
1834 LPCWSTR short_name
, long_name
;
1836 UNICODE_STRING short_maskW
, long_maskW
;
1837 WIN32_FIND_DATAW entryW
;
1839 TRACE("(%s, %s, %s, %x, %x, %x, %p)\n", debugstr_a(path
),
1840 debugstr_a(short_mask
), debugstr_a(long_mask
), drive
, attr
, skip
,
1845 RtlCreateUnicodeStringFromAsciiz(&short_maskW
, short_mask
);
1846 RtlCreateUnicodeStringFromAsciiz(&long_maskW
, long_mask
);
1848 /* Check the cached directory */
1849 if (!(info
.u
.dos_dir
&& info
.path
== path
&& !strcmpW(info
.short_mask
, short_maskW
.Buffer
)
1850 && !strcmpW(info
.long_mask
, long_maskW
.Buffer
) && info
.drive
== drive
1851 && info
.attr
== attr
&& info
.cur_pos
<= skip
))
1853 /* Not in the cache, open it anew */
1854 if (info
.u
.dos_dir
) DOSFS_CloseDir( info
.u
.dos_dir
);
1856 info
.path
= (LPSTR
)path
;
1857 RtlFreeHeap(GetProcessHeap(), 0, info
.long_mask
);
1858 RtlFreeHeap(GetProcessHeap(), 0, info
.short_mask
);
1859 info
.long_mask
= long_maskW
.Buffer
;
1860 info
.short_mask
= short_maskW
.Buffer
;
1864 info
.u
.dos_dir
= DOSFS_OpenDir( DRIVE_GetCodepage(drive
), info
.path
);
1868 RtlFreeUnicodeString(&short_maskW
);
1869 RtlFreeUnicodeString(&long_maskW
);
1872 /* Skip to desired position */
1873 while (info
.cur_pos
< skip
)
1874 if (info
.u
.dos_dir
&& DOSFS_ReadDir( info
.u
.dos_dir
, &long_name
, &short_name
))
1879 if (info
.u
.dos_dir
&& info
.cur_pos
== skip
&& DOSFS_FindNextEx( &info
, &entryW
))
1881 WideCharToMultiByte(CP_ACP
, 0, entryW
.cFileName
, -1,
1882 entry
->cFileName
, sizeof(entry
->cFileName
), NULL
, NULL
);
1883 WideCharToMultiByte(CP_ACP
, 0, entryW
.cAlternateFileName
, -1,
1884 entry
->cAlternateFileName
, sizeof(entry
->cAlternateFileName
), NULL
, NULL
);
1885 count
= info
.cur_pos
- skip
;
1887 entry
->dwFileAttributes
= entryW
.dwFileAttributes
;
1888 entry
->nFileSizeHigh
= entryW
.nFileSizeHigh
;
1889 entry
->nFileSizeLow
= entryW
.nFileSizeLow
;
1890 entry
->ftCreationTime
= entryW
.ftCreationTime
;
1891 entry
->ftLastAccessTime
= entryW
.ftLastAccessTime
;
1892 entry
->ftLastWriteTime
= entryW
.ftLastWriteTime
;
1900 if (info
.u
.dos_dir
) DOSFS_CloseDir( info
.u
.dos_dir
);
1901 memset( &info
, '\0', sizeof(info
) );
1909 /*************************************************************************
1910 * FindFirstFileExW (KERNEL32.@)
1912 HANDLE WINAPI
FindFirstFileExW(
1914 FINDEX_INFO_LEVELS fInfoLevelId
,
1915 LPVOID lpFindFileData
,
1916 FINDEX_SEARCH_OPS fSearchOp
,
1917 LPVOID lpSearchFilter
,
1918 DWORD dwAdditionalFlags
)
1921 FIND_FIRST_INFO
*info
;
1925 SetLastError(ERROR_PATH_NOT_FOUND
);
1926 return INVALID_HANDLE_VALUE
;
1929 if ((fSearchOp
!= FindExSearchNameMatch
) || (dwAdditionalFlags
!= 0))
1931 FIXME("options not implemented 0x%08x 0x%08lx\n", fSearchOp
, dwAdditionalFlags
);
1932 return INVALID_HANDLE_VALUE
;
1935 switch(fInfoLevelId
)
1937 case FindExInfoStandard
:
1939 WIN32_FIND_DATAW
* data
= (WIN32_FIND_DATAW
*) lpFindFileData
;
1944 data
->dwReserved0
= data
->dwReserved1
= 0x0;
1945 if (lpFileName
[0] == '\\' && lpFileName
[1] == '\\')
1947 ERR("UNC path name\n");
1948 if (!(handle
= GlobalAlloc(GMEM_MOVEABLE
, sizeof(FIND_FIRST_INFO
)))) break;
1950 info
= (FIND_FIRST_INFO
*)GlobalLock( handle
);
1951 info
->u
.smb_dir
= SMB_FindFirst(lpFileName
);
1952 if(!info
->u
.smb_dir
)
1954 GlobalUnlock( handle
);
1961 GlobalUnlock( handle
);
1965 DOS_FULL_NAME full_name
;
1967 if (lpFileName
[0] && lpFileName
[1] == ':')
1969 /* don't allow root directories */
1970 if (!lpFileName
[2] ||
1971 ((lpFileName
[2] == '/' || lpFileName
[2] == '\\') && !lpFileName
[3]))
1973 SetLastError(ERROR_FILE_NOT_FOUND
);
1974 return INVALID_HANDLE_VALUE
;
1977 if (!DOSFS_GetFullName( lpFileName
, FALSE
, &full_name
)) break;
1978 if (!(handle
= GlobalAlloc(GMEM_MOVEABLE
, sizeof(FIND_FIRST_INFO
)))) break;
1979 info
= (FIND_FIRST_INFO
*)GlobalLock( handle
);
1980 info
->path
= HeapAlloc( GetProcessHeap(), 0, strlen(full_name
.long_name
)+1 );
1981 strcpy( info
->path
, full_name
.long_name
);
1983 codepage
= DRIVE_GetCodepage(full_name
.drive
);
1984 p
= strrchr( info
->path
, '/' );
1986 long_mask_len
= MultiByteToWideChar(codepage
, 0, p
, -1, NULL
, 0);
1987 info
->long_mask
= HeapAlloc( GetProcessHeap(), 0, long_mask_len
* sizeof(WCHAR
) );
1988 MultiByteToWideChar(codepage
, 0, p
, -1, info
->long_mask
, long_mask_len
);
1990 info
->short_mask
= NULL
;
1992 info
->drive
= full_name
.drive
;
1995 info
->u
.dos_dir
= DOSFS_OpenDir( codepage
, info
->path
);
1996 GlobalUnlock( handle
);
1998 if (!FindNextFileW( handle
, data
))
2000 FindClose( handle
);
2001 SetLastError( ERROR_FILE_NOT_FOUND
);
2008 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId
);
2010 return INVALID_HANDLE_VALUE
;
2013 /*************************************************************************
2014 * FindFirstFileA (KERNEL32.@)
2016 HANDLE WINAPI
FindFirstFileA(
2018 WIN32_FIND_DATAA
*lpFindData
)
2020 return FindFirstFileExA(lpFileName
, FindExInfoStandard
, lpFindData
,
2021 FindExSearchNameMatch
, NULL
, 0);
2024 /*************************************************************************
2025 * FindFirstFileExA (KERNEL32.@)
2027 HANDLE WINAPI
FindFirstFileExA(
2029 FINDEX_INFO_LEVELS fInfoLevelId
,
2030 LPVOID lpFindFileData
,
2031 FINDEX_SEARCH_OPS fSearchOp
,
2032 LPVOID lpSearchFilter
,
2033 DWORD dwAdditionalFlags
)
2036 WIN32_FIND_DATAA
*dataA
;
2037 WIN32_FIND_DATAW dataW
;
2038 UNICODE_STRING pathW
;
2042 SetLastError(ERROR_PATH_NOT_FOUND
);
2043 return INVALID_HANDLE_VALUE
;
2046 if (!RtlCreateUnicodeStringFromAsciiz(&pathW
, lpFileName
))
2048 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
2049 return INVALID_HANDLE_VALUE
;
2052 handle
= FindFirstFileExW(pathW
.Buffer
, fInfoLevelId
, &dataW
, fSearchOp
, lpSearchFilter
, dwAdditionalFlags
);
2053 RtlFreeUnicodeString(&pathW
);
2054 if (handle
== INVALID_HANDLE_VALUE
) return handle
;
2056 dataA
= (WIN32_FIND_DATAA
*) lpFindFileData
;
2057 dataA
->dwFileAttributes
= dataW
.dwFileAttributes
;
2058 dataA
->ftCreationTime
= dataW
.ftCreationTime
;
2059 dataA
->ftLastAccessTime
= dataW
.ftLastAccessTime
;
2060 dataA
->ftLastWriteTime
= dataW
.ftLastWriteTime
;
2061 dataA
->nFileSizeHigh
= dataW
.nFileSizeHigh
;
2062 dataA
->nFileSizeLow
= dataW
.nFileSizeLow
;
2063 WideCharToMultiByte( CP_ACP
, 0, dataW
.cFileName
, -1,
2064 dataA
->cFileName
, sizeof(dataA
->cFileName
), NULL
, NULL
);
2065 WideCharToMultiByte( CP_ACP
, 0, dataW
.cAlternateFileName
, -1,
2066 dataA
->cAlternateFileName
, sizeof(dataA
->cAlternateFileName
), NULL
, NULL
);
2070 /*************************************************************************
2071 * FindFirstFileW (KERNEL32.@)
2073 HANDLE WINAPI
FindFirstFileW( LPCWSTR lpFileName
, WIN32_FIND_DATAW
*lpFindData
)
2075 return FindFirstFileExW(lpFileName
, FindExInfoStandard
, lpFindData
,
2076 FindExSearchNameMatch
, NULL
, 0);
2079 /*************************************************************************
2080 * FindNextFileW (KERNEL32.@)
2082 BOOL WINAPI
FindNextFileW( HANDLE handle
, WIN32_FIND_DATAW
*data
)
2084 FIND_FIRST_INFO
*info
;
2086 DWORD gle
= ERROR_NO_MORE_FILES
;
2088 if ((handle
== INVALID_HANDLE_VALUE
) ||
2089 !(info
= (FIND_FIRST_INFO
*)GlobalLock( handle
)))
2091 SetLastError( ERROR_INVALID_HANDLE
);
2094 if (info
->drive
== -1)
2096 ret
= SMB_FindNext( info
->u
.smb_dir
, data
);
2099 SMB_CloseDir( info
->u
.smb_dir
);
2100 HeapFree( GetProcessHeap(), 0, info
->path
);
2104 else if (!info
->path
|| !info
->u
.dos_dir
)
2108 else if (!DOSFS_FindNextEx( info
, data
))
2110 DOSFS_CloseDir( info
->u
.dos_dir
); info
->u
.dos_dir
= NULL
;
2111 HeapFree( GetProcessHeap(), 0, info
->path
);
2113 HeapFree( GetProcessHeap(), 0, info
->long_mask
);
2114 info
->long_mask
= NULL
;
2119 GlobalUnlock( handle
);
2120 if( !ret
) SetLastError( gle
);
2125 /*************************************************************************
2126 * FindNextFileA (KERNEL32.@)
2128 BOOL WINAPI
FindNextFileA( HANDLE handle
, WIN32_FIND_DATAA
*data
)
2130 WIN32_FIND_DATAW dataW
;
2131 if (!FindNextFileW( handle
, &dataW
)) return FALSE
;
2132 data
->dwFileAttributes
= dataW
.dwFileAttributes
;
2133 data
->ftCreationTime
= dataW
.ftCreationTime
;
2134 data
->ftLastAccessTime
= dataW
.ftLastAccessTime
;
2135 data
->ftLastWriteTime
= dataW
.ftLastWriteTime
;
2136 data
->nFileSizeHigh
= dataW
.nFileSizeHigh
;
2137 data
->nFileSizeLow
= dataW
.nFileSizeLow
;
2138 WideCharToMultiByte( CP_ACP
, 0, dataW
.cFileName
, -1,
2139 data
->cFileName
, sizeof(data
->cFileName
), NULL
, NULL
);
2140 WideCharToMultiByte( CP_ACP
, 0, dataW
.cAlternateFileName
, -1,
2141 data
->cAlternateFileName
,
2142 sizeof(data
->cAlternateFileName
), NULL
, NULL
);
2146 /*************************************************************************
2147 * FindClose (KERNEL32.@)
2149 BOOL WINAPI
FindClose( HANDLE handle
)
2151 FIND_FIRST_INFO
*info
;
2153 if (handle
== INVALID_HANDLE_VALUE
) goto error
;
2157 if ((info
= (FIND_FIRST_INFO
*)GlobalLock( handle
)))
2159 if (info
->u
.dos_dir
) DOSFS_CloseDir( info
->u
.dos_dir
);
2160 if (info
->path
) HeapFree( GetProcessHeap(), 0, info
->path
);
2161 if (info
->long_mask
) HeapFree( GetProcessHeap(), 0, info
->long_mask
);
2164 __EXCEPT(page_fault
)
2166 WARN("Illegal handle %p\n", handle
);
2167 SetLastError( ERROR_INVALID_HANDLE
);
2171 if (!info
) goto error
;
2172 GlobalUnlock( handle
);
2173 GlobalFree( handle
);
2177 SetLastError( ERROR_INVALID_HANDLE
);
2181 /***********************************************************************
2182 * DOSFS_UnixTimeToFileTime
2184 * Convert a Unix time to FILETIME format.
2185 * The FILETIME structure is a 64-bit value representing the number of
2186 * 100-nanosecond intervals since January 1, 1601, 0:00.
2187 * 'remainder' is the nonnegative number of 100-ns intervals
2188 * corresponding to the time fraction smaller than 1 second that
2189 * couldn't be stored in the time_t value.
2191 void DOSFS_UnixTimeToFileTime( time_t unix_time
, FILETIME
*filetime
,
2197 The time difference between 1 January 1601, 00:00:00 and
2198 1 January 1970, 00:00:00 is 369 years, plus the leap years
2199 from 1604 to 1968, excluding 1700, 1800, 1900.
2200 This makes (1968 - 1600) / 4 - 3 = 89 leap days, and a total
2203 Any day in that period had 24 * 60 * 60 = 86400 seconds.
2205 The time difference is 134774 * 86400 * 10000000, which can be written
2207 27111902 * 2^32 + 3577643008
2208 413 * 2^48 + 45534 * 2^32 + 54590 * 2^16 + 32768
2210 If you find that these constants are buggy, please change them in all
2211 instances in both conversion functions.
2214 There are two versions, one of them uses long long variables and
2215 is presumably faster but not ISO C. The other one uses standard C
2216 data types and operations but relies on the assumption that negative
2217 numbers are stored as 2's complement (-1 is 0xffff....). If this
2218 assumption is violated, dates before 1970 will not convert correctly.
2219 This should however work on any reasonable architecture where WINE
2224 Take care not to remove the casts. I have tested these functions
2225 (in both versions) for a lot of numbers. I would be interested in
2226 results on other compilers than GCC.
2228 The operations have been designed to account for the possibility
2229 of 64-bit time_t in future UNICES. Even the versions without
2230 internal long long numbers will work if time_t only is 64 bit.
2231 A 32-bit shift, which was necessary for that operation, turned out
2232 not to work correctly in GCC, besides giving the warning. So I
2233 used a double 16-bit shift instead. Numbers are in the ISO version
2234 represented by three limbs, the most significant with 32 bit, the
2235 other two with 16 bit each.
2237 As the modulo-operator % is not well-defined for negative numbers,
2238 negative divisors have been avoided in DOSFS_FileTimeToUnixTime.
2240 There might be quicker ways to do this in C. Certainly so in
2243 Claus Fischer, fischer@iue.tuwien.ac.at
2246 #if SIZEOF_LONG_LONG >= 8
2247 # define USE_LONG_LONG 1
2249 # define USE_LONG_LONG 0
2252 #if USE_LONG_LONG /* gcc supports long long type */
2254 long long int t
= unix_time
;
2256 t
+= 116444736000000000LL;
2258 filetime
->dwLowDateTime
= (UINT
)t
;
2259 filetime
->dwHighDateTime
= (UINT
)(t
>> 32);
2261 #else /* ISO version */
2263 UINT a0
; /* 16 bit, low bits */
2264 UINT a1
; /* 16 bit, medium bits */
2265 UINT a2
; /* 32 bit, high bits */
2267 /* Copy the unix time to a2/a1/a0 */
2268 a0
= unix_time
& 0xffff;
2269 a1
= (unix_time
>> 16) & 0xffff;
2270 /* This is obsolete if unix_time is only 32 bits, but it does not hurt.
2271 Do not replace this by >> 32, it gives a compiler warning and it does
2273 a2
= (unix_time
>= 0 ? (unix_time
>> 16) >> 16 :
2274 ~((~unix_time
>> 16) >> 16));
2276 /* Multiply a by 10000000 (a = a2/a1/a0)
2277 Split the factor into 10000 * 1000 which are both less than 0xffff. */
2279 a1
= a1
* 10000 + (a0
>> 16);
2280 a2
= a2
* 10000 + (a1
>> 16);
2285 a1
= a1
* 1000 + (a0
>> 16);
2286 a2
= a2
* 1000 + (a1
>> 16);
2290 /* Add the time difference and the remainder */
2291 a0
+= 32768 + (remainder
& 0xffff);
2292 a1
+= 54590 + (remainder
>> 16 ) + (a0
>> 16);
2293 a2
+= 27111902 + (a1
>> 16);
2298 filetime
->dwLowDateTime
= (a1
<< 16) + a0
;
2299 filetime
->dwHighDateTime
= a2
;
2304 /***********************************************************************
2305 * DOSFS_FileTimeToUnixTime
2307 * Convert a FILETIME format to Unix time.
2308 * If not NULL, 'remainder' contains the fractional part of the filetime,
2309 * in the range of [0..9999999] (even if time_t is negative).
2311 time_t DOSFS_FileTimeToUnixTime( const FILETIME
*filetime
, DWORD
*remainder
)
2313 /* Read the comment in the function DOSFS_UnixTimeToFileTime. */
2316 long long int t
= filetime
->dwHighDateTime
;
2318 t
+= (UINT
)filetime
->dwLowDateTime
;
2319 t
-= 116444736000000000LL;
2322 if (remainder
) *remainder
= 9999999 - (-t
- 1) % 10000000;
2323 return -1 - ((-t
- 1) / 10000000);
2327 if (remainder
) *remainder
= t
% 10000000;
2328 return t
/ 10000000;
2331 #else /* ISO version */
2333 UINT a0
; /* 16 bit, low bits */
2334 UINT a1
; /* 16 bit, medium bits */
2335 UINT a2
; /* 32 bit, high bits */
2336 UINT r
; /* remainder of division */
2337 unsigned int carry
; /* carry bit for subtraction */
2338 int negative
; /* whether a represents a negative value */
2340 /* Copy the time values to a2/a1/a0 */
2341 a2
= (UINT
)filetime
->dwHighDateTime
;
2342 a1
= ((UINT
)filetime
->dwLowDateTime
) >> 16;
2343 a0
= ((UINT
)filetime
->dwLowDateTime
) & 0xffff;
2345 /* Subtract the time difference */
2346 if (a0
>= 32768 ) a0
-= 32768 , carry
= 0;
2347 else a0
+= (1 << 16) - 32768 , carry
= 1;
2349 if (a1
>= 54590 + carry
) a1
-= 54590 + carry
, carry
= 0;
2350 else a1
+= (1 << 16) - 54590 - carry
, carry
= 1;
2352 a2
-= 27111902 + carry
;
2354 /* If a is negative, replace a by (-1-a) */
2355 negative
= (a2
>= ((UINT
)1) << 31);
2358 /* Set a to -a - 1 (a is a2/a1/a0) */
2364 /* Divide a by 10000000 (a = a2/a1/a0), put the rest into r.
2365 Split the divisor into 10000 * 1000 which are both less than 0xffff. */
2366 a1
+= (a2
% 10000) << 16;
2368 a0
+= (a1
% 10000) << 16;
2373 a1
+= (a2
% 1000) << 16;
2375 a0
+= (a1
% 1000) << 16;
2377 r
+= (a0
% 1000) * 10000;
2380 /* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
2383 /* Set a to -a - 1 (a is a2/a1/a0) */
2391 if (remainder
) *remainder
= r
;
2393 /* Do not replace this by << 32, it gives a compiler warning and it does
2395 return ((((time_t)a2
) << 16) << 16) + (a1
<< 16) + a0
;
2400 /***********************************************************************
2401 * MulDiv (KERNEL32.@)
2403 * Result of multiplication and division
2404 * -1: Overflow occurred or Divisor was 0
2411 #if SIZEOF_LONG_LONG >= 8
2414 if (!nDivisor
) return -1;
2416 /* We want to deal with a positive divisor to simplify the logic. */
2419 nMultiplicand
= - nMultiplicand
;
2420 nDivisor
= -nDivisor
;
2423 /* If the result is positive, we "add" to round. else, we subtract to round. */
2424 if ( ( (nMultiplicand
< 0) && (nMultiplier
< 0) ) ||
2425 ( (nMultiplicand
>= 0) && (nMultiplier
>= 0) ) )
2426 ret
= (((long long)nMultiplicand
* nMultiplier
) + (nDivisor
/2)) / nDivisor
;
2428 ret
= (((long long)nMultiplicand
* nMultiplier
) - (nDivisor
/2)) / nDivisor
;
2430 if ((ret
> 2147483647) || (ret
< -2147483647)) return -1;
2433 if (!nDivisor
) return -1;
2435 /* We want to deal with a positive divisor to simplify the logic. */
2438 nMultiplicand
= - nMultiplicand
;
2439 nDivisor
= -nDivisor
;
2442 /* If the result is positive, we "add" to round. else, we subtract to round. */
2443 if ( ( (nMultiplicand
< 0) && (nMultiplier
< 0) ) ||
2444 ( (nMultiplicand
>= 0) && (nMultiplier
>= 0) ) )
2445 return ((nMultiplicand
* nMultiplier
) + (nDivisor
/2)) / nDivisor
;
2447 return ((nMultiplicand
* nMultiplier
) - (nDivisor
/2)) / nDivisor
;
2453 /***********************************************************************
2454 * DosDateTimeToFileTime (KERNEL32.@)
2456 BOOL WINAPI
DosDateTimeToFileTime( WORD fatdate
, WORD fattime
, LPFILETIME ft
)
2461 time_t time1
, time2
;
2464 newtm
.tm_sec
= (fattime
& 0x1f) * 2;
2465 newtm
.tm_min
= (fattime
>> 5) & 0x3f;
2466 newtm
.tm_hour
= (fattime
>> 11);
2467 newtm
.tm_mday
= (fatdate
& 0x1f);
2468 newtm
.tm_mon
= ((fatdate
>> 5) & 0x0f) - 1;
2469 newtm
.tm_year
= (fatdate
>> 9) + 80;
2471 RtlSecondsSince1970ToTime( timegm(&newtm
), (LARGE_INTEGER
*)ft
);
2473 time1
= mktime(&newtm
);
2474 gtm
= gmtime(&time1
);
2475 time2
= mktime(gtm
);
2476 RtlSecondsSince1970ToTime( 2*time1
-time2
, (LARGE_INTEGER
*)ft
);
2482 /***********************************************************************
2483 * FileTimeToDosDateTime (KERNEL32.@)
2485 BOOL WINAPI
FileTimeToDosDateTime( const FILETIME
*ft
, LPWORD fatdate
,
2488 time_t unixtime
= DOSFS_FileTimeToUnixTime( ft
, NULL
);
2489 struct tm
*tm
= gmtime( &unixtime
);
2491 *fattime
= (tm
->tm_hour
<< 11) + (tm
->tm_min
<< 5) + (tm
->tm_sec
/ 2);
2493 *fatdate
= ((tm
->tm_year
- 80) << 9) + ((tm
->tm_mon
+ 1) << 5)
2499 /***********************************************************************
2500 * QueryDosDeviceA (KERNEL32.@)
2502 * returns array of strings terminated by \0, terminated by \0
2504 DWORD WINAPI
QueryDosDeviceA(LPCSTR devname
,LPSTR target
,DWORD bufsize
)
2509 TRACE("(%s,...)\n", devname
? devname
: "<null>");
2511 /* return known MSDOS devices */
2512 static const char devices
[24] = "CON\0COM1\0COM2\0LPT1\0NUL\0\0";
2513 memcpy( target
, devices
, min(bufsize
,sizeof(devices
)) );
2514 return min(bufsize
,sizeof(devices
));
2516 /* In theory all that are possible and have been defined.
2517 * Now just those below, since mirc uses it to check for special files.
2519 * (It is more complex, and supports netmounted stuff, and \\.\ stuff,
2520 * but currently we just ignore that.)
2522 #define CHECK(x) (strstr(devname,#x)==devname)
2523 if (CHECK(con
) || CHECK(com
) || CHECK(lpt
) || CHECK(nul
)) {
2524 strcpy(buffer
,"\\DEV\\");
2525 strcat(buffer
,devname
);
2526 if ((s
=strchr(buffer
,':'))) *s
='\0';
2527 lstrcpynA(target
,buffer
,bufsize
);
2528 return strlen(buffer
)+1;
2530 if (strchr(devname
,':') || devname
[0]=='\\') {
2531 /* This might be a DOS device we do not handle yet ... */
2532 FIXME("(%s) not detected as DOS device!\n",devname
);
2534 SetLastError(ERROR_DEV_NOT_EXIST
);
2541 /***********************************************************************
2542 * QueryDosDeviceW (KERNEL32.@)
2544 * returns array of strings terminated by \0, terminated by \0
2546 DWORD WINAPI
QueryDosDeviceW(LPCWSTR devname
,LPWSTR target
,DWORD bufsize
)
2548 LPSTR devnameA
= devname
?HEAP_strdupWtoA(GetProcessHeap(),0,devname
):NULL
;
2549 LPSTR targetA
= (LPSTR
)HeapAlloc(GetProcessHeap(),0,bufsize
);
2550 DWORD ret
= QueryDosDeviceA(devnameA
,targetA
,bufsize
);
2552 ret
= MultiByteToWideChar( CP_ACP
, 0, targetA
, ret
, target
, bufsize
);
2553 if (devnameA
) HeapFree(GetProcessHeap(),0,devnameA
);
2554 if (targetA
) HeapFree(GetProcessHeap(),0,targetA
);
2559 /***********************************************************************
2560 * DefineDosDeviceA (KERNEL32.@)
2562 BOOL WINAPI
DefineDosDeviceA(DWORD flags
,LPCSTR devname
,LPCSTR targetpath
) {
2563 FIXME("(0x%08lx,%s,%s),stub!\n",flags
,devname
,targetpath
);
2564 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
2569 --- 16 bit functions ---
2572 /*************************************************************************
2573 * FindFirstFile (KERNEL.413)
2575 HANDLE16 WINAPI
FindFirstFile16( LPCSTR path
, WIN32_FIND_DATAA
*data
)
2577 DOS_FULL_NAME full_name
;
2579 FIND_FIRST_INFO
*info
;
2580 WCHAR pathW
[MAX_PATH
];
2585 data
->dwReserved0
= data
->dwReserved1
= 0x0;
2586 if (!path
) return INVALID_HANDLE_VALUE16
;
2587 MultiByteToWideChar(CP_ACP
, 0, path
, -1, pathW
, MAX_PATH
);
2588 if (!DOSFS_GetFullName( pathW
, FALSE
, &full_name
))
2589 return INVALID_HANDLE_VALUE16
;
2590 if (!(handle
= GlobalAlloc16( GMEM_MOVEABLE
, sizeof(FIND_FIRST_INFO
) )))
2591 return INVALID_HANDLE_VALUE16
;
2592 info
= (FIND_FIRST_INFO
*)GlobalLock16( handle
);
2593 info
->path
= HeapAlloc( GetProcessHeap(), 0, strlen(full_name
.long_name
)+1 );
2594 strcpy( info
->path
, full_name
.long_name
);
2596 codepage
= DRIVE_GetCodepage(full_name
.drive
);
2597 p
= strrchr( info
->path
, '/' );
2599 long_mask_len
= MultiByteToWideChar(codepage
, 0, p
, -1, NULL
, 0);
2600 info
->long_mask
= HeapAlloc( GetProcessHeap(), 0, long_mask_len
* sizeof(WCHAR
) );
2601 MultiByteToWideChar(codepage
, 0, p
, -1, info
->long_mask
, long_mask_len
);
2603 info
->short_mask
= NULL
;
2605 info
->drive
= full_name
.drive
;
2608 info
->u
.dos_dir
= DOSFS_OpenDir( codepage
, info
->path
);
2610 GlobalUnlock16( handle
);
2611 if (!FindNextFile16( handle
, data
))
2613 FindClose16( handle
);
2614 SetLastError( ERROR_NO_MORE_FILES
);
2615 return INVALID_HANDLE_VALUE16
;
2620 /*************************************************************************
2621 * FindNextFile (KERNEL.414)
2623 BOOL16 WINAPI
FindNextFile16( HANDLE16 handle
, WIN32_FIND_DATAA
*data
)
2625 FIND_FIRST_INFO
*info
;
2626 WIN32_FIND_DATAW dataW
;
2628 DWORD gle
= ERROR_NO_MORE_FILES
;
2630 if ((handle
== INVALID_HANDLE_VALUE16
) ||
2631 !(info
= (FIND_FIRST_INFO
*)GlobalLock16( handle
)))
2633 SetLastError( ERROR_INVALID_HANDLE
);
2636 if (!info
->path
|| !info
->u
.dos_dir
)
2640 if (!DOSFS_FindNextEx( info
, &dataW
))
2642 DOSFS_CloseDir( info
->u
.dos_dir
); info
->u
.dos_dir
= NULL
;
2643 HeapFree( GetProcessHeap(), 0, info
->path
);
2645 HeapFree( GetProcessHeap(), 0, info
->long_mask
);
2646 info
->long_mask
= NULL
;
2652 data
->dwFileAttributes
= dataW
.dwFileAttributes
;
2653 data
->ftCreationTime
= dataW
.ftCreationTime
;
2654 data
->ftLastAccessTime
= dataW
.ftLastAccessTime
;
2655 data
->ftLastWriteTime
= dataW
.ftLastWriteTime
;
2656 data
->nFileSizeHigh
= dataW
.nFileSizeHigh
;
2657 data
->nFileSizeLow
= dataW
.nFileSizeLow
;
2658 WideCharToMultiByte( CP_ACP
, 0, dataW
.cFileName
, -1,
2659 data
->cFileName
, sizeof(data
->cFileName
), NULL
, NULL
);
2660 WideCharToMultiByte( CP_ACP
, 0, dataW
.cAlternateFileName
, -1,
2661 data
->cAlternateFileName
,
2662 sizeof(data
->cAlternateFileName
), NULL
, NULL
);
2664 if( !ret
) SetLastError( gle
);
2665 GlobalUnlock16( handle
);
2670 /*************************************************************************
2671 * FindClose (KERNEL.415)
2673 BOOL16 WINAPI
FindClose16( HANDLE16 handle
)
2675 FIND_FIRST_INFO
*info
;
2677 if ((handle
== INVALID_HANDLE_VALUE16
) ||
2678 !(info
= (FIND_FIRST_INFO
*)GlobalLock16( handle
)))
2680 SetLastError( ERROR_INVALID_HANDLE
);
2683 if (info
->u
.dos_dir
) DOSFS_CloseDir( info
->u
.dos_dir
);
2684 if (info
->path
) HeapFree( GetProcessHeap(), 0, info
->path
);
2685 if (info
->long_mask
) HeapFree( GetProcessHeap(), 0, info
->long_mask
);
2686 GlobalUnlock16( handle
);
2687 GlobalFree16( handle
);