First step in using faster approach for A<->W message mapping.
[wine] / files / dos_fs.c
CommitLineData
4f8c37b4
AJ
1/*
2 * DOS file system functions
3 *
4 * Copyright 1993 Erik Bos
5 * Copyright 1996 Alexandre Julliard
0799c1a7
AJ
6 *
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.
11 *
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.
16 *
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
4f8c37b4
AJ
20 */
21
c7c217b3 22#include "config.h"
402b79a1 23#include "wine/port.h"
33929be4 24
0c126c7c 25#include <sys/types.h>
4f8c37b4
AJ
26#include <ctype.h>
27#include <dirent.h>
44ed71f5 28#include <errno.h>
13277480 29#ifdef HAVE_SYS_ERRNO_H
d30dfd24 30#include <sys/errno.h>
13277480 31#endif
9ea19e54 32#include <fcntl.h>
e37c6e18 33#include <stdarg.h>
4f8c37b4
AJ
34#include <string.h>
35#include <stdlib.h>
4f8c37b4 36#include <sys/stat.h>
9a624916 37#ifdef HAVE_SYS_IOCTL_H
9ea19e54 38#include <sys/ioctl.h>
48ac89b6 39#endif
4f8c37b4 40#include <time.h>
d016f819
PS
41#ifdef HAVE_UNISTD_H
42# include <unistd.h>
43#endif
4f8c37b4 44
f3d2a8d4
EP
45#define NONAMELESSUNION
46#define NONAMELESSSTRUCT
e37c6e18 47#include "ntstatus.h"
317af320 48#include "windef.h"
e37c6e18 49#include "winbase.h"
9ea19e54 50#include "winerror.h"
33929be4
PS
51#include "wingdi.h"
52
53#include "wine/unicode.h"
54#include "wine/winbase16.h"
4f8c37b4
AJ
55#include "drive.h"
56#include "file.h"
9c1de6de 57#include "winternl.h"
37e9503a 58#include "wine/server.h"
402b79a1 59#include "wine/exception.h"
737d4be8 60#include "excpt.h"
33929be4 61
963985b3
MM
62#include "smb.h"
63
0799c1a7 64#include "wine/debug.h"
9ea19e54 65
0799c1a7
AJ
66WINE_DEFAULT_DEBUG_CHANNEL(dosfs);
67WINE_DECLARE_DEBUG_CHANNEL(file);
b4b9fae6 68
9ea19e54 69/* Define the VFAT ioctl to get both short and long file names */
c6c09442 70/* FIXME: is it possible to get this to work on other systems? */
9ea19e54 71#ifdef linux
df2673b7
AJ
72/* We want the real kernel dirent structure, not the libc one */
73typedef struct
74{
75 long d_ino;
76 long d_off;
77 unsigned short d_reclen;
78 char d_name[256];
79} KERNEL_DIRENT;
80
0bac5e93
PG
81#define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, KERNEL_DIRENT [2] )
82
9786b058
MJM
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
87
9ea19e54
AJ
88#else /* linux */
89#undef VFAT_IOCTL_READDIR_BOTH /* just in case... */
90#endif /* linux */
4f8c37b4 91
e0deb0c6
AJ
92#define IS_OPTION_TRUE(ch) ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1')
93
4f8c37b4 94/* Chars we don't want to see in DOS file names */
7e56f684 95#define INVALID_DOS_CHARS "*?<>|\"+=,;[] \345"
4f8c37b4 96
829fe323
AJ
97static const DOS_DEVICE DOSFS_Devices[] =
98/* name, device flags (see Int 21/AX=0x4400) */
99{
d75aed2c
DT
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 }
4f8c37b4
AJ
115};
116
a98f1297
SL
117static const WCHAR devW[] = {'\\','D','e','v','i','c','e','\\',0};
118static const WCHAR dosW[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\',0};
119
120static const WCHAR auxW[] = {'A','U','X',0};
121static const WCHAR comW[] = {'C','O','M',0};
122static const WCHAR lptW[] = {'L','P','T',0};
123static const WCHAR nulW[] = {'N','U','L',0};
124
125static const WCHAR nullW[] = {'N','u','l','l',0};
126static const WCHAR parW[] = {'P','a','r','a','l','l','e','l',0};
127static const WCHAR serW[] = {'S','e','r','i','a','l',0};
128static const WCHAR oneW[] = {'1',0};
129
d75aed2c
DT
130/*
131 * Directory info for DOSFS_ReadDir
132 * contains the names of *all* the files in the directory
133 */
9ea19e54
AJ
134typedef struct
135{
d75aed2c
DT
136 int used;
137 int size;
11d09a83 138 WCHAR names[1];
9ea19e54
AJ
139} DOS_DIR;
140
85ed45e3
AJ
141/* Info structure for FindFirstFile handle */
142typedef struct
143{
d75aed2c
DT
144 char *path; /* unix path */
145 LPWSTR long_mask;
85ed45e3
AJ
146 int drive;
147 int cur_pos;
33854afb 148 CRITICAL_SECTION cs;
963985b3
MM
149 union
150 {
151 DOS_DIR *dos_dir;
152 SMB_DIR *smb_dir;
153 } u;
85ed45e3
AJ
154} FIND_FIRST_INFO;
155
156
4f46b5de
DS
157static WINE_EXCEPTION_FILTER(page_fault)
158{
159 if (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION)
160 return EXCEPTION_EXECUTE_HANDLER;
161 return EXCEPTION_CONTINUE_SEARCH;
162}
163
4f8c37b4
AJ
164
165/***********************************************************************
166 * DOSFS_ValidDOSName
167 *
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 '/'.
171 */
d75aed2c 172static int DOSFS_ValidDOSName( LPCWSTR name, int ignore_case )
4f8c37b4 173{
1e37a181 174 static const char invalid_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS;
d75aed2c 175 const WCHAR *p = name;
1e37a181 176 const char *invalid = ignore_case ? (invalid_chars + 26) : invalid_chars;
4f8c37b4
AJ
177 int len = 0;
178
179 if (*p == '.')
180 {
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));
186 }
187 while (!IS_END_OF_NAME(*p))
188 {
d75aed2c 189 if (*p < 256 && strchr( invalid, (char)*p )) return 0; /* Invalid char */
4f8c37b4
AJ
190 if (*p == '.') break; /* Start of the extension */
191 if (++len > 8) return 0; /* Name too long */
192 p++;
193 }
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))
199 {
d75aed2c 200 if (*p < 256 && strchr( invalid, (char)*p )) return 0; /* Invalid char */
4f8c37b4
AJ
201 if (*p == '.') return 0; /* Second extension not allowed */
202 if (++len > 3) return 0; /* Extension too long */
203 p++;
204 }
205 return 1;
206}
207
208
4f8c37b4
AJ
209/***********************************************************************
210 * DOSFS_ToDosFCBFormat
211 *
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 '/'.
c6c09442
AJ
215 * Return FALSE if the name is not a valid DOS name.
216 * 'buffer' must be at least 12 characters long.
4f8c37b4 217 */
d75aed2c 218BOOL DOSFS_ToDosFCBFormat( LPCWSTR name, LPWSTR buffer )
4f8c37b4
AJ
219{
220 static const char invalid_chars[] = INVALID_DOS_CHARS;
d75aed2c 221 LPCWSTR p = name;
4f8c37b4
AJ
222 int i;
223
224 /* Check for "." and ".." */
225 if (*p == '.')
226 {
227 p++;
d75aed2c
DT
228 buffer[0] = '.';
229 for(i = 1; i < 11; i++) buffer[i] = ' ';
230 buffer[11] = 0;
9ea19e54
AJ
231 if (*p == '.')
232 {
233 buffer[1] = '.';
234 p++;
235 }
c6c09442 236 return (!*p || (*p == '/') || (*p == '\\'));
4f8c37b4
AJ
237 }
238
239 for (i = 0; i < 8; i++)
240 {
241 switch(*p)
242 {
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:
d75aed2c
DT
256 if (*p < 256 && strchr( invalid_chars, (char)*p )) return FALSE;
257 buffer[i] = toupperW(*p);
4f8c37b4
AJ
258 p++;
259 break;
260 }
261 }
262
263 if (*p == '*')
264 {
265 /* Skip all chars after wildcard up to first dot */
266 while (*p && (*p != '/') && (*p != '\\') && (*p != '.')) p++;
267 }
268 else
269 {
270 /* Check if name too long */
c6c09442 271 if (*p && (*p != '/') && (*p != '\\') && (*p != '.')) return FALSE;
4f8c37b4
AJ
272 }
273 if (*p == '.') p++; /* Skip dot */
274
275 for (i = 8; i < 11; i++)
276 {
277 switch(*p)
278 {
279 case '\0':
280 case '\\':
281 case '/':
282 buffer[i] = ' ';
283 break;
284 case '.':
c6c09442 285 return FALSE; /* Second extension not allowed */
4f8c37b4
AJ
286 case '?':
287 p++;
288 /* fall through */
289 case '*':
290 buffer[i] = '?';
291 break;
292 default:
d75aed2c
DT
293 if (*p < 256 && strchr( invalid_chars, (char)*p )) return FALSE;
294 buffer[i] = toupperW(*p);
4f8c37b4
AJ
295 p++;
296 break;
297 }
298 }
299 buffer[11] = '\0';
7cc51fae
SL
300
301 /* at most 3 character of the extension are processed
9a624916 302 * is something behind this ?
7cc51fae 303 */
199aebaa 304 while (*p == '*' || *p == ' ') p++; /* skip wildcards and spaces */
7cc51fae 305 return IS_END_OF_NAME(*p);
4f8c37b4
AJ
306}
307
308
309/***********************************************************************
310 * DOSFS_ToDosDTAFormat
311 *
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 '/'.
c6c09442 315 * 'buffer' must be at least 13 characters long.
4f8c37b4 316 */
d75aed2c 317static void DOSFS_ToDosDTAFormat( LPCWSTR name, LPWSTR buffer )
4f8c37b4 318{
d75aed2c 319 LPWSTR p;
4f8c37b4 320
d75aed2c 321 memcpy( buffer, name, 8 * sizeof(WCHAR) );
566a52ad
AJ
322 p = buffer + 8;
323 while ((p > buffer) && (p[-1] == ' ')) p--;
4f8c37b4 324 *p++ = '.';
d75aed2c 325 memcpy( p, name + 8, 3 * sizeof(WCHAR) );
566a52ad
AJ
326 p += 3;
327 while (p[-1] == ' ') p--;
4f8c37b4
AJ
328 if (p[-1] == '.') p--;
329 *p = '\0';
4f8c37b4
AJ
330}
331
332
139a4b18
AJ
333/***********************************************************************
334 * DOSFS_MatchLong
335 *
336 * Check a long file name against a mask.
07291056
AM
337 *
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 -
9a624916 346 * *test1.txt* test1.txt *
07291056 347 * h?l?o*t.dat hellothisisatest.dat *
139a4b18 348 */
d75aed2c 349static int DOSFS_MatchLong( LPCWSTR mask, LPCWSTR name, int case_sensitive )
139a4b18 350{
d75aed2c
DT
351 LPCWSTR lastjoker = NULL;
352 LPCWSTR next_to_retry = NULL;
353 static const WCHAR asterisk_dot_asterisk[] = {'*','.','*',0};
07291056 354
4610c0a9
GNJ
355 TRACE("(%s, %s, %x)\n", debugstr_w(mask), debugstr_w(name), case_sensitive);
356
d75aed2c 357 if (!strcmpW( mask, asterisk_dot_asterisk )) return 1;
139a4b18
AJ
358 while (*name && *mask)
359 {
360 if (*mask == '*')
361 {
362 mask++;
363 while (*mask == '*') mask++; /* Skip consecutive '*' */
07291056
AM
364 lastjoker = mask;
365 if (!*mask) return 1; /* end of mask is all '*', so match */
366
367 /* skip to the next match after the joker(s) */
139a4b18 368 if (case_sensitive) while (*name && (*name != *mask)) name++;
d75aed2c 369 else while (*name && (toupperW(*name) != toupperW(*mask))) name++;
07291056 370
9132a78b 371 if (!*name) break;
07291056 372 next_to_retry = name;
139a4b18
AJ
373 }
374 else if (*mask != '?')
375 {
07291056 376 int mismatch = 0;
139a4b18
AJ
377 if (case_sensitive)
378 {
07291056
AM
379 if (*mask != *name) mismatch = 1;
380 }
381 else
382 {
d75aed2c 383 if (toupperW(*mask) != toupperW(*name)) mismatch = 1;
07291056
AM
384 }
385 if (!mismatch)
386 {
387 mask++;
388 name++;
389 if (*mask == '\0')
390 {
391 if (*name == '\0')
392 return 1;
393 if (lastjoker)
394 mask = lastjoker;
395 }
396 }
397 else /* mismatch ! */
398 {
399 if (lastjoker) /* we had an '*', so we can try unlimitedly */
400 {
401 mask = lastjoker;
402
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;
407 }
408 else
409 return 0; /* bad luck */
139a4b18 410 }
139a4b18 411 }
07291056
AM
412 else /* '?' */
413 {
414 mask++;
415 name++;
416 }
139a4b18 417 }
07291056
AM
418 while ((*mask == '.') || (*mask == '*'))
419 mask++; /* Ignore trailing '.' or '*' in mask */
139a4b18
AJ
420 return (!*name && !*mask);
421}
422
423
9ea19e54 424/***********************************************************************
d75aed2c
DT
425 * DOSFS_AddDirEntry
426 *
427 * Used to construct an array of filenames in DOSFS_OpenDir
9ea19e54 428 */
d75aed2c 429static BOOL DOSFS_AddDirEntry(DOS_DIR **dir, LPCWSTR name, LPCWSTR dosname)
9ea19e54 430{
11d09a83
MM
431 int extra1 = strlenW(name) + 1;
432 int extra2 = strlenW(dosname) + 1;
d75aed2c
DT
433
434 /* if we need more, at minimum double the size */
435 if( (extra1 + extra2 + (*dir)->used) > (*dir)->size)
9ea19e54 436 {
d75aed2c
DT
437 int more = (*dir)->size;
438 DOS_DIR *t;
439
440 if(more<(extra1+extra2))
441 more = extra1+extra2;
442
11d09a83
MM
443 t = HeapReAlloc(GetProcessHeap(), 0, *dir, sizeof(**dir) +
444 ((*dir)->size + more)*sizeof(WCHAR) );
d75aed2c
DT
445 if(!t)
446 {
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;
451 }
452 (*dir) = t;
453 (*dir)->size += more;
9ea19e54
AJ
454 }
455
d75aed2c 456 /* at this point, the dir structure is big enough to hold these names */
11d09a83 457 strcpyW(&(*dir)->names[(*dir)->used], name);
d75aed2c 458 (*dir)->used += extra1;
11d09a83 459 strcpyW(&(*dir)->names[(*dir)->used], dosname);
d75aed2c
DT
460 (*dir)->used += extra2;
461
462 return TRUE;
463}
a0d77315 464
d75aed2c
DT
465
466/***********************************************************************
467 * DOSFS_OpenDir_VFAT
468 */
49a0224f 469static BOOL DOSFS_OpenDir_VFAT(DOS_DIR **dir, const char *unix_path)
d75aed2c 470{
9ea19e54 471#ifdef VFAT_IOCTL_READDIR_BOTH
d75aed2c 472 KERNEL_DIRENT de[2];
9786b058 473 int fd = open( unix_path, O_RDONLY|O_DIRECTORY );
d75aed2c 474 BOOL r = TRUE;
9ea19e54
AJ
475
476 /* Check if the VFAT ioctl is supported on this directory */
477
d75aed2c
DT
478 if ( fd<0 )
479 return FALSE;
480
481 while (1)
9ea19e54 482 {
d75aed2c
DT
483 WCHAR long_name[MAX_PATH];
484 WCHAR short_name[12];
485
486 r = (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de ) != -1);
487 if(!r)
488 break;
489 if (!de[0].d_reclen)
490 break;
49a0224f 491 MultiByteToWideChar(CP_UNIXCP, 0, de[0].d_name, -1, long_name, MAX_PATH);
d75aed2c
DT
492 if (!DOSFS_ToDosFCBFormat( long_name, short_name ))
493 short_name[0] = '\0';
494 if (de[1].d_name[0])
49a0224f 495 MultiByteToWideChar(CP_UNIXCP, 0, de[1].d_name, -1, long_name, MAX_PATH);
9ea19e54 496 else
49a0224f 497 MultiByteToWideChar(CP_UNIXCP, 0, de[0].d_name, -1, long_name, MAX_PATH);
d75aed2c
DT
498 r = DOSFS_AddDirEntry(dir, long_name, short_name );
499 if(!r)
500 break;
9ea19e54 501 }
d75aed2c
DT
502 if(r)
503 {
504 static const WCHAR empty_strW[] = { 0 };
505 DOSFS_AddDirEntry(dir, empty_strW, empty_strW);
506 }
507 close(fd);
508 return r;
509#else
510 return FALSE;
9ea19e54 511#endif /* VFAT_IOCTL_READDIR_BOTH */
d75aed2c
DT
512}
513
514
515/***********************************************************************
516 * DOSFS_OpenDir_Normal
517 *
518 * Now use the standard opendir/readdir interface
519 */
49a0224f 520static BOOL DOSFS_OpenDir_Normal( DOS_DIR **dir, const char *unix_path )
d75aed2c
DT
521{
522 DIR *unixdir = opendir( unix_path );
523 BOOL r = TRUE;
524 static const WCHAR empty_strW[] = { 0 };
525
526 if(!unixdir)
527 return FALSE;
528 while(1)
529 {
530 WCHAR long_name[MAX_PATH];
531 struct dirent *de = readdir(unixdir);
532
533 if(!de)
534 break;
49a0224f 535 MultiByteToWideChar(CP_UNIXCP, 0, de->d_name, -1, long_name, MAX_PATH);
d75aed2c
DT
536 r = DOSFS_AddDirEntry(dir, long_name, empty_strW);
537 if(!r)
538 break;
539 }
540 if(r)
541 DOSFS_AddDirEntry(dir, empty_strW, empty_strW);
542 closedir(unixdir);
543 return r;
544}
545
546/***********************************************************************
547 * DOSFS_OpenDir
548 */
49a0224f 549static DOS_DIR *DOSFS_OpenDir( const char *unix_path )
d75aed2c
DT
550{
551 const int init_size = 0x100;
11d09a83 552 DOS_DIR *dir = HeapAlloc( GetProcessHeap(), 0, sizeof(*dir) + init_size*sizeof (WCHAR));
d75aed2c
DT
553 BOOL r;
554
555 TRACE("%s\n",debugstr_a(unix_path));
556
557 if (!dir)
558 {
559 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
560 return NULL;
561 }
562 dir->used = 0;
563 dir->size = init_size;
564
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 = "/";
568
49a0224f 569 r = DOSFS_OpenDir_VFAT( &dir, unix_path);
9ea19e54 570
d75aed2c 571 if(!r)
49a0224f 572 r = DOSFS_OpenDir_Normal( &dir, unix_path);
9ea19e54 573
d75aed2c 574 if(!r)
9ea19e54 575 {
d75aed2c 576 HeapFree(GetProcessHeap(), 0, dir);
9ea19e54
AJ
577 return NULL;
578 }
d75aed2c
DT
579 dir->used = 0;
580
9ea19e54
AJ
581 return dir;
582}
583
584
585/***********************************************************************
586 * DOSFS_CloseDir
587 */
588static void DOSFS_CloseDir( DOS_DIR *dir )
589{
90476d6b 590 HeapFree( GetProcessHeap(), 0, dir );
9ea19e54
AJ
591}
592
593
594/***********************************************************************
595 * DOSFS_ReadDir
596 */
d75aed2c
DT
597static BOOL DOSFS_ReadDir( DOS_DIR *dir, LPCWSTR *long_name,
598 LPCWSTR *short_name )
9ea19e54 599{
d75aed2c 600 LPCWSTR sn, ln;
9ea19e54 601
d75aed2c
DT
602 if (!dir)
603 return FALSE;
604
605 /* the long pathname is first */
11d09a83 606 ln = &dir->names[dir->used];
d75aed2c
DT
607 if(ln[0])
608 *long_name = ln;
609 else
610 return FALSE;
11d09a83 611 dir->used += (strlenW(ln) + 1);
d75aed2c
DT
612
613 /* followed by the short path name */
11d09a83 614 sn = &dir->names[dir->used];
d75aed2c
DT
615 if(sn[0])
616 *short_name = sn;
617 else
618 *short_name = NULL;
11d09a83 619 dir->used += (strlenW(sn) + 1);
4610c0a9 620
9ea19e54
AJ
621 return TRUE;
622}
623
624
4f8c37b4
AJ
625/***********************************************************************
626 * DOSFS_Hash
627 *
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 '/'.
c6c09442 632 * 'buffer' must be at least 13 characters long.
4f8c37b4 633 */
d75aed2c 634static void DOSFS_Hash( LPCWSTR name, LPWSTR buffer, BOOL dir_format,
a3960292 635 BOOL ignore_case )
4f8c37b4
AJ
636{
637 static const char invalid_chars[] = INVALID_DOS_CHARS "~.";
638 static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
639
d75aed2c
DT
640 LPCWSTR p, ext;
641 LPWSTR dst;
4f8c37b4
AJ
642 unsigned short hash;
643 int i;
644
d75aed2c
DT
645 if (dir_format)
646 {
647 for(i = 0; i < 11; i++) buffer[i] = ' ';
648 buffer[11] = 0;
649 }
4f8c37b4 650
1e37a181 651 if (DOSFS_ValidDOSName( name, ignore_case ))
4f8c37b4
AJ
652 {
653 /* Check for '.' and '..' */
654 if (*name == '.')
655 {
656 buffer[0] = '.';
657 if (!dir_format) buffer[1] = buffer[2] = '\0';
658 if (name[1] == '.') buffer[1] = '.';
c6c09442 659 return;
4f8c37b4
AJ
660 }
661
662 /* Simply copy the name, converting to uppercase */
663
664 for (dst = buffer; !IS_END_OF_NAME(*name) && (*name != '.'); name++)
d75aed2c 665 *dst++ = toupperW(*name);
4f8c37b4
AJ
666 if (*name == '.')
667 {
668 if (dir_format) dst = buffer + 8;
669 else *dst++ = '.';
670 for (name++; !IS_END_OF_NAME(*name); name++)
d75aed2c 671 *dst++ = toupperW(*name);
4f8c37b4
AJ
672 }
673 if (!dir_format) *dst = '\0';
c6c09442
AJ
674 return;
675 }
676
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)
681 {
682 for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
d75aed2c
DT
683 hash = (hash<<3) ^ (hash>>5) ^ tolowerW(*p) ^ (tolowerW(p[1]) << 8);
684 hash = (hash<<3) ^ (hash>>5) ^ tolowerW(*p); /* Last character */
4f8c37b4
AJ
685 }
686 else
687 {
c6c09442
AJ
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 */
691 }
1e37a181 692
c6c09442
AJ
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 */
4f8c37b4 698
c6c09442
AJ
699 /* Copy first 4 chars, replacing invalid chars with '_' */
700 for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
701 {
702 if (IS_END_OF_NAME(*p) || (p == ext)) break;
d75aed2c 703 *dst++ = (*p < 256 && strchr( invalid_chars, (char)*p )) ? '_' : toupperW(*p);
c6c09442
AJ
704 }
705 /* Pad to 5 chars with '~' */
706 while (i-- >= 0) *dst++ = '~';
4f8c37b4 707
c6c09442
AJ
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];
4f8c37b4 712
c6c09442
AJ
713 /* Copy the first 3 chars of the extension (if any) */
714 if (ext)
715 {
716 if (!dir_format) *dst++ = '.';
717 for (i = 3, ext++; (i > 0) && !IS_END_OF_NAME(*ext); i--, ext++)
d75aed2c 718 *dst++ = (*ext < 256 && strchr( invalid_chars, (char)*ext )) ? '_' : toupperW(*ext);
4f8c37b4 719 }
c6c09442 720 if (!dir_format) *dst = '\0';
4f8c37b4
AJ
721}
722
723
724/***********************************************************************
725 * DOSFS_FindUnixName
726 *
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 '/'.
c6c09442
AJ
730 * Return TRUE if OK, FALSE if no file name matches.
731 *
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.
4f8c37b4 735 */
d75aed2c
DT
736BOOL DOSFS_FindUnixName( const DOS_FULL_NAME *path, LPCWSTR name, char *long_buf,
737 INT long_len, LPWSTR short_buf, BOOL ignore_case)
4f8c37b4 738{
9ea19e54 739 DOS_DIR *dir;
d75aed2c
DT
740 LPCWSTR long_name, short_name;
741 WCHAR dos_name[12], tmp_buf[13];
a3960292 742 BOOL ret;
4f8c37b4 743
d75aed2c
DT
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 );
bb9e66e2
DH
747 /* Ignore trailing dots and spaces */
748 while (len > 1 && (name[len-1] == '.' || name[len-1] == ' ')) len--;
c6c09442 749 if (long_len < len + 1) return FALSE;
4f8c37b4 750
d75aed2c 751 TRACE("%s,%s\n", path->long_name, debugstr_w(name) );
4f8c37b4 752
c6c09442 753 if (!DOSFS_ToDosFCBFormat( name, dos_name )) dos_name[0] = '\0';
9ea19e54 754
49a0224f 755 if (!(dir = DOSFS_OpenDir( path->long_name )))
4f8c37b4 756 {
dd03cc19 757 WARN("(%s,%s): can't open dir: %s\n",
d75aed2c 758 path->long_name, debugstr_w(name), strerror(errno) );
c6c09442 759 return FALSE;
4f8c37b4 760 }
9ea19e54
AJ
761
762 while ((ret = DOSFS_ReadDir( dir, &long_name, &short_name )))
4f8c37b4
AJ
763 {
764 /* Check against Unix name */
d75aed2c 765 if (len == strlenW(long_name))
1e37a181 766 {
c6c09442 767 if (!ignore_case)
1e37a181 768 {
d75aed2c 769 if (!strncmpW( long_name, name, len )) break;
1e37a181
AJ
770 }
771 else
772 {
d75aed2c 773 if (!strncmpiW( long_name, name, len )) break;
1e37a181
AJ
774 }
775 }
9ea19e54 776 if (dos_name[0])
4f8c37b4
AJ
777 {
778 /* Check against hashed DOS name */
9ea19e54 779 if (!short_name)
c6c09442
AJ
780 {
781 DOSFS_Hash( long_name, tmp_buf, TRUE, ignore_case );
782 short_name = tmp_buf;
783 }
d75aed2c 784 if (!strcmpW( dos_name, short_name )) break;
4f8c37b4
AJ
785 }
786 }
c6c09442
AJ
787 if (ret)
788 {
49a0224f 789 if (long_buf) WideCharToMultiByte(CP_UNIXCP, 0, long_name, -1, long_buf, long_len, NULL, NULL);
c6c09442
AJ
790 if (short_buf)
791 {
792 if (short_name)
793 DOSFS_ToDosDTAFormat( short_name, short_buf );
794 else
795 DOSFS_Hash( long_name, short_buf, FALSE, ignore_case );
796 }
d75aed2c
DT
797 TRACE("(%s,%s) -> %s (%s)\n", path->long_name, debugstr_w(name),
798 debugstr_w(long_name), short_buf ? debugstr_w(short_buf) : "***");
c6c09442
AJ
799 }
800 else
d75aed2c 801 WARN("%s not found in '%s'\n", debugstr_w(name), path->long_name);
9ea19e54
AJ
802 DOSFS_CloseDir( dir );
803 return ret;
4f8c37b4
AJ
804}
805
806
807/***********************************************************************
829fe323 808 * DOSFS_GetDevice
4f8c37b4 809 *
829fe323 810 * Check if a DOS file name represents a DOS device and return the device.
a11d7b1a 811 */
d75aed2c 812const DOS_DEVICE *DOSFS_GetDevice( LPCWSTR name )
a11d7b1a 813{
267ca682 814 unsigned int i;
d75aed2c 815 const WCHAR *p;
a11d7b1a 816
11d09a83 817 if (!name) return NULL; /* if wine_server_handle_to_fd was used */
a11d7b1a 818 if (name[0] && (name[1] == ':')) name += 2;
d75aed2c
DT
819 if ((p = strrchrW( name, '/' ))) name = p + 1;
820 if ((p = strrchrW( name, '\\' ))) name = p + 1;
a11d7b1a
AJ
821 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
822 {
d75aed2c
DT
823 const WCHAR *dev = DOSFS_Devices[i].name;
824 if (!strncmpiW( dev, name, strlenW(dev) ))
a11d7b1a 825 {
d75aed2c 826 p = name + strlenW( dev );
e3178f9f 827 if (!*p || (*p == '.') || (*p == ':')) return &DOSFS_Devices[i];
a11d7b1a
AJ
828 }
829 }
829fe323 830 return NULL;
a11d7b1a
AJ
831}
832
62a8b433
AJ
833
834/***********************************************************************
835 * DOSFS_GetDeviceByHandle
836 */
267ca682 837const DOS_DEVICE *DOSFS_GetDeviceByHandle( HANDLE hFile )
62a8b433 838{
92643003 839 const DOS_DEVICE *ret = NULL;
cf27a7fa 840 SERVER_START_REQ( get_device_id )
62a8b433 841 {
92643003 842 req->handle = hFile;
cf27a7fa 843 if (!wine_server_call( req ))
92643003 844 {
cf27a7fa
AJ
845 if ((reply->id >= 0) &&
846 (reply->id < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0])))
847 ret = &DOSFS_Devices[reply->id];
92643003 848 }
62a8b433 849 }
92643003
AJ
850 SERVER_END_REQ;
851 return ret;
62a8b433
AJ
852}
853
854
11776c1f
MM
855/**************************************************************************
856 * DOSFS_CreateCommPort
857 */
d75aed2c 858static HANDLE DOSFS_CreateCommPort(LPCWSTR name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa)
11776c1f 859{
8081e5a1 860 HANDLE ret;
e0deb0c6
AJ
861 HKEY hkey;
862 DWORD dummy;
863 OBJECT_ATTRIBUTES attr;
864 UNICODE_STRING nameW;
865 WCHAR *devnameW;
866 char tmp[128];
11776c1f 867 char devname[40];
e0deb0c6
AJ
868
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};
11776c1f 874
d75aed2c 875 TRACE_(file)("%s %lx %lx\n", debugstr_w(name), access, attributes);
11776c1f 876
e0deb0c6
AJ
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 );
884
885 if (NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr )) return 0;
11776c1f 886
e0deb0c6
AJ
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;
892
893 NtClose( hkey );
894
895 if (!devnameW) return 0;
d75aed2c
DT
896 WideCharToMultiByte(CP_ACP, 0, devnameW, -1, devname, sizeof(devname), NULL, NULL);
897
898 TRACE("opening %s as %s\n", devname, debugstr_w(name));
11776c1f 899
9caa71ee 900 SERVER_START_REQ( create_serial )
57f05e19 901 {
57f05e19 902 req->access = access;
3bbeb72d 903 req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
568c67e1 904 req->attributes = attributes;
57f05e19 905 req->sharing = FILE_SHARE_READ|FILE_SHARE_WRITE;
9caa71ee 906 wine_server_add_data( req, devname, strlen(devname) );
57f05e19 907 SetLastError(0);
9caa71ee
AJ
908 wine_server_call_err( req );
909 ret = reply->handle;
57f05e19 910 }
9caa71ee 911 SERVER_END_REQ;
57f05e19 912
0a788579 913 if(!ret)
e15badb4 914 ERR("Couldn't open device '%s' ! (check permissions)\n",devname);
0a788579 915 else
ed800c69 916 TRACE("return %p\n", ret );
57f05e19 917 return ret;
11776c1f
MM
918}
919
a11d7b1a
AJ
920/***********************************************************************
921 * DOSFS_OpenDevice
922 *
923 * Open a DOS device. This might not map 1:1 into the UNIX device concept.
8081e5a1 924 * Returns 0 on failure.
4f8c37b4 925 */
d75aed2c 926HANDLE DOSFS_OpenDevice( LPCWSTR name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa )
4f8c37b4 927{
267ca682 928 unsigned int i;
d75aed2c 929 const WCHAR *p;
8081e5a1 930 HANDLE handle;
4f8c37b4 931
0c126c7c 932 if (name[0] && (name[1] == ':')) name += 2;
d75aed2c
DT
933 if ((p = strrchrW( name, '/' ))) name = p + 1;
934 if ((p = strrchrW( name, '\\' ))) name = p + 1;
4f8c37b4
AJ
935 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
936 {
d75aed2c
DT
937 const WCHAR *dev = DOSFS_Devices[i].name;
938 if (!strncmpiW( dev, name, strlenW(dev) ))
4f8c37b4 939 {
d75aed2c 940 p = name + strlenW( dev );
e3178f9f 941 if (!*p || (*p == '.') || (*p == ':')) {
d75aed2c
DT
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};
a11d7b1a 947 /* got it */
d75aed2c 948 if (!strcmpiW(DOSFS_Devices[i].name, nulW))
0562539d 949 return FILE_CreateFile( "/dev/null", access,
3bbeb72d 950 FILE_SHARE_READ|FILE_SHARE_WRITE, sa,
708a846a 951 OPEN_EXISTING, 0, 0, TRUE, DRIVE_UNKNOWN );
d75aed2c 952 if (!strcmpiW(DOSFS_Devices[i].name, conW)) {
8081e5a1 953 HANDLE to_dup;
0562539d
AJ
954 switch (access & (GENERIC_READ|GENERIC_WRITE)) {
955 case GENERIC_READ:
a0d77315 956 to_dup = GetStdHandle( STD_INPUT_HANDLE );
a11d7b1a 957 break;
0562539d 958 case GENERIC_WRITE:
a0d77315 959 to_dup = GetStdHandle( STD_OUTPUT_HANDLE );
a11d7b1a
AJ
960 break;
961 default:
dd03cc19 962 FIXME("can't open CON read/write\n");
8081e5a1 963 return 0;
a11d7b1a 964 }
a0d77315 965 if (!DuplicateHandle( GetCurrentProcess(), to_dup, GetCurrentProcess(),
9a624916
VB
966 &handle, 0,
967 sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle,
3bbeb72d 968 DUPLICATE_SAME_ACCESS ))
8081e5a1 969 handle = 0;
a0d77315 970 return handle;
a11d7b1a 971 }
d75aed2c
DT
972 if (!strcmpiW(DOSFS_Devices[i].name, scsimgrW) ||
973 !strcmpiW(DOSFS_Devices[i].name, hpscanW) ||
974 !strcmpiW(DOSFS_Devices[i].name, emmxxxx0W))
fbe63adc 975 {
3bbeb72d 976 return FILE_CreateDevice( i, access, sa );
f90efa9c 977 }
44b5bf59 978
3bbeb72d 979 if( (handle=DOSFS_CreateCommPort(DOSFS_Devices[i].name,access,attributes,sa)) )
44b5bf59 980 return handle;
d75aed2c 981 FIXME("device open %s not supported (yet)\n", debugstr_w(DOSFS_Devices[i].name));
8081e5a1 982 return 0;
a11d7b1a 983 }
4f8c37b4
AJ
984 }
985 }
8081e5a1 986 return 0;
4f8c37b4
AJ
987}
988
a11d7b1a 989
4f8c37b4 990/***********************************************************************
9ea19e54 991 * DOSFS_GetPathDrive
4f8c37b4 992 *
9ea19e54 993 * Get the drive specified by a given path name (DOS or Unix format).
4f8c37b4 994 */
d75aed2c 995static int DOSFS_GetPathDrive( LPCWSTR *name )
4f8c37b4 996{
9ea19e54 997 int drive;
d75aed2c 998 LPCWSTR p = *name;
4f8c37b4 999
9ea19e54 1000 if (*p && (p[1] == ':'))
4f8c37b4 1001 {
d75aed2c 1002 drive = toupperW(*p) - 'A';
9ea19e54 1003 *name += 2;
4f8c37b4 1004 }
9ea19e54 1005 else if (*p == '/') /* Absolute Unix path? */
4f8c37b4 1006 {
d75aed2c 1007 if ((drive = DRIVE_FindDriveRootW( name )) == -1)
4f8c37b4 1008 {
d75aed2c 1009 MESSAGE("Warning: %s not accessible from a configured DOS drive\n", debugstr_w(*name) );
4f8c37b4 1010 /* Assume it really was a DOS name */
9a624916 1011 drive = DRIVE_GetCurrentDrive();
4f8c37b4
AJ
1012 }
1013 }
1014 else drive = DRIVE_GetCurrentDrive();
1015
1016 if (!DRIVE_IsValid(drive))
1017 {
4ff2a27c 1018 SetLastError( ERROR_INVALID_DRIVE );
9ea19e54 1019 return -1;
4f8c37b4 1020 }
9ea19e54
AJ
1021 return drive;
1022}
1023
1024
1025/***********************************************************************
c6c09442 1026 * DOSFS_GetFullName
9ea19e54 1027 *
c6c09442
AJ
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
9ea19e54 1031 * component is only checked if 'check_last' is non-zero.
c6c09442
AJ
1032 * The buffers pointed to by 'long_buf' and 'short_buf' must be
1033 * at least MAX_PATHNAME_LEN long.
9ea19e54 1034 */
d75aed2c 1035BOOL DOSFS_GetFullName( LPCWSTR name, BOOL check_last, DOS_FULL_NAME *full )
9ea19e54 1036{
a3960292 1037 BOOL found;
49a0224f 1038 UINT flags;
d75aed2c
DT
1039 char *p_l, *root;
1040 LPWSTR p_s;
1041 static const WCHAR driveA_rootW[] = {'A',':','\\',0};
1042 static const WCHAR dos_rootW[] = {'\\',0};
c6c09442 1043
d75aed2c 1044 TRACE("%s (last=%d)\n", debugstr_w(name), check_last );
c6c09442 1045
d52e1c4b
GP
1046 if ((!*name) || (*name=='\n'))
1047 { /* error code for Win98 */
1048 SetLastError(ERROR_BAD_PATHNAME);
1049 return FALSE;
1050 }
1051
c6c09442
AJ
1052 if ((full->drive = DOSFS_GetPathDrive( &name )) == -1) return FALSE;
1053 flags = DRIVE_GetFlags( full->drive );
9ea19e54 1054
a3960292 1055 lstrcpynA( full->long_name, DRIVE_GetRoot( full->drive ),
c6c09442
AJ
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 */
9ea19e54 1059
d75aed2c 1060 strcpyW( full->short_name, driveA_rootW );
c6c09442 1061 full->short_name[0] += full->drive;
4f8c37b4 1062
c6c09442 1063 if ((*name == '\\') || (*name == '/')) /* Absolute path */
4f8c37b4
AJ
1064 {
1065 while ((*name == '\\') || (*name == '/')) name++;
1066 }
dcf0beac 1067 else /* Relative path */
4f8c37b4 1068 {
a3960292 1069 lstrcpynA( root + 1, DRIVE_GetUnixCwd( full->drive ),
c6c09442 1070 sizeof(full->long_name) - (root - full->long_name) - 1 );
4f8c37b4 1071 if (root[1]) *root = '/';
d75aed2c
DT
1072 lstrcpynW( full->short_name + 3, DRIVE_GetDosCwd( full->drive ),
1073 sizeof(full->short_name)/sizeof(full->short_name[0]) - 3 );
4f8c37b4
AJ
1074 }
1075
c6c09442
AJ
1076 p_l = full->long_name[1] ? full->long_name + strlen(full->long_name)
1077 : full->long_name;
d75aed2c 1078 p_s = full->short_name[3] ? full->short_name + strlenW(full->short_name)
c6c09442 1079 : full->short_name + 2;
9ea19e54 1080 found = TRUE;
c6c09442 1081
4f8c37b4
AJ
1082 while (*name && found)
1083 {
c6c09442
AJ
1084 /* Check for '.' and '..' */
1085
1086 if (*name == '.')
4f8c37b4 1087 {
c6c09442
AJ
1088 if (IS_END_OF_NAME(name[1]))
1089 {
1090 name++;
1091 while ((*name == '\\') || (*name == '/')) name++;
1092 continue;
1093 }
1094 else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
1095 {
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;
1102 }
4f8c37b4 1103 }
c6c09442
AJ
1104
1105 /* Make sure buffers are large enough */
1106
d75aed2c 1107 if ((p_s >= full->short_name + sizeof(full->short_name)/sizeof(full->short_name[0]) - 14) ||
c6c09442 1108 (p_l >= full->long_name + sizeof(full->long_name) - 1))
4f8c37b4 1109 {
4ff2a27c 1110 SetLastError( ERROR_PATH_NOT_FOUND );
c6c09442 1111 return FALSE;
4f8c37b4 1112 }
c6c09442
AJ
1113
1114 /* Get the long and short name matching the file name */
1115
d75aed2c 1116 if ((found = DOSFS_FindUnixName( full, name, p_l + 1,
c6c09442
AJ
1117 sizeof(full->long_name) - (p_l - full->long_name) - 1,
1118 p_s + 1, !(flags & DRIVE_CASE_SENSITIVE) )))
4f8c37b4 1119 {
c6c09442
AJ
1120 *p_l++ = '/';
1121 p_l += strlen(p_l);
1122 *p_s++ = '\\';
d75aed2c 1123 p_s += strlenW(p_s);
4f8c37b4
AJ
1124 while (!IS_END_OF_NAME(*name)) name++;
1125 }
7e56f684 1126 else if (!check_last)
4f8c37b4 1127 {
c6c09442
AJ
1128 *p_l++ = '/';
1129 *p_s++ = '\\';
1130 while (!IS_END_OF_NAME(*name) &&
d75aed2c 1131 (p_s < full->short_name + sizeof(full->short_name)/sizeof(full->short_name[0]) - 1) &&
c6c09442
AJ
1132 (p_l < full->long_name + sizeof(full->long_name) - 1))
1133 {
d75aed2c
DT
1134 WCHAR wch;
1135 *p_s++ = tolowerW(*name);
829fe323
AJ
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. */
d75aed2c
DT
1139 if (flags & DRIVE_CASE_SENSITIVE) wch = tolowerW(*name);
1140 else wch = *name;
49a0224f 1141 p_l += WideCharToMultiByte(CP_UNIXCP, 0, &wch, 1, p_l, 2, NULL, NULL);
c6c09442
AJ
1142 name++;
1143 }
bb9e66e2
DH
1144 /* Ignore trailing dots and spaces */
1145 while(p_l[-1] == '.' || p_l[-1] == ' ') {
1146 --p_l;
1147 --p_s;
1148 }
d75aed2c
DT
1149 *p_l = '\0';
1150 *p_s = '\0';
4f8c37b4
AJ
1151 }
1152 while ((*name == '\\') || (*name == '/')) name++;
1153 }
c6c09442 1154
4f8c37b4
AJ
1155 if (!found)
1156 {
2772a67c 1157 if (check_last)
4f8c37b4 1158 {
4ff2a27c 1159 SetLastError( ERROR_FILE_NOT_FOUND );
c6c09442 1160 return FALSE;
4f8c37b4 1161 }
2772a67c 1162 if (*name) /* Not last */
4f8c37b4 1163 {
4ff2a27c 1164 SetLastError( ERROR_PATH_NOT_FOUND );
c6c09442 1165 return FALSE;
4f8c37b4
AJ
1166 }
1167 }
c6c09442 1168 if (!full->long_name[0]) strcpy( full->long_name, "/" );
d75aed2c
DT
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) );
c6c09442 1171 return TRUE;
4f8c37b4
AJ
1172}
1173
1174
3a0f8b79 1175/***********************************************************************
044855c6 1176 * wine_get_unix_file_name (KERNEL32.@) Not a Windows API
3a0f8b79
AJ
1177 *
1178 * Return the full Unix file name for a given path.
1179 */
5bf3a266 1180BOOL WINAPI wine_get_unix_file_name( LPCWSTR dosW, LPSTR buffer, DWORD len )
3a0f8b79
AJ
1181{
1182 BOOL ret;
1183 DOS_FULL_NAME path;
d75aed2c 1184
d75aed2c
DT
1185 ret = DOSFS_GetFullName( dosW, FALSE, &path );
1186 if (ret && len)
1187 {
1188 strncpy( buffer, path.long_name, len );
1189 buffer[len - 1] = 0; /* ensure 0 termination */
1190 }
3a0f8b79
AJ
1191 return ret;
1192}
1193
1194
e0deb0c6
AJ
1195/***********************************************************************
1196 * get_show_dir_symlinks_option
1197 */
1198static BOOL get_show_dir_symlinks_option(void)
1199{
1200 static const WCHAR WineW[] = {'M','a','c','h','i','n','e','\\',
1201 'S','o','f','t','w','a','r','e','\\',
1202 'W','i','n','e','\\','W','i','n','e','\\',
1203 'C','o','n','f','i','g','\\','W','i','n','e',0};
1204 static const WCHAR ShowDirSymlinksW[] = {'S','h','o','w','D','i','r','S','y','m','l','i','n','k','s',0};
1205
1206 char tmp[80];
1207 HKEY hkey;
1208 DWORD dummy;
1209 OBJECT_ATTRIBUTES attr;
1210 UNICODE_STRING nameW;
1211 BOOL ret = FALSE;
1212
1213 attr.Length = sizeof(attr);
1214 attr.RootDirectory = 0;
1215 attr.ObjectName = &nameW;
1216 attr.Attributes = 0;
1217 attr.SecurityDescriptor = NULL;
1218 attr.SecurityQualityOfService = NULL;
1219 RtlInitUnicodeString( &nameW, WineW );
1220
1221 if (!NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr ))
1222 {
1223 RtlInitUnicodeString( &nameW, ShowDirSymlinksW );
1224 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
1225 {
1226 WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
1227 ret = IS_OPTION_TRUE( str[0] );
1228 }
1229 NtClose( hkey );
1230 }
1231 return ret;
1232}
1233
1234
4f8c37b4 1235/***********************************************************************
85ed45e3 1236 * DOSFS_FindNextEx
4f8c37b4 1237 */
d75aed2c 1238static int DOSFS_FindNextEx( FIND_FIRST_INFO *info, WIN32_FIND_DATAW *entry )
4f8c37b4 1239{
a3960292 1240 UINT flags = DRIVE_GetFlags( info->drive );
85ed45e3
AJ
1241 char *p, buffer[MAX_PATHNAME_LEN];
1242 const char *drive_path;
1243 int drive_root;
d75aed2c 1244 LPCWSTR long_name, short_name;
85ed45e3 1245 BY_HANDLE_FILE_INFORMATION fileinfo;
64e047fe 1246 BOOL is_symlink;
1e37a181 1247
85ed45e3
AJ
1248 drive_path = info->path + strlen(DRIVE_GetRoot( info->drive ));
1249 while ((*drive_path == '/') || (*drive_path == '\\')) drive_path++;
1250 drive_root = !*drive_path;
1251
a3960292 1252 lstrcpynA( buffer, info->path, sizeof(buffer) - 1 );
4f8c37b4
AJ
1253 strcat( buffer, "/" );
1254 p = buffer + strlen(buffer);
4f8c37b4 1255
963985b3 1256 while (DOSFS_ReadDir( info->u.dos_dir, &long_name, &short_name ))
4f8c37b4 1257 {
85ed45e3 1258 info->cur_pos++;
139a4b18 1259
0c126c7c 1260 /* Don't return '.' and '..' in the root of the drive */
9ea19e54
AJ
1261 if (drive_root && (long_name[0] == '.') &&
1262 (!long_name[1] || ((long_name[1] == '.') && !long_name[2])))
1263 continue;
4f8c37b4 1264
139a4b18
AJ
1265 /* Check the long mask */
1266
4610c0a9 1267 if (info->long_mask && *info->long_mask)
139a4b18 1268 {
85ed45e3 1269 if (!DOSFS_MatchLong( info->long_mask, long_name,
139a4b18
AJ
1270 flags & DRIVE_CASE_SENSITIVE )) continue;
1271 }
1272
139a4b18 1273 /* Check the file attributes */
49a0224f 1274 WideCharToMultiByte(CP_UNIXCP, 0, long_name, -1,
d75aed2c 1275 p, sizeof(buffer) - (int)(p - buffer), NULL, NULL);
64e047fe 1276 if (!FILE_Stat( buffer, &fileinfo, &is_symlink ))
4f8c37b4 1277 {
dd03cc19 1278 WARN("can't stat %s\n", buffer);
4f8c37b4
AJ
1279 continue;
1280 }
64e047fe 1281 if (is_symlink && (fileinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
220312e9
AM
1282 {
1283 static int show_dir_symlinks = -1;
1284 if (show_dir_symlinks == -1)
e0deb0c6 1285 show_dir_symlinks = get_show_dir_symlinks_option();
220312e9
AM
1286 if (!show_dir_symlinks) continue;
1287 }
1288
139a4b18
AJ
1289 /* We now have a matching entry; fill the result and return */
1290
85ed45e3
AJ
1291 entry->dwFileAttributes = fileinfo.dwFileAttributes;
1292 entry->ftCreationTime = fileinfo.ftCreationTime;
1293 entry->ftLastAccessTime = fileinfo.ftLastAccessTime;
1294 entry->ftLastWriteTime = fileinfo.ftLastWriteTime;
1295 entry->nFileSizeHigh = fileinfo.nFileSizeHigh;
1296 entry->nFileSizeLow = fileinfo.nFileSizeLow;
c6c09442
AJ
1297
1298 if (short_name)
1299 DOSFS_ToDosDTAFormat( short_name, entry->cAlternateFileName );
1300 else
1301 DOSFS_Hash( long_name, entry->cAlternateFileName, FALSE,
1302 !(flags & DRIVE_CASE_SENSITIVE) );
1303
d75aed2c
DT
1304 lstrcpynW( entry->cFileName, long_name, sizeof(entry->cFileName)/sizeof(entry->cFileName[0]) );
1305 if (!(flags & DRIVE_CASE_PRESERVING)) strlwrW( entry->cFileName );
dd03cc19 1306 TRACE("returning %s (%s) %02lx %ld\n",
d75aed2c 1307 debugstr_w(entry->cFileName), debugstr_w(entry->cAlternateFileName),
dd03cc19 1308 entry->dwFileAttributes, entry->nFileSizeLow );
85ed45e3 1309 return 1;
4f8c37b4 1310 }
4f8c37b4
AJ
1311 return 0; /* End of directory */
1312}
ca22b33d 1313
9ea19e54 1314/*************************************************************************
d75aed2c 1315 * FindFirstFileExW (KERNEL32.@)
9ea19e54 1316 */
d75aed2c
DT
1317HANDLE WINAPI FindFirstFileExW(
1318 LPCWSTR lpFileName,
2250f12c
JS
1319 FINDEX_INFO_LEVELS fInfoLevelId,
1320 LPVOID lpFindFileData,
1321 FINDEX_SEARCH_OPS fSearchOp,
1322 LPVOID lpSearchFilter,
1323 DWORD dwAdditionalFlags)
9ea19e54 1324{
9ea19e54 1325 FIND_FIRST_INFO *info;
9a624916 1326
d75aed2c
DT
1327 if (!lpFileName)
1328 {
1329 SetLastError(ERROR_PATH_NOT_FOUND);
1330 return INVALID_HANDLE_VALUE;
1331 }
1332
2250f12c
JS
1333 if ((fSearchOp != FindExSearchNameMatch) || (dwAdditionalFlags != 0))
1334 {
1335 FIXME("options not implemented 0x%08x 0x%08lx\n", fSearchOp, dwAdditionalFlags );
1336 return INVALID_HANDLE_VALUE;
1337 }
9ea19e54 1338
2250f12c 1339 switch(fInfoLevelId)
9ea19e54 1340 {
2250f12c
JS
1341 case FindExInfoStandard:
1342 {
d75aed2c
DT
1343 WIN32_FIND_DATAW * data = (WIN32_FIND_DATAW *) lpFindFileData;
1344 char *p;
1345 INT long_mask_len;
d75aed2c 1346
2250f12c 1347 data->dwReserved0 = data->dwReserved1 = 0x0;
963985b3
MM
1348 if (lpFileName[0] == '\\' && lpFileName[1] == '\\')
1349 {
1350 ERR("UNC path name\n");
33854afb 1351 if (!(info = HeapAlloc( GetProcessHeap(), 0, sizeof(FIND_FIRST_INFO)))) break;
963985b3 1352 info->u.smb_dir = SMB_FindFirst(lpFileName);
2a5c1460 1353 if(!info->u.smb_dir)
963985b3 1354 {
33854afb 1355 HeapFree(GetProcessHeap(), 0, info);
963985b3
MM
1356 break;
1357 }
963985b3 1358 info->drive = -1;
33854afb 1359 RtlInitializeCriticalSection( &info->cs );
963985b3
MM
1360 }
1361 else
1362 {
1363 DOS_FULL_NAME full_name;
1364
0de21b65
UB
1365 if (lpFileName[0] && lpFileName[1] == ':')
1366 {
1367 /* don't allow root directories */
1368 if (!lpFileName[2] ||
1369 ((lpFileName[2] == '/' || lpFileName[2] == '\\') && !lpFileName[3]))
1370 {
1371 SetLastError(ERROR_FILE_NOT_FOUND);
1372 return INVALID_HANDLE_VALUE;
1373 }
1374 }
d75aed2c 1375 if (!DOSFS_GetFullName( lpFileName, FALSE, &full_name )) break;
33854afb
AP
1376 if (!(info = HeapAlloc( GetProcessHeap(), 0, sizeof(FIND_FIRST_INFO)))) break;
1377 RtlInitializeCriticalSection( &info->cs );
d75aed2c
DT
1378 info->path = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
1379 strcpy( info->path, full_name.long_name );
1380
d75aed2c
DT
1381 p = strrchr( info->path, '/' );
1382 *p++ = '\0';
49a0224f 1383 long_mask_len = MultiByteToWideChar(CP_UNIXCP, 0, p, -1, NULL, 0);
d75aed2c 1384 info->long_mask = HeapAlloc( GetProcessHeap(), 0, long_mask_len * sizeof(WCHAR) );
49a0224f 1385 MultiByteToWideChar(CP_UNIXCP, 0, p, -1, info->long_mask, long_mask_len);
d75aed2c 1386
d75aed2c
DT
1387 info->drive = full_name.drive;
1388 info->cur_pos = 0;
1389
49a0224f 1390 info->u.dos_dir = DOSFS_OpenDir( info->path );
963985b3 1391 }
33854afb 1392 if (!FindNextFileW( (HANDLE) info, data ))
2250f12c 1393 {
33854afb 1394 FindClose( (HANDLE) info );
1fc2142a 1395 SetLastError( ERROR_FILE_NOT_FOUND );
2250f12c
JS
1396 break;
1397 }
33854afb 1398 return (HANDLE) info;
2250f12c
JS
1399 }
1400 break;
1401 default:
1402 FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
9ea19e54 1403 }
2250f12c 1404 return INVALID_HANDLE_VALUE;
9ea19e54
AJ
1405}
1406
9ea19e54 1407/*************************************************************************
dae8de69 1408 * FindFirstFileA (KERNEL32.@)
9ea19e54 1409 */
2250f12c
JS
1410HANDLE WINAPI FindFirstFileA(
1411 LPCSTR lpFileName,
1412 WIN32_FIND_DATAA *lpFindData )
9ea19e54 1413{
2250f12c
JS
1414 return FindFirstFileExA(lpFileName, FindExInfoStandard, lpFindData,
1415 FindExSearchNameMatch, NULL, 0);
9ea19e54
AJ
1416}
1417
9ea19e54 1418/*************************************************************************
d75aed2c 1419 * FindFirstFileExA (KERNEL32.@)
9ea19e54 1420 */
d75aed2c
DT
1421HANDLE WINAPI FindFirstFileExA(
1422 LPCSTR lpFileName,
2250f12c
JS
1423 FINDEX_INFO_LEVELS fInfoLevelId,
1424 LPVOID lpFindFileData,
1425 FINDEX_SEARCH_OPS fSearchOp,
1426 LPVOID lpSearchFilter,
1427 DWORD dwAdditionalFlags)
9ea19e54 1428{
2250f12c 1429 HANDLE handle;
d75aed2c
DT
1430 WIN32_FIND_DATAA *dataA;
1431 WIN32_FIND_DATAW dataW;
1432 UNICODE_STRING pathW;
2250f12c 1433
d75aed2c 1434 if (!lpFileName)
2250f12c 1435 {
d75aed2c
DT
1436 SetLastError(ERROR_PATH_NOT_FOUND);
1437 return INVALID_HANDLE_VALUE;
2250f12c
JS
1438 }
1439
d75aed2c 1440 if (!RtlCreateUnicodeStringFromAsciiz(&pathW, lpFileName))
9ea19e54 1441 {
d75aed2c 1442 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2250f12c 1443 return INVALID_HANDLE_VALUE;
9ea19e54 1444 }
d75aed2c
DT
1445
1446 handle = FindFirstFileExW(pathW.Buffer, fInfoLevelId, &dataW, fSearchOp, lpSearchFilter, dwAdditionalFlags);
1447 RtlFreeUnicodeString(&pathW);
1448 if (handle == INVALID_HANDLE_VALUE) return handle;
1449
1450 dataA = (WIN32_FIND_DATAA *) lpFindFileData;
1451 dataA->dwFileAttributes = dataW.dwFileAttributes;
1452 dataA->ftCreationTime = dataW.ftCreationTime;
1453 dataA->ftLastAccessTime = dataW.ftLastAccessTime;
1454 dataA->ftLastWriteTime = dataW.ftLastWriteTime;
1455 dataA->nFileSizeHigh = dataW.nFileSizeHigh;
1456 dataA->nFileSizeLow = dataW.nFileSizeLow;
1457 WideCharToMultiByte( CP_ACP, 0, dataW.cFileName, -1,
1458 dataA->cFileName, sizeof(dataA->cFileName), NULL, NULL );
1459 WideCharToMultiByte( CP_ACP, 0, dataW.cAlternateFileName, -1,
1460 dataA->cAlternateFileName, sizeof(dataA->cAlternateFileName), NULL, NULL );
9ea19e54
AJ
1461 return handle;
1462}
1463
2250f12c 1464/*************************************************************************
dae8de69 1465 * FindFirstFileW (KERNEL32.@)
2250f12c
JS
1466 */
1467HANDLE WINAPI FindFirstFileW( LPCWSTR lpFileName, WIN32_FIND_DATAW *lpFindData )
1468{
1469 return FindFirstFileExW(lpFileName, FindExInfoStandard, lpFindData,
1470 FindExSearchNameMatch, NULL, 0);
1471}
9ea19e54
AJ
1472
1473/*************************************************************************
d75aed2c 1474 * FindNextFileW (KERNEL32.@)
9ea19e54 1475 */
d75aed2c 1476BOOL WINAPI FindNextFileW( HANDLE handle, WIN32_FIND_DATAW *data )
9ea19e54
AJ
1477{
1478 FIND_FIRST_INFO *info;
963985b3 1479 BOOL ret = FALSE;
e1db8bd0 1480 DWORD gle = ERROR_NO_MORE_FILES;
9ea19e54 1481
33854afb 1482 if (handle == INVALID_HANDLE_VALUE)
9ea19e54 1483 {
4ff2a27c 1484 SetLastError( ERROR_INVALID_HANDLE );
963985b3 1485 return ret;
9ea19e54 1486 }
33854afb
AP
1487 info = (FIND_FIRST_INFO*) handle;
1488 RtlEnterCriticalSection( &info->cs );
963985b3
MM
1489 if (info->drive == -1)
1490 {
1491 ret = SMB_FindNext( info->u.smb_dir, data );
1492 if(!ret)
1493 {
1494 SMB_CloseDir( info->u.smb_dir );
1495 HeapFree( GetProcessHeap(), 0, info->path );
963985b3
MM
1496 }
1497 goto done;
1498 }
1499 else if (!info->path || !info->u.dos_dir)
9ea19e54 1500 {
e1db8bd0 1501 goto done;
9ea19e54 1502 }
963985b3 1503 else if (!DOSFS_FindNextEx( info, data ))
9ea19e54 1504 {
963985b3 1505 DOSFS_CloseDir( info->u.dos_dir ); info->u.dos_dir = NULL;
90476d6b 1506 HeapFree( GetProcessHeap(), 0, info->path );
d75aed2c
DT
1507 info->path = NULL;
1508 HeapFree( GetProcessHeap(), 0, info->long_mask );
1509 info->long_mask = NULL;
1510 goto done;
9ea19e54 1511 }
d75aed2c 1512 ret = TRUE;
963985b3 1513done:
33854afb 1514 RtlLeaveCriticalSection( &info->cs );
e1db8bd0 1515 if( !ret ) SetLastError( gle );
963985b3 1516 return ret;
9ea19e54
AJ
1517}
1518
1519
9ea19e54 1520/*************************************************************************
d75aed2c 1521 * FindNextFileA (KERNEL32.@)
9ea19e54 1522 */
d75aed2c 1523BOOL WINAPI FindNextFileA( HANDLE handle, WIN32_FIND_DATAA *data )
9ea19e54 1524{
d75aed2c
DT
1525 WIN32_FIND_DATAW dataW;
1526 if (!FindNextFileW( handle, &dataW )) return FALSE;
1527 data->dwFileAttributes = dataW.dwFileAttributes;
1528 data->ftCreationTime = dataW.ftCreationTime;
1529 data->ftLastAccessTime = dataW.ftLastAccessTime;
1530 data->ftLastWriteTime = dataW.ftLastWriteTime;
1531 data->nFileSizeHigh = dataW.nFileSizeHigh;
1532 data->nFileSizeLow = dataW.nFileSizeLow;
1533 WideCharToMultiByte( CP_ACP, 0, dataW.cFileName, -1,
1534 data->cFileName, sizeof(data->cFileName), NULL, NULL );
1535 WideCharToMultiByte( CP_ACP, 0, dataW.cAlternateFileName, -1,
072dfb57 1536 data->cAlternateFileName,
d75aed2c 1537 sizeof(data->cAlternateFileName), NULL, NULL );
9ea19e54
AJ
1538 return TRUE;
1539}
1540
9ea19e54 1541/*************************************************************************
dae8de69 1542 * FindClose (KERNEL32.@)
9ea19e54 1543 */
2250f12c 1544BOOL WINAPI FindClose( HANDLE handle )
9ea19e54 1545{
33854afb 1546 FIND_FIRST_INFO *info = (FIND_FIRST_INFO*) handle;
9ea19e54 1547
77fbbcf3
MM
1548 if (handle == INVALID_HANDLE_VALUE) goto error;
1549
4f46b5de
DS
1550 __TRY
1551 {
33854afb
AP
1552 RtlEnterCriticalSection( &info->cs );
1553 if (info)
77fbbcf3 1554 {
963985b3 1555 if (info->u.dos_dir) DOSFS_CloseDir( info->u.dos_dir );
77fbbcf3 1556 if (info->path) HeapFree( GetProcessHeap(), 0, info->path );
d75aed2c 1557 if (info->long_mask) HeapFree( GetProcessHeap(), 0, info->long_mask );
77fbbcf3 1558 }
4f46b5de
DS
1559 }
1560 __EXCEPT(page_fault)
1561 {
ed800c69 1562 WARN("Illegal handle %p\n", handle);
4f46b5de
DS
1563 SetLastError( ERROR_INVALID_HANDLE );
1564 return FALSE;
1565 }
1566 __ENDTRY
77fbbcf3 1567 if (!info) goto error;
33854afb
AP
1568 RtlLeaveCriticalSection( &info->cs );
1569 RtlDeleteCriticalSection( &info->cs );
1570 HeapFree(GetProcessHeap(), 0, info);
9ea19e54 1571 return TRUE;
77fbbcf3
MM
1572
1573 error:
1574 SetLastError( ERROR_INVALID_HANDLE );
1575 return FALSE;
9ea19e54
AJ
1576}
1577
15467bfb 1578/***********************************************************************
dae8de69 1579 * MulDiv (KERNEL32.@)
15467bfb
AJ
1580 * RETURNS
1581 * Result of multiplication and division
1582 * -1: Overflow occurred or Divisor was 0
1583 */
1584INT WINAPI MulDiv(
9a624916 1585 INT nMultiplicand,
15467bfb
AJ
1586 INT nMultiplier,
1587 INT nDivisor)
1588{
1589#if SIZEOF_LONG_LONG >= 8
1590 long long ret;
1591
1592 if (!nDivisor) return -1;
1593
1594 /* We want to deal with a positive divisor to simplify the logic. */
1595 if (nDivisor < 0)
1596 {
1597 nMultiplicand = - nMultiplicand;
1598 nDivisor = -nDivisor;
1599 }
1600
1601 /* If the result is positive, we "add" to round. else, we subtract to round. */
1602 if ( ( (nMultiplicand < 0) && (nMultiplier < 0) ) ||
1603 ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
1604 ret = (((long long)nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
1605 else
1606 ret = (((long long)nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
1607
1608 if ((ret > 2147483647) || (ret < -2147483647)) return -1;
1609 return ret;
1610#else
1611 if (!nDivisor) return -1;
1612
1613 /* We want to deal with a positive divisor to simplify the logic. */
1614 if (nDivisor < 0)
1615 {
1616 nMultiplicand = - nMultiplicand;
1617 nDivisor = -nDivisor;
1618 }
1619
1620 /* If the result is positive, we "add" to round. else, we subtract to round. */
1621 if ( ( (nMultiplicand < 0) && (nMultiplier < 0) ) ||
1622 ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
1623 return ((nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
9a624916 1624
15467bfb 1625 return ((nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
9a624916 1626
15467bfb
AJ
1627#endif
1628}
1629
1630
75d86e1f 1631/***********************************************************************
dae8de69 1632 * DosDateTimeToFileTime (KERNEL32.@)
75d86e1f 1633 */
a3960292 1634BOOL WINAPI DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft)
75d86e1f 1635{
c6c09442 1636 struct tm newtm;
a0322773
VB
1637#ifndef HAVE_TIMEGM
1638 struct tm *gtm;
1639 time_t time1, time2;
1640#endif
c6c09442
AJ
1641
1642 newtm.tm_sec = (fattime & 0x1f) * 2;
1643 newtm.tm_min = (fattime >> 5) & 0x3f;
1644 newtm.tm_hour = (fattime >> 11);
1645 newtm.tm_mday = (fatdate & 0x1f);
1646 newtm.tm_mon = ((fatdate >> 5) & 0x0f) - 1;
1647 newtm.tm_year = (fatdate >> 9) + 80;
a0322773 1648#ifdef HAVE_TIMEGM
1668870f 1649 RtlSecondsSince1970ToTime( timegm(&newtm), (LARGE_INTEGER *)ft );
a0322773
VB
1650#else
1651 time1 = mktime(&newtm);
1652 gtm = gmtime(&time1);
1653 time2 = mktime(gtm);
1668870f 1654 RtlSecondsSince1970ToTime( 2*time1-time2, (LARGE_INTEGER *)ft );
a0322773 1655#endif
75d86e1f
AJ
1656 return TRUE;
1657}
1658
1659
1660/***********************************************************************
dae8de69 1661 * FileTimeToDosDateTime (KERNEL32.@)
75d86e1f 1662 */
a3960292 1663BOOL WINAPI FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate,
670cdc45 1664 LPWORD fattime )
75d86e1f 1665{
f3d2a8d4
EP
1666 LARGE_INTEGER li;
1667 ULONG t;
1668 time_t unixtime;
1669 struct tm* tm;
1670
399901e0
GG
1671 li.u.LowPart = ft->dwLowDateTime;
1672 li.u.HighPart = ft->dwHighDateTime;
f3d2a8d4
EP
1673 RtlTimeToSecondsSince1970( &li, &t );
1674 unixtime = t;
1675 tm = gmtime( &unixtime );
c6c09442
AJ
1676 if (fattime)
1677 *fattime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
1678 if (fatdate)
1679 *fatdate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
1680 + tm->tm_mday;
75d86e1f
AJ
1681 return TRUE;
1682}
1683
75d86e1f 1684
c6c09442 1685/***********************************************************************
dae8de69 1686 * QueryDosDeviceA (KERNEL32.@)
c6c09442
AJ
1687 *
1688 * returns array of strings terminated by \0, terminated by \0
1689 */
a3960292 1690DWORD WINAPI QueryDosDeviceA(LPCSTR devname,LPSTR target,DWORD bufsize)
c6c09442 1691{
a98f1297
SL
1692 DWORD ret = 0, retW;
1693 LPWSTR targetW = (LPWSTR)HeapAlloc(GetProcessHeap(),0,
1694 bufsize * sizeof(WCHAR));
1695 UNICODE_STRING devnameW;
1696
1697 if(devname) RtlCreateUnicodeStringFromAsciiz(&devnameW, devname);
1698 else devnameW.Buffer = NULL;
1699
1700 retW = QueryDosDeviceW(devnameW.Buffer, targetW, bufsize);
1701
1702 ret = WideCharToMultiByte(CP_ACP, 0, targetW, retW, target,
1703 bufsize, NULL, NULL);
1704
1705 RtlFreeUnicodeString(&devnameW);
1706 if (targetW) HeapFree(GetProcessHeap(),0,targetW);
1707 return ret;
1708}
1709
1710
1711/***********************************************************************
1712 * QueryDosDeviceW (KERNEL32.@)
1713 *
1714 * returns array of strings terminated by \0, terminated by \0
1715 *
1716 * FIXME
1717 * - Win9x returns for all calls ERROR_INVALID_PARAMETER
1718 * - the returned devices for devname == NULL is far from complete
1719 * - its not checked that the returned device exist
1720 */
1721DWORD WINAPI QueryDosDeviceW(LPCWSTR devname,LPWSTR target,DWORD bufsize)
1722{
1723 const WCHAR *pDev, *pName, *pNum = NULL;
1724 int numsiz=0;
1725 DWORD ret;
c6c09442 1726
a98f1297 1727 TRACE("(%s,...)\n", debugstr_w(devname));
c6c09442
AJ
1728 if (!devname) {
1729 /* return known MSDOS devices */
a98f1297
SL
1730 DWORD ret = 0;
1731 int i;
1732 static const WCHAR devices[][5] = {{'A','U','X',0},
1733 {'C','O','M','1',0},
1734 {'C','O','M','2',0},
1735 {'L','P','T','1',0},
1736 {'N','U','L',0,}};
1737 for(i=0; (i< (sizeof(devices)/sizeof(devices[0]))); i++) {
1738 DWORD len = strlenW(devices[i]);
1739 if(target && (bufsize >= ret + len + 2)) {
65e7196f 1740 strcpyW(target+ret, devices[i]);
a98f1297
SL
1741 ret += len + 1;
1742 } else {
1743 /* in this case WinXP returns 0 */
1744 FIXME("function return is wrong for WinXP!\n");
1745 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1746 break;
1747 }
1748 }
1749 /* append drives here */
1750 if(target && bufsize > 0) target[ret++] = 0;
1751 FIXME("Returned list is not complete\n");
1752 return ret;
c6c09442 1753 }
04d5efdf
MM
1754 /* In theory all that are possible and have been defined.
1755 * Now just those below, since mirc uses it to check for special files.
1756 *
9a624916 1757 * (It is more complex, and supports netmounted stuff, and \\.\ stuff,
04d5efdf
MM
1758 * but currently we just ignore that.)
1759 */
a98f1297
SL
1760 if (!strcmpiW(devname, auxW)) {
1761 pDev = dosW;
1762 pName = comW;
1763 numsiz = 1;
1764 pNum = oneW;
1765 } else if (!strcmpiW(devname, nulW)) {
1766 pDev = devW;
1767 pName = nullW;
1768 } else if (!strncmpiW(devname, comW, strlenW(comW))) {
1769 pDev = devW;
1770 pName = serW;
1771 pNum = devname + strlenW(comW);
1772 for(numsiz=0; isdigitW(*(pNum+numsiz)); numsiz++);
1773 if(*(pNum + numsiz)) {
1774 SetLastError(ERROR_FILE_NOT_FOUND);
1775 return 0;
04d5efdf 1776 }
a98f1297
SL
1777 } else if (!strncmpiW(devname, lptW, strlenW(lptW))) {
1778 pDev = devW;
1779 pName = parW;
1780 pNum = devname + strlenW(lptW);
1781 for(numsiz=0; isdigitW(*(pNum+numsiz)); numsiz++);
1782 if(*(pNum + numsiz)) {
1783 SetLastError(ERROR_FILE_NOT_FOUND);
04d5efdf
MM
1784 return 0;
1785 }
a98f1297
SL
1786 } else {
1787 /* This might be a DOS device we do not handle yet ... */
1788 FIXME("(%s) not detected as DOS device!\n",debugstr_w(devname));
04d5efdf 1789
a98f1297
SL
1790 /* Win9x set the error ERROR_INVALID_PARAMETER */
1791 SetLastError(ERROR_FILE_NOT_FOUND);
1792 return 0;
c6c09442 1793}
a98f1297 1794 FIXME("device %s may not exist on this computer\n", debugstr_w(devname));
c6c09442 1795
a98f1297
SL
1796 ret = strlenW(pDev) + strlenW(pName) + numsiz + 2;
1797 if (ret > bufsize) ret = 0;
1798 if (target && ret) {
65e7196f
AJ
1799 strcpyW(target,pDev);
1800 strcatW(target,pName);
1801 if (pNum) strcatW(target,pNum);
a98f1297
SL
1802 target[ret-1] = 0;
1803 }
c6c09442
AJ
1804 return ret;
1805}