#define __INTERNAL__
#include "stdafx.h"
#include "malloc.h"

#ifdef _DEBUG
#undef THIS_FILE
static CHAR THIS_FILE[] = __FILE__;
#define new DEBUG_NEW
#define delete DEBUG_DELETE
#endif

#ifdef WIN32
#pragma pack(push, 1)
#endif // WIN32
struct
#ifdef WIN32
__declspec(novtable)
#endif // WIN32
SStrDummy
{
#ifdef USE_STRLOCK
    WORD m_wRefCount;
#endif // USE_STRLOCK
    CHAR m_sz[1];
};
#ifdef WIN32
#pragma pack(pop)
#endif // WIN32

#define NIL_LOCKCOUNT 0x8000

#ifdef WIN32
#pragma data_seg(".SHARED_DATA")
#endif // WIN32
// all data in this section must be INITIALIZED!!!!!!!!!!!!!
static SStrDummy l_sz =
{
#ifdef USE_STRLOCK
    NIL_LOCKCOUNT,  // WORD m_wRefCount;
#endif // USE_STRLOCK
    { 0, },         // CHAR m_sz;
};
#ifdef WIN32
#pragma data_seg()
#pragma comment(linker, "/section:.SHARED_DATA,RWS")
#endif // WIN32

#ifdef _DEBUG
void CStrTestMain();

class s_CAutoRun
{
public:
    s_CAutoRun()
    {
        CStrTestMain();
    }
} s_a;
#endif // _DEBUG

#define l_SZ (&l_sz.m_sz[0])

#ifdef USE_STRLOCK
#define STR_LOCKCOUNT STR_LOCKCOUNT2(m_psz)
#define STR_LOCKCOUNT2(psz) ((WORD*)psz)[-1]
static inline CHAR* str_alloc(int nMax)
{
    CHAR* psz = (CHAR*)malloc(sizeof(WORD) + sizeof(CHAR) * nMax);
    *(WORD*)psz = 0;

	CHAR* p = (CHAR*)&((BYTE*)psz)[sizeof(WORD)];
	p[0] = '\0';

    return p;
}

static inline CHAR* str_realloc(CHAR* psz, int nMax)
{
    return (CHAR*)&((BYTE*)realloc(&STR_LOCKCOUNT2(psz), sizeof(WORD) + sizeof(CHAR) * nMax))[sizeof(WORD)];
}

static inline void str_free(CHAR* psz)
{
    free(&STR_LOCKCOUNT2(psz));
}
#else // !USE_STRLOCK
#define STR_LOCKCOUNT ASSERT(FALSE)
#define STR_LOCKCOUNT2(psz) ASSERT(FALSE)
//#define str_alloc(nMax) ((CHAR*)malloc(sizeof(CHAR)*nMax))
static inline CHAR* str_alloc(int nMax)
{
    CHAR* p = (CHAR*)malloc(sizeof(CHAR) * nMax);
	p[0] = '\0';

    return p;
}
#define str_realloc(psz, nMax) ((CHAR*)realloc(psz,sizeof(CHAR)*nMax))
#define str_free(psz) (free(psz))
#endif // USE_STRLOCK

#ifdef USE_STRLOCK
#define IS_NIL(psz) (((const WORD*)psz)[-1] == NIL_LOCKCOUNT) // check m_wRefCount
#else // !USE_STRLOCK
#define IS_NIL(psz) ((psz) == l_SZ) // check pointer...
#endif // USE_STRLOCK

static BOOL LSTRS_IsValid(int c, const char* pcszVals);

static BOOL LSTRS_IsValid(int c, const char* pcszVals)
{
    if (pcszVals) {
        if (strchr(pcszVals, c)) {
            return TRUE;
        }
    } else {
        if ((' ' < c) && (c != 0x7f) && (c != 0xff)) {
            return TRUE;
        }
    }

    return FALSE;
}

int __cdecl str_memcmp(const void* elem1, const void* elem2)
{
    const char* s1 = ((const CStr*)elem1)->String();
    const char* s2 = ((const CStr*)elem2)->String();

    int n1 = strlen(s1);
    int n2 = strlen(s2);
    if (n1 > n2) {
        n2 = n1;
    }
    return memcmp(s1, s2, n2);
}
#if !defined(WIN32)
char* _strupr(char *string)
{
    char c, *p = string;

    while((c = *p) != '\0') {
        if (('a' <= c) && (c <= 'z')) {
            c -= ('a' - 'A');
            *p = c;
        }
        p++;
    }

    return string;
}

