/***************************************************************************** * * 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; }