Check for an empty string in DOSFS_FindNextEx to avoid an unnecessary
[wine.git] / files / dos_fs.c
blobd1c8b54bb31d2e81cdad500639b4e740a45831f9
1 /*
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
22 #include "config.h"
24 #include <sys/types.h>
25 #include <ctype.h>
26 #include <dirent.h>
27 #include <errno.h>
28 #ifdef HAVE_SYS_ERRNO_H
29 #include <sys/errno.h>
30 #endif
31 #include <fcntl.h>
32 #include <string.h>
33 #include <stdlib.h>
34 #include <sys/stat.h>
35 #ifdef HAVE_SYS_IOCTL_H
36 #include <sys/ioctl.h>
37 #endif
38 #include <time.h>
39 #ifdef HAVE_UNISTD_H
40 # include <unistd.h>
41 #endif
43 #include "windef.h"
44 #include "winerror.h"
45 #include "wingdi.h"
47 #include "wine/unicode.h"
48 #include "wine/winbase16.h"
49 #include "drive.h"
50 #include "file.h"
51 #include "heap.h"
52 #include "msdos.h"
53 #include "winternl.h"
54 #include "wine/server.h"
55 #include "msvcrt/excpt.h"
57 #include "smb.h"
59 #include "wine/debug.h"
61 WINE_DEFAULT_DEBUG_CHANNEL(dosfs);
62 WINE_DECLARE_DEBUG_CHANNEL(file);
64 /* Define the VFAT ioctl to get both short and long file names */
65 /* FIXME: is it possible to get this to work on other systems? */
66 #ifdef linux
67 /* We want the real kernel dirent structure, not the libc one */
68 typedef struct
70 long d_ino;
71 long d_off;
72 unsigned short d_reclen;
73 char d_name[256];
74 } KERNEL_DIRENT;
76 #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, KERNEL_DIRENT [2] )
78 #else /* linux */
79 #undef VFAT_IOCTL_READDIR_BOTH /* just in case... */
80 #endif /* linux */
82 /* Chars we don't want to see in DOS file names */
83 #define INVALID_DOS_CHARS "*?<>|\"+=,;[] \345"
85 static const DOS_DEVICE DOSFS_Devices[] =
86 /* name, device flags (see Int 21/AX=0x4400) */
88 { {'C','O','N',0}, 0xc0d3 },
89 { {'P','R','N',0}, 0xa0c0 },
90 { {'N','U','L',0}, 0x80c4 },
91 { {'A','U','X',0}, 0x80c0 },
92 { {'L','P','T','1',0}, 0xa0c0 },
93 { {'L','P','T','2',0}, 0xa0c0 },
94 { {'L','P','T','3',0}, 0xa0c0 },
95 { {'L','P','T','4',0}, 0xc0d3 },
96 { {'C','O','M','1',0}, 0x80c0 },
97 { {'C','O','M','2',0}, 0x80c0 },
98 { {'C','O','M','3',0}, 0x80c0 },
99 { {'C','O','M','4',0}, 0x80c0 },
100 { {'S','C','S','I','M','G','R','$',0}, 0xc0c0 },
101 { {'H','P','S','C','A','N',0}, 0xc0c0 },
102 { {'E','M','M','X','X','X','X','0',0}, 0x0000 }
106 * Directory info for DOSFS_ReadDir
107 * contains the names of *all* the files in the directory
109 typedef struct
111 int used;
112 int size;
113 char names[1];
114 } DOS_DIR;
116 /* Info structure for FindFirstFile handle */
117 typedef struct
119 char *path; /* unix path */
120 LPWSTR long_mask;
121 LPWSTR short_mask;
122 BYTE attr;
123 int drive;
124 int cur_pos;
125 union
127 DOS_DIR *dos_dir;
128 SMB_DIR *smb_dir;
129 } u;
130 } FIND_FIRST_INFO;
133 static WINE_EXCEPTION_FILTER(page_fault)
135 if (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION)
136 return EXCEPTION_EXECUTE_HANDLER;
137 return EXCEPTION_CONTINUE_SEARCH;
141 /***********************************************************************
142 * DOSFS_ValidDOSName
144 * Return 1 if Unix file 'name' is also a valid MS-DOS name
145 * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
146 * File name can be terminated by '\0', '\\' or '/'.
148 static int DOSFS_ValidDOSName( LPCWSTR name, int ignore_case )
150 static const char invalid_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS;
151 const WCHAR *p = name;
152 const char *invalid = ignore_case ? (invalid_chars + 26) : invalid_chars;
153 int len = 0;
155 if (*p == '.')
157 /* Check for "." and ".." */
158 p++;
159 if (*p == '.') p++;
160 /* All other names beginning with '.' are invalid */
161 return (IS_END_OF_NAME(*p));
163 while (!IS_END_OF_NAME(*p))
165 if (*p < 256 && strchr( invalid, (char)*p )) return 0; /* Invalid char */
166 if (*p == '.') break; /* Start of the extension */
167 if (++len > 8) return 0; /* Name too long */
168 p++;
170 if (*p != '.') return 1; /* End of name */
171 p++;
172 if (IS_END_OF_NAME(*p)) return 0; /* Empty extension not allowed */
173 len = 0;
174 while (!IS_END_OF_NAME(*p))
176 if (*p < 256 && strchr( invalid, (char)*p )) return 0; /* Invalid char */
177 if (*p == '.') return 0; /* Second extension not allowed */
178 if (++len > 3) return 0; /* Extension too long */
179 p++;
181 return 1;
185 /***********************************************************************
186 * DOSFS_ToDosFCBFormat
188 * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
189 * expanding wild cards and converting to upper-case in the process.
190 * File name can be terminated by '\0', '\\' or '/'.
191 * Return FALSE if the name is not a valid DOS name.
192 * 'buffer' must be at least 12 characters long.
194 BOOL DOSFS_ToDosFCBFormat( LPCWSTR name, LPWSTR buffer )
196 static const char invalid_chars[] = INVALID_DOS_CHARS;
197 LPCWSTR p = name;
198 int i;
200 TRACE("(%s, %p)\n", debugstr_w(name), buffer);
202 /* Check for "." and ".." */
203 if (*p == '.')
205 p++;
206 buffer[0] = '.';
207 for(i = 1; i < 11; i++) buffer[i] = ' ';
208 buffer[11] = 0;
209 if (*p == '.')
211 buffer[1] = '.';
212 p++;
214 return (!*p || (*p == '/') || (*p == '\\'));
217 for (i = 0; i < 8; i++)
219 switch(*p)
221 case '\0':
222 case '\\':
223 case '/':
224 case '.':
225 buffer[i] = ' ';
226 break;
227 case '?':
228 p++;
229 /* fall through */
230 case '*':
231 buffer[i] = '?';
232 break;
233 default:
234 if (*p < 256 && strchr( invalid_chars, (char)*p )) return FALSE;
235 buffer[i] = toupperW(*p);
236 p++;
237 break;
241 if (*p == '*')
243 /* Skip all chars after wildcard up to first dot */
244 while (*p && (*p != '/') && (*p != '\\') && (*p != '.')) p++;
246 else
248 /* Check if name too long */
249 if (*p && (*p != '/') && (*p != '\\') && (*p != '.')) return FALSE;
251 if (*p == '.') p++; /* Skip dot */
253 for (i = 8; i < 11; i++)
255 switch(*p)
257 case '\0':
258 case '\\':
259 case '/':
260 buffer[i] = ' ';
261 break;
262 case '.':
263 return FALSE; /* Second extension not allowed */
264 case '?':
265 p++;
266 /* fall through */
267 case '*':
268 buffer[i] = '?';
269 break;
270 default:
271 if (*p < 256 && strchr( invalid_chars, (char)*p )) return FALSE;
272 buffer[i] = toupperW(*p);
273 p++;
274 break;
277 buffer[11] = '\0';
279 /* at most 3 character of the extension are processed
280 * is something behind this ?
282 while (*p == '*' || *p == ' ') p++; /* skip wildcards and spaces */
283 return IS_END_OF_NAME(*p);
287 /***********************************************************************
288 * DOSFS_ToDosDTAFormat
290 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
291 * converting to upper-case in the process.
292 * File name can be terminated by '\0', '\\' or '/'.
293 * 'buffer' must be at least 13 characters long.
295 static void DOSFS_ToDosDTAFormat( LPCWSTR name, LPWSTR buffer )
297 LPWSTR p;
299 memcpy( buffer, name, 8 * sizeof(WCHAR) );
300 p = buffer + 8;
301 while ((p > buffer) && (p[-1] == ' ')) p--;
302 *p++ = '.';
303 memcpy( p, name + 8, 3 * sizeof(WCHAR) );
304 p += 3;
305 while (p[-1] == ' ') p--;
306 if (p[-1] == '.') p--;
307 *p = '\0';
311 /***********************************************************************
312 * DOSFS_MatchShort
314 * Check a DOS file name against a mask (both in FCB format).
316 static int DOSFS_MatchShort( LPCWSTR mask, LPCWSTR name )
318 int i;
319 for (i = 11; i > 0; i--, mask++, name++)
320 if ((*mask != '?') && (*mask != *name)) return 0;
321 return 1;
325 /***********************************************************************
326 * DOSFS_MatchLong
328 * Check a long file name against a mask.
330 * Tests (done in W95 DOS shell - case insensitive):
331 * *.txt test1.test.txt *
332 * *st1* test1.txt *
333 * *.t??????.t* test1.ta.tornado.txt *
334 * *tornado* test1.ta.tornado.txt *
335 * t*t test1.ta.tornado.txt *
336 * ?est* test1.txt *
337 * ?est??? test1.txt -
338 * *test1.txt* test1.txt *
339 * h?l?o*t.dat hellothisisatest.dat *
341 static int DOSFS_MatchLong( LPCWSTR mask, LPCWSTR name, int case_sensitive )
343 LPCWSTR lastjoker = NULL;
344 LPCWSTR next_to_retry = NULL;
345 static const WCHAR asterisk_dot_asterisk[] = {'*','.','*',0};
347 TRACE("(%s, %s, %x)\n", debugstr_w(mask), debugstr_w(name), case_sensitive);
349 if (!strcmpW( mask, asterisk_dot_asterisk )) return 1;
350 while (*name && *mask)
352 if (*mask == '*')
354 mask++;
355 while (*mask == '*') mask++; /* Skip consecutive '*' */
356 lastjoker = mask;
357 if (!*mask) return 1; /* end of mask is all '*', so match */
359 /* skip to the next match after the joker(s) */
360 if (case_sensitive) while (*name && (*name != *mask)) name++;
361 else while (*name && (toupperW(*name) != toupperW(*mask))) name++;
363 if (!*name) break;
364 next_to_retry = name;
366 else if (*mask != '?')
368 int mismatch = 0;
369 if (case_sensitive)
371 if (*mask != *name) mismatch = 1;
373 else
375 if (toupperW(*mask) != toupperW(*name)) mismatch = 1;
377 if (!mismatch)
379 mask++;
380 name++;
381 if (*mask == '\0')
383 if (*name == '\0')
384 return 1;
385 if (lastjoker)
386 mask = lastjoker;
389 else /* mismatch ! */
391 if (lastjoker) /* we had an '*', so we can try unlimitedly */
393 mask = lastjoker;
395 /* this scan sequence was a mismatch, so restart
396 * 1 char after the first char we checked last time */
397 next_to_retry++;
398 name = next_to_retry;
400 else
401 return 0; /* bad luck */
404 else /* '?' */
406 mask++;
407 name++;
410 while ((*mask == '.') || (*mask == '*'))
411 mask++; /* Ignore trailing '.' or '*' in mask */
412 return (!*name && !*mask);
416 /***********************************************************************
417 * DOSFS_AddDirEntry
419 * Used to construct an array of filenames in DOSFS_OpenDir
421 static BOOL DOSFS_AddDirEntry(DOS_DIR **dir, LPCWSTR name, LPCWSTR dosname)
423 int extra1 = (strlenW(name) + 1) * sizeof(WCHAR);
424 int extra2 = (strlenW(dosname) + 1) * sizeof(WCHAR);
426 /* if we need more, at minimum double the size */
427 if( (extra1 + extra2 + (*dir)->used) > (*dir)->size)
429 int more = (*dir)->size;
430 DOS_DIR *t;
432 if(more<(extra1+extra2))
433 more = extra1+extra2;
435 t = HeapReAlloc(GetProcessHeap(), 0, *dir, sizeof(**dir) + (*dir)->size + more );
436 if(!t)
438 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
439 ERR("Out of memory caching directory structure %d %d %d\n",
440 (*dir)->size, more, (*dir)->used);
441 return FALSE;
443 (*dir) = t;
444 (*dir)->size += more;
447 /* at this point, the dir structure is big enough to hold these names */
448 strcpyW((LPWSTR)&(*dir)->names[(*dir)->used], name);
449 (*dir)->used += extra1;
450 strcpyW((LPWSTR)&(*dir)->names[(*dir)->used], dosname);
451 (*dir)->used += extra2;
453 return TRUE;
457 /***********************************************************************
458 * DOSFS_OpenDir_VFAT
460 static BOOL DOSFS_OpenDir_VFAT(UINT codepage, DOS_DIR **dir, const char *unix_path)
462 #ifdef VFAT_IOCTL_READDIR_BOTH
463 KERNEL_DIRENT de[2];
464 int fd = open( unix_path, O_RDONLY );
465 BOOL r = TRUE;
467 /* Check if the VFAT ioctl is supported on this directory */
469 if ( fd<0 )
470 return FALSE;
472 while (1)
474 WCHAR long_name[MAX_PATH];
475 WCHAR short_name[12];
477 r = (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de ) != -1);
478 if(!r)
479 break;
480 if (!de[0].d_reclen)
481 break;
482 MultiByteToWideChar(codepage, 0, de[0].d_name, -1, long_name, MAX_PATH);
483 if (!DOSFS_ToDosFCBFormat( long_name, short_name ))
484 short_name[0] = '\0';
485 if (de[1].d_name[0])
486 MultiByteToWideChar(codepage, 0, de[1].d_name, -1, long_name, MAX_PATH);
487 else
488 MultiByteToWideChar(codepage, 0, de[0].d_name, -1, long_name, MAX_PATH);
489 r = DOSFS_AddDirEntry(dir, long_name, short_name );
490 if(!r)
491 break;
493 if(r)
495 static const WCHAR empty_strW[] = { 0 };
496 DOSFS_AddDirEntry(dir, empty_strW, empty_strW);
498 close(fd);
499 return r;
500 #else
501 return FALSE;
502 #endif /* VFAT_IOCTL_READDIR_BOTH */
506 /***********************************************************************
507 * DOSFS_OpenDir_Normal
509 * Now use the standard opendir/readdir interface
511 static BOOL DOSFS_OpenDir_Normal( UINT codepage, DOS_DIR **dir, const char *unix_path )
513 DIR *unixdir = opendir( unix_path );
514 BOOL r = TRUE;
515 static const WCHAR empty_strW[] = { 0 };
517 if(!unixdir)
518 return FALSE;
519 while(1)
521 WCHAR long_name[MAX_PATH];
522 struct dirent *de = readdir(unixdir);
524 if(!de)
525 break;
526 MultiByteToWideChar(codepage, 0, de->d_name, -1, long_name, MAX_PATH);
527 r = DOSFS_AddDirEntry(dir, long_name, empty_strW);
528 if(!r)
529 break;
531 if(r)
532 DOSFS_AddDirEntry(dir, empty_strW, empty_strW);
533 closedir(unixdir);
534 return r;
537 /***********************************************************************
538 * DOSFS_OpenDir
540 static DOS_DIR *DOSFS_OpenDir( UINT codepage, const char *unix_path )
542 const int init_size = 0x100;
543 DOS_DIR *dir = HeapAlloc( GetProcessHeap(), 0, sizeof(*dir) + init_size);
544 BOOL r;
546 TRACE("%s\n",debugstr_a(unix_path));
548 if (!dir)
550 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
551 return NULL;
553 dir->used = 0;
554 dir->size = init_size;
556 /* Treat empty path as root directory. This simplifies path split into
557 directory and mask in several other places */
558 if (!*unix_path) unix_path = "/";
560 r = DOSFS_OpenDir_VFAT( codepage, &dir, unix_path);
562 if(!r)
563 r = DOSFS_OpenDir_Normal( codepage, &dir, unix_path);
565 if(!r)
567 HeapFree(GetProcessHeap(), 0, dir);
568 return NULL;
570 dir->used = 0;
572 return dir;
576 /***********************************************************************
577 * DOSFS_CloseDir
579 static void DOSFS_CloseDir( DOS_DIR *dir )
581 HeapFree( GetProcessHeap(), 0, dir );
585 /***********************************************************************
586 * DOSFS_ReadDir
588 static BOOL DOSFS_ReadDir( DOS_DIR *dir, LPCWSTR *long_name,
589 LPCWSTR *short_name )
591 LPCWSTR sn, ln;
593 if (!dir)
594 return FALSE;
596 /* the long pathname is first */
597 ln = (LPCWSTR)&dir->names[dir->used];
598 if(ln[0])
599 *long_name = ln;
600 else
601 return FALSE;
602 dir->used += (strlenW(ln) + 1) * sizeof(WCHAR);
604 /* followed by the short path name */
605 sn = (LPCWSTR)&dir->names[dir->used];
606 if(sn[0])
607 *short_name = sn;
608 else
609 *short_name = NULL;
610 dir->used += (strlenW(sn) + 1) * sizeof(WCHAR);
612 TRACE("Read: long_name: %s, short_name: %s\n",
613 debugstr_w(*long_name), debugstr_w(*short_name));
615 return TRUE;
619 /***********************************************************************
620 * DOSFS_Hash
622 * Transform a Unix file name into a hashed DOS name. If the name is a valid
623 * DOS name, it is converted to upper-case; otherwise it is replaced by a
624 * hashed version that fits in 8.3 format.
625 * File name can be terminated by '\0', '\\' or '/'.
626 * 'buffer' must be at least 13 characters long.
628 static void DOSFS_Hash( LPCWSTR name, LPWSTR buffer, BOOL dir_format,
629 BOOL ignore_case )
631 static const char invalid_chars[] = INVALID_DOS_CHARS "~.";
632 static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
634 LPCWSTR p, ext;
635 LPWSTR dst;
636 unsigned short hash;
637 int i;
639 if (dir_format)
641 for(i = 0; i < 11; i++) buffer[i] = ' ';
642 buffer[11] = 0;
645 if (DOSFS_ValidDOSName( name, ignore_case ))
647 /* Check for '.' and '..' */
648 if (*name == '.')
650 buffer[0] = '.';
651 if (!dir_format) buffer[1] = buffer[2] = '\0';
652 if (name[1] == '.') buffer[1] = '.';
653 return;
656 /* Simply copy the name, converting to uppercase */
658 for (dst = buffer; !IS_END_OF_NAME(*name) && (*name != '.'); name++)
659 *dst++ = toupperW(*name);
660 if (*name == '.')
662 if (dir_format) dst = buffer + 8;
663 else *dst++ = '.';
664 for (name++; !IS_END_OF_NAME(*name); name++)
665 *dst++ = toupperW(*name);
667 if (!dir_format) *dst = '\0';
668 return;
671 /* Compute the hash code of the file name */
672 /* If you know something about hash functions, feel free to */
673 /* insert a better algorithm here... */
674 if (ignore_case)
676 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
677 hash = (hash<<3) ^ (hash>>5) ^ tolowerW(*p) ^ (tolowerW(p[1]) << 8);
678 hash = (hash<<3) ^ (hash>>5) ^ tolowerW(*p); /* Last character */
680 else
682 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
683 hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
684 hash = (hash << 3) ^ (hash >> 5) ^ *p; /* Last character */
687 /* Find last dot for start of the extension */
688 for (p = name+1, ext = NULL; !IS_END_OF_NAME(*p); p++)
689 if (*p == '.') ext = p;
690 if (ext && IS_END_OF_NAME(ext[1]))
691 ext = NULL; /* Empty extension ignored */
693 /* Copy first 4 chars, replacing invalid chars with '_' */
694 for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
696 if (IS_END_OF_NAME(*p) || (p == ext)) break;
697 *dst++ = (*p < 256 && strchr( invalid_chars, (char)*p )) ? '_' : toupperW(*p);
699 /* Pad to 5 chars with '~' */
700 while (i-- >= 0) *dst++ = '~';
702 /* Insert hash code converted to 3 ASCII chars */
703 *dst++ = hash_chars[(hash >> 10) & 0x1f];
704 *dst++ = hash_chars[(hash >> 5) & 0x1f];
705 *dst++ = hash_chars[hash & 0x1f];
707 /* Copy the first 3 chars of the extension (if any) */
708 if (ext)
710 if (!dir_format) *dst++ = '.';
711 for (i = 3, ext++; (i > 0) && !IS_END_OF_NAME(*ext); i--, ext++)
712 *dst++ = (*ext < 256 && strchr( invalid_chars, (char)*ext )) ? '_' : toupperW(*ext);
714 if (!dir_format) *dst = '\0';
718 /***********************************************************************
719 * DOSFS_FindUnixName
721 * Find the Unix file name in a given directory that corresponds to
722 * a file name (either in Unix or DOS format).
723 * File name can be terminated by '\0', '\\' or '/'.
724 * Return TRUE if OK, FALSE if no file name matches.
726 * 'long_buf' must be at least 'long_len' characters long. If the long name
727 * turns out to be larger than that, the function returns FALSE.
728 * 'short_buf' must be at least 13 characters long.
730 BOOL DOSFS_FindUnixName( const DOS_FULL_NAME *path, LPCWSTR name, char *long_buf,
731 INT long_len, LPWSTR short_buf, BOOL ignore_case)
733 DOS_DIR *dir;
734 LPCWSTR long_name, short_name;
735 WCHAR dos_name[12], tmp_buf[13];
736 BOOL ret;
738 LPCWSTR p = strchrW( name, '/' );
739 int len = p ? (int)(p - name) : strlenW(name);
740 if ((p = strchrW( name, '\\' ))) len = min( (int)(p - name), len );
741 /* Ignore trailing dots and spaces */
742 while (len > 1 && (name[len-1] == '.' || name[len-1] == ' ')) len--;
743 if (long_len < len + 1) return FALSE;
745 TRACE("%s,%s\n", path->long_name, debugstr_w(name) );
747 if (!DOSFS_ToDosFCBFormat( name, dos_name )) dos_name[0] = '\0';
749 if (!(dir = DOSFS_OpenDir( DRIVE_GetCodepage(path->drive), path->long_name )))
751 WARN("(%s,%s): can't open dir: %s\n",
752 path->long_name, debugstr_w(name), strerror(errno) );
753 return FALSE;
756 while ((ret = DOSFS_ReadDir( dir, &long_name, &short_name )))
758 /* Check against Unix name */
759 if (len == strlenW(long_name))
761 if (!ignore_case)
763 if (!strncmpW( long_name, name, len )) break;
765 else
767 if (!strncmpiW( long_name, name, len )) break;
770 if (dos_name[0])
772 /* Check against hashed DOS name */
773 if (!short_name)
775 DOSFS_Hash( long_name, tmp_buf, TRUE, ignore_case );
776 short_name = tmp_buf;
778 if (!strcmpW( dos_name, short_name )) break;
781 if (ret)
783 if (long_buf) WideCharToMultiByte(DRIVE_GetCodepage(path->drive), 0,
784 long_name, -1, long_buf, long_len, NULL, NULL);
785 if (short_buf)
787 if (short_name)
788 DOSFS_ToDosDTAFormat( short_name, short_buf );
789 else
790 DOSFS_Hash( long_name, short_buf, FALSE, ignore_case );
792 TRACE("(%s,%s) -> %s (%s)\n", path->long_name, debugstr_w(name),
793 debugstr_w(long_name), short_buf ? debugstr_w(short_buf) : "***");
795 else
796 WARN("%s not found in '%s'\n", debugstr_w(name), path->long_name);
797 DOSFS_CloseDir( dir );
798 return ret;
802 /***********************************************************************
803 * DOSFS_GetDevice
805 * Check if a DOS file name represents a DOS device and return the device.
807 const DOS_DEVICE *DOSFS_GetDevice( LPCWSTR name )
809 unsigned int i;
810 const WCHAR *p;
812 if (!name) return NULL; /* if FILE_DupUnixHandle was used */
813 if (name[0] && (name[1] == ':')) name += 2;
814 if ((p = strrchrW( name, '/' ))) name = p + 1;
815 if ((p = strrchrW( name, '\\' ))) name = p + 1;
816 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
818 const WCHAR *dev = DOSFS_Devices[i].name;
819 if (!strncmpiW( dev, name, strlenW(dev) ))
821 p = name + strlenW( dev );
822 if (!*p || (*p == '.') || (*p == ':')) return &DOSFS_Devices[i];
825 return NULL;
829 /***********************************************************************
830 * DOSFS_GetDeviceByHandle
832 const DOS_DEVICE *DOSFS_GetDeviceByHandle( HANDLE hFile )
834 const DOS_DEVICE *ret = NULL;
835 SERVER_START_REQ( get_file_info )
837 req->handle = hFile;
838 if (!wine_server_call( req ) && (reply->type == FILE_TYPE_UNKNOWN))
840 if ((reply->attr >= 0) &&
841 (reply->attr < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0])))
842 ret = &DOSFS_Devices[reply->attr];
845 SERVER_END_REQ;
846 return ret;
850 /**************************************************************************
851 * DOSFS_CreateCommPort
853 static HANDLE DOSFS_CreateCommPort(LPCWSTR name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa)
855 HANDLE ret;
856 char devname[40];
857 WCHAR devnameW[40];
858 static const WCHAR serialportsW[] = {'s','e','r','i','a','l','p','o','r','t','s',0};
859 static const WCHAR empty_strW[] = { 0 };
861 TRACE_(file)("%s %lx %lx\n", debugstr_w(name), access, attributes);
863 PROFILE_GetWineIniString(serialportsW, name, empty_strW, devnameW, 40);
864 if(!devnameW[0])
865 return 0;
867 WideCharToMultiByte(CP_ACP, 0, devnameW, -1, devname, sizeof(devname), NULL, NULL);
869 TRACE("opening %s as %s\n", devname, debugstr_w(name));
871 SERVER_START_REQ( create_serial )
873 req->access = access;
874 req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
875 req->attributes = attributes;
876 req->sharing = FILE_SHARE_READ|FILE_SHARE_WRITE;
877 wine_server_add_data( req, devname, strlen(devname) );
878 SetLastError(0);
879 wine_server_call_err( req );
880 ret = reply->handle;
882 SERVER_END_REQ;
884 if(!ret)
885 ERR("Couldn't open device '%s' ! (check permissions)\n",devname);
886 else
887 TRACE("return %08X\n", ret );
888 return ret;
891 /***********************************************************************
892 * DOSFS_OpenDevice
894 * Open a DOS device. This might not map 1:1 into the UNIX device concept.
895 * Returns 0 on failure.
897 HANDLE DOSFS_OpenDevice( LPCWSTR name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa )
899 unsigned int i;
900 const WCHAR *p;
901 HANDLE handle;
903 if (name[0] && (name[1] == ':')) name += 2;
904 if ((p = strrchrW( name, '/' ))) name = p + 1;
905 if ((p = strrchrW( name, '\\' ))) name = p + 1;
906 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
908 const WCHAR *dev = DOSFS_Devices[i].name;
909 if (!strncmpiW( dev, name, strlenW(dev) ))
911 p = name + strlenW( dev );
912 if (!*p || (*p == '.') || (*p == ':')) {
913 static const WCHAR nulW[] = {'N','U','L',0};
914 static const WCHAR conW[] = {'C','O','N',0};
915 static const WCHAR scsimgrW[] = {'S','C','S','I','M','G','R','$',0};
916 static const WCHAR hpscanW[] = {'H','P','S','C','A','N',0};
917 static const WCHAR emmxxxx0W[] = {'E','M','M','X','X','X','X','0',0};
918 /* got it */
919 if (!strcmpiW(DOSFS_Devices[i].name, nulW))
920 return FILE_CreateFile( "/dev/null", access,
921 FILE_SHARE_READ|FILE_SHARE_WRITE, sa,
922 OPEN_EXISTING, 0, 0, TRUE, DRIVE_UNKNOWN );
923 if (!strcmpiW(DOSFS_Devices[i].name, conW)) {
924 HANDLE to_dup;
925 switch (access & (GENERIC_READ|GENERIC_WRITE)) {
926 case GENERIC_READ:
927 to_dup = GetStdHandle( STD_INPUT_HANDLE );
928 break;
929 case GENERIC_WRITE:
930 to_dup = GetStdHandle( STD_OUTPUT_HANDLE );
931 break;
932 default:
933 FIXME("can't open CON read/write\n");
934 return 0;
936 if (!DuplicateHandle( GetCurrentProcess(), to_dup, GetCurrentProcess(),
937 &handle, 0,
938 sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle,
939 DUPLICATE_SAME_ACCESS ))
940 handle = 0;
941 return handle;
943 if (!strcmpiW(DOSFS_Devices[i].name, scsimgrW) ||
944 !strcmpiW(DOSFS_Devices[i].name, hpscanW) ||
945 !strcmpiW(DOSFS_Devices[i].name, emmxxxx0W))
947 return FILE_CreateDevice( i, access, sa );
950 if( (handle=DOSFS_CreateCommPort(DOSFS_Devices[i].name,access,attributes,sa)) )
951 return handle;
952 FIXME("device open %s not supported (yet)\n", debugstr_w(DOSFS_Devices[i].name));
953 return 0;
957 return 0;
961 /***********************************************************************
962 * DOSFS_GetPathDrive
964 * Get the drive specified by a given path name (DOS or Unix format).
966 static int DOSFS_GetPathDrive( LPCWSTR *name )
968 int drive;
969 LPCWSTR p = *name;
971 if (*p && (p[1] == ':'))
973 drive = toupperW(*p) - 'A';
974 *name += 2;
976 else if (*p == '/') /* Absolute Unix path? */
978 if ((drive = DRIVE_FindDriveRootW( name )) == -1)
980 MESSAGE("Warning: %s not accessible from a configured DOS drive\n", debugstr_w(*name) );
981 /* Assume it really was a DOS name */
982 drive = DRIVE_GetCurrentDrive();
985 else drive = DRIVE_GetCurrentDrive();
987 if (!DRIVE_IsValid(drive))
989 SetLastError( ERROR_INVALID_DRIVE );
990 return -1;
992 return drive;
996 /***********************************************************************
997 * DOSFS_GetFullName
999 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
1000 * Unix name / short DOS name pair.
1001 * Return FALSE if one of the path components does not exist. The last path
1002 * component is only checked if 'check_last' is non-zero.
1003 * The buffers pointed to by 'long_buf' and 'short_buf' must be
1004 * at least MAX_PATHNAME_LEN long.
1006 BOOL DOSFS_GetFullName( LPCWSTR name, BOOL check_last, DOS_FULL_NAME *full )
1008 BOOL found;
1009 UINT flags, codepage;
1010 char *p_l, *root;
1011 LPWSTR p_s;
1012 static const WCHAR driveA_rootW[] = {'A',':','\\',0};
1013 static const WCHAR dos_rootW[] = {'\\',0};
1015 TRACE("%s (last=%d)\n", debugstr_w(name), check_last );
1017 if ((!*name) || (*name=='\n'))
1018 { /* error code for Win98 */
1019 SetLastError(ERROR_BAD_PATHNAME);
1020 return FALSE;
1023 if ((full->drive = DOSFS_GetPathDrive( &name )) == -1) return FALSE;
1024 flags = DRIVE_GetFlags( full->drive );
1025 codepage = DRIVE_GetCodepage(full->drive);
1027 lstrcpynA( full->long_name, DRIVE_GetRoot( full->drive ),
1028 sizeof(full->long_name) );
1029 if (full->long_name[1]) root = full->long_name + strlen(full->long_name);
1030 else root = full->long_name; /* root directory */
1032 strcpyW( full->short_name, driveA_rootW );
1033 full->short_name[0] += full->drive;
1035 if ((*name == '\\') || (*name == '/')) /* Absolute path */
1037 while ((*name == '\\') || (*name == '/')) name++;
1039 else /* Relative path */
1041 lstrcpynA( root + 1, DRIVE_GetUnixCwd( full->drive ),
1042 sizeof(full->long_name) - (root - full->long_name) - 1 );
1043 if (root[1]) *root = '/';
1044 lstrcpynW( full->short_name + 3, DRIVE_GetDosCwd( full->drive ),
1045 sizeof(full->short_name)/sizeof(full->short_name[0]) - 3 );
1048 p_l = full->long_name[1] ? full->long_name + strlen(full->long_name)
1049 : full->long_name;
1050 p_s = full->short_name[3] ? full->short_name + strlenW(full->short_name)
1051 : full->short_name + 2;
1052 found = TRUE;
1054 while (*name && found)
1056 /* Check for '.' and '..' */
1058 if (*name == '.')
1060 if (IS_END_OF_NAME(name[1]))
1062 name++;
1063 while ((*name == '\\') || (*name == '/')) name++;
1064 continue;
1066 else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
1068 name += 2;
1069 while ((*name == '\\') || (*name == '/')) name++;
1070 while ((p_l > root) && (*p_l != '/')) p_l--;
1071 while ((p_s > full->short_name + 2) && (*p_s != '\\')) p_s--;
1072 *p_l = *p_s = '\0'; /* Remove trailing separator */
1073 continue;
1077 /* Make sure buffers are large enough */
1079 if ((p_s >= full->short_name + sizeof(full->short_name)/sizeof(full->short_name[0]) - 14) ||
1080 (p_l >= full->long_name + sizeof(full->long_name) - 1))
1082 SetLastError( ERROR_PATH_NOT_FOUND );
1083 return FALSE;
1086 /* Get the long and short name matching the file name */
1088 if ((found = DOSFS_FindUnixName( full, name, p_l + 1,
1089 sizeof(full->long_name) - (p_l - full->long_name) - 1,
1090 p_s + 1, !(flags & DRIVE_CASE_SENSITIVE) )))
1092 *p_l++ = '/';
1093 p_l += strlen(p_l);
1094 *p_s++ = '\\';
1095 p_s += strlenW(p_s);
1096 while (!IS_END_OF_NAME(*name)) name++;
1098 else if (!check_last)
1100 *p_l++ = '/';
1101 *p_s++ = '\\';
1102 while (!IS_END_OF_NAME(*name) &&
1103 (p_s < full->short_name + sizeof(full->short_name)/sizeof(full->short_name[0]) - 1) &&
1104 (p_l < full->long_name + sizeof(full->long_name) - 1))
1106 WCHAR wch;
1107 *p_s++ = tolowerW(*name);
1108 /* If the drive is case-sensitive we want to create new */
1109 /* files in lower-case otherwise we can't reopen them */
1110 /* under the same short name. */
1111 if (flags & DRIVE_CASE_SENSITIVE) wch = tolowerW(*name);
1112 else wch = *name;
1113 p_l += WideCharToMultiByte(codepage, 0, &wch, 1, p_l, 2, NULL, NULL);
1114 name++;
1116 /* Ignore trailing dots and spaces */
1117 while(p_l[-1] == '.' || p_l[-1] == ' ') {
1118 --p_l;
1119 --p_s;
1121 *p_l = '\0';
1122 *p_s = '\0';
1124 while ((*name == '\\') || (*name == '/')) name++;
1127 if (!found)
1129 if (check_last)
1131 SetLastError( ERROR_FILE_NOT_FOUND );
1132 return FALSE;
1134 if (*name) /* Not last */
1136 SetLastError( ERROR_PATH_NOT_FOUND );
1137 return FALSE;
1140 if (!full->long_name[0]) strcpy( full->long_name, "/" );
1141 if (!full->short_name[2]) strcpyW( full->short_name + 2, dos_rootW );
1142 TRACE("returning %s = %s\n", full->long_name, debugstr_w(full->short_name) );
1143 return TRUE;
1147 /***********************************************************************
1148 * GetShortPathNameW (KERNEL32.@)
1150 * NOTES
1151 * observed:
1152 * longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1153 * longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0
1155 * more observations ( with NT 3.51 (WinDD) ):
1156 * longpath <= 8.3 -> just copy longpath to shortpath
1157 * longpath > 8.3 ->
1158 * a) file does not exist -> return 0, LastError = ERROR_FILE_NOT_FOUND
1159 * b) file does exist -> set the short filename.
1160 * - trailing slashes are reproduced in the short name, even if the
1161 * file is not a directory
1162 * - the absolute/relative path of the short name is reproduced like found
1163 * in the long name
1164 * - longpath and shortpath may have the same address
1165 * Peter Ganten, 1999
1167 DWORD WINAPI GetShortPathNameW( LPCWSTR longpath, LPWSTR shortpath, DWORD shortlen )
1169 DOS_FULL_NAME full_name;
1170 WCHAR tmpshortpath[MAX_PATHNAME_LEN];
1171 const WCHAR *p;
1172 DWORD sp = 0, lp = 0;
1173 int drive;
1174 DWORD tmplen;
1175 UINT flags;
1176 BOOL unixabsolute = *longpath == '/';
1178 TRACE("%s\n", debugstr_w(longpath));
1180 if (!longpath) {
1181 SetLastError(ERROR_INVALID_PARAMETER);
1182 return 0;
1184 if (!longpath[0]) {
1185 SetLastError(ERROR_BAD_PATHNAME);
1186 return 0;
1189 /* check for drive letter */
1190 if (!unixabsolute && longpath[1] == ':' ) {
1191 tmpshortpath[0] = longpath[0];
1192 tmpshortpath[1] = ':';
1193 sp = 2;
1196 if ( ( drive = DOSFS_GetPathDrive ( &longpath )) == -1 ) return 0;
1197 flags = DRIVE_GetFlags ( drive );
1199 if (unixabsolute && drive != DRIVE_GetCurrentDrive()) {
1200 tmpshortpath[0] = drive + 'A';
1201 tmpshortpath[1] = ':';
1202 sp = 2;
1205 while ( longpath[lp] ) {
1207 /* check for path delimiters and reproduce them */
1208 if ( longpath[lp] == '\\' || longpath[lp] == '/' ) {
1209 if (!sp || tmpshortpath[sp-1]!= '\\')
1211 /* strip double "\\" */
1212 tmpshortpath[sp] = '\\';
1213 sp++;
1215 tmpshortpath[sp]=0;/*terminate string*/
1216 lp++;
1217 continue;
1220 tmplen = 0;
1221 for(p = longpath + lp; *p && *p != '/' && *p != '\\'; p++)
1222 tmplen++;
1223 lstrcpynW(tmpshortpath + sp, longpath + lp, tmplen + 1);
1225 /* Check, if the current element is a valid dos name */
1226 if ( DOSFS_ValidDOSName ( longpath + lp, !(flags & DRIVE_CASE_SENSITIVE) ) ) {
1227 sp += tmplen;
1228 lp += tmplen;
1229 continue;
1232 /* Check if the file exists and use the existing file name */
1233 if ( DOSFS_GetFullName ( tmpshortpath, TRUE, &full_name ) ) {
1234 strcpyW(tmpshortpath + sp, strrchrW(full_name.short_name, '\\') + 1);
1235 sp += strlenW(tmpshortpath + sp);
1236 lp += tmplen;
1237 continue;
1240 TRACE("not found!\n" );
1241 SetLastError ( ERROR_FILE_NOT_FOUND );
1242 return 0;
1244 tmpshortpath[sp] = 0;
1246 tmplen = strlenW(tmpshortpath) + 1;
1247 if (tmplen <= shortlen)
1249 strcpyW(shortpath, tmpshortpath);
1250 TRACE("returning %s\n", debugstr_w(shortpath));
1251 tmplen--; /* length without 0 */
1254 return tmplen;
1258 /***********************************************************************
1259 * GetShortPathNameA (KERNEL32.@)
1261 DWORD WINAPI GetShortPathNameA( LPCSTR longpath, LPSTR shortpath, DWORD shortlen )
1263 UNICODE_STRING longpathW;
1264 WCHAR shortpathW[MAX_PATH];
1265 DWORD ret, retW;
1267 if (!longpath)
1269 SetLastError(ERROR_INVALID_PARAMETER);
1270 return 0;
1273 TRACE("%s\n", debugstr_a(longpath));
1275 if (!RtlCreateUnicodeStringFromAsciiz(&longpathW, longpath))
1277 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1278 return 0;
1281 retW = GetShortPathNameW(longpathW.Buffer, shortpathW, MAX_PATH);
1283 if (!retW)
1284 ret = 0;
1285 else if (retW > MAX_PATH)
1287 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1288 ret = 0;
1290 else
1292 ret = WideCharToMultiByte(CP_ACP, 0, shortpathW, -1, NULL, 0, NULL, NULL);
1293 if (ret <= shortlen)
1295 WideCharToMultiByte(CP_ACP, 0, shortpathW, -1, shortpath, shortlen, NULL, NULL);
1296 ret--; /* length without 0 */
1300 RtlFreeUnicodeString(&longpathW);
1301 return ret;
1305 /***********************************************************************
1306 * GetLongPathNameW (KERNEL32.@)
1308 * NOTES
1309 * observed (Win2000):
1310 * shortpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1311 * shortpath="": LastError=ERROR_PATH_NOT_FOUND, ret=0
1313 DWORD WINAPI GetLongPathNameW( LPCWSTR shortpath, LPWSTR longpath, DWORD longlen )
1315 DOS_FULL_NAME full_name;
1316 const char *root;
1317 LPWSTR p;
1318 int drive;
1319 UINT codepage;
1320 DWORD ret, len = 0;
1322 if (!shortpath) {
1323 SetLastError(ERROR_INVALID_PARAMETER);
1324 return 0;
1326 if (!shortpath[0]) {
1327 SetLastError(ERROR_PATH_NOT_FOUND);
1328 return 0;
1331 TRACE("%s,%p,%ld\n", debugstr_w(shortpath), longpath, longlen);
1333 if(shortpath[0]=='\\' && shortpath[1]=='\\')
1335 ERR("UNC pathname %s\n",debugstr_w(shortpath));
1336 lstrcpynW( longpath, full_name.short_name, longlen );
1337 return strlenW(longpath);
1340 if (!DOSFS_GetFullName( shortpath, TRUE, &full_name )) return 0;
1342 root = full_name.long_name;
1343 drive = DRIVE_FindDriveRoot(&root);
1344 codepage = DRIVE_GetCodepage(drive);
1346 ret = MultiByteToWideChar(codepage, 0, root, -1, NULL, 0);
1347 ret += 3; /* A:\ */
1348 /* reproduce terminating slash */
1349 if (ret > 4) /* if not drive root */
1351 len = strlenW(shortpath);
1352 if (shortpath[len - 1] == '\\' || shortpath[len - 1] == '/')
1353 len = 1;
1355 ret += len;
1356 if (ret <= longlen)
1358 longpath[0] = 'A' + drive;
1359 longpath[1] = ':';
1360 MultiByteToWideChar(codepage, 0, root, -1, longpath + 2, longlen - 2);
1361 for (p = longpath; *p; p++) if (*p == '/') *p = '\\';
1362 if (len)
1364 longpath[ret - 2] = '\\';
1365 longpath[ret - 1] = 0;
1367 TRACE("returning %s\n", debugstr_w(longpath));
1368 ret--; /* length without 0 */
1370 return ret;
1374 /***********************************************************************
1375 * GetLongPathNameA (KERNEL32.@)
1377 DWORD WINAPI GetLongPathNameA( LPCSTR shortpath, LPSTR longpath, DWORD longlen )
1379 UNICODE_STRING shortpathW;
1380 WCHAR longpathW[MAX_PATH];
1381 DWORD ret, retW;
1383 if (!shortpath)
1385 SetLastError(ERROR_INVALID_PARAMETER);
1386 return 0;
1389 TRACE("%s\n", debugstr_a(shortpath));
1391 if (!RtlCreateUnicodeStringFromAsciiz(&shortpathW, shortpath))
1393 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1394 return 0;
1397 retW = GetLongPathNameW(shortpathW.Buffer, longpathW, MAX_PATH);
1399 if (!retW)
1400 ret = 0;
1401 else if (retW > MAX_PATH)
1403 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1404 ret = 0;
1406 else
1408 ret = WideCharToMultiByte(CP_ACP, 0, longpathW, -1, NULL, 0, NULL, NULL);
1409 if (ret <= longlen)
1411 WideCharToMultiByte(CP_ACP, 0, longpathW, -1, longpath, longlen, NULL, NULL);
1412 ret--; /* length without 0 */
1416 RtlFreeUnicodeString(&shortpathW);
1417 return ret;
1421 /***********************************************************************
1422 * DOSFS_DoGetFullPathName
1424 * Implementation of GetFullPathNameA/W.
1426 * bon@elektron 000331:
1427 * A test for GetFullPathName with many pathological cases
1428 * now gives identical output for Wine and OSR2
1430 static DWORD DOSFS_DoGetFullPathName( LPCWSTR name, DWORD len, LPWSTR result )
1432 DWORD ret;
1433 DOS_FULL_NAME full_name;
1434 LPWSTR p, q;
1435 char *p_l;
1436 const char * root;
1437 WCHAR drivecur[] = {'C',':','.',0};
1438 WCHAR driveletter=0;
1439 int namelen,drive=0;
1440 static const WCHAR bkslashW[] = {'\\',0};
1441 static const WCHAR dotW[] = {'.',0};
1442 static const WCHAR updir_slashW[] = {'\\','.','.','\\',0};
1443 static const WCHAR curdirW[] = {'\\','.','\\',0};
1444 static const WCHAR updirW[] = {'\\','.','.',0};
1446 if (!name[0])
1448 SetLastError(ERROR_BAD_PATHNAME);
1449 return 0;
1452 TRACE("passed %s\n", debugstr_w(name));
1454 if (name[1]==':')
1455 /*drive letter given */
1457 driveletter = name[0];
1459 if ((name[1]==':') && ((name[2]=='\\') || (name[2]=='/')))
1460 /*absolute path given */
1462 strncpyW(full_name.short_name, name, MAX_PATHNAME_LEN);
1463 full_name.short_name[MAX_PATHNAME_LEN - 1] = 0; /* ensure 0 termination */
1464 drive = toupperW(name[0]) - 'A';
1466 else
1468 if (driveletter)
1469 drivecur[0]=driveletter;
1470 else if ((name[0]=='\\') || (name[0]=='/'))
1471 strcpyW(drivecur, bkslashW);
1472 else
1473 strcpyW(drivecur, dotW);
1475 if (!DOSFS_GetFullName( drivecur, FALSE, &full_name ))
1477 FIXME("internal: error getting drive/path\n");
1478 return 0;
1480 /* find path that drive letter substitutes*/
1481 drive = toupperW(full_name.short_name[0]) - 'A';
1482 root= DRIVE_GetRoot(drive);
1483 if (!root)
1485 FIXME("internal: error getting DOS Drive Root\n");
1486 return 0;
1488 if (!strcmp(root,"/"))
1490 /* we have just the last / and we need it. */
1491 p_l = full_name.long_name;
1493 else
1495 p_l = full_name.long_name + strlen(root);
1497 /* append long name (= unix name) to drive */
1498 MultiByteToWideChar(DRIVE_GetCodepage(drive), 0, p_l, -1,
1499 full_name.short_name + 2, MAX_PATHNAME_LEN - 3);
1500 /* append name to treat */
1501 namelen= strlenW(full_name.short_name);
1502 p = (LPWSTR)name;
1503 if (driveletter)
1504 p += 2; /* skip drive name when appending */
1505 if (namelen + 2 + strlenW(p) > MAX_PATHNAME_LEN)
1507 FIXME("internal error: buffer too small\n");
1508 return 0;
1510 full_name.short_name[namelen++] ='\\';
1511 full_name.short_name[namelen] = 0;
1512 strncpyW(full_name.short_name + namelen, p, MAX_PATHNAME_LEN - namelen);
1513 full_name.short_name[MAX_PATHNAME_LEN - 1] = 0; /* ensure 0 termination */
1515 /* reverse all slashes */
1516 for (p=full_name.short_name;
1517 p < full_name.short_name + strlenW(full_name.short_name);
1518 p++)
1520 if ( *p == '/' )
1521 *p = '\\';
1523 /* Use memmove, as areas overlap */
1524 /* Delete .. */
1525 while ((p = strstrW(full_name.short_name, updir_slashW)))
1527 if (p > full_name.short_name+2)
1529 *p = 0;
1530 q = strrchrW(full_name.short_name, '\\');
1531 memmove(q+1, p+4, (strlenW(p+4)+1) * sizeof(WCHAR));
1533 else
1535 memmove(full_name.short_name+3, p+4, (strlenW(p+4)+1) * sizeof(WCHAR));
1538 if ((full_name.short_name[2]=='.')&&(full_name.short_name[3]=='.'))
1540 /* This case istn't treated yet : c:..\test */
1541 memmove(full_name.short_name+2,full_name.short_name+4,
1542 (strlenW(full_name.short_name+4)+1) * sizeof(WCHAR));
1544 /* Delete . */
1545 while ((p = strstrW(full_name.short_name, curdirW)))
1547 *(p+1) = 0;
1548 memmove(p+1, p+3, (strlenW(p+3)+1) * sizeof(WCHAR));
1550 if (!(DRIVE_GetFlags(drive) & DRIVE_CASE_PRESERVING))
1551 for (p = full_name.short_name; *p; p++) *p = toupperW(*p);
1552 namelen = strlenW(full_name.short_name);
1553 if (!strcmpW(full_name.short_name+namelen-3, updirW))
1555 /* one more strange case: "c:\test\test1\.."
1556 return "c:\test" */
1557 *(full_name.short_name+namelen-3)=0;
1558 q = strrchrW(full_name.short_name, '\\');
1559 *q =0;
1561 if (full_name.short_name[namelen-1]=='.')
1562 full_name.short_name[(namelen--)-1] =0;
1563 if (!driveletter)
1564 if (full_name.short_name[namelen-1]=='\\')
1565 full_name.short_name[(namelen--)-1] =0;
1566 TRACE("got %s\n", debugstr_w(full_name.short_name));
1568 /* If the lpBuffer buffer is too small, the return value is the
1569 size of the buffer, in characters, required to hold the path
1570 plus the terminating \0 (tested against win95osr2, bon 001118)
1571 . */
1572 ret = strlenW(full_name.short_name);
1573 if (ret >= len )
1575 /* don't touch anything when the buffer is not large enough */
1576 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1577 return ret+1;
1579 if (result)
1581 strncpyW( result, full_name.short_name, len );
1582 result[len - 1] = 0; /* ensure 0 termination */
1585 TRACE("returning %s\n", debugstr_w(full_name.short_name) );
1586 return ret;
1590 /***********************************************************************
1591 * GetFullPathNameA (KERNEL32.@)
1592 * NOTES
1593 * if the path closed with '\', *lastpart is 0
1595 DWORD WINAPI GetFullPathNameA( LPCSTR name, DWORD len, LPSTR buffer,
1596 LPSTR *lastpart )
1598 UNICODE_STRING nameW;
1599 WCHAR bufferW[MAX_PATH];
1600 DWORD ret, retW;
1602 if (!name)
1604 SetLastError(ERROR_INVALID_PARAMETER);
1605 return 0;
1608 if (!RtlCreateUnicodeStringFromAsciiz(&nameW, name))
1610 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1611 return 0;
1614 retW = GetFullPathNameW( nameW.Buffer, MAX_PATH, bufferW, NULL);
1616 if (!retW)
1617 ret = 0;
1618 else if (retW > MAX_PATH)
1620 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1621 ret = 0;
1623 else
1625 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL);
1626 if (ret <= len)
1628 WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, len, NULL, NULL);
1629 ret--; /* length without 0 */
1631 if (lastpart)
1633 LPSTR p = buffer + strlen(buffer);
1635 if (*p != '\\')
1637 while ((p > buffer + 2) && (*p != '\\')) p--;
1638 *lastpart = p + 1;
1640 else *lastpart = NULL;
1645 RtlFreeUnicodeString(&nameW);
1646 return ret;
1650 /***********************************************************************
1651 * GetFullPathNameW (KERNEL32.@)
1653 DWORD WINAPI GetFullPathNameW( LPCWSTR name, DWORD len, LPWSTR buffer,
1654 LPWSTR *lastpart )
1656 DWORD ret = DOSFS_DoGetFullPathName( name, len, buffer );
1657 if (ret && (ret<=len) && buffer && lastpart)
1659 LPWSTR p = buffer + strlenW(buffer);
1660 if (*p != (WCHAR)'\\')
1662 while ((p > buffer + 2) && (*p != (WCHAR)'\\')) p--;
1663 *lastpart = p + 1;
1665 else *lastpart = NULL;
1667 return ret;
1671 /***********************************************************************
1672 * wine_get_unix_file_name (KERNEL32.@) Not a Windows API
1674 * Return the full Unix file name for a given path.
1675 * FIXME: convert dos file name to unicode
1677 BOOL WINAPI wine_get_unix_file_name( LPCSTR dos, LPSTR buffer, DWORD len )
1679 BOOL ret;
1680 DOS_FULL_NAME path;
1681 WCHAR dosW[MAX_PATHNAME_LEN];
1683 MultiByteToWideChar(CP_ACP, 0, dos, -1, dosW, MAX_PATHNAME_LEN);
1684 ret = DOSFS_GetFullName( dosW, FALSE, &path );
1685 if (ret && len)
1687 strncpy( buffer, path.long_name, len );
1688 buffer[len - 1] = 0; /* ensure 0 termination */
1690 return ret;
1694 /***********************************************************************
1695 * DOSFS_FindNextEx
1697 static int DOSFS_FindNextEx( FIND_FIRST_INFO *info, WIN32_FIND_DATAW *entry )
1699 DWORD attr = info->attr | FA_UNUSED | FA_ARCHIVE | FA_RDONLY | FILE_ATTRIBUTE_SYMLINK;
1700 UINT flags = DRIVE_GetFlags( info->drive );
1701 char *p, buffer[MAX_PATHNAME_LEN];
1702 const char *drive_path;
1703 int drive_root;
1704 LPCWSTR long_name, short_name;
1705 BY_HANDLE_FILE_INFORMATION fileinfo;
1706 WCHAR dos_name[13];
1708 if ((info->attr & ~(FA_UNUSED | FA_ARCHIVE | FA_RDONLY)) == FA_LABEL)
1710 if (info->cur_pos) return 0;
1711 entry->dwFileAttributes = FILE_ATTRIBUTE_LABEL;
1712 RtlSecondsSince1970ToTime( (time_t)0, (LARGE_INTEGER *)&entry->ftCreationTime );
1713 RtlSecondsSince1970ToTime( (time_t)0, (LARGE_INTEGER *)&entry->ftLastAccessTime );
1714 RtlSecondsSince1970ToTime( (time_t)0, (LARGE_INTEGER *)&entry->ftLastWriteTime );
1715 entry->nFileSizeHigh = 0;
1716 entry->nFileSizeLow = 0;
1717 entry->dwReserved0 = 0;
1718 entry->dwReserved1 = 0;
1719 DOSFS_ToDosDTAFormat( DRIVE_GetLabel( info->drive ), entry->cFileName );
1720 strcpyW( entry->cAlternateFileName, entry->cFileName );
1721 info->cur_pos++;
1722 TRACE("returning %s (%s) as label\n",
1723 debugstr_w(entry->cFileName), debugstr_w(entry->cAlternateFileName));
1724 return 1;
1727 drive_path = info->path + strlen(DRIVE_GetRoot( info->drive ));
1728 while ((*drive_path == '/') || (*drive_path == '\\')) drive_path++;
1729 drive_root = !*drive_path;
1731 lstrcpynA( buffer, info->path, sizeof(buffer) - 1 );
1732 strcat( buffer, "/" );
1733 p = buffer + strlen(buffer);
1735 while (DOSFS_ReadDir( info->u.dos_dir, &long_name, &short_name ))
1737 info->cur_pos++;
1739 /* Don't return '.' and '..' in the root of the drive */
1740 if (drive_root && (long_name[0] == '.') &&
1741 (!long_name[1] || ((long_name[1] == '.') && !long_name[2])))
1742 continue;
1744 /* Check the long mask */
1746 if (info->long_mask && *info->long_mask)
1748 if (!DOSFS_MatchLong( info->long_mask, long_name,
1749 flags & DRIVE_CASE_SENSITIVE )) continue;
1752 /* Check the short mask */
1754 if (info->short_mask)
1756 if (!short_name)
1758 DOSFS_Hash( long_name, dos_name, TRUE,
1759 !(flags & DRIVE_CASE_SENSITIVE) );
1760 short_name = dos_name;
1762 if (!DOSFS_MatchShort( info->short_mask, short_name )) continue;
1765 /* Check the file attributes */
1766 WideCharToMultiByte(DRIVE_GetCodepage(info->drive), 0, long_name, -1,
1767 p, sizeof(buffer) - (int)(p - buffer), NULL, NULL);
1768 if (!FILE_Stat( buffer, &fileinfo ))
1770 WARN("can't stat %s\n", buffer);
1771 continue;
1773 if ((fileinfo.dwFileAttributes & FILE_ATTRIBUTE_SYMLINK) &&
1774 (fileinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
1776 static const WCHAR wineW[] = {'w','i','n','e',0};
1777 static const WCHAR ShowDirSymlinksW[] = {'S','h','o','w','D','i','r','S','y','m','l','i','n','k','s',0};
1778 static int show_dir_symlinks = -1;
1779 if (show_dir_symlinks == -1)
1780 show_dir_symlinks = PROFILE_GetWineIniBool(wineW, ShowDirSymlinksW, 0);
1781 if (!show_dir_symlinks) continue;
1784 if (fileinfo.dwFileAttributes & ~attr) continue;
1786 /* We now have a matching entry; fill the result and return */
1788 entry->dwFileAttributes = fileinfo.dwFileAttributes;
1789 entry->ftCreationTime = fileinfo.ftCreationTime;
1790 entry->ftLastAccessTime = fileinfo.ftLastAccessTime;
1791 entry->ftLastWriteTime = fileinfo.ftLastWriteTime;
1792 entry->nFileSizeHigh = fileinfo.nFileSizeHigh;
1793 entry->nFileSizeLow = fileinfo.nFileSizeLow;
1795 if (short_name)
1796 DOSFS_ToDosDTAFormat( short_name, entry->cAlternateFileName );
1797 else
1798 DOSFS_Hash( long_name, entry->cAlternateFileName, FALSE,
1799 !(flags & DRIVE_CASE_SENSITIVE) );
1801 lstrcpynW( entry->cFileName, long_name, sizeof(entry->cFileName)/sizeof(entry->cFileName[0]) );
1802 if (!(flags & DRIVE_CASE_PRESERVING)) strlwrW( entry->cFileName );
1803 TRACE("returning %s (%s) %02lx %ld\n",
1804 debugstr_w(entry->cFileName), debugstr_w(entry->cAlternateFileName),
1805 entry->dwFileAttributes, entry->nFileSizeLow );
1806 return 1;
1808 return 0; /* End of directory */
1811 /***********************************************************************
1812 * DOSFS_FindNext
1814 * Find the next matching file. Return the number of entries read to find
1815 * the matching one, or 0 if no more entries.
1816 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
1817 * file name mask. Either or both can be NULL.
1819 * NOTE: This is supposed to be only called by the int21 emulation
1820 * routines. Thus, we should own the Win16Mutex anyway.
1821 * Nevertheless, we explicitly enter it to ensure the static
1822 * directory cache is protected.
1824 int DOSFS_FindNext( const char *path, const char *short_mask,
1825 const char *long_mask, int drive, BYTE attr,
1826 int skip, WIN32_FIND_DATAA *entry )
1828 static FIND_FIRST_INFO info;
1829 LPCWSTR short_name, long_name;
1830 int count;
1831 UNICODE_STRING short_maskW, long_maskW;
1832 WIN32_FIND_DATAW entryW;
1834 TRACE("(%s, %s, %s, %x, %x, %x, %p)\n", debugstr_a(path),
1835 debugstr_a(short_mask), debugstr_a(long_mask), drive, attr, skip,
1836 entry);
1838 _EnterWin16Lock();
1840 RtlCreateUnicodeStringFromAsciiz(&short_maskW, short_mask);
1841 RtlCreateUnicodeStringFromAsciiz(&long_maskW, long_mask);
1843 /* Check the cached directory */
1844 if (!(info.u.dos_dir && info.path == path && !strcmpW(info.short_mask, short_maskW.Buffer)
1845 && !strcmpW(info.long_mask, long_maskW.Buffer) && info.drive == drive
1846 && info.attr == attr && info.cur_pos <= skip))
1848 /* Not in the cache, open it anew */
1849 if (info.u.dos_dir) DOSFS_CloseDir( info.u.dos_dir );
1851 info.path = (LPSTR)path;
1852 RtlFreeHeap(GetProcessHeap(), 0, info.long_mask);
1853 RtlFreeHeap(GetProcessHeap(), 0, info.short_mask);
1854 info.long_mask = long_maskW.Buffer;
1855 info.short_mask = short_maskW.Buffer;
1856 info.attr = attr;
1857 info.drive = drive;
1858 info.cur_pos = 0;
1859 info.u.dos_dir = DOSFS_OpenDir( DRIVE_GetCodepage(drive), info.path );
1861 else
1863 RtlFreeUnicodeString(&short_maskW);
1864 RtlFreeUnicodeString(&long_maskW);
1867 /* Skip to desired position */
1868 while (info.cur_pos < skip)
1869 if (info.u.dos_dir && DOSFS_ReadDir( info.u.dos_dir, &long_name, &short_name ))
1870 info.cur_pos++;
1871 else
1872 break;
1874 if (info.u.dos_dir && info.cur_pos == skip && DOSFS_FindNextEx( &info, &entryW ))
1876 WideCharToMultiByte(CP_ACP, 0, entryW.cFileName, -1,
1877 entry->cFileName, sizeof(entry->cFileName), NULL, NULL);
1878 WideCharToMultiByte(CP_ACP, 0, entryW.cAlternateFileName, -1,
1879 entry->cAlternateFileName, sizeof(entry->cAlternateFileName), NULL, NULL);
1880 count = info.cur_pos - skip;
1882 else
1883 count = 0;
1885 if (!count)
1887 if (info.u.dos_dir) DOSFS_CloseDir( info.u.dos_dir );
1888 memset( &info, '\0', sizeof(info) );
1891 _LeaveWin16Lock();
1893 return count;
1896 /*************************************************************************
1897 * FindFirstFileExW (KERNEL32.@)
1899 HANDLE WINAPI FindFirstFileExW(
1900 LPCWSTR lpFileName,
1901 FINDEX_INFO_LEVELS fInfoLevelId,
1902 LPVOID lpFindFileData,
1903 FINDEX_SEARCH_OPS fSearchOp,
1904 LPVOID lpSearchFilter,
1905 DWORD dwAdditionalFlags)
1907 HGLOBAL handle;
1908 FIND_FIRST_INFO *info;
1910 if (!lpFileName)
1912 SetLastError(ERROR_PATH_NOT_FOUND);
1913 return INVALID_HANDLE_VALUE;
1916 if ((fSearchOp != FindExSearchNameMatch) || (dwAdditionalFlags != 0))
1918 FIXME("options not implemented 0x%08x 0x%08lx\n", fSearchOp, dwAdditionalFlags );
1919 return INVALID_HANDLE_VALUE;
1922 switch(fInfoLevelId)
1924 case FindExInfoStandard:
1926 WIN32_FIND_DATAW * data = (WIN32_FIND_DATAW *) lpFindFileData;
1927 char *p;
1928 INT long_mask_len;
1929 UINT codepage;
1931 data->dwReserved0 = data->dwReserved1 = 0x0;
1932 if (!lpFileName) return 0;
1933 if (lpFileName[0] == '\\' && lpFileName[1] == '\\')
1935 ERR("UNC path name\n");
1936 if (!(handle = GlobalAlloc(GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO)))) break;
1938 info = (FIND_FIRST_INFO *)GlobalLock( handle );
1939 info->u.smb_dir = SMB_FindFirst(lpFileName);
1940 if(!info->u.smb_dir)
1942 GlobalUnlock( handle );
1943 GlobalFree(handle);
1944 break;
1947 info->drive = -1;
1949 GlobalUnlock( handle );
1951 else
1953 DOS_FULL_NAME full_name;
1955 if (!DOSFS_GetFullName( lpFileName, FALSE, &full_name )) break;
1956 if (!(handle = GlobalAlloc(GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO)))) break;
1957 info = (FIND_FIRST_INFO *)GlobalLock( handle );
1958 info->path = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
1959 strcpy( info->path, full_name.long_name );
1961 codepage = DRIVE_GetCodepage(full_name.drive);
1962 p = strrchr( info->path, '/' );
1963 *p++ = '\0';
1964 long_mask_len = MultiByteToWideChar(codepage, 0, p, -1, NULL, 0);
1965 info->long_mask = HeapAlloc( GetProcessHeap(), 0, long_mask_len * sizeof(WCHAR) );
1966 MultiByteToWideChar(codepage, 0, p, -1, info->long_mask, long_mask_len);
1968 info->short_mask = NULL;
1969 info->attr = 0xff;
1970 info->drive = full_name.drive;
1971 info->cur_pos = 0;
1973 info->u.dos_dir = DOSFS_OpenDir( codepage, info->path );
1974 GlobalUnlock( handle );
1976 if (!FindNextFileW( handle, data ))
1978 FindClose( handle );
1979 SetLastError( ERROR_NO_MORE_FILES );
1980 break;
1982 return handle;
1984 break;
1985 default:
1986 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
1988 return INVALID_HANDLE_VALUE;
1991 /*************************************************************************
1992 * FindFirstFileA (KERNEL32.@)
1994 HANDLE WINAPI FindFirstFileA(
1995 LPCSTR lpFileName,
1996 WIN32_FIND_DATAA *lpFindData )
1998 return FindFirstFileExA(lpFileName, FindExInfoStandard, lpFindData,
1999 FindExSearchNameMatch, NULL, 0);
2002 /*************************************************************************
2003 * FindFirstFileExA (KERNEL32.@)
2005 HANDLE WINAPI FindFirstFileExA(
2006 LPCSTR lpFileName,
2007 FINDEX_INFO_LEVELS fInfoLevelId,
2008 LPVOID lpFindFileData,
2009 FINDEX_SEARCH_OPS fSearchOp,
2010 LPVOID lpSearchFilter,
2011 DWORD dwAdditionalFlags)
2013 HANDLE handle;
2014 WIN32_FIND_DATAA *dataA;
2015 WIN32_FIND_DATAW dataW;
2016 UNICODE_STRING pathW;
2018 if (!lpFileName)
2020 SetLastError(ERROR_PATH_NOT_FOUND);
2021 return INVALID_HANDLE_VALUE;
2024 if (!RtlCreateUnicodeStringFromAsciiz(&pathW, lpFileName))
2026 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2027 return INVALID_HANDLE_VALUE;
2030 handle = FindFirstFileExW(pathW.Buffer, fInfoLevelId, &dataW, fSearchOp, lpSearchFilter, dwAdditionalFlags);
2031 RtlFreeUnicodeString(&pathW);
2032 if (handle == INVALID_HANDLE_VALUE) return handle;
2034 dataA = (WIN32_FIND_DATAA *) lpFindFileData;
2035 dataA->dwFileAttributes = dataW.dwFileAttributes;
2036 dataA->ftCreationTime = dataW.ftCreationTime;
2037 dataA->ftLastAccessTime = dataW.ftLastAccessTime;
2038 dataA->ftLastWriteTime = dataW.ftLastWriteTime;
2039 dataA->nFileSizeHigh = dataW.nFileSizeHigh;
2040 dataA->nFileSizeLow = dataW.nFileSizeLow;
2041 WideCharToMultiByte( CP_ACP, 0, dataW.cFileName, -1,
2042 dataA->cFileName, sizeof(dataA->cFileName), NULL, NULL );
2043 WideCharToMultiByte( CP_ACP, 0, dataW.cAlternateFileName, -1,
2044 dataA->cAlternateFileName, sizeof(dataA->cAlternateFileName), NULL, NULL );
2045 return handle;
2048 /*************************************************************************
2049 * FindFirstFileW (KERNEL32.@)
2051 HANDLE WINAPI FindFirstFileW( LPCWSTR lpFileName, WIN32_FIND_DATAW *lpFindData )
2053 return FindFirstFileExW(lpFileName, FindExInfoStandard, lpFindData,
2054 FindExSearchNameMatch, NULL, 0);
2057 /*************************************************************************
2058 * FindNextFileW (KERNEL32.@)
2060 BOOL WINAPI FindNextFileW( HANDLE handle, WIN32_FIND_DATAW *data )
2062 FIND_FIRST_INFO *info;
2063 BOOL ret = FALSE;
2064 DWORD gle = ERROR_NO_MORE_FILES;
2066 if ((handle == INVALID_HANDLE_VALUE) ||
2067 !(info = (FIND_FIRST_INFO *)GlobalLock( handle )))
2069 SetLastError( ERROR_INVALID_HANDLE );
2070 return ret;
2072 if (info->drive == -1)
2074 ret = SMB_FindNext( info->u.smb_dir, data );
2075 if(!ret)
2077 SMB_CloseDir( info->u.smb_dir );
2078 HeapFree( GetProcessHeap(), 0, info->path );
2080 goto done;
2082 else if (!info->path || !info->u.dos_dir)
2084 goto done;
2086 else if (!DOSFS_FindNextEx( info, data ))
2088 DOSFS_CloseDir( info->u.dos_dir ); info->u.dos_dir = NULL;
2089 HeapFree( GetProcessHeap(), 0, info->path );
2090 info->path = NULL;
2091 HeapFree( GetProcessHeap(), 0, info->long_mask );
2092 info->long_mask = NULL;
2093 goto done;
2095 ret = TRUE;
2096 done:
2097 GlobalUnlock( handle );
2098 if( !ret ) SetLastError( gle );
2099 return ret;
2103 /*************************************************************************
2104 * FindNextFileA (KERNEL32.@)
2106 BOOL WINAPI FindNextFileA( HANDLE handle, WIN32_FIND_DATAA *data )
2108 WIN32_FIND_DATAW dataW;
2109 if (!FindNextFileW( handle, &dataW )) return FALSE;
2110 data->dwFileAttributes = dataW.dwFileAttributes;
2111 data->ftCreationTime = dataW.ftCreationTime;
2112 data->ftLastAccessTime = dataW.ftLastAccessTime;
2113 data->ftLastWriteTime = dataW.ftLastWriteTime;
2114 data->nFileSizeHigh = dataW.nFileSizeHigh;
2115 data->nFileSizeLow = dataW.nFileSizeLow;
2116 WideCharToMultiByte( CP_ACP, 0, dataW.cFileName, -1,
2117 data->cFileName, sizeof(data->cFileName), NULL, NULL );
2118 WideCharToMultiByte( CP_ACP, 0, dataW.cAlternateFileName, -1,
2119 data->cAlternateFileName,
2120 sizeof(data->cAlternateFileName), NULL, NULL );
2121 return TRUE;
2124 /*************************************************************************
2125 * FindClose (KERNEL32.@)
2127 BOOL WINAPI FindClose( HANDLE handle )
2129 FIND_FIRST_INFO *info;
2131 if (handle == INVALID_HANDLE_VALUE) goto error;
2133 __TRY
2135 if ((info = (FIND_FIRST_INFO *)GlobalLock( handle )))
2137 if (info->u.dos_dir) DOSFS_CloseDir( info->u.dos_dir );
2138 if (info->path) HeapFree( GetProcessHeap(), 0, info->path );
2139 if (info->long_mask) HeapFree( GetProcessHeap(), 0, info->long_mask );
2142 __EXCEPT(page_fault)
2144 WARN("Illegal handle %x\n", handle);
2145 SetLastError( ERROR_INVALID_HANDLE );
2146 return FALSE;
2148 __ENDTRY
2149 if (!info) goto error;
2150 GlobalUnlock( handle );
2151 GlobalFree( handle );
2152 return TRUE;
2154 error:
2155 SetLastError( ERROR_INVALID_HANDLE );
2156 return FALSE;
2159 /***********************************************************************
2160 * DOSFS_UnixTimeToFileTime
2162 * Convert a Unix time to FILETIME format.
2163 * The FILETIME structure is a 64-bit value representing the number of
2164 * 100-nanosecond intervals since January 1, 1601, 0:00.
2165 * 'remainder' is the nonnegative number of 100-ns intervals
2166 * corresponding to the time fraction smaller than 1 second that
2167 * couldn't be stored in the time_t value.
2169 void DOSFS_UnixTimeToFileTime( time_t unix_time, FILETIME *filetime,
2170 DWORD remainder )
2172 /* NOTES:
2174 CONSTANTS:
2175 The time difference between 1 January 1601, 00:00:00 and
2176 1 January 1970, 00:00:00 is 369 years, plus the leap years
2177 from 1604 to 1968, excluding 1700, 1800, 1900.
2178 This makes (1968 - 1600) / 4 - 3 = 89 leap days, and a total
2179 of 134774 days.
2181 Any day in that period had 24 * 60 * 60 = 86400 seconds.
2183 The time difference is 134774 * 86400 * 10000000, which can be written
2184 116444736000000000
2185 27111902 * 2^32 + 3577643008
2186 413 * 2^48 + 45534 * 2^32 + 54590 * 2^16 + 32768
2188 If you find that these constants are buggy, please change them in all
2189 instances in both conversion functions.
2191 VERSIONS:
2192 There are two versions, one of them uses long long variables and
2193 is presumably faster but not ISO C. The other one uses standard C
2194 data types and operations but relies on the assumption that negative
2195 numbers are stored as 2's complement (-1 is 0xffff....). If this
2196 assumption is violated, dates before 1970 will not convert correctly.
2197 This should however work on any reasonable architecture where WINE
2198 will run.
2200 DETAILS:
2202 Take care not to remove the casts. I have tested these functions
2203 (in both versions) for a lot of numbers. I would be interested in
2204 results on other compilers than GCC.
2206 The operations have been designed to account for the possibility
2207 of 64-bit time_t in future UNICES. Even the versions without
2208 internal long long numbers will work if time_t only is 64 bit.
2209 A 32-bit shift, which was necessary for that operation, turned out
2210 not to work correctly in GCC, besides giving the warning. So I
2211 used a double 16-bit shift instead. Numbers are in the ISO version
2212 represented by three limbs, the most significant with 32 bit, the
2213 other two with 16 bit each.
2215 As the modulo-operator % is not well-defined for negative numbers,
2216 negative divisors have been avoided in DOSFS_FileTimeToUnixTime.
2218 There might be quicker ways to do this in C. Certainly so in
2219 assembler.
2221 Claus Fischer, fischer@iue.tuwien.ac.at
2224 #if SIZEOF_LONG_LONG >= 8
2225 # define USE_LONG_LONG 1
2226 #else
2227 # define USE_LONG_LONG 0
2228 #endif
2230 #if USE_LONG_LONG /* gcc supports long long type */
2232 long long int t = unix_time;
2233 t *= 10000000;
2234 t += 116444736000000000LL;
2235 t += remainder;
2236 filetime->dwLowDateTime = (UINT)t;
2237 filetime->dwHighDateTime = (UINT)(t >> 32);
2239 #else /* ISO version */
2241 UINT a0; /* 16 bit, low bits */
2242 UINT a1; /* 16 bit, medium bits */
2243 UINT a2; /* 32 bit, high bits */
2245 /* Copy the unix time to a2/a1/a0 */
2246 a0 = unix_time & 0xffff;
2247 a1 = (unix_time >> 16) & 0xffff;
2248 /* This is obsolete if unix_time is only 32 bits, but it does not hurt.
2249 Do not replace this by >> 32, it gives a compiler warning and it does
2250 not work. */
2251 a2 = (unix_time >= 0 ? (unix_time >> 16) >> 16 :
2252 ~((~unix_time >> 16) >> 16));
2254 /* Multiply a by 10000000 (a = a2/a1/a0)
2255 Split the factor into 10000 * 1000 which are both less than 0xffff. */
2256 a0 *= 10000;
2257 a1 = a1 * 10000 + (a0 >> 16);
2258 a2 = a2 * 10000 + (a1 >> 16);
2259 a0 &= 0xffff;
2260 a1 &= 0xffff;
2262 a0 *= 1000;
2263 a1 = a1 * 1000 + (a0 >> 16);
2264 a2 = a2 * 1000 + (a1 >> 16);
2265 a0 &= 0xffff;
2266 a1 &= 0xffff;
2268 /* Add the time difference and the remainder */
2269 a0 += 32768 + (remainder & 0xffff);
2270 a1 += 54590 + (remainder >> 16 ) + (a0 >> 16);
2271 a2 += 27111902 + (a1 >> 16);
2272 a0 &= 0xffff;
2273 a1 &= 0xffff;
2275 /* Set filetime */
2276 filetime->dwLowDateTime = (a1 << 16) + a0;
2277 filetime->dwHighDateTime = a2;
2278 #endif
2282 /***********************************************************************
2283 * DOSFS_FileTimeToUnixTime
2285 * Convert a FILETIME format to Unix time.
2286 * If not NULL, 'remainder' contains the fractional part of the filetime,
2287 * in the range of [0..9999999] (even if time_t is negative).
2289 time_t DOSFS_FileTimeToUnixTime( const FILETIME *filetime, DWORD *remainder )
2291 /* Read the comment in the function DOSFS_UnixTimeToFileTime. */
2292 #if USE_LONG_LONG
2294 long long int t = filetime->dwHighDateTime;
2295 t <<= 32;
2296 t += (UINT)filetime->dwLowDateTime;
2297 t -= 116444736000000000LL;
2298 if (t < 0)
2300 if (remainder) *remainder = 9999999 - (-t - 1) % 10000000;
2301 return -1 - ((-t - 1) / 10000000);
2303 else
2305 if (remainder) *remainder = t % 10000000;
2306 return t / 10000000;
2309 #else /* ISO version */
2311 UINT a0; /* 16 bit, low bits */
2312 UINT a1; /* 16 bit, medium bits */
2313 UINT a2; /* 32 bit, high bits */
2314 UINT r; /* remainder of division */
2315 unsigned int carry; /* carry bit for subtraction */
2316 int negative; /* whether a represents a negative value */
2318 /* Copy the time values to a2/a1/a0 */
2319 a2 = (UINT)filetime->dwHighDateTime;
2320 a1 = ((UINT)filetime->dwLowDateTime ) >> 16;
2321 a0 = ((UINT)filetime->dwLowDateTime ) & 0xffff;
2323 /* Subtract the time difference */
2324 if (a0 >= 32768 ) a0 -= 32768 , carry = 0;
2325 else a0 += (1 << 16) - 32768 , carry = 1;
2327 if (a1 >= 54590 + carry) a1 -= 54590 + carry, carry = 0;
2328 else a1 += (1 << 16) - 54590 - carry, carry = 1;
2330 a2 -= 27111902 + carry;
2332 /* If a is negative, replace a by (-1-a) */
2333 negative = (a2 >= ((UINT)1) << 31);
2334 if (negative)
2336 /* Set a to -a - 1 (a is a2/a1/a0) */
2337 a0 = 0xffff - a0;
2338 a1 = 0xffff - a1;
2339 a2 = ~a2;
2342 /* Divide a by 10000000 (a = a2/a1/a0), put the rest into r.
2343 Split the divisor into 10000 * 1000 which are both less than 0xffff. */
2344 a1 += (a2 % 10000) << 16;
2345 a2 /= 10000;
2346 a0 += (a1 % 10000) << 16;
2347 a1 /= 10000;
2348 r = a0 % 10000;
2349 a0 /= 10000;
2351 a1 += (a2 % 1000) << 16;
2352 a2 /= 1000;
2353 a0 += (a1 % 1000) << 16;
2354 a1 /= 1000;
2355 r += (a0 % 1000) * 10000;
2356 a0 /= 1000;
2358 /* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
2359 if (negative)
2361 /* Set a to -a - 1 (a is a2/a1/a0) */
2362 a0 = 0xffff - a0;
2363 a1 = 0xffff - a1;
2364 a2 = ~a2;
2366 r = 9999999 - r;
2369 if (remainder) *remainder = r;
2371 /* Do not replace this by << 32, it gives a compiler warning and it does
2372 not work. */
2373 return ((((time_t)a2) << 16) << 16) + (a1 << 16) + a0;
2374 #endif
2378 /***********************************************************************
2379 * MulDiv (KERNEL32.@)
2380 * RETURNS
2381 * Result of multiplication and division
2382 * -1: Overflow occurred or Divisor was 0
2384 INT WINAPI MulDiv(
2385 INT nMultiplicand,
2386 INT nMultiplier,
2387 INT nDivisor)
2389 #if SIZEOF_LONG_LONG >= 8
2390 long long ret;
2392 if (!nDivisor) return -1;
2394 /* We want to deal with a positive divisor to simplify the logic. */
2395 if (nDivisor < 0)
2397 nMultiplicand = - nMultiplicand;
2398 nDivisor = -nDivisor;
2401 /* If the result is positive, we "add" to round. else, we subtract to round. */
2402 if ( ( (nMultiplicand < 0) && (nMultiplier < 0) ) ||
2403 ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
2404 ret = (((long long)nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
2405 else
2406 ret = (((long long)nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
2408 if ((ret > 2147483647) || (ret < -2147483647)) return -1;
2409 return ret;
2410 #else
2411 if (!nDivisor) return -1;
2413 /* We want to deal with a positive divisor to simplify the logic. */
2414 if (nDivisor < 0)
2416 nMultiplicand = - nMultiplicand;
2417 nDivisor = -nDivisor;
2420 /* If the result is positive, we "add" to round. else, we subtract to round. */
2421 if ( ( (nMultiplicand < 0) && (nMultiplier < 0) ) ||
2422 ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
2423 return ((nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
2425 return ((nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
2427 #endif
2431 /***********************************************************************
2432 * DosDateTimeToFileTime (KERNEL32.@)
2434 BOOL WINAPI DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft)
2436 struct tm newtm;
2437 #ifndef HAVE_TIMEGM
2438 struct tm *gtm;
2439 time_t time1, time2;
2440 #endif
2442 newtm.tm_sec = (fattime & 0x1f) * 2;
2443 newtm.tm_min = (fattime >> 5) & 0x3f;
2444 newtm.tm_hour = (fattime >> 11);
2445 newtm.tm_mday = (fatdate & 0x1f);
2446 newtm.tm_mon = ((fatdate >> 5) & 0x0f) - 1;
2447 newtm.tm_year = (fatdate >> 9) + 80;
2448 #ifdef HAVE_TIMEGM
2449 RtlSecondsSince1970ToTime( timegm(&newtm), (LARGE_INTEGER *)ft );
2450 #else
2451 time1 = mktime(&newtm);
2452 gtm = gmtime(&time1);
2453 time2 = mktime(gtm);
2454 RtlSecondsSince1970ToTime( 2*time1-time2, (LARGE_INTEGER *)ft );
2455 #endif
2456 return TRUE;
2460 /***********************************************************************
2461 * FileTimeToDosDateTime (KERNEL32.@)
2463 BOOL WINAPI FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate,
2464 LPWORD fattime )
2466 time_t unixtime = DOSFS_FileTimeToUnixTime( ft, NULL );
2467 struct tm *tm = gmtime( &unixtime );
2468 if (fattime)
2469 *fattime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
2470 if (fatdate)
2471 *fatdate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
2472 + tm->tm_mday;
2473 return TRUE;
2477 /***********************************************************************
2478 * LocalFileTimeToFileTime (KERNEL32.@)
2480 BOOL WINAPI LocalFileTimeToFileTime( const FILETIME *localft,
2481 LPFILETIME utcft )
2483 struct tm *xtm;
2484 DWORD remainder;
2485 time_t utctime;
2487 /* Converts from local to UTC. */
2488 time_t localtime = DOSFS_FileTimeToUnixTime( localft, &remainder );
2489 xtm = gmtime( &localtime );
2490 utctime = mktime(xtm);
2491 if(xtm->tm_isdst > 0) utctime-=3600;
2492 DOSFS_UnixTimeToFileTime( utctime, utcft, remainder );
2493 return TRUE;
2497 /***********************************************************************
2498 * FileTimeToLocalFileTime (KERNEL32.@)
2500 BOOL WINAPI FileTimeToLocalFileTime( const FILETIME *utcft,
2501 LPFILETIME localft )
2503 DWORD remainder;
2504 /* Converts from UTC to local. */
2505 time_t unixtime = DOSFS_FileTimeToUnixTime( utcft, &remainder );
2506 #ifdef HAVE_TIMEGM
2507 struct tm *xtm = localtime( &unixtime );
2508 time_t localtime;
2510 localtime = timegm(xtm);
2511 DOSFS_UnixTimeToFileTime( localtime, localft, remainder );
2513 #else
2514 struct tm *xtm;
2515 time_t time;
2517 xtm = gmtime( &unixtime );
2518 time = mktime(xtm);
2519 if(xtm->tm_isdst > 0) time-=3600;
2520 DOSFS_UnixTimeToFileTime( 2*unixtime-time, localft, remainder );
2521 #endif
2522 return TRUE;
2526 /***********************************************************************
2527 * FileTimeToSystemTime (KERNEL32.@)
2529 BOOL WINAPI FileTimeToSystemTime( const FILETIME *ft, LPSYSTEMTIME syst )
2531 struct tm *xtm;
2532 DWORD remainder;
2533 time_t xtime = DOSFS_FileTimeToUnixTime( ft, &remainder );
2534 xtm = gmtime(&xtime);
2535 syst->wYear = xtm->tm_year+1900;
2536 syst->wMonth = xtm->tm_mon + 1;
2537 syst->wDayOfWeek = xtm->tm_wday;
2538 syst->wDay = xtm->tm_mday;
2539 syst->wHour = xtm->tm_hour;
2540 syst->wMinute = xtm->tm_min;
2541 syst->wSecond = xtm->tm_sec;
2542 syst->wMilliseconds = remainder / 10000;
2543 return TRUE;
2546 /***********************************************************************
2547 * QueryDosDeviceA (KERNEL32.@)
2549 * returns array of strings terminated by \0, terminated by \0
2551 DWORD WINAPI QueryDosDeviceA(LPCSTR devname,LPSTR target,DWORD bufsize)
2553 LPSTR s;
2554 char buffer[200];
2556 TRACE("(%s,...)\n", devname ? devname : "<null>");
2557 if (!devname) {
2558 /* return known MSDOS devices */
2559 static const char devices[24] = "CON\0COM1\0COM2\0LPT1\0NUL\0\0";
2560 memcpy( target, devices, min(bufsize,sizeof(devices)) );
2561 return min(bufsize,sizeof(devices));
2563 /* In theory all that are possible and have been defined.
2564 * Now just those below, since mirc uses it to check for special files.
2566 * (It is more complex, and supports netmounted stuff, and \\.\ stuff,
2567 * but currently we just ignore that.)
2569 #define CHECK(x) (strstr(devname,#x)==devname)
2570 if (CHECK(con) || CHECK(com) || CHECK(lpt) || CHECK(nul)) {
2571 strcpy(buffer,"\\DEV\\");
2572 strcat(buffer,devname);
2573 if ((s=strchr(buffer,':'))) *s='\0';
2574 lstrcpynA(target,buffer,bufsize);
2575 return strlen(buffer)+1;
2576 } else {
2577 if (strchr(devname,':') || devname[0]=='\\') {
2578 /* This might be a DOS device we do not handle yet ... */
2579 FIXME("(%s) not detected as DOS device!\n",devname);
2581 SetLastError(ERROR_DEV_NOT_EXIST);
2582 return 0;
2588 /***********************************************************************
2589 * QueryDosDeviceW (KERNEL32.@)
2591 * returns array of strings terminated by \0, terminated by \0
2593 DWORD WINAPI QueryDosDeviceW(LPCWSTR devname,LPWSTR target,DWORD bufsize)
2595 LPSTR devnameA = devname?HEAP_strdupWtoA(GetProcessHeap(),0,devname):NULL;
2596 LPSTR targetA = (LPSTR)HeapAlloc(GetProcessHeap(),0,bufsize);
2597 DWORD ret = QueryDosDeviceA(devnameA,targetA,bufsize);
2599 ret = MultiByteToWideChar( CP_ACP, 0, targetA, ret, target, bufsize );
2600 if (devnameA) HeapFree(GetProcessHeap(),0,devnameA);
2601 if (targetA) HeapFree(GetProcessHeap(),0,targetA);
2602 return ret;
2606 /***********************************************************************
2607 * SystemTimeToFileTime (KERNEL32.@)
2609 BOOL WINAPI SystemTimeToFileTime( const SYSTEMTIME *syst, LPFILETIME ft )
2611 #ifdef HAVE_TIMEGM
2612 struct tm xtm;
2613 time_t utctime;
2614 #else
2615 struct tm xtm,*utc_tm;
2616 time_t localtim,utctime;
2617 #endif
2619 xtm.tm_year = syst->wYear-1900;
2620 xtm.tm_mon = syst->wMonth - 1;
2621 xtm.tm_wday = syst->wDayOfWeek;
2622 xtm.tm_mday = syst->wDay;
2623 xtm.tm_hour = syst->wHour;
2624 xtm.tm_min = syst->wMinute;
2625 xtm.tm_sec = syst->wSecond; /* this is UTC */
2626 xtm.tm_isdst = -1;
2627 #ifdef HAVE_TIMEGM
2628 utctime = timegm(&xtm);
2629 DOSFS_UnixTimeToFileTime( utctime, ft,
2630 syst->wMilliseconds * 10000 );
2631 #else
2632 localtim = mktime(&xtm); /* now we've got local time */
2633 utc_tm = gmtime(&localtim);
2634 utctime = mktime(utc_tm);
2635 DOSFS_UnixTimeToFileTime( 2*localtim -utctime, ft,
2636 syst->wMilliseconds * 10000 );
2637 #endif
2638 return TRUE;
2641 /***********************************************************************
2642 * DefineDosDeviceA (KERNEL32.@)
2644 BOOL WINAPI DefineDosDeviceA(DWORD flags,LPCSTR devname,LPCSTR targetpath) {
2645 FIXME("(0x%08lx,%s,%s),stub!\n",flags,devname,targetpath);
2646 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2647 return FALSE;
2651 --- 16 bit functions ---
2654 /*************************************************************************
2655 * FindFirstFile (KERNEL.413)
2657 HANDLE16 WINAPI FindFirstFile16( LPCSTR path, WIN32_FIND_DATAA *data )
2659 DOS_FULL_NAME full_name;
2660 HGLOBAL16 handle;
2661 FIND_FIRST_INFO *info;
2662 WCHAR pathW[MAX_PATH];
2663 char *p;
2664 INT long_mask_len;
2665 UINT codepage;
2667 data->dwReserved0 = data->dwReserved1 = 0x0;
2668 if (!path) return INVALID_HANDLE_VALUE16;
2669 MultiByteToWideChar(CP_ACP, 0, path, -1, pathW, MAX_PATH);
2670 if (!DOSFS_GetFullName( pathW, FALSE, &full_name ))
2671 return INVALID_HANDLE_VALUE16;
2672 if (!(handle = GlobalAlloc16( GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO) )))
2673 return INVALID_HANDLE_VALUE16;
2674 info = (FIND_FIRST_INFO *)GlobalLock16( handle );
2675 info->path = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
2676 strcpy( info->path, full_name.long_name );
2678 codepage = DRIVE_GetCodepage(full_name.drive);
2679 p = strrchr( info->path, '/' );
2680 *p++ = '\0';
2681 long_mask_len = MultiByteToWideChar(codepage, 0, p, -1, NULL, 0);
2682 info->long_mask = HeapAlloc( GetProcessHeap(), 0, long_mask_len * sizeof(WCHAR) );
2683 MultiByteToWideChar(codepage, 0, p, -1, info->long_mask, long_mask_len);
2685 info->short_mask = NULL;
2686 info->attr = 0xff;
2687 info->drive = full_name.drive;
2688 info->cur_pos = 0;
2690 info->u.dos_dir = DOSFS_OpenDir( codepage, info->path );
2692 GlobalUnlock16( handle );
2693 if (!FindNextFile16( handle, data ))
2695 FindClose16( handle );
2696 SetLastError( ERROR_NO_MORE_FILES );
2697 return INVALID_HANDLE_VALUE16;
2699 return handle;
2702 /*************************************************************************
2703 * FindNextFile (KERNEL.414)
2705 BOOL16 WINAPI FindNextFile16( HANDLE16 handle, WIN32_FIND_DATAA *data )
2707 FIND_FIRST_INFO *info;
2708 WIN32_FIND_DATAW dataW;
2709 BOOL ret = FALSE;
2710 DWORD gle = ERROR_NO_MORE_FILES;
2712 if ((handle == INVALID_HANDLE_VALUE16) ||
2713 !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
2715 SetLastError( ERROR_INVALID_HANDLE );
2716 return ret;
2718 if (!info->path || !info->u.dos_dir)
2720 goto done;
2722 if (!DOSFS_FindNextEx( info, &dataW ))
2724 DOSFS_CloseDir( info->u.dos_dir ); info->u.dos_dir = NULL;
2725 HeapFree( GetProcessHeap(), 0, info->path );
2726 info->path = NULL;
2727 HeapFree( GetProcessHeap(), 0, info->long_mask );
2728 info->long_mask = NULL;
2729 goto done;
2732 ret = TRUE;
2734 data->dwFileAttributes = dataW.dwFileAttributes;
2735 data->ftCreationTime = dataW.ftCreationTime;
2736 data->ftLastAccessTime = dataW.ftLastAccessTime;
2737 data->ftLastWriteTime = dataW.ftLastWriteTime;
2738 data->nFileSizeHigh = dataW.nFileSizeHigh;
2739 data->nFileSizeLow = dataW.nFileSizeLow;
2740 WideCharToMultiByte( CP_ACP, 0, dataW.cFileName, -1,
2741 data->cFileName, sizeof(data->cFileName), NULL, NULL );
2742 WideCharToMultiByte( CP_ACP, 0, dataW.cAlternateFileName, -1,
2743 data->cAlternateFileName,
2744 sizeof(data->cAlternateFileName), NULL, NULL );
2745 done:
2746 if( !ret ) SetLastError( gle );
2747 GlobalUnlock16( handle );
2749 return ret;
2752 /*************************************************************************
2753 * FindClose (KERNEL.415)
2755 BOOL16 WINAPI FindClose16( HANDLE16 handle )
2757 FIND_FIRST_INFO *info;
2759 if ((handle == INVALID_HANDLE_VALUE16) ||
2760 !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
2762 SetLastError( ERROR_INVALID_HANDLE );
2763 return FALSE;
2765 if (info->u.dos_dir) DOSFS_CloseDir( info->u.dos_dir );
2766 if (info->path) HeapFree( GetProcessHeap(), 0, info->path );
2767 if (info->long_mask) HeapFree( GetProcessHeap(), 0, info->long_mask );
2768 GlobalUnlock16( handle );
2769 GlobalFree16( handle );
2770 return TRUE;