//-----------------------------------------------------------------------------
//
// Microsoft Forms
// Copyright: (c) 1994-1995, Microsoft Corporation
// All rights Reserved.
// Information contained herein is Proprietary and Confidential.
//
// File         CSTR.CXX
//
// Contents     Class implementation for length prefix string class
//
// Classes      CStr
//
// Maintained by Istvanc
//
//
//  History:
//              5-22-95     kfl     converted WCHAR to TCHAR
//-----------------------------------------------------------------------------

#include "headers.hxx"

ANSIString::ANSIString(WCHAR *pchWide)
{
    _pch = NULL;

    Set(pchWide);
}

void
ANSIString::Set(WCHAR *pchWide)
{
    int cch = wcslen(pchWide) + 1;

    if (_pch)
        delete _pch;

    if (cch > 0)
    {
        _pch = new char[cch];

        MemSetName(_pch, "ANSIString");

        if (_pch)
        {
            WideCharToMultiByte(CP_ACP,
                                0,
                                pchWide,
                                -1,
                                _pch,
                                cch,
                                NULL,
                                NULL);
        }
    }
    else
        _pch = NULL;
}

//+---------------------------------------------------------------------------
//
//  Member:     CStr::Set, public
//
//  Synopsis:   Allocates memory for a string and initializes it from a given
//              string.
//
//  Arguments:  [pch] -- String to initialize with. Can be NULL
//              [uc]  -- Number of characters to allocate.
//
//  Returns:    HRESULT
//
//  Notes:      The total number of characters allocated is uc+1, to allow
//              for a NULL terminator. If [pch] is NULL, then the string is
//              uninitialized except for the NULL terminator, which is at
//              character position [uc].
//
//----------------------------------------------------------------------------

HRESULT
CStr::Set(LPCTSTR pch, UINT uc)
{
    if (pch == _pch)
    {
        if (uc == Length())
            return S_OK;
        // when the ptrs are the same the length can only be
        // different if the ptrs are NULL.  this is a hack used
        // internally to implement realloc type expansion
        Assert(pch == NULL && _pch == NULL);
    }

    Free();

    BYTE * p = new BYTE [sizeof(TCHAR) + sizeof(TCHAR) * uc + sizeof(UINT)];
    if (p)
    {
        MemSetName(p, "CStr String");

        *(UINT *)(p)  = uc * sizeof(TCHAR);
        _pch = (TCHAR *)(p + sizeof(UINT));
        if (pch)
        {
            _tcsncpy(_pch, pch, uc);
        }

        ((TCHAR *)_pch) [uc] = 0;
    }
    else
    {
        return E_OUTOFMEMORY;
    }
    return S_OK;
}

//+---------------------------------------------------------------------------
//
//  Member:     CStr::Set, public
//
//  Synopsis:   Allocates a string and initializes it
//
//  Arguments:  [pch] -- String to initialize from
//
//----------------------------------------------------------------------------

HRESULT
CStr::Set(LPCTSTR pch)
{
    RRETURN(Set(pch, pch ? _tcsclen(pch) : 0));
}


//+---------------------------------------------------------------------------
//
//  Member:     CStr::Set, public
//
//  Synopsis:   Allocates a string and initializes it
//
//  Arguments:  [cstr] -- String to initialize from
//
//----------------------------------------------------------------------------

HRESULT
CStr::Set(const CStr &cstr)
{
    RRETURN(Set(cstr, cstr.Length()));
}


//+---------------------------------------------------------------------------
//
//  Member:     CStr::TakeOwnership, public
//
//  Synopsis:   Takes the ownership of a string from another CStr class.
//
//  Arguments:  [cstr] -- Class to take string from
//
//  Notes:      This method just transfers a string from one CStr class to
//              another. The class which is the source of the transfer has
//              a NULL value at the end of the operation.
//
//----------------------------------------------------------------------------

void
CStr::TakeOwnership(CStr &cstr)
{
    _Free();
    _pch = cstr._pch;
    cstr._pch = NULL;
}


