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
)
870 BOOL unixabsolute
= *name
== '/';
873 char *p_l
, *p_s
, *root
;
875 TRACE("%s (last=%d)\n", name
, check_last
);
877 if ((!*name
) || (*name
=='\n'))
878 { /* error code for Win98 */
879 SetLastError(ERROR_BAD_PATHNAME
);
883 if ((full
->drive
= DOSFS_GetPathDrive( &name
)) == -1) return FALSE
;
884 flags
= DRIVE_GetFlags( full
->drive
);
886 lstrcpynA( full
->long_name
, DRIVE_GetRoot( full
->drive
),
887 sizeof(full
->long_name
) );
888 if (full
->long_name
[1]) root
= full
->long_name
+ strlen(full
->long_name
);
889 else root
= full
->long_name
; /* root directory */
891 strcpy( full
->short_name
, "A:\\" );
892 full
->short_name
[0] += full
->drive
;
894 if ((*name
== '\\') || (*name
== '/')) /* Absolute path */
896 while ((*name
== '\\') || (*name
== '/')) name
++;
898 else if (!unixabsolute
) /* Relative path */
900 lstrcpynA( root
+ 1, DRIVE_GetUnixCwd( full
->drive
),
901 sizeof(full
->long_name
) - (root
- full
->long_name
) - 1 );
902 if (root
[1]) *root
= '/';
903 lstrcpynA( full
->short_name
+ 3, DRIVE_GetDosCwd( full
->drive
),
904 sizeof(full
->short_name
) - 3 );
907 p_l
= full
->long_name
[1] ? full
->long_name
+ strlen(full
->long_name
)
909 p_s
= full
->short_name
[3] ? full
->short_name
+ strlen(full
->short_name
)
910 : full
->short_name
+ 2;
913 while (*name
&& found
)
915 /* Check for '.' and '..' */
919 if (IS_END_OF_NAME(name
[1]))
922 while ((*name
== '\\') || (*name
== '/')) name
++;
925 else if ((name
[1] == '.') && IS_END_OF_NAME(name
[2]))
928 while ((*name
== '\\') || (*name
== '/')) name
++;
929 while ((p_l
> root
) && (*p_l
!= '/')) p_l
--;
930 while ((p_s
> full
->short_name
+ 2) && (*p_s
!= '\\')) p_s
--;
931 *p_l
= *p_s
= '\0'; /* Remove trailing separator */
936 /* Make sure buffers are large enough */
938 if ((p_s
>= full
->short_name
+ sizeof(full
->short_name
) - 14) ||
939 (p_l
>= full
->long_name
+ sizeof(full
->long_name
) - 1))
941 SetLastError( ERROR_PATH_NOT_FOUND
);
945 /* Get the long and short name matching the file name */
947 if ((found
= DOSFS_FindUnixName( full
->long_name
, name
, p_l
+ 1,
948 sizeof(full
->long_name
) - (p_l
- full
->long_name
) - 1,
949 p_s
+ 1, !(flags
& DRIVE_CASE_SENSITIVE
) )))
955 while (!IS_END_OF_NAME(*name
)) name
++;
957 else if (!check_last
)
961 while (!IS_END_OF_NAME(*name
) &&
962 (p_s
< full
->short_name
+ sizeof(full
->short_name
) - 1) &&
963 (p_l
< full
->long_name
+ sizeof(full
->long_name
) - 1))
965 *p_s
++ = FILE_tolower(*name
);
966 /* If the drive is case-sensitive we want to create new */
967 /* files in lower-case otherwise we can't reopen them */
968 /* under the same short name. */
969 if (flags
& DRIVE_CASE_SENSITIVE
) *p_l
++ = FILE_tolower(*name
);
973 /* Ignore trailing dots and spaces */
974 while(p_l
[-1] == '.' || p_l
[-1] == ' ') {
980 while ((*name
== '\\') || (*name
== '/')) name
++;
987 SetLastError( ERROR_FILE_NOT_FOUND
);
990 if (*name
) /* Not last */
992 SetLastError( ERROR_PATH_NOT_FOUND
);
996 if (!full
->long_name
[0]) strcpy( full
->long_name
, "/" );
997 if (!full
->short_name
[2]) strcpy( full
->short_name
+ 2, "\\" );
998 TRACE("returning %s = %s\n", full
->long_name
, full
->short_name
);
1003 /***********************************************************************
1004 * GetShortPathNameA (KERNEL32.@)
1008 * longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1009 * longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0
1011 * more observations ( with NT 3.51 (WinDD) ):
1012 * longpath <= 8.3 -> just copy longpath to shortpath
1014 * a) file does not exist -> return 0, LastError = ERROR_FILE_NOT_FOUND
1015 * b) file does exist -> set the short filename.
1016 * - trailing slashes are reproduced in the short name, even if the
1017 * file is not a directory
1018 * - the absolute/relative path of the short name is reproduced like found
1020 * - longpath and shortpath may have the same address
1021 * Peter Ganten, 1999
1023 DWORD WINAPI
GetShortPathNameA( LPCSTR longpath
, LPSTR shortpath
,
1026 DOS_FULL_NAME full_name
;
1028 DWORD sp
= 0, lp
= 0;
1031 BOOL unixabsolute
= *longpath
== '/';
1033 TRACE("%s\n", debugstr_a(longpath
));
1036 SetLastError(ERROR_INVALID_PARAMETER
);
1040 SetLastError(ERROR_BAD_PATHNAME
);
1044 if ( ( tmpshortpath
= HeapAlloc ( GetProcessHeap(), 0, MAX_PATHNAME_LEN
) ) == NULL
) {
1045 SetLastError ( ERROR_NOT_ENOUGH_MEMORY
);
1049 /* check for drive letter */
1050 if ( longpath
[1] == ':' ) {
1051 tmpshortpath
[0] = longpath
[0];
1052 tmpshortpath
[1] = ':';
1056 if ( ( drive
= DOSFS_GetPathDrive ( &longpath
)) == -1 ) return 0;
1057 flags
= DRIVE_GetFlags ( drive
);
1059 if ( unixabsolute
) {
1060 tmpshortpath
[0] = drive
+ 'A';
1061 tmpshortpath
[1] = ':';
1062 tmpshortpath
[2] = '\\';
1066 while ( longpath
[lp
] ) {
1068 /* check for path delimiters and reproduce them */
1069 if ( longpath
[lp
] == '\\' || longpath
[lp
] == '/' ) {
1070 if (!sp
|| tmpshortpath
[sp
-1]!= '\\')
1072 /* strip double "\\" */
1073 tmpshortpath
[sp
] = '\\';
1076 tmpshortpath
[sp
]=0;/*terminate string*/
1081 tmplen
= strcspn ( longpath
+ lp
, "\\/" );
1082 lstrcpynA ( tmpshortpath
+sp
, longpath
+ lp
, tmplen
+1 );
1084 /* Check, if the current element is a valid dos name */
1085 if ( DOSFS_ValidDOSName ( longpath
+ lp
, !(flags
& DRIVE_CASE_SENSITIVE
) ) ) {
1091 /* Check if the file exists and use the existing file name */
1092 if ( DOSFS_GetFullName ( tmpshortpath
, TRUE
, &full_name
) ) {
1093 strcpy( tmpshortpath
+sp
, strrchr ( full_name
.short_name
, '\\' ) + 1 );
1094 sp
+= strlen ( tmpshortpath
+sp
);
1099 TRACE("not found!\n" );
1100 SetLastError ( ERROR_FILE_NOT_FOUND
);
1103 tmpshortpath
[sp
] = 0;
1105 lstrcpynA ( shortpath
, tmpshortpath
, shortlen
);
1106 TRACE("returning %s\n", debugstr_a(shortpath
) );
1107 tmplen
= strlen ( tmpshortpath
);
1108 HeapFree ( GetProcessHeap(), 0, tmpshortpath
);
1114 /***********************************************************************
1115 * GetShortPathNameW (KERNEL32.@)
1117 DWORD WINAPI
GetShortPathNameW( LPCWSTR longpath
, LPWSTR shortpath
,
1120 LPSTR longpathA
, shortpathA
;
1123 longpathA
= HEAP_strdupWtoA( GetProcessHeap(), 0, longpath
);
1124 shortpathA
= HeapAlloc ( GetProcessHeap(), 0, shortlen
);
1126 ret
= GetShortPathNameA ( longpathA
, shortpathA
, shortlen
);
1127 if (shortlen
> 0 && !MultiByteToWideChar( CP_ACP
, 0, shortpathA
, -1, shortpath
, shortlen
))
1128 shortpath
[shortlen
-1] = 0;
1129 HeapFree( GetProcessHeap(), 0, longpathA
);
1130 HeapFree( GetProcessHeap(), 0, shortpathA
);
1136 /***********************************************************************
1137 * GetLongPathNameA (KERNEL32.@)
1140 * observed (Win2000):
1141 * shortpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1142 * shortpath="": LastError=ERROR_PATH_NOT_FOUND, ret=0
1144 DWORD WINAPI
GetLongPathNameA( LPCSTR shortpath
, LPSTR longpath
,
1147 DOS_FULL_NAME full_name
;
1148 char *p
, *r
, *ll
, *ss
;
1151 SetLastError(ERROR_INVALID_PARAMETER
);
1154 if (!shortpath
[0]) {
1155 SetLastError(ERROR_PATH_NOT_FOUND
);
1159 if (!DOSFS_GetFullName( shortpath
, TRUE
, &full_name
)) return 0;
1160 lstrcpynA( longpath
, full_name
.short_name
, longlen
);
1162 /* Do some hackery to get the long filename. */
1165 ss
=longpath
+strlen(longpath
);
1166 ll
=full_name
.long_name
+strlen(full_name
.long_name
);
1168 while (ss
>=longpath
)
1170 /* FIXME: aren't we more paranoid, than needed? */
1171 while ((ss
[0]=='\\') && (ss
>=longpath
)) ss
--;
1173 while ((ss
[0]!='\\') && (ss
>=longpath
)) ss
--;
1176 /* FIXME: aren't we more paranoid, than needed? */
1177 while ((ll
[0]=='/') && (ll
>=full_name
.long_name
)) ll
--;
1178 while ((ll
[0]!='/') && (ll
>=full_name
.long_name
)) ll
--;
1179 if (ll
<full_name
.long_name
)
1181 ERR("Bad longname! (ss=%s ll=%s)\n This should never happen !\n"
1188 /* FIXME: fix for names like "C:\\" (ie. with more '\'s) */
1192 if ((p
-longpath
)>0) longlen
-= (p
-longpath
);
1193 lstrcpynA( p
, ll
, longlen
);
1195 /* Now, change all '/' to '\' */
1196 for (r
=p
; r
<(p
+longlen
); r
++ )
1197 if (r
[0]=='/') r
[0]='\\';
1198 return strlen(longpath
) - strlen(p
) + longlen
;
1202 return strlen(longpath
);
1206 /***********************************************************************
1207 * GetLongPathNameW (KERNEL32.@)
1209 DWORD WINAPI
GetLongPathNameW( LPCWSTR shortpath
, LPWSTR longpath
,
1212 DOS_FULL_NAME full_name
;
1214 LPSTR shortpathA
= HEAP_strdupWtoA( GetProcessHeap(), 0, shortpath
);
1216 /* FIXME: is it correct to always return a fully qualified short path? */
1217 if (DOSFS_GetFullName( shortpathA
, TRUE
, &full_name
))
1219 ret
= strlen( full_name
.short_name
);
1220 if (longlen
> 0 && !MultiByteToWideChar( CP_ACP
, 0, full_name
.long_name
, -1,
1221 longpath
, longlen
))
1222 longpath
[longlen
-1] = 0;
1224 HeapFree( GetProcessHeap(), 0, shortpathA
);
1229 /***********************************************************************
1230 * DOSFS_DoGetFullPathName
1232 * Implementation of GetFullPathNameA/W.
1234 * bon@elektron 000331:
1235 * A test for GetFullPathName with many pathological cases
1236 * now gives identical output for Wine and OSR2
1238 static DWORD
DOSFS_DoGetFullPathName( LPCSTR name
, DWORD len
, LPSTR result
,
1242 DOS_FULL_NAME full_name
;
1245 char drivecur
[]="c:.";
1247 int namelen
,drive
=0;
1249 if (!name
[0]) return 0;
1251 TRACE("passed '%s'\n", name
);
1254 /*drive letter given */
1256 driveletter
= name
[0];
1258 if ((name
[1]==':') && ((name
[2]=='\\') || (name
[2]=='/')))
1259 /*absolute path given */
1261 lstrcpynA(full_name
.short_name
,name
,MAX_PATHNAME_LEN
);
1262 drive
= (int)FILE_toupper(name
[0]) - 'A';
1267 drivecur
[0]=driveletter
;
1268 else if ((name
[0]=='\\') || (name
[0]=='/'))
1269 strcpy(drivecur
,"\\");
1271 strcpy(drivecur
,".");
1273 if (!DOSFS_GetFullName( drivecur
, FALSE
, &full_name
))
1275 FIXME("internal: error getting drive/path\n");
1278 /* find path that drive letter substitutes*/
1279 drive
= (int)FILE_toupper(full_name
.short_name
[0]) -0x41;
1280 root
= DRIVE_GetRoot(drive
);
1283 FIXME("internal: error getting DOS Drive Root\n");
1286 if (!strcmp(root
,"/"))
1288 /* we have just the last / and we need it. */
1289 p
= full_name
.long_name
;
1293 p
= full_name
.long_name
+strlen(root
);
1295 /* append long name (= unix name) to drive */
1296 lstrcpynA(full_name
.short_name
+2,p
,MAX_PATHNAME_LEN
-3);
1297 /* append name to treat */
1298 namelen
= strlen(full_name
.short_name
);
1301 p
+= +2; /* skip drive name when appending */
1302 if (namelen
+2 + strlen(p
) > MAX_PATHNAME_LEN
)
1304 FIXME("internal error: buffer too small\n");
1307 full_name
.short_name
[namelen
++] ='\\';
1308 full_name
.short_name
[namelen
] = 0;
1309 lstrcpynA(full_name
.short_name
+namelen
,p
,MAX_PATHNAME_LEN
-namelen
);
1311 /* reverse all slashes */
1312 for (p
=full_name
.short_name
;
1313 p
< full_name
.short_name
+strlen(full_name
.short_name
);
1319 /* Use memmove, as areas overlap */
1321 while ((p
= strstr(full_name
.short_name
,"\\..\\")))
1323 if (p
> full_name
.short_name
+2)
1326 q
= strrchr(full_name
.short_name
,'\\');
1327 memmove(q
+1,p
+4,strlen(p
+4)+1);
1331 memmove(full_name
.short_name
+3,p
+4,strlen(p
+4)+1);
1334 if ((full_name
.short_name
[2]=='.')&&(full_name
.short_name
[3]=='.'))
1336 /* This case istn't treated yet : c:..\test */
1337 memmove(full_name
.short_name
+2,full_name
.short_name
+4,
1338 strlen(full_name
.short_name
+4)+1);
1341 while ((p
= strstr(full_name
.short_name
,"\\.\\")))
1344 memmove(p
+1,p
+3,strlen(p
+3)+1);
1346 if (!(DRIVE_GetFlags(drive
) & DRIVE_CASE_PRESERVING
))
1347 for (p
= full_name
.short_name
; *p
; p
++) *p
= FILE_toupper(*p
);
1348 namelen
=strlen(full_name
.short_name
);
1349 if (!strcmp(full_name
.short_name
+namelen
-3,"\\.."))
1351 /* one more strange case: "c:\test\test1\.."
1353 *(full_name
.short_name
+namelen
-3)=0;
1354 q
= strrchr(full_name
.short_name
,'\\');
1357 if (full_name
.short_name
[namelen
-1]=='.')
1358 full_name
.short_name
[(namelen
--)-1] =0;
1360 if (full_name
.short_name
[namelen
-1]=='\\')
1361 full_name
.short_name
[(namelen
--)-1] =0;
1362 TRACE("got %s\n",full_name
.short_name
);
1364 /* If the lpBuffer buffer is too small, the return value is the
1365 size of the buffer, in characters, required to hold the path
1366 plus the terminating \0 (tested against win95osr2, bon 001118)
1368 ret
= strlen(full_name
.short_name
);
1371 /* don't touch anything when the buffer is not large enough */
1372 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
1378 MultiByteToWideChar( CP_ACP
, 0, full_name
.short_name
, -1, (LPWSTR
)result
, len
);
1380 lstrcpynA( result
, full_name
.short_name
, len
);
1383 TRACE("returning '%s'\n", full_name
.short_name
);
1388 /***********************************************************************
1389 * GetFullPathNameA (KERNEL32.@)
1391 * if the path closed with '\', *lastpart is 0
1393 DWORD WINAPI
GetFullPathNameA( LPCSTR name
, DWORD len
, LPSTR buffer
,
1396 DWORD ret
= DOSFS_DoGetFullPathName( name
, len
, buffer
, FALSE
);
1397 if (ret
&& (ret
<=len
) && buffer
&& lastpart
)
1399 LPSTR p
= buffer
+ strlen(buffer
);
1403 while ((p
> buffer
+ 2) && (*p
!= '\\')) p
--;
1406 else *lastpart
= NULL
;
1412 /***********************************************************************
1413 * GetFullPathNameW (KERNEL32.@)
1415 DWORD WINAPI
GetFullPathNameW( LPCWSTR name
, DWORD len
, LPWSTR buffer
,
1418 LPSTR nameA
= HEAP_strdupWtoA( GetProcessHeap(), 0, name
);
1419 DWORD ret
= DOSFS_DoGetFullPathName( nameA
, len
, (LPSTR
)buffer
, TRUE
);
1420 HeapFree( GetProcessHeap(), 0, nameA
);
1421 if (ret
&& (ret
<=len
) && buffer
&& lastpart
)
1423 LPWSTR p
= buffer
+ strlenW(buffer
);
1424 if (*p
!= (WCHAR
)'\\')
1426 while ((p
> buffer
+ 2) && (*p
!= (WCHAR
)'\\')) p
--;
1429 else *lastpart
= NULL
;
1435 /***********************************************************************
1436 * wine_get_unix_file_name (KERNEL32.@) Not a Windows API
1438 * Return the full Unix file name for a given path.
1440 BOOL WINAPI
wine_get_unix_file_name( LPCSTR dos
, LPSTR buffer
, DWORD len
)
1444 if ((ret
= DOSFS_GetFullName( dos
, FALSE
, &path
))) lstrcpynA( buffer
, path
.long_name
, len
);
1449 /***********************************************************************
1452 static int DOSFS_FindNextEx( FIND_FIRST_INFO
*info
, WIN32_FIND_DATAA
*entry
)
1454 DWORD attr
= info
->attr
| FA_UNUSED
| FA_ARCHIVE
| FA_RDONLY
| FILE_ATTRIBUTE_SYMLINK
;
1455 UINT flags
= DRIVE_GetFlags( info
->drive
);
1456 char *p
, buffer
[MAX_PATHNAME_LEN
];
1457 const char *drive_path
;
1459 LPCSTR long_name
, short_name
;
1460 BY_HANDLE_FILE_INFORMATION fileinfo
;
1463 if ((info
->attr
& ~(FA_UNUSED
| FA_ARCHIVE
| FA_RDONLY
)) == FA_LABEL
)
1465 if (info
->cur_pos
) return 0;
1466 entry
->dwFileAttributes
= FILE_ATTRIBUTE_LABEL
;
1467 RtlSecondsSince1970ToTime( (time_t)0, &entry
->ftCreationTime
);
1468 RtlSecondsSince1970ToTime( (time_t)0, &entry
->ftLastAccessTime
);
1469 RtlSecondsSince1970ToTime( (time_t)0, &entry
->ftLastWriteTime
);
1470 entry
->nFileSizeHigh
= 0;
1471 entry
->nFileSizeLow
= 0;
1472 entry
->dwReserved0
= 0;
1473 entry
->dwReserved1
= 0;
1474 DOSFS_ToDosDTAFormat( DRIVE_GetLabel( info
->drive
), entry
->cFileName
);
1475 strcpy( entry
->cAlternateFileName
, entry
->cFileName
);
1477 TRACE("returning %s (%s) as label\n",
1478 entry
->cFileName
, entry
->cAlternateFileName
);
1482 drive_path
= info
->path
+ strlen(DRIVE_GetRoot( info
->drive
));
1483 while ((*drive_path
== '/') || (*drive_path
== '\\')) drive_path
++;
1484 drive_root
= !*drive_path
;
1486 lstrcpynA( buffer
, info
->path
, sizeof(buffer
) - 1 );
1487 strcat( buffer
, "/" );
1488 p
= buffer
+ strlen(buffer
);
1490 while (DOSFS_ReadDir( info
->dir
, &long_name
, &short_name
))
1494 /* Don't return '.' and '..' in the root of the drive */
1495 if (drive_root
&& (long_name
[0] == '.') &&
1496 (!long_name
[1] || ((long_name
[1] == '.') && !long_name
[2])))
1499 /* Check the long mask */
1501 if (info
->long_mask
)
1503 if (!DOSFS_MatchLong( info
->long_mask
, long_name
,
1504 flags
& DRIVE_CASE_SENSITIVE
)) continue;
1507 /* Check the short mask */
1509 if (info
->short_mask
)
1513 DOSFS_Hash( long_name
, dos_name
, TRUE
,
1514 !(flags
& DRIVE_CASE_SENSITIVE
) );
1515 short_name
= dos_name
;
1517 if (!DOSFS_MatchShort( info
->short_mask
, short_name
)) continue;
1520 /* Check the file attributes */
1522 lstrcpynA( p
, long_name
, sizeof(buffer
) - (int)(p
- buffer
) );
1523 if (!FILE_Stat( buffer
, &fileinfo
))
1525 WARN("can't stat %s\n", buffer
);
1528 if ((fileinfo
.dwFileAttributes
& FILE_ATTRIBUTE_SYMLINK
) &&
1529 (fileinfo
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
))
1531 static int show_dir_symlinks
= -1;
1532 if (show_dir_symlinks
== -1)
1533 show_dir_symlinks
= PROFILE_GetWineIniBool("wine", "ShowDirSymlinks", 0);
1534 if (!show_dir_symlinks
) continue;
1537 if (fileinfo
.dwFileAttributes
& ~attr
) continue;
1539 /* We now have a matching entry; fill the result and return */
1541 entry
->dwFileAttributes
= fileinfo
.dwFileAttributes
;
1542 entry
->ftCreationTime
= fileinfo
.ftCreationTime
;
1543 entry
->ftLastAccessTime
= fileinfo
.ftLastAccessTime
;
1544 entry
->ftLastWriteTime
= fileinfo
.ftLastWriteTime
;
1545 entry
->nFileSizeHigh
= fileinfo
.nFileSizeHigh
;
1546 entry
->nFileSizeLow
= fileinfo
.nFileSizeLow
;
1549 DOSFS_ToDosDTAFormat( short_name
, entry
->cAlternateFileName
);
1551 DOSFS_Hash( long_name
, entry
->cAlternateFileName
, FALSE
,
1552 !(flags
& DRIVE_CASE_SENSITIVE
) );
1554 lstrcpynA( entry
->cFileName
, long_name
, sizeof(entry
->cFileName
) );
1555 if (!(flags
& DRIVE_CASE_PRESERVING
)) _strlwr( entry
->cFileName
);
1556 TRACE("returning %s (%s) %02lx %ld\n",
1557 entry
->cFileName
, entry
->cAlternateFileName
,
1558 entry
->dwFileAttributes
, entry
->nFileSizeLow
);
1561 return 0; /* End of directory */
1564 /***********************************************************************
1567 * Find the next matching file. Return the number of entries read to find
1568 * the matching one, or 0 if no more entries.
1569 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
1570 * file name mask. Either or both can be NULL.
1572 * NOTE: This is supposed to be only called by the int21 emulation
1573 * routines. Thus, we should own the Win16Mutex anyway.
1574 * Nevertheless, we explicitly enter it to ensure the static
1575 * directory cache is protected.
1577 int DOSFS_FindNext( const char *path
, const char *short_mask
,
1578 const char *long_mask
, int drive
, BYTE attr
,
1579 int skip
, WIN32_FIND_DATAA
*entry
)
1581 static FIND_FIRST_INFO info
;
1582 LPCSTR short_name
, long_name
;
1587 /* Check the cached directory */
1588 if (!(info
.dir
&& info
.path
== path
&& info
.short_mask
== short_mask
1589 && info
.long_mask
== long_mask
&& info
.drive
== drive
1590 && info
.attr
== attr
&& info
.cur_pos
<= skip
))
1592 /* Not in the cache, open it anew */
1593 if (info
.dir
) DOSFS_CloseDir( info
.dir
);
1595 info
.path
= (LPSTR
)path
;
1596 info
.long_mask
= (LPSTR
)long_mask
;
1597 info
.short_mask
= (LPSTR
)short_mask
;
1601 info
.dir
= DOSFS_OpenDir( info
.path
);
1604 /* Skip to desired position */
1605 while (info
.cur_pos
< skip
)
1606 if (info
.dir
&& DOSFS_ReadDir( info
.dir
, &long_name
, &short_name
))
1611 if (info
.dir
&& info
.cur_pos
== skip
&& DOSFS_FindNextEx( &info
, entry
))
1612 count
= info
.cur_pos
- skip
;
1618 if (info
.dir
) DOSFS_CloseDir( info
.dir
);
1619 memset( &info
, '\0', sizeof(info
) );
1627 /*************************************************************************
1628 * FindFirstFileExA (KERNEL32.@)
1630 HANDLE WINAPI
FindFirstFileExA(
1632 FINDEX_INFO_LEVELS fInfoLevelId
,
1633 LPVOID lpFindFileData
,
1634 FINDEX_SEARCH_OPS fSearchOp
,
1635 LPVOID lpSearchFilter
,
1636 DWORD dwAdditionalFlags
)
1638 DOS_FULL_NAME full_name
;
1640 FIND_FIRST_INFO
*info
;
1642 if ((fSearchOp
!= FindExSearchNameMatch
) || (dwAdditionalFlags
!= 0))
1644 FIXME("options not implemented 0x%08x 0x%08lx\n", fSearchOp
, dwAdditionalFlags
);
1645 return INVALID_HANDLE_VALUE
;
1648 switch(fInfoLevelId
)
1650 case FindExInfoStandard
:
1652 WIN32_FIND_DATAA
* data
= (WIN32_FIND_DATAA
*) lpFindFileData
;
1653 data
->dwReserved0
= data
->dwReserved1
= 0x0;
1654 if (!lpFileName
) return 0;
1655 if (!DOSFS_GetFullName( lpFileName
, FALSE
, &full_name
)) break;
1656 if (!(handle
= GlobalAlloc(GMEM_MOVEABLE
, sizeof(FIND_FIRST_INFO
)))) break;
1657 info
= (FIND_FIRST_INFO
*)GlobalLock( handle
);
1658 info
->path
= HeapAlloc( GetProcessHeap(), 0, strlen(full_name
.long_name
)+1 );
1659 strcpy( info
->path
, full_name
.long_name
);
1660 info
->long_mask
= strrchr( info
->path
, '/' );
1661 *(info
->long_mask
++) = '\0';
1662 info
->short_mask
= NULL
;
1664 if (lpFileName
[0] && (lpFileName
[1] == ':'))
1665 info
->drive
= FILE_toupper(*lpFileName
) - 'A';
1666 else info
->drive
= DRIVE_GetCurrentDrive();
1669 info
->dir
= DOSFS_OpenDir( info
->path
);
1671 GlobalUnlock( handle
);
1672 if (!FindNextFileA( handle
, data
))
1674 FindClose( handle
);
1675 SetLastError( ERROR_NO_MORE_FILES
);
1682 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId
);
1684 return INVALID_HANDLE_VALUE
;
1687 /*************************************************************************
1688 * FindFirstFileA (KERNEL32.@)
1690 HANDLE WINAPI
FindFirstFileA(
1692 WIN32_FIND_DATAA
*lpFindData
)
1694 return FindFirstFileExA(lpFileName
, FindExInfoStandard
, lpFindData
,
1695 FindExSearchNameMatch
, NULL
, 0);
1698 /*************************************************************************
1699 * FindFirstFileExW (KERNEL32.@)
1701 HANDLE WINAPI
FindFirstFileExW(
1703 FINDEX_INFO_LEVELS fInfoLevelId
,
1704 LPVOID lpFindFileData
,
1705 FINDEX_SEARCH_OPS fSearchOp
,
1706 LPVOID lpSearchFilter
,
1707 DWORD dwAdditionalFlags
)
1710 WIN32_FIND_DATAA dataA
;
1711 LPVOID _lpFindFileData
;
1714 switch(fInfoLevelId
)
1716 case FindExInfoStandard
:
1718 _lpFindFileData
= &dataA
;
1722 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId
);
1723 return INVALID_HANDLE_VALUE
;
1726 pathA
= HEAP_strdupWtoA( GetProcessHeap(), 0, lpFileName
);
1727 handle
= FindFirstFileExA(pathA
, fInfoLevelId
, _lpFindFileData
, fSearchOp
, lpSearchFilter
, dwAdditionalFlags
);
1728 HeapFree( GetProcessHeap(), 0, pathA
);
1729 if (handle
== INVALID_HANDLE_VALUE
) return handle
;
1731 switch(fInfoLevelId
)
1733 case FindExInfoStandard
:
1735 WIN32_FIND_DATAW
*dataW
= (WIN32_FIND_DATAW
*) lpFindFileData
;
1736 dataW
->dwFileAttributes
= dataA
.dwFileAttributes
;
1737 dataW
->ftCreationTime
= dataA
.ftCreationTime
;
1738 dataW
->ftLastAccessTime
= dataA
.ftLastAccessTime
;
1739 dataW
->ftLastWriteTime
= dataA
.ftLastWriteTime
;
1740 dataW
->nFileSizeHigh
= dataA
.nFileSizeHigh
;
1741 dataW
->nFileSizeLow
= dataA
.nFileSizeLow
;
1742 MultiByteToWideChar( CP_ACP
, 0, dataA
.cFileName
, -1,
1743 dataW
->cFileName
, sizeof(dataW
->cFileName
)/sizeof(WCHAR
) );
1744 MultiByteToWideChar( CP_ACP
, 0, dataA
.cAlternateFileName
, -1,
1745 dataW
->cAlternateFileName
,
1746 sizeof(dataW
->cAlternateFileName
)/sizeof(WCHAR
) );
1750 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId
);
1751 return INVALID_HANDLE_VALUE
;
1756 /*************************************************************************
1757 * FindFirstFileW (KERNEL32.@)
1759 HANDLE WINAPI
FindFirstFileW( LPCWSTR lpFileName
, WIN32_FIND_DATAW
*lpFindData
)
1761 return FindFirstFileExW(lpFileName
, FindExInfoStandard
, lpFindData
,
1762 FindExSearchNameMatch
, NULL
, 0);
1765 /*************************************************************************
1766 * FindNextFileA (KERNEL32.@)
1768 BOOL WINAPI
FindNextFileA( HANDLE handle
, WIN32_FIND_DATAA
*data
)
1770 FIND_FIRST_INFO
*info
;
1772 if ((handle
== INVALID_HANDLE_VALUE
) ||
1773 !(info
= (FIND_FIRST_INFO
*)GlobalLock( handle
)))
1775 SetLastError( ERROR_INVALID_HANDLE
);
1778 GlobalUnlock( handle
);
1779 if (!info
->path
|| !info
->dir
)
1781 SetLastError( ERROR_NO_MORE_FILES
);
1784 if (!DOSFS_FindNextEx( info
, data
))
1786 DOSFS_CloseDir( info
->dir
); info
->dir
= NULL
;
1787 HeapFree( GetProcessHeap(), 0, info
->path
);
1788 info
->path
= info
->long_mask
= NULL
;
1789 SetLastError( ERROR_NO_MORE_FILES
);
1796 /*************************************************************************
1797 * FindNextFileW (KERNEL32.@)
1799 BOOL WINAPI
FindNextFileW( HANDLE handle
, WIN32_FIND_DATAW
*data
)
1801 WIN32_FIND_DATAA dataA
;
1802 if (!FindNextFileA( handle
, &dataA
)) return FALSE
;
1803 data
->dwFileAttributes
= dataA
.dwFileAttributes
;
1804 data
->ftCreationTime
= dataA
.ftCreationTime
;
1805 data
->ftLastAccessTime
= dataA
.ftLastAccessTime
;
1806 data
->ftLastWriteTime
= dataA
.ftLastWriteTime
;
1807 data
->nFileSizeHigh
= dataA
.nFileSizeHigh
;
1808 data
->nFileSizeLow
= dataA
.nFileSizeLow
;
1809 MultiByteToWideChar( CP_ACP
, 0, dataA
.cFileName
, -1,
1810 data
->cFileName
, sizeof(data
->cFileName
)/sizeof(WCHAR
) );
1811 MultiByteToWideChar( CP_ACP
, 0, dataA
.cAlternateFileName
, -1,
1812 data
->cAlternateFileName
,
1813 sizeof(data
->cAlternateFileName
)/sizeof(WCHAR
) );
1817 /*************************************************************************
1818 * FindClose (KERNEL32.@)
1820 BOOL WINAPI
FindClose( HANDLE handle
)
1822 FIND_FIRST_INFO
*info
;
1824 if (handle
== INVALID_HANDLE_VALUE
) goto error
;
1828 if ((info
= (FIND_FIRST_INFO
*)GlobalLock( handle
)))
1830 if (info
->dir
) DOSFS_CloseDir( info
->dir
);
1831 if (info
->path
) HeapFree( GetProcessHeap(), 0, info
->path
);
1834 __EXCEPT(page_fault
)
1836 WARN("Illegal handle %x\n", handle
);
1837 SetLastError( ERROR_INVALID_HANDLE
);
1841 if (!info
) goto error
;
1842 GlobalUnlock( handle
);
1843 GlobalFree( handle
);
1847 SetLastError( ERROR_INVALID_HANDLE
);
1851 /***********************************************************************
1852 * DOSFS_UnixTimeToFileTime
1854 * Convert a Unix time to FILETIME format.
1855 * The FILETIME structure is a 64-bit value representing the number of
1856 * 100-nanosecond intervals since January 1, 1601, 0:00.
1857 * 'remainder' is the nonnegative number of 100-ns intervals
1858 * corresponding to the time fraction smaller than 1 second that
1859 * couldn't be stored in the time_t value.
1861 void DOSFS_UnixTimeToFileTime( time_t unix_time
, FILETIME
*filetime
,
1867 The time difference between 1 January 1601, 00:00:00 and
1868 1 January 1970, 00:00:00 is 369 years, plus the leap years
1869 from 1604 to 1968, excluding 1700, 1800, 1900.
1870 This makes (1968 - 1600) / 4 - 3 = 89 leap days, and a total
1873 Any day in that period had 24 * 60 * 60 = 86400 seconds.
1875 The time difference is 134774 * 86400 * 10000000, which can be written
1877 27111902 * 2^32 + 3577643008
1878 413 * 2^48 + 45534 * 2^32 + 54590 * 2^16 + 32768
1880 If you find that these constants are buggy, please change them in all
1881 instances in both conversion functions.
1884 There are two versions, one of them uses long long variables and
1885 is presumably faster but not ISO C. The other one uses standard C
1886 data types and operations but relies on the assumption that negative
1887 numbers are stored as 2's complement (-1 is 0xffff....). If this
1888 assumption is violated, dates before 1970 will not convert correctly.
1889 This should however work on any reasonable architecture where WINE
1894 Take care not to remove the casts. I have tested these functions
1895 (in both versions) for a lot of numbers. I would be interested in
1896 results on other compilers than GCC.
1898 The operations have been designed to account for the possibility
1899 of 64-bit time_t in future UNICES. Even the versions without
1900 internal long long numbers will work if time_t only is 64 bit.
1901 A 32-bit shift, which was necessary for that operation, turned out
1902 not to work correctly in GCC, besides giving the warning. So I
1903 used a double 16-bit shift instead. Numbers are in the ISO version
1904 represented by three limbs, the most significant with 32 bit, the
1905 other two with 16 bit each.
1907 As the modulo-operator % is not well-defined for negative numbers,
1908 negative divisors have been avoided in DOSFS_FileTimeToUnixTime.
1910 There might be quicker ways to do this in C. Certainly so in
1913 Claus Fischer, fischer@iue.tuwien.ac.at
1916 #if SIZEOF_LONG_LONG >= 8
1917 # define USE_LONG_LONG 1
1919 # define USE_LONG_LONG 0
1922 #if USE_LONG_LONG /* gcc supports long long type */
1924 long long int t
= unix_time
;
1926 t
+= 116444736000000000LL;
1928 filetime
->dwLowDateTime
= (UINT
)t
;
1929 filetime
->dwHighDateTime
= (UINT
)(t
>> 32);
1931 #else /* ISO version */
1933 UINT a0
; /* 16 bit, low bits */
1934 UINT a1
; /* 16 bit, medium bits */
1935 UINT a2
; /* 32 bit, high bits */
1937 /* Copy the unix time to a2/a1/a0 */
1938 a0
= unix_time
& 0xffff;
1939 a1
= (unix_time
>> 16) & 0xffff;
1940 /* This is obsolete if unix_time is only 32 bits, but it does not hurt.
1941 Do not replace this by >> 32, it gives a compiler warning and it does
1943 a2
= (unix_time
>= 0 ? (unix_time
>> 16) >> 16 :
1944 ~((~unix_time
>> 16) >> 16));
1946 /* Multiply a by 10000000 (a = a2/a1/a0)
1947 Split the factor into 10000 * 1000 which are both less than 0xffff. */
1949 a1
= a1
* 10000 + (a0
>> 16);
1950 a2
= a2
* 10000 + (a1
>> 16);
1955 a1
= a1
* 1000 + (a0
>> 16);
1956 a2
= a2
* 1000 + (a1
>> 16);
1960 /* Add the time difference and the remainder */
1961 a0
+= 32768 + (remainder
& 0xffff);
1962 a1
+= 54590 + (remainder
>> 16 ) + (a0
>> 16);
1963 a2
+= 27111902 + (a1
>> 16);
1968 filetime
->dwLowDateTime
= (a1
<< 16) + a0
;
1969 filetime
->dwHighDateTime
= a2
;
1974 /***********************************************************************
1975 * DOSFS_FileTimeToUnixTime
1977 * Convert a FILETIME format to Unix time.
1978 * If not NULL, 'remainder' contains the fractional part of the filetime,
1979 * in the range of [0..9999999] (even if time_t is negative).
1981 time_t DOSFS_FileTimeToUnixTime( const FILETIME
*filetime
, DWORD
*remainder
)
1983 /* Read the comment in the function DOSFS_UnixTimeToFileTime. */
1986 long long int t
= filetime
->dwHighDateTime
;
1988 t
+= (UINT
)filetime
->dwLowDateTime
;
1989 t
-= 116444736000000000LL;
1992 if (remainder
) *remainder
= 9999999 - (-t
- 1) % 10000000;
1993 return -1 - ((-t
- 1) / 10000000);
1997 if (remainder
) *remainder
= t
% 10000000;
1998 return t
/ 10000000;
2001 #else /* ISO version */
2003 UINT a0
; /* 16 bit, low bits */
2004 UINT a1
; /* 16 bit, medium bits */
2005 UINT a2
; /* 32 bit, high bits */
2006 UINT r
; /* remainder of division */
2007 unsigned int carry
; /* carry bit for subtraction */
2008 int negative
; /* whether a represents a negative value */
2010 /* Copy the time values to a2/a1/a0 */
2011 a2
= (UINT
)filetime
->dwHighDateTime
;
2012 a1
= ((UINT
)filetime
->dwLowDateTime
) >> 16;
2013 a0
= ((UINT
)filetime
->dwLowDateTime
) & 0xffff;
2015 /* Subtract the time difference */
2016 if (a0
>= 32768 ) a0
-= 32768 , carry
= 0;
2017 else a0
+= (1 << 16) - 32768 , carry
= 1;
2019 if (a1
>= 54590 + carry
) a1
-= 54590 + carry
, carry
= 0;
2020 else a1
+= (1 << 16) - 54590 - carry
, carry
= 1;
2022 a2
-= 27111902 + carry
;
2024 /* If a is negative, replace a by (-1-a) */
2025 negative
= (a2
>= ((UINT
)1) << 31);
2028 /* Set a to -a - 1 (a is a2/a1/a0) */
2034 /* Divide a by 10000000 (a = a2/a1/a0), put the rest into r.
2035 Split the divisor into 10000 * 1000 which are both less than 0xffff. */
2036 a1
+= (a2
% 10000) << 16;
2038 a0
+= (a1
% 10000) << 16;
2043 a1
+= (a2
% 1000) << 16;
2045 a0
+= (a1
% 1000) << 16;
2047 r
+= (a0
% 1000) * 10000;
2050 /* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
2053 /* Set a to -a - 1 (a is a2/a1/a0) */
2061 if (remainder
) *remainder
= r
;
2063 /* Do not replace this by << 32, it gives a compiler warning and it does
2065 return ((((time_t)a2
) << 16) << 16) + (a1
<< 16) + a0
;
2070 /***********************************************************************
2071 * MulDiv (KERNEL32.@)
2073 * Result of multiplication and division
2074 * -1: Overflow occurred or Divisor was 0
2081 #if SIZEOF_LONG_LONG >= 8
2084 if (!nDivisor
) return -1;
2086 /* We want to deal with a positive divisor to simplify the logic. */
2089 nMultiplicand
= - nMultiplicand
;
2090 nDivisor
= -nDivisor
;
2093 /* If the result is positive, we "add" to round. else, we subtract to round. */
2094 if ( ( (nMultiplicand
< 0) && (nMultiplier
< 0) ) ||
2095 ( (nMultiplicand
>= 0) && (nMultiplier
>= 0) ) )
2096 ret
= (((long long)nMultiplicand
* nMultiplier
) + (nDivisor
/2)) / nDivisor
;
2098 ret
= (((long long)nMultiplicand
* nMultiplier
) - (nDivisor
/2)) / nDivisor
;
2100 if ((ret
> 2147483647) || (ret
< -2147483647)) return -1;
2103 if (!nDivisor
) return -1;
2105 /* We want to deal with a positive divisor to simplify the logic. */
2108 nMultiplicand
= - nMultiplicand
;
2109 nDivisor
= -nDivisor
;
2112 /* If the result is positive, we "add" to round. else, we subtract to round. */
2113 if ( ( (nMultiplicand
< 0) && (nMultiplier
< 0) ) ||
2114 ( (nMultiplicand
>= 0) && (nMultiplier
>= 0) ) )
2115 return ((nMultiplicand
* nMultiplier
) + (nDivisor
/2)) / nDivisor
;
2117 return ((nMultiplicand
* nMultiplier
) - (nDivisor
/2)) / nDivisor
;
2123 /***********************************************************************
2124 * DosDateTimeToFileTime (KERNEL32.@)
2126 BOOL WINAPI
DosDateTimeToFileTime( WORD fatdate
, WORD fattime
, LPFILETIME ft
)
2131 time_t time1
, time2
;
2134 newtm
.tm_sec
= (fattime
& 0x1f) * 2;
2135 newtm
.tm_min
= (fattime
>> 5) & 0x3f;
2136 newtm
.tm_hour
= (fattime
>> 11);
2137 newtm
.tm_mday
= (fatdate
& 0x1f);
2138 newtm
.tm_mon
= ((fatdate
>> 5) & 0x0f) - 1;
2139 newtm
.tm_year
= (fatdate
>> 9) + 80;
2141 RtlSecondsSince1970ToTime( timegm(&newtm
), ft
);
2143 time1
= mktime(&newtm
);
2144 gtm
= gmtime(&time1
);
2145 time2
= mktime(gtm
);
2146 RtlSecondsSince1970ToTime( 2*time1
-time2
, ft
);
2152 /***********************************************************************
2153 * FileTimeToDosDateTime (KERNEL32.@)
2155 BOOL WINAPI
FileTimeToDosDateTime( const FILETIME
*ft
, LPWORD fatdate
,
2158 time_t unixtime
= DOSFS_FileTimeToUnixTime( ft
, NULL
);
2159 struct tm
*tm
= gmtime( &unixtime
);
2161 *fattime
= (tm
->tm_hour
<< 11) + (tm
->tm_min
<< 5) + (tm
->tm_sec
/ 2);
2163 *fatdate
= ((tm
->tm_year
- 80) << 9) + ((tm
->tm_mon
+ 1) << 5)
2169 /***********************************************************************
2170 * LocalFileTimeToFileTime (KERNEL32.@)
2172 BOOL WINAPI
LocalFileTimeToFileTime( const FILETIME
*localft
,
2179 /* Converts from local to UTC. */
2180 time_t localtime
= DOSFS_FileTimeToUnixTime( localft
, &remainder
);
2181 xtm
= gmtime( &localtime
);
2182 utctime
= mktime(xtm
);
2183 if(xtm
->tm_isdst
> 0) utctime
-=3600;
2184 DOSFS_UnixTimeToFileTime( utctime
, utcft
, remainder
);
2189 /***********************************************************************
2190 * FileTimeToLocalFileTime (KERNEL32.@)
2192 BOOL WINAPI
FileTimeToLocalFileTime( const FILETIME
*utcft
,
2193 LPFILETIME localft
)
2196 /* Converts from UTC to local. */
2197 time_t unixtime
= DOSFS_FileTimeToUnixTime( utcft
, &remainder
);
2199 struct tm
*xtm
= localtime( &unixtime
);
2202 localtime
= timegm(xtm
);
2203 DOSFS_UnixTimeToFileTime( localtime
, localft
, remainder
);
2209 xtm
= gmtime( &unixtime
);
2211 if(xtm
->tm_isdst
> 0) time
-=3600;
2212 DOSFS_UnixTimeToFileTime( 2*unixtime
-time
, localft
, remainder
);
2218 /***********************************************************************
2219 * FileTimeToSystemTime (KERNEL32.@)
2221 BOOL WINAPI
FileTimeToSystemTime( const FILETIME
*ft
, LPSYSTEMTIME syst
)
2225 time_t xtime
= DOSFS_FileTimeToUnixTime( ft
, &remainder
);
2226 xtm
= gmtime(&xtime
);
2227 syst
->wYear
= xtm
->tm_year
+1900;
2228 syst
->wMonth
= xtm
->tm_mon
+ 1;
2229 syst
->wDayOfWeek
= xtm
->tm_wday
;
2230 syst
->wDay
= xtm
->tm_mday
;
2231 syst
->wHour
= xtm
->tm_hour
;
2232 syst
->wMinute
= xtm
->tm_min
;
2233 syst
->wSecond
= xtm
->tm_sec
;
2234 syst
->wMilliseconds
= remainder
/ 10000;
2238 /***********************************************************************
2239 * QueryDosDeviceA (KERNEL32.@)
2241 * returns array of strings terminated by \0, terminated by \0
2243 DWORD WINAPI
QueryDosDeviceA(LPCSTR devname
,LPSTR target
,DWORD bufsize
)
2248 TRACE("(%s,...)\n", devname
? devname
: "<null>");
2250 /* return known MSDOS devices */
2251 static const char devices
[24] = "CON\0COM1\0COM2\0LPT1\0NUL\0\0";
2252 memcpy( target
, devices
, min(bufsize
,sizeof(devices
)) );
2253 return min(bufsize
,sizeof(devices
));
2255 /* In theory all that are possible and have been defined.
2256 * Now just those below, since mirc uses it to check for special files.
2258 * (It is more complex, and supports netmounted stuff, and \\.\ stuff,
2259 * but currently we just ignore that.)
2261 #define CHECK(x) (strstr(devname,#x)==devname)
2262 if (CHECK(con
) || CHECK(com
) || CHECK(lpt
) || CHECK(nul
)) {
2263 strcpy(buffer
,"\\DEV\\");
2264 strcat(buffer
,devname
);
2265 if ((s
=strchr(buffer
,':'))) *s
='\0';
2266 lstrcpynA(target
,buffer
,bufsize
);
2267 return strlen(buffer
)+1;
2269 if (strchr(devname
,':') || devname
[0]=='\\') {
2270 /* This might be a DOS device we do not handle yet ... */
2271 FIXME("(%s) not detected as DOS device!\n",devname
);
2273 SetLastError(ERROR_DEV_NOT_EXIST
);
2280 /***********************************************************************
2281 * QueryDosDeviceW (KERNEL32.@)
2283 * returns array of strings terminated by \0, terminated by \0
2285 DWORD WINAPI
QueryDosDeviceW(LPCWSTR devname
,LPWSTR target
,DWORD bufsize
)
2287 LPSTR devnameA
= devname
?HEAP_strdupWtoA(GetProcessHeap(),0,devname
):NULL
;
2288 LPSTR targetA
= (LPSTR
)HeapAlloc(GetProcessHeap(),0,bufsize
);
2289 DWORD ret
= QueryDosDeviceA(devnameA
,targetA
,bufsize
);
2291 ret
= MultiByteToWideChar( CP_ACP
, 0, targetA
, ret
, target
, bufsize
);
2292 if (devnameA
) HeapFree(GetProcessHeap(),0,devnameA
);
2293 if (targetA
) HeapFree(GetProcessHeap(),0,targetA
);
2298 /***********************************************************************
2299 * SystemTimeToFileTime (KERNEL32.@)
2301 BOOL WINAPI
SystemTimeToFileTime( const SYSTEMTIME
*syst
, LPFILETIME ft
)
2307 struct tm xtm
,*utc_tm
;
2308 time_t localtim
,utctime
;
2311 xtm
.tm_year
= syst
->wYear
-1900;
2312 xtm
.tm_mon
= syst
->wMonth
- 1;
2313 xtm
.tm_wday
= syst
->wDayOfWeek
;
2314 xtm
.tm_mday
= syst
->wDay
;
2315 xtm
.tm_hour
= syst
->wHour
;
2316 xtm
.tm_min
= syst
->wMinute
;
2317 xtm
.tm_sec
= syst
->wSecond
; /* this is UTC */
2320 utctime
= timegm(&xtm
);
2321 DOSFS_UnixTimeToFileTime( utctime
, ft
,
2322 syst
->wMilliseconds
* 10000 );
2324 localtim
= mktime(&xtm
); /* now we've got local time */
2325 utc_tm
= gmtime(&localtim
);
2326 utctime
= mktime(utc_tm
);
2327 DOSFS_UnixTimeToFileTime( 2*localtim
-utctime
, ft
,
2328 syst
->wMilliseconds
* 10000 );
2333 /***********************************************************************
2334 * DefineDosDeviceA (KERNEL32.@)
2336 BOOL WINAPI
DefineDosDeviceA(DWORD flags
,LPCSTR devname
,LPCSTR targetpath
) {
2337 FIXME("(0x%08lx,%s,%s),stub!\n",flags
,devname
,targetpath
);
2338 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
2343 --- 16 bit functions ---
2346 /*************************************************************************
2347 * FindFirstFile (KERNEL.413)
2349 HANDLE16 WINAPI
FindFirstFile16( LPCSTR path
, WIN32_FIND_DATAA
*data
)
2351 DOS_FULL_NAME full_name
;
2353 FIND_FIRST_INFO
*info
;
2355 data
->dwReserved0
= data
->dwReserved1
= 0x0;
2356 if (!path
) return 0;
2357 if (!DOSFS_GetFullName( path
, FALSE
, &full_name
))
2358 return INVALID_HANDLE_VALUE16
;
2359 if (!(handle
= GlobalAlloc16( GMEM_MOVEABLE
, sizeof(FIND_FIRST_INFO
) )))
2360 return INVALID_HANDLE_VALUE16
;
2361 info
= (FIND_FIRST_INFO
*)GlobalLock16( handle
);
2362 info
->path
= HeapAlloc( GetProcessHeap(), 0, strlen(full_name
.long_name
)+1 );
2363 strcpy( info
->path
, full_name
.long_name
);
2364 info
->long_mask
= strrchr( info
->path
, '/' );
2365 if (info
->long_mask
)
2366 *(info
->long_mask
++) = '\0';
2367 info
->short_mask
= NULL
;
2369 if (path
[0] && (path
[1] == ':')) info
->drive
= FILE_toupper(*path
) - 'A';
2370 else info
->drive
= DRIVE_GetCurrentDrive();
2373 info
->dir
= DOSFS_OpenDir( info
->path
);
2375 GlobalUnlock16( handle
);
2376 if (!FindNextFile16( handle
, data
))
2378 FindClose16( handle
);
2379 SetLastError( ERROR_NO_MORE_FILES
);
2380 return INVALID_HANDLE_VALUE16
;
2385 /*************************************************************************
2386 * FindNextFile (KERNEL.414)
2388 BOOL16 WINAPI
FindNextFile16( HANDLE16 handle
, WIN32_FIND_DATAA
*data
)
2390 FIND_FIRST_INFO
*info
;
2392 if ((handle
== INVALID_HANDLE_VALUE16
) ||
2393 !(info
= (FIND_FIRST_INFO
*)GlobalLock16( handle
)))
2395 SetLastError( ERROR_INVALID_HANDLE
);
2398 GlobalUnlock16( handle
);
2399 if (!info
->path
|| !info
->dir
)
2401 SetLastError( ERROR_NO_MORE_FILES
);
2404 if (!DOSFS_FindNextEx( info
, data
))
2406 DOSFS_CloseDir( info
->dir
); info
->dir
= NULL
;
2407 HeapFree( GetProcessHeap(), 0, info
->path
);
2408 info
->path
= info
->long_mask
= NULL
;
2409 SetLastError( ERROR_NO_MORE_FILES
);
2415 /*************************************************************************
2416 * FindClose (KERNEL.415)
2418 BOOL16 WINAPI
FindClose16( HANDLE16 handle
)
2420 FIND_FIRST_INFO
*info
;
2422 if ((handle
== INVALID_HANDLE_VALUE16
) ||
2423 !(info
= (FIND_FIRST_INFO
*)GlobalLock16( handle
)))
2425 SetLastError( ERROR_INVALID_HANDLE
);
2428 if (info
->dir
) DOSFS_CloseDir( info
->dir
);
2429 if (info
->path
) HeapFree( GetProcessHeap(), 0, info
->path
);
2430 GlobalUnlock16( handle
);
2431 GlobalFree16( handle
);