Skip to content
Snippets Groups Projects
dos_fs.c 52.8 KiB
Newer Older
Alexandre Julliard's avatar
Alexandre Julliard committed
/*
 * DOS file system functions
 *
 * Copyright 1993 Erik Bos
 * Copyright 1996 Alexandre Julliard
 */

Alexandre Julliard's avatar
Alexandre Julliard committed
#include "config.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
#include <sys/types.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
#include <ctype.h>
#include <dirent.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
#include <errno.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
#include <fcntl.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
#include <sys/ioctl.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
#include <time.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
#include <unistd.h>
Alexandre Julliard's avatar
Alexandre Julliard committed

#include "windows.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
#include "winerror.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
#include "drive.h"
#include "file.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
#include "heap.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
#include "msdos.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
#include "syslevel.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
#include "debug.h"
Alexandre Julliard's avatar
Alexandre Julliard committed

/* Define the VFAT ioctl to get both short and long file names */
Alexandre Julliard's avatar
Alexandre Julliard committed
/* FIXME: is it possible to get this to work on other systems? */
Alexandre Julliard's avatar
Alexandre Julliard committed
#ifdef linux
#define VFAT_IOCTL_READDIR_BOTH  _IOR('r', 1, long)
Alexandre Julliard's avatar
Alexandre Julliard committed
/* 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;

Alexandre Julliard's avatar
Alexandre Julliard committed
#else   /* linux */
#undef VFAT_IOCTL_READDIR_BOTH  /* just in case... */
#endif  /* linux */
Alexandre Julliard's avatar
Alexandre Julliard committed

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

Alexandre Julliard's avatar
Alexandre Julliard committed
static const DOS_DEVICE DOSFS_Devices[] =
/* name, device flags (see Int 21/AX=0x4400) */
{
    { "CON",		0xc0d3 },
    { "PRN",		0xa0c0 },
    { "NUL",		0x80c4 },
    { "AUX",		0x80c0 },
    { "LPT1",		0xa0c0 },
    { "LPT2",		0xa0c0 },
    { "LPT3",		0xa0c0 },
    { "LPT4",		0xc0d3 },
    { "COM1",		0x80c0 },
    { "COM2",		0x80c0 },
    { "COM3",		0x80c0 },
    { "COM4",		0x80c0 },
    { "SCSIMGR$",	0xc0c0 },
    { "HPSCAN",		0xc0c0 }
Alexandre Julliard's avatar
Alexandre Julliard committed
};

#define GET_DRIVE(path) \
    (((path)[1] == ':') ? toupper((path)[0]) - 'A' : DOSFS_CurDrive)

    /* DOS extended error status */
WORD DOS_ExtendedError;
BYTE DOS_ErrorClass;
BYTE DOS_ErrorAction;
BYTE DOS_ErrorLocus;

Alexandre Julliard's avatar
Alexandre Julliard committed
/* Directory info for DOSFS_ReadDir */
typedef struct
{
    DIR           *dir;
#ifdef VFAT_IOCTL_READDIR_BOTH
    int            fd;
    char           short_name[12];
Alexandre Julliard's avatar
Alexandre Julliard committed
    KERNEL_DIRENT  dirent[2];
Alexandre Julliard's avatar
Alexandre Julliard committed
#endif
} DOS_DIR;

Alexandre Julliard's avatar
Alexandre Julliard committed
/* Info structure for FindFirstFile handle */
typedef struct
{
    LPSTR path;
    LPSTR long_mask;
    LPSTR short_mask;
    BYTE  attr;
    int   drive;
    int   cur_pos;
    DOS_DIR *dir;
} FIND_FIRST_INFO;


Alexandre Julliard's avatar
Alexandre Julliard committed

/***********************************************************************
 *           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 '/'.
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
static int DOSFS_ValidDOSName( const char *name, int ignore_case )
Alexandre Julliard's avatar
Alexandre Julliard committed
{
Alexandre Julliard's avatar
Alexandre Julliard committed
    static const char invalid_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS;
Alexandre Julliard's avatar
Alexandre Julliard committed
    const char *p = name;
Alexandre Julliard's avatar
Alexandre Julliard committed
    const char *invalid = ignore_case ? (invalid_chars + 26) : invalid_chars;
Alexandre Julliard's avatar
Alexandre Julliard committed
    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))
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
        if (strchr( invalid, *p )) return 0;  /* Invalid char */
Alexandre Julliard's avatar
Alexandre Julliard committed
        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))
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
        if (strchr( invalid, *p )) return 0;  /* Invalid char */