//+---------------------------------------------------------------------------
//
//  Member:     CStr::SetBSTR, public
//
//  Synopsis:   Allocates a string and initializes it from a BSTR
//
//  Arguments:  [bstr] -- Initialization string
//
//  Notes:      This method is more efficient than Set(LPCWSTR pch) because
//              of the length-prefix on BSTRs.
//
//----------------------------------------------------------------------------

HRESULT
CStr::SetBSTR(const BSTR bstr)
{
    RRETURN(Set(bstr, bstr ? SysStringLen(bstr) : 0));
}

//+---------------------------------------------------------------------------
//
//  Member:     CStr::SetMultiByte, public
//
//  Synopsis:   Sets the string value from an MultiByte string
//
//  Arguments:  [pch] -- MultiByte string to convert to UNICODE
//
//----------------------------------------------------------------------------

HRESULT
CStr::SetMultiByte(LPCSTR pch)
{
    HRESULT hr;
    DWORD   dwLen;

    dwLen = strlen(pch);

    hr = Set(NULL, dwLen);
    if (hr)
        RRETURN(hr);

    dwLen = MultiByteToWideChar(CP_ACP,
                                0,
                                pch,
                                dwLen,
                                _pch,
                                dwLen + 1);

    if (dwLen == 0)
    {
        hr = HRESULT_FROM_WIN32(GetLastError());
    }

    RRETURN(hr);
}

//+---------------------------------------------------------------------------
//
//  Member:     CStr::GetMultiByte, public
//
//  Synopsis:   Gets the string value in MultiByte format
//
//  Arguments:  [pch] -- Place to put MultiByte string
//              [cch] -- Size of buffer pch points to
//
//----------------------------------------------------------------------------

HRESULT
CStr::GetMultiByte(LPSTR pch, UINT cch)
{
    HRESULT hr = S_OK;

    DWORD dwLen = WideCharToMultiByte(CP_ACP,
                                      0,
                                      _pch,
                                      -1,
                                      pch,
                                      cch,
                                      NULL,
                                      NULL);
    if (dwLen == 0)
    {
        hr = HRESULT_FROM_WIN32(GetLastError());
    }

    RRETURN(hr);
}

//+---------------------------------------------------------------------------
//
//  Member:     CStr::Length, public
//
//  Synopsis:   Returns the length of the string associated with this class
//
//----------------------------------------------------------------------------

UINT
CStr::Length() const
{
    if (_pch)
        return (((UINT *)_pch) [-1]) / sizeof(TCHAR);
    else
        return 0;
}

//+---------------------------------------------------------------------------
//
//  Member:     CStr::ReAlloc, public
//
//  Synopsis:   Reallocate the string to a different size buffer.
//              The length of the string is not affected, but it is allocated
//              within a larger (or maybe smaller) memory allocation.
//
//----------------------------------------------------------------------------

HRESULT
CStr::ReAlloc( UINT uc )
{
    HRESULT hr;
    TCHAR * pchOld;
    UINT    ubOld;

    Assert(uc >= Length());  // Disallowed to allocate a too-short buffer.

    if (uc)
    {
        pchOld = _pch;      // Save pointer to old string.
        _pch = 0;           // Prevent Set from Freeing the string.
        ubOld = pchOld ?    // Save old length
                    *(UINT *) (((BYTE *)pchOld) - sizeof(UINT))
                  : 0;

        hr = Set(pchOld, uc);                   // Alloc new; Copy old string.
        if (hr)
        {
            _pch = pchOld;
            RRETURN(hr);
        }
        *(UINT *)(((BYTE *)_pch) - sizeof(UINT)) = ubOld; // Restore length.

        if (pchOld )
        {
            delete [] (((BYTE *)pchOld) - sizeof(UINT));
        }
    }

    // else if uc == 0, then, since we have already checked that uc >= Length,
    // length must == 0.

    return S_OK;
}


//+---------------------------------------------------------------------------
//
//  Member:     CStr::Append
//
//  Synopsis:   Append chars to the end of the string, reallocating & updating
//              its length.
//
//----------------------------------------------------------------------------