char* _strlwr(char *string)
{
    char c, *p = string;

    while((c = *p) != '\0') {
        if (('A' <= c) && (c <= 'Z')) {
            c += ('a' - 'A');
            *p = c;
        }
        p++;
    }

    return string;
}
#endif // !defined(WIN32)
int __cdecl str_Compare(const void* elem1, const void* elem2)
{
    return lstrcmp(((const CStr*)elem1)->String(), ((const CStr*)elem2)->String());
}

int __cdecl str_CompareReverse(const void* elem1, const void* elem2)
{
    return -lstrcmp(((const CStr*)elem1)->String(), ((const CStr*)elem2)->String());
}

int __cdecl str_CompareIC(const void* elem1, const void* elem2)
{
    return lstrcmpi(((const CStr*)elem1)->String(), ((const CStr*)elem2)->String());
}

int __cdecl str_CompareICReverse(const void* elem1, const void* elem2)
{
    return -lstrcmpi(((const CStr*)elem1)->String(), ((const CStr*)elem2)->String());
}

CStr::CStr()
{
    m_psz = l_SZ;
}

CStr::CStr(const CHAR* pcsz)
{
#ifdef _DEBUG
    m_psz = (CHAR*)l_SZ;
#endif // _DEBUG
    AssignNoCheck(pcsz);
}

CStr::CStr(const CHAR* pcsz, int nLen)
{
#ifdef _DEBUG
    m_psz = (CHAR*)l_SZ;
#endif // _DEBUG
    AssignNoCheck(pcsz, nLen);
}

CStr::CStr(CHAR c, int nLen)
{
    ASSERT((c != '\0') && (nLen >= 0));

    if ((c != '\0') && (nLen > 0)) {
        CHAR* psz = str_alloc(nLen + 1);
        memset(psz, c, nLen);
        psz[nLen] = '\0';

        m_psz = psz;
    } else {
        m_psz = l_SZ;
    }
}

CStr::CStr(const CStr& that)
{
#ifdef _DEBUG
    m_psz = l_SZ;
#endif // _DEBUG
    AssignNoCheck(that.m_psz);
}

CStr::CStr(int nMax)
{
    ASSERT(nMax > 0);
    m_psz = str_alloc(nMax);
}

CStr::~CStr()
{
#ifdef USE_STRLOCK
#ifdef _DEBUG
    if (!IS_NIL(m_psz) && (STR_LOCKCOUNT != 0)) {
        TRACE("CStr::~CStr() called for CStr Locked: %d times\n", (int)STR_LOCKCOUNT);
    }
#endif // _DEBUG
#endif // USE_STRLOCK
    if (!IS_NIL(m_psz)) {
        str_free(m_psz);
    }
#ifdef _DEBUG
    m_psz = NULL;
#endif
}

const CHAR* CStr::Empty()
{
    if (!IS_NIL(m_psz)) {
#ifdef USE_STRLOCK
        if (STR_LOCKCOUNT) {
            m_psz[0] = '\0';
            return m_psz;
        }
#endif // USE_STRLOCK
        str_free(m_psz);
        m_psz = l_SZ;
    }

    return l_SZ;
}

void CStr::Swap(CStr& that)
{
#ifdef _DEBUG
  #ifdef USE_STRLOCK
    if (IsLocked() || that.IsLocked())
        TRACE("CStr::Swap() called for Locked CStr(s)\n");
  #endif // USE_STRLOCK
#endif // _DEBUG
	CHAR* psz = m_psz;
	m_psz = that.m_psz;
	that.m_psz = psz;
}
#ifdef USE_STRLOCK
WORD CStr::IsLocked() const
{
    ASSERT_VALIDSTRING(m_psz);
    if (!IS_NIL(m_psz))
        return STR_LOCKCOUNT;
    return 0;
}

CHAR* CStr::LockBuffer()
{
    ASSERT_VALIDSTRING(m_psz);
    if (!IS_NIL(m_psz)) {
        ++STR_LOCKCOUNT;
    } else {
        TRACE("Trying To Lock Empty String...\n");
    }
    return m_psz;
}

