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