windows-nt/Source/XPSP1/NT/shell/ext/shtl/tstring.cpp
2020-09-26 16:20:57 +08:00

617 lines
19 KiB
C++

// FILE: tstring.cpp
// AUTHOR: Davepl
// REMARKS:
//
// (c) 1999 Dave Plummer. Portions of this code derived from source
// produced by Joe O'Leary with the following license:
// > This code is free. Use it anywhere you want. Rewrite
// > it, restructure it, whatever you want. Please don't blame me if it causes your
// > $30 billion dollar satellite to explode. If you redistribute it in any form, I
// > would appreciate it if you would leave this notice here.
#include "shtl.h"
#include "tstring.h"
#include <xutility>
#if defined (max)
#undef max
#undef min
#endif
#define max(a,b) std::_cpp_max(a,b)
#define min(a,b) std::_cpp_min(a,b)
#include <algorithm>
#include <functional>
#include <locale>
// If conversion has NOT been explicitly turned off...
#ifndef _NO_STDCONVERSION
// Global MBCS-to-UNICODE helper function
__DATL_INLINE PWSTR StdA2WHelper(PWSTR pw, PCSTR pa, int nChars)
{
if (pa == NULL)
return NULL;
ASSERT(pw != NULL);
pw[0] = '\0';
VERIFY(MultiByteToWideChar(CP_ACP, 0, pa, -1, pw, nChars));
return pw;
}
// Global UNICODE-to_MBCS helper function
__DATL_INLINE PSTR StdW2AHelper(PSTR pa, PCWSTR pw, int nChars)
{
if (pw == NULL)
return NULL;
ASSERT(pa != NULL);
pa[0] = '\0';
VERIFY(WideCharToMultiByte(CP_ACP, 0, pw, -1, pa, nChars, NULL, NULL));
return pa;
}
#endif // _NO_STDCONVERSION
// CONSTRUCTOR: tstring::tstring
// tstring(PCTSTR pT)
//
// DESCRIPTION:
// This particular overload of the tstring constructor takes either a real
// string or a resource ID which has been converted with the MAKEINTRESOURCE()
// macro
//
// PARAMETERS:
// pT - a NULL-terminated raw string with which the tstring object should be
// initialized or a resource ID converted with MAKEINTRESOURCE (or NULL)
__DATL_INLINE tstring::tstring(PCTSTR pT) : STRBASE(szTNull) // constructor for either a literal string or a resource ID
{
if ( pT == NULL )
;
else if ( HIWORD(pT) == 0 )
{
if ( !Load(_TRES(pT)) )
TRACE(_T("Can't load string %u\n"), _TRES(pT));
}
else
*this = pT;
}
__DATL_INLINE tstring::tstring(UINT nID) : STRBASE(szTNull)
{
if ( !Load(nID) )
TRACE(_T("Can't load string %u\n"), nID);
}
// FUNCTION: tstring::Load
// bool Load(UINT nId)
//
// DESCRIPTION:
// This function attempts to load the specified string resource from application's
// resource table.
//
// PARAMETERS:
// nId - the resource Identifier from the string table.
//
// RETURN VALUE:
// true if the function succeeds, false otherwise
#define MAX_LOAD_TRIES 8 // max # of times we'll attempt to load a string
#define LOAD_BUF_SIZE 256
__DATL_INLINE bool tstring::Load(UINT nId)
{
#ifdef _MFC_VER
CString strRes(MAKEINTRESOURCE(nId));
*this = strRes;
return !empty();
#else
// Get the resource name via MAKEINTRESOURCE. This line is pretty much lifted from CString
HINSTANCE hInstance = tstring::GetResourceHandle();
HRSRC hrsrc;
int cwch = 0;
WCHAR * pwch;
// String tables are broken up into "bundles" of 16 strings each.
if (HIWORD(nId) == 0)
{
hrsrc = ::FindResource(hInstance, reinterpret_cast<LPTSTR>((UINT_PTR)(1 + nId / 16)), RT_STRING);
if (hrsrc)
{
pwch = (PWCHAR)LoadResource(hInstance, hrsrc);
if (pwch)
{
// Now skip over the strings in the resource until we
// hit the one we want. Each entry is a counted string,
// just like Pascal.
for (nId %= 16; nId; nId--) {
pwch += *pwch + 1;
}
cwch = *pwch;
if (cwch)
{
LPTSTR pszBuffer = GetBuffer(cwch);
#ifdef UNICODE
memcpy(pszBuffer, pwch+1, cwch * sizeof(WCHAR)); /* Copy the goo */
#else
WideCharToMultiByte(CP_ACP, 0, pwch+1, cwch, pszBuffer, cwch+1, NULL, NULL);
#endif
*(pszBuffer+cwch) = TEXT('\0');
ReleaseBuffer(cwch);
}
}
}
}
return (cwch != 0);
#endif
}
// FUNCTION: tstring::Format
// void _cdecl Formst(tstring& PCTSTR szFormat, ...)
// void _cdecl Format(PCTSTR szFormat);
//
// DESCRIPTION:
// This function does sprintf/wsprintf style formatting on tstring objects. It
// is very much a copy of the MFC CString::Format function. Some people might even
// call this identical. However all these people are now dead.
//
// PARAMETERS:
// nId - ID of string resource holding the format string
// szFormat - a PCTSTR holding the format specifiers
// argList - a va_list holding the arguments for the format specifiers.
//
// RETURN VALUE: None.
__DATL_INLINE tstring& tstring::Format(UINT nId, ...)
{
va_list argList;
va_start(argList, nId);
tstring strFmt;
if ( strFmt.Load(nId) )
FormatV(strFmt, argList);
va_end(argList);
return *this;
}
__DATL_INLINE tstring& tstring::Format(PCTSTR szFormat, ...)
{
va_list argList;
va_start(argList, szFormat);
FormatV(szFormat, argList);
va_end(argList);
return *this;
}
// FUNCTION: tstring::FormatV
// void FormatV(PCTSTR szFormat, va_list, argList);
//
// DESCRIPTION:
// This function formats the string with sprintf style format-specifications. It
// makes a general guess at required buffer size and then tries successively larger
// buffers until it finds one big enough or a threshold (MAX_FMT_TRIES) is exceeded.
//
// PARAMETERS:
// szFormat - a PCTSTR holding the format of the output
// argList - va_list for variable argument lists
//
// RETURN VALUE:
#define MAX_FMT_TRIES 5
#define FMT_BLOCK_SIZE 256
__DATL_INLINE tstring& tstring::FormatV(PCTSTR szFormat, va_list argList)
{
va_list argListSave = argList;
// We're just going to use the normal _vsntprintf function, assuming FMT_BLOCK_SIZE characters.
// However, if FMT_BLOCK_SIZE characters is not be enough, we will try 2 * FMT_BLOCK_SIZE, then
// 3 * FMT_BLOCK_SIZE, up to MAX_FMT_TRIES * FMT_BLOCK_SIZE characters.
int nTriesLeft = MAX_FMT_TRIES-1;
int nCharsUsed = - 1;
int nTChars = 0;
// Keep looping until either we succeed or we have exhausted the number of tries
do
{
nTChars += FMT_BLOCK_SIZE; // number of TCHARS in the string
// Allocate a buffer on the stack to hold the characters and NULL terminate it
TCHAR* szBuf = reinterpret_cast<TCHAR*>(_alloca((nTChars+1) * sizeof(TCHAR)));
szBuf[nTChars+1] = '\0';
// Now try the actual formatting. The docs say even the wide version takes the
// number of BYTES as the second argument, not the number of characters (TCHARs).
// However the docs are wrong. I checked the actual implementation of
// _vsnprintf and _vsnwprintf and they multiply count by sizeof TCHAR.
nCharsUsed = _vsntprintf(szBuf, nTChars+1, szFormat, argListSave);
if ( nCharsUsed >= 0 )
*this = szBuf;
} while ( nCharsUsed < 0 && nTriesLeft > 0);
va_end(argListSave);
return *this;
}
// This class is used for TrimRight() and TrimLeft() function implementations.
class NotSpace : public std::unary_function<TCHAR, bool>
{
public:
inline bool operator() (TCHAR tchar)
{
return !_istspace(tchar);
};
};
// FUNCTION: tstring::TrimRight
// tstring& TrimRight();
//
// DESCRIPTION:
// This function removes any whitespace characters from the right end of the string.
//
// PARAMETERS: none
// RETURN VALUE:
// a reference to this object (*this) -- allows chaining together of
// these calls, eg. strTest.TrimRight().TrimLeft().ToUpper();
__DATL_INLINE tstring& tstring::TrimRight()
{
tstring::reverse_iterator iter = std::find_if(rbegin(), rend(), NotSpace());
if ( iter != rend() )
{
tstring::size_type nNewSize = find_last_of(*iter);
erase(nNewSize+1);
}
else
{
erase();
}
return *this;
}
// FUNCTION: tstring::TrimLeft
// tstring& TrimLeft();
//
// DESCRIPTION:
// This function removes any whitespace characters from the left end of the string.
//
// PARAMETERS: none
// RETURN VALUE:
// a reference to this object (*this) -- allows chaining together of
// these calls, (eg. strTest.TrimRight().TrimLeft().ToUpper();)
__DATL_INLINE tstring& tstring::TrimLeft()
{
tstring::iterator iter = std::find_if(begin(), end(), NotSpace());
tstring strNew(iter, end());
STRBASE::assign(strNew);
return *this;
}
// FUNCTION: tstring::ToUpper
// tstring& ToUpper()
//
// DESCRIPTION:
// This function converts the tstring to all uppercase characters using ctype
//
// PARAMETERS:
// RETURN VALUE:
// a reference to this object (*this) -- allows chaining together of
// these calls, (eg. strTest.TrimRight().TrimLeft().ToUpper();)
__DATL_INLINE tstring& tstring::ToUpper()
{
// std::transform(begin(), end(), begin(), toupper); // slow and portable
_tcsupr(const_cast<PTSTR>(data())); // fast and not portable
return *this;
}
// FUNCTION: tstring::ToLower
// tstring& ToLower()
//
// DESCRIPTION:
// This function converts the tstring to all lowercase characters using ctype
//
// PARAMETERS:
// RETURN VALUE:
// a reference to this object (*this) -- allows chaining together of
// these calls, (eg. strTest.ToLower().TrimLeft().ToUpper();)
__DATL_INLINE tstring& tstring::ToLower()
{
//std::transform(begin(), end(), begin(), tolower); // portable, slow way of doing it
_tcslwr(const_cast<PTSTR>(data())); // unportable, fast way of doing it
return *this;
}
// FUNCTION: tstring::CopyString
// static void CopyString(PCTSTR p_szSource, PTSTR p_szDest, int p_nMaxChars=0);
// static void CopyString(PCOSTR p_szSource, POSTR p_szDest, int p_nMaxChars=0);
// static void CopyString(PCSTR p_szSource, PWSTR p_szDest, int p_nMaxChars=0);
// static void CopyString(PCWSTR p_szSource, PSTR p_szDest, int p_nMaxChars=0);
//
// DESCRIPTION:
// These 3 overloads simplify copying one C-style string into another.
//
// PARAMETERS:
// p_szSource - the string to be copied FROM. May be either an MBCS string (char) or
// a wide string (wchar_t)
// p_szDest - the string to be copied TO. Also may be either MBCS or wide
// p_nMaxChars - the maximum number of characters to be copied into p_szDest. Note
// that this is expressed in whatever a "character" means to p_szDest.
// If p_szDest is a wchar_t type string than this will be the maximum
// number of wchar_ts that my be copied. The p_szDest string must be
// large enough to hold least p_nMaxChars+1 characters.
__DATL_INLINE void tstring::CopyString(PCTSTR p_szSource, PTSTR p_szDest, int p_nMaxChars)
{
int nSrcLen = ( p_szSource == NULL ? 0 : _tcslen(p_szSource) );
int nChars = ( p_nMaxChars > 0 ? min(p_nMaxChars,nSrcLen) : nSrcLen );
memcpy(p_szDest, p_szSource, nChars * sizeof(TCHAR));
p_szDest[nChars] = '\0';
}
__DATL_INLINE void tstring::CopyString(PCOSTR p_szSource, POSTR p_szDest, int p_nMaxChars)
{
#ifdef _UNICODE
int nSrcLen = ( p_szSource == NULL ? 0 : strlen(p_szSource) );
#else
int nSrcLen = ( p_szSource == NULL ? 0 : wcslen(p_szSource) );
#endif
int nChars = ( p_nMaxChars > 0 ? min(p_nMaxChars,nSrcLen) : nSrcLen );
memcpy(p_szDest, p_szSource, nChars * sizeof(TOTHER));
p_szDest[nChars] = '\0';
}
__DATL_INLINE void tstring::CopyString(PCSTR p_szSource, PWSTR p_szDest, int p_nMaxChars)
{
USES_CONVERSION;
PCWSTR szConverted = (A2W(p_szSource));
int nSrcLen = ( szConverted == NULL ? 0 : wcslen(szConverted) );
int nChars = ( p_nMaxChars > 0 ? min(p_nMaxChars,nSrcLen) : nSrcLen );
memcpy(p_szDest, szConverted, nChars * sizeof(wchar_t));
p_szDest[nChars] = '\0';
}
__DATL_INLINE void tstring::CopyString(PCWSTR p_szSource, PSTR p_szDest, int p_nMaxChars)
{
USES_CONVERSION;
PCSTR szConverted = (W2A(p_szSource));
int nSrcLen = ( szConverted == NULL ? 0 : strlen(szConverted) );
int nChars = ( p_nMaxChars > 0 ? min(p_nMaxChars,nSrcLen) : nSrcLen );
memcpy(p_szDest, szConverted, nChars);
p_szDest[nChars] = '\0';
}
// Special, TEMPORARY operators that allow us to serialize tstrings to CArchives.
#ifdef _MFC_VER
__DATL_INLINE CArchive& AFXAPI operator<<(CArchive& ar, const tstring& string)
{
USES_CONVERSION;
// All tstrings are serialized as wide strings
PCWSTR pWide = T2CW(string.data());
int nChars = wcslen(pWide);
ar << nChars;
ar.Write(pWide, nChars*sizeof(wchar_t));
return ar;
}
__DATL_INLINE CArchive& AFXAPI operator>>(CArchive& ar, tstring& string)
{
// All tstrings are serialized as wide strings
UINT nLen;
ar >> nLen;
if ( nLen > 0 )
{
UINT nByteLen = nLen * sizeof(wchar_t);
PWSTR pWide = (PWSTR)_alloca(nByteLen+sizeof(wchar_t));
VERIFY(ar.Read(pWide, nByteLen) == nByteLen);
pWide[nLen] = '\0';
string = tstring(pWide);
}
else
{
string.erase();
}
return ar;
}
#endif
#ifdef STDSTRING_INC_COMDEF
// FUNCTION: tstring::StreamSave
// HRESULT StreamSave(IStream* pStream) const;
//
// DESCRIPTION:
// This function write the length and contents of the tstring object
// out to an IStream as a char based string;
//
// PARAMETERS:
// pStream - the stream to which the string must be written
//
// RETURN VALUE:
// HRESULT return valued of IStream Write function
__DATL_INLINE HRESULT tstring::StreamSave(IStream* pStream) const
{
USES_CONVERSION;
HRESULT hr = E_FAIL;
ASSERT(pStream != NULL);
// All tstrings are serialized as wide strings
PCSTR pStr = T2CA(this->data());
ULONG nChars = strlen(pStr);
if ( FAILED(hr=pStream->Write(&nChars, sizeof(ULONG), NULL)) )
TRACE(_T("tstring::StreamSave -- Unable to write length to IStream due to error 0x%X\n"), hr);
else if ( FAILED(hr=pStream->Write(pStr, nChars*sizeof(char), NULL)) )
TRACE(_T("tstring::StreamSave -- Unable to write string to IStream due to error 0x%X\n"), hr);
return hr;
}
// FUNCTION: tstring::StreamLoad
// HRESULT StreamLoad(IStream* pStream);
//
// DESCRIPTION:
// This function reads in a tstring object from an IStream
//
// PARAMETERS:
// pStream - the stream from which the string must be read
//
// RETURN VALUE:
// HRESULT return value of the IStream Read function
__DATL_INLINE HRESULT tstring::StreamLoad(IStream* pStream)
{
// All tstrings are serialized as char strings
ASSERT(pStream != NULL);
ULONG nChars;
HRESULT hr = E_FAIL;
if ( FAILED(hr=pStream->Read(&nChars, sizeof(ULONG), NULL)) )
{
TRACE(_T("tstring::StreamLoad -- Unable to read length from IStream due to error 0x%X\n"), hr);
}
else if ( nChars > 0 )
{
ULONG nByteLen = nChars * sizeof(char);
PSTR pStr = (PSTR)_alloca(nByteLen+sizeof(char)); // add an extra char for terminating NULL
if ( FAILED(hr=pStream->Read(pStr, nByteLen, NULL)) )
TRACE(_T("tstring::StreamLoad -- Unable to read string from IStream due to error 0x%X\n"), hr);
pStr[nChars] = '\0';
*this = tstring(pStr);
}
else
{
this->erase();
}
return hr;
}
// FUNCTION: tstring::StreamSize
// ULONG StreamSize() const;
//
// DESCRIPTION:
// This function tells the caller how many bytes will be required to write
// this tstring object to an IStream using the StreamSave() function.
// This is the capability lacking in CComBSTR which would force an IPersistXXX
// implementation to know the implementation details of CComBSTR::StreamSave
// in order to use CComBSTR in an IPersistXXX implementation.
//
// PARAMETERS: none
// RETURN VALUE:
// length in >> bytes << required to write the tstring
__DATL_INLINE ULONG tstring::StreamSize() const
{
USES_CONVERSION;
return ( strlen(T2CA(this->data())) * sizeof(char) ) + sizeof(ULONG);
/// return ( wcslen(T2CW(this->data())) * sizeof(wchar_t) ) + sizeof(ULONG);
}
#endif
// FUNCTION: WUSysMessage
// TSTRING WUSysMessage(DWORD p_dwError, bool bUseDefault=false)
//
// DESCRIPTION:
// This function simplifies the process of obtaining a string equivalent
// of a system error code returned from GetLastError(). You simply
// supply the value returned by GetLastError() to this function and the
// corresponding system string is returned in the form of a tstring.
//
// PARAMETERS:
// p_dwError - a DWORD value representing the error code to be translated
// p_dwLangId - the language id to use. defaults to english.
//
// RETURN VALUE:
// a tstring equivalent of the error code. Currently, this function
// only returns either English of the system default language strings.
#define MAX_FMT_TRIES 5
#define FMT_BLOCK_SIZE 256
__DATL_INLINE tstring WUSysMessage(DWORD p_dwError, DWORD p_dwLangId)
{
TCHAR szBuf[512];
if ( ::FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, p_dwError, p_dwLangId, szBuf, 511, NULL) != 0 )
return WUFormat(_T("%s (0x%X)"), szBuf, p_dwError);
else
return WUFormat(_T("Unknown error (0x%X)"), p_dwError);
}
// GLOBAL FUNCTION: WUFormat
// tstring WUFormat(UINT nId, ...);
// tstring WUFormat(PCTSTR szFormat, ...);
//
// REMARKS:
// This function allows the caller for format and return a tstring object with a single line
// of code. Frequently you want to print out a formatted string but don't care about it once
// you are done with it. You end up having to create temporary tstring objects and then
// calling their Format() functions. By using this function instead, you can cut down on the
// clutter.
__DATL_INLINE tstring WUFormat(UINT nId, ...)
{
va_list argList;
va_start(argList, nId);
tstring strFmt;
tstring strOut;
if ( strFmt.Load(nId) )
strOut.FormatV(strFmt, argList);
va_end(argList);
return strOut;
}
__DATL_INLINE tstring WUFormat(PCTSTR szFormat, ...)
{
va_list argList;
va_start(argList, szFormat);
tstring strOut;
strOut.FormatV(szFormat, argList);
va_end(argList);
return strOut;
}