WORD CStr::UnlockBuffer()
{
    ASSERT_VALIDSTRING(m_psz);
    if (!IS_NIL(m_psz)) {
        ASSERT(STR_LOCKCOUNT != 0);
        WORD wRefCount = --STR_LOCKCOUNT;
        if (wRefCount == 0) {
            if (m_psz[0] == '\0') {
                Reset();
            }
        }
        return wRefCount;
    } else {
        TRACE("Trying To Unlock Empty String...\n");
        return 0;
    }
}
#endif // USE_STRLOCK
const CHAR* CStr::Assign(const CHAR* pcsz, int nLen/* = -1*/)
{
    ASSERT(this);
    ASSERT_VALIDSTRING(m_psz);

#ifdef USE_STRLOCK
    if (IsLocked()) {
        if (nLen < 0) {
            if (!pcsz || ((nLen = strlen(pcsz)) <= 0)) {
                return ResetLocked();
            }
            nLen++;
            memcpy(m_psz, pcsz, nLen);
        } else {
            if (nLen <= 0) {
                return ResetLocked();
            }
            ASSERT(pcsz);
            ASSERT((UINT)nLen <= strlen(pcsz));
            memcpy(m_psz, pcsz, nLen);
            m_psz[nLen] = '\0';
        }
    } else {
#endif // USE_STRLOCK
        if (IS_NIL(m_psz)) {
            return AssignNoCheck(pcsz, nLen);
        }

        if (nLen < 0) {
            if (!pcsz || ((nLen = strlen(pcsz)) <= 0)) {
                return Reset();
            }
            nLen++;
            memcpy(m_psz = str_realloc(m_psz, nLen), pcsz, nLen);
        } else {
            if (nLen <= 0) {
                return Reset();
            }
            ASSERT(pcsz);
            ASSERT((UINT)nLen <= strlen(pcsz));
            int nAlloc = nLen + 1;
            memcpy(m_psz = str_realloc(m_psz, nAlloc), pcsz, nLen);
            m_psz[nLen] = '\0';
        }
#ifdef USE_STRLOCK
    }
#endif // USE_STRLOCK

    return m_psz;
}

CStr CStr::operator + (const CHAR* pcsz) const
{
    int nLen;

    if (pcsz && ((nLen = strlen(pcsz)) > 0)) {
        CHAR* psz = m_psz;

        if (psz[0] == '\0') {
            return CStr(pcsz, nLen);
        } else {
            int n = strlen(psz);

            CStr str(n + ++nLen);

            CHAR* p = str.m_psz;

            memcpy(p, psz, n);
            memcpy(&p[n], pcsz, nLen);

            return str;
        }
    }

    return *this;
}

CStr& CStr::operator += (const CHAR* pcsz)
{
    int nLen;

#ifdef USE_STRLOCK
    if (IsLocked()) {
        if (pcsz && ((nLen = strlen(pcsz)) > 0)) {
            CHAR* psz = m_psz;
            memcpy(&psz[strlen(psz)], pcsz, nLen + 1);
        }
    } else {
#endif // USE_STRLOCK
        if (pcsz && ((nLen = strlen(pcsz)) > 0)) {
            CHAR* psz = m_psz;

            if (IS_NIL(psz)) {
                AssignNoCheck(pcsz, nLen);
            } else {
                nLen++;
                int n = strlen(psz);
                psz = str_realloc(psz, n + nLen);
                memcpy(&psz[n], pcsz, nLen);
                m_psz = psz;
            }
        }
#ifdef USE_STRLOCK
    }
#endif // USE_STRLOCK

    return *this;
}

CStr CStr::operator + (CHAR c) const
{
    CHAR ca[2];
    ca[0] = c;
    ca[1] = '\0';

    return *this + ca;
}

CStr& CStr::operator += (CHAR c)
{
    CHAR ca[2];
    ca[0] = c;
    ca[1] = '\0';

    return *this += ca;
}

void CStr::MakeReverse()
{
    int nLen = strlen(m_psz);
    if (nLen >= 2) {
        CHAR* pszStart = m_psz;
        CHAR* pszEnd = &pszStart[nLen - 1];
        nLen /= 2;
        while(nLen-- > 0) {
            CHAR c;

            c = *pszStart;
            *pszStart = *pszEnd;
            *pszEnd = c;

            pszStart++;
            pszEnd--;
        }
    }
}

