dos_fs.c 80.3 KB
Newer Older
Alexandre Julliard's avatar
Alexandre Julliard committed
1 2 3 4 5
/*
 * DOS file system functions
 *
 * Copyright 1993 Erik Bos
 * Copyright 1996 Alexandre Julliard
6 7 8 9 10 11 12 13 14 15 16 17 18 19
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
Alexandre Julliard's avatar
Alexandre Julliard committed
20 21
 */

Alexandre Julliard's avatar
Alexandre Julliard committed
22
#include "config.h"
23

Alexandre Julliard's avatar
Alexandre Julliard committed
24
#include <sys/types.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
25 26
#include <ctype.h>
#include <dirent.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
27
#include <errno.h>
28
#ifdef HAVE_SYS_ERRNO_H
Alexandre Julliard's avatar
Alexandre Julliard committed
29
#include <sys/errno.h>
30
#endif
Alexandre Julliard's avatar
Alexandre Julliard committed
31
#include <fcntl.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
32 33 34
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
35
#ifdef HAVE_SYS_IOCTL_H
Alexandre Julliard's avatar
Alexandre Julliard committed
36
#include <sys/ioctl.h>
37
#endif
Alexandre Julliard's avatar
Alexandre Julliard committed
38
#include <time.h>
39 40 41
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
Alexandre Julliard's avatar
Alexandre Julliard committed
42

43
#include "windef.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
44
#include "winerror.h"
45 46 47 48
#include "wingdi.h"

#include "wine/unicode.h"
#include "wine/winbase16.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
49 50
#include "drive.h"
#include "file.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
51
#include "heap.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
52
#include "msdos.h"
53
#include "winternl.h"
54
#include "wine/server.h"
55
#include "excpt.h"
56

57 58
#include "smb.h"

59
#include "wine/debug.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
60

61 62
WINE_DEFAULT_DEBUG_CHANNEL(dosfs);
WINE_DECLARE_DEBUG_CHANNEL(file);
63

Alexandre Julliard's avatar
Alexandre Julliard committed
64
/* Define the VFAT ioctl to get both short and long file names */
Alexandre Julliard's avatar
Alexandre Julliard committed
65
/* FIXME: is it possible to get this to work on other systems? */
Alexandre Julliard's avatar
Alexandre Julliard committed
66
#ifdef linux
Alexandre Julliard's avatar
Alexandre Julliard committed
67 68 69 70 71 72 73 74 75
/* We want the real kernel dirent structure, not the libc one */
typedef struct
{
    long d_ino;
    long d_off;
    unsigned short d_reclen;
    char d_name[256];
} KERNEL_DIRENT;

76 77
#define VFAT_IOCTL_READDIR_BOTH  _IOR('r', 1, KERNEL_DIRENT [2] )

Alexandre Julliard's avatar
Alexandre Julliard committed
78 79 80
#else   /* linux */
#undef VFAT_IOCTL_READDIR_BOTH  /* just in case... */
#endif  /* linux */
Alexandre Julliard's avatar
Alexandre Julliard committed
81 82

/* Chars we don't want to see in DOS file names */
Alexandre Julliard's avatar
Alexandre Julliard committed
83
#define INVALID_DOS_CHARS  "*?<>|\"+=,;[] \345"
Alexandre Julliard's avatar
Alexandre Julliard committed
84

Alexandre Julliard's avatar
Alexandre Julliard committed
85 86 87
static const DOS_DEVICE DOSFS_Devices[] =
/* name, device flags (see Int 21/AX=0x4400) */
{
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
    { {'C','O','N',0}, 0xc0d3 },
    { {'P','R','N',0}, 0xa0c0 },
    { {'N','U','L',0}, 0x80c4 },
    { {'A','U','X',0}, 0x80c0 },
    { {'L','P','T','1',0}, 0xa0c0 },
    { {'L','P','T','2',0}, 0xa0c0 },
    { {'L','P','T','3',0}, 0xa0c0 },
    { {'L','P','T','4',0}, 0xc0d3 },
    { {'C','O','M','1',0}, 0x80c0 },
    { {'C','O','M','2',0}, 0x80c0 },
    { {'C','O','M','3',0}, 0x80c0 },
    { {'C','O','M','4',0}, 0x80c0 },
    { {'S','C','S','I','M','G','R','$',0}, 0xc0c0 },
    { {'H','P','S','C','A','N',0}, 0xc0c0 },
    { {'E','M','M','X','X','X','X','0',0}, 0x0000 }
Alexandre Julliard's avatar
Alexandre Julliard committed
103 104
};

105 106 107 108
/*
 * Directory info for DOSFS_ReadDir
 * contains the names of *all* the files in the directory
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
109 110
typedef struct
{
111 112 113
    int used;
    int size;
    char names[1];
Alexandre Julliard's avatar
Alexandre Julliard committed
114 115
} DOS_DIR;

Alexandre Julliard's avatar
Alexandre Julliard committed
116 117 118
/* Info structure for FindFirstFile handle */
typedef struct
{
119 120 121
    char *path; /* unix path */
    LPWSTR long_mask;
    LPWSTR short_mask;
Alexandre Julliard's avatar
Alexandre Julliard committed
122 123 124
    BYTE  attr;
    int   drive;
    int   cur_pos;
125 126 127 128 129
    union
    {
        DOS_DIR *dos_dir;
        SMB_DIR *smb_dir;
    } u;
Alexandre Julliard's avatar
Alexandre Julliard committed
130 131 132
} FIND_FIRST_INFO;


