Windows-Server-2003/sdktools/sdv/string.cpp

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