/*++

Copyright (c) 1996  Microsoft Corporation

Module Name: Shell String Class Implementation

    shstr.cpp

Author:

    Zeke Lucas (zekel)  27-Oct-96

Environment:

    User Mode - Win32

Revision History:


 Abstract:

    this allows automatic resizing and stuff.

  NOTE: this class is specifically designed to be used as a stack variable


--*/

#include "stock.h"
#pragma hdrstop

#include <shstr.h>


#define MALLOC(c)       LocalAlloc(LPTR, (c))
#define FREE(p)         LocalFree(p)

//
//  ShStr Public Methods
//

//
//  Constructors
//

ShStr :: ShStr () 
{
    _szDefaultBuffer[0] = '\0';
    _pszStr = _szDefaultBuffer;
    _cchSize = ARRAYSIZE(_szDefaultBuffer);
}


/**************
    StrStr SetStr() methods

  Return:   
    Success - a pointer to the object
    Failure - NULL
**************/

HRESULT 
ShStr :: SetStr (LPCSTR pszStr)
{
    Reset();
    
    return _SetStr(pszStr);

}

HRESULT 
ShStr :: SetStr (LPCSTR pszStr, DWORD cchStr)
{
    Reset();
    
    return _SetStr(pszStr, cchStr);

}

HRESULT 
ShStr :: SetStr (LPCWSTR pwszStr, DWORD cchStr)
{
    Reset();
    
    return _SetStr(pwszStr, cchStr);

}


HRESULT
ShStr :: Append(LPCTSTR pszStr, DWORD cchStr)
{
    HRESULT hr = S_OK;

    if(pszStr)
    {
        DWORD cchLen = GetLen();

        if(cchStr == (DWORD) -1)
            cchStr = lstrlen(pszStr);

        //
        //  StrCpyN automagically appends the null term, 
        //  so we need to give room for it
        //
        cchStr++;

        if(SUCCEEDED(SetSize(cchStr + cchLen)))
            StrCpyN(_pszStr + cchLen, pszStr, cchStr);
        else
            hr = E_OUTOFMEMORY;
    }
    else
        hr = E_INVALIDARG;

    return hr;
}

ShStr *
ShStr :: Clone()
{
    ShStr *pshstr = new ShStr;

    if (pshstr)
    {
        pshstr->SetStr(_pszStr);
    
        if(pshstr->IsValid())
            return pshstr;
    }

    if(pshstr)
        delete pshstr;

    return NULL;
}

LPSTR 
ShStr :: CloneStrA()
#ifdef UNICODE
{
    LPSTR pszStr = NULL;

    if(_pszStr)
    {
        DWORD cchStr;
    
        cchStr = WideCharToMultiByte(CP_ACP, 0,
            _pszStr, -1,
            NULL, 0,
            NULL, NULL);

        ASSERT(cchStr);

        if(cchStr)
        {
            pszStr = (LPSTR) MALLOC (CbFromCch(cchStr +1));

            if(pszStr)
            {
                cchStr = WideCharToMultiByte(CP_ACP, 0,
                    _pszStr, -1,
                    pszStr, cchStr,
                    NULL, NULL);
                ASSERT (cchStr);
            }
        }
    }

    return pszStr;
}

#else //!UNICODE

    {return _pszStr ? StrDupA(_pszStr) : NULL;}
#endif //UNICODE


#ifdef UNICODE
  
#endif
LPWSTR 
ShStr :: CloneStrW()
#ifdef UNICODE
    {return _pszStr ? StrDupW(_pszStr) : NULL;}
#else //!UNICODE
{
    LPWSTR pwsz;
    DWORD cch = lstrlenA(_pszStr) +1;

    pwsz = (LPWSTR) MALLOC (sizeof(WCHAR) * cch);
    
    if(pwsz)
        MultiByteToWideChar(CP_ACP, 0,
            _pszStr, -1,
            pwsz, cch);

    return pwsz;
}
#endif //UNICODE


/**************
    ShStr Utility methods

**************/


