- Save more registers (exception, float...) if they are defined.
[wine/wine64.git] / files / dos_fs.c
blobfb9fb91304167a8bbdeb3a3f7566a15ca49bd2e9
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 /* To avoid blocking on non-directories in DOSFS_OpenDir_VFAT*/
79 #ifndef O_DIRECTORY
80 # define O_DIRECTORY 0200000 /* must be directory */
81 #endif
83 #else /* linux */
84 #undef VFAT_IOCTL_READDIR_BOTH /* just in case... */
85 #endif /* linux */
87 /* Chars we don't want to see in DOS file names */
88 #define INVALID_DOS_CHARS "*?<>|\"+=,;[] \345"
90 static const DOS_DEVICE DOSFS_Devices[] =
91 /* name, device flags (see Int 21/AX=0x4400) */
93 { {'C','O','N',0}, 0xc0d3 },
94 { {'P','R','N',0}, 0xa0c0 },
95 { {'N','U','L',0}, 0x80c4 },
96 { {'A','U','X',0}, 0x80c0 },
97 { {'L','P','T','1',0}, 0xa0c0 },
98 { {'L','P','T','2',0}, 0xa0c0 },
99 { {'L','P','T','3',0}, 0xa0c0 },
100 { {'L','P','T','4',0}, 0xc0d3 },
101 { {'C','O','M','1',0}, 0x80c0 },
102 { {'C','O','M','2',0}, 0x80c0 },
103 { {'C','O','M','3',0}, 0x80c0 },
104 { {'C','O','M','4',0}, 0x80c0 },
105 { {'S','C','S','I','M','G','R','$',0}, 0xc0c0 },
106 { {'H','P','S','C','A','N',0}, 0xc0c0 },
107 { {'E','M','M','X','X','X','X','0',0}, 0x0000 }
111 * Directory info for DOSFS_ReadDir
112 * contains the names of *all* the files in the directory
114 typedef struct
116 int used;
117 int size;
118 char names[1];
119 } DOS_DIR;
121 /* Info structure for FindFirstFile handle */
122 typedef struct
124 char *path; /* unix path */
125 LPWSTR long_mask;
126 LPWSTR short_mask;
127 BYTE attr;
128 int drive;
129 int cur_pos;
130 union
132 DOS_DIR *dos_dir;
133 SMB_DIR *smb_dir;
134 } u;
135 } FIND_FIRST_INFO;
138 static WINE_EXCEPTION_FILTER(page_fault)
140 if (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION)
141 return EXCEPTION_EXECUTE_HANDLER;
142 return EXCEPTION_CONTINUE_SEARCH;
146 /***********************************************************************
147 * DOSFS_ValidDOSName
149 * Return 1 if Unix file 'name' is also a valid MS-DOS name
150 * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
151 * File name can be terminated by '\0', '\\' or '/'.
153 static int DOSFS_ValidDOSName( LPCWSTR name, int ignore_case )
155 static const char invalid_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS;
156 const WCHAR *p = name;
157 const char *invalid = ignore_case ? (invalid_chars + 26) : invalid_chars;
158 int len = 0;
160 if (*p == '.')
162 /* Check for "." and ".." */
163 p++;
164 if (*p == '.') p++;
165 /* All other names beginning with '.' are invalid */
166 return (IS_END_OF_NAME(*p));
168 while (!IS_END_OF_NAME(*p))
170 if (*p < 256 && strchr( invalid, (char)*p )) return 0; /* Invalid char */
171 if (*p == '.') break; /* Start of the extension */
172 if (++len > 8) return 0; /* Name too long */
173 p++;
175 if (*p != '.') return 1; /* End of name */
176 p++;
177 if (IS_END_OF_NAME(*p)) return 0; /* Empty extension not allowed */
178 len = 0;
179 while (!IS_END_OF_NAME(*p))
181 if (*p < 256 && strchr( invalid, (char)*p )) return 0; /* Invalid char */
182 if (*p == '.') return 0; /* Second extension not allowed */
183 if (++len > 3) return 0; /* Extension too long */
184 p++;
186 return 1;
190 /***********************************************************************
191 * DOSFS_ToDosFCBFormat
193 * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
194 * expanding wild cards and converting to upper-case in the process.
195 * File name can be terminated by '\0', '\\' or '/'.
196 * Return FALSE if the name is not a valid DOS name.
197 * 'buffer' must be at least 12 characters long.
199 BOOL DOSFS_ToDosFCBFormat( LPCWSTR name, LPWSTR buffer )
201 static const char invalid_chars[] = INVALID_DOS_CHARS;
202 LPCWSTR p = name;
203 int i;
205 TRACE("(%s, %p)\n", debugstr_w(name), buffer);
207 /* Check for "." and ".." */
208 if (*p == '.')
210 p++;
211 buffer[0] = '.';
212 for(i = 1; i < 11; i++) buffer[i] = ' ';
213 buffer[11] = 0;
214 if (*p == '.')
216 buffer[1] = '.';
217 p++;
219 return (!*p || (*p == '/') || (*p == '\\'));
222 for (i = 0; i < 8; i++)
224 switch(*p)
226 case '\0':
227 case '\\':
228 case '/':
229 case '.':
230 buffer[i] = ' ';
231 break;
232 case '?':
233 p++;
234 /* fall through */
235 case '*':
236 buffer[i] = '?';
237 break;
238 default:
239 if (*p < 256 && strchr( invalid_chars, (char)*p )) return FALSE;
240 buffer[i] = toupperW(*p);
241 p++;
242 break;
246 if (*p == '*')
248 /* Skip all chars after wildcard up to first dot */
249 while (*p && (*p != '/') && (*p != '\\') && (*p != '.')) p++;
251 else
253 /* Check if name too long */
254 if (*p && (*p != '/') && (*p != '\\') && (*p != '.')) return FALSE;
256 if (*p == '.') p++; /* Skip dot */
258 for (i = 8; i < 11; i++)
260 switch(*p)
262 case '\0':
263 case '\\':
264 case '/':
265 buffer[i] = ' ';
266 break;
267 case '.':
268 return FALSE; /* Second extension not allowed */
269 case '?':
270 p++;
271 /* fall through */
272 case '*':
273 buffer[i] = '?';
274 break;
275 default:
276 if (*p < 256 && strchr( invalid_chars, (char)*p )) return FALSE;
277 buffer[i] = toupperW(*p);
278 p++;
279 break;
282 buffer[11] = '\0';
284 /* at most 3 character of the extension are processed
285 * is something behind this ?
287 while (*p == '*' || *p == ' ') p++; /* skip wildcards and spaces */
288 return IS_END_OF_NAME(*p);
292 /***********************************************************************
293 * DOSFS_ToDosDTAFormat
295 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
296 * converting to upper-case in the process.
297 * File name can be terminated by '\0', '\\' or '/'.
298 * 'buffer' must be at least 13 characters long.
300 static void DOSFS_ToDosDTAFormat( LPCWSTR name, LPWSTR buffer )
302 LPWSTR p;
304 memcpy( buffer, name, 8 * sizeof(WCHAR) );
305 p = buffer + 8;
306 while ((p > buffer) && (p[-1] == ' ')) p--;
307 *p++ = '.';
308 memcpy( p, name + 8, 3 * sizeof(WCHAR) );
309 p += 3;
310 while (p[-1] == ' ') p--;
311 if (p[-1] == '.') p--;
312 *p = '\0';
316 /***********************************************************************
317 * DOSFS_MatchShort
319 * Check a DOS file name against a mask (both in FCB format).
321 static int DOSFS_MatchShort( LPCWSTR mask, LPCWSTR name )
323 int i;
324 for (i = 11; i > 0; i--, mask++, name++)
325 if ((*mask != '?') && (*mask != *name)) return 0;
326 return 1;
330 /***********************************************************************
331 * DOSFS_MatchLong
333 * Check a long file name against a mask.
335 * Tests (done in W95 DOS shell - case insensitive):
336 * *.txt test1.test.txt *
337 * *st1* test1.txt *
338 * *.t??????.t* test1.ta.tornado.txt *
339 * *tornado* test1.ta.tornado.txt *
340 * t*t test1.ta.tornado.txt *
341 * ?est* test1.txt *
342 * ?est??? test1.txt -
343 * *test1.txt* test1.txt *
344 * h?l?o*t.dat hellothisisatest.dat *
346 static int DOSFS_MatchLong( LPCWSTR mask, LPCWSTR name, int case_sensitive )
348 LPCWSTR lastjoker = NULL;
349 LPCWSTR next_to_retry = NULL;
350 static const WCHAR asterisk_dot_asterisk[] = {'*','.','*',0};
352 TRACE("(%s, %s, %x)\n", debugstr_w(mask), debugstr_w(name), case_sensitive);
354 if (!strcmpW( mask, asterisk_dot_asterisk )) return 1;
355 while (*name && *mask)
357 if (*mask == '*')
359 mask++;
360 while (*mask == '*') mask++; /* Skip consecutive '*' */
361 lastjoker = mask;
362 if (!*mask) return 1; /* end of mask is all '*', so match */
364 /* skip to the next match after the joker(s) */
365 if (case_sensitive) while (*name && (*name != *mask)) name++;
366 else while (*name && (toupperW(*name) != toupperW(*mask))) name++;
368 if (!*name) break;
369 next_to_retry = name;
371 else if (*mask != '?')
373 int mismatch = 0;
374 if (case_sensitive)
376 if (*mask != *name) mismatch = 1;
378 else
380 if (toupperW(*mask) != toupperW(*name)) mismatch = 1;
382 if (!mismatch)
384 mask++;
385 name++;
386 if (*mask == '\0')
388 if (*name == '\0')
389 return 1;
390 if (lastjoker)
391 mask = lastjoker;
394 else /* mismatch ! */
396 if (lastjoker) /* we had an '*', so we can try unlimitedly */
398 mask = lastjoker;
400 /* this scan sequence was a mismatch, so restart
401 * 1 char after the first char we checked last time */
402 next_to_retry++;
403 name = next_to_retry;
405 else
406 return 0; /* bad luck */
409 else /* '?' */
411 mask++;
412 name++;
415 while ((*mask == '.') || (*mask == '*'))
416 mask++; /* Ignore trailing '.' or '*' in mask */
417 return (!*name && !*mask);
421 /***********************************************************************
422 * DOSFS_AddDirEntry
424 * Used to construct an array of filenames in DOSFS_OpenDir
426 static BOOL DOSFS_AddDirEntry(DOS_DIR **dir, LPCWSTR name, LPCWSTR dosname)
428 int extra1 = (strlenW(name) + 1) * sizeof(WCHAR);
429 int extra2 = (strlenW(dosname) + 1) * sizeof(WCHAR);
431 /* if we need more, at minimum double the size */
432 if( (extra1 + extra2 + (*dir)->used) > (*dir)->size)
434 int more = (*dir)->size;
435 DOS_DIR *t;
437 if(more<(extra1+extra2))
438 more = extra1+extra2;
440 t = HeapReAlloc(GetProcessHeap(), 0, *dir, sizeof(**dir) + (*dir)->size + more );
441 if(!t)
443 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
444 ERR("Out of memory caching directory structure %d %d %d\n",
445 (*dir)->size, more, (*dir)->used);
446 return FALSE;
448 (*dir) = t;
449 (*dir)->size += more;
452 /* at this point, the dir structure is big enough to hold these names */
453 strcpyW((LPWSTR)&(*dir)->names[(*dir)->used], name);
454 (*dir)->used += extra1;
455 strcpyW((LPWSTR)&(*dir)->names[(*dir)->used], dosname);
456 (*dir)->used += extra2;
458 return TRUE;
462 /***********************************************************************
463 * DOSFS_OpenDir_VFAT
465 static BOOL DOSFS_OpenDir_VFAT(UINT codepage, DOS_DIR **dir, const char *unix_path)
467 #ifdef VFAT_IOCTL_READDIR_BOTH
468 KERNEL_DIRENT de[2];
469 int fd = open( unix_path, O_RDONLY|O_DIRECTORY );
470 BOOL r = TRUE;
472 /* Check if the VFAT ioctl is supported on this directory */
474 if ( fd<0 )
475 return FALSE;
477 while (1)
479 WCHAR long_name[MAX_PATH];
480 WCHAR short_name[12];
482 r = (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de ) != -1);
483 if(!r)
484 break;
485 if (!de[0].d_reclen)
486 break;
487 MultiByteToWideChar(codepage, 0, de[0].d_name, -1, long_name, MAX_PATH);
488 if (!DOSFS_ToDosFCBFormat( long_name, short_name ))
489 short_name[0] = '\0';
490 if (de[1].d_name[0])
491 MultiByteToWideChar(codepage, 0, de[1].d_name, -1, long_name, MAX_PATH);
492 else
493 MultiByteToWideChar(codepage, 0, de[0].d_name, -1, long_name, MAX_PATH);
494 r = DOSFS_AddDirEntry(dir, long_name, short_name );
495 if(!r)
496 break;
498 if(r)
500 static const WCHAR empty_strW[] = { 0 };
501 DOSFS_AddDirEntry(dir, empty_strW, empty_strW);
503 close(fd);
504 return r;
505 #else
506 return FALSE;
507 #endif /* VFAT_IOCTL_READDIR_BOTH */
511 /***********************************************************************
512 * DOSFS_OpenDir_Normal
514 * Now use the standard opendir/readdir interface
516 static BOOL DOSFS_OpenDir_Normal( UINT codepage, DOS_DIR **dir, const char *unix_path )
518 DIR *unixdir = opendir( unix_path );
519 BOOL r = TRUE;
520 static const WCHAR empty_strW[] = { 0 };
522 if(!unixdir)
523 return FALSE;
524 while(1)
526 WCHAR long_name[MAX_PATH];
527 struct dirent *de = readdir(unixdir);
529 if(!de)
530 break;
531 MultiByteToWideChar(codepage, 0, de->d_name, -1, long_name, MAX_PATH);
532 r = DOSFS_AddDirEntry(dir, long_name, empty_strW);
533 if(!r)
534 break;
536 if(r)
537 DOSFS_AddDirEntry(dir, empty_strW, empty_strW);
538 closedir(unixdir);
539 return r;
542 /***********************************************************************
543 * DOSFS_OpenDir
545 static DOS_DIR *DOSFS_OpenDir( UINT codepage, const char *unix_path )
547 const int init_size = 0x100;
548 DOS_DIR *dir = HeapAlloc( GetProcessHeap(), 0, sizeof(*dir) + init_size);
549 BOOL r;
551 TRACE("%s\n",debugstr_a(unix_path));
553 if (!dir)
555 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
556 return NULL;
558 dir->used = 0;
559 dir->size = init_size;
561 /* Treat empty path as root directory. This simplifies path split into
562 directory and mask in several other places */
563 if (!*unix_path) unix_path = "/";
565 r = DOSFS_OpenDir_VFAT( codepage, &dir, unix_path);
567 if(!r)
568 r = DOSFS_OpenDir_Normal( codepage, &dir, unix_path);
570 if(!r)
572 HeapFree(GetProcessHeap(), 0, dir);
573 return NULL;
575 dir->used = 0;
577 return dir;
581 /***********************************************************************
582 * DOSFS_CloseDir
584 static void DOSFS_CloseDir( DOS_DIR *dir )
586 HeapFree( GetProcessHeap(), 0, dir );
590 /***********************************************************************
591 * DOSFS_ReadDir
593 static BOOL DOSFS_ReadDir( DOS_DIR *dir, LPCWSTR *long_name,
594 LPCWSTR *short_name )
596 LPCWSTR sn, ln;
598 if (!dir)
599 return FALSE;
601 /* the long pathname is first */
602 ln = (LPCWSTR)&dir->names[dir->used];
603 if(ln[0])
604 *long_name = ln;
605 else
606 return FALSE;
607 dir->used += (strlenW(ln) + 1) * sizeof(WCHAR);
609 /* followed by the short path name */
610 sn = (LPCWSTR)&dir->names[dir->used];
611 if(sn[0])
612 *short_name = sn;
613 else
614 *short_name = NULL;
615 dir->used += (strlenW(sn) + 1) * sizeof(WCHAR);
617 TRACE("Read: long_name: %s, short_name: %s\n",
618 debugstr_w(*long_name), debugstr_w(*short_name));
620 return TRUE;
624 /***********************************************************************
625 * DOSFS_Hash
627 * Transform a Unix file name into a hashed DOS name. If the name is a valid
628 * DOS name, it is converted to upper-case; otherwise it is replaced by a
629 * hashed version that fits in 8.3 format.
630 * File name can be terminated by '\0', '\\' or '/'.
631 * 'buffer' must be at least 13 characters long.
633 static void DOSFS_Hash( LPCWSTR name, LPWSTR buffer, BOOL dir_format,
634 BOOL ignore_case )
636 static const char invalid_chars[] = INVALID_DOS_CHARS "~.";
637 static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
639 LPCWSTR p, ext;
640 LPWSTR dst;
641 unsigned short hash;
642 int i;
644 if (dir_format)
646 for(i = 0; i < 11; i++) buffer[i] = ' ';
647 buffer[11] = 0;
650 if (DOSFS_ValidDOSName( name, ignore_case ))
652 /* Check for '.' and '..' */
653 if (*name == '.')
655 buffer[0] = '.';
656 if (!dir_format) buffer[1] = buffer[2] = '\0';
657 if (name[1] == '.') buffer[1] = '.';
658 return;
661 /* Simply copy the name, converting to uppercase */
663 for (dst = buffer; !IS_END_OF_NAME(*name) && (*name != '.'); name++)
664 *dst++ = toupperW(*name);
665 if (*name == '.')
667 if (dir_format) dst = buffer + 8;
668 else *dst++ = '.';
669 for (name++; !IS_END_OF_NAME(*name); name++)
670 *dst++ = toupperW(*name);
672 if (!dir_format) *dst = '\0';
673 return;
676 /* Compute the hash code of the file name */
677 /* If you know something about hash functions, feel free to */
678 /* insert a better algorithm here... */
679 if (ignore_case)
681 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
682 hash = (hash<<3) ^ (hash>>5) ^ tolowerW(*p) ^ (tolowerW(p[1]) << 8);
683 hash = (hash<<3) ^ (hash>>5) ^ tolowerW(*p); /* Last character */
685 else
687 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
688 hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
689 hash = (hash << 3) ^ (hash >> 5) ^ *p; /* Last character */
692 /* Find last dot for start of the extension */
693 for (p = name+1, ext = NULL; !IS_END_OF_NAME(*p); p++)
694 if (*p == '.') ext = p;
695 if (ext && IS_END_OF_NAME(ext[1]))
696 ext = NULL; /* Empty extension ignored */
698 /* Copy first 4 chars, replacing invalid chars with '_' */
699 for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
701 if (IS_END_OF_NAME(*p) || (p == ext)) break;
702 *dst++ = (*p < 256 && strchr( invalid_chars, (char)*p )) ? '_' : toupperW(*p);
704 /* Pad to 5 chars with '~' */
705 while (i-- >= 0) *dst++ = '~';
707 /* Insert hash code converted to 3 ASCII chars */
708 *dst++ = hash_chars[(hash >> 10) & 0x1f];
709 *dst++ = hash_chars[(hash >> 5) & 0x1f];
710 *dst++ = hash_chars[hash & 0x1f];
712 /* Copy the first 3 chars of the extension (if any) */
713 if (ext)
715 if (!dir_format) *dst++ = '.';
716 for (i = 3, ext++; (i > 0) && !IS_END_OF_NAME(*ext); i--, ext++)
717 *dst++ = (*ext < 256 && strchr( invalid_chars, (char)*ext )) ? '_' : toupperW(*ext);
719 if (!dir_format) *dst = '\0';
723 /***********************************************************************
724 * DOSFS_FindUnixName
726 * Find the Unix file name in a given directory that corresponds to
727 * a file name (either in Unix or DOS format).
728 * File name can be terminated by '\0', '\\' or '/'.
729 * Return TRUE if OK, FALSE if no file name matches.
731 * 'long_buf' must be at least 'long_len' characters long. If the long name
732 * turns out to be larger than that, the function returns FALSE.
733 * 'short_buf' must be at least 13 characters long.
735 BOOL DOSFS_FindUnixName( const DOS_FULL_NAME *path, LPCWSTR name, char *long_buf,
736 INT long_len, LPWSTR short_buf, BOOL ignore_case)
738 DOS_DIR *dir;
739 LPCWSTR long_name, short_name;
740 WCHAR dos_name[12], tmp_buf[13];
741 BOOL ret;
743 LPCWSTR p = strchrW( name, '/' );
744 int len = p ? (int)(p - name) : strlenW(name);
745 if ((p = strchrW( name, '\\' ))) len = min( (int)(p - name), len );
746 /* Ignore trailing dots and spaces */
747 while (len > 1 && (name[len-1] == '.' || name[len-1] == ' ')) len--;
748 if (long_len < len + 1) return FALSE;
750 TRACE("%s,%s\n", path->long_name, debugstr_w(name) );
752 if (!DOSFS_ToDosFCBFormat( name, dos_name )) dos_name[0] = '\0';
754 if (!(dir = DOSFS_OpenDir( DRIVE_GetCodepage(path->drive), path->long_name )))
756 WARN("(%s,%s): can't open dir: %s\n",
757 path->long_name, debugstr_w(name), strerror(errno) );
758 return FALSE;
761 while ((ret = DOSFS_ReadDir( dir, &long_name, &short_name )))
763 /* Check against Unix name */
764 if (len == strlenW(long_name))
766 if (!ignore_case)
768 if (!strncmpW( long_name, name, len )) break;
770 else
772 if (!strncmpiW( long_name, name, len )) break;
775 if (dos_name[0])
777 /* Check against hashed DOS name */
778 if (!short_name)
780 DOSFS_Hash( long_name, tmp_buf, TRUE, ignore_case );
781 short_name = tmp_buf;
783 if (!strcmpW( dos_name, short_name )) break;
786 if (ret)
788 if (long_buf) WideCharToMultiByte(DRIVE_GetCodepage(path->drive), 0,
789 long_name, -1, long_buf, long_len, NULL, NULL);
790 if (short_buf)
792 if (short_name)
793 DOSFS_ToDosDTAFormat( short_name, short_buf );
794 else
795 DOSFS_Hash( long_name, short_buf, FALSE, ignore_case );
797 TRACE("(%s,%s) -> %s (%s)\n", path->long_name, debugstr_w(name),
798 debugstr_w(long_name), short_buf ? debugstr_w(short_buf) : "***");
800 else
801 WARN("%s not found in '%s'\n", debugstr_w(name), path->long_name);
802 DOSFS_CloseDir( dir );
803 return ret;
807 /***********************************************************************
808 * DOSFS_GetDevice
810 * Check if a DOS file name represents a DOS device and return the device.
812 const DOS_DEVICE *DOSFS_GetDevice( LPCWSTR name )
814 unsigned int i;
815 const WCHAR *p;
817 if (!name) return NULL; /* if FILE_DupUnixHandle was used */
818 if (name[0] && (name[1] == ':')) name += 2;
819 if ((p = strrchrW( name, '/' ))) name = p + 1;
820 if ((p = strrchrW( name, '\\' ))) name = p + 1;
821 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
823 const WCHAR *dev = DOSFS_Devices[i].name;
824 if (!strncmpiW( dev, name, strlenW(dev) ))
826 p = name + strlenW( dev );
827 if (!*p || (*p == '.') || (*p == ':')) return &DOSFS_Devices[i];
830 return NULL;
834 /***********************************************************************
835 * DOSFS_GetDeviceByHandle
837 const DOS_DEVICE *DOSFS_GetDeviceByHandle( HANDLE hFile )
839 const DOS_DEVICE *ret = NULL;
840 SERVER_START_REQ( get_device_id )
842 req->handle = hFile;
843 if (!wine_server_call( req ))
845 if ((reply->id >= 0) &&
846 (reply->id < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0])))
847 ret = &DOSFS_Devices[reply->id];
850 SERVER_END_REQ;
851 return ret;
855 /**************************************************************************
856 * DOSFS_CreateCommPort
858 static HANDLE DOSFS_CreateCommPort(LPCWSTR name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa)
860 HANDLE ret;
861 char devname[40];
862 WCHAR devnameW[40];
863 static const WCHAR serialportsW[] = {'s','e','r','i','a','l','p','o','r','t','s',0};
864 static const WCHAR empty_strW[] = { 0 };
866 TRACE_(file)("%s %lx %lx\n", debugstr_w(name), access, attributes);
868 PROFILE_GetWineIniString(serialportsW, name, empty_strW, devnameW, 40);
869 if(!devnameW[0])
870 return 0;
872 WideCharToMultiByte(CP_ACP, 0, devnameW, -1, devname, sizeof(devname), NULL, NULL);
874 TRACE("opening %s as %s\n", devname, debugstr_w(name));
876 SERVER_START_REQ( create_serial )
878 req->access = access;
879 req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
880 req->attributes = attributes;
881 req->sharing = FILE_SHARE_READ|FILE_SHARE_WRITE;
882 wine_server_add_data( req, devname, strlen(devname) );
883 SetLastError(0);
884 wine_server_call_err( req );
885 ret = reply->handle;
887 SERVER_END_REQ;
889 if(!ret)
890 ERR("Couldn't open device '%s' ! (check permissions)\n",devname);
891 else
892 TRACE("return %p\n", ret );
893 return ret;
896 /***********************************************************************
897 * DOSFS_OpenDevice
899 * Open a DOS device. This might not map 1:1 into the UNIX device concept.
900 * Returns 0 on failure.
902 HANDLE DOSFS_OpenDevice( LPCWSTR name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa )
904 unsigned int i;
905 const WCHAR *p;
906 HANDLE handle;
908 if (name[0] && (name[1] == ':')) name += 2;
909 if ((p = strrchrW( name, '/' ))) name = p + 1;
910 if ((p = strrchrW( name, '\\' ))) name = p + 1;
911 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
913 const WCHAR *dev = DOSFS_Devices[i].name;
914 if (!strncmpiW( dev, name, strlenW(dev) ))
916 p = name + strlenW( dev );
917 if (!*p || (*p == '.') || (*p == ':')) {
918 static const WCHAR nulW[] = {'N','U','L',0};
919 static const WCHAR conW[] = {'C','O','N',0};
920 static const WCHAR scsimgrW[] = {'S','C','S','I','M','G','R','$',0};
921 static const WCHAR hpscanW[] = {'H','P','S','C','A','N',0};
922 static const WCHAR emmxxxx0W[] = {'E','M','M','X','X','X','X','0',0};
923 /* got it */
924 if (!strcmpiW(DOSFS_Devices[i].name, nulW))
925 return FILE_CreateFile( "/dev/null", access,
926 FILE_SHARE_READ|FILE_SHARE_WRITE, sa,
927 OPEN_EXISTING, 0, 0, TRUE, DRIVE_UNKNOWN );
928 if (!strcmpiW(DOSFS_Devices[i].name, conW)) {
929 HANDLE to_dup;
930 switch (access & (GENERIC_READ|GENERIC_WRITE)) {
931 case GENERIC_READ:
932 to_dup = GetStdHandle( STD_INPUT_HANDLE );
933 break;
934 case GENERIC_WRITE:
935 to_dup = GetStdHandle( STD_OUTPUT_HANDLE );
936 break;
937 default:
938 FIXME("can't open CON read/write\n");
939 return 0;
941 if (!DuplicateHandle( GetCurrentProcess(), to_dup, GetCurrentProcess(),
942 &handle, 0,
943 sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle,
944 DUPLICATE_SAME_ACCESS ))
945 handle = 0;
946 return handle;
948 if (!strcmpiW(DOSFS_Devices[i].name, scsimgrW) ||
949 !strcmpiW(DOSFS_Devices[i].name, hpscanW) ||
950 !strcmpiW(DOSFS_Devices[i].name, emmxxxx0W))
952 return FILE_CreateDevice( i, access, sa );
955 if( (handle=DOSFS_CreateCommPort(DOSFS_Devices[i].name,access,attributes,sa)) )
956 return handle;
957 FIXME("device open %s not supported (yet)\n", debugstr_w(DOSFS_Devices[i].name));
958 return 0;
962 return 0;
966 /***********************************************************************
967 * DOSFS_GetPathDrive
969 * Get the drive specified by a given path name (DOS or Unix format).
971 static int DOSFS_GetPathDrive( LPCWSTR *name )
973 int drive;
974 LPCWSTR p = *name;
976 if (*p && (p[1] == ':'))
978 drive = toupperW(*p) - 'A';
979 *name += 2;
981 else if (*p == '/') /* Absolute Unix path? */
983 if ((drive = DRIVE_FindDriveRootW( name )) == -1)
985 MESSAGE("Warning: %s not accessible from a configured DOS drive\n", debugstr_w(*name) );
986 /* Assume it really was a DOS name */
987 drive = DRIVE_GetCurrentDrive();
990 else drive = DRIVE_GetCurrentDrive();
992 if (!DRIVE_IsValid(drive))
994 SetLastError( ERROR_INVALID_DRIVE );
995 return -1;
997 return drive;
1001 /***********************************************************************
1002 * DOSFS_GetFullName
1004 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
1005 * Unix name / short DOS name pair.
1006 * Return FALSE if one of the path components does not exist. The last path
1007 * component is only checked if 'check_last' is non-zero.
1008 * The buffers pointed to by 'long_buf' and 'short_buf' must be
1009 * at least MAX_PATHNAME_LEN long.
1011 BOOL DOSFS_GetFullName( LPCWSTR name, BOOL check_last, DOS_FULL_NAME *full )
1013 BOOL found;
1014 UINT flags, codepage;
1015 char *p_l, *root;
1016 LPWSTR p_s;
1017 static const WCHAR driveA_rootW[] = {'A',':','\\',0};
1018 static const WCHAR dos_rootW[] = {'\\',0};
1020 TRACE("%s (last=%d)\n", debugstr_w(name), check_last );
1022 if ((!*name) || (*name=='\n'))
1023 { /* error code for Win98 */
1024 SetLastError(ERROR_BAD_PATHNAME);
1025 return FALSE;
1028 if ((full->drive = DOSFS_GetPathDrive( &name )) == -1) return FALSE;
1029 flags = DRIVE_GetFlags( full->drive );
1030 codepage = DRIVE_GetCodepage(full->drive);
1032 lstrcpynA( full->long_name, DRIVE_GetRoot( full->drive ),
1033 sizeof(full->long_name) );
1034 if (full->long_name[1]) root = full->long_name + strlen(full->long_name);
1035 else root = full->long_name; /* root directory */
1037 strcpyW( full->short_name, driveA_rootW );
1038 full->short_name[0] += full->drive;
1040 if ((*name == '\\') || (*name == '/')) /* Absolute path */
1042 while ((*name == '\\') || (*name == '/')) name++;
1044 else /* Relative path */
1046 lstrcpynA( root + 1, DRIVE_GetUnixCwd( full->drive ),
1047 sizeof(full->long_name) - (root - full->long_name) - 1 );
1048 if (root[1]) *root = '/';
1049 lstrcpynW( full->short_name + 3, DRIVE_GetDosCwd( full->drive ),
1050 sizeof(full->short_name)/sizeof(full->short_name[0]) - 3 );
1053 p_l = full->long_name[1] ? full->long_name + strlen(full->long_name)
1054 : full->long_name;
1055 p_s = full->short_name[3] ? full->short_name + strlenW(full->short_name)
1056 : full->short_name + 2;
1057 found = TRUE;
1059 while (*name && found)
1061 /* Check for '.' and '..' */
1063 if (*name == '.')
1065 if (IS_END_OF_NAME(name[1]))
1067 name++;
1068 while ((*name == '\\') || (*name == '/')) name++;
1069 continue;
1071 else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
1073 name += 2;
1074 while ((*name == '\\') || (*name == '/')) name++;
1075 while ((p_l > root) && (*p_l != '/')) p_l--;
1076 while ((p_s > full->short_name + 2) && (*p_s != '\\')) p_s--;
1077 *p_l = *p_s = '\0'; /* Remove trailing separator */
1078 continue;
1082 /* Make sure buffers are large enough */
1084 if ((p_s >= full->short_name + sizeof(full->short_name)/sizeof(full->short_name[0]) - 14) ||
1085 (p_l >= full->long_name + sizeof(full->long_name) - 1))
1087 SetLastError( ERROR_PATH_NOT_FOUND );
1088 return FALSE;
1091 /* Get the long and short name matching the file name */
1093 if ((found = DOSFS_FindUnixName( full, name, p_l + 1,
1094 sizeof(full->long_name) - (p_l - full->long_name) - 1,
1095 p_s + 1, !(flags & DRIVE_CASE_SENSITIVE) )))
1097 *p_l++ = '/';
1098 p_l += strlen(p_l);
1099 *p_s++ = '\\';
1100 p_s += strlenW(p_s);
1101 while (!IS_END_OF_NAME(*name)) name++;
1103 else if (!check_last)
1105 *p_l++ = '/';
1106 *p_s++ = '\\';
1107 while (!IS_END_OF_NAME(*name) &&
1108 (p_s < full->short_name + sizeof(full->short_name)/sizeof(full->short_name[0]) - 1) &&
1109 (p_l < full->long_name + sizeof(full->long_name) - 1))
1111 WCHAR wch;
1112 *p_s++ = tolowerW(*name);
1113 /* If the drive is case-sensitive we want to create new */
1114 /* files in lower-case otherwise we can't reopen them */
1115 /* under the same short name. */
1116 if (flags & DRIVE_CASE_SENSITIVE) wch = tolowerW(*name);
1117 else wch = *name;
1118 p_l += WideCharToMultiByte(codepage, 0, &wch, 1, p_l, 2, NULL, NULL);
1119 name++;
1121 /* Ignore trailing dots and spaces */
1122 while(p_l[-1] == '.' || p_l[-1] == ' ') {
1123 --p_l;
1124 --p_s;
1126 *p_l = '\0';
1127 *p_s = '\0';
1129 while ((*name == '\\') || (*name == '/')) name++;
1132 if (!found)
1134 if (check_last)
1136 SetLastError( ERROR_FILE_NOT_FOUND );
1137 return FALSE;
1139 if (*name) /* Not last */
1141 SetLastError( ERROR_PATH_NOT_FOUND );
1142 return FALSE;
1145 if (!full->long_name[0]) strcpy( full->long_name, "/" );
1146 if (!full->short_name[2]) strcpyW( full->short_name + 2, dos_rootW );
1147 TRACE("returning %s = %s\n", full->long_name, debugstr_w(full->short_name) );
1148 return TRUE;
1152 /***********************************************************************
1153 * GetShortPathNameW (KERNEL32.@)
1155 * NOTES
1156 * observed:
1157 * longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1158 * longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0
1160 * more observations ( with NT 3.51 (WinDD) ):
1161 * longpath <= 8.3 -> just copy longpath to shortpath
1162 * longpath > 8.3 ->
1163 * a) file does not exist -> return 0, LastError = ERROR_FILE_NOT_FOUND
1164 * b) file does exist -> set the short filename.
1165 * - trailing slashes are reproduced in the short name, even if the
1166 * file is not a directory
1167 * - the absolute/relative path of the short name is reproduced like found
1168 * in the long name
1169 * - longpath and shortpath may have the same address
1170 * Peter Ganten, 1999
1172 DWORD WINAPI GetShortPathNameW( LPCWSTR longpath, LPWSTR shortpath, DWORD shortlen )
1174 DOS_FULL_NAME full_name;
1175 WCHAR tmpshortpath[MAX_PATHNAME_LEN];
1176 const WCHAR *p;
1177 DWORD sp = 0, lp = 0;
1178 int drive;
1179 DWORD tmplen;
1180 UINT flags;
1181 BOOL unixabsolute = *longpath == '/';
1183 TRACE("%s\n", debugstr_w(longpath));
1185 if (!longpath) {
1186 SetLastError(ERROR_INVALID_PARAMETER);
1187 return 0;
1189 if (!longpath[0]) {
1190 SetLastError(ERROR_BAD_PATHNAME);
1191 return 0;
1194 /* check for drive letter */
1195 if (!unixabsolute && longpath[1] == ':' ) {
1196 tmpshortpath[0] = longpath[0];
1197 tmpshortpath[1] = ':';
1198 sp = 2;
1201 if ( ( drive = DOSFS_GetPathDrive ( &longpath )) == -1 ) return 0;
1202 flags = DRIVE_GetFlags ( drive );
1204 if (unixabsolute && drive != DRIVE_GetCurrentDrive()) {
1205 tmpshortpath[0] = drive + 'A';
1206 tmpshortpath[1] = ':';
1207 sp = 2;
1210 while ( longpath[lp] ) {
1212 /* check for path delimiters and reproduce them */
1213 if ( longpath[lp] == '\\' || longpath[lp] == '/' ) {
1214 if (!sp || tmpshortpath[sp-1]!= '\\')
1216 /* strip double "\\" */
1217 tmpshortpath[sp] = '\\';
1218 sp++;
1220 tmpshortpath[sp]=0;/*terminate string*/
1221 lp++;
1222 continue;
1225 tmplen = 0;
1226 for(p = longpath + lp; *p && *p != '/' && *p != '\\'; p++)
1227 tmplen++;
1228 lstrcpynW(tmpshortpath + sp, longpath + lp, tmplen + 1);
1230 /* Check, if the current element is a valid dos name */
1231 if ( DOSFS_ValidDOSName ( longpath + lp, !(flags & DRIVE_CASE_SENSITIVE) ) ) {
1232 sp += tmplen;
1233 lp += tmplen;
1234 continue;
1237 /* Check if the file exists and use the existing file name */
1238 if ( DOSFS_GetFullName ( tmpshortpath, TRUE, &full_name ) ) {
1239 strcpyW(tmpshortpath + sp, strrchrW(full_name.short_name, '\\') + 1);
1240 sp += strlenW(tmpshortpath + sp);
1241 lp += tmplen;
1242 continue;
1245 TRACE("not found!\n" );
1246 SetLastError ( ERROR_FILE_NOT_FOUND );
1247 return 0;
1249 tmpshortpath[sp] = 0;
1251 tmplen = strlenW(tmpshortpath) + 1;
1252 if (tmplen <= shortlen)
1254 strcpyW(shortpath, tmpshortpath);
1255 TRACE("returning %s\n", debugstr_w(shortpath));
1256 tmplen--; /* length without 0 */
1259 return tmplen;
1263 /***********************************************************************
1264 * GetShortPathNameA (KERNEL32.@)
1266 DWORD WINAPI GetShortPathNameA( LPCSTR longpath, LPSTR shortpath, DWORD shortlen )
1268 UNICODE_STRING longpathW;
1269 WCHAR shortpathW[MAX_PATH];
1270 DWORD ret, retW;
1272 if (!longpath)
1274 SetLastError(ERROR_INVALID_PARAMETER);
1275 return 0;
1278 TRACE("%s\n", debugstr_a(longpath));
1280 if (!RtlCreateUnicodeStringFromAsciiz(&longpathW, longpath))
1282 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1283 return 0;
1286 retW = GetShortPathNameW(longpathW.Buffer, shortpathW, MAX_PATH);
1288 if (!retW)
1289 ret = 0;
1290 else if (retW > MAX_PATH)
1292 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1293 ret = 0;
1295 else
1297 ret = WideCharToMultiByte(CP_ACP, 0, shortpathW, -1, NULL, 0, NULL, NULL);
1298 if (ret <= shortlen)
1300 WideCharToMultiByte(CP_ACP, 0, shortpathW, -1, shortpath, shortlen, NULL, NULL);
1301 ret--; /* length without 0 */
1305 RtlFreeUnicodeString(&longpathW);
1306 return ret;
1310 /***********************************************************************
1311 * GetLongPathNameW (KERNEL32.@)
1313 * NOTES
1314 * observed (Win2000):
1315 * shortpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1316 * shortpath="": LastError=ERROR_PATH_NOT_FOUND, ret=0
1318 DWORD WINAPI GetLongPathNameW( LPCWSTR shortpath, LPWSTR longpath, DWORD longlen )
1320 DOS_FULL_NAME full_name;
1321 const char *root;
1322 LPWSTR p;
1323 int drive;
1324 UINT codepage;
1325 DWORD ret, len = 0;
1327 if (!shortpath) {
1328 SetLastError(ERROR_INVALID_PARAMETER);
1329 return 0;
1331 if (!shortpath[0]) {
1332 SetLastError(ERROR_PATH_NOT_FOUND);
1333 return 0;
1336 TRACE("%s,%p,%ld\n", debugstr_w(shortpath), longpath, longlen);
1338 if(shortpath[0]=='\\' && shortpath[1]=='\\')
1340 ERR("UNC pathname %s\n",debugstr_w(shortpath));
1341 lstrcpynW( longpath, full_name.short_name, longlen );
1342 return strlenW(longpath);
1345 if (!DOSFS_GetFullName( shortpath, TRUE, &full_name )) return 0;
1347 root = full_name.long_name;
1348 drive = DRIVE_FindDriveRoot(&root);
1349 codepage = DRIVE_GetCodepage(drive);
1351 ret = MultiByteToWideChar(codepage, 0, root, -1, NULL, 0);
1352 ret += 3; /* A:\ */
1353 /* reproduce terminating slash */
1354 if (ret > 4) /* if not drive root */
1356 len = strlenW(shortpath);
1357 if (shortpath[len - 1] == '\\' || shortpath[len - 1] == '/')
1358 len = 1;
1360 ret += len;
1361 if (ret <= longlen)
1363 longpath[0] = 'A' + drive;
1364 longpath[1] = ':';
1365 MultiByteToWideChar(codepage, 0, root, -1, longpath + 2, longlen - 2);
1366 for (p = longpath; *p; p++) if (*p == '/') *p = '\\';
1367 if (len)
1369 longpath[ret - 2] = '\\';
1370 longpath[ret - 1] = 0;
1372 TRACE("returning %s\n", debugstr_w(longpath));
1373 ret--; /* length without 0 */
1375 return ret;
1379 /***********************************************************************
1380 * GetLongPathNameA (KERNEL32.@)
1382 DWORD WINAPI GetLongPathNameA( LPCSTR shortpath, LPSTR longpath, DWORD longlen )
1384 UNICODE_STRING shortpathW;
1385 WCHAR longpathW[MAX_PATH];
1386 DWORD ret, retW;
1388 if (!shortpath)
1390 SetLastError(ERROR_INVALID_PARAMETER);
1391 return 0;
1394 TRACE("%s\n", debugstr_a(shortpath));
1396 if (!RtlCreateUnicodeStringFromAsciiz(&shortpathW, shortpath))
1398 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1399 return 0;
1402 retW = GetLongPathNameW(shortpathW.Buffer, longpathW, MAX_PATH);
1404 if (!retW)
1405 ret = 0;
1406 else if (retW > MAX_PATH)
1408 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1409 ret = 0;
1411 else
1413 ret = WideCharToMultiByte(CP_ACP, 0, longpathW, -1, NULL, 0, NULL, NULL);
1414 if (ret <= longlen)
1416 WideCharToMultiByte(CP_ACP, 0, longpathW, -1, longpath, longlen, NULL, NULL);
1417 ret--; /* length without 0 */
1421 RtlFreeUnicodeString(&shortpathW);
1422 return ret;
1426 /***********************************************************************
1427 * DOSFS_DoGetFullPathName
1429 * Implementation of GetFullPathNameA/W.
1431 * bon@elektron 000331:
1432 * A test for GetFullPathName with many pathological cases
1433 * now gives identical output for Wine and OSR2
1435 static DWORD DOSFS_DoGetFullPathName( LPCWSTR name, DWORD len, LPWSTR result )
1437 DWORD ret;
1438 DOS_FULL_NAME full_name;
1439 LPWSTR p, q;
1440 char *p_l;
1441 const char * root;
1442 WCHAR drivecur[] = {'C',':','.',0};
1443 WCHAR driveletter=0;
1444 int namelen,drive=0;
1445 static const WCHAR bkslashW[] = {'\\',0};
1446 static const WCHAR dotW[] = {'.',0};
1447 static const WCHAR updir_slashW[] = {'\\','.','.','\\',0};
1448 static const WCHAR curdirW[] = {'\\','.','\\',0};
1449 static const WCHAR updirW[] = {'\\','.','.',0};
1451 if (!name[0])
1453 SetLastError(ERROR_BAD_PATHNAME);
1454 return 0;
1457 TRACE("passed %s\n", debugstr_w(name));
1459 if (name[1]==':')
1460 /*drive letter given */
1462 driveletter = name[0];
1464 if ((name[1]==':') && ((name[2]=='\\') || (name[2]=='/')))
1465 /*absolute path given */
1467 strncpyW(full_name.short_name, name, MAX_PATHNAME_LEN);
1468 full_name.short_name[MAX_PATHNAME_LEN - 1] = 0; /* ensure 0 termination */
1469 drive = toupperW(name[0]) - 'A';
1471 else
1473 if (driveletter)
1474 drivecur[0]=driveletter;
1475 else if ((name[0]=='\\') || (name[0]=='/'))
1476 strcpyW(drivecur, bkslashW);
1477 else
1478 strcpyW(drivecur, dotW);
1480 if (!DOSFS_GetFullName( drivecur, FALSE, &full_name ))
1482 FIXME("internal: error getting drive/path\n");
1483 return 0;
1485 /* find path that drive letter substitutes*/
1486 drive = toupperW(full_name.short_name[0]) - 'A';
1487 root= DRIVE_GetRoot(drive);
1488 if (!root)
1490 FIXME("internal: error getting DOS Drive Root\n");
1491 return 0;
1493 if (!strcmp(root,"/"))
1495 /* we have just the last / and we need it. */
1496 p_l = full_name.long_name;
1498 else
1500 p_l = full_name.long_name + strlen(root);
1502 /* append long name (= unix name) to drive */
1503 MultiByteToWideChar(DRIVE_GetCodepage(drive), 0, p_l, -1,
1504 full_name.short_name + 2, MAX_PATHNAME_LEN - 3);
1505 /* append name to treat */
1506 namelen= strlenW(full_name.short_name);
1507 p = (LPWSTR)name;
1508 if (driveletter)
1509 p += 2; /* skip drive name when appending */
1510 if (namelen + 2 + strlenW(p) > MAX_PATHNAME_LEN)
1512 FIXME("internal error: buffer too small\n");
1513 return 0;
1515 full_name.short_name[namelen++] ='\\';
1516 full_name.short_name[namelen] = 0;
1517 strncpyW(full_name.short_name + namelen, p, MAX_PATHNAME_LEN - namelen);
1518 full_name.short_name[MAX_PATHNAME_LEN - 1] = 0; /* ensure 0 termination */
1520 /* reverse all slashes */
1521 for (p=full_name.short_name;
1522 p < full_name.short_name + strlenW(full_name.short_name);
1523 p++)
1525 if ( *p == '/' )
1526 *p = '\\';
1528 /* Use memmove, as areas overlap */
1529 /* Delete .. */
1530 while ((p = strstrW(full_name.short_name, updir_slashW)))
1532 if (p > full_name.short_name+2)
1534 *p = 0;
1535 q = strrchrW(full_name.short_name, '\\');
1536 memmove(q+1, p+4, (strlenW(p+4)+1) * sizeof(WCHAR));
1538 else
1540 memmove(full_name.short_name+3, p+4, (strlenW(p+4)+1) * sizeof(WCHAR));
1543 if ((full_name.short_name[2]=='.')&&(full_name.short_name[3]=='.'))
1545 /* This case istn't treated yet : c:..\test */
1546 memmove(full_name.short_name+2,full_name.short_name+4,
1547 (strlenW(full_name.short_name+4)+1) * sizeof(WCHAR));
1549 /* Delete . */
1550 while ((p = strstrW(full_name.short_name, curdirW)))
1552 *(p+1) = 0;
1553 memmove(p+1, p+3, (strlenW(p+3)+1) * sizeof(WCHAR));
1555 if (!(DRIVE_GetFlags(drive) & DRIVE_CASE_PRESERVING))
1556 for (p = full_name.short_name; *p; p++) *p = toupperW(*p);
1557 namelen = strlenW(full_name.short_name);
1558 if (!strcmpW(full_name.short_name+namelen-3, updirW))
1560 /* one more strange case: "c:\test\test1\.."
1561 return "c:\test" */
1562 *(full_name.short_name+namelen-3)=0;
1563 q = strrchrW(full_name.short_name, '\\');
1564 *q =0;
1566 if (full_name.short_name[namelen-1]=='.')
1567 full_name.short_name[(namelen--)-1] =0;
1568 if (!driveletter)
1569 if (full_name.short_name[namelen-1]=='\\')
1570 full_name.short_name[(namelen--)-1] =0;
1571 TRACE("got %s\n", debugstr_w(full_name.short_name));
1573 /* If the lpBuffer buffer is too small, the return value is the
1574 size of the buffer, in characters, required to hold the path
1575 plus the terminating \0 (tested against win95osr2, bon 001118)
1576 . */
1577 ret = strlenW(full_name.short_name);
1578 if (ret >= len )
1580 /* don't touch anything when the buffer is not large enough */
1581 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1582 return ret+1;
1584 if (result)
1586 strncpyW( result, full_name.short_name, len );
1587 result[len - 1] = 0; /* ensure 0 termination */
1590 TRACE("returning %s\n", debugstr_w(full_name.short_name) );
1591 return ret;
1595 /***********************************************************************
1596 * GetFullPathNameA (KERNEL32.@)
1597 * NOTES
1598 * if the path closed with '\', *lastpart is 0
1600 DWORD WINAPI GetFullPathNameA( LPCSTR name, DWORD len, LPSTR buffer,
1601 LPSTR *lastpart )
1603 UNICODE_STRING nameW;
1604 WCHAR bufferW[MAX_PATH];
1605 DWORD ret, retW;
1607 if (!name)
1609 SetLastError(ERROR_INVALID_PARAMETER);
1610 return 0;
1613 if (!RtlCreateUnicodeStringFromAsciiz(&nameW, name))
1615 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1616 return 0;
1619 retW = GetFullPathNameW( nameW.Buffer, MAX_PATH, bufferW, NULL);
1621 if (!retW)
1622 ret = 0;
1623 else if (retW > MAX_PATH)
1625 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1626 ret = 0;
1628 else
1630 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL);
1631 if (ret <= len)
1633 WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, len, NULL, NULL);
1634 ret--; /* length without 0 */
1636 if (lastpart)
1638 LPSTR p = buffer + strlen(buffer);
1640 if (*p != '\\')
1642 while ((p > buffer + 2) && (*p != '\\')) p--;
1643 *lastpart = p + 1;
1645 else *lastpart = NULL;
1650 RtlFreeUnicodeString(&nameW);
1651 return ret;
1655 /***********************************************************************
1656 * GetFullPathNameW (KERNEL32.@)
1658 DWORD WINAPI GetFullPathNameW( LPCWSTR name, DWORD len, LPWSTR buffer,
1659 LPWSTR *lastpart )
1661 DWORD ret = DOSFS_DoGetFullPathName( name, len, buffer );
1662 if (ret && (ret<=len) && buffer && lastpart)
1664 LPWSTR p = buffer + strlenW(buffer);
1665 if (*p != (WCHAR)'\\')
1667 while ((p > buffer + 2) && (*p != (WCHAR)'\\')) p--;
1668 *lastpart = p + 1;
1670 else *lastpart = NULL;
1672 return ret;
1676 /***********************************************************************
1677 * wine_get_unix_file_name (KERNEL32.@) Not a Windows API
1679 * Return the full Unix file name for a given path.
1680 * FIXME: convert dos file name to unicode
1682 BOOL WINAPI wine_get_unix_file_name( LPCSTR dos, LPSTR buffer, DWORD len )
1684 BOOL ret;
1685 DOS_FULL_NAME path;
1686 WCHAR dosW[MAX_PATHNAME_LEN];
1688 MultiByteToWideChar(CP_ACP, 0, dos, -1, dosW, MAX_PATHNAME_LEN);
1689 ret = DOSFS_GetFullName( dosW, FALSE, &path );
1690 if (ret && len)
1692 strncpy( buffer, path.long_name, len );
1693 buffer[len - 1] = 0; /* ensure 0 termination */
1695 return ret;
1699 /***********************************************************************
1700 * DOSFS_FindNextEx
1702 static int DOSFS_FindNextEx( FIND_FIRST_INFO *info, WIN32_FIND_DATAW *entry )
1704 DWORD attr = info->attr | FA_UNUSED | FA_ARCHIVE | FA_RDONLY;
1705 UINT flags = DRIVE_GetFlags( info->drive );
1706 char *p, buffer[MAX_PATHNAME_LEN];
1707 const char *drive_path;
1708 int drive_root;
1709 LPCWSTR long_name, short_name;
1710 BY_HANDLE_FILE_INFORMATION fileinfo;
1711 WCHAR dos_name[13];
1712 BOOL is_symlink;
1714 if ((info->attr & ~(FA_UNUSED | FA_ARCHIVE | FA_RDONLY)) == FA_LABEL)
1716 if (info->cur_pos) return 0;
1717 entry->dwFileAttributes = FILE_ATTRIBUTE_LABEL;
1718 RtlSecondsSince1970ToTime( (time_t)0, (LARGE_INTEGER *)&entry->ftCreationTime );
1719 RtlSecondsSince1970ToTime( (time_t)0, (LARGE_INTEGER *)&entry->ftLastAccessTime );
1720 RtlSecondsSince1970ToTime( (time_t)0, (LARGE_INTEGER *)&entry->ftLastWriteTime );
1721 entry->nFileSizeHigh = 0;
1722 entry->nFileSizeLow = 0;
1723 entry->dwReserved0 = 0;
1724 entry->dwReserved1 = 0;
1725 DOSFS_ToDosDTAFormat( DRIVE_GetLabel( info->drive ), entry->cFileName );
1726 strcpyW( entry->cAlternateFileName, entry->cFileName );
1727 info->cur_pos++;
1728 TRACE("returning %s (%s) as label\n",
1729 debugstr_w(entry->cFileName), debugstr_w(entry->cAlternateFileName));
1730 return 1;
1733 drive_path = info->path + strlen(DRIVE_GetRoot( info->drive ));
1734 while ((*drive_path == '/') || (*drive_path == '\\')) drive_path++;
1735 drive_root = !*drive_path;
1737 lstrcpynA( buffer, info->path, sizeof(buffer) - 1 );
1738 strcat( buffer, "/" );
1739 p = buffer + strlen(buffer);
1741 while (DOSFS_ReadDir( info->u.dos_dir, &long_name, &short_name ))
1743 info->cur_pos++;
1745 /* Don't return '.' and '..' in the root of the drive */
1746 if (drive_root && (long_name[0] == '.') &&
1747 (!long_name[1] || ((long_name[1] == '.') && !long_name[2])))
1748 continue;
1750 /* Check the long mask */
1752 if (info->long_mask && *info->long_mask)
1754 if (!DOSFS_MatchLong( info->long_mask, long_name,
1755 flags & DRIVE_CASE_SENSITIVE )) continue;
1758 /* Check the short mask */
1760 if (info->short_mask)
1762 if (!short_name)
1764 DOSFS_Hash( long_name, dos_name, TRUE,
1765 !(flags & DRIVE_CASE_SENSITIVE) );
1766 short_name = dos_name;
1768 if (!DOSFS_MatchShort( info->short_mask, short_name )) continue;
1771 /* Check the file attributes */
1772 WideCharToMultiByte(DRIVE_GetCodepage(info->drive), 0, long_name, -1,
1773 p, sizeof(buffer) - (int)(p - buffer), NULL, NULL);
1774 if (!FILE_Stat( buffer, &fileinfo, &is_symlink ))
1776 WARN("can't stat %s\n", buffer);
1777 continue;
1779 if (is_symlink && (fileinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
1781 static const WCHAR wineW[] = {'w','i','n','e',0};
1782 static const WCHAR ShowDirSymlinksW[] = {'S','h','o','w','D','i','r','S','y','m','l','i','n','k','s',0};
1783 static int show_dir_symlinks = -1;
1784 if (show_dir_symlinks == -1)
1785 show_dir_symlinks = PROFILE_GetWineIniBool(wineW, ShowDirSymlinksW, 0);
1786 if (!show_dir_symlinks) continue;
1789 if (fileinfo.dwFileAttributes & ~attr) continue;
1791 /* We now have a matching entry; fill the result and return */
1793 entry->dwFileAttributes = fileinfo.dwFileAttributes;
1794 entry->ftCreationTime = fileinfo.ftCreationTime;
1795 entry->ftLastAccessTime = fileinfo.ftLastAccessTime;
1796 entry->ftLastWriteTime = fileinfo.ftLastWriteTime;
1797 entry->nFileSizeHigh = fileinfo.nFileSizeHigh;
1798 entry->nFileSizeLow = fileinfo.nFileSizeLow;
1800 if (short_name)
1801 DOSFS_ToDosDTAFormat( short_name, entry->cAlternateFileName );
1802 else
1803 DOSFS_Hash( long_name, entry->cAlternateFileName, FALSE,
1804 !(flags & DRIVE_CASE_SENSITIVE) );
1806 lstrcpynW( entry->cFileName, long_name, sizeof(entry->cFileName)/sizeof(entry->cFileName[0]) );
1807 if (!(flags & DRIVE_CASE_PRESERVING)) strlwrW( entry->cFileName );
1808 TRACE("returning %s (%s) %02lx %ld\n",
1809 debugstr_w(entry->cFileName), debugstr_w(entry->cAlternateFileName),
1810 entry->dwFileAttributes, entry->nFileSizeLow );
1811 return 1;
1813 return 0; /* End of directory */
1816 /***********************************************************************
1817 * DOSFS_FindNext
1819 * Find the next matching file. Return the number of entries read to find
1820 * the matching one, or 0 if no more entries.
1821 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
1822 * file name mask. Either or both can be NULL.
1824 * NOTE: This is supposed to be only called by the int21 emulation
1825 * routines. Thus, we should own the Win16Mutex anyway.
1826 * Nevertheless, we explicitly enter it to ensure the static
1827 * directory cache is protected.
1829 int DOSFS_FindNext( const char *path, const char *short_mask,
1830 const char *long_mask, int drive, BYTE attr,
1831 int skip, WIN32_FIND_DATAA *entry )
1833 static FIND_FIRST_INFO info;
1834 LPCWSTR short_name, long_name;
1835 int count;
1836 UNICODE_STRING short_maskW, long_maskW;
1837 WIN32_FIND_DATAW entryW;
1839 TRACE("(%s, %s, %s, %x, %x, %x, %p)\n", debugstr_a(path),
1840 debugstr_a(short_mask), debugstr_a(long_mask), drive, attr, skip,
1841 entry);
1843 _EnterWin16Lock();
1845 RtlCreateUnicodeStringFromAsciiz(&short_maskW, short_mask);
1846 RtlCreateUnicodeStringFromAsciiz(&long_maskW, long_mask);
1848 /* Check the cached directory */
1849 if (!(info.u.dos_dir && info.path == path && !strcmpW(info.short_mask, short_maskW.Buffer)
1850 && !strcmpW(info.long_mask, long_maskW.Buffer) && info.drive == drive
1851 && info.attr == attr && info.cur_pos <= skip))
1853 /* Not in the cache, open it anew */
1854 if (info.u.dos_dir) DOSFS_CloseDir( info.u.dos_dir );
1856 info.path = (LPSTR)path;
1857 RtlFreeHeap(GetProcessHeap(), 0, info.long_mask);
1858 RtlFreeHeap(GetProcessHeap(), 0, info.short_mask);
1859 info.long_mask = long_maskW.Buffer;
1860 info.short_mask = short_maskW.Buffer;
1861 info.attr = attr;
1862 info.drive = drive;
1863 info.cur_pos = 0;
1864 info.u.dos_dir = DOSFS_OpenDir( DRIVE_GetCodepage(drive), info.path );
1866 else
1868 RtlFreeUnicodeString(&short_maskW);
1869 RtlFreeUnicodeString(&long_maskW);
1872 /* Skip to desired position */
1873 while (info.cur_pos < skip)
1874 if (info.u.dos_dir && DOSFS_ReadDir( info.u.dos_dir, &long_name, &short_name ))
1875 info.cur_pos++;
1876 else
1877 break;
1879 if (info.u.dos_dir && info.cur_pos == skip && DOSFS_FindNextEx( &info, &entryW ))
1881 WideCharToMultiByte(CP_ACP, 0, entryW.cFileName, -1,
1882 entry->cFileName, sizeof(entry->cFileName), NULL, NULL);
1883 WideCharToMultiByte(CP_ACP, 0, entryW.cAlternateFileName, -1,
1884 entry->cAlternateFileName, sizeof(entry->cAlternateFileName), NULL, NULL);
1885 count = info.cur_pos - skip;
1887 entry->dwFileAttributes = entryW.dwFileAttributes;
1888 entry->nFileSizeHigh = entryW.nFileSizeHigh;
1889 entry->nFileSizeLow = entryW.nFileSizeLow;
1890 entry->ftCreationTime = entryW.ftCreationTime;
1891 entry->ftLastAccessTime = entryW.ftLastAccessTime;
1892 entry->ftLastWriteTime = entryW.ftLastWriteTime;
1895 else
1896 count = 0;
1898 if (!count)
1900 if (info.u.dos_dir) DOSFS_CloseDir( info.u.dos_dir );
1901 memset( &info, '\0', sizeof(info) );
1904 _LeaveWin16Lock();
1906 return count;
1909 /*************************************************************************
1910 * FindFirstFileExW (KERNEL32.@)
1912 HANDLE WINAPI FindFirstFileExW(
1913 LPCWSTR lpFileName,
1914 FINDEX_INFO_LEVELS fInfoLevelId,
1915 LPVOID lpFindFileData,
1916 FINDEX_SEARCH_OPS fSearchOp,
1917 LPVOID lpSearchFilter,
1918 DWORD dwAdditionalFlags)
1920 HGLOBAL handle;
1921 FIND_FIRST_INFO *info;
1923 if (!lpFileName)
1925 SetLastError(ERROR_PATH_NOT_FOUND);
1926 return INVALID_HANDLE_VALUE;
1929 if ((fSearchOp != FindExSearchNameMatch) || (dwAdditionalFlags != 0))
1931 FIXME("options not implemented 0x%08x 0x%08lx\n", fSearchOp, dwAdditionalFlags );
1932 return INVALID_HANDLE_VALUE;
1935 switch(fInfoLevelId)
1937 case FindExInfoStandard:
1939 WIN32_FIND_DATAW * data = (WIN32_FIND_DATAW *) lpFindFileData;
1940 char *p;
1941 INT long_mask_len;
1942 UINT codepage;
1944 data->dwReserved0 = data->dwReserved1 = 0x0;
1945 if (lpFileName[0] == '\\' && lpFileName[1] == '\\')
1947 ERR("UNC path name\n");
1948 if (!(handle = GlobalAlloc(GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO)))) break;
1950 info = (FIND_FIRST_INFO *)GlobalLock( handle );
1951 info->u.smb_dir = SMB_FindFirst(lpFileName);
1952 if(!info->u.smb_dir)
1954 GlobalUnlock( handle );
1955 GlobalFree(handle);
1956 break;
1959 info->drive = -1;
1961 GlobalUnlock( handle );
1963 else
1965 DOS_FULL_NAME full_name;
1967 if (lpFileName[0] && lpFileName[1] == ':')
1969 /* don't allow root directories */
1970 if (!lpFileName[2] ||
1971 ((lpFileName[2] == '/' || lpFileName[2] == '\\') && !lpFileName[3]))
1973 SetLastError(ERROR_FILE_NOT_FOUND);
1974 return INVALID_HANDLE_VALUE;
1977 if (!DOSFS_GetFullName( lpFileName, FALSE, &full_name )) break;
1978 if (!(handle = GlobalAlloc(GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO)))) break;
1979 info = (FIND_FIRST_INFO *)GlobalLock( handle );
1980 info->path = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
1981 strcpy( info->path, full_name.long_name );
1983 codepage = DRIVE_GetCodepage(full_name.drive);
1984 p = strrchr( info->path, '/' );
1985 *p++ = '\0';
1986 long_mask_len = MultiByteToWideChar(codepage, 0, p, -1, NULL, 0);
1987 info->long_mask = HeapAlloc( GetProcessHeap(), 0, long_mask_len * sizeof(WCHAR) );
1988 MultiByteToWideChar(codepage, 0, p, -1, info->long_mask, long_mask_len);
1990 info->short_mask = NULL;
1991 info->attr = 0xff;
1992 info->drive = full_name.drive;
1993 info->cur_pos = 0;
1995 info->u.dos_dir = DOSFS_OpenDir( codepage, info->path );
1996 GlobalUnlock( handle );
1998 if (!FindNextFileW( handle, data ))
2000 FindClose( handle );
2001 SetLastError( ERROR_FILE_NOT_FOUND );
2002 break;
2004 return handle;
2006 break;
2007 default:
2008 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
2010 return INVALID_HANDLE_VALUE;
2013 /*************************************************************************
2014 * FindFirstFileA (KERNEL32.@)
2016 HANDLE WINAPI FindFirstFileA(
2017 LPCSTR lpFileName,
2018 WIN32_FIND_DATAA *lpFindData )
2020 return FindFirstFileExA(lpFileName, FindExInfoStandard, lpFindData,
2021 FindExSearchNameMatch, NULL, 0);
2024 /*************************************************************************
2025 * FindFirstFileExA (KERNEL32.@)
2027 HANDLE WINAPI FindFirstFileExA(
2028 LPCSTR lpFileName,
2029 FINDEX_INFO_LEVELS fInfoLevelId,
2030 LPVOID lpFindFileData,
2031 FINDEX_SEARCH_OPS fSearchOp,
2032 LPVOID lpSearchFilter,
2033 DWORD dwAdditionalFlags)
2035 HANDLE handle;
2036 WIN32_FIND_DATAA *dataA;
2037 WIN32_FIND_DATAW dataW;
2038 UNICODE_STRING pathW;
2040 if (!lpFileName)
2042 SetLastError(ERROR_PATH_NOT_FOUND);
2043 return INVALID_HANDLE_VALUE;
2046 if (!RtlCreateUnicodeStringFromAsciiz(&pathW, lpFileName))
2048 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2049 return INVALID_HANDLE_VALUE;
2052 handle = FindFirstFileExW(pathW.Buffer, fInfoLevelId, &dataW, fSearchOp, lpSearchFilter, dwAdditionalFlags);
2053 RtlFreeUnicodeString(&pathW);
2054 if (handle == INVALID_HANDLE_VALUE) return handle;
2056 dataA = (WIN32_FIND_DATAA *) lpFindFileData;
2057 dataA->dwFileAttributes = dataW.dwFileAttributes;
2058 dataA->ftCreationTime = dataW.ftCreationTime;
2059 dataA->ftLastAccessTime = dataW.ftLastAccessTime;
2060 dataA->ftLastWriteTime = dataW.ftLastWriteTime;
2061 dataA->nFileSizeHigh = dataW.nFileSizeHigh;
2062 dataA->nFileSizeLow = dataW.nFileSizeLow;
2063 WideCharToMultiByte( CP_ACP, 0, dataW.cFileName, -1,
2064 dataA->cFileName, sizeof(dataA->cFileName), NULL, NULL );
2065 WideCharToMultiByte( CP_ACP, 0, dataW.cAlternateFileName, -1,
2066 dataA->cAlternateFileName, sizeof(dataA->cAlternateFileName), NULL, NULL );
2067 return handle;
2070 /*************************************************************************
2071 * FindFirstFileW (KERNEL32.@)
2073 HANDLE WINAPI FindFirstFileW( LPCWSTR lpFileName, WIN32_FIND_DATAW *lpFindData )
2075 return FindFirstFileExW(lpFileName, FindExInfoStandard, lpFindData,
2076 FindExSearchNameMatch, NULL, 0);
2079 /*************************************************************************
2080 * FindNextFileW (KERNEL32.@)
2082 BOOL WINAPI FindNextFileW( HANDLE handle, WIN32_FIND_DATAW *data )
2084 FIND_FIRST_INFO *info;
2085 BOOL ret = FALSE;
2086 DWORD gle = ERROR_NO_MORE_FILES;
2088 if ((handle == INVALID_HANDLE_VALUE) ||
2089 !(info = (FIND_FIRST_INFO *)GlobalLock( handle )))
2091 SetLastError( ERROR_INVALID_HANDLE );
2092 return ret;
2094 if (info->drive == -1)
2096 ret = SMB_FindNext( info->u.smb_dir, data );
2097 if(!ret)
2099 SMB_CloseDir( info->u.smb_dir );
2100 HeapFree( GetProcessHeap(), 0, info->path );
2102 goto done;
2104 else if (!info->path || !info->u.dos_dir)
2106 goto done;
2108 else if (!DOSFS_FindNextEx( info, data ))
2110 DOSFS_CloseDir( info->u.dos_dir ); info->u.dos_dir = NULL;
2111 HeapFree( GetProcessHeap(), 0, info->path );
2112 info->path = NULL;
2113 HeapFree( GetProcessHeap(), 0, info->long_mask );
2114 info->long_mask = NULL;
2115 goto done;
2117 ret = TRUE;
2118 done:
2119 GlobalUnlock( handle );
2120 if( !ret ) SetLastError( gle );
2121 return ret;
2125 /*************************************************************************
2126 * FindNextFileA (KERNEL32.@)
2128 BOOL WINAPI FindNextFileA( HANDLE handle, WIN32_FIND_DATAA *data )
2130 WIN32_FIND_DATAW dataW;
2131 if (!FindNextFileW( handle, &dataW )) return FALSE;
2132 data->dwFileAttributes = dataW.dwFileAttributes;
2133 data->ftCreationTime = dataW.ftCreationTime;
2134 data->ftLastAccessTime = dataW.ftLastAccessTime;
2135 data->ftLastWriteTime = dataW.ftLastWriteTime;
2136 data->nFileSizeHigh = dataW.nFileSizeHigh;
2137 data->nFileSizeLow = dataW.nFileSizeLow;
2138 WideCharToMultiByte( CP_ACP, 0, dataW.cFileName, -1,
2139 data->cFileName, sizeof(data->cFileName), NULL, NULL );
2140 WideCharToMultiByte( CP_ACP, 0, dataW.cAlternateFileName, -1,
2141 data->cAlternateFileName,
2142 sizeof(data->cAlternateFileName), NULL, NULL );
2143 return TRUE;
2146 /*************************************************************************
2147 * FindClose (KERNEL32.@)
2149 BOOL WINAPI FindClose( HANDLE handle )
2151 FIND_FIRST_INFO *info;
2153 if (handle == INVALID_HANDLE_VALUE) goto error;
2155 __TRY
2157 if ((info = (FIND_FIRST_INFO *)GlobalLock( handle )))
2159 if (info->u.dos_dir) DOSFS_CloseDir( info->u.dos_dir );
2160 if (info->path) HeapFree( GetProcessHeap(), 0, info->path );
2161 if (info->long_mask) HeapFree( GetProcessHeap(), 0, info->long_mask );
2164 __EXCEPT(page_fault)
2166 WARN("Illegal handle %p\n", handle);
2167 SetLastError( ERROR_INVALID_HANDLE );
2168 return FALSE;
2170 __ENDTRY
2171 if (!info) goto error;
2172 GlobalUnlock( handle );
2173 GlobalFree( handle );
2174 return TRUE;
2176 error:
2177 SetLastError( ERROR_INVALID_HANDLE );
2178 return FALSE;
2181 /***********************************************************************
2182 * DOSFS_UnixTimeToFileTime
2184 * Convert a Unix time to FILETIME format.
2185 * The FILETIME structure is a 64-bit value representing the number of
2186 * 100-nanosecond intervals since January 1, 1601, 0:00.
2187 * 'remainder' is the nonnegative number of 100-ns intervals
2188 * corresponding to the time fraction smaller than 1 second that
2189 * couldn't be stored in the time_t value.
2191 void DOSFS_UnixTimeToFileTime( time_t unix_time, FILETIME *filetime,
2192 DWORD remainder )
2194 /* NOTES:
2196 CONSTANTS:
2197 The time difference between 1 January 1601, 00:00:00 and
2198 1 January 1970, 00:00:00 is 369 years, plus the leap years
2199 from 1604 to 1968, excluding 1700, 1800, 1900.
2200 This makes (1968 - 1600) / 4 - 3 = 89 leap days, and a total
2201 of 134774 days.
2203 Any day in that period had 24 * 60 * 60 = 86400 seconds.
2205 The time difference is 134774 * 86400 * 10000000, which can be written
2206 116444736000000000
2207 27111902 * 2^32 + 3577643008
2208 413 * 2^48 + 45534 * 2^32 + 54590 * 2^16 + 32768
2210 If you find that these constants are buggy, please change them in all
2211 instances in both conversion functions.
2213 VERSIONS:
2214 There are two versions, one of them uses long long variables and
2215 is presumably faster but not ISO C. The other one uses standard C
2216 data types and operations but relies on the assumption that negative
2217 numbers are stored as 2's complement (-1 is 0xffff....). If this
2218 assumption is violated, dates before 1970 will not convert correctly.
2219 This should however work on any reasonable architecture where WINE
2220 will run.
2222 DETAILS:
2224 Take care not to remove the casts. I have tested these functions
2225 (in both versions) for a lot of numbers. I would be interested in
2226 results on other compilers than GCC.
2228 The operations have been designed to account for the possibility
2229 of 64-bit time_t in future UNICES. Even the versions without
2230 internal long long numbers will work if time_t only is 64 bit.
2231 A 32-bit shift, which was necessary for that operation, turned out
2232 not to work correctly in GCC, besides giving the warning. So I
2233 used a double 16-bit shift instead. Numbers are in the ISO version
2234 represented by three limbs, the most significant with 32 bit, the
2235 other two with 16 bit each.
2237 As the modulo-operator % is not well-defined for negative numbers,
2238 negative divisors have been avoided in DOSFS_FileTimeToUnixTime.
2240 There might be quicker ways to do this in C. Certainly so in
2241 assembler.
2243 Claus Fischer, fischer@iue.tuwien.ac.at
2246 #if SIZEOF_LONG_LONG >= 8
2247 # define USE_LONG_LONG 1
2248 #else
2249 # define USE_LONG_LONG 0
2250 #endif
2252 #if USE_LONG_LONG /* gcc supports long long type */
2254 long long int t = unix_time;
2255 t *= 10000000;
2256 t += 116444736000000000LL;
2257 t += remainder;
2258 filetime->dwLowDateTime = (UINT)t;
2259 filetime->dwHighDateTime = (UINT)(t >> 32);
2261 #else /* ISO version */
2263 UINT a0; /* 16 bit, low bits */
2264 UINT a1; /* 16 bit, medium bits */
2265 UINT a2; /* 32 bit, high bits */
2267 /* Copy the unix time to a2/a1/a0 */
2268 a0 = unix_time & 0xffff;
2269 a1 = (unix_time >> 16) & 0xffff;
2270 /* This is obsolete if unix_time is only 32 bits, but it does not hurt.
2271 Do not replace this by >> 32, it gives a compiler warning and it does
2272 not work. */
2273 a2 = (unix_time >= 0 ? (unix_time >> 16) >> 16 :
2274 ~((~unix_time >> 16) >> 16));
2276 /* Multiply a by 10000000 (a = a2/a1/a0)
2277 Split the factor into 10000 * 1000 which are both less than 0xffff. */
2278 a0 *= 10000;
2279 a1 = a1 * 10000 + (a0 >> 16);
2280 a2 = a2 * 10000 + (a1 >> 16);
2281 a0 &= 0xffff;
2282 a1 &= 0xffff;
2284 a0 *= 1000;
2285 a1 = a1 * 1000 + (a0 >> 16);
2286 a2 = a2 * 1000 + (a1 >> 16);
2287 a0 &= 0xffff;
2288 a1 &= 0xffff;
2290 /* Add the time difference and the remainder */
2291 a0 += 32768 + (remainder & 0xffff);
2292 a1 += 54590 + (remainder >> 16 ) + (a0 >> 16);
2293 a2 += 27111902 + (a1 >> 16);
2294 a0 &= 0xffff;
2295 a1 &= 0xffff;
2297 /* Set filetime */
2298 filetime->dwLowDateTime = (a1 << 16) + a0;
2299 filetime->dwHighDateTime = a2;
2300 #endif
2304 /***********************************************************************
2305 * DOSFS_FileTimeToUnixTime
2307 * Convert a FILETIME format to Unix time.
2308 * If not NULL, 'remainder' contains the fractional part of the filetime,
2309 * in the range of [0..9999999] (even if time_t is negative).
2311 time_t DOSFS_FileTimeToUnixTime( const FILETIME *filetime, DWORD *remainder )
2313 /* Read the comment in the function DOSFS_UnixTimeToFileTime. */
2314 #if USE_LONG_LONG
2316 long long int t = filetime->dwHighDateTime;
2317 t <<= 32;
2318 t += (UINT)filetime->dwLowDateTime;
2319 t -= 116444736000000000LL;
2320 if (t < 0)
2322 if (remainder) *remainder = 9999999 - (-t - 1) % 10000000;
2323 return -1 - ((-t - 1) / 10000000);
2325 else
2327 if (remainder) *remainder = t % 10000000;
2328 return t / 10000000;
2331 #else /* ISO version */
2333 UINT a0; /* 16 bit, low bits */
2334 UINT a1; /* 16 bit, medium bits */
2335 UINT a2; /* 32 bit, high bits */
2336 UINT r; /* remainder of division */
2337 unsigned int carry; /* carry bit for subtraction */
2338 int negative; /* whether a represents a negative value */
2340 /* Copy the time values to a2/a1/a0 */
2341 a2 = (UINT)filetime->dwHighDateTime;
2342 a1 = ((UINT)filetime->dwLowDateTime ) >> 16;
2343 a0 = ((UINT)filetime->dwLowDateTime ) & 0xffff;
2345 /* Subtract the time difference */
2346 if (a0 >= 32768 ) a0 -= 32768 , carry = 0;
2347 else a0 += (1 << 16) - 32768 , carry = 1;
2349 if (a1 >= 54590 + carry) a1 -= 54590 + carry, carry = 0;
2350 else a1 += (1 << 16) - 54590 - carry, carry = 1;
2352 a2 -= 27111902 + carry;
2354 /* If a is negative, replace a by (-1-a) */
2355 negative = (a2 >= ((UINT)1) << 31);
2356 if (negative)
2358 /* Set a to -a - 1 (a is a2/a1/a0) */
2359 a0 = 0xffff - a0;
2360 a1 = 0xffff - a1;
2361 a2 = ~a2;
2364 /* Divide a by 10000000 (a = a2/a1/a0), put the rest into r.
2365 Split the divisor into 10000 * 1000 which are both less than 0xffff. */
2366 a1 += (a2 % 10000) << 16;
2367 a2 /= 10000;
2368 a0 += (a1 % 10000) << 16;
2369 a1 /= 10000;
2370 r = a0 % 10000;
2371 a0 /= 10000;
2373 a1 += (a2 % 1000) << 16;
2374 a2 /= 1000;
2375 a0 += (a1 % 1000) << 16;
2376 a1 /= 1000;
2377 r += (a0 % 1000) * 10000;
2378 a0 /= 1000;
2380 /* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
2381 if (negative)
2383 /* Set a to -a - 1 (a is a2/a1/a0) */
2384 a0 = 0xffff - a0;
2385 a1 = 0xffff - a1;
2386 a2 = ~a2;
2388 r = 9999999 - r;
2391 if (remainder) *remainder = r;
2393 /* Do not replace this by << 32, it gives a compiler warning and it does
2394 not work. */
2395 return ((((time_t)a2) << 16) << 16) + (a1 << 16) + a0;
2396 #endif
2400 /***********************************************************************
2401 * MulDiv (KERNEL32.@)
2402 * RETURNS
2403 * Result of multiplication and division
2404 * -1: Overflow occurred or Divisor was 0
2406 INT WINAPI MulDiv(
2407 INT nMultiplicand,
2408 INT nMultiplier,
2409 INT nDivisor)
2411 #if SIZEOF_LONG_LONG >= 8
2412 long long ret;
2414 if (!nDivisor) return -1;
2416 /* We want to deal with a positive divisor to simplify the logic. */
2417 if (nDivisor < 0)
2419 nMultiplicand = - nMultiplicand;
2420 nDivisor = -nDivisor;
2423 /* If the result is positive, we "add" to round. else, we subtract to round. */
2424 if ( ( (nMultiplicand < 0) && (nMultiplier < 0) ) ||
2425 ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
2426 ret = (((long long)nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
2427 else
2428 ret = (((long long)nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
2430 if ((ret > 2147483647) || (ret < -2147483647)) return -1;
2431 return ret;
2432 #else
2433 if (!nDivisor) return -1;
2435 /* We want to deal with a positive divisor to simplify the logic. */
2436 if (nDivisor < 0)
2438 nMultiplicand = - nMultiplicand;
2439 nDivisor = -nDivisor;
2442 /* If the result is positive, we "add" to round. else, we subtract to round. */
2443 if ( ( (nMultiplicand < 0) && (nMultiplier < 0) ) ||
2444 ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
2445 return ((nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
2447 return ((nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
2449 #endif
2453 /***********************************************************************
2454 * DosDateTimeToFileTime (KERNEL32.@)
2456 BOOL WINAPI DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft)
2458 struct tm newtm;
2459 #ifndef HAVE_TIMEGM
2460 struct tm *gtm;
2461 time_t time1, time2;
2462 #endif
2464 newtm.tm_sec = (fattime & 0x1f) * 2;
2465 newtm.tm_min = (fattime >> 5) & 0x3f;
2466 newtm.tm_hour = (fattime >> 11);
2467 newtm.tm_mday = (fatdate & 0x1f);
2468 newtm.tm_mon = ((fatdate >> 5) & 0x0f) - 1;
2469 newtm.tm_year = (fatdate >> 9) + 80;
2470 #ifdef HAVE_TIMEGM
2471 RtlSecondsSince1970ToTime( timegm(&newtm), (LARGE_INTEGER *)ft );
2472 #else
2473 time1 = mktime(&newtm);
2474 gtm = gmtime(&time1);
2475 time2 = mktime(gtm);
2476 RtlSecondsSince1970ToTime( 2*time1-time2, (LARGE_INTEGER *)ft );
2477 #endif
2478 return TRUE;
2482 /***********************************************************************
2483 * FileTimeToDosDateTime (KERNEL32.@)
2485 BOOL WINAPI FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate,
2486 LPWORD fattime )
2488 time_t unixtime = DOSFS_FileTimeToUnixTime( ft, NULL );
2489 struct tm *tm = gmtime( &unixtime );
2490 if (fattime)
2491 *fattime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
2492 if (fatdate)
2493 *fatdate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
2494 + tm->tm_mday;
2495 return TRUE;
2499 /***********************************************************************
2500 * QueryDosDeviceA (KERNEL32.@)
2502 * returns array of strings terminated by \0, terminated by \0
2504 DWORD WINAPI QueryDosDeviceA(LPCSTR devname,LPSTR target,DWORD bufsize)
2506 LPSTR s;
2507 char buffer[200];
2509 TRACE("(%s,...)\n", devname ? devname : "<null>");
2510 if (!devname) {
2511 /* return known MSDOS devices */
2512 static const char devices[24] = "CON\0COM1\0COM2\0LPT1\0NUL\0\0";
2513 memcpy( target, devices, min(bufsize,sizeof(devices)) );
2514 return min(bufsize,sizeof(devices));
2516 /* In theory all that are possible and have been defined.
2517 * Now just those below, since mirc uses it to check for special files.
2519 * (It is more complex, and supports netmounted stuff, and \\.\ stuff,
2520 * but currently we just ignore that.)
2522 #define CHECK(x) (strstr(devname,#x)==devname)
2523 if (CHECK(con) || CHECK(com) || CHECK(lpt) || CHECK(nul)) {
2524 strcpy(buffer,"\\DEV\\");
2525 strcat(buffer,devname);
2526 if ((s=strchr(buffer,':'))) *s='\0';
2527 lstrcpynA(target,buffer,bufsize);
2528 return strlen(buffer)+1;
2529 } else {
2530 if (strchr(devname,':') || devname[0]=='\\') {
2531 /* This might be a DOS device we do not handle yet ... */
2532 FIXME("(%s) not detected as DOS device!\n",devname);
2534 SetLastError(ERROR_DEV_NOT_EXIST);
2535 return 0;
2541 /***********************************************************************
2542 * QueryDosDeviceW (KERNEL32.@)
2544 * returns array of strings terminated by \0, terminated by \0
2546 DWORD WINAPI QueryDosDeviceW(LPCWSTR devname,LPWSTR target,DWORD bufsize)
2548 LPSTR devnameA = devname?HEAP_strdupWtoA(GetProcessHeap(),0,devname):NULL;
2549 LPSTR targetA = (LPSTR)HeapAlloc(GetProcessHeap(),0,bufsize);
2550 DWORD ret = QueryDosDeviceA(devnameA,targetA,bufsize);
2552 ret = MultiByteToWideChar( CP_ACP, 0, targetA, ret, target, bufsize );
2553 if (devnameA) HeapFree(GetProcessHeap(),0,devnameA);
2554 if (targetA) HeapFree(GetProcessHeap(),0,targetA);
2555 return ret;
2559 /***********************************************************************
2560 * DefineDosDeviceA (KERNEL32.@)
2562 BOOL WINAPI DefineDosDeviceA(DWORD flags,LPCSTR devname,LPCSTR targetpath) {
2563 FIXME("(0x%08lx,%s,%s),stub!\n",flags,devname,targetpath);
2564 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2565 return FALSE;
2569 --- 16 bit functions ---
2572 /*************************************************************************
2573 * FindFirstFile (KERNEL.413)
2575 HANDLE16 WINAPI FindFirstFile16( LPCSTR path, WIN32_FIND_DATAA *data )
2577 DOS_FULL_NAME full_name;
2578 HGLOBAL16 handle;
2579 FIND_FIRST_INFO *info;
2580 WCHAR pathW[MAX_PATH];
2581 char *p;
2582 INT long_mask_len;
2583 UINT codepage;
2585 data->dwReserved0 = data->dwReserved1 = 0x0;
2586 if (!path) return INVALID_HANDLE_VALUE16;
2587 MultiByteToWideChar(CP_ACP, 0, path, -1, pathW, MAX_PATH);
2588 if (!DOSFS_GetFullName( pathW, FALSE, &full_name ))
2589 return INVALID_HANDLE_VALUE16;
2590 if (!(handle = GlobalAlloc16( GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO) )))
2591 return INVALID_HANDLE_VALUE16;
2592 info = (FIND_FIRST_INFO *)GlobalLock16( handle );
2593 info->path = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
2594 strcpy( info->path, full_name.long_name );
2596 codepage = DRIVE_GetCodepage(full_name.drive);
2597 p = strrchr( info->path, '/' );
2598 *p++ = '\0';
2599 long_mask_len = MultiByteToWideChar(codepage, 0, p, -1, NULL, 0);
2600 info->long_mask = HeapAlloc( GetProcessHeap(), 0, long_mask_len * sizeof(WCHAR) );
2601 MultiByteToWideChar(codepage, 0, p, -1, info->long_mask, long_mask_len);
2603 info->short_mask = NULL;
2604 info->attr = 0xff;
2605 info->drive = full_name.drive;
2606 info->cur_pos = 0;
2608 info->u.dos_dir = DOSFS_OpenDir( codepage, info->path );
2610 GlobalUnlock16( handle );
2611 if (!FindNextFile16( handle, data ))
2613 FindClose16( handle );
2614 SetLastError( ERROR_NO_MORE_FILES );
2615 return INVALID_HANDLE_VALUE16;
2617 return handle;
2620 /*************************************************************************
2621 * FindNextFile (KERNEL.414)
2623 BOOL16 WINAPI FindNextFile16( HANDLE16 handle, WIN32_FIND_DATAA *data )
2625 FIND_FIRST_INFO *info;
2626 WIN32_FIND_DATAW dataW;
2627 BOOL ret = FALSE;
2628 DWORD gle = ERROR_NO_MORE_FILES;
2630 if ((handle == INVALID_HANDLE_VALUE16) ||
2631 !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
2633 SetLastError( ERROR_INVALID_HANDLE );
2634 return ret;
2636 if (!info->path || !info->u.dos_dir)
2638 goto done;
2640 if (!DOSFS_FindNextEx( info, &dataW ))
2642 DOSFS_CloseDir( info->u.dos_dir ); info->u.dos_dir = NULL;
2643 HeapFree( GetProcessHeap(), 0, info->path );
2644 info->path = NULL;
2645 HeapFree( GetProcessHeap(), 0, info->long_mask );
2646 info->long_mask = NULL;
2647 goto done;
2650 ret = TRUE;
2652 data->dwFileAttributes = dataW.dwFileAttributes;
2653 data->ftCreationTime = dataW.ftCreationTime;
2654 data->ftLastAccessTime = dataW.ftLastAccessTime;
2655 data->ftLastWriteTime = dataW.ftLastWriteTime;
2656 data->nFileSizeHigh = dataW.nFileSizeHigh;
2657 data->nFileSizeLow = dataW.nFileSizeLow;
2658 WideCharToMultiByte( CP_ACP, 0, dataW.cFileName, -1,
2659 data->cFileName, sizeof(data->cFileName), NULL, NULL );
2660 WideCharToMultiByte( CP_ACP, 0, dataW.cAlternateFileName, -1,
2661 data->cAlternateFileName,
2662 sizeof(data->cAlternateFileName), NULL, NULL );
2663 done:
2664 if( !ret ) SetLastError( gle );
2665 GlobalUnlock16( handle );
2667 return ret;
2670 /*************************************************************************
2671 * FindClose (KERNEL.415)
2673 BOOL16 WINAPI FindClose16( HANDLE16 handle )
2675 FIND_FIRST_INFO *info;
2677 if ((handle == INVALID_HANDLE_VALUE16) ||
2678 !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
2680 SetLastError( ERROR_INVALID_HANDLE );
2681 return FALSE;
2683 if (info->u.dos_dir) DOSFS_CloseDir( info->u.dos_dir );
2684 if (info->path) HeapFree( GetProcessHeap(), 0, info->path );
2685 if (info->long_mask) HeapFree( GetProcessHeap(), 0, info->long_mask );
2686 GlobalUnlock16( handle );
2687 GlobalFree16( handle );
2688 return TRUE;