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.
288 lines
8.2 KiB
288 lines
8.2 KiB
// Copyright (c) 1997-1999 Microsoft Corporation
|
|
|
|
#include "precomp.h"
|
|
#include "shlwapi2.h"
|
|
#include <platform.h>
|
|
|
|
//---------------------------------------------------------
|
|
#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;
|
|
}
|
|
|