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 /* Check for "." and ".." */
205 for(i
= 1; i
< 11; i
++) buffer
[i
] = ' ';
212 return (!*p
|| (*p
== '/') || (*p
== '\\'));
215 for (i
= 0; i
< 8; i
++)
232 if (*p
< 256 && strchr( invalid_chars
, (char)*p
)) return FALSE
;
233 buffer
[i
] = toupperW(*p
);
241 /* Skip all chars after wildcard up to first dot */
242 while (*p
&& (*p
!= '/') && (*p
!= '\\') && (*p
!= '.')) p
++;
246 /* Check if name too long */
247 if (*p
&& (*p
!= '/') && (*p
!= '\\') && (*p
!= '.')) return FALSE
;
249 if (*p
== '.') p
++; /* Skip dot */
251 for (i
= 8; i
< 11; i
++)
261 return FALSE
; /* Second extension not allowed */
269 if (*p
< 256 && strchr( invalid_chars
, (char)*p
)) return FALSE
;
270 buffer
[i
] = toupperW(*p
);
277 /* at most 3 character of the extension are processed
278 * is something behind this ?
280 while (*p
== '*' || *p
== ' ') p
++; /* skip wildcards and spaces */
281 return IS_END_OF_NAME(*p
);
285 /***********************************************************************
286 * DOSFS_ToDosDTAFormat
288 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
289 * converting to upper-case in the process.
290 * File name can be terminated by '\0', '\\' or '/'.
291 * 'buffer' must be at least 13 characters long.
293 static void DOSFS_ToDosDTAFormat( LPCWSTR name
, LPWSTR buffer
)
297 memcpy( buffer
, name
, 8 * sizeof(WCHAR
) );
299 while ((p
> buffer
) && (p
[-1] == ' ')) p
--;
301 memcpy( p
, name
+ 8, 3 * sizeof(WCHAR
) );
303 while (p
[-1] == ' ') p
--;
304 if (p
[-1] == '.') p
--;
309 /***********************************************************************
312 * Check a DOS file name against a mask (both in FCB format).
314 static int DOSFS_MatchShort( LPCWSTR mask
, LPCWSTR name
)
317 for (i
= 11; i
> 0; i
--, mask
++, name
++)
318 if ((*mask
!= '?') && (*mask
!= *name
)) return 0;
323 /***********************************************************************
326 * Check a long file name against a mask.
328 * Tests (done in W95 DOS shell - case insensitive):
329 * *.txt test1.test.txt *
331 * *.t??????.t* test1.ta.tornado.txt *
332 * *tornado* test1.ta.tornado.txt *
333 * t*t test1.ta.tornado.txt *
335 * ?est??? test1.txt -
336 * *test1.txt* test1.txt *
337 * h?l?o*t.dat hellothisisatest.dat *
339 static int DOSFS_MatchLong( LPCWSTR mask
, LPCWSTR name
, int case_sensitive
)
341 LPCWSTR lastjoker
= NULL
;
342 LPCWSTR next_to_retry
= NULL
;
343 static const WCHAR asterisk_dot_asterisk
[] = {'*','.','*',0};
345 if (!strcmpW( mask
, asterisk_dot_asterisk
)) return 1;
346 while (*name
&& *mask
)
351 while (*mask
== '*') mask
++; /* Skip consecutive '*' */
353 if (!*mask
) return 1; /* end of mask is all '*', so match */
355 /* skip to the next match after the joker(s) */
356 if (case_sensitive
) while (*name
&& (*name
!= *mask
)) name
++;
357 else while (*name
&& (toupperW(*name
) != toupperW(*mask
))) name
++;
360 next_to_retry
= name
;
362 else if (*mask
!= '?')
367 if (*mask
!= *name
) mismatch
= 1;
371 if (toupperW(*mask
) != toupperW(*name
)) mismatch
= 1;
385 else /* mismatch ! */
387 if (lastjoker
) /* we had an '*', so we can try unlimitedly */
391 /* this scan sequence was a mismatch, so restart
392 * 1 char after the first char we checked last time */
394 name
= next_to_retry
;
397 return 0; /* bad luck */
406 while ((*mask
== '.') || (*mask
== '*'))
407 mask
++; /* Ignore trailing '.' or '*' in mask */
408 return (!*name
&& !*mask
);
412 /***********************************************************************
415 * Used to construct an array of filenames in DOSFS_OpenDir
417 static BOOL
DOSFS_AddDirEntry(DOS_DIR
**dir
, LPCWSTR name
, LPCWSTR dosname
)
419 int extra1
= (strlenW(name
) + 1) * sizeof(WCHAR
);
420 int extra2
= (strlenW(dosname
) + 1) * sizeof(WCHAR
);
422 /* if we need more, at minimum double the size */
423 if( (extra1
+ extra2
+ (*dir
)->used
) > (*dir
)->size
)
425 int more
= (*dir
)->size
;
428 if(more
<(extra1
+extra2
))
429 more
= extra1
+extra2
;
431 t
= HeapReAlloc(GetProcessHeap(), 0, *dir
, sizeof(**dir
) + (*dir
)->size
+ more
);
434 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
435 ERR("Out of memory caching directory structure %d %d %d\n",
436 (*dir
)->size
, more
, (*dir
)->used
);
440 (*dir
)->size
+= more
;
443 /* at this point, the dir structure is big enough to hold these names */
444 strcpyW((LPWSTR
)&(*dir
)->names
[(*dir
)->used
], name
);
445 (*dir
)->used
+= extra1
;
446 strcpyW((LPWSTR
)&(*dir
)->names
[(*dir
)->used
], dosname
);
447 (*dir
)->used
+= extra2
;
453 /***********************************************************************
456 static BOOL
DOSFS_OpenDir_VFAT(UINT codepage
, DOS_DIR
**dir
, const char *unix_path
)
458 #ifdef VFAT_IOCTL_READDIR_BOTH
460 int fd
= open( unix_path
, O_RDONLY
);
463 /* Check if the VFAT ioctl is supported on this directory */
470 WCHAR long_name
[MAX_PATH
];
471 WCHAR short_name
[12];
473 r
= (ioctl( fd
, VFAT_IOCTL_READDIR_BOTH
, (long)de
) != -1);
478 MultiByteToWideChar(codepage
, 0, de
[0].d_name
, -1, long_name
, MAX_PATH
);
479 if (!DOSFS_ToDosFCBFormat( long_name
, short_name
))
480 short_name
[0] = '\0';
482 MultiByteToWideChar(codepage
, 0, de
[1].d_name
, -1, long_name
, MAX_PATH
);
484 MultiByteToWideChar(codepage
, 0, de
[0].d_name
, -1, long_name
, MAX_PATH
);
485 r
= DOSFS_AddDirEntry(dir
, long_name
, short_name
);
491 static const WCHAR empty_strW
[] = { 0 };
492 DOSFS_AddDirEntry(dir
, empty_strW
, empty_strW
);
498 #endif /* VFAT_IOCTL_READDIR_BOTH */
502 /***********************************************************************
503 * DOSFS_OpenDir_Normal
505 * Now use the standard opendir/readdir interface
507 static BOOL
DOSFS_OpenDir_Normal( UINT codepage
, DOS_DIR
**dir
, const char *unix_path
)
509 DIR *unixdir
= opendir( unix_path
);
511 static const WCHAR empty_strW
[] = { 0 };
517 WCHAR long_name
[MAX_PATH
];
518 struct dirent
*de
= readdir(unixdir
);
522 MultiByteToWideChar(codepage
, 0, de
->d_name
, -1, long_name
, MAX_PATH
);
523 r
= DOSFS_AddDirEntry(dir
, long_name
, empty_strW
);
528 DOSFS_AddDirEntry(dir
, empty_strW
, empty_strW
);
533 /***********************************************************************
536 static DOS_DIR
*DOSFS_OpenDir( UINT codepage
, const char *unix_path
)
538 const int init_size
= 0x100;
539 DOS_DIR
*dir
= HeapAlloc( GetProcessHeap(), 0, sizeof(*dir
) + init_size
);
542 TRACE("%s\n",debugstr_a(unix_path
));
546 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
550 dir
->size
= init_size
;
552 /* Treat empty path as root directory. This simplifies path split into
553 directory and mask in several other places */
554 if (!*unix_path
) unix_path
= "/";
556 r
= DOSFS_OpenDir_VFAT( codepage
, &dir
, unix_path
);
559 r
= DOSFS_OpenDir_Normal( codepage
, &dir
, unix_path
);
563 HeapFree(GetProcessHeap(), 0, dir
);
572 /***********************************************************************
575 static void DOSFS_CloseDir( DOS_DIR
*dir
)
577 HeapFree( GetProcessHeap(), 0, dir
);
581 /***********************************************************************
584 static BOOL
DOSFS_ReadDir( DOS_DIR
*dir
, LPCWSTR
*long_name
,
585 LPCWSTR
*short_name
)
592 /* the long pathname is first */
593 ln
= (LPCWSTR
)&dir
->names
[dir
->used
];
598 dir
->used
+= (strlenW(ln
) + 1) * sizeof(WCHAR
);
600 /* followed by the short path name */
601 sn
= (LPCWSTR
)&dir
->names
[dir
->used
];
606 dir
->used
+= (strlenW(sn
) + 1) * sizeof(WCHAR
);
612 /***********************************************************************
615 * Transform a Unix file name into a hashed DOS name. If the name is a valid
616 * DOS name, it is converted to upper-case; otherwise it is replaced by a
617 * hashed version that fits in 8.3 format.
618 * File name can be terminated by '\0', '\\' or '/'.
619 * 'buffer' must be at least 13 characters long.
621 static void DOSFS_Hash( LPCWSTR name
, LPWSTR buffer
, BOOL dir_format
,
624 static const char invalid_chars
[] = INVALID_DOS_CHARS
"~.";
625 static const char hash_chars
[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
634 for(i
= 0; i
< 11; i
++) buffer
[i
] = ' ';
638 if (DOSFS_ValidDOSName( name
, ignore_case
))
640 /* Check for '.' and '..' */
644 if (!dir_format
) buffer
[1] = buffer
[2] = '\0';
645 if (name
[1] == '.') buffer
[1] = '.';
649 /* Simply copy the name, converting to uppercase */
651 for (dst
= buffer
; !IS_END_OF_NAME(*name
) && (*name
!= '.'); name
++)
652 *dst
++ = toupperW(*name
);
655 if (dir_format
) dst
= buffer
+ 8;
657 for (name
++; !IS_END_OF_NAME(*name
); name
++)
658 *dst
++ = toupperW(*name
);
660 if (!dir_format
) *dst
= '\0';
664 /* Compute the hash code of the file name */
665 /* If you know something about hash functions, feel free to */
666 /* insert a better algorithm here... */
669 for (p
= name
, hash
= 0xbeef; !IS_END_OF_NAME(p
[1]); p
++)
670 hash
= (hash
<<3) ^ (hash
>>5) ^ tolowerW(*p
) ^ (tolowerW(p
[1]) << 8);
671 hash
= (hash
<<3) ^ (hash
>>5) ^ tolowerW(*p
); /* Last character */
675 for (p
= name
, hash
= 0xbeef; !IS_END_OF_NAME(p
[1]); p
++)
676 hash
= (hash
<< 3) ^ (hash
>> 5) ^ *p
^ (p
[1] << 8);
677 hash
= (hash
<< 3) ^ (hash
>> 5) ^ *p
; /* Last character */
680 /* Find last dot for start of the extension */
681 for (p
= name
+1, ext
= NULL
; !IS_END_OF_NAME(*p
); p
++)
682 if (*p
== '.') ext
= p
;
683 if (ext
&& IS_END_OF_NAME(ext
[1]))
684 ext
= NULL
; /* Empty extension ignored */
686 /* Copy first 4 chars, replacing invalid chars with '_' */
687 for (i
= 4, p
= name
, dst
= buffer
; i
> 0; i
--, p
++)
689 if (IS_END_OF_NAME(*p
) || (p
== ext
)) break;
690 *dst
++ = (*p
< 256 && strchr( invalid_chars
, (char)*p
)) ? '_' : toupperW(*p
);
692 /* Pad to 5 chars with '~' */
693 while (i
-- >= 0) *dst
++ = '~';
695 /* Insert hash code converted to 3 ASCII chars */
696 *dst
++ = hash_chars
[(hash
>> 10) & 0x1f];
697 *dst
++ = hash_chars
[(hash
>> 5) & 0x1f];
698 *dst
++ = hash_chars
[hash
& 0x1f];
700 /* Copy the first 3 chars of the extension (if any) */
703 if (!dir_format
) *dst
++ = '.';
704 for (i
= 3, ext
++; (i
> 0) && !IS_END_OF_NAME(*ext
); i
--, ext
++)
705 *dst
++ = (*ext
< 256 && strchr( invalid_chars
, (char)*ext
)) ? '_' : toupperW(*ext
);
707 if (!dir_format
) *dst
= '\0';
711 /***********************************************************************
714 * Find the Unix file name in a given directory that corresponds to
715 * a file name (either in Unix or DOS format).
716 * File name can be terminated by '\0', '\\' or '/'.
717 * Return TRUE if OK, FALSE if no file name matches.
719 * 'long_buf' must be at least 'long_len' characters long. If the long name
720 * turns out to be larger than that, the function returns FALSE.
721 * 'short_buf' must be at least 13 characters long.
723 BOOL
DOSFS_FindUnixName( const DOS_FULL_NAME
*path
, LPCWSTR name
, char *long_buf
,
724 INT long_len
, LPWSTR short_buf
, BOOL ignore_case
)
727 LPCWSTR long_name
, short_name
;
728 WCHAR dos_name
[12], tmp_buf
[13];
731 LPCWSTR p
= strchrW( name
, '/' );
732 int len
= p
? (int)(p
- name
) : strlenW(name
);
733 if ((p
= strchrW( name
, '\\' ))) len
= min( (int)(p
- name
), len
);
734 /* Ignore trailing dots and spaces */
735 while (len
> 1 && (name
[len
-1] == '.' || name
[len
-1] == ' ')) len
--;
736 if (long_len
< len
+ 1) return FALSE
;
738 TRACE("%s,%s\n", path
->long_name
, debugstr_w(name
) );
740 if (!DOSFS_ToDosFCBFormat( name
, dos_name
)) dos_name
[0] = '\0';
742 if (!(dir
= DOSFS_OpenDir( DRIVE_GetCodepage(path
->drive
), path
->long_name
)))
744 WARN("(%s,%s): can't open dir: %s\n",
745 path
->long_name
, debugstr_w(name
), strerror(errno
) );
749 while ((ret
= DOSFS_ReadDir( dir
, &long_name
, &short_name
)))
751 /* Check against Unix name */
752 if (len
== strlenW(long_name
))
756 if (!strncmpW( long_name
, name
, len
)) break;
760 if (!strncmpiW( long_name
, name
, len
)) break;
765 /* Check against hashed DOS name */
768 DOSFS_Hash( long_name
, tmp_buf
, TRUE
, ignore_case
);
769 short_name
= tmp_buf
;
771 if (!strcmpW( dos_name
, short_name
)) break;
776 if (long_buf
) WideCharToMultiByte(DRIVE_GetCodepage(path
->drive
), 0,
777 long_name
, -1, long_buf
, long_len
, NULL
, NULL
);
781 DOSFS_ToDosDTAFormat( short_name
, short_buf
);
783 DOSFS_Hash( long_name
, short_buf
, FALSE
, ignore_case
);
785 TRACE("(%s,%s) -> %s (%s)\n", path
->long_name
, debugstr_w(name
),
786 debugstr_w(long_name
), short_buf
? debugstr_w(short_buf
) : "***");
789 WARN("%s not found in '%s'\n", debugstr_w(name
), path
->long_name
);
790 DOSFS_CloseDir( dir
);
795 /***********************************************************************
798 * Check if a DOS file name represents a DOS device and return the device.
800 const DOS_DEVICE
*DOSFS_GetDevice( LPCWSTR name
)
805 if (!name
) return NULL
; /* if FILE_DupUnixHandle was used */
806 if (name
[0] && (name
[1] == ':')) name
+= 2;
807 if ((p
= strrchrW( name
, '/' ))) name
= p
+ 1;
808 if ((p
= strrchrW( name
, '\\' ))) name
= p
+ 1;
809 for (i
= 0; i
< sizeof(DOSFS_Devices
)/sizeof(DOSFS_Devices
[0]); i
++)
811 const WCHAR
*dev
= DOSFS_Devices
[i
].name
;
812 if (!strncmpiW( dev
, name
, strlenW(dev
) ))
814 p
= name
+ strlenW( dev
);
815 if (!*p
|| (*p
== '.') || (*p
== ':')) return &DOSFS_Devices
[i
];
822 /***********************************************************************
823 * DOSFS_GetDeviceByHandle
825 const DOS_DEVICE
*DOSFS_GetDeviceByHandle( HANDLE hFile
)
827 const DOS_DEVICE
*ret
= NULL
;
828 SERVER_START_REQ( get_file_info
)
831 if (!wine_server_call( req
) && (reply
->type
== FILE_TYPE_UNKNOWN
))
833 if ((reply
->attr
>= 0) &&
834 (reply
->attr
< sizeof(DOSFS_Devices
)/sizeof(DOSFS_Devices
[0])))
835 ret
= &DOSFS_Devices
[reply
->attr
];
843 /**************************************************************************
844 * DOSFS_CreateCommPort
846 static HANDLE
DOSFS_CreateCommPort(LPCWSTR name
, DWORD access
, DWORD attributes
, LPSECURITY_ATTRIBUTES sa
)
851 static const WCHAR serialportsW
[] = {'s','e','r','i','a','l','p','o','r','t','s',0};
852 static const WCHAR empty_strW
[] = { 0 };
854 TRACE_(file
)("%s %lx %lx\n", debugstr_w(name
), access
, attributes
);
856 PROFILE_GetWineIniString(serialportsW
, name
, empty_strW
, devnameW
, 40);
860 WideCharToMultiByte(CP_ACP
, 0, devnameW
, -1, devname
, sizeof(devname
), NULL
, NULL
);
862 TRACE("opening %s as %s\n", devname
, debugstr_w(name
));
864 SERVER_START_REQ( create_serial
)
866 req
->access
= access
;
867 req
->inherit
= (sa
&& (sa
->nLength
>=sizeof(*sa
)) && sa
->bInheritHandle
);
868 req
->attributes
= attributes
;
869 req
->sharing
= FILE_SHARE_READ
|FILE_SHARE_WRITE
;
870 wine_server_add_data( req
, devname
, strlen(devname
) );
872 wine_server_call_err( req
);
878 ERR("Couldn't open device '%s' ! (check permissions)\n",devname
);
880 TRACE("return %08X\n", ret
);
884 /***********************************************************************
887 * Open a DOS device. This might not map 1:1 into the UNIX device concept.
888 * Returns 0 on failure.
890 HANDLE
DOSFS_OpenDevice( LPCWSTR name
, DWORD access
, DWORD attributes
, LPSECURITY_ATTRIBUTES sa
)
896 if (name
[0] && (name
[1] == ':')) name
+= 2;
897 if ((p
= strrchrW( name
, '/' ))) name
= p
+ 1;
898 if ((p
= strrchrW( name
, '\\' ))) name
= p
+ 1;
899 for (i
= 0; i
< sizeof(DOSFS_Devices
)/sizeof(DOSFS_Devices
[0]); i
++)
901 const WCHAR
*dev
= DOSFS_Devices
[i
].name
;
902 if (!strncmpiW( dev
, name
, strlenW(dev
) ))
904 p
= name
+ strlenW( dev
);
905 if (!*p
|| (*p
== '.') || (*p
== ':')) {
906 static const WCHAR nulW
[] = {'N','U','L',0};
907 static const WCHAR conW
[] = {'C','O','N',0};
908 static const WCHAR scsimgrW
[] = {'S','C','S','I','M','G','R','$',0};
909 static const WCHAR hpscanW
[] = {'H','P','S','C','A','N',0};
910 static const WCHAR emmxxxx0W
[] = {'E','M','M','X','X','X','X','0',0};
912 if (!strcmpiW(DOSFS_Devices
[i
].name
, nulW
))
913 return FILE_CreateFile( "/dev/null", access
,
914 FILE_SHARE_READ
|FILE_SHARE_WRITE
, sa
,
915 OPEN_EXISTING
, 0, 0, TRUE
, DRIVE_UNKNOWN
);
916 if (!strcmpiW(DOSFS_Devices
[i
].name
, conW
)) {
918 switch (access
& (GENERIC_READ
|GENERIC_WRITE
)) {
920 to_dup
= GetStdHandle( STD_INPUT_HANDLE
);
923 to_dup
= GetStdHandle( STD_OUTPUT_HANDLE
);
926 FIXME("can't open CON read/write\n");
929 if (!DuplicateHandle( GetCurrentProcess(), to_dup
, GetCurrentProcess(),
931 sa
&& (sa
->nLength
>=sizeof(*sa
)) && sa
->bInheritHandle
,
932 DUPLICATE_SAME_ACCESS
))
936 if (!strcmpiW(DOSFS_Devices
[i
].name
, scsimgrW
) ||
937 !strcmpiW(DOSFS_Devices
[i
].name
, hpscanW
) ||
938 !strcmpiW(DOSFS_Devices
[i
].name
, emmxxxx0W
))
940 return FILE_CreateDevice( i
, access
, sa
);
943 if( (handle
=DOSFS_CreateCommPort(DOSFS_Devices
[i
].name
,access
,attributes
,sa
)) )
945 FIXME("device open %s not supported (yet)\n", debugstr_w(DOSFS_Devices
[i
].name
));
954 /***********************************************************************
957 * Get the drive specified by a given path name (DOS or Unix format).
959 static int DOSFS_GetPathDrive( LPCWSTR
*name
)
964 if (*p
&& (p
[1] == ':'))
966 drive
= toupperW(*p
) - 'A';
969 else if (*p
== '/') /* Absolute Unix path? */
971 if ((drive
= DRIVE_FindDriveRootW( name
)) == -1)
973 MESSAGE("Warning: %s not accessible from a configured DOS drive\n", debugstr_w(*name
) );
974 /* Assume it really was a DOS name */
975 drive
= DRIVE_GetCurrentDrive();
978 else drive
= DRIVE_GetCurrentDrive();
980 if (!DRIVE_IsValid(drive
))
982 SetLastError( ERROR_INVALID_DRIVE
);
989 /***********************************************************************
992 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
993 * Unix name / short DOS name pair.
994 * Return FALSE if one of the path components does not exist. The last path
995 * component is only checked if 'check_last' is non-zero.
996 * The buffers pointed to by 'long_buf' and 'short_buf' must be
997 * at least MAX_PATHNAME_LEN long.
999 BOOL
DOSFS_GetFullName( LPCWSTR name
, BOOL check_last
, DOS_FULL_NAME
*full
)
1002 UINT flags
, codepage
;
1005 static const WCHAR driveA_rootW
[] = {'A',':','\\',0};
1006 static const WCHAR dos_rootW
[] = {'\\',0};
1008 TRACE("%s (last=%d)\n", debugstr_w(name
), check_last
);
1010 if ((!*name
) || (*name
=='\n'))
1011 { /* error code for Win98 */
1012 SetLastError(ERROR_BAD_PATHNAME
);
1016 if ((full
->drive
= DOSFS_GetPathDrive( &name
)) == -1) return FALSE
;
1017 flags
= DRIVE_GetFlags( full
->drive
);
1018 codepage
= DRIVE_GetCodepage(full
->drive
);
1020 lstrcpynA( full
->long_name
, DRIVE_GetRoot( full
->drive
),
1021 sizeof(full
->long_name
) );
1022 if (full
->long_name
[1]) root
= full
->long_name
+ strlen(full
->long_name
);
1023 else root
= full
->long_name
; /* root directory */
1025 strcpyW( full
->short_name
, driveA_rootW
);
1026 full
->short_name
[0] += full
->drive
;
1028 if ((*name
== '\\') || (*name
== '/')) /* Absolute path */
1030 while ((*name
== '\\') || (*name
== '/')) name
++;
1032 else /* Relative path */
1034 lstrcpynA( root
+ 1, DRIVE_GetUnixCwd( full
->drive
),
1035 sizeof(full
->long_name
) - (root
- full
->long_name
) - 1 );
1036 if (root
[1]) *root
= '/';
1037 lstrcpynW( full
->short_name
+ 3, DRIVE_GetDosCwd( full
->drive
),
1038 sizeof(full
->short_name
)/sizeof(full
->short_name
[0]) - 3 );
1041 p_l
= full
->long_name
[1] ? full
->long_name
+ strlen(full
->long_name
)
1043 p_s
= full
->short_name
[3] ? full
->short_name
+ strlenW(full
->short_name
)
1044 : full
->short_name
+ 2;
1047 while (*name
&& found
)
1049 /* Check for '.' and '..' */
1053 if (IS_END_OF_NAME(name
[1]))
1056 while ((*name
== '\\') || (*name
== '/')) name
++;
1059 else if ((name
[1] == '.') && IS_END_OF_NAME(name
[2]))
1062 while ((*name
== '\\') || (*name
== '/')) name
++;
1063 while ((p_l
> root
) && (*p_l
!= '/')) p_l
--;
1064 while ((p_s
> full
->short_name
+ 2) && (*p_s
!= '\\')) p_s
--;
1065 *p_l
= *p_s
= '\0'; /* Remove trailing separator */
1070 /* Make sure buffers are large enough */
1072 if ((p_s
>= full
->short_name
+ sizeof(full
->short_name
)/sizeof(full
->short_name
[0]) - 14) ||
1073 (p_l
>= full
->long_name
+ sizeof(full
->long_name
) - 1))
1075 SetLastError( ERROR_PATH_NOT_FOUND
);
1079 /* Get the long and short name matching the file name */
1081 if ((found
= DOSFS_FindUnixName( full
, name
, p_l
+ 1,
1082 sizeof(full
->long_name
) - (p_l
- full
->long_name
) - 1,
1083 p_s
+ 1, !(flags
& DRIVE_CASE_SENSITIVE
) )))
1088 p_s
+= strlenW(p_s
);
1089 while (!IS_END_OF_NAME(*name
)) name
++;
1091 else if (!check_last
)
1095 while (!IS_END_OF_NAME(*name
) &&
1096 (p_s
< full
->short_name
+ sizeof(full
->short_name
)/sizeof(full
->short_name
[0]) - 1) &&
1097 (p_l
< full
->long_name
+ sizeof(full
->long_name
) - 1))
1100 *p_s
++ = tolowerW(*name
);
1101 /* If the drive is case-sensitive we want to create new */
1102 /* files in lower-case otherwise we can't reopen them */
1103 /* under the same short name. */
1104 if (flags
& DRIVE_CASE_SENSITIVE
) wch
= tolowerW(*name
);
1106 p_l
+= WideCharToMultiByte(codepage
, 0, &wch
, 1, p_l
, 2, NULL
, NULL
);
1109 /* Ignore trailing dots and spaces */
1110 while(p_l
[-1] == '.' || p_l
[-1] == ' ') {
1117 while ((*name
== '\\') || (*name
== '/')) name
++;
1124 SetLastError( ERROR_FILE_NOT_FOUND
);
1127 if (*name
) /* Not last */
1129 SetLastError( ERROR_PATH_NOT_FOUND
);
1133 if (!full
->long_name
[0]) strcpy( full
->long_name
, "/" );
1134 if (!full
->short_name
[2]) strcpyW( full
->short_name
+ 2, dos_rootW
);
1135 TRACE("returning %s = %s\n", full
->long_name
, debugstr_w(full
->short_name
) );
1140 /***********************************************************************
1141 * GetShortPathNameW (KERNEL32.@)
1145 * longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1146 * longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0
1148 * more observations ( with NT 3.51 (WinDD) ):
1149 * longpath <= 8.3 -> just copy longpath to shortpath
1151 * a) file does not exist -> return 0, LastError = ERROR_FILE_NOT_FOUND
1152 * b) file does exist -> set the short filename.
1153 * - trailing slashes are reproduced in the short name, even if the
1154 * file is not a directory
1155 * - the absolute/relative path of the short name is reproduced like found
1157 * - longpath and shortpath may have the same address
1158 * Peter Ganten, 1999
1160 DWORD WINAPI
GetShortPathNameW( LPCWSTR longpath
, LPWSTR shortpath
, DWORD shortlen
)
1162 DOS_FULL_NAME full_name
;
1163 WCHAR tmpshortpath
[MAX_PATHNAME_LEN
];
1165 DWORD sp
= 0, lp
= 0;
1169 BOOL unixabsolute
= *longpath
== '/';
1171 TRACE("%s\n", debugstr_w(longpath
));
1174 SetLastError(ERROR_INVALID_PARAMETER
);
1178 SetLastError(ERROR_BAD_PATHNAME
);
1182 /* check for drive letter */
1183 if (!unixabsolute
&& longpath
[1] == ':' ) {
1184 tmpshortpath
[0] = longpath
[0];
1185 tmpshortpath
[1] = ':';
1189 if ( ( drive
= DOSFS_GetPathDrive ( &longpath
)) == -1 ) return 0;
1190 flags
= DRIVE_GetFlags ( drive
);
1192 if (unixabsolute
&& drive
!= DRIVE_GetCurrentDrive()) {
1193 tmpshortpath
[0] = drive
+ 'A';
1194 tmpshortpath
[1] = ':';
1198 while ( longpath
[lp
] ) {
1200 /* check for path delimiters and reproduce them */
1201 if ( longpath
[lp
] == '\\' || longpath
[lp
] == '/' ) {
1202 if (!sp
|| tmpshortpath
[sp
-1]!= '\\')
1204 /* strip double "\\" */
1205 tmpshortpath
[sp
] = '\\';
1208 tmpshortpath
[sp
]=0;/*terminate string*/
1214 for(p
= longpath
+ lp
; *p
&& *p
!= '/' && *p
!= '\\'; p
++)
1216 lstrcpynW(tmpshortpath
+ sp
, longpath
+ lp
, tmplen
+ 1);
1218 /* Check, if the current element is a valid dos name */
1219 if ( DOSFS_ValidDOSName ( longpath
+ lp
, !(flags
& DRIVE_CASE_SENSITIVE
) ) ) {
1225 /* Check if the file exists and use the existing file name */
1226 if ( DOSFS_GetFullName ( tmpshortpath
, TRUE
, &full_name
) ) {
1227 strcpyW(tmpshortpath
+ sp
, strrchrW(full_name
.short_name
, '\\') + 1);
1228 sp
+= strlenW(tmpshortpath
+ sp
);
1233 TRACE("not found!\n" );
1234 SetLastError ( ERROR_FILE_NOT_FOUND
);
1237 tmpshortpath
[sp
] = 0;
1239 tmplen
= strlenW(tmpshortpath
) + 1;
1240 if (tmplen
<= shortlen
)
1242 strcpyW(shortpath
, tmpshortpath
);
1243 TRACE("returning %s\n", debugstr_w(shortpath
));
1244 tmplen
--; /* length without 0 */
1251 /***********************************************************************
1252 * GetShortPathNameA (KERNEL32.@)
1254 DWORD WINAPI
GetShortPathNameA( LPCSTR longpath
, LPSTR shortpath
, DWORD shortlen
)
1256 UNICODE_STRING longpathW
;
1257 WCHAR shortpathW
[MAX_PATH
];
1262 SetLastError(ERROR_INVALID_PARAMETER
);
1266 TRACE("%s\n", debugstr_a(longpath
));
1268 if (!RtlCreateUnicodeStringFromAsciiz(&longpathW
, longpath
))
1270 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1274 retW
= GetShortPathNameW(longpathW
.Buffer
, shortpathW
, MAX_PATH
);
1278 else if (retW
> MAX_PATH
)
1280 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
1285 ret
= WideCharToMultiByte(CP_ACP
, 0, shortpathW
, -1, NULL
, 0, NULL
, NULL
);
1286 if (ret
<= shortlen
)
1288 WideCharToMultiByte(CP_ACP
, 0, shortpathW
, -1, shortpath
, shortlen
, NULL
, NULL
);
1289 ret
--; /* length without 0 */
1293 RtlFreeUnicodeString(&longpathW
);
1298 /***********************************************************************
1299 * GetLongPathNameW (KERNEL32.@)
1302 * observed (Win2000):
1303 * shortpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1304 * shortpath="": LastError=ERROR_PATH_NOT_FOUND, ret=0
1306 DWORD WINAPI
GetLongPathNameW( LPCWSTR shortpath
, LPWSTR longpath
, DWORD longlen
)
1308 DOS_FULL_NAME full_name
;
1316 SetLastError(ERROR_INVALID_PARAMETER
);
1319 if (!shortpath
[0]) {
1320 SetLastError(ERROR_PATH_NOT_FOUND
);
1324 TRACE("%s,%p,%ld\n", debugstr_w(shortpath
), longpath
, longlen
);
1326 if(shortpath
[0]=='\\' && shortpath
[1]=='\\')
1328 ERR("UNC pathname %s\n",debugstr_w(shortpath
));
1329 lstrcpynW( longpath
, full_name
.short_name
, longlen
);
1330 return strlenW(longpath
);
1333 if (!DOSFS_GetFullName( shortpath
, TRUE
, &full_name
)) return 0;
1335 root
= full_name
.long_name
;
1336 drive
= DRIVE_FindDriveRoot(&root
);
1337 codepage
= DRIVE_GetCodepage(drive
);
1339 ret
= MultiByteToWideChar(codepage
, 0, root
, -1, NULL
, 0);
1341 /* reproduce terminating slash */
1342 if (ret
> 4) /* if not drive root */
1344 len
= strlenW(shortpath
);
1345 if (shortpath
[len
- 1] == '\\' || shortpath
[len
- 1] == '/')
1351 longpath
[0] = 'A' + drive
;
1353 MultiByteToWideChar(codepage
, 0, root
, -1, longpath
+ 2, longlen
- 2);
1354 for (p
= longpath
; *p
; p
++) if (*p
== '/') *p
= '\\';
1357 longpath
[ret
- 2] = '\\';
1358 longpath
[ret
- 1] = 0;
1360 TRACE("returning %s\n", debugstr_w(longpath
));
1361 ret
--; /* length without 0 */
1367 /***********************************************************************
1368 * GetLongPathNameA (KERNEL32.@)
1370 DWORD WINAPI
GetLongPathNameA( LPCSTR shortpath
, LPSTR longpath
, DWORD longlen
)
1372 UNICODE_STRING shortpathW
;
1373 WCHAR longpathW
[MAX_PATH
];
1378 SetLastError(ERROR_INVALID_PARAMETER
);
1382 TRACE("%s\n", debugstr_a(shortpath
));
1384 if (!RtlCreateUnicodeStringFromAsciiz(&shortpathW
, shortpath
))
1386 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1390 retW
= GetLongPathNameW(shortpathW
.Buffer
, longpathW
, MAX_PATH
);
1394 else if (retW
> MAX_PATH
)
1396 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
1401 ret
= WideCharToMultiByte(CP_ACP
, 0, longpathW
, -1, NULL
, 0, NULL
, NULL
);
1404 WideCharToMultiByte(CP_ACP
, 0, longpathW
, -1, longpath
, longlen
, NULL
, NULL
);
1405 ret
--; /* length without 0 */
1409 RtlFreeUnicodeString(&shortpathW
);
1414 /***********************************************************************
1415 * DOSFS_DoGetFullPathName
1417 * Implementation of GetFullPathNameA/W.
1419 * bon@elektron 000331:
1420 * A test for GetFullPathName with many pathological cases
1421 * now gives identical output for Wine and OSR2
1423 static DWORD
DOSFS_DoGetFullPathName( LPCWSTR name
, DWORD len
, LPWSTR result
)
1426 DOS_FULL_NAME full_name
;
1430 WCHAR drivecur
[] = {'C',':','.',0};
1431 WCHAR driveletter
=0;
1432 int namelen
,drive
=0;
1433 static const WCHAR bkslashW
[] = {'\\',0};
1434 static const WCHAR dotW
[] = {'.',0};
1435 static const WCHAR updir_slashW
[] = {'\\','.','.','\\',0};
1436 static const WCHAR curdirW
[] = {'\\','.','\\',0};
1437 static const WCHAR updirW
[] = {'\\','.','.',0};
1441 SetLastError(ERROR_BAD_PATHNAME
);
1445 TRACE("passed %s\n", debugstr_w(name
));
1448 /*drive letter given */
1450 driveletter
= name
[0];
1452 if ((name
[1]==':') && ((name
[2]=='\\') || (name
[2]=='/')))
1453 /*absolute path given */
1455 strncpyW(full_name
.short_name
, name
, MAX_PATHNAME_LEN
);
1456 full_name
.short_name
[MAX_PATHNAME_LEN
- 1] = 0; /* ensure 0 termination */
1457 drive
= toupperW(name
[0]) - 'A';
1462 drivecur
[0]=driveletter
;
1463 else if ((name
[0]=='\\') || (name
[0]=='/'))
1464 strcpyW(drivecur
, bkslashW
);
1466 strcpyW(drivecur
, dotW
);
1468 if (!DOSFS_GetFullName( drivecur
, FALSE
, &full_name
))
1470 FIXME("internal: error getting drive/path\n");
1473 /* find path that drive letter substitutes*/
1474 drive
= toupperW(full_name
.short_name
[0]) - 'A';
1475 root
= DRIVE_GetRoot(drive
);
1478 FIXME("internal: error getting DOS Drive Root\n");
1481 if (!strcmp(root
,"/"))
1483 /* we have just the last / and we need it. */
1484 p_l
= full_name
.long_name
;
1488 p_l
= full_name
.long_name
+ strlen(root
);
1490 /* append long name (= unix name) to drive */
1491 MultiByteToWideChar(DRIVE_GetCodepage(drive
), 0, p_l
, -1,
1492 full_name
.short_name
+ 2, MAX_PATHNAME_LEN
- 3);
1493 /* append name to treat */
1494 namelen
= strlenW(full_name
.short_name
);
1497 p
+= 2; /* skip drive name when appending */
1498 if (namelen
+ 2 + strlenW(p
) > MAX_PATHNAME_LEN
)
1500 FIXME("internal error: buffer too small\n");
1503 full_name
.short_name
[namelen
++] ='\\';
1504 full_name
.short_name
[namelen
] = 0;
1505 strncpyW(full_name
.short_name
+ namelen
, p
, MAX_PATHNAME_LEN
- namelen
);
1506 full_name
.short_name
[MAX_PATHNAME_LEN
- 1] = 0; /* ensure 0 termination */
1508 /* reverse all slashes */
1509 for (p
=full_name
.short_name
;
1510 p
< full_name
.short_name
+ strlenW(full_name
.short_name
);
1516 /* Use memmove, as areas overlap */
1518 while ((p
= strstrW(full_name
.short_name
, updir_slashW
)))
1520 if (p
> full_name
.short_name
+2)
1523 q
= strrchrW(full_name
.short_name
, '\\');
1524 memmove(q
+1, p
+4, (strlenW(p
+4)+1) * sizeof(WCHAR
));
1528 memmove(full_name
.short_name
+3, p
+4, (strlenW(p
+4)+1) * sizeof(WCHAR
));
1531 if ((full_name
.short_name
[2]=='.')&&(full_name
.short_name
[3]=='.'))
1533 /* This case istn't treated yet : c:..\test */
1534 memmove(full_name
.short_name
+2,full_name
.short_name
+4,
1535 (strlenW(full_name
.short_name
+4)+1) * sizeof(WCHAR
));
1538 while ((p
= strstrW(full_name
.short_name
, curdirW
)))
1541 memmove(p
+1, p
+3, (strlenW(p
+3)+1) * sizeof(WCHAR
));
1543 if (!(DRIVE_GetFlags(drive
) & DRIVE_CASE_PRESERVING
))
1544 for (p
= full_name
.short_name
; *p
; p
++) *p
= toupperW(*p
);
1545 namelen
= strlenW(full_name
.short_name
);
1546 if (!strcmpW(full_name
.short_name
+namelen
-3, updirW
))
1548 /* one more strange case: "c:\test\test1\.."
1550 *(full_name
.short_name
+namelen
-3)=0;
1551 q
= strrchrW(full_name
.short_name
, '\\');
1554 if (full_name
.short_name
[namelen
-1]=='.')
1555 full_name
.short_name
[(namelen
--)-1] =0;
1557 if (full_name
.short_name
[namelen
-1]=='\\')
1558 full_name
.short_name
[(namelen
--)-1] =0;
1559 TRACE("got %s\n", debugstr_w(full_name
.short_name
));
1561 /* If the lpBuffer buffer is too small, the return value is the
1562 size of the buffer, in characters, required to hold the path
1563 plus the terminating \0 (tested against win95osr2, bon 001118)
1565 ret
= strlenW(full_name
.short_name
);
1568 /* don't touch anything when the buffer is not large enough */
1569 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
1574 strncpyW( result
, full_name
.short_name
, len
);
1575 result
[len
- 1] = 0; /* ensure 0 termination */
1578 TRACE("returning %s\n", debugstr_w(full_name
.short_name
) );
1583 /***********************************************************************
1584 * GetFullPathNameA (KERNEL32.@)
1586 * if the path closed with '\', *lastpart is 0
1588 DWORD WINAPI
GetFullPathNameA( LPCSTR name
, DWORD len
, LPSTR buffer
,
1591 UNICODE_STRING nameW
;
1592 WCHAR bufferW
[MAX_PATH
];
1597 SetLastError(ERROR_INVALID_PARAMETER
);
1601 if (!RtlCreateUnicodeStringFromAsciiz(&nameW
, name
))
1603 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1607 retW
= GetFullPathNameW( nameW
.Buffer
, MAX_PATH
, bufferW
, NULL
);
1611 else if (retW
> MAX_PATH
)
1613 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
1618 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, -1, NULL
, 0, NULL
, NULL
);
1621 WideCharToMultiByte(CP_ACP
, 0, bufferW
, -1, buffer
, len
, NULL
, NULL
);
1622 ret
--; /* length without 0 */
1626 LPSTR p
= buffer
+ strlen(buffer
);
1630 while ((p
> buffer
+ 2) && (*p
!= '\\')) p
--;
1633 else *lastpart
= NULL
;
1638 RtlFreeUnicodeString(&nameW
);
1643 /***********************************************************************
1644 * GetFullPathNameW (KERNEL32.@)
1646 DWORD WINAPI
GetFullPathNameW( LPCWSTR name
, DWORD len
, LPWSTR buffer
,
1649 DWORD ret
= DOSFS_DoGetFullPathName( name
, len
, buffer
);
1650 if (ret
&& (ret
<=len
) && buffer
&& lastpart
)
1652 LPWSTR p
= buffer
+ strlenW(buffer
);
1653 if (*p
!= (WCHAR
)'\\')
1655 while ((p
> buffer
+ 2) && (*p
!= (WCHAR
)'\\')) p
--;
1658 else *lastpart
= NULL
;
1664 /***********************************************************************
1665 * wine_get_unix_file_name (KERNEL32.@) Not a Windows API
1667 * Return the full Unix file name for a given path.
1668 * FIXME: convert dos file name to unicode
1670 BOOL WINAPI
wine_get_unix_file_name( LPCSTR dos
, LPSTR buffer
, DWORD len
)
1674 WCHAR dosW
[MAX_PATHNAME_LEN
];
1676 MultiByteToWideChar(CP_ACP
, 0, dos
, -1, dosW
, MAX_PATHNAME_LEN
);
1677 ret
= DOSFS_GetFullName( dosW
, FALSE
, &path
);
1680 strncpy( buffer
, path
.long_name
, len
);
1681 buffer
[len
- 1] = 0; /* ensure 0 termination */
1687 /***********************************************************************
1690 static int DOSFS_FindNextEx( FIND_FIRST_INFO
*info
, WIN32_FIND_DATAW
*entry
)
1692 DWORD attr
= info
->attr
| FA_UNUSED
| FA_ARCHIVE
| FA_RDONLY
| FILE_ATTRIBUTE_SYMLINK
;
1693 UINT flags
= DRIVE_GetFlags( info
->drive
);
1694 char *p
, buffer
[MAX_PATHNAME_LEN
];
1695 const char *drive_path
;
1697 LPCWSTR long_name
, short_name
;
1698 BY_HANDLE_FILE_INFORMATION fileinfo
;
1701 if ((info
->attr
& ~(FA_UNUSED
| FA_ARCHIVE
| FA_RDONLY
)) == FA_LABEL
)
1703 if (info
->cur_pos
) return 0;
1704 entry
->dwFileAttributes
= FILE_ATTRIBUTE_LABEL
;
1705 RtlSecondsSince1970ToTime( (time_t)0, &entry
->ftCreationTime
);
1706 RtlSecondsSince1970ToTime( (time_t)0, &entry
->ftLastAccessTime
);
1707 RtlSecondsSince1970ToTime( (time_t)0, &entry
->ftLastWriteTime
);
1708 entry
->nFileSizeHigh
= 0;
1709 entry
->nFileSizeLow
= 0;
1710 entry
->dwReserved0
= 0;
1711 entry
->dwReserved1
= 0;
1712 DOSFS_ToDosDTAFormat( DRIVE_GetLabel( info
->drive
), entry
->cFileName
);
1713 strcpyW( entry
->cAlternateFileName
, entry
->cFileName
);
1715 TRACE("returning %s (%s) as label\n",
1716 debugstr_w(entry
->cFileName
), debugstr_w(entry
->cAlternateFileName
));
1720 drive_path
= info
->path
+ strlen(DRIVE_GetRoot( info
->drive
));
1721 while ((*drive_path
== '/') || (*drive_path
== '\\')) drive_path
++;
1722 drive_root
= !*drive_path
;
1724 lstrcpynA( buffer
, info
->path
, sizeof(buffer
) - 1 );
1725 strcat( buffer
, "/" );
1726 p
= buffer
+ strlen(buffer
);
1728 while (DOSFS_ReadDir( info
->u
.dos_dir
, &long_name
, &short_name
))
1732 /* Don't return '.' and '..' in the root of the drive */
1733 if (drive_root
&& (long_name
[0] == '.') &&
1734 (!long_name
[1] || ((long_name
[1] == '.') && !long_name
[2])))
1737 /* Check the long mask */
1739 if (info
->long_mask
)
1741 if (!DOSFS_MatchLong( info
->long_mask
, long_name
,
1742 flags
& DRIVE_CASE_SENSITIVE
)) continue;
1745 /* Check the short mask */
1747 if (info
->short_mask
)
1751 DOSFS_Hash( long_name
, dos_name
, TRUE
,
1752 !(flags
& DRIVE_CASE_SENSITIVE
) );
1753 short_name
= dos_name
;
1755 if (!DOSFS_MatchShort( info
->short_mask
, short_name
)) continue;
1758 /* Check the file attributes */
1759 WideCharToMultiByte(DRIVE_GetCodepage(info
->drive
), 0, long_name
, -1,
1760 p
, sizeof(buffer
) - (int)(p
- buffer
), NULL
, NULL
);
1761 if (!FILE_Stat( buffer
, &fileinfo
))
1763 WARN("can't stat %s\n", buffer
);
1766 if ((fileinfo
.dwFileAttributes
& FILE_ATTRIBUTE_SYMLINK
) &&
1767 (fileinfo
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
))
1769 static const WCHAR wineW
[] = {'w','i','n','e',0};
1770 static const WCHAR ShowDirSymlinksW
[] = {'S','h','o','w','D','i','r','S','y','m','l','i','n','k','s',0};
1771 static int show_dir_symlinks
= -1;
1772 if (show_dir_symlinks
== -1)
1773 show_dir_symlinks
= PROFILE_GetWineIniBool(wineW
, ShowDirSymlinksW
, 0);
1774 if (!show_dir_symlinks
) continue;
1777 if (fileinfo
.dwFileAttributes
& ~attr
) continue;
1779 /* We now have a matching entry; fill the result and return */
1781 entry
->dwFileAttributes
= fileinfo
.dwFileAttributes
;
1782 entry
->ftCreationTime
= fileinfo
.ftCreationTime
;
1783 entry
->ftLastAccessTime
= fileinfo
.ftLastAccessTime
;
1784 entry
->ftLastWriteTime
= fileinfo
.ftLastWriteTime
;
1785 entry
->nFileSizeHigh
= fileinfo
.nFileSizeHigh
;
1786 entry
->nFileSizeLow
= fileinfo
.nFileSizeLow
;
1789 DOSFS_ToDosDTAFormat( short_name
, entry
->cAlternateFileName
);
1791 DOSFS_Hash( long_name
, entry
->cAlternateFileName
, FALSE
,
1792 !(flags
& DRIVE_CASE_SENSITIVE
) );
1794 lstrcpynW( entry
->cFileName
, long_name
, sizeof(entry
->cFileName
)/sizeof(entry
->cFileName
[0]) );
1795 if (!(flags
& DRIVE_CASE_PRESERVING
)) strlwrW( entry
->cFileName
);
1796 TRACE("returning %s (%s) %02lx %ld\n",
1797 debugstr_w(entry
->cFileName
), debugstr_w(entry
->cAlternateFileName
),
1798 entry
->dwFileAttributes
, entry
->nFileSizeLow
);
1801 return 0; /* End of directory */
1804 /***********************************************************************
1807 * Find the next matching file. Return the number of entries read to find
1808 * the matching one, or 0 if no more entries.
1809 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
1810 * file name mask. Either or both can be NULL.
1812 * NOTE: This is supposed to be only called by the int21 emulation
1813 * routines. Thus, we should own the Win16Mutex anyway.
1814 * Nevertheless, we explicitly enter it to ensure the static
1815 * directory cache is protected.
1817 int DOSFS_FindNext( const char *path
, const char *short_mask
,
1818 const char *long_mask
, int drive
, BYTE attr
,
1819 int skip
, WIN32_FIND_DATAA
*entry
)
1821 static FIND_FIRST_INFO info
;
1822 LPCWSTR short_name
, long_name
;
1824 UNICODE_STRING short_maskW
, long_maskW
;
1825 WIN32_FIND_DATAW entryW
;
1829 RtlCreateUnicodeStringFromAsciiz(&short_maskW
, short_mask
);
1830 RtlCreateUnicodeStringFromAsciiz(&long_maskW
, long_mask
);
1832 /* Check the cached directory */
1833 if (!(info
.u
.dos_dir
&& info
.path
== path
&& !strcmpW(info
.short_mask
, short_maskW
.Buffer
)
1834 && !strcmpW(info
.long_mask
, long_maskW
.Buffer
) && info
.drive
== drive
1835 && info
.attr
== attr
&& info
.cur_pos
<= skip
))
1837 /* Not in the cache, open it anew */
1838 if (info
.u
.dos_dir
) DOSFS_CloseDir( info
.u
.dos_dir
);
1840 info
.path
= (LPSTR
)path
;
1841 RtlFreeHeap(GetProcessHeap(), 0, info
.long_mask
);
1842 RtlFreeHeap(GetProcessHeap(), 0, info
.short_mask
);
1843 info
.long_mask
= long_maskW
.Buffer
;
1844 info
.short_mask
= short_maskW
.Buffer
;
1848 info
.u
.dos_dir
= DOSFS_OpenDir( DRIVE_GetCodepage(drive
), info
.path
);
1852 RtlFreeUnicodeString(&short_maskW
);
1853 RtlFreeUnicodeString(&long_maskW
);
1856 /* Skip to desired position */
1857 while (info
.cur_pos
< skip
)
1858 if (info
.u
.dos_dir
&& DOSFS_ReadDir( info
.u
.dos_dir
, &long_name
, &short_name
))
1863 if (info
.u
.dos_dir
&& info
.cur_pos
== skip
&& DOSFS_FindNextEx( &info
, &entryW
))
1865 WideCharToMultiByte(CP_ACP
, 0, entryW
.cFileName
, -1,
1866 entry
->cFileName
, sizeof(entry
->cFileName
), NULL
, NULL
);
1867 WideCharToMultiByte(CP_ACP
, 0, entryW
.cAlternateFileName
, -1,
1868 entry
->cAlternateFileName
, sizeof(entry
->cAlternateFileName
), NULL
, NULL
);
1869 count
= info
.cur_pos
- skip
;
1876 if (info
.u
.dos_dir
) DOSFS_CloseDir( info
.u
.dos_dir
);
1877 memset( &info
, '\0', sizeof(info
) );
1885 /*************************************************************************
1886 * FindFirstFileExW (KERNEL32.@)
1888 HANDLE WINAPI
FindFirstFileExW(
1890 FINDEX_INFO_LEVELS fInfoLevelId
,
1891 LPVOID lpFindFileData
,
1892 FINDEX_SEARCH_OPS fSearchOp
,
1893 LPVOID lpSearchFilter
,
1894 DWORD dwAdditionalFlags
)
1897 FIND_FIRST_INFO
*info
;
1901 SetLastError(ERROR_PATH_NOT_FOUND
);
1902 return INVALID_HANDLE_VALUE
;
1905 if ((fSearchOp
!= FindExSearchNameMatch
) || (dwAdditionalFlags
!= 0))
1907 FIXME("options not implemented 0x%08x 0x%08lx\n", fSearchOp
, dwAdditionalFlags
);
1908 return INVALID_HANDLE_VALUE
;
1911 switch(fInfoLevelId
)
1913 case FindExInfoStandard
:
1915 WIN32_FIND_DATAW
* data
= (WIN32_FIND_DATAW
*) lpFindFileData
;
1920 data
->dwReserved0
= data
->dwReserved1
= 0x0;
1921 if (!lpFileName
) return 0;
1922 if (lpFileName
[0] == '\\' && lpFileName
[1] == '\\')
1924 ERR("UNC path name\n");
1925 if (!(handle
= GlobalAlloc(GMEM_MOVEABLE
, sizeof(FIND_FIRST_INFO
)))) break;
1927 info
= (FIND_FIRST_INFO
*)GlobalLock( handle
);
1928 info
->u
.smb_dir
= SMB_FindFirst(lpFileName
);
1929 if(info
->u
.smb_dir
< 0)
1931 GlobalUnlock( handle
);
1938 GlobalUnlock( handle
);
1942 DOS_FULL_NAME full_name
;
1944 if (!DOSFS_GetFullName( lpFileName
, FALSE
, &full_name
)) break;
1945 if (!(handle
= GlobalAlloc(GMEM_MOVEABLE
, sizeof(FIND_FIRST_INFO
)))) break;
1946 info
= (FIND_FIRST_INFO
*)GlobalLock( handle
);
1947 info
->path
= HeapAlloc( GetProcessHeap(), 0, strlen(full_name
.long_name
)+1 );
1948 strcpy( info
->path
, full_name
.long_name
);
1950 codepage
= DRIVE_GetCodepage(full_name
.drive
);
1951 p
= strrchr( info
->path
, '/' );
1953 long_mask_len
= MultiByteToWideChar(codepage
, 0, p
, -1, NULL
, 0);
1954 info
->long_mask
= HeapAlloc( GetProcessHeap(), 0, long_mask_len
* sizeof(WCHAR
) );
1955 MultiByteToWideChar(codepage
, 0, p
, -1, info
->long_mask
, long_mask_len
);
1957 info
->short_mask
= NULL
;
1959 info
->drive
= full_name
.drive
;
1962 info
->u
.dos_dir
= DOSFS_OpenDir( codepage
, info
->path
);
1963 GlobalUnlock( handle
);
1965 if (!FindNextFileW( handle
, data
))
1967 FindClose( handle
);
1968 SetLastError( ERROR_NO_MORE_FILES
);
1975 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId
);
1977 return INVALID_HANDLE_VALUE
;
1980 /*************************************************************************
1981 * FindFirstFileA (KERNEL32.@)
1983 HANDLE WINAPI
FindFirstFileA(
1985 WIN32_FIND_DATAA
*lpFindData
)
1987 return FindFirstFileExA(lpFileName
, FindExInfoStandard
, lpFindData
,
1988 FindExSearchNameMatch
, NULL
, 0);
1991 /*************************************************************************
1992 * FindFirstFileExA (KERNEL32.@)
1994 HANDLE WINAPI
FindFirstFileExA(
1996 FINDEX_INFO_LEVELS fInfoLevelId
,
1997 LPVOID lpFindFileData
,
1998 FINDEX_SEARCH_OPS fSearchOp
,
1999 LPVOID lpSearchFilter
,
2000 DWORD dwAdditionalFlags
)
2003 WIN32_FIND_DATAA
*dataA
;
2004 WIN32_FIND_DATAW dataW
;
2005 UNICODE_STRING pathW
;
2009 SetLastError(ERROR_PATH_NOT_FOUND
);
2010 return INVALID_HANDLE_VALUE
;
2013 if (!RtlCreateUnicodeStringFromAsciiz(&pathW
, lpFileName
))
2015 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
2016 return INVALID_HANDLE_VALUE
;
2019 handle
= FindFirstFileExW(pathW
.Buffer
, fInfoLevelId
, &dataW
, fSearchOp
, lpSearchFilter
, dwAdditionalFlags
);
2020 RtlFreeUnicodeString(&pathW
);
2021 if (handle
== INVALID_HANDLE_VALUE
) return handle
;
2023 dataA
= (WIN32_FIND_DATAA
*) lpFindFileData
;
2024 dataA
->dwFileAttributes
= dataW
.dwFileAttributes
;
2025 dataA
->ftCreationTime
= dataW
.ftCreationTime
;
2026 dataA
->ftLastAccessTime
= dataW
.ftLastAccessTime
;
2027 dataA
->ftLastWriteTime
= dataW
.ftLastWriteTime
;
2028 dataA
->nFileSizeHigh
= dataW
.nFileSizeHigh
;
2029 dataA
->nFileSizeLow
= dataW
.nFileSizeLow
;
2030 WideCharToMultiByte( CP_ACP
, 0, dataW
.cFileName
, -1,
2031 dataA
->cFileName
, sizeof(dataA
->cFileName
), NULL
, NULL
);
2032 WideCharToMultiByte( CP_ACP
, 0, dataW
.cAlternateFileName
, -1,
2033 dataA
->cAlternateFileName
, sizeof(dataA
->cAlternateFileName
), NULL
, NULL
);
2037 /*************************************************************************
2038 * FindFirstFileW (KERNEL32.@)
2040 HANDLE WINAPI
FindFirstFileW( LPCWSTR lpFileName
, WIN32_FIND_DATAW
*lpFindData
)
2042 return FindFirstFileExW(lpFileName
, FindExInfoStandard
, lpFindData
,
2043 FindExSearchNameMatch
, NULL
, 0);
2046 /*************************************************************************
2047 * FindNextFileW (KERNEL32.@)
2049 BOOL WINAPI
FindNextFileW( HANDLE handle
, WIN32_FIND_DATAW
*data
)
2051 FIND_FIRST_INFO
*info
;
2053 DWORD gle
= ERROR_NO_MORE_FILES
;
2055 if ((handle
== INVALID_HANDLE_VALUE
) ||
2056 !(info
= (FIND_FIRST_INFO
*)GlobalLock( handle
)))
2058 SetLastError( ERROR_INVALID_HANDLE
);
2061 if (info
->drive
== -1)
2063 ret
= SMB_FindNext( info
->u
.smb_dir
, data
);
2066 SMB_CloseDir( info
->u
.smb_dir
);
2067 HeapFree( GetProcessHeap(), 0, info
->path
);
2071 else if (!info
->path
|| !info
->u
.dos_dir
)
2075 else if (!DOSFS_FindNextEx( info
, data
))
2077 DOSFS_CloseDir( info
->u
.dos_dir
); info
->u
.dos_dir
= NULL
;
2078 HeapFree( GetProcessHeap(), 0, info
->path
);
2080 HeapFree( GetProcessHeap(), 0, info
->long_mask
);
2081 info
->long_mask
= NULL
;
2086 GlobalUnlock( handle
);
2087 if( !ret
) SetLastError( gle
);
2092 /*************************************************************************
2093 * FindNextFileA (KERNEL32.@)
2095 BOOL WINAPI
FindNextFileA( HANDLE handle
, WIN32_FIND_DATAA
*data
)
2097 WIN32_FIND_DATAW dataW
;
2098 if (!FindNextFileW( handle
, &dataW
)) return FALSE
;
2099 data
->dwFileAttributes
= dataW
.dwFileAttributes
;
2100 data
->ftCreationTime
= dataW
.ftCreationTime
;
2101 data
->ftLastAccessTime
= dataW
.ftLastAccessTime
;
2102 data
->ftLastWriteTime
= dataW
.ftLastWriteTime
;
2103 data
->nFileSizeHigh
= dataW
.nFileSizeHigh
;
2104 data
->nFileSizeLow
= dataW
.nFileSizeLow
;
2105 WideCharToMultiByte( CP_ACP
, 0, dataW
.cFileName
, -1,
2106 data
->cFileName
, sizeof(data
->cFileName
), NULL
, NULL
);
2107 WideCharToMultiByte( CP_ACP
, 0, dataW
.cAlternateFileName
, -1,
2108 data
->cAlternateFileName
,
2109 sizeof(data
->cAlternateFileName
), NULL
, NULL
);
2113 /*************************************************************************
2114 * FindClose (KERNEL32.@)
2116 BOOL WINAPI
FindClose( HANDLE handle
)
2118 FIND_FIRST_INFO
*info
;
2120 if (handle
== INVALID_HANDLE_VALUE
) goto error
;
2124 if ((info
= (FIND_FIRST_INFO
*)GlobalLock( handle
)))
2126 if (info
->u
.dos_dir
) DOSFS_CloseDir( info
->u
.dos_dir
);
2127 if (info
->path
) HeapFree( GetProcessHeap(), 0, info
->path
);
2128 if (info
->long_mask
) HeapFree( GetProcessHeap(), 0, info
->long_mask
);
2131 __EXCEPT(page_fault
)
2133 WARN("Illegal handle %x\n", handle
);
2134 SetLastError( ERROR_INVALID_HANDLE
);
2138 if (!info
) goto error
;
2139 GlobalUnlock( handle
);
2140 GlobalFree( handle
);
2144 SetLastError( ERROR_INVALID_HANDLE
);
2148 /***********************************************************************
2149 * DOSFS_UnixTimeToFileTime
2151 * Convert a Unix time to FILETIME format.
2152 * The FILETIME structure is a 64-bit value representing the number of
2153 * 100-nanosecond intervals since January 1, 1601, 0:00.
2154 * 'remainder' is the nonnegative number of 100-ns intervals
2155 * corresponding to the time fraction smaller than 1 second that
2156 * couldn't be stored in the time_t value.
2158 void DOSFS_UnixTimeToFileTime( time_t unix_time
, FILETIME
*filetime
,
2164 The time difference between 1 January 1601, 00:00:00 and
2165 1 January 1970, 00:00:00 is 369 years, plus the leap years
2166 from 1604 to 1968, excluding 1700, 1800, 1900.
2167 This makes (1968 - 1600) / 4 - 3 = 89 leap days, and a total
2170 Any day in that period had 24 * 60 * 60 = 86400 seconds.
2172 The time difference is 134774 * 86400 * 10000000, which can be written
2174 27111902 * 2^32 + 3577643008
2175 413 * 2^48 + 45534 * 2^32 + 54590 * 2^16 + 32768
2177 If you find that these constants are buggy, please change them in all
2178 instances in both conversion functions.
2181 There are two versions, one of them uses long long variables and
2182 is presumably faster but not ISO C. The other one uses standard C
2183 data types and operations but relies on the assumption that negative
2184 numbers are stored as 2's complement (-1 is 0xffff....). If this
2185 assumption is violated, dates before 1970 will not convert correctly.
2186 This should however work on any reasonable architecture where WINE
2191 Take care not to remove the casts. I have tested these functions
2192 (in both versions) for a lot of numbers. I would be interested in
2193 results on other compilers than GCC.
2195 The operations have been designed to account for the possibility
2196 of 64-bit time_t in future UNICES. Even the versions without
2197 internal long long numbers will work if time_t only is 64 bit.
2198 A 32-bit shift, which was necessary for that operation, turned out
2199 not to work correctly in GCC, besides giving the warning. So I
2200 used a double 16-bit shift instead. Numbers are in the ISO version
2201 represented by three limbs, the most significant with 32 bit, the
2202 other two with 16 bit each.
2204 As the modulo-operator % is not well-defined for negative numbers,
2205 negative divisors have been avoided in DOSFS_FileTimeToUnixTime.
2207 There might be quicker ways to do this in C. Certainly so in
2210 Claus Fischer, fischer@iue.tuwien.ac.at
2213 #if SIZEOF_LONG_LONG >= 8
2214 # define USE_LONG_LONG 1
2216 # define USE_LONG_LONG 0
2219 #if USE_LONG_LONG /* gcc supports long long type */
2221 long long int t
= unix_time
;
2223 t
+= 116444736000000000LL;
2225 filetime
->dwLowDateTime
= (UINT
)t
;
2226 filetime
->dwHighDateTime
= (UINT
)(t
>> 32);
2228 #else /* ISO version */
2230 UINT a0
; /* 16 bit, low bits */
2231 UINT a1
; /* 16 bit, medium bits */
2232 UINT a2
; /* 32 bit, high bits */
2234 /* Copy the unix time to a2/a1/a0 */
2235 a0
= unix_time
& 0xffff;
2236 a1
= (unix_time
>> 16) & 0xffff;
2237 /* This is obsolete if unix_time is only 32 bits, but it does not hurt.
2238 Do not replace this by >> 32, it gives a compiler warning and it does
2240 a2
= (unix_time
>= 0 ? (unix_time
>> 16) >> 16 :
2241 ~((~unix_time
>> 16) >> 16));
2243 /* Multiply a by 10000000 (a = a2/a1/a0)
2244 Split the factor into 10000 * 1000 which are both less than 0xffff. */
2246 a1
= a1
* 10000 + (a0
>> 16);
2247 a2
= a2
* 10000 + (a1
>> 16);
2252 a1
= a1
* 1000 + (a0
>> 16);
2253 a2
= a2
* 1000 + (a1
>> 16);
2257 /* Add the time difference and the remainder */
2258 a0
+= 32768 + (remainder
& 0xffff);
2259 a1
+= 54590 + (remainder
>> 16 ) + (a0
>> 16);
2260 a2
+= 27111902 + (a1
>> 16);
2265 filetime
->dwLowDateTime
= (a1
<< 16) + a0
;
2266 filetime
->dwHighDateTime
= a2
;
2271 /***********************************************************************
2272 * DOSFS_FileTimeToUnixTime
2274 * Convert a FILETIME format to Unix time.
2275 * If not NULL, 'remainder' contains the fractional part of the filetime,
2276 * in the range of [0..9999999] (even if time_t is negative).
2278 time_t DOSFS_FileTimeToUnixTime( const FILETIME
*filetime
, DWORD
*remainder
)
2280 /* Read the comment in the function DOSFS_UnixTimeToFileTime. */
2283 long long int t
= filetime
->dwHighDateTime
;
2285 t
+= (UINT
)filetime
->dwLowDateTime
;
2286 t
-= 116444736000000000LL;
2289 if (remainder
) *remainder
= 9999999 - (-t
- 1) % 10000000;
2290 return -1 - ((-t
- 1) / 10000000);
2294 if (remainder
) *remainder
= t
% 10000000;
2295 return t
/ 10000000;
2298 #else /* ISO version */
2300 UINT a0
; /* 16 bit, low bits */
2301 UINT a1
; /* 16 bit, medium bits */
2302 UINT a2
; /* 32 bit, high bits */
2303 UINT r
; /* remainder of division */
2304 unsigned int carry
; /* carry bit for subtraction */
2305 int negative
; /* whether a represents a negative value */
2307 /* Copy the time values to a2/a1/a0 */
2308 a2
= (UINT
)filetime
->dwHighDateTime
;
2309 a1
= ((UINT
)filetime
->dwLowDateTime
) >> 16;
2310 a0
= ((UINT
)filetime
->dwLowDateTime
) & 0xffff;
2312 /* Subtract the time difference */
2313 if (a0
>= 32768 ) a0
-= 32768 , carry
= 0;
2314 else a0
+= (1 << 16) - 32768 , carry
= 1;
2316 if (a1
>= 54590 + carry
) a1
-= 54590 + carry
, carry
= 0;
2317 else a1
+= (1 << 16) - 54590 - carry
, carry
= 1;
2319 a2
-= 27111902 + carry
;
2321 /* If a is negative, replace a by (-1-a) */
2322 negative
= (a2
>= ((UINT
)1) << 31);
2325 /* Set a to -a - 1 (a is a2/a1/a0) */
2331 /* Divide a by 10000000 (a = a2/a1/a0), put the rest into r.
2332 Split the divisor into 10000 * 1000 which are both less than 0xffff. */
2333 a1
+= (a2
% 10000) << 16;
2335 a0
+= (a1
% 10000) << 16;
2340 a1
+= (a2
% 1000) << 16;
2342 a0
+= (a1
% 1000) << 16;
2344 r
+= (a0
% 1000) * 10000;
2347 /* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
2350 /* Set a to -a - 1 (a is a2/a1/a0) */
2358 if (remainder
) *remainder
= r
;
2360 /* Do not replace this by << 32, it gives a compiler warning and it does
2362 return ((((time_t)a2
) << 16) << 16) + (a1
<< 16) + a0
;
2367 /***********************************************************************
2368 * MulDiv (KERNEL32.@)
2370 * Result of multiplication and division
2371 * -1: Overflow occurred or Divisor was 0
2378 #if SIZEOF_LONG_LONG >= 8
2381 if (!nDivisor
) return -1;
2383 /* We want to deal with a positive divisor to simplify the logic. */
2386 nMultiplicand
= - nMultiplicand
;
2387 nDivisor
= -nDivisor
;
2390 /* If the result is positive, we "add" to round. else, we subtract to round. */
2391 if ( ( (nMultiplicand
< 0) && (nMultiplier
< 0) ) ||
2392 ( (nMultiplicand
>= 0) && (nMultiplier
>= 0) ) )
2393 ret
= (((long long)nMultiplicand
* nMultiplier
) + (nDivisor
/2)) / nDivisor
;
2395 ret
= (((long long)nMultiplicand
* nMultiplier
) - (nDivisor
/2)) / nDivisor
;
2397 if ((ret
> 2147483647) || (ret
< -2147483647)) return -1;
2400 if (!nDivisor
) return -1;
2402 /* We want to deal with a positive divisor to simplify the logic. */
2405 nMultiplicand
= - nMultiplicand
;
2406 nDivisor
= -nDivisor
;
2409 /* If the result is positive, we "add" to round. else, we subtract to round. */
2410 if ( ( (nMultiplicand
< 0) && (nMultiplier
< 0) ) ||
2411 ( (nMultiplicand
>= 0) && (nMultiplier
>= 0) ) )
2412 return ((nMultiplicand
* nMultiplier
) + (nDivisor
/2)) / nDivisor
;
2414 return ((nMultiplicand
* nMultiplier
) - (nDivisor
/2)) / nDivisor
;
2420 /***********************************************************************
2421 * DosDateTimeToFileTime (KERNEL32.@)
2423 BOOL WINAPI
DosDateTimeToFileTime( WORD fatdate
, WORD fattime
, LPFILETIME ft
)
2428 time_t time1
, time2
;
2431 newtm
.tm_sec
= (fattime
& 0x1f) * 2;
2432 newtm
.tm_min
= (fattime
>> 5) & 0x3f;
2433 newtm
.tm_hour
= (fattime
>> 11);
2434 newtm
.tm_mday
= (fatdate
& 0x1f);
2435 newtm
.tm_mon
= ((fatdate
>> 5) & 0x0f) - 1;
2436 newtm
.tm_year
= (fatdate
>> 9) + 80;
2438 RtlSecondsSince1970ToTime( timegm(&newtm
), ft
);
2440 time1
= mktime(&newtm
);
2441 gtm
= gmtime(&time1
);
2442 time2
= mktime(gtm
);
2443 RtlSecondsSince1970ToTime( 2*time1
-time2
, ft
);
2449 /***********************************************************************
2450 * FileTimeToDosDateTime (KERNEL32.@)
2452 BOOL WINAPI
FileTimeToDosDateTime( const FILETIME
*ft
, LPWORD fatdate
,
2455 time_t unixtime
= DOSFS_FileTimeToUnixTime( ft
, NULL
);
2456 struct tm
*tm
= gmtime( &unixtime
);
2458 *fattime
= (tm
->tm_hour
<< 11) + (tm
->tm_min
<< 5) + (tm
->tm_sec
/ 2);
2460 *fatdate
= ((tm
->tm_year
- 80) << 9) + ((tm
->tm_mon
+ 1) << 5)
2466 /***********************************************************************
2467 * LocalFileTimeToFileTime (KERNEL32.@)
2469 BOOL WINAPI
LocalFileTimeToFileTime( const FILETIME
*localft
,
2476 /* Converts from local to UTC. */
2477 time_t localtime
= DOSFS_FileTimeToUnixTime( localft
, &remainder
);
2478 xtm
= gmtime( &localtime
);
2479 utctime
= mktime(xtm
);
2480 if(xtm
->tm_isdst
> 0) utctime
-=3600;
2481 DOSFS_UnixTimeToFileTime( utctime
, utcft
, remainder
);
2486 /***********************************************************************
2487 * FileTimeToLocalFileTime (KERNEL32.@)
2489 BOOL WINAPI
FileTimeToLocalFileTime( const FILETIME
*utcft
,
2490 LPFILETIME localft
)
2493 /* Converts from UTC to local. */
2494 time_t unixtime
= DOSFS_FileTimeToUnixTime( utcft
, &remainder
);
2496 struct tm
*xtm
= localtime( &unixtime
);
2499 localtime
= timegm(xtm
);
2500 DOSFS_UnixTimeToFileTime( localtime
, localft
, remainder
);
2506 xtm
= gmtime( &unixtime
);
2508 if(xtm
->tm_isdst
> 0) time
-=3600;
2509 DOSFS_UnixTimeToFileTime( 2*unixtime
-time
, localft
, remainder
);
2515 /***********************************************************************
2516 * FileTimeToSystemTime (KERNEL32.@)
2518 BOOL WINAPI
FileTimeToSystemTime( const FILETIME
*ft
, LPSYSTEMTIME syst
)
2522 time_t xtime
= DOSFS_FileTimeToUnixTime( ft
, &remainder
);
2523 xtm
= gmtime(&xtime
);
2524 syst
->wYear
= xtm
->tm_year
+1900;
2525 syst
->wMonth
= xtm
->tm_mon
+ 1;
2526 syst
->wDayOfWeek
= xtm
->tm_wday
;
2527 syst
->wDay
= xtm
->tm_mday
;
2528 syst
->wHour
= xtm
->tm_hour
;
2529 syst
->wMinute
= xtm
->tm_min
;
2530 syst
->wSecond
= xtm
->tm_sec
;
2531 syst
->wMilliseconds
= remainder
/ 10000;
2535 /***********************************************************************
2536 * QueryDosDeviceA (KERNEL32.@)
2538 * returns array of strings terminated by \0, terminated by \0
2540 DWORD WINAPI
QueryDosDeviceA(LPCSTR devname
,LPSTR target
,DWORD bufsize
)
2545 TRACE("(%s,...)\n", devname
? devname
: "<null>");
2547 /* return known MSDOS devices */
2548 static const char devices
[24] = "CON\0COM1\0COM2\0LPT1\0NUL\0\0";
2549 memcpy( target
, devices
, min(bufsize
,sizeof(devices
)) );
2550 return min(bufsize
,sizeof(devices
));
2552 /* In theory all that are possible and have been defined.
2553 * Now just those below, since mirc uses it to check for special files.
2555 * (It is more complex, and supports netmounted stuff, and \\.\ stuff,
2556 * but currently we just ignore that.)
2558 #define CHECK(x) (strstr(devname,#x)==devname)
2559 if (CHECK(con
) || CHECK(com
) || CHECK(lpt
) || CHECK(nul
)) {
2560 strcpy(buffer
,"\\DEV\\");
2561 strcat(buffer
,devname
);
2562 if ((s
=strchr(buffer
,':'))) *s
='\0';
2563 lstrcpynA(target
,buffer
,bufsize
);
2564 return strlen(buffer
)+1;
2566 if (strchr(devname
,':') || devname
[0]=='\\') {
2567 /* This might be a DOS device we do not handle yet ... */
2568 FIXME("(%s) not detected as DOS device!\n",devname
);
2570 SetLastError(ERROR_DEV_NOT_EXIST
);
2577 /***********************************************************************
2578 * QueryDosDeviceW (KERNEL32.@)
2580 * returns array of strings terminated by \0, terminated by \0
2582 DWORD WINAPI
QueryDosDeviceW(LPCWSTR devname
,LPWSTR target
,DWORD bufsize
)
2584 LPSTR devnameA
= devname
?HEAP_strdupWtoA(GetProcessHeap(),0,devname
):NULL
;
2585 LPSTR targetA
= (LPSTR
)HeapAlloc(GetProcessHeap(),0,bufsize
);
2586 DWORD ret
= QueryDosDeviceA(devnameA
,targetA
,bufsize
);
2588 ret
= MultiByteToWideChar( CP_ACP
, 0, targetA
, ret
, target
, bufsize
);
2589 if (devnameA
) HeapFree(GetProcessHeap(),0,devnameA
);
2590 if (targetA
) HeapFree(GetProcessHeap(),0,targetA
);
2595 /***********************************************************************
2596 * SystemTimeToFileTime (KERNEL32.@)
2598 BOOL WINAPI
SystemTimeToFileTime( const SYSTEMTIME
*syst
, LPFILETIME ft
)
2604 struct tm xtm
,*utc_tm
;
2605 time_t localtim
,utctime
;
2608 xtm
.tm_year
= syst
->wYear
-1900;
2609 xtm
.tm_mon
= syst
->wMonth
- 1;
2610 xtm
.tm_wday
= syst
->wDayOfWeek
;
2611 xtm
.tm_mday
= syst
->wDay
;
2612 xtm
.tm_hour
= syst
->wHour
;
2613 xtm
.tm_min
= syst
->wMinute
;
2614 xtm
.tm_sec
= syst
->wSecond
; /* this is UTC */
2617 utctime
= timegm(&xtm
);
2618 DOSFS_UnixTimeToFileTime( utctime
, ft
,
2619 syst
->wMilliseconds
* 10000 );
2621 localtim
= mktime(&xtm
); /* now we've got local time */
2622 utc_tm
= gmtime(&localtim
);
2623 utctime
= mktime(utc_tm
);
2624 DOSFS_UnixTimeToFileTime( 2*localtim
-utctime
, ft
,
2625 syst
->wMilliseconds
* 10000 );
2630 /***********************************************************************
2631 * DefineDosDeviceA (KERNEL32.@)
2633 BOOL WINAPI
DefineDosDeviceA(DWORD flags
,LPCSTR devname
,LPCSTR targetpath
) {
2634 FIXME("(0x%08lx,%s,%s),stub!\n",flags
,devname
,targetpath
);
2635 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
2640 --- 16 bit functions ---
2643 /*************************************************************************
2644 * FindFirstFile (KERNEL.413)
2646 HANDLE16 WINAPI
FindFirstFile16( LPCSTR path
, WIN32_FIND_DATAA
*data
)
2648 DOS_FULL_NAME full_name
;
2650 FIND_FIRST_INFO
*info
;
2651 WCHAR pathW
[MAX_PATH
];
2656 data
->dwReserved0
= data
->dwReserved1
= 0x0;
2657 if (!path
) return INVALID_HANDLE_VALUE16
;
2658 MultiByteToWideChar(CP_ACP
, 0, path
, -1, pathW
, MAX_PATH
);
2659 if (!DOSFS_GetFullName( pathW
, FALSE
, &full_name
))
2660 return INVALID_HANDLE_VALUE16
;
2661 if (!(handle
= GlobalAlloc16( GMEM_MOVEABLE
, sizeof(FIND_FIRST_INFO
) )))
2662 return INVALID_HANDLE_VALUE16
;
2663 info
= (FIND_FIRST_INFO
*)GlobalLock16( handle
);
2664 info
->path
= HeapAlloc( GetProcessHeap(), 0, strlen(full_name
.long_name
)+1 );
2665 strcpy( info
->path
, full_name
.long_name
);
2667 codepage
= DRIVE_GetCodepage(full_name
.drive
);
2668 p
= strrchr( info
->path
, '/' );
2670 long_mask_len
= MultiByteToWideChar(codepage
, 0, p
, -1, NULL
, 0);
2671 info
->long_mask
= HeapAlloc( GetProcessHeap(), 0, long_mask_len
* sizeof(WCHAR
) );
2672 MultiByteToWideChar(codepage
, 0, p
, -1, info
->long_mask
, long_mask_len
);
2674 info
->short_mask
= NULL
;
2676 info
->drive
= full_name
.drive
;
2679 info
->u
.dos_dir
= DOSFS_OpenDir( codepage
, info
->path
);
2681 GlobalUnlock16( handle
);
2682 if (!FindNextFile16( handle
, data
))
2684 FindClose16( handle
);
2685 SetLastError( ERROR_NO_MORE_FILES
);
2686 return INVALID_HANDLE_VALUE16
;
2691 /*************************************************************************
2692 * FindNextFile (KERNEL.414)
2694 BOOL16 WINAPI
FindNextFile16( HANDLE16 handle
, WIN32_FIND_DATAA
*data
)
2696 FIND_FIRST_INFO
*info
;
2697 WIN32_FIND_DATAW dataW
;
2699 DWORD gle
= ERROR_NO_MORE_FILES
;
2701 if ((handle
== INVALID_HANDLE_VALUE16
) ||
2702 !(info
= (FIND_FIRST_INFO
*)GlobalLock16( handle
)))
2704 SetLastError( ERROR_INVALID_HANDLE
);
2707 if (!info
->path
|| !info
->u
.dos_dir
)
2711 if (!DOSFS_FindNextEx( info
, &dataW
))
2713 DOSFS_CloseDir( info
->u
.dos_dir
); info
->u
.dos_dir
= NULL
;
2714 HeapFree( GetProcessHeap(), 0, info
->path
);
2716 HeapFree( GetProcessHeap(), 0, info
->long_mask
);
2717 info
->long_mask
= NULL
;
2723 data
->dwFileAttributes
= dataW
.dwFileAttributes
;
2724 data
->ftCreationTime
= dataW
.ftCreationTime
;
2725 data
->ftLastAccessTime
= dataW
.ftLastAccessTime
;
2726 data
->ftLastWriteTime
= dataW
.ftLastWriteTime
;
2727 data
->nFileSizeHigh
= dataW
.nFileSizeHigh
;
2728 data
->nFileSizeLow
= dataW
.nFileSizeLow
;
2729 WideCharToMultiByte( CP_ACP
, 0, dataW
.cFileName
, -1,
2730 data
->cFileName
, sizeof(data
->cFileName
), NULL
, NULL
);
2731 WideCharToMultiByte( CP_ACP
, 0, dataW
.cAlternateFileName
, -1,
2732 data
->cAlternateFileName
,
2733 sizeof(data
->cAlternateFileName
), NULL
, NULL
);
2735 if( !ret
) SetLastError( gle
);
2736 GlobalUnlock16( handle
);
2741 /*************************************************************************
2742 * FindClose (KERNEL.415)
2744 BOOL16 WINAPI
FindClose16( HANDLE16 handle
)
2746 FIND_FIRST_INFO
*info
;
2748 if ((handle
== INVALID_HANDLE_VALUE16
) ||
2749 !(info
= (FIND_FIRST_INFO
*)GlobalLock16( handle
)))
2751 SetLastError( ERROR_INVALID_HANDLE
);
2754 if (info
->u
.dos_dir
) DOSFS_CloseDir( info
->u
.dos_dir
);
2755 if (info
->path
) HeapFree( GetProcessHeap(), 0, info
->path
);
2756 if (info
->long_mask
) HeapFree( GetProcessHeap(), 0, info
->long_mask
);
2757 GlobalUnlock16( handle
);
2758 GlobalFree16( handle
);