Alexandre Julliard's avatar
Alexandre Julliard committed
        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
 * 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
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
BOOL32 DOSFS_ToDosFCBFormat( LPCSTR name, LPSTR buffer )
Alexandre Julliard's avatar
Alexandre Julliard committed
{
    static const char invalid_chars[] = INVALID_DOS_CHARS;
    const char *p = name;
    int i;

    /* Check for "." and ".." */
    if (*p == '.')
    {
        p++;
        strcpy( buffer, ".          " );
Alexandre Julliard's avatar
Alexandre Julliard committed
        if (*p == '.')
        {
            buffer[1] = '.';
            p++;
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
        return (!*p || (*p == '/') || (*p == '\\'));
Alexandre Julliard's avatar
Alexandre Julliard committed
    }

    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:
Alexandre Julliard's avatar
Alexandre Julliard committed
            if (strchr( invalid_chars, *p )) return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
            buffer[i] = toupper(*p);
            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
        if (*p && (*p != '/') && (*p != '\\') && (*p != '.')) return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
    }
    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
            return FALSE;  /* Second extension not allowed */
Alexandre Julliard's avatar
Alexandre Julliard committed
        case '?':
            p++;
            /* fall through */
        case '*':
            buffer[i] = '?';
            break;
        default:
Alexandre Julliard's avatar
Alexandre Julliard committed
            if (strchr( invalid_chars, *p )) return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
            buffer[i] = toupper(*p);
            p++;
            break;
        }
    }
    buffer[11] = '\0';
Alexandre Julliard's avatar
Alexandre Julliard committed
    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
}


/***********************************************************************
 *           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
 * 'buffer' must be at least 13 characters long.
Alexandre Julliard's avatar
Alexandre Julliard committed
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
static void DOSFS_ToDosDTAFormat( LPCSTR name, LPSTR buffer )
Alexandre Julliard's avatar
Alexandre Julliard committed
{
    char *p;

    memcpy( buffer, name, 8 );
    for (p = buffer + 8; (p > buffer) && (p[-1] == ' '); p--);
    *p++ = '.';
    memcpy( p, name + 8, 3 );
    for (p += 3; p[-1] == ' '; p--);
    if (p[-1] == '.') p--;
    *p = '\0';
}


/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
 *           DOSFS_MatchShort
Alexandre Julliard's avatar
Alexandre Julliard committed
 *
 * Check a DOS file name against a mask (both in FCB format).
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
static int DOSFS_MatchShort( const char *mask, const char *name )
Alexandre Julliard's avatar
Alexandre Julliard committed
{
    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
/***********************************************************************
 *           DOSFS_MatchLong
 *
 * Check a long file name against a mask.
 */
static int DOSFS_MatchLong( const char *mask, const char *name,
                            int case_sensitive )
{
Alexandre Julliard's avatar
Alexandre Julliard committed
    if (!strcmp( mask, "*.*" )) return 1;
Alexandre Julliard's avatar
Alexandre Julliard committed
    while (*name && *mask)
    {
        if (*mask == '*')
        {
            mask++;
            while (*mask == '*') mask++;  /* Skip consecutive '*' */
            if (!*mask) return 1;
            if (case_sensitive) while (*name && (*name != *mask)) name++;
            else while (*name && (toupper(*name) != toupper(*mask))) name++;
            if (!*name) return 0;
        }
        else if (*mask != '?')
        {
            if (case_sensitive)
            {
                if (*mask != *name) return 0;
            }
            else if (toupper(*mask) != toupper(*name)) return 0;
        }
        mask++;
        name++;
    }
    return (!*name && !*mask);
}


Alexandre Julliard's avatar
Alexandre Julliard committed
/***********************************************************************
 *           DOSFS_OpenDir
 */
static DOS_DIR *DOSFS_OpenDir( LPCSTR path )
{
    DOS_DIR *dir = HeapAlloc( SystemHeap, 0, sizeof(*dir) );
    if (!dir)
    {
        DOS_ERROR( ER_OutOfMemory, EC_OutOfResource, SA_Abort, EL_Memory );
        return NULL;
    }

#ifdef VFAT_IOCTL_READDIR_BOTH

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

    if ((dir->fd = open( path, O_RDONLY )) != -1)
    {
        if (ioctl( dir->fd, VFAT_IOCTL_READDIR_BOTH, (long)dir->dirent ) == -1)
        {
            close( dir->fd );
            dir->fd = -1;
        }
        else
        {
            /* Set the file pointer back at the start of the directory */
            lseek( dir->fd, 0, SEEK_SET );
            dir->dir = NULL;
            return dir;
        }
    }
#endif  /* VFAT_IOCTL_READDIR_BOTH */

    /* Now use the standard opendir/readdir interface */

    if (!(dir->dir = opendir( path )))
    {
        HeapFree( SystemHeap, 0, dir );
        return NULL;
    }
    return dir;
}


/***********************************************************************
 *           DOSFS_CloseDir
 */
