Put the .spec.o file first and the so libraries last on the link
[wine.git] / files / dos_fs.c
blob7c4ebbdd66eac543075dff8e575c2fcaf7167f9d
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"
23 #include "wine/port.h"
25 #include <sys/types.h>
26 #include <ctype.h>
27 #include <dirent.h>
28 #include <errno.h>
29 #ifdef HAVE_SYS_ERRNO_H
30 #include <sys/errno.h>
31 #endif
32 #include <fcntl.h>
33 #include <stdarg.h>
34 #include <string.h>
35 #include <stdlib.h>
36 #include <sys/stat.h>
37 #ifdef HAVE_SYS_IOCTL_H
38 #include <sys/ioctl.h>
39 #endif
40 #ifdef HAVE_LINUX_IOCTL_H
41 #include <linux/ioctl.h>
42 #endif
43 #include <time.h>
44 #ifdef HAVE_UNISTD_H
45 # include <unistd.h>
46 #endif
48 #define NONAMELESSUNION
49 #define NONAMELESSSTRUCT
50 #include "ntstatus.h"
51 #include "windef.h"
52 #include "winbase.h"
53 #include "winerror.h"
54 #include "wingdi.h"
56 #include "wine/unicode.h"
57 #include "wine/winbase16.h"
58 #include "drive.h"
59 #include "file.h"
60 #include "winternl.h"
61 #include "wine/server.h"
62 #include "wine/exception.h"
63 #include "excpt.h"
65 #include "smb.h"
67 #include "wine/debug.h"
69 WINE_DEFAULT_DEBUG_CHANNEL(dosfs);
70 WINE_DECLARE_DEBUG_CHANNEL(file);
72 /* Define the VFAT ioctl to get both short and long file names */
73 /* FIXME: is it possible to get this to work on other systems? */
74 #ifdef linux
75 /* We want the real kernel dirent structure, not the libc one */
76 typedef struct
78 long d_ino;
79 long d_off;
80 unsigned short d_reclen;
81 char d_name[256];
82 } KERNEL_DIRENT;
84 #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, KERNEL_DIRENT [2] )
86 /* To avoid blocking on non-directories in DOSFS_OpenDir_VFAT*/
87 #ifndef O_DIRECTORY
88 # define O_DIRECTORY 0200000 /* must be directory */
89 #endif
91 #else /* linux */
92 #undef VFAT_IOCTL_READDIR_BOTH /* just in case... */
93 #endif /* linux */
95 #define IS_OPTION_TRUE(ch) ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1')
97 /* Chars we don't want to see in DOS file names */
98 #define INVALID_DOS_CHARS "*?<>|\"+=,;[] \345"
100 /* DOS device descriptor */
101 typedef struct
103 const WCHAR name[5];
104 } DOS_DEVICE;
106 static const DOS_DEVICE DOSFS_Devices[] =
107 /* name, device flags (see Int 21/AX=0x4400) */
109 { {'C','O','N',0} },
110 { {'P','R','N',0} },
111 { {'N','U','L',0} },
112 { {'A','U','X',0} },
113 { {'L','P','T','1',0} },
114 { {'L','P','T','2',0} },
115 { {'L','P','T','3',0} },
116 { {'L','P','T','4',0} },
117 { {'C','O','M','1',0} },
118 { {'C','O','M','2',0} },
119 { {'C','O','M','3',0} },
120 { {'C','O','M','4',0} }
123 static const WCHAR devW[] = {'\\','D','e','v','i','c','e','\\',0};
124 static const WCHAR dosW[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\',0};
126 static const WCHAR auxW[] = {'A','U','X',0};
127 static const WCHAR comW[] = {'C','O','M',0};
128 static const WCHAR lptW[] = {'L','P','T',0};
129 static const WCHAR nulW[] = {'N','U','L',0};
131 static const WCHAR nullW[] = {'N','u','l','l',0};
132 static const WCHAR parW[] = {'P','a','r','a','l','l','e','l',0};
133 static const WCHAR serW[] = {'S','e','r','i','a','l',0};
134 static const WCHAR oneW[] = {'1',0};
137 * Directory info for DOSFS_ReadDir
138 * contains the names of *all* the files in the directory
140 typedef struct
142 int used;
143 int size;
144 WCHAR names[1];
145 } DOS_DIR;
147 /* Info structure for FindFirstFile handle */
148 typedef struct
150 char *path; /* unix path */
151 LPWSTR long_mask;
152 int drive;
153 int cur_pos;
154 CRITICAL_SECTION cs;
155 union
157 DOS_DIR *dos_dir;
158 SMB_DIR *smb_dir;
159 } u;
160 } FIND_FIRST_INFO;
163 static WINE_EXCEPTION_FILTER(page_fault)
165 if (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION)
166 return EXCEPTION_EXECUTE_HANDLER;
167 return EXCEPTION_CONTINUE_SEARCH;
171 /***********************************************************************
172 * DOSFS_ValidDOSName
174 * Return 1 if Unix file 'name' is also a valid MS-DOS name
175 * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
176 * File name can be terminated by '\0', '\\' or '/'.
178 static int DOSFS_ValidDOSName( LPCWSTR name, int ignore_case )
180 static const char invalid_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS;
181 const WCHAR *p = name;
182 const char *invalid = ignore_case ? (invalid_chars + 26) : invalid_chars;
183 int len = 0;
185 if (*p == '.')
187 /* Check for "." and ".." */
188 p++;
189 if (*p == '.') p++;
190 /* All other names beginning with '.' are invalid */
191 return (IS_END_OF_NAME(*p));
193 while (!IS_END_OF_NAME(*p))
195 if (*p < 256 && strchr( invalid, (char)*p )) return 0; /* Invalid char */
196 if (*p == '.') break; /* Start of the extension */
197 if (++len > 8) return 0; /* Name too long */
198 p++;
200 if (*p != '.') return 1; /* End of name */
201 p++;
202 if (IS_END_OF_NAME(*p)) return 0; /* Empty extension not allowed */
203 len = 0;
204 while (!IS_END_OF_NAME(*p))
206 if (*p < 256 && strchr( invalid, (char)*p )) return 0; /* Invalid char */
207 if (*p == '.') return 0; /* Second extension not allowed */
208 if (++len > 3) return 0; /* Extension too long */
209 p++;
211 return 1;
215 /***********************************************************************
216 * DOSFS_ToDosFCBFormat
218 * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
219 * expanding wild cards and converting to upper-case in the process.
220 * File name can be terminated by '\0', '\\' or '/'.
221 * Return FALSE if the name is not a valid DOS name.
222 * 'buffer' must be at least 12 characters long.
224 BOOL DOSFS_ToDosFCBFormat( LPCWSTR name, LPWSTR buffer )
226 static const char invalid_chars[] = INVALID_DOS_CHARS;
227 LPCWSTR p = name;
228 int i;
230 /* Check for "." and ".." */
231 if (*p == '.')
233 p++;
234 buffer[0] = '.';
235 for(i = 1; i < 11; i++) buffer[i] = ' ';
236 buffer[11] = 0;
237 if (*p == '.')
239 buffer[1] = '.';
240 p++;
242 return (!*p || (*p == '/') || (*p == '\\'));
245 for (i = 0; i < 8; i++)
247 switch(*p)
249 case '\0':
250 case '\\':
251 case '/':
252 case '.':
253 buffer[i] = ' ';
254 break;
255 case '?':
256 p++;
257 /* fall through */
258 case '*':
259 buffer[i] = '?';
260 break;
261 default:
262 if (*p < 256 && strchr( invalid_chars, (char)*p )) return FALSE;
263 buffer[i] = toupperW(*p);
264 p++;
265 break;
269 if (*p == '*')
271 /* Skip all chars after wildcard up to first dot */
272 while (*p && (*p != '/') && (*p != '\\') && (*p != '.')) p++;
274 else
276 /* Check if name too long */
277 if (*p && (*p != '/') && (*p != '\\') && (*p != '.')) return FALSE;
279 if (*p == '.') p++; /* Skip dot */
281 for (i = 8; i < 11; i++)
283 switch(*p)
285 case '\0':
286 case '\\':
287 case '/':
288 buffer[i] = ' ';
289 break;
290 case '.':
291 return FALSE; /* Second extension not allowed */
292 case '?':
293 p++;
294 /* fall through */
295 case '*':
296 buffer[i] = '?';
297 break;
298 default:
299 if (*p < 256 && strchr( invalid_chars, (char)*p )) return FALSE;
300 buffer[i] = toupperW(*p);
301 p++;
302 break;
305 buffer[11] = '\0';
307 /* at most 3 character of the extension are processed
308 * is something behind this ?
310 while (*p == '*' || *p == ' ') p++; /* skip wildcards and spaces */
311 return IS_END_OF_NAME(*p);
315 /***********************************************************************
316 * DOSFS_ToDosDTAFormat
318 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
319 * converting to upper-case in the process.
320 * File name can be terminated by '\0', '\\' or '/'.
321 * 'buffer' must be at least 13 characters long.
323 static void DOSFS_ToDosDTAFormat( LPCWSTR name, LPWSTR buffer )
325 LPWSTR p;
327 memcpy( buffer, name, 8 * sizeof(WCHAR) );
328 p = buffer + 8;
329 while ((p > buffer) && (p[-1] == ' ')) p--;
330 *p++ = '.';
331 memcpy( p, name + 8, 3 * sizeof(WCHAR) );
332 p += 3;
333 while (p[-1] == ' ') p--;
334 if (p[-1] == '.') p--;
335 *p = '\0';
339 /***********************************************************************
340 * DOSFS_MatchLong
342 * Check a long file name against a mask.
344 * Tests (done in W95 DOS shell - case insensitive):
345 * *.txt test1.test.txt *
346 * *st1* test1.txt *
347 * *.t??????.t* test1.ta.tornado.txt *
348 * *tornado* test1.ta.tornado.txt *
349 * t*t test1.ta.tornado.txt *
350 * ?est* test1.txt *
351 * ?est??? test1.txt -
352 * *test1.txt* test1.txt *
353 * h?l?o*t.dat hellothisisatest.dat *
355 static int DOSFS_MatchLong( LPCWSTR mask, LPCWSTR name, int case_sensitive )
357 LPCWSTR lastjoker = NULL;
358 LPCWSTR next_to_retry = NULL;
359 static const WCHAR asterisk_dot_asterisk[] = {'*','.','*',0};
361 TRACE("(%s, %s, %x)\n", debugstr_w(mask), debugstr_w(name), case_sensitive);
363 if (!strcmpW( mask, asterisk_dot_asterisk )) return 1;
364 while (*name && *mask)
366 if (*mask == '*')
368 mask++;
369 while (*mask == '*') mask++; /* Skip consecutive '*' */
370 lastjoker = mask;
371 if (!*mask) return 1; /* end of mask is all '*', so match */
373 /* skip to the next match after the joker(s) */
374 if (case_sensitive) while (*name && (*name != *mask)) name++;
375 else while (*name && (toupperW(*name) != toupperW(*mask))) name++;
377 if (!*name) break;
378 next_to_retry = name;
380 else if (*mask != '?')
382 int mismatch = 0;
383 if (case_sensitive)
385 if (*mask != *name) mismatch = 1;
387 else
389 if (toupperW(*mask) != toupperW(*name)) mismatch = 1;
391 if (!mismatch)
393 mask++;
394 name++;
395 if (*mask == '\0')
397 if (*name == '\0')
398 return 1;
399 if (lastjoker)
400 mask = lastjoker;
403 else /* mismatch ! */
405 if (lastjoker) /* we had an '*', so we can try unlimitedly */
407 mask = lastjoker;
409 /* this scan sequence was a mismatch, so restart
410 * 1 char after the first char we checked last time */
411 next_to_retry++;
412 name = next_to_retry;
414 else
415 return 0; /* bad luck */
418 else /* '?' */
420 mask++;
421 name++;
424 while ((*mask == '.') || (*mask == '*'))
425 mask++; /* Ignore trailing '.' or '*' in mask */
426 return (!*name && !*mask);
430 /***********************************************************************
431 * DOSFS_AddDirEntry
433 * Used to construct an array of filenames in DOSFS_OpenDir
435 static BOOL DOSFS_AddDirEntry(DOS_DIR **dir, LPCWSTR name, LPCWSTR dosname)
437 int extra1 = strlenW(name) + 1;
438 int extra2 = strlenW(dosname) + 1;
440 /* if we need more, at minimum double the size */
441 if( (extra1 + extra2 + (*dir)->used) > (*dir)->size)
443 int more = (*dir)->size;
444 DOS_DIR *t;
446 if(more<(extra1+extra2))
447 more = extra1+extra2;
449 t = HeapReAlloc(GetProcessHeap(), 0, *dir, sizeof(**dir) +
450 ((*dir)->size + more)*sizeof(WCHAR) );
451 if(!t)
453 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
454 ERR("Out of memory caching directory structure %d %d %d\n",
455 (*dir)->size, more, (*dir)->used);
456 return FALSE;
458 (*dir) = t;
459 (*dir)->size += more;
462 /* at this point, the dir structure is big enough to hold these names */
463 strcpyW(&(*dir)->names[(*dir)->used], name);
464 (*dir)->used += extra1;
465 strcpyW(&(*dir)->names[(*dir)->used], dosname);
466 (*dir)->used += extra2;
468 return TRUE;
472 /***********************************************************************
473 * DOSFS_OpenDir_VFAT
475 static BOOL DOSFS_OpenDir_VFAT(DOS_DIR **dir, const char *unix_path)
477 #ifdef VFAT_IOCTL_READDIR_BOTH
478 KERNEL_DIRENT de[2];
479 int fd = open( unix_path, O_RDONLY|O_DIRECTORY );
480 BOOL r = TRUE;
482 /* Check if the VFAT ioctl is supported on this directory */
484 if ( fd<0 )
485 return FALSE;
487 while (1)
489 WCHAR long_name[MAX_PATH];
490 WCHAR short_name[12];
492 r = (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de ) != -1);
493 if(!r)
494 break;
495 if (!de[0].d_reclen)
496 break;
497 MultiByteToWideChar(CP_UNIXCP, 0, de[0].d_name, -1, long_name, MAX_PATH);
498 if (!DOSFS_ToDosFCBFormat( long_name, short_name ))
499 short_name[0] = '\0';
500 if (de[1].d_name[0])
501 MultiByteToWideChar(CP_UNIXCP, 0, de[1].d_name, -1, long_name, MAX_PATH);
502 else
503 MultiByteToWideChar(CP_UNIXCP, 0, de[0].d_name, -1, long_name, MAX_PATH);
504 r = DOSFS_AddDirEntry(dir, long_name, short_name );
505 if(!r)
506 break;
508 if(r)
510 static const WCHAR empty_strW[] = { 0 };
511 DOSFS_AddDirEntry(dir, empty_strW, empty_strW);
513 close(fd);
514 return r;
515 #else
516 return FALSE;
517 #endif /* VFAT_IOCTL_READDIR_BOTH */
521 /***********************************************************************
522 * DOSFS_OpenDir_Normal
524 * Now use the standard opendir/readdir interface
526 static BOOL DOSFS_OpenDir_Normal( DOS_DIR **dir, const char *unix_path )
528 DIR *unixdir = opendir( unix_path );
529 BOOL r = TRUE;
530 static const WCHAR empty_strW[] = { 0 };
532 if(!unixdir)
533 return FALSE;
534 while(1)
536 WCHAR long_name[MAX_PATH];
537 struct dirent *de = readdir(unixdir);
539 if(!de)
540 break;
541 MultiByteToWideChar(CP_UNIXCP, 0, de->d_name, -1, long_name, MAX_PATH);
542 r = DOSFS_AddDirEntry(dir, long_name, empty_strW);
543 if(!r)
544 break;
546 if(r)
547 DOSFS_AddDirEntry(dir, empty_strW, empty_strW);
548 closedir(unixdir);
549 return r;
552 /***********************************************************************
553 * DOSFS_OpenDir
555 static DOS_DIR *DOSFS_OpenDir( const char *unix_path )
557 const int init_size = 0x100;
558 DOS_DIR *dir = HeapAlloc( GetProcessHeap(), 0, sizeof(*dir) + init_size*sizeof (WCHAR));
559 BOOL r;
561 TRACE("%s\n",debugstr_a(unix_path));
563 if (!dir)
565 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
566 return NULL;
568 dir->used = 0;
569 dir->size = init_size;
571 /* Treat empty path as root directory. This simplifies path split into
572 directory and mask in several other places */
573 if (!*unix_path) unix_path = "/";
575 r = DOSFS_OpenDir_VFAT( &dir, unix_path);
577 if(!r)
578 r = DOSFS_OpenDir_Normal( &dir, unix_path);
580 if(!r)
582 HeapFree(GetProcessHeap(), 0, dir);
583 return NULL;
585 dir->used = 0;
587 return dir;
591 /***********************************************************************
592 * DOSFS_CloseDir
594 static void DOSFS_CloseDir( DOS_DIR *dir )
596 HeapFree( GetProcessHeap(), 0, dir );
600 /***********************************************************************
601 * DOSFS_ReadDir
603 static BOOL DOSFS_ReadDir( DOS_DIR *dir, LPCWSTR *long_name,
604 LPCWSTR *short_name )
606 LPCWSTR sn, ln;
608 if (!dir)
609 return FALSE;
611 /* the long pathname is first */
612 ln = &dir->names[dir->used];
613 if(ln[0])
614 *long_name = ln;
615 else
616 return FALSE;
617 dir->used += (strlenW(ln) + 1);
619 /* followed by the short path name */
620 sn = &dir->names[dir->used];
621 if(sn[0])
622 *short_name = sn;
623 else
624 *short_name = NULL;
625 dir->used += (strlenW(sn) + 1);
627 return TRUE;
631 /***********************************************************************
632 * DOSFS_Hash
634 * Transform a Unix file name into a hashed DOS name. If the name is a valid
635 * DOS name, it is converted to upper-case; otherwise it is replaced by a
636 * hashed version that fits in 8.3 format.
637 * File name can be terminated by '\0', '\\' or '/'.
638 * 'buffer' must be at least 13 characters long.
640 static void DOSFS_Hash( LPCWSTR name, LPWSTR buffer, BOOL dir_format,
641 BOOL ignore_case )
643 static const char invalid_chars[] = INVALID_DOS_CHARS "~.";
644 static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
646 LPCWSTR p, ext;
647 LPWSTR dst;
648 unsigned short hash;
649 int i;
651 if (dir_format)
653 for(i = 0; i < 11; i++) buffer[i] = ' ';
654 buffer[11] = 0;
657 if (DOSFS_ValidDOSName( name, ignore_case ))
659 /* Check for '.' and '..' */
660 if (*name == '.')
662 buffer[0] = '.';
663 if (!dir_format) buffer[1] = buffer[2] = '\0';
664 if (name[1] == '.') buffer[1] = '.';
665 return;
668 /* Simply copy the name, converting to uppercase */
670 for (dst = buffer; !IS_END_OF_NAME(*name) && (*name != '.'); name++)
671 *dst++ = toupperW(*name);
672 if (*name == '.')
674 if (dir_format) dst = buffer + 8;
675 else *dst++ = '.';
676 for (name++; !IS_END_OF_NAME(*name); name++)
677 *dst++ = toupperW(*name);
679 if (!dir_format) *dst = '\0';
680 return;
683 /* Compute the hash code of the file name */
684 /* If you know something about hash functions, feel free to */
685 /* insert a better algorithm here... */
686 if (ignore_case)
688 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
689 hash = (hash<<3) ^ (hash>>5) ^ tolowerW(*p) ^ (tolowerW(p[1]) << 8);
690 hash = (hash<<3) ^ (hash>>5) ^ tolowerW(*p); /* Last character */
692 else
694 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
695 hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
696 hash = (hash << 3) ^ (hash >> 5) ^ *p; /* Last character */
699 /* Find last dot for start of the extension */
700 for (p = name+1, ext = NULL; !IS_END_OF_NAME(*p); p++)
701 if (*p == '.') ext = p;
702 if (ext && IS_END_OF_NAME(ext[1]))
703 ext = NULL; /* Empty extension ignored */
705 /* Copy first 4 chars, replacing invalid chars with '_' */
706 for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
708 if (IS_END_OF_NAME(*p) || (p == ext)) break;
709 *dst++ = (*p < 256 && strchr( invalid_chars, (char)*p )) ? '_' : toupperW(*p);
711 /* Pad to 5 chars with '~' */
712 while (i-- >= 0) *dst++ = '~';
714 /* Insert hash code converted to 3 ASCII chars */
715 *dst++ = hash_chars[(hash >> 10) & 0x1f];
716 *dst++ = hash_chars[(hash >> 5) & 0x1f];
717 *dst++ = hash_chars[hash & 0x1f];
719 /* Copy the first 3 chars of the extension (if any) */
720 if (ext)
722 if (!dir_format) *dst++ = '.';
723 for (i = 3, ext++; (i > 0) && !IS_END_OF_NAME(*ext); i--, ext++)
724 *dst++ = (*ext < 256 && strchr( invalid_chars, (char)*ext )) ? '_' : toupperW(*ext);
726 if (!dir_format) *dst = '\0';
730 /***********************************************************************
731 * DOSFS_FindUnixName
733 * Find the Unix file name in a given directory that corresponds to
734 * a file name (either in Unix or DOS format).
735 * File name can be terminated by '\0', '\\' or '/'.
736 * Return TRUE if OK, FALSE if no file name matches.
738 * 'long_buf' must be at least 'long_len' characters long. If the long name
739 * turns out to be larger than that, the function returns FALSE.
740 * 'short_buf' must be at least 13 characters long.
742 BOOL DOSFS_FindUnixName( const DOS_FULL_NAME *path, LPCWSTR name, char *long_buf,
743 INT long_len, LPWSTR short_buf, BOOL ignore_case)
745 DOS_DIR *dir;
746 LPCWSTR long_name, short_name;
747 WCHAR dos_name[12], tmp_buf[13];
748 BOOL ret;
750 LPCWSTR p = strchrW( name, '/' );
751 int len = p ? (int)(p - name) : strlenW(name);
752 if ((p = strchrW( name, '\\' ))) len = min( (int)(p - name), len );
753 /* Ignore trailing dots and spaces */
754 while (len > 1 && (name[len-1] == '.' || name[len-1] == ' ')) len--;
755 if (long_len < len + 1) return FALSE;
757 TRACE("%s,%s\n", path->long_name, debugstr_w(name) );
759 if (!DOSFS_ToDosFCBFormat( name, dos_name )) dos_name[0] = '\0';
761 if (!(dir = DOSFS_OpenDir( path->long_name )))
763 WARN("(%s,%s): can't open dir: %s\n",
764 path->long_name, debugstr_w(name), strerror(errno) );
765 return FALSE;
768 while ((ret = DOSFS_ReadDir( dir, &long_name, &short_name )))
770 /* Check against Unix name */
771 if (len == strlenW(long_name))
773 if (!ignore_case)
775 if (!strncmpW( long_name, name, len )) break;
777 else
779 if (!strncmpiW( long_name, name, len )) break;
782 if (dos_name[0])
784 /* Check against hashed DOS name */
785 if (!short_name)
787 DOSFS_Hash( long_name, tmp_buf, TRUE, ignore_case );
788 short_name = tmp_buf;
790 if (!strcmpW( dos_name, short_name )) break;
793 if (ret)
795 if (long_buf) WideCharToMultiByte(CP_UNIXCP, 0, long_name, -1, long_buf, long_len, NULL, NULL);
796 if (short_buf)
798 if (short_name)
799 DOSFS_ToDosDTAFormat( short_name, short_buf );
800 else
801 DOSFS_Hash( long_name, short_buf, FALSE, ignore_case );
803 TRACE("(%s,%s) -> %s (%s)\n", path->long_name, debugstr_w(name),
804 debugstr_w(long_name), short_buf ? debugstr_w(short_buf) : "***");
806 else
807 WARN("%s not found in '%s'\n", debugstr_w(name), path->long_name);
808 DOSFS_CloseDir( dir );
809 return ret;
813 /**************************************************************************
814 * DOSFS_CreateCommPort
816 static HANDLE DOSFS_CreateCommPort(LPCWSTR name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa)
818 HANDLE ret;
819 HKEY hkey;
820 DWORD dummy;
821 OBJECT_ATTRIBUTES attr;
822 UNICODE_STRING nameW;
823 WCHAR *devnameW;
824 char tmp[128];
825 char devname[40];
827 static const WCHAR serialportsW[] = {'M','a','c','h','i','n','e','\\',
828 'S','o','f','t','w','a','r','e','\\',
829 'W','i','n','e','\\','W','i','n','e','\\',
830 'C','o','n','f','i','g','\\',
831 'S','e','r','i','a','l','P','o','r','t','s',0};
833 TRACE_(file)("%s %lx %lx\n", debugstr_w(name), access, attributes);
835 attr.Length = sizeof(attr);
836 attr.RootDirectory = 0;
837 attr.ObjectName = &nameW;
838 attr.Attributes = 0;
839 attr.SecurityDescriptor = NULL;
840 attr.SecurityQualityOfService = NULL;
841 RtlInitUnicodeString( &nameW, serialportsW );
843 if (NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr )) return 0;
845 RtlInitUnicodeString( &nameW, name );
846 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
847 devnameW = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
848 else
849 devnameW = NULL;
851 NtClose( hkey );
853 if (!devnameW) return 0;
854 WideCharToMultiByte(CP_ACP, 0, devnameW, -1, devname, sizeof(devname), NULL, NULL);
856 TRACE("opening %s as %s\n", devname, debugstr_w(name));
858 SERVER_START_REQ( create_serial )
860 req->access = access;
861 req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
862 req->attributes = attributes;
863 req->sharing = FILE_SHARE_READ|FILE_SHARE_WRITE;
864 wine_server_add_data( req, devname, strlen(devname) );
865 SetLastError(0);
866 wine_server_call_err( req );
867 ret = reply->handle;
869 SERVER_END_REQ;
871 if(!ret)
872 ERR("Couldn't open device '%s' ! (check permissions)\n",devname);
873 else
874 TRACE("return %p\n", ret );
875 return ret;
878 /***********************************************************************
879 * DOSFS_OpenDevice
881 * Open a DOS device. This might not map 1:1 into the UNIX device concept.
882 * Returns 0 on failure.
884 HANDLE DOSFS_OpenDevice( LPCWSTR name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa )
886 unsigned int i;
887 const WCHAR *p;
888 HANDLE handle;
890 if (name[0] && (name[1] == ':')) name += 2;
891 if ((p = strrchrW( name, '/' ))) name = p + 1;
892 if ((p = strrchrW( name, '\\' ))) name = p + 1;
893 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
895 const WCHAR *dev = DOSFS_Devices[i].name;
896 if (!strncmpiW( dev, name, strlenW(dev) ))
898 p = name + strlenW( dev );
899 if (!*p || (*p == '.') || (*p == ':')) {
900 static const WCHAR nulW[] = {'N','U','L',0};
901 static const WCHAR conW[] = {'C','O','N',0};
902 /* got it */
903 if (!strcmpiW(DOSFS_Devices[i].name, nulW))
904 return FILE_CreateFile( "/dev/null", access,
905 FILE_SHARE_READ|FILE_SHARE_WRITE, sa,
906 OPEN_EXISTING, 0, 0, TRUE, DRIVE_UNKNOWN );
907 if (!strcmpiW(DOSFS_Devices[i].name, conW)) {
908 HANDLE to_dup;
909 switch (access & (GENERIC_READ|GENERIC_WRITE)) {
910 case GENERIC_READ:
911 to_dup = GetStdHandle( STD_INPUT_HANDLE );
912 break;
913 case GENERIC_WRITE:
914 to_dup = GetStdHandle( STD_OUTPUT_HANDLE );
915 break;
916 default:
917 FIXME("can't open CON read/write\n");
918 return 0;
920 if (!DuplicateHandle( GetCurrentProcess(), to_dup, GetCurrentProcess(),
921 &handle, 0,
922 sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle,
923 DUPLICATE_SAME_ACCESS ))
924 handle = 0;
925 return handle;
928 if( (handle=DOSFS_CreateCommPort(DOSFS_Devices[i].name,access,attributes,sa)) )
929 return handle;
930 FIXME("device open %s not supported (yet)\n", debugstr_w(DOSFS_Devices[i].name));
931 return 0;
935 return 0;
939 /***********************************************************************
940 * DOSFS_GetPathDrive
942 * Get the drive specified by a given path name (DOS or Unix format).
944 static int DOSFS_GetPathDrive( LPCWSTR *name )
946 int drive;
947 LPCWSTR p = *name;
949 if (*p && (p[1] == ':'))
951 drive = toupperW(*p) - 'A';
952 *name += 2;
954 else if (*p == '/') /* Absolute Unix path? */
956 if ((drive = DRIVE_FindDriveRootW( name )) == -1)
958 MESSAGE("Warning: %s not accessible from a configured DOS drive\n", debugstr_w(*name) );
959 /* Assume it really was a DOS name */
960 drive = DRIVE_GetCurrentDrive();
963 else drive = DRIVE_GetCurrentDrive();
965 if (!DRIVE_IsValid(drive))
967 SetLastError( ERROR_INVALID_DRIVE );
968 return -1;
970 return drive;
974 /***********************************************************************
975 * DOSFS_GetFullName
977 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
978 * Unix name / short DOS name pair.
979 * Return FALSE if one of the path components does not exist. The last path
980 * component is only checked if 'check_last' is non-zero.
981 * The buffers pointed to by 'long_buf' and 'short_buf' must be
982 * at least MAX_PATHNAME_LEN long.
984 BOOL DOSFS_GetFullName( LPCWSTR name, BOOL check_last, DOS_FULL_NAME *full )
986 BOOL found;
987 UINT flags;
988 char *p_l, *root;
989 LPWSTR p_s;
990 static const WCHAR driveA_rootW[] = {'A',':','\\',0};
991 static const WCHAR dos_rootW[] = {'\\',0};
993 TRACE("%s (last=%d)\n", debugstr_w(name), check_last );
995 if ((!*name) || (*name=='\n'))
996 { /* error code for Win98 */
997 SetLastError(ERROR_BAD_PATHNAME);
998 return FALSE;
1001 if ((full->drive = DOSFS_GetPathDrive( &name )) == -1) return FALSE;
1002 flags = DRIVE_GetFlags( full->drive );
1004 lstrcpynA( full->long_name, DRIVE_GetRoot( full->drive ),
1005 sizeof(full->long_name) );
1006 if (full->long_name[1]) root = full->long_name + strlen(full->long_name);
1007 else root = full->long_name; /* root directory */
1009 strcpyW( full->short_name, driveA_rootW );
1010 full->short_name[0] += full->drive;
1012 if ((*name == '\\') || (*name == '/')) /* Absolute path */
1014 while ((*name == '\\') || (*name == '/')) name++;
1016 else /* Relative path */
1018 lstrcpynA( root + 1, DRIVE_GetUnixCwd( full->drive ),
1019 sizeof(full->long_name) - (root - full->long_name) - 1 );
1020 if (root[1]) *root = '/';
1021 lstrcpynW( full->short_name + 3, DRIVE_GetDosCwd( full->drive ),
1022 sizeof(full->short_name)/sizeof(full->short_name[0]) - 3 );
1025 p_l = full->long_name[1] ? full->long_name + strlen(full->long_name)
1026 : full->long_name;
1027 p_s = full->short_name[3] ? full->short_name + strlenW(full->short_name)
1028 : full->short_name + 2;
1029 found = TRUE;
1031 while (*name && found)
1033 /* Check for '.' and '..' */
1035 if (*name == '.')
1037 if (IS_END_OF_NAME(name[1]))
1039 name++;
1040 while ((*name == '\\') || (*name == '/')) name++;
1041 continue;
1043 else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
1045 name += 2;
1046 while ((*name == '\\') || (*name == '/')) name++;
1047 while ((p_l > root) && (*p_l != '/')) p_l--;
1048 while ((p_s > full->short_name + 2) && (*p_s != '\\')) p_s--;
1049 *p_l = *p_s = '\0'; /* Remove trailing separator */
1050 continue;
1054 /* Make sure buffers are large enough */
1056 if ((p_s >= full->short_name + sizeof(full->short_name)/sizeof(full->short_name[0]) - 14) ||
1057 (p_l >= full->long_name + sizeof(full->long_name) - 1))
1059 SetLastError( ERROR_PATH_NOT_FOUND );
1060 return FALSE;
1063 /* Get the long and short name matching the file name */
1065 if ((found = DOSFS_FindUnixName( full, name, p_l + 1,
1066 sizeof(full->long_name) - (p_l - full->long_name) - 1,
1067 p_s + 1, !(flags & DRIVE_CASE_SENSITIVE) )))
1069 *p_l++ = '/';
1070 p_l += strlen(p_l);
1071 *p_s++ = '\\';
1072 p_s += strlenW(p_s);
1073 while (!IS_END_OF_NAME(*name)) name++;
1075 else if (!check_last)
1077 *p_l++ = '/';
1078 *p_s++ = '\\';
1079 while (!IS_END_OF_NAME(*name) &&
1080 (p_s < full->short_name + sizeof(full->short_name)/sizeof(full->short_name[0]) - 1) &&
1081 (p_l < full->long_name + sizeof(full->long_name) - 1))
1083 WCHAR wch;
1084 *p_s++ = tolowerW(*name);
1085 /* If the drive is case-sensitive we want to create new */
1086 /* files in lower-case otherwise we can't reopen them */
1087 /* under the same short name. */
1088 if (flags & DRIVE_CASE_SENSITIVE) wch = tolowerW(*name);
1089 else wch = *name;
1090 p_l += WideCharToMultiByte(CP_UNIXCP, 0, &wch, 1, p_l, 2, NULL, NULL);
1091 name++;
1093 /* Ignore trailing dots and spaces */
1094 while(p_l[-1] == '.' || p_l[-1] == ' ') {
1095 --p_l;
1096 --p_s;
1098 *p_l = '\0';
1099 *p_s = '\0';
1101 while ((*name == '\\') || (*name == '/')) name++;
1104 if (!found)
1106 if (check_last)
1108 SetLastError( ERROR_FILE_NOT_FOUND );
1109 return FALSE;
1111 if (*name) /* Not last */
1113 SetLastError( ERROR_PATH_NOT_FOUND );
1114 return FALSE;
1117 if (!full->long_name[0]) strcpy( full->long_name, "/" );
1118 if (!full->short_name[2]) strcpyW( full->short_name + 2, dos_rootW );
1119 TRACE("returning %s = %s\n", full->long_name, debugstr_w(full->short_name) );
1120 return TRUE;
1124 /***********************************************************************
1125 * wine_get_unix_file_name (KERNEL32.@) Not a Windows API
1127 * Return the full Unix file name for a given path.
1129 BOOL WINAPI wine_get_unix_file_name( LPCWSTR dosW, LPSTR buffer, DWORD len )
1131 BOOL ret;
1132 DOS_FULL_NAME path;
1134 ret = DOSFS_GetFullName( dosW, FALSE, &path );
1135 if (ret && len)
1137 strncpy( buffer, path.long_name, len );
1138 buffer[len - 1] = 0; /* ensure 0 termination */
1140 return ret;
1144 /***********************************************************************
1145 * get_show_dir_symlinks_option
1147 static BOOL get_show_dir_symlinks_option(void)
1149 static const WCHAR WineW[] = {'M','a','c','h','i','n','e','\\',
1150 'S','o','f','t','w','a','r','e','\\',
1151 'W','i','n','e','\\','W','i','n','e','\\',
1152 'C','o','n','f','i','g','\\','W','i','n','e',0};
1153 static const WCHAR ShowDirSymlinksW[] = {'S','h','o','w','D','i','r','S','y','m','l','i','n','k','s',0};
1155 char tmp[80];
1156 HKEY hkey;
1157 DWORD dummy;
1158 OBJECT_ATTRIBUTES attr;
1159 UNICODE_STRING nameW;
1160 BOOL ret = FALSE;
1162 attr.Length = sizeof(attr);
1163 attr.RootDirectory = 0;
1164 attr.ObjectName = &nameW;
1165 attr.Attributes = 0;
1166 attr.SecurityDescriptor = NULL;
1167 attr.SecurityQualityOfService = NULL;
1168 RtlInitUnicodeString( &nameW, WineW );
1170 if (!NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr ))
1172 RtlInitUnicodeString( &nameW, ShowDirSymlinksW );
1173 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
1175 WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
1176 ret = IS_OPTION_TRUE( str[0] );
1178 NtClose( hkey );
1180 return ret;
1184 /***********************************************************************
1185 * DOSFS_FindNextEx
1187 static int DOSFS_FindNextEx( FIND_FIRST_INFO *info, WIN32_FIND_DATAW *entry )
1189 UINT flags = DRIVE_GetFlags( info->drive );
1190 char *p, buffer[MAX_PATHNAME_LEN];
1191 const char *drive_path;
1192 int drive_root;
1193 LPCWSTR long_name, short_name;
1194 BY_HANDLE_FILE_INFORMATION fileinfo;
1195 BOOL is_symlink;
1197 drive_path = info->path + strlen(DRIVE_GetRoot( info->drive ));
1198 while ((*drive_path == '/') || (*drive_path == '\\')) drive_path++;
1199 drive_root = !*drive_path;
1201 lstrcpynA( buffer, info->path, sizeof(buffer) - 1 );
1202 strcat( buffer, "/" );
1203 p = buffer + strlen(buffer);
1205 while (DOSFS_ReadDir( info->u.dos_dir, &long_name, &short_name ))
1207 info->cur_pos++;
1209 /* Don't return '.' and '..' in the root of the drive */
1210 if (drive_root && (long_name[0] == '.') &&
1211 (!long_name[1] || ((long_name[1] == '.') && !long_name[2])))
1212 continue;
1214 /* Check the long mask */
1216 if (info->long_mask && *info->long_mask)
1218 if (!DOSFS_MatchLong( info->long_mask, long_name,
1219 flags & DRIVE_CASE_SENSITIVE )) continue;
1222 /* Check the file attributes */
1223 WideCharToMultiByte(CP_UNIXCP, 0, long_name, -1,
1224 p, sizeof(buffer) - (int)(p - buffer), NULL, NULL);
1225 if (!FILE_Stat( buffer, &fileinfo, &is_symlink ))
1227 WARN("can't stat %s\n", buffer);
1228 continue;
1230 if (is_symlink && (fileinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
1232 static int show_dir_symlinks = -1;
1233 if (show_dir_symlinks == -1)
1234 show_dir_symlinks = get_show_dir_symlinks_option();
1235 if (!show_dir_symlinks) continue;
1238 /* We now have a matching entry; fill the result and return */
1240 entry->dwFileAttributes = fileinfo.dwFileAttributes;
1241 entry->ftCreationTime = fileinfo.ftCreationTime;
1242 entry->ftLastAccessTime = fileinfo.ftLastAccessTime;
1243 entry->ftLastWriteTime = fileinfo.ftLastWriteTime;
1244 entry->nFileSizeHigh = fileinfo.nFileSizeHigh;
1245 entry->nFileSizeLow = fileinfo.nFileSizeLow;
1247 if (short_name)
1248 DOSFS_ToDosDTAFormat( short_name, entry->cAlternateFileName );
1249 else
1250 DOSFS_Hash( long_name, entry->cAlternateFileName, FALSE,
1251 !(flags & DRIVE_CASE_SENSITIVE) );
1253 lstrcpynW( entry->cFileName, long_name, sizeof(entry->cFileName)/sizeof(entry->cFileName[0]) );
1254 if (!(flags & DRIVE_CASE_PRESERVING)) strlwrW( entry->cFileName );
1255 TRACE("returning %s (%s) %02lx %ld\n",
1256 debugstr_w(entry->cFileName), debugstr_w(entry->cAlternateFileName),
1257 entry->dwFileAttributes, entry->nFileSizeLow );
1258 return 1;
1260 return 0; /* End of directory */
1263 /*************************************************************************
1264 * FindFirstFileExW (KERNEL32.@)
1266 HANDLE WINAPI FindFirstFileExW(
1267 LPCWSTR lpFileName,
1268 FINDEX_INFO_LEVELS fInfoLevelId,
1269 LPVOID lpFindFileData,
1270 FINDEX_SEARCH_OPS fSearchOp,
1271 LPVOID lpSearchFilter,
1272 DWORD dwAdditionalFlags)
1274 FIND_FIRST_INFO *info;
1276 if (!lpFileName)
1278 SetLastError(ERROR_PATH_NOT_FOUND);
1279 return INVALID_HANDLE_VALUE;
1282 if ((fSearchOp != FindExSearchNameMatch) || (dwAdditionalFlags != 0))
1284 FIXME("options not implemented 0x%08x 0x%08lx\n", fSearchOp, dwAdditionalFlags );
1285 return INVALID_HANDLE_VALUE;
1288 switch(fInfoLevelId)
1290 case FindExInfoStandard:
1292 WIN32_FIND_DATAW * data = (WIN32_FIND_DATAW *) lpFindFileData;
1293 char *p;
1294 INT long_mask_len;
1296 data->dwReserved0 = data->dwReserved1 = 0x0;
1297 if (lpFileName[0] == '\\' && lpFileName[1] == '\\')
1299 ERR("UNC path name\n");
1300 if (!(info = HeapAlloc( GetProcessHeap(), 0, sizeof(FIND_FIRST_INFO)))) break;
1301 info->u.smb_dir = SMB_FindFirst(lpFileName);
1302 if(!info->u.smb_dir)
1304 HeapFree(GetProcessHeap(), 0, info);
1305 break;
1307 info->drive = -1;
1308 RtlInitializeCriticalSection( &info->cs );
1310 else
1312 DOS_FULL_NAME full_name;
1314 if (lpFileName[0] && lpFileName[1] == ':')
1316 /* don't allow root directories */
1317 if (!lpFileName[2] ||
1318 ((lpFileName[2] == '/' || lpFileName[2] == '\\') && !lpFileName[3]))
1320 SetLastError(ERROR_FILE_NOT_FOUND);
1321 return INVALID_HANDLE_VALUE;
1324 if (!DOSFS_GetFullName( lpFileName, FALSE, &full_name )) break;
1325 if (!(info = HeapAlloc( GetProcessHeap(), 0, sizeof(FIND_FIRST_INFO)))) break;
1326 RtlInitializeCriticalSection( &info->cs );
1327 info->path = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
1328 strcpy( info->path, full_name.long_name );
1330 p = strrchr( info->path, '/' );
1331 *p++ = '\0';
1332 long_mask_len = MultiByteToWideChar(CP_UNIXCP, 0, p, -1, NULL, 0);
1333 info->long_mask = HeapAlloc( GetProcessHeap(), 0, long_mask_len * sizeof(WCHAR) );
1334 MultiByteToWideChar(CP_UNIXCP, 0, p, -1, info->long_mask, long_mask_len);
1336 info->drive = full_name.drive;
1337 info->cur_pos = 0;
1339 info->u.dos_dir = DOSFS_OpenDir( info->path );
1341 if (!FindNextFileW( (HANDLE) info, data ))
1343 FindClose( (HANDLE) info );
1344 SetLastError( ERROR_FILE_NOT_FOUND );
1345 break;
1347 return (HANDLE) info;
1349 break;
1350 default:
1351 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
1353 return INVALID_HANDLE_VALUE;
1356 /*************************************************************************
1357 * FindFirstFileA (KERNEL32.@)
1359 HANDLE WINAPI FindFirstFileA(
1360 LPCSTR lpFileName,
1361 WIN32_FIND_DATAA *lpFindData )
1363 return FindFirstFileExA(lpFileName, FindExInfoStandard, lpFindData,
1364 FindExSearchNameMatch, NULL, 0);
1367 /*************************************************************************
1368 * FindFirstFileExA (KERNEL32.@)
1370 HANDLE WINAPI FindFirstFileExA(
1371 LPCSTR lpFileName,
1372 FINDEX_INFO_LEVELS fInfoLevelId,
1373 LPVOID lpFindFileData,
1374 FINDEX_SEARCH_OPS fSearchOp,
1375 LPVOID lpSearchFilter,
1376 DWORD dwAdditionalFlags)
1378 HANDLE handle;
1379 WIN32_FIND_DATAA *dataA;
1380 WIN32_FIND_DATAW dataW;
1381 UNICODE_STRING pathW;
1383 if (!lpFileName)
1385 SetLastError(ERROR_PATH_NOT_FOUND);
1386 return INVALID_HANDLE_VALUE;
1389 if (!RtlCreateUnicodeStringFromAsciiz(&pathW, lpFileName))
1391 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1392 return INVALID_HANDLE_VALUE;
1395 handle = FindFirstFileExW(pathW.Buffer, fInfoLevelId, &dataW, fSearchOp, lpSearchFilter, dwAdditionalFlags);
1396 RtlFreeUnicodeString(&pathW);
1397 if (handle == INVALID_HANDLE_VALUE) return handle;
1399 dataA = (WIN32_FIND_DATAA *) lpFindFileData;
1400 dataA->dwFileAttributes = dataW.dwFileAttributes;
1401 dataA->ftCreationTime = dataW.ftCreationTime;
1402 dataA->ftLastAccessTime = dataW.ftLastAccessTime;
1403 dataA->ftLastWriteTime = dataW.ftLastWriteTime;
1404 dataA->nFileSizeHigh = dataW.nFileSizeHigh;
1405 dataA->nFileSizeLow = dataW.nFileSizeLow;
1406 WideCharToMultiByte( CP_ACP, 0, dataW.cFileName, -1,
1407 dataA->cFileName, sizeof(dataA->cFileName), NULL, NULL );
1408 WideCharToMultiByte( CP_ACP, 0, dataW.cAlternateFileName, -1,
1409 dataA->cAlternateFileName, sizeof(dataA->cAlternateFileName), NULL, NULL );
1410 return handle;
1413 /*************************************************************************
1414 * FindFirstFileW (KERNEL32.@)
1416 HANDLE WINAPI FindFirstFileW( LPCWSTR lpFileName, WIN32_FIND_DATAW *lpFindData )
1418 return FindFirstFileExW(lpFileName, FindExInfoStandard, lpFindData,
1419 FindExSearchNameMatch, NULL, 0);
1422 /*************************************************************************
1423 * FindNextFileW (KERNEL32.@)
1425 BOOL WINAPI FindNextFileW( HANDLE handle, WIN32_FIND_DATAW *data )
1427 FIND_FIRST_INFO *info;
1428 BOOL ret = FALSE;
1429 DWORD gle = ERROR_NO_MORE_FILES;
1431 if (handle == INVALID_HANDLE_VALUE)
1433 SetLastError( ERROR_INVALID_HANDLE );
1434 return ret;
1436 info = (FIND_FIRST_INFO*) handle;
1437 RtlEnterCriticalSection( &info->cs );
1438 if (info->drive == -1)
1440 ret = SMB_FindNext( info->u.smb_dir, data );
1441 if(!ret)
1443 SMB_CloseDir( info->u.smb_dir );
1444 HeapFree( GetProcessHeap(), 0, info->path );
1446 goto done;
1448 else if (!info->path || !info->u.dos_dir)
1450 goto done;
1452 else if (!DOSFS_FindNextEx( info, data ))
1454 DOSFS_CloseDir( info->u.dos_dir ); info->u.dos_dir = NULL;
1455 HeapFree( GetProcessHeap(), 0, info->path );
1456 info->path = NULL;
1457 HeapFree( GetProcessHeap(), 0, info->long_mask );
1458 info->long_mask = NULL;
1459 goto done;
1461 ret = TRUE;
1462 done:
1463 RtlLeaveCriticalSection( &info->cs );
1464 if( !ret ) SetLastError( gle );
1465 return ret;
1469 /*************************************************************************
1470 * FindNextFileA (KERNEL32.@)
1472 BOOL WINAPI FindNextFileA( HANDLE handle, WIN32_FIND_DATAA *data )
1474 WIN32_FIND_DATAW dataW;
1475 if (!FindNextFileW( handle, &dataW )) return FALSE;
1476 data->dwFileAttributes = dataW.dwFileAttributes;
1477 data->ftCreationTime = dataW.ftCreationTime;
1478 data->ftLastAccessTime = dataW.ftLastAccessTime;
1479 data->ftLastWriteTime = dataW.ftLastWriteTime;
1480 data->nFileSizeHigh = dataW.nFileSizeHigh;
1481 data->nFileSizeLow = dataW.nFileSizeLow;
1482 WideCharToMultiByte( CP_ACP, 0, dataW.cFileName, -1,
1483 data->cFileName, sizeof(data->cFileName), NULL, NULL );
1484 WideCharToMultiByte( CP_ACP, 0, dataW.cAlternateFileName, -1,
1485 data->cAlternateFileName,
1486 sizeof(data->cAlternateFileName), NULL, NULL );
1487 return TRUE;
1490 /*************************************************************************
1491 * FindClose (KERNEL32.@)
1493 BOOL WINAPI FindClose( HANDLE handle )
1495 FIND_FIRST_INFO *info = (FIND_FIRST_INFO*) handle;
1497 if (handle == INVALID_HANDLE_VALUE) goto error;
1499 __TRY
1501 RtlEnterCriticalSection( &info->cs );
1502 if (info)
1504 if (info->u.dos_dir) DOSFS_CloseDir( info->u.dos_dir );
1505 if (info->path) HeapFree( GetProcessHeap(), 0, info->path );
1506 if (info->long_mask) HeapFree( GetProcessHeap(), 0, info->long_mask );
1509 __EXCEPT(page_fault)
1511 WARN("Illegal handle %p\n", handle);
1512 SetLastError( ERROR_INVALID_HANDLE );
1513 return FALSE;
1515 __ENDTRY
1516 if (!info) goto error;
1517 RtlLeaveCriticalSection( &info->cs );
1518 RtlDeleteCriticalSection( &info->cs );
1519 HeapFree(GetProcessHeap(), 0, info);
1520 return TRUE;
1522 error:
1523 SetLastError( ERROR_INVALID_HANDLE );
1524 return FALSE;
1527 /***********************************************************************
1528 * MulDiv (KERNEL32.@)
1529 * RETURNS
1530 * Result of multiplication and division
1531 * -1: Overflow occurred or Divisor was 0
1533 INT WINAPI MulDiv(
1534 INT nMultiplicand,
1535 INT nMultiplier,
1536 INT nDivisor)
1538 LONGLONG ret;
1540 if (!nDivisor) return -1;
1542 /* We want to deal with a positive divisor to simplify the logic. */
1543 if (nDivisor < 0)
1545 nMultiplicand = - nMultiplicand;
1546 nDivisor = -nDivisor;
1549 /* If the result is positive, we "add" to round. else, we subtract to round. */
1550 if ( ( (nMultiplicand < 0) && (nMultiplier < 0) ) ||
1551 ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
1552 ret = (((LONGLONG)nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
1553 else
1554 ret = (((LONGLONG)nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
1556 if ((ret > 2147483647) || (ret < -2147483647)) return -1;
1557 return ret;
1561 /***********************************************************************
1562 * DosDateTimeToFileTime (KERNEL32.@)
1564 BOOL WINAPI DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft)
1566 struct tm newtm;
1567 #ifndef HAVE_TIMEGM
1568 struct tm *gtm;
1569 time_t time1, time2;
1570 #endif
1572 newtm.tm_sec = (fattime & 0x1f) * 2;
1573 newtm.tm_min = (fattime >> 5) & 0x3f;
1574 newtm.tm_hour = (fattime >> 11);
1575 newtm.tm_mday = (fatdate & 0x1f);
1576 newtm.tm_mon = ((fatdate >> 5) & 0x0f) - 1;
1577 newtm.tm_year = (fatdate >> 9) + 80;
1578 #ifdef HAVE_TIMEGM
1579 RtlSecondsSince1970ToTime( timegm(&newtm), (LARGE_INTEGER *)ft );
1580 #else
1581 time1 = mktime(&newtm);
1582 gtm = gmtime(&time1);
1583 time2 = mktime(gtm);
1584 RtlSecondsSince1970ToTime( 2*time1-time2, (LARGE_INTEGER *)ft );
1585 #endif
1586 return TRUE;
1590 /***********************************************************************
1591 * FileTimeToDosDateTime (KERNEL32.@)
1593 BOOL WINAPI FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate,
1594 LPWORD fattime )
1596 LARGE_INTEGER li;
1597 ULONG t;
1598 time_t unixtime;
1599 struct tm* tm;
1601 li.u.LowPart = ft->dwLowDateTime;
1602 li.u.HighPart = ft->dwHighDateTime;
1603 RtlTimeToSecondsSince1970( &li, &t );
1604 unixtime = t;
1605 tm = gmtime( &unixtime );
1606 if (fattime)
1607 *fattime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
1608 if (fatdate)
1609 *fatdate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
1610 + tm->tm_mday;
1611 return TRUE;
1615 /***********************************************************************
1616 * QueryDosDeviceA (KERNEL32.@)
1618 * returns array of strings terminated by \0, terminated by \0
1620 DWORD WINAPI QueryDosDeviceA(LPCSTR devname,LPSTR target,DWORD bufsize)
1622 DWORD ret = 0, retW;
1623 LPWSTR targetW = (LPWSTR)HeapAlloc(GetProcessHeap(),0,
1624 bufsize * sizeof(WCHAR));
1625 UNICODE_STRING devnameW;
1627 if(devname) RtlCreateUnicodeStringFromAsciiz(&devnameW, devname);
1628 else devnameW.Buffer = NULL;
1630 retW = QueryDosDeviceW(devnameW.Buffer, targetW, bufsize);
1632 ret = WideCharToMultiByte(CP_ACP, 0, targetW, retW, target,
1633 bufsize, NULL, NULL);
1635 RtlFreeUnicodeString(&devnameW);
1636 if (targetW) HeapFree(GetProcessHeap(),0,targetW);
1637 return ret;
1641 /***********************************************************************
1642 * QueryDosDeviceW (KERNEL32.@)
1644 * returns array of strings terminated by \0, terminated by \0
1646 * FIXME
1647 * - Win9x returns for all calls ERROR_INVALID_PARAMETER
1648 * - the returned devices for devname == NULL is far from complete
1649 * - its not checked that the returned device exist
1651 DWORD WINAPI QueryDosDeviceW(LPCWSTR devname,LPWSTR target,DWORD bufsize)
1653 const WCHAR *pDev, *pName, *pNum = NULL;
1654 int numsiz=0;
1655 DWORD ret;
1657 TRACE("(%s,...)\n", debugstr_w(devname));
1658 if (!devname) {
1659 /* return known MSDOS devices */
1660 DWORD ret = 0;
1661 int i;
1662 static const WCHAR devices[][5] = {{'A','U','X',0},
1663 {'C','O','M','1',0},
1664 {'C','O','M','2',0},
1665 {'L','P','T','1',0},
1666 {'N','U','L',0,}};
1667 for(i=0; (i< (sizeof(devices)/sizeof(devices[0]))); i++) {
1668 DWORD len = strlenW(devices[i]);
1669 if(target && (bufsize >= ret + len + 2)) {
1670 strcpyW(target+ret, devices[i]);
1671 ret += len + 1;
1672 } else {
1673 /* in this case WinXP returns 0 */
1674 FIXME("function return is wrong for WinXP!\n");
1675 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1676 break;
1679 /* append drives here */
1680 if(target && bufsize > 0) target[ret++] = 0;
1681 FIXME("Returned list is not complete\n");
1682 return ret;
1684 /* In theory all that are possible and have been defined.
1685 * Now just those below, since mirc uses it to check for special files.
1687 * (It is more complex, and supports netmounted stuff, and \\.\ stuff,
1688 * but currently we just ignore that.)
1690 if (!strcmpiW(devname, auxW)) {
1691 pDev = dosW;
1692 pName = comW;
1693 numsiz = 1;
1694 pNum = oneW;
1695 } else if (!strcmpiW(devname, nulW)) {
1696 pDev = devW;
1697 pName = nullW;
1698 } else if (!strncmpiW(devname, comW, strlenW(comW))) {
1699 pDev = devW;
1700 pName = serW;
1701 pNum = devname + strlenW(comW);
1702 for(numsiz=0; isdigitW(*(pNum+numsiz)); numsiz++);
1703 if(*(pNum + numsiz)) {
1704 SetLastError(ERROR_FILE_NOT_FOUND);
1705 return 0;
1707 } else if (!strncmpiW(devname, lptW, strlenW(lptW))) {
1708 pDev = devW;
1709 pName = parW;
1710 pNum = devname + strlenW(lptW);
1711 for(numsiz=0; isdigitW(*(pNum+numsiz)); numsiz++);
1712 if(*(pNum + numsiz)) {
1713 SetLastError(ERROR_FILE_NOT_FOUND);
1714 return 0;
1716 } else {
1717 /* This might be a DOS device we do not handle yet ... */
1718 FIXME("(%s) not detected as DOS device!\n",debugstr_w(devname));
1720 /* Win9x set the error ERROR_INVALID_PARAMETER */
1721 SetLastError(ERROR_FILE_NOT_FOUND);
1722 return 0;
1724 FIXME("device %s may not exist on this computer\n", debugstr_w(devname));
1726 ret = strlenW(pDev) + strlenW(pName) + numsiz + 2;
1727 if (ret > bufsize) ret = 0;
1728 if (target && ret) {
1729 strcpyW(target,pDev);
1730 strcatW(target,pName);
1731 if (pNum) strcatW(target,pNum);
1732 target[ret-1] = 0;
1734 return ret;