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"
55 #include "msvcrt/excpt.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] )
79 #undef VFAT_IOCTL_READDIR_BOTH /* just in case... */
82 /* Chars we don't want to see in DOS file names */
83 #define INVALID_DOS_CHARS "*?<>|\"+=,;[] \345"
85 static const DOS_DEVICE DOSFS_Devices
[] =
86 /* name, device flags (see Int 21/AX=0x4400) */
88 { {'C','O','N',0}, 0xc0d3 },
89 { {'P','R','N',0}, 0xa0c0 },
90 { {'N','U','L',0}, 0x80c4 },
91 { {'A','U','X',0}, 0x80c0 },
92 { {'L','P','T','1',0}, 0xa0c0 },
93 { {'L','P','T','2',0}, 0xa0c0 },
94 { {'L','P','T','3',0}, 0xa0c0 },
95 { {'L','P','T','4',0}, 0xc0d3 },
96 { {'C','O','M','1',0}, 0x80c0 },
97 { {'C','O','M','2',0}, 0x80c0 },
98 { {'C','O','M','3',0}, 0x80c0 },
99 { {'C','O','M','4',0}, 0x80c0 },
100 { {'S','C','S','I','M','G','R','$',0}, 0xc0c0 },
101 { {'H','P','S','C','A','N',0}, 0xc0c0 },
102 { {'E','M','M','X','X','X','X','0',0}, 0x0000 }
106 * Directory info for DOSFS_ReadDir
107 * contains the names of *all* the files in the directory
116 /* Info structure for FindFirstFile handle */
119 char *path
; /* unix path */
133 static WINE_EXCEPTION_FILTER(page_fault
)
135 if (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION
)
136 return EXCEPTION_EXECUTE_HANDLER
;
137 return EXCEPTION_CONTINUE_SEARCH
;
141 /***********************************************************************
144 * Return 1 if Unix file 'name' is also a valid MS-DOS name
145 * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
146 * File name can be terminated by '\0', '\\' or '/'.
148 static int DOSFS_ValidDOSName( LPCWSTR name
, int ignore_case
)
150 static const char invalid_chars
[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS
;
151 const WCHAR
*p
= name
;
152 const char *invalid
= ignore_case
? (invalid_chars
+ 26) : invalid_chars
;
157 /* Check for "." and ".." */
160 /* All other names beginning with '.' are invalid */
161 return (IS_END_OF_NAME(*p
));
163 while (!IS_END_OF_NAME(*p
))
165 if (*p
< 256 && strchr( invalid
, (char)*p
)) return 0; /* Invalid char */
166 if (*p
== '.') break; /* Start of the extension */
167 if (++len
> 8) return 0; /* Name too long */
170 if (*p
!= '.') return 1; /* End of name */
172 if (IS_END_OF_NAME(*p
)) return 0; /* Empty extension not allowed */
174 while (!IS_END_OF_NAME(*p
))
176 if (*p
< 256 && strchr( invalid
, (char)*p
)) return 0; /* Invalid char */
177 if (*p
== '.') return 0; /* Second extension not allowed */
178 if (++len
> 3) return 0; /* Extension too long */
185 /***********************************************************************
186 * DOSFS_ToDosFCBFormat
188 * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
189 * expanding wild cards and converting to upper-case in the process.
190 * File name can be terminated by '\0', '\\' or '/'.
191 * Return FALSE if the name is not a valid DOS name.
192 * 'buffer' must be at least 12 characters long.
194 BOOL
DOSFS_ToDosFCBFormat( LPCWSTR name
, LPWSTR buffer
)
196 static const char invalid_chars
[] = INVALID_DOS_CHARS
;
200 TRACE("(%s, %p)\n", debugstr_w(name
), buffer
);
202 /* Check for "." and ".." */
207 for(i
= 1; i
< 11; i
++) buffer
[i
] = ' ';
214 return (!*p
|| (*p
== '/') || (*p
== '\\'));
217 for (i
= 0; i
< 8; i
++)
234 if (*p
< 256 && strchr( invalid_chars
, (char)*p
)) return FALSE
;
235 buffer
[i
] = toupperW(*p
);
243 /* Skip all chars after wildcard up to first dot */
244 while (*p
&& (*p
!= '/') && (*p
!= '\\') && (*p
!= '.')) p
++;
248 /* Check if name too long */
249 if (*p
&& (*p
!= '/') && (*p
!= '\\') && (*p
!= '.')) return FALSE
;
251 if (*p
== '.') p
++; /* Skip dot */
253 for (i
= 8; i
< 11; i
++)
263 return FALSE
; /* Second extension not allowed */
271 if (*p
< 256 && strchr( invalid_chars
, (char)*p
)) return FALSE
;
272 buffer
[i
] = toupperW(*p
);
279 /* at most 3 character of the extension are processed
280 * is something behind this ?
282 while (*p
== '*' || *p
== ' ') p
++; /* skip wildcards and spaces */
283 return IS_END_OF_NAME(*p
);
287 /***********************************************************************
288 * DOSFS_ToDosDTAFormat
290 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
291 * converting to upper-case in the process.
292 * File name can be terminated by '\0', '\\' or '/'.
293 * 'buffer' must be at least 13 characters long.
295 static void DOSFS_ToDosDTAFormat( LPCWSTR name
, LPWSTR buffer
)
299 memcpy( buffer
, name
, 8 * sizeof(WCHAR
) );
301 while ((p
> buffer
) && (p
[-1] == ' ')) p
--;
303 memcpy( p
, name
+ 8, 3 * sizeof(WCHAR
) );
305 while (p
[-1] == ' ') p
--;
306 if (p
[-1] == '.') p
--;
311 /***********************************************************************
314 * Check a DOS file name against a mask (both in FCB format).
316 static int DOSFS_MatchShort( LPCWSTR mask
, LPCWSTR name
)
319 for (i
= 11; i
> 0; i
--, mask
++, name
++)
320 if ((*mask
!= '?') && (*mask
!= *name
)) return 0;
325 /***********************************************************************
328 * Check a long file name against a mask.
330 * Tests (done in W95 DOS shell - case insensitive):
331 * *.txt test1.test.txt *
333 * *.t??????.t* test1.ta.tornado.txt *
334 * *tornado* test1.ta.tornado.txt *
335 * t*t test1.ta.tornado.txt *
337 * ?est??? test1.txt -
338 * *test1.txt* test1.txt *
339 * h?l?o*t.dat hellothisisatest.dat *
341 static int DOSFS_MatchLong( LPCWSTR mask
, LPCWSTR name
, int case_sensitive
)
343 LPCWSTR lastjoker
= NULL
;
344 LPCWSTR next_to_retry
= NULL
;
345 static const WCHAR asterisk_dot_asterisk
[] = {'*','.','*',0};
347 TRACE("(%s, %s, %x)\n", debugstr_w(mask
), debugstr_w(name
), case_sensitive
);
349 if (!strcmpW( mask
, asterisk_dot_asterisk
)) return 1;
350 while (*name
&& *mask
)
355 while (*mask
== '*') mask
++; /* Skip consecutive '*' */
357 if (!*mask
) return 1; /* end of mask is all '*', so match */
359 /* skip to the next match after the joker(s) */
360 if (case_sensitive
) while (*name
&& (*name
!= *mask
)) name
++;
361 else while (*name
&& (toupperW(*name
) != toupperW(*mask
))) name
++;
364 next_to_retry
= name
;
366 else if (*mask
!= '?')
371 if (*mask
!= *name
) mismatch
= 1;
375 if (toupperW(*mask
) != toupperW(*name
)) mismatch
= 1;
389 else /* mismatch ! */
391 if (lastjoker
) /* we had an '*', so we can try unlimitedly */
395 /* this scan sequence was a mismatch, so restart
396 * 1 char after the first char we checked last time */
398 name
= next_to_retry
;
401 return 0; /* bad luck */
410 while ((*mask
== '.') || (*mask
== '*'))
411 mask
++; /* Ignore trailing '.' or '*' in mask */
412 return (!*name
&& !*mask
);
416 /***********************************************************************
419 * Used to construct an array of filenames in DOSFS_OpenDir
421 static BOOL
DOSFS_AddDirEntry(DOS_DIR
**dir
, LPCWSTR name
, LPCWSTR dosname
)
423 int extra1
= (strlenW(name
) + 1) * sizeof(WCHAR
);
424 int extra2
= (strlenW(dosname
) + 1) * sizeof(WCHAR
);
426 /* if we need more, at minimum double the size */
427 if( (extra1
+ extra2
+ (*dir
)->used
) > (*dir
)->size
)
429 int more
= (*dir
)->size
;
432 if(more
<(extra1
+extra2
))
433 more
= extra1
+extra2
;
435 t
= HeapReAlloc(GetProcessHeap(), 0, *dir
, sizeof(**dir
) + (*dir
)->size
+ more
);
438 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
439 ERR("Out of memory caching directory structure %d %d %d\n",
440 (*dir
)->size
, more
, (*dir
)->used
);
444 (*dir
)->size
+= more
;
447 /* at this point, the dir structure is big enough to hold these names */
448 strcpyW((LPWSTR
)&(*dir
)->names
[(*dir
)->used
], name
);
449 (*dir
)->used
+= extra1
;
450 strcpyW((LPWSTR
)&(*dir
)->names
[(*dir
)->used
], dosname
);
451 (*dir
)->used
+= extra2
;
457 /***********************************************************************
460 static BOOL
DOSFS_OpenDir_VFAT(UINT codepage
, DOS_DIR
**dir
, const char *unix_path
)
462 #ifdef VFAT_IOCTL_READDIR_BOTH
464 int fd
= open( unix_path
, O_RDONLY
);
467 /* Check if the VFAT ioctl is supported on this directory */
474 WCHAR long_name
[MAX_PATH
];
475 WCHAR short_name
[12];
477 r
= (ioctl( fd
, VFAT_IOCTL_READDIR_BOTH
, (long)de
) != -1);
482 MultiByteToWideChar(codepage
, 0, de
[0].d_name
, -1, long_name
, MAX_PATH
);
483 if (!DOSFS_ToDosFCBFormat( long_name
, short_name
))
484 short_name
[0] = '\0';
486 MultiByteToWideChar(codepage
, 0, de
[1].d_name
, -1, long_name
, MAX_PATH
);
488 MultiByteToWideChar(codepage
, 0, de
[0].d_name
, -1, long_name
, MAX_PATH
);
489 r
= DOSFS_AddDirEntry(dir
, long_name
, short_name
);
495 static const WCHAR empty_strW
[] = { 0 };
496 DOSFS_AddDirEntry(dir
, empty_strW
, empty_strW
);
502 #endif /* VFAT_IOCTL_READDIR_BOTH */
506 /***********************************************************************
507 * DOSFS_OpenDir_Normal
509 * Now use the standard opendir/readdir interface
511 static BOOL
DOSFS_OpenDir_Normal( UINT codepage
, DOS_DIR
**dir
, const char *unix_path
)
513 DIR *unixdir
= opendir( unix_path
);
515 static const WCHAR empty_strW
[] = { 0 };
521 WCHAR long_name
[MAX_PATH
];
522 struct dirent
*de
= readdir(unixdir
);
526 MultiByteToWideChar(codepage
, 0, de
->d_name
, -1, long_name
, MAX_PATH
);
527 r
= DOSFS_AddDirEntry(dir
, long_name
, empty_strW
);
532 DOSFS_AddDirEntry(dir
, empty_strW
, empty_strW
);
537 /***********************************************************************
540 static DOS_DIR
*DOSFS_OpenDir( UINT codepage
, const char *unix_path
)
542 const int init_size
= 0x100;
543 DOS_DIR
*dir
= HeapAlloc( GetProcessHeap(), 0, sizeof(*dir
) + init_size
);
546 TRACE("%s\n",debugstr_a(unix_path
));
550 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
554 dir
->size
= init_size
;
556 /* Treat empty path as root directory. This simplifies path split into
557 directory and mask in several other places */
558 if (!*unix_path
) unix_path
= "/";
560 r
= DOSFS_OpenDir_VFAT( codepage
, &dir
, unix_path
);
563 r
= DOSFS_OpenDir_Normal( codepage
, &dir
, unix_path
);
567 HeapFree(GetProcessHeap(), 0, dir
);
576 /***********************************************************************
579 static void DOSFS_CloseDir( DOS_DIR
*dir
)
581 HeapFree( GetProcessHeap(), 0, dir
);
585 /***********************************************************************
588 static BOOL
DOSFS_ReadDir( DOS_DIR
*dir
, LPCWSTR
*long_name
,
589 LPCWSTR
*short_name
)
596 /* the long pathname is first */
597 ln
= (LPCWSTR
)&dir
->names
[dir
->used
];
602 dir
->used
+= (strlenW(ln
) + 1) * sizeof(WCHAR
);
604 /* followed by the short path name */
605 sn
= (LPCWSTR
)&dir
->names
[dir
->used
];
610 dir
->used
+= (strlenW(sn
) + 1) * sizeof(WCHAR
);
612 TRACE("Read: long_name: %s, short_name: %s\n",
613 debugstr_w(*long_name
), debugstr_w(*short_name
));
619 /***********************************************************************
622 * Transform a Unix file name into a hashed DOS name. If the name is a valid
623 * DOS name, it is converted to upper-case; otherwise it is replaced by a
624 * hashed version that fits in 8.3 format.
625 * File name can be terminated by '\0', '\\' or '/'.
626 * 'buffer' must be at least 13 characters long.
628 static void DOSFS_Hash( LPCWSTR name
, LPWSTR buffer
, BOOL dir_format
,
631 static const char invalid_chars
[] = INVALID_DOS_CHARS
"~.";
632 static const char hash_chars
[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
641 for(i
= 0; i
< 11; i
++) buffer
[i
] = ' ';
645 if (DOSFS_ValidDOSName( name
, ignore_case
))
647 /* Check for '.' and '..' */
651 if (!dir_format
) buffer
[1] = buffer
[2] = '\0';
652 if (name
[1] == '.') buffer
[1] = '.';
656 /* Simply copy the name, converting to uppercase */
658 for (dst
= buffer
; !IS_END_OF_NAME(*name
) && (*name
!= '.'); name
++)
659 *dst
++ = toupperW(*name
);
662 if (dir_format
) dst
= buffer
+ 8;
664 for (name
++; !IS_END_OF_NAME(*name
); name
++)
665 *dst
++ = toupperW(*name
);
667 if (!dir_format
) *dst
= '\0';
671 /* Compute the hash code of the file name */
672 /* If you know something about hash functions, feel free to */
673 /* insert a better algorithm here... */
676 for (p
= name
, hash
= 0xbeef; !IS_END_OF_NAME(p
[1]); p
++)
677 hash
= (hash
<<3) ^ (hash
>>5) ^ tolowerW(*p
) ^ (tolowerW(p
[1]) << 8);
678 hash
= (hash
<<3) ^ (hash
>>5) ^ tolowerW(*p
); /* Last character */
682 for (p
= name
, hash
= 0xbeef; !IS_END_OF_NAME(p
[1]); p
++)
683 hash
= (hash
<< 3) ^ (hash
>> 5) ^ *p
^ (p
[1] << 8);
684 hash
= (hash
<< 3) ^ (hash
>> 5) ^ *p
; /* Last character */
687 /* Find last dot for start of the extension */
688 for (p
= name
+1, ext
= NULL
; !IS_END_OF_NAME(*p
); p
++)
689 if (*p
== '.') ext
= p
;
690 if (ext
&& IS_END_OF_NAME(ext
[1]))
691 ext
= NULL
; /* Empty extension ignored */
693 /* Copy first 4 chars, replacing invalid chars with '_' */
694 for (i
= 4, p
= name
, dst
= buffer
; i
> 0; i
--, p
++)
696 if (IS_END_OF_NAME(*p
) || (p
== ext
)) break;
697 *dst
++ = (*p
< 256 && strchr( invalid_chars
, (char)*p
)) ? '_' : toupperW(*p
);
699 /* Pad to 5 chars with '~' */
700 while (i
-- >= 0) *dst
++ = '~';
702 /* Insert hash code converted to 3 ASCII chars */
703 *dst
++ = hash_chars
[(hash
>> 10) & 0x1f];
704 *dst
++ = hash_chars
[(hash
>> 5) & 0x1f];
705 *dst
++ = hash_chars
[hash
& 0x1f];
707 /* Copy the first 3 chars of the extension (if any) */
710 if (!dir_format
) *dst
++ = '.';
711 for (i
= 3, ext
++; (i
> 0) && !IS_END_OF_NAME(*ext
); i
--, ext
++)
712 *dst
++ = (*ext
< 256 && strchr( invalid_chars
, (char)*ext
)) ? '_' : toupperW(*ext
);
714 if (!dir_format
) *dst
= '\0';
718 /***********************************************************************
721 * Find the Unix file name in a given directory that corresponds to
722 * a file name (either in Unix or DOS format).
723 * File name can be terminated by '\0', '\\' or '/'.
724 * Return TRUE if OK, FALSE if no file name matches.
726 * 'long_buf' must be at least 'long_len' characters long. If the long name
727 * turns out to be larger than that, the function returns FALSE.
728 * 'short_buf' must be at least 13 characters long.
730 BOOL
DOSFS_FindUnixName( const DOS_FULL_NAME
*path
, LPCWSTR name
, char *long_buf
,
731 INT long_len
, LPWSTR short_buf
, BOOL ignore_case
)
734 LPCWSTR long_name
, short_name
;
735 WCHAR dos_name
[12], tmp_buf
[13];
738 LPCWSTR p
= strchrW( name
, '/' );
739 int len
= p
? (int)(p
- name
) : strlenW(name
);
740 if ((p
= strchrW( name
, '\\' ))) len
= min( (int)(p
- name
), len
);
741 /* Ignore trailing dots and spaces */
742 while (len
> 1 && (name
[len
-1] == '.' || name
[len
-1] == ' ')) len
--;
743 if (long_len
< len
+ 1) return FALSE
;
745 TRACE("%s,%s\n", path
->long_name
, debugstr_w(name
) );
747 if (!DOSFS_ToDosFCBFormat( name
, dos_name
)) dos_name
[0] = '\0';
749 if (!(dir
= DOSFS_OpenDir( DRIVE_GetCodepage(path
->drive
), path
->long_name
)))
751 WARN("(%s,%s): can't open dir: %s\n",
752 path
->long_name
, debugstr_w(name
), strerror(errno
) );
756 while ((ret
= DOSFS_ReadDir( dir
, &long_name
, &short_name
)))
758 /* Check against Unix name */
759 if (len
== strlenW(long_name
))
763 if (!strncmpW( long_name
, name
, len
)) break;
767 if (!strncmpiW( long_name
, name
, len
)) break;
772 /* Check against hashed DOS name */
775 DOSFS_Hash( long_name
, tmp_buf
, TRUE
, ignore_case
);
776 short_name
= tmp_buf
;
778 if (!strcmpW( dos_name
, short_name
)) break;
783 if (long_buf
) WideCharToMultiByte(DRIVE_GetCodepage(path
->drive
), 0,
784 long_name
, -1, long_buf
, long_len
, NULL
, NULL
);
788 DOSFS_ToDosDTAFormat( short_name
, short_buf
);
790 DOSFS_Hash( long_name
, short_buf
, FALSE
, ignore_case
);
792 TRACE("(%s,%s) -> %s (%s)\n", path
->long_name
, debugstr_w(name
),
793 debugstr_w(long_name
), short_buf
? debugstr_w(short_buf
) : "***");
796 WARN("%s not found in '%s'\n", debugstr_w(name
), path
->long_name
);
797 DOSFS_CloseDir( dir
);
802 /***********************************************************************
805 * Check if a DOS file name represents a DOS device and return the device.
807 const DOS_DEVICE
*DOSFS_GetDevice( LPCWSTR name
)
812 if (!name
) return NULL
; /* if FILE_DupUnixHandle was used */
813 if (name
[0] && (name
[1] == ':')) name
+= 2;
814 if ((p
= strrchrW( name
, '/' ))) name
= p
+ 1;
815 if ((p
= strrchrW( name
, '\\' ))) name
= p
+ 1;
816 for (i
= 0; i
< sizeof(DOSFS_Devices
)/sizeof(DOSFS_Devices
[0]); i
++)
818 const WCHAR
*dev
= DOSFS_Devices
[i
].name
;
819 if (!strncmpiW( dev
, name
, strlenW(dev
) ))
821 p
= name
+ strlenW( dev
);
822 if (!*p
|| (*p
== '.') || (*p
== ':')) return &DOSFS_Devices
[i
];
829 /***********************************************************************
830 * DOSFS_GetDeviceByHandle
832 const DOS_DEVICE
*DOSFS_GetDeviceByHandle( HANDLE hFile
)
834 const DOS_DEVICE
*ret
= NULL
;
835 SERVER_START_REQ( get_file_info
)
838 if (!wine_server_call( req
) && (reply
->type
== FILE_TYPE_UNKNOWN
))
840 if ((reply
->attr
>= 0) &&
841 (reply
->attr
< sizeof(DOSFS_Devices
)/sizeof(DOSFS_Devices
[0])))
842 ret
= &DOSFS_Devices
[reply
->attr
];
850 /**************************************************************************
851 * DOSFS_CreateCommPort
853 static HANDLE
DOSFS_CreateCommPort(LPCWSTR name
, DWORD access
, DWORD attributes
, LPSECURITY_ATTRIBUTES sa
)
858 static const WCHAR serialportsW
[] = {'s','e','r','i','a','l','p','o','r','t','s',0};
859 static const WCHAR empty_strW
[] = { 0 };
861 TRACE_(file
)("%s %lx %lx\n", debugstr_w(name
), access
, attributes
);
863 PROFILE_GetWineIniString(serialportsW
, name
, empty_strW
, devnameW
, 40);
867 WideCharToMultiByte(CP_ACP
, 0, devnameW
, -1, devname
, sizeof(devname
), NULL
, NULL
);
869 TRACE("opening %s as %s\n", devname
, debugstr_w(name
));
871 SERVER_START_REQ( create_serial
)
873 req
->access
= access
;
874 req
->inherit
= (sa
&& (sa
->nLength
>=sizeof(*sa
)) && sa
->bInheritHandle
);
875 req
->attributes
= attributes
;
876 req
->sharing
= FILE_SHARE_READ
|FILE_SHARE_WRITE
;
877 wine_server_add_data( req
, devname
, strlen(devname
) );
879 wine_server_call_err( req
);
885 ERR("Couldn't open device '%s' ! (check permissions)\n",devname
);
887 TRACE("return %08X\n", ret
);
891 /***********************************************************************
894 * Open a DOS device. This might not map 1:1 into the UNIX device concept.
895 * Returns 0 on failure.
897 HANDLE
DOSFS_OpenDevice( LPCWSTR name
, DWORD access
, DWORD attributes
, LPSECURITY_ATTRIBUTES sa
)
903 if (name
[0] && (name
[1] == ':')) name
+= 2;
904 if ((p
= strrchrW( name
, '/' ))) name
= p
+ 1;
905 if ((p
= strrchrW( name
, '\\' ))) name
= p
+ 1;
906 for (i
= 0; i
< sizeof(DOSFS_Devices
)/sizeof(DOSFS_Devices
[0]); i
++)
908 const WCHAR
*dev
= DOSFS_Devices
[i
].name
;
909 if (!strncmpiW( dev
, name
, strlenW(dev
) ))
911 p
= name
+ strlenW( dev
);
912 if (!*p
|| (*p
== '.') || (*p
== ':')) {
913 static const WCHAR nulW
[] = {'N','U','L',0};
914 static const WCHAR conW
[] = {'C','O','N',0};
915 static const WCHAR scsimgrW
[] = {'S','C','S','I','M','G','R','$',0};
916 static const WCHAR hpscanW
[] = {'H','P','S','C','A','N',0};
917 static const WCHAR emmxxxx0W
[] = {'E','M','M','X','X','X','X','0',0};
919 if (!strcmpiW(DOSFS_Devices
[i
].name
, nulW
))
920 return FILE_CreateFile( "/dev/null", access
,
921 FILE_SHARE_READ
|FILE_SHARE_WRITE
, sa
,
922 OPEN_EXISTING
, 0, 0, TRUE
, DRIVE_UNKNOWN
);
923 if (!strcmpiW(DOSFS_Devices
[i
].name
, conW
)) {
925 switch (access
& (GENERIC_READ
|GENERIC_WRITE
)) {
927 to_dup
= GetStdHandle( STD_INPUT_HANDLE
);
930 to_dup
= GetStdHandle( STD_OUTPUT_HANDLE
);
933 FIXME("can't open CON read/write\n");
936 if (!DuplicateHandle( GetCurrentProcess(), to_dup
, GetCurrentProcess(),
938 sa
&& (sa
->nLength
>=sizeof(*sa
)) && sa
->bInheritHandle
,
939 DUPLICATE_SAME_ACCESS
))
943 if (!strcmpiW(DOSFS_Devices
[i
].name
, scsimgrW
) ||
944 !strcmpiW(DOSFS_Devices
[i
].name
, hpscanW
) ||
945 !strcmpiW(DOSFS_Devices
[i
].name
, emmxxxx0W
))
947 return FILE_CreateDevice( i
, access
, sa
);
950 if( (handle
=DOSFS_CreateCommPort(DOSFS_Devices
[i
].name
,access
,attributes
,sa
)) )
952 FIXME("device open %s not supported (yet)\n", debugstr_w(DOSFS_Devices
[i
].name
));
961 /***********************************************************************
964 * Get the drive specified by a given path name (DOS or Unix format).
966 static int DOSFS_GetPathDrive( LPCWSTR
*name
)
971 if (*p
&& (p
[1] == ':'))
973 drive
= toupperW(*p
) - 'A';
976 else if (*p
== '/') /* Absolute Unix path? */
978 if ((drive
= DRIVE_FindDriveRootW( name
)) == -1)
980 MESSAGE("Warning: %s not accessible from a configured DOS drive\n", debugstr_w(*name
) );
981 /* Assume it really was a DOS name */
982 drive
= DRIVE_GetCurrentDrive();
985 else drive
= DRIVE_GetCurrentDrive();
987 if (!DRIVE_IsValid(drive
))
989 SetLastError( ERROR_INVALID_DRIVE
);
996 /***********************************************************************
999 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
1000 * Unix name / short DOS name pair.
1001 * Return FALSE if one of the path components does not exist. The last path
1002 * component is only checked if 'check_last' is non-zero.
1003 * The buffers pointed to by 'long_buf' and 'short_buf' must be
1004 * at least MAX_PATHNAME_LEN long.
1006 BOOL
DOSFS_GetFullName( LPCWSTR name
, BOOL check_last
, DOS_FULL_NAME
*full
)
1009 UINT flags
, codepage
;
1012 static const WCHAR driveA_rootW
[] = {'A',':','\\',0};
1013 static const WCHAR dos_rootW
[] = {'\\',0};
1015 TRACE("%s (last=%d)\n", debugstr_w(name
), check_last
);
1017 if ((!*name
) || (*name
=='\n'))
1018 { /* error code for Win98 */
1019 SetLastError(ERROR_BAD_PATHNAME
);
1023 if ((full
->drive
= DOSFS_GetPathDrive( &name
)) == -1) return FALSE
;
1024 flags
= DRIVE_GetFlags( full
->drive
);
1025 codepage
= DRIVE_GetCodepage(full
->drive
);
1027 lstrcpynA( full
->long_name
, DRIVE_GetRoot( full
->drive
),
1028 sizeof(full
->long_name
) );
1029 if (full
->long_name
[1]) root
= full
->long_name
+ strlen(full
->long_name
);
1030 else root
= full
->long_name
; /* root directory */
1032 strcpyW( full
->short_name
, driveA_rootW
);
1033 full
->short_name
[0] += full
->drive
;
1035 if ((*name
== '\\') || (*name
== '/')) /* Absolute path */
1037 while ((*name
== '\\') || (*name
== '/')) name
++;
1039 else /* Relative path */
1041 lstrcpynA( root
+ 1, DRIVE_GetUnixCwd( full
->drive
),
1042 sizeof(full
->long_name
) - (root
- full
->long_name
) - 1 );
1043 if (root
[1]) *root
= '/';
1044 lstrcpynW( full
->short_name
+ 3, DRIVE_GetDosCwd( full
->drive
),
1045 sizeof(full
->short_name
)/sizeof(full
->short_name
[0]) - 3 );
1048 p_l
= full
->long_name
[1] ? full
->long_name
+ strlen(full
->long_name
)
1050 p_s
= full
->short_name
[3] ? full
->short_name
+ strlenW(full
->short_name
)
1051 : full
->short_name
+ 2;
1054 while (*name
&& found
)
1056 /* Check for '.' and '..' */
1060 if (IS_END_OF_NAME(name
[1]))
1063 while ((*name
== '\\') || (*name
== '/')) name
++;
1066 else if ((name
[1] == '.') && IS_END_OF_NAME(name
[2]))
1069 while ((*name
== '\\') || (*name
== '/')) name
++;
1070 while ((p_l
> root
) && (*p_l
!= '/')) p_l
--;
1071 while ((p_s
> full
->short_name
+ 2) && (*p_s
!= '\\')) p_s
--;
1072 *p_l
= *p_s
= '\0'; /* Remove trailing separator */
1077 /* Make sure buffers are large enough */
1079 if ((p_s
>= full
->short_name
+ sizeof(full
->short_name
)/sizeof(full
->short_name
[0]) - 14) ||
1080 (p_l
>= full
->long_name
+ sizeof(full
->long_name
) - 1))
1082 SetLastError( ERROR_PATH_NOT_FOUND
);
1086 /* Get the long and short name matching the file name */
1088 if ((found
= DOSFS_FindUnixName( full
, name
, p_l
+ 1,
1089 sizeof(full
->long_name
) - (p_l
- full
->long_name
) - 1,
1090 p_s
+ 1, !(flags
& DRIVE_CASE_SENSITIVE
) )))
1095 p_s
+= strlenW(p_s
);
1096 while (!IS_END_OF_NAME(*name
)) name
++;
1098 else if (!check_last
)
1102 while (!IS_END_OF_NAME(*name
) &&
1103 (p_s
< full
->short_name
+ sizeof(full
->short_name
)/sizeof(full
->short_name
[0]) - 1) &&
1104 (p_l
< full
->long_name
+ sizeof(full
->long_name
) - 1))
1107 *p_s
++ = tolowerW(*name
);
1108 /* If the drive is case-sensitive we want to create new */
1109 /* files in lower-case otherwise we can't reopen them */
1110 /* under the same short name. */
1111 if (flags
& DRIVE_CASE_SENSITIVE
) wch
= tolowerW(*name
);
1113 p_l
+= WideCharToMultiByte(codepage
, 0, &wch
, 1, p_l
, 2, NULL
, NULL
);
1116 /* Ignore trailing dots and spaces */
1117 while(p_l
[-1] == '.' || p_l
[-1] == ' ') {
1124 while ((*name
== '\\') || (*name
== '/')) name
++;
1131 SetLastError( ERROR_FILE_NOT_FOUND
);
1134 if (*name
) /* Not last */
1136 SetLastError( ERROR_PATH_NOT_FOUND
);
1140 if (!full
->long_name
[0]) strcpy( full
->long_name
, "/" );
1141 if (!full
->short_name
[2]) strcpyW( full
->short_name
+ 2, dos_rootW
);
1142 TRACE("returning %s = %s\n", full
->long_name
, debugstr_w(full
->short_name
) );
1147 /***********************************************************************
1148 * GetShortPathNameW (KERNEL32.@)
1152 * longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1153 * longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0
1155 * more observations ( with NT 3.51 (WinDD) ):
1156 * longpath <= 8.3 -> just copy longpath to shortpath
1158 * a) file does not exist -> return 0, LastError = ERROR_FILE_NOT_FOUND
1159 * b) file does exist -> set the short filename.
1160 * - trailing slashes are reproduced in the short name, even if the
1161 * file is not a directory
1162 * - the absolute/relative path of the short name is reproduced like found
1164 * - longpath and shortpath may have the same address
1165 * Peter Ganten, 1999
1167 DWORD WINAPI
GetShortPathNameW( LPCWSTR longpath
, LPWSTR shortpath
, DWORD shortlen
)
1169 DOS_FULL_NAME full_name
;
1170 WCHAR tmpshortpath
[MAX_PATHNAME_LEN
];
1172 DWORD sp
= 0, lp
= 0;
1176 BOOL unixabsolute
= *longpath
== '/';
1178 TRACE("%s\n", debugstr_w(longpath
));
1181 SetLastError(ERROR_INVALID_PARAMETER
);
1185 SetLastError(ERROR_BAD_PATHNAME
);
1189 /* check for drive letter */
1190 if (!unixabsolute
&& longpath
[1] == ':' ) {
1191 tmpshortpath
[0] = longpath
[0];
1192 tmpshortpath
[1] = ':';
1196 if ( ( drive
= DOSFS_GetPathDrive ( &longpath
)) == -1 ) return 0;
1197 flags
= DRIVE_GetFlags ( drive
);
1199 if (unixabsolute
&& drive
!= DRIVE_GetCurrentDrive()) {
1200 tmpshortpath
[0] = drive
+ 'A';
1201 tmpshortpath
[1] = ':';
1205 while ( longpath
[lp
] ) {
1207 /* check for path delimiters and reproduce them */
1208 if ( longpath
[lp
] == '\\' || longpath
[lp
] == '/' ) {
1209 if (!sp
|| tmpshortpath
[sp
-1]!= '\\')
1211 /* strip double "\\" */
1212 tmpshortpath
[sp
] = '\\';
1215 tmpshortpath
[sp
]=0;/*terminate string*/
1221 for(p
= longpath
+ lp
; *p
&& *p
!= '/' && *p
!= '\\'; p
++)
1223 lstrcpynW(tmpshortpath
+ sp
, longpath
+ lp
, tmplen
+ 1);
1225 /* Check, if the current element is a valid dos name */
1226 if ( DOSFS_ValidDOSName ( longpath
+ lp
, !(flags
& DRIVE_CASE_SENSITIVE
) ) ) {
1232 /* Check if the file exists and use the existing file name */
1233 if ( DOSFS_GetFullName ( tmpshortpath
, TRUE
, &full_name
) ) {
1234 strcpyW(tmpshortpath
+ sp
, strrchrW(full_name
.short_name
, '\\') + 1);
1235 sp
+= strlenW(tmpshortpath
+ sp
);
1240 TRACE("not found!\n" );
1241 SetLastError ( ERROR_FILE_NOT_FOUND
);
1244 tmpshortpath
[sp
] = 0;
1246 tmplen
= strlenW(tmpshortpath
) + 1;
1247 if (tmplen
<= shortlen
)
1249 strcpyW(shortpath
, tmpshortpath
);
1250 TRACE("returning %s\n", debugstr_w(shortpath
));
1251 tmplen
--; /* length without 0 */
1258 /***********************************************************************
1259 * GetShortPathNameA (KERNEL32.@)
1261 DWORD WINAPI
GetShortPathNameA( LPCSTR longpath
, LPSTR shortpath
, DWORD shortlen
)
1263 UNICODE_STRING longpathW
;
1264 WCHAR shortpathW
[MAX_PATH
];
1269 SetLastError(ERROR_INVALID_PARAMETER
);
1273 TRACE("%s\n", debugstr_a(longpath
));
1275 if (!RtlCreateUnicodeStringFromAsciiz(&longpathW
, longpath
))
1277 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1281 retW
= GetShortPathNameW(longpathW
.Buffer
, shortpathW
, MAX_PATH
);
1285 else if (retW
> MAX_PATH
)
1287 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
1292 ret
= WideCharToMultiByte(CP_ACP
, 0, shortpathW
, -1, NULL
, 0, NULL
, NULL
);
1293 if (ret
<= shortlen
)
1295 WideCharToMultiByte(CP_ACP
, 0, shortpathW
, -1, shortpath
, shortlen
, NULL
, NULL
);
1296 ret
--; /* length without 0 */
1300 RtlFreeUnicodeString(&longpathW
);
1305 /***********************************************************************
1306 * GetLongPathNameW (KERNEL32.@)
1309 * observed (Win2000):
1310 * shortpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1311 * shortpath="": LastError=ERROR_PATH_NOT_FOUND, ret=0
1313 DWORD WINAPI
GetLongPathNameW( LPCWSTR shortpath
, LPWSTR longpath
, DWORD longlen
)
1315 DOS_FULL_NAME full_name
;
1323 SetLastError(ERROR_INVALID_PARAMETER
);
1326 if (!shortpath
[0]) {
1327 SetLastError(ERROR_PATH_NOT_FOUND
);
1331 TRACE("%s,%p,%ld\n", debugstr_w(shortpath
), longpath
, longlen
);
1333 if(shortpath
[0]=='\\' && shortpath
[1]=='\\')
1335 ERR("UNC pathname %s\n",debugstr_w(shortpath
));
1336 lstrcpynW( longpath
, full_name
.short_name
, longlen
);
1337 return strlenW(longpath
);
1340 if (!DOSFS_GetFullName( shortpath
, TRUE
, &full_name
)) return 0;
1342 root
= full_name
.long_name
;
1343 drive
= DRIVE_FindDriveRoot(&root
);
1344 codepage
= DRIVE_GetCodepage(drive
);
1346 ret
= MultiByteToWideChar(codepage
, 0, root
, -1, NULL
, 0);
1348 /* reproduce terminating slash */
1349 if (ret
> 4) /* if not drive root */
1351 len
= strlenW(shortpath
);
1352 if (shortpath
[len
- 1] == '\\' || shortpath
[len
- 1] == '/')
1358 longpath
[0] = 'A' + drive
;
1360 MultiByteToWideChar(codepage
, 0, root
, -1, longpath
+ 2, longlen
- 2);
1361 for (p
= longpath
; *p
; p
++) if (*p
== '/') *p
= '\\';
1364 longpath
[ret
- 2] = '\\';
1365 longpath
[ret
- 1] = 0;
1367 TRACE("returning %s\n", debugstr_w(longpath
));
1368 ret
--; /* length without 0 */
1374 /***********************************************************************
1375 * GetLongPathNameA (KERNEL32.@)
1377 DWORD WINAPI
GetLongPathNameA( LPCSTR shortpath
, LPSTR longpath
, DWORD longlen
)
1379 UNICODE_STRING shortpathW
;
1380 WCHAR longpathW
[MAX_PATH
];
1385 SetLastError(ERROR_INVALID_PARAMETER
);
1389 TRACE("%s\n", debugstr_a(shortpath
));
1391 if (!RtlCreateUnicodeStringFromAsciiz(&shortpathW
, shortpath
))
1393 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1397 retW
= GetLongPathNameW(shortpathW
.Buffer
, longpathW
, MAX_PATH
);
1401 else if (retW
> MAX_PATH
)
1403 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
1408 ret
= WideCharToMultiByte(CP_ACP
, 0, longpathW
, -1, NULL
, 0, NULL
, NULL
);
1411 WideCharToMultiByte(CP_ACP
, 0, longpathW
, -1, longpath
, longlen
, NULL
, NULL
);
1412 ret
--; /* length without 0 */
1416 RtlFreeUnicodeString(&shortpathW
);
1421 /***********************************************************************
1422 * DOSFS_DoGetFullPathName
1424 * Implementation of GetFullPathNameA/W.
1426 * bon@elektron 000331:
1427 * A test for GetFullPathName with many pathological cases
1428 * now gives identical output for Wine and OSR2
1430 static DWORD
DOSFS_DoGetFullPathName( LPCWSTR name
, DWORD len
, LPWSTR result
)
1433 DOS_FULL_NAME full_name
;
1437 WCHAR drivecur
[] = {'C',':','.',0};
1438 WCHAR driveletter
=0;
1439 int namelen
,drive
=0;
1440 static const WCHAR bkslashW
[] = {'\\',0};
1441 static const WCHAR dotW
[] = {'.',0};
1442 static const WCHAR updir_slashW
[] = {'\\','.','.','\\',0};
1443 static const WCHAR curdirW
[] = {'\\','.','\\',0};
1444 static const WCHAR updirW
[] = {'\\','.','.',0};
1448 SetLastError(ERROR_BAD_PATHNAME
);
1452 TRACE("passed %s\n", debugstr_w(name
));
1455 /*drive letter given */
1457 driveletter
= name
[0];
1459 if ((name
[1]==':') && ((name
[2]=='\\') || (name
[2]=='/')))
1460 /*absolute path given */
1462 strncpyW(full_name
.short_name
, name
, MAX_PATHNAME_LEN
);
1463 full_name
.short_name
[MAX_PATHNAME_LEN
- 1] = 0; /* ensure 0 termination */
1464 drive
= toupperW(name
[0]) - 'A';
1469 drivecur
[0]=driveletter
;
1470 else if ((name
[0]=='\\') || (name
[0]=='/'))
1471 strcpyW(drivecur
, bkslashW
);
1473 strcpyW(drivecur
, dotW
);
1475 if (!DOSFS_GetFullName( drivecur
, FALSE
, &full_name
))
1477 FIXME("internal: error getting drive/path\n");
1480 /* find path that drive letter substitutes*/
1481 drive
= toupperW(full_name
.short_name
[0]) - 'A';
1482 root
= DRIVE_GetRoot(drive
);
1485 FIXME("internal: error getting DOS Drive Root\n");
1488 if (!strcmp(root
,"/"))
1490 /* we have just the last / and we need it. */
1491 p_l
= full_name
.long_name
;
1495 p_l
= full_name
.long_name
+ strlen(root
);
1497 /* append long name (= unix name) to drive */
1498 MultiByteToWideChar(DRIVE_GetCodepage(drive
), 0, p_l
, -1,
1499 full_name
.short_name
+ 2, MAX_PATHNAME_LEN
- 3);
1500 /* append name to treat */
1501 namelen
= strlenW(full_name
.short_name
);
1504 p
+= 2; /* skip drive name when appending */
1505 if (namelen
+ 2 + strlenW(p
) > MAX_PATHNAME_LEN
)
1507 FIXME("internal error: buffer too small\n");
1510 full_name
.short_name
[namelen
++] ='\\';
1511 full_name
.short_name
[namelen
] = 0;
1512 strncpyW(full_name
.short_name
+ namelen
, p
, MAX_PATHNAME_LEN
- namelen
);
1513 full_name
.short_name
[MAX_PATHNAME_LEN
- 1] = 0; /* ensure 0 termination */
1515 /* reverse all slashes */
1516 for (p
=full_name
.short_name
;
1517 p
< full_name
.short_name
+ strlenW(full_name
.short_name
);
1523 /* Use memmove, as areas overlap */
1525 while ((p
= strstrW(full_name
.short_name
, updir_slashW
)))
1527 if (p
> full_name
.short_name
+2)
1530 q
= strrchrW(full_name
.short_name
, '\\');
1531 memmove(q
+1, p
+4, (strlenW(p
+4)+1) * sizeof(WCHAR
));
1535 memmove(full_name
.short_name
+3, p
+4, (strlenW(p
+4)+1) * sizeof(WCHAR
));
1538 if ((full_name
.short_name
[2]=='.')&&(full_name
.short_name
[3]=='.'))
1540 /* This case istn't treated yet : c:..\test */
1541 memmove(full_name
.short_name
+2,full_name
.short_name
+4,
1542 (strlenW(full_name
.short_name
+4)+1) * sizeof(WCHAR
));
1545 while ((p
= strstrW(full_name
.short_name
, curdirW
)))
1548 memmove(p
+1, p
+3, (strlenW(p
+3)+1) * sizeof(WCHAR
));
1550 if (!(DRIVE_GetFlags(drive
) & DRIVE_CASE_PRESERVING
))
1551 for (p
= full_name
.short_name
; *p
; p
++) *p
= toupperW(*p
);
1552 namelen
= strlenW(full_name
.short_name
);
1553 if (!strcmpW(full_name
.short_name
+namelen
-3, updirW
))
1555 /* one more strange case: "c:\test\test1\.."
1557 *(full_name
.short_name
+namelen
-3)=0;
1558 q
= strrchrW(full_name
.short_name
, '\\');
1561 if (full_name
.short_name
[namelen
-1]=='.')
1562 full_name
.short_name
[(namelen
--)-1] =0;
1564 if (full_name
.short_name
[namelen
-1]=='\\')
1565 full_name
.short_name
[(namelen
--)-1] =0;
1566 TRACE("got %s\n", debugstr_w(full_name
.short_name
));
1568 /* If the lpBuffer buffer is too small, the return value is the
1569 size of the buffer, in characters, required to hold the path
1570 plus the terminating \0 (tested against win95osr2, bon 001118)
1572 ret
= strlenW(full_name
.short_name
);
1575 /* don't touch anything when the buffer is not large enough */
1576 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
1581 strncpyW( result
, full_name
.short_name
, len
);
1582 result
[len
- 1] = 0; /* ensure 0 termination */
1585 TRACE("returning %s\n", debugstr_w(full_name
.short_name
) );
1590 /***********************************************************************
1591 * GetFullPathNameA (KERNEL32.@)
1593 * if the path closed with '\', *lastpart is 0
1595 DWORD WINAPI
GetFullPathNameA( LPCSTR name
, DWORD len
, LPSTR buffer
,
1598 UNICODE_STRING nameW
;
1599 WCHAR bufferW
[MAX_PATH
];
1604 SetLastError(ERROR_INVALID_PARAMETER
);
1608 if (!RtlCreateUnicodeStringFromAsciiz(&nameW
, name
))
1610 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1614 retW
= GetFullPathNameW( nameW
.Buffer
, MAX_PATH
, bufferW
, NULL
);
1618 else if (retW
> MAX_PATH
)
1620 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
1625 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, -1, NULL
, 0, NULL
, NULL
);
1628 WideCharToMultiByte(CP_ACP
, 0, bufferW
, -1, buffer
, len
, NULL
, NULL
);
1629 ret
--; /* length without 0 */
1633 LPSTR p
= buffer
+ strlen(buffer
);
1637 while ((p
> buffer
+ 2) && (*p
!= '\\')) p
--;
1640 else *lastpart
= NULL
;
1645 RtlFreeUnicodeString(&nameW
);
1650 /***********************************************************************
1651 * GetFullPathNameW (KERNEL32.@)
1653 DWORD WINAPI
GetFullPathNameW( LPCWSTR name
, DWORD len
, LPWSTR buffer
,
1656 DWORD ret
= DOSFS_DoGetFullPathName( name
, len
, buffer
);
1657 if (ret
&& (ret
<=len
) && buffer
&& lastpart
)
1659 LPWSTR p
= buffer
+ strlenW(buffer
);
1660 if (*p
!= (WCHAR
)'\\')
1662 while ((p
> buffer
+ 2) && (*p
!= (WCHAR
)'\\')) p
--;
1665 else *lastpart
= NULL
;
1671 /***********************************************************************
1672 * wine_get_unix_file_name (KERNEL32.@) Not a Windows API
1674 * Return the full Unix file name for a given path.
1675 * FIXME: convert dos file name to unicode
1677 BOOL WINAPI
wine_get_unix_file_name( LPCSTR dos
, LPSTR buffer
, DWORD len
)
1681 WCHAR dosW
[MAX_PATHNAME_LEN
];
1683 MultiByteToWideChar(CP_ACP
, 0, dos
, -1, dosW
, MAX_PATHNAME_LEN
);
1684 ret
= DOSFS_GetFullName( dosW
, FALSE
, &path
);
1687 strncpy( buffer
, path
.long_name
, len
);
1688 buffer
[len
- 1] = 0; /* ensure 0 termination */
1694 /***********************************************************************
1697 static int DOSFS_FindNextEx( FIND_FIRST_INFO
*info
, WIN32_FIND_DATAW
*entry
)
1699 DWORD attr
= info
->attr
| FA_UNUSED
| FA_ARCHIVE
| FA_RDONLY
| FILE_ATTRIBUTE_SYMLINK
;
1700 UINT flags
= DRIVE_GetFlags( info
->drive
);
1701 char *p
, buffer
[MAX_PATHNAME_LEN
];
1702 const char *drive_path
;
1704 LPCWSTR long_name
, short_name
;
1705 BY_HANDLE_FILE_INFORMATION fileinfo
;
1708 if ((info
->attr
& ~(FA_UNUSED
| FA_ARCHIVE
| FA_RDONLY
)) == FA_LABEL
)
1710 if (info
->cur_pos
) return 0;
1711 entry
->dwFileAttributes
= FILE_ATTRIBUTE_LABEL
;
1712 RtlSecondsSince1970ToTime( (time_t)0, (LARGE_INTEGER
*)&entry
->ftCreationTime
);
1713 RtlSecondsSince1970ToTime( (time_t)0, (LARGE_INTEGER
*)&entry
->ftLastAccessTime
);
1714 RtlSecondsSince1970ToTime( (time_t)0, (LARGE_INTEGER
*)&entry
->ftLastWriteTime
);
1715 entry
->nFileSizeHigh
= 0;
1716 entry
->nFileSizeLow
= 0;
1717 entry
->dwReserved0
= 0;
1718 entry
->dwReserved1
= 0;
1719 DOSFS_ToDosDTAFormat( DRIVE_GetLabel( info
->drive
), entry
->cFileName
);
1720 strcpyW( entry
->cAlternateFileName
, entry
->cFileName
);
1722 TRACE("returning %s (%s) as label\n",
1723 debugstr_w(entry
->cFileName
), debugstr_w(entry
->cAlternateFileName
));
1727 drive_path
= info
->path
+ strlen(DRIVE_GetRoot( info
->drive
));
1728 while ((*drive_path
== '/') || (*drive_path
== '\\')) drive_path
++;
1729 drive_root
= !*drive_path
;
1731 lstrcpynA( buffer
, info
->path
, sizeof(buffer
) - 1 );
1732 strcat( buffer
, "/" );
1733 p
= buffer
+ strlen(buffer
);
1735 while (DOSFS_ReadDir( info
->u
.dos_dir
, &long_name
, &short_name
))
1739 /* Don't return '.' and '..' in the root of the drive */
1740 if (drive_root
&& (long_name
[0] == '.') &&
1741 (!long_name
[1] || ((long_name
[1] == '.') && !long_name
[2])))
1744 /* Check the long mask */
1746 if (info
->long_mask
&& *info
->long_mask
)
1748 if (!DOSFS_MatchLong( info
->long_mask
, long_name
,
1749 flags
& DRIVE_CASE_SENSITIVE
)) continue;
1752 /* Check the short mask */
1754 if (info
->short_mask
)
1758 DOSFS_Hash( long_name
, dos_name
, TRUE
,
1759 !(flags
& DRIVE_CASE_SENSITIVE
) );
1760 short_name
= dos_name
;
1762 if (!DOSFS_MatchShort( info
->short_mask
, short_name
)) continue;
1765 /* Check the file attributes */
1766 WideCharToMultiByte(DRIVE_GetCodepage(info
->drive
), 0, long_name
, -1,
1767 p
, sizeof(buffer
) - (int)(p
- buffer
), NULL
, NULL
);
1768 if (!FILE_Stat( buffer
, &fileinfo
))
1770 WARN("can't stat %s\n", buffer
);
1773 if ((fileinfo
.dwFileAttributes
& FILE_ATTRIBUTE_SYMLINK
) &&
1774 (fileinfo
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
))
1776 static const WCHAR wineW
[] = {'w','i','n','e',0};
1777 static const WCHAR ShowDirSymlinksW
[] = {'S','h','o','w','D','i','r','S','y','m','l','i','n','k','s',0};
1778 static int show_dir_symlinks
= -1;
1779 if (show_dir_symlinks
== -1)
1780 show_dir_symlinks
= PROFILE_GetWineIniBool(wineW
, ShowDirSymlinksW
, 0);
1781 if (!show_dir_symlinks
) continue;
1784 if (fileinfo
.dwFileAttributes
& ~attr
) continue;
1786 /* We now have a matching entry; fill the result and return */
1788 entry
->dwFileAttributes
= fileinfo
.dwFileAttributes
;
1789 entry
->ftCreationTime
= fileinfo
.ftCreationTime
;
1790 entry
->ftLastAccessTime
= fileinfo
.ftLastAccessTime
;
1791 entry
->ftLastWriteTime
= fileinfo
.ftLastWriteTime
;
1792 entry
->nFileSizeHigh
= fileinfo
.nFileSizeHigh
;
1793 entry
->nFileSizeLow
= fileinfo
.nFileSizeLow
;
1796 DOSFS_ToDosDTAFormat( short_name
, entry
->cAlternateFileName
);
1798 DOSFS_Hash( long_name
, entry
->cAlternateFileName
, FALSE
,
1799 !(flags
& DRIVE_CASE_SENSITIVE
) );
1801 lstrcpynW( entry
->cFileName
, long_name
, sizeof(entry
->cFileName
)/sizeof(entry
->cFileName
[0]) );
1802 if (!(flags
& DRIVE_CASE_PRESERVING
)) strlwrW( entry
->cFileName
);
1803 TRACE("returning %s (%s) %02lx %ld\n",
1804 debugstr_w(entry
->cFileName
), debugstr_w(entry
->cAlternateFileName
),
1805 entry
->dwFileAttributes
, entry
->nFileSizeLow
);
1808 return 0; /* End of directory */
1811 /***********************************************************************
1814 * Find the next matching file. Return the number of entries read to find
1815 * the matching one, or 0 if no more entries.
1816 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
1817 * file name mask. Either or both can be NULL.
1819 * NOTE: This is supposed to be only called by the int21 emulation
1820 * routines. Thus, we should own the Win16Mutex anyway.
1821 * Nevertheless, we explicitly enter it to ensure the static
1822 * directory cache is protected.
1824 int DOSFS_FindNext( const char *path
, const char *short_mask
,
1825 const char *long_mask
, int drive
, BYTE attr
,
1826 int skip
, WIN32_FIND_DATAA
*entry
)
1828 static FIND_FIRST_INFO info
;
1829 LPCWSTR short_name
, long_name
;
1831 UNICODE_STRING short_maskW
, long_maskW
;
1832 WIN32_FIND_DATAW entryW
;
1834 TRACE("(%s, %s, %s, %x, %x, %x, %p)\n", debugstr_a(path
),
1835 debugstr_a(short_mask
), debugstr_a(long_mask
), drive
, attr
, skip
,
1840 RtlCreateUnicodeStringFromAsciiz(&short_maskW
, short_mask
);
1841 RtlCreateUnicodeStringFromAsciiz(&long_maskW
, long_mask
);
1843 /* Check the cached directory */
1844 if (!(info
.u
.dos_dir
&& info
.path
== path
&& !strcmpW(info
.short_mask
, short_maskW
.Buffer
)
1845 && !strcmpW(info
.long_mask
, long_maskW
.Buffer
) && info
.drive
== drive
1846 && info
.attr
== attr
&& info
.cur_pos
<= skip
))
1848 /* Not in the cache, open it anew */
1849 if (info
.u
.dos_dir
) DOSFS_CloseDir( info
.u
.dos_dir
);
1851 info
.path
= (LPSTR
)path
;
1852 RtlFreeHeap(GetProcessHeap(), 0, info
.long_mask
);
1853 RtlFreeHeap(GetProcessHeap(), 0, info
.short_mask
);
1854 info
.long_mask
= long_maskW
.Buffer
;
1855 info
.short_mask
= short_maskW
.Buffer
;
1859 info
.u
.dos_dir
= DOSFS_OpenDir( DRIVE_GetCodepage(drive
), info
.path
);
1863 RtlFreeUnicodeString(&short_maskW
);
1864 RtlFreeUnicodeString(&long_maskW
);
1867 /* Skip to desired position */
1868 while (info
.cur_pos
< skip
)
1869 if (info
.u
.dos_dir
&& DOSFS_ReadDir( info
.u
.dos_dir
, &long_name
, &short_name
))
1874 if (info
.u
.dos_dir
&& info
.cur_pos
== skip
&& DOSFS_FindNextEx( &info
, &entryW
))
1876 WideCharToMultiByte(CP_ACP
, 0, entryW
.cFileName
, -1,
1877 entry
->cFileName
, sizeof(entry
->cFileName
), NULL
, NULL
);
1878 WideCharToMultiByte(CP_ACP
, 0, entryW
.cAlternateFileName
, -1,
1879 entry
->cAlternateFileName
, sizeof(entry
->cAlternateFileName
), NULL
, NULL
);
1880 count
= info
.cur_pos
- skip
;
1887 if (info
.u
.dos_dir
) DOSFS_CloseDir( info
.u
.dos_dir
);
1888 memset( &info
, '\0', sizeof(info
) );
1896 /*************************************************************************
1897 * FindFirstFileExW (KERNEL32.@)
1899 HANDLE WINAPI
FindFirstFileExW(
1901 FINDEX_INFO_LEVELS fInfoLevelId
,
1902 LPVOID lpFindFileData
,
1903 FINDEX_SEARCH_OPS fSearchOp
,
1904 LPVOID lpSearchFilter
,
1905 DWORD dwAdditionalFlags
)
1908 FIND_FIRST_INFO
*info
;
1912 SetLastError(ERROR_PATH_NOT_FOUND
);
1913 return INVALID_HANDLE_VALUE
;
1916 if ((fSearchOp
!= FindExSearchNameMatch
) || (dwAdditionalFlags
!= 0))
1918 FIXME("options not implemented 0x%08x 0x%08lx\n", fSearchOp
, dwAdditionalFlags
);
1919 return INVALID_HANDLE_VALUE
;
1922 switch(fInfoLevelId
)
1924 case FindExInfoStandard
:
1926 WIN32_FIND_DATAW
* data
= (WIN32_FIND_DATAW
*) lpFindFileData
;
1931 data
->dwReserved0
= data
->dwReserved1
= 0x0;
1932 if (!lpFileName
) return 0;
1933 if (lpFileName
[0] == '\\' && lpFileName
[1] == '\\')
1935 ERR("UNC path name\n");
1936 if (!(handle
= GlobalAlloc(GMEM_MOVEABLE
, sizeof(FIND_FIRST_INFO
)))) break;
1938 info
= (FIND_FIRST_INFO
*)GlobalLock( handle
);
1939 info
->u
.smb_dir
= SMB_FindFirst(lpFileName
);
1940 if(!info
->u
.smb_dir
)
1942 GlobalUnlock( handle
);
1949 GlobalUnlock( handle
);
1953 DOS_FULL_NAME full_name
;
1955 if (!DOSFS_GetFullName( lpFileName
, FALSE
, &full_name
)) break;
1956 if (!(handle
= GlobalAlloc(GMEM_MOVEABLE
, sizeof(FIND_FIRST_INFO
)))) break;
1957 info
= (FIND_FIRST_INFO
*)GlobalLock( handle
);
1958 info
->path
= HeapAlloc( GetProcessHeap(), 0, strlen(full_name
.long_name
)+1 );
1959 strcpy( info
->path
, full_name
.long_name
);
1961 codepage
= DRIVE_GetCodepage(full_name
.drive
);
1962 p
= strrchr( info
->path
, '/' );
1964 long_mask_len
= MultiByteToWideChar(codepage
, 0, p
, -1, NULL
, 0);
1965 info
->long_mask
= HeapAlloc( GetProcessHeap(), 0, long_mask_len
* sizeof(WCHAR
) );
1966 MultiByteToWideChar(codepage
, 0, p
, -1, info
->long_mask
, long_mask_len
);
1968 info
->short_mask
= NULL
;
1970 info
->drive
= full_name
.drive
;
1973 info
->u
.dos_dir
= DOSFS_OpenDir( codepage
, info
->path
);
1974 GlobalUnlock( handle
);
1976 if (!FindNextFileW( handle
, data
))
1978 FindClose( handle
);
1979 SetLastError( ERROR_NO_MORE_FILES
);
1986 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId
);
1988 return INVALID_HANDLE_VALUE
;
1991 /*************************************************************************
1992 * FindFirstFileA (KERNEL32.@)
1994 HANDLE WINAPI
FindFirstFileA(
1996 WIN32_FIND_DATAA
*lpFindData
)
1998 return FindFirstFileExA(lpFileName
, FindExInfoStandard
, lpFindData
,
1999 FindExSearchNameMatch
, NULL
, 0);
2002 /*************************************************************************
2003 * FindFirstFileExA (KERNEL32.@)
2005 HANDLE WINAPI
FindFirstFileExA(
2007 FINDEX_INFO_LEVELS fInfoLevelId
,
2008 LPVOID lpFindFileData
,
2009 FINDEX_SEARCH_OPS fSearchOp
,
2010 LPVOID lpSearchFilter
,
2011 DWORD dwAdditionalFlags
)
2014 WIN32_FIND_DATAA
*dataA
;
2015 WIN32_FIND_DATAW dataW
;
2016 UNICODE_STRING pathW
;
2020 SetLastError(ERROR_PATH_NOT_FOUND
);
2021 return INVALID_HANDLE_VALUE
;
2024 if (!RtlCreateUnicodeStringFromAsciiz(&pathW
, lpFileName
))
2026 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
2027 return INVALID_HANDLE_VALUE
;
2030 handle
= FindFirstFileExW(pathW
.Buffer
, fInfoLevelId
, &dataW
, fSearchOp
, lpSearchFilter
, dwAdditionalFlags
);
2031 RtlFreeUnicodeString(&pathW
);
2032 if (handle
== INVALID_HANDLE_VALUE
) return handle
;
2034 dataA
= (WIN32_FIND_DATAA
*) lpFindFileData
;
2035 dataA
->dwFileAttributes
= dataW
.dwFileAttributes
;
2036 dataA
->ftCreationTime
= dataW
.ftCreationTime
;
2037 dataA
->ftLastAccessTime
= dataW
.ftLastAccessTime
;
2038 dataA
->ftLastWriteTime
= dataW
.ftLastWriteTime
;
2039 dataA
->nFileSizeHigh
= dataW
.nFileSizeHigh
;
2040 dataA
->nFileSizeLow
= dataW
.nFileSizeLow
;
2041 WideCharToMultiByte( CP_ACP
, 0, dataW
.cFileName
, -1,
2042 dataA
->cFileName
, sizeof(dataA
->cFileName
), NULL
, NULL
);
2043 WideCharToMultiByte( CP_ACP
, 0, dataW
.cAlternateFileName
, -1,
2044 dataA
->cAlternateFileName
, sizeof(dataA
->cAlternateFileName
), NULL
, NULL
);
2048 /*************************************************************************
2049 * FindFirstFileW (KERNEL32.@)
2051 HANDLE WINAPI
FindFirstFileW( LPCWSTR lpFileName
, WIN32_FIND_DATAW
*lpFindData
)
2053 return FindFirstFileExW(lpFileName
, FindExInfoStandard
, lpFindData
,
2054 FindExSearchNameMatch
, NULL
, 0);
2057 /*************************************************************************
2058 * FindNextFileW (KERNEL32.@)
2060 BOOL WINAPI
FindNextFileW( HANDLE handle
, WIN32_FIND_DATAW
*data
)
2062 FIND_FIRST_INFO
*info
;
2064 DWORD gle
= ERROR_NO_MORE_FILES
;
2066 if ((handle
== INVALID_HANDLE_VALUE
) ||
2067 !(info
= (FIND_FIRST_INFO
*)GlobalLock( handle
)))
2069 SetLastError( ERROR_INVALID_HANDLE
);
2072 if (info
->drive
== -1)
2074 ret
= SMB_FindNext( info
->u
.smb_dir
, data
);
2077 SMB_CloseDir( info
->u
.smb_dir
);
2078 HeapFree( GetProcessHeap(), 0, info
->path
);
2082 else if (!info
->path
|| !info
->u
.dos_dir
)
2086 else if (!DOSFS_FindNextEx( info
, data
))
2088 DOSFS_CloseDir( info
->u
.dos_dir
); info
->u
.dos_dir
= NULL
;
2089 HeapFree( GetProcessHeap(), 0, info
->path
);
2091 HeapFree( GetProcessHeap(), 0, info
->long_mask
);
2092 info
->long_mask
= NULL
;
2097 GlobalUnlock( handle
);
2098 if( !ret
) SetLastError( gle
);
2103 /*************************************************************************
2104 * FindNextFileA (KERNEL32.@)
2106 BOOL WINAPI
FindNextFileA( HANDLE handle
, WIN32_FIND_DATAA
*data
)
2108 WIN32_FIND_DATAW dataW
;
2109 if (!FindNextFileW( handle
, &dataW
)) return FALSE
;
2110 data
->dwFileAttributes
= dataW
.dwFileAttributes
;
2111 data
->ftCreationTime
= dataW
.ftCreationTime
;
2112 data
->ftLastAccessTime
= dataW
.ftLastAccessTime
;
2113 data
->ftLastWriteTime
= dataW
.ftLastWriteTime
;
2114 data
->nFileSizeHigh
= dataW
.nFileSizeHigh
;
2115 data
->nFileSizeLow
= dataW
.nFileSizeLow
;
2116 WideCharToMultiByte( CP_ACP
, 0, dataW
.cFileName
, -1,
2117 data
->cFileName
, sizeof(data
->cFileName
), NULL
, NULL
);
2118 WideCharToMultiByte( CP_ACP
, 0, dataW
.cAlternateFileName
, -1,
2119 data
->cAlternateFileName
,
2120 sizeof(data
->cAlternateFileName
), NULL
, NULL
);
2124 /*************************************************************************
2125 * FindClose (KERNEL32.@)
2127 BOOL WINAPI
FindClose( HANDLE handle
)
2129 FIND_FIRST_INFO
*info
;
2131 if (handle
== INVALID_HANDLE_VALUE
) goto error
;
2135 if ((info
= (FIND_FIRST_INFO
*)GlobalLock( handle
)))
2137 if (info
->u
.dos_dir
) DOSFS_CloseDir( info
->u
.dos_dir
);
2138 if (info
->path
) HeapFree( GetProcessHeap(), 0, info
->path
);
2139 if (info
->long_mask
) HeapFree( GetProcessHeap(), 0, info
->long_mask
);
2142 __EXCEPT(page_fault
)
2144 WARN("Illegal handle %x\n", handle
);
2145 SetLastError( ERROR_INVALID_HANDLE
);
2149 if (!info
) goto error
;
2150 GlobalUnlock( handle
);
2151 GlobalFree( handle
);
2155 SetLastError( ERROR_INVALID_HANDLE
);
2159 /***********************************************************************
2160 * DOSFS_UnixTimeToFileTime
2162 * Convert a Unix time to FILETIME format.
2163 * The FILETIME structure is a 64-bit value representing the number of
2164 * 100-nanosecond intervals since January 1, 1601, 0:00.
2165 * 'remainder' is the nonnegative number of 100-ns intervals
2166 * corresponding to the time fraction smaller than 1 second that
2167 * couldn't be stored in the time_t value.
2169 void DOSFS_UnixTimeToFileTime( time_t unix_time
, FILETIME
*filetime
,
2175 The time difference between 1 January 1601, 00:00:00 and
2176 1 January 1970, 00:00:00 is 369 years, plus the leap years
2177 from 1604 to 1968, excluding 1700, 1800, 1900.
2178 This makes (1968 - 1600) / 4 - 3 = 89 leap days, and a total
2181 Any day in that period had 24 * 60 * 60 = 86400 seconds.
2183 The time difference is 134774 * 86400 * 10000000, which can be written
2185 27111902 * 2^32 + 3577643008
2186 413 * 2^48 + 45534 * 2^32 + 54590 * 2^16 + 32768
2188 If you find that these constants are buggy, please change them in all
2189 instances in both conversion functions.
2192 There are two versions, one of them uses long long variables and
2193 is presumably faster but not ISO C. The other one uses standard C
2194 data types and operations but relies on the assumption that negative
2195 numbers are stored as 2's complement (-1 is 0xffff....). If this
2196 assumption is violated, dates before 1970 will not convert correctly.
2197 This should however work on any reasonable architecture where WINE
2202 Take care not to remove the casts. I have tested these functions
2203 (in both versions) for a lot of numbers. I would be interested in
2204 results on other compilers than GCC.
2206 The operations have been designed to account for the possibility
2207 of 64-bit time_t in future UNICES. Even the versions without
2208 internal long long numbers will work if time_t only is 64 bit.
2209 A 32-bit shift, which was necessary for that operation, turned out
2210 not to work correctly in GCC, besides giving the warning. So I
2211 used a double 16-bit shift instead. Numbers are in the ISO version
2212 represented by three limbs, the most significant with 32 bit, the
2213 other two with 16 bit each.
2215 As the modulo-operator % is not well-defined for negative numbers,
2216 negative divisors have been avoided in DOSFS_FileTimeToUnixTime.
2218 There might be quicker ways to do this in C. Certainly so in
2221 Claus Fischer, fischer@iue.tuwien.ac.at
2224 #if SIZEOF_LONG_LONG >= 8
2225 # define USE_LONG_LONG 1
2227 # define USE_LONG_LONG 0
2230 #if USE_LONG_LONG /* gcc supports long long type */
2232 long long int t
= unix_time
;
2234 t
+= 116444736000000000LL;
2236 filetime
->dwLowDateTime
= (UINT
)t
;
2237 filetime
->dwHighDateTime
= (UINT
)(t
>> 32);
2239 #else /* ISO version */
2241 UINT a0
; /* 16 bit, low bits */
2242 UINT a1
; /* 16 bit, medium bits */
2243 UINT a2
; /* 32 bit, high bits */
2245 /* Copy the unix time to a2/a1/a0 */
2246 a0
= unix_time
& 0xffff;
2247 a1
= (unix_time
>> 16) & 0xffff;
2248 /* This is obsolete if unix_time is only 32 bits, but it does not hurt.
2249 Do not replace this by >> 32, it gives a compiler warning and it does
2251 a2
= (unix_time
>= 0 ? (unix_time
>> 16) >> 16 :
2252 ~((~unix_time
>> 16) >> 16));
2254 /* Multiply a by 10000000 (a = a2/a1/a0)
2255 Split the factor into 10000 * 1000 which are both less than 0xffff. */
2257 a1
= a1
* 10000 + (a0
>> 16);
2258 a2
= a2
* 10000 + (a1
>> 16);
2263 a1
= a1
* 1000 + (a0
>> 16);
2264 a2
= a2
* 1000 + (a1
>> 16);
2268 /* Add the time difference and the remainder */
2269 a0
+= 32768 + (remainder
& 0xffff);
2270 a1
+= 54590 + (remainder
>> 16 ) + (a0
>> 16);
2271 a2
+= 27111902 + (a1
>> 16);
2276 filetime
->dwLowDateTime
= (a1
<< 16) + a0
;
2277 filetime
->dwHighDateTime
= a2
;
2282 /***********************************************************************
2283 * DOSFS_FileTimeToUnixTime
2285 * Convert a FILETIME format to Unix time.
2286 * If not NULL, 'remainder' contains the fractional part of the filetime,
2287 * in the range of [0..9999999] (even if time_t is negative).
2289 time_t DOSFS_FileTimeToUnixTime( const FILETIME
*filetime
, DWORD
*remainder
)
2291 /* Read the comment in the function DOSFS_UnixTimeToFileTime. */
2294 long long int t
= filetime
->dwHighDateTime
;
2296 t
+= (UINT
)filetime
->dwLowDateTime
;
2297 t
-= 116444736000000000LL;
2300 if (remainder
) *remainder
= 9999999 - (-t
- 1) % 10000000;
2301 return -1 - ((-t
- 1) / 10000000);
2305 if (remainder
) *remainder
= t
% 10000000;
2306 return t
/ 10000000;
2309 #else /* ISO version */
2311 UINT a0
; /* 16 bit, low bits */
2312 UINT a1
; /* 16 bit, medium bits */
2313 UINT a2
; /* 32 bit, high bits */
2314 UINT r
; /* remainder of division */
2315 unsigned int carry
; /* carry bit for subtraction */
2316 int negative
; /* whether a represents a negative value */
2318 /* Copy the time values to a2/a1/a0 */
2319 a2
= (UINT
)filetime
->dwHighDateTime
;
2320 a1
= ((UINT
)filetime
->dwLowDateTime
) >> 16;
2321 a0
= ((UINT
)filetime
->dwLowDateTime
) & 0xffff;
2323 /* Subtract the time difference */
2324 if (a0
>= 32768 ) a0
-= 32768 , carry
= 0;
2325 else a0
+= (1 << 16) - 32768 , carry
= 1;
2327 if (a1
>= 54590 + carry
) a1
-= 54590 + carry
, carry
= 0;
2328 else a1
+= (1 << 16) - 54590 - carry
, carry
= 1;
2330 a2
-= 27111902 + carry
;
2332 /* If a is negative, replace a by (-1-a) */
2333 negative
= (a2
>= ((UINT
)1) << 31);
2336 /* Set a to -a - 1 (a is a2/a1/a0) */
2342 /* Divide a by 10000000 (a = a2/a1/a0), put the rest into r.
2343 Split the divisor into 10000 * 1000 which are both less than 0xffff. */
2344 a1
+= (a2
% 10000) << 16;
2346 a0
+= (a1
% 10000) << 16;
2351 a1
+= (a2
% 1000) << 16;
2353 a0
+= (a1
% 1000) << 16;
2355 r
+= (a0
% 1000) * 10000;
2358 /* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
2361 /* Set a to -a - 1 (a is a2/a1/a0) */
2369 if (remainder
) *remainder
= r
;
2371 /* Do not replace this by << 32, it gives a compiler warning and it does
2373 return ((((time_t)a2
) << 16) << 16) + (a1
<< 16) + a0
;
2378 /***********************************************************************
2379 * MulDiv (KERNEL32.@)
2381 * Result of multiplication and division
2382 * -1: Overflow occurred or Divisor was 0
2389 #if SIZEOF_LONG_LONG >= 8
2392 if (!nDivisor
) return -1;
2394 /* We want to deal with a positive divisor to simplify the logic. */
2397 nMultiplicand
= - nMultiplicand
;
2398 nDivisor
= -nDivisor
;
2401 /* If the result is positive, we "add" to round. else, we subtract to round. */
2402 if ( ( (nMultiplicand
< 0) && (nMultiplier
< 0) ) ||
2403 ( (nMultiplicand
>= 0) && (nMultiplier
>= 0) ) )
2404 ret
= (((long long)nMultiplicand
* nMultiplier
) + (nDivisor
/2)) / nDivisor
;
2406 ret
= (((long long)nMultiplicand
* nMultiplier
) - (nDivisor
/2)) / nDivisor
;
2408 if ((ret
> 2147483647) || (ret
< -2147483647)) return -1;
2411 if (!nDivisor
) return -1;
2413 /* We want to deal with a positive divisor to simplify the logic. */
2416 nMultiplicand
= - nMultiplicand
;
2417 nDivisor
= -nDivisor
;
2420 /* If the result is positive, we "add" to round. else, we subtract to round. */
2421 if ( ( (nMultiplicand
< 0) && (nMultiplier
< 0) ) ||
2422 ( (nMultiplicand
>= 0) && (nMultiplier
>= 0) ) )
2423 return ((nMultiplicand
* nMultiplier
) + (nDivisor
/2)) / nDivisor
;
2425 return ((nMultiplicand
* nMultiplier
) - (nDivisor
/2)) / nDivisor
;
2431 /***********************************************************************
2432 * DosDateTimeToFileTime (KERNEL32.@)
2434 BOOL WINAPI
DosDateTimeToFileTime( WORD fatdate
, WORD fattime
, LPFILETIME ft
)
2439 time_t time1
, time2
;
2442 newtm
.tm_sec
= (fattime
& 0x1f) * 2;
2443 newtm
.tm_min
= (fattime
>> 5) & 0x3f;
2444 newtm
.tm_hour
= (fattime
>> 11);
2445 newtm
.tm_mday
= (fatdate
& 0x1f);
2446 newtm
.tm_mon
= ((fatdate
>> 5) & 0x0f) - 1;
2447 newtm
.tm_year
= (fatdate
>> 9) + 80;
2449 RtlSecondsSince1970ToTime( timegm(&newtm
), (LARGE_INTEGER
*)ft
);
2451 time1
= mktime(&newtm
);
2452 gtm
= gmtime(&time1
);
2453 time2
= mktime(gtm
);
2454 RtlSecondsSince1970ToTime( 2*time1
-time2
, (LARGE_INTEGER
*)ft
);
2460 /***********************************************************************
2461 * FileTimeToDosDateTime (KERNEL32.@)
2463 BOOL WINAPI
FileTimeToDosDateTime( const FILETIME
*ft
, LPWORD fatdate
,
2466 time_t unixtime
= DOSFS_FileTimeToUnixTime( ft
, NULL
);
2467 struct tm
*tm
= gmtime( &unixtime
);
2469 *fattime
= (tm
->tm_hour
<< 11) + (tm
->tm_min
<< 5) + (tm
->tm_sec
/ 2);
2471 *fatdate
= ((tm
->tm_year
- 80) << 9) + ((tm
->tm_mon
+ 1) << 5)
2477 /***********************************************************************
2478 * LocalFileTimeToFileTime (KERNEL32.@)
2480 BOOL WINAPI
LocalFileTimeToFileTime( const FILETIME
*localft
,
2487 /* Converts from local to UTC. */
2488 time_t localtime
= DOSFS_FileTimeToUnixTime( localft
, &remainder
);
2489 xtm
= gmtime( &localtime
);
2490 utctime
= mktime(xtm
);
2491 if(xtm
->tm_isdst
> 0) utctime
-=3600;
2492 DOSFS_UnixTimeToFileTime( utctime
, utcft
, remainder
);
2497 /***********************************************************************
2498 * FileTimeToLocalFileTime (KERNEL32.@)
2500 BOOL WINAPI
FileTimeToLocalFileTime( const FILETIME
*utcft
,
2501 LPFILETIME localft
)
2504 /* Converts from UTC to local. */
2505 time_t unixtime
= DOSFS_FileTimeToUnixTime( utcft
, &remainder
);
2507 struct tm
*xtm
= localtime( &unixtime
);
2510 localtime
= timegm(xtm
);
2511 DOSFS_UnixTimeToFileTime( localtime
, localft
, remainder
);
2517 xtm
= gmtime( &unixtime
);
2519 if(xtm
->tm_isdst
> 0) time
-=3600;
2520 DOSFS_UnixTimeToFileTime( 2*unixtime
-time
, localft
, remainder
);
2526 /***********************************************************************
2527 * FileTimeToSystemTime (KERNEL32.@)
2529 BOOL WINAPI
FileTimeToSystemTime( const FILETIME
*ft
, LPSYSTEMTIME syst
)
2533 time_t xtime
= DOSFS_FileTimeToUnixTime( ft
, &remainder
);
2534 xtm
= gmtime(&xtime
);
2535 syst
->wYear
= xtm
->tm_year
+1900;
2536 syst
->wMonth
= xtm
->tm_mon
+ 1;
2537 syst
->wDayOfWeek
= xtm
->tm_wday
;
2538 syst
->wDay
= xtm
->tm_mday
;
2539 syst
->wHour
= xtm
->tm_hour
;
2540 syst
->wMinute
= xtm
->tm_min
;
2541 syst
->wSecond
= xtm
->tm_sec
;
2542 syst
->wMilliseconds
= remainder
/ 10000;
2546 /***********************************************************************
2547 * QueryDosDeviceA (KERNEL32.@)
2549 * returns array of strings terminated by \0, terminated by \0
2551 DWORD WINAPI
QueryDosDeviceA(LPCSTR devname
,LPSTR target
,DWORD bufsize
)
2556 TRACE("(%s,...)\n", devname
? devname
: "<null>");
2558 /* return known MSDOS devices */
2559 static const char devices
[24] = "CON\0COM1\0COM2\0LPT1\0NUL\0\0";
2560 memcpy( target
, devices
, min(bufsize
,sizeof(devices
)) );
2561 return min(bufsize
,sizeof(devices
));
2563 /* In theory all that are possible and have been defined.
2564 * Now just those below, since mirc uses it to check for special files.
2566 * (It is more complex, and supports netmounted stuff, and \\.\ stuff,
2567 * but currently we just ignore that.)
2569 #define CHECK(x) (strstr(devname,#x)==devname)
2570 if (CHECK(con
) || CHECK(com
) || CHECK(lpt
) || CHECK(nul
)) {
2571 strcpy(buffer
,"\\DEV\\");
2572 strcat(buffer
,devname
);
2573 if ((s
=strchr(buffer
,':'))) *s
='\0';
2574 lstrcpynA(target
,buffer
,bufsize
);
2575 return strlen(buffer
)+1;
2577 if (strchr(devname
,':') || devname
[0]=='\\') {
2578 /* This might be a DOS device we do not handle yet ... */
2579 FIXME("(%s) not detected as DOS device!\n",devname
);
2581 SetLastError(ERROR_DEV_NOT_EXIST
);
2588 /***********************************************************************
2589 * QueryDosDeviceW (KERNEL32.@)
2591 * returns array of strings terminated by \0, terminated by \0
2593 DWORD WINAPI
QueryDosDeviceW(LPCWSTR devname
,LPWSTR target
,DWORD bufsize
)
2595 LPSTR devnameA
= devname
?HEAP_strdupWtoA(GetProcessHeap(),0,devname
):NULL
;
2596 LPSTR targetA
= (LPSTR
)HeapAlloc(GetProcessHeap(),0,bufsize
);
2597 DWORD ret
= QueryDosDeviceA(devnameA
,targetA
,bufsize
);
2599 ret
= MultiByteToWideChar( CP_ACP
, 0, targetA
, ret
, target
, bufsize
);
2600 if (devnameA
) HeapFree(GetProcessHeap(),0,devnameA
);
2601 if (targetA
) HeapFree(GetProcessHeap(),0,targetA
);
2606 /***********************************************************************
2607 * SystemTimeToFileTime (KERNEL32.@)
2609 BOOL WINAPI
SystemTimeToFileTime( const SYSTEMTIME
*syst
, LPFILETIME ft
)
2615 struct tm xtm
,*utc_tm
;
2616 time_t localtim
,utctime
;
2619 xtm
.tm_year
= syst
->wYear
-1900;
2620 xtm
.tm_mon
= syst
->wMonth
- 1;
2621 xtm
.tm_wday
= syst
->wDayOfWeek
;
2622 xtm
.tm_mday
= syst
->wDay
;
2623 xtm
.tm_hour
= syst
->wHour
;
2624 xtm
.tm_min
= syst
->wMinute
;
2625 xtm
.tm_sec
= syst
->wSecond
; /* this is UTC */
2628 utctime
= timegm(&xtm
);
2629 DOSFS_UnixTimeToFileTime( utctime
, ft
,
2630 syst
->wMilliseconds
* 10000 );
2632 localtim
= mktime(&xtm
); /* now we've got local time */
2633 utc_tm
= gmtime(&localtim
);
2634 utctime
= mktime(utc_tm
);
2635 DOSFS_UnixTimeToFileTime( 2*localtim
-utctime
, ft
,
2636 syst
->wMilliseconds
* 10000 );
2641 /***********************************************************************
2642 * DefineDosDeviceA (KERNEL32.@)
2644 BOOL WINAPI
DefineDosDeviceA(DWORD flags
,LPCSTR devname
,LPCSTR targetpath
) {
2645 FIXME("(0x%08lx,%s,%s),stub!\n",flags
,devname
,targetpath
);
2646 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
2651 --- 16 bit functions ---
2654 /*************************************************************************
2655 * FindFirstFile (KERNEL.413)
2657 HANDLE16 WINAPI
FindFirstFile16( LPCSTR path
, WIN32_FIND_DATAA
*data
)
2659 DOS_FULL_NAME full_name
;
2661 FIND_FIRST_INFO
*info
;
2662 WCHAR pathW
[MAX_PATH
];
2667 data
->dwReserved0
= data
->dwReserved1
= 0x0;
2668 if (!path
) return INVALID_HANDLE_VALUE16
;
2669 MultiByteToWideChar(CP_ACP
, 0, path
, -1, pathW
, MAX_PATH
);
2670 if (!DOSFS_GetFullName( pathW
, FALSE
, &full_name
))
2671 return INVALID_HANDLE_VALUE16
;
2672 if (!(handle
= GlobalAlloc16( GMEM_MOVEABLE
, sizeof(FIND_FIRST_INFO
) )))
2673 return INVALID_HANDLE_VALUE16
;
2674 info
= (FIND_FIRST_INFO
*)GlobalLock16( handle
);
2675 info
->path
= HeapAlloc( GetProcessHeap(), 0, strlen(full_name
.long_name
)+1 );
2676 strcpy( info
->path
, full_name
.long_name
);
2678 codepage
= DRIVE_GetCodepage(full_name
.drive
);
2679 p
= strrchr( info
->path
, '/' );
2681 long_mask_len
= MultiByteToWideChar(codepage
, 0, p
, -1, NULL
, 0);
2682 info
->long_mask
= HeapAlloc( GetProcessHeap(), 0, long_mask_len
* sizeof(WCHAR
) );
2683 MultiByteToWideChar(codepage
, 0, p
, -1, info
->long_mask
, long_mask_len
);
2685 info
->short_mask
= NULL
;
2687 info
->drive
= full_name
.drive
;
2690 info
->u
.dos_dir
= DOSFS_OpenDir( codepage
, info
->path
);
2692 GlobalUnlock16( handle
);
2693 if (!FindNextFile16( handle
, data
))
2695 FindClose16( handle
);
2696 SetLastError( ERROR_NO_MORE_FILES
);
2697 return INVALID_HANDLE_VALUE16
;
2702 /*************************************************************************
2703 * FindNextFile (KERNEL.414)
2705 BOOL16 WINAPI
FindNextFile16( HANDLE16 handle
, WIN32_FIND_DATAA
*data
)
2707 FIND_FIRST_INFO
*info
;
2708 WIN32_FIND_DATAW dataW
;
2710 DWORD gle
= ERROR_NO_MORE_FILES
;
2712 if ((handle
== INVALID_HANDLE_VALUE16
) ||
2713 !(info
= (FIND_FIRST_INFO
*)GlobalLock16( handle
)))
2715 SetLastError( ERROR_INVALID_HANDLE
);
2718 if (!info
->path
|| !info
->u
.dos_dir
)
2722 if (!DOSFS_FindNextEx( info
, &dataW
))
2724 DOSFS_CloseDir( info
->u
.dos_dir
); info
->u
.dos_dir
= NULL
;
2725 HeapFree( GetProcessHeap(), 0, info
->path
);
2727 HeapFree( GetProcessHeap(), 0, info
->long_mask
);
2728 info
->long_mask
= NULL
;
2734 data
->dwFileAttributes
= dataW
.dwFileAttributes
;
2735 data
->ftCreationTime
= dataW
.ftCreationTime
;
2736 data
->ftLastAccessTime
= dataW
.ftLastAccessTime
;
2737 data
->ftLastWriteTime
= dataW
.ftLastWriteTime
;
2738 data
->nFileSizeHigh
= dataW
.nFileSizeHigh
;
2739 data
->nFileSizeLow
= dataW
.nFileSizeLow
;
2740 WideCharToMultiByte( CP_ACP
, 0, dataW
.cFileName
, -1,
2741 data
->cFileName
, sizeof(data
->cFileName
), NULL
, NULL
);
2742 WideCharToMultiByte( CP_ACP
, 0, dataW
.cAlternateFileName
, -1,
2743 data
->cAlternateFileName
,
2744 sizeof(data
->cAlternateFileName
), NULL
, NULL
);
2746 if( !ret
) SetLastError( gle
);
2747 GlobalUnlock16( handle
);
2752 /*************************************************************************
2753 * FindClose (KERNEL.415)
2755 BOOL16 WINAPI
FindClose16( HANDLE16 handle
)
2757 FIND_FIRST_INFO
*info
;
2759 if ((handle
== INVALID_HANDLE_VALUE16
) ||
2760 !(info
= (FIND_FIRST_INFO
*)GlobalLock16( handle
)))
2762 SetLastError( ERROR_INVALID_HANDLE
);
2765 if (info
->u
.dos_dir
) DOSFS_CloseDir( info
->u
.dos_dir
);
2766 if (info
->path
) HeapFree( GetProcessHeap(), 0, info
->path
);
2767 if (info
->long_mask
) HeapFree( GetProcessHeap(), 0, info
->long_mask
);
2768 GlobalUnlock16( handle
);
2769 GlobalFree16( handle
);