/**************
    ShStr SetSize method

    Sets the size of the internal buffer if larger than default

  Return:   
    Success - a pointer to the object
    Failure - NULL
**************/
HRESULT
ShStr :: SetSize(DWORD cchSize)
{
    HRESULT hr = S_OK;
    DWORD cchNewSize = _cchSize;

    ASSERT(!(_cchSize % DEFAULT_SHSTR_LENGTH));

    // so that we always allocate in increments
    while (cchSize > cchNewSize)
        cchNewSize <<= 2;
    
    if(cchNewSize != _cchSize)
    {
        if(cchNewSize > DEFAULT_SHSTR_LENGTH)
        {
            LPTSTR psz;

            psz = (LPTSTR) LocalAlloc(LPTR, CbFromCch(cchNewSize));
    
            if(psz)
            {
                StrCpyN(psz, _pszStr, cchSize);
                Reset();
                _cchSize = cchNewSize;
                _pszStr = psz;
            }
            else 
                hr = E_OUTOFMEMORY;
        }
        else
        {
            if (_pszStr && _cchSize) 
                StrCpyN(_szDefaultBuffer, _pszStr, ARRAYSIZE(_szDefaultBuffer));

            Reset();

            _pszStr = _szDefaultBuffer;
        }
    }

    return hr;
}

#ifdef DEBUG
BOOL
ShStr :: IsValid()
{
    BOOL fRet = TRUE;

    if(!_pszStr)
        fRet = FALSE;

    ASSERT( ((_cchSize != ARRAYSIZE(_szDefaultBuffer)) && (_pszStr != _szDefaultBuffer)) ||
            ((_cchSize == ARRAYSIZE(_szDefaultBuffer)) && (_pszStr == _szDefaultBuffer)) );

    ASSERT(!(_cchSize % DEFAULT_SHSTR_LENGTH));

    return fRet;
}
#endif //DEBUG

VOID 
ShStr :: Reset()
{
    if (_pszStr && (_cchSize != ARRAYSIZE(_szDefaultBuffer))) 
        LocalFree(_pszStr);

    _szDefaultBuffer[0] = TEXT('\0');
    _pszStr = _szDefaultBuffer;
    _cchSize = ARRAYSIZE(_szDefaultBuffer);
}

#define IsWhite(c)      ((DWORD) (c) > 32 ? FALSE : TRUE)
VOID
ShStr :: Trim()
{

    if(_pszStr)
    {
        // APPCOMPAT - NETSCAPE compatibility - zekel 29-JAN-97
        //  we want to leave one space in the string
        TCHAR chFirst = *_pszStr;

        //  first trim the backside
        TCHAR *pchLastWhite = NULL;
        LPTSTR pch = _pszStr;
        
        // the front side
        while (*pch && IsWhite(*pch))
            pch = CharNext(pch);

        if (pch > _pszStr)
        {
            LPTSTR pchDst = _pszStr;

            while (*pchDst = *pch)
            {
                pch = CharNext(pch);
                pchDst = CharNext(pchDst);
            }
        }

        // then the backside
        for (pch = _pszStr; *pch; pch = CharNext(pch))
        {
            if(pchLastWhite && !IsWhite(*pch))
                pchLastWhite = NULL;
            else if(!pchLastWhite && IsWhite(*pch))
                pchLastWhite = pch;
        }

        if(pchLastWhite)
            *pchLastWhite = TEXT('\0');

        if(TEXT(' ') == chFirst && !*_pszStr)
        {
            _pszStr[0] = TEXT(' ');
            _pszStr[1] = TEXT('\0');
        }
    }
}

    


//
//  ShStr Private Methods
//


/**************
    StrStr Set* methods

  Return:   
    Success - a pointer to the object
    Failure - NULL
**************/
HRESULT 
ShStr :: _SetStr(LPCSTR pszStr)
{
    HRESULT hr = S_FALSE;

    if(pszStr)
    {
        DWORD cchStr;

        cchStr = lstrlenA(pszStr);
    
        if(cchStr)
        {
            hr = SetSize(cchStr +1);

            if (SUCCEEDED(hr))
#ifdef UNICODE
                MultiByteToWideChar(CP_ACP, 0,
                    pszStr, -1,
                    _pszStr, _cchSize);
#else //!UNICODE
                lstrcpyA(_pszStr, pszStr);
#endif //UNICODE
        }
    }

    return hr;
}