const CHAR* CStr::Prune(const CHAR* pcszDELs/* = NULL*/)
{
    if (!pcszDELs) {
        pcszDELs = "\t ";
    }
    ASSERT_VALIDSTRING(pcszDELs);
    ASSERT(pcszDELs[0]);
    CHAR* pszHead = m_psz;
    CHAR c;

    while((c = *pszHead) != '\0') {
        if (strchr(pcszDELs, c)) {
            CHAR* pszStart = pszHead;
            while((c = *++pszHead) != '\0') {
                if (!strchr(pcszDELs, c)) {
                    strcpy(pszStart, pszHead);
                    goto no_DELs;
                }
            }
            ASSERT(c == '\0');
            pszStart[0] = '\0';
            break;
        } else {
no_DELs:
            pszHead++;
        }
    }
    ReleaseBuffer();

    return m_psz;
}

const CHAR* CStr::TrimLeft()
{
    CHAR* pszHead = m_psz;
    CHAR c;

    while((c = *pszHead) != '\0') {
        if ((c != ' ') && (c != '\t')) {
            if (m_psz != pszHead)
                strcpy(m_psz, pszHead);
            return m_psz;
        }
        pszHead++;
    }
    return Empty();
}

const CHAR* CStr::TrimRight()
{
    CHAR* pszHead = m_psz;
    CHAR c;
    CHAR* pszTail = &pszHead[strlen(pszHead)];

    while(pszTail != pszHead) {
        c = *--pszTail;
        if ((c != ' ') && (c != '\t')) {
            pszTail[1] = '\0';
            return m_psz;
        }
    }
    return Empty();
}

const CHAR* CStr::TrimAll()
{
    TrimLeft();

    return TrimRight();
}

void __cdecl CStr::Format(LPCTSTR lpszFormat, ...)
{
    va_list marker;

    va_start(marker, lpszFormat);
    vFormat(lpszFormat, marker);
    va_end(marker);
}

void CStr::vFormat(LPCTSTR lpszFormat, va_list marker)
{
    CHAR szBuf[512];

#ifdef _DEBUG
    int n = vsprintf(szBuf, lpszFormat, marker);
    ASSERT((0 <= n) && (n + 1 <= NOFA(szBuf)));
#else // !_DEBUG
    vsprintf(szBuf, lpszFormat, marker);
#endif // _DEBUG

    Assign(szBuf);
}

CHAR* CStr::GetBuffer(int nLen)
{
    ASSERT(nLen >= 0);
    ASSERT_VALIDSTRING(m_psz);

    CHAR* psz = m_psz;

#ifdef USE_STRLOCK
    if (!IsLocked()) {
#endif // USE_STRLOCK
        int n = strlen(psz);
    
        if ((n -= nLen) < 0) {
            nLen++;
            psz = (IS_NIL(psz)) ? str_alloc(nLen): str_realloc(psz, nLen);
            m_psz = psz;
        }
#ifdef USE_STRLOCK
    }
#endif // USE_STRLOCK

    return psz;
}

const CHAR* CStr::ReleaseBuffer(int nLen/* = -1*/)
{
    ASSERT_VALIDSTRING(m_psz);

    CHAR* psz = m_psz;

#ifdef USE_STRLOCK
    if (IsLocked()) {
        if (nLen >= 0) {
            psz[nLen] = '\0';
        }
        return psz;
    } else {
#endif // USE_STRLOCK
        if (nLen >= 0) {
#ifdef _DEBUG
            if (psz[nLen] != '\0') // when _DEBUG, (pcsz == const l_SZ) is possible
#endif // _DEBUG
                psz[nLen] = '\0';
        } else {
            nLen = strlen(psz);
        }
        if (nLen == 0) {
            return Reset();
        } else {
            return m_psz = str_realloc(psz, nLen + 1);
        }
#ifdef USE_STRLOCK
    }
#endif // USE_STRLOCK
}

CHAR* CStr::GetBufferSetLength(int nLen)
{
	ASSERT(nLen >= 0);

#ifdef _DEBUG
	CHAR* p1 = GetBuffer(nLen);
    if (!IS_NIL(p1)) // when _DEBUG, (pcsz == const l_SZ) is possible
	    m_psz[nLen] = '\0';
#else // !_DEBUG
	GetBuffer(nLen);
#endif // _DEBUG

	return m_psz;
}

