// Copyright (c) 1997-1999 Microsoft Corporation #include "precomp.h" #include "shlwapi2.h" #include //--------------------------------------------------------- #ifdef UNICODE //*** FAST_CharNext -- fast CharNext for path operations // DESCRIPTION // when we're just stepping thru chars in a path, a simple '++' is fine. #define FAST_CharNext(p) (DBNotNULL(p) + 1) #ifdef DEBUG LPWSTR WINAPI DBNotNULL(LPCWSTR lpszCurrent) { ATLASSERT(*lpszCurrent); return (LPWSTR) lpszCurrent; } #else #define DBNotNULL(p) (p) #endif #else #define FAST_CharNext(p) CharNext(p) #endif //--------------------------------------------------------- LPTSTR PathFindFileName(LPCTSTR pPath) { LPCTSTR pT = pPath; if(pPath) { for( ; *pPath; pPath = FAST_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 } //--------------------------------------------------------- #ifndef UNICODE // light weight logic for charprev that is not painful for sbcs BOOL IsTrailByte(LPCTSTR pszSt, LPCTSTR pszCur) { LPCTSTR psz = pszCur; // if the given pointer is at the top of string, at least it's not a trail // byte. // if (psz <= pszSt) return FALSE; while (psz > pszSt) { psz--; if (!IsDBCSLeadByte(*psz)) { // This is either a trail byte of double byte char // or a single byte character we've first seen. // Thus, the next pointer must be at either of a leadbyte // or pszCur itself. psz++; break; } } // Now psz can point to: // 1) a leadbyte of double byte character. // 2) pszSt // 3) pszCur // // if psz == pszSt, psz should point to a valid double byte char. // because we didn't hit the above if statement. // // if psz == pszCur, the *(pszCur-1) was non lead byte so pszCur can't // be a trail byte. // // Thus, we can see pszCur as trail byte pointer if the distance from // psz is not DBCS boundary that is 2. // return (BOOL) ((pszCur-psz) & 1); } #endif //---------------------------------------------------------------------- #define LEN_MID_ELLIPSES 4 #define LEN_END_ELLIPSES 3 #define MIN_CCHMAX LEN_MID_ELLIPSES + LEN_END_ELLIPSES // PathCompactPathEx // Output: // "." // ".." // "..." // "...\" // "...\." // "...\.." // "...\..." // "...\Truncated filename..." // "...\whole filename" // "Truncated path\...\whole filename" // "Whole path\whole filename" // The '/' might be used instead of a '\' if the original string used it // If there is no path, but only a file name that does not fit, the output is: // "truncated filename..." BOOL PathCompactPathEx(LPTSTR pszOut, LPCTSTR pszSrc, UINT cchMax, DWORD dwFlags) { if(pszSrc) { TCHAR * pszFileName, *pszWalk; UINT uiFNLen = 0; int cchToCopy = 0, n; TCHAR chSlash = TEXT('0'); ZeroMemory(pszOut, cchMax * sizeof(TCHAR)); if((UINT)lstrlen(pszSrc)+1 < cchMax) { lstrcpy(pszOut, pszSrc); ATLASSERT(pszOut[cchMax-1] == TEXT('\0')); return TRUE; } // Determine what we use as a slash - a / or a \ (default \) pszWalk = (TCHAR*)pszSrc; chSlash = TEXT('\\'); // Scan the entire string as we want the path separator closest to the end // eg. "file://\\Themesrv\desktop\desktop.htm" while(*pszWalk) { if((*pszWalk == TEXT('/')) || (*pszWalk == TEXT('\\'))) chSlash = *pszWalk; pszWalk = FAST_CharNext(pszWalk); } pszFileName = PathFindFileName(pszSrc); uiFNLen = lstrlen(pszFileName); // if the whole string is a file name if(pszFileName == pszSrc && cchMax > LEN_END_ELLIPSES) { lstrcpyn(pszOut, pszSrc, cchMax - LEN_END_ELLIPSES); #ifndef UNICODE if(IsTrailByte(pszSrc, pszSrc+cchMax-LEN_END_ELLIPSES)) *(pszOut+cchMax-LEN_END_ELLIPSES-1) = TEXT('\0'); #endif lstrcat(pszOut, TEXT("...")); ATLASSERT(pszOut[cchMax-1] == TEXT('\0')); return TRUE; } // Handle all the cases where we just use ellipses ie '.' to '.../...' if((cchMax < MIN_CCHMAX)) { for(n = 0; n < (int)cchMax-1; n++) { if((n+1) == LEN_MID_ELLIPSES) pszOut[n] = chSlash; else pszOut[n] = TEXT('.'); } ATLASSERT(0==cchMax || pszOut[cchMax-1] == TEXT('\0')); return TRUE; } // Ok, how much of the path can we copy ? Buffer - (Lenght of MID_ELLIPSES + Len_Filename) cchToCopy = cchMax - (LEN_MID_ELLIPSES + uiFNLen); if (cchToCopy < 0) cchToCopy = 0; #ifndef UNICODE if (cchToCopy > 0 && IsTrailByte(pszSrc, pszSrc+cchToCopy)) cchToCopy--; #endif lstrcpyn(pszOut, pszSrc, cchToCopy); // Now throw in the ".../" or "...\" lstrcat(pszOut, TEXT(".../")); pszOut[lstrlen(pszOut) - 1] = chSlash; //Finally the filename and ellipses if necessary if(cchMax > (LEN_MID_ELLIPSES + uiFNLen)) { lstrcat(pszOut, pszFileName); } else { cchToCopy = cchMax - LEN_MID_ELLIPSES - LEN_END_ELLIPSES; #ifndef UNICODE if(cchToCopy >0 && IsTrailByte(pszFileName, pszFileName+cchToCopy)) cchToCopy--; #endif lstrcpyn(pszOut + LEN_MID_ELLIPSES, pszFileName, cchToCopy); lstrcat(pszOut, TEXT("...")); } ATLASSERT(pszOut[cchMax-1] == TEXT('\0')); return TRUE; } return FALSE; } //---------------------------------------------------------------------- // Returns TRUE if the given string is a UNC path. // // TRUE // "\\foo\bar" // "\\foo" <- careful // "\\" // FALSE // "\foo" // "foo" // "c:\foo" // // bool PathIsUNC(LPCTSTR pszPath) { if(pszPath) { return ((pszPath[0] == _T('\\')) && (pszPath[1] == _T('\\'))); } return false; } // 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 PathAddBackslash(LPTSTR lpszPath) { if(lpszPath) { LPTSTR lpszEnd; // perf: avoid lstrlen call for guys who pass in ptr to end // of buffer (or rather, EOB - 1). // note that such callers need to check for overflow themselves. int ichPath = (*lpszPath && !*(lpszPath + 1)) ? 1 : lstrlen(lpszPath); // 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! if(ichPath >= (_MAX_PATH - 1)) { 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 _T(FILENAME_SEPARATOR): break; default: *lpszEnd++ = _T(FILENAME_SEPARATOR); *lpszEnd = _T('\0'); } return lpszEnd; } return NULL; }