HRESULT 
ShStr :: _SetStr(LPCSTR pszStr, DWORD cchStr)
{
    HRESULT hr = S_FALSE;

    if(pszStr && cchStr)
    {
        if (cchStr == (DWORD) -1)
            cchStr = lstrlenA(pszStr);

        hr = SetSize(cchStr +1);

        if(SUCCEEDED(hr))
        {
#ifdef UNICODE
            MultiByteToWideChar(CP_ACP, 0,
                pszStr, cchStr,
                _pszStr, _cchSize);
            _pszStr[cchStr] = TEXT('\0');

#else //!UNICODE
            StrCpyN(_pszStr, pszStr, (++cchStr < _cchSize ? cchStr : _cchSize) );
#endif //UNICODE
        }
    }

    return hr;
}

HRESULT 
ShStr :: _SetStr (LPCWSTR pwszStr, DWORD cchStrIn)
{
    DWORD cchStr = cchStrIn;
    HRESULT hr = S_FALSE;

    if(pwszStr && cchStr)
    {
        if (cchStr == (DWORD) -1)
#ifdef UNICODE
            cchStr = lstrlen(pwszStr);
#else //!UNICODE
        cchStr = WideCharToMultiByte(CP_ACP, 0,
            pwszStr, cchStrIn,
            NULL, 0,
            NULL, NULL);
#endif //UNICODE

        if(cchStr)
        {
            hr = SetSize(cchStr +1);

            if(SUCCEEDED(hr))
            {
#ifdef UNICODE 
                StrCpyN(_pszStr, pwszStr, (cchStr + 1< _cchSize ? cchStr + 1: _cchSize));
#else //!UNICODE
                cchStr = WideCharToMultiByte(CP_ACP, 0,
                    pwszStr, cchStrIn,
                    _pszStr, _cchSize,
                    NULL, NULL);
                _pszStr[cchStr < _cchSize ? cchStr : _cchSize] = TEXT('\0');
                ASSERT (cchStr);
#endif //UNICODE
            }
        }
#ifdef DEBUG
        else
        {
            DWORD dw;
            dw = GetLastError();
        }
#endif //DEBUG

    }
#ifdef DEBUG
    else
    {
        DWORD dw;
        dw = GetLastError();
    }
#endif //DEBUG

    return hr;
}

#if 0  //DISABLED until i have written the SHUrl* functions - zekel 7-Nov-96
//
//  UrlStr Methods
//
  
  UrlStr &
UrlStr::SetUrl(LPCSTR pszUrl)
{
    return SetUrl(pszUrl, (DWORD) -1);
}

  UrlStr &
UrlStr::SetUrl(LPCWSTR pwszUrl)
{
    return SetUrl(pwszUrl, (DWORD) -1);
}

  UrlStr &
UrlStr::SetUrl(LPCSTR pszUrl, DWORD cchUrl)
{
    _strUrl.SetStr(pszUrl, cchUrl);
    return *this;
}

  UrlStr &
UrlStr::SetUrl(LPCWSTR pwszUrl, DWORD cchUrl)
{
    _strUrl.SetStr(pwszUrl, cchUrl);
    return *this;
}

 
UrlStr::operator LPCTSTR()
{
    return _strUrl.GetStr();
}

  
UrlStr::operator SHSTR()
{
    return _strUrl;
}




  HRESULT
UrlStr::Combine(LPCTSTR pszUrl, DWORD dwFlags)
{
    SHSTR strRel;
    SHSTR strOut;
    HRESULT hr;

    strRel.SetStr(pszUrl);

    hr = UrlCombine(_strUrl.GetStr(), 
    hr = SHUrlParse(&_strUrl, &strRel, &strOut, NULL, URL_PARSE_CREATE);

    if(SUCCEEDED(hr))
        _strUrl = strOut;

    return hr;
}

/*
    ShStr &GetLocation();
    ShStr &GetAnchor();
    ShStr &GetQuery();

    HRESULT Canonicalize(DWORD dwFlags);
    HRESULT Combine(LPCTSTR pszUrl, DWORD dwFlags);
    HRESULT Encode(DWORD dwFlags);
    HRESULT EncodeSpaces()
        {return Encode(URL_ENCODE_SPACES_ONLY)}
    HRESULT Decode(DWORD dwFlags)
*/
#endif  //DISABLED