void CStr::ChangeChars(CHAR cBefore, CHAR cAfter)
{
    ASSERT(cBefore && cAfter);
    CHAR* psz = m_psz;
    CHAR c;

    while((c = *psz) != '\0') {
        if (c == cBefore) {
            *psz = cAfter;
        }
        psz++;
    }
}

BOOL CStr::AsDirectory(BOOL bRemoveLastDEL/* = FALSE*/)
{
    int nPos = rFindSet("/\\");

    if (nPos != -1) {
        if (!bRemoveLastDEL)
            nPos++;
        ReleaseBuffer(nPos);

        return TRUE;
    }

    return FALSE;
}

BOOL CStr::AddDirDEL()
{
    int nLen = GetLength();

    if (nLen > 0) {
        CHAR c = m_psz[nLen - 1];
        if ((c != '\\') && (c != '/')) {
#ifdef _WINDOWS
            *this += '\\';
#else // !_WINDOWS
            *this += '/';
#endif // _WINDOWS
            return TRUE;
        }
    }

    return FALSE;
}

int CStr::FindExtPos() const
{
    int nPos = rFind('.');

    if (nPos != -1) {
        const CHAR* pcsz = &m_psz[nPos + 1];
        if (strchr(pcsz, '\\') || strchr(pcsz, '/')) {
            return -1;
        }
    }

    return nPos;
}

const char* CStr::FindExt() const
{
    int nPos = FindExtPos();

    if (nPos != -1) {
        return &m_psz[nPos];
    }

    return NULL;
}

BOOL CStr::ChangeExt(const char* pcszExt, BOOL bForce/* = TRUE*/)
{
    ASSERT_VALIDSTRING(pcszExt);
    int nPos = FindExtPos();

    if (nPos != -1) {
        if (bForce && strcmp(&m_psz[nPos], pcszExt)) {
            int nLen = nPos + strlen(pcszExt);
            CHAR* psz = GetBuffer(nLen);
            strcpy(&psz[nPos], pcszExt);
            return TRUE;
        }
    } else {
        if (*pcszExt) {
            *this += pcszExt;
            return TRUE;
        }
    }

    return FALSE;
}

const CHAR* CStr::Left(CStr& strLeft, int nCount) const
{
    ASSERT(&strLeft);
    ASSERT(nCount >= 0);
    ASSERT_VALIDSTRING(strLeft);
    ASSERT_VALIDSTRING(m_psz);

    CHAR* psz = m_psz;
    int n = strlen(psz);

    if ((n -= nCount) < 0) {
        nCount += n;
        return strLeft.Assign(psz, nCount);
    } else {
        CHAR* p = strLeft.GetBuffer(nCount);
        memcpy(p, psz, nCount);
        return strLeft.ReleaseBuffer(nCount);
    }
}

const CHAR* CStr::Right(CStr& strRight, int nCount) const
{
    ASSERT(&strRight);
    ASSERT(nCount >= 0);
    ASSERT_VALIDSTRING(strRight);
    ASSERT_VALIDSTRING(m_psz);

    CHAR* psz = m_psz;
    int n = strlen(psz);

    if ((n -= nCount) < 0) {
        nCount += n;
    } else {
        psz += n;
    }

    return strRight.Assign(psz, nCount);
}

const CHAR* CStr::Mid(CStr& strMid, int nFirst) const
{
    ASSERT(&strMid);
    ASSERT(nFirst >= 0);
    ASSERT_VALIDSTRING(strMid);
    ASSERT_VALIDSTRING(m_psz);

    CHAR* psz = m_psz;
    int n = strlen(psz);

    if (nFirst > 0) {
        if ((n -= nFirst) <= 0) {
            strMid.Empty();
            return l_SZ;
        }
        psz += nFirst;
    }

    return strMid.Assign(psz, n);
}