HRESULT
CStr::Append(LPCTSTR pch, UINT uc)
{
    HRESULT hr = S_OK;
    UINT ucOld, ucNew;
    BYTE *p;

    if (uc)
    {
        ucOld = Length();
        ucNew = ucOld + uc;
        hr = ReAlloc(ucNew);
        if (hr)
            goto Cleanup;
        _tcsncpy(_pch + ucOld, pch, uc);
        ((TCHAR *)_pch) [ucNew] = 0;
        p = ((BYTE*)_pch - sizeof(UINT));
        *(UINT *)p = ucNew * sizeof(TCHAR);
    }
Cleanup:
    RRETURN(hr);
}


HRESULT
CStr::Append(LPCTSTR pch)
{
    RRETURN(Append(pch, pch ? _tcsclen(pch) : 0));
}

//+---------------------------------------------------------------------------
//
//  Member:     CStr::AppendMultiByte, public
//
//  Synopsis:   Append a multibyte string to the end,
//
//----------------------------------------------------------------------------

HRESULT
CStr::AppendMultiByte(LPCSTR pch)
{
    HRESULT hr = S_OK;
    UINT ucOld, ucNew, uc;
    BYTE *p;

    uc = strlen(pch);

    if (uc)
    {
        ucOld = Length();
        ucNew = ucOld + uc;
        hr = ReAlloc(ucNew);
        if (hr)
            goto Cleanup;

        uc = MultiByteToWideChar(CP_ACP,
                                 0,
                                 pch,
                                 uc,
                                 _pch + ucOld,
                                 uc + 1);
        if (uc == 0)
        {
            hr = HRESULT_FROM_WIN32(GetLastError());

            TraceTag((tagError, "CSTR: Fatal string conversion error %x", hr));

            goto Cleanup;
        }

        ((TCHAR *)_pch) [ucNew] = 0;
        p = ((BYTE*)_pch - sizeof(UINT));
        *(UINT *)p = ucNew * sizeof(TCHAR);
    }

Cleanup:
    RRETURN(hr);
}

//+---------------------------------------------------------------------------
//
//  Member:     CStr::SetLengthNoAlloc, public
//
//  Synopsis:   Sets the internal length of the string. Note.  There is no
//              verification that the length that is set is within the allocated
//              range of the string. If the caller sets the length too large,
//              this blasts a null byte into memory.
//
//----------------------------------------------------------------------------

HRESULT
CStr::SetLengthNoAlloc( UINT uc )
{
    if (_pch)
    {
        BYTE * p = ( (BYTE *)_pch - sizeof(UINT));
        *(UINT *)p = uc * sizeof(TCHAR);    // Set the length prefix.
        ((TCHAR *)_pch) [uc] = 0;           // Set null terminator
        return S_OK;
    }
    else
        return E_POINTER;
}

//+---------------------------------------------------------------------------
//
//  Member:     CStr::AllocBSTR, public
//
//  Synopsis:   Allocates a BSTR and initializes it with the string that is
//              associated with this class.
//
//  Arguments:  [pBSTR] -- Place to put new BSTR. This pointer should not
//                         be pointing to an existing BSTR.
//
//----------------------------------------------------------------------------

HRESULT
CStr::AllocBSTR(BSTR *pBSTR) const
{
    if (!_pch)
    {
        *pBSTR = 0;
        return S_OK;
    }

    *pBSTR = SysAllocStringLen(*this, Length());
    RRETURN(*pBSTR ? S_OK: E_OUTOFMEMORY);
}

//+---------------------------------------------------------------------------
//
//  Member:     CStr::TrimTrailingWhitespace, public
//
//  Synopsis:   Removes any trailing whitespace in the string that is
//              associated with this class.
//
//  Arguments:  None.
//
//----------------------------------------------------------------------------

HRESULT CStr::TrimTrailingWhitespace()
{
    if (!_pch)
        return S_OK;

    UINT ucNewLength = Length();

    for ( ; ucNewLength > 0; ucNewLength-- )
    {
        if ( !_istspace( ((TCHAR *)_pch)[ ucNewLength - 1 ] ) )
            break;
        ((TCHAR *)_pch)[ ucNewLength - 1 ] = (TCHAR) 0;
    }

    BYTE *p = ((BYTE*)_pch - sizeof(UINT));
    *(UINT *)p = ucNewLength * sizeof(TCHAR);
        return S_OK;
}

