- RtlGetFullPathName_U: rewritten so that the source & destination

buffer can be the same
- GetFullPathName[AW]: now call RtlGetFullPathName_U
- GetShortPathNameW: fixed regression introduced in last patch
This commit is contained in:
Eric Pouech 2004-01-20 01:40:23 +00:00 committed by Alexandre Julliard
parent eae9444bc8
commit 6c8ee62340
3 changed files with 147 additions and 349 deletions

View File

@ -42,6 +42,76 @@ WINE_DEFAULT_DEBUG_CHANNEL(file);
#define MAX_PATHNAME_LEN 1024
/***********************************************************************
* GetFullPathNameW (KERNEL32.@)
* NOTES
* if the path closed with '\', *lastpart is 0
*/
DWORD WINAPI GetFullPathNameW( LPCWSTR name, DWORD len, LPWSTR buffer,
LPWSTR *lastpart )
{
return RtlGetFullPathName_U(name, len * sizeof(WCHAR), buffer, lastpart) / sizeof(WCHAR);
}
/***********************************************************************
* GetFullPathNameA (KERNEL32.@)
* NOTES
* if the path closed with '\', *lastpart is 0
*/
DWORD WINAPI GetFullPathNameA( LPCSTR name, DWORD len, LPSTR buffer,
LPSTR *lastpart )
{
UNICODE_STRING nameW;
WCHAR bufferW[MAX_PATH];
DWORD ret, retW;
if (!name)
{
SetLastError(ERROR_INVALID_PARAMETER);
return 0;
}
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 && ret <= len)
{
WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, len, NULL, NULL);
ret--; /* length without 0 */
if (lastpart)
{
LPSTR p = buffer + strlen(buffer) - 1;
if (*p != '\\')
{
while ((p > buffer + 2) && (*p != '\\')) p--;
*lastpart = p + 1;
}
else *lastpart = NULL;
}
}
}
RtlFreeUnicodeString(&nameW);
return ret;
}
/***********************************************************************
* GetLongPathNameW (KERNEL32.@)
@ -266,20 +336,21 @@ DWORD WINAPI GetShortPathNameW( LPCWSTR longpath, LPWSTR shortpath, DWORD shortl
for (p = longpath + lp; *p && *p != '/' && *p != '\\'; p++);
tmplen = p - (longpath + lp);
lstrcpynW(tmpshortpath + sp, longpath + lp, tmplen + 1);
/* Check, if the current element is a valid dos name */
if (tmplen <= 8+1+3+1)
{
memcpy(ustr_buf, longpath + lp, tmplen * sizeof(WCHAR));
ustr_buf[tmplen] = '\0';
ustr.Length = tmplen * sizeof(WCHAR);
/* Check, if the current element is a valid dos name */
if (!RtlIsNameLegalDOS8Dot3(&ustr, NULL, NULL))
goto notfound;
if (RtlIsNameLegalDOS8Dot3(&ustr, NULL, NULL))
{
sp += tmplen;
lp += tmplen;
continue;
}
}
/* Check if the file exists and use the existing file name */
/* Check if the file exists and use the existing short file name */
goit = FindFirstFileW(tmpshortpath, &wfd);
if (goit == INVALID_HANDLE_VALUE) goto notfound;
FindClose(goit);

View File