const CHAR* CStr::Mid(CStr& strMid, int nFirst, int nCount) const
{
    ASSERT(&strMid);
    ASSERT(nFirst >= 0);
    ASSERT(nCount >= 0);
    ASSERT_VALIDSTRING(strMid);
    ASSERT_VALIDSTRING(m_psz);

    if (nCount <= 0) {
        return strMid.Empty();
    }

    CHAR* psz = m_psz;
    int n = strlen(psz);

    if (nFirst > 0) {
        if ((n -= nFirst) <= 0) {
            return strMid.Empty();
        }
        psz += nFirst;
    }

    if ((n -= nCount) <= 0) {
        nCount += n;
        return strMid.Assign(psz, nCount);
    } else {
        CHAR* p = strMid.GetBuffer(nCount);
        memcpy(p, psz, nCount);
        return strMid.ReleaseBuffer(nCount);
    }
}

CStr CStr::Left(int nCount) const
{
    CStr str;

    Left(str, nCount);

    return str;
}

CStr CStr::Right(int nCount) const
{
    CStr str;

    Right(str, nCount);

    return str;
}

CStr CStr::Mid(int nFirst) const
{
    CStr str;

    Mid(str, nFirst);

    return str;
}

CStr CStr::Mid(int nFirst, int nCount) const
{
    CStr str;

    Mid(str, nFirst, nCount);

    return str;
}

int CStr::FindNext(CHAR c, int nStart) const
{
    ASSERT(c != '\0');
    const CHAR* pcsz = m_psz;
    if (nStart < 0)
        nStart = 0;
    else {
        ASSERT(strlen(pcsz) >= nStart);
        pcsz = &pcsz[nStart];
        if (*pcsz == '\0')
            return -1;
        pcsz++;
        nStart++;
    }
    const char* pcsz2 = strchr(pcsz, c);

    if (pcsz2) {
        return nStart + (pcsz2 - pcsz);
    }

    return -1;
}

int CStr::Find(CHAR c) const
{
    ASSERT(c != '\0');
    const CHAR* pcsz = strchr(m_psz, c);

    if (pcsz) {
        return pcsz - m_psz;
    }

    return -1;
}

int CStr::rFindNext(CHAR c, int nStart) const
{
    ASSERT(c != '\0');
    const CHAR* pcsz = m_psz;
    const CHAR* pcsz2 = pcsz;
    if (nStart < 0) {
        nStart = strlen(pcsz2);
    } else {
        ASSERT(strlen(pcsz2) >= nStart);
    }
    pcsz2 = &pcsz2[nStart];
    while(--pcsz2 >= pcsz) {
        nStart--;
        if (*pcsz2 == c) {
            return nStart;
        }
    }

    return -1;
}

int CStr::rFind(CHAR c) const
{
    ASSERT(c != '\0');
    const CHAR* pcsz = strrchr(m_psz, c);

    if (pcsz) {
        return pcsz - m_psz;
    }

    return -1;
}

int CStr::Find(const CHAR* pcsz, int nStart) const
{
    ASSERT_VALIDSTRING(pcsz);

    if (nStart < 0)
        nStart = 0;
    const CHAR* pcsz2 = m_psz;
    while(nStart > 0) {
        if (*pcsz2++ == '\0')
            return -1;
        nStart--;
    }
    pcsz = strstr(pcsz2, pcsz);

    if (pcsz) {
        return pcsz - m_psz;
    }

    return -1;
}

int CStr::Find(const CHAR* pcsz) const
{
    ASSERT_VALIDSTRING(pcsz);

    pcsz = strstr(m_psz, pcsz);

    if (pcsz) {
        return pcsz - m_psz;
    }

    return -1;
}

int CStr::FindSet(const CHAR* pcszSet) const
{
    ASSERT_VALIDSTRING(pcszSet);

    CHAR* psz = m_psz;
    CHAR c;

    while((c = *psz) != '\0') {
        if (strchr(pcszSet, c)) {
            return psz - m_psz;
        }
        psz++;
    }

    return -1;
}

