|
|
#include "priv.h"
#include "advpub.h"
#include "sdsutils.h"
#include "utils.h"
#ifdef WINNT_ENV
#include <winnlsp.h> // Get private NORM_ flag for StrEqIntl()
#endif
#ifndef NORM_STOP_ON_NULL // Until we sync up with nt headers again...
#define NORM_STOP_ON_NULL 0x10000000 /* stop at the null termination */
#endif
#define StrIntlEqNI( s1, s2, nChar) StrIsIntlEqualA( TRUE, s1, s2, nChar)
static const TCHAR c_szPATH[] = TEXT("PATH"); static const TCHAR c_szEllipses[] = TEXT("..."); static const TCHAR c_szColonSlash[] = TEXT(":\\"); //
// Inline function to check for a double-backslash at the
// beginning of a string
//
static __inline BOOL DBL_BSLASH(LPCTSTR psz) { return (psz[0] == TEXT('\\') && psz[1] == TEXT('\\')); }
BOOL RunningOnNT(void) { OSVERSIONINFO VerInfo;
VerInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&VerInfo);
return (VerInfo.dwPlatformId == VER_PLATFORM_WIN32_NT); }
// 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 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
} }
// Returns a pointer to the last component of a path string.
//
// in:
// path name, either fully qualified or not
//
// returns:
// pointer into the path where the path is. if none is found
// returns a poiter to the start of the path
//
// c:\foo\bar -> bar
// c:\foo -> foo
// c:\foo\ -> c:\foo\ (REVIEW: is this case busted?)
// c:\ -> c:\ (REVIEW: this case is strange)
// c: -> c:
// foo -> foo
LPTSTR PathFindFileName(LPCTSTR pPath) { LPCTSTR pT;
for (pT = pPath; *pPath; pPath = CharNext(pPath)) { if ((pPath[0] == TEXT('\\') || pPath[0] == TEXT(':') || pPath[0] == TEXT('/')) && pPath[1] && pPath[1] != TEXT('\\') && pPath[1] != TEXT('/')) pT = pPath + 1; }
return (LPTSTR)pT; // const -> non const
}
//---------------------------------------------------------------------------
// Returns TRUE if the given string is a UNC path.
//
// TRUE
// "\\foo\bar"
// "\\foo" <- careful
// "\\"
// FALSE
// "\foo"
// "foo"
// "c:\foo"
//
// Cond: Note that SHELL32 implements its own copy of this
// function.
BOOL PathIsUNC(LPCTSTR pszPath) { return DBL_BSLASH(pszPath); }
//---------------------------------------------------------------------------
// Returns 0 through 25 (corresponding to 'A' through 'Z') if the path has
// a drive letter, otherwise returns -1.
//
//
// Cond: Note that SHELL32 implements its own copy of this
// function.
int PathGetDriveNumber(LPCTSTR lpsz) { if (!IsDBCSLeadByte(lpsz[0]) && lpsz[1] == TEXT(':')) { if (lpsz[0] >= TEXT('a') && lpsz[0] <= TEXT('z')) return (lpsz[0] - TEXT('a')); else if (lpsz[0] >= TEXT('A') && lpsz[0] <= TEXT('Z')) return (lpsz[0] - TEXT('A')); } return -1; }
//---------------------------------------------------------------------------
// Returns TRUE if the given string is a UNC path to a server only (no share name).
//
// TRUE
// "\\foo" <- careful
// "\\"
// FALSE
// "\\foo\bar"
// "\foo"
// "foo"
// "c:\foo"
BOOL PathIsUNCServer(LPCTSTR pszPath) { if (DBL_BSLASH(pszPath)) { int i = 0; LPTSTR szTmp;
for (szTmp = (LPTSTR)pszPath; szTmp && *szTmp; szTmp = CharNext(szTmp) ) { if (*szTmp==TEXT('\\')) { i++; } }
return (i == 2); }
return FALSE; }
/*----------------------------------------------------------
Purpose: Determines if pszPath is a directory. "C:\" is considered a directory too.
Returns: TRUE if it is
Cond: Note that SHELL32 implements its own copy of this function. */ BOOL PathIsDirectory(LPCTSTR pszPath) { DWORD dwAttribs;
// SHELL32's PathIsDirectory also handles server/share
// paths, but calls WNet APIs, which we cannot call.
if (PathIsUNCServer(pszPath)) { return FALSE; } else { dwAttribs = GetFileAttributes(pszPath); if (dwAttribs != (DWORD)-1) return (BOOL)(dwAttribs & FILE_ATTRIBUTE_DIRECTORY); }
return FALSE; }
// check if a path is a root
//
// returns:
// TRUE for "\" "X:\" "\\foo\asdf" "\\foo\"
// FALSE for others
BOOL 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; }
// 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 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;
}
// find the next slash or null terminator
static LPCTSTR StrSlash(LPCTSTR psz) { for (; *psz && *psz != TEXT('\\'); psz = CharNext(psz));
return psz; }
/*
* IntlStrEq * * returns TRUE if strings are equal, FALSE if not */ BOOL StrIsIntlEqualA(BOOL fCaseSens, LPCSTR lpString1, LPCSTR lpString2, int nChar) { int retval; DWORD dwFlags = fCaseSens ? LOCALE_USE_CP_ACP : (NORM_IGNORECASE | LOCALE_USE_CP_ACP);
if ( RunningOnNT() ) { // On NT we can tell CompareString to stop at a '\0' if one is found before nChar chars
//
dwFlags |= NORM_STOP_ON_NULL; } else if (nChar != -1) { // On Win9x we have to do the check manually
//
LPCSTR psz1, psz2; int cch = 0;
psz1 = lpString1; psz2 = lpString2;
while( *psz1 != '\0' && *psz2 != '\0' && cch < nChar) { psz1 = CharNextA(psz1); psz2 = CharNextA(psz2);
cch = min((int)(psz1 - lpString1), (int)(psz2 - lpString2)); }
// add one in for terminating '\0'
cch++;
if (cch < nChar) { nChar = cch; } }
retval = CompareStringA( GetThreadLocale(), dwFlags, lpString1, nChar, lpString2, nChar ); if (retval == 0) { //
// The caller is not expecting failure. Try the system
// default locale id.
//
retval = CompareStringA( LOCALE_SYSTEM_DEFAULT, dwFlags, lpString1, nChar, lpString2, nChar ); }
return (retval == 2);
}
//
// in:
// pszFile1 -- fully qualified path name to file #1.
// pszFile2 -- fully qualified path name to file #2.
//
// out:
// pszPath -- pointer to a string buffer (may be NULL)
//
// returns:
// length of output buffer not including the NULL
//
// examples:
// c:\win\desktop\foo.txt
// c:\win\tray\bar.txt
// -> c:\win
//
// c:\ ;
// c:\ ;
// -> c:\ NOTE, includes slash
//
// Returns:
// Length of the common prefix string usually does NOT include
// trailing slash, BUT for roots it does.
//
int PathCommonPrefix( LPCTSTR pszFile1, LPCTSTR pszFile2, LPTSTR pszPath) { LPCTSTR psz1, psz2, pszNext1, pszNext2, pszCommon; int cch;
pszCommon = NULL; if (pszPath) *pszPath = TEXT('\0');
psz1 = pszFile1; psz2 = pszFile2;
// special cases for UNC, don't allow "\\" to be a common prefix
if (DBL_BSLASH(pszFile1)) { if (!DBL_BSLASH(pszFile2)) return 0;
psz1 = pszFile1 + 2; } if (DBL_BSLASH(pszFile2)) { if (!DBL_BSLASH(pszFile1)) return 0;
psz2 = pszFile2 + 2; }
while (1) { //ASSERT(*psz1 != TEXT('\\') && *psz2 != TEXT('\\'));
pszNext1 = StrSlash(psz1); pszNext2 = StrSlash(psz2);
cch = (int)(pszNext1 - psz1);
if (cch != (pszNext2 - psz2)) break; // lengths of segments not equal
if (StrIntlEqNI(psz1, psz2, cch)) pszCommon = pszNext1; else break;
//ASSERT(*pszNext1 == TEXT('\0') || *pszNext1 == TEXT('\\'));
//ASSERT(*pszNext2 == TEXT('\0') || *pszNext2 == TEXT('\\'));
if (*pszNext1 == TEXT('\0')) break;
psz1 = pszNext1 + 1;
if (*pszNext2 == TEXT('\0')) break;
psz2 = pszNext2 + 1; }
if (pszCommon) { cch = (int)(pszCommon - pszFile1);
// special case the root to include the slash
if (cch == 2) { //ASSERT(pszFile1[1] == TEXT(':'));
cch++; } } else cch = 0;
if (pszPath) { CopyMemory(pszPath, pszFile1, cch * sizeof(TCHAR)); pszPath[cch] = TEXT('\0'); }
return cch; }
/*----------------------------------------------------------
Purpose: Returns TRUE if pszPrefix is the full prefix of pszPath.
Returns: Cond: -- */ BOOL PathIsPrefix( LPCTSTR pszPrefix, LPCTSTR pszPath) { int cch = PathCommonPrefix(pszPath, pszPrefix, NULL);
return (lstrlen(pszPrefix) == cch); }
|