|
|
//---------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation 1993-1994
//
// File: path.c
//
// This files contains the path whacking code.
//
// History:
// 01-31-94 ScottH Moved from shellext.c
//
//---------------------------------------------------------------------------
///////////////////////////////////////////////////// INCLUDES
#include "brfprv.h" // common headers
#include "res.h"
/*----------------------------------------------------------
Purpose: Removes the trailing backslash from a path.
A:\ --> A:\ C:\foo\ --> C:\foo \\Pyrex\User\ --> \\Pyrex\User
Returns: pointer to NULL that replaced the backslash or the pointer to the last character if it isn't a backslash
Cond: pimped this code from the shell */ LPTSTR PUBLIC MyPathRemoveBackslash( LPTSTR lpszPath) { int len = lstrlen(lpszPath)-1; if (IsDBCSLeadByte((BYTE)*CharPrev(lpszPath,lpszPath+len+1))) len--;
if (!PathIsRoot(lpszPath) && lpszPath[len] == TEXT('\\')) lpszPath[len] = TEXT('\0');
return lpszPath + len; }
/*----------------------------------------------------------
Purpose: Convert a file spec to make it look a bit better if it is all upper case chars.
Returns: -- Cond: -- */ BOOL PRIVATE PathMakeComponentPretty(LPTSTR lpPath) { LPTSTR lp;
// REVIEW: INTL need to deal with lower case chars in (>127) range?
// check for all uppercase
for (lp = lpPath; *lp; lp = CharNext(lp)) { if ((*lp >= TEXT('a')) && (*lp <= TEXT('z'))) return FALSE; // this is a LFN, dont mess with it
}
CharLower(lpPath); CharUpperBuff(lpPath, 1); return TRUE; // did the conversion
}
//---------------------------------------------------------------------------
// Given a pointer to a point in a path - return a ptr the start of the
// next path component. Path components are delimted by slashes or the
// null at the end.
// There's special handling for UNC names.
// This returns NULL if you pass in a pointer to a NULL ie if you're about
// to go off the end of the path.
LPTSTR PUBLIC PathFindNextComponentI(LPCTSTR lpszPath) { LPTSTR lpszLastSlash;
// Are we at the end of a path.
if (!*lpszPath) { // Yep, quit.
return NULL; } // Find the next slash.
// REVIEW UNDONE - can slashes be quoted?
lpszLastSlash = StrChr(lpszPath, TEXT('\\')); // Is there a slash?
if (!lpszLastSlash) { // No - Return a ptr to the NULL.
return (LPTSTR) (lpszPath+lstrlen(lpszPath)); } else { // Is it a UNC style name?
if (TEXT('\\') == *(lpszLastSlash+1)) { // Yep, skip over the second slash.
return lpszLastSlash+2; } else { // Nope. just skip over one slash.
return lpszLastSlash+1; } } }
/*----------------------------------------------------------
Purpose: Takes the path and makes it presentable.
The rules are: If the LFN name is simply the short name (all caps), then convert to lowercase with first letter capitalized
Returns: -- Cond: -- */ void PUBLIC PathMakePresentable( LPTSTR pszPath) { LPTSTR pszComp; // pointers to begining and
LPTSTR pszEnd; // end of path component
LPTSTR pch; int cComponent = 0; BOOL bUNCPath; TCHAR ch;
bUNCPath = PathIsUNC(pszPath);
pszComp = pszPath; while (pszEnd = PathFindNextComponentI(pszComp)) { // pszEnd may be pointing to the right of the backslash
// beyond the path component, so back up one
//
ch = *pszEnd; *pszEnd = 0; // temporary null
// pszComp points to the path component
//
pch = CharNext(pszComp); if (TEXT(':') == *pch) { // Simply capitalize the drive-portion of the path
//
CharUpper(pszComp); } else if (bUNCPath && cComponent++ < 3) { // Network server or share name
// FEATURE: handle LFN network names
//
CharUpper(pszComp); PathMakeComponentPretty(pszComp); } else { // Normal path component
//
PathMakeComponentPretty(pszComp); }
*pszEnd = ch; pszComp = pszEnd; } }
#ifdef NOTUSED
/*----------------------------------------------------------
Purpose: Takes the path and pretties up each component of the path.
The rules are: Use the LFN name of the component If the LFN name is simply the short name (all caps), then convert to lowercase with first letter capitalized
Returns: -- Cond: -- */ void PRIVATE PathGetCompleteLFN( LPCTSTR pszPath, LPTSTR pszLong, int cbLong) { TCHAR sz[MAX_PATH]; TCHAR szPath[MAX_PATH+1]; LPTSTR pszComp; // pointers to begining and end of path component
LPTSTR pszEnd; int cbPath; int cb; BOOL bAtEnd = FALSE; int cComponent = 0; BOOL bUNCPath; TCHAR ch;
// REARCHITECT: this is broken for double-byte characters for sure
// For each component in string, get the LFN and add it to
// the pszLong buffer.
//
cbPath = lstrlen(pszPath) * sizeof(TCHAR); ASSERT(cbPath+1 <= sizeof(szPath)); lstrcpyn(szPath, pszPath, ARRAYSIZE(szPath));
bUNCPath = PathIsUNC(szPath);
*pszLong = NULL_CHAR; cb = 0;
pszComp = szPath; while (pszEnd = PathFindNextComponentI(pszComp)) { // pszEnd may be pointing to the right of the backslash beyond the
// path component, so back up one
//
if (0 == *pszEnd) bAtEnd = TRUE; else { if (!bUNCPath || cComponent > 0) pszEnd--; // not the server or share portions of a UNC path
ch = *pszEnd; *pszEnd = 0; // temporary null
}
// pszComp points to the path component now
//
if (TEXT(':') == *(pszEnd-1) || TEXT(':') == *(pszEnd-2)) { // Simply capitalize the drive-portion of the path
//
CharUpper(szPath); } else if (bUNCPath && cComponent++ < 3) { // Network server or share name
// FEATURE: handle LFN network names
//
CharUpper(pszComp); PathMakeComponentPretty(pszComp); } else { int ib;
// Try to get the LFN
//
*sz = NULL_CHAR; PathGetLongName(szPath, sz, ARRAYSIZE(sz));
// If an LFN does not exist, keep the path component
// as it is. (Sometimes the path component can be
// something like "Link to Foo.txt")
//
if (*sz) { // Make pszComp point to the same offset in sz now
// (the components in each are the same offsets)
//
ib = pszComp - (LPTSTR)szPath; pszComp = &sz[ib]; } PathMakeComponentPretty(pszComp); }
// Save new LFN-ized component to buffer
//
cb += lstrlen(pszComp) * sizeof(TCHAR); if (cbLong <= cb) break; // reached end of pszLong buffer
StrCatBuff(pszLong, pszComp, cbLong/sizeof(TCHAR)); if (!bAtEnd) { PathAddBackslash(pszLong); *pszEnd = ch; if (bUNCPath && 1 == cComponent) pszComp = pszEnd; // pointing to share portion of path
else pszComp = pszEnd+1; // Move component pointer to next part
} else pszComp = pszEnd; } } #endif
/*----------------------------------------------------------
Purpose: Returns TRUE if the combined path of pszFolder and pszName is greater than MAX_PATH.
Returns: see above Cond: -- */ BOOL PUBLIC PathsTooLong( LPCTSTR pszFolder, LPCTSTR pszName) { // +1 for possible '\' between the two path components
return lstrlen(pszFolder) + lstrlen(pszName) + 1 >= MAX_PATH; }
/*----------------------------------------------------------
Purpose: Fully qualifies a path Returns: -- Cond: -- */ void PUBLIC BrfPathCanonicalize( LPCTSTR pszPath, LPTSTR pszBuf, int cchMax) // Must be MAX_PATH
{ DWORD dwcPathLen;
dwcPathLen = GetFullPathName(pszPath, MAX_PATH, pszBuf, NULL);
if (! dwcPathLen || dwcPathLen >= MAX_PATH) lstrcpyn(pszBuf, pszPath, cchMax);
// If pszBuf won't cover losslessly to ANSI, use the short name instead
#if defined(UNICODE)
{ CHAR szAnsi[MAX_PATH]; WCHAR szUnicode[MAX_PATH]; szUnicode[0] = L'\0';
WideCharToMultiByte(CP_ACP, 0, pszBuf, -1, szAnsi, ARRAYSIZE(szAnsi), NULL, NULL); MultiByteToWideChar(CP_ACP, 0, szAnsi, -1, szUnicode, ARRAYSIZE(szUnicode)); if (lstrcmp(szUnicode, pszBuf)) { // Cannot convert losslessly from Unicode -> Ansi, so get the short path
lstrcpyn(szUnicode, pszBuf, ARRAYSIZE(szUnicode)); SheShortenPath(szUnicode, TRUE); lstrcpyn(pszBuf, szUnicode, cchMax); } } #endif
PathMakePresentable(pszBuf);
ASSERT(lstrlen(pszBuf) < MAX_PATH); }
/*----------------------------------------------------------
Purpose: Gets the displayable filename of the path. The filename is placed in the provided buffer.
Returns: pointer to buffer Cond: -- */ LPTSTR PUBLIC PathGetDisplayName( LPCTSTR pszPath, LPTSTR pszBuf, int cchMax) { SHFILEINFO sfi;
if (SHGetFileInfo(pszPath, 0, &sfi, sizeof(sfi), SHGFI_DISPLAYNAME)) lstrcpyn(pszBuf, sfi.szDisplayName, cchMax); else lstrcpyn(pszBuf, PathFindFileName(pszPath), cchMax);
return pszBuf; }
/*----------------------------------------------------------
Purpose: Checks if the attributes of the path. If it is a directory and has the system bit set, and if the brfcase.dat file exists in the directory, then return TRUE.
Worst case: performs two GetFileAttributes.
Returns: see above Cond: -- */ BOOL PUBLIC PathCheckForBriefcase( LPCTSTR pszPath, DWORD dwAttrib) // if -1, then function gets the attributes
{ ASSERT(pszPath);
if (0xFFFFFFFF == dwAttrib) { dwAttrib = GetFileAttributes(pszPath); if (0xFFFFFFFF == dwAttrib) return FALSE; }
if (IsFlagSet(dwAttrib, FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_READONLY) || IsFlagSet(dwAttrib, FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM)) { TCHAR szT[MAX_PATH]; LPCTSTR pszDBName;
// Check for the existence of the brfcase.dat file.
//
if (IsLFNDrive(pszPath)) pszDBName = g_szDBName; else pszDBName = g_szDBNameShort;
if (PathsTooLong(pszPath, pszDBName)) return FALSE; else { PathCombine(szT, pszPath, pszDBName); return PathExists(szT); } }
return FALSE; }
/*----------------------------------------------------------
Purpose: Returns TRUE if the path is to a briefcase root.
This function may hit the file-system to achieve its goal.
Worst case: performs two GetFileAttributes.
Returns: TRUE if the path refers to a briefcase root. Cond: -- */ BOOL PUBLIC PathIsBriefcase( LPCTSTR pszPath) { UINT uRet;
ASSERT(pszPath);
// We perform our search by first looking in our cache
// of known briefcase paths (CPATH). If we don't find
// anything, then we proceed to iterate thru each
// component of the path, checking for these two things:
//
// 1) A directory with the system attribute
// 2) The existence of a brfcase.dat file in the directory.
//
uRet = CPATH_GetLocality(pszPath, NULL, 0); if (PL_FALSE == uRet) { uRet = PathCheckForBriefcase(pszPath, (DWORD)-1) ? PL_ROOT : PL_FALSE;
if (PL_ROOT == uRet) { int atom;
// Add this path to the briefcase path cache.
//
atom = Atom_Add(pszPath); if (ATOM_ERR != atom) CPATH_Replace(atom); } }
return PL_ROOT == uRet; }
/*----------------------------------------------------------
Purpose: Gets the locality of the path, relative to any briefcase. If PL_ROOT or PL_INSIDE is returned, pszBuf will contain the path to the root of the briefcase.
This function may hit the file-system to achieve its goal.
Worst case: performs 2*n GetFileAttributes, where n is the number of components in pszPath.
Returns: Path locality (PL_FALSE, PL_ROOT, PL_INSIDE)
Cond: -- */ UINT PUBLIC PathGetLocality( LPCTSTR pszPath, LPTSTR pszBuf, // Buffer for root path
int cchMax) { UINT uRet;
ASSERT(pszPath); ASSERT(pszBuf);
*pszBuf = NULL_CHAR;
// pszPath may be:
// 1) a path to the briefcase folder itself
// 2) a path to a file or folder beneath the briefcase
// 3) a path to something unrelated to a briefcase
// We perform our search by first looking in our cache
// of known briefcase paths (CPATH). If we don't find
// anything, then we proceed to iterate thru each
// component of the path, checking for these two things:
//
// 1) A directory with the system attribute
// 2) The existence of a brfcase.dat file in the directory.
//
uRet = CPATH_GetLocality(pszPath, pszBuf, cchMax); if (PL_FALSE == uRet) { int cnt = 0;
lstrcpyn(pszBuf, pszPath, cchMax); do { if (PathCheckForBriefcase(pszBuf, (DWORD)-1)) { int atom;
uRet = cnt > 0 ? PL_INSIDE : PL_ROOT;
// Add this briefcase path to our cache
//
atom = Atom_Add(pszBuf); if (ATOM_ERR != atom) CPATH_Replace(atom);
break; // Done
}
cnt++;
} while (PathRemoveFileSpec(pszBuf));
if (PL_FALSE == uRet) *pszBuf = NULL_CHAR; }
return uRet; }
/*----------------------------------------------------------
Purpose: Returns TRUE if the file/directory exists.
Returns: see above Cond: -- */ BOOL PUBLIC PathExists( LPCTSTR pszPath) { return GetFileAttributes(pszPath) != 0xFFFFFFFF; }
/*----------------------------------------------------------
Purpose: Finds the end of the root specification in a path.
input path output string ---------- ------------- c: <empty string> c:\ <empty string> c:\foo foo c:\foo\bar foo\bar \\pyrex\user <empty string> \\pyrex\user\ <empty string> \\pyrex\user\foo foo \\pyrex\user\foo\bar foo\bar
Returns: pointer to first character after end of root spec.
Cond: -- */ LPCTSTR PUBLIC PathFindEndOfRoot( LPCTSTR pszPath) { LPCTSTR psz;
ASSERT(pszPath);
if (TEXT(':') == pszPath[1]) { if (TEXT('\\') == pszPath[2]) psz = &pszPath[3]; else psz = &pszPath[2]; } else if (PathIsUNC(pszPath)) { psz = PathFindNextComponentI(pszPath); // hop double-slash
psz = PathFindNextComponentI(psz); // hop server name
if (psz) psz = PathFindNextComponentI(psz); // hop share name
if (!psz) { ASSERT(0); // There is no share name
psz = pszPath; } } else { ASSERT(0); psz = pszPath; }
return psz; }
/*----------------------------------------------------------
Purpose: Sends a notify message to the shell regarding a file-status change. Returns: -- Cond: -- */ void PUBLIC PathNotifyShell( LPCTSTR pszPath, NOTIFYSHELLEVENT nse, BOOL bDoNow) // TRUE: force the event to be processed right away
{
static LONG const rgShEvents[] = { SHCNE_CREATE, SHCNE_MKDIR, SHCNE_UPDATEITEM, SHCNE_UPDATEDIR };
ASSERT(pszPath); ASSERT(nse < ARRAYSIZE(rgShEvents));
SHChangeNotify(rgShEvents[nse], SHCNF_PATH, pszPath, NULL);
if (bDoNow) { SHChangeNotify(0, SHCNF_FLUSHNOWAIT, NULL, NULL); } }
|