int CStr::rFindSet(const CHAR* pcszSet) const
{
    ASSERT_VALIDSTRING(pcszSet);

    CHAR* pszStart = m_psz;
    CHAR* psz = &pszStart[strlen(pszStart)];

    while(psz-- > pszStart) {
        if (strchr(pcszSet, *psz)) {
            return psz - m_psz;
        }
    }

    return -1;
}
#if defined(WIN32)
BOOL CStr::GetWindowText(HWND hWnd)
{
    Empty();
    if (IsWindow(hWnd)) {
        int nSize = SendMessage(hWnd, WM_GETTEXTLENGTH, 0, 0);
        if (nSize > 0) {
            nSize += 10; // for safety...
            char* p = GetBuffer(nSize + 1/*'\0'*/);
            SendMessage(hWnd, WM_GETTEXT, (WPARAM)nSize, (LPARAM)p);
        }
        return TRUE;
    }

    return FALSE;
}
#endif // defined(WIN32)
const CHAR* CStr::AssignNoCheck(const CHAR* pcsz, int nLen/* = -1*/)
{
    ASSERT(IS_NIL(m_psz)); // when !_DEBUG then m_psz is !initialized...

    CHAR* psz;
    if (nLen < 0) {
        if (!pcsz || ((nLen = strlen(pcsz)) <= 0)) {
            return m_psz = l_SZ;
        }
        nLen++;
        psz = str_alloc(nLen);
        memcpy(psz, pcsz, nLen);
    } else {
        if (nLen <= 0) {
            return m_psz = l_SZ;
        }
        ASSERT(pcsz);
        ASSERT((UINT)nLen <= strlen(pcsz));
        int nAlloc = nLen + 1;
        psz = str_alloc(nAlloc);
        memcpy(psz, pcsz, nLen);
        psz[nLen] = '\0';
    }

    return m_psz = psz;
}

CHAR* CStr::Reset()
{
#ifdef USE_STRLOCK
    ASSERT(!IsLocked());
#endif // USE_STRLOCK
    if (!IS_NIL(m_psz)) {
        str_free(m_psz);
        m_psz = l_SZ;
    }
    return l_SZ;
}
#ifdef USE_STRLOCK
CHAR* CStr::ResetLocked()
{
    ASSERT(IsLocked());
    ASSERT(!IS_NIL(m_psz));

    m_psz[0] = '\0';
    return m_psz;
}
#endif // USE_STRLOCK
#ifdef USE_CSTR_ISTREAM
istream& operator >> (istream& s, CStr& str)
{
    str.Empty();

    int c;

    while((c = s.get()) != EOF) {
        if (!isspace(c)) {
            do {
                str += (char)c;
                if ((c = s.get()) == EOF)
                    break;
            } while(!isspace(c));
            break;
        }
    }

    return s;
}
#endif // USE_CSTR_ISTREAM
int CStrArray::GetTotalLength() const
{
    int nTotalLength;
    int i, nSize = GetSize();

    nTotalLength = nSize; // nSize null terminators '\0'
    for(i = 0; i < nSize; i++) {
        nTotalLength += strlen(GetAt(i));
    }

    return nTotalLength;
}

int CStrArray::Find(const char* pcsz) const
{
    ASSERT(pcsz);
    int i, nSize = GetSize();

    for(i = 0; i < nSize; i++) {
        if (strcmp(GetAt(i), pcsz) == 0)
            return i;
    }

    return -1;
}

int CStrArray::AddIfNotFound(const char* pcsz)
{
    int nFound = Find(pcsz);
    if (nFound == -1) {
        Add() = pcsz;
    }

    return nFound;
}

int CStrArray::AddIfNotFound(const CStr astrs[], int nCount)
{
    int nAdded = 0;

    for(int i = 0; i < nCount; i++) {
        if (AddIfNotFound(astrs[i]) == -1)
            nAdded++;
    }

    return nAdded;
}

void CStrArray::Sort(QSortCompareProc pQSortCompareProc/* = (QSortCompareProc)NULL*/)
{
    int nSize = GetSize();

    if (nSize > 0) {
        if (!pQSortCompareProc)
            pQSortCompareProc = str_Compare;
        qsort(&GetAt(0), nSize, sizeof(CStr), pQSortCompareProc);
    }
}

BOOL CStrArray::LoadStrs(const char* pcszFile, const char* pcszVals/* = NULL*/, UINT uFlags/* = 0*/)
{
    BOOL bRet = FALSE;

    FILE* pFile = fopen(pcszFile, "rt");
    if (pFile) {
        bRet = LoadStrs(pFile, pcszVals, uFlags);
        fclose(pFile);
    }

    return bRet;
}

