344 lines
8.5 KiB
C++
344 lines
8.5 KiB
C++
|
/*****************************************************************************
|
||
|
*
|
||
|
* string.cpp
|
||
|
*
|
||
|
* World's lamest string class.
|
||
|
*
|
||
|
*****************************************************************************/
|
||
|
|
||
|
#include "sdview.h"
|
||
|
|
||
|
_String::_String(LPTSTR pszBufOrig, UINT cchBufOrig)
|
||
|
: _pszBufOrig(pszBufOrig)
|
||
|
, _pszBuf(pszBufOrig)
|
||
|
, _cchBuf(cchBufOrig)
|
||
|
{
|
||
|
Reset();
|
||
|
}
|
||
|
|
||
|
_String::~_String()
|
||
|
{
|
||
|
if (_pszBuf != _pszBufOrig) {
|
||
|
LocalFree(_pszBuf);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Notice that Reset does not free the allocated buffer. Once we've
|
||
|
// switched to using an allocated buffer, we may as well continue to
|
||
|
// use it.
|
||
|
//
|
||
|
|
||
|
void _String::Reset()
|
||
|
{
|
||
|
ASSERT(_cchBuf);
|
||
|
_cchLen = 0;
|
||
|
_pszBuf[0] = TEXT('\0');
|
||
|
}
|
||
|
|
||
|
BOOL _String::Append(LPCTSTR psz, int cch)
|
||
|
{
|
||
|
int cchNeeded = _cchLen + cch + 1;
|
||
|
if (cchNeeded > _cchBuf)
|
||
|
{
|
||
|
LPTSTR pszNew;
|
||
|
if (_pszBuf != _pszBufOrig) {
|
||
|
pszNew = RECAST(LPTSTR, LocalReAlloc(_pszBuf, cchNeeded * sizeof(TCHAR), LMEM_MOVEABLE));
|
||
|
} else {
|
||
|
pszNew = RECAST(LPTSTR, LocalAlloc(LMEM_FIXED, cchNeeded * sizeof(TCHAR)));
|
||
|
}
|
||
|
|
||
|
if (!pszNew) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
if (_pszBuf == _pszBufOrig) {
|
||
|
memcpy(pszNew, _pszBuf, _cchBuf * sizeof(TCHAR));
|
||
|
}
|
||
|
_cchBuf = cchNeeded;
|
||
|
_pszBuf = pszNew;
|
||
|
}
|
||
|
|
||
|
if (psz) {
|
||
|
lstrcpyn(_pszBuf + _cchLen, psz, cch + 1);
|
||
|
}
|
||
|
_cchLen += cch;
|
||
|
_pszBuf[_cchLen] = TEXT('\0');
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
_String& _String::operator<<(int i)
|
||
|
{
|
||
|
TCHAR sz[64];
|
||
|
wsprintf(sz, TEXT("%d"), i);
|
||
|
return *this << sz;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// This could be inline but it's not worth it.
|
||
|
//
|
||
|
_String& _String::operator<<(TCHAR tch)
|
||
|
{
|
||
|
Append(&tch, 1);
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// This could be inline but it's not worth it.
|
||
|
//
|
||
|
BOOL _String::Append(LPCTSTR psz)
|
||
|
{
|
||
|
return Append(psz, lstrlen(psz));
|
||
|
}
|
||
|
|
||
|
BOOL _String::Ensure(int cch)
|
||
|
{
|
||
|
BOOL f;
|
||
|
|
||
|
if (Length() + cch < BufferLength()) {
|
||
|
f = TRUE; // Already big enough
|
||
|
} else {
|
||
|
f = Grow(cch);
|
||
|
if (f) {
|
||
|
_cchLen -= cch;
|
||
|
}
|
||
|
}
|
||
|
return f;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Remove any trailing CRLF
|
||
|
//
|
||
|
void _String::Chomp()
|
||
|
{
|
||
|
if (Length() > 0 && Buffer()[Length()-1] == TEXT('\n')) {
|
||
|
Trim();
|
||
|
}
|
||
|
if (Length() > 0 && Buffer()[Length()-1] == TEXT('\r')) {
|
||
|
Trim();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
OutputStringBuffer::~OutputStringBuffer()
|
||
|
{
|
||
|
if (Buffer() != OriginalBuffer()) {
|
||
|
lstrcpyn(OriginalBuffer(), Buffer(), _cchBufOrig);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*****************************************************************************
|
||
|
*
|
||
|
* QuoteSpaces
|
||
|
*
|
||
|
* Append the string, quoting it if it contains any spaces
|
||
|
* or if it is the null string.
|
||
|
*
|
||
|
*****************************************************************************/
|
||
|
|
||
|
_String& operator<<(_String& str, QuoteSpaces qs)
|
||
|
{
|
||
|
if (qs) {
|
||
|
if (qs[0] == TEXT('\0') || StrChr(qs, TEXT(' '))) {
|
||
|
str << '"' << SAFECAST(LPCTSTR, qs) << '"';
|
||
|
} else {
|
||
|
str << SAFECAST(LPCTSTR, qs);
|
||
|
}
|
||
|
}
|
||
|
return str;
|
||
|
}
|
||
|
|
||
|
/*****************************************************************************
|
||
|
*
|
||
|
* BranchOf
|
||
|
*
|
||
|
* Given a full depot path, append the branch name.
|
||
|
*
|
||
|
*****************************************************************************/
|
||
|
|
||
|
_String& operator<<(_String& str, BranchOf bof)
|
||
|
{
|
||
|
if (bof && bof[0] == TEXT('/') && bof[1] == TEXT('/')) {
|
||
|
//
|
||
|
// Skip over the word "//depot" -- or whatever it is.
|
||
|
// Some admins are stupid and give the root of the depot
|
||
|
// some other strange name.
|
||
|
//
|
||
|
LPCTSTR pszBranch = StrChr(bof + 2, TEXT('/'));
|
||
|
if (pszBranch) {
|
||
|
pszBranch++;
|
||
|
//
|
||
|
// If the next phrase is "private", then we are in a
|
||
|
// private branch; skip a step.
|
||
|
//
|
||
|
if (StringBeginsWith(pszBranch, TEXT("private/"))) {
|
||
|
pszBranch += 8;
|
||
|
}
|
||
|
|
||
|
LPCTSTR pszSlash = StrChr(pszBranch, TEXT('/'));
|
||
|
if (pszSlash) {
|
||
|
str << Substring(pszBranch, pszSlash);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return str;
|
||
|
}
|
||
|
|
||
|
/*****************************************************************************
|
||
|
*
|
||
|
* FilenameOf
|
||
|
*
|
||
|
* Given a full depot path, possibly with revision tag,
|
||
|
* append just the filename part.
|
||
|
*
|
||
|
*****************************************************************************/
|
||
|
|
||
|
_String& operator<<(_String& str, FilenameOf fof)
|
||
|
{
|
||
|
if (fof) {
|
||
|
LPCTSTR pszFile = StrRChr(fof, NULL, TEXT('/'));
|
||
|
if (pszFile) {
|
||
|
pszFile++;
|
||
|
} else {
|
||
|
pszFile = fof;
|
||
|
}
|
||
|
str.Append(pszFile, StrCSpn(pszFile, TEXT("#")));
|
||
|
}
|
||
|
return str;
|
||
|
}
|
||
|
|
||
|
/*****************************************************************************
|
||
|
*
|
||
|
* StringResource
|
||
|
*
|
||
|
* Given a string resource identifier, append the corresponding string.
|
||
|
*
|
||
|
*****************************************************************************/
|
||
|
|
||
|
_String& operator<<(_String& str, StringResource sr)
|
||
|
{
|
||
|
HRSRC hrsrc = FindResource(g_hinst, MAKEINTRESOURCE(1 + sr / 16), RT_STRING);
|
||
|
if (hrsrc) {
|
||
|
HGLOBAL hglob = LoadResource(g_hinst, hrsrc);
|
||
|
if (hglob) {
|
||
|
LPWSTR pwch = RECAST(LPWSTR, LockResource(hglob));
|
||
|
if (pwch) {
|
||
|
UINT ui;
|
||
|
for (ui = 0; ui < sr % 16; ui++) {
|
||
|
pwch += *pwch + 1;
|
||
|
}
|
||
|
#ifdef UNICODE
|
||
|
str.Append(pwch+1, *pwch);
|
||
|
#else
|
||
|
int cch = WideCharToMultiByte(CP_ACP, 0, pwch+1, *pwch,
|
||
|
NULL, 0, NULL, NULL);
|
||
|
if (str.Grow(cch)) {
|
||
|
WideCharToMultiByte(CP_ACP, 0, pwch+1, *pwch,
|
||
|
str.Buffer() + str.Length() - cch,
|
||
|
cch,
|
||
|
NULL, NULL);
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return str;
|
||
|
}
|
||
|
|
||
|
/*****************************************************************************
|
||
|
*
|
||
|
* ResolveBranchAndQuoteSpaces
|
||
|
*
|
||
|
* If the file specifier contains a "branch:" prefix, resolve it.
|
||
|
* Then append the result (with spaces quoted).
|
||
|
*
|
||
|
*****************************************************************************/
|
||
|
|
||
|
//
|
||
|
// The real work happens in the worker function.
|
||
|
//
|
||
|
_String& _ResolveBranchAndQuoteSpaces(_String& strOut, LPCTSTR pszSpec, LPCTSTR pszColon)
|
||
|
{
|
||
|
String str;
|
||
|
String strFull;
|
||
|
LPCTSTR pszSD = pszColon + 1;
|
||
|
|
||
|
if (MapToFullDepotPath(pszSD, strFull)) {
|
||
|
|
||
|
//
|
||
|
// Copy the word "//depot" -- or whatever it is.
|
||
|
// Some admins are stupid and give the root of the depot
|
||
|
// some other strange name.
|
||
|
//
|
||
|
LPCTSTR pszBranch = StrChr(strFull + 2, TEXT('/'));
|
||
|
if (pszBranch) {
|
||
|
pszBranch++; // Include the slash
|
||
|
str << Substring(strFull, pszBranch);
|
||
|
|
||
|
//
|
||
|
// Bonus: If the branch name begins with "/" then
|
||
|
// we treat it as a private branch.
|
||
|
//
|
||
|
if (pszSpec[0] == TEXT('/')) {
|
||
|
str << "private";
|
||
|
}
|
||
|
str << Substring(pszSpec, pszColon);
|
||
|
|
||
|
//
|
||
|
// If the next phrase is "private", then we are in a
|
||
|
// private branch; skip a step.
|
||
|
//
|
||
|
if (StringBeginsWith(pszBranch, TEXT("private/"))) {
|
||
|
pszBranch += 8;
|
||
|
}
|
||
|
|
||
|
LPCTSTR pszSlash = StrChr(pszBranch, TEXT('/'));
|
||
|
if (pszSlash) {
|
||
|
str << pszSlash;
|
||
|
}
|
||
|
strOut << QuoteSpaces(str);
|
||
|
} else {
|
||
|
str << QuoteSpaces(strFull);
|
||
|
}
|
||
|
} else {
|
||
|
//
|
||
|
// If anything went wrong, then just ignore the branch prefix.
|
||
|
//
|
||
|
str << QuoteSpaces(pszSD);
|
||
|
}
|
||
|
|
||
|
return str;
|
||
|
}
|
||
|
|
||
|
_String& operator<<(_String& str, ResolveBranchAndQuoteSpaces rb)
|
||
|
{
|
||
|
Substring ss;
|
||
|
if (Parse(TEXT("$b:"), rb, &ss)) {
|
||
|
ASSERT(ss._pszMax[0] == TEXT(':'));
|
||
|
return _ResolveBranchAndQuoteSpaces(str, rb, ss._pszMax);
|
||
|
} else {
|
||
|
return str << QuoteSpaces(rb);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*****************************************************************************
|
||
|
*
|
||
|
* _StringCache=
|
||
|
*
|
||
|
*****************************************************************************/
|
||
|
|
||
|
_StringCache& _StringCache::operator=(LPCTSTR psz)
|
||
|
{
|
||
|
if (_psz) {
|
||
|
LocalFree(_psz);
|
||
|
}
|
||
|
if (psz) {
|
||
|
_psz = StrDup(psz);
|
||
|
} else {
|
||
|
_psz = NULL;
|
||
|
}
|
||
|
return *this;
|
||
|
}
|