//+---------------------------------------------------------------------------
//
//  Member:     CStr::_Free, private
//
//  Synopsis:   Frees any memory held onto by this class.
//
//----------------------------------------------------------------------------

void
CStr::_Free()
{
    if (_pch )
    {
        delete [] (((BYTE *)_pch) - sizeof(UINT));
    }
    _pch=0;
}

//+---------------------------------------------------------------------------
//
//  Member:     CStr::Clone
//
//  Synopsis:   Make copy of current string
//
//----------------------------------------------------------------------------
HRESULT
CStr::Clone(CStr **ppCStr) const
{
    HRESULT hr;
    Assert(ppCStr);
    *ppCStr = new CStr;
    hr = *ppCStr?S_OK:E_OUTOFMEMORY;
    if (hr)
        goto Cleanup;

    hr = THR( (*ppCStr)->Set(*this) );

Cleanup:
    RRETURN(hr);
}

//+---------------------------------------------------------------------------
//
//  Member:     CStr::Compare
//
//  Synopsis:   Case insensitive comparison of 2 strings
//
//----------------------------------------------------------------------------
BOOL
CStr::Compare (const CStr *pCStr) const
{
    return (!_tcsicmp(*pCStr, *this));
}

//+---------------------------------------------------------------------------
//
//  Member:     CStr::ComputeCrc
//
//  Synopsis:   Computes a hash of the string.
//
//----------------------------------------------------------------------------
#pragma warning(disable:4305)
WORD
CStr::ComputeCrc() const
{
    WORD wHash=0;
    const TCHAR* pch;
    int i;

    pch=*this;
    for(i = Length();i > 0;i--, pch++)
    {
        wHash = wHash << 7 ^ wHash >> (16-7) ^ (TCHAR)CharUpper((LPTSTR)((DWORD)(*pch)));
    }

    return wHash;
}
#pragma warning(default:4305)

//+---------------------------------------------------------------------------
//
//  Member:     CStr::Load
//
//  Synopsis:   Initializes the CStr from a stream
//
//----------------------------------------------------------------------------
HRESULT
CStr::Load(IStream * pstm)
{
    DWORD cch;
    HRESULT hr;

    hr = THR(pstm->Read(&cch, sizeof(DWORD), NULL));
    if (hr)
        goto Cleanup;

    if (cch == 0xFFFFFFFF)
    {
        Free();
    }
    else
    {
        hr = THR(Set(NULL, cch));
        if (hr)
            goto Cleanup;

        if (cch)
        {
            hr = THR(pstm->Read(_pch, cch * sizeof(TCHAR), NULL));
            if (hr)
                goto Cleanup;
        }
    }

Cleanup:
    RRETURN(hr);
}

//+---------------------------------------------------------------------------
//
//  Member:     CStr::Save
//
//  Synopsis:   Writes the contents of the CStr to a stream
//
//----------------------------------------------------------------------------
HRESULT
CStr::Save(IStream * pstm) const
{
    DWORD   cch = _pch ? Length() : 0xFFFFFFFF;
    HRESULT hr;

    hr = THR(pstm->Write(&cch, sizeof(DWORD), NULL));
    if (hr)
        goto Cleanup;

    if (cch && cch != 0xFFFFFFFF)
    {
        hr = THR(pstm->Write(_pch, cch * sizeof(TCHAR), NULL));
        if (hr)
            goto Cleanup;
    }

Cleanup:
    RRETURN(hr);
}

//+---------------------------------------------------------------------------
//
//  Member:     CStr::GetSaveSize
//
//  Synopsis:   Returns the number of bytes which will be written by
//              CStr::Save
//
//----------------------------------------------------------------------------
ULONG
CStr::GetSaveSize() const
{
    return(sizeof(DWORD) + (_pch ? (Length() * sizeof(TCHAR)) : 0));
}