- moved the remaining of int21 calls to dlls/winedos
[wine/hacks.git] / files / dos_fs.c
blob7c3a1f847b86b2032635f4a0370d58272b6c0b8a
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 <stdarg.h>
33 #include <string.h>
34 #include <stdlib.h>
35 #include <sys/stat.h>
36 #ifdef HAVE_SYS_IOCTL_H
37 #include <sys/ioctl.h>
38 #endif
39 #include <time.h>
40 #ifdef HAVE_UNISTD_H
41 # include <unistd.h>
42 #endif
44 #define NONAMELESSUNION
45 #define NONAMELESSSTRUCT
46 #include "ntstatus.h"
47 #include "windef.h"
48 #include "winbase.h"
49 #include "winerror.h"
50 #include "wingdi.h"
52 #include "wine/unicode.h"
53 #include "wine/winbase16.h"
54 #include "drive.h"
55 #include "file.h"
56 #include "msdos.h"
57 #include "winternl.h"
58 #include "wine/server.h"
59 #include "excpt.h"
61 #include "smb.h"
63 #include "wine/debug.h"
65 WINE_DEFAULT_DEBUG_CHANNEL(dosfs);
66 WINE_DECLARE_DEBUG_CHANNEL(file);
68 /* Define the VFAT ioctl to get both short and long file names */
69 /* FIXME: is it possible to get this to work on other systems? */
70 #ifdef linux
71 /* We want the real kernel dirent structure, not the libc one */
72 typedef struct
74 long d_ino;
75 long d_off;
76 unsigned short d_reclen;
77 char d_name[256];
78 } KERNEL_DIRENT;
80 #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, KERNEL_DIRENT [2] )
82 /* To avoid blocking on non-directories in DOSFS_OpenDir_VFAT*/
83 #ifndef O_DIRECTORY
84 # define O_DIRECTORY 0200000 /* must be directory */
85 #endif
87 #else /* linux */
88 #undef VFAT_IOCTL_READDIR_BOTH /* just in case... */
89 #endif /* linux */
91 #define IS_OPTION_TRUE(ch) ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1')
93 /* Chars we don't want to see in DOS file names */
94 #define INVALID_DOS_CHARS "*?<>|\"+=,;[] \345"
96 static const DOS_DEVICE DOSFS_Devices[] =
97 /* name, device flags (see Int 21/AX=0x4400) */
99 { {'C','O','N',0}, 0xc0d3 },
100 { {'P','R','N',0}, 0xa0c0 },
101 { {'N','U','L',0}, 0x80c4 },
102 { {'A','U','X',0}, 0x80c0 },
103 { {'L','P','T','1',0}, 0xa0c0 },
104 { {'L','P','T','2',0}, 0xa0c0 },
105 { {'L','P','T','3',0}, 0xa0c0 },
106 { {'L','P','T','4',0}, 0xc0d3 },
107 { {'C','O','M','1',0}, 0x80c0 },
108 { {'C','O','M','2',0}, 0x80c0 },
109 { {'C','O','M','3',0}, 0x80c0 },
110 { {'C','O','M','4',0}, 0x80c0 },
111 { {'S','C','S','I','M','G','R','$',0}, 0xc0c0 },
112 { {'H','P','S','C','A','N',0}, 0xc0c0 },
113 { {'E','M','M','X','X','X','X','0',0}, 0x0000 }
116 static const WCHAR devW[] = {'\\','D','e','v','i','c','e','\\',0};
117 static const WCHAR dosW[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\',0};
119 static const WCHAR auxW[] = {'A','U','X',0};
120 static const WCHAR comW[] = {'C','O','M',0};
121 static const WCHAR lptW[] = {'L','P','T',0};
122 static const WCHAR nulW[] = {'N','U','L',0};
124 static const WCHAR nullW[] = {'N','u','l','l',0};
125 static const WCHAR parW[] = {'P','a','r','a','l','l','e','l',0};
126 static const WCHAR serW[] = {'S','e','r','i','a','l',0};
127 static const WCHAR oneW[] = {'1',0};
130 * Directory info for DOSFS_ReadDir
131 * contains the names of *all* the files in the directory
133 typedef struct
135 int used;
136 int size;
137 WCHAR names[1];
138 } DOS_DIR;
140 /* Info structure for FindFirstFile handle */
141 typedef struct
143 char *path; /* unix path */
144 LPWSTR long_mask;
145 int drive;
146 int cur_pos;
147 CRITICAL_SECTION cs;
148 union
150 DOS_DIR *dos_dir;
151 SMB_DIR *smb_dir;
152 } u;
153 } FIND_FIRST_INFO;
156 static WINE_EXCEPTION_FILTER(page_fault)
158 if (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION)
159 return EXCEPTION_EXECUTE_HANDLER;
160 return EXCEPTION_CONTINUE_SEARCH;
164 /***********************************************************************
165 * DOSFS_ValidDOSName
167 * Return 1 if Unix file 'name' is also a valid MS-DOS name
168 * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
169 * File name can be terminated by '\0', '\\' or '/'.
171 static int DOSFS_ValidDOSName( LPCWSTR name, int ignore_case )
173 static const char invalid_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS;
174 const WCHAR *p = name;
175 const char *invalid = ignore_case ? (invalid_chars + 26) : invalid_chars;
176 int len = 0;
178 if (*p == '.')
180 /* Check for "." and ".." */
181 p++;
182 if (*p == '.') p++;
183 /* All other names beginning with '.' are invalid */
184 return (IS_END_OF_NAME(*p));
186 while (!IS_END_OF_NAME(*p))
188 if (*p < 256 && strchr( invalid, (char)*p )) return 0; /* Invalid char */
189 if (*p == '.') break; /* Start of the extension */
190 if (++len > 8) return 0; /* Name too long */
191 p++;
193 if (*p != '.') return 1; /* End of name */
194 p++;
195 if (IS_END_OF_NAME(*p)) return 0; /* Empty extension not allowed */
196 len = 0;
197 while (!IS_END_OF_NAME(*p))
199 if (*p < 256 && strchr( invalid, (char)*p )) return 0; /* Invalid char */
200 if (*p == '.') return 0; /* Second extension not allowed */
201 if (++len > 3) return 0; /* Extension too long */
202 p++;
204 return 1;
208 /***********************************************************************
209 * DOSFS_ToDosFCBFormat
211 * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
212 * expanding wild cards and converting to upper-case in the process.
213 * File name can be terminated by '\0', '\\' or '/'.
214 * Return FALSE if the name is not a valid DOS name.
215 * 'buffer' must be at least 12 characters long.
217 BOOL DOSFS_ToDosFCBFormat( LPCWSTR name, LPWSTR buffer )
219 static const char invalid_chars[] = INVALID_DOS_CHARS;
220 LPCWSTR p = name;
221 int i;
223 /* Check for "." and ".." */
224 if (*p == '.')
226 p++;
227 buffer[0] = '.';
228 for(i = 1; i < 11; i++) buffer[i] = ' ';
229 buffer[11] = 0;
230 if (*p == '.')
232 buffer[1] = '.';
233 p++;
235 return (!*p || (*p == '/') || (*p == '\\'));
238 for (i = 0; i < 8; i++)
240 switch(*p)
242 case '\0':
243 case '\\':
244 case '/':
245 case '.':
246 buffer[i] = ' ';
247 break;
248 case '?':
249 p++;
250 /* fall through */
251 case '*':
252 buffer[i] = '?';
253 break;
254 default:
255 if (*p < 256 && strchr( invalid_chars, (char)*p )) return FALSE;
256 buffer[i] = toupperW(*p);
257 p++;
258 break;
262 if (*p == '*')
264 /* Skip all chars after wildcard up to first dot */
265 while (*p && (*p != '/') && (*p != '\\') && (*p != '.')) p++;
267 else
269 /* Check if name too long */
270 if (*p && (*p != '/') && (*p != '\\') && (*p != '.')) return FALSE;
272 if (*p == '.') p++; /* Skip dot */
274 for (i = 8; i < 11; i++)
276 switch(*p)
278 case '\0':
279 case '\\':
280 case '/':
281 buffer[i] = ' ';
282 break;
283 case '.':
284 return FALSE; /* Second extension not allowed */
285 case '?':
286 p++;
287 /* fall through */
288 case '*':
289 buffer[i] = '?';
290 break;
291 default:
292 if (*p < 256 && strchr( invalid_chars, (char)*p )) return FALSE;
293 buffer[i] = toupperW(*p);
294 p++;
295 break;
298 buffer[11] = '\0';
300 /* at most 3 character of the extension are processed
301 * is something behind this ?
303 while (*p == '*' || *p == ' ') p++; /* skip wildcards and spaces */
304 return IS_END_OF_NAME(*p);
308 /***********************************************************************
309 * DOSFS_ToDosDTAFormat
311 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
312 * converting to upper-case in the process.
313 * File name can be terminated by '\0', '\\' or '/'.
314 * 'buffer' must be at least 13 characters long.
316 static void DOSFS_ToDosDTAFormat( LPCWSTR name, LPWSTR buffer )
318 LPWSTR p;
320 memcpy( buffer, name, 8 * sizeof(WCHAR) );
321 p = buffer + 8;
322 while ((p > buffer) && (p[-1] == ' ')) p--;
323 *p++ = '.';
324 memcpy( p, name + 8, 3 * sizeof(WCHAR) );
325 p += 3;
326 while (p[-1] == ' ') p--;
327 if (p[-1] == '.') p--;
328 *p = '\0';
332 /***********************************************************************
333 * DOSFS_MatchLong
335 * Check a long file name against a mask.
337 * Tests (done in W95 DOS shell - case insensitive):
338 * *.txt test1.test.txt *
339 * *st1* test1.txt *
340 * *.t??????.t* test1.ta.tornado.txt *
341 * *tornado* test1.ta.tornado.txt *
342 * t*t test1.ta.tornado.txt *
343 * ?est* test1.txt *
344 * ?est??? test1.txt -
345 * *test1.txt* test1.txt *
346 * h?l?o*t.dat hellothisisatest.dat *
348 static int DOSFS_MatchLong( LPCWSTR mask, LPCWSTR name, int case_sensitive )
350 LPCWSTR lastjoker = NULL;
351 LPCWSTR next_to_retry = NULL;
352 static const WCHAR asterisk_dot_asterisk[] = {'*','.','*',0};
354 TRACE("(%s, %s, %x)\n", debugstr_w(mask), debugstr_w(name), case_sensitive);
356 if (!strcmpW( mask, asterisk_dot_asterisk )) return 1;
357 while (*name && *mask)
359 if (*mask == '*')
361 mask++;
362 while (*mask == '*') mask++; /* Skip consecutive '*' */
363 lastjoker = mask;
364 if (!*mask) return 1; /* end of mask is all '*', so match */
366 /* skip to the next match after the joker(s) */
367 if (case_sensitive) while (*name && (*name != *mask)) name++;
368 else while (*name && (toupperW(*name) != toupperW(*mask))) name++;
370 if (!*name) break;
371 next_to_retry = name;
373 else if (*mask != '?')
375 int mismatch = 0;
376 if (case_sensitive)
378 if (*mask != *name) mismatch = 1;
380 else
382 if (toupperW(*mask) != toupperW(*name)) mismatch = 1;
384 if (!mismatch)
386 mask++;
387 name++;
388 if (*mask == '\0')
390 if (*name == '\0')
391 return 1;
392 if (lastjoker)
393 mask = lastjoker;
396 else /* mismatch ! */
398 if (lastjoker) /* we had an '*', so we can try unlimitedly */
400 mask = lastjoker;
402 /* this scan sequence was a mismatch, so restart
403 * 1 char after the first char we checked last time */
404 next_to_retry++;
405 name = next_to_retry;
407 else
408 return 0; /* bad luck */
411 else /* '?' */
413 mask++;
414 name++;
417 while ((*mask == '.') || (*mask == '*'))
418 mask++; /* Ignore trailing '.' or '*' in mask */
419 return (!*name && !*mask);
423 /***********************************************************************
424 * DOSFS_AddDirEntry
426 * Used to construct an array of filenames in DOSFS_OpenDir
428 static BOOL DOSFS_AddDirEntry(DOS_DIR **dir, LPCWSTR name, LPCWSTR dosname)
430 int extra1 = strlenW(name) + 1;
431 int extra2 = strlenW(dosname) + 1;
433 /* if we need more, at minimum double the size */
434 if( (extra1 + extra2 + (*dir)->used) > (*dir)->size)
436 int more = (*dir)->size;
437 DOS_DIR *t;
439 if(more<(extra1+extra2))
440 more = extra1+extra2;
442 t = HeapReAlloc(GetProcessHeap(), 0, *dir, sizeof(**dir) +
443 ((*dir)->size + more)*sizeof(WCHAR) );
444 if(!t)
446 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
447 ERR("Out of memory caching directory structure %d %d %d\n",
448 (*dir)->size, more, (*dir)->used);
449 return FALSE;
451 (*dir) = t;
452 (*dir)->size += more;
455 /* at this point, the dir structure is big enough to hold these names */
456 strcpyW(&(*dir)->names[(*dir)->used], name);
457 (*dir)->used += extra1;
458 strcpyW(&(*dir)->names[(*dir)->used], dosname);
459 (*dir)->used += extra2;
461 return TRUE;
465 /***********************************************************************
466 * DOSFS_OpenDir_VFAT
468 static BOOL DOSFS_OpenDir_VFAT(DOS_DIR **dir, const char *unix_path)
470 #ifdef VFAT_IOCTL_READDIR_BOTH
471 KERNEL_DIRENT de[2];
472 int fd = open( unix_path, O_RDONLY|O_DIRECTORY );
473 BOOL r = TRUE;
475 /* Check if the VFAT ioctl is supported on this directory */
477 if ( fd<0 )
478 return FALSE;
480 while (1)
482 WCHAR long_name[MAX_PATH];
483 WCHAR short_name[12];
485 r = (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de ) != -1);
486 if(!r)
487 break;
488 if (!de[0].d_reclen)
489 break;
490 MultiByteToWideChar(CP_UNIXCP, 0, de[0].d_name, -1, long_name, MAX_PATH);
491 if (!DOSFS_ToDosFCBFormat( long_name, short_name ))
492 short_name[0] = '\0';
493 if (de[1].d_name[0])
494 MultiByteToWideChar(CP_UNIXCP, 0, de[1].d_name, -1, long_name, MAX_PATH);
495 else
496 MultiByteToWideChar(CP_UNIXCP, 0, de[0].d_name, -1, long_name, MAX_PATH);
497 r = DOSFS_AddDirEntry(dir, long_name, short_name );
498 if(!r)
499 break;
501 if(r)
503 static const WCHAR empty_strW[] = { 0 };
504 DOSFS_AddDirEntry(dir, empty_strW, empty_strW);
506 close(fd);
507 return r;
508 #else
509 return FALSE;
510 #endif /* VFAT_IOCTL_READDIR_BOTH */
514 /***********************************************************************
515 * DOSFS_OpenDir_Normal
517 * Now use the standard opendir/readdir interface
519 static BOOL DOSFS_OpenDir_Normal( DOS_DIR **dir, const char *unix_path )
521 DIR *unixdir = opendir( unix_path );
522 BOOL r = TRUE;
523 static const WCHAR empty_strW[] = { 0 };
525 if(!unixdir)
526 return FALSE;
527 while(1)
529 WCHAR long_name[MAX_PATH];
530 struct dirent *de = readdir(unixdir);
532 if(!de)
533 break;
534 MultiByteToWideChar(CP_UNIXCP, 0, de->d_name, -1, long_name, MAX_PATH);
535 r = DOSFS_AddDirEntry(dir, long_name, empty_strW);
536 if(!r)
537 break;
539 if(r)
540 DOSFS_AddDirEntry(dir, empty_strW, empty_strW);
541 closedir(unixdir);
542 return r;
545 /***********************************************************************
546 * DOSFS_OpenDir
548 static DOS_DIR *DOSFS_OpenDir( const char *unix_path )
550 const int init_size = 0x100;
551 DOS_DIR *dir = HeapAlloc( GetProcessHeap(), 0, sizeof(*dir) + init_size*sizeof (WCHAR));
552 BOOL r;
554 TRACE("%s\n",debugstr_a(unix_path));
556 if (!dir)
558 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
559 return NULL;
561 dir->used = 0;
562 dir->size = init_size;
564 /* Treat empty path as root directory. This simplifies path split into
565 directory and mask in several other places */
566 if (!*unix_path) unix_path = "/";
568 r = DOSFS_OpenDir_VFAT( &dir, unix_path);
570 if(!r)
571 r = DOSFS_OpenDir_Normal( &dir, unix_path);
573 if(!r)
575 HeapFree(GetProcessHeap(), 0, dir);
576 return NULL;
578 dir->used = 0;
580 return dir;
584 /***********************************************************************
585 * DOSFS_CloseDir
587 static void DOSFS_CloseDir( DOS_DIR *dir )
589 HeapFree( GetProcessHeap(), 0, dir );
593 /***********************************************************************
594 * DOSFS_ReadDir
596 static BOOL DOSFS_ReadDir( DOS_DIR *dir, LPCWSTR *long_name,
597 LPCWSTR *short_name )
599 LPCWSTR sn, ln;
601 if (!dir)
602 return FALSE;
604 /* the long pathname is first */
605 ln = &dir->names[dir->used];
606 if(ln[0])
607 *long_name = ln;
608 else
609 return FALSE;
610 dir->used += (strlenW(ln) + 1);
612 /* followed by the short path name */
613 sn = &dir->names[dir->used];
614 if(sn[0])
615 *short_name = sn;
616 else
617 *short_name = NULL;
618 dir->used += (strlenW(sn) + 1);
620 return TRUE;
624 /***********************************************************************
625 * DOSFS_Hash
627 * Transform a Unix file name into a hashed DOS name. If the name is a valid
628 * DOS name, it is converted to upper-case; otherwise it is replaced by a
629 * hashed version that fits in 8.3 format.
630 * File name can be terminated by '\0', '\\' or '/'.
631 * 'buffer' must be at least 13 characters long.
633 static void DOSFS_Hash( LPCWSTR name, LPWSTR buffer, BOOL dir_format,
634 BOOL ignore_case )
636 static const char invalid_chars[] = INVALID_DOS_CHARS "~.";
637 static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
639 LPCWSTR p, ext;
640 LPWSTR dst;
641 unsigned short hash;
642 int i;
644 if (dir_format)
646 for(i = 0; i < 11; i++) buffer[i] = ' ';
647 buffer[11] = 0;
650 if (DOSFS_ValidDOSName( name, ignore_case ))
652 /* Check for '.' and '..' */
653 if (*name == '.')
655 buffer[0] = '.';
656 if (!dir_format) buffer[1] = buffer[2] = '\0';
657 if (name[1] == '.') buffer[1] = '.';
658 return;
661 /* Simply copy the name, converting to uppercase */
663 for (dst = buffer; !IS_END_OF_NAME(*name) && (*name != '.'); name++)
664 *dst++ = toupperW(*name);
665 if (*name == '.')
667 if (dir_format) dst = buffer + 8;
668 else *dst++ = '.';
669 for (name++; !IS_END_OF_NAME(*name); name++)
670 *dst++ = toupperW(*name);
672 if (!dir_format) *dst = '\0';
673 return;
676 /* Compute the hash code of the file name */
677 /* If you know something about hash functions, feel free to */
678 /* insert a better algorithm here... */
679 if (ignore_case)
681 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
682 hash = (hash<<3) ^ (hash>>5) ^ tolowerW(*p) ^ (tolowerW(p[1]) << 8);
683 hash = (hash<<3) ^ (hash>>5) ^ tolowerW(*p); /* Last character */
685 else
687 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
688 hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
689 hash = (hash << 3) ^ (hash >> 5) ^ *p; /* Last character */
692 /* Find last dot for start of the extension */
693 for (p = name+1, ext = NULL; !IS_END_OF_NAME(*p); p++)
694 if (*p == '.') ext = p;
695 if (ext && IS_END_OF_NAME(ext[1]))
696 ext = NULL; /* Empty extension ignored */
698 /* Copy first 4 chars, replacing invalid chars with '_' */
699 for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
701 if (IS_END_OF_NAME(*p) || (p == ext)) break;
702 *dst++ = (*p < 256 && strchr( invalid_chars, (char)*p )) ? '_' : toupperW(*p);
704 /* Pad to 5 chars with '~' */
705 while (i-- >= 0) *dst++ = '~';
707 /* Insert hash code converted to 3 ASCII chars */
708 *dst++ = hash_chars[(hash >> 10) & 0x1f];
709 *dst++ = hash_chars[(hash >> 5) & 0x1f];
710 *dst++ = hash_chars[hash & 0x1f];
712 /* Copy the first 3 chars of the extension (if any) */
713 if (ext)
715 if (!dir_format) *dst++ = '.';
716 for (i = 3, ext++; (i > 0) && !IS_END_OF_NAME(*ext); i--, ext++)
717 *dst++ = (*ext < 256 && strchr( invalid_chars, (char)*ext )) ? '_' : toupperW(*ext);
719 if (!dir_format) *dst = '\0';
723 /***********************************************************************
724 * DOSFS_FindUnixName
726 * Find the Unix file name in a given directory that corresponds to
727 * a file name (either in Unix or DOS format).
728 * File name can be terminated by '\0', '\\' or '/'.
729 * Return TRUE if OK, FALSE if no file name matches.
731 * 'long_buf' must be at least 'long_len' characters long. If the long name
732 * turns out to be larger than that, the function returns FALSE.
733 * 'short_buf' must be at least 13 characters long.
735 BOOL DOSFS_FindUnixName( const DOS_FULL_NAME *path, LPCWSTR name, char *long_buf,
736 INT long_len, LPWSTR short_buf, BOOL ignore_case)
738 DOS_DIR *dir;
739 LPCWSTR long_name, short_name;
740 WCHAR dos_name[12], tmp_buf[13];
741 BOOL ret;
743 LPCWSTR p = strchrW( name, '/' );
744 int len = p ? (int)(p - name) : strlenW(name);
745 if ((p = strchrW( name, '\\' ))) len = min( (int)(p - name), len );
746 /* Ignore trailing dots and spaces */
747 while (len > 1 && (name[len-1] == '.' || name[len-1] == ' ')) len--;
748 if (long_len < len + 1) return FALSE;
750 TRACE("%s,%s\n", path->long_name, debugstr_w(name) );
752 if (!DOSFS_ToDosFCBFormat( name, dos_name )) dos_name[0] = '\0';
754 if (!(dir = DOSFS_OpenDir( 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(CP_UNIXCP, 0, long_name, -1, long_buf, long_len, NULL, NULL);
789 if (short_buf)
791 if (short_name)
792 DOSFS_ToDosDTAFormat( short_name, short_buf );
793 else
794 DOSFS_Hash( long_name, short_buf, FALSE, ignore_case );
796 TRACE("(%s,%s) -> %s (%s)\n", path->long_name, debugstr_w(name),
797 debugstr_w(long_name), short_buf ? debugstr_w(short_buf) : "***");
799 else
800 WARN("%s not found in '%s'\n", debugstr_w(name), path->long_name);
801 DOSFS_CloseDir( dir );
802 return ret;
806 /***********************************************************************
807 * DOSFS_GetDevice
809 * Check if a DOS file name represents a DOS device and return the device.
811 const DOS_DEVICE *DOSFS_GetDevice( LPCWSTR name )
813 unsigned int i;
814 const WCHAR *p;
816 if (!name) return NULL; /* if wine_server_handle_to_fd was used */
817 if (name[0] && (name[1] == ':')) name += 2;
818 if ((p = strrchrW( name, '/' ))) name = p + 1;
819 if ((p = strrchrW( name, '\\' ))) name = p + 1;
820 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
822 const WCHAR *dev = DOSFS_Devices[i].name;
823 if (!strncmpiW( dev, name, strlenW(dev) ))
825 p = name + strlenW( dev );
826 if (!*p || (*p == '.') || (*p == ':')) return &DOSFS_Devices[i];
829 return NULL;
833 /***********************************************************************
834 * DOSFS_GetDeviceByHandle
836 const DOS_DEVICE *DOSFS_GetDeviceByHandle( HANDLE hFile )
838 const DOS_DEVICE *ret = NULL;
839 SERVER_START_REQ( get_device_id )
841 req->handle = hFile;
842 if (!wine_server_call( req ))
844 if ((reply->id >= 0) &&
845 (reply->id < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0])))
846 ret = &DOSFS_Devices[reply->id];
849 SERVER_END_REQ;
850 return ret;
854 /**************************************************************************
855 * DOSFS_CreateCommPort
857 static HANDLE DOSFS_CreateCommPort(LPCWSTR name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa)
859 HANDLE ret;
860 HKEY hkey;
861 DWORD dummy;
862 OBJECT_ATTRIBUTES attr;
863 UNICODE_STRING nameW;
864 WCHAR *devnameW;
865 char tmp[128];
866 char devname[40];
868 static const WCHAR serialportsW[] = {'M','a','c','h','i','n','e','\\',
869 'S','o','f','t','w','a','r','e','\\',
870 'W','i','n','e','\\','W','i','n','e','\\',
871 'C','o','n','f','i','g','\\',
872 'S','e','r','i','a','l','P','o','r','t','s',0};
874 TRACE_(file)("%s %lx %lx\n", debugstr_w(name), access, attributes);
876 attr.Length = sizeof(attr);
877 attr.RootDirectory = 0;
878 attr.ObjectName = &nameW;
879 attr.Attributes = 0;
880 attr.SecurityDescriptor = NULL;
881 attr.SecurityQualityOfService = NULL;
882 RtlInitUnicodeString( &nameW, serialportsW );
884 if (NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr )) return 0;
886 RtlInitUnicodeString( &nameW, name );
887 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
888 devnameW = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
889 else
890 devnameW = NULL;
892 NtClose( hkey );
894 if (!devnameW) return 0;
895 WideCharToMultiByte(CP_ACP, 0, devnameW, -1, devname, sizeof(devname), NULL, NULL);
897 TRACE("opening %s as %s\n", devname, debugstr_w(name));
899 SERVER_START_REQ( create_serial )
901 req->access = access;
902 req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
903 req->attributes = attributes;
904 req->sharing = FILE_SHARE_READ|FILE_SHARE_WRITE;
905 wine_server_add_data( req, devname, strlen(devname) );
906 SetLastError(0);
907 wine_server_call_err( req );
908 ret = reply->handle;
910 SERVER_END_REQ;
912 if(!ret)
913 ERR("Couldn't open device '%s' ! (check permissions)\n",devname);
914 else
915 TRACE("return %p\n", ret );
916 return ret;
919 /***********************************************************************
920 * DOSFS_OpenDevice
922 * Open a DOS device. This might not map 1:1 into the UNIX device concept.
923 * Returns 0 on failure.
925 HANDLE DOSFS_OpenDevice( LPCWSTR name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa )
927 unsigned int i;
928 const WCHAR *p;
929 HANDLE handle;
931 if (name[0] && (name[1] == ':')) name += 2;
932 if ((p = strrchrW( name, '/' ))) name = p + 1;
933 if ((p = strrchrW( name, '\\' ))) name = p + 1;
934 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
936 const WCHAR *dev = DOSFS_Devices[i].name;
937 if (!strncmpiW( dev, name, strlenW(dev) ))
939 p = name + strlenW( dev );
940 if (!*p || (*p == '.') || (*p == ':')) {
941 static const WCHAR nulW[] = {'N','U','L',0};
942 static const WCHAR conW[] = {'C','O','N',0};
943 static const WCHAR scsimgrW[] = {'S','C','S','I','M','G','R','$',0};
944 static const WCHAR hpscanW[] = {'H','P','S','C','A','N',0};
945 static const WCHAR emmxxxx0W[] = {'E','M','M','X','X','X','X','0',0};
946 /* got it */
947 if (!strcmpiW(DOSFS_Devices[i].name, nulW))
948 return FILE_CreateFile( "/dev/null", access,
949 FILE_SHARE_READ|FILE_SHARE_WRITE, sa,
950 OPEN_EXISTING, 0, 0, TRUE, DRIVE_UNKNOWN );
951 if (!strcmpiW(DOSFS_Devices[i].name, conW)) {
952 HANDLE to_dup;
953 switch (access & (GENERIC_READ|GENERIC_WRITE)) {
954 case GENERIC_READ:
955 to_dup = GetStdHandle( STD_INPUT_HANDLE );
956 break;
957 case GENERIC_WRITE:
958 to_dup = GetStdHandle( STD_OUTPUT_HANDLE );
959 break;
960 default:
961 FIXME("can't open CON read/write\n");
962 return 0;
964 if (!DuplicateHandle( GetCurrentProcess(), to_dup, GetCurrentProcess(),
965 &handle, 0,
966 sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle,
967 DUPLICATE_SAME_ACCESS ))
968 handle = 0;
969 return handle;
971 if (!strcmpiW(DOSFS_Devices[i].name, scsimgrW) ||
972 !strcmpiW(DOSFS_Devices[i].name, hpscanW) ||
973 !strcmpiW(DOSFS_Devices[i].name, emmxxxx0W))
975 return FILE_CreateDevice( i, access, sa );
978 if( (handle=DOSFS_CreateCommPort(DOSFS_Devices[i].name,access,attributes,sa)) )
979 return handle;
980 FIXME("device open %s not supported (yet)\n", debugstr_w(DOSFS_Devices[i].name));
981 return 0;
985 return 0;
989 /***********************************************************************
990 * DOSFS_GetPathDrive
992 * Get the drive specified by a given path name (DOS or Unix format).
994 static int DOSFS_GetPathDrive( LPCWSTR *name )
996 int drive;
997 LPCWSTR p = *name;
999 if (*p && (p[1] == ':'))
1001 drive = toupperW(*p) - 'A';
1002 *name += 2;
1004 else if (*p == '/') /* Absolute Unix path? */
1006 if ((drive = DRIVE_FindDriveRootW( name )) == -1)
1008 MESSAGE("Warning: %s not accessible from a configured DOS drive\n", debugstr_w(*name) );
1009 /* Assume it really was a DOS name */
1010 drive = DRIVE_GetCurrentDrive();
1013 else drive = DRIVE_GetCurrentDrive();
1015 if (!DRIVE_IsValid(drive))
1017 SetLastError( ERROR_INVALID_DRIVE );
1018 return -1;
1020 return drive;
1024 /***********************************************************************
1025 * DOSFS_GetFullName
1027 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
1028 * Unix name / short DOS name pair.
1029 * Return FALSE if one of the path components does not exist. The last path
1030 * component is only checked if 'check_last' is non-zero.
1031 * The buffers pointed to by 'long_buf' and 'short_buf' must be
1032 * at least MAX_PATHNAME_LEN long.
1034 BOOL DOSFS_GetFullName( LPCWSTR name, BOOL check_last, DOS_FULL_NAME *full )
1036 BOOL found;
1037 UINT flags;
1038 char *p_l, *root;
1039 LPWSTR p_s;
1040 static const WCHAR driveA_rootW[] = {'A',':','\\',0};
1041 static const WCHAR dos_rootW[] = {'\\',0};
1043 TRACE("%s (last=%d)\n", debugstr_w(name), check_last );
1045 if ((!*name) || (*name=='\n'))
1046 { /* error code for Win98 */
1047 SetLastError(ERROR_BAD_PATHNAME);
1048 return FALSE;
1051 if ((full->drive = DOSFS_GetPathDrive( &name )) == -1) return FALSE;
1052 flags = DRIVE_GetFlags( full->drive );
1054 lstrcpynA( full->long_name, DRIVE_GetRoot( full->drive ),
1055 sizeof(full->long_name) );
1056 if (full->long_name[1]) root = full->long_name + strlen(full->long_name);
1057 else root = full->long_name; /* root directory */
1059 strcpyW( full->short_name, driveA_rootW );
1060 full->short_name[0] += full->drive;
1062 if ((*name == '\\') || (*name == '/')) /* Absolute path */
1064 while ((*name == '\\') || (*name == '/')) name++;
1066 else /* Relative path */
1068 lstrcpynA( root + 1, DRIVE_GetUnixCwd( full->drive ),
1069 sizeof(full->long_name) - (root - full->long_name) - 1 );
1070 if (root[1]) *root = '/';
1071 lstrcpynW( full->short_name + 3, DRIVE_GetDosCwd( full->drive ),
1072 sizeof(full->short_name)/sizeof(full->short_name[0]) - 3 );
1075 p_l = full->long_name[1] ? full->long_name + strlen(full->long_name)
1076 : full->long_name;
1077 p_s = full->short_name[3] ? full->short_name + strlenW(full->short_name)
1078 : full->short_name + 2;
1079 found = TRUE;
1081 while (*name && found)
1083 /* Check for '.' and '..' */
1085 if (*name == '.')
1087 if (IS_END_OF_NAME(name[1]))
1089 name++;
1090 while ((*name == '\\') || (*name == '/')) name++;
1091 continue;
1093 else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
1095 name += 2;
1096 while ((*name == '\\') || (*name == '/')) name++;
1097 while ((p_l > root) && (*p_l != '/')) p_l--;
1098 while ((p_s > full->short_name + 2) && (*p_s != '\\')) p_s--;
1099 *p_l = *p_s = '\0'; /* Remove trailing separator */
1100 continue;
1104 /* Make sure buffers are large enough */
1106 if ((p_s >= full->short_name + sizeof(full->short_name)/sizeof(full->short_name[0]) - 14) ||
1107 (p_l >= full->long_name + sizeof(full->long_name) - 1))
1109 SetLastError( ERROR_PATH_NOT_FOUND );
1110 return FALSE;
1113 /* Get the long and short name matching the file name */
1115 if ((found = DOSFS_FindUnixName( full, name, p_l + 1,
1116 sizeof(full->long_name) - (p_l - full->long_name) - 1,
1117 p_s + 1, !(flags & DRIVE_CASE_SENSITIVE) )))
1119 *p_l++ = '/';
1120 p_l += strlen(p_l);
1121 *p_s++ = '\\';
1122 p_s += strlenW(p_s);
1123 while (!IS_END_OF_NAME(*name)) name++;
1125 else if (!check_last)
1127 *p_l++ = '/';
1128 *p_s++ = '\\';
1129 while (!IS_END_OF_NAME(*name) &&
1130 (p_s < full->short_name + sizeof(full->short_name)/sizeof(full->short_name[0]) - 1) &&
1131 (p_l < full->long_name + sizeof(full->long_name) - 1))
1133 WCHAR wch;
1134 *p_s++ = tolowerW(*name);
1135 /* If the drive is case-sensitive we want to create new */
1136 /* files in lower-case otherwise we can't reopen them */
1137 /* under the same short name. */
1138 if (flags & DRIVE_CASE_SENSITIVE) wch = tolowerW(*name);
1139 else wch = *name;
1140 p_l += WideCharToMultiByte(CP_UNIXCP, 0, &wch, 1, p_l, 2, NULL, NULL);
1141 name++;
1143 /* Ignore trailing dots and spaces */
1144 while(p_l[-1] == '.' || p_l[-1] == ' ') {
1145 --p_l;
1146 --p_s;
1148 *p_l = '\0';
1149 *p_s = '\0';
1151 while ((*name == '\\') || (*name == '/')) name++;
1154 if (!found)
1156 if (check_last)
1158 SetLastError( ERROR_FILE_NOT_FOUND );
1159 return FALSE;
1161 if (*name) /* Not last */
1163 SetLastError( ERROR_PATH_NOT_FOUND );
1164 return FALSE;
1167 if (!full->long_name[0]) strcpy( full->long_name, "/" );
1168 if (!full->short_name[2]) strcpyW( full->short_name + 2, dos_rootW );
1169 TRACE("returning %s = %s\n", full->long_name, debugstr_w(full->short_name) );
1170 return TRUE;
1174 /***********************************************************************
1175 * GetShortPathNameW (KERNEL32.@)
1177 * NOTES
1178 * observed:
1179 * longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1180 * longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0
1182 * more observations ( with NT 3.51 (WinDD) ):
1183 * longpath <= 8.3 -> just copy longpath to shortpath
1184 * longpath > 8.3 ->
1185 * a) file does not exist -> return 0, LastError = ERROR_FILE_NOT_FOUND
1186 * b) file does exist -> set the short filename.
1187 * - trailing slashes are reproduced in the short name, even if the
1188 * file is not a directory
1189 * - the absolute/relative path of the short name is reproduced like found
1190 * in the long name
1191 * - longpath and shortpath may have the same address
1192 * Peter Ganten, 1999
1194 DWORD WINAPI GetShortPathNameW( LPCWSTR longpath, LPWSTR shortpath, DWORD shortlen )
1196 DOS_FULL_NAME full_name;
1197 WCHAR tmpshortpath[MAX_PATHNAME_LEN];
1198 const WCHAR *p;
1199 DWORD sp = 0, lp = 0;
1200 int drive;
1201 DWORD tmplen;
1202 UINT flags;
1203 BOOL unixabsolute = *longpath == '/';
1205 TRACE("%s\n", debugstr_w(longpath));
1207 if (!longpath) {
1208 SetLastError(ERROR_INVALID_PARAMETER);
1209 return 0;
1211 if (!longpath[0]) {
1212 SetLastError(ERROR_BAD_PATHNAME);
1213 return 0;
1216 /* check for drive letter */
1217 if (!unixabsolute && longpath[1] == ':' ) {
1218 tmpshortpath[0] = longpath[0];
1219 tmpshortpath[1] = ':';
1220 sp = 2;
1223 if ( ( drive = DOSFS_GetPathDrive ( &longpath )) == -1 ) return 0;
1224 flags = DRIVE_GetFlags ( drive );
1226 if (unixabsolute && drive != DRIVE_GetCurrentDrive()) {
1227 tmpshortpath[0] = drive + 'A';
1228 tmpshortpath[1] = ':';
1229 sp = 2;
1232 while ( longpath[lp] ) {
1234 /* check for path delimiters and reproduce them */
1235 if ( longpath[lp] == '\\' || longpath[lp] == '/' ) {
1236 if (!sp || tmpshortpath[sp-1]!= '\\')
1238 /* strip double "\\" */
1239 tmpshortpath[sp] = '\\';
1240 sp++;
1242 tmpshortpath[sp]=0;/*terminate string*/
1243 lp++;
1244 continue;
1247 tmplen = 0;
1248 for(p = longpath + lp; *p && *p != '/' && *p != '\\'; p++)
1249 tmplen++;
1250 lstrcpynW(tmpshortpath + sp, longpath + lp, tmplen + 1);
1252 /* Check, if the current element is a valid dos name */
1253 if ( DOSFS_ValidDOSName ( longpath + lp, !(flags & DRIVE_CASE_SENSITIVE) ) ) {
1254 sp += tmplen;
1255 lp += tmplen;
1256 continue;
1259 /* Check if the file exists and use the existing file name */
1260 if ( DOSFS_GetFullName ( tmpshortpath, TRUE, &full_name ) ) {
1261 strcpyW(tmpshortpath + sp, strrchrW(full_name.short_name, '\\') + 1);
1262 sp += strlenW(tmpshortpath + sp);
1263 lp += tmplen;
1264 continue;
1267 TRACE("not found!\n" );
1268 SetLastError ( ERROR_FILE_NOT_FOUND );
1269 return 0;
1271 tmpshortpath[sp] = 0;
1273 tmplen = strlenW(tmpshortpath) + 1;
1274 if (tmplen <= shortlen)
1276 strcpyW(shortpath, tmpshortpath);
1277 TRACE("returning %s\n", debugstr_w(shortpath));
1278 tmplen--; /* length without 0 */
1281 return tmplen;
1285 /***********************************************************************
1286 * GetShortPathNameA (KERNEL32.@)
1288 DWORD WINAPI GetShortPathNameA( LPCSTR longpath, LPSTR shortpath, DWORD shortlen )
1290 UNICODE_STRING longpathW;
1291 WCHAR shortpathW[MAX_PATH];
1292 DWORD ret, retW;
1294 if (!longpath)
1296 SetLastError(ERROR_INVALID_PARAMETER);
1297 return 0;
1300 TRACE("%s\n", debugstr_a(longpath));
1302 if (!RtlCreateUnicodeStringFromAsciiz(&longpathW, longpath))
1304 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1305 return 0;
1308 retW = GetShortPathNameW(longpathW.Buffer, shortpathW, MAX_PATH);
1310 if (!retW)
1311 ret = 0;
1312 else if (retW > MAX_PATH)
1314 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1315 ret = 0;
1317 else
1319 ret = WideCharToMultiByte(CP_ACP, 0, shortpathW, -1, NULL, 0, NULL, NULL);
1320 if (ret <= shortlen)
1322 WideCharToMultiByte(CP_ACP, 0, shortpathW, -1, shortpath, shortlen, NULL, NULL);
1323 ret--; /* length without 0 */
1327 RtlFreeUnicodeString(&longpathW);
1328 return ret;
1332 /***********************************************************************
1333 * GetLongPathNameW (KERNEL32.@)
1335 * NOTES
1336 * observed (Win2000):
1337 * shortpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1338 * shortpath="": LastError=ERROR_PATH_NOT_FOUND, ret=0
1340 DWORD WINAPI GetLongPathNameW( LPCWSTR shortpath, LPWSTR longpath, DWORD longlen )
1342 DOS_FULL_NAME full_name;
1343 const char *root;
1344 LPWSTR p;
1345 int drive;
1346 DWORD ret, len = 0;
1348 if (!shortpath) {
1349 SetLastError(ERROR_INVALID_PARAMETER);
1350 return 0;
1352 if (!shortpath[0]) {
1353 SetLastError(ERROR_PATH_NOT_FOUND);
1354 return 0;
1357 TRACE("%s,%p,%ld\n", debugstr_w(shortpath), longpath, longlen);
1359 if(shortpath[0]=='\\' && shortpath[1]=='\\')
1361 ERR("UNC pathname %s\n",debugstr_w(shortpath));
1362 lstrcpynW( longpath, full_name.short_name, longlen );
1363 return strlenW(longpath);
1366 if (!DOSFS_GetFullName( shortpath, TRUE, &full_name )) return 0;
1368 root = full_name.long_name;
1369 drive = DRIVE_FindDriveRoot(&root);
1371 ret = MultiByteToWideChar(CP_UNIXCP, 0, root, -1, NULL, 0);
1372 ret += 3; /* A:\ */
1373 /* reproduce terminating slash */
1374 if (ret > 4) /* if not drive root */
1376 len = strlenW(shortpath);
1377 if (shortpath[len - 1] == '\\' || shortpath[len - 1] == '/')
1378 len = 1;
1380 ret += len;
1381 if (ret <= longlen)
1383 longpath[0] = 'A' + drive;
1384 longpath[1] = ':';
1385 MultiByteToWideChar(CP_UNIXCP, 0, root, -1, longpath + 2, longlen - 2);
1386 for (p = longpath; *p; p++) if (*p == '/') *p = '\\';
1387 if (len)
1389 longpath[ret - 2] = '\\';
1390 longpath[ret - 1] = 0;
1392 TRACE("returning %s\n", debugstr_w(longpath));
1393 ret--; /* length without 0 */
1395 return ret;
1399 /***********************************************************************
1400 * GetLongPathNameA (KERNEL32.@)
1402 DWORD WINAPI GetLongPathNameA( LPCSTR shortpath, LPSTR longpath, DWORD longlen )
1404 UNICODE_STRING shortpathW;
1405 WCHAR longpathW[MAX_PATH];
1406 DWORD ret, retW;
1408 if (!shortpath)
1410 SetLastError(ERROR_INVALID_PARAMETER);
1411 return 0;
1414 TRACE("%s\n", debugstr_a(shortpath));
1416 if (!RtlCreateUnicodeStringFromAsciiz(&shortpathW, shortpath))
1418 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1419 return 0;
1422 retW = GetLongPathNameW(shortpathW.Buffer, longpathW, MAX_PATH);
1424 if (!retW)
1425 ret = 0;
1426 else if (retW > MAX_PATH)
1428 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1429 ret = 0;
1431 else
1433 ret = WideCharToMultiByte(CP_ACP, 0, longpathW, -1, NULL, 0, NULL, NULL);
1434 if (ret <= longlen)
1436 WideCharToMultiByte(CP_ACP, 0, longpathW, -1, longpath, longlen, NULL, NULL);
1437 ret--; /* length without 0 */
1441 RtlFreeUnicodeString(&shortpathW);
1442 return ret;
1446 /***********************************************************************
1447 * DOSFS_DoGetFullPathName
1449 * Implementation of GetFullPathNameA/W.
1451 * bon@elektron 000331:
1452 * A test for GetFullPathName with many pathological cases
1453 * now gives identical output for Wine and OSR2
1455 static DWORD DOSFS_DoGetFullPathName( LPCWSTR name, DWORD len, LPWSTR result )
1457 DWORD ret;
1458 DOS_FULL_NAME full_name;
1459 LPWSTR p, q;
1460 char *p_l;
1461 const char * root;
1462 WCHAR drivecur[] = {'C',':','.',0};
1463 WCHAR driveletter=0;
1464 int namelen,drive=0;
1465 static const WCHAR bkslashW[] = {'\\',0};
1466 static const WCHAR dotW[] = {'.',0};
1467 static const WCHAR updir_slashW[] = {'\\','.','.','\\',0};
1468 static const WCHAR curdirW[] = {'\\','.','\\',0};
1469 static const WCHAR updirW[] = {'\\','.','.',0};
1471 if (!name[0])
1473 SetLastError(ERROR_BAD_PATHNAME);
1474 return 0;
1477 TRACE("passed %s\n", debugstr_w(name));
1479 if (name[1]==':')
1480 /*drive letter given */
1482 driveletter = name[0];
1484 if ((name[1]==':') && ((name[2]=='\\') || (name[2]=='/')))
1485 /*absolute path given */
1487 strncpyW(full_name.short_name, name, MAX_PATHNAME_LEN);
1488 full_name.short_name[MAX_PATHNAME_LEN - 1] = 0; /* ensure 0 termination */
1489 drive = toupperW(name[0]) - 'A';
1491 else
1493 if (driveletter)
1494 drivecur[0]=driveletter;
1495 else if ((name[0]=='\\') || (name[0]=='/'))
1496 strcpyW(drivecur, bkslashW);
1497 else
1498 strcpyW(drivecur, dotW);
1500 if (!DOSFS_GetFullName( drivecur, FALSE, &full_name ))
1502 FIXME("internal: error getting drive/path\n");
1503 return 0;
1505 /* find path that drive letter substitutes*/
1506 drive = toupperW(full_name.short_name[0]) - 'A';
1507 root= DRIVE_GetRoot(drive);
1508 if (!root)
1510 FIXME("internal: error getting DOS Drive Root\n");
1511 return 0;
1513 if (!strcmp(root,"/"))
1515 /* we have just the last / and we need it. */
1516 p_l = full_name.long_name;
1518 else
1520 p_l = full_name.long_name + strlen(root);
1522 /* append long name (= unix name) to drive */
1523 MultiByteToWideChar(CP_UNIXCP, 0, p_l, -1, full_name.short_name + 2, MAX_PATHNAME_LEN - 3);
1524 /* append name to treat */
1525 namelen= strlenW(full_name.short_name);
1526 p = (LPWSTR)name;
1527 if (driveletter)
1528 p += 2; /* skip drive name when appending */
1529 if (namelen + 2 + strlenW(p) > MAX_PATHNAME_LEN)
1531 FIXME("internal error: buffer too small\n");
1532 return 0;
1534 full_name.short_name[namelen++] ='\\';
1535 full_name.short_name[namelen] = 0;
1536 strncpyW(full_name.short_name + namelen, p, MAX_PATHNAME_LEN - namelen);
1537 full_name.short_name[MAX_PATHNAME_LEN - 1] = 0; /* ensure 0 termination */
1539 /* reverse all slashes */
1540 for (p=full_name.short_name;
1541 p < full_name.short_name + strlenW(full_name.short_name);
1542 p++)
1544 if ( *p == '/' )
1545 *p = '\\';
1547 /* Use memmove, as areas overlap */
1548 /* Delete .. */
1549 while ((p = strstrW(full_name.short_name, updir_slashW)))
1551 if (p > full_name.short_name+2)
1553 *p = 0;
1554 q = strrchrW(full_name.short_name, '\\');
1555 memmove(q+1, p+4, (strlenW(p+4)+1) * sizeof(WCHAR));
1557 else
1559 memmove(full_name.short_name+3, p+4, (strlenW(p+4)+1) * sizeof(WCHAR));
1562 if ((full_name.short_name[2]=='.')&&(full_name.short_name[3]=='.'))
1564 /* This case istn't treated yet : c:..\test */
1565 memmove(full_name.short_name+2,full_name.short_name+4,
1566 (strlenW(full_name.short_name+4)+1) * sizeof(WCHAR));
1568 /* Delete . */
1569 while ((p = strstrW(full_name.short_name, curdirW)))
1571 *(p+1) = 0;
1572 memmove(p+1, p+3, (strlenW(p+3)+1) * sizeof(WCHAR));
1574 if (!(DRIVE_GetFlags(drive) & DRIVE_CASE_PRESERVING))
1575 for (p = full_name.short_name; *p; p++) *p = toupperW(*p);
1576 namelen = strlenW(full_name.short_name);
1577 if (!strcmpW(full_name.short_name+namelen-3, updirW))
1579 /* one more strange case: "c:\test\test1\.."
1580 return "c:\test" */
1581 *(full_name.short_name+namelen-3)=0;
1582 q = strrchrW(full_name.short_name, '\\');
1583 *q =0;
1585 if (full_name.short_name[namelen-1]=='.')
1586 full_name.short_name[(namelen--)-1] =0;
1587 if (!driveletter)
1588 if (full_name.short_name[namelen-1]=='\\')
1589 full_name.short_name[(namelen--)-1] =0;
1590 TRACE("got %s\n", debugstr_w(full_name.short_name));
1592 /* If the lpBuffer buffer is too small, the return value is the
1593 size of the buffer, in characters, required to hold the path
1594 plus the terminating \0 (tested against win95osr2, bon 001118)
1595 . */
1596 ret = strlenW(full_name.short_name);
1597 if (ret >= len )
1599 /* don't touch anything when the buffer is not large enough */
1600 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1601 return ret+1;
1603 if (result)
1605 strncpyW( result, full_name.short_name, len );
1606 result[len - 1] = 0; /* ensure 0 termination */
1609 TRACE("returning %s\n", debugstr_w(full_name.short_name) );
1610 return ret;
1614 /***********************************************************************
1615 * GetFullPathNameA (KERNEL32.@)
1616 * NOTES
1617 * if the path closed with '\', *lastpart is 0
1619 DWORD WINAPI GetFullPathNameA( LPCSTR name, DWORD len, LPSTR buffer,
1620 LPSTR *lastpart )
1622 UNICODE_STRING nameW;
1623 WCHAR bufferW[MAX_PATH];
1624 DWORD ret, retW;
1626 if (!name)
1628 SetLastError(ERROR_INVALID_PARAMETER);
1629 return 0;
1632 if (!RtlCreateUnicodeStringFromAsciiz(&nameW, name))
1634 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1635 return 0;
1638 retW = GetFullPathNameW( nameW.Buffer, MAX_PATH, bufferW, NULL);
1640 if (!retW)
1641 ret = 0;
1642 else if (retW > MAX_PATH)
1644 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1645 ret = 0;
1647 else
1649 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL);
1650 if (ret <= len)
1652 WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, len, NULL, NULL);
1653 ret--; /* length without 0 */
1655 if (lastpart)
1657 LPSTR p = buffer + strlen(buffer);
1659 if (*p != '\\')
1661 while ((p > buffer + 2) && (*p != '\\')) p--;
1662 *lastpart = p + 1;
1664 else *lastpart = NULL;
1669 RtlFreeUnicodeString(&nameW);
1670 return ret;
1674 /***********************************************************************
1675 * GetFullPathNameW (KERNEL32.@)
1677 DWORD WINAPI GetFullPathNameW( LPCWSTR name, DWORD len, LPWSTR buffer,
1678 LPWSTR *lastpart )
1680 DWORD ret = DOSFS_DoGetFullPathName( name, len, buffer );
1681 if (ret && (ret<=len) && buffer && lastpart)
1683 LPWSTR p = buffer + strlenW(buffer);
1684 if (*p != (WCHAR)'\\')
1686 while ((p > buffer + 2) && (*p != (WCHAR)'\\')) p--;
1687 *lastpart = p + 1;
1689 else *lastpart = NULL;
1691 return ret;
1695 /***********************************************************************
1696 * wine_get_unix_file_name (KERNEL32.@) Not a Windows API
1698 * Return the full Unix file name for a given path.
1699 * FIXME: convert dos file name to unicode
1701 BOOL WINAPI wine_get_unix_file_name( LPCSTR dos, LPSTR buffer, DWORD len )
1703 BOOL ret;
1704 DOS_FULL_NAME path;
1705 WCHAR dosW[MAX_PATHNAME_LEN];
1707 MultiByteToWideChar(CP_ACP, 0, dos, -1, dosW, MAX_PATHNAME_LEN);
1708 ret = DOSFS_GetFullName( dosW, FALSE, &path );
1709 if (ret && len)
1711 strncpy( buffer, path.long_name, len );
1712 buffer[len - 1] = 0; /* ensure 0 termination */
1714 return ret;
1718 /***********************************************************************
1719 * get_show_dir_symlinks_option
1721 static BOOL get_show_dir_symlinks_option(void)
1723 static const WCHAR WineW[] = {'M','a','c','h','i','n','e','\\',
1724 'S','o','f','t','w','a','r','e','\\',
1725 'W','i','n','e','\\','W','i','n','e','\\',
1726 'C','o','n','f','i','g','\\','W','i','n','e',0};
1727 static const WCHAR ShowDirSymlinksW[] = {'S','h','o','w','D','i','r','S','y','m','l','i','n','k','s',0};
1729 char tmp[80];
1730 HKEY hkey;
1731 DWORD dummy;
1732 OBJECT_ATTRIBUTES attr;
1733 UNICODE_STRING nameW;
1734 BOOL ret = FALSE;
1736 attr.Length = sizeof(attr);
1737 attr.RootDirectory = 0;
1738 attr.ObjectName = &nameW;
1739 attr.Attributes = 0;
1740 attr.SecurityDescriptor = NULL;
1741 attr.SecurityQualityOfService = NULL;
1742 RtlInitUnicodeString( &nameW, WineW );
1744 if (!NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr ))
1746 RtlInitUnicodeString( &nameW, ShowDirSymlinksW );
1747 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
1749 WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
1750 ret = IS_OPTION_TRUE( str[0] );
1752 NtClose( hkey );
1754 return ret;
1758 /***********************************************************************
1759 * DOSFS_FindNextEx
1761 static int DOSFS_FindNextEx( FIND_FIRST_INFO *info, WIN32_FIND_DATAW *entry )
1763 UINT flags = DRIVE_GetFlags( info->drive );
1764 char *p, buffer[MAX_PATHNAME_LEN];
1765 const char *drive_path;
1766 int drive_root;
1767 LPCWSTR long_name, short_name;
1768 BY_HANDLE_FILE_INFORMATION fileinfo;
1769 BOOL is_symlink;
1771 drive_path = info->path + strlen(DRIVE_GetRoot( info->drive ));
1772 while ((*drive_path == '/') || (*drive_path == '\\')) drive_path++;
1773 drive_root = !*drive_path;
1775 lstrcpynA( buffer, info->path, sizeof(buffer) - 1 );
1776 strcat( buffer, "/" );
1777 p = buffer + strlen(buffer);
1779 while (DOSFS_ReadDir( info->u.dos_dir, &long_name, &short_name ))
1781 info->cur_pos++;
1783 /* Don't return '.' and '..' in the root of the drive */
1784 if (drive_root && (long_name[0] == '.') &&
1785 (!long_name[1] || ((long_name[1] == '.') && !long_name[2])))
1786 continue;
1788 /* Check the long mask */
1790 if (info->long_mask && *info->long_mask)
1792 if (!DOSFS_MatchLong( info->long_mask, long_name,
1793 flags & DRIVE_CASE_SENSITIVE )) continue;
1796 /* Check the file attributes */
1797 WideCharToMultiByte(CP_UNIXCP, 0, long_name, -1,
1798 p, sizeof(buffer) - (int)(p - buffer), NULL, NULL);
1799 if (!FILE_Stat( buffer, &fileinfo, &is_symlink ))
1801 WARN("can't stat %s\n", buffer);
1802 continue;
1804 if (is_symlink && (fileinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
1806 static int show_dir_symlinks = -1;
1807 if (show_dir_symlinks == -1)
1808 show_dir_symlinks = get_show_dir_symlinks_option();
1809 if (!show_dir_symlinks) continue;
1812 /* We now have a matching entry; fill the result and return */
1814 entry->dwFileAttributes = fileinfo.dwFileAttributes;
1815 entry->ftCreationTime = fileinfo.ftCreationTime;
1816 entry->ftLastAccessTime = fileinfo.ftLastAccessTime;
1817 entry->ftLastWriteTime = fileinfo.ftLastWriteTime;
1818 entry->nFileSizeHigh = fileinfo.nFileSizeHigh;
1819 entry->nFileSizeLow = fileinfo.nFileSizeLow;
1821 if (short_name)
1822 DOSFS_ToDosDTAFormat( short_name, entry->cAlternateFileName );
1823 else
1824 DOSFS_Hash( long_name, entry->cAlternateFileName, FALSE,
1825 !(flags & DRIVE_CASE_SENSITIVE) );
1827 lstrcpynW( entry->cFileName, long_name, sizeof(entry->cFileName)/sizeof(entry->cFileName[0]) );
1828 if (!(flags & DRIVE_CASE_PRESERVING)) strlwrW( entry->cFileName );
1829 TRACE("returning %s (%s) %02lx %ld\n",
1830 debugstr_w(entry->cFileName), debugstr_w(entry->cAlternateFileName),
1831 entry->dwFileAttributes, entry->nFileSizeLow );
1832 return 1;
1834 return 0; /* End of directory */
1837 /*************************************************************************
1838 * FindFirstFileExW (KERNEL32.@)
1840 HANDLE WINAPI FindFirstFileExW(
1841 LPCWSTR lpFileName,
1842 FINDEX_INFO_LEVELS fInfoLevelId,
1843 LPVOID lpFindFileData,
1844 FINDEX_SEARCH_OPS fSearchOp,
1845 LPVOID lpSearchFilter,
1846 DWORD dwAdditionalFlags)
1848 FIND_FIRST_INFO *info;
1850 if (!lpFileName)
1852 SetLastError(ERROR_PATH_NOT_FOUND);
1853 return INVALID_HANDLE_VALUE;
1856 if ((fSearchOp != FindExSearchNameMatch) || (dwAdditionalFlags != 0))
1858 FIXME("options not implemented 0x%08x 0x%08lx\n", fSearchOp, dwAdditionalFlags );
1859 return INVALID_HANDLE_VALUE;
1862 switch(fInfoLevelId)
1864 case FindExInfoStandard:
1866 WIN32_FIND_DATAW * data = (WIN32_FIND_DATAW *) lpFindFileData;
1867 char *p;
1868 INT long_mask_len;
1870 data->dwReserved0 = data->dwReserved1 = 0x0;
1871 if (lpFileName[0] == '\\' && lpFileName[1] == '\\')
1873 ERR("UNC path name\n");
1874 if (!(info = HeapAlloc( GetProcessHeap(), 0, sizeof(FIND_FIRST_INFO)))) break;
1875 info->u.smb_dir = SMB_FindFirst(lpFileName);
1876 if(!info->u.smb_dir)
1878 HeapFree(GetProcessHeap(), 0, info);
1879 break;
1881 info->drive = -1;
1882 RtlInitializeCriticalSection( &info->cs );
1884 else
1886 DOS_FULL_NAME full_name;
1888 if (lpFileName[0] && lpFileName[1] == ':')
1890 /* don't allow root directories */
1891 if (!lpFileName[2] ||
1892 ((lpFileName[2] == '/' || lpFileName[2] == '\\') && !lpFileName[3]))
1894 SetLastError(ERROR_FILE_NOT_FOUND);
1895 return INVALID_HANDLE_VALUE;
1898 if (!DOSFS_GetFullName( lpFileName, FALSE, &full_name )) break;
1899 if (!(info = HeapAlloc( GetProcessHeap(), 0, sizeof(FIND_FIRST_INFO)))) break;
1900 RtlInitializeCriticalSection( &info->cs );
1901 info->path = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
1902 strcpy( info->path, full_name.long_name );
1904 p = strrchr( info->path, '/' );
1905 *p++ = '\0';
1906 long_mask_len = MultiByteToWideChar(CP_UNIXCP, 0, p, -1, NULL, 0);
1907 info->long_mask = HeapAlloc( GetProcessHeap(), 0, long_mask_len * sizeof(WCHAR) );
1908 MultiByteToWideChar(CP_UNIXCP, 0, p, -1, info->long_mask, long_mask_len);
1910 info->drive = full_name.drive;
1911 info->cur_pos = 0;
1913 info->u.dos_dir = DOSFS_OpenDir( info->path );
1915 if (!FindNextFileW( (HANDLE) info, data ))
1917 FindClose( (HANDLE) info );
1918 SetLastError( ERROR_FILE_NOT_FOUND );
1919 break;
1921 return (HANDLE) info;
1923 break;
1924 default:
1925 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
1927 return INVALID_HANDLE_VALUE;
1930 /*************************************************************************
1931 * FindFirstFileA (KERNEL32.@)
1933 HANDLE WINAPI FindFirstFileA(
1934 LPCSTR lpFileName,
1935 WIN32_FIND_DATAA *lpFindData )
1937 return FindFirstFileExA(lpFileName, FindExInfoStandard, lpFindData,
1938 FindExSearchNameMatch, NULL, 0);
1941 /*************************************************************************
1942 * FindFirstFileExA (KERNEL32.@)
1944 HANDLE WINAPI FindFirstFileExA(
1945 LPCSTR lpFileName,
1946 FINDEX_INFO_LEVELS fInfoLevelId,
1947 LPVOID lpFindFileData,
1948 FINDEX_SEARCH_OPS fSearchOp,
1949 LPVOID lpSearchFilter,
1950 DWORD dwAdditionalFlags)
1952 HANDLE handle;
1953 WIN32_FIND_DATAA *dataA;
1954 WIN32_FIND_DATAW dataW;
1955 UNICODE_STRING pathW;
1957 if (!lpFileName)
1959 SetLastError(ERROR_PATH_NOT_FOUND);
1960 return INVALID_HANDLE_VALUE;
1963 if (!RtlCreateUnicodeStringFromAsciiz(&pathW, lpFileName))
1965 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1966 return INVALID_HANDLE_VALUE;
1969 handle = FindFirstFileExW(pathW.Buffer, fInfoLevelId, &dataW, fSearchOp, lpSearchFilter, dwAdditionalFlags);
1970 RtlFreeUnicodeString(&pathW);
1971 if (handle == INVALID_HANDLE_VALUE) return handle;
1973 dataA = (WIN32_FIND_DATAA *) lpFindFileData;
1974 dataA->dwFileAttributes = dataW.dwFileAttributes;
1975 dataA->ftCreationTime = dataW.ftCreationTime;
1976 dataA->ftLastAccessTime = dataW.ftLastAccessTime;
1977 dataA->ftLastWriteTime = dataW.ftLastWriteTime;
1978 dataA->nFileSizeHigh = dataW.nFileSizeHigh;
1979 dataA->nFileSizeLow = dataW.nFileSizeLow;
1980 WideCharToMultiByte( CP_ACP, 0, dataW.cFileName, -1,
1981 dataA->cFileName, sizeof(dataA->cFileName), NULL, NULL );
1982 WideCharToMultiByte( CP_ACP, 0, dataW.cAlternateFileName, -1,
1983 dataA->cAlternateFileName, sizeof(dataA->cAlternateFileName), NULL, NULL );
1984 return handle;
1987 /*************************************************************************
1988 * FindFirstFileW (KERNEL32.@)
1990 HANDLE WINAPI FindFirstFileW( LPCWSTR lpFileName, WIN32_FIND_DATAW *lpFindData )
1992 return FindFirstFileExW(lpFileName, FindExInfoStandard, lpFindData,
1993 FindExSearchNameMatch, NULL, 0);
1996 /*************************************************************************
1997 * FindNextFileW (KERNEL32.@)
1999 BOOL WINAPI FindNextFileW( HANDLE handle, WIN32_FIND_DATAW *data )
2001 FIND_FIRST_INFO *info;
2002 BOOL ret = FALSE;
2003 DWORD gle = ERROR_NO_MORE_FILES;
2005 if (handle == INVALID_HANDLE_VALUE)
2007 SetLastError( ERROR_INVALID_HANDLE );
2008 return ret;
2010 info = (FIND_FIRST_INFO*) handle;
2011 RtlEnterCriticalSection( &info->cs );
2012 if (info->drive == -1)
2014 ret = SMB_FindNext( info->u.smb_dir, data );
2015 if(!ret)
2017 SMB_CloseDir( info->u.smb_dir );
2018 HeapFree( GetProcessHeap(), 0, info->path );
2020 goto done;
2022 else if (!info->path || !info->u.dos_dir)
2024 goto done;
2026 else if (!DOSFS_FindNextEx( info, data ))
2028 DOSFS_CloseDir( info->u.dos_dir ); info->u.dos_dir = NULL;
2029 HeapFree( GetProcessHeap(), 0, info->path );
2030 info->path = NULL;
2031 HeapFree( GetProcessHeap(), 0, info->long_mask );
2032 info->long_mask = NULL;
2033 goto done;
2035 ret = TRUE;
2036 done:
2037 RtlLeaveCriticalSection( &info->cs );
2038 if( !ret ) SetLastError( gle );
2039 return ret;
2043 /*************************************************************************
2044 * FindNextFileA (KERNEL32.@)
2046 BOOL WINAPI FindNextFileA( HANDLE handle, WIN32_FIND_DATAA *data )
2048 WIN32_FIND_DATAW dataW;
2049 if (!FindNextFileW( handle, &dataW )) return FALSE;
2050 data->dwFileAttributes = dataW.dwFileAttributes;
2051 data->ftCreationTime = dataW.ftCreationTime;
2052 data->ftLastAccessTime = dataW.ftLastAccessTime;
2053 data->ftLastWriteTime = dataW.ftLastWriteTime;
2054 data->nFileSizeHigh = dataW.nFileSizeHigh;
2055 data->nFileSizeLow = dataW.nFileSizeLow;
2056 WideCharToMultiByte( CP_ACP, 0, dataW.cFileName, -1,
2057 data->cFileName, sizeof(data->cFileName), NULL, NULL );
2058 WideCharToMultiByte( CP_ACP, 0, dataW.cAlternateFileName, -1,
2059 data->cAlternateFileName,
2060 sizeof(data->cAlternateFileName), NULL, NULL );
2061 return TRUE;
2064 /*************************************************************************
2065 * FindClose (KERNEL32.@)
2067 BOOL WINAPI FindClose( HANDLE handle )
2069 FIND_FIRST_INFO *info = (FIND_FIRST_INFO*) handle;
2071 if (handle == INVALID_HANDLE_VALUE) goto error;
2073 __TRY
2075 RtlEnterCriticalSection( &info->cs );
2076 if (info)
2078 if (info->u.dos_dir) DOSFS_CloseDir( info->u.dos_dir );
2079 if (info->path) HeapFree( GetProcessHeap(), 0, info->path );
2080 if (info->long_mask) HeapFree( GetProcessHeap(), 0, info->long_mask );
2083 __EXCEPT(page_fault)
2085 WARN("Illegal handle %p\n", handle);
2086 SetLastError( ERROR_INVALID_HANDLE );
2087 return FALSE;
2089 __ENDTRY
2090 if (!info) goto error;
2091 RtlLeaveCriticalSection( &info->cs );
2092 RtlDeleteCriticalSection( &info->cs );
2093 HeapFree(GetProcessHeap(), 0, info);
2094 return TRUE;
2096 error:
2097 SetLastError( ERROR_INVALID_HANDLE );
2098 return FALSE;
2101 /***********************************************************************
2102 * MulDiv (KERNEL32.@)
2103 * RETURNS
2104 * Result of multiplication and division
2105 * -1: Overflow occurred or Divisor was 0
2107 INT WINAPI MulDiv(
2108 INT nMultiplicand,
2109 INT nMultiplier,
2110 INT nDivisor)
2112 #if SIZEOF_LONG_LONG >= 8
2113 long long ret;
2115 if (!nDivisor) return -1;
2117 /* We want to deal with a positive divisor to simplify the logic. */
2118 if (nDivisor < 0)
2120 nMultiplicand = - nMultiplicand;
2121 nDivisor = -nDivisor;
2124 /* If the result is positive, we "add" to round. else, we subtract to round. */
2125 if ( ( (nMultiplicand < 0) && (nMultiplier < 0) ) ||
2126 ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
2127 ret = (((long long)nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
2128 else
2129 ret = (((long long)nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
2131 if ((ret > 2147483647) || (ret < -2147483647)) return -1;
2132 return ret;
2133 #else
2134 if (!nDivisor) return -1;
2136 /* We want to deal with a positive divisor to simplify the logic. */
2137 if (nDivisor < 0)
2139 nMultiplicand = - nMultiplicand;
2140 nDivisor = -nDivisor;
2143 /* If the result is positive, we "add" to round. else, we subtract to round. */
2144 if ( ( (nMultiplicand < 0) && (nMultiplier < 0) ) ||
2145 ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
2146 return ((nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
2148 return ((nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
2150 #endif
2154 /***********************************************************************
2155 * DosDateTimeToFileTime (KERNEL32.@)
2157 BOOL WINAPI DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft)
2159 struct tm newtm;
2160 #ifndef HAVE_TIMEGM
2161 struct tm *gtm;
2162 time_t time1, time2;
2163 #endif
2165 newtm.tm_sec = (fattime & 0x1f) * 2;
2166 newtm.tm_min = (fattime >> 5) & 0x3f;
2167 newtm.tm_hour = (fattime >> 11);
2168 newtm.tm_mday = (fatdate & 0x1f);
2169 newtm.tm_mon = ((fatdate >> 5) & 0x0f) - 1;
2170 newtm.tm_year = (fatdate >> 9) + 80;
2171 #ifdef HAVE_TIMEGM
2172 RtlSecondsSince1970ToTime( timegm(&newtm), (LARGE_INTEGER *)ft );
2173 #else
2174 time1 = mktime(&newtm);
2175 gtm = gmtime(&time1);
2176 time2 = mktime(gtm);
2177 RtlSecondsSince1970ToTime( 2*time1-time2, (LARGE_INTEGER *)ft );
2178 #endif
2179 return TRUE;
2183 /***********************************************************************
2184 * FileTimeToDosDateTime (KERNEL32.@)
2186 BOOL WINAPI FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate,
2187 LPWORD fattime )
2189 LARGE_INTEGER li;
2190 ULONG t;
2191 time_t unixtime;
2192 struct tm* tm;
2194 li.s.LowPart = ft->dwLowDateTime;
2195 li.s.HighPart = ft->dwHighDateTime;
2196 RtlTimeToSecondsSince1970( &li, &t );
2197 unixtime = t;
2198 tm = gmtime( &unixtime );
2199 if (fattime)
2200 *fattime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
2201 if (fatdate)
2202 *fatdate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
2203 + tm->tm_mday;
2204 return TRUE;
2208 /***********************************************************************
2209 * QueryDosDeviceA (KERNEL32.@)
2211 * returns array of strings terminated by \0, terminated by \0
2213 DWORD WINAPI QueryDosDeviceA(LPCSTR devname,LPSTR target,DWORD bufsize)
2215 DWORD ret = 0, retW;
2216 LPWSTR targetW = (LPWSTR)HeapAlloc(GetProcessHeap(),0,
2217 bufsize * sizeof(WCHAR));
2218 UNICODE_STRING devnameW;
2220 if(devname) RtlCreateUnicodeStringFromAsciiz(&devnameW, devname);
2221 else devnameW.Buffer = NULL;
2223 retW = QueryDosDeviceW(devnameW.Buffer, targetW, bufsize);
2225 ret = WideCharToMultiByte(CP_ACP, 0, targetW, retW, target,
2226 bufsize, NULL, NULL);
2228 RtlFreeUnicodeString(&devnameW);
2229 if (targetW) HeapFree(GetProcessHeap(),0,targetW);
2230 return ret;
2234 /***********************************************************************
2235 * QueryDosDeviceW (KERNEL32.@)
2237 * returns array of strings terminated by \0, terminated by \0
2239 * FIXME
2240 * - Win9x returns for all calls ERROR_INVALID_PARAMETER
2241 * - the returned devices for devname == NULL is far from complete
2242 * - its not checked that the returned device exist
2244 DWORD WINAPI QueryDosDeviceW(LPCWSTR devname,LPWSTR target,DWORD bufsize)
2246 const WCHAR *pDev, *pName, *pNum = NULL;
2247 int numsiz=0;
2248 DWORD ret;
2250 TRACE("(%s,...)\n", debugstr_w(devname));
2251 if (!devname) {
2252 /* return known MSDOS devices */
2253 DWORD ret = 0;
2254 int i;
2255 static const WCHAR devices[][5] = {{'A','U','X',0},
2256 {'C','O','M','1',0},
2257 {'C','O','M','2',0},
2258 {'L','P','T','1',0},
2259 {'N','U','L',0,}};
2260 for(i=0; (i< (sizeof(devices)/sizeof(devices[0]))); i++) {
2261 DWORD len = strlenW(devices[i]);
2262 if(target && (bufsize >= ret + len + 2)) {
2263 strcpyW(target+ret, devices[i]);
2264 ret += len + 1;
2265 } else {
2266 /* in this case WinXP returns 0 */
2267 FIXME("function return is wrong for WinXP!\n");
2268 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2269 break;
2272 /* append drives here */
2273 if(target && bufsize > 0) target[ret++] = 0;
2274 FIXME("Returned list is not complete\n");
2275 return ret;
2277 /* In theory all that are possible and have been defined.
2278 * Now just those below, since mirc uses it to check for special files.
2280 * (It is more complex, and supports netmounted stuff, and \\.\ stuff,
2281 * but currently we just ignore that.)
2283 if (!strcmpiW(devname, auxW)) {
2284 pDev = dosW;
2285 pName = comW;
2286 numsiz = 1;
2287 pNum = oneW;
2288 } else if (!strcmpiW(devname, nulW)) {
2289 pDev = devW;
2290 pName = nullW;
2291 } else if (!strncmpiW(devname, comW, strlenW(comW))) {
2292 pDev = devW;
2293 pName = serW;
2294 pNum = devname + strlenW(comW);
2295 for(numsiz=0; isdigitW(*(pNum+numsiz)); numsiz++);
2296 if(*(pNum + numsiz)) {
2297 SetLastError(ERROR_FILE_NOT_FOUND);
2298 return 0;
2300 } else if (!strncmpiW(devname, lptW, strlenW(lptW))) {
2301 pDev = devW;
2302 pName = parW;
2303 pNum = devname + strlenW(lptW);
2304 for(numsiz=0; isdigitW(*(pNum+numsiz)); numsiz++);
2305 if(*(pNum + numsiz)) {
2306 SetLastError(ERROR_FILE_NOT_FOUND);
2307 return 0;
2309 } else {
2310 /* This might be a DOS device we do not handle yet ... */
2311 FIXME("(%s) not detected as DOS device!\n",debugstr_w(devname));
2313 /* Win9x set the error ERROR_INVALID_PARAMETER */
2314 SetLastError(ERROR_FILE_NOT_FOUND);
2315 return 0;
2317 FIXME("device %s may not exist on this computer\n", debugstr_w(devname));
2319 ret = strlenW(pDev) + strlenW(pName) + numsiz + 2;
2320 if (ret > bufsize) ret = 0;
2321 if (target && ret) {
2322 strcpyW(target,pDev);
2323 strcatW(target,pName);
2324 if (pNum) strcatW(target,pNum);
2325 target[ret-1] = 0;
2327 return ret;