Newer
Older
/*
* DOS file system functions
*
* Copyright 1993 Erik Bos
* Copyright 1996 Alexandre Julliard
*/
#ifdef HAVE_SYS_ERRNO_H
#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"
#include "options.h"
#include "debugtools.h"
DEFAULT_DEBUG_CHANNEL(dosfs)
DECLARE_DEBUG_CHANNEL(file)
/* Define the VFAT ioctl to get both short and long file names */
/* FIXME: is it possible to get this to work on other systems? */
/* 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;
#define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, KERNEL_DIRENT [2] )
#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 )
{
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) break;
}
else if (*mask != '?')
{
if (case_sensitive)
{
if (*mask != *name) return 0;
}
else if (toupper(*mask) != toupper(*name)) return 0;
}
mask++;
name++;
}
if (*mask == '.') mask++; /* Ignore trailing '.' in 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 = "/";
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
374
375
376
377
378
379
380
381
#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 );
/* Ignore trailing dots */
while (len > 1 && name[len-1] == '.') len--;
TRACE("%s,%s\n", path, name );
if (!DOSFS_ToDosFCBFormat( name, dos_name )) dos_name[0] = '\0';
WARN("(%s,%s): can't open dir: %s\n",
while ((ret = DOSFS_ReadDir( dir, &long_name, &short_name )))
if (!strncmp( 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 );
}
TRACE("(%s,%s) -> %s (%s)\n",
path, name, long_name, short_buf ? short_buf : "***");
WARN("'%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 = get_req_buffer();
req->handle = hFile;
if (!server_call( REQ_GET_FILE_INFO ) && (req->type == FILE_TYPE_UNKNOWN))
if ((req->attr >= 0) &&
(req->attr < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0])))
return &DOSFS_Devices[req->attr];
/***********************************************************************
* 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:
FIXME("can't open CON read/write\n");
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 );
{
HFILE r;
char devname[40];
PROFILE_GetWineIniString("serialports",name,"",devname,sizeof devname);
if(devname[0])
{
TRACE_(file)("DOSFS_OpenDevice %s is %s\n",
DOSFS_Devices[i].name,devname);
r = FILE_CreateFile( devname, access,
FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
OPEN_EXISTING, 0, -1 );
TRACE_(file)("Create_File return %08X\n",r);
return r;
}
}
FIXME("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).
MESSAGE("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;
TRACE("%s (last=%d)\n", 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, "\\" );
TRACE("returning %s = %s\n", full->long_name, full->short_name );
}
/***********************************************************************
* GetShortPathNameA (KERNEL32.271)
*
* NOTES
* observed:
* longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
* *longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0
*
* more observations ( with NT 3.51 (WinDD) ):
* longpath <= 8.3 -> just copy longpath to shortpath
* longpath > 8.3 ->
* 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
* - the absolute/relative path of the short name is reproduced like found
* in the long name
* - longpath and shortpath may have the same adress
* Peter Ganten, 1999
DWORD WINAPI GetShortPathNameA( LPCSTR longpath, LPSTR shortpath,
DWORD sp = 0, lp = 0;
int tmplen, drive;
UINT flags;
TRACE("%s\n", longpath);
if (!longpath) {
SetLastError(ERROR_INVALID_PARAMETER);
return 0;
}
if (!longpath[0]) {
SetLastError(ERROR_BAD_PATHNAME);
return 0;
if ( ( tmpshortpath = HeapAlloc ( GetProcessHeap(), 0, MAX_PATHNAME_LEN ) ) == NULL ) {
SetLastError ( ERROR_NOT_ENOUGH_MEMORY );
return 0;
if ( longpath[1] == ':' ) {
tmpshortpath[0] = longpath[0];
tmpshortpath[1] = ':';
sp = 2;
}
if ( ( drive = DOSFS_GetPathDrive ( &longpath )) == -1 ) return 0;
flags = DRIVE_GetFlags ( drive );
while ( longpath[lp] ) {
/* check for path delimiters and reproduce them */
if ( longpath[lp] == '\\' || longpath[lp] == '/' ) {
tmpshortpath[sp] = longpath[lp];
sp++;
lp++;
tmplen = strcspn ( longpath + lp, "\\/" );
lstrcpynA ( tmpshortpath+sp, longpath + lp, tmplen+1 );
/* Check, if the current element is a valid dos name */
if ( DOSFS_ValidDOSName ( longpath + lp, !(flags & DRIVE_CASE_SENSITIVE) ) ) {
sp += tmplen;
lp += tmplen;
continue;
/* Check if the file exists and use the existing file name */
if ( DOSFS_GetFullName ( tmpshortpath, TRUE, &full_name ) ) {
lstrcpyA ( tmpshortpath+sp, strrchr ( full_name.short_name, '\\' ) + 1 );
sp += lstrlenA ( tmpshortpath+sp );
lp += tmplen;
continue;
}
TRACE("not found!\n" );
SetLastError ( ERROR_FILE_NOT_FOUND );
return 0;
lstrcpynA ( shortpath, tmpshortpath, shortlen );
TRACE("returning %s\n", shortpath );
tmplen = lstrlenA ( tmpshortpath );
HeapFree ( GetProcessHeap(), 0, tmpshortpath );
}
/***********************************************************************
* GetShortPathName32W (KERNEL32.272)
*/
DWORD WINAPI GetShortPathNameW( LPCWSTR longpath, LPWSTR shortpath,
LPSTR longpathA, shortpathA;
longpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, longpath );
shortpathA = HEAP_xalloc ( GetProcessHeap(), 0, shortlen );
ret = GetShortPathNameA ( longpathA, shortpathA, shortlen );
lstrcpynAtoW ( shortpath, shortpathA, shortlen );
HeapFree( GetProcessHeap(), 0, shortpathA );
return ret;
/***********************************************************************
* GetLongPathName32A (KERNEL32.xxx)
*/
DWORD WINAPI GetLongPathNameA( LPCSTR shortpath, LPSTR longpath,