NT4/private/ole32/com/class/longpath.cxx
2020-09-30 17:12:29 +02:00

545 lines
14 KiB
C++

//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1994.
//
// File: longpath.cxx
//
// Contents: GetLongPathName implementation
//
// History: 25-Aug-94 DrewB Created from Win32 sources for
// GetShortPathName
// 06-Sep-94 DrewB Rewrote using Win32 for portability
// 11-Nov-94 BruceMa Use this version for both Chicago
// and NT
//
//----------------------------------------------------------------------------
#include <ole2int.h>
#include <widewrap.h>
#include <longname.h>
#define ARGUMENT_PRESENT2(p) ((p) != NULL)
//+---------------------------------------------------------------------------
//
// Function: IsLongComponent, public
//
// Synopsis: Determines whether the current path component is a legal
// 8.3 name or not. If not, it is considered to be a long
// component.
//
// Arguments: [pwcsPath] - Path to check
// [ppwcsEnd] - Return for end of component pointer
//
// Returns: BOOL
//
// Modifies: [ppwcsEnd]
//
// History: 28-Aug-94 DrewB Created
//
// Notes: An empty path is considered to be long
// The following characters are not valid in file name domain:
// * + , : ; < = > ? [ ] |
//
//----------------------------------------------------------------------------
BOOL IsLongComponent(LPCWSTR pwcsPath,
PWSTR *ppwcsEnd)
{
LPWSTR pwcEnd, pwcDot;
BOOL fLongNameFound;
WCHAR wc;
pwcEnd = (LPWSTR)pwcsPath;
fLongNameFound = FALSE;
pwcDot = NULL;
while (TRUE)
{
wc = *pwcEnd;
if (wc == L'\\' || wc == 0)
{
*ppwcsEnd = pwcEnd;
// We're at a component terminator, so make the
// determination of whether what we've seen is a long
// name or short one
// If we've aready seen illegal characters or invalid
// structure for a short name, don't bother to check lengths
if (pwcEnd-pwcsPath > 0 && !fLongNameFound)
{
// If this component fits in 8.3 then it is a short name
if ((!pwcDot && (ULONG)(pwcEnd - pwcsPath) <= 8) ||
(pwcDot && ((ULONG)(pwcEnd - pwcDot) <= 3 + 1 &&
(ULONG)(pwcEnd - pwcsPath) <= 8 + 3 + 1)))
{
return FALSE;
}
}
return TRUE;
}
// Handle dots
if (wc == L'.')
{
// If two or more '.' or the base name is longer than
// 8 characters or no base name at all, it is an illegal dos
// file name
if (pwcDot != NULL ||
((ULONG)(pwcEnd - pwcsPath)) > 8 ||
(pwcEnd == pwcsPath && *(pwcEnd + 1) != L'\\'))
{
fLongNameFound = TRUE;
}
pwcDot = pwcEnd;
}
// Check for characters which aren't valid in short names
else if (wc <= L' ' ||
wc == L'*' ||
wc == L'+' ||
wc == L',' ||
wc == L':' ||
wc == L';' ||
wc == L'<' ||
wc == L'=' ||
wc == L'>' ||
wc == L'?' ||
wc == L'[' ||
wc == L']' ||
wc == L'|')
{
fLongNameFound = TRUE;
}
pwcEnd++;
}
}
//
// The following code was stolen from NT's RTL in curdir.c
//
#define IS_PATH_SEPARATOR(wch) \
((wch) == L'\\' || (wch) == L'/')
typedef enum
{
PATH_TYPE_UNKNOWN,
PATH_TYPE_UNC_ABSOLUTE,
PATH_TYPE_LOCAL_DEVICE,
PATH_TYPE_ROOT_LOCAL_DEVICE,
PATH_TYPE_DRIVE_ABSOLUTE,
PATH_TYPE_DRIVE_RELATIVE,
PATH_TYPE_ROOTED,
PATH_TYPE_RELATIVE
} PATH_TYPE;
PATH_TYPE
DetermineDosPathNameType(
IN PCWSTR DosFileName
)
/*++
Routine Description:
This function examines the Dos format file name and determines the
type of file name (i.e. UNC, DriveAbsolute, Current Directory
rooted, or Relative.
Arguments:
DosFileName - Supplies the Dos format file name whose type is to be
determined.
Return Value:
PATH_TYPE_UNKNOWN - The path type can not be determined
PATH_TYPE_UNC_ABSOLUTE - The path specifies a Unc absolute path
in the format \\server-name\sharename\rest-of-path
PATH_TYPE_LOCAL_DEVICE - The path specifies a local device in the format
\\.\rest-of-path this can be used for any device where the nt and
Win32 names are the same. For example mailslots.
PATH_TYPE_ROOT_LOCAL_DEVICE - The path specifies the root of the local
devices in the format \\.
PATH_TYPE_DRIVE_ABSOLUTE - The path specifies a drive letter absolute
path in the form drive:\rest-of-path
PATH_TYPE_DRIVE_RELATIVE - The path specifies a drive letter relative
path in the form drive:rest-of-path
PATH_TYPE_ROOTED - The path is rooted relative to the current disk
designator (either Unc disk, or drive). The form is \rest-of-path.
PATH_TYPE_RELATIVE - The path is relative (i.e. not absolute or rooted).
--*/
{
PATH_TYPE ReturnValue;
if ( IS_PATH_SEPARATOR(*DosFileName) )
{
if ( IS_PATH_SEPARATOR(*(DosFileName+1)) )
{
if ( DosFileName[2] == L'.' )
{
if ( IS_PATH_SEPARATOR(*(DosFileName+3)) )
{
ReturnValue = PATH_TYPE_LOCAL_DEVICE;
}
else if ( (*(DosFileName+3)) == 0 )
{
ReturnValue = PATH_TYPE_ROOT_LOCAL_DEVICE;
}
else
{
ReturnValue = PATH_TYPE_UNC_ABSOLUTE;
}
}
else
{
ReturnValue = PATH_TYPE_UNC_ABSOLUTE;
}
}
else
{
ReturnValue = PATH_TYPE_ROOTED;
}
}
else if (*(DosFileName+1) == L':')
{
if (IS_PATH_SEPARATOR(*(DosFileName+2)))
{
ReturnValue = PATH_TYPE_DRIVE_ABSOLUTE;
}
else
{
ReturnValue = PATH_TYPE_DRIVE_RELATIVE;
}
}
else
{
ReturnValue = PATH_TYPE_RELATIVE;
}
return ReturnValue;
}
//+---------------------------------------------------------------------------
//
// Function: GetLongPathName, public
//
// Synopsis: Expand each component of the given path into its
// long form
//
// Arguments: [pwcsPath] - Path
// [pwcsLongPath] - Long path return buffer
// [cchLongPath] - Size of return buffer in characters
//
// Returns: 0 for errors
// Number of characters needed for buffer if buffer is too small
// includes NULL terminator
// Length of long path, doesn't include NULL terminator
//
// Modifies: [pwcsLongPath]
//
// History: 28-Aug-94 DrewB Created
// 11-Nov-94 BruceMa Modifed to use for Chicago at
// FindFirstFile
//
// Notes: The source and destination buffers can be the same memory
// Doesn't handle paths with internal . and .., although
// they are handled at the beginning
//
//----------------------------------------------------------------------------
ULONG
APIENTRY
GetLongPathNameW(LPCWSTR pwcsPath,
LPWSTR pwcsLongPath,
ULONG cchLongPath)
{
PATH_TYPE pt;
HANDLE h;
LPWSTR pwcsLocalLongPath;
ULONG cchReturn, cb, cch, cchOutput;
LPWSTR pwcStart = NULL;
LPWSTR pwcEnd;
LPWSTR pwcLong;
WCHAR wcSave;
BOOL fLong;
WIN32_FIND_DATA wfd;
cchReturn = 0;
pwcsLocalLongPath = NULL;
if (!ARGUMENT_PRESENT2(pwcsPath))
{
return 0;
}
__try
{
//
// First, run down the string checking for tilde's. Any path
// that has a short name section to it will have a tilde. If
// there are no tilde's, then we already have the long path,
// so we can return the string.
//
fLong = TRUE;
for (pwcLong = (LPWSTR)pwcsPath; *pwcLong != 0; pwcLong++)
{
if (*pwcLong == L'~')
{
fLong = FALSE;
}
}
//
// This derives the number of characters, including the NULL
//
cch = (pwcLong - pwcsPath) + 1;
//
// If it isn't a long path already, then we are going to have
// to parse it.
//
if (!fLong)
{
// Decide the path type, we want find out the position of
// the first character of the first name
pt = DetermineDosPathNameType(pwcsPath);
switch(pt)
{
// Form: "\\server_name\share_name\rest_of_the_path"
case PATH_TYPE_UNC_ABSOLUTE:
if ((pwcStart = wcschr(pwcsPath + 2, L'\\')) != NULL &&
(pwcStart = wcschr(pwcStart + 1, L'\\')) != NULL)
{
pwcStart++;
}
else
{
pwcStart = NULL;
}
break;
// Form: "\\.\rest_of_the_path"
case PATH_TYPE_LOCAL_DEVICE:
pwcStart = (LPWSTR)pwcsPath + 4;
break;
// Form: "\\."
case PATH_TYPE_ROOT_LOCAL_DEVICE:
pwcStart = NULL;
break;
// Form: "D:\rest_of_the_path"
case PATH_TYPE_DRIVE_ABSOLUTE:
pwcStart = (LPWSTR)pwcsPath + 3;
break;
// Form: "rest_of_the_path"
case PATH_TYPE_RELATIVE:
pwcStart = (LPWSTR) pwcsPath;
goto EatDots;
// Form: "D:rest_of_the_path"
case PATH_TYPE_DRIVE_RELATIVE:
pwcStart = (LPWSTR)pwcsPath+2;
EatDots:
// Handle .\ and ..\ cases
while (*pwcStart != 0 && *pwcStart == L'.')
{
if (pwcStart[1] == L'\\')
{
pwcStart += 2;
}
else if (pwcStart[1] == L'.' && pwcStart[2] == L'\\')
{
pwcStart += 3;
}
else
{
break;
}
}
break;
// Form: "\rest_of_the_path"
case PATH_TYPE_ROOTED:
pwcStart = (LPWSTR)pwcsPath + 1;
break;
default:
pwcStart = NULL;
break;
}
}
// In the special case where we have no work to do, exit quickly
// This saves a lot of instructions for trivial cases
// In one case the path as given requires no processing
// The middle case, we determine there were no tilde's in the path
// In the other, the path only has one component and it is already
// long
///
if (pwcStart == NULL ||
(fLong == TRUE) ||
((fLong = IsLongComponent(pwcStart, &pwcEnd)) &&
*pwcEnd == 0))
{
// Nothing to convert, copy down the source string
// to the buffer if necessary
if (pwcStart != NULL)
{
cch = (ULONG)(pwcEnd - pwcsPath + 1);
}
if (cchLongPath >= cch)
{
// If there's an output buffer which is different from
// the input buffer, fill it in
if (ARGUMENT_PRESENT2(pwcsLongPath) &&
pwcsLongPath != pwcsPath)
{
memcpy(pwcsLongPath, pwcsPath, cch * sizeof(WCHAR));
}
cchReturn = cch - 1;
goto gsnTryExit;
}
else
{
cchReturn = cch;
goto gsnTryExit;
}
}
// Make a local buffer so that we won't overlap the
// source pathname in case the long name is longer than the
// source name.
if (cchLongPath > 0 && ARGUMENT_PRESENT2(pwcsLongPath))
{
pwcsLocalLongPath = (PWCHAR)PrivMemAlloc(cchLongPath * sizeof(WCHAR));
if (pwcsLocalLongPath == NULL)
{
goto gsnTryExit;
}
}
// Set up pointer to copy output to
pwcLong = pwcsLocalLongPath;
cchOutput = 0;
// Copy the portions of the path that we skipped initially
cch = pwcStart-pwcsPath;
cchOutput += cch;
if (cchOutput <= cchLongPath && ARGUMENT_PRESENT2(pwcsLongPath))
{
memcpy(pwcLong, pwcsPath, cch*sizeof(WCHAR));
pwcLong += cch;
}
for (;;)
{
// Determine whether the current component is long or short
cch = pwcEnd-pwcStart+1;
cb = cch*sizeof(WCHAR);
if (fLong)
{
// If the component is already long, just copy it into
// the output. Copy the terminating character along with it
// so the output remains properly punctuated
cchOutput += cch;
if (cchOutput <= cchLongPath && ARGUMENT_PRESENT2(pwcsLongPath))
{
memcpy(pwcLong, pwcStart, cb);
pwcLong += cch;
}
}
else
{
WCHAR wcsTmp[MAX_PATH];
// For a short component we need to determine the
// long name, if there is one. The only way to
// do this reliably is to enumerate for the child
wcSave = *pwcEnd;
*pwcEnd = 0;
h = FindFirstFile(pwcsPath, &wfd);
*pwcEnd = wcSave;
if (h == INVALID_HANDLE_VALUE)
{
goto gsnTryExit;
}
FindClose(h);
lstrcpyW(wcsTmp, wfd.cFileName);
// Copy the filename returned by the query into the output
// Copy the terminator from the original component into
// the output to maintain punctuation
cch = lstrlenW(wcsTmp)+1;
cchOutput += cch;
if (cchOutput <= cchLongPath && ARGUMENT_PRESENT2(pwcsLongPath))
{
memcpy(pwcLong, wcsTmp, (cch-1)*sizeof(WCHAR));
pwcLong += cch;
*(pwcLong-1) = *pwcEnd;
}
}
if (*pwcEnd == 0)
{
break;
}
// Update start pointer to next component
pwcStart = pwcEnd+1;
fLong = IsLongComponent(pwcStart, &pwcEnd);
}
// Copy local output buffer to given output buffer if necessary
if (cchLongPath >= cchOutput && ARGUMENT_PRESENT2(pwcsLongPath))
{
memcpy(pwcsLongPath, pwcsLocalLongPath, cchOutput * sizeof(WCHAR));
cchReturn = cchOutput-1;
}
else
{
cchReturn = cchOutput;
}
gsnTryExit:;
}
__finally
{
if (pwcsLocalLongPath != NULL)
{
PrivMemFree(pwcsLocalLongPath);
pwcsLocalLongPath = NULL;
}
}
return cchReturn;
}