1054 lines
29 KiB
C
1054 lines
29 KiB
C
|
/*******************************************************************************
|
||
|
*
|
||
|
* (C) COPYRIGHT MICROSOFT CORPORATION, 1998
|
||
|
*
|
||
|
* TITLE: SIMSTR.H
|
||
|
*
|
||
|
* VERSION: 1.0
|
||
|
*
|
||
|
* AUTHOR: ShaunIv
|
||
|
*
|
||
|
* DATE: 5/12/1998
|
||
|
*
|
||
|
* DESCRIPTION: Simple string classes
|
||
|
*
|
||
|
*******************************************************************************/
|
||
|
#ifndef _SIMSTR_H_INCLUDED
|
||
|
#define _SIMSTR_H_INCLUDED
|
||
|
|
||
|
/*
|
||
|
* Simple string class.
|
||
|
*
|
||
|
* Template class:
|
||
|
* CSimpleStringBase<T>
|
||
|
* Implementations:
|
||
|
* CSimpleStringBase<wchar_t> CSimpleStringWide
|
||
|
* CSimpleStringBase<char> CSimpleStringAnsi
|
||
|
* CSimpleString = CSimpleString[Ansi|Wide] depending on UNICODE macro
|
||
|
* Inline functions:
|
||
|
* CSimpleStringAnsi CSimpleStringConvert::AnsiString(T n)
|
||
|
* CSimpleStringWide CSimpleStringConvert::WideString(T n)
|
||
|
* CSimpleString CSimpleStringConvert::NaturalString(T n)
|
||
|
* Macros:
|
||
|
* IS_CHAR(T)
|
||
|
* IS_WCHAR(T)
|
||
|
*/
|
||
|
|
||
|
#include <windows.h>
|
||
|
#include <stdarg.h>
|
||
|
#include <stdio.h>
|
||
|
#include <tchar.h>
|
||
|
|
||
|
#define IS_CHAR(x) (sizeof(x)==sizeof(char))
|
||
|
#define IS_WCHAR(x) (sizeof(x)==sizeof(wchar_t))
|
||
|
|
||
|
#ifndef ARRAYSIZE
|
||
|
#define ARRAYSIZE(x) (sizeof(x) / sizeof(x[0]))
|
||
|
#endif
|
||
|
|
||
|
template <class T>
|
||
|
class CSimpleStringBase
|
||
|
{
|
||
|
private:
|
||
|
enum
|
||
|
{
|
||
|
m_nDefaultGranularity = 16, // Default number of extra characters to allocate when we have to grow
|
||
|
m_nMaxLoadStringBuffer = 1024, // Maximum length of .RC string
|
||
|
m_nMaxAutoDataLength = 128 // Length of non-dynamically allocated string
|
||
|
};
|
||
|
|
||
|
private:
|
||
|
T *m_pstrData;
|
||
|
T m_pstrAutoData[m_nMaxAutoDataLength];
|
||
|
UINT m_nMaxSize;
|
||
|
UINT m_nGranularity;
|
||
|
|
||
|
private:
|
||
|
static int Min( int a, int b )
|
||
|
{
|
||
|
return((a < b) ? a : b);
|
||
|
}
|
||
|
|
||
|
public:
|
||
|
// Replacements (in some cases just wrappers) for strlen, strcpy, ...
|
||
|
static inline T *GenericCopy( T *pstrTgtStr, const T *pstrSrcStr );
|
||
|
static inline T *GenericCopyLength( T *pstrTgtStr, const T *pstrSrcStr, UINT nSize );
|
||
|
static inline UINT GenericLength( const T *pstrStr );
|
||
|
static inline T *GenericConcatenate( T *pstrTgtStr, const T *pstrSrcStr );
|
||
|
static inline int GenericCompare( const T *pstrTgtStr, const T *pstrSrcStr );
|
||
|
static inline int GenericCompareNoCase( const T *pstrStrA, const T *pstrStrB );
|
||
|
static inline int GenericCompareLength( const T *pstrTgtStr, const T *pstrSrcStr, UINT nLength );
|
||
|
static inline T *GenericCharNext( const T *pszStr );
|
||
|
|
||
|
public:
|
||
|
// Constructors and destructor
|
||
|
CSimpleStringBase( void );
|
||
|
CSimpleStringBase( const CSimpleStringBase & );
|
||
|
CSimpleStringBase( const T *szStr );
|
||
|
CSimpleStringBase( T ch );
|
||
|
CSimpleStringBase( UINT nResId, HMODULE hModule );
|
||
|
virtual ~CSimpleStringBase(void);
|
||
|
|
||
|
bool EnsureLength( UINT nMaxSize );
|
||
|
UINT Length(void) const;
|
||
|
void Concat( const CSimpleStringBase &other );
|
||
|
int Resize(void);
|
||
|
UINT Truncate( UINT nLen );
|
||
|
bool Assign( const T *szStr );
|
||
|
bool Assign( const CSimpleStringBase & );
|
||
|
void DeleteStorage(void);
|
||
|
static inline T *CreateStorage( UINT nCount );
|
||
|
void Destroy(void);
|
||
|
|
||
|
CSimpleStringBase &Format( const T *strFmt, ... );
|
||
|
CSimpleStringBase &Format( int nResId, HINSTANCE hInst, ... );
|
||
|
|
||
|
// Handy Win32 wrappers
|
||
|
CSimpleStringBase &GetWindowText( HWND hWnd );
|
||
|
bool SetWindowText( HWND hWnd );
|
||
|
bool LoadString( UINT nResId, HMODULE hModule );
|
||
|
bool Load( HKEY hRegKey, const T *pszValueName, const T *pszDefault=NULL );
|
||
|
bool Store( HKEY hRegKey, const T *pszValueName, DWORD nType = REG_SZ );
|
||
|
void SetAt( UINT nIndex, T chValue );
|
||
|
CSimpleStringBase &operator=( const CSimpleStringBase &other );
|
||
|
CSimpleStringBase &operator=( const T *other );
|
||
|
CSimpleStringBase &operator+=( const CSimpleStringBase &other );
|
||
|
CSimpleStringBase operator+( const CSimpleStringBase &other ) const;
|
||
|
T &operator[](int index);
|
||
|
const T &operator[](int index) const;
|
||
|
|
||
|
CSimpleStringBase ToUpper(void) const;
|
||
|
CSimpleStringBase ToLower(void) const;
|
||
|
|
||
|
CSimpleStringBase &MakeUpper(void);
|
||
|
CSimpleStringBase &MakeLower(void);
|
||
|
CSimpleStringBase &TrimRight(void);
|
||
|
CSimpleStringBase &TrimLeft(void);
|
||
|
CSimpleStringBase &Trim(void);
|
||
|
CSimpleStringBase &Reverse(void);
|
||
|
|
||
|
int Find( T cChar ) const;
|
||
|
int Find( const CSimpleStringBase &other, UINT nStart=0 ) const;
|
||
|
int ReverseFind( T cChar ) const;
|
||
|
int ReverseFind( const CSimpleStringBase &other ) const;
|
||
|
|
||
|
CSimpleStringBase SubStr( int nStart, int nCount=-1 ) const;
|
||
|
int CompareNoCase( const CSimpleStringBase &other, int nLength=-1 ) const;
|
||
|
int Compare( const CSimpleStringBase &other, int nLength=-1 ) const;
|
||
|
bool MatchLastCharacter( T cChar ) const;
|
||
|
|
||
|
// Some useful inlines
|
||
|
UINT Granularity( UINT nGranularity ) { if (nGranularity>0) m_nGranularity = nGranularity;return m_nGranularity;}
|
||
|
UINT Granularity( void ) const { return m_nGranularity;}
|
||
|
CSimpleStringBase Left( int nCount ) const { return SubStr( 0, nCount );}
|
||
|
CSimpleStringBase Right( int nCount ) const { return SubStr( max(0,(int)Length()-nCount), -1 );}
|
||
|
bool operator<( const CSimpleStringBase &other ) const { return(Compare(other) < 0);}
|
||
|
bool operator<=( const CSimpleStringBase &other ) const { return(Compare(other) <= 0);}
|
||
|
bool operator==( const CSimpleStringBase &other ) const { return(Compare(other) == 0);}
|
||
|
bool operator!=( const CSimpleStringBase &other ) const { return(Compare(other) != 0);}
|
||
|
bool operator>=( const CSimpleStringBase &other ) const { return(Compare(other) >= 0);}
|
||
|
bool operator>( const CSimpleStringBase &other ) const { return(Compare(other) > 0);}
|
||
|
const T *String(void) const { return m_pstrData;}
|
||
|
operator const T *(void) const { return String();}
|
||
|
bool IsValid(void) const { return(NULL != m_pstrData);}
|
||
|
};
|
||
|
|
||
|
|
||
|
template <class T>
|
||
|
inline T *CSimpleStringBase<T>::GenericCopy( T *pszDest, const T *pszSource )
|
||
|
{
|
||
|
T *pCurr = pszDest;
|
||
|
while (*pCurr++ = *pszSource++)
|
||
|
;
|
||
|
return(pszDest);
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
inline T *CSimpleStringBase<T>::GenericCharNext( const T *pszStr )
|
||
|
{
|
||
|
if (IS_CHAR(*pszStr))
|
||
|
return (T*)CharNextA((LPCSTR)pszStr);
|
||
|
else if (!*pszStr)
|
||
|
return (T*)pszStr;
|
||
|
else return (T*)((LPWSTR)pszStr + 1);
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
inline T *CSimpleStringBase<T>::GenericCopyLength( T *pszDest, const T *source, UINT count )
|
||
|
{
|
||
|
T *start = pszDest;
|
||
|
while (count && (*pszDest++ = *source++))
|
||
|
count--;
|
||
|
if (count)
|
||
|
while (--count)
|
||
|
*pszDest++ = 0;
|
||
|
return(start);
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
inline UINT CSimpleStringBase<T>::GenericLength( const T *pszString )
|
||
|
{
|
||
|
const T *eos = pszString;
|
||
|
|
||
|
while (*eos++)
|
||
|
;
|
||
|
return((UINT)(eos - pszString - 1));
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
inline T*CSimpleStringBase<T>::GenericConcatenate( T *pszDest, const T *pszSource )
|
||
|
{
|
||
|
T *pCurr = pszDest;
|
||
|
|
||
|
while (*pCurr)
|
||
|
pCurr++;
|
||
|
|
||
|
while (*pCurr++ = *pszSource++)
|
||
|
;
|
||
|
|
||
|
return( pszDest );
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
inline int CSimpleStringBase<T>::GenericCompare( const T *pszSource, const T *pszDest )
|
||
|
{
|
||
|
#if defined(DBG) && !defined(UNICODE) && !defined(_UNICODE)
|
||
|
if (sizeof(T) == sizeof(wchar_t))
|
||
|
{
|
||
|
OutputDebugString(TEXT("CompareStringW is not supported under win9x, so this call is going to fail!"));
|
||
|
}
|
||
|
#endif
|
||
|
int nRes = IS_CHAR(*pszSource) ?
|
||
|
CompareStringA( LOCALE_USER_DEFAULT, 0, (LPCSTR)pszSource, -1, (LPCSTR)pszDest, -1 ) :
|
||
|
CompareStringW( LOCALE_USER_DEFAULT, 0, (LPCWSTR)pszSource, -1, (LPCWSTR)pszDest, -1 );
|
||
|
switch (nRes)
|
||
|
{
|
||
|
case CSTR_LESS_THAN:
|
||
|
return -1;
|
||
|
case CSTR_GREATER_THAN:
|
||
|
return 1;
|
||
|
default:
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
template <class T>
|
||
|
inline int CSimpleStringBase<T>::GenericCompareNoCase( const T *pszSource, const T *pszDest )
|
||
|
{
|
||
|
#if defined(DBG) && !defined(UNICODE) && !defined(_UNICODE)
|
||
|
if (sizeof(T) == sizeof(wchar_t))
|
||
|
{
|
||
|
OutputDebugString(TEXT("CompareStringW is not supported under win9x, so this call is going to fail!"));
|
||
|
}
|
||
|
#endif
|
||
|
int nRes = IS_CHAR(*pszSource) ?
|
||
|
CompareStringA( LOCALE_USER_DEFAULT, NORM_IGNORECASE, (LPCSTR)pszSource, -1, (LPCSTR)pszDest, -1 ) :
|
||
|
CompareStringW( LOCALE_USER_DEFAULT, NORM_IGNORECASE, (LPCWSTR)pszSource, -1, (LPCWSTR)pszDest, -1 );
|
||
|
switch (nRes)
|
||
|
{
|
||
|
case CSTR_LESS_THAN:
|
||
|
return -1;
|
||
|
case CSTR_GREATER_THAN:
|
||
|
return 1;
|
||
|
default:
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
inline int CSimpleStringBase<T>::GenericCompareLength( const T *pszStringA, const T *pszStringB, UINT nLength )
|
||
|
{
|
||
|
#if defined(DBG) && !defined(UNICODE) && !defined(_UNICODE)
|
||
|
if (sizeof(T) == sizeof(wchar_t))
|
||
|
{
|
||
|
OutputDebugString(TEXT("CompareStringW is not supported under win9x, so this call is going to fail!"));
|
||
|
}
|
||
|
#endif
|
||
|
if (!nLength)
|
||
|
return(0);
|
||
|
int nRes = IS_CHAR(*pszStringA) ?
|
||
|
CompareStringA( LOCALE_USER_DEFAULT, 0, (LPCSTR)pszStringA, Min(nLength,CSimpleStringBase<CHAR>::GenericLength((LPCSTR)pszStringA)), (LPCSTR)pszStringB, Min(nLength,CSimpleStringBase<CHAR>::GenericLength((LPCSTR)pszStringB)) ) :
|
||
|
CompareStringW( LOCALE_USER_DEFAULT, 0, (LPWSTR)pszStringA, Min(nLength,CSimpleStringBase<WCHAR>::GenericLength((LPCWSTR)pszStringA)), (LPCWSTR)pszStringB, Min(nLength,CSimpleStringBase<WCHAR>::GenericLength((LPCWSTR)pszStringB)) );
|
||
|
switch (nRes)
|
||
|
{
|
||
|
case CSTR_LESS_THAN:
|
||
|
return -1;
|
||
|
case CSTR_GREATER_THAN:
|
||
|
return 1;
|
||
|
default:
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
bool CSimpleStringBase<T>::EnsureLength( UINT nMaxSize )
|
||
|
{
|
||
|
//
|
||
|
// If the string is already long enough, just return true
|
||
|
//
|
||
|
if (m_nMaxSize >= nMaxSize)
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Save the old max size
|
||
|
//
|
||
|
UINT nOldMaxSize = m_nMaxSize;
|
||
|
|
||
|
//
|
||
|
// Get the new size
|
||
|
//
|
||
|
UINT nNewMaxSize = nMaxSize + m_nGranularity;
|
||
|
|
||
|
//
|
||
|
// Allocate the new buffer
|
||
|
//
|
||
|
T *pszTmp = CreateStorage(nNewMaxSize);
|
||
|
|
||
|
//
|
||
|
// Make sure the allocation succeded
|
||
|
//
|
||
|
if (pszTmp)
|
||
|
{
|
||
|
//
|
||
|
// If we have an existing string, copy it and delete it
|
||
|
//
|
||
|
if (m_pstrData)
|
||
|
{
|
||
|
GenericCopy(pszTmp,m_pstrData);
|
||
|
DeleteStorage();
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Save the new max size
|
||
|
//
|
||
|
m_nMaxSize = nNewMaxSize;
|
||
|
|
||
|
//
|
||
|
// Save this new string
|
||
|
//
|
||
|
m_pstrData = pszTmp;
|
||
|
|
||
|
//
|
||
|
// Return success
|
||
|
//
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Couldn't allocate memory
|
||
|
//
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
CSimpleStringBase<T> &CSimpleStringBase<T>::GetWindowText( HWND hWnd )
|
||
|
{
|
||
|
Destroy();
|
||
|
// Assume it didn't work
|
||
|
bool bSuccess = false;
|
||
|
int nLen = ::GetWindowTextLength(hWnd);
|
||
|
if (nLen)
|
||
|
{
|
||
|
if (EnsureLength(nLen+1))
|
||
|
{
|
||
|
if (::GetWindowText( hWnd, m_pstrData, (nLen+1) ))
|
||
|
{
|
||
|
bSuccess = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (!bSuccess)
|
||
|
Destroy();
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
bool CSimpleStringBase<T>::SetWindowText( HWND hWnd )
|
||
|
{
|
||
|
return(::SetWindowText( hWnd, String() ) != FALSE);
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
UINT CSimpleStringBase<T>::Truncate( UINT nLen )
|
||
|
{
|
||
|
if (Length() < nLen)
|
||
|
return Length();
|
||
|
if (!nLen)
|
||
|
return 0;
|
||
|
m_pstrData[nLen-1] = 0;
|
||
|
Resize();
|
||
|
return Length();
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
int CSimpleStringBase<T>::Resize(void)
|
||
|
{
|
||
|
m_nMaxSize = m_pstrData ? GenericLength(m_pstrData) : 0;
|
||
|
++m_nMaxSize;
|
||
|
T *pszTmp = CreateStorage(m_nMaxSize);
|
||
|
if (pszTmp)
|
||
|
{
|
||
|
if (m_pstrData)
|
||
|
{
|
||
|
GenericCopy(pszTmp,m_pstrData);
|
||
|
DeleteStorage();
|
||
|
}
|
||
|
else *pszTmp = 0;
|
||
|
m_pstrData = pszTmp;
|
||
|
}
|
||
|
return Length();
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
CSimpleStringBase<T>::CSimpleStringBase(void)
|
||
|
: m_pstrData(m_pstrAutoData),m_nMaxSize(ARRAYSIZE(m_pstrAutoData)),m_nGranularity(m_nDefaultGranularity)
|
||
|
{
|
||
|
m_pstrAutoData[0] = 0;
|
||
|
T szTmp[1] = { 0};
|
||
|
Assign(szTmp);
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
CSimpleStringBase<T>::CSimpleStringBase( const CSimpleStringBase &other )
|
||
|
: m_pstrData(m_pstrAutoData),m_nMaxSize(ARRAYSIZE(m_pstrAutoData)),m_nGranularity(m_nDefaultGranularity)
|
||
|
{
|
||
|
m_pstrAutoData[0] = 0;
|
||
|
Assign(other.String());
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
CSimpleStringBase<T>::CSimpleStringBase( const T *szStr )
|
||
|
: m_pstrData(m_pstrAutoData),m_nMaxSize(ARRAYSIZE(m_pstrAutoData)),m_nGranularity(m_nDefaultGranularity)
|
||
|
{
|
||
|
m_pstrAutoData[0] = 0;
|
||
|
Assign(szStr);
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
CSimpleStringBase<T>::CSimpleStringBase( T ch )
|
||
|
: m_pstrData(m_pstrAutoData),m_nMaxSize(ARRAYSIZE(m_pstrAutoData)),m_nGranularity(m_nDefaultGranularity)
|
||
|
{
|
||
|
m_pstrAutoData[0] = 0;
|
||
|
T szTmp[2];
|
||
|
szTmp[0] = ch;
|
||
|
szTmp[1] = 0;
|
||
|
Assign(szTmp);
|
||
|
}
|
||
|
|
||
|
|
||
|
template <class T>
|
||
|
CSimpleStringBase<T>::CSimpleStringBase( UINT nResId, HMODULE hModule )
|
||
|
: m_pstrData(m_pstrAutoData),m_nMaxSize(ARRAYSIZE(m_pstrAutoData)),m_nGranularity(m_nDefaultGranularity)
|
||
|
{
|
||
|
m_pstrAutoData[0] = 0;
|
||
|
LoadString( nResId, hModule );
|
||
|
}
|
||
|
|
||
|
|
||
|
template <class T>
|
||
|
CSimpleStringBase<T> &CSimpleStringBase<T>::Format( const T *strFmt, ... )
|
||
|
{
|
||
|
T szTmp[1024];
|
||
|
va_list arglist;
|
||
|
|
||
|
va_start(arglist, strFmt);
|
||
|
int nRet = IS_CHAR(*m_pstrData) ?
|
||
|
_vsnprintf((LPSTR)szTmp, ARRAYSIZE(szTmp), (LPCSTR)strFmt, arglist) :
|
||
|
_vsnwprintf((LPWSTR)szTmp, ARRAYSIZE(szTmp), (LPCWSTR)strFmt, arglist);
|
||
|
va_end(arglist);
|
||
|
Assign(szTmp);
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
CSimpleStringBase<T> &CSimpleStringBase<T>::Format( int nResId, HINSTANCE hInst, ... )
|
||
|
{
|
||
|
CSimpleStringBase<T> strFmt;
|
||
|
va_list arglist;
|
||
|
if (strFmt.LoadString(nResId,hInst))
|
||
|
{
|
||
|
T szTmp[1024];
|
||
|
va_start(arglist, hInst);
|
||
|
int nRet = IS_CHAR(*m_pstrData) ?
|
||
|
_vsnprintf((LPSTR)szTmp, ARRAYSIZE(szTmp), (LPCSTR)strFmt.String(), arglist) :
|
||
|
_vsnwprintf((LPWSTR)szTmp, ARRAYSIZE(szTmp), (LPCWSTR)strFmt.String(), arglist);
|
||
|
va_end(arglist);
|
||
|
Assign(szTmp);
|
||
|
}
|
||
|
else Assign(NULL);
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
bool CSimpleStringBase<T>::LoadString( UINT nResId, HMODULE hModule )
|
||
|
{
|
||
|
if (!hModule)
|
||
|
hModule = GetModuleHandle(NULL);
|
||
|
T szTmp[m_nMaxLoadStringBuffer];
|
||
|
int nRet = IS_CHAR(*m_pstrData) ?
|
||
|
::LoadStringA( hModule, nResId, (LPSTR)szTmp, ARRAYSIZE(szTmp)) :
|
||
|
::LoadStringW( hModule, nResId, (LPWSTR)szTmp, ARRAYSIZE(szTmp));
|
||
|
if (nRet)
|
||
|
return Assign(szTmp);
|
||
|
else return Assign(NULL);
|
||
|
}
|
||
|
|
||
|
|
||
|
template <class T>
|
||
|
CSimpleStringBase<T>::~CSimpleStringBase(void)
|
||
|
{
|
||
|
Destroy();
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
void CSimpleStringBase<T>::DeleteStorage(void)
|
||
|
{
|
||
|
//
|
||
|
// Only delete the string if it is non-NULL and not pointing to our non-dynamically allocated buffer
|
||
|
//
|
||
|
if (m_pstrData && m_pstrData != m_pstrAutoData)
|
||
|
{
|
||
|
delete[] m_pstrData;
|
||
|
}
|
||
|
m_pstrData = NULL;
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
T *CSimpleStringBase<T>::CreateStorage( UINT nCount )
|
||
|
{
|
||
|
return new T[nCount];
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
void CSimpleStringBase<T>::Destroy(void)
|
||
|
{
|
||
|
DeleteStorage();
|
||
|
m_nMaxSize = 0;
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
UINT CSimpleStringBase<T>::Length(void) const
|
||
|
{
|
||
|
return(m_pstrData ? GenericLength(m_pstrData) : 0);
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
CSimpleStringBase<T> &CSimpleStringBase<T>::operator=( const CSimpleStringBase &other )
|
||
|
{
|
||
|
if (&other != this)
|
||
|
{
|
||
|
Assign(other.String());
|
||
|
}
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
CSimpleStringBase<T> &CSimpleStringBase<T>::operator=( const T *other )
|
||
|
{
|
||
|
if (other != String())
|
||
|
{
|
||
|
Assign(other);
|
||
|
}
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
CSimpleStringBase<T> &CSimpleStringBase<T>::operator+=( const CSimpleStringBase &other )
|
||
|
{
|
||
|
Concat(other.String());
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
CSimpleStringBase<T> CSimpleStringBase<T>::operator+( const CSimpleStringBase &other ) const
|
||
|
{
|
||
|
CSimpleStringBase tmp(*this);
|
||
|
tmp.Concat(other);
|
||
|
return tmp;
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
bool CSimpleStringBase<T>::Assign( const T *szStr )
|
||
|
{
|
||
|
if (szStr && EnsureLength(GenericLength(szStr)+1))
|
||
|
{
|
||
|
GenericCopy(m_pstrData,szStr);
|
||
|
}
|
||
|
else if (EnsureLength(1))
|
||
|
{
|
||
|
*m_pstrData = 0;
|
||
|
}
|
||
|
else Destroy();
|
||
|
return(NULL != m_pstrData);
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
bool CSimpleStringBase<T>::Assign( const CSimpleStringBase &other )
|
||
|
{
|
||
|
return Assign( other.String() );
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
void CSimpleStringBase<T>::SetAt( UINT nIndex, T chValue )
|
||
|
{
|
||
|
//
|
||
|
// Make sure we don't go off the end of the string or overwrite the '\0'
|
||
|
//
|
||
|
if (m_pstrData && Length() > nIndex)
|
||
|
{
|
||
|
m_pstrData[nIndex] = chValue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
template <class T>
|
||
|
void CSimpleStringBase<T>::Concat( const CSimpleStringBase &other )
|
||
|
{
|
||
|
if (EnsureLength( Length() + other.Length() + 1 ))
|
||
|
{
|
||
|
GenericConcatenate(m_pstrData,other.String());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
CSimpleStringBase<T> &CSimpleStringBase<T>::MakeUpper(void)
|
||
|
{
|
||
|
//
|
||
|
// Make sure the string is not NULL
|
||
|
//
|
||
|
if (m_pstrData)
|
||
|
{
|
||
|
IS_CHAR(*m_pstrData) ? CharUpperBuffA( (LPSTR)m_pstrData, Length() ) : CharUpperBuffW( (LPWSTR)m_pstrData, Length() );
|
||
|
}
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
CSimpleStringBase<T> &CSimpleStringBase<T>::MakeLower(void)
|
||
|
{
|
||
|
//
|
||
|
// Make sure the string is not NULL
|
||
|
//
|
||
|
if (m_pstrData)
|
||
|
{
|
||
|
IS_CHAR(*m_pstrData) ? CharLowerBuffA( (LPSTR)m_pstrData, Length() ) : CharLowerBuffW( (LPWSTR)m_pstrData, Length() );
|
||
|
}
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
CSimpleStringBase<T> CSimpleStringBase<T>::ToUpper(void) const
|
||
|
{
|
||
|
CSimpleStringBase str(*this);
|
||
|
str.MakeUpper();
|
||
|
return str;
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
CSimpleStringBase<T> CSimpleStringBase<T>::ToLower(void) const
|
||
|
{
|
||
|
CSimpleStringBase str(*this);
|
||
|
str.MakeLower();
|
||
|
return str;
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
T &CSimpleStringBase<T>::operator[](int nIndex)
|
||
|
{
|
||
|
return m_pstrData[nIndex];
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
const T &CSimpleStringBase<T>::operator[](int index) const
|
||
|
{
|
||
|
return m_pstrData[index];
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
CSimpleStringBase<T> &CSimpleStringBase<T>::TrimRight(void)
|
||
|
{
|
||
|
T *pFirstWhitespaceCharacterInSequence = NULL;
|
||
|
bool bInWhiteSpace = false;
|
||
|
T *pszPtr = m_pstrData;
|
||
|
while (pszPtr && *pszPtr)
|
||
|
{
|
||
|
if (*pszPtr == L' ' || *pszPtr == L'\t' || *pszPtr == L'\n' || *pszPtr == L'\r')
|
||
|
{
|
||
|
if (!bInWhiteSpace)
|
||
|
{
|
||
|
pFirstWhitespaceCharacterInSequence = pszPtr;
|
||
|
bInWhiteSpace = true;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
bInWhiteSpace = false;
|
||
|
}
|
||
|
pszPtr = GenericCharNext(pszPtr);
|
||
|
}
|
||
|
if (pFirstWhitespaceCharacterInSequence && bInWhiteSpace)
|
||
|
*pFirstWhitespaceCharacterInSequence = 0;
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
CSimpleStringBase<T> &CSimpleStringBase<T>::TrimLeft(void)
|
||
|
{
|
||
|
T *pszPtr = m_pstrData;
|
||
|
while (pszPtr && *pszPtr)
|
||
|
{
|
||
|
if (*pszPtr == L' ' || *pszPtr == L'\t' || *pszPtr == L'\n' || *pszPtr == L'\r')
|
||
|
{
|
||
|
pszPtr = GenericCharNext(pszPtr);
|
||
|
}
|
||
|
else break;
|
||
|
}
|
||
|
Assign(CSimpleStringBase<T>(pszPtr).String());
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
inline CSimpleStringBase<T> &CSimpleStringBase<T>::Trim(void)
|
||
|
{
|
||
|
TrimLeft();
|
||
|
TrimRight();
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Note that this function WILL NOT WORK CORRECTLY for multi-byte characters in ANSI strings
|
||
|
//
|
||
|
template <class T>
|
||
|
CSimpleStringBase<T> &CSimpleStringBase<T>::Reverse(void)
|
||
|
{
|
||
|
UINT nLen = Length();
|
||
|
for (UINT i = 0;i<nLen/2;i++)
|
||
|
{
|
||
|
T tmp = m_pstrData[i];
|
||
|
m_pstrData[i] = m_pstrData[nLen-i-1];
|
||
|
m_pstrData[nLen-i-1] = tmp;
|
||
|
}
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
int CSimpleStringBase<T>::Find( T cChar ) const
|
||
|
{
|
||
|
T strTemp[2] = { cChar, 0};
|
||
|
return Find(strTemp);
|
||
|
}
|
||
|
|
||
|
|
||
|
template <class T>
|
||
|
int CSimpleStringBase<T>::Find( const CSimpleStringBase &other, UINT nStart ) const
|
||
|
{
|
||
|
if (!m_pstrData)
|
||
|
return -1;
|
||
|
if (nStart > Length())
|
||
|
return -1;
|
||
|
T *pstrCurr = m_pstrData+nStart, *pstrSrc, *pstrSubStr;
|
||
|
while (*pstrCurr)
|
||
|
{
|
||
|
pstrSrc = pstrCurr;
|
||
|
pstrSubStr = (T *)other.String();
|
||
|
while (*pstrSrc && *pstrSubStr && *pstrSrc == *pstrSubStr)
|
||
|
{
|
||
|
pstrSrc = GenericCharNext(pstrSrc);
|
||
|
pstrSubStr = GenericCharNext(pstrSubStr);
|
||
|
}
|
||
|
if (!*pstrSubStr)
|
||
|
return static_cast<int>(pstrCurr-m_pstrData);
|
||
|
pstrCurr = GenericCharNext(pstrCurr);
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
int CSimpleStringBase<T>::ReverseFind( T cChar ) const
|
||
|
{
|
||
|
T strTemp[2] = { cChar, 0};
|
||
|
return ReverseFind(strTemp);
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
int CSimpleStringBase<T>::ReverseFind( const CSimpleStringBase &srcStr ) const
|
||
|
{
|
||
|
int nLastFind = -1, nFind=0;
|
||
|
while ((nFind = Find( srcStr, nFind )) >= 0)
|
||
|
{
|
||
|
nLastFind = nFind;
|
||
|
++nFind;
|
||
|
}
|
||
|
return nLastFind;
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
CSimpleStringBase<T> CSimpleStringBase<T>::SubStr( int nStart, int nCount ) const
|
||
|
{
|
||
|
if (nStart >= (int)Length() || nStart < 0)
|
||
|
return CSimpleStringBase<T>();
|
||
|
if (nCount < 0)
|
||
|
nCount = Length() - nStart;
|
||
|
CSimpleStringBase<T> strTmp;
|
||
|
T *pszTmp = CreateStorage(nCount+1);
|
||
|
if (pszTmp)
|
||
|
{
|
||
|
GenericCopyLength( pszTmp, m_pstrData+nStart, nCount+1 );
|
||
|
pszTmp[nCount] = 0;
|
||
|
strTmp = pszTmp;
|
||
|
delete[] pszTmp;
|
||
|
}
|
||
|
return strTmp;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
template <class T>
|
||
|
int CSimpleStringBase<T>::CompareNoCase( const CSimpleStringBase &other, int nLength ) const
|
||
|
{
|
||
|
if (nLength < 0)
|
||
|
{
|
||
|
//
|
||
|
// Make sure both strings are non-NULL
|
||
|
//
|
||
|
if (!String() && !other.String())
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
else if (!String())
|
||
|
{
|
||
|
return -1;
|
||
|
}
|
||
|
else if (!other.String())
|
||
|
{
|
||
|
return 1;
|
||
|
}
|
||
|
else return GenericCompareNoCase(m_pstrData,other.String());
|
||
|
}
|
||
|
CSimpleStringBase<T> strSrc(*this);
|
||
|
CSimpleStringBase<T> strTgt(other);
|
||
|
strSrc.MakeUpper();
|
||
|
strTgt.MakeUpper();
|
||
|
//
|
||
|
// Make sure both strings are non-NULL
|
||
|
//
|
||
|
if (!strSrc.String() && !strTgt.String())
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
else if (!strSrc.String())
|
||
|
{
|
||
|
return -1;
|
||
|
}
|
||
|
else if (!strTgt.String())
|
||
|
{
|
||
|
return 1;
|
||
|
}
|
||
|
else return GenericCompareLength(strSrc.String(),strTgt.String(),nLength);
|
||
|
}
|
||
|
|
||
|
|
||
|
template <class T>
|
||
|
int CSimpleStringBase<T>::Compare( const CSimpleStringBase &other, int nLength ) const
|
||
|
{
|
||
|
//
|
||
|
// Make sure both strings are non-NULL
|
||
|
//
|
||
|
if (!String() && !other.String())
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
else if (!String())
|
||
|
{
|
||
|
return -1;
|
||
|
}
|
||
|
else if (!other.String())
|
||
|
{
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
if (nLength < 0)
|
||
|
{
|
||
|
return GenericCompare(String(),other.String());
|
||
|
}
|
||
|
return GenericCompareLength(String(),other.String(),nLength);
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
bool CSimpleStringBase<T>::MatchLastCharacter( T cChar ) const
|
||
|
{
|
||
|
int nFind = ReverseFind(cChar);
|
||
|
if (nFind < 0)
|
||
|
return false;
|
||
|
if (nFind == (int)Length()-1)
|
||
|
return true;
|
||
|
else return false;
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
bool CSimpleStringBase<T>::Load( HKEY hRegKey, const T *pszValueName, const T *pszDefault )
|
||
|
{
|
||
|
bool bResult = false;
|
||
|
Assign(pszDefault);
|
||
|
DWORD nType=0;
|
||
|
DWORD nSize=0;
|
||
|
LONG nRet;
|
||
|
if (IS_CHAR(*m_pstrData))
|
||
|
nRet = RegQueryValueExA( hRegKey, (LPCSTR)pszValueName, NULL, &nType, NULL, &nSize);
|
||
|
else nRet = RegQueryValueExW( hRegKey, (LPCWSTR)pszValueName, NULL, &nType, NULL, &nSize);
|
||
|
if (ERROR_SUCCESS == nRet)
|
||
|
{
|
||
|
if ((nType == REG_SZ) || (nType == REG_EXPAND_SZ))
|
||
|
{
|
||
|
// Round up to the nearest 2
|
||
|
nSize = ((nSize + 1) & 0xFFFFFFFE);
|
||
|
T *pstrTemp = CreateStorage(nSize / sizeof(T));
|
||
|
if (pstrTemp)
|
||
|
{
|
||
|
if (IS_CHAR(*m_pstrData))
|
||
|
nRet = RegQueryValueExA( hRegKey, (LPCSTR)pszValueName, NULL, &nType, (PBYTE)pstrTemp, &nSize);
|
||
|
else nRet = RegQueryValueExW( hRegKey, (LPCWSTR)pszValueName, NULL, &nType, (PBYTE)pstrTemp, &nSize);
|
||
|
if (ERROR_SUCCESS == nRet)
|
||
|
{
|
||
|
Assign(pstrTemp);
|
||
|
bResult = true;
|
||
|
}
|
||
|
delete pstrTemp;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return bResult;
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
bool CSimpleStringBase<T>::Store( HKEY hRegKey, const T *pszValueName, DWORD nType )
|
||
|
{
|
||
|
bool bResult = false;
|
||
|
long nRet;
|
||
|
if (Length())
|
||
|
{
|
||
|
if (IS_CHAR(*m_pstrData))
|
||
|
nRet = RegSetValueExA( hRegKey, (LPCSTR)pszValueName, 0, nType, (PBYTE)m_pstrData, sizeof(*m_pstrData)*(Length()+1) );
|
||
|
else nRet = RegSetValueExW( hRegKey, (LPCWSTR)pszValueName, 0, nType, (PBYTE)m_pstrData, sizeof(*m_pstrData)*(Length()+1) );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
T strBlank = 0;
|
||
|
if (IS_CHAR(*m_pstrData))
|
||
|
nRet = RegSetValueExA( hRegKey, (LPCSTR)pszValueName, 0, nType, (PBYTE)&strBlank, sizeof(T) );
|
||
|
else nRet = RegSetValueExW( hRegKey, (LPCWSTR)pszValueName, 0, nType, (PBYTE)&strBlank, sizeof(T) );
|
||
|
}
|
||
|
return(ERROR_SUCCESS == nRet);
|
||
|
}
|
||
|
|
||
|
|
||
|
typedef CSimpleStringBase<char> CSimpleStringAnsi;
|
||
|
typedef CSimpleStringBase<wchar_t> CSimpleStringWide;
|
||
|
|
||
|
#if defined(UNICODE) || defined(_UNICODE)
|
||
|
#define CSimpleString CSimpleStringWide
|
||
|
#else
|
||
|
#define CSimpleString CSimpleStringAnsi
|
||
|
#endif
|
||
|
|
||
|
namespace CSimpleStringConvert
|
||
|
{
|
||
|
template <class T>
|
||
|
CSimpleStringWide WideString(const T &str)
|
||
|
{
|
||
|
if (IS_WCHAR(str[0]))
|
||
|
return CSimpleStringWide((LPCWSTR)str.String());
|
||
|
else
|
||
|
{
|
||
|
if (!str.Length())
|
||
|
return CSimpleStringWide(L"");
|
||
|
int iLen = MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, (LPCSTR)str.String(), str.Length()+1, NULL, 0 );
|
||
|
CSimpleStringWide sswTmp;
|
||
|
LPWSTR pwszTmp = new WCHAR[iLen];
|
||
|
if (pwszTmp)
|
||
|
{
|
||
|
MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, (LPCSTR)str.String(), str.Length()+1, pwszTmp, iLen );
|
||
|
sswTmp = pwszTmp;
|
||
|
delete[] pwszTmp;
|
||
|
}
|
||
|
return sswTmp;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
CSimpleStringAnsi AnsiString(const T &str)
|
||
|
{
|
||
|
if (IS_CHAR(str[0]))
|
||
|
return CSimpleStringAnsi((LPCSTR)str.String());
|
||
|
else
|
||
|
{
|
||
|
if (!str.Length())
|
||
|
return CSimpleStringAnsi("");
|
||
|
int iLen = WideCharToMultiByte( CP_ACP, 0, (LPCWSTR)str.String(), str.Length()+1, NULL, 0, NULL, NULL );
|
||
|
CSimpleStringAnsi ssaTmp;
|
||
|
LPSTR pszTmp = new CHAR[iLen];
|
||
|
if (pszTmp)
|
||
|
{
|
||
|
WideCharToMultiByte( CP_ACP, 0, (LPCWSTR)str.String(), str.Length()+1, pszTmp, iLen, NULL, NULL );
|
||
|
ssaTmp = pszTmp;
|
||
|
delete[] pszTmp;
|
||
|
}
|
||
|
return ssaTmp;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#if defined(_UNICODE) || defined(UNICODE)
|
||
|
template <class T>
|
||
|
CSimpleStringWide NaturalString(const T &str)
|
||
|
{
|
||
|
return WideString(str);
|
||
|
}
|
||
|
#else
|
||
|
template <class T>
|
||
|
CSimpleStringAnsi NaturalString(const T &str)
|
||
|
{
|
||
|
return AnsiString(str);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
inline CSimpleString NumberToString( int nNumber, LCID Locale=LOCALE_USER_DEFAULT )
|
||
|
{
|
||
|
TCHAR szTmp[MAX_PATH]=TEXT("");
|
||
|
TCHAR szNumberStr[MAX_PATH]=TEXT("");
|
||
|
TCHAR szDigitGrouping[32]=TEXT("");
|
||
|
TCHAR szThousandsSeparator[32]=TEXT("");
|
||
|
TCHAR szDecimalSeparator[32]=TEXT("");
|
||
|
|
||
|
// Initialize the number format
|
||
|
NUMBERFMT NumberFormat;
|
||
|
NumberFormat.NumDigits = 0;
|
||
|
NumberFormat.LeadingZero = 0;
|
||
|
NumberFormat.NegativeOrder = 0;
|
||
|
// This turns a string into a number, like so: 3;2;0=32 or 3;0 = 3 or 1;2;3;4;5;6;0 = 123456. Got it?
|
||
|
GetLocaleInfo( Locale, LOCALE_SGROUPING, szDigitGrouping, ARRAYSIZE(szDigitGrouping));
|
||
|
NumberFormat.Grouping = 0;
|
||
|
LPTSTR pszCurr = szDigitGrouping;
|
||
|
while (*pszCurr && *pszCurr >= TEXT('1') && *pszCurr <= TEXT('9'))
|
||
|
{
|
||
|
NumberFormat.Grouping *= 10;
|
||
|
NumberFormat.Grouping += (*pszCurr - TEXT('0'));
|
||
|
pszCurr += 2;
|
||
|
}
|
||
|
GetLocaleInfo( Locale, LOCALE_STHOUSAND, szThousandsSeparator, ARRAYSIZE(szThousandsSeparator));
|
||
|
NumberFormat.lpThousandSep = szThousandsSeparator;
|
||
|
GetLocaleInfo( Locale, LOCALE_SDECIMAL, szDecimalSeparator, ARRAYSIZE(szDecimalSeparator));
|
||
|
NumberFormat.lpDecimalSep = szDecimalSeparator;
|
||
|
// Create the number string
|
||
|
_sntprintf( szTmp, ARRAYSIZE(szTmp), TEXT("%d"), nNumber );
|
||
|
if (GetNumberFormat( Locale, 0, szTmp, &NumberFormat, szNumberStr, ARRAYSIZE(szNumberStr)))
|
||
|
return szNumberStr;
|
||
|
else return TEXT("");
|
||
|
}
|
||
|
} // End CSimpleStringConvert namespace
|
||
|
|
||
|
#endif // ifndef _SIMSTR_H_INCLUDED
|
||
|
|