You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2060 lines
65 KiB
2060 lines
65 KiB
#include "shellprv.h"
|
|
#pragma hdrstop
|
|
|
|
// from mtpt.cpp
|
|
STDAPI_(BOOL) CMtPt_IsLFN(int iDrive);
|
|
STDAPI_(BOOL) CMtPt_IsSlow(int iDrive);
|
|
|
|
__inline BOOL DBL_BSLASH(LPNCTSTR psz)
|
|
{
|
|
return (psz[0] == TEXT('\\') && psz[1] == TEXT('\\'));
|
|
}
|
|
|
|
#define IsPathSep(ch) ((ch) == TEXT('\\') || (ch) == TEXT('/'))
|
|
|
|
// in:
|
|
// pszPath fully qualified path (unc or x:\) to test
|
|
// NULL for windows directory
|
|
//
|
|
// returns:
|
|
// TRUE volume supports name longer than 12 chars
|
|
//
|
|
// note: this caches drive letters, but UNCs go through every time
|
|
//
|
|
STDAPI_(BOOL) IsLFNDrive(LPCTSTR pszPath)
|
|
{
|
|
TCHAR szRoot[MAX_PATH];
|
|
DWORD dwMaxLength = 13; // assume yes
|
|
|
|
ASSERT(NULL == pszPath || IS_VALID_STRING_PTR(pszPath, -1));
|
|
|
|
if ((pszPath == NULL) || !*pszPath)
|
|
{
|
|
*szRoot = 0;
|
|
GetWindowsDirectory(szRoot, ARRAYSIZE(szRoot));
|
|
pszPath = szRoot;
|
|
}
|
|
|
|
ASSERT(!PathIsRelative(pszPath));
|
|
|
|
//
|
|
// UNC name? gota check each time
|
|
//
|
|
if (PathIsUNC(pszPath))
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = StringCchCopy(szRoot, ARRAYSIZE(szRoot), pszPath);
|
|
if (FAILED(hr))
|
|
{
|
|
return FALSE; // sorry, path to long, assume no LFN
|
|
}
|
|
PathStripToRoot(szRoot);
|
|
|
|
// Deal with busted kernel UNC stuff
|
|
// Is it a \\foo or a \\foo\bar thing?
|
|
|
|
if (StrChr(szRoot+2, TEXT('\\')))
|
|
{
|
|
// "\\foo\bar - Append a slash to be NT compatible.
|
|
hr = StringCchCat(szRoot, ARRAYSIZE(szRoot), TEXT("\\"));
|
|
if (FAILED(hr))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// "\\foo" - assume it's always a LFN volume
|
|
return TRUE;
|
|
}
|
|
}
|
|
//
|
|
// removable media? gota check each time
|
|
//
|
|
else if (IsRemovableDrive(DRIVEID(pszPath)))
|
|
{
|
|
PathBuildRoot(szRoot, DRIVEID(pszPath));
|
|
}
|
|
//
|
|
// fixed media use cached value.
|
|
//
|
|
else
|
|
{
|
|
return CMtPt_IsLFN(DRIVEID(pszPath));
|
|
}
|
|
|
|
//
|
|
// Right now we will say that it is an LFN Drive if the maximum
|
|
// component is > 12
|
|
GetVolumeInformation(szRoot, NULL, 0, NULL, &dwMaxLength, NULL, NULL, 0);
|
|
return dwMaxLength > 12;
|
|
}
|
|
|
|
|
|
STDAPI_(BOOL) IsLFNDriveA(LPCSTR pszPath) OPTIONAL
|
|
{
|
|
WCHAR wsz[MAX_PATH];
|
|
|
|
ASSERT(NULL == pszPath || IS_VALID_STRING_PTRA(pszPath, -1));
|
|
|
|
if (pszPath)
|
|
{
|
|
SHAnsiToUnicode(pszPath, wsz, ARRAYSIZE(wsz));
|
|
pszPath = (LPCSTR)wsz;
|
|
}
|
|
return IsLFNDrive((LPCWSTR)pszPath);
|
|
}
|
|
|
|
STDAPI_(BOOL) PathIsRemovable(LPCTSTR pszPath)
|
|
{
|
|
BOOL fIsEjectable = FALSE;
|
|
int iDrive = PathGetDriveNumber(pszPath);
|
|
|
|
if (iDrive != -1)
|
|
{
|
|
int nType = DriveType(iDrive);
|
|
|
|
if ((DRIVE_CDROM == nType) ||
|
|
(DRIVE_DVD == nType) ||
|
|
(DRIVE_REMOVABLE == nType))
|
|
{
|
|
fIsEjectable = TRUE;
|
|
}
|
|
}
|
|
|
|
return fIsEjectable;
|
|
}
|
|
|
|
STDAPI_(BOOL) PathIsRemote(LPCTSTR pszPath)
|
|
{
|
|
BOOL fIsRemote = FALSE;
|
|
if (PathIsUNC(pszPath))
|
|
{
|
|
fIsRemote = TRUE;
|
|
}
|
|
else
|
|
{
|
|
int iDrive = PathGetDriveNumber(pszPath);
|
|
|
|
if (iDrive != -1)
|
|
{
|
|
int nType = DriveType(iDrive);
|
|
|
|
if (DRIVE_REMOTE == nType || DRIVE_NO_ROOT_DIR == nType)
|
|
{
|
|
fIsRemote = TRUE;
|
|
}
|
|
}
|
|
}
|
|
return fIsRemote;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
// The following are creterias we currently use to tell whether a file is a temporary file
|
|
// Files with FILE_ATTRIBUTE_TEMPORARY set
|
|
// Files in Windows temp directory
|
|
// Files from the internet cache directory
|
|
// Files in the CD burning area
|
|
//---------------------------------------------------------------------------
|
|
STDAPI_(BOOL) PathIsTemporary(LPCTSTR pszPath)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
DWORD dwAttrib = GetFileAttributes(pszPath);
|
|
if ((-1 != dwAttrib) && (dwAttrib & FILE_ATTRIBUTE_TEMPORARY))
|
|
{
|
|
bRet = TRUE; // we got the attributes and the file says it is temprary
|
|
}
|
|
else
|
|
{
|
|
TCHAR szTemp[MAX_PATH];
|
|
if (GetTempPath(ARRAYSIZE(szTemp), szTemp))
|
|
{
|
|
// if possible, expand the input to the long path name so we can compare strings
|
|
TCHAR szPath[MAX_PATH];
|
|
if (GetLongPathName(pszPath, szPath, ARRAYSIZE(szPath)))
|
|
pszPath = szPath;
|
|
|
|
// GetTempPath() returns short name due to compatibility constraints.
|
|
// we need to convert to long name
|
|
if (GetLongPathName(szTemp, szTemp, ARRAYSIZE(szTemp)))
|
|
{
|
|
bRet = PathIsEqualOrSubFolder(szTemp, pszPath) ||
|
|
PathIsEqualOrSubFolder(MAKEINTRESOURCE(CSIDL_INTERNET_CACHE), pszPath) ||
|
|
PathIsEqualOrSubFolder(MAKEINTRESOURCE(CSIDL_CDBURN_AREA), pszPath);
|
|
}
|
|
}
|
|
}
|
|
return bRet;
|
|
}
|
|
|
|
|
|
STDAPI_(BOOL) PathIsTemporaryA(LPCSTR pszPath)
|
|
{
|
|
TCHAR szPath[MAX_PATH];
|
|
SHOtherToTChar(pszPath, szPath, ARRAYSIZE(szPath));
|
|
return PathIsTemporary(szPath);
|
|
}
|
|
|
|
|
|
// unfortunately, this is exported so we need to support it
|
|
STDAPI_(LPTSTR) PathGetExtension(LPCTSTR pszPath, LPTSTR pszExtension, int cchExt)
|
|
{
|
|
LPTSTR pszExt = PathFindExtension(pszPath);
|
|
|
|
RIPMSG(FALSE, "PathGetExtension should not be called, use PathFindExtension instead");
|
|
|
|
if (pszExt && *pszExt)
|
|
pszExt += 1;
|
|
|
|
return pszExt;
|
|
}
|
|
|
|
//
|
|
// Attempts to truncate the filename pszSpec such that pszDir+pszSpec are less than MAX_PATH-5.
|
|
// The extension is protected so it won't get truncated or altered.
|
|
//
|
|
// in:
|
|
// pszDir the path to a directory. No trailing '\' is needed.
|
|
// pszSpec the filespec to be truncated. This should not include a path but can have an extension.
|
|
// This input buffer can be of any length.
|
|
// iTruncLimit The minimum length to truncate pszSpec. If addition truncation would be required we fail.
|
|
// out:
|
|
// pszSpec The truncated filespec with it's extension unaltered.
|
|
// return:
|
|
// TRUE if the filename was truncated, FALSE if we were unable to truncate because the directory name
|
|
// was too long, the extension was too long, or the iTruncLimit is too high. pszSpec is unaltered
|
|
// when this function returns FALSE.
|
|
//
|
|
STDAPI_(BOOL) PathTruncateKeepExtension(LPCTSTR pszDir, LPTSTR pszSpec, int iTruncLimit)
|
|
{
|
|
LPTSTR pszExt = PathFindExtension(pszSpec);
|
|
|
|
RIPMSG(pszDir && IS_VALID_STRING_PTR(pszDir, -1), "PathTruncateKeepExtension: Caller passed bad pszDir");
|
|
RIPMSG(pszSpec && IS_VALID_STRING_PTR(pszSpec, -1) && IS_VALID_WRITE_BUFFER(pszSpec, TCHAR, MAX_PATH), "PathTruncateKeepExtension: Caller passed bad pszSpec");
|
|
DEBUGWhackPathString(pszSpec, MAX_PATH);
|
|
|
|
if (pszExt)
|
|
{
|
|
int cchExt = lstrlen(pszExt);
|
|
int cchSpec = (int)(pszExt - pszSpec + cchExt);
|
|
int cchKeep = MAX_PATH - lstrlen(pszDir) - 5; // the -5 is just to provide extra padding (max lstrlen(pszExt))
|
|
|
|
// IF...
|
|
// ...the filename is to long
|
|
// ...we are within the limit to which we can truncate
|
|
// ...the extension is short enough to allow the trunctation
|
|
if ((cchSpec > cchKeep) && (cchKeep >= iTruncLimit) && (cchKeep > cchExt))
|
|
{
|
|
// THEN... go ahead and truncate
|
|
if (SUCCEEDED(StringCchCopy(pszSpec + cchKeep - cchExt, MAX_PATH - (cchKeep - cchExt), pszExt)))
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
STDAPI_(int) PathCleanupSpec(LPCTSTR pszDir, LPTSTR pszSpec)
|
|
{
|
|
LPTSTR pszNext, pszCur;
|
|
UINT uMatch = IsLFNDrive(pszDir) ? GCT_LFNCHAR : GCT_SHORTCHAR;
|
|
int iRet = 0;
|
|
LPTSTR pszPrevDot = NULL;
|
|
|
|
for (pszCur = pszNext = pszSpec; *pszNext; /*pszNext = CharNext(pszNext)*/)
|
|
{
|
|
if (PathGetCharType(*pszNext) & uMatch)
|
|
{
|
|
*pszCur = *pszNext;
|
|
if (uMatch == GCT_SHORTCHAR && *pszCur == TEXT('.'))
|
|
{
|
|
if (pszPrevDot) // Only one '.' allowed for short names
|
|
{
|
|
*pszPrevDot = TEXT('-');
|
|
iRet |= PCS_REPLACEDCHAR;
|
|
}
|
|
pszPrevDot = pszCur;
|
|
}
|
|
if (IsDBCSLeadByte(*pszNext))
|
|
{
|
|
LPTSTR pszDBCSNext;
|
|
|
|
pszDBCSNext = CharNext(pszNext);
|
|
*(pszCur + 1) = *(pszNext + 1);
|
|
pszNext = pszDBCSNext;
|
|
}
|
|
else
|
|
pszNext = CharNext(pszNext);
|
|
pszCur = CharNext(pszCur);
|
|
}
|
|
else
|
|
{
|
|
switch (*pszNext)
|
|
{
|
|
case TEXT('/'): // used often for things like add/remove
|
|
case TEXT(' '): // blank (only replaced for short name drives)
|
|
*pszCur = TEXT('-');
|
|
pszCur = CharNext(pszCur);
|
|
iRet |= PCS_REPLACEDCHAR;
|
|
break;
|
|
default:
|
|
iRet |= PCS_REMOVEDCHAR;
|
|
}
|
|
pszNext = CharNext(pszNext);
|
|
}
|
|
}
|
|
*pszCur = 0; // null terminate
|
|
|
|
//
|
|
// For short names, limit to 8.3
|
|
//
|
|
if (uMatch == GCT_SHORTCHAR)
|
|
{
|
|
int i = 8;
|
|
for (pszCur = pszNext = pszSpec; *pszNext; pszNext = CharNext(pszNext))
|
|
{
|
|
if (*pszNext == TEXT('.'))
|
|
{
|
|
i = 4; // Copy "." + 3 more characters
|
|
}
|
|
if (i > 0)
|
|
{
|
|
*pszCur = *pszNext;
|
|
pszCur = CharNext(pszCur);
|
|
i--;
|
|
}
|
|
else
|
|
{
|
|
iRet |= PCS_TRUNCATED;
|
|
}
|
|
}
|
|
*pszCur = 0;
|
|
CharUpperNoDBCS(pszSpec);
|
|
}
|
|
else // Path too long only possible on LFN drives
|
|
{
|
|
if (pszDir && (lstrlen(pszDir) + lstrlen(pszSpec) > MAX_PATH - 1))
|
|
{
|
|
iRet |= PCS_PATHTOOLONG | PCS_FATAL;
|
|
}
|
|
}
|
|
return iRet;
|
|
}
|
|
|
|
|
|
// PathCleanupSpecEx
|
|
//
|
|
// Just like PathCleanupSpec, PathCleanupSpecEx removes illegal characters from pszSpec
|
|
// and enforces 8.3 format on non-LFN drives. In addition, this function will attempt to
|
|
// truncate pszSpec if the combination of pszDir + pszSpec is greater than MAX_PATH.
|
|
//
|
|
// in:
|
|
// pszDir The directory in which the filespec pszSpec will reside
|
|
// pszSpec The filespec that is being cleaned up which includes any extension being used
|
|
// out:
|
|
// pszSpec The modified filespec with illegal characters removed, truncated to
|
|
// 8.3 if pszDir is on a non-LFN drive, and truncated to a shorter number
|
|
// of characters if pszDir is an LFN drive but pszDir + pszSpec is more
|
|
// than MAX_PATH characters.
|
|
// return:
|
|
// returns a bit mask indicating what happened. This mask can include the following cases:
|
|
// PCS_REPLACEDCHAR One or more illegal characters were replaced with legal characters
|
|
// PCS_REMOVEDCHAR One or more illegal characters were removed
|
|
// PCS_TRUNCATED Truncated to fit 8.3 format or because pszDir+pszSpec was too long
|
|
// PCS_PATHTOOLONG pszDir is so long that we cannot truncate pszSpec to form a legal filename
|
|
// PCS_FATAL The resultant pszDir+pszSpec is not a legal filename. Always used with PCS_PATHTOOLONG.
|
|
//
|
|
STDAPI_(int) PathCleanupSpecEx(LPCTSTR pszDir, LPTSTR pszSpec)
|
|
{
|
|
int iRet = PathCleanupSpec(pszDir, pszSpec);
|
|
if (iRet & (PCS_PATHTOOLONG | PCS_FATAL))
|
|
{
|
|
// 30 is the shortest we want to truncate pszSpec to to satisfy the
|
|
// pszDir+pszSpec<MAX_PATH requirement. If this amount of truncation isn't enough
|
|
// then we go ahead and return PCS_PATHTOOLONG|PCS_FATAL without doing any further
|
|
// truncation of pszSpec
|
|
if (PathTruncateKeepExtension(pszDir, pszSpec, 30))
|
|
{
|
|
// We fixed the error returned by PathCleanupSpec so mask out the error.
|
|
iRet |= PCS_TRUNCATED;
|
|
iRet &= ~(PCS_PATHTOOLONG|PCS_FATAL);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// ensure that if both of these aren't set then neither is set.
|
|
ASSERT(!(iRet&PCS_PATHTOOLONG) && !(iRet&PCS_FATAL));
|
|
}
|
|
|
|
return iRet;
|
|
}
|
|
|
|
|
|
STDAPI_(BOOL) PathIsWild(LPCTSTR pszPath)
|
|
{
|
|
while (*pszPath)
|
|
{
|
|
if (*pszPath == TEXT('?') || *pszPath == TEXT('*'))
|
|
return TRUE;
|
|
pszPath = CharNext(pszPath);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
// given a path that potentially points to an un-extensioned program
|
|
// file, check to see if a program file exists with that name.
|
|
//
|
|
// returns: TRUE if a program with that name is found.
|
|
// (extension is added to name).
|
|
// FALSE no program file found or the path did not have an extension
|
|
//
|
|
BOOL LookForExtensions(LPTSTR pszPath, LPCTSTR dirs[], BOOL bPathSearch, UINT fExt)
|
|
{
|
|
ASSERT(fExt); // should have some bits set
|
|
|
|
if (*PathFindExtension(pszPath) == 0)
|
|
{
|
|
if (bPathSearch)
|
|
{
|
|
// NB Try every extension on each path component in turn to
|
|
// mimic command.com's search order.
|
|
return PathFindOnPathEx(pszPath, dirs, fExt);
|
|
}
|
|
else
|
|
{
|
|
return PathFileExistsDefExt(pszPath, fExt);
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
//
|
|
// converts the relative or unqualified path name to the fully
|
|
// qualified path name.
|
|
//
|
|
// If this path is a URL, this function leaves it alone and
|
|
// returns FALSE.
|
|
//
|
|
// in:
|
|
// pszPath path to convert
|
|
// pszCurrentDir current directory to use
|
|
//
|
|
// PRF_TRYPROGRAMEXTENSIONS (implies PRF_VERIFYEXISTS)
|
|
// PRF_VERIFYEXISTS
|
|
//
|
|
// returns:
|
|
// TRUE the file was verified to exist
|
|
// FALSE the file was not verified to exist (but it may)
|
|
//
|
|
STDAPI_(BOOL) PathResolve(LPTSTR lpszPath, LPCTSTR dirs[], UINT fFlags)
|
|
{
|
|
UINT fExt = (fFlags & PRF_DONTFINDLNK) ? (PFOPEX_COM | PFOPEX_BAT | PFOPEX_PIF | PFOPEX_EXE) : PFOPEX_DEFAULT;
|
|
|
|
//
|
|
// NOTE: if VERIFY SetLastError() default to FNF. - ZekeL 9-APR-98
|
|
// ShellExec uses GLE() to find out why we failed.
|
|
// any win32 API that we end up calling
|
|
// will do a SLE() to overrider ours. specifically
|
|
// if VERIFY is set we call GetFileAttributes()
|
|
//
|
|
if (fFlags & PRF_VERIFYEXISTS)
|
|
SetLastError(ERROR_FILE_NOT_FOUND);
|
|
|
|
PathUnquoteSpaces(lpszPath);
|
|
|
|
if (PathIsRoot(lpszPath))
|
|
{
|
|
// No sense qualifying just a server or share name...
|
|
if (!PathIsUNCServer(lpszPath) && !PathIsUNCServerShare(lpszPath))
|
|
{
|
|
// Be able to resolve "\" from different drives.
|
|
if (lpszPath[0] == TEXT('\\') && lpszPath[1] == 0)
|
|
{
|
|
PathQualifyDef(lpszPath, fFlags & PRF_FIRSTDIRDEF ? dirs[0] : NULL, 0);
|
|
}
|
|
}
|
|
|
|
if (fFlags & PRF_VERIFYEXISTS)
|
|
{
|
|
if (PathFileExistsAndAttributes(lpszPath, NULL))
|
|
{
|
|
return(TRUE);
|
|
}
|
|
#ifdef DEBUG
|
|
// PathFileExistsAndAttributes() should catch this well enough.
|
|
// If it is a UNC root, then we will see if the root exists
|
|
//
|
|
if (PathIsUNC(lpszPath))
|
|
{
|
|
// See if the network knows about this one.
|
|
// It appears like some network provider croak if not everything
|
|
// if filled in, so we might as well bloat ourself to make them happy...
|
|
NETRESOURCE nr = {RESOURCE_GLOBALNET,RESOURCETYPE_ANY,
|
|
RESOURCEDISPLAYTYPE_GENERIC, RESOURCEUSAGE_CONTAINER,
|
|
NULL, lpszPath, NULL, NULL};
|
|
HANDLE hEnum;
|
|
|
|
if (WNetOpenEnum(RESOURCE_GLOBALNET, RESOURCETYPE_ANY,
|
|
RESOURCEUSAGE_ALL, &nr, &hEnum) == WN_SUCCESS)
|
|
{
|
|
// If it succeeded then assume it worked...
|
|
WNetCloseEnum(hEnum);
|
|
ASSERT(FALSE);
|
|
return(TRUE);
|
|
}
|
|
}
|
|
#endif // DEBUG
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
else if (PathIsFileSpec(lpszPath))
|
|
{
|
|
|
|
// REVIEW: look for programs before looking for paths
|
|
|
|
if ((fFlags & PRF_TRYPROGRAMEXTENSIONS) && (LookForExtensions(lpszPath, dirs, TRUE, fExt)))
|
|
return TRUE;
|
|
|
|
if (PathFindOnPath(lpszPath, dirs))
|
|
{
|
|
// PathFindOnPath() returns TRUE iff PathFileExists(lpszPath),
|
|
// so we always returns true here:
|
|
//return (!(fFlags & PRF_VERIFYEXISTS)) || PathFileExists(lpszPath);
|
|
return TRUE;
|
|
}
|
|
}
|
|
else if (!PathIsURL(lpszPath))
|
|
{
|
|
// If there is a trailing '.', we should not try extensions
|
|
PathQualifyDef(lpszPath, fFlags & PRF_FIRSTDIRDEF ? dirs[0] : NULL,
|
|
PQD_NOSTRIPDOTS);
|
|
if (fFlags & PRF_VERIFYEXISTS)
|
|
{
|
|
if ((fFlags & PRF_TRYPROGRAMEXTENSIONS) && (LookForExtensions(lpszPath, dirs, FALSE, fExt)))
|
|
return TRUE;
|
|
|
|
if (PathFileExistsAndAttributes(lpszPath, NULL))
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
// qualify a DOS (or LFN) file name based on the currently active window.
|
|
// this code is careful to not write more than MAX_PATH characters
|
|
// into psz
|
|
//
|
|
// in:
|
|
// psz path to be qualified of at least MAX_PATH characters
|
|
// ANSI string
|
|
//
|
|
// out:
|
|
// psz fully qualified version of input string based
|
|
// on the current active window (current directory)
|
|
//
|
|
|
|
void PathQualifyDef(LPTSTR psz, LPCTSTR szDefDir, DWORD dwFlags)
|
|
{
|
|
int cb, nSpaceLeft;
|
|
TCHAR szTemp[MAX_PATH], szRoot[MAX_PATH];
|
|
int iDrive;
|
|
LPTSTR pOrig, pFileName;
|
|
BOOL fLFN;
|
|
LPTSTR pExt;
|
|
|
|
RIPMSG(psz && IS_VALID_STRING_PTR(psz, -1) && IS_VALID_WRITE_BUFFER(psz, TCHAR, MAX_PATH), "PathQualifyDef: caller passed bad psz");
|
|
RIPMSG(!szDefDir || (IS_VALID_STRING_PTR(szDefDir, -1) && lstrlen(szDefDir)<MAX_PATH), "PathQualifyDef: caller passed bad szDefDir");
|
|
DEBUGWhackPathString(psz, MAX_PATH);
|
|
|
|
/* Save it away. */
|
|
if (FAILED(StringCchCopy(szTemp, ARRAYSIZE(szTemp), psz)))
|
|
{
|
|
return; // invalid parameter, leave it alone
|
|
}
|
|
|
|
FixSlashesAndColon(szTemp);
|
|
|
|
nSpaceLeft = ARRAYSIZE(szTemp); // MAX_PATH limited by this...
|
|
|
|
pOrig = szTemp;
|
|
pFileName = PathFindFileName(szTemp);
|
|
|
|
if (PathIsUNC(pOrig))
|
|
{
|
|
// leave the \\ in the buffer so that the various parts
|
|
// of the UNC path will be qualified and appended. Note
|
|
// we must assume that UNCs are LFN's, since computernames
|
|
// and sharenames can be longer than 11 characters.
|
|
fLFN = IsLFNDrive(pOrig);
|
|
if (fLFN)
|
|
{
|
|
psz[2] = 0;
|
|
nSpaceLeft -= 3; // "\\" + nul
|
|
pOrig += 2;
|
|
}
|
|
else
|
|
{
|
|
// NB UNC doesn't support LFN's but we don't want to truncate
|
|
// \\foo or \\foo\bar so skip them here.
|
|
|
|
// Is it a \\foo\bar\fred thing?
|
|
LPTSTR pszSlash = StrChr(psz+2, TEXT('\\'));
|
|
if (pszSlash && (NULL != (pszSlash = StrChr(pszSlash+1, TEXT('\\')))))
|
|
{
|
|
// Yep - skip the first bits but mush the rest.
|
|
*(pszSlash+1) = 0; // truncate to "\\345\78\"
|
|
nSpaceLeft -= (int)(pszSlash-psz)+1; // "\\345\78\" + nul
|
|
pOrig += pszSlash-psz; // skip over "\\345\78\" part
|
|
}
|
|
else
|
|
{
|
|
// Nope - just pretend it's an LFN and leave it alone.
|
|
fLFN = TRUE;
|
|
psz[2] = 0;
|
|
nSpaceLeft -= 3; // "\\" + nul
|
|
pOrig+=2;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Not a UNC
|
|
iDrive = PathGetDriveNumber(pOrig);
|
|
if (iDrive != -1)
|
|
{
|
|
PathBuildRoot(szRoot, iDrive); // root specified by the file name
|
|
|
|
ASSERT(pOrig[1] == TEXT(':')); // PathGetDriveNumber does this
|
|
|
|
pOrig += 2; // Skip over the drive letter
|
|
|
|
// and the slash if it is there...
|
|
if (pOrig[0] == TEXT('\\'))
|
|
pOrig++;
|
|
}
|
|
else
|
|
{
|
|
if (szDefDir && SUCCEEDED(StringCchCopy(szRoot, ARRAYSIZE(szRoot), szDefDir)))
|
|
{
|
|
// use the szDefDir as szRoot
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// As a default, use the windows drive (usually "C:\").
|
|
//
|
|
*szRoot = 0;
|
|
GetWindowsDirectory(szRoot, ARRAYSIZE(szRoot));
|
|
iDrive = PathGetDriveNumber(szRoot);
|
|
if (iDrive != -1)
|
|
{
|
|
PathBuildRoot(szRoot, iDrive);
|
|
}
|
|
}
|
|
|
|
// if path is scoped to the root with "\" use working dir root
|
|
|
|
if (pOrig[0] == TEXT('\\'))
|
|
PathStripToRoot(szRoot);
|
|
}
|
|
fLFN = IsLFNDrive(szRoot);
|
|
|
|
// REVIEW, do we really need to do different stuff on LFN names here?
|
|
// on FAT devices, replace any illegal chars with underscores
|
|
if (!fLFN)
|
|
{
|
|
LPTSTR pT;
|
|
for (pT = pOrig; *pT; pT = CharNext(pT))
|
|
{
|
|
if (!PathIsValidChar(*pT, PIVC_SFN_FULLPATH))
|
|
{
|
|
// not a valid sfn path character
|
|
*pT = TEXT('_');
|
|
}
|
|
}
|
|
}
|
|
|
|
StringCchCopy(psz, MAX_PATH, szRoot); // ok to truncate - we check'd size above
|
|
nSpaceLeft -= (lstrlen(psz) + 1);
|
|
}
|
|
|
|
while (*pOrig && nSpaceLeft > 0)
|
|
{
|
|
// If the component is parent dir, go up one dir.
|
|
// If its the current dir, skip it, else add it normally
|
|
if (pOrig[0] == TEXT('.'))
|
|
{
|
|
if (pOrig[1] == TEXT('.') && (!pOrig[2] || pOrig[2] == TEXT('\\')))
|
|
PathRemoveFileSpec(psz);
|
|
else if (pOrig[1] && pOrig[1] != TEXT('\\'))
|
|
goto addcomponent;
|
|
|
|
while (*pOrig && *pOrig != TEXT('\\'))
|
|
pOrig = CharNext(pOrig);
|
|
|
|
if (*pOrig)
|
|
pOrig++;
|
|
}
|
|
else
|
|
{
|
|
LPTSTR pT, pTT = NULL;
|
|
|
|
addcomponent:
|
|
if (PathAddBackslash(psz) == NULL)
|
|
{
|
|
nSpaceLeft = 0;
|
|
continue; // fail adding this component if the '\' doesn't fit
|
|
}
|
|
|
|
nSpaceLeft--;
|
|
|
|
pT = psz + lstrlen(psz);
|
|
|
|
if (fLFN)
|
|
{
|
|
// copy the component
|
|
while (*pOrig && *pOrig != TEXT('\\') && nSpaceLeft>0)
|
|
{
|
|
nSpaceLeft--;
|
|
if (IsDBCSLeadByte(*pOrig))
|
|
{
|
|
if (nSpaceLeft <= 0)
|
|
{
|
|
// Copy nothing more
|
|
continue;
|
|
}
|
|
|
|
nSpaceLeft--;
|
|
*pT++ = *pOrig++;
|
|
}
|
|
*pT++ = *pOrig++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// copy the filename (up to 8 chars)
|
|
for (cb = 8; *pOrig && !IsPathSep(*pOrig) && *pOrig != TEXT('.') && nSpaceLeft > 0;)
|
|
{
|
|
if (cb > 0)
|
|
{
|
|
cb--;
|
|
nSpaceLeft--;
|
|
if (IsDBCSLeadByte(*pOrig))
|
|
{
|
|
if (nSpaceLeft<=0 || cb<=0)
|
|
{
|
|
// Copy nothing more
|
|
cb = 0;
|
|
continue;
|
|
}
|
|
|
|
cb--;
|
|
nSpaceLeft--;
|
|
*pT++ = *pOrig++;
|
|
}
|
|
*pT++ = *pOrig++;
|
|
}
|
|
else
|
|
{
|
|
pOrig = CharNext(pOrig);
|
|
}
|
|
}
|
|
|
|
// if there's an extension, copy it, up to 3 chars
|
|
if (*pOrig == TEXT('.') && nSpaceLeft > 0)
|
|
{
|
|
int nOldSpaceLeft;
|
|
|
|
*pT++ = TEXT('.');
|
|
nSpaceLeft--;
|
|
pOrig++;
|
|
pExt = pT;
|
|
nOldSpaceLeft = nSpaceLeft;
|
|
|
|
for (cb = 3; *pOrig && *pOrig != TEXT('\\') && nSpaceLeft > 0;)
|
|
{
|
|
if (*pOrig == TEXT('.'))
|
|
{
|
|
// Another extension, start again.
|
|
cb = 3;
|
|
pT = pExt;
|
|
nSpaceLeft = nOldSpaceLeft;
|
|
pOrig++;
|
|
}
|
|
|
|
if (cb > 0)
|
|
{
|
|
cb--;
|
|
nSpaceLeft--;
|
|
if (IsDBCSLeadByte(*pOrig))
|
|
{
|
|
if (nSpaceLeft<=0 || cb<=0)
|
|
{
|
|
// Copy nothing more
|
|
cb = 0;
|
|
continue;
|
|
}
|
|
|
|
cb--;
|
|
nSpaceLeft--;
|
|
*pT++ = *pOrig++;
|
|
}
|
|
*pT++ = *pOrig++;
|
|
}
|
|
else
|
|
{
|
|
pOrig = CharNext(pOrig);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// skip the backslash
|
|
|
|
if (*pOrig)
|
|
pOrig++;
|
|
|
|
// null terminate for next pass...
|
|
*pT = 0;
|
|
}
|
|
}
|
|
|
|
PathRemoveBackslash(psz);
|
|
|
|
if (!(dwFlags & PQD_NOSTRIPDOTS))
|
|
{
|
|
// remove any trailing dots
|
|
|
|
LPTSTR pszPrev = CharPrev(psz, psz + lstrlen(psz));
|
|
if (*pszPrev == TEXT('.'))
|
|
{
|
|
*pszPrev = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
STDAPI_(void) PathQualify(LPTSTR psz)
|
|
{
|
|
PathQualifyDef(psz, NULL, 0);
|
|
}
|
|
|
|
BOOL OnExtList(LPCTSTR pszExtList, LPCTSTR pszExt)
|
|
{
|
|
for (; *pszExtList; pszExtList += lstrlen(pszExtList) + 1)
|
|
{
|
|
if (!lstrcmpi(pszExt, pszExtList))
|
|
{
|
|
// yes
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
// Character offset where binary exe extensions begin in above
|
|
#define BINARY_EXE_OFFSET 20
|
|
const TCHAR c_achExes[] = TEXT(".cmd\0.bat\0.pif\0.scf\0.exe\0.com\0.scr\0");
|
|
|
|
STDAPI_(BOOL) PathIsBinaryExe(LPCTSTR szFile)
|
|
{
|
|
ASSERT(BINARY_EXE_OFFSET < ARRAYSIZE(c_achExes) &&
|
|
c_achExes[BINARY_EXE_OFFSET] == TEXT('.'));
|
|
|
|
return OnExtList(c_achExes + BINARY_EXE_OFFSET, PathFindExtension(szFile));
|
|
}
|
|
|
|
|
|
//
|
|
// determine if a path is a program by looking at the extension
|
|
//
|
|
STDAPI_(BOOL) PathIsExe(LPCTSTR szFile)
|
|
{
|
|
LPCTSTR temp = PathFindExtension(szFile);
|
|
return OnExtList(c_achExes, temp);
|
|
}
|
|
|
|
//
|
|
// determine if a path is a .lnk file by looking at the extension
|
|
//
|
|
STDAPI_(BOOL) PathIsLnk(LPCTSTR szFile)
|
|
{
|
|
if (szFile)
|
|
{
|
|
// Both PathFindExtension() and lstrcmpi() will crash
|
|
// if passed NULL. PathFindExtension() will never return
|
|
// NULL.
|
|
LPCTSTR lpszFileName = PathFindExtension(szFile);
|
|
return lstrcmpi(TEXT(".lnk"), lpszFileName) == 0;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// Port names are invalid path names
|
|
|
|
#define IsDigit(c) ((c) >= TEXT('0') && c <= TEXT('9'))
|
|
STDAPI_(BOOL) PathIsInvalid(LPCWSTR pszName)
|
|
{
|
|
static const TCHAR *rgszPorts3[] = {
|
|
TEXT("NUL"),
|
|
TEXT("PRN"),
|
|
TEXT("CON"),
|
|
TEXT("AUX"),
|
|
};
|
|
|
|
static const TCHAR *rgszPorts4[] = {
|
|
TEXT("LPT"), // LPT#
|
|
TEXT("COM"), // COM#
|
|
};
|
|
|
|
TCHAR sz[7];
|
|
DWORD cch;
|
|
int iMax;
|
|
LPCTSTR* rgszPorts;
|
|
|
|
if (FAILED(StringCchCopy(sz, ARRAYSIZE(sz), pszName)))
|
|
{
|
|
return FALSE; // longer names aren't port names
|
|
}
|
|
|
|
PathRemoveExtension(sz);
|
|
cch = lstrlen(sz);
|
|
|
|
iMax = ARRAYSIZE(rgszPorts3);
|
|
rgszPorts = rgszPorts3;
|
|
if (cch == 4 && IsDigit(sz[3]))
|
|
{
|
|
// if 4 chars start with LPT checks
|
|
// need to filter out:
|
|
// COM1, COM2, etc. LPT1, LPT2, etc
|
|
// but not:
|
|
// COM or LPT or LPT10 or COM10
|
|
// COM == 1 and LPT == 0
|
|
|
|
iMax = ARRAYSIZE(rgszPorts4);
|
|
rgszPorts = rgszPorts4;
|
|
sz[3] = 0;
|
|
cch = 3;
|
|
}
|
|
|
|
if (cch == 3)
|
|
{
|
|
int i;
|
|
for (i = 0; i < iMax; i++)
|
|
{
|
|
if (!lstrcmpi(rgszPorts[i], sz))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
return (i == iMax) ? FALSE : TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
//
|
|
// Funciton: PathMakeUniqueName
|
|
//
|
|
// Parameters:
|
|
// pszUniqueName -- Specify the buffer where the unique name should be copied
|
|
// cchMax -- Specify the size of the buffer
|
|
// pszTemplate -- Specify the base name
|
|
// pszLongPlate -- Specify the base name for a LFN drive. format below
|
|
// pszDir -- Specify the directory (at most MAX_PATH in length)
|
|
//
|
|
// History:
|
|
// 03-11-93 SatoNa Created
|
|
//
|
|
// REVIEW:
|
|
// For long names, we should be able to generate more user friendly name
|
|
// such as "Copy of MyDocument" of "Link #2 to MyDocument". In this case,
|
|
// we need additional flags which indicates if it is copy, or link.
|
|
//
|
|
// Format:
|
|
// pszLongPlate will search for the first (and then finds the matching)
|
|
// to look for a number:
|
|
// given: Copy () of my doc gives: Copy (_number_) of my doc
|
|
// given: Copy (1023) of my doc gives: Copy (_number_) of my doc
|
|
//
|
|
// PERF: if making n unique names, the time grows n^2 because it always
|
|
// starts from 0 and checks for existing file.
|
|
//
|
|
STDAPI_(BOOL) PathMakeUniqueNameEx(LPTSTR pszUniqueName, UINT cchMax,
|
|
LPCTSTR pszTemplate, LPCTSTR pszLongPlate, LPCTSTR pszDir, int iMinLong)
|
|
{
|
|
TCHAR szFormat[MAX_PATH]; // should be plenty big
|
|
LPTSTR pszName, pszDigit;
|
|
LPCTSTR pszStem;
|
|
int cchStem, cchDir;
|
|
int iMax, iMin, i;
|
|
int cchMaxName;
|
|
HRESULT hr;
|
|
|
|
RIPMSG(pszUniqueName && IS_VALID_WRITE_BUFFER(pszUniqueName, TCHAR, cchMax), "PathMakeUniqueNameEx: caller passed bad pszUniqueName");
|
|
DEBUGWhackPathBuffer(pszUniqueName, cchMax);
|
|
RIPMSG(!pszDir || lstrlen(pszDir)<MAX_PATH, "PathMakeUniqueNameEx: pszDir exceeds MAX_PATH, helper routines don't take cch so this call is broken");
|
|
RIPMSG(iMinLong >= 0, "PathMakeUniqueNameEx: negative iMinLong doesn't make sense");
|
|
|
|
if (0==cchMax || !pszUniqueName)
|
|
return FALSE;
|
|
*pszUniqueName = 0; // just in case of failure
|
|
|
|
if (pszLongPlate == NULL)
|
|
pszLongPlate = pszTemplate;
|
|
|
|
// all cases below check the length of optional pszDir, calculate early.
|
|
// side effect: this set's up pszName and the directory portion of pszUniqueName;
|
|
if (pszDir)
|
|
{
|
|
hr = StringCchCopy(pszUniqueName, cchMax-1, pszDir); // -1 to allow for '\' from PathAddBackslash
|
|
if (FAILED(hr))
|
|
{
|
|
*pszUniqueName = TEXT('\0');
|
|
return FALSE;
|
|
}
|
|
pszName = PathAddBackslash(pszUniqueName); // shouldn't fail
|
|
if (NULL == pszName)
|
|
{
|
|
*pszUniqueName = TEXT('\0');
|
|
return FALSE;
|
|
}
|
|
cchDir = lstrlen(pszDir); // we need an accurate count
|
|
}
|
|
else
|
|
{
|
|
cchDir = 0;
|
|
pszName = pszUniqueName;
|
|
}
|
|
|
|
// Set up:
|
|
// pszStem : template we're going to use
|
|
// cchStem : length of pszStem we're going to use w/o wsprintf
|
|
// szFormat : format string to wsprintf the number with, catenates on to pszStem[0..cchStem]
|
|
// iMin : starting number for wsprintf loop
|
|
// iMax : maximum number for wsprintf loop
|
|
// cchMaxname : !0 implies -> if resulting name length > cchMaxname, then --cchStem (only used in short name case)
|
|
//
|
|
if (pszLongPlate && IsLFNDrive(pszDir))
|
|
{
|
|
LPCTSTR pszRest;
|
|
int cchTmp;
|
|
|
|
cchMaxName = 0;
|
|
|
|
// for long name drives
|
|
pszStem = pszLongPlate;
|
|
|
|
// Has this already been a uniquified name?
|
|
pszRest = StrChr(pszLongPlate, TEXT('('));
|
|
while (pszRest)
|
|
{
|
|
// First validate that this is the right one
|
|
LPCTSTR pszEndUniq = CharNext(pszRest);
|
|
while (*pszEndUniq && *pszEndUniq >= TEXT('0') && *pszEndUniq <= TEXT('9')) {
|
|
pszEndUniq++;
|
|
}
|
|
if (*pszEndUniq == TEXT(')'))
|
|
break; // We have the right one!
|
|
pszRest = StrChr(CharNext(pszRest), TEXT('('));
|
|
}
|
|
|
|
if (!pszRest)
|
|
{
|
|
// Never been unique'd before -- tack it on at the end. (but before the extension)
|
|
// eg. New Link yields New Link (1)
|
|
pszRest = PathFindExtension(pszLongPlate);
|
|
cchStem = (int)(pszRest - pszLongPlate);
|
|
|
|
hr = StringCchPrintf(szFormat, ARRAYSIZE(szFormat), TEXT(" (%%d)%s"), pszRest ? pszRest : c_szNULL);
|
|
}
|
|
else
|
|
{
|
|
// we found (#), so remove the #
|
|
// eg. New Link (999) yields New Link (1)
|
|
|
|
pszRest++; // step over the '('
|
|
|
|
cchStem = (int) (pszRest - pszLongPlate);
|
|
|
|
// eat the '#'
|
|
while (*pszRest && *pszRest >= TEXT('0') && *pszRest <= TEXT('9')) {
|
|
pszRest++;
|
|
}
|
|
|
|
// we are guaranteed enough room because we don't include
|
|
// the stuff before the # in this format
|
|
hr = StringCchPrintf(szFormat, ARRAYSIZE(szFormat), TEXT("%%d%s"), pszRest);
|
|
}
|
|
if (FAILED(hr))
|
|
{
|
|
*pszUniqueName = TEXT('\0');
|
|
return FALSE;
|
|
}
|
|
|
|
// how much room do we have to play?
|
|
iMin = iMinLong;
|
|
cchTmp = cchMax - cchDir - cchStem - (lstrlen(szFormat)-2); // -2 for "%d" which will be replaced
|
|
switch(cchTmp)
|
|
{
|
|
case 1:
|
|
iMax = 10;
|
|
break;
|
|
case 2:
|
|
iMax = 100;
|
|
break;
|
|
default:
|
|
if (cchTmp <= 0)
|
|
iMax = iMin; // no room, bail
|
|
else
|
|
iMax = 1000;
|
|
break;
|
|
}
|
|
}
|
|
else // short filename case
|
|
{
|
|
LPCTSTR pszRest;
|
|
int cchRest;
|
|
int cchFormat;
|
|
|
|
if (pszTemplate == NULL)
|
|
return FALSE;
|
|
|
|
// for short name drives
|
|
pszStem = pszTemplate;
|
|
pszRest = PathFindExtension(pszTemplate);
|
|
|
|
// Calculate cchMaxName, ensuring our base name (cchStem+digits) will never go over 8
|
|
//
|
|
cchRest=lstrlen(pszRest);
|
|
cchMaxName = 8+cchRest;
|
|
|
|
// Now that we have the extension, we know the format string
|
|
//
|
|
hr = StringCchPrintf(szFormat, ARRAYSIZE(szFormat), TEXT("%%d%s"), pszRest);
|
|
if (FAILED(hr))
|
|
{
|
|
*pszUniqueName = TEXT('\0');
|
|
return FALSE;
|
|
}
|
|
ASSERT(lstrlen(szFormat)-2 == cchRest); // -2 for "%d" in format string
|
|
cchFormat = cchRest;
|
|
|
|
// Figure out how long the stem really is:
|
|
//
|
|
cchStem = (int)(pszRest-pszTemplate); // 8 for "fooobarr.foo"
|
|
|
|
// Remove all the digit characters (previous uniquifying) from the stem
|
|
//
|
|
for(; cchStem > 1 ; cchStem--)
|
|
{
|
|
TCHAR ch;
|
|
|
|
LPCTSTR pszPrev = CharPrev(pszTemplate, pszTemplate + cchStem);
|
|
// Don't remove if it is a DBCS character
|
|
if (pszPrev != pszTemplate+cchStem-1)
|
|
break;
|
|
|
|
// Don't remove it it is not a digit
|
|
ch=pszPrev[0];
|
|
if (ch<TEXT('0') || ch>TEXT('9'))
|
|
break;
|
|
}
|
|
|
|
// Short file names mean we use the 8.3 rule, so the stem can't be > 8...
|
|
//
|
|
if ((UINT)cchStem > 8-1)
|
|
cchStem = 8-1; // need 1 for a digit
|
|
|
|
// Truncate the stem to make it fit when we take the directory path into consideration
|
|
//
|
|
while ((cchStem + cchFormat + cchDir + 1 > (int)cchMax - 1) && (cchStem > 1)) // -1 for NULL, +1 for a digit
|
|
cchStem--;
|
|
|
|
// We've allowed for 1 character of digit space, but...
|
|
// How many digits can we really use?
|
|
//
|
|
iMin = 1;
|
|
if (cchStem < 1)
|
|
iMax = iMin; // NONE!
|
|
else if (1 == cchStem)
|
|
iMax = 10; // There's only 1 character of stem left, so use digits 0-9
|
|
else
|
|
iMax = 100; // Room for stem and digits 0-99
|
|
}
|
|
|
|
// pszUniqueName has the optional directory in it,
|
|
// pszName points into pszUniqueName where the stem goes,
|
|
// now try to find a unique name!
|
|
//
|
|
hr = StringCchCopyN(pszName, pszUniqueName + MAX_PATH - pszName, pszStem, cchStem);
|
|
if (FAILED(hr))
|
|
{
|
|
*pszUniqueName = TEXT('\0');
|
|
return FALSE;
|
|
}
|
|
pszDigit = pszName + cchStem;
|
|
|
|
for (i = iMin; i < iMax ; i++)
|
|
{
|
|
TCHAR szTemp[MAX_PATH];
|
|
|
|
hr = StringCchPrintf(szTemp, ARRAYSIZE(szTemp), szFormat, i);
|
|
if (FAILED(hr))
|
|
{
|
|
*pszUniqueName = TEXT('\0');
|
|
return FALSE;
|
|
}
|
|
|
|
if (cchMaxName)
|
|
{
|
|
//
|
|
// if we have a limit on the length of the name (ie on a non-LFN drive)
|
|
// backup the pszDigit pointer when i wraps from 9to10 and 99to100 etc
|
|
//
|
|
while (cchStem > 0 && cchStem + lstrlen(szTemp) > cchMaxName)
|
|
{
|
|
--cchStem;
|
|
pszDigit = CharPrev(pszName, pszDigit);
|
|
}
|
|
if (cchStem == 0)
|
|
{
|
|
*pszUniqueName = TEXT('\0');
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
hr = StringCchCopy(pszDigit, pszUniqueName + MAX_PATH - pszDigit, szTemp);
|
|
if (FAILED(hr))
|
|
{
|
|
*pszUniqueName = TEXT('\0');
|
|
return FALSE;
|
|
}
|
|
|
|
TraceMsg(TF_PATH, "PathMakeUniqueNameEx: trying %s", (LPCTSTR)pszUniqueName);
|
|
|
|
//
|
|
// Check if this name is unique or not.
|
|
//
|
|
if (!PathFileExists(pszUniqueName))
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
*pszUniqueName = 0; // we failed, clear out our last attempt
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
STDAPI_(BOOL) PathMakeUniqueName(LPTSTR pszUniqueName, UINT cchMax,
|
|
LPCTSTR pszTemplate, LPCTSTR pszLongPlate, LPCTSTR pszDir)
|
|
{
|
|
return PathMakeUniqueNameEx(pszUniqueName, cchMax, pszTemplate, pszLongPlate, pszDir, 1);
|
|
}
|
|
|
|
|
|
// in:
|
|
// pszPath directory to do this into or full dest path
|
|
// if pszShort is NULL
|
|
// pszShort file name (short version) if NULL assumes
|
|
// pszPath is both path and spec
|
|
// pszFileSpec file name (long version)
|
|
//
|
|
// out:
|
|
// pszUniqueName
|
|
//
|
|
// note:
|
|
// pszUniqueName can be the same buffer as pszPath or pszShort or pszFileSpec
|
|
//
|
|
// returns:
|
|
// TRUE success, name can be used
|
|
|
|
STDAPI_(BOOL) PathYetAnotherMakeUniqueName(LPTSTR pszUniqueName, LPCTSTR pszPath, LPCTSTR pszShort, LPCTSTR pszFileSpec)
|
|
{
|
|
BOOL fRet = FALSE;
|
|
|
|
TCHAR szTemp[MAX_PATH];
|
|
TCHAR szPath[MAX_PATH];
|
|
HRESULT hr;
|
|
|
|
RIPMSG(pszPath && IS_VALID_STRING_PTR(pszPath, -1) && lstrlen(pszPath) < MAX_PATH, "PathYetAnotherMakeUniqueName: caller passed invalid pszPath");
|
|
RIPMSG(!pszShort || IS_VALID_STRING_PTR(pszShort, -1), "PathYetAnotherMakeUniqueName: caller passed invalid pszShort");
|
|
RIPMSG(!pszFileSpec || (IS_VALID_STRING_PTR(pszFileSpec, -1) && lstrlen(pszFileSpec) < MAX_PATH), "PathYetAnotherMakeUniqueName: caller passed invalid pszFileSpec");
|
|
RIPMSG(pszUniqueName && IS_VALID_WRITE_BUFFER(pszUniqueName, TCHAR, MAX_PATH), "PathYetAnotherMakeUniqueName: caller passed invalid pszUniqueName");
|
|
#ifdef DEBUG
|
|
if (pszUniqueName == pszPath || pszUniqueName == pszShort || pszUniqueName == pszFileSpec)
|
|
DEBUGWhackPathString(pszUniqueName, MAX_PATH);
|
|
else
|
|
DEBUGWhackPathBuffer(pszUniqueName, MAX_PATH);
|
|
#endif
|
|
|
|
if (pszShort == NULL)
|
|
{
|
|
pszShort = PathFindFileName(pszPath);
|
|
hr = StringCchCopy(szPath, ARRAYSIZE(szPath), pszPath);
|
|
if (FAILED(hr))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
PathRemoveFileSpec(szPath);
|
|
pszPath = szPath;
|
|
}
|
|
if (pszFileSpec == NULL)
|
|
{
|
|
pszFileSpec = pszShort;
|
|
}
|
|
|
|
if (IsLFNDrive(pszPath))
|
|
{
|
|
LPTSTR lpsz;
|
|
LPTSTR lpszNew;
|
|
|
|
// REVIEW: If the path+filename is too long how about this, instead of bailing out we trunctate the name
|
|
// using my new PathTruncateKeepExtension? Currently we have many places where the return result of this
|
|
// function is not checked which cause failures in abserdly long filename cases. The result ends up having
|
|
// the wrong path which screws things up.
|
|
if ((lstrlen(pszPath) + lstrlen(pszFileSpec) + 5) > MAX_PATH)
|
|
return FALSE;
|
|
|
|
// try it without the (if there's a space after it
|
|
lpsz = StrChr(pszFileSpec, TEXT('('));
|
|
while (lpsz)
|
|
{
|
|
if (*(CharNext(lpsz)) == TEXT(')'))
|
|
break;
|
|
lpsz = StrChr(CharNext(lpsz), TEXT('('));
|
|
}
|
|
|
|
if (lpsz)
|
|
{
|
|
// We have the (). See if we have either x () y or x ().y in which case
|
|
// we probably want to get rid of one of the blanks...
|
|
int ichSkip = 2;
|
|
LPTSTR lpszT = CharPrev(pszFileSpec, lpsz);
|
|
if (*lpszT == TEXT(' '))
|
|
{
|
|
ichSkip = 3;
|
|
lpsz = lpszT;
|
|
}
|
|
|
|
hr = StringCchCopy(szTemp, ARRAYSIZE(szTemp), pszPath);
|
|
if (FAILED(hr))
|
|
{
|
|
return FALSE;
|
|
}
|
|
lpszNew = PathAddBackslash(szTemp);
|
|
if (NULL == lpszNew)
|
|
{
|
|
return FALSE;
|
|
}
|
|
hr = StringCchCopy(lpszNew, szTemp + ARRAYSIZE(szTemp) - lpszNew, pszFileSpec);
|
|
if (FAILED(hr))
|
|
{
|
|
return FALSE;
|
|
}
|
|
lpszNew += (lpsz - pszFileSpec);
|
|
hr = StringCchCopy(lpszNew, szTemp + ARRAYSIZE(szTemp) - lpszNew, lpsz + ichSkip);
|
|
if (FAILED(hr))
|
|
{
|
|
return FALSE;
|
|
}
|
|
fRet = !PathFileExists(szTemp);
|
|
}
|
|
else
|
|
{
|
|
// 1taro registers its document with '/'.
|
|
if (lpsz = StrChr(pszFileSpec, '/'))
|
|
{
|
|
LPTSTR lpszT = CharNext(lpsz);
|
|
hr = StringCchCopy(szTemp, ARRAYSIZE(szTemp), pszPath);
|
|
if (FAILED(hr))
|
|
{
|
|
return FALSE;
|
|
}
|
|
lpszNew = PathAddBackslash(szTemp);
|
|
if (NULL == lpszNew)
|
|
{
|
|
return FALSE;
|
|
}
|
|
hr = StringCchCopy(lpszNew, szTemp + ARRAYSIZE(szTemp) - lpszNew, pszFileSpec);
|
|
if (FAILED(hr))
|
|
{
|
|
return FALSE;
|
|
}
|
|
lpszNew += (lpsz - pszFileSpec);
|
|
hr = StringCchCopy(lpszNew, szTemp + ARRAYSIZE(szTemp) - lpszNew, lpszT);
|
|
if (FAILED(hr))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (NULL == PathCombine(szTemp, pszPath, pszFileSpec))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
fRet = !PathFileExists(szTemp);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ASSERT(lstrlen(PathFindExtension(pszShort)) <= 4);
|
|
|
|
hr = StringCchCopy(szTemp, ARRAYSIZE(szTemp), pszShort);
|
|
if (FAILED(hr))
|
|
{
|
|
return FALSE;
|
|
}
|
|
PathRemoveExtension(szTemp);
|
|
|
|
if (lstrlen(szTemp) <= 8)
|
|
{
|
|
if (NULL == PathCombine(szTemp, pszPath, pszShort))
|
|
{
|
|
return FALSE;
|
|
}
|
|
fRet = !PathFileExists(szTemp);
|
|
}
|
|
}
|
|
|
|
if (!fRet)
|
|
{
|
|
fRet = PathMakeUniqueNameEx(szTemp, ARRAYSIZE(szTemp), pszShort, pszFileSpec, pszPath, 2);
|
|
if (NULL == PathCombine(szTemp, pszPath, szTemp))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (fRet)
|
|
{
|
|
hr = StringCchCopy(pszUniqueName, MAX_PATH, szTemp);
|
|
if (FAILED(hr))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return fRet;
|
|
}
|
|
|
|
STDAPI_(void) PathGetShortPath(LPTSTR pszLongPath)
|
|
{
|
|
TCHAR szShortPath[MAX_PATH];
|
|
UINT cch;
|
|
|
|
RIPMSG(pszLongPath && IS_VALID_STRING_PTR(pszLongPath, -1) && IS_VALID_WRITE_BUFFER(pszLongPath, TCHAR, MAX_PATH), "PathGetShortPath: caller passed invalid pszLongPath");
|
|
DEBUGWhackPathString(pszLongPath, MAX_PATH);
|
|
|
|
cch = GetShortPathName(pszLongPath, szShortPath, ARRAYSIZE(szShortPath));
|
|
if (cch != 0 && cch < ARRAYSIZE(szShortPath))
|
|
{
|
|
StringCchCopy(pszLongPath, MAX_PATH, szShortPath); // must fit, MAX_PATH vs. MAX_PATH
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// pszFile -- file path
|
|
// dwFileAttr -- The file attributes, pass -1 if not available
|
|
//
|
|
// Note: pszFile arg may be NULL if dwFileAttr != -1.
|
|
|
|
BOOL PathIsHighLatency(LPCTSTR pszFile /*optional*/, DWORD dwFileAttr)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
if (dwFileAttr == -1)
|
|
{
|
|
ASSERT(pszFile != NULL) ;
|
|
dwFileAttr = pszFile ? GetFileAttributes(pszFile) : -1;
|
|
}
|
|
|
|
if ((dwFileAttr != -1) && (dwFileAttr & FILE_ATTRIBUTE_OFFLINE))
|
|
{
|
|
bRet = TRUE;
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
//
|
|
// is a path slow or not
|
|
// dwFileAttr -- The file attributes, pass -1 if not available
|
|
//
|
|
STDAPI_(BOOL) PathIsSlow(LPCTSTR pszFile, DWORD dwFileAttr)
|
|
{
|
|
BOOL bSlow = FALSE;
|
|
if (PathIsUNC(pszFile))
|
|
{
|
|
DWORD speed = GetPathSpeed(pszFile);
|
|
bSlow = (speed != 0) && (speed <= SPEED_SLOW);
|
|
}
|
|
else if (CMtPt_IsSlow(PathGetDriveNumber(pszFile)))
|
|
bSlow = TRUE;
|
|
|
|
if (!bSlow)
|
|
bSlow = PathIsHighLatency(pszFile, dwFileAttr);
|
|
|
|
return bSlow;
|
|
}
|
|
|
|
STDAPI_(BOOL) PathIsSlowA(LPCSTR pszFile, DWORD dwFileAttr)
|
|
{
|
|
WCHAR szBuffer[MAX_PATH];
|
|
|
|
SHAnsiToUnicode(pszFile, szBuffer, ARRAYSIZE(szBuffer));
|
|
return PathIsSlowW(szBuffer, dwFileAttr);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------
|
|
/ Purpose:
|
|
/ Process the specified command line and generate a suitably quoted
|
|
/ name, with arguments attached if required.
|
|
/
|
|
/ Notes:
|
|
/ - The destination buffer size can be determined if NULL is passed as a
|
|
/ destination pointer.
|
|
/ - If the source string is quoted then we assume that it exists on the
|
|
/ filing system.
|
|
/
|
|
/ In:
|
|
/ lpSrc -> null terminate source path
|
|
/ lpDest -> destination buffer / = NULL to return buffer size
|
|
/ iMax = maximum number of characters to return into destination
|
|
/ dwFlags =
|
|
/ PPCF_ADDQUOTES = 1 => if path requires quotes then add them
|
|
/ PPCF_ADDARGUMENTS = 1 => append trailing arguments to resulting string (forces ADDQUOTES)
|
|
/ PPCF_NODIRECTORIES = 1 => don't match against directories, only file objects
|
|
/ PPCF_LONGESTPOSSIBLE = 1 => always choose the longest possible executable name ex: d:\program files\fun.exe vs. d:\program.exe
|
|
/ Out:
|
|
/ > 0 if the call works
|
|
/ < 0 if the call fails (object not found, buffer too small for resulting string)
|
|
/----------------------------------------------------------------------------*/
|
|
|
|
STDAPI_(LONG) PathProcessCommand(LPCTSTR lpSrc, LPTSTR lpDest, int iDestMax, DWORD dwFlags)
|
|
{
|
|
TCHAR szName[MAX_PATH];
|
|
TCHAR szLastChoice[MAX_PATH];
|
|
|
|
LPTSTR lpBuffer, lpBuffer2;
|
|
LPCTSTR lpArgs = NULL;
|
|
DWORD dwAttrib;
|
|
LONG i, iTotal;
|
|
LONG iResult = -1;
|
|
BOOL bAddQuotes = FALSE;
|
|
BOOL bQualify = FALSE;
|
|
BOOL bFound = FALSE;
|
|
BOOL bHitSpace = FALSE;
|
|
BOOL bRelative = FALSE;
|
|
LONG iLastChoice = 0;
|
|
HRESULT hr;
|
|
|
|
RIPMSG(lpSrc && IS_VALID_STRING_PTR(lpSrc, -1), "PathProcessCommand: caller passed invalid lpSrc");
|
|
RIPMSG(!lpDest || (iDestMax > 0 && IS_VALID_WRITE_BUFFER(lpDest, TCHAR, iDestMax)), "PathProcessCommand: caller passed invalid lpDest,iDestMax");
|
|
|
|
// Process the given source string, attempting to find what is that path, and what is its
|
|
// arguments.
|
|
|
|
if (lpSrc)
|
|
{
|
|
// Extract the sub string, if its is realative then resolve (if required).
|
|
|
|
if (*lpSrc == TEXT('\"'))
|
|
{
|
|
for (lpSrc++, i=0 ; i<MAX_PATH && *lpSrc && *lpSrc!=TEXT('\"') ; i++, lpSrc++)
|
|
szName[i] = *lpSrc;
|
|
|
|
szName[i] = 0;
|
|
|
|
if (*lpSrc)
|
|
lpArgs = lpSrc+1;
|
|
|
|
if ((dwFlags & PPCF_FORCEQUALIFY) || PathIsRelative(szName))
|
|
{
|
|
if (!PathResolve(szName, NULL, PRF_TRYPROGRAMEXTENSIONS))
|
|
goto exit_gracefully;
|
|
}
|
|
|
|
bFound = TRUE;
|
|
}
|
|
else
|
|
{
|
|
// Is this a relative object, and then take each element upto a seperator
|
|
// and see if we hit an file system object. If not then we can
|
|
|
|
bRelative = PathIsRelative(lpSrc);
|
|
if (bRelative)
|
|
dwFlags &= ~PPCF_LONGESTPOSSIBLE;
|
|
|
|
bQualify = bRelative || ((dwFlags & PPCF_FORCEQUALIFY) != 0);
|
|
|
|
for (i=0; i < MAX_PATH; i++)
|
|
{
|
|
szName[i] = lpSrc[i];
|
|
|
|
// If we hit a space then the string either contains a LFN or we have
|
|
// some arguments. Therefore attempt to get the attributes for the string
|
|
// we have so far, if we are unable to then we can continue
|
|
// checking, if we hit then we know that the object exists and the
|
|
// trailing string are its arguments.
|
|
|
|
if (!szName[i] || szName[i] == TEXT(' '))
|
|
{
|
|
szName[i] = 0;
|
|
if (!bQualify || PathResolve(szName, NULL, PRF_TRYPROGRAMEXTENSIONS))
|
|
{
|
|
dwAttrib = GetFileAttributes(szName);
|
|
|
|
if ((dwAttrib != -1) && (! ((dwAttrib & FILE_ATTRIBUTE_DIRECTORY) && (dwFlags & PPCF_NODIRECTORIES))))
|
|
{
|
|
bFound = TRUE; // success
|
|
lpArgs = &lpSrc[i];
|
|
|
|
if (dwFlags & PPCF_LONGESTPOSSIBLE)
|
|
{
|
|
hr = StringCchCopyN(szLastChoice, ARRAYSIZE(szLastChoice), szName, i);
|
|
if (FAILED(hr))
|
|
{
|
|
goto exit_gracefully;
|
|
}
|
|
iLastChoice = i;
|
|
}
|
|
else
|
|
goto exit_gracefully;
|
|
}
|
|
}
|
|
|
|
if (bQualify)
|
|
memcpy(szName, lpSrc, (i+1)*sizeof(TCHAR));
|
|
else
|
|
szName[i]=lpSrc[i];
|
|
|
|
bHitSpace = TRUE;
|
|
}
|
|
|
|
if (!szName[i])
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
exit_gracefully:
|
|
|
|
// Work out how big the temporary buffer should be, allocate it and
|
|
// build the returning string into it. Then compose the string
|
|
// to be returned.
|
|
|
|
if (bFound)
|
|
{
|
|
if ((dwFlags & PPCF_LONGESTPOSSIBLE) && iLastChoice)
|
|
{
|
|
StringCchCopyN(szName, ARRAYSIZE(szName), szLastChoice, iLastChoice);
|
|
lpArgs = &lpSrc[iLastChoice];
|
|
}
|
|
|
|
if (StrChr(szName, TEXT(' ')))
|
|
bAddQuotes = dwFlags & PPCF_ADDQUOTES;
|
|
|
|
iTotal = lstrlen(szName) + 1; // for terminator
|
|
iTotal += bAddQuotes ? 2 : 0;
|
|
iTotal += (dwFlags & PPCF_ADDARGUMENTS) && lpArgs ? lstrlen(lpArgs) : 0;
|
|
|
|
if (lpDest)
|
|
{
|
|
if (iTotal <= iDestMax)
|
|
{
|
|
lpBuffer = lpBuffer2 = (LPTSTR)LocalAlloc(LPTR, sizeof(TCHAR) * iTotal);
|
|
|
|
if (lpBuffer)
|
|
{
|
|
// First quote if required
|
|
if (bAddQuotes)
|
|
*lpBuffer2++ = TEXT('\"');
|
|
|
|
// Matching name
|
|
hr = StringCchCopy(lpBuffer2, lpBuffer + iTotal - lpBuffer2, szName);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Closing quote if required
|
|
if (bAddQuotes)
|
|
{
|
|
hr = StringCchCat(lpBuffer2, lpBuffer + iTotal - lpBuffer2, TEXT("\""));
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Arguments (if requested)
|
|
if ((dwFlags & PPCF_ADDARGUMENTS) && lpArgs)
|
|
{
|
|
hr = StringCchCat(lpBuffer2, lpBuffer + iTotal - lpBuffer2, lpArgs);
|
|
}
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Then copy into callers buffer, and free out temporary buffer
|
|
hr = StringCchCopy(lpDest, iDestMax, lpBuffer);
|
|
}
|
|
}
|
|
|
|
}
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Return the length of the resulting string
|
|
iResult = iTotal;
|
|
}
|
|
else
|
|
{
|
|
// iResult left at -1
|
|
}
|
|
|
|
LocalFree((HGLOBAL)lpBuffer);
|
|
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Resulting string is this big, although nothing returned (allows them to allocate a buffer)
|
|
iResult = iTotal;
|
|
}
|
|
}
|
|
|
|
return iResult;
|
|
}
|
|
|
|
|
|
// Gets the mounting point for the path passed in
|
|
//
|
|
// Return Value: TRUE: means that we found mountpoint, e.g. c:\ or c:\hostfolder\
|
|
// FALSE: for now means that the path is UNC or buffer too small
|
|
//
|
|
// Mounted volume Returned Path
|
|
//
|
|
// Passed in E:\MountPoint\path 1\path 2
|
|
// C:\ as E:\MountPoint E:\MountPoint
|
|
//
|
|
// Passed in E:\MountPoint\MountInter\path 1
|
|
// C:\ as D:\MountInter and D:\ as E:\MountPoint E:\MountPoint\MountInter
|
|
//
|
|
// Passed in E:\MountPoint\MountInter\path 1
|
|
// No mount E:\
|
|
BOOL PathGetMountPointFromPath(LPCTSTR pcszPath, LPTSTR pszMountPoint, int cchMountPoint)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
HRESULT hr;
|
|
|
|
RIPMSG(pcszPath && IS_VALID_STRING_PTR(pcszPath, -1), "PathGetMountPointFromPath: caller passed invalid pcszPath");
|
|
RIPMSG(pszMountPoint && cchMountPoint >= 0 && IS_VALID_WRITE_BUFFER(pszMountPoint, TCHAR, cchMountPoint), "PathGetMountPointFromPath: caller passed invalid pszMountPoint, cchMountPoint");
|
|
|
|
if (!PathIsUNC(pcszPath))
|
|
{
|
|
hr = StringCchCopy(pszMountPoint, cchMountPoint, pcszPath);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
bRet = TRUE;
|
|
|
|
// Is this only 'c:' or 'c:\'
|
|
if (lstrlen(pcszPath) > 3)
|
|
{
|
|
//no
|
|
LPTSTR pszNextComp = NULL;
|
|
LPTSTR pszBestChoice = NULL;
|
|
TCHAR cTmpChar;
|
|
|
|
if (PathAddBackslash(pszMountPoint))
|
|
{
|
|
// skip the first one, e.g. "c:\"
|
|
pszBestChoice = pszNextComp = PathFindNextComponent(pszMountPoint);
|
|
pszNextComp = PathFindNextComponent(pszNextComp);
|
|
while (pszNextComp)
|
|
{
|
|
cTmpChar = *pszNextComp;
|
|
*pszNextComp = 0;
|
|
|
|
if (GetVolumeInformation(pszMountPoint, NULL, 0, NULL, NULL, NULL, NULL, 0))
|
|
{//found something better than previous shorter path
|
|
pszBestChoice = pszNextComp;
|
|
}
|
|
|
|
*pszNextComp = cTmpChar;
|
|
pszNextComp = PathFindNextComponent(pszNextComp);
|
|
}
|
|
|
|
*pszBestChoice = 0;
|
|
}
|
|
else
|
|
{
|
|
bRet = FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!bRet)
|
|
{
|
|
*pszMountPoint = TEXT('\0');
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
// Returns TRUE if the path is a shortcut to an installed program that can
|
|
// be found under Add/Remvoe Programs
|
|
// The current algorithm is just to make sure the target is an exe and is
|
|
// located under "program files"
|
|
|
|
STDAPI_(BOOL) PathIsShortcutToProgram(LPCTSTR pszFile)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
if (PathIsShortcut(pszFile, -1))
|
|
{
|
|
TCHAR szTarget[MAX_PATH];
|
|
HRESULT hr = GetPathFromLinkFile(pszFile, szTarget, ARRAYSIZE(szTarget));
|
|
if (hr == S_OK)
|
|
{
|
|
if (PathIsExe(szTarget))
|
|
{
|
|
BOOL bSpecialApp = FALSE;
|
|
HKEY hkeySystemPrograms = NULL;
|
|
if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\App Management\\System Programs"),
|
|
0, KEY_QUERY_VALUE, &hkeySystemPrograms))
|
|
{
|
|
TCHAR szValue[MAX_PATH];
|
|
TCHAR szSystemPrograms[MAX_PATH];
|
|
DWORD cbSystemPrograms = sizeof(szSystemPrograms);
|
|
DWORD cchValue = ARRAYSIZE(szValue);
|
|
|
|
DWORD dwType;
|
|
LPTSTR pszFileName = PathFindFileName(szTarget);
|
|
int iValue = 0;
|
|
while (RegEnumValue(hkeySystemPrograms, iValue, szValue, &cchValue, NULL, &dwType,
|
|
(LPBYTE)szSystemPrograms, &cbSystemPrograms) == ERROR_SUCCESS)
|
|
{
|
|
if ((dwType == REG_SZ) && !StrCmpI(pszFileName, szSystemPrograms))
|
|
{
|
|
bSpecialApp = TRUE;
|
|
break;
|
|
}
|
|
|
|
cbSystemPrograms = sizeof(szSystemPrograms);
|
|
cchValue = ARRAYSIZE(szValue);
|
|
iValue++;
|
|
}
|
|
|
|
RegCloseKey(hkeySystemPrograms);
|
|
}
|
|
|
|
if (!bSpecialApp)
|
|
{
|
|
TCHAR szProgramFiles[MAX_PATH];
|
|
if (SHGetSpecialFolderPath(NULL, szProgramFiles, CSIDL_PROGRAM_FILES, FALSE))
|
|
{
|
|
if (PathIsPrefix(szProgramFiles, szTarget))
|
|
{
|
|
bRet = TRUE;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
bRet = FALSE;
|
|
}
|
|
}
|
|
else if (hr == S_FALSE && szTarget[0])
|
|
{
|
|
// Darwin shortcuts, say yes
|
|
bRet = TRUE;
|
|
}
|
|
}
|
|
return bRet;
|
|
}
|
|
|
|
//
|
|
// needed because we export TCHAR versions of these functions that
|
|
// internal components still call
|
|
//
|
|
// Functions are forwarded to shlwapi
|
|
//
|
|
|
|
#undef PathMakePretty
|
|
STDAPI_(BOOL) PathMakePretty(LPTSTR pszPath)
|
|
{
|
|
SHELLSTATE ss;
|
|
|
|
SHGetSetSettings(&ss, SSF_DONTPRETTYPATH, FALSE);
|
|
if (ss.fDontPrettyPath)
|
|
return FALSE;
|
|
|
|
return PathMakePrettyW(pszPath);
|
|
}
|
|
|
|
#undef PathGetArgs
|
|
STDAPI_(LPTSTR) PathGetArgs(LPCTSTR pszPath)
|
|
{
|
|
return PathGetArgsW(pszPath);
|
|
}
|
|
|
|
#undef PathRemoveArgs
|
|
STDAPI_(void) PathRemoveArgs(LPTSTR pszPath)
|
|
{
|
|
PathRemoveArgsW(pszPath);
|
|
}
|
|
|
|
#undef PathFindOnPath
|
|
STDAPI_(BOOL) PathFindOnPath(LPTSTR pszFile, LPCTSTR *ppszOtherDirs)
|
|
{
|
|
return PathFindOnPathW(pszFile, ppszOtherDirs);
|
|
}
|
|
|
|
#undef PathFindExtension
|
|
STDAPI_(LPTSTR) PathFindExtension(LPCTSTR pszPath)
|
|
{
|
|
return PathFindExtensionW(pszPath);
|
|
}
|
|
|
|
#undef PathRemoveExtension
|
|
STDAPI_(void) PathRemoveExtension(LPTSTR pszPath)
|
|
{
|
|
PathRemoveExtensionW(pszPath);
|
|
}
|
|
|
|
#undef PathRemoveBlanks
|
|
STDAPI_(void) PathRemoveBlanks(LPTSTR pszString)
|
|
{
|
|
PathRemoveBlanksW(pszString);
|
|
}
|
|
|
|
#undef PathStripToRoot
|
|
STDAPI_(BOOL) PathStripToRoot(LPTSTR szRoot)
|
|
{
|
|
return PathStripToRootW(szRoot);
|
|
}
|
|
|
|
//CD-Autorun for Win9x called the TCHAR internal api's. So as a workaround we stub them through these function calls.
|
|
|
|
#undef PathRemoveFileSpec
|
|
STDAPI_(BOOL) PathRemoveFileSpec(LPTSTR pFile)
|
|
{
|
|
if (SHGetAppCompatFlags(ACF_ANSI) == ACF_ANSI)
|
|
return PathRemoveFileSpecA((LPSTR)pFile);
|
|
else
|
|
return PathRemoveFileSpecW(pFile);
|
|
}
|
|
|
|
#undef PathAddBackslash
|
|
STDAPI_(LPTSTR) PathAddBackslash(LPTSTR pszPath)
|
|
{
|
|
return PathAddBackslashW(pszPath);
|
|
}
|
|
|
|
#undef PathFindFileName
|
|
STDAPI_(LPTSTR) PathFindFileName(LPCTSTR pszPath)
|
|
{
|
|
return PathFindFileNameW(pszPath);
|
|
}
|
|
|
|
#undef PathStripPath
|
|
STDAPI_(void) PathStripPath(LPTSTR pszPath)
|
|
{
|
|
PathStripPathW(pszPath);
|
|
}
|
|
|
|
// CD-Autorun for Win9x called the TCHAR internal api's. So as a workaround we stub them through these function calls.
|
|
|
|
#undef PathIsRoot
|
|
STDAPI_(BOOL) PathIsRoot(LPCTSTR pszPath)
|
|
{
|
|
if (SHGetAppCompatFlags(ACF_ANSI) == ACF_ANSI)
|
|
return PathIsRootA((LPCSTR)pszPath);
|
|
else
|
|
return PathIsRootW(pszPath);
|
|
}
|
|
|
|
#undef PathSetDlgItemPath
|
|
STDAPI_(void) PathSetDlgItemPath(HWND hDlg, int id, LPCTSTR pszPath)
|
|
{
|
|
PathSetDlgItemPathW(hDlg, id, pszPath);
|
|
}
|
|
|
|
#undef PathUnquoteSpaces
|
|
STDAPI_(void) PathUnquoteSpaces(LPTSTR psz)
|
|
{
|
|
PathUnquoteSpacesW(psz);
|
|
}
|
|
|
|
#undef PathQuoteSpaces
|
|
STDAPI_(void) PathQuoteSpaces(LPTSTR psz)
|
|
{
|
|
PathQuoteSpacesW(psz);
|
|
}
|
|
|
|
#undef PathMatchSpec
|
|
STDAPI_(BOOL) PathMatchSpec(LPCTSTR pszFileParam, LPCTSTR pszSpec)
|
|
{
|
|
return PathMatchSpecW(pszFileParam, pszSpec);
|
|
}
|
|
|
|
#undef PathIsSameRoot
|
|
STDAPI_(BOOL) PathIsSameRoot(LPCTSTR pszPath1, LPCTSTR pszPath2)
|
|
{
|
|
return PathIsSameRootW(pszPath1, pszPath2);
|
|
}
|
|
|
|
#undef PathParseIconLocation
|
|
STDAPI_(int) PathParseIconLocation(IN OUT LPTSTR pszIconFile)
|
|
{
|
|
return PathParseIconLocationW(pszIconFile);
|
|
}
|
|
|
|
#undef PathIsURL
|
|
STDAPI_(BOOL) PathIsURL(IN LPCTSTR pszPath)
|
|
{
|
|
return PathIsURLW(pszPath);
|
|
}
|
|
|
|
#undef PathIsDirectory
|
|
STDAPI_(BOOL) PathIsDirectory(LPCTSTR pszPath)
|
|
{
|
|
return PathIsDirectoryW(pszPath);
|
|
}
|
|
|
|
// CD-Autorun for Win9x called the TCHAR internal api's. So as a workaround we stub them through these function calls.
|
|
|
|
#undef PathFileExists
|
|
STDAPI_(BOOL) PathFileExists(LPCTSTR pszPath)
|
|
{
|
|
if (SHGetAppCompatFlags(ACF_ANSI) == ACF_ANSI)
|
|
return PathFileExistsAndAttributesA((LPCSTR)pszPath, NULL);
|
|
else
|
|
return PathFileExistsAndAttributesW(pszPath, NULL);
|
|
}
|
|
|
|
#undef PathAppend
|
|
STDAPI_(BOOL) PathAppend(LPTSTR pszPath, LPCTSTR pszMore)
|
|
{
|
|
if (SHGetAppCompatFlags(ACF_ANSI) == ACF_ANSI)
|
|
return PathAppendA((LPSTR)pszPath, (LPCSTR)pszMore);
|
|
else
|
|
return PathAppendW(pszPath, pszMore);
|
|
}
|