2 * DOS file system functions
4 * Copyright 1993 Erik Bos
5 * Copyright 1996 Alexandre Julliard
10 #include <sys/types.h>
14 #ifdef HAVE_SYS_ERRNO_H
15 #include <sys/errno.h>
21 #include <sys/ioctl.h>
29 #include "wine/unicode.h"
30 #include "wine/winbase16.h"
37 #include "wine/server.h"
38 #include "msvcrt/excpt.h"
40 #include "debugtools.h"
42 DEFAULT_DEBUG_CHANNEL(dosfs
);
43 DECLARE_DEBUG_CHANNEL(file
);
45 /* Define the VFAT ioctl to get both short and long file names */
46 /* FIXME: is it possible to get this to work on other systems? */
48 /* We want the real kernel dirent structure, not the libc one */
53 unsigned short d_reclen
;
57 #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, KERNEL_DIRENT [2] )
60 #undef VFAT_IOCTL_READDIR_BOTH /* just in case... */
63 /* Chars we don't want to see in DOS file names */
64 #define INVALID_DOS_CHARS "*?<>|\"+=,;[] \345"
66 static const DOS_DEVICE DOSFS_Devices
[] =
67 /* name, device flags (see Int 21/AX=0x4400) */
81 { "SCSIMGR$", 0xc0c0 },
83 { "EMMXXXX0", 0x0000 }
86 #define GET_DRIVE(path) \
87 (((path)[1] == ':') ? FILE_toupper((path)[0]) - 'A' : DOSFS_CurDrive)
89 /* Directory info for DOSFS_ReadDir */
93 #ifdef VFAT_IOCTL_READDIR_BOTH
96 KERNEL_DIRENT dirent
[2];
100 /* Info structure for FindFirstFile handle */
113 static WINE_EXCEPTION_FILTER(page_fault
)
115 if (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION
)
116 return EXCEPTION_EXECUTE_HANDLER
;
117 return EXCEPTION_CONTINUE_SEARCH
;
121 /***********************************************************************
124 * Return 1 if Unix file 'name' is also a valid MS-DOS name
125 * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
126 * File name can be terminated by '\0', '\\' or '/'.
128 static int DOSFS_ValidDOSName( const char *name
, int ignore_case
)
130 static const char invalid_chars
[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS
;
131 const char *p
= name
;
132 const char *invalid
= ignore_case
? (invalid_chars
+ 26) : invalid_chars
;
137 /* Check for "." and ".." */
140 /* All other names beginning with '.' are invalid */
141 return (IS_END_OF_NAME(*p
));
143 while (!IS_END_OF_NAME(*p
))
145 if (strchr( invalid
, *p
)) return 0; /* Invalid char */
146 if (*p
== '.') break; /* Start of the extension */
147 if (++len
> 8) return 0; /* Name too long */
150 if (*p
!= '.') return 1; /* End of name */
152 if (IS_END_OF_NAME(*p
)) return 0; /* Empty extension not allowed */
154 while (!IS_END_OF_NAME(*p
))
156 if (strchr( invalid
, *p
)) return 0; /* Invalid char */
157 if (*p
== '.') return 0; /* Second extension not allowed */
158 if (++len
> 3) return 0; /* Extension too long */
165 /***********************************************************************
166 * DOSFS_ToDosFCBFormat
168 * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
169 * expanding wild cards and converting to upper-case in the process.
170 * File name can be terminated by '\0', '\\' or '/'.
171 * Return FALSE if the name is not a valid DOS name.
172 * 'buffer' must be at least 12 characters long.
174 BOOL
DOSFS_ToDosFCBFormat( LPCSTR name
, LPSTR buffer
)
176 static const char invalid_chars
[] = INVALID_DOS_CHARS
;
177 const char *p
= name
;
180 /* Check for "." and ".." */
184 strcpy( buffer
, ". " );
190 return (!*p
|| (*p
== '/') || (*p
== '\\'));
193 for (i
= 0; i
< 8; i
++)
210 if (strchr( invalid_chars
, *p
)) return FALSE
;
211 buffer
[i
] = FILE_toupper(*p
);
219 /* Skip all chars after wildcard up to first dot */
220 while (*p
&& (*p
!= '/') && (*p
!= '\\') && (*p
!= '.')) p
++;
224 /* Check if name too long */
225 if (*p
&& (*p
!= '/') && (*p
!= '\\') && (*p
!= '.')) return FALSE
;
227 if (*p
== '.') p
++; /* Skip dot */
229 for (i
= 8; i
< 11; i
++)
239 return FALSE
; /* Second extension not allowed */
247 if (strchr( invalid_chars
, *p
)) return FALSE
;
248 buffer
[i
] = FILE_toupper(*p
);
255 /* at most 3 character of the extension are processed
256 * is something behind this ?
258 while (*p
== '*' || *p
== ' ') p
++; /* skip wildcards and spaces */
259 return IS_END_OF_NAME(*p
);
263 /***********************************************************************
264 * DOSFS_ToDosDTAFormat
266 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
267 * converting to upper-case in the process.
268 * File name can be terminated by '\0', '\\' or '/'.
269 * 'buffer' must be at least 13 characters long.
271 static void DOSFS_ToDosDTAFormat( LPCSTR name
, LPSTR buffer
)
275 memcpy( buffer
, name
, 8 );
277 while ((p
> buffer
) && (p
[-1] == ' ')) p
--;
279 memcpy( p
, name
+ 8, 3 );
281 while (p
[-1] == ' ') p
--;
282 if (p
[-1] == '.') p
--;
287 /***********************************************************************
290 * Check a DOS file name against a mask (both in FCB format).
292 static int DOSFS_MatchShort( const char *mask
, const char *name
)
295 for (i
= 11; i
> 0; i
--, mask
++, name
++)
296 if ((*mask
!= '?') && (*mask
!= *name
)) return 0;
301 /***********************************************************************
304 * Check a long file name against a mask.
306 * Tests (done in W95 DOS shell - case insensitive):
307 * *.txt test1.test.txt *
309 * *.t??????.t* test1.ta.tornado.txt *
310 * *tornado* test1.ta.tornado.txt *
311 * t*t test1.ta.tornado.txt *
313 * ?est??? test1.txt -
314 * *test1.txt* test1.txt *
315 * h?l?o*t.dat hellothisisatest.dat *
317 static int DOSFS_MatchLong( const char *mask
, const char *name
,
320 const char *lastjoker
= NULL
;
321 const char *next_to_retry
= NULL
;
323 if (!strcmp( mask
, "*.*" )) return 1;
324 while (*name
&& *mask
)
329 while (*mask
== '*') mask
++; /* Skip consecutive '*' */
331 if (!*mask
) return 1; /* end of mask is all '*', so match */
333 /* skip to the next match after the joker(s) */
334 if (case_sensitive
) while (*name
&& (*name
!= *mask
)) name
++;
335 else while (*name
&& (FILE_toupper(*name
) != FILE_toupper(*mask
))) name
++;
338 next_to_retry
= name
;
340 else if (*mask
!= '?')
345 if (*mask
!= *name
) mismatch
= 1;
349 if (FILE_toupper(*mask
) != FILE_toupper(*name
)) mismatch
= 1;
363 else /* mismatch ! */
365 if (lastjoker
) /* we had an '*', so we can try unlimitedly */
369 /* this scan sequence was a mismatch, so restart
370 * 1 char after the first char we checked last time */
372 name
= next_to_retry
;
375 return 0; /* bad luck */
384 while ((*mask
== '.') || (*mask
== '*'))
385 mask
++; /* Ignore trailing '.' or '*' in mask */
386 return (!*name
&& !*mask
);
390 /***********************************************************************
393 static DOS_DIR
*DOSFS_OpenDir( LPCSTR path
)
395 DOS_DIR
*dir
= HeapAlloc( GetProcessHeap(), 0, sizeof(*dir
) );
398 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
402 /* Treat empty path as root directory. This simplifies path split into
403 directory and mask in several other places */
404 if (!*path
) path
= "/";
406 #ifdef VFAT_IOCTL_READDIR_BOTH
408 /* Check if the VFAT ioctl is supported on this directory */
410 if ((dir
->fd
= open( path
, O_RDONLY
)) != -1)
412 if (ioctl( dir
->fd
, VFAT_IOCTL_READDIR_BOTH
, (long)dir
->dirent
) == -1)
419 /* Set the file pointer back at the start of the directory */
420 lseek( dir
->fd
, 0, SEEK_SET
);
425 #endif /* VFAT_IOCTL_READDIR_BOTH */
427 /* Now use the standard opendir/readdir interface */
429 if (!(dir
->dir
= opendir( path
)))
431 HeapFree( GetProcessHeap(), 0, dir
);
438 /***********************************************************************
441 static void DOSFS_CloseDir( DOS_DIR
*dir
)
443 #ifdef VFAT_IOCTL_READDIR_BOTH
444 if (dir
->fd
!= -1) close( dir
->fd
);
445 #endif /* VFAT_IOCTL_READDIR_BOTH */
446 if (dir
->dir
) closedir( dir
->dir
);
447 HeapFree( GetProcessHeap(), 0, dir
);
451 /***********************************************************************
454 static BOOL
DOSFS_ReadDir( DOS_DIR
*dir
, LPCSTR
*long_name
,
457 struct dirent
*dirent
;
459 #ifdef VFAT_IOCTL_READDIR_BOTH
462 if (ioctl( dir
->fd
, VFAT_IOCTL_READDIR_BOTH
, (long)dir
->dirent
) != -1) {
463 if (!dir
->dirent
[0].d_reclen
) return FALSE
;
464 if (!DOSFS_ToDosFCBFormat( dir
->dirent
[0].d_name
, dir
->short_name
))
465 dir
->short_name
[0] = '\0';
466 *short_name
= dir
->short_name
;
467 if (dir
->dirent
[1].d_name
[0]) *long_name
= dir
->dirent
[1].d_name
;
468 else *long_name
= dir
->dirent
[0].d_name
;
472 #endif /* VFAT_IOCTL_READDIR_BOTH */
474 if (!(dirent
= readdir( dir
->dir
))) return FALSE
;
475 *long_name
= dirent
->d_name
;
481 /***********************************************************************
484 * Transform a Unix file name into a hashed DOS name. If the name is a valid
485 * DOS name, it is converted to upper-case; otherwise it is replaced by a
486 * hashed version that fits in 8.3 format.
487 * File name can be terminated by '\0', '\\' or '/'.
488 * 'buffer' must be at least 13 characters long.
490 static void DOSFS_Hash( LPCSTR name
, LPSTR buffer
, BOOL dir_format
,
493 static const char invalid_chars
[] = INVALID_DOS_CHARS
"~.";
494 static const char hash_chars
[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
501 if (dir_format
) strcpy( buffer
, " " );
503 if (DOSFS_ValidDOSName( name
, ignore_case
))
505 /* Check for '.' and '..' */
509 if (!dir_format
) buffer
[1] = buffer
[2] = '\0';
510 if (name
[1] == '.') buffer
[1] = '.';
514 /* Simply copy the name, converting to uppercase */
516 for (dst
= buffer
; !IS_END_OF_NAME(*name
) && (*name
!= '.'); name
++)
517 *dst
++ = FILE_toupper(*name
);
520 if (dir_format
) dst
= buffer
+ 8;
522 for (name
++; !IS_END_OF_NAME(*name
); name
++)
523 *dst
++ = FILE_toupper(*name
);
525 if (!dir_format
) *dst
= '\0';
529 /* Compute the hash code of the file name */
530 /* If you know something about hash functions, feel free to */
531 /* insert a better algorithm here... */
534 for (p
= name
, hash
= 0xbeef; !IS_END_OF_NAME(p
[1]); p
++)
535 hash
= (hash
<<3) ^ (hash
>>5) ^ FILE_tolower(*p
) ^ (FILE_tolower(p
[1]) << 8);
536 hash
= (hash
<<3) ^ (hash
>>5) ^ FILE_tolower(*p
); /* Last character*/
540 for (p
= name
, hash
= 0xbeef; !IS_END_OF_NAME(p
[1]); p
++)
541 hash
= (hash
<< 3) ^ (hash
>> 5) ^ *p
^ (p
[1] << 8);
542 hash
= (hash
<< 3) ^ (hash
>> 5) ^ *p
; /* Last character */
545 /* Find last dot for start of the extension */
546 for (p
= name
+1, ext
= NULL
; !IS_END_OF_NAME(*p
); p
++)
547 if (*p
== '.') ext
= p
;
548 if (ext
&& IS_END_OF_NAME(ext
[1]))
549 ext
= NULL
; /* Empty extension ignored */
551 /* Copy first 4 chars, replacing invalid chars with '_' */
552 for (i
= 4, p
= name
, dst
= buffer
; i
> 0; i
--, p
++)
554 if (IS_END_OF_NAME(*p
) || (p
== ext
)) break;
555 *dst
++ = strchr( invalid_chars
, *p
) ? '_' : FILE_toupper(*p
);
557 /* Pad to 5 chars with '~' */
558 while (i
-- >= 0) *dst
++ = '~';
560 /* Insert hash code converted to 3 ASCII chars */
561 *dst
++ = hash_chars
[(hash
>> 10) & 0x1f];
562 *dst
++ = hash_chars
[(hash
>> 5) & 0x1f];
563 *dst
++ = hash_chars
[hash
& 0x1f];
565 /* Copy the first 3 chars of the extension (if any) */
568 if (!dir_format
) *dst
++ = '.';
569 for (i
= 3, ext
++; (i
> 0) && !IS_END_OF_NAME(*ext
); i
--, ext
++)
570 *dst
++ = strchr( invalid_chars
, *ext
) ? '_' : FILE_toupper(*ext
);
572 if (!dir_format
) *dst
= '\0';
576 /***********************************************************************
579 * Find the Unix file name in a given directory that corresponds to
580 * a file name (either in Unix or DOS format).
581 * File name can be terminated by '\0', '\\' or '/'.
582 * Return TRUE if OK, FALSE if no file name matches.
584 * 'long_buf' must be at least 'long_len' characters long. If the long name
585 * turns out to be larger than that, the function returns FALSE.
586 * 'short_buf' must be at least 13 characters long.
588 BOOL
DOSFS_FindUnixName( LPCSTR path
, LPCSTR name
, LPSTR long_buf
,
589 INT long_len
, LPSTR short_buf
, BOOL ignore_case
)
592 LPCSTR long_name
, short_name
;
593 char dos_name
[12], tmp_buf
[13];
596 const char *p
= strchr( name
, '/' );
597 int len
= p
? (int)(p
- name
) : strlen(name
);
598 if ((p
= strchr( name
, '\\' ))) len
= min( (int)(p
- name
), len
);
599 /* Ignore trailing dots and spaces */
600 while (len
> 1 && (name
[len
-1] == '.' || name
[len
-1] == ' ')) len
--;
601 if (long_len
< len
+ 1) return FALSE
;
603 TRACE("%s,%s\n", path
, name
);
605 if (!DOSFS_ToDosFCBFormat( name
, dos_name
)) dos_name
[0] = '\0';
607 if (!(dir
= DOSFS_OpenDir( path
)))
609 WARN("(%s,%s): can't open dir: %s\n",
610 path
, name
, strerror(errno
) );
614 while ((ret
= DOSFS_ReadDir( dir
, &long_name
, &short_name
)))
616 /* Check against Unix name */
617 if (len
== strlen(long_name
))
621 if (!strncmp( long_name
, name
, len
)) break;
625 if (!FILE_strncasecmp( long_name
, name
, len
)) break;
630 /* Check against hashed DOS name */
633 DOSFS_Hash( long_name
, tmp_buf
, TRUE
, ignore_case
);
634 short_name
= tmp_buf
;
636 if (!strcmp( dos_name
, short_name
)) break;
641 if (long_buf
) strcpy( long_buf
, long_name
);
645 DOSFS_ToDosDTAFormat( short_name
, short_buf
);
647 DOSFS_Hash( long_name
, short_buf
, FALSE
, ignore_case
);
649 TRACE("(%s,%s) -> %s (%s)\n",
650 path
, name
, long_name
, short_buf
? short_buf
: "***");
653 WARN("'%s' not found in '%s'\n", name
, path
);
654 DOSFS_CloseDir( dir
);
659 /***********************************************************************
662 * Check if a DOS file name represents a DOS device and return the device.
664 const DOS_DEVICE
*DOSFS_GetDevice( const char *name
)
669 if (!name
) return NULL
; /* if FILE_DupUnixHandle was used */
670 if (name
[0] && (name
[1] == ':')) name
+= 2;
671 if ((p
= strrchr( name
, '/' ))) name
= p
+ 1;
672 if ((p
= strrchr( name
, '\\' ))) name
= p
+ 1;
673 for (i
= 0; i
< sizeof(DOSFS_Devices
)/sizeof(DOSFS_Devices
[0]); i
++)
675 const char *dev
= DOSFS_Devices
[i
].name
;
676 if (!FILE_strncasecmp( dev
, name
, strlen(dev
) ))
678 p
= name
+ strlen( dev
);
679 if (!*p
|| (*p
== '.') || (*p
== ':')) return &DOSFS_Devices
[i
];
686 /***********************************************************************
687 * DOSFS_GetDeviceByHandle
689 const DOS_DEVICE
*DOSFS_GetDeviceByHandle( HFILE hFile
)
691 const DOS_DEVICE
*ret
= NULL
;
692 SERVER_START_REQ( get_file_info
)
695 if (!wine_server_call( req
) && (reply
->type
== FILE_TYPE_UNKNOWN
))
697 if ((reply
->attr
>= 0) &&
698 (reply
->attr
< sizeof(DOSFS_Devices
)/sizeof(DOSFS_Devices
[0])))
699 ret
= &DOSFS_Devices
[reply
->attr
];
707 /**************************************************************************
708 * DOSFS_CreateCommPort
710 static HANDLE
DOSFS_CreateCommPort(LPCSTR name
, DWORD access
, DWORD attributes
, LPSECURITY_ATTRIBUTES sa
)
715 TRACE_(file
)("%s %lx %lx\n", name
, access
, attributes
);
717 PROFILE_GetWineIniString("serialports",name
,"",devname
,sizeof devname
);
721 TRACE("opening %s as %s\n", devname
, name
);
723 SERVER_START_REQ( create_serial
)
725 req
->access
= access
;
726 req
->inherit
= (sa
&& (sa
->nLength
>=sizeof(*sa
)) && sa
->bInheritHandle
);
727 req
->attributes
= attributes
;
728 req
->sharing
= FILE_SHARE_READ
|FILE_SHARE_WRITE
;
729 wine_server_add_data( req
, devname
, strlen(devname
) );
731 wine_server_call_err( req
);
737 ERR("Couldn't open device '%s' ! (check permissions)\n",devname
);
739 TRACE("return %08X\n", ret
);
743 /***********************************************************************
746 * Open a DOS device. This might not map 1:1 into the UNIX device concept.
747 * Returns 0 on failure.
749 HANDLE
DOSFS_OpenDevice( const char *name
, DWORD access
, DWORD attributes
, LPSECURITY_ATTRIBUTES sa
)
755 if (name
[0] && (name
[1] == ':')) name
+= 2;
756 if ((p
= strrchr( name
, '/' ))) name
= p
+ 1;
757 if ((p
= strrchr( name
, '\\' ))) name
= p
+ 1;
758 for (i
= 0; i
< sizeof(DOSFS_Devices
)/sizeof(DOSFS_Devices
[0]); i
++)
760 const char *dev
= DOSFS_Devices
[i
].name
;
761 if (!FILE_strncasecmp( dev
, name
, strlen(dev
) ))
763 p
= name
+ strlen( dev
);
764 if (!*p
|| (*p
== '.') || (*p
== ':')) {
766 if (!strcmp(DOSFS_Devices
[i
].name
,"NUL"))
767 return FILE_CreateFile( "/dev/null", access
,
768 FILE_SHARE_READ
|FILE_SHARE_WRITE
, sa
,
769 OPEN_EXISTING
, 0, 0, TRUE
, DRIVE_UNKNOWN
);
770 if (!strcmp(DOSFS_Devices
[i
].name
,"CON")) {
772 switch (access
& (GENERIC_READ
|GENERIC_WRITE
)) {
774 to_dup
= GetStdHandle( STD_INPUT_HANDLE
);
777 to_dup
= GetStdHandle( STD_OUTPUT_HANDLE
);
780 FIXME("can't open CON read/write\n");
783 if (!DuplicateHandle( GetCurrentProcess(), to_dup
, GetCurrentProcess(),
785 sa
&& (sa
->nLength
>=sizeof(*sa
)) && sa
->bInheritHandle
,
786 DUPLICATE_SAME_ACCESS
))
790 if (!strcmp(DOSFS_Devices
[i
].name
,"SCSIMGR$") ||
791 !strcmp(DOSFS_Devices
[i
].name
,"HPSCAN") ||
792 !strcmp(DOSFS_Devices
[i
].name
,"EMMXXXX0"))
794 return FILE_CreateDevice( i
, access
, sa
);
797 if( (handle
=DOSFS_CreateCommPort(DOSFS_Devices
[i
].name
,access
,attributes
,sa
)) )
799 FIXME("device open %s not supported (yet)\n",DOSFS_Devices
[i
].name
);
808 /***********************************************************************
811 * Get the drive specified by a given path name (DOS or Unix format).
813 static int DOSFS_GetPathDrive( const char **name
)
816 const char *p
= *name
;
818 if (*p
&& (p
[1] == ':'))
820 drive
= FILE_toupper(*p
) - 'A';
823 else if (*p
== '/') /* Absolute Unix path? */
825 if ((drive
= DRIVE_FindDriveRoot( name
)) == -1)
827 MESSAGE("Warning: %s not accessible from a configured DOS drive\n", *name
);
828 /* Assume it really was a DOS name */
829 drive
= DRIVE_GetCurrentDrive();
832 else drive
= DRIVE_GetCurrentDrive();
834 if (!DRIVE_IsValid(drive
))
836 SetLastError( ERROR_INVALID_DRIVE
);
843 /***********************************************************************
846 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
847 * Unix name / short DOS name pair.
848 * Return FALSE if one of the path components does not exist. The last path
849 * component is only checked if 'check_last' is non-zero.
850 * The buffers pointed to by 'long_buf' and 'short_buf' must be
851 * at least MAX_PATHNAME_LEN long.
853 BOOL
DOSFS_GetFullName( LPCSTR name
, BOOL check_last
, DOS_FULL_NAME
*full
)
857 char *p_l
, *p_s
, *root
;
859 TRACE("%s (last=%d)\n", name
, check_last
);
861 if ((!*name
) || (*name
=='\n'))
862 { /* error code for Win98 */
863 SetLastError(ERROR_BAD_PATHNAME
);
867 if ((full
->drive
= DOSFS_GetPathDrive( &name
)) == -1) return FALSE
;
868 flags
= DRIVE_GetFlags( full
->drive
);
870 lstrcpynA( full
->long_name
, DRIVE_GetRoot( full
->drive
),
871 sizeof(full
->long_name
) );
872 if (full
->long_name
[1]) root
= full
->long_name
+ strlen(full
->long_name
);
873 else root
= full
->long_name
; /* root directory */
875 strcpy( full
->short_name
, "A:\\" );
876 full
->short_name
[0] += full
->drive
;
878 if ((*name
== '\\') || (*name
== '/')) /* Absolute path */
880 while ((*name
== '\\') || (*name
== '/')) name
++;
882 else /* Relative path */
884 lstrcpynA( root
+ 1, DRIVE_GetUnixCwd( full
->drive
),
885 sizeof(full
->long_name
) - (root
- full
->long_name
) - 1 );
886 if (root
[1]) *root
= '/';
887 lstrcpynA( full
->short_name
+ 3, DRIVE_GetDosCwd( full
->drive
),
888 sizeof(full
->short_name
) - 3 );
891 p_l
= full
->long_name
[1] ? full
->long_name
+ strlen(full
->long_name
)
893 p_s
= full
->short_name
[3] ? full
->short_name
+ strlen(full
->short_name
)
894 : full
->short_name
+ 2;
897 while (*name
&& found
)
899 /* Check for '.' and '..' */
903 if (IS_END_OF_NAME(name
[1]))
906 while ((*name
== '\\') || (*name
== '/')) name
++;
909 else if ((name
[1] == '.') && IS_END_OF_NAME(name
[2]))
912 while ((*name
== '\\') || (*name
== '/')) name
++;
913 while ((p_l
> root
) && (*p_l
!= '/')) p_l
--;
914 while ((p_s
> full
->short_name
+ 2) && (*p_s
!= '\\')) p_s
--;
915 *p_l
= *p_s
= '\0'; /* Remove trailing separator */
920 /* Make sure buffers are large enough */
922 if ((p_s
>= full
->short_name
+ sizeof(full
->short_name
) - 14) ||
923 (p_l
>= full
->long_name
+ sizeof(full
->long_name
) - 1))
925 SetLastError( ERROR_PATH_NOT_FOUND
);
929 /* Get the long and short name matching the file name */
931 if ((found
= DOSFS_FindUnixName( full
->long_name
, name
, p_l
+ 1,
932 sizeof(full
->long_name
) - (p_l
- full
->long_name
) - 1,
933 p_s
+ 1, !(flags
& DRIVE_CASE_SENSITIVE
) )))
939 while (!IS_END_OF_NAME(*name
)) name
++;
941 else if (!check_last
)
945 while (!IS_END_OF_NAME(*name
) &&
946 (p_s
< full
->short_name
+ sizeof(full
->short_name
) - 1) &&
947 (p_l
< full
->long_name
+ sizeof(full
->long_name
) - 1))
949 *p_s
++ = FILE_tolower(*name
);
950 /* If the drive is case-sensitive we want to create new */
951 /* files in lower-case otherwise we can't reopen them */
952 /* under the same short name. */
953 if (flags
& DRIVE_CASE_SENSITIVE
) *p_l
++ = FILE_tolower(*name
);
957 /* Ignore trailing dots and spaces */
958 while(p_l
[-1] == '.' || p_l
[-1] == ' ') {
964 while ((*name
== '\\') || (*name
== '/')) name
++;
971 SetLastError( ERROR_FILE_NOT_FOUND
);
974 if (*name
) /* Not last */
976 SetLastError( ERROR_PATH_NOT_FOUND
);
980 if (!full
->long_name
[0]) strcpy( full
->long_name
, "/" );
981 if (!full
->short_name
[2]) strcpy( full
->short_name
+ 2, "\\" );
982 TRACE("returning %s = %s\n", full
->long_name
, full
->short_name
);
987 /***********************************************************************
988 * GetShortPathNameA (KERNEL32.@)
992 * longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
993 * longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0
995 * more observations ( with NT 3.51 (WinDD) ):
996 * longpath <= 8.3 -> just copy longpath to shortpath
998 * a) file does not exist -> return 0, LastError = ERROR_FILE_NOT_FOUND
999 * b) file does exist -> set the short filename.
1000 * - trailing slashes are reproduced in the short name, even if the
1001 * file is not a directory
1002 * - the absolute/relative path of the short name is reproduced like found
1004 * - longpath and shortpath may have the same address
1005 * Peter Ganten, 1999
1007 DWORD WINAPI
GetShortPathNameA( LPCSTR longpath
, LPSTR shortpath
,
1010 DOS_FULL_NAME full_name
;
1012 DWORD sp
= 0, lp
= 0;
1016 TRACE("%s\n", debugstr_a(longpath
));
1019 SetLastError(ERROR_INVALID_PARAMETER
);
1023 SetLastError(ERROR_BAD_PATHNAME
);
1027 if ( ( tmpshortpath
= HeapAlloc ( GetProcessHeap(), 0, MAX_PATHNAME_LEN
) ) == NULL
) {
1028 SetLastError ( ERROR_NOT_ENOUGH_MEMORY
);
1032 /* check for drive letter */
1033 if ( longpath
[1] == ':' ) {
1034 tmpshortpath
[0] = longpath
[0];
1035 tmpshortpath
[1] = ':';
1039 if ( ( drive
= DOSFS_GetPathDrive ( &longpath
)) == -1 ) return 0;
1040 flags
= DRIVE_GetFlags ( drive
);
1042 while ( longpath
[lp
] ) {
1044 /* check for path delimiters and reproduce them */
1045 if ( longpath
[lp
] == '\\' || longpath
[lp
] == '/' ) {
1046 if (!sp
|| tmpshortpath
[sp
-1]!= '\\')
1048 /* strip double "\\" */
1049 tmpshortpath
[sp
] = '\\';
1052 tmpshortpath
[sp
]=0;/*terminate string*/
1057 tmplen
= strcspn ( longpath
+ lp
, "\\/" );
1058 lstrcpynA ( tmpshortpath
+sp
, longpath
+ lp
, tmplen
+1 );
1060 /* Check, if the current element is a valid dos name */
1061 if ( DOSFS_ValidDOSName ( longpath
+ lp
, !(flags
& DRIVE_CASE_SENSITIVE
) ) ) {
1067 /* Check if the file exists and use the existing file name */
1068 if ( DOSFS_GetFullName ( tmpshortpath
, TRUE
, &full_name
) ) {
1069 strcpy( tmpshortpath
+sp
, strrchr ( full_name
.short_name
, '\\' ) + 1 );
1070 sp
+= strlen ( tmpshortpath
+sp
);
1075 TRACE("not found!\n" );
1076 SetLastError ( ERROR_FILE_NOT_FOUND
);
1079 tmpshortpath
[sp
] = 0;
1081 lstrcpynA ( shortpath
, tmpshortpath
, shortlen
);
1082 TRACE("returning %s\n", debugstr_a(shortpath
) );
1083 tmplen
= strlen ( tmpshortpath
);
1084 HeapFree ( GetProcessHeap(), 0, tmpshortpath
);
1090 /***********************************************************************
1091 * GetShortPathNameW (KERNEL32.@)
1093 DWORD WINAPI
GetShortPathNameW( LPCWSTR longpath
, LPWSTR shortpath
,
1096 LPSTR longpathA
, shortpathA
;
1099 longpathA
= HEAP_strdupWtoA( GetProcessHeap(), 0, longpath
);
1100 shortpathA
= HeapAlloc ( GetProcessHeap(), 0, shortlen
);
1102 ret
= GetShortPathNameA ( longpathA
, shortpathA
, shortlen
);
1103 if (shortlen
> 0 && !MultiByteToWideChar( CP_ACP
, 0, shortpathA
, -1, shortpath
, shortlen
))
1104 shortpath
[shortlen
-1] = 0;
1105 HeapFree( GetProcessHeap(), 0, longpathA
);
1106 HeapFree( GetProcessHeap(), 0, shortpathA
);
1112 /***********************************************************************
1113 * GetLongPathNameA (KERNEL32.@)
1116 * observed (Win2000):
1117 * shortpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1118 * shortpath="": LastError=ERROR_PATH_NOT_FOUND, ret=0
1120 DWORD WINAPI
GetLongPathNameA( LPCSTR shortpath
, LPSTR longpath
,
1123 DOS_FULL_NAME full_name
;
1124 char *p
, *r
, *ll
, *ss
;
1127 SetLastError(ERROR_INVALID_PARAMETER
);
1130 if (!shortpath
[0]) {
1131 SetLastError(ERROR_PATH_NOT_FOUND
);
1135 if (!DOSFS_GetFullName( shortpath
, TRUE
, &full_name
)) return 0;
1136 lstrcpynA( longpath
, full_name
.short_name
, longlen
);
1138 /* Do some hackery to get the long filename. */
1141 ss
=longpath
+strlen(longpath
);
1142 ll
=full_name
.long_name
+strlen(full_name
.long_name
);
1144 while (ss
>=longpath
)
1146 /* FIXME: aren't we more paranoid, than needed? */
1147 while ((ss
[0]=='\\') && (ss
>=longpath
)) ss
--;
1149 while ((ss
[0]!='\\') && (ss
>=longpath
)) ss
--;
1152 /* FIXME: aren't we more paranoid, than needed? */
1153 while ((ll
[0]=='/') && (ll
>=full_name
.long_name
)) ll
--;
1154 while ((ll
[0]!='/') && (ll
>=full_name
.long_name
)) ll
--;
1155 if (ll
<full_name
.long_name
)
1157 ERR("Bad longname! (ss=%s ll=%s)\n This should never happen !\n"
1164 /* FIXME: fix for names like "C:\\" (ie. with more '\'s) */
1168 if ((p
-longpath
)>0) longlen
-= (p
-longpath
);
1169 lstrcpynA( p
, ll
, longlen
);
1171 /* Now, change all '/' to '\' */
1172 for (r
=p
; r
<(p
+longlen
); r
++ )
1173 if (r
[0]=='/') r
[0]='\\';
1174 return strlen(longpath
) - strlen(p
) + longlen
;
1178 return strlen(longpath
);
1182 /***********************************************************************
1183 * GetLongPathNameW (KERNEL32.@)
1185 DWORD WINAPI
GetLongPathNameW( LPCWSTR shortpath
, LPWSTR longpath
,
1188 DOS_FULL_NAME full_name
;
1190 LPSTR shortpathA
= HEAP_strdupWtoA( GetProcessHeap(), 0, shortpath
);
1192 /* FIXME: is it correct to always return a fully qualified short path? */
1193 if (DOSFS_GetFullName( shortpathA
, TRUE
, &full_name
))
1195 ret
= strlen( full_name
.short_name
);
1196 if (longlen
> 0 && !MultiByteToWideChar( CP_ACP
, 0, full_name
.long_name
, -1,
1197 longpath
, longlen
))
1198 longpath
[longlen
-1] = 0;
1200 HeapFree( GetProcessHeap(), 0, shortpathA
);
1205 /***********************************************************************
1206 * DOSFS_DoGetFullPathName
1208 * Implementation of GetFullPathNameA/W.
1210 * bon@elektron 000331:
1211 * A test for GetFullPathName with many pathological cases
1212 * now gives identical output for Wine and OSR2
1214 static DWORD
DOSFS_DoGetFullPathName( LPCSTR name
, DWORD len
, LPSTR result
,
1218 DOS_FULL_NAME full_name
;
1221 char drivecur
[]="c:.";
1223 int namelen
,drive
=0;
1225 if ((strlen(name
) >1)&& (name
[1]==':'))
1226 /* drive letter given */
1228 driveletter
= name
[0];
1230 if ((strlen(name
) >2)&& (name
[1]==':') &&
1231 ((name
[2]=='\\') || (name
[2]=='/')))
1232 /* absolute path given */
1234 lstrcpynA(full_name
.short_name
,name
,MAX_PATHNAME_LEN
);
1235 drive
= (int)FILE_toupper(name
[0]) - 'A';
1240 drivecur
[0]=driveletter
;
1242 strcpy(drivecur
,".");
1243 if (!DOSFS_GetFullName( drivecur
, FALSE
, &full_name
))
1245 FIXME("internal: error getting drive/path\n");
1248 /* find path that drive letter substitutes*/
1249 drive
= (int)FILE_toupper(full_name
.short_name
[0]) -0x41;
1250 root
= DRIVE_GetRoot(drive
);
1253 FIXME("internal: error getting DOS Drive Root\n");
1256 if (!strcmp(root
,"/"))
1258 /* we have just the last / and we need it. */
1259 p
= full_name
.long_name
;
1263 p
= full_name
.long_name
+strlen(root
);
1265 /* append long name (= unix name) to drive */
1266 lstrcpynA(full_name
.short_name
+2,p
,MAX_PATHNAME_LEN
-3);
1267 /* append name to treat */
1268 namelen
= strlen(full_name
.short_name
);
1271 p
+= +2; /* skip drive name when appending */
1272 if (namelen
+2 + strlen(p
) > MAX_PATHNAME_LEN
)
1274 FIXME("internal error: buffer too small\n");
1277 full_name
.short_name
[namelen
++] ='\\';
1278 full_name
.short_name
[namelen
] = 0;
1279 lstrcpynA(full_name
.short_name
+namelen
,p
,MAX_PATHNAME_LEN
-namelen
);
1281 /* reverse all slashes */
1282 for (p
=full_name
.short_name
;
1283 p
< full_name
.short_name
+strlen(full_name
.short_name
);
1289 /* Use memmove, as areas overlap */
1291 while ((p
= strstr(full_name
.short_name
,"\\..\\")))
1293 if (p
> full_name
.short_name
+2)
1296 q
= strrchr(full_name
.short_name
,'\\');
1297 memmove(q
+1,p
+4,strlen(p
+4)+1);
1301 memmove(full_name
.short_name
+3,p
+4,strlen(p
+4)+1);
1304 if ((full_name
.short_name
[2]=='.')&&(full_name
.short_name
[3]=='.'))
1306 /* This case istn't treated yet : c:..\test */
1307 memmove(full_name
.short_name
+2,full_name
.short_name
+4,
1308 strlen(full_name
.short_name
+4)+1);
1311 while ((p
= strstr(full_name
.short_name
,"\\.\\")))
1314 memmove(p
+1,p
+3,strlen(p
+3)+1);
1316 if (!(DRIVE_GetFlags(drive
) & DRIVE_CASE_PRESERVING
))
1317 for (p
= full_name
.short_name
; *p
; p
++) *p
= FILE_toupper(*p
);
1318 namelen
=strlen(full_name
.short_name
);
1319 if (!strcmp(full_name
.short_name
+namelen
-3,"\\.."))
1321 /* one more strange case: "c:\test\test1\.."
1323 *(full_name
.short_name
+namelen
-3)=0;
1324 q
= strrchr(full_name
.short_name
,'\\');
1327 if (full_name
.short_name
[namelen
-1]=='.')
1328 full_name
.short_name
[(namelen
--)-1] =0;
1330 if (full_name
.short_name
[namelen
-1]=='\\')
1331 full_name
.short_name
[(namelen
--)-1] =0;
1332 TRACE("got %s\n",full_name
.short_name
);
1334 /* If the lpBuffer buffer is too small, the return value is the
1335 size of the buffer, in characters, required to hold the path
1336 plus the terminating \0 (tested against win95osr2, bon 001118)
1338 ret
= strlen(full_name
.short_name
);
1341 /* don't touch anything when the buffer is not large enough */
1342 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
1348 MultiByteToWideChar( CP_ACP
, 0, full_name
.short_name
, -1, (LPWSTR
)result
, len
);
1350 lstrcpynA( result
, full_name
.short_name
, len
);
1353 TRACE("returning '%s'\n", full_name
.short_name
);
1358 /***********************************************************************
1359 * GetFullPathNameA (KERNEL32.@)
1361 * if the path closed with '\', *lastpart is 0
1363 DWORD WINAPI
GetFullPathNameA( LPCSTR name
, DWORD len
, LPSTR buffer
,
1366 DWORD ret
= DOSFS_DoGetFullPathName( name
, len
, buffer
, FALSE
);
1367 if (ret
&& (ret
<=len
) && buffer
&& lastpart
)
1369 LPSTR p
= buffer
+ strlen(buffer
);
1373 while ((p
> buffer
+ 2) && (*p
!= '\\')) p
--;
1376 else *lastpart
= NULL
;
1382 /***********************************************************************
1383 * GetFullPathNameW (KERNEL32.@)
1385 DWORD WINAPI
GetFullPathNameW( LPCWSTR name
, DWORD len
, LPWSTR buffer
,
1388 LPSTR nameA
= HEAP_strdupWtoA( GetProcessHeap(), 0, name
);
1389 DWORD ret
= DOSFS_DoGetFullPathName( nameA
, len
, (LPSTR
)buffer
, TRUE
);
1390 HeapFree( GetProcessHeap(), 0, nameA
);
1391 if (ret
&& (ret
<=len
) && buffer
&& lastpart
)
1393 LPWSTR p
= buffer
+ strlenW(buffer
);
1394 if (*p
!= (WCHAR
)'\\')
1396 while ((p
> buffer
+ 2) && (*p
!= (WCHAR
)'\\')) p
--;
1399 else *lastpart
= NULL
;
1405 /***********************************************************************
1406 * wine_get_unix_file_name (KERNEL32.@) Not a Windows API
1408 * Return the full Unix file name for a given path.
1410 BOOL WINAPI
wine_get_unix_file_name( LPCSTR dos
, LPSTR buffer
, DWORD len
)
1414 if ((ret
= DOSFS_GetFullName( dos
, FALSE
, &path
))) lstrcpynA( buffer
, path
.long_name
, len
);
1419 /***********************************************************************
1422 static int DOSFS_FindNextEx( FIND_FIRST_INFO
*info
, WIN32_FIND_DATAA
*entry
)
1424 DWORD attr
= info
->attr
| FA_UNUSED
| FA_ARCHIVE
| FA_RDONLY
| FILE_ATTRIBUTE_SYMLINK
;
1425 UINT flags
= DRIVE_GetFlags( info
->drive
);
1426 char *p
, buffer
[MAX_PATHNAME_LEN
];
1427 const char *drive_path
;
1429 LPCSTR long_name
, short_name
;
1430 BY_HANDLE_FILE_INFORMATION fileinfo
;
1433 if ((info
->attr
& ~(FA_UNUSED
| FA_ARCHIVE
| FA_RDONLY
)) == FA_LABEL
)
1435 if (info
->cur_pos
) return 0;
1436 entry
->dwFileAttributes
= FILE_ATTRIBUTE_LABEL
;
1437 RtlSecondsSince1970ToTime( (time_t)0, &entry
->ftCreationTime
);
1438 RtlSecondsSince1970ToTime( (time_t)0, &entry
->ftLastAccessTime
);
1439 RtlSecondsSince1970ToTime( (time_t)0, &entry
->ftLastWriteTime
);
1440 entry
->nFileSizeHigh
= 0;
1441 entry
->nFileSizeLow
= 0;
1442 entry
->dwReserved0
= 0;
1443 entry
->dwReserved1
= 0;
1444 DOSFS_ToDosDTAFormat( DRIVE_GetLabel( info
->drive
), entry
->cFileName
);
1445 strcpy( entry
->cAlternateFileName
, entry
->cFileName
);
1447 TRACE("returning %s (%s) as label\n",
1448 entry
->cFileName
, entry
->cAlternateFileName
);
1452 drive_path
= info
->path
+ strlen(DRIVE_GetRoot( info
->drive
));
1453 while ((*drive_path
== '/') || (*drive_path
== '\\')) drive_path
++;
1454 drive_root
= !*drive_path
;
1456 lstrcpynA( buffer
, info
->path
, sizeof(buffer
) - 1 );
1457 strcat( buffer
, "/" );
1458 p
= buffer
+ strlen(buffer
);
1460 while (DOSFS_ReadDir( info
->dir
, &long_name
, &short_name
))
1464 /* Don't return '.' and '..' in the root of the drive */
1465 if (drive_root
&& (long_name
[0] == '.') &&
1466 (!long_name
[1] || ((long_name
[1] == '.') && !long_name
[2])))
1469 /* Check the long mask */
1471 if (info
->long_mask
)
1473 if (!DOSFS_MatchLong( info
->long_mask
, long_name
,
1474 flags
& DRIVE_CASE_SENSITIVE
)) continue;
1477 /* Check the short mask */
1479 if (info
->short_mask
)
1483 DOSFS_Hash( long_name
, dos_name
, TRUE
,
1484 !(flags
& DRIVE_CASE_SENSITIVE
) );
1485 short_name
= dos_name
;
1487 if (!DOSFS_MatchShort( info
->short_mask
, short_name
)) continue;
1490 /* Check the file attributes */
1492 lstrcpynA( p
, long_name
, sizeof(buffer
) - (int)(p
- buffer
) );
1493 if (!FILE_Stat( buffer
, &fileinfo
))
1495 WARN("can't stat %s\n", buffer
);
1498 if ((fileinfo
.dwFileAttributes
& FILE_ATTRIBUTE_SYMLINK
) &&
1499 (fileinfo
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
))
1501 static int show_dir_symlinks
= -1;
1502 if (show_dir_symlinks
== -1)
1503 show_dir_symlinks
= PROFILE_GetWineIniBool("wine", "ShowDirSymlinks", 0);
1504 if (!show_dir_symlinks
) continue;
1507 if (fileinfo
.dwFileAttributes
& ~attr
) continue;
1509 /* We now have a matching entry; fill the result and return */
1511 entry
->dwFileAttributes
= fileinfo
.dwFileAttributes
;
1512 entry
->ftCreationTime
= fileinfo
.ftCreationTime
;
1513 entry
->ftLastAccessTime
= fileinfo
.ftLastAccessTime
;
1514 entry
->ftLastWriteTime
= fileinfo
.ftLastWriteTime
;
1515 entry
->nFileSizeHigh
= fileinfo
.nFileSizeHigh
;
1516 entry
->nFileSizeLow
= fileinfo
.nFileSizeLow
;
1519 DOSFS_ToDosDTAFormat( short_name
, entry
->cAlternateFileName
);
1521 DOSFS_Hash( long_name
, entry
->cAlternateFileName
, FALSE
,
1522 !(flags
& DRIVE_CASE_SENSITIVE
) );
1524 lstrcpynA( entry
->cFileName
, long_name
, sizeof(entry
->cFileName
) );
1525 if (!(flags
& DRIVE_CASE_PRESERVING
)) _strlwr( entry
->cFileName
);
1526 TRACE("returning %s (%s) %02lx %ld\n",
1527 entry
->cFileName
, entry
->cAlternateFileName
,
1528 entry
->dwFileAttributes
, entry
->nFileSizeLow
);
1531 return 0; /* End of directory */
1534 /***********************************************************************
1537 * Find the next matching file. Return the number of entries read to find
1538 * the matching one, or 0 if no more entries.
1539 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
1540 * file name mask. Either or both can be NULL.
1542 * NOTE: This is supposed to be only called by the int21 emulation
1543 * routines. Thus, we should own the Win16Mutex anyway.
1544 * Nevertheless, we explicitly enter it to ensure the static
1545 * directory cache is protected.
1547 int DOSFS_FindNext( const char *path
, const char *short_mask
,
1548 const char *long_mask
, int drive
, BYTE attr
,
1549 int skip
, WIN32_FIND_DATAA
*entry
)
1551 static FIND_FIRST_INFO info
;
1552 LPCSTR short_name
, long_name
;
1557 /* Check the cached directory */
1558 if (!(info
.dir
&& info
.path
== path
&& info
.short_mask
== short_mask
1559 && info
.long_mask
== long_mask
&& info
.drive
== drive
1560 && info
.attr
== attr
&& info
.cur_pos
<= skip
))
1562 /* Not in the cache, open it anew */
1563 if (info
.dir
) DOSFS_CloseDir( info
.dir
);
1565 info
.path
= (LPSTR
)path
;
1566 info
.long_mask
= (LPSTR
)long_mask
;
1567 info
.short_mask
= (LPSTR
)short_mask
;
1571 info
.dir
= DOSFS_OpenDir( info
.path
);
1574 /* Skip to desired position */
1575 while (info
.cur_pos
< skip
)
1576 if (info
.dir
&& DOSFS_ReadDir( info
.dir
, &long_name
, &short_name
))
1581 if (info
.dir
&& info
.cur_pos
== skip
&& DOSFS_FindNextEx( &info
, entry
))
1582 count
= info
.cur_pos
- skip
;
1588 if (info
.dir
) DOSFS_CloseDir( info
.dir
);
1589 memset( &info
, '\0', sizeof(info
) );
1597 /*************************************************************************
1598 * FindFirstFileExA (KERNEL32.@)
1600 HANDLE WINAPI
FindFirstFileExA(
1602 FINDEX_INFO_LEVELS fInfoLevelId
,
1603 LPVOID lpFindFileData
,
1604 FINDEX_SEARCH_OPS fSearchOp
,
1605 LPVOID lpSearchFilter
,
1606 DWORD dwAdditionalFlags
)
1608 DOS_FULL_NAME full_name
;
1610 FIND_FIRST_INFO
*info
;
1612 if ((fSearchOp
!= FindExSearchNameMatch
) || (dwAdditionalFlags
!= 0))
1614 FIXME("options not implemented 0x%08x 0x%08lx\n", fSearchOp
, dwAdditionalFlags
);
1615 return INVALID_HANDLE_VALUE
;
1618 switch(fInfoLevelId
)
1620 case FindExInfoStandard
:
1622 WIN32_FIND_DATAA
* data
= (WIN32_FIND_DATAA
*) lpFindFileData
;
1623 data
->dwReserved0
= data
->dwReserved1
= 0x0;
1624 if (!lpFileName
) return 0;
1625 if (!DOSFS_GetFullName( lpFileName
, FALSE
, &full_name
)) break;
1626 if (!(handle
= GlobalAlloc(GMEM_MOVEABLE
, sizeof(FIND_FIRST_INFO
)))) break;
1627 info
= (FIND_FIRST_INFO
*)GlobalLock( handle
);
1628 info
->path
= HeapAlloc( GetProcessHeap(), 0, strlen(full_name
.long_name
)+1 );
1629 strcpy( info
->path
, full_name
.long_name
);
1630 info
->long_mask
= strrchr( info
->path
, '/' );
1631 *(info
->long_mask
++) = '\0';
1632 info
->short_mask
= NULL
;
1634 if (lpFileName
[0] && (lpFileName
[1] == ':'))
1635 info
->drive
= FILE_toupper(*lpFileName
) - 'A';
1636 else info
->drive
= DRIVE_GetCurrentDrive();
1639 info
->dir
= DOSFS_OpenDir( info
->path
);
1641 GlobalUnlock( handle
);
1642 if (!FindNextFileA( handle
, data
))
1644 FindClose( handle
);
1645 SetLastError( ERROR_NO_MORE_FILES
);
1652 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId
);
1654 return INVALID_HANDLE_VALUE
;
1657 /*************************************************************************
1658 * FindFirstFileA (KERNEL32.@)
1660 HANDLE WINAPI
FindFirstFileA(
1662 WIN32_FIND_DATAA
*lpFindData
)
1664 return FindFirstFileExA(lpFileName
, FindExInfoStandard
, lpFindData
,
1665 FindExSearchNameMatch
, NULL
, 0);
1668 /*************************************************************************
1669 * FindFirstFileExW (KERNEL32.@)
1671 HANDLE WINAPI
FindFirstFileExW(
1673 FINDEX_INFO_LEVELS fInfoLevelId
,
1674 LPVOID lpFindFileData
,
1675 FINDEX_SEARCH_OPS fSearchOp
,
1676 LPVOID lpSearchFilter
,
1677 DWORD dwAdditionalFlags
)
1680 WIN32_FIND_DATAA dataA
;
1681 LPVOID _lpFindFileData
;
1684 switch(fInfoLevelId
)
1686 case FindExInfoStandard
:
1688 _lpFindFileData
= &dataA
;
1692 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId
);
1693 return INVALID_HANDLE_VALUE
;
1696 pathA
= HEAP_strdupWtoA( GetProcessHeap(), 0, lpFileName
);
1697 handle
= FindFirstFileExA(pathA
, fInfoLevelId
, _lpFindFileData
, fSearchOp
, lpSearchFilter
, dwAdditionalFlags
);
1698 HeapFree( GetProcessHeap(), 0, pathA
);
1699 if (handle
== INVALID_HANDLE_VALUE
) return handle
;
1701 switch(fInfoLevelId
)
1703 case FindExInfoStandard
:
1705 WIN32_FIND_DATAW
*dataW
= (WIN32_FIND_DATAW
*) lpFindFileData
;
1706 dataW
->dwFileAttributes
= dataA
.dwFileAttributes
;
1707 dataW
->ftCreationTime
= dataA
.ftCreationTime
;
1708 dataW
->ftLastAccessTime
= dataA
.ftLastAccessTime
;
1709 dataW
->ftLastWriteTime
= dataA
.ftLastWriteTime
;
1710 dataW
->nFileSizeHigh
= dataA
.nFileSizeHigh
;
1711 dataW
->nFileSizeLow
= dataA
.nFileSizeLow
;
1712 MultiByteToWideChar( CP_ACP
, 0, dataA
.cFileName
, -1,
1713 dataW
->cFileName
, sizeof(dataW
->cFileName
)/sizeof(WCHAR
) );
1714 MultiByteToWideChar( CP_ACP
, 0, dataA
.cAlternateFileName
, -1,
1715 dataW
->cAlternateFileName
,
1716 sizeof(dataW
->cAlternateFileName
)/sizeof(WCHAR
) );
1720 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId
);
1721 return INVALID_HANDLE_VALUE
;
1726 /*************************************************************************
1727 * FindFirstFileW (KERNEL32.@)
1729 HANDLE WINAPI
FindFirstFileW( LPCWSTR lpFileName
, WIN32_FIND_DATAW
*lpFindData
)
1731 return FindFirstFileExW(lpFileName
, FindExInfoStandard
, lpFindData
,
1732 FindExSearchNameMatch
, NULL
, 0);
1735 /*************************************************************************
1736 * FindNextFileA (KERNEL32.@)
1738 BOOL WINAPI
FindNextFileA( HANDLE handle
, WIN32_FIND_DATAA
*data
)
1740 FIND_FIRST_INFO
*info
;
1742 if ((handle
== INVALID_HANDLE_VALUE
) ||
1743 !(info
= (FIND_FIRST_INFO
*)GlobalLock( handle
)))
1745 SetLastError( ERROR_INVALID_HANDLE
);
1748 GlobalUnlock( handle
);
1749 if (!info
->path
|| !info
->dir
)
1751 SetLastError( ERROR_NO_MORE_FILES
);
1754 if (!DOSFS_FindNextEx( info
, data
))
1756 DOSFS_CloseDir( info
->dir
); info
->dir
= NULL
;
1757 HeapFree( GetProcessHeap(), 0, info
->path
);
1758 info
->path
= info
->long_mask
= NULL
;
1759 SetLastError( ERROR_NO_MORE_FILES
);
1766 /*************************************************************************
1767 * FindNextFileW (KERNEL32.@)
1769 BOOL WINAPI
FindNextFileW( HANDLE handle
, WIN32_FIND_DATAW
*data
)
1771 WIN32_FIND_DATAA dataA
;
1772 if (!FindNextFileA( handle
, &dataA
)) return FALSE
;
1773 data
->dwFileAttributes
= dataA
.dwFileAttributes
;
1774 data
->ftCreationTime
= dataA
.ftCreationTime
;
1775 data
->ftLastAccessTime
= dataA
.ftLastAccessTime
;
1776 data
->ftLastWriteTime
= dataA
.ftLastWriteTime
;
1777 data
->nFileSizeHigh
= dataA
.nFileSizeHigh
;
1778 data
->nFileSizeLow
= dataA
.nFileSizeLow
;
1779 MultiByteToWideChar( CP_ACP
, 0, dataA
.cFileName
, -1,
1780 data
->cFileName
, sizeof(data
->cFileName
)/sizeof(WCHAR
) );
1781 MultiByteToWideChar( CP_ACP
, 0, dataA
.cAlternateFileName
, -1,
1782 data
->cAlternateFileName
,
1783 sizeof(data
->cAlternateFileName
)/sizeof(WCHAR
) );
1787 /*************************************************************************
1788 * FindClose (KERNEL32.@)
1790 BOOL WINAPI
FindClose( HANDLE handle
)
1792 FIND_FIRST_INFO
*info
;
1794 if ((handle
== INVALID_HANDLE_VALUE
) ||
1795 !(info
= (FIND_FIRST_INFO
*)GlobalLock( handle
)))
1797 SetLastError( ERROR_INVALID_HANDLE
);
1802 if (info
->dir
) DOSFS_CloseDir( info
->dir
);
1803 if (info
->path
) HeapFree( GetProcessHeap(), 0, info
->path
);
1805 __EXCEPT(page_fault
)
1807 WARN("Illegal handle %x\n", handle
);
1808 SetLastError( ERROR_INVALID_HANDLE
);
1812 GlobalUnlock( handle
);
1813 GlobalFree( handle
);
1817 /***********************************************************************
1818 * DOSFS_UnixTimeToFileTime
1820 * Convert a Unix time to FILETIME format.
1821 * The FILETIME structure is a 64-bit value representing the number of
1822 * 100-nanosecond intervals since January 1, 1601, 0:00.
1823 * 'remainder' is the nonnegative number of 100-ns intervals
1824 * corresponding to the time fraction smaller than 1 second that
1825 * couldn't be stored in the time_t value.
1827 void DOSFS_UnixTimeToFileTime( time_t unix_time
, FILETIME
*filetime
,
1833 The time difference between 1 January 1601, 00:00:00 and
1834 1 January 1970, 00:00:00 is 369 years, plus the leap years
1835 from 1604 to 1968, excluding 1700, 1800, 1900.
1836 This makes (1968 - 1600) / 4 - 3 = 89 leap days, and a total
1839 Any day in that period had 24 * 60 * 60 = 86400 seconds.
1841 The time difference is 134774 * 86400 * 10000000, which can be written
1843 27111902 * 2^32 + 3577643008
1844 413 * 2^48 + 45534 * 2^32 + 54590 * 2^16 + 32768
1846 If you find that these constants are buggy, please change them in all
1847 instances in both conversion functions.
1850 There are two versions, one of them uses long long variables and
1851 is presumably faster but not ISO C. The other one uses standard C
1852 data types and operations but relies on the assumption that negative
1853 numbers are stored as 2's complement (-1 is 0xffff....). If this
1854 assumption is violated, dates before 1970 will not convert correctly.
1855 This should however work on any reasonable architecture where WINE
1860 Take care not to remove the casts. I have tested these functions
1861 (in both versions) for a lot of numbers. I would be interested in
1862 results on other compilers than GCC.
1864 The operations have been designed to account for the possibility
1865 of 64-bit time_t in future UNICES. Even the versions without
1866 internal long long numbers will work if time_t only is 64 bit.
1867 A 32-bit shift, which was necessary for that operation, turned out
1868 not to work correctly in GCC, besides giving the warning. So I
1869 used a double 16-bit shift instead. Numbers are in the ISO version
1870 represented by three limbs, the most significant with 32 bit, the
1871 other two with 16 bit each.
1873 As the modulo-operator % is not well-defined for negative numbers,
1874 negative divisors have been avoided in DOSFS_FileTimeToUnixTime.
1876 There might be quicker ways to do this in C. Certainly so in
1879 Claus Fischer, fischer@iue.tuwien.ac.at
1882 #if SIZEOF_LONG_LONG >= 8
1883 # define USE_LONG_LONG 1
1885 # define USE_LONG_LONG 0
1888 #if USE_LONG_LONG /* gcc supports long long type */
1890 long long int t
= unix_time
;
1892 t
+= 116444736000000000LL;
1894 filetime
->dwLowDateTime
= (UINT
)t
;
1895 filetime
->dwHighDateTime
= (UINT
)(t
>> 32);
1897 #else /* ISO version */
1899 UINT a0
; /* 16 bit, low bits */
1900 UINT a1
; /* 16 bit, medium bits */
1901 UINT a2
; /* 32 bit, high bits */
1903 /* Copy the unix time to a2/a1/a0 */
1904 a0
= unix_time
& 0xffff;
1905 a1
= (unix_time
>> 16) & 0xffff;
1906 /* This is obsolete if unix_time is only 32 bits, but it does not hurt.
1907 Do not replace this by >> 32, it gives a compiler warning and it does
1909 a2
= (unix_time
>= 0 ? (unix_time
>> 16) >> 16 :
1910 ~((~unix_time
>> 16) >> 16));
1912 /* Multiply a by 10000000 (a = a2/a1/a0)
1913 Split the factor into 10000 * 1000 which are both less than 0xffff. */
1915 a1
= a1
* 10000 + (a0
>> 16);
1916 a2
= a2
* 10000 + (a1
>> 16);
1921 a1
= a1
* 1000 + (a0
>> 16);
1922 a2
= a2
* 1000 + (a1
>> 16);
1926 /* Add the time difference and the remainder */
1927 a0
+= 32768 + (remainder
& 0xffff);
1928 a1
+= 54590 + (remainder
>> 16 ) + (a0
>> 16);
1929 a2
+= 27111902 + (a1
>> 16);
1934 filetime
->dwLowDateTime
= (a1
<< 16) + a0
;
1935 filetime
->dwHighDateTime
= a2
;
1940 /***********************************************************************
1941 * DOSFS_FileTimeToUnixTime
1943 * Convert a FILETIME format to Unix time.
1944 * If not NULL, 'remainder' contains the fractional part of the filetime,
1945 * in the range of [0..9999999] (even if time_t is negative).
1947 time_t DOSFS_FileTimeToUnixTime( const FILETIME
*filetime
, DWORD
*remainder
)
1949 /* Read the comment in the function DOSFS_UnixTimeToFileTime. */
1952 long long int t
= filetime
->dwHighDateTime
;
1954 t
+= (UINT
)filetime
->dwLowDateTime
;
1955 t
-= 116444736000000000LL;
1958 if (remainder
) *remainder
= 9999999 - (-t
- 1) % 10000000;
1959 return -1 - ((-t
- 1) / 10000000);
1963 if (remainder
) *remainder
= t
% 10000000;
1964 return t
/ 10000000;
1967 #else /* ISO version */
1969 UINT a0
; /* 16 bit, low bits */
1970 UINT a1
; /* 16 bit, medium bits */
1971 UINT a2
; /* 32 bit, high bits */
1972 UINT r
; /* remainder of division */
1973 unsigned int carry
; /* carry bit for subtraction */
1974 int negative
; /* whether a represents a negative value */
1976 /* Copy the time values to a2/a1/a0 */
1977 a2
= (UINT
)filetime
->dwHighDateTime
;
1978 a1
= ((UINT
)filetime
->dwLowDateTime
) >> 16;
1979 a0
= ((UINT
)filetime
->dwLowDateTime
) & 0xffff;
1981 /* Subtract the time difference */
1982 if (a0
>= 32768 ) a0
-= 32768 , carry
= 0;
1983 else a0
+= (1 << 16) - 32768 , carry
= 1;
1985 if (a1
>= 54590 + carry
) a1
-= 54590 + carry
, carry
= 0;
1986 else a1
+= (1 << 16) - 54590 - carry
, carry
= 1;
1988 a2
-= 27111902 + carry
;
1990 /* If a is negative, replace a by (-1-a) */
1991 negative
= (a2
>= ((UINT
)1) << 31);
1994 /* Set a to -a - 1 (a is a2/a1/a0) */
2000 /* Divide a by 10000000 (a = a2/a1/a0), put the rest into r.
2001 Split the divisor into 10000 * 1000 which are both less than 0xffff. */
2002 a1
+= (a2
% 10000) << 16;
2004 a0
+= (a1
% 10000) << 16;
2009 a1
+= (a2
% 1000) << 16;
2011 a0
+= (a1
% 1000) << 16;
2013 r
+= (a0
% 1000) * 10000;
2016 /* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
2019 /* Set a to -a - 1 (a is a2/a1/a0) */
2027 if (remainder
) *remainder
= r
;
2029 /* Do not replace this by << 32, it gives a compiler warning and it does
2031 return ((((time_t)a2
) << 16) << 16) + (a1
<< 16) + a0
;
2036 /***********************************************************************
2037 * MulDiv (KERNEL32.@)
2039 * Result of multiplication and division
2040 * -1: Overflow occurred or Divisor was 0
2047 #if SIZEOF_LONG_LONG >= 8
2050 if (!nDivisor
) return -1;
2052 /* We want to deal with a positive divisor to simplify the logic. */
2055 nMultiplicand
= - nMultiplicand
;
2056 nDivisor
= -nDivisor
;
2059 /* If the result is positive, we "add" to round. else, we subtract to round. */
2060 if ( ( (nMultiplicand
< 0) && (nMultiplier
< 0) ) ||
2061 ( (nMultiplicand
>= 0) && (nMultiplier
>= 0) ) )
2062 ret
= (((long long)nMultiplicand
* nMultiplier
) + (nDivisor
/2)) / nDivisor
;
2064 ret
= (((long long)nMultiplicand
* nMultiplier
) - (nDivisor
/2)) / nDivisor
;
2066 if ((ret
> 2147483647) || (ret
< -2147483647)) return -1;
2069 if (!nDivisor
) return -1;
2071 /* We want to deal with a positive divisor to simplify the logic. */
2074 nMultiplicand
= - nMultiplicand
;
2075 nDivisor
= -nDivisor
;
2078 /* If the result is positive, we "add" to round. else, we subtract to round. */
2079 if ( ( (nMultiplicand
< 0) && (nMultiplier
< 0) ) ||
2080 ( (nMultiplicand
>= 0) && (nMultiplier
>= 0) ) )
2081 return ((nMultiplicand
* nMultiplier
) + (nDivisor
/2)) / nDivisor
;
2083 return ((nMultiplicand
* nMultiplier
) - (nDivisor
/2)) / nDivisor
;
2089 /***********************************************************************
2090 * DosDateTimeToFileTime (KERNEL32.@)
2092 BOOL WINAPI
DosDateTimeToFileTime( WORD fatdate
, WORD fattime
, LPFILETIME ft
)
2096 newtm
.tm_sec
= (fattime
& 0x1f) * 2;
2097 newtm
.tm_min
= (fattime
>> 5) & 0x3f;
2098 newtm
.tm_hour
= (fattime
>> 11);
2099 newtm
.tm_mday
= (fatdate
& 0x1f);
2100 newtm
.tm_mon
= ((fatdate
>> 5) & 0x0f) - 1;
2101 newtm
.tm_year
= (fatdate
>> 9) + 80;
2102 RtlSecondsSince1970ToTime( mktime( &newtm
), ft
);
2107 /***********************************************************************
2108 * FileTimeToDosDateTime (KERNEL32.@)
2110 BOOL WINAPI
FileTimeToDosDateTime( const FILETIME
*ft
, LPWORD fatdate
,
2113 time_t unixtime
= DOSFS_FileTimeToUnixTime( ft
, NULL
);
2114 struct tm
*tm
= localtime( &unixtime
);
2116 *fattime
= (tm
->tm_hour
<< 11) + (tm
->tm_min
<< 5) + (tm
->tm_sec
/ 2);
2118 *fatdate
= ((tm
->tm_year
- 80) << 9) + ((tm
->tm_mon
+ 1) << 5)
2124 /***********************************************************************
2125 * LocalFileTimeToFileTime (KERNEL32.@)
2127 BOOL WINAPI
LocalFileTimeToFileTime( const FILETIME
*localft
,
2133 /* convert from local to UTC. Perhaps not correct. FIXME */
2134 time_t unixtime
= DOSFS_FileTimeToUnixTime( localft
, &remainder
);
2135 xtm
= gmtime( &unixtime
);
2136 DOSFS_UnixTimeToFileTime( mktime(xtm
), utcft
, remainder
);
2141 /***********************************************************************
2142 * FileTimeToLocalFileTime (KERNEL32.@)
2144 BOOL WINAPI
FileTimeToLocalFileTime( const FILETIME
*utcft
,
2145 LPFILETIME localft
)
2148 /* convert from UTC to local. Perhaps not correct. FIXME */
2149 time_t unixtime
= DOSFS_FileTimeToUnixTime( utcft
, &remainder
);
2151 struct tm
*xtm
= localtime( &unixtime
);
2154 localtime
= timegm(xtm
);
2155 DOSFS_UnixTimeToFileTime( localtime
, localft
, remainder
);
2158 struct tm
*xtm
,*gtm
;
2161 xtm
= localtime( &unixtime
);
2162 gtm
= gmtime( &unixtime
);
2163 time1
= mktime(xtm
);
2164 time2
= mktime(gtm
);
2165 DOSFS_UnixTimeToFileTime( 2*time1
-time2
, localft
, remainder
);
2171 /***********************************************************************
2172 * FileTimeToSystemTime (KERNEL32.@)
2174 BOOL WINAPI
FileTimeToSystemTime( const FILETIME
*ft
, LPSYSTEMTIME syst
)
2178 time_t xtime
= DOSFS_FileTimeToUnixTime( ft
, &remainder
);
2179 xtm
= gmtime(&xtime
);
2180 syst
->wYear
= xtm
->tm_year
+1900;
2181 syst
->wMonth
= xtm
->tm_mon
+ 1;
2182 syst
->wDayOfWeek
= xtm
->tm_wday
;
2183 syst
->wDay
= xtm
->tm_mday
;
2184 syst
->wHour
= xtm
->tm_hour
;
2185 syst
->wMinute
= xtm
->tm_min
;
2186 syst
->wSecond
= xtm
->tm_sec
;
2187 syst
->wMilliseconds
= remainder
/ 10000;
2191 /***********************************************************************
2192 * QueryDosDeviceA (KERNEL32.@)
2194 * returns array of strings terminated by \0, terminated by \0
2196 DWORD WINAPI
QueryDosDeviceA(LPCSTR devname
,LPSTR target
,DWORD bufsize
)
2201 TRACE("(%s,...)\n", devname
? devname
: "<null>");
2203 /* return known MSDOS devices */
2204 static const char devices
[24] = "CON\0COM1\0COM2\0LPT1\0NUL\0\0";
2205 memcpy( target
, devices
, min(bufsize
,sizeof(devices
)) );
2206 return min(bufsize
,sizeof(devices
));
2208 /* In theory all that are possible and have been defined.
2209 * Now just those below, since mirc uses it to check for special files.
2211 * (It is more complex, and supports netmounted stuff, and \\.\ stuff,
2212 * but currently we just ignore that.)
2214 #define CHECK(x) (strstr(devname,#x)==devname)
2215 if (CHECK(con
) || CHECK(com
) || CHECK(lpt
) || CHECK(nul
)) {
2216 strcpy(buffer
,"\\DEV\\");
2217 strcat(buffer
,devname
);
2218 if ((s
=strchr(buffer
,':'))) *s
='\0';
2219 lstrcpynA(target
,buffer
,bufsize
);
2220 return strlen(buffer
)+1;
2222 if (strchr(devname
,':') || devname
[0]=='\\') {
2223 /* This might be a DOS device we do not handle yet ... */
2224 FIXME("(%s) not detected as DOS device!\n",devname
);
2226 SetLastError(ERROR_DEV_NOT_EXIST
);
2233 /***********************************************************************
2234 * QueryDosDeviceW (KERNEL32.@)
2236 * returns array of strings terminated by \0, terminated by \0
2238 DWORD WINAPI
QueryDosDeviceW(LPCWSTR devname
,LPWSTR target
,DWORD bufsize
)
2240 LPSTR devnameA
= devname
?HEAP_strdupWtoA(GetProcessHeap(),0,devname
):NULL
;
2241 LPSTR targetA
= (LPSTR
)HeapAlloc(GetProcessHeap(),0,bufsize
);
2242 DWORD ret
= QueryDosDeviceA(devnameA
,targetA
,bufsize
);
2244 ret
= MultiByteToWideChar( CP_ACP
, 0, targetA
, ret
, target
, bufsize
);
2245 if (devnameA
) HeapFree(GetProcessHeap(),0,devnameA
);
2246 if (targetA
) HeapFree(GetProcessHeap(),0,targetA
);
2251 /***********************************************************************
2252 * SystemTimeToFileTime (KERNEL32.@)
2254 BOOL WINAPI
SystemTimeToFileTime( const SYSTEMTIME
*syst
, LPFILETIME ft
)
2260 struct tm xtm
,*local_tm
,*utc_tm
;
2261 time_t localtim
,utctime
;
2264 xtm
.tm_year
= syst
->wYear
-1900;
2265 xtm
.tm_mon
= syst
->wMonth
- 1;
2266 xtm
.tm_wday
= syst
->wDayOfWeek
;
2267 xtm
.tm_mday
= syst
->wDay
;
2268 xtm
.tm_hour
= syst
->wHour
;
2269 xtm
.tm_min
= syst
->wMinute
;
2270 xtm
.tm_sec
= syst
->wSecond
; /* this is UTC */
2273 utctime
= timegm(&xtm
);
2274 DOSFS_UnixTimeToFileTime( utctime
, ft
,
2275 syst
->wMilliseconds
* 10000 );
2277 localtim
= mktime(&xtm
); /* now we've got local time */
2278 local_tm
= localtime(&localtim
);
2279 utc_tm
= gmtime(&localtim
);
2280 utctime
= mktime(utc_tm
);
2281 DOSFS_UnixTimeToFileTime( 2*localtim
-utctime
, ft
,
2282 syst
->wMilliseconds
* 10000 );
2287 /***********************************************************************
2288 * DefineDosDeviceA (KERNEL32.@)
2290 BOOL WINAPI
DefineDosDeviceA(DWORD flags
,LPCSTR devname
,LPCSTR targetpath
) {
2291 FIXME("(0x%08lx,%s,%s),stub!\n",flags
,devname
,targetpath
);
2292 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
2297 --- 16 bit functions ---
2300 /*************************************************************************
2301 * FindFirstFile (KERNEL.413)
2303 HANDLE16 WINAPI
FindFirstFile16( LPCSTR path
, WIN32_FIND_DATAA
*data
)
2305 DOS_FULL_NAME full_name
;
2307 FIND_FIRST_INFO
*info
;
2309 data
->dwReserved0
= data
->dwReserved1
= 0x0;
2310 if (!path
) return 0;
2311 if (!DOSFS_GetFullName( path
, FALSE
, &full_name
))
2312 return INVALID_HANDLE_VALUE16
;
2313 if (!(handle
= GlobalAlloc16( GMEM_MOVEABLE
, sizeof(FIND_FIRST_INFO
) )))
2314 return INVALID_HANDLE_VALUE16
;
2315 info
= (FIND_FIRST_INFO
*)GlobalLock16( handle
);
2316 info
->path
= HeapAlloc( GetProcessHeap(), 0, strlen(full_name
.long_name
)+1 );
2317 strcpy( info
->path
, full_name
.long_name
);
2318 info
->long_mask
= strrchr( info
->path
, '/' );
2319 if (info
->long_mask
)
2320 *(info
->long_mask
++) = '\0';
2321 info
->short_mask
= NULL
;
2323 if (path
[0] && (path
[1] == ':')) info
->drive
= FILE_toupper(*path
) - 'A';
2324 else info
->drive
= DRIVE_GetCurrentDrive();
2327 info
->dir
= DOSFS_OpenDir( info
->path
);
2329 GlobalUnlock16( handle
);
2330 if (!FindNextFile16( handle
, data
))
2332 FindClose16( handle
);
2333 SetLastError( ERROR_NO_MORE_FILES
);
2334 return INVALID_HANDLE_VALUE16
;
2339 /*************************************************************************
2340 * FindNextFile (KERNEL.414)
2342 BOOL16 WINAPI
FindNextFile16( HANDLE16 handle
, WIN32_FIND_DATAA
*data
)
2344 FIND_FIRST_INFO
*info
;
2346 if ((handle
== INVALID_HANDLE_VALUE16
) ||
2347 !(info
= (FIND_FIRST_INFO
*)GlobalLock16( handle
)))
2349 SetLastError( ERROR_INVALID_HANDLE
);
2352 GlobalUnlock16( handle
);
2353 if (!info
->path
|| !info
->dir
)
2355 SetLastError( ERROR_NO_MORE_FILES
);
2358 if (!DOSFS_FindNextEx( info
, data
))
2360 DOSFS_CloseDir( info
->dir
); info
->dir
= NULL
;
2361 HeapFree( GetProcessHeap(), 0, info
->path
);
2362 info
->path
= info
->long_mask
= NULL
;
2363 SetLastError( ERROR_NO_MORE_FILES
);
2369 /*************************************************************************
2370 * FindClose (KERNEL.415)
2372 BOOL16 WINAPI
FindClose16( HANDLE16 handle
)
2374 FIND_FIRST_INFO
*info
;
2376 if ((handle
== INVALID_HANDLE_VALUE16
) ||
2377 !(info
= (FIND_FIRST_INFO
*)GlobalLock16( handle
)))
2379 SetLastError( ERROR_INVALID_HANDLE
);
2382 if (info
->dir
) DOSFS_CloseDir( info
->dir
);
2383 if (info
->path
) HeapFree( GetProcessHeap(), 0, info
->path
);
2384 GlobalUnlock16( handle
);
2385 GlobalFree16( handle
);