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 /* Check for "." and ".." */
210 for(i
= 1; i
< 11; i
++) buffer
[i
] = ' ';
217 return (!*p
|| (*p
== '/') || (*p
== '\\'));
220 for (i
= 0; i
< 8; i
++)
237 if (*p
< 256 && strchr( invalid_chars
, (char)*p
)) return FALSE
;
238 buffer
[i
] = toupperW(*p
);
246 /* Skip all chars after wildcard up to first dot */
247 while (*p
&& (*p
!= '/') && (*p
!= '\\') && (*p
!= '.')) p
++;
251 /* Check if name too long */
252 if (*p
&& (*p
!= '/') && (*p
!= '\\') && (*p
!= '.')) return FALSE
;
254 if (*p
== '.') p
++; /* Skip dot */
256 for (i
= 8; i
< 11; i
++)
266 return FALSE
; /* Second extension not allowed */
274 if (*p
< 256 && strchr( invalid_chars
, (char)*p
)) return FALSE
;
275 buffer
[i
] = toupperW(*p
);
282 /* at most 3 character of the extension are processed
283 * is something behind this ?
285 while (*p
== '*' || *p
== ' ') p
++; /* skip wildcards and spaces */
286 return IS_END_OF_NAME(*p
);
290 /***********************************************************************
291 * DOSFS_ToDosDTAFormat
293 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
294 * converting to upper-case in the process.
295 * File name can be terminated by '\0', '\\' or '/'.
296 * 'buffer' must be at least 13 characters long.
298 static void DOSFS_ToDosDTAFormat( LPCWSTR name
, LPWSTR buffer
)
302 memcpy( buffer
, name
, 8 * sizeof(WCHAR
) );
304 while ((p
> buffer
) && (p
[-1] == ' ')) p
--;
306 memcpy( p
, name
+ 8, 3 * sizeof(WCHAR
) );
308 while (p
[-1] == ' ') p
--;
309 if (p
[-1] == '.') p
--;
314 /***********************************************************************
317 * Check a DOS file name against a mask (both in FCB format).
319 static int DOSFS_MatchShort( LPCWSTR mask
, LPCWSTR name
)
322 for (i
= 11; i
> 0; i
--, mask
++, name
++)
323 if ((*mask
!= '?') && (*mask
!= *name
)) return 0;
328 /***********************************************************************
331 * Check a long file name against a mask.
333 * Tests (done in W95 DOS shell - case insensitive):
334 * *.txt test1.test.txt *
336 * *.t??????.t* test1.ta.tornado.txt *
337 * *tornado* test1.ta.tornado.txt *
338 * t*t test1.ta.tornado.txt *
340 * ?est??? test1.txt -
341 * *test1.txt* test1.txt *
342 * h?l?o*t.dat hellothisisatest.dat *
344 static int DOSFS_MatchLong( LPCWSTR mask
, LPCWSTR name
, int case_sensitive
)
346 LPCWSTR lastjoker
= NULL
;
347 LPCWSTR next_to_retry
= NULL
;
348 static const WCHAR asterisk_dot_asterisk
[] = {'*','.','*',0};
350 TRACE("(%s, %s, %x)\n", debugstr_w(mask
), debugstr_w(name
), case_sensitive
);
352 if (!strcmpW( mask
, asterisk_dot_asterisk
)) return 1;
353 while (*name
&& *mask
)
358 while (*mask
== '*') mask
++; /* Skip consecutive '*' */
360 if (!*mask
) return 1; /* end of mask is all '*', so match */
362 /* skip to the next match after the joker(s) */
363 if (case_sensitive
) while (*name
&& (*name
!= *mask
)) name
++;
364 else while (*name
&& (toupperW(*name
) != toupperW(*mask
))) name
++;
367 next_to_retry
= name
;
369 else if (*mask
!= '?')
374 if (*mask
!= *name
) mismatch
= 1;
378 if (toupperW(*mask
) != toupperW(*name
)) mismatch
= 1;
392 else /* mismatch ! */
394 if (lastjoker
) /* we had an '*', so we can try unlimitedly */
398 /* this scan sequence was a mismatch, so restart
399 * 1 char after the first char we checked last time */
401 name
= next_to_retry
;
404 return 0; /* bad luck */
413 while ((*mask
== '.') || (*mask
== '*'))
414 mask
++; /* Ignore trailing '.' or '*' in mask */
415 return (!*name
&& !*mask
);
419 /***********************************************************************
422 * Used to construct an array of filenames in DOSFS_OpenDir
424 static BOOL
DOSFS_AddDirEntry(DOS_DIR
**dir
, LPCWSTR name
, LPCWSTR dosname
)
426 int extra1
= strlenW(name
) + 1;
427 int extra2
= strlenW(dosname
) + 1;
429 /* if we need more, at minimum double the size */
430 if( (extra1
+ extra2
+ (*dir
)->used
) > (*dir
)->size
)
432 int more
= (*dir
)->size
;
435 if(more
<(extra1
+extra2
))
436 more
= extra1
+extra2
;
438 t
= HeapReAlloc(GetProcessHeap(), 0, *dir
, sizeof(**dir
) +
439 ((*dir
)->size
+ more
)*sizeof(WCHAR
) );
442 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
443 ERR("Out of memory caching directory structure %d %d %d\n",
444 (*dir
)->size
, more
, (*dir
)->used
);
448 (*dir
)->size
+= more
;
451 /* at this point, the dir structure is big enough to hold these names */
452 strcpyW(&(*dir
)->names
[(*dir
)->used
], name
);
453 (*dir
)->used
+= extra1
;
454 strcpyW(&(*dir
)->names
[(*dir
)->used
], dosname
);
455 (*dir
)->used
+= extra2
;
461 /***********************************************************************
464 static BOOL
DOSFS_OpenDir_VFAT(UINT codepage
, DOS_DIR
**dir
, const char *unix_path
)
466 #ifdef VFAT_IOCTL_READDIR_BOTH
468 int fd
= open( unix_path
, O_RDONLY
|O_DIRECTORY
);
471 /* Check if the VFAT ioctl is supported on this directory */
478 WCHAR long_name
[MAX_PATH
];
479 WCHAR short_name
[12];
481 r
= (ioctl( fd
, VFAT_IOCTL_READDIR_BOTH
, (long)de
) != -1);
486 MultiByteToWideChar(codepage
, 0, de
[0].d_name
, -1, long_name
, MAX_PATH
);
487 if (!DOSFS_ToDosFCBFormat( long_name
, short_name
))
488 short_name
[0] = '\0';
490 MultiByteToWideChar(codepage
, 0, de
[1].d_name
, -1, long_name
, MAX_PATH
);
492 MultiByteToWideChar(codepage
, 0, de
[0].d_name
, -1, long_name
, MAX_PATH
);
493 r
= DOSFS_AddDirEntry(dir
, long_name
, short_name
);
499 static const WCHAR empty_strW
[] = { 0 };
500 DOSFS_AddDirEntry(dir
, empty_strW
, empty_strW
);
506 #endif /* VFAT_IOCTL_READDIR_BOTH */
510 /***********************************************************************
511 * DOSFS_OpenDir_Normal
513 * Now use the standard opendir/readdir interface
515 static BOOL
DOSFS_OpenDir_Normal( UINT codepage
, DOS_DIR
**dir
, const char *unix_path
)
517 DIR *unixdir
= opendir( unix_path
);
519 static const WCHAR empty_strW
[] = { 0 };
525 WCHAR long_name
[MAX_PATH
];
526 struct dirent
*de
= readdir(unixdir
);
530 MultiByteToWideChar(codepage
, 0, de
->d_name
, -1, long_name
, MAX_PATH
);
531 r
= DOSFS_AddDirEntry(dir
, long_name
, empty_strW
);
536 DOSFS_AddDirEntry(dir
, empty_strW
, empty_strW
);
541 /***********************************************************************
544 static DOS_DIR
*DOSFS_OpenDir( UINT codepage
, const char *unix_path
)
546 const int init_size
= 0x100;
547 DOS_DIR
*dir
= HeapAlloc( GetProcessHeap(), 0, sizeof(*dir
) + init_size
*sizeof (WCHAR
));
550 TRACE("%s\n",debugstr_a(unix_path
));
554 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
558 dir
->size
= init_size
;
560 /* Treat empty path as root directory. This simplifies path split into
561 directory and mask in several other places */
562 if (!*unix_path
) unix_path
= "/";
564 r
= DOSFS_OpenDir_VFAT( codepage
, &dir
, unix_path
);
567 r
= DOSFS_OpenDir_Normal( codepage
, &dir
, unix_path
);
571 HeapFree(GetProcessHeap(), 0, dir
);
580 /***********************************************************************
583 static void DOSFS_CloseDir( DOS_DIR
*dir
)
585 HeapFree( GetProcessHeap(), 0, dir
);
589 /***********************************************************************
592 static BOOL
DOSFS_ReadDir( DOS_DIR
*dir
, LPCWSTR
*long_name
,
593 LPCWSTR
*short_name
)
600 /* the long pathname is first */
601 ln
= &dir
->names
[dir
->used
];
606 dir
->used
+= (strlenW(ln
) + 1);
608 /* followed by the short path name */
609 sn
= &dir
->names
[dir
->used
];
614 dir
->used
+= (strlenW(sn
) + 1);
620 /***********************************************************************
623 * Transform a Unix file name into a hashed DOS name. If the name is a valid
624 * DOS name, it is converted to upper-case; otherwise it is replaced by a
625 * hashed version that fits in 8.3 format.
626 * File name can be terminated by '\0', '\\' or '/'.
627 * 'buffer' must be at least 13 characters long.
629 static void DOSFS_Hash( LPCWSTR name
, LPWSTR buffer
, BOOL dir_format
,
632 static const char invalid_chars
[] = INVALID_DOS_CHARS
"~.";
633 static const char hash_chars
[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
642 for(i
= 0; i
< 11; i
++) buffer
[i
] = ' ';
646 if (DOSFS_ValidDOSName( name
, ignore_case
))
648 /* Check for '.' and '..' */
652 if (!dir_format
) buffer
[1] = buffer
[2] = '\0';
653 if (name
[1] == '.') buffer
[1] = '.';
657 /* Simply copy the name, converting to uppercase */
659 for (dst
= buffer
; !IS_END_OF_NAME(*name
) && (*name
!= '.'); name
++)
660 *dst
++ = toupperW(*name
);
663 if (dir_format
) dst
= buffer
+ 8;
665 for (name
++; !IS_END_OF_NAME(*name
); name
++)
666 *dst
++ = toupperW(*name
);
668 if (!dir_format
) *dst
= '\0';
672 /* Compute the hash code of the file name */
673 /* If you know something about hash functions, feel free to */
674 /* insert a better algorithm here... */
677 for (p
= name
, hash
= 0xbeef; !IS_END_OF_NAME(p
[1]); p
++)
678 hash
= (hash
<<3) ^ (hash
>>5) ^ tolowerW(*p
) ^ (tolowerW(p
[1]) << 8);
679 hash
= (hash
<<3) ^ (hash
>>5) ^ tolowerW(*p
); /* Last character */
683 for (p
= name
, hash
= 0xbeef; !IS_END_OF_NAME(p
[1]); p
++)
684 hash
= (hash
<< 3) ^ (hash
>> 5) ^ *p
^ (p
[1] << 8);
685 hash
= (hash
<< 3) ^ (hash
>> 5) ^ *p
; /* Last character */
688 /* Find last dot for start of the extension */
689 for (p
= name
+1, ext
= NULL
; !IS_END_OF_NAME(*p
); p
++)
690 if (*p
== '.') ext
= p
;
691 if (ext
&& IS_END_OF_NAME(ext
[1]))
692 ext
= NULL
; /* Empty extension ignored */
694 /* Copy first 4 chars, replacing invalid chars with '_' */
695 for (i
= 4, p
= name
, dst
= buffer
; i
> 0; i
--, p
++)
697 if (IS_END_OF_NAME(*p
) || (p
== ext
)) break;
698 *dst
++ = (*p
< 256 && strchr( invalid_chars
, (char)*p
)) ? '_' : toupperW(*p
);
700 /* Pad to 5 chars with '~' */
701 while (i
-- >= 0) *dst
++ = '~';
703 /* Insert hash code converted to 3 ASCII chars */
704 *dst
++ = hash_chars
[(hash
>> 10) & 0x1f];
705 *dst
++ = hash_chars
[(hash
>> 5) & 0x1f];
706 *dst
++ = hash_chars
[hash
& 0x1f];
708 /* Copy the first 3 chars of the extension (if any) */
711 if (!dir_format
) *dst
++ = '.';
712 for (i
= 3, ext
++; (i
> 0) && !IS_END_OF_NAME(*ext
); i
--, ext
++)
713 *dst
++ = (*ext
< 256 && strchr( invalid_chars
, (char)*ext
)) ? '_' : toupperW(*ext
);
715 if (!dir_format
) *dst
= '\0';
719 /***********************************************************************
722 * Find the Unix file name in a given directory that corresponds to
723 * a file name (either in Unix or DOS format).
724 * File name can be terminated by '\0', '\\' or '/'.
725 * Return TRUE if OK, FALSE if no file name matches.
727 * 'long_buf' must be at least 'long_len' characters long. If the long name
728 * turns out to be larger than that, the function returns FALSE.
729 * 'short_buf' must be at least 13 characters long.
731 BOOL
DOSFS_FindUnixName( const DOS_FULL_NAME
*path
, LPCWSTR name
, char *long_buf
,
732 INT long_len
, LPWSTR short_buf
, BOOL ignore_case
)
735 LPCWSTR long_name
, short_name
;
736 WCHAR dos_name
[12], tmp_buf
[13];
739 LPCWSTR p
= strchrW( name
, '/' );
740 int len
= p
? (int)(p
- name
) : strlenW(name
);
741 if ((p
= strchrW( name
, '\\' ))) len
= min( (int)(p
- name
), len
);
742 /* Ignore trailing dots and spaces */
743 while (len
> 1 && (name
[len
-1] == '.' || name
[len
-1] == ' ')) len
--;
744 if (long_len
< len
+ 1) return FALSE
;
746 TRACE("%s,%s\n", path
->long_name
, debugstr_w(name
) );
748 if (!DOSFS_ToDosFCBFormat( name
, dos_name
)) dos_name
[0] = '\0';
750 if (!(dir
= DOSFS_OpenDir( DRIVE_GetCodepage(path
->drive
), path
->long_name
)))
752 WARN("(%s,%s): can't open dir: %s\n",
753 path
->long_name
, debugstr_w(name
), strerror(errno
) );
757 while ((ret
= DOSFS_ReadDir( dir
, &long_name
, &short_name
)))
759 /* Check against Unix name */
760 if (len
== strlenW(long_name
))
764 if (!strncmpW( long_name
, name
, len
)) break;
768 if (!strncmpiW( long_name
, name
, len
)) break;
773 /* Check against hashed DOS name */
776 DOSFS_Hash( long_name
, tmp_buf
, TRUE
, ignore_case
);
777 short_name
= tmp_buf
;
779 if (!strcmpW( dos_name
, short_name
)) break;
784 if (long_buf
) WideCharToMultiByte(DRIVE_GetCodepage(path
->drive
), 0,
785 long_name
, -1, long_buf
, long_len
, NULL
, NULL
);
789 DOSFS_ToDosDTAFormat( short_name
, short_buf
);
791 DOSFS_Hash( long_name
, short_buf
, FALSE
, ignore_case
);
793 TRACE("(%s,%s) -> %s (%s)\n", path
->long_name
, debugstr_w(name
),
794 debugstr_w(long_name
), short_buf
? debugstr_w(short_buf
) : "***");
797 WARN("%s not found in '%s'\n", debugstr_w(name
), path
->long_name
);
798 DOSFS_CloseDir( dir
);
803 /***********************************************************************
806 * Check if a DOS file name represents a DOS device and return the device.
808 const DOS_DEVICE
*DOSFS_GetDevice( LPCWSTR name
)
813 if (!name
) return NULL
; /* if wine_server_handle_to_fd was used */
814 if (name
[0] && (name
[1] == ':')) name
+= 2;
815 if ((p
= strrchrW( name
, '/' ))) name
= p
+ 1;
816 if ((p
= strrchrW( name
, '\\' ))) name
= p
+ 1;
817 for (i
= 0; i
< sizeof(DOSFS_Devices
)/sizeof(DOSFS_Devices
[0]); i
++)
819 const WCHAR
*dev
= DOSFS_Devices
[i
].name
;
820 if (!strncmpiW( dev
, name
, strlenW(dev
) ))
822 p
= name
+ strlenW( dev
);
823 if (!*p
|| (*p
== '.') || (*p
== ':')) return &DOSFS_Devices
[i
];
830 /***********************************************************************
831 * DOSFS_GetDeviceByHandle
833 const DOS_DEVICE
*DOSFS_GetDeviceByHandle( HANDLE hFile
)
835 const DOS_DEVICE
*ret
= NULL
;
836 SERVER_START_REQ( get_device_id
)
839 if (!wine_server_call( req
))
841 if ((reply
->id
>= 0) &&
842 (reply
->id
< sizeof(DOSFS_Devices
)/sizeof(DOSFS_Devices
[0])))
843 ret
= &DOSFS_Devices
[reply
->id
];
851 /**************************************************************************
852 * DOSFS_CreateCommPort
854 static HANDLE
DOSFS_CreateCommPort(LPCWSTR name
, DWORD access
, DWORD attributes
, LPSECURITY_ATTRIBUTES sa
)
859 static const WCHAR serialportsW
[] = {'s','e','r','i','a','l','p','o','r','t','s',0};
860 static const WCHAR empty_strW
[] = { 0 };
862 TRACE_(file
)("%s %lx %lx\n", debugstr_w(name
), access
, attributes
);
864 PROFILE_GetWineIniString(serialportsW
, name
, empty_strW
, devnameW
, 40);
868 WideCharToMultiByte(CP_ACP
, 0, devnameW
, -1, devname
, sizeof(devname
), NULL
, NULL
);
870 TRACE("opening %s as %s\n", devname
, debugstr_w(name
));
872 SERVER_START_REQ( create_serial
)
874 req
->access
= access
;
875 req
->inherit
= (sa
&& (sa
->nLength
>=sizeof(*sa
)) && sa
->bInheritHandle
);
876 req
->attributes
= attributes
;
877 req
->sharing
= FILE_SHARE_READ
|FILE_SHARE_WRITE
;
878 wine_server_add_data( req
, devname
, strlen(devname
) );
880 wine_server_call_err( req
);
886 ERR("Couldn't open device '%s' ! (check permissions)\n",devname
);
888 TRACE("return %p\n", ret
);
892 /***********************************************************************
895 * Open a DOS device. This might not map 1:1 into the UNIX device concept.
896 * Returns 0 on failure.
898 HANDLE
DOSFS_OpenDevice( LPCWSTR name
, DWORD access
, DWORD attributes
, LPSECURITY_ATTRIBUTES sa
)
904 if (name
[0] && (name
[1] == ':')) name
+= 2;
905 if ((p
= strrchrW( name
, '/' ))) name
= p
+ 1;
906 if ((p
= strrchrW( name
, '\\' ))) name
= p
+ 1;
907 for (i
= 0; i
< sizeof(DOSFS_Devices
)/sizeof(DOSFS_Devices
[0]); i
++)
909 const WCHAR
*dev
= DOSFS_Devices
[i
].name
;
910 if (!strncmpiW( dev
, name
, strlenW(dev
) ))
912 p
= name
+ strlenW( dev
);
913 if (!*p
|| (*p
== '.') || (*p
== ':')) {
914 static const WCHAR nulW
[] = {'N','U','L',0};
915 static const WCHAR conW
[] = {'C','O','N',0};
916 static const WCHAR scsimgrW
[] = {'S','C','S','I','M','G','R','$',0};
917 static const WCHAR hpscanW
[] = {'H','P','S','C','A','N',0};
918 static const WCHAR emmxxxx0W
[] = {'E','M','M','X','X','X','X','0',0};
920 if (!strcmpiW(DOSFS_Devices
[i
].name
, nulW
))
921 return FILE_CreateFile( "/dev/null", access
,
922 FILE_SHARE_READ
|FILE_SHARE_WRITE
, sa
,
923 OPEN_EXISTING
, 0, 0, TRUE
, DRIVE_UNKNOWN
);
924 if (!strcmpiW(DOSFS_Devices
[i
].name
, conW
)) {
926 switch (access
& (GENERIC_READ
|GENERIC_WRITE
)) {
928 to_dup
= GetStdHandle( STD_INPUT_HANDLE
);
931 to_dup
= GetStdHandle( STD_OUTPUT_HANDLE
);
934 FIXME("can't open CON read/write\n");
937 if (!DuplicateHandle( GetCurrentProcess(), to_dup
, GetCurrentProcess(),
939 sa
&& (sa
->nLength
>=sizeof(*sa
)) && sa
->bInheritHandle
,
940 DUPLICATE_SAME_ACCESS
))
944 if (!strcmpiW(DOSFS_Devices
[i
].name
, scsimgrW
) ||
945 !strcmpiW(DOSFS_Devices
[i
].name
, hpscanW
) ||
946 !strcmpiW(DOSFS_Devices
[i
].name
, emmxxxx0W
))
948 return FILE_CreateDevice( i
, access
, sa
);
951 if( (handle
=DOSFS_CreateCommPort(DOSFS_Devices
[i
].name
,access
,attributes
,sa
)) )
953 FIXME("device open %s not supported (yet)\n", debugstr_w(DOSFS_Devices
[i
].name
));
962 /***********************************************************************
965 * Get the drive specified by a given path name (DOS or Unix format).
967 static int DOSFS_GetPathDrive( LPCWSTR
*name
)
972 if (*p
&& (p
[1] == ':'))
974 drive
= toupperW(*p
) - 'A';
977 else if (*p
== '/') /* Absolute Unix path? */
979 if ((drive
= DRIVE_FindDriveRootW( name
)) == -1)
981 MESSAGE("Warning: %s not accessible from a configured DOS drive\n", debugstr_w(*name
) );
982 /* Assume it really was a DOS name */
983 drive
= DRIVE_GetCurrentDrive();
986 else drive
= DRIVE_GetCurrentDrive();
988 if (!DRIVE_IsValid(drive
))
990 SetLastError( ERROR_INVALID_DRIVE
);
997 /***********************************************************************
1000 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
1001 * Unix name / short DOS name pair.
1002 * Return FALSE if one of the path components does not exist. The last path
1003 * component is only checked if 'check_last' is non-zero.
1004 * The buffers pointed to by 'long_buf' and 'short_buf' must be
1005 * at least MAX_PATHNAME_LEN long.
1007 BOOL
DOSFS_GetFullName( LPCWSTR name
, BOOL check_last
, DOS_FULL_NAME
*full
)
1010 UINT flags
, codepage
;
1013 static const WCHAR driveA_rootW
[] = {'A',':','\\',0};
1014 static const WCHAR dos_rootW
[] = {'\\',0};
1016 TRACE("%s (last=%d)\n", debugstr_w(name
), check_last
);
1018 if ((!*name
) || (*name
=='\n'))
1019 { /* error code for Win98 */
1020 SetLastError(ERROR_BAD_PATHNAME
);
1024 if ((full
->drive
= DOSFS_GetPathDrive( &name
)) == -1) return FALSE
;
1025 flags
= DRIVE_GetFlags( full
->drive
);
1026 codepage
= DRIVE_GetCodepage(full
->drive
);
1028 lstrcpynA( full
->long_name
, DRIVE_GetRoot( full
->drive
),
1029 sizeof(full
->long_name
) );
1030 if (full
->long_name
[1]) root
= full
->long_name
+ strlen(full
->long_name
);
1031 else root
= full
->long_name
; /* root directory */
1033 strcpyW( full
->short_name
, driveA_rootW
);
1034 full
->short_name
[0] += full
->drive
;
1036 if ((*name
== '\\') || (*name
== '/')) /* Absolute path */
1038 while ((*name
== '\\') || (*name
== '/')) name
++;
1040 else /* Relative path */
1042 lstrcpynA( root
+ 1, DRIVE_GetUnixCwd( full
->drive
),
1043 sizeof(full
->long_name
) - (root
- full
->long_name
) - 1 );
1044 if (root
[1]) *root
= '/';
1045 lstrcpynW( full
->short_name
+ 3, DRIVE_GetDosCwd( full
->drive
),
1046 sizeof(full
->short_name
)/sizeof(full
->short_name
[0]) - 3 );
1049 p_l
= full
->long_name
[1] ? full
->long_name
+ strlen(full
->long_name
)
1051 p_s
= full
->short_name
[3] ? full
->short_name
+ strlenW(full
->short_name
)
1052 : full
->short_name
+ 2;
1055 while (*name
&& found
)
1057 /* Check for '.' and '..' */
1061 if (IS_END_OF_NAME(name
[1]))
1064 while ((*name
== '\\') || (*name
== '/')) name
++;
1067 else if ((name
[1] == '.') && IS_END_OF_NAME(name
[2]))
1070 while ((*name
== '\\') || (*name
== '/')) name
++;
1071 while ((p_l
> root
) && (*p_l
!= '/')) p_l
--;
1072 while ((p_s
> full
->short_name
+ 2) && (*p_s
!= '\\')) p_s
--;
1073 *p_l
= *p_s
= '\0'; /* Remove trailing separator */
1078 /* Make sure buffers are large enough */
1080 if ((p_s
>= full
->short_name
+ sizeof(full
->short_name
)/sizeof(full
->short_name
[0]) - 14) ||
1081 (p_l
>= full
->long_name
+ sizeof(full
->long_name
) - 1))
1083 SetLastError( ERROR_PATH_NOT_FOUND
);
1087 /* Get the long and short name matching the file name */
1089 if ((found
= DOSFS_FindUnixName( full
, name
, p_l
+ 1,
1090 sizeof(full
->long_name
) - (p_l
- full
->long_name
) - 1,
1091 p_s
+ 1, !(flags
& DRIVE_CASE_SENSITIVE
) )))
1096 p_s
+= strlenW(p_s
);
1097 while (!IS_END_OF_NAME(*name
)) name
++;
1099 else if (!check_last
)
1103 while (!IS_END_OF_NAME(*name
) &&
1104 (p_s
< full
->short_name
+ sizeof(full
->short_name
)/sizeof(full
->short_name
[0]) - 1) &&
1105 (p_l
< full
->long_name
+ sizeof(full
->long_name
) - 1))
1108 *p_s
++ = tolowerW(*name
);
1109 /* If the drive is case-sensitive we want to create new */
1110 /* files in lower-case otherwise we can't reopen them */
1111 /* under the same short name. */
1112 if (flags
& DRIVE_CASE_SENSITIVE
) wch
= tolowerW(*name
);
1114 p_l
+= WideCharToMultiByte(codepage
, 0, &wch
, 1, p_l
, 2, NULL
, NULL
);
1117 /* Ignore trailing dots and spaces */
1118 while(p_l
[-1] == '.' || p_l
[-1] == ' ') {
1125 while ((*name
== '\\') || (*name
== '/')) name
++;
1132 SetLastError( ERROR_FILE_NOT_FOUND
);
1135 if (*name
) /* Not last */
1137 SetLastError( ERROR_PATH_NOT_FOUND
);
1141 if (!full
->long_name
[0]) strcpy( full
->long_name
, "/" );
1142 if (!full
->short_name
[2]) strcpyW( full
->short_name
+ 2, dos_rootW
);
1143 TRACE("returning %s = %s\n", full
->long_name
, debugstr_w(full
->short_name
) );
1148 /***********************************************************************
1149 * GetShortPathNameW (KERNEL32.@)
1153 * longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1154 * longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0
1156 * more observations ( with NT 3.51 (WinDD) ):
1157 * longpath <= 8.3 -> just copy longpath to shortpath
1159 * a) file does not exist -> return 0, LastError = ERROR_FILE_NOT_FOUND
1160 * b) file does exist -> set the short filename.
1161 * - trailing slashes are reproduced in the short name, even if the
1162 * file is not a directory
1163 * - the absolute/relative path of the short name is reproduced like found
1165 * - longpath and shortpath may have the same address
1166 * Peter Ganten, 1999
1168 DWORD WINAPI
GetShortPathNameW( LPCWSTR longpath
, LPWSTR shortpath
, DWORD shortlen
)
1170 DOS_FULL_NAME full_name
;
1171 WCHAR tmpshortpath
[MAX_PATHNAME_LEN
];
1173 DWORD sp
= 0, lp
= 0;
1177 BOOL unixabsolute
= *longpath
== '/';
1179 TRACE("%s\n", debugstr_w(longpath
));
1182 SetLastError(ERROR_INVALID_PARAMETER
);
1186 SetLastError(ERROR_BAD_PATHNAME
);
1190 /* check for drive letter */
1191 if (!unixabsolute
&& longpath
[1] == ':' ) {
1192 tmpshortpath
[0] = longpath
[0];
1193 tmpshortpath
[1] = ':';
1197 if ( ( drive
= DOSFS_GetPathDrive ( &longpath
)) == -1 ) return 0;
1198 flags
= DRIVE_GetFlags ( drive
);
1200 if (unixabsolute
&& drive
!= DRIVE_GetCurrentDrive()) {
1201 tmpshortpath
[0] = drive
+ 'A';
1202 tmpshortpath
[1] = ':';
1206 while ( longpath
[lp
] ) {
1208 /* check for path delimiters and reproduce them */
1209 if ( longpath
[lp
] == '\\' || longpath
[lp
] == '/' ) {
1210 if (!sp
|| tmpshortpath
[sp
-1]!= '\\')
1212 /* strip double "\\" */
1213 tmpshortpath
[sp
] = '\\';
1216 tmpshortpath
[sp
]=0;/*terminate string*/
1222 for(p
= longpath
+ lp
; *p
&& *p
!= '/' && *p
!= '\\'; p
++)
1224 lstrcpynW(tmpshortpath
+ sp
, longpath
+ lp
, tmplen
+ 1);
1226 /* Check, if the current element is a valid dos name */
1227 if ( DOSFS_ValidDOSName ( longpath
+ lp
, !(flags
& DRIVE_CASE_SENSITIVE
) ) ) {
1233 /* Check if the file exists and use the existing file name */
1234 if ( DOSFS_GetFullName ( tmpshortpath
, TRUE
, &full_name
) ) {
1235 strcpyW(tmpshortpath
+ sp
, strrchrW(full_name
.short_name
, '\\') + 1);
1236 sp
+= strlenW(tmpshortpath
+ sp
);
1241 TRACE("not found!\n" );
1242 SetLastError ( ERROR_FILE_NOT_FOUND
);
1245 tmpshortpath
[sp
] = 0;
1247 tmplen
= strlenW(tmpshortpath
) + 1;
1248 if (tmplen
<= shortlen
)
1250 strcpyW(shortpath
, tmpshortpath
);
1251 TRACE("returning %s\n", debugstr_w(shortpath
));
1252 tmplen
--; /* length without 0 */
1259 /***********************************************************************
1260 * GetShortPathNameA (KERNEL32.@)
1262 DWORD WINAPI
GetShortPathNameA( LPCSTR longpath
, LPSTR shortpath
, DWORD shortlen
)
1264 UNICODE_STRING longpathW
;
1265 WCHAR shortpathW
[MAX_PATH
];
1270 SetLastError(ERROR_INVALID_PARAMETER
);
1274 TRACE("%s\n", debugstr_a(longpath
));
1276 if (!RtlCreateUnicodeStringFromAsciiz(&longpathW
, longpath
))
1278 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1282 retW
= GetShortPathNameW(longpathW
.Buffer
, shortpathW
, MAX_PATH
);
1286 else if (retW
> MAX_PATH
)
1288 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
1293 ret
= WideCharToMultiByte(CP_ACP
, 0, shortpathW
, -1, NULL
, 0, NULL
, NULL
);
1294 if (ret
<= shortlen
)
1296 WideCharToMultiByte(CP_ACP
, 0, shortpathW
, -1, shortpath
, shortlen
, NULL
, NULL
);
1297 ret
--; /* length without 0 */
1301 RtlFreeUnicodeString(&longpathW
);
1306 /***********************************************************************
1307 * GetLongPathNameW (KERNEL32.@)
1310 * observed (Win2000):
1311 * shortpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1312 * shortpath="": LastError=ERROR_PATH_NOT_FOUND, ret=0
1314 DWORD WINAPI
GetLongPathNameW( LPCWSTR shortpath
, LPWSTR longpath
, DWORD longlen
)
1316 DOS_FULL_NAME full_name
;
1324 SetLastError(ERROR_INVALID_PARAMETER
);
1327 if (!shortpath
[0]) {
1328 SetLastError(ERROR_PATH_NOT_FOUND
);
1332 TRACE("%s,%p,%ld\n", debugstr_w(shortpath
), longpath
, longlen
);
1334 if(shortpath
[0]=='\\' && shortpath
[1]=='\\')
1336 ERR("UNC pathname %s\n",debugstr_w(shortpath
));
1337 lstrcpynW( longpath
, full_name
.short_name
, longlen
);
1338 return strlenW(longpath
);
1341 if (!DOSFS_GetFullName( shortpath
, TRUE
, &full_name
)) return 0;
1343 root
= full_name
.long_name
;
1344 drive
= DRIVE_FindDriveRoot(&root
);
1345 codepage
= DRIVE_GetCodepage(drive
);
1347 ret
= MultiByteToWideChar(codepage
, 0, root
, -1, NULL
, 0);
1349 /* reproduce terminating slash */
1350 if (ret
> 4) /* if not drive root */
1352 len
= strlenW(shortpath
);
1353 if (shortpath
[len
- 1] == '\\' || shortpath
[len
- 1] == '/')
1359 longpath
[0] = 'A' + drive
;
1361 MultiByteToWideChar(codepage
, 0, root
, -1, longpath
+ 2, longlen
- 2);
1362 for (p
= longpath
; *p
; p
++) if (*p
== '/') *p
= '\\';
1365 longpath
[ret
- 2] = '\\';
1366 longpath
[ret
- 1] = 0;
1368 TRACE("returning %s\n", debugstr_w(longpath
));
1369 ret
--; /* length without 0 */
1375 /***********************************************************************
1376 * GetLongPathNameA (KERNEL32.@)
1378 DWORD WINAPI
GetLongPathNameA( LPCSTR shortpath
, LPSTR longpath
, DWORD longlen
)
1380 UNICODE_STRING shortpathW
;
1381 WCHAR longpathW
[MAX_PATH
];
1386 SetLastError(ERROR_INVALID_PARAMETER
);
1390 TRACE("%s\n", debugstr_a(shortpath
));
1392 if (!RtlCreateUnicodeStringFromAsciiz(&shortpathW
, shortpath
))
1394 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1398 retW
= GetLongPathNameW(shortpathW
.Buffer
, longpathW
, MAX_PATH
);
1402 else if (retW
> MAX_PATH
)
1404 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
1409 ret
= WideCharToMultiByte(CP_ACP
, 0, longpathW
, -1, NULL
, 0, NULL
, NULL
);
1412 WideCharToMultiByte(CP_ACP
, 0, longpathW
, -1, longpath
, longlen
, NULL
, NULL
);
1413 ret
--; /* length without 0 */
1417 RtlFreeUnicodeString(&shortpathW
);
1422 /***********************************************************************
1423 * DOSFS_DoGetFullPathName
1425 * Implementation of GetFullPathNameA/W.
1427 * bon@elektron 000331:
1428 * A test for GetFullPathName with many pathological cases
1429 * now gives identical output for Wine and OSR2
1431 static DWORD
DOSFS_DoGetFullPathName( LPCWSTR name
, DWORD len
, LPWSTR result
)
1434 DOS_FULL_NAME full_name
;
1438 WCHAR drivecur
[] = {'C',':','.',0};
1439 WCHAR driveletter
=0;
1440 int namelen
,drive
=0;
1441 static const WCHAR bkslashW
[] = {'\\',0};
1442 static const WCHAR dotW
[] = {'.',0};
1443 static const WCHAR updir_slashW
[] = {'\\','.','.','\\',0};
1444 static const WCHAR curdirW
[] = {'\\','.','\\',0};
1445 static const WCHAR updirW
[] = {'\\','.','.',0};
1449 SetLastError(ERROR_BAD_PATHNAME
);
1453 TRACE("passed %s\n", debugstr_w(name
));
1456 /*drive letter given */
1458 driveletter
= name
[0];
1460 if ((name
[1]==':') && ((name
[2]=='\\') || (name
[2]=='/')))
1461 /*absolute path given */
1463 strncpyW(full_name
.short_name
, name
, MAX_PATHNAME_LEN
);
1464 full_name
.short_name
[MAX_PATHNAME_LEN
- 1] = 0; /* ensure 0 termination */
1465 drive
= toupperW(name
[0]) - 'A';
1470 drivecur
[0]=driveletter
;
1471 else if ((name
[0]=='\\') || (name
[0]=='/'))
1472 strcpyW(drivecur
, bkslashW
);
1474 strcpyW(drivecur
, dotW
);
1476 if (!DOSFS_GetFullName( drivecur
, FALSE
, &full_name
))
1478 FIXME("internal: error getting drive/path\n");
1481 /* find path that drive letter substitutes*/
1482 drive
= toupperW(full_name
.short_name
[0]) - 'A';
1483 root
= DRIVE_GetRoot(drive
);
1486 FIXME("internal: error getting DOS Drive Root\n");
1489 if (!strcmp(root
,"/"))
1491 /* we have just the last / and we need it. */
1492 p_l
= full_name
.long_name
;
1496 p_l
= full_name
.long_name
+ strlen(root
);
1498 /* append long name (= unix name) to drive */
1499 MultiByteToWideChar(DRIVE_GetCodepage(drive
), 0, p_l
, -1,
1500 full_name
.short_name
+ 2, MAX_PATHNAME_LEN
- 3);
1501 /* append name to treat */
1502 namelen
= strlenW(full_name
.short_name
);
1505 p
+= 2; /* skip drive name when appending */
1506 if (namelen
+ 2 + strlenW(p
) > MAX_PATHNAME_LEN
)
1508 FIXME("internal error: buffer too small\n");
1511 full_name
.short_name
[namelen
++] ='\\';
1512 full_name
.short_name
[namelen
] = 0;
1513 strncpyW(full_name
.short_name
+ namelen
, p
, MAX_PATHNAME_LEN
- namelen
);
1514 full_name
.short_name
[MAX_PATHNAME_LEN
- 1] = 0; /* ensure 0 termination */
1516 /* reverse all slashes */
1517 for (p
=full_name
.short_name
;
1518 p
< full_name
.short_name
+ strlenW(full_name
.short_name
);
1524 /* Use memmove, as areas overlap */
1526 while ((p
= strstrW(full_name
.short_name
, updir_slashW
)))
1528 if (p
> full_name
.short_name
+2)
1531 q
= strrchrW(full_name
.short_name
, '\\');
1532 memmove(q
+1, p
+4, (strlenW(p
+4)+1) * sizeof(WCHAR
));
1536 memmove(full_name
.short_name
+3, p
+4, (strlenW(p
+4)+1) * sizeof(WCHAR
));
1539 if ((full_name
.short_name
[2]=='.')&&(full_name
.short_name
[3]=='.'))
1541 /* This case istn't treated yet : c:..\test */
1542 memmove(full_name
.short_name
+2,full_name
.short_name
+4,
1543 (strlenW(full_name
.short_name
+4)+1) * sizeof(WCHAR
));
1546 while ((p
= strstrW(full_name
.short_name
, curdirW
)))
1549 memmove(p
+1, p
+3, (strlenW(p
+3)+1) * sizeof(WCHAR
));
1551 if (!(DRIVE_GetFlags(drive
) & DRIVE_CASE_PRESERVING
))
1552 for (p
= full_name
.short_name
; *p
; p
++) *p
= toupperW(*p
);
1553 namelen
= strlenW(full_name
.short_name
);
1554 if (!strcmpW(full_name
.short_name
+namelen
-3, updirW
))
1556 /* one more strange case: "c:\test\test1\.."
1558 *(full_name
.short_name
+namelen
-3)=0;
1559 q
= strrchrW(full_name
.short_name
, '\\');
1562 if (full_name
.short_name
[namelen
-1]=='.')
1563 full_name
.short_name
[(namelen
--)-1] =0;
1565 if (full_name
.short_name
[namelen
-1]=='\\')
1566 full_name
.short_name
[(namelen
--)-1] =0;
1567 TRACE("got %s\n", debugstr_w(full_name
.short_name
));
1569 /* If the lpBuffer buffer is too small, the return value is the
1570 size of the buffer, in characters, required to hold the path
1571 plus the terminating \0 (tested against win95osr2, bon 001118)
1573 ret
= strlenW(full_name
.short_name
);
1576 /* don't touch anything when the buffer is not large enough */
1577 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
1582 strncpyW( result
, full_name
.short_name
, len
);
1583 result
[len
- 1] = 0; /* ensure 0 termination */
1586 TRACE("returning %s\n", debugstr_w(full_name
.short_name
) );
1591 /***********************************************************************
1592 * GetFullPathNameA (KERNEL32.@)
1594 * if the path closed with '\', *lastpart is 0
1596 DWORD WINAPI
GetFullPathNameA( LPCSTR name
, DWORD len
, LPSTR buffer
,
1599 UNICODE_STRING nameW
;
1600 WCHAR bufferW
[MAX_PATH
];
1605 SetLastError(ERROR_INVALID_PARAMETER
);
1609 if (!RtlCreateUnicodeStringFromAsciiz(&nameW
, name
))
1611 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1615 retW
= GetFullPathNameW( nameW
.Buffer
, MAX_PATH
, bufferW
, NULL
);
1619 else if (retW
> MAX_PATH
)
1621 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
1626 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, -1, NULL
, 0, NULL
, NULL
);
1629 WideCharToMultiByte(CP_ACP
, 0, bufferW
, -1, buffer
, len
, NULL
, NULL
);
1630 ret
--; /* length without 0 */
1634 LPSTR p
= buffer
+ strlen(buffer
);
1638 while ((p
> buffer
+ 2) && (*p
!= '\\')) p
--;
1641 else *lastpart
= NULL
;
1646 RtlFreeUnicodeString(&nameW
);
1651 /***********************************************************************
1652 * GetFullPathNameW (KERNEL32.@)
1654 DWORD WINAPI
GetFullPathNameW( LPCWSTR name
, DWORD len
, LPWSTR buffer
,
1657 DWORD ret
= DOSFS_DoGetFullPathName( name
, len
, buffer
);
1658 if (ret
&& (ret
<=len
) && buffer
&& lastpart
)
1660 LPWSTR p
= buffer
+ strlenW(buffer
);
1661 if (*p
!= (WCHAR
)'\\')
1663 while ((p
> buffer
+ 2) && (*p
!= (WCHAR
)'\\')) p
--;
1666 else *lastpart
= NULL
;
1672 /***********************************************************************
1673 * wine_get_unix_file_name (KERNEL32.@) Not a Windows API
1675 * Return the full Unix file name for a given path.
1676 * FIXME: convert dos file name to unicode
1678 BOOL WINAPI
wine_get_unix_file_name( LPCSTR dos
, LPSTR buffer
, DWORD len
)
1682 WCHAR dosW
[MAX_PATHNAME_LEN
];
1684 MultiByteToWideChar(CP_ACP
, 0, dos
, -1, dosW
, MAX_PATHNAME_LEN
);
1685 ret
= DOSFS_GetFullName( dosW
, FALSE
, &path
);
1688 strncpy( buffer
, path
.long_name
, len
);
1689 buffer
[len
- 1] = 0; /* ensure 0 termination */
1695 /***********************************************************************
1698 static int DOSFS_FindNextEx( FIND_FIRST_INFO
*info
, WIN32_FIND_DATAW
*entry
)
1700 DWORD attr
= info
->attr
| FA_UNUSED
| FA_ARCHIVE
| FA_RDONLY
;
1701 UINT flags
= DRIVE_GetFlags( info
->drive
);
1702 char *p
, buffer
[MAX_PATHNAME_LEN
];
1703 const char *drive_path
;
1705 LPCWSTR long_name
, short_name
;
1706 BY_HANDLE_FILE_INFORMATION fileinfo
;
1710 if ((info
->attr
& ~(FA_UNUSED
| FA_ARCHIVE
| FA_RDONLY
)) == FA_LABEL
)
1712 if (info
->cur_pos
) return 0;
1713 entry
->dwFileAttributes
= FILE_ATTRIBUTE_LABEL
;
1714 RtlSecondsSince1970ToTime( (time_t)0, (LARGE_INTEGER
*)&entry
->ftCreationTime
);
1715 RtlSecondsSince1970ToTime( (time_t)0, (LARGE_INTEGER
*)&entry
->ftLastAccessTime
);
1716 RtlSecondsSince1970ToTime( (time_t)0, (LARGE_INTEGER
*)&entry
->ftLastWriteTime
);
1717 entry
->nFileSizeHigh
= 0;
1718 entry
->nFileSizeLow
= 0;
1719 entry
->dwReserved0
= 0;
1720 entry
->dwReserved1
= 0;
1721 DOSFS_ToDosDTAFormat( DRIVE_GetLabel( info
->drive
), entry
->cFileName
);
1722 strcpyW( entry
->cAlternateFileName
, entry
->cFileName
);
1724 TRACE("returning %s (%s) as label\n",
1725 debugstr_w(entry
->cFileName
), debugstr_w(entry
->cAlternateFileName
));
1729 drive_path
= info
->path
+ strlen(DRIVE_GetRoot( info
->drive
));
1730 while ((*drive_path
== '/') || (*drive_path
== '\\')) drive_path
++;
1731 drive_root
= !*drive_path
;
1733 lstrcpynA( buffer
, info
->path
, sizeof(buffer
) - 1 );
1734 strcat( buffer
, "/" );
1735 p
= buffer
+ strlen(buffer
);
1737 while (DOSFS_ReadDir( info
->u
.dos_dir
, &long_name
, &short_name
))
1741 /* Don't return '.' and '..' in the root of the drive */
1742 if (drive_root
&& (long_name
[0] == '.') &&
1743 (!long_name
[1] || ((long_name
[1] == '.') && !long_name
[2])))
1746 /* Check the long mask */
1748 if (info
->long_mask
&& *info
->long_mask
)
1750 if (!DOSFS_MatchLong( info
->long_mask
, long_name
,
1751 flags
& DRIVE_CASE_SENSITIVE
)) continue;
1754 /* Check the short mask */
1756 if (info
->short_mask
)
1760 DOSFS_Hash( long_name
, dos_name
, TRUE
,
1761 !(flags
& DRIVE_CASE_SENSITIVE
) );
1762 short_name
= dos_name
;
1764 if (!DOSFS_MatchShort( info
->short_mask
, short_name
)) continue;
1767 /* Check the file attributes */
1768 WideCharToMultiByte(DRIVE_GetCodepage(info
->drive
), 0, long_name
, -1,
1769 p
, sizeof(buffer
) - (int)(p
- buffer
), NULL
, NULL
);
1770 if (!FILE_Stat( buffer
, &fileinfo
, &is_symlink
))
1772 WARN("can't stat %s\n", buffer
);
1775 if (is_symlink
&& (fileinfo
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
))
1777 static const WCHAR wineW
[] = {'w','i','n','e',0};
1778 static const WCHAR ShowDirSymlinksW
[] = {'S','h','o','w','D','i','r','S','y','m','l','i','n','k','s',0};
1779 static int show_dir_symlinks
= -1;
1780 if (show_dir_symlinks
== -1)
1781 show_dir_symlinks
= PROFILE_GetWineIniBool(wineW
, ShowDirSymlinksW
, 0);
1782 if (!show_dir_symlinks
) continue;
1785 if (fileinfo
.dwFileAttributes
& ~attr
) continue;
1787 /* We now have a matching entry; fill the result and return */
1789 entry
->dwFileAttributes
= fileinfo
.dwFileAttributes
;
1790 entry
->ftCreationTime
= fileinfo
.ftCreationTime
;
1791 entry
->ftLastAccessTime
= fileinfo
.ftLastAccessTime
;
1792 entry
->ftLastWriteTime
= fileinfo
.ftLastWriteTime
;
1793 entry
->nFileSizeHigh
= fileinfo
.nFileSizeHigh
;
1794 entry
->nFileSizeLow
= fileinfo
.nFileSizeLow
;
1797 DOSFS_ToDosDTAFormat( short_name
, entry
->cAlternateFileName
);
1799 DOSFS_Hash( long_name
, entry
->cAlternateFileName
, FALSE
,
1800 !(flags
& DRIVE_CASE_SENSITIVE
) );
1802 lstrcpynW( entry
->cFileName
, long_name
, sizeof(entry
->cFileName
)/sizeof(entry
->cFileName
[0]) );
1803 if (!(flags
& DRIVE_CASE_PRESERVING
)) strlwrW( entry
->cFileName
);
1804 TRACE("returning %s (%s) %02lx %ld\n",
1805 debugstr_w(entry
->cFileName
), debugstr_w(entry
->cAlternateFileName
),
1806 entry
->dwFileAttributes
, entry
->nFileSizeLow
);
1809 return 0; /* End of directory */
1812 /***********************************************************************
1815 * Find the next matching file. Return the number of entries read to find
1816 * the matching one, or 0 if no more entries.
1817 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
1818 * file name mask. Either or both can be NULL.
1820 * NOTE: This is supposed to be only called by the int21 emulation
1821 * routines. Thus, we should own the Win16Mutex anyway.
1822 * Nevertheless, we explicitly enter it to ensure the static
1823 * directory cache is protected.
1825 int DOSFS_FindNext( const char *path
, const char *short_mask
,
1826 const char *long_mask
, int drive
, BYTE attr
,
1827 int skip
, WIN32_FIND_DATAA
*entry
)
1829 static FIND_FIRST_INFO info
;
1830 LPCWSTR short_name
, long_name
;
1832 UNICODE_STRING short_maskW
, long_maskW
;
1833 WIN32_FIND_DATAW entryW
;
1835 TRACE("(%s, %s, %s, %x, %x, %x, %p)\n", debugstr_a(path
),
1836 debugstr_a(short_mask
), debugstr_a(long_mask
), drive
, attr
, skip
,
1841 RtlCreateUnicodeStringFromAsciiz(&short_maskW
, short_mask
);
1842 RtlCreateUnicodeStringFromAsciiz(&long_maskW
, long_mask
);
1844 /* Check the cached directory */
1845 if (!(info
.u
.dos_dir
&& info
.path
== path
&& !strcmpW(info
.short_mask
, short_maskW
.Buffer
)
1846 && !strcmpW(info
.long_mask
, long_maskW
.Buffer
) && info
.drive
== drive
1847 && info
.attr
== attr
&& info
.cur_pos
<= skip
))
1849 /* Not in the cache, open it anew */
1850 if (info
.u
.dos_dir
) DOSFS_CloseDir( info
.u
.dos_dir
);
1852 info
.path
= (LPSTR
)path
;
1853 RtlFreeHeap(GetProcessHeap(), 0, info
.long_mask
);
1854 RtlFreeHeap(GetProcessHeap(), 0, info
.short_mask
);
1855 info
.long_mask
= long_maskW
.Buffer
;
1856 info
.short_mask
= short_maskW
.Buffer
;
1860 info
.u
.dos_dir
= DOSFS_OpenDir( DRIVE_GetCodepage(drive
), info
.path
);
1864 RtlFreeUnicodeString(&short_maskW
);
1865 RtlFreeUnicodeString(&long_maskW
);
1868 /* Skip to desired position */
1869 while (info
.cur_pos
< skip
)
1870 if (info
.u
.dos_dir
&& DOSFS_ReadDir( info
.u
.dos_dir
, &long_name
, &short_name
))
1875 if (info
.u
.dos_dir
&& info
.cur_pos
== skip
&& DOSFS_FindNextEx( &info
, &entryW
))
1877 WideCharToMultiByte(CP_ACP
, 0, entryW
.cFileName
, -1,
1878 entry
->cFileName
, sizeof(entry
->cFileName
), NULL
, NULL
);
1879 WideCharToMultiByte(CP_ACP
, 0, entryW
.cAlternateFileName
, -1,
1880 entry
->cAlternateFileName
, sizeof(entry
->cAlternateFileName
), NULL
, NULL
);
1881 count
= info
.cur_pos
- skip
;
1883 entry
->dwFileAttributes
= entryW
.dwFileAttributes
;
1884 entry
->nFileSizeHigh
= entryW
.nFileSizeHigh
;
1885 entry
->nFileSizeLow
= entryW
.nFileSizeLow
;
1886 entry
->ftCreationTime
= entryW
.ftCreationTime
;
1887 entry
->ftLastAccessTime
= entryW
.ftLastAccessTime
;
1888 entry
->ftLastWriteTime
= entryW
.ftLastWriteTime
;
1896 if (info
.u
.dos_dir
) DOSFS_CloseDir( info
.u
.dos_dir
);
1897 memset( &info
, '\0', sizeof(info
) );
1905 /*************************************************************************
1906 * FindFirstFileExW (KERNEL32.@)
1908 HANDLE WINAPI
FindFirstFileExW(
1910 FINDEX_INFO_LEVELS fInfoLevelId
,
1911 LPVOID lpFindFileData
,
1912 FINDEX_SEARCH_OPS fSearchOp
,
1913 LPVOID lpSearchFilter
,
1914 DWORD dwAdditionalFlags
)
1917 FIND_FIRST_INFO
*info
;
1921 SetLastError(ERROR_PATH_NOT_FOUND
);
1922 return INVALID_HANDLE_VALUE
;
1925 if ((fSearchOp
!= FindExSearchNameMatch
) || (dwAdditionalFlags
!= 0))
1927 FIXME("options not implemented 0x%08x 0x%08lx\n", fSearchOp
, dwAdditionalFlags
);
1928 return INVALID_HANDLE_VALUE
;
1931 switch(fInfoLevelId
)
1933 case FindExInfoStandard
:
1935 WIN32_FIND_DATAW
* data
= (WIN32_FIND_DATAW
*) lpFindFileData
;
1940 data
->dwReserved0
= data
->dwReserved1
= 0x0;
1941 if (lpFileName
[0] == '\\' && lpFileName
[1] == '\\')
1943 ERR("UNC path name\n");
1944 if (!(handle
= GlobalAlloc(GMEM_MOVEABLE
, sizeof(FIND_FIRST_INFO
)))) break;
1946 info
= (FIND_FIRST_INFO
*)GlobalLock( handle
);
1947 info
->u
.smb_dir
= SMB_FindFirst(lpFileName
);
1948 if(!info
->u
.smb_dir
)
1950 GlobalUnlock( handle
);
1957 GlobalUnlock( handle
);
1961 DOS_FULL_NAME full_name
;
1963 if (lpFileName
[0] && lpFileName
[1] == ':')
1965 /* don't allow root directories */
1966 if (!lpFileName
[2] ||
1967 ((lpFileName
[2] == '/' || lpFileName
[2] == '\\') && !lpFileName
[3]))
1969 SetLastError(ERROR_FILE_NOT_FOUND
);
1970 return INVALID_HANDLE_VALUE
;
1973 if (!DOSFS_GetFullName( lpFileName
, FALSE
, &full_name
)) break;
1974 if (!(handle
= GlobalAlloc(GMEM_MOVEABLE
, sizeof(FIND_FIRST_INFO
)))) break;
1975 info
= (FIND_FIRST_INFO
*)GlobalLock( handle
);
1976 info
->path
= HeapAlloc( GetProcessHeap(), 0, strlen(full_name
.long_name
)+1 );
1977 strcpy( info
->path
, full_name
.long_name
);
1979 codepage
= DRIVE_GetCodepage(full_name
.drive
);
1980 p
= strrchr( info
->path
, '/' );
1982 long_mask_len
= MultiByteToWideChar(codepage
, 0, p
, -1, NULL
, 0);
1983 info
->long_mask
= HeapAlloc( GetProcessHeap(), 0, long_mask_len
* sizeof(WCHAR
) );
1984 MultiByteToWideChar(codepage
, 0, p
, -1, info
->long_mask
, long_mask_len
);
1986 info
->short_mask
= NULL
;
1988 info
->drive
= full_name
.drive
;
1991 info
->u
.dos_dir
= DOSFS_OpenDir( codepage
, info
->path
);
1992 GlobalUnlock( handle
);
1994 if (!FindNextFileW( handle
, data
))
1996 FindClose( handle
);
1997 SetLastError( ERROR_FILE_NOT_FOUND
);
2004 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId
);
2006 return INVALID_HANDLE_VALUE
;
2009 /*************************************************************************
2010 * FindFirstFileA (KERNEL32.@)
2012 HANDLE WINAPI
FindFirstFileA(
2014 WIN32_FIND_DATAA
*lpFindData
)
2016 return FindFirstFileExA(lpFileName
, FindExInfoStandard
, lpFindData
,
2017 FindExSearchNameMatch
, NULL
, 0);
2020 /*************************************************************************
2021 * FindFirstFileExA (KERNEL32.@)
2023 HANDLE WINAPI
FindFirstFileExA(
2025 FINDEX_INFO_LEVELS fInfoLevelId
,
2026 LPVOID lpFindFileData
,
2027 FINDEX_SEARCH_OPS fSearchOp
,
2028 LPVOID lpSearchFilter
,
2029 DWORD dwAdditionalFlags
)
2032 WIN32_FIND_DATAA
*dataA
;
2033 WIN32_FIND_DATAW dataW
;
2034 UNICODE_STRING pathW
;
2038 SetLastError(ERROR_PATH_NOT_FOUND
);
2039 return INVALID_HANDLE_VALUE
;
2042 if (!RtlCreateUnicodeStringFromAsciiz(&pathW
, lpFileName
))
2044 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
2045 return INVALID_HANDLE_VALUE
;
2048 handle
= FindFirstFileExW(pathW
.Buffer
, fInfoLevelId
, &dataW
, fSearchOp
, lpSearchFilter
, dwAdditionalFlags
);
2049 RtlFreeUnicodeString(&pathW
);
2050 if (handle
== INVALID_HANDLE_VALUE
) return handle
;
2052 dataA
= (WIN32_FIND_DATAA
*) lpFindFileData
;
2053 dataA
->dwFileAttributes
= dataW
.dwFileAttributes
;
2054 dataA
->ftCreationTime
= dataW
.ftCreationTime
;
2055 dataA
->ftLastAccessTime
= dataW
.ftLastAccessTime
;
2056 dataA
->ftLastWriteTime
= dataW
.ftLastWriteTime
;
2057 dataA
->nFileSizeHigh
= dataW
.nFileSizeHigh
;
2058 dataA
->nFileSizeLow
= dataW
.nFileSizeLow
;
2059 WideCharToMultiByte( CP_ACP
, 0, dataW
.cFileName
, -1,
2060 dataA
->cFileName
, sizeof(dataA
->cFileName
), NULL
, NULL
);
2061 WideCharToMultiByte( CP_ACP
, 0, dataW
.cAlternateFileName
, -1,
2062 dataA
->cAlternateFileName
, sizeof(dataA
->cAlternateFileName
), NULL
, NULL
);
2066 /*************************************************************************
2067 * FindFirstFileW (KERNEL32.@)
2069 HANDLE WINAPI
FindFirstFileW( LPCWSTR lpFileName
, WIN32_FIND_DATAW
*lpFindData
)
2071 return FindFirstFileExW(lpFileName
, FindExInfoStandard
, lpFindData
,
2072 FindExSearchNameMatch
, NULL
, 0);
2075 /*************************************************************************
2076 * FindNextFileW (KERNEL32.@)
2078 BOOL WINAPI
FindNextFileW( HANDLE handle
, WIN32_FIND_DATAW
*data
)
2080 FIND_FIRST_INFO
*info
;
2082 DWORD gle
= ERROR_NO_MORE_FILES
;
2084 if ((handle
== INVALID_HANDLE_VALUE
) ||
2085 !(info
= (FIND_FIRST_INFO
*)GlobalLock( handle
)))
2087 SetLastError( ERROR_INVALID_HANDLE
);
2090 if (info
->drive
== -1)
2092 ret
= SMB_FindNext( info
->u
.smb_dir
, data
);
2095 SMB_CloseDir( info
->u
.smb_dir
);
2096 HeapFree( GetProcessHeap(), 0, info
->path
);
2100 else if (!info
->path
|| !info
->u
.dos_dir
)
2104 else if (!DOSFS_FindNextEx( info
, data
))
2106 DOSFS_CloseDir( info
->u
.dos_dir
); info
->u
.dos_dir
= NULL
;
2107 HeapFree( GetProcessHeap(), 0, info
->path
);
2109 HeapFree( GetProcessHeap(), 0, info
->long_mask
);
2110 info
->long_mask
= NULL
;
2115 GlobalUnlock( handle
);
2116 if( !ret
) SetLastError( gle
);
2121 /*************************************************************************
2122 * FindNextFileA (KERNEL32.@)
2124 BOOL WINAPI
FindNextFileA( HANDLE handle
, WIN32_FIND_DATAA
*data
)
2126 WIN32_FIND_DATAW dataW
;
2127 if (!FindNextFileW( handle
, &dataW
)) return FALSE
;
2128 data
->dwFileAttributes
= dataW
.dwFileAttributes
;
2129 data
->ftCreationTime
= dataW
.ftCreationTime
;
2130 data
->ftLastAccessTime
= dataW
.ftLastAccessTime
;
2131 data
->ftLastWriteTime
= dataW
.ftLastWriteTime
;
2132 data
->nFileSizeHigh
= dataW
.nFileSizeHigh
;
2133 data
->nFileSizeLow
= dataW
.nFileSizeLow
;
2134 WideCharToMultiByte( CP_ACP
, 0, dataW
.cFileName
, -1,
2135 data
->cFileName
, sizeof(data
->cFileName
), NULL
, NULL
);
2136 WideCharToMultiByte( CP_ACP
, 0, dataW
.cAlternateFileName
, -1,
2137 data
->cAlternateFileName
,
2138 sizeof(data
->cAlternateFileName
), NULL
, NULL
);
2142 /*************************************************************************
2143 * FindClose (KERNEL32.@)
2145 BOOL WINAPI
FindClose( HANDLE handle
)
2147 FIND_FIRST_INFO
*info
;
2149 if (handle
== INVALID_HANDLE_VALUE
) goto error
;
2153 if ((info
= (FIND_FIRST_INFO
*)GlobalLock( handle
)))
2155 if (info
->u
.dos_dir
) DOSFS_CloseDir( info
->u
.dos_dir
);
2156 if (info
->path
) HeapFree( GetProcessHeap(), 0, info
->path
);
2157 if (info
->long_mask
) HeapFree( GetProcessHeap(), 0, info
->long_mask
);
2160 __EXCEPT(page_fault
)
2162 WARN("Illegal handle %p\n", handle
);
2163 SetLastError( ERROR_INVALID_HANDLE
);
2167 if (!info
) goto error
;
2168 GlobalUnlock( handle
);
2169 GlobalFree( handle
);
2173 SetLastError( ERROR_INVALID_HANDLE
);
2177 /***********************************************************************
2178 * DOSFS_UnixTimeToFileTime
2180 * Convert a Unix time to FILETIME format.
2181 * The FILETIME structure is a 64-bit value representing the number of
2182 * 100-nanosecond intervals since January 1, 1601, 0:00.
2183 * 'remainder' is the nonnegative number of 100-ns intervals
2184 * corresponding to the time fraction smaller than 1 second that
2185 * couldn't be stored in the time_t value.
2187 void DOSFS_UnixTimeToFileTime( time_t unix_time
, FILETIME
*filetime
,
2193 The time difference between 1 January 1601, 00:00:00 and
2194 1 January 1970, 00:00:00 is 369 years, plus the leap years
2195 from 1604 to 1968, excluding 1700, 1800, 1900.
2196 This makes (1968 - 1600) / 4 - 3 = 89 leap days, and a total
2199 Any day in that period had 24 * 60 * 60 = 86400 seconds.
2201 The time difference is 134774 * 86400 * 10000000, which can be written
2203 27111902 * 2^32 + 3577643008
2204 413 * 2^48 + 45534 * 2^32 + 54590 * 2^16 + 32768
2206 If you find that these constants are buggy, please change them in all
2207 instances in both conversion functions.
2210 There are two versions, one of them uses long long variables and
2211 is presumably faster but not ISO C. The other one uses standard C
2212 data types and operations but relies on the assumption that negative
2213 numbers are stored as 2's complement (-1 is 0xffff....). If this
2214 assumption is violated, dates before 1970 will not convert correctly.
2215 This should however work on any reasonable architecture where WINE
2220 Take care not to remove the casts. I have tested these functions
2221 (in both versions) for a lot of numbers. I would be interested in
2222 results on other compilers than GCC.
2224 The operations have been designed to account for the possibility
2225 of 64-bit time_t in future UNICES. Even the versions without
2226 internal long long numbers will work if time_t only is 64 bit.
2227 A 32-bit shift, which was necessary for that operation, turned out
2228 not to work correctly in GCC, besides giving the warning. So I
2229 used a double 16-bit shift instead. Numbers are in the ISO version
2230 represented by three limbs, the most significant with 32 bit, the
2231 other two with 16 bit each.
2233 As the modulo-operator % is not well-defined for negative numbers,
2234 negative divisors have been avoided in DOSFS_FileTimeToUnixTime.
2236 There might be quicker ways to do this in C. Certainly so in
2239 Claus Fischer, fischer@iue.tuwien.ac.at
2242 #if SIZEOF_LONG_LONG >= 8
2243 # define USE_LONG_LONG 1
2245 # define USE_LONG_LONG 0
2248 #if USE_LONG_LONG /* gcc supports long long type */
2250 long long int t
= unix_time
;
2252 t
+= 116444736000000000LL;
2254 filetime
->dwLowDateTime
= (UINT
)t
;
2255 filetime
->dwHighDateTime
= (UINT
)(t
>> 32);
2257 #else /* ISO version */
2259 UINT a0
; /* 16 bit, low bits */
2260 UINT a1
; /* 16 bit, medium bits */
2261 UINT a2
; /* 32 bit, high bits */
2263 /* Copy the unix time to a2/a1/a0 */
2264 a0
= unix_time
& 0xffff;
2265 a1
= (unix_time
>> 16) & 0xffff;
2266 /* This is obsolete if unix_time is only 32 bits, but it does not hurt.
2267 Do not replace this by >> 32, it gives a compiler warning and it does
2269 a2
= (unix_time
>= 0 ? (unix_time
>> 16) >> 16 :
2270 ~((~unix_time
>> 16) >> 16));
2272 /* Multiply a by 10000000 (a = a2/a1/a0)
2273 Split the factor into 10000 * 1000 which are both less than 0xffff. */
2275 a1
= a1
* 10000 + (a0
>> 16);
2276 a2
= a2
* 10000 + (a1
>> 16);
2281 a1
= a1
* 1000 + (a0
>> 16);
2282 a2
= a2
* 1000 + (a1
>> 16);
2286 /* Add the time difference and the remainder */
2287 a0
+= 32768 + (remainder
& 0xffff);
2288 a1
+= 54590 + (remainder
>> 16 ) + (a0
>> 16);
2289 a2
+= 27111902 + (a1
>> 16);
2294 filetime
->dwLowDateTime
= (a1
<< 16) + a0
;
2295 filetime
->dwHighDateTime
= a2
;
2300 /***********************************************************************
2301 * DOSFS_FileTimeToUnixTime
2303 * Convert a FILETIME format to Unix time.
2304 * If not NULL, 'remainder' contains the fractional part of the filetime,
2305 * in the range of [0..9999999] (even if time_t is negative).
2307 time_t DOSFS_FileTimeToUnixTime( const FILETIME
*filetime
, DWORD
*remainder
)
2309 /* Read the comment in the function DOSFS_UnixTimeToFileTime. */
2312 long long int t
= filetime
->dwHighDateTime
;
2314 t
+= (UINT
)filetime
->dwLowDateTime
;
2315 t
-= 116444736000000000LL;
2318 if (remainder
) *remainder
= 9999999 - (-t
- 1) % 10000000;
2319 return -1 - ((-t
- 1) / 10000000);
2323 if (remainder
) *remainder
= t
% 10000000;
2324 return t
/ 10000000;
2327 #else /* ISO version */
2329 UINT a0
; /* 16 bit, low bits */
2330 UINT a1
; /* 16 bit, medium bits */
2331 UINT a2
; /* 32 bit, high bits */
2332 UINT r
; /* remainder of division */
2333 unsigned int carry
; /* carry bit for subtraction */
2334 int negative
; /* whether a represents a negative value */
2336 /* Copy the time values to a2/a1/a0 */
2337 a2
= (UINT
)filetime
->dwHighDateTime
;
2338 a1
= ((UINT
)filetime
->dwLowDateTime
) >> 16;
2339 a0
= ((UINT
)filetime
->dwLowDateTime
) & 0xffff;
2341 /* Subtract the time difference */
2342 if (a0
>= 32768 ) a0
-= 32768 , carry
= 0;
2343 else a0
+= (1 << 16) - 32768 , carry
= 1;
2345 if (a1
>= 54590 + carry
) a1
-= 54590 + carry
, carry
= 0;
2346 else a1
+= (1 << 16) - 54590 - carry
, carry
= 1;
2348 a2
-= 27111902 + carry
;
2350 /* If a is negative, replace a by (-1-a) */
2351 negative
= (a2
>= ((UINT
)1) << 31);
2354 /* Set a to -a - 1 (a is a2/a1/a0) */
2360 /* Divide a by 10000000 (a = a2/a1/a0), put the rest into r.
2361 Split the divisor into 10000 * 1000 which are both less than 0xffff. */
2362 a1
+= (a2
% 10000) << 16;
2364 a0
+= (a1
% 10000) << 16;
2369 a1
+= (a2
% 1000) << 16;
2371 a0
+= (a1
% 1000) << 16;
2373 r
+= (a0
% 1000) * 10000;
2376 /* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
2379 /* Set a to -a - 1 (a is a2/a1/a0) */
2387 if (remainder
) *remainder
= r
;
2389 /* Do not replace this by << 32, it gives a compiler warning and it does
2391 return ((((time_t)a2
) << 16) << 16) + (a1
<< 16) + a0
;
2396 /***********************************************************************
2397 * MulDiv (KERNEL32.@)
2399 * Result of multiplication and division
2400 * -1: Overflow occurred or Divisor was 0
2407 #if SIZEOF_LONG_LONG >= 8
2410 if (!nDivisor
) return -1;
2412 /* We want to deal with a positive divisor to simplify the logic. */
2415 nMultiplicand
= - nMultiplicand
;
2416 nDivisor
= -nDivisor
;
2419 /* If the result is positive, we "add" to round. else, we subtract to round. */
2420 if ( ( (nMultiplicand
< 0) && (nMultiplier
< 0) ) ||
2421 ( (nMultiplicand
>= 0) && (nMultiplier
>= 0) ) )
2422 ret
= (((long long)nMultiplicand
* nMultiplier
) + (nDivisor
/2)) / nDivisor
;
2424 ret
= (((long long)nMultiplicand
* nMultiplier
) - (nDivisor
/2)) / nDivisor
;
2426 if ((ret
> 2147483647) || (ret
< -2147483647)) return -1;
2429 if (!nDivisor
) return -1;
2431 /* We want to deal with a positive divisor to simplify the logic. */
2434 nMultiplicand
= - nMultiplicand
;
2435 nDivisor
= -nDivisor
;
2438 /* If the result is positive, we "add" to round. else, we subtract to round. */
2439 if ( ( (nMultiplicand
< 0) && (nMultiplier
< 0) ) ||
2440 ( (nMultiplicand
>= 0) && (nMultiplier
>= 0) ) )
2441 return ((nMultiplicand
* nMultiplier
) + (nDivisor
/2)) / nDivisor
;
2443 return ((nMultiplicand
* nMultiplier
) - (nDivisor
/2)) / nDivisor
;
2449 /***********************************************************************
2450 * DosDateTimeToFileTime (KERNEL32.@)
2452 BOOL WINAPI
DosDateTimeToFileTime( WORD fatdate
, WORD fattime
, LPFILETIME ft
)
2457 time_t time1
, time2
;
2460 newtm
.tm_sec
= (fattime
& 0x1f) * 2;
2461 newtm
.tm_min
= (fattime
>> 5) & 0x3f;
2462 newtm
.tm_hour
= (fattime
>> 11);
2463 newtm
.tm_mday
= (fatdate
& 0x1f);
2464 newtm
.tm_mon
= ((fatdate
>> 5) & 0x0f) - 1;
2465 newtm
.tm_year
= (fatdate
>> 9) + 80;
2467 RtlSecondsSince1970ToTime( timegm(&newtm
), (LARGE_INTEGER
*)ft
);
2469 time1
= mktime(&newtm
);
2470 gtm
= gmtime(&time1
);
2471 time2
= mktime(gtm
);
2472 RtlSecondsSince1970ToTime( 2*time1
-time2
, (LARGE_INTEGER
*)ft
);
2478 /***********************************************************************
2479 * FileTimeToDosDateTime (KERNEL32.@)
2481 BOOL WINAPI
FileTimeToDosDateTime( const FILETIME
*ft
, LPWORD fatdate
,
2484 time_t unixtime
= DOSFS_FileTimeToUnixTime( ft
, NULL
);
2485 struct tm
*tm
= gmtime( &unixtime
);
2487 *fattime
= (tm
->tm_hour
<< 11) + (tm
->tm_min
<< 5) + (tm
->tm_sec
/ 2);
2489 *fatdate
= ((tm
->tm_year
- 80) << 9) + ((tm
->tm_mon
+ 1) << 5)
2495 /***********************************************************************
2496 * QueryDosDeviceA (KERNEL32.@)
2498 * returns array of strings terminated by \0, terminated by \0
2500 DWORD WINAPI
QueryDosDeviceA(LPCSTR devname
,LPSTR target
,DWORD bufsize
)
2505 TRACE("(%s,...)\n", devname
? devname
: "<null>");
2507 /* return known MSDOS devices */
2508 static const char devices
[24] = "CON\0COM1\0COM2\0LPT1\0NUL\0\0";
2509 memcpy( target
, devices
, min(bufsize
,sizeof(devices
)) );
2510 return min(bufsize
,sizeof(devices
));
2512 /* In theory all that are possible and have been defined.
2513 * Now just those below, since mirc uses it to check for special files.
2515 * (It is more complex, and supports netmounted stuff, and \\.\ stuff,
2516 * but currently we just ignore that.)
2518 #define CHECK(x) (strstr(devname,#x)==devname)
2519 if (CHECK(con
) || CHECK(com
) || CHECK(lpt
) || CHECK(nul
)) {
2520 strcpy(buffer
,"\\DEV\\");
2521 strcat(buffer
,devname
);
2522 if ((s
=strchr(buffer
,':'))) *s
='\0';
2523 lstrcpynA(target
,buffer
,bufsize
);
2524 return strlen(buffer
)+1;
2526 if (strchr(devname
,':') || devname
[0]=='\\') {
2527 /* This might be a DOS device we do not handle yet ... */
2528 FIXME("(%s) not detected as DOS device!\n",devname
);
2530 SetLastError(ERROR_DEV_NOT_EXIST
);
2537 /***********************************************************************
2538 * QueryDosDeviceW (KERNEL32.@)
2540 * returns array of strings terminated by \0, terminated by \0
2542 DWORD WINAPI
QueryDosDeviceW(LPCWSTR devname
,LPWSTR target
,DWORD bufsize
)
2544 LPSTR devnameA
= devname
?HEAP_strdupWtoA(GetProcessHeap(),0,devname
):NULL
;
2545 LPSTR targetA
= (LPSTR
)HeapAlloc(GetProcessHeap(),0,bufsize
);
2546 DWORD ret
= QueryDosDeviceA(devnameA
,targetA
,bufsize
);
2548 ret
= MultiByteToWideChar( CP_ACP
, 0, targetA
, ret
, target
, bufsize
);
2549 if (devnameA
) HeapFree(GetProcessHeap(),0,devnameA
);
2550 if (targetA
) HeapFree(GetProcessHeap(),0,targetA
);
2555 /***********************************************************************
2556 * DefineDosDeviceA (KERNEL32.@)
2558 BOOL WINAPI
DefineDosDeviceA(DWORD flags
,LPCSTR devname
,LPCSTR targetpath
) {
2559 FIXME("(0x%08lx,%s,%s),stub!\n",flags
,devname
,targetpath
);
2560 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
2565 --- 16 bit functions ---
2568 /*************************************************************************
2569 * FindFirstFile (KERNEL.413)
2571 HANDLE16 WINAPI
FindFirstFile16( LPCSTR path
, WIN32_FIND_DATAA
*data
)
2573 DOS_FULL_NAME full_name
;
2575 FIND_FIRST_INFO
*info
;
2576 WCHAR pathW
[MAX_PATH
];
2581 data
->dwReserved0
= data
->dwReserved1
= 0x0;
2582 if (!path
) return INVALID_HANDLE_VALUE16
;
2583 MultiByteToWideChar(CP_ACP
, 0, path
, -1, pathW
, MAX_PATH
);
2584 if (!DOSFS_GetFullName( pathW
, FALSE
, &full_name
))
2585 return INVALID_HANDLE_VALUE16
;
2586 if (!(handle
= GlobalAlloc16( GMEM_MOVEABLE
, sizeof(FIND_FIRST_INFO
) )))
2587 return INVALID_HANDLE_VALUE16
;
2588 info
= (FIND_FIRST_INFO
*)GlobalLock16( handle
);
2589 info
->path
= HeapAlloc( GetProcessHeap(), 0, strlen(full_name
.long_name
)+1 );
2590 strcpy( info
->path
, full_name
.long_name
);
2592 codepage
= DRIVE_GetCodepage(full_name
.drive
);
2593 p
= strrchr( info
->path
, '/' );
2595 long_mask_len
= MultiByteToWideChar(codepage
, 0, p
, -1, NULL
, 0);
2596 info
->long_mask
= HeapAlloc( GetProcessHeap(), 0, long_mask_len
* sizeof(WCHAR
) );
2597 MultiByteToWideChar(codepage
, 0, p
, -1, info
->long_mask
, long_mask_len
);
2599 info
->short_mask
= NULL
;
2601 info
->drive
= full_name
.drive
;
2604 info
->u
.dos_dir
= DOSFS_OpenDir( codepage
, info
->path
);
2606 GlobalUnlock16( handle
);
2607 if (!FindNextFile16( handle
, data
))
2609 FindClose16( handle
);
2610 SetLastError( ERROR_NO_MORE_FILES
);
2611 return INVALID_HANDLE_VALUE16
;
2616 /*************************************************************************
2617 * FindNextFile (KERNEL.414)
2619 BOOL16 WINAPI
FindNextFile16( HANDLE16 handle
, WIN32_FIND_DATAA
*data
)
2621 FIND_FIRST_INFO
*info
;
2622 WIN32_FIND_DATAW dataW
;
2624 DWORD gle
= ERROR_NO_MORE_FILES
;
2626 if ((handle
== INVALID_HANDLE_VALUE16
) ||
2627 !(info
= (FIND_FIRST_INFO
*)GlobalLock16( handle
)))
2629 SetLastError( ERROR_INVALID_HANDLE
);
2632 if (!info
->path
|| !info
->u
.dos_dir
)
2636 if (!DOSFS_FindNextEx( info
, &dataW
))
2638 DOSFS_CloseDir( info
->u
.dos_dir
); info
->u
.dos_dir
= NULL
;
2639 HeapFree( GetProcessHeap(), 0, info
->path
);
2641 HeapFree( GetProcessHeap(), 0, info
->long_mask
);
2642 info
->long_mask
= NULL
;
2648 data
->dwFileAttributes
= dataW
.dwFileAttributes
;
2649 data
->ftCreationTime
= dataW
.ftCreationTime
;
2650 data
->ftLastAccessTime
= dataW
.ftLastAccessTime
;
2651 data
->ftLastWriteTime
= dataW
.ftLastWriteTime
;
2652 data
->nFileSizeHigh
= dataW
.nFileSizeHigh
;
2653 data
->nFileSizeLow
= dataW
.nFileSizeLow
;
2654 WideCharToMultiByte( CP_ACP
, 0, dataW
.cFileName
, -1,
2655 data
->cFileName
, sizeof(data
->cFileName
), NULL
, NULL
);
2656 WideCharToMultiByte( CP_ACP
, 0, dataW
.cAlternateFileName
, -1,
2657 data
->cAlternateFileName
,
2658 sizeof(data
->cAlternateFileName
), NULL
, NULL
);
2660 if( !ret
) SetLastError( gle
);
2661 GlobalUnlock16( handle
);
2666 /*************************************************************************
2667 * FindClose (KERNEL.415)
2669 BOOL16 WINAPI
FindClose16( HANDLE16 handle
)
2671 FIND_FIRST_INFO
*info
;
2673 if ((handle
== INVALID_HANDLE_VALUE16
) ||
2674 !(info
= (FIND_FIRST_INFO
*)GlobalLock16( handle
)))
2676 SetLastError( ERROR_INVALID_HANDLE
);
2679 if (info
->u
.dos_dir
) DOSFS_CloseDir( info
->u
.dos_dir
);
2680 if (info
->path
) HeapFree( GetProcessHeap(), 0, info
->path
);
2681 if (info
->long_mask
) HeapFree( GetProcessHeap(), 0, info
->long_mask
);
2682 GlobalUnlock16( handle
);
2683 GlobalFree16( handle
);