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>
45 #include "wine/unicode.h"
46 #include "wine/winbase16.h"
52 #include "wine/server.h"
53 #include "msvcrt/excpt.h"
55 #include "wine/debug.h"
57 WINE_DEFAULT_DEBUG_CHANNEL(dosfs
);
58 WINE_DECLARE_DEBUG_CHANNEL(file
);
60 /* Define the VFAT ioctl to get both short and long file names */
61 /* FIXME: is it possible to get this to work on other systems? */
63 /* We want the real kernel dirent structure, not the libc one */
68 unsigned short d_reclen
;
72 #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, KERNEL_DIRENT [2] )
75 #undef VFAT_IOCTL_READDIR_BOTH /* just in case... */
78 /* Chars we don't want to see in DOS file names */
79 #define INVALID_DOS_CHARS "*?<>|\"+=,;[] \345"
81 static const DOS_DEVICE DOSFS_Devices
[] =
82 /* name, device flags (see Int 21/AX=0x4400) */
96 { "SCSIMGR$", 0xc0c0 },
98 { "EMMXXXX0", 0x0000 }
101 #define GET_DRIVE(path) \
102 (((path)[1] == ':') ? FILE_toupper((path)[0]) - 'A' : DOSFS_CurDrive)
104 /* Directory info for DOSFS_ReadDir */
108 #ifdef VFAT_IOCTL_READDIR_BOTH
111 KERNEL_DIRENT dirent
[2];
115 /* Info structure for FindFirstFile handle */
128 static WINE_EXCEPTION_FILTER(page_fault
)
130 if (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION
)
131 return EXCEPTION_EXECUTE_HANDLER
;
132 return EXCEPTION_CONTINUE_SEARCH
;
136 /***********************************************************************
139 * Return 1 if Unix file 'name' is also a valid MS-DOS name
140 * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
141 * File name can be terminated by '\0', '\\' or '/'.
143 static int DOSFS_ValidDOSName( const char *name
, int ignore_case
)
145 static const char invalid_chars
[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS
;
146 const char *p
= name
;
147 const char *invalid
= ignore_case
? (invalid_chars
+ 26) : invalid_chars
;
152 /* Check for "." and ".." */
155 /* All other names beginning with '.' are invalid */
156 return (IS_END_OF_NAME(*p
));
158 while (!IS_END_OF_NAME(*p
))
160 if (strchr( invalid
, *p
)) return 0; /* Invalid char */
161 if (*p
== '.') break; /* Start of the extension */
162 if (++len
> 8) return 0; /* Name too long */
165 if (*p
!= '.') return 1; /* End of name */
167 if (IS_END_OF_NAME(*p
)) return 0; /* Empty extension not allowed */
169 while (!IS_END_OF_NAME(*p
))
171 if (strchr( invalid
, *p
)) return 0; /* Invalid char */
172 if (*p
== '.') return 0; /* Second extension not allowed */
173 if (++len
> 3) return 0; /* Extension too long */
180 /***********************************************************************
181 * DOSFS_ToDosFCBFormat
183 * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
184 * expanding wild cards and converting to upper-case in the process.
185 * File name can be terminated by '\0', '\\' or '/'.
186 * Return FALSE if the name is not a valid DOS name.
187 * 'buffer' must be at least 12 characters long.
189 BOOL
DOSFS_ToDosFCBFormat( LPCSTR name
, LPSTR buffer
)
191 static const char invalid_chars
[] = INVALID_DOS_CHARS
;
192 const char *p
= name
;
195 /* Check for "." and ".." */
199 strcpy( buffer
, ". " );
205 return (!*p
|| (*p
== '/') || (*p
== '\\'));
208 for (i
= 0; i
< 8; i
++)
225 if (strchr( invalid_chars
, *p
)) return FALSE
;
226 buffer
[i
] = FILE_toupper(*p
);
234 /* Skip all chars after wildcard up to first dot */
235 while (*p
&& (*p
!= '/') && (*p
!= '\\') && (*p
!= '.')) p
++;
239 /* Check if name too long */
240 if (*p
&& (*p
!= '/') && (*p
!= '\\') && (*p
!= '.')) return FALSE
;
242 if (*p
== '.') p
++; /* Skip dot */
244 for (i
= 8; i
< 11; i
++)
254 return FALSE
; /* Second extension not allowed */
262 if (strchr( invalid_chars
, *p
)) return FALSE
;
263 buffer
[i
] = FILE_toupper(*p
);
270 /* at most 3 character of the extension are processed
271 * is something behind this ?
273 while (*p
== '*' || *p
== ' ') p
++; /* skip wildcards and spaces */
274 return IS_END_OF_NAME(*p
);
278 /***********************************************************************
279 * DOSFS_ToDosDTAFormat
281 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
282 * converting to upper-case in the process.
283 * File name can be terminated by '\0', '\\' or '/'.
284 * 'buffer' must be at least 13 characters long.
286 static void DOSFS_ToDosDTAFormat( LPCSTR name
, LPSTR buffer
)
290 memcpy( buffer
, name
, 8 );
292 while ((p
> buffer
) && (p
[-1] == ' ')) p
--;
294 memcpy( p
, name
+ 8, 3 );
296 while (p
[-1] == ' ') p
--;
297 if (p
[-1] == '.') p
--;
302 /***********************************************************************
305 * Check a DOS file name against a mask (both in FCB format).
307 static int DOSFS_MatchShort( const char *mask
, const char *name
)
310 for (i
= 11; i
> 0; i
--, mask
++, name
++)
311 if ((*mask
!= '?') && (*mask
!= *name
)) return 0;
316 /***********************************************************************
319 * Check a long file name against a mask.
321 * Tests (done in W95 DOS shell - case insensitive):
322 * *.txt test1.test.txt *
324 * *.t??????.t* test1.ta.tornado.txt *
325 * *tornado* test1.ta.tornado.txt *
326 * t*t test1.ta.tornado.txt *
328 * ?est??? test1.txt -
329 * *test1.txt* test1.txt *
330 * h?l?o*t.dat hellothisisatest.dat *
332 static int DOSFS_MatchLong( const char *mask
, const char *name
,
335 const char *lastjoker
= NULL
;
336 const char *next_to_retry
= NULL
;
338 if (!strcmp( mask
, "*.*" )) return 1;
339 while (*name
&& *mask
)
344 while (*mask
== '*') mask
++; /* Skip consecutive '*' */
346 if (!*mask
) return 1; /* end of mask is all '*', so match */
348 /* skip to the next match after the joker(s) */
349 if (case_sensitive
) while (*name
&& (*name
!= *mask
)) name
++;
350 else while (*name
&& (FILE_toupper(*name
) != FILE_toupper(*mask
))) name
++;
353 next_to_retry
= name
;
355 else if (*mask
!= '?')
360 if (*mask
!= *name
) mismatch
= 1;
364 if (FILE_toupper(*mask
) != FILE_toupper(*name
)) mismatch
= 1;
378 else /* mismatch ! */
380 if (lastjoker
) /* we had an '*', so we can try unlimitedly */
384 /* this scan sequence was a mismatch, so restart
385 * 1 char after the first char we checked last time */
387 name
= next_to_retry
;
390 return 0; /* bad luck */
399 while ((*mask
== '.') || (*mask
== '*'))
400 mask
++; /* Ignore trailing '.' or '*' in mask */
401 return (!*name
&& !*mask
);
405 /***********************************************************************
408 static DOS_DIR
*DOSFS_OpenDir( LPCSTR path
)
410 DOS_DIR
*dir
= HeapAlloc( GetProcessHeap(), 0, sizeof(*dir
) );
413 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
417 /* Treat empty path as root directory. This simplifies path split into
418 directory and mask in several other places */
419 if (!*path
) path
= "/";
421 #ifdef VFAT_IOCTL_READDIR_BOTH
423 /* Check if the VFAT ioctl is supported on this directory */
425 if ((dir
->fd
= open( path
, O_RDONLY
)) != -1)
427 if (ioctl( dir
->fd
, VFAT_IOCTL_READDIR_BOTH
, (long)dir
->dirent
) == -1)
434 /* Set the file pointer back at the start of the directory */
435 lseek( dir
->fd
, 0, SEEK_SET
);
440 #endif /* VFAT_IOCTL_READDIR_BOTH */
442 /* Now use the standard opendir/readdir interface */
444 if (!(dir
->dir
= opendir( path
)))
446 HeapFree( GetProcessHeap(), 0, dir
);
453 /***********************************************************************
456 static void DOSFS_CloseDir( DOS_DIR
*dir
)
458 #ifdef VFAT_IOCTL_READDIR_BOTH
459 if (dir
->fd
!= -1) close( dir
->fd
);
460 #endif /* VFAT_IOCTL_READDIR_BOTH */
461 if (dir
->dir
) closedir( dir
->dir
);
462 HeapFree( GetProcessHeap(), 0, dir
);
466 /***********************************************************************
469 static BOOL
DOSFS_ReadDir( DOS_DIR
*dir
, LPCSTR
*long_name
,
472 struct dirent
*dirent
;
474 #ifdef VFAT_IOCTL_READDIR_BOTH
477 if (ioctl( dir
->fd
, VFAT_IOCTL_READDIR_BOTH
, (long)dir
->dirent
) != -1) {
478 if (!dir
->dirent
[0].d_reclen
) return FALSE
;
479 if (!DOSFS_ToDosFCBFormat( dir
->dirent
[0].d_name
, dir
->short_name
))
480 dir
->short_name
[0] = '\0';
481 *short_name
= dir
->short_name
;
482 if (dir
->dirent
[1].d_name
[0]) *long_name
= dir
->dirent
[1].d_name
;
483 else *long_name
= dir
->dirent
[0].d_name
;
487 #endif /* VFAT_IOCTL_READDIR_BOTH */
489 if (!(dirent
= readdir( dir
->dir
))) return FALSE
;
490 *long_name
= dirent
->d_name
;
496 /***********************************************************************
499 * Transform a Unix file name into a hashed DOS name. If the name is a valid
500 * DOS name, it is converted to upper-case; otherwise it is replaced by a
501 * hashed version that fits in 8.3 format.
502 * File name can be terminated by '\0', '\\' or '/'.
503 * 'buffer' must be at least 13 characters long.
505 static void DOSFS_Hash( LPCSTR name
, LPSTR buffer
, BOOL dir_format
,
508 static const char invalid_chars
[] = INVALID_DOS_CHARS
"~.";
509 static const char hash_chars
[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
516 if (dir_format
) strcpy( buffer
, " " );
518 if (DOSFS_ValidDOSName( name
, ignore_case
))
520 /* Check for '.' and '..' */
524 if (!dir_format
) buffer
[1] = buffer
[2] = '\0';
525 if (name
[1] == '.') buffer
[1] = '.';
529 /* Simply copy the name, converting to uppercase */
531 for (dst
= buffer
; !IS_END_OF_NAME(*name
) && (*name
!= '.'); name
++)
532 *dst
++ = FILE_toupper(*name
);
535 if (dir_format
) dst
= buffer
+ 8;
537 for (name
++; !IS_END_OF_NAME(*name
); name
++)
538 *dst
++ = FILE_toupper(*name
);
540 if (!dir_format
) *dst
= '\0';
544 /* Compute the hash code of the file name */
545 /* If you know something about hash functions, feel free to */
546 /* insert a better algorithm here... */
549 for (p
= name
, hash
= 0xbeef; !IS_END_OF_NAME(p
[1]); p
++)
550 hash
= (hash
<<3) ^ (hash
>>5) ^ FILE_tolower(*p
) ^ (FILE_tolower(p
[1]) << 8);
551 hash
= (hash
<<3) ^ (hash
>>5) ^ FILE_tolower(*p
); /* Last character*/
555 for (p
= name
, hash
= 0xbeef; !IS_END_OF_NAME(p
[1]); p
++)
556 hash
= (hash
<< 3) ^ (hash
>> 5) ^ *p
^ (p
[1] << 8);
557 hash
= (hash
<< 3) ^ (hash
>> 5) ^ *p
; /* Last character */
560 /* Find last dot for start of the extension */
561 for (p
= name
+1, ext
= NULL
; !IS_END_OF_NAME(*p
); p
++)
562 if (*p
== '.') ext
= p
;
563 if (ext
&& IS_END_OF_NAME(ext
[1]))
564 ext
= NULL
; /* Empty extension ignored */
566 /* Copy first 4 chars, replacing invalid chars with '_' */
567 for (i
= 4, p
= name
, dst
= buffer
; i
> 0; i
--, p
++)
569 if (IS_END_OF_NAME(*p
) || (p
== ext
)) break;
570 *dst
++ = strchr( invalid_chars
, *p
) ? '_' : FILE_toupper(*p
);
572 /* Pad to 5 chars with '~' */
573 while (i
-- >= 0) *dst
++ = '~';
575 /* Insert hash code converted to 3 ASCII chars */
576 *dst
++ = hash_chars
[(hash
>> 10) & 0x1f];
577 *dst
++ = hash_chars
[(hash
>> 5) & 0x1f];
578 *dst
++ = hash_chars
[hash
& 0x1f];
580 /* Copy the first 3 chars of the extension (if any) */
583 if (!dir_format
) *dst
++ = '.';
584 for (i
= 3, ext
++; (i
> 0) && !IS_END_OF_NAME(*ext
); i
--, ext
++)
585 *dst
++ = strchr( invalid_chars
, *ext
) ? '_' : FILE_toupper(*ext
);
587 if (!dir_format
) *dst
= '\0';
591 /***********************************************************************
594 * Find the Unix file name in a given directory that corresponds to
595 * a file name (either in Unix or DOS format).
596 * File name can be terminated by '\0', '\\' or '/'.
597 * Return TRUE if OK, FALSE if no file name matches.
599 * 'long_buf' must be at least 'long_len' characters long. If the long name
600 * turns out to be larger than that, the function returns FALSE.
601 * 'short_buf' must be at least 13 characters long.
603 BOOL
DOSFS_FindUnixName( LPCSTR path
, LPCSTR name
, LPSTR long_buf
,
604 INT long_len
, LPSTR short_buf
, BOOL ignore_case
)
607 LPCSTR long_name
, short_name
;
608 char dos_name
[12], tmp_buf
[13];
611 const char *p
= strchr( name
, '/' );
612 int len
= p
? (int)(p
- name
) : strlen(name
);
613 if ((p
= strchr( name
, '\\' ))) len
= min( (int)(p
- name
), len
);
614 /* Ignore trailing dots and spaces */
615 while (len
> 1 && (name
[len
-1] == '.' || name
[len
-1] == ' ')) len
--;
616 if (long_len
< len
+ 1) return FALSE
;
618 TRACE("%s,%s\n", path
, name
);
620 if (!DOSFS_ToDosFCBFormat( name
, dos_name
)) dos_name
[0] = '\0';
622 if (!(dir
= DOSFS_OpenDir( path
)))
624 WARN("(%s,%s): can't open dir: %s\n",
625 path
, name
, strerror(errno
) );
629 while ((ret
= DOSFS_ReadDir( dir
, &long_name
, &short_name
)))
631 /* Check against Unix name */
632 if (len
== strlen(long_name
))
636 if (!strncmp( long_name
, name
, len
)) break;
640 if (!FILE_strncasecmp( long_name
, name
, len
)) break;
645 /* Check against hashed DOS name */
648 DOSFS_Hash( long_name
, tmp_buf
, TRUE
, ignore_case
);
649 short_name
= tmp_buf
;
651 if (!strcmp( dos_name
, short_name
)) break;
656 if (long_buf
) strcpy( long_buf
, long_name
);
660 DOSFS_ToDosDTAFormat( short_name
, short_buf
);
662 DOSFS_Hash( long_name
, short_buf
, FALSE
, ignore_case
);
664 TRACE("(%s,%s) -> %s (%s)\n",
665 path
, name
, long_name
, short_buf
? short_buf
: "***");
668 WARN("'%s' not found in '%s'\n", name
, path
);
669 DOSFS_CloseDir( dir
);
674 /***********************************************************************
677 * Check if a DOS file name represents a DOS device and return the device.
679 const DOS_DEVICE
*DOSFS_GetDevice( const char *name
)
684 if (!name
) return NULL
; /* if FILE_DupUnixHandle was used */
685 if (name
[0] && (name
[1] == ':')) name
+= 2;
686 if ((p
= strrchr( name
, '/' ))) name
= p
+ 1;
687 if ((p
= strrchr( name
, '\\' ))) name
= p
+ 1;
688 for (i
= 0; i
< sizeof(DOSFS_Devices
)/sizeof(DOSFS_Devices
[0]); i
++)
690 const char *dev
= DOSFS_Devices
[i
].name
;
691 if (!FILE_strncasecmp( dev
, name
, strlen(dev
) ))
693 p
= name
+ strlen( dev
);
694 if (!*p
|| (*p
== '.') || (*p
== ':')) return &DOSFS_Devices
[i
];
701 /***********************************************************************
702 * DOSFS_GetDeviceByHandle
704 const DOS_DEVICE
*DOSFS_GetDeviceByHandle( HFILE hFile
)
706 const DOS_DEVICE
*ret
= NULL
;
707 SERVER_START_REQ( get_file_info
)
710 if (!wine_server_call( req
) && (reply
->type
== FILE_TYPE_UNKNOWN
))
712 if ((reply
->attr
>= 0) &&
713 (reply
->attr
< sizeof(DOSFS_Devices
)/sizeof(DOSFS_Devices
[0])))
714 ret
= &DOSFS_Devices
[reply
->attr
];
722 /**************************************************************************
723 * DOSFS_CreateCommPort
725 static HANDLE
DOSFS_CreateCommPort(LPCSTR name
, DWORD access
, DWORD attributes
, LPSECURITY_ATTRIBUTES sa
)
730 TRACE_(file
)("%s %lx %lx\n", name
, access
, attributes
);
732 PROFILE_GetWineIniString("serialports",name
,"",devname
,sizeof devname
);
736 TRACE("opening %s as %s\n", devname
, name
);
738 SERVER_START_REQ( create_serial
)
740 req
->access
= access
;
741 req
->inherit
= (sa
&& (sa
->nLength
>=sizeof(*sa
)) && sa
->bInheritHandle
);
742 req
->attributes
= attributes
;
743 req
->sharing
= FILE_SHARE_READ
|FILE_SHARE_WRITE
;
744 wine_server_add_data( req
, devname
, strlen(devname
) );
746 wine_server_call_err( req
);
752 ERR("Couldn't open device '%s' ! (check permissions)\n",devname
);
754 TRACE("return %08X\n", ret
);
758 /***********************************************************************
761 * Open a DOS device. This might not map 1:1 into the UNIX device concept.
762 * Returns 0 on failure.
764 HANDLE
DOSFS_OpenDevice( const char *name
, DWORD access
, DWORD attributes
, LPSECURITY_ATTRIBUTES sa
)
770 if (name
[0] && (name
[1] == ':')) name
+= 2;
771 if ((p
= strrchr( name
, '/' ))) name
= p
+ 1;
772 if ((p
= strrchr( name
, '\\' ))) name
= p
+ 1;
773 for (i
= 0; i
< sizeof(DOSFS_Devices
)/sizeof(DOSFS_Devices
[0]); i
++)
775 const char *dev
= DOSFS_Devices
[i
].name
;
776 if (!FILE_strncasecmp( dev
, name
, strlen(dev
) ))
778 p
= name
+ strlen( dev
);
779 if (!*p
|| (*p
== '.') || (*p
== ':')) {
781 if (!strcmp(DOSFS_Devices
[i
].name
,"NUL"))
782 return FILE_CreateFile( "/dev/null", access
,
783 FILE_SHARE_READ
|FILE_SHARE_WRITE
, sa
,
784 OPEN_EXISTING
, 0, 0, TRUE
, DRIVE_UNKNOWN
);
785 if (!strcmp(DOSFS_Devices
[i
].name
,"CON")) {
787 switch (access
& (GENERIC_READ
|GENERIC_WRITE
)) {
789 to_dup
= GetStdHandle( STD_INPUT_HANDLE
);
792 to_dup
= GetStdHandle( STD_OUTPUT_HANDLE
);
795 FIXME("can't open CON read/write\n");
798 if (!DuplicateHandle( GetCurrentProcess(), to_dup
, GetCurrentProcess(),
800 sa
&& (sa
->nLength
>=sizeof(*sa
)) && sa
->bInheritHandle
,
801 DUPLICATE_SAME_ACCESS
))
805 if (!strcmp(DOSFS_Devices
[i
].name
,"SCSIMGR$") ||
806 !strcmp(DOSFS_Devices
[i
].name
,"HPSCAN") ||
807 !strcmp(DOSFS_Devices
[i
].name
,"EMMXXXX0"))
809 return FILE_CreateDevice( i
, access
, sa
);
812 if( (handle
=DOSFS_CreateCommPort(DOSFS_Devices
[i
].name
,access
,attributes
,sa
)) )
814 FIXME("device open %s not supported (yet)\n",DOSFS_Devices
[i
].name
);
823 /***********************************************************************
826 * Get the drive specified by a given path name (DOS or Unix format).
828 static int DOSFS_GetPathDrive( const char **name
)
831 const char *p
= *name
;
833 if (*p
&& (p
[1] == ':'))
835 drive
= FILE_toupper(*p
) - 'A';
838 else if (*p
== '/') /* Absolute Unix path? */
840 if ((drive
= DRIVE_FindDriveRoot( name
)) == -1)
842 MESSAGE("Warning: %s not accessible from a configured DOS drive\n", *name
);
843 /* Assume it really was a DOS name */
844 drive
= DRIVE_GetCurrentDrive();
847 else drive
= DRIVE_GetCurrentDrive();
849 if (!DRIVE_IsValid(drive
))
851 SetLastError( ERROR_INVALID_DRIVE
);
858 /***********************************************************************
861 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
862 * Unix name / short DOS name pair.
863 * Return FALSE if one of the path components does not exist. The last path
864 * component is only checked if 'check_last' is non-zero.
865 * The buffers pointed to by 'long_buf' and 'short_buf' must be
866 * at least MAX_PATHNAME_LEN long.
868 BOOL
DOSFS_GetFullName( LPCSTR name
, BOOL check_last
, DOS_FULL_NAME
*full
)
872 char *p_l
, *p_s
, *root
;
874 TRACE("%s (last=%d)\n", name
, check_last
);
876 if ((!*name
) || (*name
=='\n'))
877 { /* error code for Win98 */
878 SetLastError(ERROR_BAD_PATHNAME
);
882 if ((full
->drive
= DOSFS_GetPathDrive( &name
)) == -1) return FALSE
;
883 flags
= DRIVE_GetFlags( full
->drive
);
885 lstrcpynA( full
->long_name
, DRIVE_GetRoot( full
->drive
),
886 sizeof(full
->long_name
) );
887 if (full
->long_name
[1]) root
= full
->long_name
+ strlen(full
->long_name
);
888 else root
= full
->long_name
; /* root directory */
890 strcpy( full
->short_name
, "A:\\" );
891 full
->short_name
[0] += full
->drive
;
893 if ((*name
== '\\') || (*name
== '/')) /* Absolute path */
895 while ((*name
== '\\') || (*name
== '/')) name
++;
897 else /* Relative path */
899 lstrcpynA( root
+ 1, DRIVE_GetUnixCwd( full
->drive
),
900 sizeof(full
->long_name
) - (root
- full
->long_name
) - 1 );
901 if (root
[1]) *root
= '/';
902 lstrcpynA( full
->short_name
+ 3, DRIVE_GetDosCwd( full
->drive
),
903 sizeof(full
->short_name
) - 3 );
906 p_l
= full
->long_name
[1] ? full
->long_name
+ strlen(full
->long_name
)
908 p_s
= full
->short_name
[3] ? full
->short_name
+ strlen(full
->short_name
)
909 : full
->short_name
+ 2;
912 while (*name
&& found
)
914 /* Check for '.' and '..' */
918 if (IS_END_OF_NAME(name
[1]))
921 while ((*name
== '\\') || (*name
== '/')) name
++;
924 else if ((name
[1] == '.') && IS_END_OF_NAME(name
[2]))
927 while ((*name
== '\\') || (*name
== '/')) name
++;
928 while ((p_l
> root
) && (*p_l
!= '/')) p_l
--;
929 while ((p_s
> full
->short_name
+ 2) && (*p_s
!= '\\')) p_s
--;
930 *p_l
= *p_s
= '\0'; /* Remove trailing separator */
935 /* Make sure buffers are large enough */
937 if ((p_s
>= full
->short_name
+ sizeof(full
->short_name
) - 14) ||
938 (p_l
>= full
->long_name
+ sizeof(full
->long_name
) - 1))
940 SetLastError( ERROR_PATH_NOT_FOUND
);
944 /* Get the long and short name matching the file name */
946 if ((found
= DOSFS_FindUnixName( full
->long_name
, name
, p_l
+ 1,
947 sizeof(full
->long_name
) - (p_l
- full
->long_name
) - 1,
948 p_s
+ 1, !(flags
& DRIVE_CASE_SENSITIVE
) )))
954 while (!IS_END_OF_NAME(*name
)) name
++;
956 else if (!check_last
)
960 while (!IS_END_OF_NAME(*name
) &&
961 (p_s
< full
->short_name
+ sizeof(full
->short_name
) - 1) &&
962 (p_l
< full
->long_name
+ sizeof(full
->long_name
) - 1))
964 *p_s
++ = FILE_tolower(*name
);
965 /* If the drive is case-sensitive we want to create new */
966 /* files in lower-case otherwise we can't reopen them */
967 /* under the same short name. */
968 if (flags
& DRIVE_CASE_SENSITIVE
) *p_l
++ = FILE_tolower(*name
);
972 /* Ignore trailing dots and spaces */
973 while(p_l
[-1] == '.' || p_l
[-1] == ' ') {
979 while ((*name
== '\\') || (*name
== '/')) name
++;
986 SetLastError( ERROR_FILE_NOT_FOUND
);
989 if (*name
) /* Not last */
991 SetLastError( ERROR_PATH_NOT_FOUND
);
995 if (!full
->long_name
[0]) strcpy( full
->long_name
, "/" );
996 if (!full
->short_name
[2]) strcpy( full
->short_name
+ 2, "\\" );
997 TRACE("returning %s = %s\n", full
->long_name
, full
->short_name
);
1002 /***********************************************************************
1003 * GetShortPathNameA (KERNEL32.@)
1007 * longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1008 * longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0
1010 * more observations ( with NT 3.51 (WinDD) ):
1011 * longpath <= 8.3 -> just copy longpath to shortpath
1013 * a) file does not exist -> return 0, LastError = ERROR_FILE_NOT_FOUND
1014 * b) file does exist -> set the short filename.
1015 * - trailing slashes are reproduced in the short name, even if the
1016 * file is not a directory
1017 * - the absolute/relative path of the short name is reproduced like found
1019 * - longpath and shortpath may have the same address
1020 * Peter Ganten, 1999
1022 DWORD WINAPI
GetShortPathNameA( LPCSTR longpath
, LPSTR shortpath
,
1025 DOS_FULL_NAME full_name
;
1027 DWORD sp
= 0, lp
= 0;
1030 BOOL unixabsolute
= *longpath
== '/';
1032 TRACE("%s\n", debugstr_a(longpath
));
1035 SetLastError(ERROR_INVALID_PARAMETER
);
1039 SetLastError(ERROR_BAD_PATHNAME
);
1043 if ( ( tmpshortpath
= HeapAlloc ( GetProcessHeap(), 0, MAX_PATHNAME_LEN
) ) == NULL
) {
1044 SetLastError ( ERROR_NOT_ENOUGH_MEMORY
);
1048 /* check for drive letter */
1049 if (!unixabsolute
&& longpath
[1] == ':' ) {
1050 tmpshortpath
[0] = longpath
[0];
1051 tmpshortpath
[1] = ':';
1055 if ( ( drive
= DOSFS_GetPathDrive ( &longpath
)) == -1 ) return 0;
1056 flags
= DRIVE_GetFlags ( drive
);
1058 if (unixabsolute
&& drive
!= DRIVE_GetCurrentDrive()) {
1059 tmpshortpath
[0] = drive
+ 'A';
1060 tmpshortpath
[1] = ':';
1064 while ( longpath
[lp
] ) {
1066 /* check for path delimiters and reproduce them */
1067 if ( longpath
[lp
] == '\\' || longpath
[lp
] == '/' ) {
1068 if (!sp
|| tmpshortpath
[sp
-1]!= '\\')
1070 /* strip double "\\" */
1071 tmpshortpath
[sp
] = '\\';
1074 tmpshortpath
[sp
]=0;/*terminate string*/
1079 tmplen
= strcspn ( longpath
+ lp
, "\\/" );
1080 lstrcpynA ( tmpshortpath
+sp
, longpath
+ lp
, tmplen
+1 );
1082 /* Check, if the current element is a valid dos name */
1083 if ( DOSFS_ValidDOSName ( longpath
+ lp
, !(flags
& DRIVE_CASE_SENSITIVE
) ) ) {
1089 /* Check if the file exists and use the existing file name */
1090 if ( DOSFS_GetFullName ( tmpshortpath
, TRUE
, &full_name
) ) {
1091 strcpy( tmpshortpath
+sp
, strrchr ( full_name
.short_name
, '\\' ) + 1 );
1092 sp
+= strlen ( tmpshortpath
+sp
);
1097 TRACE("not found!\n" );
1098 SetLastError ( ERROR_FILE_NOT_FOUND
);
1101 tmpshortpath
[sp
] = 0;
1103 lstrcpynA ( shortpath
, tmpshortpath
, shortlen
);
1104 TRACE("returning %s\n", debugstr_a(shortpath
) );
1105 tmplen
= strlen ( tmpshortpath
);
1106 HeapFree ( GetProcessHeap(), 0, tmpshortpath
);
1112 /***********************************************************************
1113 * GetShortPathNameW (KERNEL32.@)
1115 DWORD WINAPI
GetShortPathNameW( LPCWSTR longpath
, LPWSTR shortpath
,
1118 LPSTR longpathA
, shortpathA
;
1121 longpathA
= HEAP_strdupWtoA( GetProcessHeap(), 0, longpath
);
1122 shortpathA
= HeapAlloc ( GetProcessHeap(), 0, shortlen
);
1124 ret
= GetShortPathNameA ( longpathA
, shortpathA
, shortlen
);
1125 if (shortlen
> 0 && !MultiByteToWideChar( CP_ACP
, 0, shortpathA
, -1, shortpath
, shortlen
))
1126 shortpath
[shortlen
-1] = 0;
1127 HeapFree( GetProcessHeap(), 0, longpathA
);
1128 HeapFree( GetProcessHeap(), 0, shortpathA
);
1134 /***********************************************************************
1135 * GetLongPathNameA (KERNEL32.@)
1138 * observed (Win2000):
1139 * shortpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1140 * shortpath="": LastError=ERROR_PATH_NOT_FOUND, ret=0
1142 DWORD WINAPI
GetLongPathNameA( LPCSTR shortpath
, LPSTR longpath
,
1145 DOS_FULL_NAME full_name
;
1146 char *p
, *r
, *ll
, *ss
;
1149 SetLastError(ERROR_INVALID_PARAMETER
);
1152 if (!shortpath
[0]) {
1153 SetLastError(ERROR_PATH_NOT_FOUND
);
1157 if (!DOSFS_GetFullName( shortpath
, TRUE
, &full_name
)) return 0;
1158 lstrcpynA( longpath
, full_name
.short_name
, longlen
);
1160 /* Do some hackery to get the long filename. */
1163 ss
=longpath
+strlen(longpath
);
1164 ll
=full_name
.long_name
+strlen(full_name
.long_name
);
1166 while (ss
>=longpath
)
1168 /* FIXME: aren't we more paranoid, than needed? */
1169 while ((ss
[0]=='\\') && (ss
>=longpath
)) ss
--;
1171 while ((ss
[0]!='\\') && (ss
>=longpath
)) ss
--;
1174 /* FIXME: aren't we more paranoid, than needed? */
1175 while ((ll
[0]=='/') && (ll
>=full_name
.long_name
)) ll
--;
1176 while ((ll
[0]!='/') && (ll
>=full_name
.long_name
)) ll
--;
1177 if (ll
<full_name
.long_name
)
1179 ERR("Bad longname! (ss=%s ll=%s)\n This should never happen !\n"
1186 /* FIXME: fix for names like "C:\\" (ie. with more '\'s) */
1190 if ((p
-longpath
)>0) longlen
-= (p
-longpath
);
1191 lstrcpynA( p
, ll
, longlen
);
1193 /* Now, change all '/' to '\' */
1194 for (r
=p
; r
<(p
+longlen
); r
++ )
1195 if (r
[0]=='/') r
[0]='\\';
1196 return strlen(longpath
) - strlen(p
) + longlen
;
1200 return strlen(longpath
);
1204 /***********************************************************************
1205 * GetLongPathNameW (KERNEL32.@)
1207 DWORD WINAPI
GetLongPathNameW( LPCWSTR shortpath
, LPWSTR longpath
,
1210 DOS_FULL_NAME full_name
;
1212 LPSTR shortpathA
= HEAP_strdupWtoA( GetProcessHeap(), 0, shortpath
);
1214 /* FIXME: is it correct to always return a fully qualified short path? */
1215 if (DOSFS_GetFullName( shortpathA
, TRUE
, &full_name
))
1217 ret
= strlen( full_name
.short_name
);
1218 if (longlen
> 0 && !MultiByteToWideChar( CP_ACP
, 0, full_name
.long_name
, -1,
1219 longpath
, longlen
))
1220 longpath
[longlen
-1] = 0;
1222 HeapFree( GetProcessHeap(), 0, shortpathA
);
1227 /***********************************************************************
1228 * DOSFS_DoGetFullPathName
1230 * Implementation of GetFullPathNameA/W.
1232 * bon@elektron 000331:
1233 * A test for GetFullPathName with many pathological cases
1234 * now gives identical output for Wine and OSR2
1236 static DWORD
DOSFS_DoGetFullPathName( LPCSTR name
, DWORD len
, LPSTR result
,
1240 DOS_FULL_NAME full_name
;
1243 char drivecur
[]="c:.";
1245 int namelen
,drive
=0;
1247 if (!name
[0]) return 0;
1249 TRACE("passed '%s'\n", name
);
1252 /*drive letter given */
1254 driveletter
= name
[0];
1256 if ((name
[1]==':') && ((name
[2]=='\\') || (name
[2]=='/')))
1257 /*absolute path given */
1259 lstrcpynA(full_name
.short_name
,name
,MAX_PATHNAME_LEN
);
1260 drive
= (int)FILE_toupper(name
[0]) - 'A';
1265 drivecur
[0]=driveletter
;
1266 else if ((name
[0]=='\\') || (name
[0]=='/'))
1267 strcpy(drivecur
,"\\");
1269 strcpy(drivecur
,".");
1271 if (!DOSFS_GetFullName( drivecur
, FALSE
, &full_name
))
1273 FIXME("internal: error getting drive/path\n");
1276 /* find path that drive letter substitutes*/
1277 drive
= (int)FILE_toupper(full_name
.short_name
[0]) -0x41;
1278 root
= DRIVE_GetRoot(drive
);
1281 FIXME("internal: error getting DOS Drive Root\n");
1284 if (!strcmp(root
,"/"))
1286 /* we have just the last / and we need it. */
1287 p
= full_name
.long_name
;
1291 p
= full_name
.long_name
+strlen(root
);
1293 /* append long name (= unix name) to drive */
1294 lstrcpynA(full_name
.short_name
+2,p
,MAX_PATHNAME_LEN
-3);
1295 /* append name to treat */
1296 namelen
= strlen(full_name
.short_name
);
1299 p
+= +2; /* skip drive name when appending */
1300 if (namelen
+2 + strlen(p
) > MAX_PATHNAME_LEN
)
1302 FIXME("internal error: buffer too small\n");
1305 full_name
.short_name
[namelen
++] ='\\';
1306 full_name
.short_name
[namelen
] = 0;
1307 lstrcpynA(full_name
.short_name
+namelen
,p
,MAX_PATHNAME_LEN
-namelen
);
1309 /* reverse all slashes */
1310 for (p
=full_name
.short_name
;
1311 p
< full_name
.short_name
+strlen(full_name
.short_name
);
1317 /* Use memmove, as areas overlap */
1319 while ((p
= strstr(full_name
.short_name
,"\\..\\")))
1321 if (p
> full_name
.short_name
+2)
1324 q
= strrchr(full_name
.short_name
,'\\');
1325 memmove(q
+1,p
+4,strlen(p
+4)+1);
1329 memmove(full_name
.short_name
+3,p
+4,strlen(p
+4)+1);
1332 if ((full_name
.short_name
[2]=='.')&&(full_name
.short_name
[3]=='.'))
1334 /* This case istn't treated yet : c:..\test */
1335 memmove(full_name
.short_name
+2,full_name
.short_name
+4,
1336 strlen(full_name
.short_name
+4)+1);
1339 while ((p
= strstr(full_name
.short_name
,"\\.\\")))
1342 memmove(p
+1,p
+3,strlen(p
+3)+1);
1344 if (!(DRIVE_GetFlags(drive
) & DRIVE_CASE_PRESERVING
))
1345 for (p
= full_name
.short_name
; *p
; p
++) *p
= FILE_toupper(*p
);
1346 namelen
=strlen(full_name
.short_name
);
1347 if (!strcmp(full_name
.short_name
+namelen
-3,"\\.."))
1349 /* one more strange case: "c:\test\test1\.."
1351 *(full_name
.short_name
+namelen
-3)=0;
1352 q
= strrchr(full_name
.short_name
,'\\');
1355 if (full_name
.short_name
[namelen
-1]=='.')
1356 full_name
.short_name
[(namelen
--)-1] =0;
1358 if (full_name
.short_name
[namelen
-1]=='\\')
1359 full_name
.short_name
[(namelen
--)-1] =0;
1360 TRACE("got %s\n",full_name
.short_name
);
1362 /* If the lpBuffer buffer is too small, the return value is the
1363 size of the buffer, in characters, required to hold the path
1364 plus the terminating \0 (tested against win95osr2, bon 001118)
1366 ret
= strlen(full_name
.short_name
);
1369 /* don't touch anything when the buffer is not large enough */
1370 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
1376 MultiByteToWideChar( CP_ACP
, 0, full_name
.short_name
, -1, (LPWSTR
)result
, len
);
1378 lstrcpynA( result
, full_name
.short_name
, len
);
1381 TRACE("returning '%s'\n", full_name
.short_name
);
1386 /***********************************************************************
1387 * GetFullPathNameA (KERNEL32.@)
1389 * if the path closed with '\', *lastpart is 0
1391 DWORD WINAPI
GetFullPathNameA( LPCSTR name
, DWORD len
, LPSTR buffer
,
1394 DWORD ret
= DOSFS_DoGetFullPathName( name
, len
, buffer
, FALSE
);
1395 if (ret
&& (ret
<=len
) && buffer
&& lastpart
)
1397 LPSTR p
= buffer
+ strlen(buffer
);
1401 while ((p
> buffer
+ 2) && (*p
!= '\\')) p
--;
1404 else *lastpart
= NULL
;
1410 /***********************************************************************
1411 * GetFullPathNameW (KERNEL32.@)
1413 DWORD WINAPI
GetFullPathNameW( LPCWSTR name
, DWORD len
, LPWSTR buffer
,
1416 LPSTR nameA
= HEAP_strdupWtoA( GetProcessHeap(), 0, name
);
1417 DWORD ret
= DOSFS_DoGetFullPathName( nameA
, len
, (LPSTR
)buffer
, TRUE
);
1418 HeapFree( GetProcessHeap(), 0, nameA
);
1419 if (ret
&& (ret
<=len
) && buffer
&& lastpart
)
1421 LPWSTR p
= buffer
+ strlenW(buffer
);
1422 if (*p
!= (WCHAR
)'\\')
1424 while ((p
> buffer
+ 2) && (*p
!= (WCHAR
)'\\')) p
--;
1427 else *lastpart
= NULL
;
1433 /***********************************************************************
1434 * wine_get_unix_file_name (KERNEL32.@) Not a Windows API
1436 * Return the full Unix file name for a given path.
1438 BOOL WINAPI
wine_get_unix_file_name( LPCSTR dos
, LPSTR buffer
, DWORD len
)
1442 if ((ret
= DOSFS_GetFullName( dos
, FALSE
, &path
))) lstrcpynA( buffer
, path
.long_name
, len
);
1447 /***********************************************************************
1450 static int DOSFS_FindNextEx( FIND_FIRST_INFO
*info
, WIN32_FIND_DATAA
*entry
)
1452 DWORD attr
= info
->attr
| FA_UNUSED
| FA_ARCHIVE
| FA_RDONLY
| FILE_ATTRIBUTE_SYMLINK
;
1453 UINT flags
= DRIVE_GetFlags( info
->drive
);
1454 char *p
, buffer
[MAX_PATHNAME_LEN
];
1455 const char *drive_path
;
1457 LPCSTR long_name
, short_name
;
1458 BY_HANDLE_FILE_INFORMATION fileinfo
;
1461 if ((info
->attr
& ~(FA_UNUSED
| FA_ARCHIVE
| FA_RDONLY
)) == FA_LABEL
)
1463 if (info
->cur_pos
) return 0;
1464 entry
->dwFileAttributes
= FILE_ATTRIBUTE_LABEL
;
1465 RtlSecondsSince1970ToTime( (time_t)0, &entry
->ftCreationTime
);
1466 RtlSecondsSince1970ToTime( (time_t)0, &entry
->ftLastAccessTime
);
1467 RtlSecondsSince1970ToTime( (time_t)0, &entry
->ftLastWriteTime
);
1468 entry
->nFileSizeHigh
= 0;
1469 entry
->nFileSizeLow
= 0;
1470 entry
->dwReserved0
= 0;
1471 entry
->dwReserved1
= 0;
1472 DOSFS_ToDosDTAFormat( DRIVE_GetLabel( info
->drive
), entry
->cFileName
);
1473 strcpy( entry
->cAlternateFileName
, entry
->cFileName
);
1475 TRACE("returning %s (%s) as label\n",
1476 entry
->cFileName
, entry
->cAlternateFileName
);
1480 drive_path
= info
->path
+ strlen(DRIVE_GetRoot( info
->drive
));
1481 while ((*drive_path
== '/') || (*drive_path
== '\\')) drive_path
++;
1482 drive_root
= !*drive_path
;
1484 lstrcpynA( buffer
, info
->path
, sizeof(buffer
) - 1 );
1485 strcat( buffer
, "/" );
1486 p
= buffer
+ strlen(buffer
);
1488 while (DOSFS_ReadDir( info
->dir
, &long_name
, &short_name
))
1492 /* Don't return '.' and '..' in the root of the drive */
1493 if (drive_root
&& (long_name
[0] == '.') &&
1494 (!long_name
[1] || ((long_name
[1] == '.') && !long_name
[2])))
1497 /* Check the long mask */
1499 if (info
->long_mask
)
1501 if (!DOSFS_MatchLong( info
->long_mask
, long_name
,
1502 flags
& DRIVE_CASE_SENSITIVE
)) continue;
1505 /* Check the short mask */
1507 if (info
->short_mask
)
1511 DOSFS_Hash( long_name
, dos_name
, TRUE
,
1512 !(flags
& DRIVE_CASE_SENSITIVE
) );
1513 short_name
= dos_name
;
1515 if (!DOSFS_MatchShort( info
->short_mask
, short_name
)) continue;
1518 /* Check the file attributes */
1520 lstrcpynA( p
, long_name
, sizeof(buffer
) - (int)(p
- buffer
) );
1521 if (!FILE_Stat( buffer
, &fileinfo
))
1523 WARN("can't stat %s\n", buffer
);
1526 if ((fileinfo
.dwFileAttributes
& FILE_ATTRIBUTE_SYMLINK
) &&
1527 (fileinfo
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
))
1529 static int show_dir_symlinks
= -1;
1530 if (show_dir_symlinks
== -1)
1531 show_dir_symlinks
= PROFILE_GetWineIniBool("wine", "ShowDirSymlinks", 0);
1532 if (!show_dir_symlinks
) continue;
1535 if (fileinfo
.dwFileAttributes
& ~attr
) continue;
1537 /* We now have a matching entry; fill the result and return */
1539 entry
->dwFileAttributes
= fileinfo
.dwFileAttributes
;
1540 entry
->ftCreationTime
= fileinfo
.ftCreationTime
;
1541 entry
->ftLastAccessTime
= fileinfo
.ftLastAccessTime
;
1542 entry
->ftLastWriteTime
= fileinfo
.ftLastWriteTime
;
1543 entry
->nFileSizeHigh
= fileinfo
.nFileSizeHigh
;
1544 entry
->nFileSizeLow
= fileinfo
.nFileSizeLow
;
1547 DOSFS_ToDosDTAFormat( short_name
, entry
->cAlternateFileName
);
1549 DOSFS_Hash( long_name
, entry
->cAlternateFileName
, FALSE
,
1550 !(flags
& DRIVE_CASE_SENSITIVE
) );
1552 lstrcpynA( entry
->cFileName
, long_name
, sizeof(entry
->cFileName
) );
1553 if (!(flags
& DRIVE_CASE_PRESERVING
)) _strlwr( entry
->cFileName
);
1554 TRACE("returning %s (%s) %02lx %ld\n",
1555 entry
->cFileName
, entry
->cAlternateFileName
,
1556 entry
->dwFileAttributes
, entry
->nFileSizeLow
);
1559 return 0; /* End of directory */
1562 /***********************************************************************
1565 * Find the next matching file. Return the number of entries read to find
1566 * the matching one, or 0 if no more entries.
1567 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
1568 * file name mask. Either or both can be NULL.
1570 * NOTE: This is supposed to be only called by the int21 emulation
1571 * routines. Thus, we should own the Win16Mutex anyway.
1572 * Nevertheless, we explicitly enter it to ensure the static
1573 * directory cache is protected.
1575 int DOSFS_FindNext( const char *path
, const char *short_mask
,
1576 const char *long_mask
, int drive
, BYTE attr
,
1577 int skip
, WIN32_FIND_DATAA
*entry
)
1579 static FIND_FIRST_INFO info
;
1580 LPCSTR short_name
, long_name
;
1585 /* Check the cached directory */
1586 if (!(info
.dir
&& info
.path
== path
&& info
.short_mask
== short_mask
1587 && info
.long_mask
== long_mask
&& info
.drive
== drive
1588 && info
.attr
== attr
&& info
.cur_pos
<= skip
))
1590 /* Not in the cache, open it anew */
1591 if (info
.dir
) DOSFS_CloseDir( info
.dir
);
1593 info
.path
= (LPSTR
)path
;
1594 info
.long_mask
= (LPSTR
)long_mask
;
1595 info
.short_mask
= (LPSTR
)short_mask
;
1599 info
.dir
= DOSFS_OpenDir( info
.path
);
1602 /* Skip to desired position */
1603 while (info
.cur_pos
< skip
)
1604 if (info
.dir
&& DOSFS_ReadDir( info
.dir
, &long_name
, &short_name
))
1609 if (info
.dir
&& info
.cur_pos
== skip
&& DOSFS_FindNextEx( &info
, entry
))
1610 count
= info
.cur_pos
- skip
;
1616 if (info
.dir
) DOSFS_CloseDir( info
.dir
);
1617 memset( &info
, '\0', sizeof(info
) );
1625 /*************************************************************************
1626 * FindFirstFileExA (KERNEL32.@)
1628 HANDLE WINAPI
FindFirstFileExA(
1630 FINDEX_INFO_LEVELS fInfoLevelId
,
1631 LPVOID lpFindFileData
,
1632 FINDEX_SEARCH_OPS fSearchOp
,
1633 LPVOID lpSearchFilter
,
1634 DWORD dwAdditionalFlags
)
1636 DOS_FULL_NAME full_name
;
1638 FIND_FIRST_INFO
*info
;
1640 if ((fSearchOp
!= FindExSearchNameMatch
) || (dwAdditionalFlags
!= 0))
1642 FIXME("options not implemented 0x%08x 0x%08lx\n", fSearchOp
, dwAdditionalFlags
);
1643 return INVALID_HANDLE_VALUE
;
1646 switch(fInfoLevelId
)
1648 case FindExInfoStandard
:
1650 WIN32_FIND_DATAA
* data
= (WIN32_FIND_DATAA
*) lpFindFileData
;
1651 data
->dwReserved0
= data
->dwReserved1
= 0x0;
1652 if (!lpFileName
) return 0;
1653 if (!DOSFS_GetFullName( lpFileName
, FALSE
, &full_name
)) break;
1654 if (!(handle
= GlobalAlloc(GMEM_MOVEABLE
, sizeof(FIND_FIRST_INFO
)))) break;
1655 info
= (FIND_FIRST_INFO
*)GlobalLock( handle
);
1656 info
->path
= HeapAlloc( GetProcessHeap(), 0, strlen(full_name
.long_name
)+1 );
1657 strcpy( info
->path
, full_name
.long_name
);
1658 info
->long_mask
= strrchr( info
->path
, '/' );
1659 *(info
->long_mask
++) = '\0';
1660 info
->short_mask
= NULL
;
1662 if (lpFileName
[0] && (lpFileName
[1] == ':'))
1663 info
->drive
= FILE_toupper(*lpFileName
) - 'A';
1664 else info
->drive
= DRIVE_GetCurrentDrive();
1667 info
->dir
= DOSFS_OpenDir( info
->path
);
1669 GlobalUnlock( handle
);
1670 if (!FindNextFileA( handle
, data
))
1672 FindClose( handle
);
1673 SetLastError( ERROR_NO_MORE_FILES
);
1680 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId
);
1682 return INVALID_HANDLE_VALUE
;
1685 /*************************************************************************
1686 * FindFirstFileA (KERNEL32.@)
1688 HANDLE WINAPI
FindFirstFileA(
1690 WIN32_FIND_DATAA
*lpFindData
)
1692 return FindFirstFileExA(lpFileName
, FindExInfoStandard
, lpFindData
,
1693 FindExSearchNameMatch
, NULL
, 0);
1696 /*************************************************************************
1697 * FindFirstFileExW (KERNEL32.@)
1699 HANDLE WINAPI
FindFirstFileExW(
1701 FINDEX_INFO_LEVELS fInfoLevelId
,
1702 LPVOID lpFindFileData
,
1703 FINDEX_SEARCH_OPS fSearchOp
,
1704 LPVOID lpSearchFilter
,
1705 DWORD dwAdditionalFlags
)
1708 WIN32_FIND_DATAA dataA
;
1709 LPVOID _lpFindFileData
;
1712 switch(fInfoLevelId
)
1714 case FindExInfoStandard
:
1716 _lpFindFileData
= &dataA
;
1720 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId
);
1721 return INVALID_HANDLE_VALUE
;
1724 pathA
= HEAP_strdupWtoA( GetProcessHeap(), 0, lpFileName
);
1725 handle
= FindFirstFileExA(pathA
, fInfoLevelId
, _lpFindFileData
, fSearchOp
, lpSearchFilter
, dwAdditionalFlags
);
1726 HeapFree( GetProcessHeap(), 0, pathA
);
1727 if (handle
== INVALID_HANDLE_VALUE
) return handle
;
1729 switch(fInfoLevelId
)
1731 case FindExInfoStandard
:
1733 WIN32_FIND_DATAW
*dataW
= (WIN32_FIND_DATAW
*) lpFindFileData
;
1734 dataW
->dwFileAttributes
= dataA
.dwFileAttributes
;
1735 dataW
->ftCreationTime
= dataA
.ftCreationTime
;
1736 dataW
->ftLastAccessTime
= dataA
.ftLastAccessTime
;
1737 dataW
->ftLastWriteTime
= dataA
.ftLastWriteTime
;
1738 dataW
->nFileSizeHigh
= dataA
.nFileSizeHigh
;
1739 dataW
->nFileSizeLow
= dataA
.nFileSizeLow
;
1740 MultiByteToWideChar( CP_ACP
, 0, dataA
.cFileName
, -1,
1741 dataW
->cFileName
, sizeof(dataW
->cFileName
)/sizeof(WCHAR
) );
1742 MultiByteToWideChar( CP_ACP
, 0, dataA
.cAlternateFileName
, -1,
1743 dataW
->cAlternateFileName
,
1744 sizeof(dataW
->cAlternateFileName
)/sizeof(WCHAR
) );
1748 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId
);
1749 return INVALID_HANDLE_VALUE
;
1754 /*************************************************************************
1755 * FindFirstFileW (KERNEL32.@)
1757 HANDLE WINAPI
FindFirstFileW( LPCWSTR lpFileName
, WIN32_FIND_DATAW
*lpFindData
)
1759 return FindFirstFileExW(lpFileName
, FindExInfoStandard
, lpFindData
,
1760 FindExSearchNameMatch
, NULL
, 0);
1763 /*************************************************************************
1764 * FindNextFileA (KERNEL32.@)
1766 BOOL WINAPI
FindNextFileA( HANDLE handle
, WIN32_FIND_DATAA
*data
)
1768 FIND_FIRST_INFO
*info
;
1770 if ((handle
== INVALID_HANDLE_VALUE
) ||
1771 !(info
= (FIND_FIRST_INFO
*)GlobalLock( handle
)))
1773 SetLastError( ERROR_INVALID_HANDLE
);
1776 GlobalUnlock( handle
);
1777 if (!info
->path
|| !info
->dir
)
1779 SetLastError( ERROR_NO_MORE_FILES
);
1782 if (!DOSFS_FindNextEx( info
, data
))
1784 DOSFS_CloseDir( info
->dir
); info
->dir
= NULL
;
1785 HeapFree( GetProcessHeap(), 0, info
->path
);
1786 info
->path
= info
->long_mask
= NULL
;
1787 SetLastError( ERROR_NO_MORE_FILES
);
1794 /*************************************************************************
1795 * FindNextFileW (KERNEL32.@)
1797 BOOL WINAPI
FindNextFileW( HANDLE handle
, WIN32_FIND_DATAW
*data
)
1799 WIN32_FIND_DATAA dataA
;
1800 if (!FindNextFileA( handle
, &dataA
)) return FALSE
;
1801 data
->dwFileAttributes
= dataA
.dwFileAttributes
;
1802 data
->ftCreationTime
= dataA
.ftCreationTime
;
1803 data
->ftLastAccessTime
= dataA
.ftLastAccessTime
;
1804 data
->ftLastWriteTime
= dataA
.ftLastWriteTime
;
1805 data
->nFileSizeHigh
= dataA
.nFileSizeHigh
;
1806 data
->nFileSizeLow
= dataA
.nFileSizeLow
;
1807 MultiByteToWideChar( CP_ACP
, 0, dataA
.cFileName
, -1,
1808 data
->cFileName
, sizeof(data
->cFileName
)/sizeof(WCHAR
) );
1809 MultiByteToWideChar( CP_ACP
, 0, dataA
.cAlternateFileName
, -1,
1810 data
->cAlternateFileName
,
1811 sizeof(data
->cAlternateFileName
)/sizeof(WCHAR
) );
1815 /*************************************************************************
1816 * FindClose (KERNEL32.@)
1818 BOOL WINAPI
FindClose( HANDLE handle
)
1820 FIND_FIRST_INFO
*info
;
1822 if (handle
== INVALID_HANDLE_VALUE
) goto error
;
1826 if ((info
= (FIND_FIRST_INFO
*)GlobalLock( handle
)))
1828 if (info
->dir
) DOSFS_CloseDir( info
->dir
);
1829 if (info
->path
) HeapFree( GetProcessHeap(), 0, info
->path
);
1832 __EXCEPT(page_fault
)
1834 WARN("Illegal handle %x\n", handle
);
1835 SetLastError( ERROR_INVALID_HANDLE
);
1839 if (!info
) goto error
;
1840 GlobalUnlock( handle
);
1841 GlobalFree( handle
);
1845 SetLastError( ERROR_INVALID_HANDLE
);
1849 /***********************************************************************
1850 * DOSFS_UnixTimeToFileTime
1852 * Convert a Unix time to FILETIME format.
1853 * The FILETIME structure is a 64-bit value representing the number of
1854 * 100-nanosecond intervals since January 1, 1601, 0:00.
1855 * 'remainder' is the nonnegative number of 100-ns intervals
1856 * corresponding to the time fraction smaller than 1 second that
1857 * couldn't be stored in the time_t value.
1859 void DOSFS_UnixTimeToFileTime( time_t unix_time
, FILETIME
*filetime
,
1865 The time difference between 1 January 1601, 00:00:00 and
1866 1 January 1970, 00:00:00 is 369 years, plus the leap years
1867 from 1604 to 1968, excluding 1700, 1800, 1900.
1868 This makes (1968 - 1600) / 4 - 3 = 89 leap days, and a total
1871 Any day in that period had 24 * 60 * 60 = 86400 seconds.
1873 The time difference is 134774 * 86400 * 10000000, which can be written
1875 27111902 * 2^32 + 3577643008
1876 413 * 2^48 + 45534 * 2^32 + 54590 * 2^16 + 32768
1878 If you find that these constants are buggy, please change them in all
1879 instances in both conversion functions.
1882 There are two versions, one of them uses long long variables and
1883 is presumably faster but not ISO C. The other one uses standard C
1884 data types and operations but relies on the assumption that negative
1885 numbers are stored as 2's complement (-1 is 0xffff....). If this
1886 assumption is violated, dates before 1970 will not convert correctly.
1887 This should however work on any reasonable architecture where WINE
1892 Take care not to remove the casts. I have tested these functions
1893 (in both versions) for a lot of numbers. I would be interested in
1894 results on other compilers than GCC.
1896 The operations have been designed to account for the possibility
1897 of 64-bit time_t in future UNICES. Even the versions without
1898 internal long long numbers will work if time_t only is 64 bit.
1899 A 32-bit shift, which was necessary for that operation, turned out
1900 not to work correctly in GCC, besides giving the warning. So I
1901 used a double 16-bit shift instead. Numbers are in the ISO version
1902 represented by three limbs, the most significant with 32 bit, the
1903 other two with 16 bit each.
1905 As the modulo-operator % is not well-defined for negative numbers,
1906 negative divisors have been avoided in DOSFS_FileTimeToUnixTime.
1908 There might be quicker ways to do this in C. Certainly so in
1911 Claus Fischer, fischer@iue.tuwien.ac.at
1914 #if SIZEOF_LONG_LONG >= 8
1915 # define USE_LONG_LONG 1
1917 # define USE_LONG_LONG 0
1920 #if USE_LONG_LONG /* gcc supports long long type */
1922 long long int t
= unix_time
;
1924 t
+= 116444736000000000LL;
1926 filetime
->dwLowDateTime
= (UINT
)t
;
1927 filetime
->dwHighDateTime
= (UINT
)(t
>> 32);
1929 #else /* ISO version */
1931 UINT a0
; /* 16 bit, low bits */
1932 UINT a1
; /* 16 bit, medium bits */
1933 UINT a2
; /* 32 bit, high bits */
1935 /* Copy the unix time to a2/a1/a0 */
1936 a0
= unix_time
& 0xffff;
1937 a1
= (unix_time
>> 16) & 0xffff;
1938 /* This is obsolete if unix_time is only 32 bits, but it does not hurt.
1939 Do not replace this by >> 32, it gives a compiler warning and it does
1941 a2
= (unix_time
>= 0 ? (unix_time
>> 16) >> 16 :
1942 ~((~unix_time
>> 16) >> 16));
1944 /* Multiply a by 10000000 (a = a2/a1/a0)
1945 Split the factor into 10000 * 1000 which are both less than 0xffff. */
1947 a1
= a1
* 10000 + (a0
>> 16);
1948 a2
= a2
* 10000 + (a1
>> 16);
1953 a1
= a1
* 1000 + (a0
>> 16);
1954 a2
= a2
* 1000 + (a1
>> 16);
1958 /* Add the time difference and the remainder */
1959 a0
+= 32768 + (remainder
& 0xffff);
1960 a1
+= 54590 + (remainder
>> 16 ) + (a0
>> 16);
1961 a2
+= 27111902 + (a1
>> 16);
1966 filetime
->dwLowDateTime
= (a1
<< 16) + a0
;
1967 filetime
->dwHighDateTime
= a2
;
1972 /***********************************************************************
1973 * DOSFS_FileTimeToUnixTime
1975 * Convert a FILETIME format to Unix time.
1976 * If not NULL, 'remainder' contains the fractional part of the filetime,
1977 * in the range of [0..9999999] (even if time_t is negative).
1979 time_t DOSFS_FileTimeToUnixTime( const FILETIME
*filetime
, DWORD
*remainder
)
1981 /* Read the comment in the function DOSFS_UnixTimeToFileTime. */
1984 long long int t
= filetime
->dwHighDateTime
;
1986 t
+= (UINT
)filetime
->dwLowDateTime
;
1987 t
-= 116444736000000000LL;
1990 if (remainder
) *remainder
= 9999999 - (-t
- 1) % 10000000;
1991 return -1 - ((-t
- 1) / 10000000);
1995 if (remainder
) *remainder
= t
% 10000000;
1996 return t
/ 10000000;
1999 #else /* ISO version */
2001 UINT a0
; /* 16 bit, low bits */
2002 UINT a1
; /* 16 bit, medium bits */
2003 UINT a2
; /* 32 bit, high bits */
2004 UINT r
; /* remainder of division */
2005 unsigned int carry
; /* carry bit for subtraction */
2006 int negative
; /* whether a represents a negative value */
2008 /* Copy the time values to a2/a1/a0 */
2009 a2
= (UINT
)filetime
->dwHighDateTime
;
2010 a1
= ((UINT
)filetime
->dwLowDateTime
) >> 16;
2011 a0
= ((UINT
)filetime
->dwLowDateTime
) & 0xffff;
2013 /* Subtract the time difference */
2014 if (a0
>= 32768 ) a0
-= 32768 , carry
= 0;
2015 else a0
+= (1 << 16) - 32768 , carry
= 1;
2017 if (a1
>= 54590 + carry
) a1
-= 54590 + carry
, carry
= 0;
2018 else a1
+= (1 << 16) - 54590 - carry
, carry
= 1;
2020 a2
-= 27111902 + carry
;
2022 /* If a is negative, replace a by (-1-a) */
2023 negative
= (a2
>= ((UINT
)1) << 31);
2026 /* Set a to -a - 1 (a is a2/a1/a0) */
2032 /* Divide a by 10000000 (a = a2/a1/a0), put the rest into r.
2033 Split the divisor into 10000 * 1000 which are both less than 0xffff. */
2034 a1
+= (a2
% 10000) << 16;
2036 a0
+= (a1
% 10000) << 16;
2041 a1
+= (a2
% 1000) << 16;
2043 a0
+= (a1
% 1000) << 16;
2045 r
+= (a0
% 1000) * 10000;
2048 /* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
2051 /* Set a to -a - 1 (a is a2/a1/a0) */
2059 if (remainder
) *remainder
= r
;
2061 /* Do not replace this by << 32, it gives a compiler warning and it does
2063 return ((((time_t)a2
) << 16) << 16) + (a1
<< 16) + a0
;
2068 /***********************************************************************
2069 * MulDiv (KERNEL32.@)
2071 * Result of multiplication and division
2072 * -1: Overflow occurred or Divisor was 0
2079 #if SIZEOF_LONG_LONG >= 8
2082 if (!nDivisor
) return -1;
2084 /* We want to deal with a positive divisor to simplify the logic. */
2087 nMultiplicand
= - nMultiplicand
;
2088 nDivisor
= -nDivisor
;
2091 /* If the result is positive, we "add" to round. else, we subtract to round. */
2092 if ( ( (nMultiplicand
< 0) && (nMultiplier
< 0) ) ||
2093 ( (nMultiplicand
>= 0) && (nMultiplier
>= 0) ) )
2094 ret
= (((long long)nMultiplicand
* nMultiplier
) + (nDivisor
/2)) / nDivisor
;
2096 ret
= (((long long)nMultiplicand
* nMultiplier
) - (nDivisor
/2)) / nDivisor
;
2098 if ((ret
> 2147483647) || (ret
< -2147483647)) return -1;
2101 if (!nDivisor
) return -1;
2103 /* We want to deal with a positive divisor to simplify the logic. */
2106 nMultiplicand
= - nMultiplicand
;
2107 nDivisor
= -nDivisor
;
2110 /* If the result is positive, we "add" to round. else, we subtract to round. */
2111 if ( ( (nMultiplicand
< 0) && (nMultiplier
< 0) ) ||
2112 ( (nMultiplicand
>= 0) && (nMultiplier
>= 0) ) )
2113 return ((nMultiplicand
* nMultiplier
) + (nDivisor
/2)) / nDivisor
;
2115 return ((nMultiplicand
* nMultiplier
) - (nDivisor
/2)) / nDivisor
;
2121 /***********************************************************************
2122 * DosDateTimeToFileTime (KERNEL32.@)
2124 BOOL WINAPI
DosDateTimeToFileTime( WORD fatdate
, WORD fattime
, LPFILETIME ft
)
2129 time_t time1
, time2
;
2132 newtm
.tm_sec
= (fattime
& 0x1f) * 2;
2133 newtm
.tm_min
= (fattime
>> 5) & 0x3f;
2134 newtm
.tm_hour
= (fattime
>> 11);
2135 newtm
.tm_mday
= (fatdate
& 0x1f);
2136 newtm
.tm_mon
= ((fatdate
>> 5) & 0x0f) - 1;
2137 newtm
.tm_year
= (fatdate
>> 9) + 80;
2139 RtlSecondsSince1970ToTime( timegm(&newtm
), ft
);
2141 time1
= mktime(&newtm
);
2142 gtm
= gmtime(&time1
);
2143 time2
= mktime(gtm
);
2144 RtlSecondsSince1970ToTime( 2*time1
-time2
, ft
);
2150 /***********************************************************************
2151 * FileTimeToDosDateTime (KERNEL32.@)
2153 BOOL WINAPI
FileTimeToDosDateTime( const FILETIME
*ft
, LPWORD fatdate
,
2156 time_t unixtime
= DOSFS_FileTimeToUnixTime( ft
, NULL
);
2157 struct tm
*tm
= gmtime( &unixtime
);
2159 *fattime
= (tm
->tm_hour
<< 11) + (tm
->tm_min
<< 5) + (tm
->tm_sec
/ 2);
2161 *fatdate
= ((tm
->tm_year
- 80) << 9) + ((tm
->tm_mon
+ 1) << 5)
2167 /***********************************************************************
2168 * LocalFileTimeToFileTime (KERNEL32.@)
2170 BOOL WINAPI
LocalFileTimeToFileTime( const FILETIME
*localft
,
2177 /* Converts from local to UTC. */
2178 time_t localtime
= DOSFS_FileTimeToUnixTime( localft
, &remainder
);
2179 xtm
= gmtime( &localtime
);
2180 utctime
= mktime(xtm
);
2181 if(xtm
->tm_isdst
> 0) utctime
-=3600;
2182 DOSFS_UnixTimeToFileTime( utctime
, utcft
, remainder
);
2187 /***********************************************************************
2188 * FileTimeToLocalFileTime (KERNEL32.@)
2190 BOOL WINAPI
FileTimeToLocalFileTime( const FILETIME
*utcft
,
2191 LPFILETIME localft
)
2194 /* Converts from UTC to local. */
2195 time_t unixtime
= DOSFS_FileTimeToUnixTime( utcft
, &remainder
);
2197 struct tm
*xtm
= localtime( &unixtime
);
2200 localtime
= timegm(xtm
);
2201 DOSFS_UnixTimeToFileTime( localtime
, localft
, remainder
);
2207 xtm
= gmtime( &unixtime
);
2209 if(xtm
->tm_isdst
> 0) time
-=3600;
2210 DOSFS_UnixTimeToFileTime( 2*unixtime
-time
, localft
, remainder
);
2216 /***********************************************************************
2217 * FileTimeToSystemTime (KERNEL32.@)
2219 BOOL WINAPI
FileTimeToSystemTime( const FILETIME
*ft
, LPSYSTEMTIME syst
)
2223 time_t xtime
= DOSFS_FileTimeToUnixTime( ft
, &remainder
);
2224 xtm
= gmtime(&xtime
);
2225 syst
->wYear
= xtm
->tm_year
+1900;
2226 syst
->wMonth
= xtm
->tm_mon
+ 1;
2227 syst
->wDayOfWeek
= xtm
->tm_wday
;
2228 syst
->wDay
= xtm
->tm_mday
;
2229 syst
->wHour
= xtm
->tm_hour
;
2230 syst
->wMinute
= xtm
->tm_min
;
2231 syst
->wSecond
= xtm
->tm_sec
;
2232 syst
->wMilliseconds
= remainder
/ 10000;
2236 /***********************************************************************
2237 * QueryDosDeviceA (KERNEL32.@)
2239 * returns array of strings terminated by \0, terminated by \0
2241 DWORD WINAPI
QueryDosDeviceA(LPCSTR devname
,LPSTR target
,DWORD bufsize
)
2246 TRACE("(%s,...)\n", devname
? devname
: "<null>");
2248 /* return known MSDOS devices */
2249 static const char devices
[24] = "CON\0COM1\0COM2\0LPT1\0NUL\0\0";
2250 memcpy( target
, devices
, min(bufsize
,sizeof(devices
)) );
2251 return min(bufsize
,sizeof(devices
));
2253 /* In theory all that are possible and have been defined.
2254 * Now just those below, since mirc uses it to check for special files.
2256 * (It is more complex, and supports netmounted stuff, and \\.\ stuff,
2257 * but currently we just ignore that.)
2259 #define CHECK(x) (strstr(devname,#x)==devname)
2260 if (CHECK(con
) || CHECK(com
) || CHECK(lpt
) || CHECK(nul
)) {
2261 strcpy(buffer
,"\\DEV\\");
2262 strcat(buffer
,devname
);
2263 if ((s
=strchr(buffer
,':'))) *s
='\0';
2264 lstrcpynA(target
,buffer
,bufsize
);
2265 return strlen(buffer
)+1;
2267 if (strchr(devname
,':') || devname
[0]=='\\') {
2268 /* This might be a DOS device we do not handle yet ... */
2269 FIXME("(%s) not detected as DOS device!\n",devname
);
2271 SetLastError(ERROR_DEV_NOT_EXIST
);
2278 /***********************************************************************
2279 * QueryDosDeviceW (KERNEL32.@)
2281 * returns array of strings terminated by \0, terminated by \0
2283 DWORD WINAPI
QueryDosDeviceW(LPCWSTR devname
,LPWSTR target
,DWORD bufsize
)
2285 LPSTR devnameA
= devname
?HEAP_strdupWtoA(GetProcessHeap(),0,devname
):NULL
;
2286 LPSTR targetA
= (LPSTR
)HeapAlloc(GetProcessHeap(),0,bufsize
);
2287 DWORD ret
= QueryDosDeviceA(devnameA
,targetA
,bufsize
);
2289 ret
= MultiByteToWideChar( CP_ACP
, 0, targetA
, ret
, target
, bufsize
);
2290 if (devnameA
) HeapFree(GetProcessHeap(),0,devnameA
);
2291 if (targetA
) HeapFree(GetProcessHeap(),0,targetA
);
2296 /***********************************************************************
2297 * SystemTimeToFileTime (KERNEL32.@)
2299 BOOL WINAPI
SystemTimeToFileTime( const SYSTEMTIME
*syst
, LPFILETIME ft
)
2305 struct tm xtm
,*utc_tm
;
2306 time_t localtim
,utctime
;
2309 xtm
.tm_year
= syst
->wYear
-1900;
2310 xtm
.tm_mon
= syst
->wMonth
- 1;
2311 xtm
.tm_wday
= syst
->wDayOfWeek
;
2312 xtm
.tm_mday
= syst
->wDay
;
2313 xtm
.tm_hour
= syst
->wHour
;
2314 xtm
.tm_min
= syst
->wMinute
;
2315 xtm
.tm_sec
= syst
->wSecond
; /* this is UTC */
2318 utctime
= timegm(&xtm
);
2319 DOSFS_UnixTimeToFileTime( utctime
, ft
,
2320 syst
->wMilliseconds
* 10000 );
2322 localtim
= mktime(&xtm
); /* now we've got local time */
2323 utc_tm
= gmtime(&localtim
);
2324 utctime
= mktime(utc_tm
);
2325 DOSFS_UnixTimeToFileTime( 2*localtim
-utctime
, ft
,
2326 syst
->wMilliseconds
* 10000 );
2331 /***********************************************************************
2332 * DefineDosDeviceA (KERNEL32.@)
2334 BOOL WINAPI
DefineDosDeviceA(DWORD flags
,LPCSTR devname
,LPCSTR targetpath
) {
2335 FIXME("(0x%08lx,%s,%s),stub!\n",flags
,devname
,targetpath
);
2336 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
2341 --- 16 bit functions ---
2344 /*************************************************************************
2345 * FindFirstFile (KERNEL.413)
2347 HANDLE16 WINAPI
FindFirstFile16( LPCSTR path
, WIN32_FIND_DATAA
*data
)
2349 DOS_FULL_NAME full_name
;
2351 FIND_FIRST_INFO
*info
;
2353 data
->dwReserved0
= data
->dwReserved1
= 0x0;
2354 if (!path
) return 0;
2355 if (!DOSFS_GetFullName( path
, FALSE
, &full_name
))
2356 return INVALID_HANDLE_VALUE16
;
2357 if (!(handle
= GlobalAlloc16( GMEM_MOVEABLE
, sizeof(FIND_FIRST_INFO
) )))
2358 return INVALID_HANDLE_VALUE16
;
2359 info
= (FIND_FIRST_INFO
*)GlobalLock16( handle
);
2360 info
->path
= HeapAlloc( GetProcessHeap(), 0, strlen(full_name
.long_name
)+1 );
2361 strcpy( info
->path
, full_name
.long_name
);
2362 info
->long_mask
= strrchr( info
->path
, '/' );
2363 if (info
->long_mask
)
2364 *(info
->long_mask
++) = '\0';
2365 info
->short_mask
= NULL
;
2367 if (path
[0] && (path
[1] == ':')) info
->drive
= FILE_toupper(*path
) - 'A';
2368 else info
->drive
= DRIVE_GetCurrentDrive();
2371 info
->dir
= DOSFS_OpenDir( info
->path
);
2373 GlobalUnlock16( handle
);
2374 if (!FindNextFile16( handle
, data
))
2376 FindClose16( handle
);
2377 SetLastError( ERROR_NO_MORE_FILES
);
2378 return INVALID_HANDLE_VALUE16
;
2383 /*************************************************************************
2384 * FindNextFile (KERNEL.414)
2386 BOOL16 WINAPI
FindNextFile16( HANDLE16 handle
, WIN32_FIND_DATAA
*data
)
2388 FIND_FIRST_INFO
*info
;
2390 if ((handle
== INVALID_HANDLE_VALUE16
) ||
2391 !(info
= (FIND_FIRST_INFO
*)GlobalLock16( handle
)))
2393 SetLastError( ERROR_INVALID_HANDLE
);
2396 GlobalUnlock16( handle
);
2397 if (!info
->path
|| !info
->dir
)
2399 SetLastError( ERROR_NO_MORE_FILES
);
2402 if (!DOSFS_FindNextEx( info
, data
))
2404 DOSFS_CloseDir( info
->dir
); info
->dir
= NULL
;
2405 HeapFree( GetProcessHeap(), 0, info
->path
);
2406 info
->path
= info
->long_mask
= NULL
;
2407 SetLastError( ERROR_NO_MORE_FILES
);
2413 /*************************************************************************
2414 * FindClose (KERNEL.415)
2416 BOOL16 WINAPI
FindClose16( HANDLE16 handle
)
2418 FIND_FIRST_INFO
*info
;
2420 if ((handle
== INVALID_HANDLE_VALUE16
) ||
2421 !(info
= (FIND_FIRST_INFO
*)GlobalLock16( handle
)))
2423 SetLastError( ERROR_INVALID_HANDLE
);
2426 if (info
->dir
) DOSFS_CloseDir( info
->dir
);
2427 if (info
->path
) HeapFree( GetProcessHeap(), 0, info
->path
);
2428 GlobalUnlock16( handle
);
2429 GlobalFree16( handle
);