Newer
Older
/*
* DOS file system functions
*
* Copyright 1993 Erik Bos
* Copyright 1996 Alexandre Julliard
*/
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include "windef.h"
#include "winuser.h"
#include "wine/winbase16.h"
#include "server.h"
#include "process.h"
/* Define the VFAT ioctl to get both short and long file names */
/* FIXME: is it possible to get this to work on other systems? */
#ifdef linux
#define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, long)
/* 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;
#else /* linux */
#undef VFAT_IOCTL_READDIR_BOTH /* just in case... */
#endif /* linux */
/* Chars we don't want to see in DOS file names */
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 }
};
#define GET_DRIVE(path) \
(((path)[1] == ':') ? toupper((path)[0]) - 'A' : DOSFS_CurDrive)
/* Directory info for DOSFS_ReadDir */
typedef struct
{
DIR *dir;
#ifdef VFAT_IOCTL_READDIR_BOTH
int fd;
char short_name[12];
/* 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;
/***********************************************************************
* 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 '/'.
*/
static int DOSFS_ValidDOSName( const char *name, int ignore_case )
static const char invalid_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS;
const char *invalid = ignore_case ? (invalid_chars + 26) : invalid_chars;
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))
{
if (strchr( invalid, *p )) return 0; /* Invalid char */
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))
{
if (strchr( invalid, *p )) return 0; /* Invalid char */
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 '/'.
* Return FALSE if the name is not a valid DOS name.
* 'buffer' must be at least 12 characters long.
BOOL DOSFS_ToDosFCBFormat( LPCSTR name, LPSTR buffer )
{
static const char invalid_chars[] = INVALID_DOS_CHARS;
const char *p = name;
int i;
/* Check for "." and ".." */
if (*p == '.')
{
p++;
strcpy( buffer, ". " );
if (*p == '.')
{
buffer[1] = '.';
p++;
}
}
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:
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 */
if (*p && (*p != '/') && (*p != '\\') && (*p != '.')) return FALSE;
}
if (*p == '.') p++; /* Skip dot */
for (i = 8; i < 11; i++)
{
switch(*p)
{
case '\0':
case '\\':
case '/':
buffer[i] = ' ';
break;
case '.':
case '?':
p++;
/* fall through */
case '*':
buffer[i] = '?';
break;
default:
buffer[i] = toupper(*p);
p++;
break;
}
}
buffer[11] = '\0';
}
/***********************************************************************
* 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 '/'.
static void DOSFS_ToDosDTAFormat( LPCSTR name, LPSTR buffer )
{
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';
}
/***********************************************************************
*
* Check a DOS file name against a mask (both in FCB format).
*/
static int DOSFS_MatchShort( const char *mask, const char *name )
{
int i;
for (i = 11; i > 0; i--, mask++, name++)
if ((*mask != '?') && (*mask != *name)) return 0;
return 1;
}
/***********************************************************************
* DOSFS_MatchLong
*
* Check a long file name against a mask.
*/
static int DOSFS_MatchLong( const char *mask, const char *name,
int case_sensitive )
{
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
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);
}
/***********************************************************************
* DOSFS_OpenDir
*/
static DOS_DIR *DOSFS_OpenDir( LPCSTR path )
{
DOS_DIR *dir = HeapAlloc( SystemHeap, 0, sizeof(*dir) );
if (!dir)
{
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
/* Treat empty path as root directory. This simplifies path split into
directory and mask in several other places */
if (!*path) path = "/";
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
#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 BOOL DOSFS_ReadDir( DOS_DIR *dir, LPCSTR *long_name,
LPCSTR *short_name )
{
struct dirent *dirent;
#ifdef VFAT_IOCTL_READDIR_BOTH
if (dir->fd != -1)
{
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;
}
}
#endif /* VFAT_IOCTL_READDIR_BOTH */
if (!(dirent = readdir( dir->dir ))) return FALSE;
*long_name = dirent->d_name;
*short_name = NULL;
return TRUE;
}
/***********************************************************************
* 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 '/'.
static void DOSFS_Hash( LPCSTR name, LPSTR buffer, BOOL dir_format,
BOOL ignore_case )
{
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, " " );
{
/* Check for '.' and '..' */
if (*name == '.')
{
buffer[0] = '.';
if (!dir_format) buffer[1] = buffer[2] = '\0';
if (name[1] == '.') buffer[1] = '.';
}
/* 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';
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*/
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 */
}
/* 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 */
/* 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++ = '~';
/* 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];
/* 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);
}
/***********************************************************************
* 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 '/'.
* 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.
BOOL DOSFS_FindUnixName( LPCSTR path, LPCSTR name, LPSTR long_buf,
INT long_len, LPSTR short_buf, BOOL ignore_case)
BOOL ret;
const char *p = strchr( name, '/' );
int len = p ? (int)(p - name) : strlen(name);
if ((p = strchr( name, '\\' ))) len = MIN( (int)(p - name), len );
if (long_len < len + 1) return FALSE;
if (!DOSFS_ToDosFCBFormat( name, dos_name )) dos_name[0] = '\0';
while ((ret = DOSFS_ReadDir( dir, &long_name, &short_name )))
if (!lstrncmpA( long_name, name, len )) break;
if (!lstrncmpiA( long_name, name, len )) break;
{
DOSFS_Hash( long_name, tmp_buf, TRUE, ignore_case );
short_name = tmp_buf;
}
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 );
}
path, name, long_name, short_buf ? short_buf : "***");
WARN(dosfs, "'%s' not found in '%s'\n", name, path);
}
/***********************************************************************
* Check if a DOS file name represents a DOS device and return the device.
const DOS_DEVICE *DOSFS_GetDevice( const char *name )
if (!name) return NULL; /* if FILE_DupUnixHandle was used */
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++)
{
if (!lstrncmpiA( dev, name, strlen(dev) ))
/***********************************************************************
* DOSFS_GetDeviceByHandle
*/
const DOS_DEVICE *DOSFS_GetDeviceByHandle( HFILE hFile )
{
struct get_file_info_request req;
struct get_file_info_reply reply;
req.handle = hFile;
CLIENT_SendRequest( REQ_GET_FILE_INFO, -1, 1, &req, sizeof(req) );
if (!CLIENT_WaitSimpleReply( &reply, sizeof(reply), NULL ) &&
(reply.type == FILE_TYPE_UNKNOWN))
{
if ((reply.attr >= 0) &&
(reply.attr < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0])))
return &DOSFS_Devices[reply.attr];
}
return NULL;
}
/***********************************************************************
* DOSFS_OpenDevice
*
* Open a DOS device. This might not map 1:1 into the UNIX device concept.
HFILE DOSFS_OpenDevice( const char *name, DWORD access )
if (!name) return (HFILE)NULL; /* if FILE_DupUnixHandle was used */
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++)
{
if (!lstrncmpiA( dev, name, strlen(dev) ))
return FILE_CreateFile( "/dev/null", access,
FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
OPEN_EXISTING, 0, -1 );
HFILE to_dup;
HFILE handle;
switch (access & (GENERIC_READ|GENERIC_WRITE)) {
case GENERIC_READ:
return HFILE_ERROR;
if (!DuplicateHandle( GetCurrentProcess(), to_dup, GetCurrentProcess(),
&handle, 0, FALSE, DUPLICATE_SAME_ACCESS ))
handle = HFILE_ERROR;
if (!strcmp(DOSFS_Devices[i].name,"SCSIMGR$") ||
!strcmp(DOSFS_Devices[i].name,"HPSCAN"))
{
return FILE_CreateDevice( i, access, NULL );
FIXME(dosfs,"device open %s not supported (yet)\n",DOSFS_Devices[i].name);
return HFILE_ERROR;
return HFILE_ERROR;
/***********************************************************************
* Get the drive specified by a given path name (DOS or Unix format).
MSG("Warning: %s not accessible from a DOS drive\n", *name );
/* Assume it really was a DOS name */
drive = DRIVE_GetCurrentDrive();
}
}
else drive = DRIVE_GetCurrentDrive();
if (!DRIVE_IsValid(drive))
{
SetLastError( ERROR_INVALID_DRIVE );
return drive;
}
/***********************************************************************
* 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
* component is only checked if 'check_last' is non-zero.
* The buffers pointed to by 'long_buf' and 'short_buf' must be
* at least MAX_PATHNAME_LEN long.
BOOL DOSFS_GetFullName( LPCSTR name, BOOL check_last, DOS_FULL_NAME *full )
BOOL found;
UINT flags;
name, check_last );
if ((full->drive = DOSFS_GetPathDrive( &name )) == -1) return FALSE;
flags = DRIVE_GetFlags( full->drive );
lstrcpynA( 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 */
strcpy( full->short_name, "A:\\" );
full->short_name[0] += full->drive;
if ((*name == '\\') || (*name == '/')) /* Absolute path */
{
while ((*name == '\\') || (*name == '/')) name++;
}
lstrcpynA( root + 1, DRIVE_GetUnixCwd( full->drive ),
sizeof(full->long_name) - (root - full->long_name) - 1 );
lstrcpynA( full->short_name + 3, DRIVE_GetDosCwd( full->drive ),
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;
/* Check for '.' and '..' */
if (*name == '.')
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;
}
/* 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))
SetLastError( ERROR_PATH_NOT_FOUND );
/* 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) )))
*p_l++ = '/';
p_l += strlen(p_l);
*p_s++ = '\\';
p_s += strlen(p_s);
*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))
{
*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;
}
while ((*name == '\\') || (*name == '/')) name++;
}
SetLastError( ERROR_FILE_NOT_FOUND );
SetLastError( ERROR_PATH_NOT_FOUND );
if (!full->long_name[0]) strcpy( full->long_name, "/" );
if (!full->short_name[2]) strcpy( full->short_name + 2, "\\" );
full->long_name, full->short_name );
return TRUE;
}
/***********************************************************************
*
* NOTES
* observed:
* longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
* *longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0
DWORD WINAPI GetShortPathNameA( LPCSTR longpath, LPSTR shortpath,
if (!longpath)
{
SetLastError(ERROR_INVALID_PARAMETER);
return 0;
}
if (!longpath[0])
{
SetLastError(ERROR_BAD_PATHNAME);
return 0;
}
/* FIXME: is it correct to always return a fully qualified short path? */
if (!DOSFS_GetFullName( longpath, TRUE, &full_name ))
{
SetLastError(ERROR_BAD_PATHNAME);
return 0;
}
lstrcpynA( shortpath, full_name.short_name, shortlen );
return strlen( full_name.short_name );
}
/***********************************************************************
* GetShortPathName32W (KERNEL32.272)
*/
DWORD WINAPI GetShortPathNameW( LPCWSTR longpath, LPWSTR shortpath,
LPSTR longpathA ;
if (!longpath)
{ SetLastError(ERROR_INVALID_PARAMETER);
return 0;
}
if (!longpath[0])
{ SetLastError(ERROR_BAD_PATHNAME);
return 0;
}
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 );
}
SetLastError(ERROR_BAD_PATHNAME);
/***********************************************************************
* GetLongPathName32A (KERNEL32.xxx)
*/
DWORD WINAPI GetLongPathNameA( LPCSTR shortpath, LPSTR longpath,
char *p;
char *longfilename;
DWORD shortpathlen;
if (!DOSFS_GetFullName( shortpath, TRUE, &full_name )) return 0;
lstrcpynA( longpath, full_name.short_name, longlen );
/* Do some hackery to get the long filename.
* FIXME: Would be better if it returned the
* long version of the directories too
*/
longfilename = strrchr(full_name.long_name, '/')+1;
if (longpath != NULL) {
if ((p = strrchr( longpath, '\\' )) != NULL) {
p++;
longlen -= (p-longpath);
lstrcpynA( p, longfilename , longlen);
}
}
shortpathlen =
((strrchr( full_name.short_name, '\\' ) - full_name.short_name) + 1);
return shortpathlen + strlen( longfilename );
}
/***********************************************************************
* GetLongPathName32W (KERNEL32.269)
*/
DWORD WINAPI GetLongPathNameW( 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;
}
/***********************************************************************
* DOSFS_DoGetFullPathName
static DWORD DOSFS_DoGetFullPathName( LPCSTR name, DWORD len, LPSTR result,
BOOL unicode )
DWORD ret;
/* last possible position for a char != 0 */
char *endchar = buffer + sizeof(buffer) - 2;
*endchar = '\0';
TRACE(dosfs, "converting '%s'\n", name );
if (!name || !result || ((drive = DOSFS_GetPathDrive( &name )) == -1) )
{ SetLastError( ERROR_INVALID_PARAMETER );
return 0;