BOOL CStrArray::LoadStrs(FILE* pFile, const char* pcszVals/* = NULL*/, UINT uFlags/* = 0*/)
{
    if (pFile) {
        int c;

        CStr str;
        while((c = getc(pFile)) != EOF) {
            if (LSTRS_IsValid(c, pcszVals)) {
                str += (char)c;
            } else {
                LSTRS_CheckAdd(str, uFlags);
            }
        }
        LSTRS_CheckAdd(str, uFlags);
        if (uFlags & LSTRS_DOSORT)
            Sort();
        return TRUE;
    }

    return FALSE;
}

void CStrArray::LSTRS_CheckAdd(CStr& str, UINT uFlags)
{
    if (!str.IsEmpty()) {
        UINT uFlagsCase = uFlags & (LSTRS_TOLOWER | LSTRS_TOUPPER);
        if (uFlagsCase) {
            if (uFlagsCase & LSTRS_TOLOWER) {
                str.MakeLower();
                if (uFlagsCase & LSTRS_TOUPPER) {
                    str.SetAt(0, (CHAR)toupper(str.GetAt(0)));
                }
            } else { // LSTRS_TOUPPER
                str.MakeUpper();
            }
        }
        if (uFlags & LSTRS_ALLOWDUP)
            Add() = str;
        else
            AddIfNotFound(str);
        str.Empty();
    }
}
#ifdef _DEBUG
BOOL IsValidString(const CHAR* pcszStr, int nLen/* = -1*/)
{
    // nLen: maximum size of string
    return pcszStr && !::IsBadStringPtr(pcszStr, (UINT)nLen);
}

BOOL IsValidStringSafe(const CHAR* pcszStr, int nLen/* = -1*/)
{
    // nLen: maximum size of string
    return !pcszStr || !::IsBadStringPtr(pcszStr, (UINT)nLen);
}

BOOL IsValidMemory(const void* pMem, UINT uBytes, BOOL bWriteCheck/* = TRUE*/)
{
    return pMem && !(::IsBadReadPtr(pMem, uBytes) || (bWriteCheck && ::IsBadWritePtr((void*)pMem, uBytes)));
}

BOOL IsValidMemorySafe(const void* pMem, UINT uBytes, BOOL bWriteCheck/* = TRUE*/)
{
    return !pMem || !(::IsBadReadPtr(pMem, uBytes) || (bWriteCheck && ::IsBadWritePtr((void*)pMem, uBytes)));
}
#endif // _DEBUG
#ifdef _DEBUG
void CStrTestMain()
{
    const CHAR* g_pcsza[] = {
        (const CHAR*)NULL,
        "",
        "a",
        "abc  ",
        "  123456789",
        "  abCABD  ",
    };
    int i, nStrs = NOFA(g_pcsza);
    CStr k;

    TARRAYREF(CStr) aStrs;

    aStrs.Add(nStrs);

    for(i = 0; i < nStrs; i++) {
        BOOL bLocked = FALSE;
        if (rand() % 2) {
            aStrs.GetAt(i).GetBuffer(256);
            aStrs.GetAt(i).LockBuffer();
            bLocked = TRUE;
        }
        CStr a(g_pcsza[i]);

        CStr b(a, a.GetLength());

        CStr c((CHAR)(rand() % 254 + 1), rand() % 3);

        a += b;
        a += c;
        b = a;
        c = a;

        a += (const CHAR*)NULL;
        b += "";
        c += "sl123KDFklsd   ";

        a = c;
        b = c;

        a.MakeLower();
        b.MakeUpper();
        c.MakeReverse();

        a += b;
        c = a + b;

        a.TrimLeft();
        a.TrimRight();
        b.TrimLeft();
        b.TrimRight();
        c.TrimLeft();
        c.TrimRight();

        aStrs[i] = c;

        a.Left(k, 1000);
        a.Right(k, 1000);
        a.Mid(k, 1);
        a.Mid(k, 100);
        a.Mid(k, 1, a.GetLength() / 2);

        b.Left(k, 1000);
        b.Right(k, 1000);
        b.Mid(k, 1);
        b.Mid(k, 100);
        b.Mid(k, 1, a.GetLength() / 2);

        c.Left(k, 1000);
        c.Right(k, 1000);
        c.Mid(k, 1);
        c.Mid(k, 100);
        c.Mid(k, 1, a.GetLength() / 2);

        if (bLocked)
            aStrs.GetAt(i).UnlockBuffer();
    }
}
#endif // _DEBUG