133 134 135 136 137 138 139
static WINE_EXCEPTION_FILTER(page_fault)
{
    if (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION)
        return EXCEPTION_EXECUTE_HANDLER;
    return EXCEPTION_CONTINUE_SEARCH;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
140 141 142 143 144 145 146 147

/***********************************************************************
 *           DOSFS_ValidDOSName
 *
 * Return 1 if Unix file 'name' is also a valid MS-DOS name
 * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
 * File name can be terminated by '\0', '\\' or '/'.
 */
148
static int DOSFS_ValidDOSName( LPCWSTR name, int ignore_case )
Alexandre Julliard's avatar
Alexandre Julliard committed
149
{
Alexandre Julliard's avatar
Alexandre Julliard committed
150
    static const char invalid_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS;
151
    const WCHAR *p = name;
Alexandre Julliard's avatar
Alexandre Julliard committed
152
    const char *invalid = ignore_case ? (invalid_chars + 26) : invalid_chars;
Alexandre Julliard's avatar
Alexandre Julliard committed
153 154 155 156 157 158 159 160 161 162 163 164
    int len = 0;

    if (*p == '.')
    {
        /* Check for "." and ".." */
        p++;
        if (*p == '.') p++;
        /* All other names beginning with '.' are invalid */
        return (IS_END_OF_NAME(*p));
    }
    while (!IS_END_OF_NAME(*p))
    {
165
        if (*p < 256 && strchr( invalid, (char)*p )) return 0;  /* Invalid char */
Alexandre Julliard's avatar
Alexandre Julliard committed
166 167 168 169 170 171 172 173 174 175
        if (*p == '.') break;  /* Start of the extension */
        if (++len > 8) return 0;  /* Name too long */
        p++;
    }
    if (*p != '.') return 1;  /* End of name */
    p++;
    if (IS_END_OF_NAME(*p)) return 0;  /* Empty extension not allowed */
    len = 0;
    while (!IS_END_OF_NAME(*p))
    {
176
        if (*p < 256 && strchr( invalid, (char)*p )) return 0;  /* Invalid char */
Alexandre Julliard's avatar
Alexandre Julliard committed
177 178 179 180 181 182 183 184 185 186 187 188 189 190
        if (*p == '.') return 0;  /* Second extension not allowed */
        if (++len > 3) return 0;  /* Extension too long */
        p++;
    }
    return 1;
}


/***********************************************************************
 *           DOSFS_ToDosFCBFormat
 *
 * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
 * expanding wild cards and converting to upper-case in the process.
 * File name can be terminated by '\0', '\\' or '/'.
Alexandre Julliard's avatar
Alexandre Julliard committed
191 192
 * Return FALSE if the name is not a valid DOS name.
 * 'buffer' must be at least 12 characters long.
Alexandre Julliard's avatar
Alexandre Julliard committed
193
 */
194
BOOL DOSFS_ToDosFCBFormat( LPCWSTR name, LPWSTR buffer )
Alexandre Julliard's avatar
Alexandre Julliard committed
195 196
{
    static const char invalid_chars[] = INVALID_DOS_CHARS;
197
    LPCWSTR p = name;
Alexandre Julliard's avatar
Alexandre Julliard committed
198 199
    int i;

200 201
    TRACE("(%s, %p)\n", debugstr_w(name), buffer);

Alexandre Julliard's avatar
Alexandre Julliard committed
202 203 204 205
    /* Check for "." and ".." */
    if (*p == '.')
    {
        p++;
206 207 208
        buffer[0] = '.';
        for(i = 1; i < 11; i++) buffer[i] = ' ';
        buffer[11] = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
209 210 211 212 213
        if (*p == '.')
        {
            buffer[1] = '.';
            p++;
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
214
        return (!*p || (*p == '/') || (*p == '\\'));
Alexandre Julliard's avatar
Alexandre Julliard committed
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
    }

    for (i = 0; i < 8; i++)
    {
        switch(*p)
        {
        case '\0':
        case '\\':
        case '/':
        case '.':
            buffer[i] = ' ';
            break;
        case '?':
            p++;
            /* fall through */
        case '*':
            buffer[i] = '?';
            break;
        default:
234 235
            if (*p < 256 && strchr( invalid_chars, (char)*p )) return FALSE;
            buffer[i] = toupperW(*p);
Alexandre Julliard's avatar
Alexandre Julliard committed
236 237 238 239 240 241 242 243 244 245 246 247 248
            p++;
            break;
        }
    }

    if (*p == '*')
    {
        /* Skip all chars after wildcard up to first dot */
        while (*p && (*p != '/') && (*p != '\\') && (*p != '.')) p++;
    }
    else
    {
        /* Check if name too long */
Alexandre Julliard's avatar
Alexandre Julliard committed
249
        if (*p && (*p != '/') && (*p != '\\') && (*p != '.')) return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
250 251 252 253 254 255 256 257 258 259 260 261 262
    }
    if (*p == '.') p++;  /* Skip dot */

    for (i = 8; i < 11; i++)
    {
        switch(*p)
        {
        case '\0':
        case '\\':
        case '/':
            buffer[i] = ' ';
            break;
        case '.':
Alexandre Julliard's avatar
Alexandre Julliard committed
263
            return FALSE;  /* Second extension not allowed */
Alexandre Julliard's avatar
Alexandre Julliard committed
264 265 266 267 268 269 270
        case '?':
            p++;
            /* fall through */
        case '*':
            buffer[i] = '?';
            break;
        default:
271 272
            if (*p < 256 && strchr( invalid_chars, (char)*p )) return FALSE;
            buffer[i] = toupperW(*p);
Alexandre Julliard's avatar
Alexandre Julliard committed
273 274 275 276 277
            p++;
            break;
        }
    }
    buffer[11] = '\0';
278 279

    /* at most 3 character of the extension are processed
280
     * is something behind this ?
281
     */
282
    while (*p == '*' || *p == ' ') p++; /* skip wildcards and spaces */
283
    return IS_END_OF_NAME(*p);
Alexandre Julliard's avatar
Alexandre Julliard committed
284 285 286 287 288 289 290 291 292
}


/***********************************************************************
 *           DOSFS_ToDosDTAFormat
 *
 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
 * converting to upper-case in the process.
 * File name can be terminated by '\0', '\\' or '/'.
Alexandre Julliard's avatar
Alexandre Julliard committed
293
 * 'buffer' must be at least 13 characters long.
Alexandre Julliard's avatar
Alexandre Julliard committed
294
 */
295
static void DOSFS_ToDosDTAFormat( LPCWSTR name, LPWSTR buffer )
Alexandre Julliard's avatar
Alexandre Julliard committed
296
{
297
    LPWSTR p;
Alexandre Julliard's avatar
Alexandre Julliard committed
298

299
    memcpy( buffer, name, 8 * sizeof(WCHAR) );
300 301
    p = buffer + 8;
    while ((p > buffer) && (p[-1] == ' ')) p--;
Alexandre Julliard's avatar
Alexandre Julliard committed
302
    *p++ = '.';
303
    memcpy( p, name + 8, 3 * sizeof(WCHAR) );
304 305
    p += 3;
    while (p[-1] == ' ') p--;
Alexandre Julliard's avatar
Alexandre Julliard committed
306 307 308 309 310 311
    if (p[-1] == '.') p--;
    *p = '\0';
}


/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
312
 *           DOSFS_MatchShort
Alexandre Julliard's avatar
Alexandre Julliard committed
313 314 315
 *
 * Check a DOS file name against a mask (both in FCB format).
 */
316
static int DOSFS_MatchShort( LPCWSTR mask, LPCWSTR name )
Alexandre Julliard's avatar
Alexandre Julliard committed
317 318 319 320 321 322 323 324
{
    int i;
    for (i = 11; i > 0; i--, mask++, name++)
        if ((*mask != '?') && (*mask != *name)) return 0;
    return 1;
}


Alexandre Julliard's avatar
Alexandre Julliard committed
325 326 327 328
/***********************************************************************
 *           DOSFS_MatchLong
 *
 * Check a long file name against a mask.
329 330 331 332 333 334 335 336 337
 *
 * Tests (done in W95 DOS shell - case insensitive):
 * *.txt			test1.test.txt				*
 * *st1*			test1.txt				*
 * *.t??????.t*			test1.ta.tornado.txt			*
 * *tornado*			test1.ta.tornado.txt			*
 * t*t				test1.ta.tornado.txt			*
 * ?est*			test1.txt				*
 * ?est???			test1.txt				-
338
 * *test1.txt*			test1.txt				*
339
 * h?l?o*t.dat			hellothisisatest.dat			*
Alexandre Julliard's avatar
Alexandre Julliard committed
340
 */
341
static int DOSFS_MatchLong( LPCWSTR mask, LPCWSTR name, int case_sensitive )
Alexandre Julliard's avatar
Alexandre Julliard committed
342
{
343 344 345
    LPCWSTR lastjoker = NULL;
    LPCWSTR next_to_retry = NULL;
    static const WCHAR asterisk_dot_asterisk[] = {'*','.','*',0};
346

347 348
    TRACE("(%s, %s, %x)\n", debugstr_w(mask), debugstr_w(name), case_sensitive);

349
    if (!strcmpW( mask, asterisk_dot_asterisk )) return 1;
Alexandre Julliard's avatar
Alexandre Julliard committed
350 351 352 353 354 355
    while (*name && *mask)
    {
        if (*mask == '*')
        {
            mask++;
            while (*mask == '*') mask++;  /* Skip consecutive '*' */
356 357 358 359
            lastjoker = mask;
            if (!*mask) return 1; /* end of mask is all '*', so match */

            /* skip to the next match after the joker(s) */
Alexandre Julliard's avatar
Alexandre Julliard committed
360
            if (case_sensitive) while (*name && (*name != *mask)) name++;
361
            else while (*name && (toupperW(*name) != toupperW(*mask))) name++;
362

363
            if (!*name) break;
364
            next_to_retry = name;
Alexandre Julliard's avatar
Alexandre Julliard committed
365 366 367
        }
        else if (*mask != '?')
        {
368
            int mismatch = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
369 370
            if (case_sensitive)
            {
371 372 373 374
                if (*mask != *name) mismatch = 1;
            }
            else
            {
375
                if (toupperW(*mask) != toupperW(*name)) mismatch = 1;
376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401
            }
            if (!mismatch)
            {
                mask++;
                name++;
                if (*mask == '\0')
                {
                    if (*name == '\0')
                        return 1;
                    if (lastjoker)
                        mask = lastjoker;
                }
            }
            else /* mismatch ! */
            {
                if (lastjoker) /* we had an '*', so we can try unlimitedly */
                {
                    mask = lastjoker;

                    /* this scan sequence was a mismatch, so restart
                     * 1 char after the first char we checked last time */
                    next_to_retry++;
                    name = next_to_retry;
                }
                else
                    return 0; /* bad luck */
Alexandre Julliard's avatar
Alexandre Julliard committed
402 403
            }
        }
404 405 406 407 408
        else /* '?' */
        {
            mask++;
            name++;
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
409
    }
410 411
    while ((*mask == '.') || (*mask == '*'))
        mask++;  /* Ignore trailing '.' or '*' in mask */
Alexandre Julliard's avatar
Alexandre Julliard committed
412 413 414 415
    return (!*name && !*mask);
}


Alexandre Julliard's avatar
Alexandre Julliard committed
416
/***********************************************************************
417 418 419
 *           DOSFS_AddDirEntry
 *
 *  Used to construct an array of filenames in DOSFS_OpenDir
Alexandre Julliard's avatar
Alexandre Julliard committed
420
 */
421
static BOOL DOSFS_AddDirEntry(DOS_DIR **dir, LPCWSTR name, LPCWSTR dosname)
Alexandre Julliard's avatar
Alexandre Julliard committed
422
{
423 424 425 426 427
    int extra1 = (strlenW(name) + 1) * sizeof(WCHAR);
    int extra2 = (strlenW(dosname) + 1) * sizeof(WCHAR);

    /* if we need more, at minimum double the size */
    if( (extra1 + extra2 + (*dir)->used) > (*dir)->size)
Alexandre Julliard's avatar
Alexandre Julliard committed
428
    {
429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444
        int more = (*dir)->size;
        DOS_DIR *t;

        if(more<(extra1+extra2))
            more = extra1+extra2;

        t = HeapReAlloc(GetProcessHeap(), 0, *dir, sizeof(**dir) + (*dir)->size + more );
        if(!t)
        {
            SetLastError( ERROR_NOT_ENOUGH_MEMORY );
            ERR("Out of memory caching directory structure %d %d %d\n",
                 (*dir)->size, more, (*dir)->used);
            return FALSE;
        }
        (*dir) = t;
        (*dir)->size += more;
Alexandre Julliard's avatar
Alexandre Julliard committed
445 446
    }

447 448 449 450 451 452 453 454
    /* at this point, the dir structure is big enough to hold these names */
    strcpyW((LPWSTR)&(*dir)->names[(*dir)->used], name);
    (*dir)->used += extra1;
    strcpyW((LPWSTR)&(*dir)->names[(*dir)->used], dosname);
    (*dir)->used += extra2;

    return TRUE;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
455

456 457 458 459 460 461

/***********************************************************************
 *           DOSFS_OpenDir_VFAT
 */
static BOOL DOSFS_OpenDir_VFAT(UINT codepage, DOS_DIR **dir, const char *unix_path)
{
Alexandre Julliard's avatar
Alexandre Julliard committed
462
#ifdef VFAT_IOCTL_READDIR_BOTH
463 464 465
    KERNEL_DIRENT de[2];
    int fd = open( unix_path, O_RDONLY );
    BOOL r = TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
466 467 468

    /* Check if the VFAT ioctl is supported on this directory */

469 470 471 472
    if ( fd<0 )
        return FALSE;

    while (1)
Alexandre Julliard's avatar
Alexandre Julliard committed
473
    {
474 475 476 477 478 479 480 481 482 483 484 485 486
        WCHAR long_name[MAX_PATH];
        WCHAR short_name[12];

        r = (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de ) != -1);
        if(!r)
            break;
        if (!de[0].d_reclen)
            break;
        MultiByteToWideChar(codepage, 0, de[0].d_name, -1, long_name, MAX_PATH);
        if (!DOSFS_ToDosFCBFormat( long_name, short_name ))
            short_name[0] = '\0';
        if (de[1].d_name[0])
            MultiByteToWideChar(codepage, 0, de[1].d_name, -1, long_name, MAX_PATH);
Alexandre Julliard's avatar
Alexandre Julliard committed
487
        else
488 489 490 491
            MultiByteToWideChar(codepage, 0, de[0].d_name, -1, long_name, MAX_PATH);
        r = DOSFS_AddDirEntry(dir, long_name, short_name );
        if(!r)
            break;
Alexandre Julliard's avatar
Alexandre Julliard committed
492
    }
493 494 495 496 497 498 499 500 501
    if(r)
    {
        static const WCHAR empty_strW[] = { 0 };
        DOSFS_AddDirEntry(dir, empty_strW, empty_strW);
    }
    close(fd);
    return r;
#else
    return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
502
#endif  /* VFAT_IOCTL_READDIR_BOTH */
503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560
}


/***********************************************************************
 *           DOSFS_OpenDir_Normal
 *
 * Now use the standard opendir/readdir interface
 */
static BOOL DOSFS_OpenDir_Normal( UINT codepage, DOS_DIR **dir, const char *unix_path )
{
    DIR *unixdir = opendir( unix_path );
    BOOL r = TRUE;
    static const WCHAR empty_strW[] = { 0 };

    if(!unixdir)
        return FALSE;
    while(1)
    {
        WCHAR long_name[MAX_PATH];
        struct dirent *de = readdir(unixdir);

        if(!de)
            break;
        MultiByteToWideChar(codepage, 0, de->d_name, -1, long_name, MAX_PATH);
        r = DOSFS_AddDirEntry(dir, long_name, empty_strW);
        if(!r)
            break;
    }
    if(r)
        DOSFS_AddDirEntry(dir, empty_strW, empty_strW);
    closedir(unixdir);
    return r;
}

/***********************************************************************
 *           DOSFS_OpenDir
 */
static DOS_DIR *DOSFS_OpenDir( UINT codepage, const char *unix_path )
{
    const int init_size = 0x100;
    DOS_DIR *dir = HeapAlloc( GetProcessHeap(), 0, sizeof(*dir) + init_size);
    BOOL r;

    TRACE("%s\n",debugstr_a(unix_path));

    if (!dir)
    {
        SetLastError( ERROR_NOT_ENOUGH_MEMORY );
        return NULL;
    }
    dir->used = 0;
    dir->size = init_size;

    /* Treat empty path as root directory. This simplifies path split into
       directory and mask in several other places */
    if (!*unix_path) unix_path = "/";

    r = DOSFS_OpenDir_VFAT( codepage, &dir, unix_path);
Alexandre Julliard's avatar
Alexandre Julliard committed
561

562 563
    if(!r)
        r = DOSFS_OpenDir_Normal( codepage, &dir, unix_path);
Alexandre Julliard's avatar
Alexandre Julliard committed
564

565
    if(!r)
Alexandre Julliard's avatar
Alexandre Julliard committed
566
    {
567
        HeapFree(GetProcessHeap(), 0, dir);
Alexandre Julliard's avatar
Alexandre Julliard committed
568 569
        return NULL;
    }
570 571
    dir->used = 0;

Alexandre Julliard's avatar
Alexandre Julliard committed
572 573 574 575 576 577 578 579 580
    return dir;
}


/***********************************************************************
 *           DOSFS_CloseDir
 */
static void DOSFS_CloseDir( DOS_DIR *dir )
{
581
    HeapFree( GetProcessHeap(), 0, dir );
Alexandre Julliard's avatar
Alexandre Julliard committed
582 583 584 585 586 587
}


/***********************************************************************
 *           DOSFS_ReadDir
 */
588 589
static BOOL DOSFS_ReadDir( DOS_DIR *dir, LPCWSTR *long_name,
                             LPCWSTR *short_name )
Alexandre Julliard's avatar
Alexandre Julliard committed
590
{
591
    LPCWSTR sn, ln;
Alexandre Julliard's avatar
Alexandre Julliard committed
592

593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610
    if (!dir)
       return FALSE;

    /* the long pathname is first */
    ln = (LPCWSTR)&dir->names[dir->used];
    if(ln[0])
        *long_name  = ln;
    else
        return FALSE;
    dir->used += (strlenW(ln) + 1) * sizeof(WCHAR);

    /* followed by the short path name */
    sn = (LPCWSTR)&dir->names[dir->used];
    if(sn[0])
        *short_name = sn;
    else
        *short_name = NULL;
    dir->used += (strlenW(sn) + 1) * sizeof(WCHAR);
Alexandre Julliard's avatar
Alexandre Julliard committed
611

612 613 614
    TRACE("Read: long_name: %s, short_name: %s\n", 
          debugstr_w(*long_name), debugstr_w(*short_name));

Alexandre Julliard's avatar
Alexandre Julliard committed
615 616 617 618
    return TRUE;
}


Alexandre Julliard's avatar
Alexandre Julliard committed
619 620 621 622 623 624 625
/***********************************************************************
 *           DOSFS_Hash
 *
 * Transform a Unix file name into a hashed DOS name. If the name is a valid
 * DOS name, it is converted to upper-case; otherwise it is replaced by a
 * hashed version that fits in 8.3 format.
 * File name can be terminated by '\0', '\\' or '/'.
Alexandre Julliard's avatar
Alexandre Julliard committed
626
 * 'buffer' must be at least 13 characters long.
Alexandre Julliard's avatar
Alexandre Julliard committed
627
 */
628
static void DOSFS_Hash( LPCWSTR name, LPWSTR buffer, BOOL dir_format,
629
                        BOOL ignore_case )
Alexandre Julliard's avatar
Alexandre Julliard committed
630 631 632 633
{
    static const char invalid_chars[] = INVALID_DOS_CHARS "~.";
    static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";

634 635
    LPCWSTR p, ext;
    LPWSTR dst;
Alexandre Julliard's avatar
Alexandre Julliard committed
636 637 638
    unsigned short hash;
    int i;

639 640 641 642 643
    if (dir_format)
    {
        for(i = 0; i < 11; i++) buffer[i] = ' ';
        buffer[11] = 0;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
644

Alexandre Julliard's avatar
Alexandre Julliard committed
645
    if (DOSFS_ValidDOSName( name, ignore_case ))
Alexandre Julliard's avatar
Alexandre Julliard committed
646 647 648 649 650 651 652
    {
        /* Check for '.' and '..' */
        if (*name == '.')
        {
            buffer[0] = '.';
            if (!dir_format) buffer[1] = buffer[2] = '\0';
            if (name[1] == '.') buffer[1] = '.';
Alexandre Julliard's avatar
Alexandre Julliard committed
653
            return;
Alexandre Julliard's avatar
Alexandre Julliard committed
654 655 656 657 658
        }

        /* Simply copy the name, converting to uppercase */

        for (dst = buffer; !IS_END_OF_NAME(*name) && (*name != '.'); name++)
659
            *dst++ = toupperW(*name);
Alexandre Julliard's avatar
Alexandre Julliard committed
660 661 662 663 664
        if (*name == '.')
        {
            if (dir_format) dst = buffer + 8;
            else *dst++ = '.';
            for (name++; !IS_END_OF_NAME(*name); name++)
665
                *dst++ = toupperW(*name);
Alexandre Julliard's avatar
Alexandre Julliard committed
666 667
        }
        if (!dir_format) *dst = '\0';
Alexandre Julliard's avatar
Alexandre Julliard committed
668 669 670 671 672 673 674 675 676
        return;
    }

    /* Compute the hash code of the file name */
    /* If you know something about hash functions, feel free to */
    /* insert a better algorithm here... */
    if (ignore_case)
    {
        for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
677 678
            hash = (hash<<3) ^ (hash>>5) ^ tolowerW(*p) ^ (tolowerW(p[1]) << 8);
        hash = (hash<<3) ^ (hash>>5) ^ tolowerW(*p); /* Last character */
Alexandre Julliard's avatar
Alexandre Julliard committed
679 680 681
    }
    else
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
682 683 684 685
        for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
            hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
        hash = (hash << 3) ^ (hash >> 5) ^ *p;  /* Last character */
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
686

Alexandre Julliard's avatar
Alexandre Julliard committed
687 688 689 690 691
    /* Find last dot for start of the extension */
    for (p = name+1, ext = NULL; !IS_END_OF_NAME(*p); p++)
        if (*p == '.') ext = p;
    if (ext && IS_END_OF_NAME(ext[1]))
        ext = NULL;  /* Empty extension ignored */
Alexandre Julliard's avatar
Alexandre Julliard committed
692

Alexandre Julliard's avatar
Alexandre Julliard committed
693 694 695 696
    /* Copy first 4 chars, replacing invalid chars with '_' */
    for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
    {
        if (IS_END_OF_NAME(*p) || (p == ext)) break;
697
        *dst++ = (*p < 256 && strchr( invalid_chars, (char)*p )) ? '_' : toupperW(*p);
Alexandre Julliard's avatar
Alexandre Julliard committed
698 699 700
    }
    /* Pad to 5 chars with '~' */
    while (i-- >= 0) *dst++ = '~';
Alexandre Julliard's avatar
Alexandre Julliard committed
701

Alexandre Julliard's avatar
Alexandre Julliard committed
702 703 704 705
    /* Insert hash code converted to 3 ASCII chars */
    *dst++ = hash_chars[(hash >> 10) & 0x1f];
    *dst++ = hash_chars[(hash >> 5) & 0x1f];
    *dst++ = hash_chars[hash & 0x1f];
Alexandre Julliard's avatar
Alexandre Julliard committed
706

Alexandre Julliard's avatar
Alexandre Julliard committed
707 708 709 710 711
    /* Copy the first 3 chars of the extension (if any) */
    if (ext)
    {
        if (!dir_format) *dst++ = '.';
        for (i = 3, ext++; (i > 0) && !IS_END_OF_NAME(*ext); i--, ext++)
712
            *dst++ = (*ext < 256 && strchr( invalid_chars, (char)*ext )) ? '_' : toupperW(*ext);
Alexandre Julliard's avatar
Alexandre Julliard committed
713
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
714
    if (!dir_format) *dst = '\0';
Alexandre Julliard's avatar
Alexandre Julliard committed
715 716 717 718 719 720 721 722 723
}


/***********************************************************************
 *           DOSFS_FindUnixName
 *
 * Find the Unix file name in a given directory that corresponds to
 * a file name (either in Unix or DOS format).
 * File name can be terminated by '\0', '\\' or '/'.
Alexandre Julliard's avatar
Alexandre Julliard committed
724 725 726 727 728
 * Return TRUE if OK, FALSE if no file name matches.
 *
 * 'long_buf' must be at least 'long_len' characters long. If the long name
 * turns out to be larger than that, the function returns FALSE.
 * 'short_buf' must be at least 13 characters long.
Alexandre Julliard's avatar
Alexandre Julliard committed
729
 */
730 731
BOOL DOSFS_FindUnixName( const DOS_FULL_NAME *path, LPCWSTR name, char *long_buf,
                         INT long_len, LPWSTR short_buf, BOOL ignore_case)
Alexandre Julliard's avatar
Alexandre Julliard committed
732
{
Alexandre Julliard's avatar
Alexandre Julliard committed
733
    DOS_DIR *dir;
734 735
    LPCWSTR long_name, short_name;
    WCHAR dos_name[12], tmp_buf[13];
736
    BOOL ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
737

738 739 740
    LPCWSTR p = strchrW( name, '/' );
    int len = p ? (int)(p - name) : strlenW(name);
    if ((p = strchrW( name, '\\' ))) len = min( (int)(p - name), len );
741 742
    /* Ignore trailing dots and spaces */
    while (len > 1 && (name[len-1] == '.' || name[len-1] == ' ')) len--;
Alexandre Julliard's avatar
Alexandre Julliard committed
743
    if (long_len < len + 1) return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
744

745
    TRACE("%s,%s\n", path->long_name, debugstr_w(name) );
Alexandre Julliard's avatar
Alexandre Julliard committed
746

Alexandre Julliard's avatar
Alexandre Julliard committed
747
    if (!DOSFS_ToDosFCBFormat( name, dos_name )) dos_name[0] = '\0';
Alexandre Julliard's avatar
Alexandre Julliard committed
748

749
    if (!(dir = DOSFS_OpenDir( DRIVE_GetCodepage(path->drive), path->long_name )))
Alexandre Julliard's avatar
Alexandre Julliard committed
750
    {
751
        WARN("(%s,%s): can't open dir: %s\n",
752
             path->long_name, debugstr_w(name), strerror(errno) );
Alexandre Julliard's avatar
Alexandre Julliard committed
753
        return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
754
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
755 756

    while ((ret = DOSFS_ReadDir( dir, &long_name, &short_name )))
Alexandre Julliard's avatar
Alexandre Julliard committed
757 758
    {
        /* Check against Unix name */
759
        if (len == strlenW(long_name))
Alexandre Julliard's avatar
Alexandre Julliard committed
760
        {
Alexandre Julliard's avatar
Alexandre Julliard committed
761
            if (!ignore_case)
Alexandre Julliard's avatar
Alexandre Julliard committed
762
            {
763
                if (!strncmpW( long_name, name, len )) break;
Alexandre Julliard's avatar
Alexandre Julliard committed
764 765 766
            }
            else
            {
767
                if (!strncmpiW( long_name, name, len )) break;
Alexandre Julliard's avatar
Alexandre Julliard committed
768 769
            }
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
770
        if (dos_name[0])
Alexandre Julliard's avatar
Alexandre Julliard committed
771 772
        {
            /* Check against hashed DOS name */
Alexandre Julliard's avatar
Alexandre Julliard committed
773
            if (!short_name)
Alexandre Julliard's avatar
Alexandre Julliard committed
774 775 776 777
            {
                DOSFS_Hash( long_name, tmp_buf, TRUE, ignore_case );
                short_name = tmp_buf;
            }
778
            if (!strcmpW( dos_name, short_name )) break;
Alexandre Julliard's avatar
Alexandre Julliard committed
779 780
        }
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
781 782
    if (ret)
    {
783 784
        if (long_buf) WideCharToMultiByte(DRIVE_GetCodepage(path->drive), 0,
                                          long_name, -1, long_buf, long_len, NULL, NULL);
Alexandre Julliard's avatar
Alexandre Julliard committed
785 786 787 788 789 790 791
        if (short_buf)
        {
            if (short_name)
                DOSFS_ToDosDTAFormat( short_name, short_buf );
            else
                DOSFS_Hash( long_name, short_buf, FALSE, ignore_case );
        }
792 793
        TRACE("(%s,%s) -> %s (%s)\n", path->long_name, debugstr_w(name),
              debugstr_w(long_name), short_buf ? debugstr_w(short_buf) : "***");
Alexandre Julliard's avatar
Alexandre Julliard committed
794 795
    }
    else
796
        WARN("%s not found in '%s'\n", debugstr_w(name), path->long_name);
Alexandre Julliard's avatar
Alexandre Julliard committed
797 798
    DOSFS_CloseDir( dir );
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
799 800 801 802
}


/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
803
 *           DOSFS_GetDevice
Alexandre Julliard's avatar
Alexandre Julliard committed
804
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
805
 * Check if a DOS file name represents a DOS device and return the device.
Alexandre Julliard's avatar
Alexandre Julliard committed
806
 */
807
const DOS_DEVICE *DOSFS_GetDevice( LPCWSTR name )
Alexandre Julliard's avatar
Alexandre Julliard committed
808
{
809
    unsigned int i;
810
    const WCHAR *p;
Alexandre Julliard's avatar
Alexandre Julliard committed
811

Alexandre Julliard's avatar
Alexandre Julliard committed
812
    if (!name) return NULL; /* if FILE_DupUnixHandle was used */
Alexandre Julliard's avatar
Alexandre Julliard committed
813
    if (name[0] && (name[1] == ':')) name += 2;
814 815
    if ((p = strrchrW( name, '/' ))) name = p + 1;
    if ((p = strrchrW( name, '\\' ))) name = p + 1;
Alexandre Julliard's avatar
Alexandre Julliard committed
816 817
    for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
    {
818 819
        const WCHAR *dev = DOSFS_Devices[i].name;
        if (!strncmpiW( dev, name, strlenW(dev) ))
Alexandre Julliard's avatar
Alexandre Julliard committed
820
        {
821
            p = name + strlenW( dev );
822
            if (!*p || (*p == '.') || (*p == ':')) return &DOSFS_Devices[i];
Alexandre Julliard's avatar
Alexandre Julliard committed
823 824
        }
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
825
    return NULL;
Alexandre Julliard's avatar
Alexandre Julliard committed
826 827
}

828 829 830 831

/***********************************************************************
 *           DOSFS_GetDeviceByHandle
 */
832
const DOS_DEVICE *DOSFS_GetDeviceByHandle( HANDLE hFile )
833
{
834
    const DOS_DEVICE *ret = NULL;
835
    SERVER_START_REQ( get_device_id )
836
    {
837
        req->handle = hFile;
838
        if (!wine_server_call( req ))
839
        {
840 841 842
            if ((reply->id >= 0) &&
                (reply->id < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0])))
                ret = &DOSFS_Devices[reply->id];
843
        }
844
    }
845 846
    SERVER_END_REQ;
    return ret;
847 848 849
}


850 851 852
/**************************************************************************
 *         DOSFS_CreateCommPort
 */
853
static HANDLE DOSFS_CreateCommPort(LPCWSTR name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa)
854
{
855
    HANDLE ret;
856
    char devname[40];
857 858 859
    WCHAR devnameW[40];
    static const WCHAR serialportsW[] = {'s','e','r','i','a','l','p','o','r','t','s',0};
    static const WCHAR empty_strW[] = { 0 };
860

861
    TRACE_(file)("%s %lx %lx\n", debugstr_w(name), access, attributes);
862

863 864
    PROFILE_GetWineIniString(serialportsW, name, empty_strW, devnameW, 40);
    if(!devnameW[0])
865 866
        return 0;

867 868 869
    WideCharToMultiByte(CP_ACP, 0, devnameW, -1, devname, sizeof(devname), NULL, NULL);

    TRACE("opening %s as %s\n", devname, debugstr_w(name));
870

871
    SERVER_START_REQ( create_serial )
872 873
    {
        req->access  = access;
874
        req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
875
        req->attributes = attributes;
876
        req->sharing = FILE_SHARE_READ|FILE_SHARE_WRITE;
877
        wine_server_add_data( req, devname, strlen(devname) );
878
        SetLastError(0);
879 880
        wine_server_call_err( req );
        ret = reply->handle;
881
    }
882
    SERVER_END_REQ;
883

884
    if(!ret)
Andreas Mohr's avatar
Andreas Mohr committed
885
        ERR("Couldn't open device '%s' ! (check permissions)\n",devname);
886
    else
887
        TRACE("return %p\n", ret );
888
    return ret;
889 890
}

Alexandre Julliard's avatar
Alexandre Julliard committed
891 892 893 894
/***********************************************************************
 *           DOSFS_OpenDevice
 *
 * Open a DOS device. This might not map 1:1 into the UNIX device concept.
895
 * Returns 0 on failure.
Alexandre Julliard's avatar
Alexandre Julliard committed
896
 */
897
HANDLE DOSFS_OpenDevice( LPCWSTR name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa )
Alexandre Julliard's avatar
Alexandre Julliard committed
898
{
899
    unsigned int i;
900
    const WCHAR *p;
901
    HANDLE handle;
Alexandre Julliard's avatar
Alexandre Julliard committed
902

Alexandre Julliard's avatar
Alexandre Julliard committed
903
    if (name[0] && (name[1] == ':')) name += 2;
904 905
    if ((p = strrchrW( name, '/' ))) name = p + 1;
    if ((p = strrchrW( name, '\\' ))) name = p + 1;
Alexandre Julliard's avatar
Alexandre Julliard committed
906 907
    for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
    {
908 909
        const WCHAR *dev = DOSFS_Devices[i].name;
        if (!strncmpiW( dev, name, strlenW(dev) ))
Alexandre Julliard's avatar
Alexandre Julliard committed
910
        {
911
            p = name + strlenW( dev );
912
            if (!*p || (*p == '.') || (*p == ':')) {
913 914 915 916 917
		static const WCHAR nulW[] = {'N','U','L',0};
		static const WCHAR conW[] = {'C','O','N',0};
		static const WCHAR scsimgrW[] = {'S','C','S','I','M','G','R','$',0};
		static const WCHAR hpscanW[] = {'H','P','S','C','A','N',0};
		static const WCHAR emmxxxx0W[] = {'E','M','M','X','X','X','X','0',0};
Alexandre Julliard's avatar
Alexandre Julliard committed
918
	    	/* got it */
919
		if (!strcmpiW(DOSFS_Devices[i].name, nulW))
920
                    return FILE_CreateFile( "/dev/null", access,
921
                                            FILE_SHARE_READ|FILE_SHARE_WRITE, sa,
922
                                            OPEN_EXISTING, 0, 0, TRUE, DRIVE_UNKNOWN );
923
		if (!strcmpiW(DOSFS_Devices[i].name, conW)) {
924
			HANDLE to_dup;
925 926
			switch (access & (GENERIC_READ|GENERIC_WRITE)) {
			case GENERIC_READ:
Alexandre Julliard's avatar
Alexandre Julliard committed
927
				to_dup = GetStdHandle( STD_INPUT_HANDLE );
Alexandre Julliard's avatar
Alexandre Julliard committed
928
				break;
929
			case GENERIC_WRITE:
Alexandre Julliard's avatar
Alexandre Julliard committed
930
				to_dup = GetStdHandle( STD_OUTPUT_HANDLE );
Alexandre Julliard's avatar
Alexandre Julliard committed
931 932
				break;
			default:
933
				FIXME("can't open CON read/write\n");
934
				return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
935
			}
Alexandre Julliard's avatar
Alexandre Julliard committed
936
			if (!DuplicateHandle( GetCurrentProcess(), to_dup, GetCurrentProcess(),
937 938
					      &handle, 0,
					      sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle,
939
					      DUPLICATE_SAME_ACCESS ))
940
			    handle = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
941
			return handle;
Alexandre Julliard's avatar
Alexandre Julliard committed
942
		}
943 944 945
		if (!strcmpiW(DOSFS_Devices[i].name, scsimgrW) ||
                    !strcmpiW(DOSFS_Devices[i].name, hpscanW) ||
                    !strcmpiW(DOSFS_Devices[i].name, emmxxxx0W))
946
                {
947
                    return FILE_CreateDevice( i, access, sa );
Alexandre Julliard's avatar
Alexandre Julliard committed
948
		}
949

950
                if( (handle=DOSFS_CreateCommPort(DOSFS_Devices[i].name,access,attributes,sa)) )
951
                    return handle;
952
                FIXME("device open %s not supported (yet)\n", debugstr_w(DOSFS_Devices[i].name));
953
    		return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
954
	    }
Alexandre Julliard's avatar
Alexandre Julliard committed
955 956
        }
    }
957
    return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
958 959
}

Alexandre Julliard's avatar
Alexandre Julliard committed
960

Alexandre Julliard's avatar
Alexandre Julliard committed
961
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
962
 *           DOSFS_GetPathDrive
Alexandre Julliard's avatar
Alexandre Julliard committed
963
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
964
 * Get the drive specified by a given path name (DOS or Unix format).
Alexandre Julliard's avatar
Alexandre Julliard committed
965
 */
966
static int DOSFS_GetPathDrive( LPCWSTR *name )
Alexandre Julliard's avatar
Alexandre Julliard committed
967
{
Alexandre Julliard's avatar
Alexandre Julliard committed
968
    int drive;
969
    LPCWSTR p = *name;
Alexandre Julliard's avatar
Alexandre Julliard committed
970

Alexandre Julliard's avatar
Alexandre Julliard committed
971
    if (*p && (p[1] == ':'))
Alexandre Julliard's avatar
Alexandre Julliard committed
972
    {
973
        drive = toupperW(*p) - 'A';
Alexandre Julliard's avatar
Alexandre Julliard committed
974
        *name += 2;
Alexandre Julliard's avatar
Alexandre Julliard committed
975
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
976
    else if (*p == '/') /* Absolute Unix path? */
Alexandre Julliard's avatar
Alexandre Julliard committed
977
    {
978
        if ((drive = DRIVE_FindDriveRootW( name )) == -1)
Alexandre Julliard's avatar
Alexandre Julliard committed
979
        {
980
            MESSAGE("Warning: %s not accessible from a configured DOS drive\n", debugstr_w(*name) );
Alexandre Julliard's avatar
Alexandre Julliard committed
981
            /* Assume it really was a DOS name */
982
            drive = DRIVE_GetCurrentDrive();
Alexandre Julliard's avatar
Alexandre Julliard committed
983 984 985 986 987 988
        }
    }
    else drive = DRIVE_GetCurrentDrive();

    if (!DRIVE_IsValid(drive))
    {
989
        SetLastError( ERROR_INVALID_DRIVE );
Alexandre Julliard's avatar
Alexandre Julliard committed
990
        return -1;
Alexandre Julliard's avatar
Alexandre Julliard committed
991
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
992 993 994 995 996
    return drive;
}


/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
997
 *           DOSFS_GetFullName
Alexandre Julliard's avatar
Alexandre Julliard committed
998
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
999 1000 1001
 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
 * Unix name / short DOS name pair.
 * Return FALSE if one of the path components does not exist. The last path
Alexandre Julliard's avatar
Alexandre Julliard committed
1002
 * component is only checked if 'check_last' is non-zero.
Alexandre Julliard's avatar
Alexandre Julliard committed
1003 1004
 * The buffers pointed to by 'long_buf' and 'short_buf' must be
 * at least MAX_PATHNAME_LEN long.
Alexandre Julliard's avatar
Alexandre Julliard committed
1005
 */
1006
BOOL DOSFS_GetFullName( LPCWSTR name, BOOL check_last, DOS_FULL_NAME *full )
Alexandre Julliard's avatar
Alexandre Julliard committed
1007
{
1008
    BOOL found;
1009 1010 1011 1012 1013
    UINT flags, codepage;
    char *p_l, *root;
    LPWSTR p_s;
    static const WCHAR driveA_rootW[] = {'A',':','\\',0};
    static const WCHAR dos_rootW[] = {'\\',0};
Alexandre Julliard's avatar
Alexandre Julliard committed
1014

1015
    TRACE("%s (last=%d)\n", debugstr_w(name), check_last );
Alexandre Julliard's avatar
Alexandre Julliard committed
1016

1017 1018 1019 1020 1021 1022
    if ((!*name) || (*name=='\n'))
    { /* error code for Win98 */
        SetLastError(ERROR_BAD_PATHNAME);
        return FALSE;
    }

Alexandre Julliard's avatar
Alexandre Julliard committed
1023 1024
    if ((full->drive = DOSFS_GetPathDrive( &name )) == -1) return FALSE;
    flags = DRIVE_GetFlags( full->drive );
1025
    codepage = DRIVE_GetCodepage(full->drive);
Alexandre Julliard's avatar
Alexandre Julliard committed
1026

1027
    lstrcpynA( full->long_name, DRIVE_GetRoot( full->drive ),
Alexandre Julliard's avatar
Alexandre Julliard committed
1028 1029 1030
                 sizeof(full->long_name) );
    if (full->long_name[1]) root = full->long_name + strlen(full->long_name);
    else root = full->long_name;  /* root directory */
Alexandre Julliard's avatar
Alexandre Julliard committed
1031

1032
    strcpyW( full->short_name, driveA_rootW );
Alexandre Julliard's avatar
Alexandre Julliard committed
1033
    full->short_name[0] += full->drive;
Alexandre Julliard's avatar
Alexandre Julliard committed
1034

Alexandre Julliard's avatar
Alexandre Julliard committed
1035
    if ((*name == '\\') || (*name == '/'))  /* Absolute path */
Alexandre Julliard's avatar
Alexandre Julliard committed
1036 1037 1038
    {
        while ((*name == '\\') || (*name == '/')) name++;
    }
1039
    else  /* Relative path */
Alexandre Julliard's avatar
Alexandre Julliard committed
1040
    {
1041
        lstrcpynA( root + 1, DRIVE_GetUnixCwd( full->drive ),
Alexandre Julliard's avatar
Alexandre Julliard committed
1042
                     sizeof(full->long_name) - (root - full->long_name) - 1 );
Alexandre Julliard's avatar
Alexandre Julliard committed
1043
        if (root[1]) *root = '/';
1044 1045
        lstrcpynW( full->short_name + 3, DRIVE_GetDosCwd( full->drive ),
                   sizeof(full->short_name)/sizeof(full->short_name[0]) - 3 );
Alexandre Julliard's avatar
Alexandre Julliard committed
1046 1047
    }

Alexandre Julliard's avatar
Alexandre Julliard committed
1048 1049
    p_l = full->long_name[1] ? full->long_name + strlen(full->long_name)
                             : full->long_name;
1050
    p_s = full->short_name[3] ? full->short_name + strlenW(full->short_name)
Alexandre Julliard's avatar
Alexandre Julliard committed
1051
                              : full->short_name + 2;
Alexandre Julliard's avatar
Alexandre Julliard committed
1052
    found = TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
1053

Alexandre Julliard's avatar
Alexandre Julliard committed
1054 1055
    while (*name && found)
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
1056 1057 1058
        /* Check for '.' and '..' */

        if (*name == '.')
Alexandre Julliard's avatar
Alexandre Julliard committed
1059
        {
Alexandre Julliard's avatar
Alexandre Julliard committed
1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074
            if (IS_END_OF_NAME(name[1]))
            {
                name++;
                while ((*name == '\\') || (*name == '/')) name++;
                continue;
            }
            else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
            {
                name += 2;
                while ((*name == '\\') || (*name == '/')) name++;
                while ((p_l > root) && (*p_l != '/')) p_l--;
                while ((p_s > full->short_name + 2) && (*p_s != '\\')) p_s--;
                *p_l = *p_s = '\0';  /* Remove trailing separator */
                continue;
            }
Alexandre Julliard's avatar
Alexandre Julliard committed
1075
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
1076 1077 1078

        /* Make sure buffers are large enough */

1079
        if ((p_s >= full->short_name + sizeof(full->short_name)/sizeof(full->short_name[0]) - 14) ||
Alexandre Julliard's avatar
Alexandre Julliard committed
1080
            (p_l >= full->long_name + sizeof(full->long_name) - 1))
Alexandre Julliard's avatar
Alexandre Julliard committed
1081
        {
1082
            SetLastError( ERROR_PATH_NOT_FOUND );
Alexandre Julliard's avatar
Alexandre Julliard committed
1083
            return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
1084
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
1085 1086 1087

        /* Get the long and short name matching the file name */

1088
        if ((found = DOSFS_FindUnixName( full, name, p_l + 1,
Alexandre Julliard's avatar
Alexandre Julliard committed
1089 1090
                         sizeof(full->long_name) - (p_l - full->long_name) - 1,
                         p_s + 1, !(flags & DRIVE_CASE_SENSITIVE) )))
Alexandre Julliard's avatar
Alexandre Julliard committed
1091
        {
Alexandre Julliard's avatar
Alexandre Julliard committed
1092 1093 1094
            *p_l++ = '/';
            p_l   += strlen(p_l);
            *p_s++ = '\\';
1095
            p_s   += strlenW(p_s);
Alexandre Julliard's avatar
Alexandre Julliard committed
1096 1097
            while (!IS_END_OF_NAME(*name)) name++;
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
1098
        else if (!check_last)
Alexandre Julliard's avatar
Alexandre Julliard committed
1099
        {
Alexandre Julliard's avatar
Alexandre Julliard committed
1100 1101 1102
            *p_l++ = '/';
            *p_s++ = '\\';
            while (!IS_END_OF_NAME(*name) &&
1103
                   (p_s < full->short_name + sizeof(full->short_name)/sizeof(full->short_name[0]) - 1) &&
Alexandre Julliard's avatar
Alexandre Julliard committed
1104 1105
                   (p_l < full->long_name + sizeof(full->long_name) - 1))
            {
1106 1107
                WCHAR wch;
                *p_s++ = tolowerW(*name);
Alexandre Julliard's avatar
Alexandre Julliard committed
1108 1109 1110
                /* If the drive is case-sensitive we want to create new */
                /* files in lower-case otherwise we can't reopen them   */
                /* under the same short name. */
1111 1112 1113
                if (flags & DRIVE_CASE_SENSITIVE) wch = tolowerW(*name);
                else wch = *name;
                p_l += WideCharToMultiByte(codepage, 0, &wch, 1, p_l, 2, NULL, NULL);
Alexandre Julliard's avatar
Alexandre Julliard committed
1114 1115
                name++;
            }
1116 1117 1118 1119 1120
	    /* Ignore trailing dots and spaces */
	    while(p_l[-1] == '.' || p_l[-1] == ' ') {
		--p_l;
		--p_s;
	    }
1121 1122
            *p_l = '\0';
            *p_s = '\0';
Alexandre Julliard's avatar
Alexandre Julliard committed
1123 1124 1125
        }
        while ((*name == '\\') || (*name == '/')) name++;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1126

Alexandre Julliard's avatar
Alexandre Julliard committed
1127 1128
    if (!found)
    {
1129
        if (check_last)
Alexandre Julliard's avatar
Alexandre Julliard committed
1130
        {
1131
            SetLastError( ERROR_FILE_NOT_FOUND );
Alexandre Julliard's avatar
Alexandre Julliard committed
1132
            return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
1133
        }
1134
        if (*name)  /* Not last */
Alexandre Julliard's avatar
Alexandre Julliard committed
1135
        {
1136
            SetLastError( ERROR_PATH_NOT_FOUND );
Alexandre Julliard's avatar
Alexandre Julliard committed
1137
            return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
1138 1139
        }
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1140
    if (!full->long_name[0]) strcpy( full->long_name, "/" );
1141 1142
    if (!full->short_name[2]) strcpyW( full->short_name + 2, dos_rootW );
    TRACE("returning %s = %s\n", full->long_name, debugstr_w(full->short_name) );
Alexandre Julliard's avatar
Alexandre Julliard committed
1143
    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
1144 1145 1146 1147
}


/***********************************************************************
1148
 *           GetShortPathNameW   (KERNEL32.@)
1149 1150 1151 1152
 *
 * NOTES
 *  observed:
 *  longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1153
 *  longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0
1154
 *
1155 1156
 * more observations ( with NT 3.51 (WinDD) ):
 * longpath <= 8.3 -> just copy longpath to shortpath
1157
 * longpath > 8.3  ->
1158 1159 1160 1161
 *             a) file does not exist -> return 0, LastError = ERROR_FILE_NOT_FOUND
 *             b) file does exist     -> set the short filename.
 * - trailing slashes are reproduced in the short name, even if the
 *   file is not a directory
Peter Ganten's avatar
Peter Ganten committed
1162 1163
 * - the absolute/relative path of the short name is reproduced like found
 *   in the long name
1164
 * - longpath and shortpath may have the same address
1165
 * Peter Ganten, 1999
Alexandre Julliard's avatar
Alexandre Julliard committed
1166
 */
1167
DWORD WINAPI GetShortPathNameW( LPCWSTR longpath, LPWSTR shortpath, DWORD shortlen )
Alexandre Julliard's avatar
Alexandre Julliard committed
1168 1169
{
    DOS_FULL_NAME full_name;
1170 1171
    WCHAR tmpshortpath[MAX_PATHNAME_LEN];
    const WCHAR *p;
Peter Ganten's avatar
Peter Ganten committed
1172
    DWORD sp = 0, lp = 0;
1173 1174
    int drive;
    DWORD tmplen;
Peter Ganten's avatar
Peter Ganten committed
1175
    UINT flags;
1176
    BOOL unixabsolute = *longpath == '/';
Peter Ganten's avatar
Peter Ganten committed
1177

1178
    TRACE("%s\n", debugstr_w(longpath));
1179 1180 1181 1182 1183 1184 1185 1186

    if (!longpath) {
      SetLastError(ERROR_INVALID_PARAMETER);
      return 0;
    }
    if (!longpath[0]) {
      SetLastError(ERROR_BAD_PATHNAME);
      return 0;
1187 1188
    }

1189 1190 1191 1192 1193 1194 1195
    /* check for drive letter */
    if (!unixabsolute && longpath[1] == ':' ) {
      tmpshortpath[0] = longpath[0];
      tmpshortpath[1] = ':';
      sp = 2;
    }

Peter Ganten's avatar
Peter Ganten committed
1196 1197
    if ( ( drive = DOSFS_GetPathDrive ( &longpath )) == -1 ) return 0;
    flags = DRIVE_GetFlags ( drive );
1198

1199 1200 1201 1202 1203
    if (unixabsolute && drive != DRIVE_GetCurrentDrive()) {
      tmpshortpath[0] = drive + 'A';
      tmpshortpath[1] = ':';
      sp = 2;
    }
1204

Peter Ganten's avatar
Peter Ganten committed
1205 1206 1207 1208
    while ( longpath[lp] ) {

      /* check for path delimiters and reproduce them */
      if ( longpath[lp] == '\\' || longpath[lp] == '/' ) {
1209
	if (!sp || tmpshortpath[sp-1]!= '\\')
1210 1211 1212 1213 1214
        {
	    /* strip double "\\" */
	    tmpshortpath[sp] = '\\';
	    sp++;
        }
1215
        tmpshortpath[sp]=0;/*terminate string*/
Peter Ganten's avatar
Peter Ganten committed
1216
	lp++;
1217 1218 1219
	continue;
      }

1220 1221 1222 1223
      tmplen = 0;
      for(p = longpath + lp; *p && *p != '/' && *p != '\\'; p++)
          tmplen++;
      lstrcpynW(tmpshortpath + sp, longpath + lp, tmplen + 1);
1224

Peter Ganten's avatar
Peter Ganten committed
1225 1226 1227 1228 1229
      /* Check, if the current element is a valid dos name */
      if ( DOSFS_ValidDOSName ( longpath + lp, !(flags & DRIVE_CASE_SENSITIVE) ) ) {
	sp += tmplen;
	lp += tmplen;
	continue;
1230
      }
Peter Ganten's avatar
Peter Ganten committed
1231 1232 1233

      /* Check if the file exists and use the existing file name */
      if ( DOSFS_GetFullName ( tmpshortpath, TRUE, &full_name ) ) {
1234 1235
	strcpyW(tmpshortpath + sp, strrchrW(full_name.short_name, '\\') + 1);
	sp += strlenW(tmpshortpath + sp);
Peter Ganten's avatar
Peter Ganten committed
1236 1237 1238 1239 1240 1241 1242
	lp += tmplen;
	continue;
      }

      TRACE("not found!\n" );
      SetLastError ( ERROR_FILE_NOT_FOUND );
      return 0;
1243
    }
1244
    tmpshortpath[sp] = 0;
Peter Ganten's avatar
Peter Ganten committed
1245

1246 1247 1248 1249 1250 1251 1252
    tmplen = strlenW(tmpshortpath) + 1;
    if (tmplen <= shortlen)
    {
        strcpyW(shortpath, tmpshortpath);
        TRACE("returning %s\n", debugstr_w(shortpath));
        tmplen--; /* length without 0 */
    }
1253

Peter Ganten's avatar
Peter Ganten committed
1254
    return tmplen;
Alexandre Julliard's avatar
Alexandre Julliard committed
1255 1256 1257 1258
}


/***********************************************************************
1259
 *           GetShortPathNameA   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1260
 */
1261
DWORD WINAPI GetShortPathNameA( LPCSTR longpath, LPSTR shortpath, DWORD shortlen )
Alexandre Julliard's avatar
Alexandre Julliard committed
1262
{
1263 1264 1265 1266 1267 1268 1269 1270 1271
    UNICODE_STRING longpathW;
    WCHAR shortpathW[MAX_PATH];
    DWORD ret, retW;

    if (!longpath)
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return 0;
    }
1272

1273 1274 1275 1276 1277 1278 1279
    TRACE("%s\n", debugstr_a(longpath));

    if (!RtlCreateUnicodeStringFromAsciiz(&longpathW, longpath))
    {
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        return 0;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1280

1281
    retW = GetShortPathNameW(longpathW.Buffer, shortpathW, MAX_PATH);
1282

1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300
    if (!retW)
        ret = 0;
    else if (retW > MAX_PATH)
    {
        SetLastError(ERROR_FILENAME_EXCED_RANGE);
        ret = 0;
    }
    else
    {
        ret = WideCharToMultiByte(CP_ACP, 0, shortpathW, -1, NULL, 0, NULL, NULL);
        if (ret <= shortlen)
        {
            WideCharToMultiByte(CP_ACP, 0, shortpathW, -1, shortpath, shortlen, NULL, NULL);
            ret--; /* length without 0 */
        }
    }

    RtlFreeUnicodeString(&longpathW);
1301
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
1302 1303 1304
}


Alexandre Julliard's avatar
Alexandre Julliard committed
1305
/***********************************************************************
1306
 *           GetLongPathNameW   (KERNEL32.@)
1307 1308 1309 1310 1311
 *
 * NOTES
 *  observed (Win2000):
 *  shortpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
 *  shortpath="":   LastError=ERROR_PATH_NOT_FOUND, ret=0
Alexandre Julliard's avatar
Alexandre Julliard committed
1312
 */
1313
DWORD WINAPI GetLongPathNameW( LPCWSTR shortpath, LPWSTR longpath, DWORD longlen )
Alexandre Julliard's avatar
Alexandre Julliard committed
1314 1315
{
    DOS_FULL_NAME full_name;
1316 1317 1318 1319 1320
    const char *root;
    LPWSTR p;
    int drive;
    UINT codepage;
    DWORD ret, len = 0;
1321 1322 1323 1324 1325 1326 1327 1328 1329 1330

    if (!shortpath) {
      SetLastError(ERROR_INVALID_PARAMETER);
      return 0;
    }
    if (!shortpath[0]) {
      SetLastError(ERROR_PATH_NOT_FOUND);
      return 0;
    }

1331 1332
    TRACE("%s,%p,%ld\n", debugstr_w(shortpath), longpath, longlen);

1333 1334
    if(shortpath[0]=='\\' && shortpath[1]=='\\')
    {
1335 1336 1337
        ERR("UNC pathname %s\n",debugstr_w(shortpath));
        lstrcpynW( longpath, full_name.short_name, longlen );
        return strlenW(longpath);
1338 1339
    }

Alexandre Julliard's avatar
Alexandre Julliard committed
1340
    if (!DOSFS_GetFullName( shortpath, TRUE, &full_name )) return 0;
Petr Tomasek's avatar
Petr Tomasek committed
1341

1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362
    root = full_name.long_name;
    drive = DRIVE_FindDriveRoot(&root);
    codepage = DRIVE_GetCodepage(drive);

    ret = MultiByteToWideChar(codepage, 0, root, -1, NULL, 0);
    ret += 3; /* A:\ */
    /* reproduce terminating slash */
    if (ret > 4) /* if not drive root */
    {
        len = strlenW(shortpath);
        if (shortpath[len - 1] == '\\' || shortpath[len - 1] == '/')
            len = 1;
    }
    ret += len;
    if (ret <= longlen)
    {
        longpath[0] = 'A' + drive;
        longpath[1] = ':';
        MultiByteToWideChar(codepage, 0, root, -1, longpath + 2, longlen - 2);
        for (p = longpath; *p; p++) if (*p == '/') *p = '\\';
        if (len)
Petr Tomasek's avatar
Petr Tomasek committed
1363
        {
1364 1365
            longpath[ret - 2] = '\\';
            longpath[ret - 1] = 0;
Petr Tomasek's avatar
Petr Tomasek committed
1366
        }
1367 1368
        TRACE("returning %s\n", debugstr_w(longpath));
        ret--; /* length without 0 */
1369
    }
1370
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
1371 1372 1373 1374
}


/***********************************************************************
1375
 *           GetLongPathNameA   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1376
 */
1377
DWORD WINAPI GetLongPathNameA( LPCSTR shortpath, LPSTR longpath, DWORD longlen )
Alexandre Julliard's avatar
Alexandre Julliard committed
1378
{
1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395
    UNICODE_STRING shortpathW;
    WCHAR longpathW[MAX_PATH];
    DWORD ret, retW;

    if (!shortpath)
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return 0;
    }

    TRACE("%s\n", debugstr_a(shortpath));

    if (!RtlCreateUnicodeStringFromAsciiz(&shortpathW, shortpath))
    {
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        return 0;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1396

1397 1398 1399 1400 1401 1402 1403 1404 1405 1406
    retW = GetLongPathNameW(shortpathW.Buffer, longpathW, MAX_PATH);

    if (!retW)
        ret = 0;
    else if (retW > MAX_PATH)
    {
        SetLastError(ERROR_FILENAME_EXCED_RANGE);
        ret = 0;
    }
    else
Alexandre Julliard's avatar
Alexandre Julliard committed
1407
    {
1408 1409 1410 1411 1412 1413
        ret = WideCharToMultiByte(CP_ACP, 0, longpathW, -1, NULL, 0, NULL, NULL);
        if (ret <= longlen)
        {
            WideCharToMultiByte(CP_ACP, 0, longpathW, -1, longpath, longlen, NULL, NULL);
            ret--; /* length without 0 */
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
1414
    }
1415 1416

    RtlFreeUnicodeString(&shortpathW);
Alexandre Julliard's avatar
Alexandre Julliard committed
1417 1418 1419 1420
    return ret;
}


Alexandre Julliard's avatar
Alexandre Julliard committed
1421 1422
/***********************************************************************
 *           DOSFS_DoGetFullPathName
Alexandre Julliard's avatar
Alexandre Julliard committed
1423
 *
1424
 * Implementation of GetFullPathNameA/W.
Uwe Bonnes's avatar
Uwe Bonnes committed
1425
 *
1426
 * bon@elektron 000331:
1427
 * A test for GetFullPathName with many pathological cases
Huw D M Davies's avatar
Huw D M Davies committed
1428
 * now gives identical output for Wine and OSR2
Alexandre Julliard's avatar
Alexandre Julliard committed
1429
 */
1430
static DWORD DOSFS_DoGetFullPathName( LPCWSTR name, DWORD len, LPWSTR result )
Alexandre Julliard's avatar
Alexandre Julliard committed
1431
{
1432
    DWORD ret;
1433
    DOS_FULL_NAME full_name;
1434 1435
    LPWSTR p, q;
    char *p_l;
1436
    const char * root;
1437 1438
    WCHAR drivecur[] = {'C',':','.',0};
    WCHAR driveletter=0;
1439
    int namelen,drive=0;
1440 1441 1442 1443 1444
    static const WCHAR bkslashW[] = {'\\',0};
    static const WCHAR dotW[] = {'.',0};
    static const WCHAR updir_slashW[] = {'\\','.','.','\\',0};
    static const WCHAR curdirW[] = {'\\','.','\\',0};
    static const WCHAR updirW[] = {'\\','.','.',0};
1445

1446 1447 1448 1449 1450
    if (!name[0])
    {
        SetLastError(ERROR_BAD_PATHNAME);
        return 0;
    }
1451

1452
    TRACE("passed %s\n", debugstr_w(name));
1453

1454 1455
    if (name[1]==':')
      /*drive letter given */
1456 1457 1458
      {
	driveletter = name[0];
      }
1459 1460
    if ((name[1]==':') && ((name[2]=='\\') || (name[2]=='/')))
      /*absolute path given */
Uwe Bonnes's avatar
Uwe Bonnes committed
1461
      {
1462 1463 1464
        strncpyW(full_name.short_name, name, MAX_PATHNAME_LEN);
        full_name.short_name[MAX_PATHNAME_LEN - 1] = 0; /* ensure 0 termination */
        drive = toupperW(name[0]) - 'A';
1465 1466 1467 1468 1469
      }
    else
      {
	if (driveletter)
	  drivecur[0]=driveletter;
1470
        else if ((name[0]=='\\') || (name[0]=='/'))
1471
          strcpyW(drivecur, bkslashW);
1472
        else
1473
	  strcpyW(drivecur, dotW);
1474

1475 1476 1477 1478 1479 1480
	if (!DOSFS_GetFullName( drivecur, FALSE, &full_name ))
	  {
	    FIXME("internal: error getting drive/path\n");
	    return 0;
	  }
	/* find path that drive letter substitutes*/
1481
	drive = toupperW(full_name.short_name[0]) - 'A';
1482
	root= DRIVE_GetRoot(drive);
1483 1484 1485 1486 1487
	if (!root)
	  {
	    FIXME("internal: error getting DOS Drive Root\n");
	    return 0;
	  }
1488 1489 1490
	if (!strcmp(root,"/"))
	  {
	    /* we have just the last / and we need it. */
1491
	    p_l = full_name.long_name;
1492 1493 1494
	  }
	else
	  {
1495
	    p_l = full_name.long_name + strlen(root);
1496
	  }
1497
	/* append long name (= unix name) to drive */
1498 1499
	MultiByteToWideChar(DRIVE_GetCodepage(drive), 0, p_l, -1,
                            full_name.short_name + 2, MAX_PATHNAME_LEN - 3);
1500
	/* append name to treat */
1501 1502
	namelen= strlenW(full_name.short_name);
	p = (LPWSTR)name;
1503
	if (driveletter)
1504 1505
	  p += 2; /* skip drive name when appending */
	if (namelen + 2 + strlenW(p) > MAX_PATHNAME_LEN)
1506 1507 1508 1509 1510 1511
	  {
	    FIXME("internal error: buffer too small\n");
	     return 0;
	  }
	full_name.short_name[namelen++] ='\\';
	full_name.short_name[namelen] = 0;
1512 1513
	strncpyW(full_name.short_name + namelen, p, MAX_PATHNAME_LEN - namelen);
	full_name.short_name[MAX_PATHNAME_LEN - 1] = 0; /* ensure 0 termination */
1514 1515 1516
      }
    /* reverse all slashes */
    for (p=full_name.short_name;
1517
	 p < full_name.short_name + strlenW(full_name.short_name);
1518 1519 1520 1521 1522
	 p++)
      {
	if ( *p == '/' )
	  *p = '\\';
      }
1523
     /* Use memmove, as areas overlap */
1524
     /* Delete .. */
1525
    while ((p = strstrW(full_name.short_name, updir_slashW)))
1526 1527 1528 1529
      {
	if (p > full_name.short_name+2)
	  {
	    *p = 0;
1530 1531
            q = strrchrW(full_name.short_name, '\\');
            memmove(q+1, p+4, (strlenW(p+4)+1) * sizeof(WCHAR));
1532 1533 1534
	  }
	else
	  {
1535
            memmove(full_name.short_name+3, p+4, (strlenW(p+4)+1) * sizeof(WCHAR));
1536 1537 1538 1539 1540 1541
	  }
      }
    if ((full_name.short_name[2]=='.')&&(full_name.short_name[3]=='.'))
	{
	  /* This case istn't treated yet : c:..\test */
	  memmove(full_name.short_name+2,full_name.short_name+4,
1542
                  (strlenW(full_name.short_name+4)+1) * sizeof(WCHAR));
1543 1544
	}
     /* Delete . */
1545
    while ((p = strstrW(full_name.short_name, curdirW)))
1546 1547
      {
	*(p+1) = 0;
1548
        memmove(p+1, p+3, (strlenW(p+3)+1) * sizeof(WCHAR));
Uwe Bonnes's avatar
Uwe Bonnes committed
1549
      }
Alexandre Julliard's avatar
Alexandre Julliard committed
1550
    if (!(DRIVE_GetFlags(drive) & DRIVE_CASE_PRESERVING))
1551 1552 1553
        for (p = full_name.short_name; *p; p++) *p = toupperW(*p);
    namelen = strlenW(full_name.short_name);
    if (!strcmpW(full_name.short_name+namelen-3, updirW))
1554
	{
1555
	  /* one more strange case: "c:\test\test1\.."
1556
	   return "c:\test" */
1557
	  *(full_name.short_name+namelen-3)=0;
1558
          q = strrchrW(full_name.short_name, '\\');
1559 1560 1561 1562 1563 1564 1565
	  *q =0;
	}
    if (full_name.short_name[namelen-1]=='.')
	full_name.short_name[(namelen--)-1] =0;
    if (!driveletter)
      if (full_name.short_name[namelen-1]=='\\')
	full_name.short_name[(namelen--)-1] =0;
1566
    TRACE("got %s\n", debugstr_w(full_name.short_name));
1567

1568 1569
    /* If the lpBuffer buffer is too small, the return value is the
    size of the buffer, in characters, required to hold the path
1570
    plus the terminating \0 (tested against win95osr2, bon 001118)
Uwe Bonnes's avatar
Uwe Bonnes committed
1571
    . */
1572
    ret = strlenW(full_name.short_name);
Uwe Bonnes's avatar
Uwe Bonnes committed
1573 1574 1575 1576 1577 1578
    if (ret >= len )
      {
	/* don't touch anything when the buffer is not large enough */
        SetLastError( ERROR_INSUFFICIENT_BUFFER );
	return ret+1;
      }
1579 1580
    if (result)
    {
1581 1582
        strncpyW( result, full_name.short_name, len );
        result[len - 1] = 0; /* ensure 0 termination */
1583
    }
1584

1585
    TRACE("returning %s\n", debugstr_w(full_name.short_name) );
1586
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
1587 1588 1589 1590
}


/***********************************************************************
1591
 *           GetFullPathNameA   (KERNEL32.@)
1592
 * NOTES
1593
 *   if the path closed with '\', *lastpart is 0
Alexandre Julliard's avatar
Alexandre Julliard committed
1594
 */
1595
DWORD WINAPI GetFullPathNameA( LPCSTR name, DWORD len, LPSTR buffer,
Alexandre Julliard's avatar
Alexandre Julliard committed
1596
                                 LPSTR *lastpart )
Alexandre Julliard's avatar
Alexandre Julliard committed
1597
{
1598 1599 1600 1601 1602
    UNICODE_STRING nameW;
    WCHAR bufferW[MAX_PATH];
    DWORD ret, retW;

    if (!name)
Alexandre Julliard's avatar
Alexandre Julliard committed
1603
    {
1604 1605 1606
        SetLastError(ERROR_INVALID_PARAMETER);
        return 0;
    }
1607

1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626
    if (!RtlCreateUnicodeStringFromAsciiz(&nameW, name))
    {
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        return 0;
    }

    retW = GetFullPathNameW( nameW.Buffer, MAX_PATH, bufferW, NULL);

    if (!retW)
        ret = 0;
    else if (retW > MAX_PATH)
    {
        SetLastError(ERROR_FILENAME_EXCED_RANGE);
        ret = 0;
    }
    else
    {
        ret = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL);
        if (ret <= len)
1627
        {
1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642
            WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, len, NULL, NULL);
            ret--; /* length without 0 */

            if (lastpart)
            {
                LPSTR p = buffer + strlen(buffer);

                if (*p != '\\')
                {
                    while ((p > buffer + 2) && (*p != '\\')) p--;
                    *lastpart = p + 1;
                }
                else *lastpart = NULL;
            }
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
1643
    }
1644 1645

    RtlFreeUnicodeString(&nameW);
Alexandre Julliard's avatar
Alexandre Julliard committed
1646 1647 1648 1649 1650
    return ret;
}


/***********************************************************************
1651
 *           GetFullPathNameW   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1652
 */
1653
DWORD WINAPI GetFullPathNameW( LPCWSTR name, DWORD len, LPWSTR buffer,
Alexandre Julliard's avatar
Alexandre Julliard committed
1654
                                 LPWSTR *lastpart )
Alexandre Julliard's avatar
Alexandre Julliard committed
1655
{
1656
    DWORD ret = DOSFS_DoGetFullPathName( name, len, buffer );
Uwe Bonnes's avatar
Uwe Bonnes committed
1657
    if (ret && (ret<=len) && buffer && lastpart)
Alexandre Julliard's avatar
Alexandre Julliard committed
1658
    {
1659
        LPWSTR p = buffer + strlenW(buffer);
1660 1661 1662 1663 1664
        if (*p != (WCHAR)'\\')
        {
            while ((p > buffer + 2) && (*p != (WCHAR)'\\')) p--;
            *lastpart = p + 1;
        }
1665
        else *lastpart = NULL;
Alexandre Julliard's avatar
Alexandre Julliard committed
1666 1667
    }
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
1668 1669
}

1670 1671

/***********************************************************************
Patrik Stridvall's avatar
Patrik Stridvall committed
1672
 *           wine_get_unix_file_name (KERNEL32.@) Not a Windows API
1673 1674
 *
 * Return the full Unix file name for a given path.
1675
 * FIXME: convert dos file name to unicode
1676 1677 1678 1679 1680
 */
BOOL WINAPI wine_get_unix_file_name( LPCSTR dos, LPSTR buffer, DWORD len )
{
    BOOL ret;
    DOS_FULL_NAME path;
1681 1682 1683 1684 1685 1686 1687 1688 1689
    WCHAR dosW[MAX_PATHNAME_LEN];

    MultiByteToWideChar(CP_ACP, 0, dos, -1, dosW, MAX_PATHNAME_LEN);
    ret = DOSFS_GetFullName( dosW, FALSE, &path );
    if (ret && len)
    {
        strncpy( buffer, path.long_name, len );
        buffer[len - 1] = 0; /* ensure 0 termination */
    }
1690 1691 1692 1693
    return ret;
}


Alexandre Julliard's avatar
Alexandre Julliard committed
1694
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
1695
 *           DOSFS_FindNextEx
Alexandre Julliard's avatar
Alexandre Julliard committed
1696
 */
1697
static int DOSFS_FindNextEx( FIND_FIRST_INFO *info, WIN32_FIND_DATAW *entry )
Alexandre Julliard's avatar
Alexandre Julliard committed
1698
{
1699
    DWORD attr = info->attr | FA_UNUSED | FA_ARCHIVE | FA_RDONLY;
1700
    UINT flags = DRIVE_GetFlags( info->drive );
Alexandre Julliard's avatar
Alexandre Julliard committed
1701 1702 1703
    char *p, buffer[MAX_PATHNAME_LEN];
    const char *drive_path;
    int drive_root;
1704
    LPCWSTR long_name, short_name;
Alexandre Julliard's avatar
Alexandre Julliard committed
1705
    BY_HANDLE_FILE_INFORMATION fileinfo;
1706
    WCHAR dos_name[13];
1707
    BOOL is_symlink;
Alexandre Julliard's avatar
Alexandre Julliard committed
1708

Alexandre Julliard's avatar
Alexandre Julliard committed
1709
    if ((info->attr & ~(FA_UNUSED | FA_ARCHIVE | FA_RDONLY)) == FA_LABEL)
Alexandre Julliard's avatar
Alexandre Julliard committed
1710
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
1711
        if (info->cur_pos) return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1712
        entry->dwFileAttributes  = FILE_ATTRIBUTE_LABEL;
1713 1714 1715
        RtlSecondsSince1970ToTime( (time_t)0, (LARGE_INTEGER *)&entry->ftCreationTime );
        RtlSecondsSince1970ToTime( (time_t)0, (LARGE_INTEGER *)&entry->ftLastAccessTime );
        RtlSecondsSince1970ToTime( (time_t)0, (LARGE_INTEGER *)&entry->ftLastWriteTime );
Alexandre Julliard's avatar
Alexandre Julliard committed
1716 1717 1718 1719
        entry->nFileSizeHigh     = 0;
        entry->nFileSizeLow      = 0;
        entry->dwReserved0       = 0;
        entry->dwReserved1       = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1720
        DOSFS_ToDosDTAFormat( DRIVE_GetLabel( info->drive ), entry->cFileName );
1721
        strcpyW( entry->cAlternateFileName, entry->cFileName );
Alexandre Julliard's avatar
Alexandre Julliard committed
1722
        info->cur_pos++;
1723
        TRACE("returning %s (%s) as label\n",
1724
               debugstr_w(entry->cFileName), debugstr_w(entry->cAlternateFileName));
Alexandre Julliard's avatar
Alexandre Julliard committed
1725 1726 1727
        return 1;
    }

Alexandre Julliard's avatar
Alexandre Julliard committed
1728 1729 1730 1731
    drive_path = info->path + strlen(DRIVE_GetRoot( info->drive ));
    while ((*drive_path == '/') || (*drive_path == '\\')) drive_path++;
    drive_root = !*drive_path;

1732
    lstrcpynA( buffer, info->path, sizeof(buffer) - 1 );
Alexandre Julliard's avatar
Alexandre Julliard committed
1733 1734 1735
    strcat( buffer, "/" );
    p = buffer + strlen(buffer);

1736
    while (DOSFS_ReadDir( info->u.dos_dir, &long_name, &short_name ))
Alexandre Julliard's avatar
Alexandre Julliard committed
1737
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
1738
        info->cur_pos++;
Alexandre Julliard's avatar
Alexandre Julliard committed
1739

Alexandre Julliard's avatar
Alexandre Julliard committed
1740
        /* Don't return '.' and '..' in the root of the drive */
Alexandre Julliard's avatar
Alexandre Julliard committed
1741 1742 1743
        if (drive_root && (long_name[0] == '.') &&
            (!long_name[1] || ((long_name[1] == '.') && !long_name[2])))
            continue;
Alexandre Julliard's avatar
Alexandre Julliard committed
1744

Alexandre Julliard's avatar
Alexandre Julliard committed
1745 1746
        /* Check the long mask */

1747
        if (info->long_mask && *info->long_mask)
Alexandre Julliard's avatar
Alexandre Julliard committed
1748
        {
Alexandre Julliard's avatar
Alexandre Julliard committed
1749
            if (!DOSFS_MatchLong( info->long_mask, long_name,
Alexandre Julliard's avatar
Alexandre Julliard committed
1750 1751 1752 1753 1754
                                  flags & DRIVE_CASE_SENSITIVE )) continue;
        }

        /* Check the short mask */

Alexandre Julliard's avatar
Alexandre Julliard committed
1755
        if (info->short_mask)
Alexandre Julliard's avatar
Alexandre Julliard committed
1756
        {
Alexandre Julliard's avatar
Alexandre Julliard committed
1757
            if (!short_name)
Alexandre Julliard's avatar
Alexandre Julliard committed
1758 1759 1760 1761 1762
            {
                DOSFS_Hash( long_name, dos_name, TRUE,
                            !(flags & DRIVE_CASE_SENSITIVE) );
                short_name = dos_name;
            }
Alexandre Julliard's avatar
Alexandre Julliard committed
1763
            if (!DOSFS_MatchShort( info->short_mask, short_name )) continue;
Alexandre Julliard's avatar
Alexandre Julliard committed
1764 1765 1766
        }

        /* Check the file attributes */
1767 1768
        WideCharToMultiByte(DRIVE_GetCodepage(info->drive), 0, long_name, -1,
                            p, sizeof(buffer) - (int)(p - buffer), NULL, NULL);
1769
        if (!FILE_Stat( buffer, &fileinfo, &is_symlink ))
Alexandre Julliard's avatar
Alexandre Julliard committed
1770
        {
1771
            WARN("can't stat %s\n", buffer);
Alexandre Julliard's avatar
Alexandre Julliard committed
1772 1773
            continue;
        }
1774
        if (is_symlink && (fileinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
1775
        {
1776 1777
            static const WCHAR wineW[] = {'w','i','n','e',0};
            static const WCHAR ShowDirSymlinksW[] = {'S','h','o','w','D','i','r','S','y','m','l','i','n','k','s',0};
1778 1779
            static int show_dir_symlinks = -1;
            if (show_dir_symlinks == -1)
1780
                show_dir_symlinks = PROFILE_GetWineIniBool(wineW, ShowDirSymlinksW, 0);
1781 1782 1783
            if (!show_dir_symlinks) continue;
        }

Alexandre Julliard's avatar
Alexandre Julliard committed
1784
        if (fileinfo.dwFileAttributes & ~attr) continue;
Alexandre Julliard's avatar
Alexandre Julliard committed
1785 1786 1787

        /* We now have a matching entry; fill the result and return */

Alexandre Julliard's avatar
Alexandre Julliard committed
1788 1789 1790 1791 1792 1793
        entry->dwFileAttributes = fileinfo.dwFileAttributes;
        entry->ftCreationTime   = fileinfo.ftCreationTime;
        entry->ftLastAccessTime = fileinfo.ftLastAccessTime;
        entry->ftLastWriteTime  = fileinfo.ftLastWriteTime;
        entry->nFileSizeHigh    = fileinfo.nFileSizeHigh;
        entry->nFileSizeLow     = fileinfo.nFileSizeLow;
Alexandre Julliard's avatar
Alexandre Julliard committed
1794 1795 1796 1797 1798 1799 1800

        if (short_name)
            DOSFS_ToDosDTAFormat( short_name, entry->cAlternateFileName );
        else
            DOSFS_Hash( long_name, entry->cAlternateFileName, FALSE,
                        !(flags & DRIVE_CASE_SENSITIVE) );

1801 1802
        lstrcpynW( entry->cFileName, long_name, sizeof(entry->cFileName)/sizeof(entry->cFileName[0]) );
        if (!(flags & DRIVE_CASE_PRESERVING)) strlwrW( entry->cFileName );
1803
        TRACE("returning %s (%s) %02lx %ld\n",
1804
              debugstr_w(entry->cFileName), debugstr_w(entry->cAlternateFileName),
1805
              entry->dwFileAttributes, entry->nFileSizeLow );
Alexandre Julliard's avatar
Alexandre Julliard committed
1806
        return 1;
Alexandre Julliard's avatar
Alexandre Julliard committed
1807 1808 1809
    }
    return 0;  /* End of directory */
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1810

Alexandre Julliard's avatar
Alexandre Julliard committed
1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825
/***********************************************************************
 *           DOSFS_FindNext
 *
 * Find the next matching file. Return the number of entries read to find
 * the matching one, or 0 if no more entries.
 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
 * file name mask. Either or both can be NULL.
 *
 * NOTE: This is supposed to be only called by the int21 emulation
 *       routines. Thus, we should own the Win16Mutex anyway.
 *       Nevertheless, we explicitly enter it to ensure the static
 *       directory cache is protected.
 */
int DOSFS_FindNext( const char *path, const char *short_mask,
                    const char *long_mask, int drive, BYTE attr,
1826
                    int skip, WIN32_FIND_DATAA *entry )
Alexandre Julliard's avatar
Alexandre Julliard committed
1827
{
1828
    static FIND_FIRST_INFO info;
1829
    LPCWSTR short_name, long_name;
Alexandre Julliard's avatar
Alexandre Julliard committed
1830
    int count;
1831 1832
    UNICODE_STRING short_maskW, long_maskW;
    WIN32_FIND_DATAW entryW;
Alexandre Julliard's avatar
Alexandre Julliard committed
1833

1834 1835 1836 1837
    TRACE("(%s, %s, %s, %x, %x, %x, %p)\n", debugstr_a(path),
          debugstr_a(short_mask), debugstr_a(long_mask), drive, attr, skip,
          entry);

1838
    _EnterWin16Lock();
Alexandre Julliard's avatar
Alexandre Julliard committed
1839

1840 1841 1842
    RtlCreateUnicodeStringFromAsciiz(&short_maskW, short_mask);
    RtlCreateUnicodeStringFromAsciiz(&long_maskW, long_mask);

Alexandre Julliard's avatar
Alexandre Julliard committed
1843
    /* Check the cached directory */
1844 1845
    if (!(info.u.dos_dir && info.path == path && !strcmpW(info.short_mask, short_maskW.Buffer)
                   && !strcmpW(info.long_mask, long_maskW.Buffer) && info.drive == drive
Alexandre Julliard's avatar
Alexandre Julliard committed
1846
                   && info.attr == attr && info.cur_pos <= skip))
1847
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
1848
        /* Not in the cache, open it anew */
1849
        if (info.u.dos_dir) DOSFS_CloseDir( info.u.dos_dir );
Alexandre Julliard's avatar
Alexandre Julliard committed
1850 1851

        info.path = (LPSTR)path;
1852 1853 1854 1855
        RtlFreeHeap(GetProcessHeap(), 0, info.long_mask);
        RtlFreeHeap(GetProcessHeap(), 0, info.short_mask);
        info.long_mask = long_maskW.Buffer;
        info.short_mask = short_maskW.Buffer;
Alexandre Julliard's avatar
Alexandre Julliard committed
1856 1857 1858
        info.attr = attr;
        info.drive = drive;
        info.cur_pos = 0;
1859 1860 1861 1862 1863 1864
        info.u.dos_dir = DOSFS_OpenDir( DRIVE_GetCodepage(drive), info.path );
    }
    else
    {
        RtlFreeUnicodeString(&short_maskW);
        RtlFreeUnicodeString(&long_maskW);
Alexandre Julliard's avatar
Alexandre Julliard committed
1865 1866 1867 1868
    }

    /* Skip to desired position */
    while (info.cur_pos < skip)
1869
        if (info.u.dos_dir && DOSFS_ReadDir( info.u.dos_dir, &long_name, &short_name ))
Alexandre Julliard's avatar
Alexandre Julliard committed
1870 1871 1872 1873
            info.cur_pos++;
        else
            break;

1874 1875 1876 1877 1878 1879
    if (info.u.dos_dir && info.cur_pos == skip && DOSFS_FindNextEx( &info, &entryW ))
    {
        WideCharToMultiByte(CP_ACP, 0, entryW.cFileName, -1,
                            entry->cFileName, sizeof(entry->cFileName), NULL, NULL);
        WideCharToMultiByte(CP_ACP, 0, entryW.cAlternateFileName, -1,
                            entry->cAlternateFileName, sizeof(entry->cAlternateFileName), NULL, NULL);
Alexandre Julliard's avatar
Alexandre Julliard committed
1880
        count = info.cur_pos - skip;
1881 1882 1883 1884 1885 1886 1887 1888

        entry->dwFileAttributes = entryW.dwFileAttributes;
        entry->nFileSizeHigh    = entryW.nFileSizeHigh;
        entry->nFileSizeLow     = entryW.nFileSizeLow;
        entry->ftCreationTime   = entryW.ftCreationTime;
        entry->ftLastAccessTime = entryW.ftLastAccessTime;
        entry->ftLastWriteTime  = entryW.ftLastWriteTime;

1889
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1890 1891 1892 1893 1894
    else
        count = 0;

    if (!count)
    {
1895
        if (info.u.dos_dir) DOSFS_CloseDir( info.u.dos_dir );
Alexandre Julliard's avatar
Alexandre Julliard committed
1896 1897 1898
        memset( &info, '\0', sizeof(info) );
    }

1899
    _LeaveWin16Lock();
Alexandre Julliard's avatar
Alexandre Julliard committed
1900 1901 1902 1903

    return count;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1904
/*************************************************************************
1905
 *           FindFirstFileExW  (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1906
 */
1907 1908
HANDLE WINAPI FindFirstFileExW(
	LPCWSTR lpFileName,
1909 1910 1911 1912 1913
	FINDEX_INFO_LEVELS fInfoLevelId,
	LPVOID lpFindFileData,
	FINDEX_SEARCH_OPS fSearchOp,
	LPVOID lpSearchFilter,
	DWORD dwAdditionalFlags)
Alexandre Julliard's avatar
Alexandre Julliard committed
1914
{
1915
    HGLOBAL handle;
Alexandre Julliard's avatar
Alexandre Julliard committed
1916
    FIND_FIRST_INFO *info;
1917

1918 1919 1920 1921 1922 1923
    if (!lpFileName)
    {
        SetLastError(ERROR_PATH_NOT_FOUND);
        return INVALID_HANDLE_VALUE;
    }

1924 1925 1926 1927 1928
    if ((fSearchOp != FindExSearchNameMatch) || (dwAdditionalFlags != 0))
    {
        FIXME("options not implemented 0x%08x 0x%08lx\n", fSearchOp, dwAdditionalFlags );
        return INVALID_HANDLE_VALUE;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1929

1930
    switch(fInfoLevelId)
Alexandre Julliard's avatar
Alexandre Julliard committed
1931
    {
1932 1933
      case FindExInfoStandard:
        {
1934 1935 1936 1937 1938
          WIN32_FIND_DATAW * data = (WIN32_FIND_DATAW *) lpFindFileData;
          char *p;
          INT long_mask_len;
          UINT codepage;

1939 1940
          data->dwReserved0 = data->dwReserved1 = 0x0;
          if (!lpFileName) return 0;
1941 1942 1943 1944 1945 1946 1947
          if (lpFileName[0] == '\\' && lpFileName[1] == '\\')
          {
              ERR("UNC path name\n");
              if (!(handle = GlobalAlloc(GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO)))) break;

              info = (FIND_FIRST_INFO *)GlobalLock( handle );
              info->u.smb_dir = SMB_FindFirst(lpFileName);
1948
              if(!info->u.smb_dir)
1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962
              {
                 GlobalUnlock( handle );
                 GlobalFree(handle);
                 break;
              }

              info->drive = -1;

              GlobalUnlock( handle );
          }
          else
          {
            DOS_FULL_NAME full_name;

1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982
            if (!DOSFS_GetFullName( lpFileName, FALSE, &full_name )) break;
            if (!(handle = GlobalAlloc(GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO)))) break;
            info = (FIND_FIRST_INFO *)GlobalLock( handle );
            info->path = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
            strcpy( info->path, full_name.long_name );

            codepage = DRIVE_GetCodepage(full_name.drive);
            p = strrchr( info->path, '/' );
            *p++ = '\0';
            long_mask_len = MultiByteToWideChar(codepage, 0, p, -1, NULL, 0);
            info->long_mask = HeapAlloc( GetProcessHeap(), 0, long_mask_len * sizeof(WCHAR) );
            MultiByteToWideChar(codepage, 0, p, -1, info->long_mask, long_mask_len);

            info->short_mask = NULL;
            info->attr = 0xff;
            info->drive = full_name.drive;
            info->cur_pos = 0;

            info->u.dos_dir = DOSFS_OpenDir( codepage, info->path );
            GlobalUnlock( handle );
1983
          }
1984
          if (!FindNextFileW( handle, data ))
1985 1986 1987 1988 1989 1990 1991 1992 1993 1994
          {
              FindClose( handle );
              SetLastError( ERROR_NO_MORE_FILES );
              break;
          }
          return handle;
        }
        break;
      default:
        FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
Alexandre Julliard's avatar
Alexandre Julliard committed
1995
    }
1996
    return INVALID_HANDLE_VALUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
1997 1998 1999
}

/*************************************************************************
2000
 *           FindFirstFileA   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
2001
 */
2002 2003 2004
HANDLE WINAPI FindFirstFileA(
	LPCSTR lpFileName,
	WIN32_FIND_DATAA *lpFindData )
Alexandre Julliard's avatar
Alexandre Julliard committed
2005
{
2006 2007
    return FindFirstFileExA(lpFileName, FindExInfoStandard, lpFindData,
                            FindExSearchNameMatch, NULL, 0);
Alexandre Julliard's avatar
Alexandre Julliard committed
2008 2009 2010
}

/*************************************************************************
2011
 *           FindFirstFileExA   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
2012
 */
2013 2014
HANDLE WINAPI FindFirstFileExA(
	LPCSTR lpFileName,
2015 2016 2017 2018 2019
	FINDEX_INFO_LEVELS fInfoLevelId,
	LPVOID lpFindFileData,
	FINDEX_SEARCH_OPS fSearchOp,
	LPVOID lpSearchFilter,
	DWORD dwAdditionalFlags)
Alexandre Julliard's avatar
Alexandre Julliard committed
2020
{
2021
    HANDLE handle;
2022 2023 2024
    WIN32_FIND_DATAA *dataA;
    WIN32_FIND_DATAW dataW;
    UNICODE_STRING pathW;
2025

2026
    if (!lpFileName)
2027
    {
2028 2029
        SetLastError(ERROR_PATH_NOT_FOUND);
        return INVALID_HANDLE_VALUE;
2030 2031
    }

2032
    if (!RtlCreateUnicodeStringFromAsciiz(&pathW, lpFileName))
Alexandre Julliard's avatar
Alexandre Julliard committed
2033
    {
2034
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2035
        return INVALID_HANDLE_VALUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
2036
    }
2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052

    handle = FindFirstFileExW(pathW.Buffer, fInfoLevelId, &dataW, fSearchOp, lpSearchFilter, dwAdditionalFlags);
    RtlFreeUnicodeString(&pathW);
    if (handle == INVALID_HANDLE_VALUE) return handle;

    dataA = (WIN32_FIND_DATAA *) lpFindFileData;
    dataA->dwFileAttributes = dataW.dwFileAttributes;
    dataA->ftCreationTime   = dataW.ftCreationTime;
    dataA->ftLastAccessTime = dataW.ftLastAccessTime;
    dataA->ftLastWriteTime  = dataW.ftLastWriteTime;
    dataA->nFileSizeHigh    = dataW.nFileSizeHigh;
    dataA->nFileSizeLow     = dataW.nFileSizeLow;
    WideCharToMultiByte( CP_ACP, 0, dataW.cFileName, -1,
                         dataA->cFileName, sizeof(dataA->cFileName), NULL, NULL );
    WideCharToMultiByte( CP_ACP, 0, dataW.cAlternateFileName, -1,
                         dataA->cAlternateFileName, sizeof(dataA->cAlternateFileName), NULL, NULL );
Alexandre Julliard's avatar
Alexandre Julliard committed
2053 2054 2055
    return handle;
}

2056
/*************************************************************************
2057
 *           FindFirstFileW   (KERNEL32.@)
2058 2059 2060 2061 2062 2063
 */
HANDLE WINAPI FindFirstFileW( LPCWSTR lpFileName, WIN32_FIND_DATAW *lpFindData )
{
    return FindFirstFileExW(lpFileName, FindExInfoStandard, lpFindData,
                            FindExSearchNameMatch, NULL, 0);
}
Alexandre Julliard's avatar
Alexandre Julliard committed
2064 2065

/*************************************************************************
2066
 *           FindNextFileW   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
2067
 */
2068
BOOL WINAPI FindNextFileW( HANDLE handle, WIN32_FIND_DATAW *data )
Alexandre Julliard's avatar
Alexandre Julliard committed
2069 2070
{
    FIND_FIRST_INFO *info;
2071
    BOOL ret = FALSE;
2072
    DWORD gle = ERROR_NO_MORE_FILES;
Alexandre Julliard's avatar
Alexandre Julliard committed
2073

2074
    if ((handle == INVALID_HANDLE_VALUE) ||
2075
       !(info = (FIND_FIRST_INFO *)GlobalLock( handle )))
Alexandre Julliard's avatar
Alexandre Julliard committed
2076
    {
2077
        SetLastError( ERROR_INVALID_HANDLE );
2078
        return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
2079
    }
2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090
    if (info->drive == -1)
    {
        ret = SMB_FindNext( info->u.smb_dir, data );
        if(!ret)
        {
            SMB_CloseDir( info->u.smb_dir );
            HeapFree( GetProcessHeap(), 0, info->path );
        }
        goto done;
    }
    else if (!info->path || !info->u.dos_dir)
Alexandre Julliard's avatar
Alexandre Julliard committed
2091
    {
2092
        goto done;
Alexandre Julliard's avatar
Alexandre Julliard committed
2093
    }
2094
    else if (!DOSFS_FindNextEx( info, data ))
Alexandre Julliard's avatar
Alexandre Julliard committed
2095
    {
2096
        DOSFS_CloseDir( info->u.dos_dir ); info->u.dos_dir = NULL;
2097
        HeapFree( GetProcessHeap(), 0, info->path );
2098 2099 2100 2101
        info->path = NULL;
        HeapFree( GetProcessHeap(), 0, info->long_mask );
        info->long_mask = NULL;
        goto done;
Alexandre Julliard's avatar
Alexandre Julliard committed
2102
    }
2103
    ret = TRUE;
2104 2105
done:
    GlobalUnlock( handle );
2106
    if( !ret ) SetLastError( gle );
2107
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
2108 2109 2110 2111
}


/*************************************************************************
2112
 *           FindNextFileA   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
2113
 */
2114
BOOL WINAPI FindNextFileA( HANDLE handle, WIN32_FIND_DATAA *data )
Alexandre Julliard's avatar
Alexandre Julliard committed
2115
{
2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126
    WIN32_FIND_DATAW dataW;
    if (!FindNextFileW( handle, &dataW )) return FALSE;
    data->dwFileAttributes = dataW.dwFileAttributes;
    data->ftCreationTime   = dataW.ftCreationTime;
    data->ftLastAccessTime = dataW.ftLastAccessTime;
    data->ftLastWriteTime  = dataW.ftLastWriteTime;
    data->nFileSizeHigh    = dataW.nFileSizeHigh;
    data->nFileSizeLow     = dataW.nFileSizeLow;
    WideCharToMultiByte( CP_ACP, 0, dataW.cFileName, -1,
                         data->cFileName, sizeof(data->cFileName), NULL, NULL );
    WideCharToMultiByte( CP_ACP, 0, dataW.cAlternateFileName, -1,
2127
                         data->cAlternateFileName,
2128
                         sizeof(data->cAlternateFileName), NULL, NULL );
Alexandre Julliard's avatar
Alexandre Julliard committed
2129 2130 2131 2132
    return TRUE;
}

/*************************************************************************
2133
 *           FindClose   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
2134
 */
2135
BOOL WINAPI FindClose( HANDLE handle )
Alexandre Julliard's avatar
Alexandre Julliard committed
2136 2137 2138
{
    FIND_FIRST_INFO *info;

2139 2140
    if (handle == INVALID_HANDLE_VALUE) goto error;

2141 2142
    __TRY
    {
2143 2144
        if ((info = (FIND_FIRST_INFO *)GlobalLock( handle )))
        {
2145
            if (info->u.dos_dir) DOSFS_CloseDir( info->u.dos_dir );
2146
            if (info->path) HeapFree( GetProcessHeap(), 0, info->path );
2147
            if (info->long_mask) HeapFree( GetProcessHeap(), 0, info->long_mask );
2148
        }
2149 2150 2151
    }
    __EXCEPT(page_fault)
    {
2152
        WARN("Illegal handle %p\n", handle);
2153 2154 2155 2156
        SetLastError( ERROR_INVALID_HANDLE );
        return FALSE;
    }
    __ENDTRY
2157
    if (!info) goto error;
2158 2159
    GlobalUnlock( handle );
    GlobalFree( handle );
Alexandre Julliard's avatar
Alexandre Julliard committed
2160
    return TRUE;
2161 2162 2163 2164

 error:
    SetLastError( ERROR_INVALID_HANDLE );
    return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
2165 2166
}

Alexandre Julliard's avatar
Alexandre Julliard committed
2167
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
2168 2169 2170 2171
 *           DOSFS_UnixTimeToFileTime
 *
 * Convert a Unix time to FILETIME format.
 * The FILETIME structure is a 64-bit value representing the number of
Alexandre Julliard's avatar
Alexandre Julliard committed
2172 2173 2174 2175
 * 100-nanosecond intervals since January 1, 1601, 0:00.
 * 'remainder' is the nonnegative number of 100-ns intervals
 * corresponding to the time fraction smaller than 1 second that
 * couldn't be stored in the time_t value.
Alexandre Julliard's avatar
Alexandre Julliard committed
2176
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
2177 2178
void DOSFS_UnixTimeToFileTime( time_t unix_time, FILETIME *filetime,
                               DWORD remainder )
Alexandre Julliard's avatar
Alexandre Julliard committed
2179
{
Alexandre Julliard's avatar
Alexandre Julliard committed
2180 2181
    /* NOTES:

2182
       CONSTANTS:
Alexandre Julliard's avatar
Alexandre Julliard committed
2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208
       The time difference between 1 January 1601, 00:00:00 and
       1 January 1970, 00:00:00 is 369 years, plus the leap years
       from 1604 to 1968, excluding 1700, 1800, 1900.
       This makes (1968 - 1600) / 4 - 3 = 89 leap days, and a total
       of 134774 days.

       Any day in that period had 24 * 60 * 60 = 86400 seconds.

       The time difference is 134774 * 86400 * 10000000, which can be written
       116444736000000000
       27111902 * 2^32 + 3577643008
       413 * 2^48 + 45534 * 2^32 + 54590 * 2^16 + 32768

       If you find that these constants are buggy, please change them in all
       instances in both conversion functions.

       VERSIONS:
       There are two versions, one of them uses long long variables and
       is presumably faster but not ISO C. The other one uses standard C
       data types and operations but relies on the assumption that negative
       numbers are stored as 2's complement (-1 is 0xffff....). If this
       assumption is violated, dates before 1970 will not convert correctly.
       This should however work on any reasonable architecture where WINE
       will run.

       DETAILS:
2209

Alexandre Julliard's avatar
Alexandre Julliard committed
2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231
       Take care not to remove the casts. I have tested these functions
       (in both versions) for a lot of numbers. I would be interested in
       results on other compilers than GCC.

       The operations have been designed to account for the possibility
       of 64-bit time_t in future UNICES. Even the versions without
       internal long long numbers will work if time_t only is 64 bit.
       A 32-bit shift, which was necessary for that operation, turned out
       not to work correctly in GCC, besides giving the warning. So I
       used a double 16-bit shift instead. Numbers are in the ISO version
       represented by three limbs, the most significant with 32 bit, the
       other two with 16 bit each.

       As the modulo-operator % is not well-defined for negative numbers,
       negative divisors have been avoided in DOSFS_FileTimeToUnixTime.

       There might be quicker ways to do this in C. Certainly so in
       assembler.

       Claus Fischer, fischer@iue.tuwien.ac.at
       */

2232
#if SIZEOF_LONG_LONG >= 8
Alexandre Julliard's avatar
Alexandre Julliard committed
2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243
#  define USE_LONG_LONG 1
#else
#  define USE_LONG_LONG 0
#endif

#if USE_LONG_LONG		/* gcc supports long long type */

    long long int t = unix_time;
    t *= 10000000;
    t += 116444736000000000LL;
    t += remainder;
2244 2245
    filetime->dwLowDateTime  = (UINT)t;
    filetime->dwHighDateTime = (UINT)(t >> 32);
Alexandre Julliard's avatar
Alexandre Julliard committed
2246 2247 2248

#else  /* ISO version */

2249 2250 2251
    UINT a0;			/* 16 bit, low    bits */
    UINT a1;			/* 16 bit, medium bits */
    UINT a2;			/* 32 bit, high   bits */
Alexandre Julliard's avatar
Alexandre Julliard committed
2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286

    /* Copy the unix time to a2/a1/a0 */
    a0 =  unix_time & 0xffff;
    a1 = (unix_time >> 16) & 0xffff;
    /* This is obsolete if unix_time is only 32 bits, but it does not hurt.
       Do not replace this by >> 32, it gives a compiler warning and it does
       not work. */
    a2 = (unix_time >= 0 ? (unix_time >> 16) >> 16 :
	  ~((~unix_time >> 16) >> 16));

    /* Multiply a by 10000000 (a = a2/a1/a0)
       Split the factor into 10000 * 1000 which are both less than 0xffff. */
    a0 *= 10000;
    a1 = a1 * 10000 + (a0 >> 16);
    a2 = a2 * 10000 + (a1 >> 16);
    a0 &= 0xffff;
    a1 &= 0xffff;

    a0 *= 1000;
    a1 = a1 * 1000 + (a0 >> 16);
    a2 = a2 * 1000 + (a1 >> 16);
    a0 &= 0xffff;
    a1 &= 0xffff;

    /* Add the time difference and the remainder */
    a0 += 32768 + (remainder & 0xffff);
    a1 += 54590 + (remainder >> 16   ) + (a0 >> 16);
    a2 += 27111902                     + (a1 >> 16);
    a0 &= 0xffff;
    a1 &= 0xffff;

    /* Set filetime */
    filetime->dwLowDateTime  = (a1 << 16) + a0;
    filetime->dwHighDateTime = a2;
#endif
Alexandre Julliard's avatar
Alexandre Julliard committed
2287
}
Alexandre Julliard's avatar
Alexandre Julliard committed
2288

Alexandre Julliard's avatar
Alexandre Julliard committed
2289

Alexandre Julliard's avatar
Alexandre Julliard committed
2290
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
2291 2292 2293
 *           DOSFS_FileTimeToUnixTime
 *
 * Convert a FILETIME format to Unix time.
Alexandre Julliard's avatar
Alexandre Julliard committed
2294 2295
 * If not NULL, 'remainder' contains the fractional part of the filetime,
 * in the range of [0..9999999] (even if time_t is negative).
Alexandre Julliard's avatar
Alexandre Julliard committed
2296
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
2297
time_t DOSFS_FileTimeToUnixTime( const FILETIME *filetime, DWORD *remainder )
Alexandre Julliard's avatar
Alexandre Julliard committed
2298
{
Alexandre Julliard's avatar
Alexandre Julliard committed
2299 2300 2301 2302 2303
    /* Read the comment in the function DOSFS_UnixTimeToFileTime. */
#if USE_LONG_LONG

    long long int t = filetime->dwHighDateTime;
    t <<= 32;
2304
    t += (UINT)filetime->dwLowDateTime;
Alexandre Julliard's avatar
Alexandre Julliard committed
2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318
    t -= 116444736000000000LL;
    if (t < 0)
    {
	if (remainder) *remainder = 9999999 - (-t - 1) % 10000000;
	return -1 - ((-t - 1) / 10000000);
    }
    else
    {
	if (remainder) *remainder = t % 10000000;
	return t / 10000000;
    }

#else  /* ISO version */

2319 2320 2321 2322
    UINT a0;			/* 16 bit, low    bits */
    UINT a1;			/* 16 bit, medium bits */
    UINT a2;			/* 32 bit, high   bits */
    UINT r;			/* remainder of division */
Alexandre Julliard's avatar
Alexandre Julliard committed
2323 2324 2325 2326
    unsigned int carry;		/* carry bit for subtraction */
    int negative;		/* whether a represents a negative value */

    /* Copy the time values to a2/a1/a0 */
2327 2328 2329
    a2 =  (UINT)filetime->dwHighDateTime;
    a1 = ((UINT)filetime->dwLowDateTime ) >> 16;
    a0 = ((UINT)filetime->dwLowDateTime ) & 0xffff;
Alexandre Julliard's avatar
Alexandre Julliard committed
2330 2331 2332 2333 2334 2335 2336 2337 2338

    /* Subtract the time difference */
    if (a0 >= 32768           ) a0 -=             32768        , carry = 0;
    else                        a0 += (1 << 16) - 32768        , carry = 1;

    if (a1 >= 54590    + carry) a1 -=             54590 + carry, carry = 0;
    else                        a1 += (1 << 16) - 54590 - carry, carry = 1;

    a2 -= 27111902 + carry;
2339

Alexandre Julliard's avatar
Alexandre Julliard committed
2340
    /* If a is negative, replace a by (-1-a) */
2341
    negative = (a2 >= ((UINT)1) << 31);
Alexandre Julliard's avatar
Alexandre Julliard committed
2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382
    if (negative)
    {
	/* Set a to -a - 1 (a is a2/a1/a0) */
	a0 = 0xffff - a0;
	a1 = 0xffff - a1;
	a2 = ~a2;
    }

    /* Divide a by 10000000 (a = a2/a1/a0), put the rest into r.
       Split the divisor into 10000 * 1000 which are both less than 0xffff. */
    a1 += (a2 % 10000) << 16;
    a2 /=       10000;
    a0 += (a1 % 10000) << 16;
    a1 /=       10000;
    r   =  a0 % 10000;
    a0 /=       10000;

    a1 += (a2 % 1000) << 16;
    a2 /=       1000;
    a0 += (a1 % 1000) << 16;
    a1 /=       1000;
    r  += (a0 % 1000) * 10000;
    a0 /=       1000;

    /* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
    if (negative)
    {
	/* Set a to -a - 1 (a is a2/a1/a0) */
	a0 = 0xffff - a0;
	a1 = 0xffff - a1;
	a2 = ~a2;

        r  = 9999999 - r;
    }

    if (remainder) *remainder = r;

    /* Do not replace this by << 32, it gives a compiler warning and it does
       not work. */
    return ((((time_t)a2) << 16) << 16) + (a1 << 16) + a0;
#endif
Alexandre Julliard's avatar
Alexandre Julliard committed
2383 2384
}

Alexandre Julliard's avatar
Alexandre Julliard committed
2385

2386
/***********************************************************************
2387
 *           MulDiv   (KERNEL32.@)
2388 2389 2390 2391 2392
 * RETURNS
 *	Result of multiplication and division
 *	-1: Overflow occurred or Divisor was 0
 */
INT WINAPI MulDiv(
2393
	     INT nMultiplicand,
2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431
	     INT nMultiplier,
	     INT nDivisor)
{
#if SIZEOF_LONG_LONG >= 8
    long long ret;

    if (!nDivisor) return -1;

    /* We want to deal with a positive divisor to simplify the logic. */
    if (nDivisor < 0)
    {
      nMultiplicand = - nMultiplicand;
      nDivisor = -nDivisor;
    }

    /* If the result is positive, we "add" to round. else, we subtract to round. */
    if ( ( (nMultiplicand <  0) && (nMultiplier <  0) ) ||
	 ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
      ret = (((long long)nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
    else
      ret = (((long long)nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;

    if ((ret > 2147483647) || (ret < -2147483647)) return -1;
    return ret;
#else
    if (!nDivisor) return -1;

    /* We want to deal with a positive divisor to simplify the logic. */
    if (nDivisor < 0)
    {
      nMultiplicand = - nMultiplicand;
      nDivisor = -nDivisor;
    }

    /* If the result is positive, we "add" to round. else, we subtract to round. */
    if ( ( (nMultiplicand <  0) && (nMultiplier <  0) ) ||
	 ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
      return ((nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
2432

2433
    return ((nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
2434

2435 2436 2437 2438
#endif
}


Alexandre Julliard's avatar
Alexandre Julliard committed
2439
/***********************************************************************
2440
 *           DosDateTimeToFileTime   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
2441
 */
2442
BOOL WINAPI DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft)
Alexandre Julliard's avatar
Alexandre Julliard committed
2443
{
Alexandre Julliard's avatar
Alexandre Julliard committed
2444
    struct tm newtm;
2445 2446 2447 2448
#ifndef HAVE_TIMEGM
    struct tm *gtm;
    time_t time1, time2;
#endif
Alexandre Julliard's avatar
Alexandre Julliard committed
2449 2450 2451 2452 2453 2454 2455

    newtm.tm_sec  = (fattime & 0x1f) * 2;
    newtm.tm_min  = (fattime >> 5) & 0x3f;
    newtm.tm_hour = (fattime >> 11);
    newtm.tm_mday = (fatdate & 0x1f);
    newtm.tm_mon  = ((fatdate >> 5) & 0x0f) - 1;
    newtm.tm_year = (fatdate >> 9) + 80;
2456
#ifdef HAVE_TIMEGM
2457
    RtlSecondsSince1970ToTime( timegm(&newtm), (LARGE_INTEGER *)ft );
2458 2459 2460 2461
#else
    time1 = mktime(&newtm);
    gtm = gmtime(&time1);
    time2 = mktime(gtm);
2462
    RtlSecondsSince1970ToTime( 2*time1-time2, (LARGE_INTEGER *)ft );
2463
#endif
Alexandre Julliard's avatar
Alexandre Julliard committed
2464 2465 2466 2467 2468
    return TRUE;
}


/***********************************************************************
2469
 *           FileTimeToDosDateTime   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
2470
 */
2471
BOOL WINAPI FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate,
Alexandre Julliard's avatar
Alexandre Julliard committed
2472
                                     LPWORD fattime )
Alexandre Julliard's avatar
Alexandre Julliard committed
2473
{
Alexandre Julliard's avatar
Alexandre Julliard committed
2474
    time_t unixtime = DOSFS_FileTimeToUnixTime( ft, NULL );
2475
    struct tm *tm = gmtime( &unixtime );
Alexandre Julliard's avatar
Alexandre Julliard committed
2476 2477 2478 2479 2480
    if (fattime)
        *fattime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
    if (fatdate)
        *fatdate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
                   + tm->tm_mday;
Alexandre Julliard's avatar
Alexandre Julliard committed
2481 2482 2483 2484
    return TRUE;
}


Alexandre Julliard's avatar
Alexandre Julliard committed
2485
/***********************************************************************
2486
 *           QueryDosDeviceA   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
2487 2488 2489
 *
 * returns array of strings terminated by \0, terminated by \0
 */
2490
DWORD WINAPI QueryDosDeviceA(LPCSTR devname,LPSTR target,DWORD bufsize)
Alexandre Julliard's avatar
Alexandre Julliard committed
2491 2492 2493 2494
{
    LPSTR s;
    char  buffer[200];

2495
    TRACE("(%s,...)\n", devname ? devname : "<null>");
Alexandre Julliard's avatar
Alexandre Julliard committed
2496 2497
    if (!devname) {
	/* return known MSDOS devices */
2498 2499 2500
        static const char devices[24] = "CON\0COM1\0COM2\0LPT1\0NUL\0\0";
        memcpy( target, devices, min(bufsize,sizeof(devices)) );
        return min(bufsize,sizeof(devices));
Alexandre Julliard's avatar
Alexandre Julliard committed
2501
    }
2502 2503 2504
    /* In theory all that are possible and have been defined.
     * Now just those below, since mirc uses it to check for special files.
     *
2505
     * (It is more complex, and supports netmounted stuff, and \\.\ stuff,
2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523
     *  but currently we just ignore that.)
     */
#define CHECK(x) (strstr(devname,#x)==devname)
    if (CHECK(con) || CHECK(com) || CHECK(lpt) || CHECK(nul)) {
	strcpy(buffer,"\\DEV\\");
	strcat(buffer,devname);
	if ((s=strchr(buffer,':'))) *s='\0';
	lstrcpynA(target,buffer,bufsize);
	return strlen(buffer)+1;
    } else {
	if (strchr(devname,':') || devname[0]=='\\') {
	    /* This might be a DOS device we do not handle yet ... */
	    FIXME("(%s) not detected as DOS device!\n",devname);
	}
	SetLastError(ERROR_DEV_NOT_EXIST);
	return 0;
    }

Alexandre Julliard's avatar
Alexandre Julliard committed
2524 2525 2526 2527
}


/***********************************************************************
2528
 *           QueryDosDeviceW   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
2529 2530 2531
 *
 * returns array of strings terminated by \0, terminated by \0
 */
2532
DWORD WINAPI QueryDosDeviceW(LPCWSTR devname,LPWSTR target,DWORD bufsize)
Alexandre Julliard's avatar
Alexandre Julliard committed
2533 2534
{
    LPSTR devnameA = devname?HEAP_strdupWtoA(GetProcessHeap(),0,devname):NULL;
2535
    LPSTR targetA = (LPSTR)HeapAlloc(GetProcessHeap(),0,bufsize);
2536
    DWORD ret = QueryDosDeviceA(devnameA,targetA,bufsize);
Alexandre Julliard's avatar
Alexandre Julliard committed
2537

2538
    ret = MultiByteToWideChar( CP_ACP, 0, targetA, ret, target, bufsize );
Alexandre Julliard's avatar
Alexandre Julliard committed
2539 2540 2541 2542 2543
    if (devnameA) HeapFree(GetProcessHeap(),0,devnameA);
    if (targetA) HeapFree(GetProcessHeap(),0,targetA);
    return ret;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
2544

Patrik Stridvall's avatar
Patrik Stridvall committed
2545
/***********************************************************************
2546
 *           DefineDosDeviceA       (KERNEL32.@)
Patrik Stridvall's avatar
Patrik Stridvall committed
2547
 */
2548
BOOL WINAPI DefineDosDeviceA(DWORD flags,LPCSTR devname,LPCSTR targetpath) {
2549
	FIXME("(0x%08lx,%s,%s),stub!\n",flags,devname,targetpath);
2550 2551 2552
	SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
	return FALSE;
}
2553 2554 2555 2556 2557 2558

/*
   --- 16 bit functions ---
*/

/*************************************************************************
Patrik Stridvall's avatar
Patrik Stridvall committed
2559
 *           FindFirstFile   (KERNEL.413)
2560 2561 2562 2563 2564 2565
 */
HANDLE16 WINAPI FindFirstFile16( LPCSTR path, WIN32_FIND_DATAA *data )
{
    DOS_FULL_NAME full_name;
    HGLOBAL16 handle;
    FIND_FIRST_INFO *info;
2566 2567 2568 2569
    WCHAR pathW[MAX_PATH];
    char *p;
    INT long_mask_len;
    UINT codepage;
2570 2571

    data->dwReserved0 = data->dwReserved1 = 0x0;
2572 2573 2574
    if (!path) return INVALID_HANDLE_VALUE16;
    MultiByteToWideChar(CP_ACP, 0, path, -1, pathW, MAX_PATH);
    if (!DOSFS_GetFullName( pathW, FALSE, &full_name ))
2575 2576 2577 2578
        return INVALID_HANDLE_VALUE16;
    if (!(handle = GlobalAlloc16( GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO) )))
        return INVALID_HANDLE_VALUE16;
    info = (FIND_FIRST_INFO *)GlobalLock16( handle );
2579 2580
    info->path = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
    strcpy( info->path, full_name.long_name );
2581 2582 2583 2584 2585 2586 2587 2588

    codepage = DRIVE_GetCodepage(full_name.drive);
    p = strrchr( info->path, '/' );
    *p++ = '\0';
    long_mask_len = MultiByteToWideChar(codepage, 0, p, -1, NULL, 0);
    info->long_mask = HeapAlloc( GetProcessHeap(), 0, long_mask_len * sizeof(WCHAR) );
    MultiByteToWideChar(codepage, 0, p, -1, info->long_mask, long_mask_len);

2589 2590
    info->short_mask = NULL;
    info->attr = 0xff;
2591
    info->drive = full_name.drive;
2592 2593
    info->cur_pos = 0;

2594
    info->u.dos_dir = DOSFS_OpenDir( codepage, info->path );
2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606

    GlobalUnlock16( handle );
    if (!FindNextFile16( handle, data ))
    {
        FindClose16( handle );
        SetLastError( ERROR_NO_MORE_FILES );
        return INVALID_HANDLE_VALUE16;
    }
    return handle;
}

/*************************************************************************
Patrik Stridvall's avatar
Patrik Stridvall committed
2607
 *           FindNextFile   (KERNEL.414)
2608 2609 2610 2611
 */
BOOL16 WINAPI FindNextFile16( HANDLE16 handle, WIN32_FIND_DATAA *data )
{
    FIND_FIRST_INFO *info;
2612 2613 2614
    WIN32_FIND_DATAW dataW;
    BOOL ret = FALSE;
    DWORD gle = ERROR_NO_MORE_FILES;
2615 2616 2617 2618 2619

    if ((handle == INVALID_HANDLE_VALUE16) ||
       !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
    {
        SetLastError( ERROR_INVALID_HANDLE );
2620
        return ret;
2621
    }
2622
    if (!info->path || !info->u.dos_dir)
2623
    {
2624
        goto done;
2625
    }
2626
    if (!DOSFS_FindNextEx( info, &dataW ))
2627
    {
2628
        DOSFS_CloseDir( info->u.dos_dir ); info->u.dos_dir = NULL;
2629
        HeapFree( GetProcessHeap(), 0, info->path );
2630 2631 2632 2633
        info->path = NULL;
        HeapFree( GetProcessHeap(), 0, info->long_mask );
        info->long_mask = NULL;
        goto done;
2634
    }
2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653

    ret = TRUE;

    data->dwFileAttributes = dataW.dwFileAttributes;
    data->ftCreationTime   = dataW.ftCreationTime;
    data->ftLastAccessTime = dataW.ftLastAccessTime;
    data->ftLastWriteTime  = dataW.ftLastWriteTime;
    data->nFileSizeHigh    = dataW.nFileSizeHigh;
    data->nFileSizeLow     = dataW.nFileSizeLow;
    WideCharToMultiByte( CP_ACP, 0, dataW.cFileName, -1,
                         data->cFileName, sizeof(data->cFileName), NULL, NULL );
    WideCharToMultiByte( CP_ACP, 0, dataW.cAlternateFileName, -1,
                         data->cAlternateFileName,
                         sizeof(data->cAlternateFileName), NULL, NULL );
done:
    if( !ret ) SetLastError( gle );
    GlobalUnlock16( handle );

    return ret;
2654 2655 2656
}

/*************************************************************************
Patrik Stridvall's avatar
Patrik Stridvall committed
2657
 *           FindClose   (KERNEL.415)
2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668
 */
BOOL16 WINAPI FindClose16( HANDLE16 handle )
{
    FIND_FIRST_INFO *info;

    if ((handle == INVALID_HANDLE_VALUE16) ||
        !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
    {
        SetLastError( ERROR_INVALID_HANDLE );
        return FALSE;
    }
2669
    if (info->u.dos_dir) DOSFS_CloseDir( info->u.dos_dir );
2670
    if (info->path) HeapFree( GetProcessHeap(), 0, info->path );
2671
    if (info->long_mask) HeapFree( GetProcessHeap(), 0, info->long_mask );
2672 2673 2674 2675
    GlobalUnlock16( handle );
    GlobalFree16( handle );
    return TRUE;
}