@ -320,54 +320,49 @@ ULONG WINAPI RtlDosSearchPath_U(LPCWSTR paths, LPCWSTR search, LPCWSTR ext,
* get_full_path_helper
*
* Helper for RtlGetFullPathName_U
* Note: name and buffer are allowed to point to the same memory spot
*/
static ULONG get_full_path_helper(LPCWSTR name, LPWSTR buffer, ULONG size)
{
ULONG reqsize, mark = 0;
ULONG reqsize = 0, mark = 0, dep = 0, deplen;
DOS_PATHNAME_TYPE type;
LPWSTR ptr;
UNICODE_STRING* cd;
reqsize = sizeof(WCHAR); /* '\0' at the end */
LPWSTR ptr, ins_str = NULL;
const UNICODE_STRING* cd;
WCHAR tmp[4];
RtlAcquirePebLock();
cd = &ntdll_get_process_pmts()->CurrentDirectoryName;
switch (type = RtlDetermineDosPathNameType_U(name))
{
case UNC_PATH: /* \\foo */
case DEVICE_PATH: /* \\.\foo */
if (reqsize <= size) buffer[0] = '\0';
break;
case ABSOLUTE_DRIVE_PATH: /* c:\foo */
reqsize += sizeof(WCHAR);
if (reqsize <= size)
{
buffer[0] = toupperW(name[0]);
buffer[1] = '\0';
}
name++;
reqsize = sizeof(WCHAR);
tmp[0] = toupperW(name[0]);
ins_str = tmp;
dep = 1;
break;
case RELATIVE_DRIVE_PATH: /* c:foo */
dep = 2;
if (toupperW(name[0]) != toupperW(cd->Buffer[0]) || cd->Buffer[1] != ':')
{
WCHAR drive[4];
UNICODE_STRING var, val;
drive[0] = '=';
drive[1] = name[0];
drive[2] = ':';
drive[3] = '\0';
var.Length = 6;
var.MaximumLength = 8;
var.Buffer = drive;
tmp[0] = '=';
tmp[1] = name[0];
tmp[2] = ':';
tmp[3] = '\0';
var.Length = 3 * sizeof(WCHAR);
var.MaximumLength = 4 * sizeof(WCHAR);
var.Buffer = tmp;
val.Length = 0;
val.MaximumLength = size;
val.Buffer = buffer;
name += 2;
val.Buffer = RtlAllocateHeap(GetProcessHeap(), 0, size);
switch (RtlQueryEnvironmentVariable_U(NULL, &var, &val))
{
@ -379,24 +374,16 @@ static ULONG get_full_path_helper(LPCWSTR name, LPWSTR buffer, ULONG size)
*/
/* fall thru */
case STATUS_BUFFER_TOO_SMALL:
reqsize += val.Length;
/* append trailing \\ */
reqsize += sizeof(WCHAR);
if (reqsize <= size)
{
buffer[reqsize / sizeof(WCHAR) - 2] = '\\';
buffer[reqsize / sizeof(WCHAR) - 1] = '\0';
}
reqsize = val.Length + sizeof(WCHAR); /* append trailing '\\' */
val.Buffer[val.Length / sizeof(WCHAR)] = '\\';
ins_str = val.Buffer;
break;
case STATUS_VARIABLE_NOT_FOUND:
reqsize += 3 * sizeof(WCHAR);
if (reqsize <= size)
{
buffer[0] = drive[1];
buffer[1] = ':';
buffer[2] = '\\';
buffer[3] = '\0';
}
reqsize = 3 * sizeof(WCHAR);
tmp[0] = name[0];
tmp[1] = ':';
tmp[2] = '\\';
ins_str = tmp;
break;
default:
ERR("Unsupported status code\n");
@ -404,16 +391,11 @@ static ULONG get_full_path_helper(LPCWSTR name, LPWSTR buffer, ULONG size)
}
break;
}
name += 2;
/* fall through */
case RELATIVE_PATH: /* foo */
reqsize += cd->Length;
if (reqsize <= size)
{
memcpy(buffer, cd->Buffer, cd->Length);
buffer[cd->Length / sizeof(WCHAR)] = 0;
}
reqsize = cd->Length;
ins_str = cd->Buffer;
if (cd->Buffer[1] != ':')
{
ptr = strchrW(cd->Buffer + 2, '\\');
@ -426,69 +408,62 @@ static ULONG get_full_path_helper(LPCWSTR name, LPWSTR buffer, ULONG size)
case ABSOLUTE_PATH: /* \xxx */
if (cd->Buffer[1] == ':')
{
reqsize += 2 * sizeof(WCHAR);
if (reqsize <= size)
{
buffer[0] = cd->Buffer[0];
buffer[1] = ':';
buffer[2] = '\0';
}
reqsize = 2 * sizeof(WCHAR);
tmp[0] = cd->Buffer[0];
tmp[1] = ':';
ins_str = tmp;
}
else
{
unsigned len;
ptr = strchrW(cd->Buffer + 2, '\\');
if (ptr) ptr = strchrW(ptr + 1, '\\');
if (!ptr) ptr = cd->Buffer + strlenW(cd->Buffer);
len = (ptr - cd->Buffer) * sizeof(WCHAR);
reqsize += len;
mark = len / sizeof(WCHAR);
if (reqsize <= size)
{
memcpy(buffer, cd->Buffer, len);
buffer[len / sizeof(WCHAR)] = '\0';
}
else
buffer[0] = '\0';
reqsize = (ptr - cd->Buffer) * sizeof(WCHAR);
mark = reqsize / sizeof(WCHAR);
ins_str = cd->Buffer;
}
break;
case UNC_DOT_PATH: /* \\. */
reqsize += 4 * sizeof(WCHAR);
name += 3;
if (reqsize <= size)
{
buffer[0] = '\\';
buffer[1] = '\\';
buffer[2] = '.';
buffer[3] = '\\';
buffer[4] = '\0';
}
reqsize = 4 * sizeof(WCHAR);
dep = 3;
tmp[0] = '\\';
tmp[1] = '\\';
tmp[2] = '.';
tmp[3] = '\\';
ins_str = tmp;
break;
case INVALID_PATH:
reqsize = 0;
goto done;
}
reqsize += strlenW(name) * sizeof(WCHAR);
if (reqsize > size) goto done;
/* enough space ? */
deplen = strlenW(name + dep) * sizeof(WCHAR);
if (reqsize + deplen + sizeof(WCHAR) > size)
{
/* not enough space, return need size (including terminating '\0') */
reqsize += deplen + sizeof(WCHAR);
goto done;
}
strcatW(buffer, name);
memmove(buffer + reqsize / sizeof(WCHAR), name + dep, deplen + sizeof(WCHAR));
if (reqsize) memcpy(buffer, ins_str, reqsize);
reqsize += deplen;
if (ins_str && ins_str != tmp && ins_str != cd->Buffer)
RtlFreeHeap(GetProcessHeap(), 0, ins_str);
/* convert every / into a \ */
for (ptr = buffer; *ptr; ptr++)
if (*ptr == '/') *ptr = '\\';
reqsize -= sizeof(WCHAR); /* don't count trailing \0 */
for (ptr = buffer; *ptr; ptr++) if (*ptr == '/') *ptr = '\\';
/* mark is non NULL for UNC names, so start path collapsing after server & share name
* otherwise, it's a fully qualified DOS name, so start after the drive designation
*/
for (ptr = buffer + (mark ? mark : 2); ptr < buffer + reqsize / sizeof(WCHAR); )
{
WCHAR* p = strchrW(ptr, '\\');
LPWSTR prev, p = strchrW(ptr, '\\');
if (!p) break;
p++;
@ -500,15 +475,13 @@ static ULONG get_full_path_helper(LPCWSTR name, LPWSTR buffer, ULONG size)
switch (p[2])
{
case '\\':
{
WCHAR* prev = p - 2;
prev = p - 2;
while (prev >= buffer + mark && *prev != '\\') prev--;
/* either collapse \foo\.. into \ or \.. into \ */
if (prev < buffer + mark) prev = p - 1;
reqsize -= (p + 2 - prev) * sizeof(WCHAR);
memmove(prev, p + 2, reqsize + sizeof(WCHAR) - (prev - buffer) * sizeof(WCHAR));
p = prev;
}
break;
case '\0':
reqsize -= 2 * sizeof(WCHAR);

View File

@ -1172,252 +1172,6 @@ BOOL DOSFS_GetFullName( LPCWSTR name, BOOL check_last, DOS_FULL_NAME *full )
}
/***********************************************************************
* DOSFS_DoGetFullPathName
*
* Implementation of GetFullPathNameA/W.
*
* bon@elektron 000331:
* A test for GetFullPathName with many pathological cases
* now gives identical output for Wine and OSR2
*/
static DWORD DOSFS_DoGetFullPathName( LPCWSTR name, DWORD len, LPWSTR result )
{
DWORD ret;
DOS_FULL_NAME full_name;
LPWSTR p, q;
char *p_l;
const char * root;
WCHAR drivecur[] = {'C',':','.',0};
WCHAR driveletter=0;
int namelen,drive=0;
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};
if (!name[0])
{
SetLastError(ERROR_BAD_PATHNAME);
return 0;
}
TRACE("passed %s\n", debugstr_w(name));
if (name[1]==':')
/*drive letter given */
{
driveletter = name[0];
}
if ((name[1]==':') && ((name[2]=='\\') || (name[2]=='/')))
/*absolute path given */
{
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';
}
else
{
if (driveletter)
drivecur[0]=driveletter;
else if ((name[0]=='\\') || (name[0]=='/'))
strcpyW(drivecur, bkslashW);
else
strcpyW(drivecur, dotW);
if (!DOSFS_GetFullName( drivecur, FALSE, &full_name ))
{
FIXME("internal: error getting drive/path\n");
return 0;
}
/* find path that drive letter substitutes*/
drive = toupperW(full_name.short_name[0]) - 'A';
root= DRIVE_GetRoot(drive);
if (!root)
{
FIXME("internal: error getting DOS Drive Root\n");
return 0;
}
if (!strcmp(root,"/"))
{
/* we have just the last / and we need it. */
p_l = full_name.long_name;
}
else
{
p_l = full_name.long_name + strlen(root);
}
/* append long name (= unix name) to drive */
MultiByteToWideChar(CP_UNIXCP, 0, p_l, -1, full_name.short_name + 2, MAX_PATHNAME_LEN - 3);
/* append name to treat */
namelen= strlenW(full_name.short_name);
p = (LPWSTR)name;
if (driveletter)
p += 2; /* skip drive name when appending */
if (namelen + 2 + strlenW(p) > MAX_PATHNAME_LEN)
{
FIXME("internal error: buffer too small\n");
return 0;
}
full_name.short_name[namelen++] ='\\';
full_name.short_name[namelen] = 0;
strncpyW(full_name.short_name + namelen, p, MAX_PATHNAME_LEN - namelen);
full_name.short_name[MAX_PATHNAME_LEN - 1] = 0; /* ensure 0 termination */
}
/* reverse all slashes */
for (p=full_name.short_name;
p < full_name.short_name + strlenW(full_name.short_name);
p++)
{
if ( *p == '/' )
*p = '\\';
}
/* Use memmove, as areas overlap */
/* Delete .. */
while ((p = strstrW(full_name.short_name, updir_slashW)))
{
if (p > full_name.short_name+2)
{
*p = 0;
q = strrchrW(full_name.short_name, '\\');
memmove(q+1, p+4, (strlenW(p+4)+1) * sizeof(WCHAR));
}
else
{
memmove(full_name.short_name+3, p+4, (strlenW(p+4)+1) * sizeof(WCHAR));
}
}
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,
(strlenW(full_name.short_name+4)+1) * sizeof(WCHAR));
}
/* Delete . */
while ((p = strstrW(full_name.short_name, curdirW)))
{
*(p+1) = 0;
memmove(p+1, p+3, (strlenW(p+3)+1) * sizeof(WCHAR));
}
if (!(DRIVE_GetFlags(drive) & DRIVE_CASE_PRESERVING))
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))
{
/* one more strange case: "c:\test\test1\.."
return "c:\test" */
*(full_name.short_name+namelen-3)=0;
q = strrchrW(full_name.short_name, '\\');
*q =0;
}
if (full_name.short_name[namelen-1]=='.')
full_name.short_name[(namelen--)-1] =0;
TRACE("got %s\n", debugstr_w(full_name.short_name));
/* If the lpBuffer buffer is too small, the return value is the
size of the buffer, in characters, required to hold the path
plus the terminating \0 (tested against win95osr2, bon 001118)
. */
ret = strlenW(full_name.short_name);
if (ret >= len )
{
/* don't touch anything when the buffer is not large enough */
SetLastError( ERROR_INSUFFICIENT_BUFFER );
return ret+1;
}
if (result)
{
strncpyW( result, full_name.short_name, len );
result[len - 1] = 0; /* ensure 0 termination */
}
TRACE("returning %s\n", debugstr_w(full_name.short_name) );
return ret;
}
/***********************************************************************
* GetFullPathNameA (KERNEL32.@)
* NOTES
* if the path closed with '\', *lastpart is 0
*/
DWORD WINAPI GetFullPathNameA( LPCSTR name, DWORD len, LPSTR buffer,
LPSTR *lastpart )
{
UNICODE_STRING nameW;
WCHAR bufferW[MAX_PATH];
DWORD ret, retW;
if (!name)
{
SetLastError(ERROR_INVALID_PARAMETER);
return 0;
}
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)
{
WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, len, NULL, NULL);
ret--; /* length without 0 */
if (lastpart)
{
LPSTR p = buffer + strlen(buffer) - 1;
if (*p != '\\')
{
while ((p > buffer + 2) && (*p != '\\')) p--;
*lastpart = p + 1;
}
else *lastpart = NULL;
}
}
}
RtlFreeUnicodeString(&nameW);
return ret;
}
/***********************************************************************
* GetFullPathNameW (KERNEL32.@)
*/
DWORD WINAPI GetFullPathNameW( LPCWSTR name, DWORD len, LPWSTR buffer,
LPWSTR *lastpart )
{
DWORD ret = DOSFS_DoGetFullPathName( name, len, buffer );
if (ret && (ret<=len) && buffer && lastpart)
{
LPWSTR p = buffer + strlenW(buffer) - 1;
if (*p != (WCHAR)'\\')
{
while ((p > buffer + 2) && (*p != (WCHAR)'\\')) p--;
*lastpart = p + 1;
}
else *lastpart = NULL;
}
return ret;
}
/***********************************************************************
* wine_get_unix_file_name (KERNEL32.@) Not a Windows API
*