Moved a number of DOS definitions out of the global headers and into
[wine/hacks.git] / files / dos_fs.c
blob9e0bba49852682ecbcfb7248336145889f79ea6b
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 "winternl.h"
57 #include "wine/server.h"
58 #include "excpt.h"
60 #include "smb.h"
62 #include "wine/debug.h"
64 WINE_DEFAULT_DEBUG_CHANNEL(dosfs);
65 WINE_DECLARE_DEBUG_CHANNEL(file);
67 /* Define the VFAT ioctl to get both short and long file names */
68 /* FIXME: is it possible to get this to work on other systems? */
69 #ifdef linux
70 /* We want the real kernel dirent structure, not the libc one */
71 typedef struct
73 long d_ino;
74 long d_off;
75 unsigned short d_reclen;
76 char d_name[256];
77 } KERNEL_DIRENT;
79 #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, KERNEL_DIRENT [2] )
81 /* To avoid blocking on non-directories in DOSFS_OpenDir_VFAT*/
82 #ifndef O_DIRECTORY
83 # define O_DIRECTORY 0200000 /* must be directory */
84 #endif
86 #else /* linux */
87 #undef VFAT_IOCTL_READDIR_BOTH /* just in case... */
88 #endif /* linux */
90 #define IS_OPTION_TRUE(ch) ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1')
92 /* Chars we don't want to see in DOS file names */
93 #define INVALID_DOS_CHARS "*?<>|\"+=,;[] \345"
95 static const DOS_DEVICE DOSFS_Devices[] =
96 /* name, device flags (see Int 21/AX=0x4400) */
98 { {'C','O','N',0}, 0xc0d3 },
99 { {'P','R','N',0}, 0xa0c0 },
100 { {'N','U','L',0}, 0x80c4 },
101 { {'A','U','X',0}, 0x80c0 },
102 { {'L','P','T','1',0}, 0xa0c0 },
103 { {'L','P','T','2',0}, 0xa0c0 },
104 { {'L','P','T','3',0}, 0xa0c0 },
105 { {'L','P','T','4',0}, 0xc0d3 },
106 { {'C','O','M','1',0}, 0x80c0 },
107 { {'C','O','M','2',0}, 0x80c0 },
108 { {'C','O','M','3',0}, 0x80c0 },
109 { {'C','O','M','4',0}, 0x80c0 },
110 { {'S','C','S','I','M','G','R','$',0}, 0xc0c0 },
111 { {'H','P','S','C','A','N',0}, 0xc0c0 },
112 { {'E','M','M','X','X','X','X','0',0}, 0x0000 }
115 static const WCHAR devW[] = {'\\','D','e','v','i','c','e','\\',0};
116 static const WCHAR dosW[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\',0};
118 static const WCHAR auxW[] = {'A','U','X',0};
119 static const WCHAR comW[] = {'C','O','M',0};
120 static const WCHAR lptW[] = {'L','P','T',0};
121 static const WCHAR nulW[] = {'N','U','L',0};
123 static const WCHAR nullW[] = {'N','u','l','l',0};
124 static const WCHAR parW[] = {'P','a','r','a','l','l','e','l',0};
125 static const WCHAR serW[] = {'S','e','r','i','a','l',0};
126 static const WCHAR oneW[] = {'1',0};
129 * Directory info for DOSFS_ReadDir
130 * contains the names of *all* the files in the directory
132 typedef struct
134 int used;
135 int size;
136 WCHAR names[1];
137 } DOS_DIR;
139 /* Info structure for FindFirstFile handle */
140 typedef struct
142 char *path; /* unix path */
143 LPWSTR long_mask;
144 int drive;
145 int cur_pos;
146 CRITICAL_SECTION cs;
147 union
149 DOS_DIR *dos_dir;
150 SMB_DIR *smb_dir;
151 } u;
152 } FIND_FIRST_INFO;
155 static WINE_EXCEPTION_FILTER(page_fault)
157 if (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION)
158 return EXCEPTION_EXECUTE_HANDLER;
159 return EXCEPTION_CONTINUE_SEARCH;
163 /***********************************************************************
164 * DOSFS_ValidDOSName
166 * Return 1 if Unix file 'name' is also a valid MS-DOS name
167 * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
168 * File name can be terminated by '\0', '\\' or '/'.
170 static int DOSFS_ValidDOSName( LPCWSTR name, int ignore_case )
172 static const char invalid_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS;
173 const WCHAR *p = name;
174 const char *invalid = ignore_case ? (invalid_chars + 26) : invalid_chars;
175 int len = 0;
177 if (*p == '.')
179 /* Check for "." and ".." */
180 p++;
181 if (*p == '.') p++;
182 /* All other names beginning with '.' are invalid */
183 return (IS_END_OF_NAME(*p));
185 while (!IS_END_OF_NAME(*p))
187 if (*p < 256 && strchr( invalid, (char)*p )) return 0; /* Invalid char */
188 if (*p == '.') break; /* Start of the extension */
189 if (++len > 8) return 0; /* Name too long */
190 p++;
192 if (*p != '.') return 1; /* End of name */
193 p++;
194 if (IS_END_OF_NAME(*p)) return 0; /* Empty extension not allowed */
195 len = 0;
196 while (!IS_END_OF_NAME(*p))
198 if (*p < 256 && strchr( invalid, (char)*p )) return 0; /* Invalid char */
199 if (*p == '.') return 0; /* Second extension not allowed */
200 if (++len > 3) return 0; /* Extension too long */
201 p++;
203 return 1;
207 /***********************************************************************
208 * DOSFS_ToDosFCBFormat
210 * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
211 * expanding wild cards and converting to upper-case in the process.
212 * File name can be terminated by '\0', '\\' or '/'.
213 * Return FALSE if the name is not a valid DOS name.
214 * 'buffer' must be at least 12 characters long.
216 BOOL DOSFS_ToDosFCBFormat( LPCWSTR name, LPWSTR buffer )
218 static const char invalid_chars[] = INVALID_DOS_CHARS;
219 LPCWSTR p = name;
220 int i;
222 /* Check for "." and ".." */
223 if (*p == '.')
225 p++;
226 buffer[0] = '.';
227 for(i = 1; i < 11; i++) buffer[i] = ' ';
228 buffer[11] = 0;
229 if (*p == '.')
231 buffer[1] = '.';
232 p++;
234 return (!*p || (*p == '/') || (*p == '\\'));
237 for (i = 0; i < 8; i++)
239 switch(*p)
241 case '\0':
242 case '\\':
243 case '/':
244 case '.':
245 buffer[i] = ' ';
246 break;
247 case '?':
248 p++;
249 /* fall through */
250 case '*':
251 buffer[i] = '?';
252 break;
253 default:
254 if (*p < 256 && strchr( invalid_chars, (char)*p )) return FALSE;
255 buffer[i] = toupperW(*p);
256 p++;
257 break;
261 if (*p == '*')
263 /* Skip all chars after wildcard up to first dot */
264 while (*p && (*p != '/') && (*p != '\\') && (*p != '.')) p++;
266 else
268 /* Check if name too long */
269 if (*p && (*p != '/') && (*p != '\\') && (*p != '.')) return FALSE;
271 if (*p == '.') p++; /* Skip dot */
273 for (i = 8; i < 11; i++)
275 switch(*p)
277 case '\0':
278 case '\\':
279 case '/':
280 buffer[i] = ' ';
281 break;
282 case '.':
283 return FALSE; /* Second extension not allowed */
284 case '?':
285 p++;
286 /* fall through */
287 case '*':
288 buffer[i] = '?';
289 break;
290 default:
291 if (*p < 256 && strchr( invalid_chars, (char)*p )) return FALSE;
292 buffer[i] = toupperW(*p);
293 p++;
294 break;
297 buffer[11] = '\0';
299 /* at most 3 character of the extension are processed
300 * is something behind this ?
302 while (*p == '*' || *p == ' ') p++; /* skip wildcards and spaces */
303 return IS_END_OF_NAME(*p);
307 /***********************************************************************
308 * DOSFS_ToDosDTAFormat
310 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
311 * converting to upper-case in the process.
312 * File name can be terminated by '\0', '\\' or '/'.
313 * 'buffer' must be at least 13 characters long.
315 static void DOSFS_ToDosDTAFormat( LPCWSTR name, LPWSTR buffer )
317 LPWSTR p;
319 memcpy( buffer, name, 8 * sizeof(WCHAR) );
320 p = buffer + 8;
321 while ((p > buffer) && (p[-1] == ' ')) p--;
322 *p++ = '.';
323 memcpy( p, name + 8, 3 * sizeof(WCHAR) );
324 p += 3;
325 while (p[-1] == ' ') p--;
326 if (p[-1] == '.') p--;
327 *p = '\0';
331 /***********************************************************************
332 * DOSFS_MatchLong
334 * Check a long file name against a mask.
336 * Tests (done in W95 DOS shell - case insensitive):
337 * *.txt test1.test.txt *
338 * *st1* test1.txt *
339 * *.t??????.t* test1.ta.tornado.txt *
340 * *tornado* test1.ta.tornado.txt *
341 * t*t test1.ta.tornado.txt *
342 * ?est* test1.txt *
343 * ?est??? test1.txt -
344 * *test1.txt* test1.txt *
345 * h?l?o*t.dat hellothisisatest.dat *
347 static int DOSFS_MatchLong( LPCWSTR mask, LPCWSTR name, int case_sensitive )
349 LPCWSTR lastjoker = NULL;
350 LPCWSTR next_to_retry = NULL;
351 static const WCHAR asterisk_dot_asterisk[] = {'*','.','*',0};
353 TRACE("(%s, %s, %x)\n", debugstr_w(mask), debugstr_w(name), case_sensitive);
355 if (!strcmpW( mask, asterisk_dot_asterisk )) return 1;
356 while (*name && *mask)
358 if (*mask == '*')
360 mask++;
361 while (*mask == '*') mask++; /* Skip consecutive '*' */
362 lastjoker = mask;
363 if (!*mask) return 1; /* end of mask is all '*', so match */
365 /* skip to the next match after the joker(s) */
366 if (case_sensitive) while (*name && (*name != *mask)) name++;
367 else while (*name && (toupperW(*name) != toupperW(*mask))) name++;
369 if (!*name) break;
370 next_to_retry = name;
372 else if (*mask != '?')
374 int mismatch = 0;
375 if (case_sensitive)
377 if (*mask != *name) mismatch = 1;
379 else
381 if (toupperW(*mask) != toupperW(*name)) mismatch = 1;
383 if (!mismatch)
385 mask++;
386 name++;
387 if (*mask == '\0')
389 if (*name == '\0')
390 return 1;
391 if (lastjoker)
392 mask = lastjoker;
395 else /* mismatch ! */
397 if (lastjoker) /* we had an '*', so we can try unlimitedly */
399 mask = lastjoker;
401 /* this scan sequence was a mismatch, so restart
402 * 1 char after the first char we checked last time */
403 next_to_retry++;
404 name = next_to_retry;
406 else
407 return 0; /* bad luck */
410 else /* '?' */
412 mask++;
413 name++;
416 while ((*mask == '.') || (*mask == '*'))
417 mask++; /* Ignore trailing '.' or '*' in mask */
418 return (!*name && !*mask);
422 /***********************************************************************
423 * DOSFS_AddDirEntry
425 * Used to construct an array of filenames in DOSFS_OpenDir
427 static BOOL DOSFS_AddDirEntry(DOS_DIR **dir, LPCWSTR name, LPCWSTR dosname)
429 int extra1 = strlenW(name) + 1;
430 int extra2 = strlenW(dosname) + 1;
432 /* if we need more, at minimum double the size */
433 if( (extra1 + extra2 + (*dir)->used) > (*dir)->size)
435 int more = (*dir)->size;
436 DOS_DIR *t;
438 if(more<(extra1+extra2))
439 more = extra1+extra2;
441 t = HeapReAlloc(GetProcessHeap(), 0, *dir, sizeof(**dir) +
442 ((*dir)->size + more)*sizeof(WCHAR) );
443 if(!t)
445 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
446 ERR("Out of memory caching directory structure %d %d %d\n",
447 (*dir)->size, more, (*dir)->used);
448 return FALSE;
450 (*dir) = t;
451 (*dir)->size += more;
454 /* at this point, the dir structure is big enough to hold these names */
455 strcpyW(&(*dir)->names[(*dir)->used], name);
456 (*dir)->used += extra1;
457 strcpyW(&(*dir)->names[(*dir)->used], dosname);
458 (*dir)->used += extra2;
460 return TRUE;
464 /***********************************************************************
465 * DOSFS_OpenDir_VFAT
467 static BOOL DOSFS_OpenDir_VFAT(DOS_DIR **dir, const char *unix_path)
469 #ifdef VFAT_IOCTL_READDIR_BOTH
470 KERNEL_DIRENT de[2];
471 int fd = open( unix_path, O_RDONLY|O_DIRECTORY );
472 BOOL r = TRUE;
474 /* Check if the VFAT ioctl is supported on this directory */
476 if ( fd<0 )
477 return FALSE;
479 while (1)
481 WCHAR long_name[MAX_PATH];
482 WCHAR short_name[12];
484 r = (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de ) != -1);
485 if(!r)
486 break;
487 if (!de[0].d_reclen)
488 break;
489 MultiByteToWideChar(CP_UNIXCP, 0, de[0].d_name, -1, long_name, MAX_PATH);
490 if (!DOSFS_ToDosFCBFormat( long_name, short_name ))
491 short_name[0] = '\0';
492 if (de[1].d_name[0])
493 MultiByteToWideChar(CP_UNIXCP, 0, de[1].d_name, -1, long_name, MAX_PATH);
494 else
495 MultiByteToWideChar(CP_UNIXCP, 0, de[0].d_name, -1, long_name, MAX_PATH);
496 r = DOSFS_AddDirEntry(dir, long_name, short_name );
497 if(!r)
498 break;
500 if(r)
502 static const WCHAR empty_strW[] = { 0 };
503 DOSFS_AddDirEntry(dir, empty_strW, empty_strW);
505 close(fd);
506 return r;
507 #else
508 return FALSE;
509 #endif /* VFAT_IOCTL_READDIR_BOTH */
513 /***********************************************************************
514 * DOSFS_OpenDir_Normal
516 * Now use the standard opendir/readdir interface
518 static BOOL DOSFS_OpenDir_Normal( DOS_DIR **dir, const char *unix_path )
520 DIR *unixdir = opendir( unix_path );
521 BOOL r = TRUE;
522 static const WCHAR empty_strW[] = { 0 };
524 if(!unixdir)
525 return FALSE;
526 while(1)
528 WCHAR long_name[MAX_PATH];
529 struct dirent *de = readdir(unixdir);
531 if(!de)
532 break;
533 MultiByteToWideChar(CP_UNIXCP, 0, de->d_name, -1, long_name, MAX_PATH);
534 r = DOSFS_AddDirEntry(dir, long_name, empty_strW);
535 if(!r)
536 break;
538 if(r)
539 DOSFS_AddDirEntry(dir, empty_strW, empty_strW);
540 closedir(unixdir);
541 return r;
544 /***********************************************************************
545 * DOSFS_OpenDir
547 static DOS_DIR *DOSFS_OpenDir( const char *unix_path )
549 const int init_size = 0x100;
550 DOS_DIR *dir = HeapAlloc( GetProcessHeap(), 0, sizeof(*dir) + init_size*sizeof (WCHAR));
551 BOOL r;
553 TRACE("%s\n",debugstr_a(unix_path));
555 if (!dir)
557 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
558 return NULL;
560 dir->used = 0;
561 dir->size = init_size;
563 /* Treat empty path as root directory. This simplifies path split into
564 directory and mask in several other places */
565 if (!*unix_path) unix_path = "/";
567 r = DOSFS_OpenDir_VFAT( &dir, unix_path);
569 if(!r)
570 r = DOSFS_OpenDir_Normal( &dir, unix_path);
572 if(!r)
574 HeapFree(GetProcessHeap(), 0, dir);
575 return NULL;
577 dir->used = 0;
579 return dir;
583 /***********************************************************************
584 * DOSFS_CloseDir
586 static void DOSFS_CloseDir( DOS_DIR *dir )
588 HeapFree( GetProcessHeap(), 0, dir );
592 /***********************************************************************
593 * DOSFS_ReadDir
595 static BOOL DOSFS_ReadDir( DOS_DIR *dir, LPCWSTR *long_name,
596 LPCWSTR *short_name )
598 LPCWSTR sn, ln;
600 if (!dir)
601 return FALSE;
603 /* the long pathname is first */
604 ln = &dir->names[dir->used];
605 if(ln[0])
606 *long_name = ln;
607 else
608 return FALSE;
609 dir->used += (strlenW(ln) + 1);
611 /* followed by the short path name */
612 sn = &dir->names[dir->used];
613 if(sn[0])
614 *short_name = sn;
615 else
616 *short_name = NULL;
617 dir->used += (strlenW(sn) + 1);
619 return TRUE;
623 /***********************************************************************
624 * DOSFS_Hash
626 * Transform a Unix file name into a hashed DOS name. If the name is a valid
627 * DOS name, it is converted to upper-case; otherwise it is replaced by a
628 * hashed version that fits in 8.3 format.
629 * File name can be terminated by '\0', '\\' or '/'.
630 * 'buffer' must be at least 13 characters long.
632 static void DOSFS_Hash( LPCWSTR name, LPWSTR buffer, BOOL dir_format,
633 BOOL ignore_case )
635 static const char invalid_chars[] = INVALID_DOS_CHARS "~.";
636 static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
638 LPCWSTR p, ext;
639 LPWSTR dst;
640 unsigned short hash;
641 int i;
643 if (dir_format)
645 for(i = 0; i < 11; i++) buffer[i] = ' ';
646 buffer[11] = 0;
649 if (DOSFS_ValidDOSName( name, ignore_case ))
651 /* Check for '.' and '..' */
652 if (*name == '.')
654 buffer[0] = '.';
655 if (!dir_format) buffer[1] = buffer[2] = '\0';
656 if (name[1] == '.') buffer[1] = '.';
657 return;
660 /* Simply copy the name, converting to uppercase */
662 for (dst = buffer; !IS_END_OF_NAME(*name) && (*name != '.'); name++)
663 *dst++ = toupperW(*name);
664 if (*name == '.')
666 if (dir_format) dst = buffer + 8;
667 else *dst++ = '.';
668 for (name++; !IS_END_OF_NAME(*name); name++)
669 *dst++ = toupperW(*name);
671 if (!dir_format) *dst = '\0';
672 return;
675 /* Compute the hash code of the file name */
676 /* If you know something about hash functions, feel free to */
677 /* insert a better algorithm here... */
678 if (ignore_case)
680 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
681 hash = (hash<<3) ^ (hash>>5) ^ tolowerW(*p) ^ (tolowerW(p[1]) << 8);
682 hash = (hash<<3) ^ (hash>>5) ^ tolowerW(*p); /* Last character */
684 else
686 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
687 hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
688 hash = (hash << 3) ^ (hash >> 5) ^ *p; /* Last character */
691 /* Find last dot for start of the extension */
692 for (p = name+1, ext = NULL; !IS_END_OF_NAME(*p); p++)
693 if (*p == '.') ext = p;
694 if (ext && IS_END_OF_NAME(ext[1]))
695 ext = NULL; /* Empty extension ignored */
697 /* Copy first 4 chars, replacing invalid chars with '_' */
698 for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
700 if (IS_END_OF_NAME(*p) || (p == ext)) break;
701 *dst++ = (*p < 256 && strchr( invalid_chars, (char)*p )) ? '_' : toupperW(*p);
703 /* Pad to 5 chars with '~' */
704 while (i-- >= 0) *dst++ = '~';
706 /* Insert hash code converted to 3 ASCII chars */
707 *dst++ = hash_chars[(hash >> 10) & 0x1f];
708 *dst++ = hash_chars[(hash >> 5) & 0x1f];
709 *dst++ = hash_chars[hash & 0x1f];
711 /* Copy the first 3 chars of the extension (if any) */
712 if (ext)
714 if (!dir_format) *dst++ = '.';
715 for (i = 3, ext++; (i > 0) && !IS_END_OF_NAME(*ext); i--, ext++)
716 *dst++ = (*ext < 256 && strchr( invalid_chars, (char)*ext )) ? '_' : toupperW(*ext);
718 if (!dir_format) *dst = '\0';
722 /***********************************************************************
723 * DOSFS_FindUnixName
725 * Find the Unix file name in a given directory that corresponds to
726 * a file name (either in Unix or DOS format).
727 * File name can be terminated by '\0', '\\' or '/'.
728 * Return TRUE if OK, FALSE if no file name matches.
730 * 'long_buf' must be at least 'long_len' characters long. If the long name
731 * turns out to be larger than that, the function returns FALSE.
732 * 'short_buf' must be at least 13 characters long.
734 BOOL DOSFS_FindUnixName( const DOS_FULL_NAME *path, LPCWSTR name, char *long_buf,
735 INT long_len, LPWSTR short_buf, BOOL ignore_case)
737 DOS_DIR *dir;
738 LPCWSTR long_name, short_name;
739 WCHAR dos_name[12], tmp_buf[13];
740 BOOL ret;
742 LPCWSTR p = strchrW( name, '/' );
743 int len = p ? (int)(p - name) : strlenW(name);
744 if ((p = strchrW( name, '\\' ))) len = min( (int)(p - name), len );
745 /* Ignore trailing dots and spaces */
746 while (len > 1 && (name[len-1] == '.' || name[len-1] == ' ')) len--;
747 if (long_len < len + 1) return FALSE;
749 TRACE("%s,%s\n", path->long_name, debugstr_w(name) );
751 if (!DOSFS_ToDosFCBFormat( name, dos_name )) dos_name[0] = '\0';
753 if (!(dir = DOSFS_OpenDir( path->long_name )))
755 WARN("(%s,%s): can't open dir: %s\n",
756 path->long_name, debugstr_w(name), strerror(errno) );
757 return FALSE;
760 while ((ret = DOSFS_ReadDir( dir, &long_name, &short_name )))
762 /* Check against Unix name */
763 if (len == strlenW(long_name))
765 if (!ignore_case)
767 if (!strncmpW( long_name, name, len )) break;
769 else
771 if (!strncmpiW( long_name, name, len )) break;
774 if (dos_name[0])
776 /* Check against hashed DOS name */
777 if (!short_name)
779 DOSFS_Hash( long_name, tmp_buf, TRUE, ignore_case );
780 short_name = tmp_buf;
782 if (!strcmpW( dos_name, short_name )) break;
785 if (ret)
787 if (long_buf) WideCharToMultiByte(CP_UNIXCP, 0, long_name, -1, long_buf, long_len, NULL, NULL);
788 if (short_buf)
790 if (short_name)
791 DOSFS_ToDosDTAFormat( short_name, short_buf );
792 else
793 DOSFS_Hash( long_name, short_buf, FALSE, ignore_case );
795 TRACE("(%s,%s) -> %s (%s)\n", path->long_name, debugstr_w(name),
796 debugstr_w(long_name), short_buf ? debugstr_w(short_buf) : "***");
798 else
799 WARN("%s not found in '%s'\n", debugstr_w(name), path->long_name);
800 DOSFS_CloseDir( dir );
801 return ret;
805 /***********************************************************************
806 * DOSFS_GetDevice
808 * Check if a DOS file name represents a DOS device and return the device.
810 const DOS_DEVICE *DOSFS_GetDevice( LPCWSTR name )
812 unsigned int i;
813 const WCHAR *p;
815 if (!name) return NULL; /* if wine_server_handle_to_fd was used */
816 if (name[0] && (name[1] == ':')) name += 2;
817 if ((p = strrchrW( name, '/' ))) name = p + 1;
818 if ((p = strrchrW( name, '\\' ))) name = p + 1;
819 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
821 const WCHAR *dev = DOSFS_Devices[i].name;
822 if (!strncmpiW( dev, name, strlenW(dev) ))
824 p = name + strlenW( dev );
825 if (!*p || (*p == '.') || (*p == ':')) return &DOSFS_Devices[i];
828 return NULL;
832 /***********************************************************************
833 * DOSFS_GetDeviceByHandle
835 const DOS_DEVICE *DOSFS_GetDeviceByHandle( HANDLE hFile )
837 const DOS_DEVICE *ret = NULL;
838 SERVER_START_REQ( get_device_id )
840 req->handle = hFile;
841 if (!wine_server_call( req ))
843 if ((reply->id >= 0) &&
844 (reply->id < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0])))
845 ret = &DOSFS_Devices[reply->id];
848 SERVER_END_REQ;
849 return ret;
853 /**************************************************************************
854 * DOSFS_CreateCommPort
856 static HANDLE DOSFS_CreateCommPort(LPCWSTR name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa)
858 HANDLE ret;
859 HKEY hkey;
860 DWORD dummy;
861 OBJECT_ATTRIBUTES attr;
862 UNICODE_STRING nameW;
863 WCHAR *devnameW;
864 char tmp[128];
865 char devname[40];
867 static const WCHAR serialportsW[] = {'M','a','c','h','i','n','e','\\',
868 'S','o','f','t','w','a','r','e','\\',
869 'W','i','n','e','\\','W','i','n','e','\\',
870 'C','o','n','f','i','g','\\',
871 'S','e','r','i','a','l','P','o','r','t','s',0};
873 TRACE_(file)("%s %lx %lx\n", debugstr_w(name), access, attributes);
875 attr.Length = sizeof(attr);
876 attr.RootDirectory = 0;
877 attr.ObjectName = &nameW;
878 attr.Attributes = 0;
879 attr.SecurityDescriptor = NULL;
880 attr.SecurityQualityOfService = NULL;
881 RtlInitUnicodeString( &nameW, serialportsW );
883 if (NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr )) return 0;
885 RtlInitUnicodeString( &nameW, name );
886 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
887 devnameW = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
888 else
889 devnameW = NULL;
891 NtClose( hkey );
893 if (!devnameW) return 0;
894 WideCharToMultiByte(CP_ACP, 0, devnameW, -1, devname, sizeof(devname), NULL, NULL);
896 TRACE("opening %s as %s\n", devname, debugstr_w(name));
898 SERVER_START_REQ( create_serial )
900 req->access = access;
901 req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
902 req->attributes = attributes;
903 req->sharing = FILE_SHARE_READ|FILE_SHARE_WRITE;
904 wine_server_add_data( req, devname, strlen(devname) );
905 SetLastError(0);
906 wine_server_call_err( req );
907 ret = reply->handle;
909 SERVER_END_REQ;
911 if(!ret)
912 ERR("Couldn't open device '%s' ! (check permissions)\n",devname);
913 else
914 TRACE("return %p\n", ret );
915 return ret;
918 /***********************************************************************
919 * DOSFS_OpenDevice
921 * Open a DOS device. This might not map 1:1 into the UNIX device concept.
922 * Returns 0 on failure.
924 HANDLE DOSFS_OpenDevice( LPCWSTR name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa )
926 unsigned int i;
927 const WCHAR *p;
928 HANDLE handle;
930 if (name[0] && (name[1] == ':')) name += 2;
931 if ((p = strrchrW( name, '/' ))) name = p + 1;
932 if ((p = strrchrW( name, '\\' ))) name = p + 1;
933 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
935 const WCHAR *dev = DOSFS_Devices[i].name;
936 if (!strncmpiW( dev, name, strlenW(dev) ))
938 p = name + strlenW( dev );
939 if (!*p || (*p == '.') || (*p == ':')) {
940 static const WCHAR nulW[] = {'N','U','L',0};
941 static const WCHAR conW[] = {'C','O','N',0};
942 static const WCHAR scsimgrW[] = {'S','C','S','I','M','G','R','$',0};
943 static const WCHAR hpscanW[] = {'H','P','S','C','A','N',0};
944 static const WCHAR emmxxxx0W[] = {'E','M','M','X','X','X','X','0',0};
945 /* got it */
946 if (!strcmpiW(DOSFS_Devices[i].name, nulW))
947 return FILE_CreateFile( "/dev/null", access,
948 FILE_SHARE_READ|FILE_SHARE_WRITE, sa,
949 OPEN_EXISTING, 0, 0, TRUE, DRIVE_UNKNOWN );
950 if (!strcmpiW(DOSFS_Devices[i].name, conW)) {
951 HANDLE to_dup;
952 switch (access & (GENERIC_READ|GENERIC_WRITE)) {
953 case GENERIC_READ:
954 to_dup = GetStdHandle( STD_INPUT_HANDLE );
955 break;
956 case GENERIC_WRITE:
957 to_dup = GetStdHandle( STD_OUTPUT_HANDLE );
958 break;
959 default:
960 FIXME("can't open CON read/write\n");
961 return 0;
963 if (!DuplicateHandle( GetCurrentProcess(), to_dup, GetCurrentProcess(),
964 &handle, 0,
965 sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle,
966 DUPLICATE_SAME_ACCESS ))
967 handle = 0;
968 return handle;
970 if (!strcmpiW(DOSFS_Devices[i].name, scsimgrW) ||
971 !strcmpiW(DOSFS_Devices[i].name, hpscanW) ||
972 !strcmpiW(DOSFS_Devices[i].name, emmxxxx0W))
974 return FILE_CreateDevice( i, access, sa );
977 if( (handle=DOSFS_CreateCommPort(DOSFS_Devices[i].name,access,attributes,sa)) )
978 return handle;
979 FIXME("device open %s not supported (yet)\n", debugstr_w(DOSFS_Devices[i].name));
980 return 0;
984 return 0;
988 /***********************************************************************
989 * DOSFS_GetPathDrive
991 * Get the drive specified by a given path name (DOS or Unix format).
993 static int DOSFS_GetPathDrive( LPCWSTR *name )
995 int drive;
996 LPCWSTR p = *name;
998 if (*p && (p[1] == ':'))
1000 drive = toupperW(*p) - 'A';
1001 *name += 2;
1003 else if (*p == '/') /* Absolute Unix path? */
1005 if ((drive = DRIVE_FindDriveRootW( name )) == -1)
1007 MESSAGE("Warning: %s not accessible from a configured DOS drive\n", debugstr_w(*name) );
1008 /* Assume it really was a DOS name */
1009 drive = DRIVE_GetCurrentDrive();
1012 else drive = DRIVE_GetCurrentDrive();
1014 if (!DRIVE_IsValid(drive))
1016 SetLastError( ERROR_INVALID_DRIVE );
1017 return -1;
1019 return drive;
1023 /***********************************************************************
1024 * DOSFS_GetFullName
1026 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
1027 * Unix name / short DOS name pair.
1028 * Return FALSE if one of the path components does not exist. The last path
1029 * component is only checked if 'check_last' is non-zero.
1030 * The buffers pointed to by 'long_buf' and 'short_buf' must be
1031 * at least MAX_PATHNAME_LEN long.
1033 BOOL DOSFS_GetFullName( LPCWSTR name, BOOL check_last, DOS_FULL_NAME *full )
1035 BOOL found;
1036 UINT flags;
1037 char *p_l, *root;
1038 LPWSTR p_s;
1039 static const WCHAR driveA_rootW[] = {'A',':','\\',0};
1040 static const WCHAR dos_rootW[] = {'\\',0};
1042 TRACE("%s (last=%d)\n", debugstr_w(name), check_last );
1044 if ((!*name) || (*name=='\n'))
1045 { /* error code for Win98 */
1046 SetLastError(ERROR_BAD_PATHNAME);
1047 return FALSE;
1050 if ((full->drive = DOSFS_GetPathDrive( &name )) == -1) return FALSE;
1051 flags = DRIVE_GetFlags( full->drive );
1053 lstrcpynA( full->long_name, DRIVE_GetRoot( full->drive ),
1054 sizeof(full->long_name) );
1055 if (full->long_name[1]) root = full->long_name + strlen(full->long_name);
1056 else root = full->long_name; /* root directory */
1058 strcpyW( full->short_name, driveA_rootW );
1059 full->short_name[0] += full->drive;
1061 if ((*name == '\\') || (*name == '/')) /* Absolute path */
1063 while ((*name == '\\') || (*name == '/')) name++;
1065 else /* Relative path */
1067 lstrcpynA( root + 1, DRIVE_GetUnixCwd( full->drive ),
1068 sizeof(full->long_name) - (root - full->long_name) - 1 );
1069 if (root[1]) *root = '/';
1070 lstrcpynW( full->short_name + 3, DRIVE_GetDosCwd( full->drive ),
1071 sizeof(full->short_name)/sizeof(full->short_name[0]) - 3 );
1074 p_l = full->long_name[1] ? full->long_name + strlen(full->long_name)
1075 : full->long_name;
1076 p_s = full->short_name[3] ? full->short_name + strlenW(full->short_name)
1077 : full->short_name + 2;
1078 found = TRUE;
1080 while (*name && found)
1082 /* Check for '.' and '..' */
1084 if (*name == '.')
1086 if (IS_END_OF_NAME(name[1]))
1088 name++;
1089 while ((*name == '\\') || (*name == '/')) name++;
1090 continue;
1092 else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
1094 name += 2;
1095 while ((*name == '\\') || (*name == '/')) name++;
1096 while ((p_l > root) && (*p_l != '/')) p_l--;
1097 while ((p_s > full->short_name + 2) && (*p_s != '\\')) p_s--;
1098 *p_l = *p_s = '\0'; /* Remove trailing separator */
1099 continue;
1103 /* Make sure buffers are large enough */
1105 if ((p_s >= full->short_name + sizeof(full->short_name)/sizeof(full->short_name[0]) - 14) ||
1106 (p_l >= full->long_name + sizeof(full->long_name) - 1))
1108 SetLastError( ERROR_PATH_NOT_FOUND );
1109 return FALSE;
1112 /* Get the long and short name matching the file name */
1114 if ((found = DOSFS_FindUnixName( full, name, p_l + 1,
1115 sizeof(full->long_name) - (p_l - full->long_name) - 1,
1116 p_s + 1, !(flags & DRIVE_CASE_SENSITIVE) )))
1118 *p_l++ = '/';
1119 p_l += strlen(p_l);
1120 *p_s++ = '\\';
1121 p_s += strlenW(p_s);
1122 while (!IS_END_OF_NAME(*name)) name++;
1124 else if (!check_last)
1126 *p_l++ = '/';
1127 *p_s++ = '\\';
1128 while (!IS_END_OF_NAME(*name) &&
1129 (p_s < full->short_name + sizeof(full->short_name)/sizeof(full->short_name[0]) - 1) &&
1130 (p_l < full->long_name + sizeof(full->long_name) - 1))
1132 WCHAR wch;
1133 *p_s++ = tolowerW(*name);
1134 /* If the drive is case-sensitive we want to create new */
1135 /* files in lower-case otherwise we can't reopen them */
1136 /* under the same short name. */
1137 if (flags & DRIVE_CASE_SENSITIVE) wch = tolowerW(*name);
1138 else wch = *name;
1139 p_l += WideCharToMultiByte(CP_UNIXCP, 0, &wch, 1, p_l, 2, NULL, NULL);
1140 name++;
1142 /* Ignore trailing dots and spaces */
1143 while(p_l[-1] == '.' || p_l[-1] == ' ') {
1144 --p_l;
1145 --p_s;
1147 *p_l = '\0';
1148 *p_s = '\0';
1150 while ((*name == '\\') || (*name == '/')) name++;
1153 if (!found)
1155 if (check_last)
1157 SetLastError( ERROR_FILE_NOT_FOUND );
1158 return FALSE;
1160 if (*name) /* Not last */
1162 SetLastError( ERROR_PATH_NOT_FOUND );
1163 return FALSE;
1166 if (!full->long_name[0]) strcpy( full->long_name, "/" );
1167 if (!full->short_name[2]) strcpyW( full->short_name + 2, dos_rootW );
1168 TRACE("returning %s = %s\n", full->long_name, debugstr_w(full->short_name) );
1169 return TRUE;
1173 /***********************************************************************
1174 * GetShortPathNameW (KERNEL32.@)
1176 * NOTES
1177 * observed:
1178 * longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1179 * longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0
1181 * more observations ( with NT 3.51 (WinDD) ):
1182 * longpath <= 8.3 -> just copy longpath to shortpath
1183 * longpath > 8.3 ->
1184 * a) file does not exist -> return 0, LastError = ERROR_FILE_NOT_FOUND
1185 * b) file does exist -> set the short filename.
1186 * - trailing slashes are reproduced in the short name, even if the
1187 * file is not a directory
1188 * - the absolute/relative path of the short name is reproduced like found
1189 * in the long name
1190 * - longpath and shortpath may have the same address
1191 * Peter Ganten, 1999
1193 DWORD WINAPI GetShortPathNameW( LPCWSTR longpath, LPWSTR shortpath, DWORD shortlen )
1195 DOS_FULL_NAME full_name;
1196 WCHAR tmpshortpath[MAX_PATHNAME_LEN];
1197 const WCHAR *p;
1198 DWORD sp = 0, lp = 0;
1199 int drive;
1200 DWORD tmplen;
1201 UINT flags;
1202 BOOL unixabsolute = *longpath == '/';
1204 TRACE("%s\n", debugstr_w(longpath));
1206 if (!longpath) {
1207 SetLastError(ERROR_INVALID_PARAMETER);
1208 return 0;
1210 if (!longpath[0]) {
1211 SetLastError(ERROR_BAD_PATHNAME);
1212 return 0;
1215 /* check for drive letter */
1216 if (!unixabsolute && longpath[1] == ':' ) {
1217 tmpshortpath[0] = longpath[0];
1218 tmpshortpath[1] = ':';
1219 sp = 2;
1222 if ( ( drive = DOSFS_GetPathDrive ( &longpath )) == -1 ) return 0;
1223 flags = DRIVE_GetFlags ( drive );
1225 if (unixabsolute && drive != DRIVE_GetCurrentDrive()) {
1226 tmpshortpath[0] = drive + 'A';
1227 tmpshortpath[1] = ':';
1228 sp = 2;
1231 while ( longpath[lp] ) {
1233 /* check for path delimiters and reproduce them */
1234 if ( longpath[lp] == '\\' || longpath[lp] == '/' ) {
1235 if (!sp || tmpshortpath[sp-1]!= '\\')
1237 /* strip double "\\" */
1238 tmpshortpath[sp] = '\\';
1239 sp++;
1241 tmpshortpath[sp]=0;/*terminate string*/
1242 lp++;
1243 continue;
1246 tmplen = 0;
1247 for(p = longpath + lp; *p && *p != '/' && *p != '\\'; p++)
1248 tmplen++;
1249 lstrcpynW(tmpshortpath + sp, longpath + lp, tmplen + 1);
1251 /* Check, if the current element is a valid dos name */
1252 if ( DOSFS_ValidDOSName ( longpath + lp, !(flags & DRIVE_CASE_SENSITIVE) ) ) {
1253 sp += tmplen;
1254 lp += tmplen;
1255 continue;
1258 /* Check if the file exists and use the existing file name */
1259 if ( DOSFS_GetFullName ( tmpshortpath, TRUE, &full_name ) ) {
1260 strcpyW(tmpshortpath + sp, strrchrW(full_name.short_name, '\\') + 1);
1261 sp += strlenW(tmpshortpath + sp);
1262 lp += tmplen;
1263 continue;
1266 TRACE("not found!\n" );
1267 SetLastError ( ERROR_FILE_NOT_FOUND );
1268 return 0;
1270 tmpshortpath[sp] = 0;
1272 tmplen = strlenW(tmpshortpath) + 1;
1273 if (tmplen <= shortlen)
1275 strcpyW(shortpath, tmpshortpath);
1276 TRACE("returning %s\n", debugstr_w(shortpath));
1277 tmplen--; /* length without 0 */
1280 return tmplen;
1284 /***********************************************************************
1285 * GetShortPathNameA (KERNEL32.@)
1287 DWORD WINAPI GetShortPathNameA( LPCSTR longpath, LPSTR shortpath, DWORD shortlen )
1289 UNICODE_STRING longpathW;
1290 WCHAR shortpathW[MAX_PATH];
1291 DWORD ret, retW;
1293 if (!longpath)
1295 SetLastError(ERROR_INVALID_PARAMETER);
1296 return 0;
1299 TRACE("%s\n", debugstr_a(longpath));
1301 if (!RtlCreateUnicodeStringFromAsciiz(&longpathW, longpath))
1303 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1304 return 0;
1307 retW = GetShortPathNameW(longpathW.Buffer, shortpathW, MAX_PATH);
1309 if (!retW)
1310 ret = 0;
1311 else if (retW > MAX_PATH)
1313 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1314 ret = 0;
1316 else
1318 ret = WideCharToMultiByte(CP_ACP, 0, shortpathW, -1, NULL, 0, NULL, NULL);
1319 if (ret <= shortlen)
1321 WideCharToMultiByte(CP_ACP, 0, shortpathW, -1, shortpath, shortlen, NULL, NULL);
1322 ret--; /* length without 0 */
1326 RtlFreeUnicodeString(&longpathW);
1327 return ret;
1331 /***********************************************************************
1332 * GetLongPathNameW (KERNEL32.@)
1334 * NOTES
1335 * observed (Win2000):
1336 * shortpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1337 * shortpath="": LastError=ERROR_PATH_NOT_FOUND, ret=0
1339 DWORD WINAPI GetLongPathNameW( LPCWSTR shortpath, LPWSTR longpath, DWORD longlen )
1341 DOS_FULL_NAME full_name;
1342 const char *root;
1343 LPWSTR p;
1344 int drive;
1345 DWORD ret, len = 0;
1347 if (!shortpath) {
1348 SetLastError(ERROR_INVALID_PARAMETER);
1349 return 0;
1351 if (!shortpath[0]) {
1352 SetLastError(ERROR_PATH_NOT_FOUND);
1353 return 0;
1356 TRACE("%s,%p,%ld\n", debugstr_w(shortpath), longpath, longlen);
1358 if(shortpath[0]=='\\' && shortpath[1]=='\\')
1360 ERR("UNC pathname %s\n",debugstr_w(shortpath));
1361 lstrcpynW( longpath, full_name.short_name, longlen );
1362 return strlenW(longpath);
1365 if (!DOSFS_GetFullName( shortpath, TRUE, &full_name )) return 0;
1367 root = full_name.long_name;
1368 drive = DRIVE_FindDriveRoot(&root);
1370 ret = MultiByteToWideChar(CP_UNIXCP, 0, root, -1, NULL, 0);
1371 ret += 3; /* A:\ */
1372 /* reproduce terminating slash */
1373 if (ret > 4) /* if not drive root */
1375 len = strlenW(shortpath);
1376 if (shortpath[len - 1] == '\\' || shortpath[len - 1] == '/')
1377 len = 1;
1379 ret += len;
1380 if (ret <= longlen)
1382 longpath[0] = 'A' + drive;
1383 longpath[1] = ':';
1384 MultiByteToWideChar(CP_UNIXCP, 0, root, -1, longpath + 2, longlen - 2);
1385 for (p = longpath; *p; p++) if (*p == '/') *p = '\\';
1386 if (len)
1388 longpath[ret - 2] = '\\';
1389 longpath[ret - 1] = 0;
1391 TRACE("returning %s\n", debugstr_w(longpath));
1392 ret--; /* length without 0 */
1394 return ret;
1398 /***********************************************************************
1399 * GetLongPathNameA (KERNEL32.@)
1401 DWORD WINAPI GetLongPathNameA( LPCSTR shortpath, LPSTR longpath, DWORD longlen )
1403 UNICODE_STRING shortpathW;
1404 WCHAR longpathW[MAX_PATH];
1405 DWORD ret, retW;
1407 if (!shortpath)
1409 SetLastError(ERROR_INVALID_PARAMETER);
1410 return 0;
1413 TRACE("%s\n", debugstr_a(shortpath));
1415 if (!RtlCreateUnicodeStringFromAsciiz(&shortpathW, shortpath))
1417 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1418 return 0;
1421 retW = GetLongPathNameW(shortpathW.Buffer, longpathW, MAX_PATH);
1423 if (!retW)
1424 ret = 0;
1425 else if (retW > MAX_PATH)
1427 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1428 ret = 0;
1430 else
1432 ret = WideCharToMultiByte(CP_ACP, 0, longpathW, -1, NULL, 0, NULL, NULL);
1433 if (ret <= longlen)
1435 WideCharToMultiByte(CP_ACP, 0, longpathW, -1, longpath, longlen, NULL, NULL);
1436 ret--; /* length without 0 */
1440 RtlFreeUnicodeString(&shortpathW);
1441 return ret;
1445 /***********************************************************************
1446 * DOSFS_DoGetFullPathName
1448 * Implementation of GetFullPathNameA/W.
1450 * bon@elektron 000331:
1451 * A test for GetFullPathName with many pathological cases
1452 * now gives identical output for Wine and OSR2
1454 static DWORD DOSFS_DoGetFullPathName( LPCWSTR name, DWORD len, LPWSTR result )
1456 DWORD ret;
1457 DOS_FULL_NAME full_name;
1458 LPWSTR p, q;
1459 char *p_l;
1460 const char * root;
1461 WCHAR drivecur[] = {'C',':','.',0};
1462 WCHAR driveletter=0;
1463 int namelen,drive=0;
1464 static const WCHAR bkslashW[] = {'\\',0};
1465 static const WCHAR dotW[] = {'.',0};
1466 static const WCHAR updir_slashW[] = {'\\','.','.','\\',0};
1467 static const WCHAR curdirW[] = {'\\','.','\\',0};
1468 static const WCHAR updirW[] = {'\\','.','.',0};
1470 if (!name[0])
1472 SetLastError(ERROR_BAD_PATHNAME);
1473 return 0;
1476 TRACE("passed %s\n", debugstr_w(name));
1478 if (name[1]==':')
1479 /*drive letter given */
1481 driveletter = name[0];
1483 if ((name[1]==':') && ((name[2]=='\\') || (name[2]=='/')))
1484 /*absolute path given */
1486 strncpyW(full_name.short_name, name, MAX_PATHNAME_LEN);
1487 full_name.short_name[MAX_PATHNAME_LEN - 1] = 0; /* ensure 0 termination */
1488 drive = toupperW(name[0]) - 'A';
1490 else
1492 if (driveletter)
1493 drivecur[0]=driveletter;
1494 else if ((name[0]=='\\') || (name[0]=='/'))
1495 strcpyW(drivecur, bkslashW);
1496 else
1497 strcpyW(drivecur, dotW);
1499 if (!DOSFS_GetFullName( drivecur, FALSE, &full_name ))
1501 FIXME("internal: error getting drive/path\n");
1502 return 0;
1504 /* find path that drive letter substitutes*/
1505 drive = toupperW(full_name.short_name[0]) - 'A';
1506 root= DRIVE_GetRoot(drive);
1507 if (!root)
1509 FIXME("internal: error getting DOS Drive Root\n");
1510 return 0;
1512 if (!strcmp(root,"/"))
1514 /* we have just the last / and we need it. */
1515 p_l = full_name.long_name;
1517 else
1519 p_l = full_name.long_name + strlen(root);
1521 /* append long name (= unix name) to drive */
1522 MultiByteToWideChar(CP_UNIXCP, 0, p_l, -1, full_name.short_name + 2, MAX_PATHNAME_LEN - 3);
1523 /* append name to treat */
1524 namelen= strlenW(full_name.short_name);
1525 p = (LPWSTR)name;
1526 if (driveletter)
1527 p += 2; /* skip drive name when appending */
1528 if (namelen + 2 + strlenW(p) > MAX_PATHNAME_LEN)
1530 FIXME("internal error: buffer too small\n");
1531 return 0;
1533 full_name.short_name[namelen++] ='\\';
1534 full_name.short_name[namelen] = 0;
1535 strncpyW(full_name.short_name + namelen, p, MAX_PATHNAME_LEN - namelen);
1536 full_name.short_name[MAX_PATHNAME_LEN - 1] = 0; /* ensure 0 termination */
1538 /* reverse all slashes */
1539 for (p=full_name.short_name;
1540 p < full_name.short_name + strlenW(full_name.short_name);
1541 p++)
1543 if ( *p == '/' )
1544 *p = '\\';
1546 /* Use memmove, as areas overlap */
1547 /* Delete .. */
1548 while ((p = strstrW(full_name.short_name, updir_slashW)))
1550 if (p > full_name.short_name+2)
1552 *p = 0;
1553 q = strrchrW(full_name.short_name, '\\');
1554 memmove(q+1, p+4, (strlenW(p+4)+1) * sizeof(WCHAR));
1556 else
1558 memmove(full_name.short_name+3, p+4, (strlenW(p+4)+1) * sizeof(WCHAR));
1561 if ((full_name.short_name[2]=='.')&&(full_name.short_name[3]=='.'))
1563 /* This case istn't treated yet : c:..\test */
1564 memmove(full_name.short_name+2,full_name.short_name+4,
1565 (strlenW(full_name.short_name+4)+1) * sizeof(WCHAR));
1567 /* Delete . */
1568 while ((p = strstrW(full_name.short_name, curdirW)))
1570 *(p+1) = 0;
1571 memmove(p+1, p+3, (strlenW(p+3)+1) * sizeof(WCHAR));
1573 if (!(DRIVE_GetFlags(drive) & DRIVE_CASE_PRESERVING))
1574 for (p = full_name.short_name; *p; p++) *p = toupperW(*p);
1575 namelen = strlenW(full_name.short_name);
1576 if (!strcmpW(full_name.short_name+namelen-3, updirW))
1578 /* one more strange case: "c:\test\test1\.."
1579 return "c:\test" */
1580 *(full_name.short_name+namelen-3)=0;
1581 q = strrchrW(full_name.short_name, '\\');
1582 *q =0;
1584 if (full_name.short_name[namelen-1]=='.')
1585 full_name.short_name[(namelen--)-1] =0;
1586 if (!driveletter)
1587 if (full_name.short_name[namelen-1]=='\\')
1588 full_name.short_name[(namelen--)-1] =0;
1589 TRACE("got %s\n", debugstr_w(full_name.short_name));
1591 /* If the lpBuffer buffer is too small, the return value is the
1592 size of the buffer, in characters, required to hold the path
1593 plus the terminating \0 (tested against win95osr2, bon 001118)
1594 . */
1595 ret = strlenW(full_name.short_name);
1596 if (ret >= len )
1598 /* don't touch anything when the buffer is not large enough */
1599 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1600 return ret+1;
1602 if (result)
1604 strncpyW( result, full_name.short_name, len );
1605 result[len - 1] = 0; /* ensure 0 termination */
1608 TRACE("returning %s\n", debugstr_w(full_name.short_name) );
1609 return ret;
1613 /***********************************************************************
1614 * GetFullPathNameA (KERNEL32.@)
1615 * NOTES
1616 * if the path closed with '\', *lastpart is 0
1618 DWORD WINAPI GetFullPathNameA( LPCSTR name, DWORD len, LPSTR buffer,
1619 LPSTR *lastpart )
1621 UNICODE_STRING nameW;
1622 WCHAR bufferW[MAX_PATH];
1623 DWORD ret, retW;
1625 if (!name)
1627 SetLastError(ERROR_INVALID_PARAMETER);
1628 return 0;
1631 if (!RtlCreateUnicodeStringFromAsciiz(&nameW, name))
1633 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1634 return 0;
1637 retW = GetFullPathNameW( nameW.Buffer, MAX_PATH, bufferW, NULL);
1639 if (!retW)
1640 ret = 0;
1641 else if (retW > MAX_PATH)
1643 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1644 ret = 0;
1646 else
1648 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL);
1649 if (ret <= len)
1651 WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, len, NULL, NULL);
1652 ret--; /* length without 0 */
1654 if (lastpart)
1656 LPSTR p = buffer + strlen(buffer);
1658 if (*p != '\\')
1660 while ((p > buffer + 2) && (*p != '\\')) p--;
1661 *lastpart = p + 1;
1663 else *lastpart = NULL;
1668 RtlFreeUnicodeString(&nameW);
1669 return ret;
1673 /***********************************************************************
1674 * GetFullPathNameW (KERNEL32.@)
1676 DWORD WINAPI GetFullPathNameW( LPCWSTR name, DWORD len, LPWSTR buffer,
1677 LPWSTR *lastpart )
1679 DWORD ret = DOSFS_DoGetFullPathName( name, len, buffer );
1680 if (ret && (ret<=len) && buffer && lastpart)
1682 LPWSTR p = buffer + strlenW(buffer);
1683 if (*p != (WCHAR)'\\')
1685 while ((p > buffer + 2) && (*p != (WCHAR)'\\')) p--;
1686 *lastpart = p + 1;
1688 else *lastpart = NULL;
1690 return ret;
1694 /***********************************************************************
1695 * wine_get_unix_file_name (KERNEL32.@) Not a Windows API
1697 * Return the full Unix file name for a given path.
1698 * FIXME: convert dos file name to unicode
1700 BOOL WINAPI wine_get_unix_file_name( LPCSTR dos, LPSTR buffer, DWORD len )
1702 BOOL ret;
1703 DOS_FULL_NAME path;
1704 WCHAR dosW[MAX_PATHNAME_LEN];
1706 MultiByteToWideChar(CP_ACP, 0, dos, -1, dosW, MAX_PATHNAME_LEN);
1707 ret = DOSFS_GetFullName( dosW, FALSE, &path );
1708 if (ret && len)
1710 strncpy( buffer, path.long_name, len );
1711 buffer[len - 1] = 0; /* ensure 0 termination */
1713 return ret;
1717 /***********************************************************************
1718 * get_show_dir_symlinks_option
1720 static BOOL get_show_dir_symlinks_option(void)
1722 static const WCHAR WineW[] = {'M','a','c','h','i','n','e','\\',
1723 'S','o','f','t','w','a','r','e','\\',
1724 'W','i','n','e','\\','W','i','n','e','\\',
1725 'C','o','n','f','i','g','\\','W','i','n','e',0};
1726 static const WCHAR ShowDirSymlinksW[] = {'S','h','o','w','D','i','r','S','y','m','l','i','n','k','s',0};
1728 char tmp[80];
1729 HKEY hkey;
1730 DWORD dummy;
1731 OBJECT_ATTRIBUTES attr;
1732 UNICODE_STRING nameW;
1733 BOOL ret = FALSE;
1735 attr.Length = sizeof(attr);
1736 attr.RootDirectory = 0;
1737 attr.ObjectName = &nameW;
1738 attr.Attributes = 0;
1739 attr.SecurityDescriptor = NULL;
1740 attr.SecurityQualityOfService = NULL;
1741 RtlInitUnicodeString( &nameW, WineW );
1743 if (!NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr ))
1745 RtlInitUnicodeString( &nameW, ShowDirSymlinksW );
1746 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
1748 WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
1749 ret = IS_OPTION_TRUE( str[0] );
1751 NtClose( hkey );
1753 return ret;
1757 /***********************************************************************
1758 * DOSFS_FindNextEx
1760 static int DOSFS_FindNextEx( FIND_FIRST_INFO *info, WIN32_FIND_DATAW *entry )
1762 UINT flags = DRIVE_GetFlags( info->drive );
1763 char *p, buffer[MAX_PATHNAME_LEN];
1764 const char *drive_path;
1765 int drive_root;
1766 LPCWSTR long_name, short_name;
1767 BY_HANDLE_FILE_INFORMATION fileinfo;
1768 BOOL is_symlink;
1770 drive_path = info->path + strlen(DRIVE_GetRoot( info->drive ));
1771 while ((*drive_path == '/') || (*drive_path == '\\')) drive_path++;
1772 drive_root = !*drive_path;
1774 lstrcpynA( buffer, info->path, sizeof(buffer) - 1 );
1775 strcat( buffer, "/" );
1776 p = buffer + strlen(buffer);
1778 while (DOSFS_ReadDir( info->u.dos_dir, &long_name, &short_name ))
1780 info->cur_pos++;
1782 /* Don't return '.' and '..' in the root of the drive */
1783 if (drive_root && (long_name[0] == '.') &&
1784 (!long_name[1] || ((long_name[1] == '.') && !long_name[2])))
1785 continue;
1787 /* Check the long mask */
1789 if (info->long_mask && *info->long_mask)
1791 if (!DOSFS_MatchLong( info->long_mask, long_name,
1792 flags & DRIVE_CASE_SENSITIVE )) continue;
1795 /* Check the file attributes */
1796 WideCharToMultiByte(CP_UNIXCP, 0, long_name, -1,
1797 p, sizeof(buffer) - (int)(p - buffer), NULL, NULL);
1798 if (!FILE_Stat( buffer, &fileinfo, &is_symlink ))
1800 WARN("can't stat %s\n", buffer);
1801 continue;
1803 if (is_symlink && (fileinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
1805 static int show_dir_symlinks = -1;
1806 if (show_dir_symlinks == -1)
1807 show_dir_symlinks = get_show_dir_symlinks_option();
1808 if (!show_dir_symlinks) continue;
1811 /* We now have a matching entry; fill the result and return */
1813 entry->dwFileAttributes = fileinfo.dwFileAttributes;
1814 entry->ftCreationTime = fileinfo.ftCreationTime;
1815 entry->ftLastAccessTime = fileinfo.ftLastAccessTime;
1816 entry->ftLastWriteTime = fileinfo.ftLastWriteTime;
1817 entry->nFileSizeHigh = fileinfo.nFileSizeHigh;
1818 entry->nFileSizeLow = fileinfo.nFileSizeLow;
1820 if (short_name)
1821 DOSFS_ToDosDTAFormat( short_name, entry->cAlternateFileName );
1822 else
1823 DOSFS_Hash( long_name, entry->cAlternateFileName, FALSE,
1824 !(flags & DRIVE_CASE_SENSITIVE) );
1826 lstrcpynW( entry->cFileName, long_name, sizeof(entry->cFileName)/sizeof(entry->cFileName[0]) );
1827 if (!(flags & DRIVE_CASE_PRESERVING)) strlwrW( entry->cFileName );
1828 TRACE("returning %s (%s) %02lx %ld\n",
1829 debugstr_w(entry->cFileName), debugstr_w(entry->cAlternateFileName),
1830 entry->dwFileAttributes, entry->nFileSizeLow );
1831 return 1;
1833 return 0; /* End of directory */
1836 /*************************************************************************
1837 * FindFirstFileExW (KERNEL32.@)
1839 HANDLE WINAPI FindFirstFileExW(
1840 LPCWSTR lpFileName,
1841 FINDEX_INFO_LEVELS fInfoLevelId,
1842 LPVOID lpFindFileData,
1843 FINDEX_SEARCH_OPS fSearchOp,
1844 LPVOID lpSearchFilter,
1845 DWORD dwAdditionalFlags)
1847 FIND_FIRST_INFO *info;
1849 if (!lpFileName)
1851 SetLastError(ERROR_PATH_NOT_FOUND);
1852 return INVALID_HANDLE_VALUE;
1855 if ((fSearchOp != FindExSearchNameMatch) || (dwAdditionalFlags != 0))
1857 FIXME("options not implemented 0x%08x 0x%08lx\n", fSearchOp, dwAdditionalFlags );
1858 return INVALID_HANDLE_VALUE;
1861 switch(fInfoLevelId)
1863 case FindExInfoStandard:
1865 WIN32_FIND_DATAW * data = (WIN32_FIND_DATAW *) lpFindFileData;
1866 char *p;
1867 INT long_mask_len;
1869 data->dwReserved0 = data->dwReserved1 = 0x0;
1870 if (lpFileName[0] == '\\' && lpFileName[1] == '\\')
1872 ERR("UNC path name\n");
1873 if (!(info = HeapAlloc( GetProcessHeap(), 0, sizeof(FIND_FIRST_INFO)))) break;
1874 info->u.smb_dir = SMB_FindFirst(lpFileName);
1875 if(!info->u.smb_dir)
1877 HeapFree(GetProcessHeap(), 0, info);
1878 break;
1880 info->drive = -1;
1881 RtlInitializeCriticalSection( &info->cs );
1883 else
1885 DOS_FULL_NAME full_name;
1887 if (lpFileName[0] && lpFileName[1] == ':')
1889 /* don't allow root directories */
1890 if (!lpFileName[2] ||
1891 ((lpFileName[2] == '/' || lpFileName[2] == '\\') && !lpFileName[3]))
1893 SetLastError(ERROR_FILE_NOT_FOUND);
1894 return INVALID_HANDLE_VALUE;
1897 if (!DOSFS_GetFullName( lpFileName, FALSE, &full_name )) break;
1898 if (!(info = HeapAlloc( GetProcessHeap(), 0, sizeof(FIND_FIRST_INFO)))) break;
1899 RtlInitializeCriticalSection( &info->cs );
1900 info->path = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
1901 strcpy( info->path, full_name.long_name );
1903 p = strrchr( info->path, '/' );
1904 *p++ = '\0';
1905 long_mask_len = MultiByteToWideChar(CP_UNIXCP, 0, p, -1, NULL, 0);
1906 info->long_mask = HeapAlloc( GetProcessHeap(), 0, long_mask_len * sizeof(WCHAR) );
1907 MultiByteToWideChar(CP_UNIXCP, 0, p, -1, info->long_mask, long_mask_len);
1909 info->drive = full_name.drive;
1910 info->cur_pos = 0;
1912 info->u.dos_dir = DOSFS_OpenDir( info->path );
1914 if (!FindNextFileW( (HANDLE) info, data ))
1916 FindClose( (HANDLE) info );
1917 SetLastError( ERROR_FILE_NOT_FOUND );
1918 break;
1920 return (HANDLE) info;
1922 break;
1923 default:
1924 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
1926 return INVALID_HANDLE_VALUE;
1929 /*************************************************************************
1930 * FindFirstFileA (KERNEL32.@)
1932 HANDLE WINAPI FindFirstFileA(
1933 LPCSTR lpFileName,
1934 WIN32_FIND_DATAA *lpFindData )
1936 return FindFirstFileExA(lpFileName, FindExInfoStandard, lpFindData,
1937 FindExSearchNameMatch, NULL, 0);
1940 /*************************************************************************
1941 * FindFirstFileExA (KERNEL32.@)
1943 HANDLE WINAPI FindFirstFileExA(
1944 LPCSTR lpFileName,
1945 FINDEX_INFO_LEVELS fInfoLevelId,
1946 LPVOID lpFindFileData,
1947 FINDEX_SEARCH_OPS fSearchOp,
1948 LPVOID lpSearchFilter,
1949 DWORD dwAdditionalFlags)
1951 HANDLE handle;
1952 WIN32_FIND_DATAA *dataA;
1953 WIN32_FIND_DATAW dataW;
1954 UNICODE_STRING pathW;
1956 if (!lpFileName)
1958 SetLastError(ERROR_PATH_NOT_FOUND);
1959 return INVALID_HANDLE_VALUE;
1962 if (!RtlCreateUnicodeStringFromAsciiz(&pathW, lpFileName))
1964 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1965 return INVALID_HANDLE_VALUE;
1968 handle = FindFirstFileExW(pathW.Buffer, fInfoLevelId, &dataW, fSearchOp, lpSearchFilter, dwAdditionalFlags);
1969 RtlFreeUnicodeString(&pathW);
1970 if (handle == INVALID_HANDLE_VALUE) return handle;
1972 dataA = (WIN32_FIND_DATAA *) lpFindFileData;
1973 dataA->dwFileAttributes = dataW.dwFileAttributes;
1974 dataA->ftCreationTime = dataW.ftCreationTime;
1975 dataA->ftLastAccessTime = dataW.ftLastAccessTime;
1976 dataA->ftLastWriteTime = dataW.ftLastWriteTime;
1977 dataA->nFileSizeHigh = dataW.nFileSizeHigh;
1978 dataA->nFileSizeLow = dataW.nFileSizeLow;
1979 WideCharToMultiByte( CP_ACP, 0, dataW.cFileName, -1,
1980 dataA->cFileName, sizeof(dataA->cFileName), NULL, NULL );
1981 WideCharToMultiByte( CP_ACP, 0, dataW.cAlternateFileName, -1,
1982 dataA->cAlternateFileName, sizeof(dataA->cAlternateFileName), NULL, NULL );
1983 return handle;
1986 /*************************************************************************
1987 * FindFirstFileW (KERNEL32.@)
1989 HANDLE WINAPI FindFirstFileW( LPCWSTR lpFileName, WIN32_FIND_DATAW *lpFindData )
1991 return FindFirstFileExW(lpFileName, FindExInfoStandard, lpFindData,
1992 FindExSearchNameMatch, NULL, 0);
1995 /*************************************************************************
1996 * FindNextFileW (KERNEL32.@)
1998 BOOL WINAPI FindNextFileW( HANDLE handle, WIN32_FIND_DATAW *data )
2000 FIND_FIRST_INFO *info;
2001 BOOL ret = FALSE;
2002 DWORD gle = ERROR_NO_MORE_FILES;
2004 if (handle == INVALID_HANDLE_VALUE)
2006 SetLastError( ERROR_INVALID_HANDLE );
2007 return ret;
2009 info = (FIND_FIRST_INFO*) handle;
2010 RtlEnterCriticalSection( &info->cs );
2011 if (info->drive == -1)
2013 ret = SMB_FindNext( info->u.smb_dir, data );
2014 if(!ret)
2016 SMB_CloseDir( info->u.smb_dir );
2017 HeapFree( GetProcessHeap(), 0, info->path );
2019 goto done;
2021 else if (!info->path || !info->u.dos_dir)
2023 goto done;
2025 else if (!DOSFS_FindNextEx( info, data ))
2027 DOSFS_CloseDir( info->u.dos_dir ); info->u.dos_dir = NULL;
2028 HeapFree( GetProcessHeap(), 0, info->path );
2029 info->path = NULL;
2030 HeapFree( GetProcessHeap(), 0, info->long_mask );
2031 info->long_mask = NULL;
2032 goto done;
2034 ret = TRUE;
2035 done:
2036 RtlLeaveCriticalSection( &info->cs );
2037 if( !ret ) SetLastError( gle );
2038 return ret;
2042 /*************************************************************************
2043 * FindNextFileA (KERNEL32.@)
2045 BOOL WINAPI FindNextFileA( HANDLE handle, WIN32_FIND_DATAA *data )
2047 WIN32_FIND_DATAW dataW;
2048 if (!FindNextFileW( handle, &dataW )) return FALSE;
2049 data->dwFileAttributes = dataW.dwFileAttributes;
2050 data->ftCreationTime = dataW.ftCreationTime;
2051 data->ftLastAccessTime = dataW.ftLastAccessTime;
2052 data->ftLastWriteTime = dataW.ftLastWriteTime;
2053 data->nFileSizeHigh = dataW.nFileSizeHigh;
2054 data->nFileSizeLow = dataW.nFileSizeLow;
2055 WideCharToMultiByte( CP_ACP, 0, dataW.cFileName, -1,
2056 data->cFileName, sizeof(data->cFileName), NULL, NULL );
2057 WideCharToMultiByte( CP_ACP, 0, dataW.cAlternateFileName, -1,
2058 data->cAlternateFileName,
2059 sizeof(data->cAlternateFileName), NULL, NULL );
2060 return TRUE;
2063 /*************************************************************************
2064 * FindClose (KERNEL32.@)
2066 BOOL WINAPI FindClose( HANDLE handle )
2068 FIND_FIRST_INFO *info = (FIND_FIRST_INFO*) handle;
2070 if (handle == INVALID_HANDLE_VALUE) goto error;
2072 __TRY
2074 RtlEnterCriticalSection( &info->cs );
2075 if (info)
2077 if (info->u.dos_dir) DOSFS_CloseDir( info->u.dos_dir );
2078 if (info->path) HeapFree( GetProcessHeap(), 0, info->path );
2079 if (info->long_mask) HeapFree( GetProcessHeap(), 0, info->long_mask );
2082 __EXCEPT(page_fault)
2084 WARN("Illegal handle %p\n", handle);
2085 SetLastError( ERROR_INVALID_HANDLE );
2086 return FALSE;
2088 __ENDTRY
2089 if (!info) goto error;
2090 RtlLeaveCriticalSection( &info->cs );
2091 RtlDeleteCriticalSection( &info->cs );
2092 HeapFree(GetProcessHeap(), 0, info);
2093 return TRUE;
2095 error:
2096 SetLastError( ERROR_INVALID_HANDLE );
2097 return FALSE;
2100 /***********************************************************************
2101 * MulDiv (KERNEL32.@)
2102 * RETURNS
2103 * Result of multiplication and division
2104 * -1: Overflow occurred or Divisor was 0
2106 INT WINAPI MulDiv(
2107 INT nMultiplicand,
2108 INT nMultiplier,
2109 INT nDivisor)
2111 #if SIZEOF_LONG_LONG >= 8
2112 long long ret;
2114 if (!nDivisor) return -1;
2116 /* We want to deal with a positive divisor to simplify the logic. */
2117 if (nDivisor < 0)
2119 nMultiplicand = - nMultiplicand;
2120 nDivisor = -nDivisor;
2123 /* If the result is positive, we "add" to round. else, we subtract to round. */
2124 if ( ( (nMultiplicand < 0) && (nMultiplier < 0) ) ||
2125 ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
2126 ret = (((long long)nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
2127 else
2128 ret = (((long long)nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
2130 if ((ret > 2147483647) || (ret < -2147483647)) return -1;
2131 return ret;
2132 #else
2133 if (!nDivisor) return -1;
2135 /* We want to deal with a positive divisor to simplify the logic. */
2136 if (nDivisor < 0)
2138 nMultiplicand = - nMultiplicand;
2139 nDivisor = -nDivisor;
2142 /* If the result is positive, we "add" to round. else, we subtract to round. */
2143 if ( ( (nMultiplicand < 0) && (nMultiplier < 0) ) ||
2144 ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
2145 return ((nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
2147 return ((nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
2149 #endif
2153 /***********************************************************************
2154 * DosDateTimeToFileTime (KERNEL32.@)
2156 BOOL WINAPI DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft)
2158 struct tm newtm;
2159 #ifndef HAVE_TIMEGM
2160 struct tm *gtm;
2161 time_t time1, time2;
2162 #endif
2164 newtm.tm_sec = (fattime & 0x1f) * 2;
2165 newtm.tm_min = (fattime >> 5) & 0x3f;
2166 newtm.tm_hour = (fattime >> 11);
2167 newtm.tm_mday = (fatdate & 0x1f);
2168 newtm.tm_mon = ((fatdate >> 5) & 0x0f) - 1;
2169 newtm.tm_year = (fatdate >> 9) + 80;
2170 #ifdef HAVE_TIMEGM
2171 RtlSecondsSince1970ToTime( timegm(&newtm), (LARGE_INTEGER *)ft );
2172 #else
2173 time1 = mktime(&newtm);
2174 gtm = gmtime(&time1);
2175 time2 = mktime(gtm);
2176 RtlSecondsSince1970ToTime( 2*time1-time2, (LARGE_INTEGER *)ft );
2177 #endif
2178 return TRUE;
2182 /***********************************************************************
2183 * FileTimeToDosDateTime (KERNEL32.@)
2185 BOOL WINAPI FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate,
2186 LPWORD fattime )
2188 LARGE_INTEGER li;
2189 ULONG t;
2190 time_t unixtime;
2191 struct tm* tm;
2193 li.s.LowPart = ft->dwLowDateTime;
2194 li.s.HighPart = ft->dwHighDateTime;
2195 RtlTimeToSecondsSince1970( &li, &t );
2196 unixtime = t;
2197 tm = gmtime( &unixtime );
2198 if (fattime)
2199 *fattime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
2200 if (fatdate)
2201 *fatdate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
2202 + tm->tm_mday;
2203 return TRUE;
2207 /***********************************************************************
2208 * QueryDosDeviceA (KERNEL32.@)
2210 * returns array of strings terminated by \0, terminated by \0
2212 DWORD WINAPI QueryDosDeviceA(LPCSTR devname,LPSTR target,DWORD bufsize)
2214 DWORD ret = 0, retW;
2215 LPWSTR targetW = (LPWSTR)HeapAlloc(GetProcessHeap(),0,
2216 bufsize * sizeof(WCHAR));
2217 UNICODE_STRING devnameW;
2219 if(devname) RtlCreateUnicodeStringFromAsciiz(&devnameW, devname);
2220 else devnameW.Buffer = NULL;
2222 retW = QueryDosDeviceW(devnameW.Buffer, targetW, bufsize);
2224 ret = WideCharToMultiByte(CP_ACP, 0, targetW, retW, target,
2225 bufsize, NULL, NULL);
2227 RtlFreeUnicodeString(&devnameW);
2228 if (targetW) HeapFree(GetProcessHeap(),0,targetW);
2229 return ret;
2233 /***********************************************************************
2234 * QueryDosDeviceW (KERNEL32.@)
2236 * returns array of strings terminated by \0, terminated by \0
2238 * FIXME
2239 * - Win9x returns for all calls ERROR_INVALID_PARAMETER
2240 * - the returned devices for devname == NULL is far from complete
2241 * - its not checked that the returned device exist
2243 DWORD WINAPI QueryDosDeviceW(LPCWSTR devname,LPWSTR target,DWORD bufsize)
2245 const WCHAR *pDev, *pName, *pNum = NULL;
2246 int numsiz=0;
2247 DWORD ret;
2249 TRACE("(%s,...)\n", debugstr_w(devname));
2250 if (!devname) {
2251 /* return known MSDOS devices */
2252 DWORD ret = 0;
2253 int i;
2254 static const WCHAR devices[][5] = {{'A','U','X',0},
2255 {'C','O','M','1',0},
2256 {'C','O','M','2',0},
2257 {'L','P','T','1',0},
2258 {'N','U','L',0,}};
2259 for(i=0; (i< (sizeof(devices)/sizeof(devices[0]))); i++) {
2260 DWORD len = strlenW(devices[i]);
2261 if(target && (bufsize >= ret + len + 2)) {
2262 strcpyW(target+ret, devices[i]);
2263 ret += len + 1;
2264 } else {
2265 /* in this case WinXP returns 0 */
2266 FIXME("function return is wrong for WinXP!\n");
2267 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2268 break;
2271 /* append drives here */
2272 if(target && bufsize > 0) target[ret++] = 0;
2273 FIXME("Returned list is not complete\n");
2274 return ret;
2276 /* In theory all that are possible and have been defined.
2277 * Now just those below, since mirc uses it to check for special files.
2279 * (It is more complex, and supports netmounted stuff, and \\.\ stuff,
2280 * but currently we just ignore that.)
2282 if (!strcmpiW(devname, auxW)) {
2283 pDev = dosW;
2284 pName = comW;
2285 numsiz = 1;
2286 pNum = oneW;
2287 } else if (!strcmpiW(devname, nulW)) {
2288 pDev = devW;
2289 pName = nullW;
2290 } else if (!strncmpiW(devname, comW, strlenW(comW))) {
2291 pDev = devW;
2292 pName = serW;
2293 pNum = devname + strlenW(comW);
2294 for(numsiz=0; isdigitW(*(pNum+numsiz)); numsiz++);
2295 if(*(pNum + numsiz)) {
2296 SetLastError(ERROR_FILE_NOT_FOUND);
2297 return 0;
2299 } else if (!strncmpiW(devname, lptW, strlenW(lptW))) {
2300 pDev = devW;
2301 pName = parW;
2302 pNum = devname + strlenW(lptW);
2303 for(numsiz=0; isdigitW(*(pNum+numsiz)); numsiz++);
2304 if(*(pNum + numsiz)) {
2305 SetLastError(ERROR_FILE_NOT_FOUND);
2306 return 0;
2308 } else {
2309 /* This might be a DOS device we do not handle yet ... */
2310 FIXME("(%s) not detected as DOS device!\n",debugstr_w(devname));
2312 /* Win9x set the error ERROR_INVALID_PARAMETER */
2313 SetLastError(ERROR_FILE_NOT_FOUND);
2314 return 0;
2316 FIXME("device %s may not exist on this computer\n", debugstr_w(devname));
2318 ret = strlenW(pDev) + strlenW(pName) + numsiz + 2;
2319 if (ret > bufsize) ret = 0;
2320 if (target && ret) {
2321 strcpyW(target,pDev);
2322 strcatW(target,pName);
2323 if (pNum) strcatW(target,pNum);
2324 target[ret-1] = 0;
2326 return ret;