static void DOSFS_CloseDir( DOS_DIR *dir )
{
#ifdef VFAT_IOCTL_READDIR_BOTH
    if (dir->fd != -1) close( dir->fd );
#endif  /* VFAT_IOCTL_READDIR_BOTH */
    if (dir->dir) closedir( dir->dir );
    HeapFree( SystemHeap, 0, dir );
}


/***********************************************************************
 *           DOSFS_ReadDir
 */
static BOOL32 DOSFS_ReadDir( DOS_DIR *dir, LPCSTR *long_name,
                             LPCSTR *short_name )
{
    struct dirent *dirent;

#ifdef VFAT_IOCTL_READDIR_BOTH
    if (dir->fd != -1)
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
        if (ioctl( dir->fd, VFAT_IOCTL_READDIR_BOTH, (long)dir->dirent ) != -1) {
	    if (!dir->dirent[0].d_reclen) return FALSE;
	    if (!DOSFS_ToDosFCBFormat( dir->dirent[0].d_name, dir->short_name ))
		dir->short_name[0] = '\0';
	    *short_name = dir->short_name;
	    if (dir->dirent[1].d_name[0]) *long_name = dir->dirent[1].d_name;
	    else *long_name = dir->dirent[0].d_name;
	    return TRUE;
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
    }
#endif  /* VFAT_IOCTL_READDIR_BOTH */

    if (!(dirent = readdir( dir->dir ))) return FALSE;
    *long_name  = dirent->d_name;
    *short_name = NULL;
    return TRUE;
}


Alexandre Julliard's avatar
Alexandre Julliard committed
/***********************************************************************
 *           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
 * 'buffer' must be at least 13 characters long.
Alexandre Julliard's avatar
Alexandre Julliard committed
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
static void DOSFS_Hash( LPCSTR name, LPSTR buffer, BOOL32 dir_format,
                        BOOL32 ignore_case )
Alexandre Julliard's avatar
Alexandre Julliard committed
{
    static const char invalid_chars[] = INVALID_DOS_CHARS "~.";
    static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";

    const char *p, *ext;
    char *dst;
    unsigned short hash;
    int i;

    if (dir_format) strcpy( buffer, "           " );

Alexandre Julliard's avatar
Alexandre Julliard committed
    if (DOSFS_ValidDOSName( name, ignore_case ))
Alexandre Julliard's avatar
Alexandre Julliard committed
    {
        /* 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
            return;
Alexandre Julliard's avatar
Alexandre Julliard committed
        }

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

        for (dst = buffer; !IS_END_OF_NAME(*name) && (*name != '.'); name++)
            *dst++ = toupper(*name);
        if (*name == '.')
        {
            if (dir_format) dst = buffer + 8;
            else *dst++ = '.';
            for (name++; !IS_END_OF_NAME(*name); name++)
                *dst++ = toupper(*name);
        }
        if (!dir_format) *dst = '\0';
Alexandre Julliard's avatar
Alexandre Julliard committed
        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++)
            hash = (hash<<3) ^ (hash>>5) ^ tolower(*p) ^ (tolower(p[1]) << 8);
        hash = (hash<<3) ^ (hash>>5) ^ tolower(*p); /* Last character*/
Alexandre Julliard's avatar
Alexandre Julliard committed
    }
    else
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
        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

Alexandre Julliard's avatar
Alexandre Julliard committed
    /* 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

Alexandre Julliard's avatar
Alexandre Julliard committed
    /* 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;
        *dst++ = strchr( invalid_chars, *p ) ? '_' : toupper(*p);
    }
    /* Pad to 5 chars with '~' */
    while (i-- >= 0) *dst++ = '~';
Alexandre Julliard's avatar
Alexandre Julliard committed

Alexandre Julliard's avatar
Alexandre Julliard committed
    /* 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

Alexandre Julliard's avatar
Alexandre Julliard committed
    /* 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++)
            *dst++ = strchr( invalid_chars, *ext ) ? '_' : toupper(*ext);
Alexandre Julliard's avatar
Alexandre Julliard committed
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
    if (!dir_format) *dst = '\0';
Alexandre Julliard's avatar
Alexandre Julliard committed
}


/***********************************************************************
 *           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
 * 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
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
BOOL32 DOSFS_FindUnixName( LPCSTR path, LPCSTR name, LPSTR long_buf,
                           INT32 long_len, LPSTR short_buf, BOOL32 ignore_case)
Alexandre Julliard's avatar
Alexandre Julliard committed
{
Alexandre Julliard's avatar
Alexandre Julliard committed
    DOS_DIR *dir;
    LPCSTR long_name, short_name;
Alexandre Julliard's avatar
Alexandre Julliard committed
    char dos_name[12], tmp_buf[13];
Alexandre Julliard's avatar
Alexandre Julliard committed
    BOOL32 ret;
Alexandre Julliard's avatar
Alexandre Julliard committed

    const char *p = strchr( name, '/' );
    int len = p ? (int)(p - name) : strlen(name);
Alexandre Julliard's avatar
Alexandre Julliard committed
    if ((p = strchr( name, '\\' ))) len = MIN( (int)(p - name), len );
    if (long_len < len + 1) return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed

Alexandre Julliard's avatar
Alexandre Julliard committed
    TRACE(dosfs, "%s,%s\n", path, name );
Alexandre Julliard's avatar
Alexandre Julliard committed

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

    if (!(dir = DOSFS_OpenDir( path )))
Alexandre Julliard's avatar
Alexandre Julliard committed
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
        WARN(dosfs, "(%s,%s): can't open dir: %s\n",
Alexandre Julliard's avatar
Alexandre Julliard committed
                       path, name, strerror(errno) );
Alexandre Julliard's avatar
Alexandre Julliard committed
        return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
    }
Alexandre Julliard's avatar
Alexandre Julliard committed

    while ((ret = DOSFS_ReadDir( dir, &long_name, &short_name )))
Alexandre Julliard's avatar
Alexandre Julliard committed
    {
        /* Check against Unix name */
