mirror of https://github.com/lianthony/NT4.0
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.
1251 lines
29 KiB
1251 lines
29 KiB
/*
|
|
** Mimic functions from nt's windows\shell\shelldll\path.c
|
|
** Intended only for IE 2.0 stub functions
|
|
*/
|
|
|
|
#include "shellprv.h"
|
|
|
|
TCHAR const c_szDotPif[] = TEXT(".pif");
|
|
TCHAR const c_szDotCom[] = TEXT(".com");
|
|
TCHAR const c_szDotBat[] = TEXT(".bat");
|
|
#ifdef WINNT
|
|
TCHAR const c_szDotCmd[] = TEXT(".cmd");
|
|
#endif
|
|
TCHAR const c_szDotLnk[] = TEXT(".lnk");
|
|
TCHAR const c_szDotExe[] = TEXT(".exe");
|
|
TCHAR const c_szPATH[] = TEXT("PATH");
|
|
TCHAR const c_szNULL[] = TEXT("");
|
|
|
|
//HANDLE g_hProcessHeap; // not used just needed for the header file
|
|
|
|
void WINAPI AssertFailed(LPCTSTR pszFile, int line)
|
|
{
|
|
}
|
|
|
|
|
|
// *** WARNING *** The order of these flags must be identical to to order
|
|
// of the c_aDefExtList array. PathFileExistsDefExt relies on it.
|
|
|
|
#define EXT_NONE 0x0000
|
|
#define EXT_PIF 0x0001
|
|
#define EXT_COM 0x0002
|
|
#define EXT_EXE 0x0004
|
|
#define EXT_BAT 0x0008
|
|
#define EXT_LNK 0x0010
|
|
#define EXT_CMD 0x0020
|
|
#define EXT_DEFAULT (EXT_CMD | EXT_COM | EXT_BAT | EXT_PIF | EXT_EXE | EXT_LNK)
|
|
|
|
LPCTSTR const c_aDefExtList[] = {
|
|
c_szDotPif,
|
|
c_szDotCom,
|
|
c_szDotExe,
|
|
c_szDotBat,
|
|
c_szDotLnk,
|
|
#ifdef WINNT
|
|
c_szDotCmd
|
|
#endif
|
|
};
|
|
|
|
//
|
|
// Inline function to check for a double-backslash at the
|
|
// beginning of a string
|
|
//
|
|
|
|
__inline BOOL DBL_BSLASH(LPNCTSTR psz)
|
|
{
|
|
return (psz[0] == TEXT('\\') && psz[1] == TEXT('\\'));
|
|
}
|
|
|
|
// BUGBUG (davepl) what about .cmd?
|
|
const TCHAR achExes[] = TEXT(".bat\0.pif\0.exe\0.com\0");
|
|
|
|
// Strips leading and trailing blanks from a string.
|
|
// Alters the memory where the string sits.
|
|
//
|
|
// in:
|
|
// lpszString string to strip
|
|
//
|
|
// out:
|
|
// lpszString string sans leading/trailing blanks
|
|
|
|
void WINAPI PathRemoveBlanks(LPTSTR lpszString)
|
|
{
|
|
LPTSTR lpszPosn = lpszString;
|
|
/* strip leading blanks */
|
|
while(*lpszPosn == TEXT(' ')) {
|
|
lpszPosn++;
|
|
}
|
|
if (lpszPosn != lpszString)
|
|
lstrcpy(lpszString, lpszPosn);
|
|
|
|
/* strip trailing blanks */
|
|
|
|
// Find the last non-space
|
|
// Note that AnsiPrev is cheap is non-DBCS, but very expensive otherwise
|
|
for (lpszPosn=lpszString; *lpszString; lpszString=CharNext(lpszString))
|
|
{
|
|
if (*lpszString != TEXT(' '))
|
|
{
|
|
lpszPosn = lpszString;
|
|
}
|
|
}
|
|
|
|
// Note AnsiNext is a macro for non-DBCS, so it will not stop at NULL
|
|
if (*lpszPosn)
|
|
{
|
|
*CharNext(lpszPosn) = TEXT('\0');
|
|
}
|
|
}
|
|
|
|
|
|
// Removes a trailing backslash from a path
|
|
//
|
|
// in:
|
|
// lpszPath (A:\, C:\foo\, etc)
|
|
//
|
|
// out:
|
|
// lpszPath (A:\, C:\foo, etc)
|
|
//
|
|
// returns:
|
|
// ponter to NULL that replaced the backslash
|
|
// or the pointer to the last character if it isn't a backslash.
|
|
|
|
LPTSTR WINAPI PathRemoveBackslash(LPTSTR lpszPath)
|
|
{
|
|
int len = lstrlen(lpszPath)-1;
|
|
if (IsDBCSLeadByte(*CharPrev(lpszPath,lpszPath+len+1)))
|
|
len--;
|
|
|
|
if (!PathIsRoot(lpszPath) && lpszPath[len] == TEXT('\\'))
|
|
lpszPath[len] = TEXT('\0');
|
|
|
|
return lpszPath + len;
|
|
}
|
|
|
|
|
|
/* Appends a filename to a path. Checks the \ problem first
|
|
* (which is why one can't just use lstrcat())
|
|
* Also don't append a \ to : so we can have drive-relative paths...
|
|
* this last bit is no longer appropriate since we qualify first!
|
|
*
|
|
* REVIEW, is this relative junk needed anymore? if not this can be
|
|
* replaced with PathAddBackslash(); lstrcat() */
|
|
|
|
BOOL WINAPI PathAppend(LPTSTR pPath, LPNCTSTR pMore)
|
|
{
|
|
|
|
/* Skip any initial terminators on input. */
|
|
|
|
#ifndef UNICODE
|
|
|
|
while (*pMore == TEXT('\\'))
|
|
pMore = CharNext(pMore);
|
|
#else
|
|
|
|
while (*pMore == TEXT('\\'))
|
|
pMore++;
|
|
|
|
#endif
|
|
|
|
return (BOOL)PathCombine(pPath, pPath, pMore);
|
|
}
|
|
|
|
// returns a pointer to the arguments in a cmd type path or pointer to
|
|
// NULL if no args exist
|
|
//
|
|
// "foo.exe bar.txt" -> "bar.txt"
|
|
// "foo.exe" -> ""
|
|
//
|
|
// Spaces in filenames must be quoted.
|
|
// " "A long name.txt" bar.txt " -> "bar.txt"
|
|
LPSTR WINAPI PathGetArgs(LPCSTR pszPath)
|
|
{
|
|
BOOL fInQuotes = FALSE;
|
|
|
|
if (!pszPath)
|
|
return NULL;
|
|
|
|
while (*pszPath)
|
|
{
|
|
if (*pszPath == '"')
|
|
fInQuotes = !fInQuotes;
|
|
else if (!fInQuotes && *pszPath == ' ')
|
|
return (LPSTR)pszPath+1;
|
|
pszPath = AnsiNext(pszPath);
|
|
}
|
|
|
|
return (LPSTR)pszPath;
|
|
}
|
|
|
|
void WINAPI PathRemoveArgs(LPSTR pszPath)
|
|
{
|
|
LPSTR pArgs = PathGetArgs(pszPath);
|
|
if (*pArgs)
|
|
*(pArgs - 1) = '\0'; // clobber the ' '
|
|
// Handle trailing space.
|
|
else
|
|
{
|
|
pArgs = AnsiPrev(pszPath, pArgs);
|
|
if (*pArgs == ' ')
|
|
*pArgs = '\0';
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Given a pointer to the end of a path component, return a pointer to
|
|
// its begining.
|
|
// ie return a pointer to the previous backslash (or start of the string).
|
|
LPCTSTR PCStart(LPCTSTR lpszStart, LPCTSTR lpszEnd)
|
|
{
|
|
LPCTSTR lpszBegin = StrRChr(lpszStart, lpszEnd, TEXT('\\'));
|
|
if (!lpszBegin)
|
|
{
|
|
lpszBegin = lpszStart;
|
|
}
|
|
return lpszBegin;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Return a pointer to the end of the next path componenent in the string.
|
|
// ie return a pointer to the next backslash or terminating NULL.
|
|
LPCTSTR GetPCEnd(LPCTSTR lpszStart)
|
|
{
|
|
LPCTSTR lpszEnd;
|
|
|
|
lpszEnd = StrChr(lpszStart, TEXT('\\'));
|
|
if (!lpszEnd)
|
|
{
|
|
lpszEnd = lpszStart + lstrlen(lpszStart);
|
|
}
|
|
|
|
return lpszEnd;
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------
|
|
// Return TRUE if a file exists (by attribute check) after
|
|
// applying a default extensions (if req).
|
|
BOOL PathFileExistsDefExt(LPTSTR lpszPath, UINT fExt)
|
|
{
|
|
// Try default extensions?
|
|
if (fExt)
|
|
{
|
|
UINT i;
|
|
UINT iPathLen = lstrlen(lpszPath);
|
|
LPTSTR lpszPathEnd = lpszPath + iPathLen;
|
|
//
|
|
// Bail if not enough space for 4 more chars
|
|
//
|
|
if (MAX_PATH-iPathLen < ARRAYSIZE(c_szDotPif)) {
|
|
return FALSE;
|
|
}
|
|
|
|
for (i = 0; i < ARRAYSIZE(c_aDefExtList); i++, fExt = fExt >> 1) {
|
|
if (fExt & 1) {
|
|
lstrcpy(lpszPathEnd, c_aDefExtList[i]);
|
|
if (PathFileExists(lpszPath))
|
|
return TRUE;
|
|
}
|
|
}
|
|
*lpszPathEnd = 0; // Get rid of any extension
|
|
}
|
|
else
|
|
{
|
|
return PathFileExists(lpszPath);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Fix up a few special cases so that things roughly make sense.
|
|
void NearRootFixups(LPTSTR lpszPath, BOOL fUNC)
|
|
{
|
|
// Check for empty path.
|
|
if (lpszPath[0] == TEXT('\0'))
|
|
{
|
|
// Fix up.
|
|
lpszPath[0] = TEXT('\\');
|
|
lpszPath[1] = TEXT('\0');
|
|
}
|
|
// Check for missing slash.
|
|
if (!IsDBCSLeadByte(lpszPath[0]) && lpszPath[1] == TEXT(':') && lpszPath[2] == TEXT('\0'))
|
|
{
|
|
// Fix up.
|
|
lpszPath[2] = TEXT('\\');
|
|
lpszPath[3] = TEXT('\0');
|
|
}
|
|
// Check for UNC root.
|
|
if (fUNC && lpszPath[0] == TEXT('\\') && lpszPath[1] == TEXT('\0'))
|
|
{
|
|
// Fix up.
|
|
lpszPath[0] = TEXT('\\');
|
|
lpszPath[1] = TEXT('\\');
|
|
lpszPath[2] = TEXT('\0');
|
|
}
|
|
}
|
|
|
|
// walk through a path type string (semicolon seperated list of names)
|
|
// this deals with spaces and other bad things in the path
|
|
//
|
|
// call with initial pointer, then continue to call with the
|
|
// result pointer until it returns NULL
|
|
//
|
|
// input: "C:\FOO;C:\BAR;"
|
|
//
|
|
// in:
|
|
// lpPath starting point of path string "C:\foo;c:\dos;c:\bar"
|
|
// cbPath size of szPath
|
|
//
|
|
// out:
|
|
// szPath buffer with path piece
|
|
//
|
|
// returns:
|
|
// pointer to next piece to be used, NULL if done
|
|
//
|
|
//
|
|
// BUGBUG, we should write some test cases specifically for this code
|
|
|
|
LPCTSTR NextPath(LPCTSTR lpPath, LPTSTR szPath, int cbPath)
|
|
{
|
|
LPCTSTR lpEnd;
|
|
|
|
if (!lpPath)
|
|
return NULL;
|
|
|
|
// skip any leading ; in the path...
|
|
while (*lpPath == TEXT(';'))
|
|
lpPath++;
|
|
|
|
// See if we got to the end
|
|
if (*lpPath == 0)
|
|
return NULL; // Yep
|
|
|
|
lpEnd = StrChr(lpPath, TEXT(';'));
|
|
if (!lpEnd)
|
|
lpEnd = lpPath + lstrlen(lpPath);
|
|
|
|
lstrcpyn(szPath, lpPath, min(cbPath, lpEnd - lpPath + 1));
|
|
|
|
// BUGBUG: Neither strncpy nor StrCpyN is compatible with lstrcpyn!
|
|
szPath[lpEnd-lpPath] = TEXT('\0');
|
|
|
|
PathRemoveBlanks(szPath);
|
|
|
|
if (szPath[0]) {
|
|
//REVIEW FE: Deleted as a bug. - kenichin
|
|
//#ifdef DBCS
|
|
// if ((*lpEnd == ';') && (AnsiPrev(lpPath, lpEnd) != lpEnd-2))
|
|
//#else
|
|
if (*lpEnd == TEXT(';'))
|
|
//#endif
|
|
return lpEnd + 1; // next path string (maybe NULL)
|
|
else
|
|
return lpEnd; // pointer to NULL
|
|
} else {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
// check to see if a dir is on the other dir list
|
|
// use this to avoid looking in the same directory twice (don't make the same dos call)
|
|
|
|
BOOL IsOtherDir(LPCTSTR pszPath, LPCTSTR *ppszOtherDirs)
|
|
{
|
|
for (;*ppszOtherDirs; ppszOtherDirs++)
|
|
{
|
|
if (lstrcmpi(pszPath, *ppszOtherDirs) == 0)
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
// Return TRUE if a file exists (by attribute check)
|
|
|
|
BOOL WINAPI PathFileExists(LPCTSTR lpszPath)
|
|
{
|
|
DWORD dwErrMode;
|
|
BOOL fResult;
|
|
|
|
dwErrMode = SetErrorMode(SEM_FAILCRITICALERRORS);
|
|
|
|
fResult = ((UINT)GetFileAttributes(lpszPath) != (UINT)-1);
|
|
|
|
SetErrorMode(dwErrMode);
|
|
|
|
return fResult;
|
|
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Canonicalizes a path.
|
|
BOOL PathCanonicalize(LPTSTR lpszDst, LPCTSTR lpszSrc)
|
|
{
|
|
LPCTSTR lpchSrc;
|
|
LPCTSTR lpchPCEnd; // Pointer to end of path component.
|
|
LPTSTR lpchDst;
|
|
BOOL fUNC;
|
|
int cbPC;
|
|
|
|
fUNC = PathIsUNC(lpszSrc); // Check for UNCness.
|
|
|
|
// Init.
|
|
lpchSrc = lpszSrc;
|
|
lpchDst = lpszDst;
|
|
|
|
while (*lpchSrc)
|
|
{
|
|
// REVIEW: this should just return the count
|
|
lpchPCEnd = GetPCEnd(lpchSrc);
|
|
cbPC = (lpchPCEnd - lpchSrc)+1;
|
|
|
|
// Check for slashes.
|
|
if (cbPC == 1 && *lpchSrc == TEXT('\\'))
|
|
{
|
|
// Just copy them.
|
|
*lpchDst = TEXT('\\');
|
|
lpchDst++;
|
|
lpchSrc++;
|
|
}
|
|
// Check for dots.
|
|
else if (cbPC == 2 && *lpchSrc == TEXT('.'))
|
|
{
|
|
// Skip it...
|
|
// Are we at the end?
|
|
if (*(lpchSrc+1) == TEXT('\0'))
|
|
{
|
|
lpchDst--;
|
|
lpchSrc++;
|
|
}
|
|
else
|
|
lpchSrc += 2;
|
|
}
|
|
// Check for dot dot.
|
|
else if (cbPC == 3 && *lpchSrc == TEXT('.') && *(lpchSrc + 1) == TEXT('.'))
|
|
{
|
|
// make sure we aren't already at the root
|
|
if (!PathIsRoot(lpszDst))
|
|
{
|
|
// Go up... Remove the previous path component.
|
|
lpchDst = (LPTSTR)PCStart(lpszDst, lpchDst - 1);
|
|
}
|
|
else
|
|
{
|
|
// When we can't back up, remove the trailing backslash
|
|
// so we don't copy one again. (C:\..\FOO would otherwise
|
|
// turn into C:\\FOO).
|
|
if (*(lpchSrc + 2) == TEXT('\\'))
|
|
{
|
|
lpchSrc++;
|
|
}
|
|
}
|
|
lpchSrc += 2; // skip ".."
|
|
}
|
|
// Everything else
|
|
else
|
|
{
|
|
// Just copy it.
|
|
lstrcpyn(lpchDst, lpchSrc, cbPC);
|
|
lpchDst += cbPC - 1;
|
|
lpchSrc += cbPC - 1;
|
|
}
|
|
// Keep everything nice and tidy.
|
|
*lpchDst = TEXT('\0');
|
|
}
|
|
|
|
// Check for weirdo root directory stuff.
|
|
NearRootFixups(lpszDst, fUNC);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// returns a pointer to the extension of a file.
|
|
//
|
|
// in:
|
|
// qualified or unqualfied file name
|
|
//
|
|
// returns:
|
|
// pointer to the extension of this file. if there is no extension
|
|
// as in "foo" we return a pointer to the NULL at the end
|
|
// of the file
|
|
//
|
|
// foo.txt ==> ".txt"
|
|
// foo ==> ""
|
|
// foo. ==> "."
|
|
//
|
|
|
|
LPTSTR WINAPI PathFindExtension(LPCTSTR pszPath)
|
|
{
|
|
LPCTSTR pszDot;
|
|
|
|
for (pszDot = NULL; *pszPath; pszPath = CharNext(pszPath))
|
|
{
|
|
switch (*pszPath) {
|
|
case TEXT('.'):
|
|
pszDot = pszPath; // remember the last dot
|
|
break;
|
|
case TEXT('\\'):
|
|
case TEXT(' '): // extensions can't have spaces
|
|
pszDot = NULL; // forget last dot, it was in a directory
|
|
break;
|
|
}
|
|
}
|
|
|
|
// if we found the extension, return ptr to the dot, else
|
|
// ptr to end of the string (NULL extension) (cast->non const)
|
|
return pszDot ? (LPTSTR)pszDot : (LPTSTR)pszPath;
|
|
}
|
|
|
|
BOOL OnExtList(LPNCTSTR pszExtList, LPNCTSTR pszExt)
|
|
{
|
|
for (; *pszExtList; pszExtList += ualstrlen(pszExtList) + 1)
|
|
{
|
|
if (!ualstrcmpi(pszExt, pszExtList))
|
|
{
|
|
return TRUE; // yes
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
// determine if a path is a program by looking at the extension
|
|
//
|
|
BOOL WINAPI stub_PathIsExe(LPCTSTR szFile)
|
|
{
|
|
LPCTSTR temp = PathFindExtension(szFile);
|
|
return OnExtList((LPCTSTR) achExes, temp);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// If a path contains spaces then put quotes around the whole thing.
|
|
void WINAPI stub_PathQuoteSpaces(LPTSTR lpsz)
|
|
{
|
|
int cch;
|
|
|
|
if (StrChr(lpsz, TEXT(' ')))
|
|
{
|
|
// NB - Use hmemcpy coz it supports overlapps.
|
|
cch = lstrlen(lpsz)+1;
|
|
hmemcpy(lpsz+1, lpsz, cch * SIZEOF(TCHAR));
|
|
lpsz[0] = TEXT('"');
|
|
lpsz[cch] = TEXT('"');
|
|
lpsz[cch+1] = TEXT('\0');
|
|
}
|
|
}
|
|
|
|
const TCHAR c_szColonSlash[] = TEXT(":\\");
|
|
|
|
// check if a path is a root
|
|
//
|
|
// returns:
|
|
// TRUE for "\" "X:\" "\\foo\asdf" "\\foo\"
|
|
// FALSE for others
|
|
|
|
BOOL WINAPI PathIsRoot(LPCTSTR pPath)
|
|
{
|
|
if (!IsDBCSLeadByte(*pPath))
|
|
{
|
|
if (!lstrcmpi(pPath + 1, c_szColonSlash)) // "X:\" case
|
|
return TRUE;
|
|
}
|
|
|
|
if ((*pPath == TEXT('\\')) && (*(pPath + 1) == 0)) // "\" case
|
|
return TRUE;
|
|
|
|
if (DBL_BSLASH(pPath)) // smells like UNC name
|
|
{
|
|
LPCTSTR p;
|
|
int cBackslashes = 0;
|
|
|
|
for (p = pPath + 2; *p; p = CharNext(p)) {
|
|
if (*p == TEXT('\\') && (++cBackslashes > 1))
|
|
return FALSE; /* not a bare UNC name, therefore not a root dir */
|
|
}
|
|
return TRUE; /* end of string with only 1 more backslash */
|
|
/* must be a bare UNC, which looks like a root dir */
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
// rips the last part of the path off including the backslash
|
|
// C:\foo -> C:\ ;
|
|
// C:\foo\bar -> C:\foo
|
|
// C:\foo\ -> C:\foo
|
|
// \\x\y\x -> \\x\y
|
|
// \\x\y -> \\x
|
|
// \\x -> ?? (test this)
|
|
// \foo -> \ (Just the slash!)
|
|
//
|
|
// in/out:
|
|
// pFile fully qualified path name
|
|
// returns:
|
|
// TRUE we stripped something
|
|
// FALSE didn't strip anything (root directory case)
|
|
//
|
|
|
|
BOOL WINAPI stub_PathRemoveFileSpec(LPTSTR pFile)
|
|
{
|
|
LPTSTR pT;
|
|
LPTSTR pT2 = pFile;
|
|
|
|
for (pT = pT2; *pT2; pT2 = CharNext(pT2)) {
|
|
if (*pT2 == TEXT('\\'))
|
|
pT = pT2; // last "\" found, (we will strip here)
|
|
else if (*pT2 == TEXT(':')) { // skip ":\" so we don't
|
|
if (pT2[1] ==TEXT('\\')) // strip the "\" from "C:\"
|
|
pT2++;
|
|
pT = pT2 + 1;
|
|
}
|
|
}
|
|
if (*pT == 0)
|
|
return FALSE; // didn't strip anything
|
|
|
|
//
|
|
// handle the \foo case
|
|
//
|
|
else if ((pT == pFile) && (*pT == TEXT('\\'))) {
|
|
// Is it just a '\'?
|
|
if (*(pT+1) != TEXT('\0')) {
|
|
// Nope.
|
|
*(pT+1) = TEXT('\0');
|
|
return TRUE; // stripped something
|
|
}
|
|
else {
|
|
// Yep.
|
|
return FALSE;
|
|
}
|
|
}
|
|
else {
|
|
*pT = 0;
|
|
return TRUE; // stripped something
|
|
}
|
|
}
|
|
|
|
// Modifies:
|
|
// szRoot
|
|
//
|
|
// Returns:
|
|
// TRUE if a drive root was found
|
|
// FALSE otherwise
|
|
//
|
|
BOOL PathStripToRoot(LPTSTR szRoot)
|
|
{
|
|
while(!PathIsRoot(szRoot))
|
|
{
|
|
if (!stub_PathRemoveFileSpec(szRoot))
|
|
{
|
|
// If we didn't strip anything off,
|
|
// must be current drive
|
|
return(FALSE);
|
|
}
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Return TRUE if the path isn't absoulte.
|
|
//
|
|
// TRUE
|
|
// "foo.exe"
|
|
// ".\foo.exe"
|
|
// "..\boo\foo.exe"
|
|
//
|
|
// FALSE
|
|
// "\foo"
|
|
// "c:bar" <- be careful
|
|
// "c:\bar"
|
|
// "\\foo\bar"
|
|
|
|
BOOL WINAPI PathIsRelative(LPNCTSTR lpszPath)
|
|
{
|
|
// The NULL path is assumed relative
|
|
if (*lpszPath == 0)
|
|
return TRUE;
|
|
|
|
// Does it begin with a slash ?
|
|
if (lpszPath[0] == TEXT('\\'))
|
|
return FALSE;
|
|
// Does it begin with a drive and a colon ?
|
|
else if (!IsDBCSLeadByte(lpszPath[0]) && lpszPath[1] == TEXT(':'))
|
|
return FALSE;
|
|
// Probably relative.
|
|
else
|
|
return TRUE;
|
|
}
|
|
|
|
// add a backslash to a qualified path
|
|
//
|
|
// in:
|
|
// lpszPath path (A:, C:\foo, etc)
|
|
//
|
|
// out:
|
|
// lpszPath A:\, C:\foo\ ;
|
|
//
|
|
// returns:
|
|
// pointer to the NULL that terminates the path
|
|
|
|
|
|
LPTSTR WINAPI PathAddBackslash(LPTSTR lpszPath)
|
|
{
|
|
LPTSTR lpszEnd;
|
|
|
|
// try to keep us from tromping over MAX_PATH in size.
|
|
// if we find these cases, return NULL. Note: We need to
|
|
// check those places that call us to handle their GP fault
|
|
// if they try to use the NULL!
|
|
int ichPath = lstrlen(lpszPath);
|
|
if (ichPath >= (MAX_PATH - 1))
|
|
{
|
|
//ASSERT(FALSE); // Let the caller know!
|
|
return(NULL);
|
|
}
|
|
|
|
lpszEnd = lpszPath + ichPath;
|
|
|
|
// this is really an error, caller shouldn't pass
|
|
// an empty string
|
|
if (!*lpszPath)
|
|
return lpszEnd;
|
|
|
|
/* Get the end of the source directory
|
|
*/
|
|
switch(*CharPrev(lpszPath, lpszEnd)) {
|
|
case TEXT('\\'):
|
|
break;
|
|
|
|
default:
|
|
*lpszEnd++ = TEXT('\\');
|
|
*lpszEnd = TEXT('\0');
|
|
}
|
|
return lpszEnd;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Returns TRUE if the given string is a UNC path.
|
|
//
|
|
// TRUE
|
|
// "\\foo\bar"
|
|
// "\\foo" <- careful
|
|
// "\\"
|
|
// FALSE
|
|
// "\foo"
|
|
// "foo"
|
|
// "c:\foo"
|
|
|
|
BOOL WINAPI PathIsUNC(LPNCTSTR pszPath)
|
|
{
|
|
return DBL_BSLASH(pszPath);
|
|
}
|
|
|
|
// concatinate lpszDir and lpszFile into a properly formed path
|
|
// and canonicalizes any relative path pieces
|
|
//
|
|
// returns:
|
|
// pointer to destination buffer
|
|
//
|
|
// lpszDest and lpszFile can be the same buffer
|
|
// lpszDest and lpszDir can be the same buffer
|
|
//
|
|
// assumes:
|
|
// lpszDest is MAX_PATH bytes
|
|
//
|
|
// History:
|
|
// 01-25-93 SatoNa Made a temporary fix for the usability test.
|
|
// ??-??-?? ChrisG hacked upon
|
|
//
|
|
|
|
LPTSTR WINAPI PathCombine(LPTSTR lpszDest, LPCTSTR lpszDir, LPNCTSTR lpszFile)
|
|
{
|
|
TCHAR szTemp[MAX_PATH];
|
|
LPTSTR pszT;
|
|
|
|
if (!lpszFile || *lpszFile==TEXT('\0')) {
|
|
|
|
ualstrcpyn(szTemp, lpszDir, ARRAYSIZE(szTemp)); // lpszFile is empty
|
|
|
|
} else if (lpszDir && *lpszDir && PathIsRelative(lpszFile)) {
|
|
|
|
ualstrcpyn(szTemp, lpszDir, ARRAYSIZE(szTemp));
|
|
pszT = PathAddBackslash(szTemp);
|
|
if (pszT) {
|
|
int iLen = lstrlen(szTemp);
|
|
if ((iLen + ualstrlen(lpszFile)) < ARRAYSIZE(szTemp)) {
|
|
ualstrcpy(pszT, lpszFile);
|
|
} else
|
|
return NULL;
|
|
} else
|
|
return NULL;
|
|
|
|
} else if (lpszDir && *lpszDir &&
|
|
*lpszFile == TEXT('\\') && !PathIsUNC(lpszFile)) {
|
|
|
|
ualstrcpyn(szTemp, lpszDir, ARRAYSIZE(szTemp));
|
|
// BUGBUG: Note that we do not check that an actual root is returned;
|
|
// it is assumed that we are given valid parameters
|
|
PathStripToRoot(szTemp);
|
|
|
|
pszT = PathAddBackslash(szTemp);
|
|
if (pszT)
|
|
{
|
|
// Skip the backslash when copying
|
|
ualstrcpyn(pszT, lpszFile+1, ARRAYSIZE(szTemp) - 1 - (pszT-szTemp));
|
|
} else
|
|
return NULL;
|
|
|
|
} else {
|
|
|
|
ualstrcpyn(szTemp, lpszFile, ARRAYSIZE(szTemp)); // already fully qualified file part
|
|
|
|
}
|
|
|
|
PathCanonicalize(lpszDest, szTemp); // this deals with .. and . stuff
|
|
|
|
return lpszDest;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// fully qualify a path by walking the path and optionally other dirs
|
|
//
|
|
// in:
|
|
// ppszOtherDirs a list of LPCSTRs to other paths to look
|
|
// at first, NULL terminated.
|
|
//
|
|
// fExt
|
|
// EXT_ flags specifying what to look for (exe, com, bat, lnk, pif)
|
|
//
|
|
// in/out
|
|
// pszFile non qualified path, returned fully qualified
|
|
// if found (return was TRUE), otherwise unaltered
|
|
// (return FALSE);
|
|
//
|
|
// returns:
|
|
// TRUE the file was found on and qualified
|
|
// FALSE the file was not found
|
|
//
|
|
BOOL PathFindOnPathEx(LPTSTR pszFile, LPCTSTR *ppszOtherDirs, UINT fExt)
|
|
{
|
|
TCHAR szPath[MAX_PATH];
|
|
TCHAR szFullPath[256]; // Default size for buffer
|
|
LPTSTR pszEnv = NULL; // Use if greater than default
|
|
LPCTSTR lpPath;
|
|
int i;
|
|
|
|
|
|
// first check list of other dirs
|
|
|
|
for (i = 0; ppszOtherDirs && ppszOtherDirs[i] && *ppszOtherDirs[i]; i++)
|
|
{
|
|
PathCombine(szPath, ppszOtherDirs[i], pszFile);
|
|
if (PathFileExistsDefExt(szPath, fExt))
|
|
{
|
|
lstrcpy(pszFile, szPath);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
// Look in system dir - this should probably be optional.
|
|
GetSystemDirectory(szPath, ARRAYSIZE(szPath));
|
|
if (!PathAppend(szPath, pszFile))
|
|
return FALSE;
|
|
|
|
if (PathFileExistsDefExt(szPath, fExt))
|
|
{
|
|
lstrcpy(pszFile, szPath);
|
|
return TRUE;
|
|
}
|
|
|
|
#ifdef WINNT
|
|
// Look in WOW directory (\nt\system instead of \nt\system32)
|
|
GetWindowsDirectory(szPath, ARRAYSIZE(szPath));
|
|
|
|
if (!PathAppend(szPath,TEXT("System")))
|
|
return FALSE;
|
|
if (!PathAppend(szPath, pszFile))
|
|
return FALSE;
|
|
|
|
if (PathFileExistsDefExt(szPath, fExt))
|
|
{
|
|
lstrcpy(pszFile, szPath);
|
|
return TRUE;
|
|
}
|
|
#endif
|
|
|
|
// Look in windows dir - this should probably be optional.
|
|
GetWindowsDirectory(szPath, ARRAYSIZE(szPath));
|
|
if (!PathAppend(szPath, pszFile))
|
|
return FALSE;
|
|
|
|
if (PathFileExistsDefExt(szPath, fExt))
|
|
{
|
|
lstrcpy(pszFile, szPath);
|
|
return TRUE;
|
|
}
|
|
|
|
// Look along the path.
|
|
i = GetEnvironmentVariable(c_szPATH, szFullPath, ARRAYSIZE(szFullPath));
|
|
if (i >= ARRAYSIZE(szFullPath))
|
|
{
|
|
pszEnv = (LPTSTR)LocalAlloc(LPTR, i*SIZEOF(TCHAR)); // no need for +1, i includes it
|
|
if (pszEnv == NULL)
|
|
return FALSE;
|
|
|
|
GetEnvironmentVariable(c_szPATH, pszEnv, i);
|
|
|
|
lpPath = pszEnv;
|
|
}
|
|
else
|
|
{
|
|
if (i == 0)
|
|
return(FALSE);
|
|
|
|
lpPath = szFullPath;
|
|
}
|
|
|
|
while (lpPath = NextPath(lpPath, szPath, ARRAYSIZE(szPath)))
|
|
{
|
|
if (!ppszOtherDirs || !IsOtherDir(szPath, ppszOtherDirs))
|
|
{
|
|
PathAppend(szPath, pszFile);
|
|
if (PathFileExistsDefExt(szPath, fExt))
|
|
{
|
|
lstrcpy(pszFile, szPath);
|
|
if (pszEnv)
|
|
LocalFree((HLOCAL)pszEnv);
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pszEnv)
|
|
LocalFree((HLOCAL)pszEnv);
|
|
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
|
|
//
|
|
// 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
|
|
|
|
// BUGBUG: if making n unique names, the time grows n^2 because it always
|
|
// starts from 0 and checks for existing file.
|
|
BOOL WINAPI PathMakeUniqueNameEx(LPTSTR pszUniqueName,
|
|
UINT cchMax,
|
|
LPCTSTR pszTemplate,
|
|
LPCTSTR pszLongPlate,
|
|
LPCTSTR pszDir,
|
|
int iMinLong)
|
|
{
|
|
BOOL fSuccess=FALSE;
|
|
LPTSTR lpszFormat = pszUniqueName; // use their buffer instead of creating our own
|
|
LPTSTR pszName, pszDigit;
|
|
LPCTSTR pszRest;
|
|
LPCTSTR pszEndUniq; // End of Uniq sequence part...
|
|
LPCTSTR pszStem;
|
|
int cchRest, cchStem, cchDir, cchMaxName;
|
|
int iMax, iMin, i;
|
|
TCHAR achFullPath[MAX_PATH];
|
|
|
|
if (pszLongPlate == NULL)
|
|
pszLongPlate = pszTemplate;
|
|
|
|
// this if/else set up lpszFormat and all the other pointers for the
|
|
// sprintf/file_exists loop below
|
|
iMin = iMinLong;
|
|
if (pszLongPlate && IsLFNDrive(pszDir)) {
|
|
|
|
cchMaxName = 0;
|
|
|
|
// for long name drives
|
|
pszStem = pszLongPlate;
|
|
pszRest = StrChr(pszLongPlate, TEXT('('));
|
|
while (pszRest)
|
|
{
|
|
// First validate that this is the right one
|
|
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 no (, punt to short name
|
|
if (!pszRest) {
|
|
// if no (, then tack it on at the end. (but before the extension)
|
|
// eg. New Link yields New Link (1)
|
|
pszRest = PathFindExtension(pszLongPlate);
|
|
cchStem = pszRest - pszLongPlate;
|
|
wsprintf(lpszFormat, TEXT(" (%%d)%s"), pszRest ? pszRest : c_szNULL);
|
|
iMax = 100;
|
|
} else {
|
|
pszRest++; // stop over the #
|
|
|
|
cchStem = pszRest - pszLongPlate;
|
|
|
|
while (*pszRest && *pszRest >= TEXT('0') && *pszRest <= TEXT('9')) {
|
|
pszRest++;
|
|
}
|
|
|
|
// how much room do we have to play?
|
|
switch(cchMax - cchStem - lstrlen(pszRest)) {
|
|
case 0:
|
|
// no room, bail to short name
|
|
return PathMakeUniqueName(pszUniqueName, cchMax, pszTemplate, NULL, pszDir);
|
|
case 1:
|
|
iMax = 10;
|
|
break;
|
|
case 2:
|
|
iMax = 100;
|
|
break;
|
|
default:
|
|
iMax = 1000;
|
|
break;
|
|
}
|
|
|
|
// we are guaranteed enough room because we don't include
|
|
// the stuff before the # in this format
|
|
wsprintf(lpszFormat, TEXT("%%d%s"), pszRest);
|
|
}
|
|
|
|
} else {
|
|
|
|
// for short name drives
|
|
pszStem = pszTemplate;
|
|
pszRest = PathFindExtension(pszTemplate);
|
|
|
|
cchRest=lstrlen(pszRest)+1; // 5 for ".foo";
|
|
if (cchRest<5)
|
|
cchRest=5;
|
|
cchStem=pszRest-pszTemplate; // 8 for "fooobarr.foo"
|
|
cchDir=lstrlen(pszDir);
|
|
|
|
cchMaxName = 8+cchRest-1;
|
|
|
|
//
|
|
// Remove all the digit characters 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;
|
|
}
|
|
|
|
//
|
|
// Truncate characters from the stem, if it does not fit.
|
|
// In the case were LFNs are supported we use the cchMax that was passed in
|
|
// but for Non LFN drives we use the 8.3 rule.
|
|
//
|
|
if ((UINT)cchStem > (8-1)) {
|
|
cchStem=8-1; // Needs to fit into the 8 part of the name
|
|
}
|
|
|
|
//
|
|
// We should have at least one character in the stem.
|
|
//
|
|
if (cchStem < 1 || (cchDir+cchStem+1+cchRest+1) > MAX_PATH)
|
|
{
|
|
goto Error;
|
|
}
|
|
wsprintf(lpszFormat, TEXT("%%d%s"), pszRest);
|
|
iMax = 1000; iMin = 1;
|
|
}
|
|
|
|
if (pszDir)
|
|
{
|
|
lstrcpy(achFullPath, pszDir);
|
|
PathAddBackslash(achFullPath);
|
|
}
|
|
else
|
|
{
|
|
achFullPath[0] = 0;
|
|
}
|
|
|
|
pszName=achFullPath+lstrlen(achFullPath);
|
|
lstrcpyn(pszName, pszStem, cchStem+1);
|
|
pszDigit = pszName + cchStem;
|
|
|
|
for (i = iMin; i < iMax ; i++) {
|
|
|
|
wsprintf(pszDigit, lpszFormat, i);
|
|
|
|
//
|
|
// 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
|
|
//
|
|
if (cchMaxName && lstrlen(pszName) > cchMaxName)
|
|
{
|
|
pszDigit = CharPrev(pszName, pszDigit);
|
|
wsprintf(pszDigit, lpszFormat, i);
|
|
}
|
|
|
|
#ifdef SN_TRACE
|
|
DebugMsg(DM_TRACE, TEXT("path.c MakeUniquePath: trying %s"), (LPCTSTR)achFullPath);
|
|
#endif
|
|
//
|
|
// Check if this name is unique or not.
|
|
//
|
|
if (!PathFileExists(achFullPath))
|
|
{
|
|
lstrcpyn(pszUniqueName, pszName, cchMax);
|
|
fSuccess=TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
Error:
|
|
return fSuccess;
|
|
}
|
|
|
|
void PathRemoveExtension(LPTSTR pszPath)
|
|
{
|
|
LPTSTR pExt = PathFindExtension(pszPath);
|
|
if (*pExt)
|
|
{
|
|
*pExt = 0; // null out the "."
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
BOOL WINAPI stub_PathFindOnPath(LPTSTR pszFile, LPCTSTR *ppszOtherDirs)
|
|
{
|
|
return PathFindOnPathEx(pszFile, ppszOtherDirs, EXT_NONE);
|
|
}
|
|
|
|
// 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
|
|
//
|
|
// returns:
|
|
// TRUE success, name can be used
|
|
|
|
BOOL WINAPI stub_PathYetAnotherMakeUniqueName(LPTSTR pszUniqueName,
|
|
LPCTSTR pszPath,
|
|
LPCTSTR pszShort,
|
|
LPCTSTR pszFileSpec)
|
|
{
|
|
BOOL fRet = FALSE;
|
|
|
|
TCHAR szTemp[MAX_PATH];
|
|
TCHAR szPath[MAX_PATH];
|
|
|
|
if (pszShort == NULL) {
|
|
pszShort = PathFindFileName(pszPath);
|
|
lstrcpy(szPath, pszPath);
|
|
stub_PathRemoveFileSpec(szPath);
|
|
pszPath = szPath;
|
|
}
|
|
if (pszFileSpec == NULL) {
|
|
pszFileSpec = pszShort;
|
|
}
|
|
|
|
if (IsLFNDrive(pszPath)) {
|
|
LPTSTR lpsz;
|
|
LPTSTR lpszNew;
|
|
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;
|
|
}
|
|
|
|
lstrcpy(szTemp, pszPath);
|
|
lpszNew = PathAddBackslash(szTemp);
|
|
lstrcpy(lpszNew, pszFileSpec);
|
|
lpszNew += (lpsz - pszFileSpec);
|
|
lstrcpy(lpszNew, lpsz + ichSkip);
|
|
fRet = !PathFileExists(szTemp);
|
|
|
|
} else {
|
|
PathCombine(szTemp, pszPath, pszFileSpec);
|
|
fRet = !PathFileExists(szTemp);
|
|
}
|
|
}
|
|
else {
|
|
Assert(lstrlen(PathFindExtension(pszShort)) <= 4);
|
|
|
|
lstrcpy(szTemp,pszShort);
|
|
PathRemoveExtension(szTemp);
|
|
|
|
if (lstrlen(szTemp) <= 8) {
|
|
PathCombine(szTemp, pszPath, pszShort);
|
|
fRet = !PathFileExists(szTemp);
|
|
}
|
|
}
|
|
|
|
if (!fRet) {
|
|
fRet = PathMakeUniqueNameEx(szTemp, ARRAYSIZE(szTemp), pszShort, pszFileSpec, pszPath, 2);
|
|
PathCombine(szTemp, pszPath, szTemp);
|
|
}
|
|
|
|
if (fRet)
|
|
lstrcpy(pszUniqueName, szTemp);
|
|
|
|
return fRet;
|
|
}
|
|
|
|
|
|
|
|
BOOL WINAPI stub_SHGetSpecialFolderPath(HWND hwndOwner, LPTSTR lpszPath, int nFolder, BOOL fCreate)
|
|
{
|
|
// Not Implemented
|
|
return 0;
|
|
}
|
|
|
|
LPITEMIDLIST WINAPI stub_SHSimpleIDListFromPath(LPCTSTR pszPath)
|
|
{
|
|
// Not Implemented
|
|
return 0;
|
|
}
|