int33 21h is identical to int33 00h.
[wine.git] / files / dos_fs.c
blob62688c482ddb919af37291ec11ce3c18221750df
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 #define NONAMELESSUNION
44 #define NONAMELESSSTRUCT
45 #include "windef.h"
46 #include "winerror.h"
47 #include "wingdi.h"
49 #include "wine/unicode.h"
50 #include "wine/winbase16.h"
51 #include "drive.h"
52 #include "file.h"
53 #include "heap.h"
54 #include "msdos.h"
55 #include "winternl.h"
56 #include "wine/server.h"
57 #include "excpt.h"
59 #include "smb.h"
61 #include "wine/debug.h"
63 WINE_DEFAULT_DEBUG_CHANNEL(dosfs);
64 WINE_DECLARE_DEBUG_CHANNEL(file);
66 /* Define the VFAT ioctl to get both short and long file names */
67 /* FIXME: is it possible to get this to work on other systems? */
68 #ifdef linux
69 /* We want the real kernel dirent structure, not the libc one */
70 typedef struct
72 long d_ino;
73 long d_off;
74 unsigned short d_reclen;
75 char d_name[256];
76 } KERNEL_DIRENT;
78 #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, KERNEL_DIRENT [2] )
80 /* To avoid blocking on non-directories in DOSFS_OpenDir_VFAT*/
81 #ifndef O_DIRECTORY
82 # define O_DIRECTORY 0200000 /* must be directory */
83 #endif
85 #else /* linux */
86 #undef VFAT_IOCTL_READDIR_BOTH /* just in case... */
87 #endif /* linux */
89 #define IS_OPTION_TRUE(ch) ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1')
91 /* Chars we don't want to see in DOS file names */
92 #define INVALID_DOS_CHARS "*?<>|\"+=,;[] \345"
94 static const DOS_DEVICE DOSFS_Devices[] =
95 /* name, device flags (see Int 21/AX=0x4400) */
97 { {'C','O','N',0}, 0xc0d3 },
98 { {'P','R','N',0}, 0xa0c0 },
99 { {'N','U','L',0}, 0x80c4 },
100 { {'A','U','X',0}, 0x80c0 },
101 { {'L','P','T','1',0}, 0xa0c0 },
102 { {'L','P','T','2',0}, 0xa0c0 },
103 { {'L','P','T','3',0}, 0xa0c0 },
104 { {'L','P','T','4',0}, 0xc0d3 },
105 { {'C','O','M','1',0}, 0x80c0 },
106 { {'C','O','M','2',0}, 0x80c0 },
107 { {'C','O','M','3',0}, 0x80c0 },
108 { {'C','O','M','4',0}, 0x80c0 },
109 { {'S','C','S','I','M','G','R','$',0}, 0xc0c0 },
110 { {'H','P','S','C','A','N',0}, 0xc0c0 },
111 { {'E','M','M','X','X','X','X','0',0}, 0x0000 }
115 * Directory info for DOSFS_ReadDir
116 * contains the names of *all* the files in the directory
118 typedef struct
120 int used;
121 int size;
122 WCHAR names[1];
123 } DOS_DIR;
125 /* Info structure for FindFirstFile handle */
126 typedef struct
128 char *path; /* unix path */
129 LPWSTR long_mask;
130 LPWSTR short_mask;
131 BYTE attr;
132 int drive;
133 int cur_pos;
134 union
136 DOS_DIR *dos_dir;
137 SMB_DIR *smb_dir;
138 } u;
139 } FIND_FIRST_INFO;
142 static WINE_EXCEPTION_FILTER(page_fault)
144 if (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION)
145 return EXCEPTION_EXECUTE_HANDLER;
146 return EXCEPTION_CONTINUE_SEARCH;
150 /***********************************************************************
151 * DOSFS_ValidDOSName
153 * Return 1 if Unix file 'name' is also a valid MS-DOS name
154 * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
155 * File name can be terminated by '\0', '\\' or '/'.
157 static int DOSFS_ValidDOSName( LPCWSTR name, int ignore_case )
159 static const char invalid_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS;
160 const WCHAR *p = name;
161 const char *invalid = ignore_case ? (invalid_chars + 26) : invalid_chars;
162 int len = 0;
164 if (*p == '.')
166 /* Check for "." and ".." */
167 p++;
168 if (*p == '.') p++;
169 /* All other names beginning with '.' are invalid */
170 return (IS_END_OF_NAME(*p));
172 while (!IS_END_OF_NAME(*p))
174 if (*p < 256 && strchr( invalid, (char)*p )) return 0; /* Invalid char */
175 if (*p == '.') break; /* Start of the extension */
176 if (++len > 8) return 0; /* Name too long */
177 p++;
179 if (*p != '.') return 1; /* End of name */
180 p++;
181 if (IS_END_OF_NAME(*p)) return 0; /* Empty extension not allowed */
182 len = 0;
183 while (!IS_END_OF_NAME(*p))
185 if (*p < 256 && strchr( invalid, (char)*p )) return 0; /* Invalid char */
186 if (*p == '.') return 0; /* Second extension not allowed */
187 if (++len > 3) return 0; /* Extension too long */
188 p++;
190 return 1;
194 /***********************************************************************
195 * DOSFS_ToDosFCBFormat
197 * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
198 * expanding wild cards and converting to upper-case in the process.
199 * File name can be terminated by '\0', '\\' or '/'.
200 * Return FALSE if the name is not a valid DOS name.
201 * 'buffer' must be at least 12 characters long.
203 BOOL DOSFS_ToDosFCBFormat( LPCWSTR name, LPWSTR buffer )
205 static const char invalid_chars[] = INVALID_DOS_CHARS;
206 LPCWSTR p = name;
207 int i;
209 /* Check for "." and ".." */
210 if (*p == '.')
212 p++;
213 buffer[0] = '.';
214 for(i = 1; i < 11; i++) buffer[i] = ' ';
215 buffer[11] = 0;
216 if (*p == '.')
218 buffer[1] = '.';
219 p++;
221 return (!*p || (*p == '/') || (*p == '\\'));
224 for (i = 0; i < 8; i++)
226 switch(*p)
228 case '\0':
229 case '\\':
230 case '/':
231 case '.':
232 buffer[i] = ' ';
233 break;
234 case '?':
235 p++;
236 /* fall through */
237 case '*':
238 buffer[i] = '?';
239 break;
240 default:
241 if (*p < 256 && strchr( invalid_chars, (char)*p )) return FALSE;
242 buffer[i] = toupperW(*p);
243 p++;
244 break;
248 if (*p == '*')
250 /* Skip all chars after wildcard up to first dot */
251 while (*p && (*p != '/') && (*p != '\\') && (*p != '.')) p++;
253 else
255 /* Check if name too long */
256 if (*p && (*p != '/') && (*p != '\\') && (*p != '.')) return FALSE;
258 if (*p == '.') p++; /* Skip dot */
260 for (i = 8; i < 11; i++)
262 switch(*p)
264 case '\0':
265 case '\\':
266 case '/':
267 buffer[i] = ' ';
268 break;
269 case '.':
270 return FALSE; /* Second extension not allowed */
271 case '?':
272 p++;
273 /* fall through */
274 case '*':
275 buffer[i] = '?';
276 break;
277 default:
278 if (*p < 256 && strchr( invalid_chars, (char)*p )) return FALSE;
279 buffer[i] = toupperW(*p);
280 p++;
281 break;
284 buffer[11] = '\0';
286 /* at most 3 character of the extension are processed
287 * is something behind this ?
289 while (*p == '*' || *p == ' ') p++; /* skip wildcards and spaces */
290 return IS_END_OF_NAME(*p);
294 /***********************************************************************
295 * DOSFS_ToDosDTAFormat
297 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
298 * converting to upper-case in the process.
299 * File name can be terminated by '\0', '\\' or '/'.
300 * 'buffer' must be at least 13 characters long.
302 static void DOSFS_ToDosDTAFormat( LPCWSTR name, LPWSTR buffer )
304 LPWSTR p;
306 memcpy( buffer, name, 8 * sizeof(WCHAR) );
307 p = buffer + 8;
308 while ((p > buffer) && (p[-1] == ' ')) p--;
309 *p++ = '.';
310 memcpy( p, name + 8, 3 * sizeof(WCHAR) );
311 p += 3;
312 while (p[-1] == ' ') p--;
313 if (p[-1] == '.') p--;
314 *p = '\0';
318 /***********************************************************************
319 * DOSFS_MatchShort
321 * Check a DOS file name against a mask (both in FCB format).
323 static int DOSFS_MatchShort( LPCWSTR mask, LPCWSTR name )
325 int i;
326 for (i = 11; i > 0; i--, mask++, name++)
327 if ((*mask != '?') && (*mask != *name)) return 0;
328 return 1;
332 /***********************************************************************
333 * DOSFS_MatchLong
335 * Check a long file name against a mask.
337 * Tests (done in W95 DOS shell - case insensitive):
338 * *.txt test1.test.txt *
339 * *st1* test1.txt *
340 * *.t??????.t* test1.ta.tornado.txt *
341 * *tornado* test1.ta.tornado.txt *
342 * t*t test1.ta.tornado.txt *
343 * ?est* test1.txt *
344 * ?est??? test1.txt -
345 * *test1.txt* test1.txt *
346 * h?l?o*t.dat hellothisisatest.dat *
348 static int DOSFS_MatchLong( LPCWSTR mask, LPCWSTR name, int case_sensitive )
350 LPCWSTR lastjoker = NULL;
351 LPCWSTR next_to_retry = NULL;
352 static const WCHAR asterisk_dot_asterisk[] = {'*','.','*',0};
354 TRACE("(%s, %s, %x)\n", debugstr_w(mask), debugstr_w(name), case_sensitive);
356 if (!strcmpW( mask, asterisk_dot_asterisk )) return 1;
357 while (*name && *mask)
359 if (*mask == '*')
361 mask++;
362 while (*mask == '*') mask++; /* Skip consecutive '*' */
363 lastjoker = mask;
364 if (!*mask) return 1; /* end of mask is all '*', so match */
366 /* skip to the next match after the joker(s) */
367 if (case_sensitive) while (*name && (*name != *mask)) name++;
368 else while (*name && (toupperW(*name) != toupperW(*mask))) name++;
370 if (!*name) break;
371 next_to_retry = name;
373 else if (*mask != '?')
375 int mismatch = 0;
376 if (case_sensitive)
378 if (*mask != *name) mismatch = 1;
380 else
382 if (toupperW(*mask) != toupperW(*name)) mismatch = 1;
384 if (!mismatch)
386 mask++;
387 name++;
388 if (*mask == '\0')
390 if (*name == '\0')
391 return 1;
392 if (lastjoker)
393 mask = lastjoker;
396 else /* mismatch ! */
398 if (lastjoker) /* we had an '*', so we can try unlimitedly */
400 mask = lastjoker;
402 /* this scan sequence was a mismatch, so restart
403 * 1 char after the first char we checked last time */
404 next_to_retry++;
405 name = next_to_retry;
407 else
408 return 0; /* bad luck */
411 else /* '?' */
413 mask++;
414 name++;
417 while ((*mask == '.') || (*mask == '*'))
418 mask++; /* Ignore trailing '.' or '*' in mask */
419 return (!*name && !*mask);
423 /***********************************************************************
424 * DOSFS_AddDirEntry
426 * Used to construct an array of filenames in DOSFS_OpenDir
428 static BOOL DOSFS_AddDirEntry(DOS_DIR **dir, LPCWSTR name, LPCWSTR dosname)
430 int extra1 = strlenW(name) + 1;
431 int extra2 = strlenW(dosname) + 1;
433 /* if we need more, at minimum double the size */
434 if( (extra1 + extra2 + (*dir)->used) > (*dir)->size)
436 int more = (*dir)->size;
437 DOS_DIR *t;
439 if(more<(extra1+extra2))
440 more = extra1+extra2;
442 t = HeapReAlloc(GetProcessHeap(), 0, *dir, sizeof(**dir) +
443 ((*dir)->size + more)*sizeof(WCHAR) );
444 if(!t)
446 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
447 ERR("Out of memory caching directory structure %d %d %d\n",
448 (*dir)->size, more, (*dir)->used);
449 return FALSE;
451 (*dir) = t;
452 (*dir)->size += more;
455 /* at this point, the dir structure is big enough to hold these names */
456 strcpyW(&(*dir)->names[(*dir)->used], name);
457 (*dir)->used += extra1;
458 strcpyW(&(*dir)->names[(*dir)->used], dosname);
459 (*dir)->used += extra2;
461 return TRUE;
465 /***********************************************************************
466 * DOSFS_OpenDir_VFAT
468 static BOOL DOSFS_OpenDir_VFAT(UINT codepage, DOS_DIR **dir, const char *unix_path)
470 #ifdef VFAT_IOCTL_READDIR_BOTH
471 KERNEL_DIRENT de[2];
472 int fd = open( unix_path, O_RDONLY|O_DIRECTORY );
473 BOOL r = TRUE;
475 /* Check if the VFAT ioctl is supported on this directory */
477 if ( fd<0 )
478 return FALSE;
480 while (1)
482 WCHAR long_name[MAX_PATH];
483 WCHAR short_name[12];
485 r = (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de ) != -1);
486 if(!r)
487 break;
488 if (!de[0].d_reclen)
489 break;
490 MultiByteToWideChar(codepage, 0, de[0].d_name, -1, long_name, MAX_PATH);
491 if (!DOSFS_ToDosFCBFormat( long_name, short_name ))
492 short_name[0] = '\0';
493 if (de[1].d_name[0])
494 MultiByteToWideChar(codepage, 0, de[1].d_name, -1, long_name, MAX_PATH);
495 else
496 MultiByteToWideChar(codepage, 0, de[0].d_name, -1, long_name, MAX_PATH);
497 r = DOSFS_AddDirEntry(dir, long_name, short_name );
498 if(!r)
499 break;
501 if(r)
503 static const WCHAR empty_strW[] = { 0 };
504 DOSFS_AddDirEntry(dir, empty_strW, empty_strW);
506 close(fd);
507 return r;
508 #else
509 return FALSE;
510 #endif /* VFAT_IOCTL_READDIR_BOTH */
514 /***********************************************************************
515 * DOSFS_OpenDir_Normal
517 * Now use the standard opendir/readdir interface
519 static BOOL DOSFS_OpenDir_Normal( UINT codepage, DOS_DIR **dir, const char *unix_path )
521 DIR *unixdir = opendir( unix_path );
522 BOOL r = TRUE;
523 static const WCHAR empty_strW[] = { 0 };
525 if(!unixdir)
526 return FALSE;
527 while(1)
529 WCHAR long_name[MAX_PATH];
530 struct dirent *de = readdir(unixdir);
532 if(!de)
533 break;
534 MultiByteToWideChar(codepage, 0, de->d_name, -1, long_name, MAX_PATH);
535 r = DOSFS_AddDirEntry(dir, long_name, empty_strW);
536 if(!r)
537 break;
539 if(r)
540 DOSFS_AddDirEntry(dir, empty_strW, empty_strW);
541 closedir(unixdir);
542 return r;
545 /***********************************************************************
546 * DOSFS_OpenDir
548 static DOS_DIR *DOSFS_OpenDir( UINT codepage, const char *unix_path )
550 const int init_size = 0x100;
551 DOS_DIR *dir = HeapAlloc( GetProcessHeap(), 0, sizeof(*dir) + init_size*sizeof (WCHAR));
552 BOOL r;
554 TRACE("%s\n",debugstr_a(unix_path));
556 if (!dir)
558 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
559 return NULL;
561 dir->used = 0;
562 dir->size = init_size;
564 /* Treat empty path as root directory. This simplifies path split into
565 directory and mask in several other places */
566 if (!*unix_path) unix_path = "/";
568 r = DOSFS_OpenDir_VFAT( codepage, &dir, unix_path);
570 if(!r)
571 r = DOSFS_OpenDir_Normal( codepage, &dir, unix_path);
573 if(!r)
575 HeapFree(GetProcessHeap(), 0, dir);
576 return NULL;
578 dir->used = 0;
580 return dir;
584 /***********************************************************************
585 * DOSFS_CloseDir
587 static void DOSFS_CloseDir( DOS_DIR *dir )
589 HeapFree( GetProcessHeap(), 0, dir );
593 /***********************************************************************
594 * DOSFS_ReadDir
596 static BOOL DOSFS_ReadDir( DOS_DIR *dir, LPCWSTR *long_name,
597 LPCWSTR *short_name )
599 LPCWSTR sn, ln;
601 if (!dir)
602 return FALSE;
604 /* the long pathname is first */
605 ln = &dir->names[dir->used];
606 if(ln[0])
607 *long_name = ln;
608 else
609 return FALSE;
610 dir->used += (strlenW(ln) + 1);
612 /* followed by the short path name */
613 sn = &dir->names[dir->used];
614 if(sn[0])
615 *short_name = sn;
616 else
617 *short_name = NULL;
618 dir->used += (strlenW(sn) + 1);
620 return TRUE;
624 /***********************************************************************
625 * DOSFS_Hash
627 * Transform a Unix file name into a hashed DOS name. If the name is a valid
628 * DOS name, it is converted to upper-case; otherwise it is replaced by a
629 * hashed version that fits in 8.3 format.
630 * File name can be terminated by '\0', '\\' or '/'.
631 * 'buffer' must be at least 13 characters long.
633 static void DOSFS_Hash( LPCWSTR name, LPWSTR buffer, BOOL dir_format,
634 BOOL ignore_case )
636 static const char invalid_chars[] = INVALID_DOS_CHARS "~.";
637 static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
639 LPCWSTR p, ext;
640 LPWSTR dst;
641 unsigned short hash;
642 int i;
644 if (dir_format)
646 for(i = 0; i < 11; i++) buffer[i] = ' ';
647 buffer[11] = 0;
650 if (DOSFS_ValidDOSName( name, ignore_case ))
652 /* Check for '.' and '..' */
653 if (*name == '.')
655 buffer[0] = '.';
656 if (!dir_format) buffer[1] = buffer[2] = '\0';
657 if (name[1] == '.') buffer[1] = '.';
658 return;
661 /* Simply copy the name, converting to uppercase */
663 for (dst = buffer; !IS_END_OF_NAME(*name) && (*name != '.'); name++)
664 *dst++ = toupperW(*name);
665 if (*name == '.')
667 if (dir_format) dst = buffer + 8;
668 else *dst++ = '.';
669 for (name++; !IS_END_OF_NAME(*name); name++)
670 *dst++ = toupperW(*name);
672 if (!dir_format) *dst = '\0';
673 return;
676 /* Compute the hash code of the file name */
677 /* If you know something about hash functions, feel free to */
678 /* insert a better algorithm here... */
679 if (ignore_case)
681 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
682 hash = (hash<<3) ^ (hash>>5) ^ tolowerW(*p) ^ (tolowerW(p[1]) << 8);
683 hash = (hash<<3) ^ (hash>>5) ^ tolowerW(*p); /* Last character */
685 else
687 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
688 hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
689 hash = (hash << 3) ^ (hash >> 5) ^ *p; /* Last character */
692 /* Find last dot for start of the extension */
693 for (p = name+1, ext = NULL; !IS_END_OF_NAME(*p); p++)
694 if (*p == '.') ext = p;
695 if (ext && IS_END_OF_NAME(ext[1]))
696 ext = NULL; /* Empty extension ignored */
698 /* Copy first 4 chars, replacing invalid chars with '_' */
699 for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
701 if (IS_END_OF_NAME(*p) || (p == ext)) break;
702 *dst++ = (*p < 256 && strchr( invalid_chars, (char)*p )) ? '_' : toupperW(*p);
704 /* Pad to 5 chars with '~' */
705 while (i-- >= 0) *dst++ = '~';
707 /* Insert hash code converted to 3 ASCII chars */
708 *dst++ = hash_chars[(hash >> 10) & 0x1f];
709 *dst++ = hash_chars[(hash >> 5) & 0x1f];
710 *dst++ = hash_chars[hash & 0x1f];
712 /* Copy the first 3 chars of the extension (if any) */
713 if (ext)
715 if (!dir_format) *dst++ = '.';
716 for (i = 3, ext++; (i > 0) && !IS_END_OF_NAME(*ext); i--, ext++)
717 *dst++ = (*ext < 256 && strchr( invalid_chars, (char)*ext )) ? '_' : toupperW(*ext);
719 if (!dir_format) *dst = '\0';
723 /***********************************************************************
724 * DOSFS_FindUnixName
726 * Find the Unix file name in a given directory that corresponds to
727 * a file name (either in Unix or DOS format).
728 * File name can be terminated by '\0', '\\' or '/'.
729 * Return TRUE if OK, FALSE if no file name matches.
731 * 'long_buf' must be at least 'long_len' characters long. If the long name
732 * turns out to be larger than that, the function returns FALSE.
733 * 'short_buf' must be at least 13 characters long.
735 BOOL DOSFS_FindUnixName( const DOS_FULL_NAME *path, LPCWSTR name, char *long_buf,
736 INT long_len, LPWSTR short_buf, BOOL ignore_case)
738 DOS_DIR *dir;
739 LPCWSTR long_name, short_name;
740 WCHAR dos_name[12], tmp_buf[13];
741 BOOL ret;
743 LPCWSTR p = strchrW( name, '/' );
744 int len = p ? (int)(p - name) : strlenW(name);
745 if ((p = strchrW( name, '\\' ))) len = min( (int)(p - name), len );
746 /* Ignore trailing dots and spaces */
747 while (len > 1 && (name[len-1] == '.' || name[len-1] == ' ')) len--;
748 if (long_len < len + 1) return FALSE;
750 TRACE("%s,%s\n", path->long_name, debugstr_w(name) );
752 if (!DOSFS_ToDosFCBFormat( name, dos_name )) dos_name[0] = '\0';
754 if (!(dir = DOSFS_OpenDir( DRIVE_GetCodepage(path->drive), path->long_name )))
756 WARN("(%s,%s): can't open dir: %s\n",
757 path->long_name, debugstr_w(name), strerror(errno) );
758 return FALSE;
761 while ((ret = DOSFS_ReadDir( dir, &long_name, &short_name )))
763 /* Check against Unix name */
764 if (len == strlenW(long_name))
766 if (!ignore_case)
768 if (!strncmpW( long_name, name, len )) break;
770 else
772 if (!strncmpiW( long_name, name, len )) break;
775 if (dos_name[0])
777 /* Check against hashed DOS name */
778 if (!short_name)
780 DOSFS_Hash( long_name, tmp_buf, TRUE, ignore_case );
781 short_name = tmp_buf;
783 if (!strcmpW( dos_name, short_name )) break;
786 if (ret)
788 if (long_buf) WideCharToMultiByte(DRIVE_GetCodepage(path->drive), 0,
789 long_name, -1, long_buf, long_len, NULL, NULL);
790 if (short_buf)
792 if (short_name)
793 DOSFS_ToDosDTAFormat( short_name, short_buf );
794 else
795 DOSFS_Hash( long_name, short_buf, FALSE, ignore_case );
797 TRACE("(%s,%s) -> %s (%s)\n", path->long_name, debugstr_w(name),
798 debugstr_w(long_name), short_buf ? debugstr_w(short_buf) : "***");
800 else
801 WARN("%s not found in '%s'\n", debugstr_w(name), path->long_name);
802 DOSFS_CloseDir( dir );
803 return ret;
807 /***********************************************************************
808 * DOSFS_GetDevice
810 * Check if a DOS file name represents a DOS device and return the device.
812 const DOS_DEVICE *DOSFS_GetDevice( LPCWSTR name )
814 unsigned int i;
815 const WCHAR *p;
817 if (!name) return NULL; /* if wine_server_handle_to_fd was used */
818 if (name[0] && (name[1] == ':')) name += 2;
819 if ((p = strrchrW( name, '/' ))) name = p + 1;
820 if ((p = strrchrW( name, '\\' ))) name = p + 1;
821 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
823 const WCHAR *dev = DOSFS_Devices[i].name;
824 if (!strncmpiW( dev, name, strlenW(dev) ))
826 p = name + strlenW( dev );
827 if (!*p || (*p == '.') || (*p == ':')) return &DOSFS_Devices[i];
830 return NULL;
834 /***********************************************************************
835 * DOSFS_GetDeviceByHandle
837 const DOS_DEVICE *DOSFS_GetDeviceByHandle( HANDLE hFile )
839 const DOS_DEVICE *ret = NULL;
840 SERVER_START_REQ( get_device_id )
842 req->handle = hFile;
843 if (!wine_server_call( req ))
845 if ((reply->id >= 0) &&
846 (reply->id < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0])))
847 ret = &DOSFS_Devices[reply->id];
850 SERVER_END_REQ;
851 return ret;
855 /**************************************************************************
856 * DOSFS_CreateCommPort
858 static HANDLE DOSFS_CreateCommPort(LPCWSTR name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa)
860 HANDLE ret;
861 HKEY hkey;
862 DWORD dummy;
863 OBJECT_ATTRIBUTES attr;
864 UNICODE_STRING nameW;
865 WCHAR *devnameW;
866 char tmp[128];
867 char devname[40];
869 static const WCHAR serialportsW[] = {'M','a','c','h','i','n','e','\\',
870 'S','o','f','t','w','a','r','e','\\',
871 'W','i','n','e','\\','W','i','n','e','\\',
872 'C','o','n','f','i','g','\\',
873 'S','e','r','i','a','l','P','o','r','t','s',0};
875 TRACE_(file)("%s %lx %lx\n", debugstr_w(name), access, attributes);
877 attr.Length = sizeof(attr);
878 attr.RootDirectory = 0;
879 attr.ObjectName = &nameW;
880 attr.Attributes = 0;
881 attr.SecurityDescriptor = NULL;
882 attr.SecurityQualityOfService = NULL;
883 RtlInitUnicodeString( &nameW, serialportsW );
885 if (NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr )) return 0;
887 RtlInitUnicodeString( &nameW, name );
888 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
889 devnameW = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
890 else
891 devnameW = NULL;
893 NtClose( hkey );
895 if (!devnameW) return 0;
896 WideCharToMultiByte(CP_ACP, 0, devnameW, -1, devname, sizeof(devname), NULL, NULL);
898 TRACE("opening %s as %s\n", devname, debugstr_w(name));
900 SERVER_START_REQ( create_serial )
902 req->access = access;
903 req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
904 req->attributes = attributes;
905 req->sharing = FILE_SHARE_READ|FILE_SHARE_WRITE;
906 wine_server_add_data( req, devname, strlen(devname) );
907 SetLastError(0);
908 wine_server_call_err( req );
909 ret = reply->handle;
911 SERVER_END_REQ;
913 if(!ret)
914 ERR("Couldn't open device '%s' ! (check permissions)\n",devname);
915 else
916 TRACE("return %p\n", ret );
917 return ret;
920 /***********************************************************************
921 * DOSFS_OpenDevice
923 * Open a DOS device. This might not map 1:1 into the UNIX device concept.
924 * Returns 0 on failure.
926 HANDLE DOSFS_OpenDevice( LPCWSTR name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa )
928 unsigned int i;
929 const WCHAR *p;
930 HANDLE handle;
932 if (name[0] && (name[1] == ':')) name += 2;
933 if ((p = strrchrW( name, '/' ))) name = p + 1;
934 if ((p = strrchrW( name, '\\' ))) name = p + 1;
935 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
937 const WCHAR *dev = DOSFS_Devices[i].name;
938 if (!strncmpiW( dev, name, strlenW(dev) ))
940 p = name + strlenW( dev );
941 if (!*p || (*p == '.') || (*p == ':')) {
942 static const WCHAR nulW[] = {'N','U','L',0};
943 static const WCHAR conW[] = {'C','O','N',0};
944 static const WCHAR scsimgrW[] = {'S','C','S','I','M','G','R','$',0};
945 static const WCHAR hpscanW[] = {'H','P','S','C','A','N',0};
946 static const WCHAR emmxxxx0W[] = {'E','M','M','X','X','X','X','0',0};
947 /* got it */
948 if (!strcmpiW(DOSFS_Devices[i].name, nulW))
949 return FILE_CreateFile( "/dev/null", access,
950 FILE_SHARE_READ|FILE_SHARE_WRITE, sa,
951 OPEN_EXISTING, 0, 0, TRUE, DRIVE_UNKNOWN );
952 if (!strcmpiW(DOSFS_Devices[i].name, conW)) {
953 HANDLE to_dup;
954 switch (access & (GENERIC_READ|GENERIC_WRITE)) {
955 case GENERIC_READ:
956 to_dup = GetStdHandle( STD_INPUT_HANDLE );
957 break;
958 case GENERIC_WRITE:
959 to_dup = GetStdHandle( STD_OUTPUT_HANDLE );
960 break;
961 default:
962 FIXME("can't open CON read/write\n");
963 return 0;
965 if (!DuplicateHandle( GetCurrentProcess(), to_dup, GetCurrentProcess(),
966 &handle, 0,
967 sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle,
968 DUPLICATE_SAME_ACCESS ))
969 handle = 0;
970 return handle;
972 if (!strcmpiW(DOSFS_Devices[i].name, scsimgrW) ||
973 !strcmpiW(DOSFS_Devices[i].name, hpscanW) ||
974 !strcmpiW(DOSFS_Devices[i].name, emmxxxx0W))
976 return FILE_CreateDevice( i, access, sa );
979 if( (handle=DOSFS_CreateCommPort(DOSFS_Devices[i].name,access,attributes,sa)) )
980 return handle;
981 FIXME("device open %s not supported (yet)\n", debugstr_w(DOSFS_Devices[i].name));
982 return 0;
986 return 0;
990 /***********************************************************************
991 * DOSFS_GetPathDrive
993 * Get the drive specified by a given path name (DOS or Unix format).
995 static int DOSFS_GetPathDrive( LPCWSTR *name )
997 int drive;
998 LPCWSTR p = *name;
1000 if (*p && (p[1] == ':'))
1002 drive = toupperW(*p) - 'A';
1003 *name += 2;
1005 else if (*p == '/') /* Absolute Unix path? */
1007 if ((drive = DRIVE_FindDriveRootW( name )) == -1)
1009 MESSAGE("Warning: %s not accessible from a configured DOS drive\n", debugstr_w(*name) );
1010 /* Assume it really was a DOS name */
1011 drive = DRIVE_GetCurrentDrive();
1014 else drive = DRIVE_GetCurrentDrive();
1016 if (!DRIVE_IsValid(drive))
1018 SetLastError( ERROR_INVALID_DRIVE );
1019 return -1;
1021 return drive;
1025 /***********************************************************************
1026 * DOSFS_GetFullName
1028 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
1029 * Unix name / short DOS name pair.
1030 * Return FALSE if one of the path components does not exist. The last path
1031 * component is only checked if 'check_last' is non-zero.
1032 * The buffers pointed to by 'long_buf' and 'short_buf' must be
1033 * at least MAX_PATHNAME_LEN long.
1035 BOOL DOSFS_GetFullName( LPCWSTR name, BOOL check_last, DOS_FULL_NAME *full )
1037 BOOL found;
1038 UINT flags, codepage;
1039 char *p_l, *root;
1040 LPWSTR p_s;
1041 static const WCHAR driveA_rootW[] = {'A',':','\\',0};
1042 static const WCHAR dos_rootW[] = {'\\',0};
1044 TRACE("%s (last=%d)\n", debugstr_w(name), check_last );
1046 if ((!*name) || (*name=='\n'))
1047 { /* error code for Win98 */
1048 SetLastError(ERROR_BAD_PATHNAME);
1049 return FALSE;
1052 if ((full->drive = DOSFS_GetPathDrive( &name )) == -1) return FALSE;
1053 flags = DRIVE_GetFlags( full->drive );
1054 codepage = DRIVE_GetCodepage(full->drive);
1056 lstrcpynA( full->long_name, DRIVE_GetRoot( full->drive ),
1057 sizeof(full->long_name) );
1058 if (full->long_name[1]) root = full->long_name + strlen(full->long_name);
1059 else root = full->long_name; /* root directory */
1061 strcpyW( full->short_name, driveA_rootW );
1062 full->short_name[0] += full->drive;
1064 if ((*name == '\\') || (*name == '/')) /* Absolute path */
1066 while ((*name == '\\') || (*name == '/')) name++;
1068 else /* Relative path */
1070 lstrcpynA( root + 1, DRIVE_GetUnixCwd( full->drive ),
1071 sizeof(full->long_name) - (root - full->long_name) - 1 );
1072 if (root[1]) *root = '/';
1073 lstrcpynW( full->short_name + 3, DRIVE_GetDosCwd( full->drive ),
1074 sizeof(full->short_name)/sizeof(full->short_name[0]) - 3 );
1077 p_l = full->long_name[1] ? full->long_name + strlen(full->long_name)
1078 : full->long_name;
1079 p_s = full->short_name[3] ? full->short_name + strlenW(full->short_name)
1080 : full->short_name + 2;
1081 found = TRUE;
1083 while (*name && found)
1085 /* Check for '.' and '..' */
1087 if (*name == '.')
1089 if (IS_END_OF_NAME(name[1]))
1091 name++;
1092 while ((*name == '\\') || (*name == '/')) name++;
1093 continue;
1095 else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
1097 name += 2;
1098 while ((*name == '\\') || (*name == '/')) name++;
1099 while ((p_l > root) && (*p_l != '/')) p_l--;
1100 while ((p_s > full->short_name + 2) && (*p_s != '\\')) p_s--;
1101 *p_l = *p_s = '\0'; /* Remove trailing separator */
1102 continue;
1106 /* Make sure buffers are large enough */
1108 if ((p_s >= full->short_name + sizeof(full->short_name)/sizeof(full->short_name[0]) - 14) ||
1109 (p_l >= full->long_name + sizeof(full->long_name) - 1))
1111 SetLastError( ERROR_PATH_NOT_FOUND );
1112 return FALSE;
1115 /* Get the long and short name matching the file name */
1117 if ((found = DOSFS_FindUnixName( full, name, p_l + 1,
1118 sizeof(full->long_name) - (p_l - full->long_name) - 1,
1119 p_s + 1, !(flags & DRIVE_CASE_SENSITIVE) )))
1121 *p_l++ = '/';
1122 p_l += strlen(p_l);
1123 *p_s++ = '\\';
1124 p_s += strlenW(p_s);
1125 while (!IS_END_OF_NAME(*name)) name++;
1127 else if (!check_last)
1129 *p_l++ = '/';
1130 *p_s++ = '\\';
1131 while (!IS_END_OF_NAME(*name) &&
1132 (p_s < full->short_name + sizeof(full->short_name)/sizeof(full->short_name[0]) - 1) &&
1133 (p_l < full->long_name + sizeof(full->long_name) - 1))
1135 WCHAR wch;
1136 *p_s++ = tolowerW(*name);
1137 /* If the drive is case-sensitive we want to create new */
1138 /* files in lower-case otherwise we can't reopen them */
1139 /* under the same short name. */
1140 if (flags & DRIVE_CASE_SENSITIVE) wch = tolowerW(*name);
1141 else wch = *name;
1142 p_l += WideCharToMultiByte(codepage, 0, &wch, 1, p_l, 2, NULL, NULL);
1143 name++;
1145 /* Ignore trailing dots and spaces */
1146 while(p_l[-1] == '.' || p_l[-1] == ' ') {
1147 --p_l;
1148 --p_s;
1150 *p_l = '\0';
1151 *p_s = '\0';
1153 while ((*name == '\\') || (*name == '/')) name++;
1156 if (!found)
1158 if (check_last)
1160 SetLastError( ERROR_FILE_NOT_FOUND );
1161 return FALSE;
1163 if (*name) /* Not last */
1165 SetLastError( ERROR_PATH_NOT_FOUND );
1166 return FALSE;
1169 if (!full->long_name[0]) strcpy( full->long_name, "/" );
1170 if (!full->short_name[2]) strcpyW( full->short_name + 2, dos_rootW );
1171 TRACE("returning %s = %s\n", full->long_name, debugstr_w(full->short_name) );
1172 return TRUE;
1176 /***********************************************************************
1177 * GetShortPathNameW (KERNEL32.@)
1179 * NOTES
1180 * observed:
1181 * longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1182 * longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0
1184 * more observations ( with NT 3.51 (WinDD) ):
1185 * longpath <= 8.3 -> just copy longpath to shortpath
1186 * longpath > 8.3 ->
1187 * a) file does not exist -> return 0, LastError = ERROR_FILE_NOT_FOUND
1188 * b) file does exist -> set the short filename.
1189 * - trailing slashes are reproduced in the short name, even if the
1190 * file is not a directory
1191 * - the absolute/relative path of the short name is reproduced like found
1192 * in the long name
1193 * - longpath and shortpath may have the same address
1194 * Peter Ganten, 1999
1196 DWORD WINAPI GetShortPathNameW( LPCWSTR longpath, LPWSTR shortpath, DWORD shortlen )
1198 DOS_FULL_NAME full_name;
1199 WCHAR tmpshortpath[MAX_PATHNAME_LEN];
1200 const WCHAR *p;
1201 DWORD sp = 0, lp = 0;
1202 int drive;
1203 DWORD tmplen;
1204 UINT flags;
1205 BOOL unixabsolute = *longpath == '/';
1207 TRACE("%s\n", debugstr_w(longpath));
1209 if (!longpath) {
1210 SetLastError(ERROR_INVALID_PARAMETER);
1211 return 0;
1213 if (!longpath[0]) {
1214 SetLastError(ERROR_BAD_PATHNAME);
1215 return 0;
1218 /* check for drive letter */
1219 if (!unixabsolute && longpath[1] == ':' ) {
1220 tmpshortpath[0] = longpath[0];
1221 tmpshortpath[1] = ':';
1222 sp = 2;
1225 if ( ( drive = DOSFS_GetPathDrive ( &longpath )) == -1 ) return 0;
1226 flags = DRIVE_GetFlags ( drive );
1228 if (unixabsolute && drive != DRIVE_GetCurrentDrive()) {
1229 tmpshortpath[0] = drive + 'A';
1230 tmpshortpath[1] = ':';
1231 sp = 2;
1234 while ( longpath[lp] ) {
1236 /* check for path delimiters and reproduce them */
1237 if ( longpath[lp] == '\\' || longpath[lp] == '/' ) {
1238 if (!sp || tmpshortpath[sp-1]!= '\\')
1240 /* strip double "\\" */
1241 tmpshortpath[sp] = '\\';
1242 sp++;
1244 tmpshortpath[sp]=0;/*terminate string*/
1245 lp++;
1246 continue;
1249 tmplen = 0;
1250 for(p = longpath + lp; *p && *p != '/' && *p != '\\'; p++)
1251 tmplen++;
1252 lstrcpynW(tmpshortpath + sp, longpath + lp, tmplen + 1);
1254 /* Check, if the current element is a valid dos name */
1255 if ( DOSFS_ValidDOSName ( longpath + lp, !(flags & DRIVE_CASE_SENSITIVE) ) ) {
1256 sp += tmplen;
1257 lp += tmplen;
1258 continue;
1261 /* Check if the file exists and use the existing file name */
1262 if ( DOSFS_GetFullName ( tmpshortpath, TRUE, &full_name ) ) {
1263 strcpyW(tmpshortpath + sp, strrchrW(full_name.short_name, '\\') + 1);
1264 sp += strlenW(tmpshortpath + sp);
1265 lp += tmplen;
1266 continue;
1269 TRACE("not found!\n" );
1270 SetLastError ( ERROR_FILE_NOT_FOUND );
1271 return 0;
1273 tmpshortpath[sp] = 0;
1275 tmplen = strlenW(tmpshortpath) + 1;
1276 if (tmplen <= shortlen)
1278 strcpyW(shortpath, tmpshortpath);
1279 TRACE("returning %s\n", debugstr_w(shortpath));
1280 tmplen--; /* length without 0 */
1283 return tmplen;
1287 /***********************************************************************
1288 * GetShortPathNameA (KERNEL32.@)
1290 DWORD WINAPI GetShortPathNameA( LPCSTR longpath, LPSTR shortpath, DWORD shortlen )
1292 UNICODE_STRING longpathW;
1293 WCHAR shortpathW[MAX_PATH];
1294 DWORD ret, retW;
1296 if (!longpath)
1298 SetLastError(ERROR_INVALID_PARAMETER);
1299 return 0;
1302 TRACE("%s\n", debugstr_a(longpath));
1304 if (!RtlCreateUnicodeStringFromAsciiz(&longpathW, longpath))
1306 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1307 return 0;
1310 retW = GetShortPathNameW(longpathW.Buffer, shortpathW, MAX_PATH);
1312 if (!retW)
1313 ret = 0;
1314 else if (retW > MAX_PATH)
1316 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1317 ret = 0;
1319 else
1321 ret = WideCharToMultiByte(CP_ACP, 0, shortpathW, -1, NULL, 0, NULL, NULL);
1322 if (ret <= shortlen)
1324 WideCharToMultiByte(CP_ACP, 0, shortpathW, -1, shortpath, shortlen, NULL, NULL);
1325 ret--; /* length without 0 */
1329 RtlFreeUnicodeString(&longpathW);
1330 return ret;
1334 /***********************************************************************
1335 * GetLongPathNameW (KERNEL32.@)
1337 * NOTES
1338 * observed (Win2000):
1339 * shortpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1340 * shortpath="": LastError=ERROR_PATH_NOT_FOUND, ret=0
1342 DWORD WINAPI GetLongPathNameW( LPCWSTR shortpath, LPWSTR longpath, DWORD longlen )
1344 DOS_FULL_NAME full_name;
1345 const char *root;
1346 LPWSTR p;
1347 int drive;
1348 UINT codepage;
1349 DWORD ret, len = 0;
1351 if (!shortpath) {
1352 SetLastError(ERROR_INVALID_PARAMETER);
1353 return 0;
1355 if (!shortpath[0]) {
1356 SetLastError(ERROR_PATH_NOT_FOUND);
1357 return 0;
1360 TRACE("%s,%p,%ld\n", debugstr_w(shortpath), longpath, longlen);
1362 if(shortpath[0]=='\\' && shortpath[1]=='\\')
1364 ERR("UNC pathname %s\n",debugstr_w(shortpath));
1365 lstrcpynW( longpath, full_name.short_name, longlen );
1366 return strlenW(longpath);
1369 if (!DOSFS_GetFullName( shortpath, TRUE, &full_name )) return 0;
1371 root = full_name.long_name;
1372 drive = DRIVE_FindDriveRoot(&root);
1373 codepage = DRIVE_GetCodepage(drive);
1375 ret = MultiByteToWideChar(codepage, 0, root, -1, NULL, 0);
1376 ret += 3; /* A:\ */
1377 /* reproduce terminating slash */
1378 if (ret > 4) /* if not drive root */
1380 len = strlenW(shortpath);
1381 if (shortpath[len - 1] == '\\' || shortpath[len - 1] == '/')
1382 len = 1;
1384 ret += len;
1385 if (ret <= longlen)
1387 longpath[0] = 'A' + drive;
1388 longpath[1] = ':';
1389 MultiByteToWideChar(codepage, 0, root, -1, longpath + 2, longlen - 2);
1390 for (p = longpath; *p; p++) if (*p == '/') *p = '\\';
1391 if (len)
1393 longpath[ret - 2] = '\\';
1394 longpath[ret - 1] = 0;
1396 TRACE("returning %s\n", debugstr_w(longpath));
1397 ret--; /* length without 0 */
1399 return ret;
1403 /***********************************************************************
1404 * GetLongPathNameA (KERNEL32.@)
1406 DWORD WINAPI GetLongPathNameA( LPCSTR shortpath, LPSTR longpath, DWORD longlen )
1408 UNICODE_STRING shortpathW;
1409 WCHAR longpathW[MAX_PATH];
1410 DWORD ret, retW;
1412 if (!shortpath)
1414 SetLastError(ERROR_INVALID_PARAMETER);
1415 return 0;
1418 TRACE("%s\n", debugstr_a(shortpath));
1420 if (!RtlCreateUnicodeStringFromAsciiz(&shortpathW, shortpath))
1422 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1423 return 0;
1426 retW = GetLongPathNameW(shortpathW.Buffer, longpathW, MAX_PATH);
1428 if (!retW)
1429 ret = 0;
1430 else if (retW > MAX_PATH)
1432 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1433 ret = 0;
1435 else
1437 ret = WideCharToMultiByte(CP_ACP, 0, longpathW, -1, NULL, 0, NULL, NULL);
1438 if (ret <= longlen)
1440 WideCharToMultiByte(CP_ACP, 0, longpathW, -1, longpath, longlen, NULL, NULL);
1441 ret--; /* length without 0 */
1445 RtlFreeUnicodeString(&shortpathW);
1446 return ret;
1450 /***********************************************************************
1451 * DOSFS_DoGetFullPathName
1453 * Implementation of GetFullPathNameA/W.
1455 * bon@elektron 000331:
1456 * A test for GetFullPathName with many pathological cases
1457 * now gives identical output for Wine and OSR2
1459 static DWORD DOSFS_DoGetFullPathName( LPCWSTR name, DWORD len, LPWSTR result )
1461 DWORD ret;
1462 DOS_FULL_NAME full_name;
1463 LPWSTR p, q;
1464 char *p_l;
1465 const char * root;
1466 WCHAR drivecur[] = {'C',':','.',0};
1467 WCHAR driveletter=0;
1468 int namelen,drive=0;
1469 static const WCHAR bkslashW[] = {'\\',0};
1470 static const WCHAR dotW[] = {'.',0};
1471 static const WCHAR updir_slashW[] = {'\\','.','.','\\',0};
1472 static const WCHAR curdirW[] = {'\\','.','\\',0};
1473 static const WCHAR updirW[] = {'\\','.','.',0};
1475 if (!name[0])
1477 SetLastError(ERROR_BAD_PATHNAME);
1478 return 0;
1481 TRACE("passed %s\n", debugstr_w(name));
1483 if (name[1]==':')
1484 /*drive letter given */
1486 driveletter = name[0];
1488 if ((name[1]==':') && ((name[2]=='\\') || (name[2]=='/')))
1489 /*absolute path given */
1491 strncpyW(full_name.short_name, name, MAX_PATHNAME_LEN);
1492 full_name.short_name[MAX_PATHNAME_LEN - 1] = 0; /* ensure 0 termination */
1493 drive = toupperW(name[0]) - 'A';
1495 else
1497 if (driveletter)
1498 drivecur[0]=driveletter;
1499 else if ((name[0]=='\\') || (name[0]=='/'))
1500 strcpyW(drivecur, bkslashW);
1501 else
1502 strcpyW(drivecur, dotW);
1504 if (!DOSFS_GetFullName( drivecur, FALSE, &full_name ))
1506 FIXME("internal: error getting drive/path\n");
1507 return 0;
1509 /* find path that drive letter substitutes*/
1510 drive = toupperW(full_name.short_name[0]) - 'A';
1511 root= DRIVE_GetRoot(drive);
1512 if (!root)
1514 FIXME("internal: error getting DOS Drive Root\n");
1515 return 0;
1517 if (!strcmp(root,"/"))
1519 /* we have just the last / and we need it. */
1520 p_l = full_name.long_name;
1522 else
1524 p_l = full_name.long_name + strlen(root);
1526 /* append long name (= unix name) to drive */
1527 MultiByteToWideChar(DRIVE_GetCodepage(drive), 0, p_l, -1,
1528 full_name.short_name + 2, MAX_PATHNAME_LEN - 3);
1529 /* append name to treat */
1530 namelen= strlenW(full_name.short_name);
1531 p = (LPWSTR)name;
1532 if (driveletter)
1533 p += 2; /* skip drive name when appending */
1534 if (namelen + 2 + strlenW(p) > MAX_PATHNAME_LEN)
1536 FIXME("internal error: buffer too small\n");
1537 return 0;
1539 full_name.short_name[namelen++] ='\\';
1540 full_name.short_name[namelen] = 0;
1541 strncpyW(full_name.short_name + namelen, p, MAX_PATHNAME_LEN - namelen);
1542 full_name.short_name[MAX_PATHNAME_LEN - 1] = 0; /* ensure 0 termination */
1544 /* reverse all slashes */
1545 for (p=full_name.short_name;
1546 p < full_name.short_name + strlenW(full_name.short_name);
1547 p++)
1549 if ( *p == '/' )
1550 *p = '\\';
1552 /* Use memmove, as areas overlap */
1553 /* Delete .. */
1554 while ((p = strstrW(full_name.short_name, updir_slashW)))
1556 if (p > full_name.short_name+2)
1558 *p = 0;
1559 q = strrchrW(full_name.short_name, '\\');
1560 memmove(q+1, p+4, (strlenW(p+4)+1) * sizeof(WCHAR));
1562 else
1564 memmove(full_name.short_name+3, p+4, (strlenW(p+4)+1) * sizeof(WCHAR));
1567 if ((full_name.short_name[2]=='.')&&(full_name.short_name[3]=='.'))
1569 /* This case istn't treated yet : c:..\test */
1570 memmove(full_name.short_name+2,full_name.short_name+4,
1571 (strlenW(full_name.short_name+4)+1) * sizeof(WCHAR));
1573 /* Delete . */
1574 while ((p = strstrW(full_name.short_name, curdirW)))
1576 *(p+1) = 0;
1577 memmove(p+1, p+3, (strlenW(p+3)+1) * sizeof(WCHAR));
1579 if (!(DRIVE_GetFlags(drive) & DRIVE_CASE_PRESERVING))
1580 for (p = full_name.short_name; *p; p++) *p = toupperW(*p);
1581 namelen = strlenW(full_name.short_name);
1582 if (!strcmpW(full_name.short_name+namelen-3, updirW))
1584 /* one more strange case: "c:\test\test1\.."
1585 return "c:\test" */
1586 *(full_name.short_name+namelen-3)=0;
1587 q = strrchrW(full_name.short_name, '\\');
1588 *q =0;
1590 if (full_name.short_name[namelen-1]=='.')
1591 full_name.short_name[(namelen--)-1] =0;
1592 if (!driveletter)
1593 if (full_name.short_name[namelen-1]=='\\')
1594 full_name.short_name[(namelen--)-1] =0;
1595 TRACE("got %s\n", debugstr_w(full_name.short_name));
1597 /* If the lpBuffer buffer is too small, the return value is the
1598 size of the buffer, in characters, required to hold the path
1599 plus the terminating \0 (tested against win95osr2, bon 001118)
1600 . */
1601 ret = strlenW(full_name.short_name);
1602 if (ret >= len )
1604 /* don't touch anything when the buffer is not large enough */
1605 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1606 return ret+1;
1608 if (result)
1610 strncpyW( result, full_name.short_name, len );
1611 result[len - 1] = 0; /* ensure 0 termination */
1614 TRACE("returning %s\n", debugstr_w(full_name.short_name) );
1615 return ret;
1619 /***********************************************************************
1620 * GetFullPathNameA (KERNEL32.@)
1621 * NOTES
1622 * if the path closed with '\', *lastpart is 0
1624 DWORD WINAPI GetFullPathNameA( LPCSTR name, DWORD len, LPSTR buffer,
1625 LPSTR *lastpart )
1627 UNICODE_STRING nameW;
1628 WCHAR bufferW[MAX_PATH];
1629 DWORD ret, retW;
1631 if (!name)
1633 SetLastError(ERROR_INVALID_PARAMETER);
1634 return 0;
1637 if (!RtlCreateUnicodeStringFromAsciiz(&nameW, name))
1639 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1640 return 0;
1643 retW = GetFullPathNameW( nameW.Buffer, MAX_PATH, bufferW, NULL);
1645 if (!retW)
1646 ret = 0;
1647 else if (retW > MAX_PATH)
1649 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1650 ret = 0;
1652 else
1654 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL);
1655 if (ret <= len)
1657 WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, len, NULL, NULL);
1658 ret--; /* length without 0 */
1660 if (lastpart)
1662 LPSTR p = buffer + strlen(buffer);
1664 if (*p != '\\')
1666 while ((p > buffer + 2) && (*p != '\\')) p--;
1667 *lastpart = p + 1;
1669 else *lastpart = NULL;
1674 RtlFreeUnicodeString(&nameW);
1675 return ret;
1679 /***********************************************************************
1680 * GetFullPathNameW (KERNEL32.@)
1682 DWORD WINAPI GetFullPathNameW( LPCWSTR name, DWORD len, LPWSTR buffer,
1683 LPWSTR *lastpart )
1685 DWORD ret = DOSFS_DoGetFullPathName( name, len, buffer );
1686 if (ret && (ret<=len) && buffer && lastpart)
1688 LPWSTR p = buffer + strlenW(buffer);
1689 if (*p != (WCHAR)'\\')
1691 while ((p > buffer + 2) && (*p != (WCHAR)'\\')) p--;
1692 *lastpart = p + 1;
1694 else *lastpart = NULL;
1696 return ret;
1700 /***********************************************************************
1701 * wine_get_unix_file_name (KERNEL32.@) Not a Windows API
1703 * Return the full Unix file name for a given path.
1704 * FIXME: convert dos file name to unicode
1706 BOOL WINAPI wine_get_unix_file_name( LPCSTR dos, LPSTR buffer, DWORD len )
1708 BOOL ret;
1709 DOS_FULL_NAME path;
1710 WCHAR dosW[MAX_PATHNAME_LEN];
1712 MultiByteToWideChar(CP_ACP, 0, dos, -1, dosW, MAX_PATHNAME_LEN);
1713 ret = DOSFS_GetFullName( dosW, FALSE, &path );
1714 if (ret && len)
1716 strncpy( buffer, path.long_name, len );
1717 buffer[len - 1] = 0; /* ensure 0 termination */
1719 return ret;
1723 /***********************************************************************
1724 * get_show_dir_symlinks_option
1726 static BOOL get_show_dir_symlinks_option(void)
1728 static const WCHAR WineW[] = {'M','a','c','h','i','n','e','\\',
1729 'S','o','f','t','w','a','r','e','\\',
1730 'W','i','n','e','\\','W','i','n','e','\\',
1731 'C','o','n','f','i','g','\\','W','i','n','e',0};
1732 static const WCHAR ShowDirSymlinksW[] = {'S','h','o','w','D','i','r','S','y','m','l','i','n','k','s',0};
1734 char tmp[80];
1735 HKEY hkey;
1736 DWORD dummy;
1737 OBJECT_ATTRIBUTES attr;
1738 UNICODE_STRING nameW;
1739 BOOL ret = FALSE;
1741 attr.Length = sizeof(attr);
1742 attr.RootDirectory = 0;
1743 attr.ObjectName = &nameW;
1744 attr.Attributes = 0;
1745 attr.SecurityDescriptor = NULL;
1746 attr.SecurityQualityOfService = NULL;
1747 RtlInitUnicodeString( &nameW, WineW );
1749 if (!NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr ))
1751 RtlInitUnicodeString( &nameW, ShowDirSymlinksW );
1752 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
1754 WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
1755 ret = IS_OPTION_TRUE( str[0] );
1757 NtClose( hkey );
1759 return ret;
1763 /***********************************************************************
1764 * DOSFS_FindNextEx
1766 static int DOSFS_FindNextEx( FIND_FIRST_INFO *info, WIN32_FIND_DATAW *entry )
1768 DWORD attr = info->attr | FA_UNUSED | FA_ARCHIVE | FA_RDONLY;
1769 UINT flags = DRIVE_GetFlags( info->drive );
1770 char *p, buffer[MAX_PATHNAME_LEN];
1771 const char *drive_path;
1772 int drive_root;
1773 LPCWSTR long_name, short_name;
1774 BY_HANDLE_FILE_INFORMATION fileinfo;
1775 WCHAR dos_name[13];
1776 BOOL is_symlink;
1778 if ((info->attr & ~(FA_UNUSED | FA_ARCHIVE | FA_RDONLY)) == FA_LABEL)
1780 if (info->cur_pos) return 0;
1781 entry->dwFileAttributes = FILE_ATTRIBUTE_LABEL;
1782 RtlSecondsSince1970ToTime( (time_t)0, (LARGE_INTEGER *)&entry->ftCreationTime );
1783 RtlSecondsSince1970ToTime( (time_t)0, (LARGE_INTEGER *)&entry->ftLastAccessTime );
1784 RtlSecondsSince1970ToTime( (time_t)0, (LARGE_INTEGER *)&entry->ftLastWriteTime );
1785 entry->nFileSizeHigh = 0;
1786 entry->nFileSizeLow = 0;
1787 entry->dwReserved0 = 0;
1788 entry->dwReserved1 = 0;
1789 DOSFS_ToDosDTAFormat( DRIVE_GetLabel( info->drive ), entry->cFileName );
1790 strcpyW( entry->cAlternateFileName, entry->cFileName );
1791 info->cur_pos++;
1792 TRACE("returning %s (%s) as label\n",
1793 debugstr_w(entry->cFileName), debugstr_w(entry->cAlternateFileName));
1794 return 1;
1797 drive_path = info->path + strlen(DRIVE_GetRoot( info->drive ));
1798 while ((*drive_path == '/') || (*drive_path == '\\')) drive_path++;
1799 drive_root = !*drive_path;
1801 lstrcpynA( buffer, info->path, sizeof(buffer) - 1 );
1802 strcat( buffer, "/" );
1803 p = buffer + strlen(buffer);
1805 while (DOSFS_ReadDir( info->u.dos_dir, &long_name, &short_name ))
1807 info->cur_pos++;
1809 /* Don't return '.' and '..' in the root of the drive */
1810 if (drive_root && (long_name[0] == '.') &&
1811 (!long_name[1] || ((long_name[1] == '.') && !long_name[2])))
1812 continue;
1814 /* Check the long mask */
1816 if (info->long_mask && *info->long_mask)
1818 if (!DOSFS_MatchLong( info->long_mask, long_name,
1819 flags & DRIVE_CASE_SENSITIVE )) continue;
1822 /* Check the short mask */
1824 if (info->short_mask)
1826 if (!short_name)
1828 DOSFS_Hash( long_name, dos_name, TRUE,
1829 !(flags & DRIVE_CASE_SENSITIVE) );
1830 short_name = dos_name;
1832 if (!DOSFS_MatchShort( info->short_mask, short_name )) continue;
1835 /* Check the file attributes */
1836 WideCharToMultiByte(DRIVE_GetCodepage(info->drive), 0, long_name, -1,
1837 p, sizeof(buffer) - (int)(p - buffer), NULL, NULL);
1838 if (!FILE_Stat( buffer, &fileinfo, &is_symlink ))
1840 WARN("can't stat %s\n", buffer);
1841 continue;
1843 if (is_symlink && (fileinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
1845 static int show_dir_symlinks = -1;
1846 if (show_dir_symlinks == -1)
1847 show_dir_symlinks = get_show_dir_symlinks_option();
1848 if (!show_dir_symlinks) continue;
1851 if (fileinfo.dwFileAttributes & ~attr) continue;
1853 /* We now have a matching entry; fill the result and return */
1855 entry->dwFileAttributes = fileinfo.dwFileAttributes;
1856 entry->ftCreationTime = fileinfo.ftCreationTime;
1857 entry->ftLastAccessTime = fileinfo.ftLastAccessTime;
1858 entry->ftLastWriteTime = fileinfo.ftLastWriteTime;
1859 entry->nFileSizeHigh = fileinfo.nFileSizeHigh;
1860 entry->nFileSizeLow = fileinfo.nFileSizeLow;
1862 if (short_name)
1863 DOSFS_ToDosDTAFormat( short_name, entry->cAlternateFileName );
1864 else
1865 DOSFS_Hash( long_name, entry->cAlternateFileName, FALSE,
1866 !(flags & DRIVE_CASE_SENSITIVE) );
1868 lstrcpynW( entry->cFileName, long_name, sizeof(entry->cFileName)/sizeof(entry->cFileName[0]) );
1869 if (!(flags & DRIVE_CASE_PRESERVING)) strlwrW( entry->cFileName );
1870 TRACE("returning %s (%s) %02lx %ld\n",
1871 debugstr_w(entry->cFileName), debugstr_w(entry->cAlternateFileName),
1872 entry->dwFileAttributes, entry->nFileSizeLow );
1873 return 1;
1875 return 0; /* End of directory */
1878 /***********************************************************************
1879 * DOSFS_FindNext
1881 * Find the next matching file. Return the number of entries read to find
1882 * the matching one, or 0 if no more entries.
1883 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
1884 * file name mask. Either or both can be NULL.
1886 * NOTE: This is supposed to be only called by the int21 emulation
1887 * routines. Thus, we should own the Win16Mutex anyway.
1888 * Nevertheless, we explicitly enter it to ensure the static
1889 * directory cache is protected.
1891 int DOSFS_FindNext( const char *path, const char *short_mask,
1892 const char *long_mask, int drive, BYTE attr,
1893 int skip, WIN32_FIND_DATAA *entry )
1895 static FIND_FIRST_INFO info;
1896 LPCWSTR short_name, long_name;
1897 int count;
1898 UNICODE_STRING short_maskW, long_maskW;
1899 WIN32_FIND_DATAW entryW;
1901 TRACE("(%s, %s, %s, %x, %x, %x, %p)\n", debugstr_a(path),
1902 debugstr_a(short_mask), debugstr_a(long_mask), drive, attr, skip,
1903 entry);
1905 _EnterWin16Lock();
1907 RtlCreateUnicodeStringFromAsciiz(&short_maskW, short_mask);
1908 RtlCreateUnicodeStringFromAsciiz(&long_maskW, long_mask);
1910 /* Check the cached directory */
1911 if (!(info.u.dos_dir && info.path == path && !strcmpW(info.short_mask, short_maskW.Buffer)
1912 && !strcmpW(info.long_mask, long_maskW.Buffer) && info.drive == drive
1913 && info.attr == attr && info.cur_pos <= skip))
1915 /* Not in the cache, open it anew */
1916 if (info.u.dos_dir) DOSFS_CloseDir( info.u.dos_dir );
1918 info.path = (LPSTR)path;
1919 RtlFreeHeap(GetProcessHeap(), 0, info.long_mask);
1920 RtlFreeHeap(GetProcessHeap(), 0, info.short_mask);
1921 info.long_mask = long_maskW.Buffer;
1922 info.short_mask = short_maskW.Buffer;
1923 info.attr = attr;
1924 info.drive = drive;
1925 info.cur_pos = 0;
1926 info.u.dos_dir = DOSFS_OpenDir( DRIVE_GetCodepage(drive), info.path );
1928 else
1930 RtlFreeUnicodeString(&short_maskW);
1931 RtlFreeUnicodeString(&long_maskW);
1934 /* Skip to desired position */
1935 while (info.cur_pos < skip)
1936 if (info.u.dos_dir && DOSFS_ReadDir( info.u.dos_dir, &long_name, &short_name ))
1937 info.cur_pos++;
1938 else
1939 break;
1941 if (info.u.dos_dir && info.cur_pos == skip && DOSFS_FindNextEx( &info, &entryW ))
1943 WideCharToMultiByte(CP_ACP, 0, entryW.cFileName, -1,
1944 entry->cFileName, sizeof(entry->cFileName), NULL, NULL);
1945 WideCharToMultiByte(CP_ACP, 0, entryW.cAlternateFileName, -1,
1946 entry->cAlternateFileName, sizeof(entry->cAlternateFileName), NULL, NULL);
1947 count = info.cur_pos - skip;
1949 entry->dwFileAttributes = entryW.dwFileAttributes;
1950 entry->nFileSizeHigh = entryW.nFileSizeHigh;
1951 entry->nFileSizeLow = entryW.nFileSizeLow;
1952 entry->ftCreationTime = entryW.ftCreationTime;
1953 entry->ftLastAccessTime = entryW.ftLastAccessTime;
1954 entry->ftLastWriteTime = entryW.ftLastWriteTime;
1957 else
1958 count = 0;
1960 if (!count)
1962 if (info.u.dos_dir) DOSFS_CloseDir( info.u.dos_dir );
1963 memset( &info, '\0', sizeof(info) );
1966 _LeaveWin16Lock();
1968 return count;
1971 /*************************************************************************
1972 * FindFirstFileExW (KERNEL32.@)
1974 HANDLE WINAPI FindFirstFileExW(
1975 LPCWSTR lpFileName,
1976 FINDEX_INFO_LEVELS fInfoLevelId,
1977 LPVOID lpFindFileData,
1978 FINDEX_SEARCH_OPS fSearchOp,
1979 LPVOID lpSearchFilter,
1980 DWORD dwAdditionalFlags)
1982 HGLOBAL handle;
1983 FIND_FIRST_INFO *info;
1985 if (!lpFileName)
1987 SetLastError(ERROR_PATH_NOT_FOUND);
1988 return INVALID_HANDLE_VALUE;
1991 if ((fSearchOp != FindExSearchNameMatch) || (dwAdditionalFlags != 0))
1993 FIXME("options not implemented 0x%08x 0x%08lx\n", fSearchOp, dwAdditionalFlags );
1994 return INVALID_HANDLE_VALUE;
1997 switch(fInfoLevelId)
1999 case FindExInfoStandard:
2001 WIN32_FIND_DATAW * data = (WIN32_FIND_DATAW *) lpFindFileData;
2002 char *p;
2003 INT long_mask_len;
2004 UINT codepage;
2006 data->dwReserved0 = data->dwReserved1 = 0x0;
2007 if (lpFileName[0] == '\\' && lpFileName[1] == '\\')
2009 ERR("UNC path name\n");
2010 if (!(handle = GlobalAlloc(GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO)))) break;
2012 info = (FIND_FIRST_INFO *)GlobalLock( handle );
2013 info->u.smb_dir = SMB_FindFirst(lpFileName);
2014 if(!info->u.smb_dir)
2016 GlobalUnlock( handle );
2017 GlobalFree(handle);
2018 break;
2021 info->drive = -1;
2023 GlobalUnlock( handle );
2025 else
2027 DOS_FULL_NAME full_name;
2029 if (lpFileName[0] && lpFileName[1] == ':')
2031 /* don't allow root directories */
2032 if (!lpFileName[2] ||
2033 ((lpFileName[2] == '/' || lpFileName[2] == '\\') && !lpFileName[3]))
2035 SetLastError(ERROR_FILE_NOT_FOUND);
2036 return INVALID_HANDLE_VALUE;
2039 if (!DOSFS_GetFullName( lpFileName, FALSE, &full_name )) break;
2040 if (!(handle = GlobalAlloc(GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO)))) break;
2041 info = (FIND_FIRST_INFO *)GlobalLock( handle );
2042 info->path = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
2043 strcpy( info->path, full_name.long_name );
2045 codepage = DRIVE_GetCodepage(full_name.drive);
2046 p = strrchr( info->path, '/' );
2047 *p++ = '\0';
2048 long_mask_len = MultiByteToWideChar(codepage, 0, p, -1, NULL, 0);
2049 info->long_mask = HeapAlloc( GetProcessHeap(), 0, long_mask_len * sizeof(WCHAR) );
2050 MultiByteToWideChar(codepage, 0, p, -1, info->long_mask, long_mask_len);
2052 info->short_mask = NULL;
2053 info->attr = 0xff;
2054 info->drive = full_name.drive;
2055 info->cur_pos = 0;
2057 info->u.dos_dir = DOSFS_OpenDir( codepage, info->path );
2058 GlobalUnlock( handle );
2060 if (!FindNextFileW( handle, data ))
2062 FindClose( handle );
2063 SetLastError( ERROR_FILE_NOT_FOUND );
2064 break;
2066 return handle;
2068 break;
2069 default:
2070 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
2072 return INVALID_HANDLE_VALUE;
2075 /*************************************************************************
2076 * FindFirstFileA (KERNEL32.@)
2078 HANDLE WINAPI FindFirstFileA(
2079 LPCSTR lpFileName,
2080 WIN32_FIND_DATAA *lpFindData )
2082 return FindFirstFileExA(lpFileName, FindExInfoStandard, lpFindData,
2083 FindExSearchNameMatch, NULL, 0);
2086 /*************************************************************************
2087 * FindFirstFileExA (KERNEL32.@)
2089 HANDLE WINAPI FindFirstFileExA(
2090 LPCSTR lpFileName,
2091 FINDEX_INFO_LEVELS fInfoLevelId,
2092 LPVOID lpFindFileData,
2093 FINDEX_SEARCH_OPS fSearchOp,
2094 LPVOID lpSearchFilter,
2095 DWORD dwAdditionalFlags)
2097 HANDLE handle;
2098 WIN32_FIND_DATAA *dataA;
2099 WIN32_FIND_DATAW dataW;
2100 UNICODE_STRING pathW;
2102 if (!lpFileName)
2104 SetLastError(ERROR_PATH_NOT_FOUND);
2105 return INVALID_HANDLE_VALUE;
2108 if (!RtlCreateUnicodeStringFromAsciiz(&pathW, lpFileName))
2110 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2111 return INVALID_HANDLE_VALUE;
2114 handle = FindFirstFileExW(pathW.Buffer, fInfoLevelId, &dataW, fSearchOp, lpSearchFilter, dwAdditionalFlags);
2115 RtlFreeUnicodeString(&pathW);
2116 if (handle == INVALID_HANDLE_VALUE) return handle;
2118 dataA = (WIN32_FIND_DATAA *) lpFindFileData;
2119 dataA->dwFileAttributes = dataW.dwFileAttributes;
2120 dataA->ftCreationTime = dataW.ftCreationTime;
2121 dataA->ftLastAccessTime = dataW.ftLastAccessTime;
2122 dataA->ftLastWriteTime = dataW.ftLastWriteTime;
2123 dataA->nFileSizeHigh = dataW.nFileSizeHigh;
2124 dataA->nFileSizeLow = dataW.nFileSizeLow;
2125 WideCharToMultiByte( CP_ACP, 0, dataW.cFileName, -1,
2126 dataA->cFileName, sizeof(dataA->cFileName), NULL, NULL );
2127 WideCharToMultiByte( CP_ACP, 0, dataW.cAlternateFileName, -1,
2128 dataA->cAlternateFileName, sizeof(dataA->cAlternateFileName), NULL, NULL );
2129 return handle;
2132 /*************************************************************************
2133 * FindFirstFileW (KERNEL32.@)
2135 HANDLE WINAPI FindFirstFileW( LPCWSTR lpFileName, WIN32_FIND_DATAW *lpFindData )
2137 return FindFirstFileExW(lpFileName, FindExInfoStandard, lpFindData,
2138 FindExSearchNameMatch, NULL, 0);
2141 /*************************************************************************
2142 * FindNextFileW (KERNEL32.@)
2144 BOOL WINAPI FindNextFileW( HANDLE handle, WIN32_FIND_DATAW *data )
2146 FIND_FIRST_INFO *info;
2147 BOOL ret = FALSE;
2148 DWORD gle = ERROR_NO_MORE_FILES;
2150 if ((handle == INVALID_HANDLE_VALUE) ||
2151 !(info = (FIND_FIRST_INFO *)GlobalLock( handle )))
2153 SetLastError( ERROR_INVALID_HANDLE );
2154 return ret;
2156 if (info->drive == -1)
2158 ret = SMB_FindNext( info->u.smb_dir, data );
2159 if(!ret)
2161 SMB_CloseDir( info->u.smb_dir );
2162 HeapFree( GetProcessHeap(), 0, info->path );
2164 goto done;
2166 else if (!info->path || !info->u.dos_dir)
2168 goto done;
2170 else if (!DOSFS_FindNextEx( info, data ))
2172 DOSFS_CloseDir( info->u.dos_dir ); info->u.dos_dir = NULL;
2173 HeapFree( GetProcessHeap(), 0, info->path );
2174 info->path = NULL;
2175 HeapFree( GetProcessHeap(), 0, info->long_mask );
2176 info->long_mask = NULL;
2177 goto done;
2179 ret = TRUE;
2180 done:
2181 GlobalUnlock( handle );
2182 if( !ret ) SetLastError( gle );
2183 return ret;
2187 /*************************************************************************
2188 * FindNextFileA (KERNEL32.@)
2190 BOOL WINAPI FindNextFileA( HANDLE handle, WIN32_FIND_DATAA *data )
2192 WIN32_FIND_DATAW dataW;
2193 if (!FindNextFileW( handle, &dataW )) return FALSE;
2194 data->dwFileAttributes = dataW.dwFileAttributes;
2195 data->ftCreationTime = dataW.ftCreationTime;
2196 data->ftLastAccessTime = dataW.ftLastAccessTime;
2197 data->ftLastWriteTime = dataW.ftLastWriteTime;
2198 data->nFileSizeHigh = dataW.nFileSizeHigh;
2199 data->nFileSizeLow = dataW.nFileSizeLow;
2200 WideCharToMultiByte( CP_ACP, 0, dataW.cFileName, -1,
2201 data->cFileName, sizeof(data->cFileName), NULL, NULL );
2202 WideCharToMultiByte( CP_ACP, 0, dataW.cAlternateFileName, -1,
2203 data->cAlternateFileName,
2204 sizeof(data->cAlternateFileName), NULL, NULL );
2205 return TRUE;
2208 /*************************************************************************
2209 * FindClose (KERNEL32.@)
2211 BOOL WINAPI FindClose( HANDLE handle )
2213 FIND_FIRST_INFO *info;
2215 if (handle == INVALID_HANDLE_VALUE) goto error;
2217 __TRY
2219 if ((info = (FIND_FIRST_INFO *)GlobalLock( handle )))
2221 if (info->u.dos_dir) DOSFS_CloseDir( info->u.dos_dir );
2222 if (info->path) HeapFree( GetProcessHeap(), 0, info->path );
2223 if (info->long_mask) HeapFree( GetProcessHeap(), 0, info->long_mask );
2226 __EXCEPT(page_fault)
2228 WARN("Illegal handle %p\n", handle);
2229 SetLastError( ERROR_INVALID_HANDLE );
2230 return FALSE;
2232 __ENDTRY
2233 if (!info) goto error;
2234 GlobalUnlock( handle );
2235 GlobalFree( handle );
2236 return TRUE;
2238 error:
2239 SetLastError( ERROR_INVALID_HANDLE );
2240 return FALSE;
2243 /***********************************************************************
2244 * MulDiv (KERNEL32.@)
2245 * RETURNS
2246 * Result of multiplication and division
2247 * -1: Overflow occurred or Divisor was 0
2249 INT WINAPI MulDiv(
2250 INT nMultiplicand,
2251 INT nMultiplier,
2252 INT nDivisor)
2254 #if SIZEOF_LONG_LONG >= 8
2255 long long ret;
2257 if (!nDivisor) return -1;
2259 /* We want to deal with a positive divisor to simplify the logic. */
2260 if (nDivisor < 0)
2262 nMultiplicand = - nMultiplicand;
2263 nDivisor = -nDivisor;
2266 /* If the result is positive, we "add" to round. else, we subtract to round. */
2267 if ( ( (nMultiplicand < 0) && (nMultiplier < 0) ) ||
2268 ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
2269 ret = (((long long)nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
2270 else
2271 ret = (((long long)nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
2273 if ((ret > 2147483647) || (ret < -2147483647)) return -1;
2274 return ret;
2275 #else
2276 if (!nDivisor) return -1;
2278 /* We want to deal with a positive divisor to simplify the logic. */
2279 if (nDivisor < 0)
2281 nMultiplicand = - nMultiplicand;
2282 nDivisor = -nDivisor;
2285 /* If the result is positive, we "add" to round. else, we subtract to round. */
2286 if ( ( (nMultiplicand < 0) && (nMultiplier < 0) ) ||
2287 ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
2288 return ((nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
2290 return ((nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
2292 #endif
2296 /***********************************************************************
2297 * DosDateTimeToFileTime (KERNEL32.@)
2299 BOOL WINAPI DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft)
2301 struct tm newtm;
2302 #ifndef HAVE_TIMEGM
2303 struct tm *gtm;
2304 time_t time1, time2;
2305 #endif
2307 newtm.tm_sec = (fattime & 0x1f) * 2;
2308 newtm.tm_min = (fattime >> 5) & 0x3f;
2309 newtm.tm_hour = (fattime >> 11);
2310 newtm.tm_mday = (fatdate & 0x1f);
2311 newtm.tm_mon = ((fatdate >> 5) & 0x0f) - 1;
2312 newtm.tm_year = (fatdate >> 9) + 80;
2313 #ifdef HAVE_TIMEGM
2314 RtlSecondsSince1970ToTime( timegm(&newtm), (LARGE_INTEGER *)ft );
2315 #else
2316 time1 = mktime(&newtm);
2317 gtm = gmtime(&time1);
2318 time2 = mktime(gtm);
2319 RtlSecondsSince1970ToTime( 2*time1-time2, (LARGE_INTEGER *)ft );
2320 #endif
2321 return TRUE;
2325 /***********************************************************************
2326 * FileTimeToDosDateTime (KERNEL32.@)
2328 BOOL WINAPI FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate,
2329 LPWORD fattime )
2331 LARGE_INTEGER li;
2332 ULONG t;
2333 time_t unixtime;
2334 struct tm* tm;
2336 li.s.LowPart = ft->dwLowDateTime;
2337 li.s.HighPart = ft->dwHighDateTime;
2338 RtlTimeToSecondsSince1970( &li, &t );
2339 unixtime = t;
2340 tm = gmtime( &unixtime );
2341 if (fattime)
2342 *fattime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
2343 if (fatdate)
2344 *fatdate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
2345 + tm->tm_mday;
2346 return TRUE;
2350 /***********************************************************************
2351 * QueryDosDeviceA (KERNEL32.@)
2353 * returns array of strings terminated by \0, terminated by \0
2355 DWORD WINAPI QueryDosDeviceA(LPCSTR devname,LPSTR target,DWORD bufsize)
2357 LPSTR s;
2358 char buffer[200];
2360 TRACE("(%s,...)\n", devname ? devname : "<null>");
2361 if (!devname) {
2362 /* return known MSDOS devices */
2363 static const char devices[24] = "CON\0COM1\0COM2\0LPT1\0NUL\0\0";
2364 memcpy( target, devices, min(bufsize,sizeof(devices)) );
2365 return min(bufsize,sizeof(devices));
2367 /* In theory all that are possible and have been defined.
2368 * Now just those below, since mirc uses it to check for special files.
2370 * (It is more complex, and supports netmounted stuff, and \\.\ stuff,
2371 * but currently we just ignore that.)
2373 #define CHECK(x) (strstr(devname,#x)==devname)
2374 if (CHECK(con) || CHECK(com) || CHECK(lpt) || CHECK(nul)) {
2375 strcpy(buffer,"\\DEV\\");
2376 strcat(buffer,devname);
2377 if ((s=strchr(buffer,':'))) *s='\0';
2378 lstrcpynA(target,buffer,bufsize);
2379 return strlen(buffer)+1;
2380 } else {
2381 if (strchr(devname,':') || devname[0]=='\\') {
2382 /* This might be a DOS device we do not handle yet ... */
2383 FIXME("(%s) not detected as DOS device!\n",devname);
2385 SetLastError(ERROR_DEV_NOT_EXIST);
2386 return 0;
2392 /***********************************************************************
2393 * QueryDosDeviceW (KERNEL32.@)
2395 * returns array of strings terminated by \0, terminated by \0
2397 DWORD WINAPI QueryDosDeviceW(LPCWSTR devname,LPWSTR target,DWORD bufsize)
2399 LPSTR devnameA = devname?HEAP_strdupWtoA(GetProcessHeap(),0,devname):NULL;
2400 LPSTR targetA = (LPSTR)HeapAlloc(GetProcessHeap(),0,bufsize);
2401 DWORD ret = QueryDosDeviceA(devnameA,targetA,bufsize);
2403 ret = MultiByteToWideChar( CP_ACP, 0, targetA, ret, target, bufsize );
2404 if (devnameA) HeapFree(GetProcessHeap(),0,devnameA);
2405 if (targetA) HeapFree(GetProcessHeap(),0,targetA);
2406 return ret;
2410 /***********************************************************************
2411 * DefineDosDeviceA (KERNEL32.@)
2413 BOOL WINAPI DefineDosDeviceA(DWORD flags,LPCSTR devname,LPCSTR targetpath) {
2414 FIXME("(0x%08lx,%s,%s),stub!\n",flags,devname,targetpath);
2415 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2416 return FALSE;
2420 --- 16 bit functions ---
2423 /*************************************************************************
2424 * FindFirstFile (KERNEL.413)
2426 HANDLE16 WINAPI FindFirstFile16( LPCSTR path, WIN32_FIND_DATAA *data )
2428 DOS_FULL_NAME full_name;
2429 HGLOBAL16 handle;
2430 FIND_FIRST_INFO *info;
2431 WCHAR pathW[MAX_PATH];
2432 char *p;
2433 INT long_mask_len;
2434 UINT codepage;
2436 data->dwReserved0 = data->dwReserved1 = 0x0;
2437 if (!path) return INVALID_HANDLE_VALUE16;
2438 MultiByteToWideChar(CP_ACP, 0, path, -1, pathW, MAX_PATH);
2439 if (!DOSFS_GetFullName( pathW, FALSE, &full_name ))
2440 return INVALID_HANDLE_VALUE16;
2441 if (!(handle = GlobalAlloc16( GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO) )))
2442 return INVALID_HANDLE_VALUE16;
2443 info = (FIND_FIRST_INFO *)GlobalLock16( handle );
2444 info->path = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
2445 strcpy( info->path, full_name.long_name );
2447 codepage = DRIVE_GetCodepage(full_name.drive);
2448 p = strrchr( info->path, '/' );
2449 *p++ = '\0';
2450 long_mask_len = MultiByteToWideChar(codepage, 0, p, -1, NULL, 0);
2451 info->long_mask = HeapAlloc( GetProcessHeap(), 0, long_mask_len * sizeof(WCHAR) );
2452 MultiByteToWideChar(codepage, 0, p, -1, info->long_mask, long_mask_len);
2454 info->short_mask = NULL;
2455 info->attr = 0xff;
2456 info->drive = full_name.drive;
2457 info->cur_pos = 0;
2459 info->u.dos_dir = DOSFS_OpenDir( codepage, info->path );
2461 GlobalUnlock16( handle );
2462 if (!FindNextFile16( handle, data ))
2464 FindClose16( handle );
2465 SetLastError( ERROR_NO_MORE_FILES );
2466 return INVALID_HANDLE_VALUE16;
2468 return handle;
2471 /*************************************************************************
2472 * FindNextFile (KERNEL.414)
2474 BOOL16 WINAPI FindNextFile16( HANDLE16 handle, WIN32_FIND_DATAA *data )
2476 FIND_FIRST_INFO *info;
2477 WIN32_FIND_DATAW dataW;
2478 BOOL ret = FALSE;
2479 DWORD gle = ERROR_NO_MORE_FILES;
2481 if ((handle == INVALID_HANDLE_VALUE16) ||
2482 !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
2484 SetLastError( ERROR_INVALID_HANDLE );
2485 return ret;
2487 if (!info->path || !info->u.dos_dir)
2489 goto done;
2491 if (!DOSFS_FindNextEx( info, &dataW ))
2493 DOSFS_CloseDir( info->u.dos_dir ); info->u.dos_dir = NULL;
2494 HeapFree( GetProcessHeap(), 0, info->path );
2495 info->path = NULL;
2496 HeapFree( GetProcessHeap(), 0, info->long_mask );
2497 info->long_mask = NULL;
2498 goto done;
2501 ret = TRUE;
2503 data->dwFileAttributes = dataW.dwFileAttributes;
2504 data->ftCreationTime = dataW.ftCreationTime;
2505 data->ftLastAccessTime = dataW.ftLastAccessTime;
2506 data->ftLastWriteTime = dataW.ftLastWriteTime;
2507 data->nFileSizeHigh = dataW.nFileSizeHigh;
2508 data->nFileSizeLow = dataW.nFileSizeLow;
2509 WideCharToMultiByte( CP_ACP, 0, dataW.cFileName, -1,
2510 data->cFileName, sizeof(data->cFileName), NULL, NULL );
2511 WideCharToMultiByte( CP_ACP, 0, dataW.cAlternateFileName, -1,
2512 data->cAlternateFileName,
2513 sizeof(data->cAlternateFileName), NULL, NULL );
2514 done:
2515 if( !ret ) SetLastError( gle );
2516 GlobalUnlock16( handle );
2518 return ret;
2521 /*************************************************************************
2522 * FindClose (KERNEL.415)
2524 BOOL16 WINAPI FindClose16( HANDLE16 handle )
2526 FIND_FIRST_INFO *info;
2528 if ((handle == INVALID_HANDLE_VALUE16) ||
2529 !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
2531 SetLastError( ERROR_INVALID_HANDLE );
2532 return FALSE;
2534 if (info->u.dos_dir) DOSFS_CloseDir( info->u.dos_dir );
2535 if (info->path) HeapFree( GetProcessHeap(), 0, info->path );
2536 if (info->long_mask) HeapFree( GetProcessHeap(), 0, info->long_mask );
2537 GlobalUnlock16( handle );
2538 GlobalFree16( handle );
2539 return TRUE;