Alexandre Julliard's avatar
Alexandre Julliard committed
        if (len == strlen(long_name))
Alexandre Julliard's avatar
Alexandre Julliard committed
        {
Alexandre Julliard's avatar
Alexandre Julliard committed
            if (!ignore_case)
Alexandre Julliard's avatar
Alexandre Julliard committed
            {
Alexandre Julliard's avatar
Alexandre Julliard committed
                if (!lstrncmp32A( long_name, name, len )) break;
Alexandre Julliard's avatar
Alexandre Julliard committed
            }
            else
            {
Alexandre Julliard's avatar
Alexandre Julliard committed
                if (!lstrncmpi32A( long_name, name, len )) break;
Alexandre Julliard's avatar
Alexandre Julliard committed
        if (dos_name[0])
Alexandre Julliard's avatar
Alexandre Julliard committed
        {
            /* Check against hashed DOS name */
Alexandre Julliard's avatar
Alexandre Julliard committed
            if (!short_name)
Alexandre Julliard's avatar
Alexandre Julliard committed
            {
                DOSFS_Hash( long_name, tmp_buf, TRUE, ignore_case );
                short_name = tmp_buf;
            }
Alexandre Julliard's avatar
Alexandre Julliard committed
            if (!strcmp( dos_name, short_name )) break;
Alexandre Julliard's avatar
Alexandre Julliard committed
        }
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
    if (ret)
    {
        if (long_buf) strcpy( long_buf, long_name );
        if (short_buf)
        {
            if (short_name)
                DOSFS_ToDosDTAFormat( short_name, short_buf );
            else
                DOSFS_Hash( long_name, short_buf, FALSE, ignore_case );
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
        TRACE(dosfs, "(%s,%s) -> %s (%s)\n",
Alexandre Julliard's avatar
Alexandre Julliard committed
		     path, name, long_name, short_buf ? short_buf : "***");
Alexandre Julliard's avatar
Alexandre Julliard committed
    }
    else
Alexandre Julliard's avatar
Alexandre Julliard committed
        WARN(dosfs, "'%s' not found in '%s'\n", name, path);
Alexandre Julliard's avatar
Alexandre Julliard committed
    DOSFS_CloseDir( dir );
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
}


/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
 *           DOSFS_GetDevice
Alexandre Julliard's avatar
Alexandre Julliard committed
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
 * Check if a DOS file name represents a DOS device and return the device.
Alexandre Julliard's avatar
Alexandre Julliard committed
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
const DOS_DEVICE *DOSFS_GetDevice( const char *name )
Alexandre Julliard's avatar
Alexandre Julliard committed
{
    int	i;
    const char *p;

Alexandre Julliard's avatar
Alexandre Julliard committed
    if (!name) return NULL; /* if FILE_DupUnixHandle was used */
Alexandre Julliard's avatar
Alexandre Julliard committed
    if (name[0] && (name[1] == ':')) name += 2;
    if ((p = strrchr( name, '/' ))) name = p + 1;
    if ((p = strrchr( name, '\\' ))) name = p + 1;
    for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
        const char *dev = DOSFS_Devices[i].name;
Alexandre Julliard's avatar
Alexandre Julliard committed
        if (!lstrncmpi32A( dev, name, strlen(dev) ))
        {
            p = name + strlen( dev );
Alexandre Julliard's avatar
Alexandre Julliard committed
            if (!*p || (*p == '.')) return &DOSFS_Devices[i];
Alexandre Julliard's avatar
Alexandre Julliard committed
    return NULL;
Alexandre Julliard's avatar
Alexandre Julliard committed
}

/***********************************************************************
 *           DOSFS_OpenDevice
 *
 * Open a DOS device. This might not map 1:1 into the UNIX device concept.
Alexandre Julliard's avatar
Alexandre Julliard committed
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
HFILE32 DOSFS_OpenDevice( const char *name, int unixmode )
Alexandre Julliard's avatar
Alexandre Julliard committed
{
    int i;
    const char *p;
Alexandre Julliard's avatar
Alexandre Julliard committed
    FILE_OBJECT *file;
    HFILE32 handle;
Alexandre Julliard's avatar
Alexandre Julliard committed

Alexandre Julliard's avatar
Alexandre Julliard committed
    if (!name) return NULL; /* if FILE_DupUnixHandle was used */
Alexandre Julliard's avatar
Alexandre Julliard committed
    if (name[0] && (name[1] == ':')) name += 2;
Alexandre Julliard's avatar
Alexandre Julliard committed
    if ((p = strrchr( name, '/' ))) name = p + 1;
    if ((p = strrchr( name, '\\' ))) name = p + 1;
    for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
        const char *dev = DOSFS_Devices[i].name;
Alexandre Julliard's avatar
Alexandre Julliard committed
        if (!lstrncmpi32A( dev, name, strlen(dev) ))
Alexandre Julliard's avatar
Alexandre Julliard committed
        {
            p = name + strlen( dev );
Alexandre Julliard's avatar
Alexandre Julliard committed
            if (!*p || (*p == '.')) {
	    	/* got it */
Alexandre Julliard's avatar
Alexandre Julliard committed
		if (!strcmp(DOSFS_Devices[i].name,"NUL"))
Alexandre Julliard's avatar
Alexandre Julliard committed
			return FILE_OpenUnixFile("/dev/null",unixmode);
Alexandre Julliard's avatar
Alexandre Julliard committed
		if (!strcmp(DOSFS_Devices[i].name,"CON")) {
Alexandre Julliard's avatar
Alexandre Julliard committed
			switch (unixmode) {
			case O_RDONLY:
				return GetStdHandle( STD_INPUT_HANDLE );
				break;
			case O_WRONLY:
				return GetStdHandle( STD_OUTPUT_HANDLE );
				break;
			default:
Alexandre Julliard's avatar
Alexandre Julliard committed
				FIXME(dosfs,"can't open CON read/write\n");
Alexandre Julliard's avatar
Alexandre Julliard committed
				return HFILE_ERROR32;
				break;
			}
		}
Alexandre Julliard's avatar
Alexandre Julliard committed
		if (!strcmp(DOSFS_Devices[i].name,"SCSIMGR$")) {
Alexandre Julliard's avatar
Alexandre Julliard committed
		        if ((handle = FILE_Alloc( &file )) == INVALID_HANDLE_VALUE32)
				return HFILE_ERROR32;
			else {
				file->unix_name = HEAP_strdupA( SystemHeap, 0, name );
				return handle;
			}
		}
Alexandre Julliard's avatar
Alexandre Julliard committed
                if (!strcmp(DOSFS_Devices[i].name,"HPSCAN")) {
                        if ((handle = FILE_Alloc( &file )) == INVALID_HANDLE_VALUE32)
                                return HFILE_ERROR32;
                        else {
                                file->unix_name = HEAP_strdupA( SystemHeap, 0, name );
                                return handle;
                        }
                }
		FIXME(dosfs,"device open %s not supported (yet)\n",DOSFS_Devices[i].name);
Alexandre Julliard's avatar
Alexandre Julliard committed
    		return HFILE_ERROR32;
	    }
Alexandre Julliard's avatar
Alexandre Julliard committed
        }
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
    return HFILE_ERROR32;
Alexandre Julliard's avatar
Alexandre Julliard committed

Alexandre Julliard's avatar
Alexandre Julliard committed
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
 *           DOSFS_GetPathDrive
Alexandre Julliard's avatar
Alexandre Julliard committed
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
 * Get the drive specified by a given path name (DOS or Unix format).
Alexandre Julliard's avatar
Alexandre Julliard committed
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
static int DOSFS_GetPathDrive( const char **name )
Alexandre Julliard's avatar
Alexandre Julliard committed
{
Alexandre Julliard's avatar
Alexandre Julliard committed
    int drive;
    const char *p = *name;
Alexandre Julliard's avatar
Alexandre Julliard committed

Alexandre Julliard's avatar
Alexandre Julliard committed
    if (*p && (p[1] == ':'))
Alexandre Julliard's avatar
Alexandre Julliard committed
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
        drive = toupper(*p) - 'A';
        *name += 2;
Alexandre Julliard's avatar
Alexandre Julliard committed
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
    else if (*p == '/') /* Absolute Unix path? */
Alexandre Julliard's avatar
Alexandre Julliard committed
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
        if ((drive = DRIVE_FindDriveRoot( name )) == -1)
Alexandre Julliard's avatar
Alexandre Julliard committed
        {
Alexandre Julliard's avatar
Alexandre Julliard committed
            MSG("Warning: %s not accessible from a DOS drive\n", *name );
Alexandre Julliard's avatar
Alexandre Julliard committed
            /* Assume it really was a DOS name */
            drive = DRIVE_GetCurrentDrive();            
        }
    }
    else drive = DRIVE_GetCurrentDrive();

    if (!DRIVE_IsValid(drive))
    {
        DOS_ERROR( ER_InvalidDrive, EC_MediaError, SA_Abort, EL_Disk );
Alexandre Julliard's avatar
Alexandre Julliard committed
        return -1;
Alexandre Julliard's avatar
Alexandre Julliard committed
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
    return drive;
}


/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
 *           DOSFS_GetFullName
Alexandre Julliard's avatar
Alexandre Julliard committed
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
 * 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
 * component is only checked if 'check_last' is non-zero.
Alexandre Julliard's avatar
Alexandre Julliard committed
 * 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
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
BOOL32 DOSFS_GetFullName( LPCSTR name, BOOL32 check_last, DOS_FULL_NAME *full )
Alexandre Julliard's avatar
Alexandre Julliard committed
{
    BOOL32 found;
    UINT32 flags;
Alexandre Julliard's avatar
Alexandre Julliard committed
    char *p_l, *p_s, *root;

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

    if ((full->drive = DOSFS_GetPathDrive( &name )) == -1) return FALSE;
    flags = DRIVE_GetFlags( full->drive );
Alexandre Julliard's avatar
Alexandre Julliard committed

Alexandre Julliard's avatar
Alexandre Julliard committed
    lstrcpyn32A( full->long_name, DRIVE_GetRoot( full->drive ),
                 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

Alexandre Julliard's avatar
Alexandre Julliard committed
    strcpy( full->short_name, "A:\\" );
    full->short_name[0] += full->drive;
Alexandre Julliard's avatar
Alexandre Julliard committed

Alexandre Julliard's avatar
Alexandre Julliard committed
    if ((*name == '\\') || (*name == '/'))  /* Absolute path */
Alexandre Julliard's avatar
Alexandre Julliard committed
    {
        while ((*name == '\\') || (*name == '/')) name++;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
    else  /* Relative path */
Alexandre Julliard's avatar
Alexandre Julliard committed
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
        lstrcpyn32A( root + 1, DRIVE_GetUnixCwd( full->drive ),
                     sizeof(full->long_name) - (root - full->long_name) - 1 );
Alexandre Julliard's avatar
Alexandre Julliard committed
        if (root[1]) *root = '/';
Alexandre Julliard's avatar
Alexandre Julliard committed
        lstrcpyn32A( full->short_name + 3, DRIVE_GetDosCwd( full->drive ),
                     sizeof(full->short_name) - 3 );
Alexandre Julliard's avatar
Alexandre Julliard committed
    p_l = full->long_name[1] ? full->long_name + strlen(full->long_name)
                             : full->long_name;
    p_s = full->short_name[3] ? full->short_name + strlen(full->short_name)
                              : full->short_name + 2;
Alexandre Julliard's avatar
Alexandre Julliard committed
    found = TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed

Alexandre Julliard's avatar
Alexandre Julliard committed
    while (*name && found)
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
        /* Check for '.' and '..' */

        if (*name == '.')
Alexandre Julliard's avatar
Alexandre Julliard committed
        {
Alexandre Julliard's avatar
Alexandre Julliard committed
            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
        }
Alexandre Julliard's avatar
Alexandre Julliard committed

        /* Make sure buffers are large enough */

        if ((p_s >= full->short_name + sizeof(full->short_name) - 14) ||
            (p_l >= full->long_name + sizeof(full->long_name) - 1))
Alexandre Julliard's avatar
Alexandre Julliard committed
        {
            DOS_ERROR( ER_PathNotFound, EC_NotFound, SA_Abort, EL_Disk );
Alexandre Julliard's avatar
Alexandre Julliard committed
            return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
        }
Alexandre Julliard's avatar
Alexandre Julliard committed

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

        if ((found = DOSFS_FindUnixName( full->long_name, name, p_l + 1,
                         sizeof(full->long_name) - (p_l - full->long_name) - 1,
                         p_s + 1, !(flags & DRIVE_CASE_SENSITIVE) )))
Alexandre Julliard's avatar
Alexandre Julliard committed
        {
Alexandre Julliard's avatar
Alexandre Julliard committed
            *p_l++ = '/';
            p_l   += strlen(p_l);
            *p_s++ = '\\';
            p_s   += strlen(p_s);
Alexandre Julliard's avatar
Alexandre Julliard committed
            while (!IS_END_OF_NAME(*name)) name++;
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
        else if (!check_last)
Alexandre Julliard's avatar
Alexandre Julliard committed
        {
Alexandre Julliard's avatar
Alexandre Julliard committed
            *p_l++ = '/';
            *p_s++ = '\\';
            while (!IS_END_OF_NAME(*name) &&
                   (p_s < full->short_name + sizeof(full->short_name) - 1) &&
                   (p_l < full->long_name + sizeof(full->long_name) - 1))
            {
Alexandre Julliard's avatar
Alexandre Julliard committed
                *p_s++ = tolower(*name);
                /* 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. */
	    	if (flags & DRIVE_CASE_SENSITIVE) *p_l++ = tolower(*name);
		else *p_l++ = *name;
Alexandre Julliard's avatar
Alexandre Julliard committed
                name++;
            }
            *p_l = *p_s = '\0';
Alexandre Julliard's avatar
Alexandre Julliard committed
        }
        while ((*name == '\\') || (*name == '/')) name++;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed

Alexandre Julliard's avatar
Alexandre Julliard committed
    if (!found)
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
        if (check_last)
Alexandre Julliard's avatar
Alexandre Julliard committed
        {
Alexandre Julliard's avatar
Alexandre Julliard committed
            DOS_ERROR( ER_FileNotFound, EC_NotFound, SA_Abort, EL_Disk );
Alexandre Julliard's avatar
Alexandre Julliard committed
            return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
        if (*name)  /* Not last */
Alexandre Julliard's avatar
Alexandre Julliard committed
        {
Alexandre Julliard's avatar
Alexandre Julliard committed
            DOS_ERROR( ER_PathNotFound, EC_NotFound, SA_Abort, EL_Disk );
Alexandre Julliard's avatar
Alexandre Julliard committed
            return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
        }
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
    if (!full->long_name[0]) strcpy( full->long_name, "/" );
    if (!full->short_name[2]) strcpy( full->short_name + 2, "\\" );
Alexandre Julliard's avatar
Alexandre Julliard committed
    TRACE(dosfs, "returning %s = %s\n",
Alexandre Julliard's avatar
Alexandre Julliard committed
                   full->long_name, full->short_name );
    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
}


/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
 *           GetShortPathName32A   (KERNEL32.271)
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
DWORD WINAPI GetShortPathName32A( LPCSTR longpath, LPSTR shortpath,
                                  DWORD shortlen )
Alexandre Julliard's avatar
Alexandre Julliard committed
{
    DOS_FULL_NAME full_name;

    /* FIXME: is it correct to always return a fully qualified short path? */
    if (!DOSFS_GetFullName( longpath, TRUE, &full_name )) return 0;
    lstrcpyn32A( shortpath, full_name.short_name, shortlen );
    return strlen( full_name.short_name );
}


/***********************************************************************
 *           GetShortPathName32W   (KERNEL32.272)
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
DWORD WINAPI GetShortPathName32W( LPCWSTR longpath, LPWSTR shortpath,
                                  DWORD shortlen )
Alexandre Julliard's avatar
Alexandre Julliard committed
{
    DOS_FULL_NAME full_name;
    DWORD ret = 0;
    LPSTR longpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, longpath );

    /* FIXME: is it correct to always return a fully qualified short path? */
    if (DOSFS_GetFullName( longpathA, TRUE, &full_name ))
    {
        ret = strlen( full_name.short_name );
        lstrcpynAtoW( shortpath, full_name.short_name, shortlen );
    }
    HeapFree( GetProcessHeap(), 0, longpathA );
    return ret;
}


Alexandre Julliard's avatar
Alexandre Julliard committed
/***********************************************************************
 *           GetLongPathName32A   (KERNEL32.xxx)
 */
DWORD WINAPI GetLongPathName32A( LPCSTR shortpath, LPSTR longpath,
                                  DWORD longlen )
{
    DOS_FULL_NAME full_name;

Alexandre Julliard's avatar
Alexandre Julliard committed
    /* FIXME: Is it correct to return a UNIX style path here? */
Alexandre Julliard's avatar
Alexandre Julliard committed
    if (!DOSFS_GetFullName( shortpath, TRUE, &full_name )) return 0;
    lstrcpyn32A( longpath, full_name.long_name, longlen );
    return strlen( full_name.long_name );
}


/***********************************************************************
 *           GetLongPathName32W   (KERNEL32.269)
 */
DWORD WINAPI GetLongPathName32W( LPCWSTR shortpath, LPWSTR longpath,
                                  DWORD longlen )
{
    DOS_FULL_NAME full_name;
    DWORD ret = 0;
    LPSTR shortpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, shortpath );

    /* FIXME: is it correct to always return a fully qualified short path? */
    if (DOSFS_GetFullName( shortpathA, TRUE, &full_name ))
    {
        ret = strlen( full_name.short_name );
        lstrcpynAtoW( longpath, full_name.long_name, longlen );
    }
    HeapFree( GetProcessHeap(), 0, shortpathA );
    return ret;
}


Alexandre Julliard's avatar
Alexandre Julliard committed
/***********************************************************************
 *           DOSFS_DoGetFullPathName
Alexandre Julliard's avatar
Alexandre Julliard committed
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
 * Implementation of GetFullPathName32A/W.
Alexandre Julliard's avatar
Alexandre Julliard committed
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
static DWORD DOSFS_DoGetFullPathName( LPCSTR name, DWORD len, LPSTR result,
                                      BOOL32 unicode )
Alexandre Julliard's avatar
Alexandre Julliard committed
{
Alexandre Julliard's avatar
Alexandre Julliard committed
    char buffer[MAX_PATHNAME_LEN];
    int drive;
Alexandre Julliard's avatar
Alexandre Julliard committed
    char *p;

Alexandre Julliard's avatar
Alexandre Julliard committed
    TRACE(dosfs, "converting %s\n", name );
Alexandre Julliard's avatar
Alexandre Julliard committed

Alexandre Julliard's avatar
Alexandre Julliard committed
    if (!name || !result) return 0;

Alexandre Julliard's avatar
Alexandre Julliard committed
    if ((drive = DOSFS_GetPathDrive( &name )) == -1) return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
    p = buffer;
    *p++ = 'A' + drive;
    *p++ = ':';
Alexandre Julliard's avatar
Alexandre Julliard committed
    if (IS_END_OF_NAME(*name) && (*name))  /* Absolute path */
Alexandre Julliard's avatar
Alexandre Julliard committed
    {
        while ((*name == '\\') || (*name == '/')) name++;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
    else  /* Relative path or empty path */
Alexandre Julliard's avatar
Alexandre Julliard committed
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
        *p++ = '\\';
Alexandre Julliard's avatar
Alexandre Julliard committed
        lstrcpyn32A( p, DRIVE_GetDosCwd(drive), sizeof(buffer) - 3 );
Alexandre Julliard's avatar
Alexandre Julliard committed
        if (*p) p += strlen(p); else p--;
Alexandre Julliard's avatar
Alexandre Julliard committed
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
    if (!*name) /* empty path */
      *p++ = '\\';
Alexandre Julliard's avatar
Alexandre Julliard committed
    *p = '\0';
Alexandre Julliard's avatar
Alexandre Julliard committed

    while (*name)
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
        if (*name == '.')
Alexandre Julliard's avatar
Alexandre Julliard committed
        {
Alexandre Julliard's avatar
Alexandre Julliard committed
            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 > buffer + 2) && (*p != '\\')) p--;
                *p = '\0';  /* Remove trailing separator */
                continue;
            }
Alexandre Julliard's avatar
Alexandre Julliard committed
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
        if (p >= buffer + sizeof(buffer) - 1)
Alexandre Julliard's avatar
Alexandre Julliard committed
        {
            DOS_ERROR( ER_PathNotFound, EC_NotFound, SA_Abort, EL_Disk );
Alexandre Julliard's avatar
Alexandre Julliard committed
            return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
        }
        *p++ = '\\';
Alexandre Julliard's avatar
Alexandre Julliard committed
        while (!IS_END_OF_NAME(*name) && (p < buffer + sizeof(buffer) - 1))
            *p++ = *name++;
        *p = '\0';
Alexandre Julliard's avatar
Alexandre Julliard committed
        while ((*name == '\\') || (*name == '/')) name++;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed

Alexandre Julliard's avatar
Alexandre Julliard committed
    if (!buffer[2])
    {
        buffer[2] = '\\';
        buffer[3] = '\0';
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
    if (!(DRIVE_GetFlags(drive) & DRIVE_CASE_PRESERVING))
        CharUpper32A( buffer );

    if (unicode) lstrcpynAtoW( (LPWSTR)result, buffer, len );
    else lstrcpyn32A( result, buffer, len );

Alexandre Julliard's avatar
Alexandre Julliard committed
    TRACE(dosfs, "returning %s\n", buffer );
Alexandre Julliard's avatar
Alexandre Julliard committed
    return strlen(buffer);
}


/***********************************************************************
 *           GetFullPathName32A   (KERNEL32.272)
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
DWORD WINAPI GetFullPathName32A( LPCSTR name, DWORD len, LPSTR buffer,
                                 LPSTR *lastpart )
Alexandre Julliard's avatar
Alexandre Julliard committed
{
    DWORD ret = DOSFS_DoGetFullPathName( name, len, buffer, FALSE );
    if (ret && lastpart)
    {
        LPSTR p = buffer + strlen(buffer);
        while ((p > buffer + 2) && (*p != '\\')) p--;
        *lastpart = p + 1;
    }