Removed XInitThreads call, the X libraries still have too many bugs to
[wine/testsucceed.git] / files / dos_fs.c
blob04c9b557358cc24ebd8759ae4d46f3ea08ebd0a7
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.
1701 BOOL WINAPI wine_get_unix_file_name( LPCWSTR dosW, LPSTR buffer, DWORD len )
1703 BOOL ret;
1704 DOS_FULL_NAME path;
1706 ret = DOSFS_GetFullName( dosW, FALSE, &path );
1707 if (ret && len)
1709 strncpy( buffer, path.long_name, len );
1710 buffer[len - 1] = 0; /* ensure 0 termination */
1712 return ret;
1716 /***********************************************************************
1717 * get_show_dir_symlinks_option
1719 static BOOL get_show_dir_symlinks_option(void)
1721 static const WCHAR WineW[] = {'M','a','c','h','i','n','e','\\',
1722 'S','o','f','t','w','a','r','e','\\',
1723 'W','i','n','e','\\','W','i','n','e','\\',
1724 'C','o','n','f','i','g','\\','W','i','n','e',0};
1725 static const WCHAR ShowDirSymlinksW[] = {'S','h','o','w','D','i','r','S','y','m','l','i','n','k','s',0};
1727 char tmp[80];
1728 HKEY hkey;
1729 DWORD dummy;
1730 OBJECT_ATTRIBUTES attr;
1731 UNICODE_STRING nameW;
1732 BOOL ret = FALSE;
1734 attr.Length = sizeof(attr);
1735 attr.RootDirectory = 0;
1736 attr.ObjectName = &nameW;
1737 attr.Attributes = 0;
1738 attr.SecurityDescriptor = NULL;
1739 attr.SecurityQualityOfService = NULL;
1740 RtlInitUnicodeString( &nameW, WineW );
1742 if (!NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr ))
1744 RtlInitUnicodeString( &nameW, ShowDirSymlinksW );
1745 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
1747 WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
1748 ret = IS_OPTION_TRUE( str[0] );
1750 NtClose( hkey );
1752 return ret;
1756 /***********************************************************************
1757 * DOSFS_FindNextEx
1759 static int DOSFS_FindNextEx( FIND_FIRST_INFO *info, WIN32_FIND_DATAW *entry )
1761 UINT flags = DRIVE_GetFlags( info->drive );
1762 char *p, buffer[MAX_PATHNAME_LEN];
1763 const char *drive_path;
1764 int drive_root;
1765 LPCWSTR long_name, short_name;
1766 BY_HANDLE_FILE_INFORMATION fileinfo;
1767 BOOL is_symlink;
1769 drive_path = info->path + strlen(DRIVE_GetRoot( info->drive ));
1770 while ((*drive_path == '/') || (*drive_path == '\\')) drive_path++;
1771 drive_root = !*drive_path;
1773 lstrcpynA( buffer, info->path, sizeof(buffer) - 1 );
1774 strcat( buffer, "/" );
1775 p = buffer + strlen(buffer);
1777 while (DOSFS_ReadDir( info->u.dos_dir, &long_name, &short_name ))
1779 info->cur_pos++;
1781 /* Don't return '.' and '..' in the root of the drive */
1782 if (drive_root && (long_name[0] == '.') &&
1783 (!long_name[1] || ((long_name[1] == '.') && !long_name[2])))
1784 continue;
1786 /* Check the long mask */
1788 if (info->long_mask && *info->long_mask)
1790 if (!DOSFS_MatchLong( info->long_mask, long_name,
1791 flags & DRIVE_CASE_SENSITIVE )) continue;
1794 /* Check the file attributes */
1795 WideCharToMultiByte(CP_UNIXCP, 0, long_name, -1,
1796 p, sizeof(buffer) - (int)(p - buffer), NULL, NULL);
1797 if (!FILE_Stat( buffer, &fileinfo, &is_symlink ))
1799 WARN("can't stat %s\n", buffer);
1800 continue;
1802 if (is_symlink && (fileinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
1804 static int show_dir_symlinks = -1;
1805 if (show_dir_symlinks == -1)
1806 show_dir_symlinks = get_show_dir_symlinks_option();
1807 if (!show_dir_symlinks) continue;
1810 /* We now have a matching entry; fill the result and return */
1812 entry->dwFileAttributes = fileinfo.dwFileAttributes;
1813 entry->ftCreationTime = fileinfo.ftCreationTime;
1814 entry->ftLastAccessTime = fileinfo.ftLastAccessTime;
1815 entry->ftLastWriteTime = fileinfo.ftLastWriteTime;
1816 entry->nFileSizeHigh = fileinfo.nFileSizeHigh;
1817 entry->nFileSizeLow = fileinfo.nFileSizeLow;
1819 if (short_name)
1820 DOSFS_ToDosDTAFormat( short_name, entry->cAlternateFileName );
1821 else
1822 DOSFS_Hash( long_name, entry->cAlternateFileName, FALSE,
1823 !(flags & DRIVE_CASE_SENSITIVE) );
1825 lstrcpynW( entry->cFileName, long_name, sizeof(entry->cFileName)/sizeof(entry->cFileName[0]) );
1826 if (!(flags & DRIVE_CASE_PRESERVING)) strlwrW( entry->cFileName );
1827 TRACE("returning %s (%s) %02lx %ld\n",
1828 debugstr_w(entry->cFileName), debugstr_w(entry->cAlternateFileName),
1829 entry->dwFileAttributes, entry->nFileSizeLow );
1830 return 1;
1832 return 0; /* End of directory */
1835 /*************************************************************************
1836 * FindFirstFileExW (KERNEL32.@)
1838 HANDLE WINAPI FindFirstFileExW(
1839 LPCWSTR lpFileName,
1840 FINDEX_INFO_LEVELS fInfoLevelId,
1841 LPVOID lpFindFileData,
1842 FINDEX_SEARCH_OPS fSearchOp,
1843 LPVOID lpSearchFilter,
1844 DWORD dwAdditionalFlags)
1846 FIND_FIRST_INFO *info;
1848 if (!lpFileName)
1850 SetLastError(ERROR_PATH_NOT_FOUND);
1851 return INVALID_HANDLE_VALUE;
1854 if ((fSearchOp != FindExSearchNameMatch) || (dwAdditionalFlags != 0))
1856 FIXME("options not implemented 0x%08x 0x%08lx\n", fSearchOp, dwAdditionalFlags );
1857 return INVALID_HANDLE_VALUE;
1860 switch(fInfoLevelId)
1862 case FindExInfoStandard:
1864 WIN32_FIND_DATAW * data = (WIN32_FIND_DATAW *) lpFindFileData;
1865 char *p;
1866 INT long_mask_len;
1868 data->dwReserved0 = data->dwReserved1 = 0x0;
1869 if (lpFileName[0] == '\\' && lpFileName[1] == '\\')
1871 ERR("UNC path name\n");
1872 if (!(info = HeapAlloc( GetProcessHeap(), 0, sizeof(FIND_FIRST_INFO)))) break;
1873 info->u.smb_dir = SMB_FindFirst(lpFileName);
1874 if(!info->u.smb_dir)
1876 HeapFree(GetProcessHeap(), 0, info);
1877 break;
1879 info->drive = -1;
1880 RtlInitializeCriticalSection( &info->cs );
1882 else
1884 DOS_FULL_NAME full_name;
1886 if (lpFileName[0] && lpFileName[1] == ':')
1888 /* don't allow root directories */
1889 if (!lpFileName[2] ||
1890 ((lpFileName[2] == '/' || lpFileName[2] == '\\') && !lpFileName[3]))
1892 SetLastError(ERROR_FILE_NOT_FOUND);
1893 return INVALID_HANDLE_VALUE;
1896 if (!DOSFS_GetFullName( lpFileName, FALSE, &full_name )) break;
1897 if (!(info = HeapAlloc( GetProcessHeap(), 0, sizeof(FIND_FIRST_INFO)))) break;
1898 RtlInitializeCriticalSection( &info->cs );
1899 info->path = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
1900 strcpy( info->path, full_name.long_name );
1902 p = strrchr( info->path, '/' );
1903 *p++ = '\0';
1904 long_mask_len = MultiByteToWideChar(CP_UNIXCP, 0, p, -1, NULL, 0);
1905 info->long_mask = HeapAlloc( GetProcessHeap(), 0, long_mask_len * sizeof(WCHAR) );
1906 MultiByteToWideChar(CP_UNIXCP, 0, p, -1, info->long_mask, long_mask_len);
1908 info->drive = full_name.drive;
1909 info->cur_pos = 0;
1911 info->u.dos_dir = DOSFS_OpenDir( info->path );
1913 if (!FindNextFileW( (HANDLE) info, data ))
1915 FindClose( (HANDLE) info );
1916 SetLastError( ERROR_FILE_NOT_FOUND );
1917 break;
1919 return (HANDLE) info;
1921 break;
1922 default:
1923 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
1925 return INVALID_HANDLE_VALUE;
1928 /*************************************************************************
1929 * FindFirstFileA (KERNEL32.@)
1931 HANDLE WINAPI FindFirstFileA(
1932 LPCSTR lpFileName,
1933 WIN32_FIND_DATAA *lpFindData )
1935 return FindFirstFileExA(lpFileName, FindExInfoStandard, lpFindData,
1936 FindExSearchNameMatch, NULL, 0);
1939 /*************************************************************************
1940 * FindFirstFileExA (KERNEL32.@)
1942 HANDLE WINAPI FindFirstFileExA(
1943 LPCSTR lpFileName,
1944 FINDEX_INFO_LEVELS fInfoLevelId,
1945 LPVOID lpFindFileData,
1946 FINDEX_SEARCH_OPS fSearchOp,
1947 LPVOID lpSearchFilter,
1948 DWORD dwAdditionalFlags)
1950 HANDLE handle;
1951 WIN32_FIND_DATAA *dataA;
1952 WIN32_FIND_DATAW dataW;
1953 UNICODE_STRING pathW;
1955 if (!lpFileName)
1957 SetLastError(ERROR_PATH_NOT_FOUND);
1958 return INVALID_HANDLE_VALUE;
1961 if (!RtlCreateUnicodeStringFromAsciiz(&pathW, lpFileName))
1963 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1964 return INVALID_HANDLE_VALUE;
1967 handle = FindFirstFileExW(pathW.Buffer, fInfoLevelId, &dataW, fSearchOp, lpSearchFilter, dwAdditionalFlags);
1968 RtlFreeUnicodeString(&pathW);
1969 if (handle == INVALID_HANDLE_VALUE) return handle;
1971 dataA = (WIN32_FIND_DATAA *) lpFindFileData;
1972 dataA->dwFileAttributes = dataW.dwFileAttributes;
1973 dataA->ftCreationTime = dataW.ftCreationTime;
1974 dataA->ftLastAccessTime = dataW.ftLastAccessTime;
1975 dataA->ftLastWriteTime = dataW.ftLastWriteTime;
1976 dataA->nFileSizeHigh = dataW.nFileSizeHigh;
1977 dataA->nFileSizeLow = dataW.nFileSizeLow;
1978 WideCharToMultiByte( CP_ACP, 0, dataW.cFileName, -1,
1979 dataA->cFileName, sizeof(dataA->cFileName), NULL, NULL );
1980 WideCharToMultiByte( CP_ACP, 0, dataW.cAlternateFileName, -1,
1981 dataA->cAlternateFileName, sizeof(dataA->cAlternateFileName), NULL, NULL );
1982 return handle;
1985 /*************************************************************************
1986 * FindFirstFileW (KERNEL32.@)
1988 HANDLE WINAPI FindFirstFileW( LPCWSTR lpFileName, WIN32_FIND_DATAW *lpFindData )
1990 return FindFirstFileExW(lpFileName, FindExInfoStandard, lpFindData,
1991 FindExSearchNameMatch, NULL, 0);
1994 /*************************************************************************
1995 * FindNextFileW (KERNEL32.@)
1997 BOOL WINAPI FindNextFileW( HANDLE handle, WIN32_FIND_DATAW *data )
1999 FIND_FIRST_INFO *info;
2000 BOOL ret = FALSE;
2001 DWORD gle = ERROR_NO_MORE_FILES;
2003 if (handle == INVALID_HANDLE_VALUE)
2005 SetLastError( ERROR_INVALID_HANDLE );
2006 return ret;
2008 info = (FIND_FIRST_INFO*) handle;
2009 RtlEnterCriticalSection( &info->cs );
2010 if (info->drive == -1)
2012 ret = SMB_FindNext( info->u.smb_dir, data );
2013 if(!ret)
2015 SMB_CloseDir( info->u.smb_dir );
2016 HeapFree( GetProcessHeap(), 0, info->path );
2018 goto done;
2020 else if (!info->path || !info->u.dos_dir)
2022 goto done;
2024 else if (!DOSFS_FindNextEx( info, data ))
2026 DOSFS_CloseDir( info->u.dos_dir ); info->u.dos_dir = NULL;
2027 HeapFree( GetProcessHeap(), 0, info->path );
2028 info->path = NULL;
2029 HeapFree( GetProcessHeap(), 0, info->long_mask );
2030 info->long_mask = NULL;
2031 goto done;
2033 ret = TRUE;
2034 done:
2035 RtlLeaveCriticalSection( &info->cs );
2036 if( !ret ) SetLastError( gle );
2037 return ret;
2041 /*************************************************************************
2042 * FindNextFileA (KERNEL32.@)
2044 BOOL WINAPI FindNextFileA( HANDLE handle, WIN32_FIND_DATAA *data )
2046 WIN32_FIND_DATAW dataW;
2047 if (!FindNextFileW( handle, &dataW )) return FALSE;
2048 data->dwFileAttributes = dataW.dwFileAttributes;
2049 data->ftCreationTime = dataW.ftCreationTime;
2050 data->ftLastAccessTime = dataW.ftLastAccessTime;
2051 data->ftLastWriteTime = dataW.ftLastWriteTime;
2052 data->nFileSizeHigh = dataW.nFileSizeHigh;
2053 data->nFileSizeLow = dataW.nFileSizeLow;
2054 WideCharToMultiByte( CP_ACP, 0, dataW.cFileName, -1,
2055 data->cFileName, sizeof(data->cFileName), NULL, NULL );
2056 WideCharToMultiByte( CP_ACP, 0, dataW.cAlternateFileName, -1,
2057 data->cAlternateFileName,
2058 sizeof(data->cAlternateFileName), NULL, NULL );
2059 return TRUE;
2062 /*************************************************************************
2063 * FindClose (KERNEL32.@)
2065 BOOL WINAPI FindClose( HANDLE handle )
2067 FIND_FIRST_INFO *info = (FIND_FIRST_INFO*) handle;
2069 if (handle == INVALID_HANDLE_VALUE) goto error;
2071 __TRY
2073 RtlEnterCriticalSection( &info->cs );
2074 if (info)
2076 if (info->u.dos_dir) DOSFS_CloseDir( info->u.dos_dir );
2077 if (info->path) HeapFree( GetProcessHeap(), 0, info->path );
2078 if (info->long_mask) HeapFree( GetProcessHeap(), 0, info->long_mask );
2081 __EXCEPT(page_fault)
2083 WARN("Illegal handle %p\n", handle);
2084 SetLastError( ERROR_INVALID_HANDLE );
2085 return FALSE;
2087 __ENDTRY
2088 if (!info) goto error;
2089 RtlLeaveCriticalSection( &info->cs );
2090 RtlDeleteCriticalSection( &info->cs );
2091 HeapFree(GetProcessHeap(), 0, info);
2092 return TRUE;
2094 error:
2095 SetLastError( ERROR_INVALID_HANDLE );
2096 return FALSE;
2099 /***********************************************************************
2100 * MulDiv (KERNEL32.@)
2101 * RETURNS
2102 * Result of multiplication and division
2103 * -1: Overflow occurred or Divisor was 0
2105 INT WINAPI MulDiv(
2106 INT nMultiplicand,
2107 INT nMultiplier,
2108 INT nDivisor)
2110 #if SIZEOF_LONG_LONG >= 8
2111 long long ret;
2113 if (!nDivisor) return -1;
2115 /* We want to deal with a positive divisor to simplify the logic. */
2116 if (nDivisor < 0)
2118 nMultiplicand = - nMultiplicand;
2119 nDivisor = -nDivisor;
2122 /* If the result is positive, we "add" to round. else, we subtract to round. */
2123 if ( ( (nMultiplicand < 0) && (nMultiplier < 0) ) ||
2124 ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
2125 ret = (((long long)nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
2126 else
2127 ret = (((long long)nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
2129 if ((ret > 2147483647) || (ret < -2147483647)) return -1;
2130 return ret;
2131 #else
2132 if (!nDivisor) return -1;
2134 /* We want to deal with a positive divisor to simplify the logic. */
2135 if (nDivisor < 0)
2137 nMultiplicand = - nMultiplicand;
2138 nDivisor = -nDivisor;
2141 /* If the result is positive, we "add" to round. else, we subtract to round. */
2142 if ( ( (nMultiplicand < 0) && (nMultiplier < 0) ) ||
2143 ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
2144 return ((nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
2146 return ((nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
2148 #endif
2152 /***********************************************************************
2153 * DosDateTimeToFileTime (KERNEL32.@)
2155 BOOL WINAPI DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft)
2157 struct tm newtm;
2158 #ifndef HAVE_TIMEGM
2159 struct tm *gtm;
2160 time_t time1, time2;
2161 #endif
2163 newtm.tm_sec = (fattime & 0x1f) * 2;
2164 newtm.tm_min = (fattime >> 5) & 0x3f;
2165 newtm.tm_hour = (fattime >> 11);
2166 newtm.tm_mday = (fatdate & 0x1f);
2167 newtm.tm_mon = ((fatdate >> 5) & 0x0f) - 1;
2168 newtm.tm_year = (fatdate >> 9) + 80;
2169 #ifdef HAVE_TIMEGM
2170 RtlSecondsSince1970ToTime( timegm(&newtm), (LARGE_INTEGER *)ft );
2171 #else
2172 time1 = mktime(&newtm);
2173 gtm = gmtime(&time1);
2174 time2 = mktime(gtm);
2175 RtlSecondsSince1970ToTime( 2*time1-time2, (LARGE_INTEGER *)ft );
2176 #endif
2177 return TRUE;
2181 /***********************************************************************
2182 * FileTimeToDosDateTime (KERNEL32.@)
2184 BOOL WINAPI FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate,
2185 LPWORD fattime )
2187 LARGE_INTEGER li;
2188 ULONG t;
2189 time_t unixtime;
2190 struct tm* tm;
2192 li.s.LowPart = ft->dwLowDateTime;
2193 li.s.HighPart = ft->dwHighDateTime;
2194 RtlTimeToSecondsSince1970( &li, &t );
2195 unixtime = t;
2196 tm = gmtime( &unixtime );
2197 if (fattime)
2198 *fattime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
2199 if (fatdate)
2200 *fatdate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
2201 + tm->tm_mday;
2202 return TRUE;
2206 /***********************************************************************
2207 * QueryDosDeviceA (KERNEL32.@)
2209 * returns array of strings terminated by \0, terminated by \0
2211 DWORD WINAPI QueryDosDeviceA(LPCSTR devname,LPSTR target,DWORD bufsize)
2213 DWORD ret = 0, retW;
2214 LPWSTR targetW = (LPWSTR)HeapAlloc(GetProcessHeap(),0,
2215 bufsize * sizeof(WCHAR));
2216 UNICODE_STRING devnameW;
2218 if(devname) RtlCreateUnicodeStringFromAsciiz(&devnameW, devname);
2219 else devnameW.Buffer = NULL;
2221 retW = QueryDosDeviceW(devnameW.Buffer, targetW, bufsize);
2223 ret = WideCharToMultiByte(CP_ACP, 0, targetW, retW, target,
2224 bufsize, NULL, NULL);
2226 RtlFreeUnicodeString(&devnameW);
2227 if (targetW) HeapFree(GetProcessHeap(),0,targetW);
2228 return ret;
2232 /***********************************************************************
2233 * QueryDosDeviceW (KERNEL32.@)
2235 * returns array of strings terminated by \0, terminated by \0
2237 * FIXME
2238 * - Win9x returns for all calls ERROR_INVALID_PARAMETER
2239 * - the returned devices for devname == NULL is far from complete
2240 * - its not checked that the returned device exist
2242 DWORD WINAPI QueryDosDeviceW(LPCWSTR devname,LPWSTR target,DWORD bufsize)
2244 const WCHAR *pDev, *pName, *pNum = NULL;
2245 int numsiz=0;
2246 DWORD ret;
2248 TRACE("(%s,...)\n", debugstr_w(devname));
2249 if (!devname) {
2250 /* return known MSDOS devices */
2251 DWORD ret = 0;
2252 int i;
2253 static const WCHAR devices[][5] = {{'A','U','X',0},
2254 {'C','O','M','1',0},
2255 {'C','O','M','2',0},
2256 {'L','P','T','1',0},
2257 {'N','U','L',0,}};
2258 for(i=0; (i< (sizeof(devices)/sizeof(devices[0]))); i++) {
2259 DWORD len = strlenW(devices[i]);
2260 if(target && (bufsize >= ret + len + 2)) {
2261 strcpyW(target+ret, devices[i]);
2262 ret += len + 1;
2263 } else {
2264 /* in this case WinXP returns 0 */
2265 FIXME("function return is wrong for WinXP!\n");
2266 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2267 break;
2270 /* append drives here */
2271 if(target && bufsize > 0) target[ret++] = 0;
2272 FIXME("Returned list is not complete\n");
2273 return ret;
2275 /* In theory all that are possible and have been defined.
2276 * Now just those below, since mirc uses it to check for special files.
2278 * (It is more complex, and supports netmounted stuff, and \\.\ stuff,
2279 * but currently we just ignore that.)
2281 if (!strcmpiW(devname, auxW)) {
2282 pDev = dosW;
2283 pName = comW;
2284 numsiz = 1;
2285 pNum = oneW;
2286 } else if (!strcmpiW(devname, nulW)) {
2287 pDev = devW;
2288 pName = nullW;
2289 } else if (!strncmpiW(devname, comW, strlenW(comW))) {
2290 pDev = devW;
2291 pName = serW;
2292 pNum = devname + strlenW(comW);
2293 for(numsiz=0; isdigitW(*(pNum+numsiz)); numsiz++);
2294 if(*(pNum + numsiz)) {
2295 SetLastError(ERROR_FILE_NOT_FOUND);
2296 return 0;
2298 } else if (!strncmpiW(devname, lptW, strlenW(lptW))) {
2299 pDev = devW;
2300 pName = parW;
2301 pNum = devname + strlenW(lptW);
2302 for(numsiz=0; isdigitW(*(pNum+numsiz)); numsiz++);
2303 if(*(pNum + numsiz)) {
2304 SetLastError(ERROR_FILE_NOT_FOUND);
2305 return 0;
2307 } else {
2308 /* This might be a DOS device we do not handle yet ... */
2309 FIXME("(%s) not detected as DOS device!\n",debugstr_w(devname));
2311 /* Win9x set the error ERROR_INVALID_PARAMETER */
2312 SetLastError(ERROR_FILE_NOT_FOUND);
2313 return 0;
2315 FIXME("device %s may not exist on this computer\n", debugstr_w(devname));
2317 ret = strlenW(pDev) + strlenW(pName) + numsiz + 2;
2318 if (ret > bufsize) ret = 0;
2319 if (target && ret) {
2320 strcpyW(target,pDev);
2321 strcatW(target,pName);
2322 if (pNum) strcatW(target,pNum);
2323 target[ret-1] = 0;
2325 return ret;