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