Check if 'GLX_ARB_get_proc_address' is supported before using
[wine/multimedia.git] / files / dos_fs.c
blob569d14ef45b5821682990d4da00bcd058a7caf09
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 WCHAR 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 /* Check for "." and ".." */
206 if (*p == '.')
208 p++;
209 buffer[0] = '.';
210 for(i = 1; i < 11; i++) buffer[i] = ' ';
211 buffer[11] = 0;
212 if (*p == '.')
214 buffer[1] = '.';
215 p++;
217 return (!*p || (*p == '/') || (*p == '\\'));
220 for (i = 0; i < 8; i++)
222 switch(*p)
224 case '\0':
225 case '\\':
226 case '/':
227 case '.':
228 buffer[i] = ' ';
229 break;
230 case '?':
231 p++;
232 /* fall through */
233 case '*':
234 buffer[i] = '?';
235 break;
236 default:
237 if (*p < 256 && strchr( invalid_chars, (char)*p )) return FALSE;
238 buffer[i] = toupperW(*p);
239 p++;
240 break;
244 if (*p == '*')
246 /* Skip all chars after wildcard up to first dot */
247 while (*p && (*p != '/') && (*p != '\\') && (*p != '.')) p++;
249 else
251 /* Check if name too long */
252 if (*p && (*p != '/') && (*p != '\\') && (*p != '.')) return FALSE;
254 if (*p == '.') p++; /* Skip dot */
256 for (i = 8; i < 11; i++)
258 switch(*p)
260 case '\0':
261 case '\\':
262 case '/':
263 buffer[i] = ' ';
264 break;
265 case '.':
266 return FALSE; /* Second extension not allowed */
267 case '?':
268 p++;
269 /* fall through */
270 case '*':
271 buffer[i] = '?';
272 break;
273 default:
274 if (*p < 256 && strchr( invalid_chars, (char)*p )) return FALSE;
275 buffer[i] = toupperW(*p);
276 p++;
277 break;
280 buffer[11] = '\0';
282 /* at most 3 character of the extension are processed
283 * is something behind this ?
285 while (*p == '*' || *p == ' ') p++; /* skip wildcards and spaces */
286 return IS_END_OF_NAME(*p);
290 /***********************************************************************
291 * DOSFS_ToDosDTAFormat
293 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
294 * converting to upper-case in the process.
295 * File name can be terminated by '\0', '\\' or '/'.
296 * 'buffer' must be at least 13 characters long.
298 static void DOSFS_ToDosDTAFormat( LPCWSTR name, LPWSTR buffer )
300 LPWSTR p;
302 memcpy( buffer, name, 8 * sizeof(WCHAR) );
303 p = buffer + 8;
304 while ((p > buffer) && (p[-1] == ' ')) p--;
305 *p++ = '.';
306 memcpy( p, name + 8, 3 * sizeof(WCHAR) );
307 p += 3;
308 while (p[-1] == ' ') p--;
309 if (p[-1] == '.') p--;
310 *p = '\0';
314 /***********************************************************************
315 * DOSFS_MatchShort
317 * Check a DOS file name against a mask (both in FCB format).
319 static int DOSFS_MatchShort( LPCWSTR mask, LPCWSTR name )
321 int i;
322 for (i = 11; i > 0; i--, mask++, name++)
323 if ((*mask != '?') && (*mask != *name)) return 0;
324 return 1;
328 /***********************************************************************
329 * DOSFS_MatchLong
331 * Check a long file name against a mask.
333 * Tests (done in W95 DOS shell - case insensitive):
334 * *.txt test1.test.txt *
335 * *st1* test1.txt *
336 * *.t??????.t* test1.ta.tornado.txt *
337 * *tornado* test1.ta.tornado.txt *
338 * t*t test1.ta.tornado.txt *
339 * ?est* test1.txt *
340 * ?est??? test1.txt -
341 * *test1.txt* test1.txt *
342 * h?l?o*t.dat hellothisisatest.dat *
344 static int DOSFS_MatchLong( LPCWSTR mask, LPCWSTR name, int case_sensitive )
346 LPCWSTR lastjoker = NULL;
347 LPCWSTR next_to_retry = NULL;
348 static const WCHAR asterisk_dot_asterisk[] = {'*','.','*',0};
350 TRACE("(%s, %s, %x)\n", debugstr_w(mask), debugstr_w(name), case_sensitive);
352 if (!strcmpW( mask, asterisk_dot_asterisk )) return 1;
353 while (*name && *mask)
355 if (*mask == '*')
357 mask++;
358 while (*mask == '*') mask++; /* Skip consecutive '*' */
359 lastjoker = mask;
360 if (!*mask) return 1; /* end of mask is all '*', so match */
362 /* skip to the next match after the joker(s) */
363 if (case_sensitive) while (*name && (*name != *mask)) name++;
364 else while (*name && (toupperW(*name) != toupperW(*mask))) name++;
366 if (!*name) break;
367 next_to_retry = name;
369 else if (*mask != '?')
371 int mismatch = 0;
372 if (case_sensitive)
374 if (*mask != *name) mismatch = 1;
376 else
378 if (toupperW(*mask) != toupperW(*name)) mismatch = 1;
380 if (!mismatch)
382 mask++;
383 name++;
384 if (*mask == '\0')
386 if (*name == '\0')
387 return 1;
388 if (lastjoker)
389 mask = lastjoker;
392 else /* mismatch ! */
394 if (lastjoker) /* we had an '*', so we can try unlimitedly */
396 mask = lastjoker;
398 /* this scan sequence was a mismatch, so restart
399 * 1 char after the first char we checked last time */
400 next_to_retry++;
401 name = next_to_retry;
403 else
404 return 0; /* bad luck */
407 else /* '?' */
409 mask++;
410 name++;
413 while ((*mask == '.') || (*mask == '*'))
414 mask++; /* Ignore trailing '.' or '*' in mask */
415 return (!*name && !*mask);
419 /***********************************************************************
420 * DOSFS_AddDirEntry
422 * Used to construct an array of filenames in DOSFS_OpenDir
424 static BOOL DOSFS_AddDirEntry(DOS_DIR **dir, LPCWSTR name, LPCWSTR dosname)
426 int extra1 = strlenW(name) + 1;
427 int extra2 = strlenW(dosname) + 1;
429 /* if we need more, at minimum double the size */
430 if( (extra1 + extra2 + (*dir)->used) > (*dir)->size)
432 int more = (*dir)->size;
433 DOS_DIR *t;
435 if(more<(extra1+extra2))
436 more = extra1+extra2;
438 t = HeapReAlloc(GetProcessHeap(), 0, *dir, sizeof(**dir) +
439 ((*dir)->size + more)*sizeof(WCHAR) );
440 if(!t)
442 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
443 ERR("Out of memory caching directory structure %d %d %d\n",
444 (*dir)->size, more, (*dir)->used);
445 return FALSE;
447 (*dir) = t;
448 (*dir)->size += more;
451 /* at this point, the dir structure is big enough to hold these names */
452 strcpyW(&(*dir)->names[(*dir)->used], name);
453 (*dir)->used += extra1;
454 strcpyW(&(*dir)->names[(*dir)->used], dosname);
455 (*dir)->used += extra2;
457 return TRUE;
461 /***********************************************************************
462 * DOSFS_OpenDir_VFAT
464 static BOOL DOSFS_OpenDir_VFAT(UINT codepage, DOS_DIR **dir, const char *unix_path)
466 #ifdef VFAT_IOCTL_READDIR_BOTH
467 KERNEL_DIRENT de[2];
468 int fd = open( unix_path, O_RDONLY|O_DIRECTORY );
469 BOOL r = TRUE;
471 /* Check if the VFAT ioctl is supported on this directory */
473 if ( fd<0 )
474 return FALSE;
476 while (1)
478 WCHAR long_name[MAX_PATH];
479 WCHAR short_name[12];
481 r = (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de ) != -1);
482 if(!r)
483 break;
484 if (!de[0].d_reclen)
485 break;
486 MultiByteToWideChar(codepage, 0, de[0].d_name, -1, long_name, MAX_PATH);
487 if (!DOSFS_ToDosFCBFormat( long_name, short_name ))
488 short_name[0] = '\0';
489 if (de[1].d_name[0])
490 MultiByteToWideChar(codepage, 0, de[1].d_name, -1, long_name, MAX_PATH);
491 else
492 MultiByteToWideChar(codepage, 0, de[0].d_name, -1, long_name, MAX_PATH);
493 r = DOSFS_AddDirEntry(dir, long_name, short_name );
494 if(!r)
495 break;
497 if(r)
499 static const WCHAR empty_strW[] = { 0 };
500 DOSFS_AddDirEntry(dir, empty_strW, empty_strW);
502 close(fd);
503 return r;
504 #else
505 return FALSE;
506 #endif /* VFAT_IOCTL_READDIR_BOTH */
510 /***********************************************************************
511 * DOSFS_OpenDir_Normal
513 * Now use the standard opendir/readdir interface
515 static BOOL DOSFS_OpenDir_Normal( UINT codepage, DOS_DIR **dir, const char *unix_path )
517 DIR *unixdir = opendir( unix_path );
518 BOOL r = TRUE;
519 static const WCHAR empty_strW[] = { 0 };
521 if(!unixdir)
522 return FALSE;
523 while(1)
525 WCHAR long_name[MAX_PATH];
526 struct dirent *de = readdir(unixdir);
528 if(!de)
529 break;
530 MultiByteToWideChar(codepage, 0, de->d_name, -1, long_name, MAX_PATH);
531 r = DOSFS_AddDirEntry(dir, long_name, empty_strW);
532 if(!r)
533 break;
535 if(r)
536 DOSFS_AddDirEntry(dir, empty_strW, empty_strW);
537 closedir(unixdir);
538 return r;
541 /***********************************************************************
542 * DOSFS_OpenDir
544 static DOS_DIR *DOSFS_OpenDir( UINT codepage, const char *unix_path )
546 const int init_size = 0x100;
547 DOS_DIR *dir = HeapAlloc( GetProcessHeap(), 0, sizeof(*dir) + init_size*sizeof (WCHAR));
548 BOOL r;
550 TRACE("%s\n",debugstr_a(unix_path));
552 if (!dir)
554 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
555 return NULL;
557 dir->used = 0;
558 dir->size = init_size;
560 /* Treat empty path as root directory. This simplifies path split into
561 directory and mask in several other places */
562 if (!*unix_path) unix_path = "/";
564 r = DOSFS_OpenDir_VFAT( codepage, &dir, unix_path);
566 if(!r)
567 r = DOSFS_OpenDir_Normal( codepage, &dir, unix_path);
569 if(!r)
571 HeapFree(GetProcessHeap(), 0, dir);
572 return NULL;
574 dir->used = 0;
576 return dir;
580 /***********************************************************************
581 * DOSFS_CloseDir
583 static void DOSFS_CloseDir( DOS_DIR *dir )
585 HeapFree( GetProcessHeap(), 0, dir );
589 /***********************************************************************
590 * DOSFS_ReadDir
592 static BOOL DOSFS_ReadDir( DOS_DIR *dir, LPCWSTR *long_name,
593 LPCWSTR *short_name )
595 LPCWSTR sn, ln;
597 if (!dir)
598 return FALSE;
600 /* the long pathname is first */
601 ln = &dir->names[dir->used];
602 if(ln[0])
603 *long_name = ln;
604 else
605 return FALSE;
606 dir->used += (strlenW(ln) + 1);
608 /* followed by the short path name */
609 sn = &dir->names[dir->used];
610 if(sn[0])
611 *short_name = sn;
612 else
613 *short_name = NULL;
614 dir->used += (strlenW(sn) + 1);
616 return TRUE;
620 /***********************************************************************
621 * DOSFS_Hash
623 * Transform a Unix file name into a hashed DOS name. If the name is a valid
624 * DOS name, it is converted to upper-case; otherwise it is replaced by a
625 * hashed version that fits in 8.3 format.
626 * File name can be terminated by '\0', '\\' or '/'.
627 * 'buffer' must be at least 13 characters long.
629 static void DOSFS_Hash( LPCWSTR name, LPWSTR buffer, BOOL dir_format,
630 BOOL ignore_case )
632 static const char invalid_chars[] = INVALID_DOS_CHARS "~.";
633 static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
635 LPCWSTR p, ext;
636 LPWSTR dst;
637 unsigned short hash;
638 int i;
640 if (dir_format)
642 for(i = 0; i < 11; i++) buffer[i] = ' ';
643 buffer[11] = 0;
646 if (DOSFS_ValidDOSName( name, ignore_case ))
648 /* Check for '.' and '..' */
649 if (*name == '.')
651 buffer[0] = '.';
652 if (!dir_format) buffer[1] = buffer[2] = '\0';
653 if (name[1] == '.') buffer[1] = '.';
654 return;
657 /* Simply copy the name, converting to uppercase */
659 for (dst = buffer; !IS_END_OF_NAME(*name) && (*name != '.'); name++)
660 *dst++ = toupperW(*name);
661 if (*name == '.')
663 if (dir_format) dst = buffer + 8;
664 else *dst++ = '.';
665 for (name++; !IS_END_OF_NAME(*name); name++)
666 *dst++ = toupperW(*name);
668 if (!dir_format) *dst = '\0';
669 return;
672 /* Compute the hash code of the file name */
673 /* If you know something about hash functions, feel free to */
674 /* insert a better algorithm here... */
675 if (ignore_case)
677 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
678 hash = (hash<<3) ^ (hash>>5) ^ tolowerW(*p) ^ (tolowerW(p[1]) << 8);
679 hash = (hash<<3) ^ (hash>>5) ^ tolowerW(*p); /* Last character */
681 else
683 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
684 hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
685 hash = (hash << 3) ^ (hash >> 5) ^ *p; /* Last character */
688 /* Find last dot for start of the extension */
689 for (p = name+1, ext = NULL; !IS_END_OF_NAME(*p); p++)
690 if (*p == '.') ext = p;
691 if (ext && IS_END_OF_NAME(ext[1]))
692 ext = NULL; /* Empty extension ignored */
694 /* Copy first 4 chars, replacing invalid chars with '_' */
695 for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
697 if (IS_END_OF_NAME(*p) || (p == ext)) break;
698 *dst++ = (*p < 256 && strchr( invalid_chars, (char)*p )) ? '_' : toupperW(*p);
700 /* Pad to 5 chars with '~' */
701 while (i-- >= 0) *dst++ = '~';
703 /* Insert hash code converted to 3 ASCII chars */
704 *dst++ = hash_chars[(hash >> 10) & 0x1f];
705 *dst++ = hash_chars[(hash >> 5) & 0x1f];
706 *dst++ = hash_chars[hash & 0x1f];
708 /* Copy the first 3 chars of the extension (if any) */
709 if (ext)
711 if (!dir_format) *dst++ = '.';
712 for (i = 3, ext++; (i > 0) && !IS_END_OF_NAME(*ext); i--, ext++)
713 *dst++ = (*ext < 256 && strchr( invalid_chars, (char)*ext )) ? '_' : toupperW(*ext);
715 if (!dir_format) *dst = '\0';
719 /***********************************************************************
720 * DOSFS_FindUnixName
722 * Find the Unix file name in a given directory that corresponds to
723 * a file name (either in Unix or DOS format).
724 * File name can be terminated by '\0', '\\' or '/'.
725 * Return TRUE if OK, FALSE if no file name matches.
727 * 'long_buf' must be at least 'long_len' characters long. If the long name
728 * turns out to be larger than that, the function returns FALSE.
729 * 'short_buf' must be at least 13 characters long.
731 BOOL DOSFS_FindUnixName( const DOS_FULL_NAME *path, LPCWSTR name, char *long_buf,
732 INT long_len, LPWSTR short_buf, BOOL ignore_case)
734 DOS_DIR *dir;
735 LPCWSTR long_name, short_name;
736 WCHAR dos_name[12], tmp_buf[13];
737 BOOL ret;
739 LPCWSTR p = strchrW( name, '/' );
740 int len = p ? (int)(p - name) : strlenW(name);
741 if ((p = strchrW( name, '\\' ))) len = min( (int)(p - name), len );
742 /* Ignore trailing dots and spaces */
743 while (len > 1 && (name[len-1] == '.' || name[len-1] == ' ')) len--;
744 if (long_len < len + 1) return FALSE;
746 TRACE("%s,%s\n", path->long_name, debugstr_w(name) );
748 if (!DOSFS_ToDosFCBFormat( name, dos_name )) dos_name[0] = '\0';
750 if (!(dir = DOSFS_OpenDir( DRIVE_GetCodepage(path->drive), path->long_name )))
752 WARN("(%s,%s): can't open dir: %s\n",
753 path->long_name, debugstr_w(name), strerror(errno) );
754 return FALSE;
757 while ((ret = DOSFS_ReadDir( dir, &long_name, &short_name )))
759 /* Check against Unix name */
760 if (len == strlenW(long_name))
762 if (!ignore_case)
764 if (!strncmpW( long_name, name, len )) break;
766 else
768 if (!strncmpiW( long_name, name, len )) break;
771 if (dos_name[0])
773 /* Check against hashed DOS name */
774 if (!short_name)
776 DOSFS_Hash( long_name, tmp_buf, TRUE, ignore_case );
777 short_name = tmp_buf;
779 if (!strcmpW( dos_name, short_name )) break;
782 if (ret)
784 if (long_buf) WideCharToMultiByte(DRIVE_GetCodepage(path->drive), 0,
785 long_name, -1, long_buf, long_len, NULL, NULL);
786 if (short_buf)
788 if (short_name)
789 DOSFS_ToDosDTAFormat( short_name, short_buf );
790 else
791 DOSFS_Hash( long_name, short_buf, FALSE, ignore_case );
793 TRACE("(%s,%s) -> %s (%s)\n", path->long_name, debugstr_w(name),
794 debugstr_w(long_name), short_buf ? debugstr_w(short_buf) : "***");
796 else
797 WARN("%s not found in '%s'\n", debugstr_w(name), path->long_name);
798 DOSFS_CloseDir( dir );
799 return ret;
803 /***********************************************************************
804 * DOSFS_GetDevice
806 * Check if a DOS file name represents a DOS device and return the device.
808 const DOS_DEVICE *DOSFS_GetDevice( LPCWSTR name )
810 unsigned int i;
811 const WCHAR *p;
813 if (!name) return NULL; /* if wine_server_handle_to_fd was used */
814 if (name[0] && (name[1] == ':')) name += 2;
815 if ((p = strrchrW( name, '/' ))) name = p + 1;
816 if ((p = strrchrW( name, '\\' ))) name = p + 1;
817 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
819 const WCHAR *dev = DOSFS_Devices[i].name;
820 if (!strncmpiW( dev, name, strlenW(dev) ))
822 p = name + strlenW( dev );
823 if (!*p || (*p == '.') || (*p == ':')) return &DOSFS_Devices[i];
826 return NULL;
830 /***********************************************************************
831 * DOSFS_GetDeviceByHandle
833 const DOS_DEVICE *DOSFS_GetDeviceByHandle( HANDLE hFile )
835 const DOS_DEVICE *ret = NULL;
836 SERVER_START_REQ( get_device_id )
838 req->handle = hFile;
839 if (!wine_server_call( req ))
841 if ((reply->id >= 0) &&
842 (reply->id < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0])))
843 ret = &DOSFS_Devices[reply->id];
846 SERVER_END_REQ;
847 return ret;
851 /**************************************************************************
852 * DOSFS_CreateCommPort
854 static HANDLE DOSFS_CreateCommPort(LPCWSTR name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa)
856 HANDLE ret;
857 char devname[40];
858 WCHAR devnameW[40];
859 static const WCHAR serialportsW[] = {'s','e','r','i','a','l','p','o','r','t','s',0};
860 static const WCHAR empty_strW[] = { 0 };
862 TRACE_(file)("%s %lx %lx\n", debugstr_w(name), access, attributes);
864 PROFILE_GetWineIniString(serialportsW, name, empty_strW, devnameW, 40);
865 if(!devnameW[0])
866 return 0;
868 WideCharToMultiByte(CP_ACP, 0, devnameW, -1, devname, sizeof(devname), NULL, NULL);
870 TRACE("opening %s as %s\n", devname, debugstr_w(name));
872 SERVER_START_REQ( create_serial )
874 req->access = access;
875 req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
876 req->attributes = attributes;
877 req->sharing = FILE_SHARE_READ|FILE_SHARE_WRITE;
878 wine_server_add_data( req, devname, strlen(devname) );
879 SetLastError(0);
880 wine_server_call_err( req );
881 ret = reply->handle;
883 SERVER_END_REQ;
885 if(!ret)
886 ERR("Couldn't open device '%s' ! (check permissions)\n",devname);
887 else
888 TRACE("return %p\n", ret );
889 return ret;
892 /***********************************************************************
893 * DOSFS_OpenDevice
895 * Open a DOS device. This might not map 1:1 into the UNIX device concept.
896 * Returns 0 on failure.
898 HANDLE DOSFS_OpenDevice( LPCWSTR name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa )
900 unsigned int i;
901 const WCHAR *p;
902 HANDLE handle;
904 if (name[0] && (name[1] == ':')) name += 2;
905 if ((p = strrchrW( name, '/' ))) name = p + 1;
906 if ((p = strrchrW( name, '\\' ))) name = p + 1;
907 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
909 const WCHAR *dev = DOSFS_Devices[i].name;
910 if (!strncmpiW( dev, name, strlenW(dev) ))
912 p = name + strlenW( dev );
913 if (!*p || (*p == '.') || (*p == ':')) {
914 static const WCHAR nulW[] = {'N','U','L',0};
915 static const WCHAR conW[] = {'C','O','N',0};
916 static const WCHAR scsimgrW[] = {'S','C','S','I','M','G','R','$',0};
917 static const WCHAR hpscanW[] = {'H','P','S','C','A','N',0};
918 static const WCHAR emmxxxx0W[] = {'E','M','M','X','X','X','X','0',0};
919 /* got it */
920 if (!strcmpiW(DOSFS_Devices[i].name, nulW))
921 return FILE_CreateFile( "/dev/null", access,
922 FILE_SHARE_READ|FILE_SHARE_WRITE, sa,
923 OPEN_EXISTING, 0, 0, TRUE, DRIVE_UNKNOWN );
924 if (!strcmpiW(DOSFS_Devices[i].name, conW)) {
925 HANDLE to_dup;
926 switch (access & (GENERIC_READ|GENERIC_WRITE)) {
927 case GENERIC_READ:
928 to_dup = GetStdHandle( STD_INPUT_HANDLE );
929 break;
930 case GENERIC_WRITE:
931 to_dup = GetStdHandle( STD_OUTPUT_HANDLE );
932 break;
933 default:
934 FIXME("can't open CON read/write\n");
935 return 0;
937 if (!DuplicateHandle( GetCurrentProcess(), to_dup, GetCurrentProcess(),
938 &handle, 0,
939 sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle,
940 DUPLICATE_SAME_ACCESS ))
941 handle = 0;
942 return handle;
944 if (!strcmpiW(DOSFS_Devices[i].name, scsimgrW) ||
945 !strcmpiW(DOSFS_Devices[i].name, hpscanW) ||
946 !strcmpiW(DOSFS_Devices[i].name, emmxxxx0W))
948 return FILE_CreateDevice( i, access, sa );
951 if( (handle=DOSFS_CreateCommPort(DOSFS_Devices[i].name,access,attributes,sa)) )
952 return handle;
953 FIXME("device open %s not supported (yet)\n", debugstr_w(DOSFS_Devices[i].name));
954 return 0;
958 return 0;
962 /***********************************************************************
963 * DOSFS_GetPathDrive
965 * Get the drive specified by a given path name (DOS or Unix format).
967 static int DOSFS_GetPathDrive( LPCWSTR *name )
969 int drive;
970 LPCWSTR p = *name;
972 if (*p && (p[1] == ':'))
974 drive = toupperW(*p) - 'A';
975 *name += 2;
977 else if (*p == '/') /* Absolute Unix path? */
979 if ((drive = DRIVE_FindDriveRootW( name )) == -1)
981 MESSAGE("Warning: %s not accessible from a configured DOS drive\n", debugstr_w(*name) );
982 /* Assume it really was a DOS name */
983 drive = DRIVE_GetCurrentDrive();
986 else drive = DRIVE_GetCurrentDrive();
988 if (!DRIVE_IsValid(drive))
990 SetLastError( ERROR_INVALID_DRIVE );
991 return -1;
993 return drive;
997 /***********************************************************************
998 * DOSFS_GetFullName
1000 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
1001 * Unix name / short DOS name pair.
1002 * Return FALSE if one of the path components does not exist. The last path
1003 * component is only checked if 'check_last' is non-zero.
1004 * The buffers pointed to by 'long_buf' and 'short_buf' must be
1005 * at least MAX_PATHNAME_LEN long.
1007 BOOL DOSFS_GetFullName( LPCWSTR name, BOOL check_last, DOS_FULL_NAME *full )
1009 BOOL found;
1010 UINT flags, codepage;
1011 char *p_l, *root;
1012 LPWSTR p_s;
1013 static const WCHAR driveA_rootW[] = {'A',':','\\',0};
1014 static const WCHAR dos_rootW[] = {'\\',0};
1016 TRACE("%s (last=%d)\n", debugstr_w(name), check_last );
1018 if ((!*name) || (*name=='\n'))
1019 { /* error code for Win98 */
1020 SetLastError(ERROR_BAD_PATHNAME);
1021 return FALSE;
1024 if ((full->drive = DOSFS_GetPathDrive( &name )) == -1) return FALSE;
1025 flags = DRIVE_GetFlags( full->drive );
1026 codepage = DRIVE_GetCodepage(full->drive);
1028 lstrcpynA( full->long_name, DRIVE_GetRoot( full->drive ),
1029 sizeof(full->long_name) );
1030 if (full->long_name[1]) root = full->long_name + strlen(full->long_name);
1031 else root = full->long_name; /* root directory */
1033 strcpyW( full->short_name, driveA_rootW );
1034 full->short_name[0] += full->drive;
1036 if ((*name == '\\') || (*name == '/')) /* Absolute path */
1038 while ((*name == '\\') || (*name == '/')) name++;
1040 else /* Relative path */
1042 lstrcpynA( root + 1, DRIVE_GetUnixCwd( full->drive ),
1043 sizeof(full->long_name) - (root - full->long_name) - 1 );
1044 if (root[1]) *root = '/';
1045 lstrcpynW( full->short_name + 3, DRIVE_GetDosCwd( full->drive ),
1046 sizeof(full->short_name)/sizeof(full->short_name[0]) - 3 );
1049 p_l = full->long_name[1] ? full->long_name + strlen(full->long_name)
1050 : full->long_name;
1051 p_s = full->short_name[3] ? full->short_name + strlenW(full->short_name)
1052 : full->short_name + 2;
1053 found = TRUE;
1055 while (*name && found)
1057 /* Check for '.' and '..' */
1059 if (*name == '.')
1061 if (IS_END_OF_NAME(name[1]))
1063 name++;
1064 while ((*name == '\\') || (*name == '/')) name++;
1065 continue;
1067 else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
1069 name += 2;
1070 while ((*name == '\\') || (*name == '/')) name++;
1071 while ((p_l > root) && (*p_l != '/')) p_l--;
1072 while ((p_s > full->short_name + 2) && (*p_s != '\\')) p_s--;
1073 *p_l = *p_s = '\0'; /* Remove trailing separator */
1074 continue;
1078 /* Make sure buffers are large enough */
1080 if ((p_s >= full->short_name + sizeof(full->short_name)/sizeof(full->short_name[0]) - 14) ||
1081 (p_l >= full->long_name + sizeof(full->long_name) - 1))
1083 SetLastError( ERROR_PATH_NOT_FOUND );
1084 return FALSE;
1087 /* Get the long and short name matching the file name */
1089 if ((found = DOSFS_FindUnixName( full, name, p_l + 1,
1090 sizeof(full->long_name) - (p_l - full->long_name) - 1,
1091 p_s + 1, !(flags & DRIVE_CASE_SENSITIVE) )))
1093 *p_l++ = '/';
1094 p_l += strlen(p_l);
1095 *p_s++ = '\\';
1096 p_s += strlenW(p_s);
1097 while (!IS_END_OF_NAME(*name)) name++;
1099 else if (!check_last)
1101 *p_l++ = '/';
1102 *p_s++ = '\\';
1103 while (!IS_END_OF_NAME(*name) &&
1104 (p_s < full->short_name + sizeof(full->short_name)/sizeof(full->short_name[0]) - 1) &&
1105 (p_l < full->long_name + sizeof(full->long_name) - 1))
1107 WCHAR wch;
1108 *p_s++ = tolowerW(*name);
1109 /* If the drive is case-sensitive we want to create new */
1110 /* files in lower-case otherwise we can't reopen them */
1111 /* under the same short name. */
1112 if (flags & DRIVE_CASE_SENSITIVE) wch = tolowerW(*name);
1113 else wch = *name;
1114 p_l += WideCharToMultiByte(codepage, 0, &wch, 1, p_l, 2, NULL, NULL);
1115 name++;
1117 /* Ignore trailing dots and spaces */
1118 while(p_l[-1] == '.' || p_l[-1] == ' ') {
1119 --p_l;
1120 --p_s;
1122 *p_l = '\0';
1123 *p_s = '\0';
1125 while ((*name == '\\') || (*name == '/')) name++;
1128 if (!found)
1130 if (check_last)
1132 SetLastError( ERROR_FILE_NOT_FOUND );
1133 return FALSE;
1135 if (*name) /* Not last */
1137 SetLastError( ERROR_PATH_NOT_FOUND );
1138 return FALSE;
1141 if (!full->long_name[0]) strcpy( full->long_name, "/" );
1142 if (!full->short_name[2]) strcpyW( full->short_name + 2, dos_rootW );
1143 TRACE("returning %s = %s\n", full->long_name, debugstr_w(full->short_name) );
1144 return TRUE;
1148 /***********************************************************************
1149 * GetShortPathNameW (KERNEL32.@)
1151 * NOTES
1152 * observed:
1153 * longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1154 * longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0
1156 * more observations ( with NT 3.51 (WinDD) ):
1157 * longpath <= 8.3 -> just copy longpath to shortpath
1158 * longpath > 8.3 ->
1159 * a) file does not exist -> return 0, LastError = ERROR_FILE_NOT_FOUND
1160 * b) file does exist -> set the short filename.
1161 * - trailing slashes are reproduced in the short name, even if the
1162 * file is not a directory
1163 * - the absolute/relative path of the short name is reproduced like found
1164 * in the long name
1165 * - longpath and shortpath may have the same address
1166 * Peter Ganten, 1999
1168 DWORD WINAPI GetShortPathNameW( LPCWSTR longpath, LPWSTR shortpath, DWORD shortlen )
1170 DOS_FULL_NAME full_name;
1171 WCHAR tmpshortpath[MAX_PATHNAME_LEN];
1172 const WCHAR *p;
1173 DWORD sp = 0, lp = 0;
1174 int drive;
1175 DWORD tmplen;
1176 UINT flags;
1177 BOOL unixabsolute = *longpath == '/';
1179 TRACE("%s\n", debugstr_w(longpath));
1181 if (!longpath) {
1182 SetLastError(ERROR_INVALID_PARAMETER);
1183 return 0;
1185 if (!longpath[0]) {
1186 SetLastError(ERROR_BAD_PATHNAME);
1187 return 0;
1190 /* check for drive letter */
1191 if (!unixabsolute && longpath[1] == ':' ) {
1192 tmpshortpath[0] = longpath[0];
1193 tmpshortpath[1] = ':';
1194 sp = 2;
1197 if ( ( drive = DOSFS_GetPathDrive ( &longpath )) == -1 ) return 0;
1198 flags = DRIVE_GetFlags ( drive );
1200 if (unixabsolute && drive != DRIVE_GetCurrentDrive()) {
1201 tmpshortpath[0] = drive + 'A';
1202 tmpshortpath[1] = ':';
1203 sp = 2;
1206 while ( longpath[lp] ) {
1208 /* check for path delimiters and reproduce them */
1209 if ( longpath[lp] == '\\' || longpath[lp] == '/' ) {
1210 if (!sp || tmpshortpath[sp-1]!= '\\')
1212 /* strip double "\\" */
1213 tmpshortpath[sp] = '\\';
1214 sp++;
1216 tmpshortpath[sp]=0;/*terminate string*/
1217 lp++;
1218 continue;
1221 tmplen = 0;
1222 for(p = longpath + lp; *p && *p != '/' && *p != '\\'; p++)
1223 tmplen++;
1224 lstrcpynW(tmpshortpath + sp, longpath + lp, tmplen + 1);
1226 /* Check, if the current element is a valid dos name */
1227 if ( DOSFS_ValidDOSName ( longpath + lp, !(flags & DRIVE_CASE_SENSITIVE) ) ) {
1228 sp += tmplen;
1229 lp += tmplen;
1230 continue;
1233 /* Check if the file exists and use the existing file name */
1234 if ( DOSFS_GetFullName ( tmpshortpath, TRUE, &full_name ) ) {
1235 strcpyW(tmpshortpath + sp, strrchrW(full_name.short_name, '\\') + 1);
1236 sp += strlenW(tmpshortpath + sp);
1237 lp += tmplen;
1238 continue;
1241 TRACE("not found!\n" );
1242 SetLastError ( ERROR_FILE_NOT_FOUND );
1243 return 0;
1245 tmpshortpath[sp] = 0;
1247 tmplen = strlenW(tmpshortpath) + 1;
1248 if (tmplen <= shortlen)
1250 strcpyW(shortpath, tmpshortpath);
1251 TRACE("returning %s\n", debugstr_w(shortpath));
1252 tmplen--; /* length without 0 */
1255 return tmplen;
1259 /***********************************************************************
1260 * GetShortPathNameA (KERNEL32.@)
1262 DWORD WINAPI GetShortPathNameA( LPCSTR longpath, LPSTR shortpath, DWORD shortlen )
1264 UNICODE_STRING longpathW;
1265 WCHAR shortpathW[MAX_PATH];
1266 DWORD ret, retW;
1268 if (!longpath)
1270 SetLastError(ERROR_INVALID_PARAMETER);
1271 return 0;
1274 TRACE("%s\n", debugstr_a(longpath));
1276 if (!RtlCreateUnicodeStringFromAsciiz(&longpathW, longpath))
1278 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1279 return 0;
1282 retW = GetShortPathNameW(longpathW.Buffer, shortpathW, MAX_PATH);
1284 if (!retW)
1285 ret = 0;
1286 else if (retW > MAX_PATH)
1288 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1289 ret = 0;
1291 else
1293 ret = WideCharToMultiByte(CP_ACP, 0, shortpathW, -1, NULL, 0, NULL, NULL);
1294 if (ret <= shortlen)
1296 WideCharToMultiByte(CP_ACP, 0, shortpathW, -1, shortpath, shortlen, NULL, NULL);
1297 ret--; /* length without 0 */
1301 RtlFreeUnicodeString(&longpathW);
1302 return ret;
1306 /***********************************************************************
1307 * GetLongPathNameW (KERNEL32.@)
1309 * NOTES
1310 * observed (Win2000):
1311 * shortpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1312 * shortpath="": LastError=ERROR_PATH_NOT_FOUND, ret=0
1314 DWORD WINAPI GetLongPathNameW( LPCWSTR shortpath, LPWSTR longpath, DWORD longlen )
1316 DOS_FULL_NAME full_name;
1317 const char *root;
1318 LPWSTR p;
1319 int drive;
1320 UINT codepage;
1321 DWORD ret, len = 0;
1323 if (!shortpath) {
1324 SetLastError(ERROR_INVALID_PARAMETER);
1325 return 0;
1327 if (!shortpath[0]) {
1328 SetLastError(ERROR_PATH_NOT_FOUND);
1329 return 0;
1332 TRACE("%s,%p,%ld\n", debugstr_w(shortpath), longpath, longlen);
1334 if(shortpath[0]=='\\' && shortpath[1]=='\\')
1336 ERR("UNC pathname %s\n",debugstr_w(shortpath));
1337 lstrcpynW( longpath, full_name.short_name, longlen );
1338 return strlenW(longpath);
1341 if (!DOSFS_GetFullName( shortpath, TRUE, &full_name )) return 0;
1343 root = full_name.long_name;
1344 drive = DRIVE_FindDriveRoot(&root);
1345 codepage = DRIVE_GetCodepage(drive);
1347 ret = MultiByteToWideChar(codepage, 0, root, -1, NULL, 0);
1348 ret += 3; /* A:\ */
1349 /* reproduce terminating slash */
1350 if (ret > 4) /* if not drive root */
1352 len = strlenW(shortpath);
1353 if (shortpath[len - 1] == '\\' || shortpath[len - 1] == '/')
1354 len = 1;
1356 ret += len;
1357 if (ret <= longlen)
1359 longpath[0] = 'A' + drive;
1360 longpath[1] = ':';
1361 MultiByteToWideChar(codepage, 0, root, -1, longpath + 2, longlen - 2);
1362 for (p = longpath; *p; p++) if (*p == '/') *p = '\\';
1363 if (len)
1365 longpath[ret - 2] = '\\';
1366 longpath[ret - 1] = 0;
1368 TRACE("returning %s\n", debugstr_w(longpath));
1369 ret--; /* length without 0 */
1371 return ret;
1375 /***********************************************************************
1376 * GetLongPathNameA (KERNEL32.@)
1378 DWORD WINAPI GetLongPathNameA( LPCSTR shortpath, LPSTR longpath, DWORD longlen )
1380 UNICODE_STRING shortpathW;
1381 WCHAR longpathW[MAX_PATH];
1382 DWORD ret, retW;
1384 if (!shortpath)
1386 SetLastError(ERROR_INVALID_PARAMETER);
1387 return 0;
1390 TRACE("%s\n", debugstr_a(shortpath));
1392 if (!RtlCreateUnicodeStringFromAsciiz(&shortpathW, shortpath))
1394 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1395 return 0;
1398 retW = GetLongPathNameW(shortpathW.Buffer, longpathW, MAX_PATH);
1400 if (!retW)
1401 ret = 0;
1402 else if (retW > MAX_PATH)
1404 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1405 ret = 0;
1407 else
1409 ret = WideCharToMultiByte(CP_ACP, 0, longpathW, -1, NULL, 0, NULL, NULL);
1410 if (ret <= longlen)
1412 WideCharToMultiByte(CP_ACP, 0, longpathW, -1, longpath, longlen, NULL, NULL);
1413 ret--; /* length without 0 */
1417 RtlFreeUnicodeString(&shortpathW);
1418 return ret;
1422 /***********************************************************************
1423 * DOSFS_DoGetFullPathName
1425 * Implementation of GetFullPathNameA/W.
1427 * bon@elektron 000331:
1428 * A test for GetFullPathName with many pathological cases
1429 * now gives identical output for Wine and OSR2
1431 static DWORD DOSFS_DoGetFullPathName( LPCWSTR name, DWORD len, LPWSTR result )
1433 DWORD ret;
1434 DOS_FULL_NAME full_name;
1435 LPWSTR p, q;
1436 char *p_l;
1437 const char * root;
1438 WCHAR drivecur[] = {'C',':','.',0};
1439 WCHAR driveletter=0;
1440 int namelen,drive=0;
1441 static const WCHAR bkslashW[] = {'\\',0};
1442 static const WCHAR dotW[] = {'.',0};
1443 static const WCHAR updir_slashW[] = {'\\','.','.','\\',0};
1444 static const WCHAR curdirW[] = {'\\','.','\\',0};
1445 static const WCHAR updirW[] = {'\\','.','.',0};
1447 if (!name[0])
1449 SetLastError(ERROR_BAD_PATHNAME);
1450 return 0;
1453 TRACE("passed %s\n", debugstr_w(name));
1455 if (name[1]==':')
1456 /*drive letter given */
1458 driveletter = name[0];
1460 if ((name[1]==':') && ((name[2]=='\\') || (name[2]=='/')))
1461 /*absolute path given */
1463 strncpyW(full_name.short_name, name, MAX_PATHNAME_LEN);
1464 full_name.short_name[MAX_PATHNAME_LEN - 1] = 0; /* ensure 0 termination */
1465 drive = toupperW(name[0]) - 'A';
1467 else
1469 if (driveletter)
1470 drivecur[0]=driveletter;
1471 else if ((name[0]=='\\') || (name[0]=='/'))
1472 strcpyW(drivecur, bkslashW);
1473 else
1474 strcpyW(drivecur, dotW);
1476 if (!DOSFS_GetFullName( drivecur, FALSE, &full_name ))
1478 FIXME("internal: error getting drive/path\n");
1479 return 0;
1481 /* find path that drive letter substitutes*/
1482 drive = toupperW(full_name.short_name[0]) - 'A';
1483 root= DRIVE_GetRoot(drive);
1484 if (!root)
1486 FIXME("internal: error getting DOS Drive Root\n");
1487 return 0;
1489 if (!strcmp(root,"/"))
1491 /* we have just the last / and we need it. */
1492 p_l = full_name.long_name;
1494 else
1496 p_l = full_name.long_name + strlen(root);
1498 /* append long name (= unix name) to drive */
1499 MultiByteToWideChar(DRIVE_GetCodepage(drive), 0, p_l, -1,
1500 full_name.short_name + 2, MAX_PATHNAME_LEN - 3);
1501 /* append name to treat */
1502 namelen= strlenW(full_name.short_name);
1503 p = (LPWSTR)name;
1504 if (driveletter)
1505 p += 2; /* skip drive name when appending */
1506 if (namelen + 2 + strlenW(p) > MAX_PATHNAME_LEN)
1508 FIXME("internal error: buffer too small\n");
1509 return 0;
1511 full_name.short_name[namelen++] ='\\';
1512 full_name.short_name[namelen] = 0;
1513 strncpyW(full_name.short_name + namelen, p, MAX_PATHNAME_LEN - namelen);
1514 full_name.short_name[MAX_PATHNAME_LEN - 1] = 0; /* ensure 0 termination */
1516 /* reverse all slashes */
1517 for (p=full_name.short_name;
1518 p < full_name.short_name + strlenW(full_name.short_name);
1519 p++)
1521 if ( *p == '/' )
1522 *p = '\\';
1524 /* Use memmove, as areas overlap */
1525 /* Delete .. */
1526 while ((p = strstrW(full_name.short_name, updir_slashW)))
1528 if (p > full_name.short_name+2)
1530 *p = 0;
1531 q = strrchrW(full_name.short_name, '\\');
1532 memmove(q+1, p+4, (strlenW(p+4)+1) * sizeof(WCHAR));
1534 else
1536 memmove(full_name.short_name+3, p+4, (strlenW(p+4)+1) * sizeof(WCHAR));
1539 if ((full_name.short_name[2]=='.')&&(full_name.short_name[3]=='.'))
1541 /* This case istn't treated yet : c:..\test */
1542 memmove(full_name.short_name+2,full_name.short_name+4,
1543 (strlenW(full_name.short_name+4)+1) * sizeof(WCHAR));
1545 /* Delete . */
1546 while ((p = strstrW(full_name.short_name, curdirW)))
1548 *(p+1) = 0;
1549 memmove(p+1, p+3, (strlenW(p+3)+1) * sizeof(WCHAR));
1551 if (!(DRIVE_GetFlags(drive) & DRIVE_CASE_PRESERVING))
1552 for (p = full_name.short_name; *p; p++) *p = toupperW(*p);
1553 namelen = strlenW(full_name.short_name);
1554 if (!strcmpW(full_name.short_name+namelen-3, updirW))
1556 /* one more strange case: "c:\test\test1\.."
1557 return "c:\test" */
1558 *(full_name.short_name+namelen-3)=0;
1559 q = strrchrW(full_name.short_name, '\\');
1560 *q =0;
1562 if (full_name.short_name[namelen-1]=='.')
1563 full_name.short_name[(namelen--)-1] =0;
1564 if (!driveletter)
1565 if (full_name.short_name[namelen-1]=='\\')
1566 full_name.short_name[(namelen--)-1] =0;
1567 TRACE("got %s\n", debugstr_w(full_name.short_name));
1569 /* If the lpBuffer buffer is too small, the return value is the
1570 size of the buffer, in characters, required to hold the path
1571 plus the terminating \0 (tested against win95osr2, bon 001118)
1572 . */
1573 ret = strlenW(full_name.short_name);
1574 if (ret >= len )
1576 /* don't touch anything when the buffer is not large enough */
1577 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1578 return ret+1;
1580 if (result)
1582 strncpyW( result, full_name.short_name, len );
1583 result[len - 1] = 0; /* ensure 0 termination */
1586 TRACE("returning %s\n", debugstr_w(full_name.short_name) );
1587 return ret;
1591 /***********************************************************************
1592 * GetFullPathNameA (KERNEL32.@)
1593 * NOTES
1594 * if the path closed with '\', *lastpart is 0
1596 DWORD WINAPI GetFullPathNameA( LPCSTR name, DWORD len, LPSTR buffer,
1597 LPSTR *lastpart )
1599 UNICODE_STRING nameW;
1600 WCHAR bufferW[MAX_PATH];
1601 DWORD ret, retW;
1603 if (!name)
1605 SetLastError(ERROR_INVALID_PARAMETER);
1606 return 0;
1609 if (!RtlCreateUnicodeStringFromAsciiz(&nameW, name))
1611 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1612 return 0;
1615 retW = GetFullPathNameW( nameW.Buffer, MAX_PATH, bufferW, NULL);
1617 if (!retW)
1618 ret = 0;
1619 else if (retW > MAX_PATH)
1621 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1622 ret = 0;
1624 else
1626 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL);
1627 if (ret <= len)
1629 WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, len, NULL, NULL);
1630 ret--; /* length without 0 */
1632 if (lastpart)
1634 LPSTR p = buffer + strlen(buffer);
1636 if (*p != '\\')
1638 while ((p > buffer + 2) && (*p != '\\')) p--;
1639 *lastpart = p + 1;
1641 else *lastpart = NULL;
1646 RtlFreeUnicodeString(&nameW);
1647 return ret;
1651 /***********************************************************************
1652 * GetFullPathNameW (KERNEL32.@)
1654 DWORD WINAPI GetFullPathNameW( LPCWSTR name, DWORD len, LPWSTR buffer,
1655 LPWSTR *lastpart )
1657 DWORD ret = DOSFS_DoGetFullPathName( name, len, buffer );
1658 if (ret && (ret<=len) && buffer && lastpart)
1660 LPWSTR p = buffer + strlenW(buffer);
1661 if (*p != (WCHAR)'\\')
1663 while ((p > buffer + 2) && (*p != (WCHAR)'\\')) p--;
1664 *lastpart = p + 1;
1666 else *lastpart = NULL;
1668 return ret;
1672 /***********************************************************************
1673 * wine_get_unix_file_name (KERNEL32.@) Not a Windows API
1675 * Return the full Unix file name for a given path.
1676 * FIXME: convert dos file name to unicode
1678 BOOL WINAPI wine_get_unix_file_name( LPCSTR dos, LPSTR buffer, DWORD len )
1680 BOOL ret;
1681 DOS_FULL_NAME path;
1682 WCHAR dosW[MAX_PATHNAME_LEN];
1684 MultiByteToWideChar(CP_ACP, 0, dos, -1, dosW, MAX_PATHNAME_LEN);
1685 ret = DOSFS_GetFullName( dosW, FALSE, &path );
1686 if (ret && len)
1688 strncpy( buffer, path.long_name, len );
1689 buffer[len - 1] = 0; /* ensure 0 termination */
1691 return ret;
1695 /***********************************************************************
1696 * DOSFS_FindNextEx
1698 static int DOSFS_FindNextEx( FIND_FIRST_INFO *info, WIN32_FIND_DATAW *entry )
1700 DWORD attr = info->attr | FA_UNUSED | FA_ARCHIVE | FA_RDONLY;
1701 UINT flags = DRIVE_GetFlags( info->drive );
1702 char *p, buffer[MAX_PATHNAME_LEN];
1703 const char *drive_path;
1704 int drive_root;
1705 LPCWSTR long_name, short_name;
1706 BY_HANDLE_FILE_INFORMATION fileinfo;
1707 WCHAR dos_name[13];
1708 BOOL is_symlink;
1710 if ((info->attr & ~(FA_UNUSED | FA_ARCHIVE | FA_RDONLY)) == FA_LABEL)
1712 if (info->cur_pos) return 0;
1713 entry->dwFileAttributes = FILE_ATTRIBUTE_LABEL;
1714 RtlSecondsSince1970ToTime( (time_t)0, (LARGE_INTEGER *)&entry->ftCreationTime );
1715 RtlSecondsSince1970ToTime( (time_t)0, (LARGE_INTEGER *)&entry->ftLastAccessTime );
1716 RtlSecondsSince1970ToTime( (time_t)0, (LARGE_INTEGER *)&entry->ftLastWriteTime );
1717 entry->nFileSizeHigh = 0;
1718 entry->nFileSizeLow = 0;
1719 entry->dwReserved0 = 0;
1720 entry->dwReserved1 = 0;
1721 DOSFS_ToDosDTAFormat( DRIVE_GetLabel( info->drive ), entry->cFileName );
1722 strcpyW( entry->cAlternateFileName, entry->cFileName );
1723 info->cur_pos++;
1724 TRACE("returning %s (%s) as label\n",
1725 debugstr_w(entry->cFileName), debugstr_w(entry->cAlternateFileName));
1726 return 1;
1729 drive_path = info->path + strlen(DRIVE_GetRoot( info->drive ));
1730 while ((*drive_path == '/') || (*drive_path == '\\')) drive_path++;
1731 drive_root = !*drive_path;
1733 lstrcpynA( buffer, info->path, sizeof(buffer) - 1 );
1734 strcat( buffer, "/" );
1735 p = buffer + strlen(buffer);
1737 while (DOSFS_ReadDir( info->u.dos_dir, &long_name, &short_name ))
1739 info->cur_pos++;
1741 /* Don't return '.' and '..' in the root of the drive */
1742 if (drive_root && (long_name[0] == '.') &&
1743 (!long_name[1] || ((long_name[1] == '.') && !long_name[2])))
1744 continue;
1746 /* Check the long mask */
1748 if (info->long_mask && *info->long_mask)
1750 if (!DOSFS_MatchLong( info->long_mask, long_name,
1751 flags & DRIVE_CASE_SENSITIVE )) continue;
1754 /* Check the short mask */
1756 if (info->short_mask)
1758 if (!short_name)
1760 DOSFS_Hash( long_name, dos_name, TRUE,
1761 !(flags & DRIVE_CASE_SENSITIVE) );
1762 short_name = dos_name;
1764 if (!DOSFS_MatchShort( info->short_mask, short_name )) continue;
1767 /* Check the file attributes */
1768 WideCharToMultiByte(DRIVE_GetCodepage(info->drive), 0, long_name, -1,
1769 p, sizeof(buffer) - (int)(p - buffer), NULL, NULL);
1770 if (!FILE_Stat( buffer, &fileinfo, &is_symlink ))
1772 WARN("can't stat %s\n", buffer);
1773 continue;
1775 if (is_symlink && (fileinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
1777 static const WCHAR wineW[] = {'w','i','n','e',0};
1778 static const WCHAR ShowDirSymlinksW[] = {'S','h','o','w','D','i','r','S','y','m','l','i','n','k','s',0};
1779 static int show_dir_symlinks = -1;
1780 if (show_dir_symlinks == -1)
1781 show_dir_symlinks = PROFILE_GetWineIniBool(wineW, ShowDirSymlinksW, 0);
1782 if (!show_dir_symlinks) continue;
1785 if (fileinfo.dwFileAttributes & ~attr) continue;
1787 /* We now have a matching entry; fill the result and return */
1789 entry->dwFileAttributes = fileinfo.dwFileAttributes;
1790 entry->ftCreationTime = fileinfo.ftCreationTime;
1791 entry->ftLastAccessTime = fileinfo.ftLastAccessTime;
1792 entry->ftLastWriteTime = fileinfo.ftLastWriteTime;
1793 entry->nFileSizeHigh = fileinfo.nFileSizeHigh;
1794 entry->nFileSizeLow = fileinfo.nFileSizeLow;
1796 if (short_name)
1797 DOSFS_ToDosDTAFormat( short_name, entry->cAlternateFileName );
1798 else
1799 DOSFS_Hash( long_name, entry->cAlternateFileName, FALSE,
1800 !(flags & DRIVE_CASE_SENSITIVE) );
1802 lstrcpynW( entry->cFileName, long_name, sizeof(entry->cFileName)/sizeof(entry->cFileName[0]) );
1803 if (!(flags & DRIVE_CASE_PRESERVING)) strlwrW( entry->cFileName );
1804 TRACE("returning %s (%s) %02lx %ld\n",
1805 debugstr_w(entry->cFileName), debugstr_w(entry->cAlternateFileName),
1806 entry->dwFileAttributes, entry->nFileSizeLow );
1807 return 1;
1809 return 0; /* End of directory */
1812 /***********************************************************************
1813 * DOSFS_FindNext
1815 * Find the next matching file. Return the number of entries read to find
1816 * the matching one, or 0 if no more entries.
1817 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
1818 * file name mask. Either or both can be NULL.
1820 * NOTE: This is supposed to be only called by the int21 emulation
1821 * routines. Thus, we should own the Win16Mutex anyway.
1822 * Nevertheless, we explicitly enter it to ensure the static
1823 * directory cache is protected.
1825 int DOSFS_FindNext( const char *path, const char *short_mask,
1826 const char *long_mask, int drive, BYTE attr,
1827 int skip, WIN32_FIND_DATAA *entry )
1829 static FIND_FIRST_INFO info;
1830 LPCWSTR short_name, long_name;
1831 int count;
1832 UNICODE_STRING short_maskW, long_maskW;
1833 WIN32_FIND_DATAW entryW;
1835 TRACE("(%s, %s, %s, %x, %x, %x, %p)\n", debugstr_a(path),
1836 debugstr_a(short_mask), debugstr_a(long_mask), drive, attr, skip,
1837 entry);
1839 _EnterWin16Lock();
1841 RtlCreateUnicodeStringFromAsciiz(&short_maskW, short_mask);
1842 RtlCreateUnicodeStringFromAsciiz(&long_maskW, long_mask);
1844 /* Check the cached directory */
1845 if (!(info.u.dos_dir && info.path == path && !strcmpW(info.short_mask, short_maskW.Buffer)
1846 && !strcmpW(info.long_mask, long_maskW.Buffer) && info.drive == drive
1847 && info.attr == attr && info.cur_pos <= skip))
1849 /* Not in the cache, open it anew */
1850 if (info.u.dos_dir) DOSFS_CloseDir( info.u.dos_dir );
1852 info.path = (LPSTR)path;
1853 RtlFreeHeap(GetProcessHeap(), 0, info.long_mask);
1854 RtlFreeHeap(GetProcessHeap(), 0, info.short_mask);
1855 info.long_mask = long_maskW.Buffer;
1856 info.short_mask = short_maskW.Buffer;
1857 info.attr = attr;
1858 info.drive = drive;
1859 info.cur_pos = 0;
1860 info.u.dos_dir = DOSFS_OpenDir( DRIVE_GetCodepage(drive), info.path );
1862 else
1864 RtlFreeUnicodeString(&short_maskW);
1865 RtlFreeUnicodeString(&long_maskW);
1868 /* Skip to desired position */
1869 while (info.cur_pos < skip)
1870 if (info.u.dos_dir && DOSFS_ReadDir( info.u.dos_dir, &long_name, &short_name ))
1871 info.cur_pos++;
1872 else
1873 break;
1875 if (info.u.dos_dir && info.cur_pos == skip && DOSFS_FindNextEx( &info, &entryW ))
1877 WideCharToMultiByte(CP_ACP, 0, entryW.cFileName, -1,
1878 entry->cFileName, sizeof(entry->cFileName), NULL, NULL);
1879 WideCharToMultiByte(CP_ACP, 0, entryW.cAlternateFileName, -1,
1880 entry->cAlternateFileName, sizeof(entry->cAlternateFileName), NULL, NULL);
1881 count = info.cur_pos - skip;
1883 entry->dwFileAttributes = entryW.dwFileAttributes;
1884 entry->nFileSizeHigh = entryW.nFileSizeHigh;
1885 entry->nFileSizeLow = entryW.nFileSizeLow;
1886 entry->ftCreationTime = entryW.ftCreationTime;
1887 entry->ftLastAccessTime = entryW.ftLastAccessTime;
1888 entry->ftLastWriteTime = entryW.ftLastWriteTime;
1891 else
1892 count = 0;
1894 if (!count)
1896 if (info.u.dos_dir) DOSFS_CloseDir( info.u.dos_dir );
1897 memset( &info, '\0', sizeof(info) );
1900 _LeaveWin16Lock();
1902 return count;
1905 /*************************************************************************
1906 * FindFirstFileExW (KERNEL32.@)
1908 HANDLE WINAPI FindFirstFileExW(
1909 LPCWSTR lpFileName,
1910 FINDEX_INFO_LEVELS fInfoLevelId,
1911 LPVOID lpFindFileData,
1912 FINDEX_SEARCH_OPS fSearchOp,
1913 LPVOID lpSearchFilter,
1914 DWORD dwAdditionalFlags)
1916 HGLOBAL handle;
1917 FIND_FIRST_INFO *info;
1919 if (!lpFileName)
1921 SetLastError(ERROR_PATH_NOT_FOUND);
1922 return INVALID_HANDLE_VALUE;
1925 if ((fSearchOp != FindExSearchNameMatch) || (dwAdditionalFlags != 0))
1927 FIXME("options not implemented 0x%08x 0x%08lx\n", fSearchOp, dwAdditionalFlags );
1928 return INVALID_HANDLE_VALUE;
1931 switch(fInfoLevelId)
1933 case FindExInfoStandard:
1935 WIN32_FIND_DATAW * data = (WIN32_FIND_DATAW *) lpFindFileData;
1936 char *p;
1937 INT long_mask_len;
1938 UINT codepage;
1940 data->dwReserved0 = data->dwReserved1 = 0x0;
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 (lpFileName[0] && lpFileName[1] == ':')
1965 /* don't allow root directories */
1966 if (!lpFileName[2] ||
1967 ((lpFileName[2] == '/' || lpFileName[2] == '\\') && !lpFileName[3]))
1969 SetLastError(ERROR_FILE_NOT_FOUND);
1970 return INVALID_HANDLE_VALUE;
1973 if (!DOSFS_GetFullName( lpFileName, FALSE, &full_name )) break;
1974 if (!(handle = GlobalAlloc(GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO)))) break;
1975 info = (FIND_FIRST_INFO *)GlobalLock( handle );
1976 info->path = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
1977 strcpy( info->path, full_name.long_name );
1979 codepage = DRIVE_GetCodepage(full_name.drive);
1980 p = strrchr( info->path, '/' );
1981 *p++ = '\0';
1982 long_mask_len = MultiByteToWideChar(codepage, 0, p, -1, NULL, 0);
1983 info->long_mask = HeapAlloc( GetProcessHeap(), 0, long_mask_len * sizeof(WCHAR) );
1984 MultiByteToWideChar(codepage, 0, p, -1, info->long_mask, long_mask_len);
1986 info->short_mask = NULL;
1987 info->attr = 0xff;
1988 info->drive = full_name.drive;
1989 info->cur_pos = 0;
1991 info->u.dos_dir = DOSFS_OpenDir( codepage, info->path );
1992 GlobalUnlock( handle );
1994 if (!FindNextFileW( handle, data ))
1996 FindClose( handle );
1997 SetLastError( ERROR_FILE_NOT_FOUND );
1998 break;
2000 return handle;
2002 break;
2003 default:
2004 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
2006 return INVALID_HANDLE_VALUE;
2009 /*************************************************************************
2010 * FindFirstFileA (KERNEL32.@)
2012 HANDLE WINAPI FindFirstFileA(
2013 LPCSTR lpFileName,
2014 WIN32_FIND_DATAA *lpFindData )
2016 return FindFirstFileExA(lpFileName, FindExInfoStandard, lpFindData,
2017 FindExSearchNameMatch, NULL, 0);
2020 /*************************************************************************
2021 * FindFirstFileExA (KERNEL32.@)
2023 HANDLE WINAPI FindFirstFileExA(
2024 LPCSTR lpFileName,
2025 FINDEX_INFO_LEVELS fInfoLevelId,
2026 LPVOID lpFindFileData,
2027 FINDEX_SEARCH_OPS fSearchOp,
2028 LPVOID lpSearchFilter,
2029 DWORD dwAdditionalFlags)
2031 HANDLE handle;
2032 WIN32_FIND_DATAA *dataA;
2033 WIN32_FIND_DATAW dataW;
2034 UNICODE_STRING pathW;
2036 if (!lpFileName)
2038 SetLastError(ERROR_PATH_NOT_FOUND);
2039 return INVALID_HANDLE_VALUE;
2042 if (!RtlCreateUnicodeStringFromAsciiz(&pathW, lpFileName))
2044 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2045 return INVALID_HANDLE_VALUE;
2048 handle = FindFirstFileExW(pathW.Buffer, fInfoLevelId, &dataW, fSearchOp, lpSearchFilter, dwAdditionalFlags);
2049 RtlFreeUnicodeString(&pathW);
2050 if (handle == INVALID_HANDLE_VALUE) return handle;
2052 dataA = (WIN32_FIND_DATAA *) lpFindFileData;
2053 dataA->dwFileAttributes = dataW.dwFileAttributes;
2054 dataA->ftCreationTime = dataW.ftCreationTime;
2055 dataA->ftLastAccessTime = dataW.ftLastAccessTime;
2056 dataA->ftLastWriteTime = dataW.ftLastWriteTime;
2057 dataA->nFileSizeHigh = dataW.nFileSizeHigh;
2058 dataA->nFileSizeLow = dataW.nFileSizeLow;
2059 WideCharToMultiByte( CP_ACP, 0, dataW.cFileName, -1,
2060 dataA->cFileName, sizeof(dataA->cFileName), NULL, NULL );
2061 WideCharToMultiByte( CP_ACP, 0, dataW.cAlternateFileName, -1,
2062 dataA->cAlternateFileName, sizeof(dataA->cAlternateFileName), NULL, NULL );
2063 return handle;
2066 /*************************************************************************
2067 * FindFirstFileW (KERNEL32.@)
2069 HANDLE WINAPI FindFirstFileW( LPCWSTR lpFileName, WIN32_FIND_DATAW *lpFindData )
2071 return FindFirstFileExW(lpFileName, FindExInfoStandard, lpFindData,
2072 FindExSearchNameMatch, NULL, 0);
2075 /*************************************************************************
2076 * FindNextFileW (KERNEL32.@)
2078 BOOL WINAPI FindNextFileW( HANDLE handle, WIN32_FIND_DATAW *data )
2080 FIND_FIRST_INFO *info;
2081 BOOL ret = FALSE;
2082 DWORD gle = ERROR_NO_MORE_FILES;
2084 if ((handle == INVALID_HANDLE_VALUE) ||
2085 !(info = (FIND_FIRST_INFO *)GlobalLock( handle )))
2087 SetLastError( ERROR_INVALID_HANDLE );
2088 return ret;
2090 if (info->drive == -1)
2092 ret = SMB_FindNext( info->u.smb_dir, data );
2093 if(!ret)
2095 SMB_CloseDir( info->u.smb_dir );
2096 HeapFree( GetProcessHeap(), 0, info->path );
2098 goto done;
2100 else if (!info->path || !info->u.dos_dir)
2102 goto done;
2104 else if (!DOSFS_FindNextEx( info, data ))
2106 DOSFS_CloseDir( info->u.dos_dir ); info->u.dos_dir = NULL;
2107 HeapFree( GetProcessHeap(), 0, info->path );
2108 info->path = NULL;
2109 HeapFree( GetProcessHeap(), 0, info->long_mask );
2110 info->long_mask = NULL;
2111 goto done;
2113 ret = TRUE;
2114 done:
2115 GlobalUnlock( handle );
2116 if( !ret ) SetLastError( gle );
2117 return ret;
2121 /*************************************************************************
2122 * FindNextFileA (KERNEL32.@)
2124 BOOL WINAPI FindNextFileA( HANDLE handle, WIN32_FIND_DATAA *data )
2126 WIN32_FIND_DATAW dataW;
2127 if (!FindNextFileW( handle, &dataW )) return FALSE;
2128 data->dwFileAttributes = dataW.dwFileAttributes;
2129 data->ftCreationTime = dataW.ftCreationTime;
2130 data->ftLastAccessTime = dataW.ftLastAccessTime;
2131 data->ftLastWriteTime = dataW.ftLastWriteTime;
2132 data->nFileSizeHigh = dataW.nFileSizeHigh;
2133 data->nFileSizeLow = dataW.nFileSizeLow;
2134 WideCharToMultiByte( CP_ACP, 0, dataW.cFileName, -1,
2135 data->cFileName, sizeof(data->cFileName), NULL, NULL );
2136 WideCharToMultiByte( CP_ACP, 0, dataW.cAlternateFileName, -1,
2137 data->cAlternateFileName,
2138 sizeof(data->cAlternateFileName), NULL, NULL );
2139 return TRUE;
2142 /*************************************************************************
2143 * FindClose (KERNEL32.@)
2145 BOOL WINAPI FindClose( HANDLE handle )
2147 FIND_FIRST_INFO *info;
2149 if (handle == INVALID_HANDLE_VALUE) goto error;
2151 __TRY
2153 if ((info = (FIND_FIRST_INFO *)GlobalLock( handle )))
2155 if (info->u.dos_dir) DOSFS_CloseDir( info->u.dos_dir );
2156 if (info->path) HeapFree( GetProcessHeap(), 0, info->path );
2157 if (info->long_mask) HeapFree( GetProcessHeap(), 0, info->long_mask );
2160 __EXCEPT(page_fault)
2162 WARN("Illegal handle %p\n", handle);
2163 SetLastError( ERROR_INVALID_HANDLE );
2164 return FALSE;
2166 __ENDTRY
2167 if (!info) goto error;
2168 GlobalUnlock( handle );
2169 GlobalFree( handle );
2170 return TRUE;
2172 error:
2173 SetLastError( ERROR_INVALID_HANDLE );
2174 return FALSE;
2177 /***********************************************************************
2178 * DOSFS_UnixTimeToFileTime
2180 * Convert a Unix time to FILETIME format.
2181 * The FILETIME structure is a 64-bit value representing the number of
2182 * 100-nanosecond intervals since January 1, 1601, 0:00.
2183 * 'remainder' is the nonnegative number of 100-ns intervals
2184 * corresponding to the time fraction smaller than 1 second that
2185 * couldn't be stored in the time_t value.
2187 void DOSFS_UnixTimeToFileTime( time_t unix_time, FILETIME *filetime,
2188 DWORD remainder )
2190 /* NOTES:
2192 CONSTANTS:
2193 The time difference between 1 January 1601, 00:00:00 and
2194 1 January 1970, 00:00:00 is 369 years, plus the leap years
2195 from 1604 to 1968, excluding 1700, 1800, 1900.
2196 This makes (1968 - 1600) / 4 - 3 = 89 leap days, and a total
2197 of 134774 days.
2199 Any day in that period had 24 * 60 * 60 = 86400 seconds.
2201 The time difference is 134774 * 86400 * 10000000, which can be written
2202 116444736000000000
2203 27111902 * 2^32 + 3577643008
2204 413 * 2^48 + 45534 * 2^32 + 54590 * 2^16 + 32768
2206 If you find that these constants are buggy, please change them in all
2207 instances in both conversion functions.
2209 VERSIONS:
2210 There are two versions, one of them uses long long variables and
2211 is presumably faster but not ISO C. The other one uses standard C
2212 data types and operations but relies on the assumption that negative
2213 numbers are stored as 2's complement (-1 is 0xffff....). If this
2214 assumption is violated, dates before 1970 will not convert correctly.
2215 This should however work on any reasonable architecture where WINE
2216 will run.
2218 DETAILS:
2220 Take care not to remove the casts. I have tested these functions
2221 (in both versions) for a lot of numbers. I would be interested in
2222 results on other compilers than GCC.
2224 The operations have been designed to account for the possibility
2225 of 64-bit time_t in future UNICES. Even the versions without
2226 internal long long numbers will work if time_t only is 64 bit.
2227 A 32-bit shift, which was necessary for that operation, turned out
2228 not to work correctly in GCC, besides giving the warning. So I
2229 used a double 16-bit shift instead. Numbers are in the ISO version
2230 represented by three limbs, the most significant with 32 bit, the
2231 other two with 16 bit each.
2233 As the modulo-operator % is not well-defined for negative numbers,
2234 negative divisors have been avoided in DOSFS_FileTimeToUnixTime.
2236 There might be quicker ways to do this in C. Certainly so in
2237 assembler.
2239 Claus Fischer, fischer@iue.tuwien.ac.at
2242 #if SIZEOF_LONG_LONG >= 8
2243 # define USE_LONG_LONG 1
2244 #else
2245 # define USE_LONG_LONG 0
2246 #endif
2248 #if USE_LONG_LONG /* gcc supports long long type */
2250 long long int t = unix_time;
2251 t *= 10000000;
2252 t += 116444736000000000LL;
2253 t += remainder;
2254 filetime->dwLowDateTime = (UINT)t;
2255 filetime->dwHighDateTime = (UINT)(t >> 32);
2257 #else /* ISO version */
2259 UINT a0; /* 16 bit, low bits */
2260 UINT a1; /* 16 bit, medium bits */
2261 UINT a2; /* 32 bit, high bits */
2263 /* Copy the unix time to a2/a1/a0 */
2264 a0 = unix_time & 0xffff;
2265 a1 = (unix_time >> 16) & 0xffff;
2266 /* This is obsolete if unix_time is only 32 bits, but it does not hurt.
2267 Do not replace this by >> 32, it gives a compiler warning and it does
2268 not work. */
2269 a2 = (unix_time >= 0 ? (unix_time >> 16) >> 16 :
2270 ~((~unix_time >> 16) >> 16));
2272 /* Multiply a by 10000000 (a = a2/a1/a0)
2273 Split the factor into 10000 * 1000 which are both less than 0xffff. */
2274 a0 *= 10000;
2275 a1 = a1 * 10000 + (a0 >> 16);
2276 a2 = a2 * 10000 + (a1 >> 16);
2277 a0 &= 0xffff;
2278 a1 &= 0xffff;
2280 a0 *= 1000;
2281 a1 = a1 * 1000 + (a0 >> 16);
2282 a2 = a2 * 1000 + (a1 >> 16);
2283 a0 &= 0xffff;
2284 a1 &= 0xffff;
2286 /* Add the time difference and the remainder */
2287 a0 += 32768 + (remainder & 0xffff);
2288 a1 += 54590 + (remainder >> 16 ) + (a0 >> 16);
2289 a2 += 27111902 + (a1 >> 16);
2290 a0 &= 0xffff;
2291 a1 &= 0xffff;
2293 /* Set filetime */
2294 filetime->dwLowDateTime = (a1 << 16) + a0;
2295 filetime->dwHighDateTime = a2;
2296 #endif
2300 /***********************************************************************
2301 * DOSFS_FileTimeToUnixTime
2303 * Convert a FILETIME format to Unix time.
2304 * If not NULL, 'remainder' contains the fractional part of the filetime,
2305 * in the range of [0..9999999] (even if time_t is negative).
2307 time_t DOSFS_FileTimeToUnixTime( const FILETIME *filetime, DWORD *remainder )
2309 /* Read the comment in the function DOSFS_UnixTimeToFileTime. */
2310 #if USE_LONG_LONG
2312 long long int t = filetime->dwHighDateTime;
2313 t <<= 32;
2314 t += (UINT)filetime->dwLowDateTime;
2315 t -= 116444736000000000LL;
2316 if (t < 0)
2318 if (remainder) *remainder = 9999999 - (-t - 1) % 10000000;
2319 return -1 - ((-t - 1) / 10000000);
2321 else
2323 if (remainder) *remainder = t % 10000000;
2324 return t / 10000000;
2327 #else /* ISO version */
2329 UINT a0; /* 16 bit, low bits */
2330 UINT a1; /* 16 bit, medium bits */
2331 UINT a2; /* 32 bit, high bits */
2332 UINT r; /* remainder of division */
2333 unsigned int carry; /* carry bit for subtraction */
2334 int negative; /* whether a represents a negative value */
2336 /* Copy the time values to a2/a1/a0 */
2337 a2 = (UINT)filetime->dwHighDateTime;
2338 a1 = ((UINT)filetime->dwLowDateTime ) >> 16;
2339 a0 = ((UINT)filetime->dwLowDateTime ) & 0xffff;
2341 /* Subtract the time difference */
2342 if (a0 >= 32768 ) a0 -= 32768 , carry = 0;
2343 else a0 += (1 << 16) - 32768 , carry = 1;
2345 if (a1 >= 54590 + carry) a1 -= 54590 + carry, carry = 0;
2346 else a1 += (1 << 16) - 54590 - carry, carry = 1;
2348 a2 -= 27111902 + carry;
2350 /* If a is negative, replace a by (-1-a) */
2351 negative = (a2 >= ((UINT)1) << 31);
2352 if (negative)
2354 /* Set a to -a - 1 (a is a2/a1/a0) */
2355 a0 = 0xffff - a0;
2356 a1 = 0xffff - a1;
2357 a2 = ~a2;
2360 /* Divide a by 10000000 (a = a2/a1/a0), put the rest into r.
2361 Split the divisor into 10000 * 1000 which are both less than 0xffff. */
2362 a1 += (a2 % 10000) << 16;
2363 a2 /= 10000;
2364 a0 += (a1 % 10000) << 16;
2365 a1 /= 10000;
2366 r = a0 % 10000;
2367 a0 /= 10000;
2369 a1 += (a2 % 1000) << 16;
2370 a2 /= 1000;
2371 a0 += (a1 % 1000) << 16;
2372 a1 /= 1000;
2373 r += (a0 % 1000) * 10000;
2374 a0 /= 1000;
2376 /* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
2377 if (negative)
2379 /* Set a to -a - 1 (a is a2/a1/a0) */
2380 a0 = 0xffff - a0;
2381 a1 = 0xffff - a1;
2382 a2 = ~a2;
2384 r = 9999999 - r;
2387 if (remainder) *remainder = r;
2389 /* Do not replace this by << 32, it gives a compiler warning and it does
2390 not work. */
2391 return ((((time_t)a2) << 16) << 16) + (a1 << 16) + a0;
2392 #endif
2396 /***********************************************************************
2397 * MulDiv (KERNEL32.@)
2398 * RETURNS
2399 * Result of multiplication and division
2400 * -1: Overflow occurred or Divisor was 0
2402 INT WINAPI MulDiv(
2403 INT nMultiplicand,
2404 INT nMultiplier,
2405 INT nDivisor)
2407 #if SIZEOF_LONG_LONG >= 8
2408 long long ret;
2410 if (!nDivisor) return -1;
2412 /* We want to deal with a positive divisor to simplify the logic. */
2413 if (nDivisor < 0)
2415 nMultiplicand = - nMultiplicand;
2416 nDivisor = -nDivisor;
2419 /* If the result is positive, we "add" to round. else, we subtract to round. */
2420 if ( ( (nMultiplicand < 0) && (nMultiplier < 0) ) ||
2421 ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
2422 ret = (((long long)nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
2423 else
2424 ret = (((long long)nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
2426 if ((ret > 2147483647) || (ret < -2147483647)) return -1;
2427 return ret;
2428 #else
2429 if (!nDivisor) return -1;
2431 /* We want to deal with a positive divisor to simplify the logic. */
2432 if (nDivisor < 0)
2434 nMultiplicand = - nMultiplicand;
2435 nDivisor = -nDivisor;
2438 /* If the result is positive, we "add" to round. else, we subtract to round. */
2439 if ( ( (nMultiplicand < 0) && (nMultiplier < 0) ) ||
2440 ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
2441 return ((nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
2443 return ((nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
2445 #endif
2449 /***********************************************************************
2450 * DosDateTimeToFileTime (KERNEL32.@)
2452 BOOL WINAPI DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft)
2454 struct tm newtm;
2455 #ifndef HAVE_TIMEGM
2456 struct tm *gtm;
2457 time_t time1, time2;
2458 #endif
2460 newtm.tm_sec = (fattime & 0x1f) * 2;
2461 newtm.tm_min = (fattime >> 5) & 0x3f;
2462 newtm.tm_hour = (fattime >> 11);
2463 newtm.tm_mday = (fatdate & 0x1f);
2464 newtm.tm_mon = ((fatdate >> 5) & 0x0f) - 1;
2465 newtm.tm_year = (fatdate >> 9) + 80;
2466 #ifdef HAVE_TIMEGM
2467 RtlSecondsSince1970ToTime( timegm(&newtm), (LARGE_INTEGER *)ft );
2468 #else
2469 time1 = mktime(&newtm);
2470 gtm = gmtime(&time1);
2471 time2 = mktime(gtm);
2472 RtlSecondsSince1970ToTime( 2*time1-time2, (LARGE_INTEGER *)ft );
2473 #endif
2474 return TRUE;
2478 /***********************************************************************
2479 * FileTimeToDosDateTime (KERNEL32.@)
2481 BOOL WINAPI FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate,
2482 LPWORD fattime )
2484 time_t unixtime = DOSFS_FileTimeToUnixTime( ft, NULL );
2485 struct tm *tm = gmtime( &unixtime );
2486 if (fattime)
2487 *fattime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
2488 if (fatdate)
2489 *fatdate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
2490 + tm->tm_mday;
2491 return TRUE;
2495 /***********************************************************************
2496 * QueryDosDeviceA (KERNEL32.@)
2498 * returns array of strings terminated by \0, terminated by \0
2500 DWORD WINAPI QueryDosDeviceA(LPCSTR devname,LPSTR target,DWORD bufsize)
2502 LPSTR s;
2503 char buffer[200];
2505 TRACE("(%s,...)\n", devname ? devname : "<null>");
2506 if (!devname) {
2507 /* return known MSDOS devices */
2508 static const char devices[24] = "CON\0COM1\0COM2\0LPT1\0NUL\0\0";
2509 memcpy( target, devices, min(bufsize,sizeof(devices)) );
2510 return min(bufsize,sizeof(devices));
2512 /* In theory all that are possible and have been defined.
2513 * Now just those below, since mirc uses it to check for special files.
2515 * (It is more complex, and supports netmounted stuff, and \\.\ stuff,
2516 * but currently we just ignore that.)
2518 #define CHECK(x) (strstr(devname,#x)==devname)
2519 if (CHECK(con) || CHECK(com) || CHECK(lpt) || CHECK(nul)) {
2520 strcpy(buffer,"\\DEV\\");
2521 strcat(buffer,devname);
2522 if ((s=strchr(buffer,':'))) *s='\0';
2523 lstrcpynA(target,buffer,bufsize);
2524 return strlen(buffer)+1;
2525 } else {
2526 if (strchr(devname,':') || devname[0]=='\\') {
2527 /* This might be a DOS device we do not handle yet ... */
2528 FIXME("(%s) not detected as DOS device!\n",devname);
2530 SetLastError(ERROR_DEV_NOT_EXIST);
2531 return 0;
2537 /***********************************************************************
2538 * QueryDosDeviceW (KERNEL32.@)
2540 * returns array of strings terminated by \0, terminated by \0
2542 DWORD WINAPI QueryDosDeviceW(LPCWSTR devname,LPWSTR target,DWORD bufsize)
2544 LPSTR devnameA = devname?HEAP_strdupWtoA(GetProcessHeap(),0,devname):NULL;
2545 LPSTR targetA = (LPSTR)HeapAlloc(GetProcessHeap(),0,bufsize);
2546 DWORD ret = QueryDosDeviceA(devnameA,targetA,bufsize);
2548 ret = MultiByteToWideChar( CP_ACP, 0, targetA, ret, target, bufsize );
2549 if (devnameA) HeapFree(GetProcessHeap(),0,devnameA);
2550 if (targetA) HeapFree(GetProcessHeap(),0,targetA);
2551 return ret;
2555 /***********************************************************************
2556 * DefineDosDeviceA (KERNEL32.@)
2558 BOOL WINAPI DefineDosDeviceA(DWORD flags,LPCSTR devname,LPCSTR targetpath) {
2559 FIXME("(0x%08lx,%s,%s),stub!\n",flags,devname,targetpath);
2560 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2561 return FALSE;
2565 --- 16 bit functions ---
2568 /*************************************************************************
2569 * FindFirstFile (KERNEL.413)
2571 HANDLE16 WINAPI FindFirstFile16( LPCSTR path, WIN32_FIND_DATAA *data )
2573 DOS_FULL_NAME full_name;
2574 HGLOBAL16 handle;
2575 FIND_FIRST_INFO *info;
2576 WCHAR pathW[MAX_PATH];
2577 char *p;
2578 INT long_mask_len;
2579 UINT codepage;
2581 data->dwReserved0 = data->dwReserved1 = 0x0;
2582 if (!path) return INVALID_HANDLE_VALUE16;
2583 MultiByteToWideChar(CP_ACP, 0, path, -1, pathW, MAX_PATH);
2584 if (!DOSFS_GetFullName( pathW, FALSE, &full_name ))
2585 return INVALID_HANDLE_VALUE16;
2586 if (!(handle = GlobalAlloc16( GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO) )))
2587 return INVALID_HANDLE_VALUE16;
2588 info = (FIND_FIRST_INFO *)GlobalLock16( handle );
2589 info->path = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
2590 strcpy( info->path, full_name.long_name );
2592 codepage = DRIVE_GetCodepage(full_name.drive);
2593 p = strrchr( info->path, '/' );
2594 *p++ = '\0';
2595 long_mask_len = MultiByteToWideChar(codepage, 0, p, -1, NULL, 0);
2596 info->long_mask = HeapAlloc( GetProcessHeap(), 0, long_mask_len * sizeof(WCHAR) );
2597 MultiByteToWideChar(codepage, 0, p, -1, info->long_mask, long_mask_len);
2599 info->short_mask = NULL;
2600 info->attr = 0xff;
2601 info->drive = full_name.drive;
2602 info->cur_pos = 0;
2604 info->u.dos_dir = DOSFS_OpenDir( codepage, info->path );
2606 GlobalUnlock16( handle );
2607 if (!FindNextFile16( handle, data ))
2609 FindClose16( handle );
2610 SetLastError( ERROR_NO_MORE_FILES );
2611 return INVALID_HANDLE_VALUE16;
2613 return handle;
2616 /*************************************************************************
2617 * FindNextFile (KERNEL.414)
2619 BOOL16 WINAPI FindNextFile16( HANDLE16 handle, WIN32_FIND_DATAA *data )
2621 FIND_FIRST_INFO *info;
2622 WIN32_FIND_DATAW dataW;
2623 BOOL ret = FALSE;
2624 DWORD gle = ERROR_NO_MORE_FILES;
2626 if ((handle == INVALID_HANDLE_VALUE16) ||
2627 !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
2629 SetLastError( ERROR_INVALID_HANDLE );
2630 return ret;
2632 if (!info->path || !info->u.dos_dir)
2634 goto done;
2636 if (!DOSFS_FindNextEx( info, &dataW ))
2638 DOSFS_CloseDir( info->u.dos_dir ); info->u.dos_dir = NULL;
2639 HeapFree( GetProcessHeap(), 0, info->path );
2640 info->path = NULL;
2641 HeapFree( GetProcessHeap(), 0, info->long_mask );
2642 info->long_mask = NULL;
2643 goto done;
2646 ret = TRUE;
2648 data->dwFileAttributes = dataW.dwFileAttributes;
2649 data->ftCreationTime = dataW.ftCreationTime;
2650 data->ftLastAccessTime = dataW.ftLastAccessTime;
2651 data->ftLastWriteTime = dataW.ftLastWriteTime;
2652 data->nFileSizeHigh = dataW.nFileSizeHigh;
2653 data->nFileSizeLow = dataW.nFileSizeLow;
2654 WideCharToMultiByte( CP_ACP, 0, dataW.cFileName, -1,
2655 data->cFileName, sizeof(data->cFileName), NULL, NULL );
2656 WideCharToMultiByte( CP_ACP, 0, dataW.cAlternateFileName, -1,
2657 data->cAlternateFileName,
2658 sizeof(data->cAlternateFileName), NULL, NULL );
2659 done:
2660 if( !ret ) SetLastError( gle );
2661 GlobalUnlock16( handle );
2663 return ret;
2666 /*************************************************************************
2667 * FindClose (KERNEL.415)
2669 BOOL16 WINAPI FindClose16( HANDLE16 handle )
2671 FIND_FIRST_INFO *info;
2673 if ((handle == INVALID_HANDLE_VALUE16) ||
2674 !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
2676 SetLastError( ERROR_INVALID_HANDLE );
2677 return FALSE;
2679 if (info->u.dos_dir) DOSFS_CloseDir( info->u.dos_dir );
2680 if (info->path) HeapFree( GetProcessHeap(), 0, info->path );
2681 if (info->long_mask) HeapFree( GetProcessHeap(), 0, info->long_mask );
2682 GlobalUnlock16( handle );
2683 GlobalFree16( handle );
2684 return TRUE;