I8/UI8 are accepted in arrays in recent native dlls.
[wine/hacks.git] / files / dos_fs.c
blobed3e08bf7ddfb7caac13b94ad648ea4b4d28e327
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"
23 #include "wine/port.h"
25 #include <sys/types.h>
26 #include <ctype.h>
27 #include <dirent.h>
28 #include <errno.h>
29 #ifdef HAVE_SYS_ERRNO_H
30 #include <sys/errno.h>
31 #endif
32 #include <fcntl.h>
33 #include <stdarg.h>
34 #include <string.h>
35 #include <stdlib.h>
36 #include <sys/stat.h>
37 #ifdef HAVE_SYS_IOCTL_H
38 #include <sys/ioctl.h>
39 #endif
40 #include <time.h>
41 #ifdef HAVE_UNISTD_H
42 # include <unistd.h>
43 #endif
45 #define NONAMELESSUNION
46 #define NONAMELESSSTRUCT
47 #include "ntstatus.h"
48 #include "windef.h"
49 #include "winbase.h"
50 #include "winerror.h"
51 #include "wingdi.h"
53 #include "wine/unicode.h"
54 #include "wine/winbase16.h"
55 #include "drive.h"
56 #include "file.h"
57 #include "winternl.h"
58 #include "wine/server.h"
59 #include "wine/exception.h"
60 #include "excpt.h"
62 #include "smb.h"
64 #include "wine/debug.h"
66 WINE_DEFAULT_DEBUG_CHANNEL(dosfs);
67 WINE_DECLARE_DEBUG_CHANNEL(file);
69 /* Define the VFAT ioctl to get both short and long file names */
70 /* FIXME: is it possible to get this to work on other systems? */
71 #ifdef linux
72 /* We want the real kernel dirent structure, not the libc one */
73 typedef struct
75 long d_ino;
76 long d_off;
77 unsigned short d_reclen;
78 char d_name[256];
79 } KERNEL_DIRENT;
81 #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, KERNEL_DIRENT [2] )
83 /* To avoid blocking on non-directories in DOSFS_OpenDir_VFAT*/
84 #ifndef O_DIRECTORY
85 # define O_DIRECTORY 0200000 /* must be directory */
86 #endif
88 #else /* linux */
89 #undef VFAT_IOCTL_READDIR_BOTH /* just in case... */
90 #endif /* linux */
92 #define IS_OPTION_TRUE(ch) ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1')
94 /* Chars we don't want to see in DOS file names */
95 #define INVALID_DOS_CHARS "*?<>|\"+=,;[] \345"
97 static const DOS_DEVICE DOSFS_Devices[] =
98 /* name, device flags (see Int 21/AX=0x4400) */
100 { {'C','O','N',0}, 0xc0d3 },
101 { {'P','R','N',0}, 0xa0c0 },
102 { {'N','U','L',0}, 0x80c4 },
103 { {'A','U','X',0}, 0x80c0 },
104 { {'L','P','T','1',0}, 0xa0c0 },
105 { {'L','P','T','2',0}, 0xa0c0 },
106 { {'L','P','T','3',0}, 0xa0c0 },
107 { {'L','P','T','4',0}, 0xc0d3 },
108 { {'C','O','M','1',0}, 0x80c0 },
109 { {'C','O','M','2',0}, 0x80c0 },
110 { {'C','O','M','3',0}, 0x80c0 },
111 { {'C','O','M','4',0}, 0x80c0 },
112 { {'S','C','S','I','M','G','R','$',0}, 0xc0c0 },
113 { {'H','P','S','C','A','N',0}, 0xc0c0 },
114 { {'E','M','M','X','X','X','X','0',0}, 0x0000 }
117 static const WCHAR devW[] = {'\\','D','e','v','i','c','e','\\',0};
118 static const WCHAR dosW[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\',0};
120 static const WCHAR auxW[] = {'A','U','X',0};
121 static const WCHAR comW[] = {'C','O','M',0};
122 static const WCHAR lptW[] = {'L','P','T',0};
123 static const WCHAR nulW[] = {'N','U','L',0};
125 static const WCHAR nullW[] = {'N','u','l','l',0};
126 static const WCHAR parW[] = {'P','a','r','a','l','l','e','l',0};
127 static const WCHAR serW[] = {'S','e','r','i','a','l',0};
128 static const WCHAR oneW[] = {'1',0};
131 * Directory info for DOSFS_ReadDir
132 * contains the names of *all* the files in the directory
134 typedef struct
136 int used;
137 int size;
138 WCHAR names[1];
139 } DOS_DIR;
141 /* Info structure for FindFirstFile handle */
142 typedef struct
144 char *path; /* unix path */
145 LPWSTR long_mask;
146 int drive;
147 int cur_pos;
148 CRITICAL_SECTION cs;
149 union
151 DOS_DIR *dos_dir;
152 SMB_DIR *smb_dir;
153 } u;
154 } FIND_FIRST_INFO;
157 static WINE_EXCEPTION_FILTER(page_fault)
159 if (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION)
160 return EXCEPTION_EXECUTE_HANDLER;
161 return EXCEPTION_CONTINUE_SEARCH;
165 /***********************************************************************
166 * DOSFS_ValidDOSName
168 * Return 1 if Unix file 'name' is also a valid MS-DOS name
169 * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
170 * File name can be terminated by '\0', '\\' or '/'.
172 static int DOSFS_ValidDOSName( LPCWSTR name, int ignore_case )
174 static const char invalid_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS;
175 const WCHAR *p = name;
176 const char *invalid = ignore_case ? (invalid_chars + 26) : invalid_chars;
177 int len = 0;
179 if (*p == '.')
181 /* Check for "." and ".." */
182 p++;
183 if (*p == '.') p++;
184 /* All other names beginning with '.' are invalid */
185 return (IS_END_OF_NAME(*p));
187 while (!IS_END_OF_NAME(*p))
189 if (*p < 256 && strchr( invalid, (char)*p )) return 0; /* Invalid char */
190 if (*p == '.') break; /* Start of the extension */
191 if (++len > 8) return 0; /* Name too long */
192 p++;
194 if (*p != '.') return 1; /* End of name */
195 p++;
196 if (IS_END_OF_NAME(*p)) return 0; /* Empty extension not allowed */
197 len = 0;
198 while (!IS_END_OF_NAME(*p))
200 if (*p < 256 && strchr( invalid, (char)*p )) return 0; /* Invalid char */
201 if (*p == '.') return 0; /* Second extension not allowed */
202 if (++len > 3) return 0; /* Extension too long */
203 p++;
205 return 1;
209 /***********************************************************************
210 * DOSFS_ToDosFCBFormat
212 * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
213 * expanding wild cards and converting to upper-case in the process.
214 * File name can be terminated by '\0', '\\' or '/'.
215 * Return FALSE if the name is not a valid DOS name.
216 * 'buffer' must be at least 12 characters long.
218 BOOL DOSFS_ToDosFCBFormat( LPCWSTR name, LPWSTR buffer )
220 static const char invalid_chars[] = INVALID_DOS_CHARS;
221 LPCWSTR p = name;
222 int i;
224 /* Check for "." and ".." */
225 if (*p == '.')
227 p++;
228 buffer[0] = '.';
229 for(i = 1; i < 11; i++) buffer[i] = ' ';
230 buffer[11] = 0;
231 if (*p == '.')
233 buffer[1] = '.';
234 p++;
236 return (!*p || (*p == '/') || (*p == '\\'));
239 for (i = 0; i < 8; i++)
241 switch(*p)
243 case '\0':
244 case '\\':
245 case '/':
246 case '.':
247 buffer[i] = ' ';
248 break;
249 case '?':
250 p++;
251 /* fall through */
252 case '*':
253 buffer[i] = '?';
254 break;
255 default:
256 if (*p < 256 && strchr( invalid_chars, (char)*p )) return FALSE;
257 buffer[i] = toupperW(*p);
258 p++;
259 break;
263 if (*p == '*')
265 /* Skip all chars after wildcard up to first dot */
266 while (*p && (*p != '/') && (*p != '\\') && (*p != '.')) p++;
268 else
270 /* Check if name too long */
271 if (*p && (*p != '/') && (*p != '\\') && (*p != '.')) return FALSE;
273 if (*p == '.') p++; /* Skip dot */
275 for (i = 8; i < 11; i++)
277 switch(*p)
279 case '\0':
280 case '\\':
281 case '/':
282 buffer[i] = ' ';
283 break;
284 case '.':
285 return FALSE; /* Second extension not allowed */
286 case '?':
287 p++;
288 /* fall through */
289 case '*':
290 buffer[i] = '?';
291 break;
292 default:
293 if (*p < 256 && strchr( invalid_chars, (char)*p )) return FALSE;
294 buffer[i] = toupperW(*p);
295 p++;
296 break;
299 buffer[11] = '\0';
301 /* at most 3 character of the extension are processed
302 * is something behind this ?
304 while (*p == '*' || *p == ' ') p++; /* skip wildcards and spaces */
305 return IS_END_OF_NAME(*p);
309 /***********************************************************************
310 * DOSFS_ToDosDTAFormat
312 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
313 * converting to upper-case in the process.
314 * File name can be terminated by '\0', '\\' or '/'.
315 * 'buffer' must be at least 13 characters long.
317 static void DOSFS_ToDosDTAFormat( LPCWSTR name, LPWSTR buffer )
319 LPWSTR p;
321 memcpy( buffer, name, 8 * sizeof(WCHAR) );
322 p = buffer + 8;
323 while ((p > buffer) && (p[-1] == ' ')) p--;
324 *p++ = '.';
325 memcpy( p, name + 8, 3 * sizeof(WCHAR) );
326 p += 3;
327 while (p[-1] == ' ') p--;
328 if (p[-1] == '.') p--;
329 *p = '\0';
333 /***********************************************************************
334 * DOSFS_MatchLong
336 * Check a long file name against a mask.
338 * Tests (done in W95 DOS shell - case insensitive):
339 * *.txt test1.test.txt *
340 * *st1* test1.txt *
341 * *.t??????.t* test1.ta.tornado.txt *
342 * *tornado* test1.ta.tornado.txt *
343 * t*t test1.ta.tornado.txt *
344 * ?est* test1.txt *
345 * ?est??? test1.txt -
346 * *test1.txt* test1.txt *
347 * h?l?o*t.dat hellothisisatest.dat *
349 static int DOSFS_MatchLong( LPCWSTR mask, LPCWSTR name, int case_sensitive )
351 LPCWSTR lastjoker = NULL;
352 LPCWSTR next_to_retry = NULL;
353 static const WCHAR asterisk_dot_asterisk[] = {'*','.','*',0};
355 TRACE("(%s, %s, %x)\n", debugstr_w(mask), debugstr_w(name), case_sensitive);
357 if (!strcmpW( mask, asterisk_dot_asterisk )) return 1;
358 while (*name && *mask)
360 if (*mask == '*')
362 mask++;
363 while (*mask == '*') mask++; /* Skip consecutive '*' */
364 lastjoker = mask;
365 if (!*mask) return 1; /* end of mask is all '*', so match */
367 /* skip to the next match after the joker(s) */
368 if (case_sensitive) while (*name && (*name != *mask)) name++;
369 else while (*name && (toupperW(*name) != toupperW(*mask))) name++;
371 if (!*name) break;
372 next_to_retry = name;
374 else if (*mask != '?')
376 int mismatch = 0;
377 if (case_sensitive)
379 if (*mask != *name) mismatch = 1;
381 else
383 if (toupperW(*mask) != toupperW(*name)) mismatch = 1;
385 if (!mismatch)
387 mask++;
388 name++;
389 if (*mask == '\0')
391 if (*name == '\0')
392 return 1;
393 if (lastjoker)
394 mask = lastjoker;
397 else /* mismatch ! */
399 if (lastjoker) /* we had an '*', so we can try unlimitedly */
401 mask = lastjoker;
403 /* this scan sequence was a mismatch, so restart
404 * 1 char after the first char we checked last time */
405 next_to_retry++;
406 name = next_to_retry;
408 else
409 return 0; /* bad luck */
412 else /* '?' */
414 mask++;
415 name++;
418 while ((*mask == '.') || (*mask == '*'))
419 mask++; /* Ignore trailing '.' or '*' in mask */
420 return (!*name && !*mask);
424 /***********************************************************************
425 * DOSFS_AddDirEntry
427 * Used to construct an array of filenames in DOSFS_OpenDir
429 static BOOL DOSFS_AddDirEntry(DOS_DIR **dir, LPCWSTR name, LPCWSTR dosname)
431 int extra1 = strlenW(name) + 1;
432 int extra2 = strlenW(dosname) + 1;
434 /* if we need more, at minimum double the size */
435 if( (extra1 + extra2 + (*dir)->used) > (*dir)->size)
437 int more = (*dir)->size;
438 DOS_DIR *t;
440 if(more<(extra1+extra2))
441 more = extra1+extra2;
443 t = HeapReAlloc(GetProcessHeap(), 0, *dir, sizeof(**dir) +
444 ((*dir)->size + more)*sizeof(WCHAR) );
445 if(!t)
447 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
448 ERR("Out of memory caching directory structure %d %d %d\n",
449 (*dir)->size, more, (*dir)->used);
450 return FALSE;
452 (*dir) = t;
453 (*dir)->size += more;
456 /* at this point, the dir structure is big enough to hold these names */
457 strcpyW(&(*dir)->names[(*dir)->used], name);
458 (*dir)->used += extra1;
459 strcpyW(&(*dir)->names[(*dir)->used], dosname);
460 (*dir)->used += extra2;
462 return TRUE;
466 /***********************************************************************
467 * DOSFS_OpenDir_VFAT
469 static BOOL DOSFS_OpenDir_VFAT(DOS_DIR **dir, const char *unix_path)
471 #ifdef VFAT_IOCTL_READDIR_BOTH
472 KERNEL_DIRENT de[2];
473 int fd = open( unix_path, O_RDONLY|O_DIRECTORY );
474 BOOL r = TRUE;
476 /* Check if the VFAT ioctl is supported on this directory */
478 if ( fd<0 )
479 return FALSE;
481 while (1)
483 WCHAR long_name[MAX_PATH];
484 WCHAR short_name[12];
486 r = (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de ) != -1);
487 if(!r)
488 break;
489 if (!de[0].d_reclen)
490 break;
491 MultiByteToWideChar(CP_UNIXCP, 0, de[0].d_name, -1, long_name, MAX_PATH);
492 if (!DOSFS_ToDosFCBFormat( long_name, short_name ))
493 short_name[0] = '\0';
494 if (de[1].d_name[0])
495 MultiByteToWideChar(CP_UNIXCP, 0, de[1].d_name, -1, long_name, MAX_PATH);
496 else
497 MultiByteToWideChar(CP_UNIXCP, 0, de[0].d_name, -1, long_name, MAX_PATH);
498 r = DOSFS_AddDirEntry(dir, long_name, short_name );
499 if(!r)
500 break;
502 if(r)
504 static const WCHAR empty_strW[] = { 0 };
505 DOSFS_AddDirEntry(dir, empty_strW, empty_strW);
507 close(fd);
508 return r;
509 #else
510 return FALSE;
511 #endif /* VFAT_IOCTL_READDIR_BOTH */
515 /***********************************************************************
516 * DOSFS_OpenDir_Normal
518 * Now use the standard opendir/readdir interface
520 static BOOL DOSFS_OpenDir_Normal( DOS_DIR **dir, const char *unix_path )
522 DIR *unixdir = opendir( unix_path );
523 BOOL r = TRUE;
524 static const WCHAR empty_strW[] = { 0 };
526 if(!unixdir)
527 return FALSE;
528 while(1)
530 WCHAR long_name[MAX_PATH];
531 struct dirent *de = readdir(unixdir);
533 if(!de)
534 break;
535 MultiByteToWideChar(CP_UNIXCP, 0, de->d_name, -1, long_name, MAX_PATH);
536 r = DOSFS_AddDirEntry(dir, long_name, empty_strW);
537 if(!r)
538 break;
540 if(r)
541 DOSFS_AddDirEntry(dir, empty_strW, empty_strW);
542 closedir(unixdir);
543 return r;
546 /***********************************************************************
547 * DOSFS_OpenDir
549 static DOS_DIR *DOSFS_OpenDir( const char *unix_path )
551 const int init_size = 0x100;
552 DOS_DIR *dir = HeapAlloc( GetProcessHeap(), 0, sizeof(*dir) + init_size*sizeof (WCHAR));
553 BOOL r;
555 TRACE("%s\n",debugstr_a(unix_path));
557 if (!dir)
559 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
560 return NULL;
562 dir->used = 0;
563 dir->size = init_size;
565 /* Treat empty path as root directory. This simplifies path split into
566 directory and mask in several other places */
567 if (!*unix_path) unix_path = "/";
569 r = DOSFS_OpenDir_VFAT( &dir, unix_path);
571 if(!r)
572 r = DOSFS_OpenDir_Normal( &dir, unix_path);
574 if(!r)
576 HeapFree(GetProcessHeap(), 0, dir);
577 return NULL;
579 dir->used = 0;
581 return dir;
585 /***********************************************************************
586 * DOSFS_CloseDir
588 static void DOSFS_CloseDir( DOS_DIR *dir )
590 HeapFree( GetProcessHeap(), 0, dir );
594 /***********************************************************************
595 * DOSFS_ReadDir
597 static BOOL DOSFS_ReadDir( DOS_DIR *dir, LPCWSTR *long_name,
598 LPCWSTR *short_name )
600 LPCWSTR sn, ln;
602 if (!dir)
603 return FALSE;
605 /* the long pathname is first */
606 ln = &dir->names[dir->used];
607 if(ln[0])
608 *long_name = ln;
609 else
610 return FALSE;
611 dir->used += (strlenW(ln) + 1);
613 /* followed by the short path name */
614 sn = &dir->names[dir->used];
615 if(sn[0])
616 *short_name = sn;
617 else
618 *short_name = NULL;
619 dir->used += (strlenW(sn) + 1);
621 return TRUE;
625 /***********************************************************************
626 * DOSFS_Hash
628 * Transform a Unix file name into a hashed DOS name. If the name is a valid
629 * DOS name, it is converted to upper-case; otherwise it is replaced by a
630 * hashed version that fits in 8.3 format.
631 * File name can be terminated by '\0', '\\' or '/'.
632 * 'buffer' must be at least 13 characters long.
634 static void DOSFS_Hash( LPCWSTR name, LPWSTR buffer, BOOL dir_format,
635 BOOL ignore_case )
637 static const char invalid_chars[] = INVALID_DOS_CHARS "~.";
638 static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
640 LPCWSTR p, ext;
641 LPWSTR dst;
642 unsigned short hash;
643 int i;
645 if (dir_format)
647 for(i = 0; i < 11; i++) buffer[i] = ' ';
648 buffer[11] = 0;
651 if (DOSFS_ValidDOSName( name, ignore_case ))
653 /* Check for '.' and '..' */
654 if (*name == '.')
656 buffer[0] = '.';
657 if (!dir_format) buffer[1] = buffer[2] = '\0';
658 if (name[1] == '.') buffer[1] = '.';
659 return;
662 /* Simply copy the name, converting to uppercase */
664 for (dst = buffer; !IS_END_OF_NAME(*name) && (*name != '.'); name++)
665 *dst++ = toupperW(*name);
666 if (*name == '.')
668 if (dir_format) dst = buffer + 8;
669 else *dst++ = '.';
670 for (name++; !IS_END_OF_NAME(*name); name++)
671 *dst++ = toupperW(*name);
673 if (!dir_format) *dst = '\0';
674 return;
677 /* Compute the hash code of the file name */
678 /* If you know something about hash functions, feel free to */
679 /* insert a better algorithm here... */
680 if (ignore_case)
682 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
683 hash = (hash<<3) ^ (hash>>5) ^ tolowerW(*p) ^ (tolowerW(p[1]) << 8);
684 hash = (hash<<3) ^ (hash>>5) ^ tolowerW(*p); /* Last character */
686 else
688 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
689 hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
690 hash = (hash << 3) ^ (hash >> 5) ^ *p; /* Last character */
693 /* Find last dot for start of the extension */
694 for (p = name+1, ext = NULL; !IS_END_OF_NAME(*p); p++)
695 if (*p == '.') ext = p;
696 if (ext && IS_END_OF_NAME(ext[1]))
697 ext = NULL; /* Empty extension ignored */
699 /* Copy first 4 chars, replacing invalid chars with '_' */
700 for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
702 if (IS_END_OF_NAME(*p) || (p == ext)) break;
703 *dst++ = (*p < 256 && strchr( invalid_chars, (char)*p )) ? '_' : toupperW(*p);
705 /* Pad to 5 chars with '~' */
706 while (i-- >= 0) *dst++ = '~';
708 /* Insert hash code converted to 3 ASCII chars */
709 *dst++ = hash_chars[(hash >> 10) & 0x1f];
710 *dst++ = hash_chars[(hash >> 5) & 0x1f];
711 *dst++ = hash_chars[hash & 0x1f];
713 /* Copy the first 3 chars of the extension (if any) */
714 if (ext)
716 if (!dir_format) *dst++ = '.';
717 for (i = 3, ext++; (i > 0) && !IS_END_OF_NAME(*ext); i--, ext++)
718 *dst++ = (*ext < 256 && strchr( invalid_chars, (char)*ext )) ? '_' : toupperW(*ext);
720 if (!dir_format) *dst = '\0';
724 /***********************************************************************
725 * DOSFS_FindUnixName
727 * Find the Unix file name in a given directory that corresponds to
728 * a file name (either in Unix or DOS format).
729 * File name can be terminated by '\0', '\\' or '/'.
730 * Return TRUE if OK, FALSE if no file name matches.
732 * 'long_buf' must be at least 'long_len' characters long. If the long name
733 * turns out to be larger than that, the function returns FALSE.
734 * 'short_buf' must be at least 13 characters long.
736 BOOL DOSFS_FindUnixName( const DOS_FULL_NAME *path, LPCWSTR name, char *long_buf,
737 INT long_len, LPWSTR short_buf, BOOL ignore_case)
739 DOS_DIR *dir;
740 LPCWSTR long_name, short_name;
741 WCHAR dos_name[12], tmp_buf[13];
742 BOOL ret;
744 LPCWSTR p = strchrW( name, '/' );
745 int len = p ? (int)(p - name) : strlenW(name);
746 if ((p = strchrW( name, '\\' ))) len = min( (int)(p - name), len );
747 /* Ignore trailing dots and spaces */
748 while (len > 1 && (name[len-1] == '.' || name[len-1] == ' ')) len--;
749 if (long_len < len + 1) return FALSE;
751 TRACE("%s,%s\n", path->long_name, debugstr_w(name) );
753 if (!DOSFS_ToDosFCBFormat( name, dos_name )) dos_name[0] = '\0';
755 if (!(dir = DOSFS_OpenDir( path->long_name )))
757 WARN("(%s,%s): can't open dir: %s\n",
758 path->long_name, debugstr_w(name), strerror(errno) );
759 return FALSE;
762 while ((ret = DOSFS_ReadDir( dir, &long_name, &short_name )))
764 /* Check against Unix name */
765 if (len == strlenW(long_name))
767 if (!ignore_case)
769 if (!strncmpW( long_name, name, len )) break;
771 else
773 if (!strncmpiW( long_name, name, len )) break;
776 if (dos_name[0])
778 /* Check against hashed DOS name */
779 if (!short_name)
781 DOSFS_Hash( long_name, tmp_buf, TRUE, ignore_case );
782 short_name = tmp_buf;
784 if (!strcmpW( dos_name, short_name )) break;
787 if (ret)
789 if (long_buf) WideCharToMultiByte(CP_UNIXCP, 0, long_name, -1, long_buf, long_len, NULL, NULL);
790 if (short_buf)
792 if (short_name)
793 DOSFS_ToDosDTAFormat( short_name, short_buf );
794 else
795 DOSFS_Hash( long_name, short_buf, FALSE, ignore_case );
797 TRACE("(%s,%s) -> %s (%s)\n", path->long_name, debugstr_w(name),
798 debugstr_w(long_name), short_buf ? debugstr_w(short_buf) : "***");
800 else
801 WARN("%s not found in '%s'\n", debugstr_w(name), path->long_name);
802 DOSFS_CloseDir( dir );
803 return ret;
807 /***********************************************************************
808 * DOSFS_GetDevice
810 * Check if a DOS file name represents a DOS device and return the device.
812 const DOS_DEVICE *DOSFS_GetDevice( LPCWSTR name )
814 unsigned int i;
815 const WCHAR *p;
817 if (!name) return NULL; /* if wine_server_handle_to_fd was used */
818 if (name[0] && (name[1] == ':')) name += 2;
819 if ((p = strrchrW( name, '/' ))) name = p + 1;
820 if ((p = strrchrW( name, '\\' ))) name = p + 1;
821 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
823 const WCHAR *dev = DOSFS_Devices[i].name;
824 if (!strncmpiW( dev, name, strlenW(dev) ))
826 p = name + strlenW( dev );
827 if (!*p || (*p == '.') || (*p == ':')) return &DOSFS_Devices[i];
830 return NULL;
834 /***********************************************************************
835 * DOSFS_GetDeviceByHandle
837 const DOS_DEVICE *DOSFS_GetDeviceByHandle( HANDLE hFile )
839 const DOS_DEVICE *ret = NULL;
840 SERVER_START_REQ( get_device_id )
842 req->handle = hFile;
843 if (!wine_server_call( req ))
845 if ((reply->id >= 0) &&
846 (reply->id < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0])))
847 ret = &DOSFS_Devices[reply->id];
850 SERVER_END_REQ;
851 return ret;
855 /**************************************************************************
856 * DOSFS_CreateCommPort
858 static HANDLE DOSFS_CreateCommPort(LPCWSTR name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa)
860 HANDLE ret;
861 HKEY hkey;
862 DWORD dummy;
863 OBJECT_ATTRIBUTES attr;
864 UNICODE_STRING nameW;
865 WCHAR *devnameW;
866 char tmp[128];
867 char devname[40];
869 static const WCHAR serialportsW[] = {'M','a','c','h','i','n','e','\\',
870 'S','o','f','t','w','a','r','e','\\',
871 'W','i','n','e','\\','W','i','n','e','\\',
872 'C','o','n','f','i','g','\\',
873 'S','e','r','i','a','l','P','o','r','t','s',0};
875 TRACE_(file)("%s %lx %lx\n", debugstr_w(name), access, attributes);
877 attr.Length = sizeof(attr);
878 attr.RootDirectory = 0;
879 attr.ObjectName = &nameW;
880 attr.Attributes = 0;
881 attr.SecurityDescriptor = NULL;
882 attr.SecurityQualityOfService = NULL;
883 RtlInitUnicodeString( &nameW, serialportsW );
885 if (NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr )) return 0;
887 RtlInitUnicodeString( &nameW, name );
888 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
889 devnameW = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
890 else
891 devnameW = NULL;
893 NtClose( hkey );
895 if (!devnameW) return 0;
896 WideCharToMultiByte(CP_ACP, 0, devnameW, -1, devname, sizeof(devname), NULL, NULL);
898 TRACE("opening %s as %s\n", devname, debugstr_w(name));
900 SERVER_START_REQ( create_serial )
902 req->access = access;
903 req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
904 req->attributes = attributes;
905 req->sharing = FILE_SHARE_READ|FILE_SHARE_WRITE;
906 wine_server_add_data( req, devname, strlen(devname) );
907 SetLastError(0);
908 wine_server_call_err( req );
909 ret = reply->handle;
911 SERVER_END_REQ;
913 if(!ret)
914 ERR("Couldn't open device '%s' ! (check permissions)\n",devname);
915 else
916 TRACE("return %p\n", ret );
917 return ret;
920 /***********************************************************************
921 * DOSFS_OpenDevice
923 * Open a DOS device. This might not map 1:1 into the UNIX device concept.
924 * Returns 0 on failure.
926 HANDLE DOSFS_OpenDevice( LPCWSTR name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa )
928 unsigned int i;
929 const WCHAR *p;
930 HANDLE handle;
932 if (name[0] && (name[1] == ':')) name += 2;
933 if ((p = strrchrW( name, '/' ))) name = p + 1;
934 if ((p = strrchrW( name, '\\' ))) name = p + 1;
935 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
937 const WCHAR *dev = DOSFS_Devices[i].name;
938 if (!strncmpiW( dev, name, strlenW(dev) ))
940 p = name + strlenW( dev );
941 if (!*p || (*p == '.') || (*p == ':')) {
942 static const WCHAR nulW[] = {'N','U','L',0};
943 static const WCHAR conW[] = {'C','O','N',0};
944 static const WCHAR scsimgrW[] = {'S','C','S','I','M','G','R','$',0};
945 static const WCHAR hpscanW[] = {'H','P','S','C','A','N',0};
946 static const WCHAR emmxxxx0W[] = {'E','M','M','X','X','X','X','0',0};
947 /* got it */
948 if (!strcmpiW(DOSFS_Devices[i].name, nulW))
949 return FILE_CreateFile( "/dev/null", access,
950 FILE_SHARE_READ|FILE_SHARE_WRITE, sa,
951 OPEN_EXISTING, 0, 0, TRUE, DRIVE_UNKNOWN );
952 if (!strcmpiW(DOSFS_Devices[i].name, conW)) {
953 HANDLE to_dup;
954 switch (access & (GENERIC_READ|GENERIC_WRITE)) {
955 case GENERIC_READ:
956 to_dup = GetStdHandle( STD_INPUT_HANDLE );
957 break;
958 case GENERIC_WRITE:
959 to_dup = GetStdHandle( STD_OUTPUT_HANDLE );
960 break;
961 default:
962 FIXME("can't open CON read/write\n");
963 return 0;
965 if (!DuplicateHandle( GetCurrentProcess(), to_dup, GetCurrentProcess(),
966 &handle, 0,
967 sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle,
968 DUPLICATE_SAME_ACCESS ))
969 handle = 0;
970 return handle;
972 if (!strcmpiW(DOSFS_Devices[i].name, scsimgrW) ||
973 !strcmpiW(DOSFS_Devices[i].name, hpscanW) ||
974 !strcmpiW(DOSFS_Devices[i].name, emmxxxx0W))
976 return FILE_CreateDevice( i, access, sa );
979 if( (handle=DOSFS_CreateCommPort(DOSFS_Devices[i].name,access,attributes,sa)) )
980 return handle;
981 FIXME("device open %s not supported (yet)\n", debugstr_w(DOSFS_Devices[i].name));
982 return 0;
986 return 0;
990 /***********************************************************************
991 * DOSFS_GetPathDrive
993 * Get the drive specified by a given path name (DOS or Unix format).
995 static int DOSFS_GetPathDrive( LPCWSTR *name )
997 int drive;
998 LPCWSTR p = *name;
1000 if (*p && (p[1] == ':'))
1002 drive = toupperW(*p) - 'A';
1003 *name += 2;
1005 else if (*p == '/') /* Absolute Unix path? */
1007 if ((drive = DRIVE_FindDriveRootW( name )) == -1)
1009 MESSAGE("Warning: %s not accessible from a configured DOS drive\n", debugstr_w(*name) );
1010 /* Assume it really was a DOS name */
1011 drive = DRIVE_GetCurrentDrive();
1014 else drive = DRIVE_GetCurrentDrive();
1016 if (!DRIVE_IsValid(drive))
1018 SetLastError( ERROR_INVALID_DRIVE );
1019 return -1;
1021 return drive;
1025 /***********************************************************************
1026 * DOSFS_GetFullName
1028 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
1029 * Unix name / short DOS name pair.
1030 * Return FALSE if one of the path components does not exist. The last path
1031 * component is only checked if 'check_last' is non-zero.
1032 * The buffers pointed to by 'long_buf' and 'short_buf' must be
1033 * at least MAX_PATHNAME_LEN long.
1035 BOOL DOSFS_GetFullName( LPCWSTR name, BOOL check_last, DOS_FULL_NAME *full )
1037 BOOL found;
1038 UINT flags;
1039 char *p_l, *root;
1040 LPWSTR p_s;
1041 static const WCHAR driveA_rootW[] = {'A',':','\\',0};
1042 static const WCHAR dos_rootW[] = {'\\',0};
1044 TRACE("%s (last=%d)\n", debugstr_w(name), check_last );
1046 if ((!*name) || (*name=='\n'))
1047 { /* error code for Win98 */
1048 SetLastError(ERROR_BAD_PATHNAME);
1049 return FALSE;
1052 if ((full->drive = DOSFS_GetPathDrive( &name )) == -1) return FALSE;
1053 flags = DRIVE_GetFlags( full->drive );
1055 lstrcpynA( full->long_name, DRIVE_GetRoot( full->drive ),
1056 sizeof(full->long_name) );
1057 if (full->long_name[1]) root = full->long_name + strlen(full->long_name);
1058 else root = full->long_name; /* root directory */
1060 strcpyW( full->short_name, driveA_rootW );
1061 full->short_name[0] += full->drive;
1063 if ((*name == '\\') || (*name == '/')) /* Absolute path */
1065 while ((*name == '\\') || (*name == '/')) name++;
1067 else /* Relative path */
1069 lstrcpynA( root + 1, DRIVE_GetUnixCwd( full->drive ),
1070 sizeof(full->long_name) - (root - full->long_name) - 1 );
1071 if (root[1]) *root = '/';
1072 lstrcpynW( full->short_name + 3, DRIVE_GetDosCwd( full->drive ),
1073 sizeof(full->short_name)/sizeof(full->short_name[0]) - 3 );
1076 p_l = full->long_name[1] ? full->long_name + strlen(full->long_name)
1077 : full->long_name;
1078 p_s = full->short_name[3] ? full->short_name + strlenW(full->short_name)
1079 : full->short_name + 2;
1080 found = TRUE;
1082 while (*name && found)
1084 /* Check for '.' and '..' */
1086 if (*name == '.')
1088 if (IS_END_OF_NAME(name[1]))
1090 name++;
1091 while ((*name == '\\') || (*name == '/')) name++;
1092 continue;
1094 else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
1096 name += 2;
1097 while ((*name == '\\') || (*name == '/')) name++;
1098 while ((p_l > root) && (*p_l != '/')) p_l--;
1099 while ((p_s > full->short_name + 2) && (*p_s != '\\')) p_s--;
1100 *p_l = *p_s = '\0'; /* Remove trailing separator */
1101 continue;
1105 /* Make sure buffers are large enough */
1107 if ((p_s >= full->short_name + sizeof(full->short_name)/sizeof(full->short_name[0]) - 14) ||
1108 (p_l >= full->long_name + sizeof(full->long_name) - 1))
1110 SetLastError( ERROR_PATH_NOT_FOUND );
1111 return FALSE;
1114 /* Get the long and short name matching the file name */
1116 if ((found = DOSFS_FindUnixName( full, name, p_l + 1,
1117 sizeof(full->long_name) - (p_l - full->long_name) - 1,
1118 p_s + 1, !(flags & DRIVE_CASE_SENSITIVE) )))
1120 *p_l++ = '/';
1121 p_l += strlen(p_l);
1122 *p_s++ = '\\';
1123 p_s += strlenW(p_s);
1124 while (!IS_END_OF_NAME(*name)) name++;
1126 else if (!check_last)
1128 *p_l++ = '/';
1129 *p_s++ = '\\';
1130 while (!IS_END_OF_NAME(*name) &&
1131 (p_s < full->short_name + sizeof(full->short_name)/sizeof(full->short_name[0]) - 1) &&
1132 (p_l < full->long_name + sizeof(full->long_name) - 1))
1134 WCHAR wch;
1135 *p_s++ = tolowerW(*name);
1136 /* If the drive is case-sensitive we want to create new */
1137 /* files in lower-case otherwise we can't reopen them */
1138 /* under the same short name. */
1139 if (flags & DRIVE_CASE_SENSITIVE) wch = tolowerW(*name);
1140 else wch = *name;
1141 p_l += WideCharToMultiByte(CP_UNIXCP, 0, &wch, 1, p_l, 2, NULL, NULL);
1142 name++;
1144 /* Ignore trailing dots and spaces */
1145 while(p_l[-1] == '.' || p_l[-1] == ' ') {
1146 --p_l;
1147 --p_s;
1149 *p_l = '\0';
1150 *p_s = '\0';
1152 while ((*name == '\\') || (*name == '/')) name++;
1155 if (!found)
1157 if (check_last)
1159 SetLastError( ERROR_FILE_NOT_FOUND );
1160 return FALSE;
1162 if (*name) /* Not last */
1164 SetLastError( ERROR_PATH_NOT_FOUND );
1165 return FALSE;
1168 if (!full->long_name[0]) strcpy( full->long_name, "/" );
1169 if (!full->short_name[2]) strcpyW( full->short_name + 2, dos_rootW );
1170 TRACE("returning %s = %s\n", full->long_name, debugstr_w(full->short_name) );
1171 return TRUE;
1175 /***********************************************************************
1176 * GetShortPathNameW (KERNEL32.@)
1178 * NOTES
1179 * observed:
1180 * longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1181 * longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0
1183 * more observations ( with NT 3.51 (WinDD) ):
1184 * longpath <= 8.3 -> just copy longpath to shortpath
1185 * longpath > 8.3 ->
1186 * a) file does not exist -> return 0, LastError = ERROR_FILE_NOT_FOUND
1187 * b) file does exist -> set the short filename.
1188 * - trailing slashes are reproduced in the short name, even if the
1189 * file is not a directory
1190 * - the absolute/relative path of the short name is reproduced like found
1191 * in the long name
1192 * - longpath and shortpath may have the same address
1193 * Peter Ganten, 1999
1195 DWORD WINAPI GetShortPathNameW( LPCWSTR longpath, LPWSTR shortpath, DWORD shortlen )
1197 DOS_FULL_NAME full_name;
1198 WCHAR tmpshortpath[MAX_PATHNAME_LEN];
1199 const WCHAR *p;
1200 DWORD sp = 0, lp = 0;
1201 int drive;
1202 DWORD tmplen;
1203 UINT flags;
1204 BOOL unixabsolute = *longpath == '/';
1206 TRACE("%s\n", debugstr_w(longpath));
1208 if (!longpath) {
1209 SetLastError(ERROR_INVALID_PARAMETER);
1210 return 0;
1212 if (!longpath[0]) {
1213 SetLastError(ERROR_BAD_PATHNAME);
1214 return 0;
1217 /* check for drive letter */
1218 if (!unixabsolute && longpath[1] == ':' ) {
1219 tmpshortpath[0] = longpath[0];
1220 tmpshortpath[1] = ':';
1221 sp = 2;
1224 if ( ( drive = DOSFS_GetPathDrive ( &longpath )) == -1 ) return 0;
1225 flags = DRIVE_GetFlags ( drive );
1227 if (unixabsolute && drive != DRIVE_GetCurrentDrive()) {
1228 tmpshortpath[0] = drive + 'A';
1229 tmpshortpath[1] = ':';
1230 sp = 2;
1233 while ( longpath[lp] ) {
1235 /* check for path delimiters and reproduce them */
1236 if ( longpath[lp] == '\\' || longpath[lp] == '/' ) {
1237 if (!sp || tmpshortpath[sp-1]!= '\\')
1239 /* strip double "\\" */
1240 tmpshortpath[sp] = '\\';
1241 sp++;
1243 tmpshortpath[sp]=0;/*terminate string*/
1244 lp++;
1245 continue;
1248 tmplen = 0;
1249 for(p = longpath + lp; *p && *p != '/' && *p != '\\'; p++)
1250 tmplen++;
1251 lstrcpynW(tmpshortpath + sp, longpath + lp, tmplen + 1);
1253 /* Check, if the current element is a valid dos name */
1254 if ( DOSFS_ValidDOSName ( longpath + lp, !(flags & DRIVE_CASE_SENSITIVE) ) ) {
1255 sp += tmplen;
1256 lp += tmplen;
1257 continue;
1260 /* Check if the file exists and use the existing file name */
1261 if ( DOSFS_GetFullName ( tmpshortpath, TRUE, &full_name ) ) {
1262 strcpyW(tmpshortpath + sp, strrchrW(full_name.short_name, '\\') + 1);
1263 sp += strlenW(tmpshortpath + sp);
1264 lp += tmplen;
1265 continue;
1268 TRACE("not found!\n" );
1269 SetLastError ( ERROR_FILE_NOT_FOUND );
1270 return 0;
1272 tmpshortpath[sp] = 0;
1274 tmplen = strlenW(tmpshortpath) + 1;
1275 if (tmplen <= shortlen)
1277 strcpyW(shortpath, tmpshortpath);
1278 TRACE("returning %s\n", debugstr_w(shortpath));
1279 tmplen--; /* length without 0 */
1282 return tmplen;
1286 /***********************************************************************
1287 * GetShortPathNameA (KERNEL32.@)
1289 DWORD WINAPI GetShortPathNameA( LPCSTR longpath, LPSTR shortpath, DWORD shortlen )
1291 UNICODE_STRING longpathW;
1292 WCHAR shortpathW[MAX_PATH];
1293 DWORD ret, retW;
1295 if (!longpath)
1297 SetLastError(ERROR_INVALID_PARAMETER);
1298 return 0;
1301 TRACE("%s\n", debugstr_a(longpath));
1303 if (!RtlCreateUnicodeStringFromAsciiz(&longpathW, longpath))
1305 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1306 return 0;
1309 retW = GetShortPathNameW(longpathW.Buffer, shortpathW, MAX_PATH);
1311 if (!retW)
1312 ret = 0;
1313 else if (retW > MAX_PATH)
1315 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1316 ret = 0;
1318 else
1320 ret = WideCharToMultiByte(CP_ACP, 0, shortpathW, -1, NULL, 0, NULL, NULL);
1321 if (ret <= shortlen)
1323 WideCharToMultiByte(CP_ACP, 0, shortpathW, -1, shortpath, shortlen, NULL, NULL);
1324 ret--; /* length without 0 */
1328 RtlFreeUnicodeString(&longpathW);
1329 return ret;
1333 /***********************************************************************
1334 * GetLongPathNameW (KERNEL32.@)
1336 * NOTES
1337 * observed (Win2000):
1338 * shortpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1339 * shortpath="": LastError=ERROR_PATH_NOT_FOUND, ret=0
1341 DWORD WINAPI GetLongPathNameW( LPCWSTR shortpath, LPWSTR longpath, DWORD longlen )
1343 DOS_FULL_NAME full_name;
1344 const char *root;
1345 LPWSTR p;
1346 int drive;
1347 DWORD ret, len = 0;
1349 if (!shortpath) {
1350 SetLastError(ERROR_INVALID_PARAMETER);
1351 return 0;
1353 if (!shortpath[0]) {
1354 SetLastError(ERROR_PATH_NOT_FOUND);
1355 return 0;
1358 TRACE("%s,%p,%ld\n", debugstr_w(shortpath), longpath, longlen);
1360 if(shortpath[0]=='\\' && shortpath[1]=='\\')
1362 ERR("UNC pathname %s\n",debugstr_w(shortpath));
1363 lstrcpynW( longpath, full_name.short_name, longlen );
1364 return strlenW(longpath);
1367 if (!DOSFS_GetFullName( shortpath, TRUE, &full_name )) return 0;
1369 root = full_name.long_name;
1370 drive = DRIVE_FindDriveRoot(&root);
1372 ret = MultiByteToWideChar(CP_UNIXCP, 0, root, -1, NULL, 0);
1373 ret += 3; /* A:\ */
1374 /* reproduce terminating slash */
1375 if (ret > 4) /* if not drive root */
1377 len = strlenW(shortpath);
1378 if (shortpath[len - 1] == '\\' || shortpath[len - 1] == '/')
1379 len = 1;
1381 ret += len;
1382 if (ret <= longlen)
1384 longpath[0] = 'A' + drive;
1385 longpath[1] = ':';
1386 MultiByteToWideChar(CP_UNIXCP, 0, root, -1, longpath + 2, longlen - 2);
1387 for (p = longpath; *p; p++) if (*p == '/') *p = '\\';
1388 if (len)
1390 longpath[ret - 2] = '\\';
1391 longpath[ret - 1] = 0;
1393 TRACE("returning %s\n", debugstr_w(longpath));
1394 ret--; /* length without 0 */
1396 return ret;
1400 /***********************************************************************
1401 * GetLongPathNameA (KERNEL32.@)
1403 DWORD WINAPI GetLongPathNameA( LPCSTR shortpath, LPSTR longpath, DWORD longlen )
1405 UNICODE_STRING shortpathW;
1406 WCHAR longpathW[MAX_PATH];
1407 DWORD ret, retW;
1409 if (!shortpath)
1411 SetLastError(ERROR_INVALID_PARAMETER);
1412 return 0;
1415 TRACE("%s\n", debugstr_a(shortpath));
1417 if (!RtlCreateUnicodeStringFromAsciiz(&shortpathW, shortpath))
1419 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1420 return 0;
1423 retW = GetLongPathNameW(shortpathW.Buffer, longpathW, MAX_PATH);
1425 if (!retW)
1426 ret = 0;
1427 else if (retW > MAX_PATH)
1429 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1430 ret = 0;
1432 else
1434 ret = WideCharToMultiByte(CP_ACP, 0, longpathW, -1, NULL, 0, NULL, NULL);
1435 if (ret <= longlen)
1437 WideCharToMultiByte(CP_ACP, 0, longpathW, -1, longpath, longlen, NULL, NULL);
1438 ret--; /* length without 0 */
1442 RtlFreeUnicodeString(&shortpathW);
1443 return ret;
1447 /***********************************************************************
1448 * DOSFS_DoGetFullPathName
1450 * Implementation of GetFullPathNameA/W.
1452 * bon@elektron 000331:
1453 * A test for GetFullPathName with many pathological cases
1454 * now gives identical output for Wine and OSR2
1456 static DWORD DOSFS_DoGetFullPathName( LPCWSTR name, DWORD len, LPWSTR result )
1458 DWORD ret;
1459 DOS_FULL_NAME full_name;
1460 LPWSTR p, q;
1461 char *p_l;
1462 const char * root;
1463 WCHAR drivecur[] = {'C',':','.',0};
1464 WCHAR driveletter=0;
1465 int namelen,drive=0;
1466 static const WCHAR bkslashW[] = {'\\',0};
1467 static const WCHAR dotW[] = {'.',0};
1468 static const WCHAR updir_slashW[] = {'\\','.','.','\\',0};
1469 static const WCHAR curdirW[] = {'\\','.','\\',0};
1470 static const WCHAR updirW[] = {'\\','.','.',0};
1472 if (!name[0])
1474 SetLastError(ERROR_BAD_PATHNAME);
1475 return 0;
1478 TRACE("passed %s\n", debugstr_w(name));
1480 if (name[1]==':')
1481 /*drive letter given */
1483 driveletter = name[0];
1485 if ((name[1]==':') && ((name[2]=='\\') || (name[2]=='/')))
1486 /*absolute path given */
1488 strncpyW(full_name.short_name, name, MAX_PATHNAME_LEN);
1489 full_name.short_name[MAX_PATHNAME_LEN - 1] = 0; /* ensure 0 termination */
1490 drive = toupperW(name[0]) - 'A';
1492 else
1494 if (driveletter)
1495 drivecur[0]=driveletter;
1496 else if ((name[0]=='\\') || (name[0]=='/'))
1497 strcpyW(drivecur, bkslashW);
1498 else
1499 strcpyW(drivecur, dotW);
1501 if (!DOSFS_GetFullName( drivecur, FALSE, &full_name ))
1503 FIXME("internal: error getting drive/path\n");
1504 return 0;
1506 /* find path that drive letter substitutes*/
1507 drive = toupperW(full_name.short_name[0]) - 'A';
1508 root= DRIVE_GetRoot(drive);
1509 if (!root)
1511 FIXME("internal: error getting DOS Drive Root\n");
1512 return 0;
1514 if (!strcmp(root,"/"))
1516 /* we have just the last / and we need it. */
1517 p_l = full_name.long_name;
1519 else
1521 p_l = full_name.long_name + strlen(root);
1523 /* append long name (= unix name) to drive */
1524 MultiByteToWideChar(CP_UNIXCP, 0, p_l, -1, full_name.short_name + 2, MAX_PATHNAME_LEN - 3);
1525 /* append name to treat */
1526 namelen= strlenW(full_name.short_name);
1527 p = (LPWSTR)name;
1528 if (driveletter)
1529 p += 2; /* skip drive name when appending */
1530 if (namelen + 2 + strlenW(p) > MAX_PATHNAME_LEN)
1532 FIXME("internal error: buffer too small\n");
1533 return 0;
1535 full_name.short_name[namelen++] ='\\';
1536 full_name.short_name[namelen] = 0;
1537 strncpyW(full_name.short_name + namelen, p, MAX_PATHNAME_LEN - namelen);
1538 full_name.short_name[MAX_PATHNAME_LEN - 1] = 0; /* ensure 0 termination */
1540 /* reverse all slashes */
1541 for (p=full_name.short_name;
1542 p < full_name.short_name + strlenW(full_name.short_name);
1543 p++)
1545 if ( *p == '/' )
1546 *p = '\\';
1548 /* Use memmove, as areas overlap */
1549 /* Delete .. */
1550 while ((p = strstrW(full_name.short_name, updir_slashW)))
1552 if (p > full_name.short_name+2)
1554 *p = 0;
1555 q = strrchrW(full_name.short_name, '\\');
1556 memmove(q+1, p+4, (strlenW(p+4)+1) * sizeof(WCHAR));
1558 else
1560 memmove(full_name.short_name+3, p+4, (strlenW(p+4)+1) * sizeof(WCHAR));
1563 if ((full_name.short_name[2]=='.')&&(full_name.short_name[3]=='.'))
1565 /* This case istn't treated yet : c:..\test */
1566 memmove(full_name.short_name+2,full_name.short_name+4,
1567 (strlenW(full_name.short_name+4)+1) * sizeof(WCHAR));
1569 /* Delete . */
1570 while ((p = strstrW(full_name.short_name, curdirW)))
1572 *(p+1) = 0;
1573 memmove(p+1, p+3, (strlenW(p+3)+1) * sizeof(WCHAR));
1575 if (!(DRIVE_GetFlags(drive) & DRIVE_CASE_PRESERVING))
1576 for (p = full_name.short_name; *p; p++) *p = toupperW(*p);
1577 namelen = strlenW(full_name.short_name);
1578 if (!strcmpW(full_name.short_name+namelen-3, updirW))
1580 /* one more strange case: "c:\test\test1\.."
1581 return "c:\test" */
1582 *(full_name.short_name+namelen-3)=0;
1583 q = strrchrW(full_name.short_name, '\\');
1584 *q =0;
1586 if (full_name.short_name[namelen-1]=='.')
1587 full_name.short_name[(namelen--)-1] =0;
1588 if (!driveletter)
1589 if (full_name.short_name[namelen-1]=='\\')
1590 full_name.short_name[(namelen--)-1] =0;
1591 TRACE("got %s\n", debugstr_w(full_name.short_name));
1593 /* If the lpBuffer buffer is too small, the return value is the
1594 size of the buffer, in characters, required to hold the path
1595 plus the terminating \0 (tested against win95osr2, bon 001118)
1596 . */
1597 ret = strlenW(full_name.short_name);
1598 if (ret >= len )
1600 /* don't touch anything when the buffer is not large enough */
1601 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1602 return ret+1;
1604 if (result)
1606 strncpyW( result, full_name.short_name, len );
1607 result[len - 1] = 0; /* ensure 0 termination */
1610 TRACE("returning %s\n", debugstr_w(full_name.short_name) );
1611 return ret;
1615 /***********************************************************************
1616 * GetFullPathNameA (KERNEL32.@)
1617 * NOTES
1618 * if the path closed with '\', *lastpart is 0
1620 DWORD WINAPI GetFullPathNameA( LPCSTR name, DWORD len, LPSTR buffer,
1621 LPSTR *lastpart )
1623 UNICODE_STRING nameW;
1624 WCHAR bufferW[MAX_PATH];
1625 DWORD ret, retW;
1627 if (!name)
1629 SetLastError(ERROR_INVALID_PARAMETER);
1630 return 0;
1633 if (!RtlCreateUnicodeStringFromAsciiz(&nameW, name))
1635 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1636 return 0;
1639 retW = GetFullPathNameW( nameW.Buffer, MAX_PATH, bufferW, NULL);
1641 if (!retW)
1642 ret = 0;
1643 else if (retW > MAX_PATH)
1645 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1646 ret = 0;
1648 else
1650 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL);
1651 if (ret <= len)
1653 WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, len, NULL, NULL);
1654 ret--; /* length without 0 */
1656 if (lastpart)
1658 LPSTR p = buffer + strlen(buffer);
1660 if (*p != '\\')
1662 while ((p > buffer + 2) && (*p != '\\')) p--;
1663 *lastpart = p + 1;
1665 else *lastpart = NULL;
1670 RtlFreeUnicodeString(&nameW);
1671 return ret;
1675 /***********************************************************************
1676 * GetFullPathNameW (KERNEL32.@)
1678 DWORD WINAPI GetFullPathNameW( LPCWSTR name, DWORD len, LPWSTR buffer,
1679 LPWSTR *lastpart )
1681 DWORD ret = DOSFS_DoGetFullPathName( name, len, buffer );
1682 if (ret && (ret<=len) && buffer && lastpart)
1684 LPWSTR p = buffer + strlenW(buffer);
1685 if (*p != (WCHAR)'\\')
1687 while ((p > buffer + 2) && (*p != (WCHAR)'\\')) p--;
1688 *lastpart = p + 1;
1690 else *lastpart = NULL;
1692 return ret;
1696 /***********************************************************************
1697 * wine_get_unix_file_name (KERNEL32.@) Not a Windows API
1699 * Return the full Unix file name for a given path.
1700 * FIXME: convert dos file name to unicode
1702 BOOL WINAPI wine_get_unix_file_name( LPCSTR dos, LPSTR buffer, DWORD len )
1704 BOOL ret;
1705 DOS_FULL_NAME path;
1706 WCHAR dosW[MAX_PATHNAME_LEN];
1708 MultiByteToWideChar(CP_ACP, 0, dos, -1, dosW, MAX_PATHNAME_LEN);
1709 ret = DOSFS_GetFullName( dosW, FALSE, &path );
1710 if (ret && len)
1712 strncpy( buffer, path.long_name, len );
1713 buffer[len - 1] = 0; /* ensure 0 termination */
1715 return ret;
1719 /***********************************************************************
1720 * get_show_dir_symlinks_option
1722 static BOOL get_show_dir_symlinks_option(void)
1724 static const WCHAR WineW[] = {'M','a','c','h','i','n','e','\\',
1725 'S','o','f','t','w','a','r','e','\\',
1726 'W','i','n','e','\\','W','i','n','e','\\',
1727 'C','o','n','f','i','g','\\','W','i','n','e',0};
1728 static const WCHAR ShowDirSymlinksW[] = {'S','h','o','w','D','i','r','S','y','m','l','i','n','k','s',0};
1730 char tmp[80];
1731 HKEY hkey;
1732 DWORD dummy;
1733 OBJECT_ATTRIBUTES attr;
1734 UNICODE_STRING nameW;
1735 BOOL ret = FALSE;
1737 attr.Length = sizeof(attr);
1738 attr.RootDirectory = 0;
1739 attr.ObjectName = &nameW;
1740 attr.Attributes = 0;
1741 attr.SecurityDescriptor = NULL;
1742 attr.SecurityQualityOfService = NULL;
1743 RtlInitUnicodeString( &nameW, WineW );
1745 if (!NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr ))
1747 RtlInitUnicodeString( &nameW, ShowDirSymlinksW );
1748 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
1750 WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
1751 ret = IS_OPTION_TRUE( str[0] );
1753 NtClose( hkey );
1755 return ret;
1759 /***********************************************************************
1760 * DOSFS_FindNextEx
1762 static int DOSFS_FindNextEx( FIND_FIRST_INFO *info, WIN32_FIND_DATAW *entry )
1764 UINT flags = DRIVE_GetFlags( info->drive );
1765 char *p, buffer[MAX_PATHNAME_LEN];
1766 const char *drive_path;
1767 int drive_root;
1768 LPCWSTR long_name, short_name;
1769 BY_HANDLE_FILE_INFORMATION fileinfo;
1770 BOOL is_symlink;
1772 drive_path = info->path + strlen(DRIVE_GetRoot( info->drive ));
1773 while ((*drive_path == '/') || (*drive_path == '\\')) drive_path++;
1774 drive_root = !*drive_path;
1776 lstrcpynA( buffer, info->path, sizeof(buffer) - 1 );
1777 strcat( buffer, "/" );
1778 p = buffer + strlen(buffer);
1780 while (DOSFS_ReadDir( info->u.dos_dir, &long_name, &short_name ))
1782 info->cur_pos++;
1784 /* Don't return '.' and '..' in the root of the drive */
1785 if (drive_root && (long_name[0] == '.') &&
1786 (!long_name[1] || ((long_name[1] == '.') && !long_name[2])))
1787 continue;
1789 /* Check the long mask */
1791 if (info->long_mask && *info->long_mask)
1793 if (!DOSFS_MatchLong( info->long_mask, long_name,
1794 flags & DRIVE_CASE_SENSITIVE )) continue;
1797 /* Check the file attributes */
1798 WideCharToMultiByte(CP_UNIXCP, 0, long_name, -1,
1799 p, sizeof(buffer) - (int)(p - buffer), NULL, NULL);
1800 if (!FILE_Stat( buffer, &fileinfo, &is_symlink ))
1802 WARN("can't stat %s\n", buffer);
1803 continue;
1805 if (is_symlink && (fileinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
1807 static int show_dir_symlinks = -1;
1808 if (show_dir_symlinks == -1)
1809 show_dir_symlinks = get_show_dir_symlinks_option();
1810 if (!show_dir_symlinks) continue;
1813 /* We now have a matching entry; fill the result and return */
1815 entry->dwFileAttributes = fileinfo.dwFileAttributes;
1816 entry->ftCreationTime = fileinfo.ftCreationTime;
1817 entry->ftLastAccessTime = fileinfo.ftLastAccessTime;
1818 entry->ftLastWriteTime = fileinfo.ftLastWriteTime;
1819 entry->nFileSizeHigh = fileinfo.nFileSizeHigh;
1820 entry->nFileSizeLow = fileinfo.nFileSizeLow;
1822 if (short_name)
1823 DOSFS_ToDosDTAFormat( short_name, entry->cAlternateFileName );
1824 else
1825 DOSFS_Hash( long_name, entry->cAlternateFileName, FALSE,
1826 !(flags & DRIVE_CASE_SENSITIVE) );
1828 lstrcpynW( entry->cFileName, long_name, sizeof(entry->cFileName)/sizeof(entry->cFileName[0]) );
1829 if (!(flags & DRIVE_CASE_PRESERVING)) strlwrW( entry->cFileName );
1830 TRACE("returning %s (%s) %02lx %ld\n",
1831 debugstr_w(entry->cFileName), debugstr_w(entry->cAlternateFileName),
1832 entry->dwFileAttributes, entry->nFileSizeLow );
1833 return 1;
1835 return 0; /* End of directory */
1838 /*************************************************************************
1839 * FindFirstFileExW (KERNEL32.@)
1841 HANDLE WINAPI FindFirstFileExW(
1842 LPCWSTR lpFileName,
1843 FINDEX_INFO_LEVELS fInfoLevelId,
1844 LPVOID lpFindFileData,
1845 FINDEX_SEARCH_OPS fSearchOp,
1846 LPVOID lpSearchFilter,
1847 DWORD dwAdditionalFlags)
1849 FIND_FIRST_INFO *info;
1851 if (!lpFileName)
1853 SetLastError(ERROR_PATH_NOT_FOUND);
1854 return INVALID_HANDLE_VALUE;
1857 if ((fSearchOp != FindExSearchNameMatch) || (dwAdditionalFlags != 0))
1859 FIXME("options not implemented 0x%08x 0x%08lx\n", fSearchOp, dwAdditionalFlags );
1860 return INVALID_HANDLE_VALUE;
1863 switch(fInfoLevelId)
1865 case FindExInfoStandard:
1867 WIN32_FIND_DATAW * data = (WIN32_FIND_DATAW *) lpFindFileData;
1868 char *p;
1869 INT long_mask_len;
1871 data->dwReserved0 = data->dwReserved1 = 0x0;
1872 if (lpFileName[0] == '\\' && lpFileName[1] == '\\')
1874 ERR("UNC path name\n");
1875 if (!(info = HeapAlloc( GetProcessHeap(), 0, sizeof(FIND_FIRST_INFO)))) break;
1876 info->u.smb_dir = SMB_FindFirst(lpFileName);
1877 if(!info->u.smb_dir)
1879 HeapFree(GetProcessHeap(), 0, info);
1880 break;
1882 info->drive = -1;
1883 RtlInitializeCriticalSection( &info->cs );
1885 else
1887 DOS_FULL_NAME full_name;
1889 if (lpFileName[0] && lpFileName[1] == ':')
1891 /* don't allow root directories */
1892 if (!lpFileName[2] ||
1893 ((lpFileName[2] == '/' || lpFileName[2] == '\\') && !lpFileName[3]))
1895 SetLastError(ERROR_FILE_NOT_FOUND);
1896 return INVALID_HANDLE_VALUE;
1899 if (!DOSFS_GetFullName( lpFileName, FALSE, &full_name )) break;
1900 if (!(info = HeapAlloc( GetProcessHeap(), 0, sizeof(FIND_FIRST_INFO)))) break;
1901 RtlInitializeCriticalSection( &info->cs );
1902 info->path = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
1903 strcpy( info->path, full_name.long_name );
1905 p = strrchr( info->path, '/' );
1906 *p++ = '\0';
1907 long_mask_len = MultiByteToWideChar(CP_UNIXCP, 0, p, -1, NULL, 0);
1908 info->long_mask = HeapAlloc( GetProcessHeap(), 0, long_mask_len * sizeof(WCHAR) );
1909 MultiByteToWideChar(CP_UNIXCP, 0, p, -1, info->long_mask, long_mask_len);
1911 info->drive = full_name.drive;
1912 info->cur_pos = 0;
1914 info->u.dos_dir = DOSFS_OpenDir( info->path );
1916 if (!FindNextFileW( (HANDLE) info, data ))
1918 FindClose( (HANDLE) info );
1919 SetLastError( ERROR_FILE_NOT_FOUND );
1920 break;
1922 return (HANDLE) info;
1924 break;
1925 default:
1926 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
1928 return INVALID_HANDLE_VALUE;
1931 /*************************************************************************
1932 * FindFirstFileA (KERNEL32.@)
1934 HANDLE WINAPI FindFirstFileA(
1935 LPCSTR lpFileName,
1936 WIN32_FIND_DATAA *lpFindData )
1938 return FindFirstFileExA(lpFileName, FindExInfoStandard, lpFindData,
1939 FindExSearchNameMatch, NULL, 0);
1942 /*************************************************************************
1943 * FindFirstFileExA (KERNEL32.@)
1945 HANDLE WINAPI FindFirstFileExA(
1946 LPCSTR lpFileName,
1947 FINDEX_INFO_LEVELS fInfoLevelId,
1948 LPVOID lpFindFileData,
1949 FINDEX_SEARCH_OPS fSearchOp,
1950 LPVOID lpSearchFilter,
1951 DWORD dwAdditionalFlags)
1953 HANDLE handle;
1954 WIN32_FIND_DATAA *dataA;
1955 WIN32_FIND_DATAW dataW;
1956 UNICODE_STRING pathW;
1958 if (!lpFileName)
1960 SetLastError(ERROR_PATH_NOT_FOUND);
1961 return INVALID_HANDLE_VALUE;
1964 if (!RtlCreateUnicodeStringFromAsciiz(&pathW, lpFileName))
1966 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1967 return INVALID_HANDLE_VALUE;
1970 handle = FindFirstFileExW(pathW.Buffer, fInfoLevelId, &dataW, fSearchOp, lpSearchFilter, dwAdditionalFlags);
1971 RtlFreeUnicodeString(&pathW);
1972 if (handle == INVALID_HANDLE_VALUE) return handle;
1974 dataA = (WIN32_FIND_DATAA *) lpFindFileData;
1975 dataA->dwFileAttributes = dataW.dwFileAttributes;
1976 dataA->ftCreationTime = dataW.ftCreationTime;
1977 dataA->ftLastAccessTime = dataW.ftLastAccessTime;
1978 dataA->ftLastWriteTime = dataW.ftLastWriteTime;
1979 dataA->nFileSizeHigh = dataW.nFileSizeHigh;
1980 dataA->nFileSizeLow = dataW.nFileSizeLow;
1981 WideCharToMultiByte( CP_ACP, 0, dataW.cFileName, -1,
1982 dataA->cFileName, sizeof(dataA->cFileName), NULL, NULL );
1983 WideCharToMultiByte( CP_ACP, 0, dataW.cAlternateFileName, -1,
1984 dataA->cAlternateFileName, sizeof(dataA->cAlternateFileName), NULL, NULL );
1985 return handle;
1988 /*************************************************************************
1989 * FindFirstFileW (KERNEL32.@)
1991 HANDLE WINAPI FindFirstFileW( LPCWSTR lpFileName, WIN32_FIND_DATAW *lpFindData )
1993 return FindFirstFileExW(lpFileName, FindExInfoStandard, lpFindData,
1994 FindExSearchNameMatch, NULL, 0);
1997 /*************************************************************************
1998 * FindNextFileW (KERNEL32.@)
2000 BOOL WINAPI FindNextFileW( HANDLE handle, WIN32_FIND_DATAW *data )
2002 FIND_FIRST_INFO *info;
2003 BOOL ret = FALSE;
2004 DWORD gle = ERROR_NO_MORE_FILES;
2006 if (handle == INVALID_HANDLE_VALUE)
2008 SetLastError( ERROR_INVALID_HANDLE );
2009 return ret;
2011 info = (FIND_FIRST_INFO*) handle;
2012 RtlEnterCriticalSection( &info->cs );
2013 if (info->drive == -1)
2015 ret = SMB_FindNext( info->u.smb_dir, data );
2016 if(!ret)
2018 SMB_CloseDir( info->u.smb_dir );
2019 HeapFree( GetProcessHeap(), 0, info->path );
2021 goto done;
2023 else if (!info->path || !info->u.dos_dir)
2025 goto done;
2027 else if (!DOSFS_FindNextEx( info, data ))
2029 DOSFS_CloseDir( info->u.dos_dir ); info->u.dos_dir = NULL;
2030 HeapFree( GetProcessHeap(), 0, info->path );
2031 info->path = NULL;
2032 HeapFree( GetProcessHeap(), 0, info->long_mask );
2033 info->long_mask = NULL;
2034 goto done;
2036 ret = TRUE;
2037 done:
2038 RtlLeaveCriticalSection( &info->cs );
2039 if( !ret ) SetLastError( gle );
2040 return ret;
2044 /*************************************************************************
2045 * FindNextFileA (KERNEL32.@)
2047 BOOL WINAPI FindNextFileA( HANDLE handle, WIN32_FIND_DATAA *data )
2049 WIN32_FIND_DATAW dataW;
2050 if (!FindNextFileW( handle, &dataW )) return FALSE;
2051 data->dwFileAttributes = dataW.dwFileAttributes;
2052 data->ftCreationTime = dataW.ftCreationTime;
2053 data->ftLastAccessTime = dataW.ftLastAccessTime;
2054 data->ftLastWriteTime = dataW.ftLastWriteTime;
2055 data->nFileSizeHigh = dataW.nFileSizeHigh;
2056 data->nFileSizeLow = dataW.nFileSizeLow;
2057 WideCharToMultiByte( CP_ACP, 0, dataW.cFileName, -1,
2058 data->cFileName, sizeof(data->cFileName), NULL, NULL );
2059 WideCharToMultiByte( CP_ACP, 0, dataW.cAlternateFileName, -1,
2060 data->cAlternateFileName,
2061 sizeof(data->cAlternateFileName), NULL, NULL );
2062 return TRUE;
2065 /*************************************************************************
2066 * FindClose (KERNEL32.@)
2068 BOOL WINAPI FindClose( HANDLE handle )
2070 FIND_FIRST_INFO *info = (FIND_FIRST_INFO*) handle;
2072 if (handle == INVALID_HANDLE_VALUE) goto error;
2074 __TRY
2076 RtlEnterCriticalSection( &info->cs );
2077 if (info)
2079 if (info->u.dos_dir) DOSFS_CloseDir( info->u.dos_dir );
2080 if (info->path) HeapFree( GetProcessHeap(), 0, info->path );
2081 if (info->long_mask) HeapFree( GetProcessHeap(), 0, info->long_mask );
2084 __EXCEPT(page_fault)
2086 WARN("Illegal handle %p\n", handle);
2087 SetLastError( ERROR_INVALID_HANDLE );
2088 return FALSE;
2090 __ENDTRY
2091 if (!info) goto error;
2092 RtlLeaveCriticalSection( &info->cs );
2093 RtlDeleteCriticalSection( &info->cs );
2094 HeapFree(GetProcessHeap(), 0, info);
2095 return TRUE;
2097 error:
2098 SetLastError( ERROR_INVALID_HANDLE );
2099 return FALSE;
2102 /***********************************************************************
2103 * MulDiv (KERNEL32.@)
2104 * RETURNS
2105 * Result of multiplication and division
2106 * -1: Overflow occurred or Divisor was 0
2108 INT WINAPI MulDiv(
2109 INT nMultiplicand,
2110 INT nMultiplier,
2111 INT nDivisor)
2113 #if SIZEOF_LONG_LONG >= 8
2114 long long ret;
2116 if (!nDivisor) return -1;
2118 /* We want to deal with a positive divisor to simplify the logic. */
2119 if (nDivisor < 0)
2121 nMultiplicand = - nMultiplicand;
2122 nDivisor = -nDivisor;
2125 /* If the result is positive, we "add" to round. else, we subtract to round. */
2126 if ( ( (nMultiplicand < 0) && (nMultiplier < 0) ) ||
2127 ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
2128 ret = (((long long)nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
2129 else
2130 ret = (((long long)nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
2132 if ((ret > 2147483647) || (ret < -2147483647)) return -1;
2133 return ret;
2134 #else
2135 if (!nDivisor) return -1;
2137 /* We want to deal with a positive divisor to simplify the logic. */
2138 if (nDivisor < 0)
2140 nMultiplicand = - nMultiplicand;
2141 nDivisor = -nDivisor;
2144 /* If the result is positive, we "add" to round. else, we subtract to round. */
2145 if ( ( (nMultiplicand < 0) && (nMultiplier < 0) ) ||
2146 ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
2147 return ((nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
2149 return ((nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
2151 #endif
2155 /***********************************************************************
2156 * DosDateTimeToFileTime (KERNEL32.@)
2158 BOOL WINAPI DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft)
2160 struct tm newtm;
2161 #ifndef HAVE_TIMEGM
2162 struct tm *gtm;
2163 time_t time1, time2;
2164 #endif
2166 newtm.tm_sec = (fattime & 0x1f) * 2;
2167 newtm.tm_min = (fattime >> 5) & 0x3f;
2168 newtm.tm_hour = (fattime >> 11);
2169 newtm.tm_mday = (fatdate & 0x1f);
2170 newtm.tm_mon = ((fatdate >> 5) & 0x0f) - 1;
2171 newtm.tm_year = (fatdate >> 9) + 80;
2172 #ifdef HAVE_TIMEGM
2173 RtlSecondsSince1970ToTime( timegm(&newtm), (LARGE_INTEGER *)ft );
2174 #else
2175 time1 = mktime(&newtm);
2176 gtm = gmtime(&time1);
2177 time2 = mktime(gtm);
2178 RtlSecondsSince1970ToTime( 2*time1-time2, (LARGE_INTEGER *)ft );
2179 #endif
2180 return TRUE;
2184 /***********************************************************************
2185 * FileTimeToDosDateTime (KERNEL32.@)
2187 BOOL WINAPI FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate,
2188 LPWORD fattime )
2190 LARGE_INTEGER li;
2191 ULONG t;
2192 time_t unixtime;
2193 struct tm* tm;
2195 li.s.LowPart = ft->dwLowDateTime;
2196 li.s.HighPart = ft->dwHighDateTime;
2197 RtlTimeToSecondsSince1970( &li, &t );
2198 unixtime = t;
2199 tm = gmtime( &unixtime );
2200 if (fattime)
2201 *fattime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
2202 if (fatdate)
2203 *fatdate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
2204 + tm->tm_mday;
2205 return TRUE;
2209 /***********************************************************************
2210 * QueryDosDeviceA (KERNEL32.@)
2212 * returns array of strings terminated by \0, terminated by \0
2214 DWORD WINAPI QueryDosDeviceA(LPCSTR devname,LPSTR target,DWORD bufsize)
2216 DWORD ret = 0, retW;
2217 LPWSTR targetW = (LPWSTR)HeapAlloc(GetProcessHeap(),0,
2218 bufsize * sizeof(WCHAR));
2219 UNICODE_STRING devnameW;
2221 if(devname) RtlCreateUnicodeStringFromAsciiz(&devnameW, devname);
2222 else devnameW.Buffer = NULL;
2224 retW = QueryDosDeviceW(devnameW.Buffer, targetW, bufsize);
2226 ret = WideCharToMultiByte(CP_ACP, 0, targetW, retW, target,
2227 bufsize, NULL, NULL);
2229 RtlFreeUnicodeString(&devnameW);
2230 if (targetW) HeapFree(GetProcessHeap(),0,targetW);
2231 return ret;
2235 /***********************************************************************
2236 * QueryDosDeviceW (KERNEL32.@)
2238 * returns array of strings terminated by \0, terminated by \0
2240 * FIXME
2241 * - Win9x returns for all calls ERROR_INVALID_PARAMETER
2242 * - the returned devices for devname == NULL is far from complete
2243 * - its not checked that the returned device exist
2245 DWORD WINAPI QueryDosDeviceW(LPCWSTR devname,LPWSTR target,DWORD bufsize)
2247 const WCHAR *pDev, *pName, *pNum = NULL;
2248 int numsiz=0;
2249 DWORD ret;
2251 TRACE("(%s,...)\n", debugstr_w(devname));
2252 if (!devname) {
2253 /* return known MSDOS devices */
2254 DWORD ret = 0;
2255 int i;
2256 static const WCHAR devices[][5] = {{'A','U','X',0},
2257 {'C','O','M','1',0},
2258 {'C','O','M','2',0},
2259 {'L','P','T','1',0},
2260 {'N','U','L',0,}};
2261 for(i=0; (i< (sizeof(devices)/sizeof(devices[0]))); i++) {
2262 DWORD len = strlenW(devices[i]);
2263 if(target && (bufsize >= ret + len + 2)) {
2264 strcpyW(target+ret, devices[i]);
2265 ret += len + 1;
2266 } else {
2267 /* in this case WinXP returns 0 */
2268 FIXME("function return is wrong for WinXP!\n");
2269 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2270 break;
2273 /* append drives here */
2274 if(target && bufsize > 0) target[ret++] = 0;
2275 FIXME("Returned list is not complete\n");
2276 return ret;
2278 /* In theory all that are possible and have been defined.
2279 * Now just those below, since mirc uses it to check for special files.
2281 * (It is more complex, and supports netmounted stuff, and \\.\ stuff,
2282 * but currently we just ignore that.)
2284 if (!strcmpiW(devname, auxW)) {
2285 pDev = dosW;
2286 pName = comW;
2287 numsiz = 1;
2288 pNum = oneW;
2289 } else if (!strcmpiW(devname, nulW)) {
2290 pDev = devW;
2291 pName = nullW;
2292 } else if (!strncmpiW(devname, comW, strlenW(comW))) {
2293 pDev = devW;
2294 pName = serW;
2295 pNum = devname + strlenW(comW);
2296 for(numsiz=0; isdigitW(*(pNum+numsiz)); numsiz++);
2297 if(*(pNum + numsiz)) {
2298 SetLastError(ERROR_FILE_NOT_FOUND);
2299 return 0;
2301 } else if (!strncmpiW(devname, lptW, strlenW(lptW))) {
2302 pDev = devW;
2303 pName = parW;
2304 pNum = devname + strlenW(lptW);
2305 for(numsiz=0; isdigitW(*(pNum+numsiz)); numsiz++);
2306 if(*(pNum + numsiz)) {
2307 SetLastError(ERROR_FILE_NOT_FOUND);
2308 return 0;
2310 } else {
2311 /* This might be a DOS device we do not handle yet ... */
2312 FIXME("(%s) not detected as DOS device!\n",debugstr_w(devname));
2314 /* Win9x set the error ERROR_INVALID_PARAMETER */
2315 SetLastError(ERROR_FILE_NOT_FOUND);
2316 return 0;
2318 FIXME("device %s may not exist on this computer\n", debugstr_w(devname));
2320 ret = strlenW(pDev) + strlenW(pName) + numsiz + 2;
2321 if (ret > bufsize) ret = 0;
2322 if (target && ret) {
2323 strcpyW(target,pDev);
2324 strcatW(target,pName);
2325 if (pNum) strcatW(target,pNum);
2326 target[ret-1] = 0;
2328 return ret;