/*++

Copyright (C) Microsoft Corporation, 1995 - 1999

Module Name:

    text

Abstract:

    This module provides the runtime code to support the CTextString class.

Author:

    Doug Barlow (dbarlow) 11/7/1995

Environment:

    Win32, C++ w/ Exceptions

Notes:

    None

--*/

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <SCardLib.h>


/*++

CTextString::operator=:

    These methods set the CTextString object to the given value, properly
    adjusting the object to the type of text.

Arguments:

    tz supplies the new value as a CTextString object.
    sz supples the new value as an LPCSTR object (ANSI).
    wsz supplies the new value as an LPCWSTR object (Unicode).

Return Value:

    A reference to the CTextString object.

Author:

    Doug Barlow (dbarlow) 10/5/1995

--*/

CTextString &
CTextString::operator=(
    const CTextString &tz)
{

    //
    // See what the other CTextString object has that's good, and copy it over
    // here.
    //

    switch (m_fFlags = tz.m_fFlags)
    {
    case fNoneGood:
        // Nothing's Good!?!  ?Error?
        throw (DWORD)ERROR_INTERNAL_ERROR;
        break;

    case fAnsiGood:
        // The ANSI buffer is good.
        m_bfAnsi = tz.m_bfAnsi;
        break;

    case fUnicodeGood:
        // The Unicode buffer is good.
        m_bfUnicode = tz.m_bfUnicode;
        break;

    case fBothGood:
        // Everything is good.
        m_bfAnsi = tz.m_bfAnsi;
        m_bfUnicode = tz.m_bfUnicode;
        break;

    default:
        // Internal error.
        throw (DWORD)ERROR_INTERNAL_ERROR;
        break;
    }
    return *this;
}

LPCSTR
CTextString::operator=(
    LPCSTR sz)
{
    DWORD length;

    //
    // Reset the ANSI buffer.
    //

    if (NULL != sz)
    {
        length = Length(sz) + 1;
        m_bfAnsi.Set((LPBYTE)sz, length * sizeof(CHAR));
    }
    else
        m_bfAnsi.Reset();
    m_fFlags = fAnsiGood;
    return sz;
}

LPCWSTR
CTextString::operator=(
    LPCWSTR wsz)
{
    DWORD length;


    //
    // Reset the Unicode Buffer.
    //

    if (NULL != wsz)
    {
        length = Length(wsz) + 1;
        m_bfUnicode.Set((LPBYTE)wsz, length * sizeof(WCHAR));
    }
    else
        m_bfUnicode.Reset();
    m_fFlags = fUnicodeGood;
    return wsz;
}


/*++

CTextString::operator+=:

    These methods append the given data to the existing CTextString object
    value, properly adjusting the object to the type of text.

Arguments:

    tz supplies the new value as a CTextString object.
    sz supples the new value as an LPCSTR object (ANSI).
    wsz supplies the new value as an LPCWSTR object (Unicode).

Return Value:

    A reference to the CTextString object.

Author:

    Doug Barlow (dbarlow) 10/5/1995

--*/

CTextString &
CTextString::operator+=(
    const CTextString &tz)
{

    //
    // Append the other's good value to our value.
    //

    switch (tz.m_fFlags)
    {
    case fNoneGood:
        throw (DWORD)ERROR_INTERNAL_ERROR;
        break;

    case fAnsiGood:
        *this += (LPCSTR)tz.m_bfAnsi.Access();
        break;

    case fUnicodeGood:
        *this += (LPCWSTR)tz.m_bfUnicode.Access();
        break;

    case fBothGood:
#ifdef UNICODE
        *this += (LPCWSTR)tz.m_bfUnicode.Access();
#else
        *this += (LPCSTR)tz.m_bfAnsi.Access();
#endif
        break;

    default:
        throw (DWORD)ERROR_INTERNAL_ERROR;
        break;
    }
    return *this;
}

LPCSTR
CTextString::operator+=(
    LPCSTR sz)
{
    DWORD length;


    //
    // Extend ourself as an ANSI string.
    //

    if (NULL != sz)
    {
        length = Length(sz);
        if (0 < length)
        {
            length += 1;
            length *= sizeof(CHAR);
            Ansi();
            if (0 < m_bfAnsi.Length())
                m_bfAnsi.Resize(m_bfAnsi.Length() - sizeof(CHAR), TRUE);
            m_bfAnsi.Append((LPBYTE)sz, length);
            m_fFlags = fAnsiGood;
        }
    }
    return (LPCSTR)m_bfAnsi.Access();
}

LPCWSTR
CTextString::operator+=(
    LPCWSTR wsz)
{
    DWORD length;


    //
    // Extend ourself as a Unicode string.
    //

    if (NULL != wsz)
    {
        length = Length(wsz);
        if (0 < length)
        {
            length += 1;
            length *= sizeof(WCHAR);
            Unicode();
            if (0 < m_bfUnicode.Length())
                m_bfUnicode.Resize(m_bfUnicode.Length() - sizeof(WCHAR), TRUE);
            m_bfUnicode.Append((LPBYTE)wsz, length);
            m_fFlags = fUnicodeGood;
        }
    }
    return (LPCWSTR)m_bfUnicode.Access();
}


/*++

Unicode:

    This method returns the CTextString object as a Unicode string.

Arguments:

    None

Return Value:

    The value of the object expressed in Unicode.

Author:

    Doug Barlow (dbarlow) 10/5/1995

--*/

LPCWSTR
CTextString::Unicode(
    void)
{
    int length;


    //
    // See what data we've got, and if any conversion is necessary.
    //

    switch (m_fFlags)
    {
    case fNoneGood:
        // No valid values.  Report an error.
        throw (DWORD)ERROR_INTERNAL_ERROR;
        break;

    case fAnsiGood:
        // The ANSI value is good.  Convert it to Unicode.
        if (0 < m_bfAnsi.Length())
        {
            length =
                MultiByteToWideChar(
                    GetACP(),
                    MB_PRECOMPOSED,
                    (LPCSTR)m_bfAnsi.Access(),
                    m_bfAnsi.Length() - sizeof(CHAR),
                    NULL,
                    0);
            if (0 == length)
                throw GetLastError();
            m_bfUnicode.Resize((length + 1) * sizeof(WCHAR));
            length =
                MultiByteToWideChar(
                    GetACP(),
                    MB_PRECOMPOSED,
                    (LPCSTR)m_bfAnsi.Access(),
                    m_bfAnsi.Length() - sizeof(CHAR),
                    (LPWSTR)m_bfUnicode.Access(),
                    length);
            if (0 == length)
                throw GetLastError();
            *(LPWSTR)m_bfUnicode.Access(length * sizeof(WCHAR)) = 0;
        }
        else
            m_bfUnicode.Reset();
        m_fFlags = fBothGood;
        break;

    case fUnicodeGood:
    case fBothGood:
        // The Unicode value is good.  Just return that.
        break;

    default:
        // Internal error.
        throw (DWORD)ERROR_INTERNAL_ERROR;
        break;
    }


    //
    // If we don't have any value, return a null string.
    //

    if (0 == m_bfUnicode.Length())
        return L"\000";     // Double NULLs to support Multistrings
    else
        return (LPCWSTR)m_bfUnicode.Access();
}


/*++

CTextString::Ansi:

    This method returns the value of the object expressed in an ANSI string.

Arguments:

    None

Return Value:

    The value of the object expressed as an ANSI string.

Author:

    Doug Barlow (dbarlow) 10/5/1995

--*/

LPCSTR
CTextString::Ansi(
    void)
{
    int length;


    //
    // See what data we've got, and if any conversion is necessary.
    //

    switch (m_fFlags)
    {
    case fNoneGood:
        // Nothing is good!?!  Return an error.
        throw (DWORD)ERROR_INTERNAL_ERROR;
        break;

    case fUnicodeGood:
        // The Unicode buffer is good.  Convert it to ANSI.
        if (0 < m_bfUnicode.Length())
        {
            length =
                WideCharToMultiByte(
                    GetACP(),
                    0,
                    (LPCWSTR)m_bfUnicode.Access(),
                    (m_bfUnicode.Length() / sizeof(WCHAR)) - 1,
                    NULL,
                    0,
                    NULL,
                    NULL);
            if (0 == length)
                throw GetLastError();
            m_bfAnsi.Resize((length + 1) * sizeof(CHAR));
            length =
                WideCharToMultiByte(
                    GetACP(),
                    0,
                    (LPCWSTR)m_bfUnicode.Access(),
                    (m_bfUnicode.Length() / sizeof(WCHAR)) - 1,
                    (LPSTR)m_bfAnsi.Access(),
                    length,
                    NULL,
                    NULL);
            if (0 == length)
                throw GetLastError();
            *(LPSTR)m_bfAnsi.Access(length * sizeof(CHAR)) = 0;
        }
        else
            m_bfAnsi.Reset();
        m_fFlags = fBothGood;
        break;

    case fAnsiGood:
    case fBothGood:
        // The ANSI buffer is good.  We'll return that.
        break;

    default:
        // An internal error.
        throw (DWORD)ERROR_INTERNAL_ERROR;
        break;
    }


    //
    // If there's nothing in the ANSI buffer, return a null string.
    //

    if (0 == m_bfAnsi.Length())
        return "\000";  // Double NULLs to support Multistrings
    else
        return (LPCSTR)m_bfAnsi.Access();
}


/*++

Compare:

    These methods compare the value of this object to another value, and return
    a comparative value.

Arguments:

    tz supplies the value to be compared as a CTextString object.
    sz supplies the value to be compared as an ANSI string.
    wsz supplies the value to be compared as a Unicode string.

Return Value:

    < 0 - The supplied value is less than this object.
    = 0 - The supplied value is equal to this object.
    > 0 - The supplies value is greater than this object.

Author:

    Doug Barlow (dbarlow) 10/5/1995

--*/

int
CTextString::Compare(
    const CTextString &tz)
{
    int nResult;


    //
    // See what we've got to compare.
    //

    switch (tz.m_fFlags)
    {
    case fNoneGood:
        // Nothing!?!  Complain.
        throw (DWORD)ERROR_INTERNAL_ERROR;
        break;

    case fBothGood:
    case fAnsiGood:
        // Use the ANSI version for fastest comparison.
        Ansi();
        nResult = CompareStringA(
                    LOCALE_USER_DEFAULT,
                    0,
                    (LPCSTR)m_bfAnsi.Access(),
                    (m_bfAnsi.Length() / sizeof(CHAR)) - 1,
                    (LPCSTR)tz.m_bfAnsi.Access(),
                    (tz.m_bfAnsi.Length() / sizeof(CHAR)) - 1);
        break;

    case fUnicodeGood:
        // The Unicode version is good.
        Unicode();
        nResult = CompareStringW(
                    LOCALE_USER_DEFAULT,
                    0,
                    (LPCWSTR)m_bfUnicode.Access(),
                    (m_bfUnicode.Length() / sizeof(WCHAR)) - 1,
                    (LPCWSTR)tz.m_bfUnicode.Access(),
                    (tz.m_bfUnicode.Length() / sizeof(WCHAR)) - 1);
        break;

    default:
        // Internal Error.
        throw (DWORD)ERROR_INTERNAL_ERROR;
        break;
    }
    return nResult;
}

int
CTextString::Compare(
    LPCSTR sz)
{
    int nResult;


    //
    // Make sure our ANSI version is good.
    //

    Ansi();


    //
    // Do an ANSI comparison.
    //

    nResult = CompareStringA(
                LOCALE_USER_DEFAULT,
                0,
                (LPCSTR)m_bfAnsi.Access(),
                (m_bfAnsi.Length() / sizeof(CHAR)) - 1,
                sz,
                Length(sz));
    return nResult;
}

int
CTextString::Compare(
    LPCWSTR wsz)
{
    int nResult;


    //
    // Make sure our Unicode version is good.
    //

    Unicode();


    //
    // Do the comparison using Unicode.
    //

    nResult = CompareStringW(
                LOCALE_USER_DEFAULT,
                0,
                (LPCWSTR)m_bfUnicode.Access(),
                (m_bfUnicode.Length() / sizeof(WCHAR)) - 1,
                wsz,
                Length(wsz));
    return nResult;
}


/*++

Length:

    These routines return the length of strings, in Characters, not including
    any trailing null characters.

Arguments:

    sz supplies the value whos length is to be returned as an ANSI string.
    wsz supplies the value whos length is to be returned as a Unicode string.

Return Value:

    The length of the string, in characters, excluding the trailing null.

Author:

    Doug Barlow (dbarlow) 2/17/1997

--*/

DWORD
CTextString::Length(
    void)
{
    DWORD dwLength = 0;

    switch (m_fFlags)
    {
    case fNoneGood:
        // Nothing is good!?!  Return an error.
        throw (DWORD)ERROR_INTERNAL_ERROR;
        break;

    case fAnsiGood:
        // The ANSI buffer is good.  We'll return its length.
        if (0 < m_bfAnsi.Length())
            dwLength = (m_bfAnsi.Length() / sizeof(CHAR)) - 1;
        break;

    case fUnicodeGood:
        // The Unicode buffer is good.  Return it's length.
        if (0 < m_bfUnicode.Length())
            dwLength = (m_bfUnicode.Length() / sizeof(WCHAR)) - 1;
        break;

    case fBothGood:
#ifdef UNICODE
        // The Unicode buffer is good.  Return it's length.
        if (0 < m_bfUnicode.Length())
            dwLength = (m_bfUnicode.Length() / sizeof(WCHAR)) - 1;
#else
        // The ANSI buffer is good.  We'll return its length.
        if (0 < m_bfAnsi.Length())
            dwLength = (m_bfAnsi.Length() / sizeof(CHAR)) - 1;
#endif
        break;

    default:
        // An internal error.
        throw (DWORD)ERROR_INTERNAL_ERROR;
        break;
    }
    return dwLength;
}

DWORD
CTextString::Length(
    LPCWSTR wsz)
{
    return lstrlenW(wsz);
}

DWORD
CTextString::Length(
    LPCSTR sz)
{
    return lstrlenA(sz);
}


//
//==============================================================================
//
//  CTextMultistring
//

/*++

Length:

    These routines return the length of strings, in Characters, not including
    any trailing null characters.

Arguments:

    sz supplies the value whos length is to be returned as an ANSI string.
    wsz supplies the value whos length is to be returned as a Unicode string.

Return Value:

    The length of the string, in characters, excluding the trailing null.

Author:

    Doug Barlow (dbarlow) 2/17/1997

--*/

DWORD
CTextMultistring::Length(
    LPCWSTR wsz)
{
    return MStrLen(wsz) - 1;
}

DWORD
CTextMultistring::Length(
    LPCSTR sz)
{
    return MStrLen(sz) - 1;
}


/*++

Length:

    This routine returns the length of the stored MultiString in characters,
    including the trailing NULL characters.

Arguments:

    None

Return Value:

    The length, in characters, including trailing nulls.

Author:

    Doug Barlow (dbarlow) 2/25/1998

--*/

DWORD
CTextMultistring::Length(
    void)
{
    return CTextString::Length() + 1;
}


/*++

operator=:

    These methods assign values to the MultiString object.

Arguments:

    tz supplies the new value as a CTextMultistring
    sz supplies the new value as an ANSI string
    wsz supplies the new value as a UNICODE string

Return Value:

    The assigned string value, in its original form.

Author:

    Doug Barlow (dbarlow) 2/25/1998

--*/

CTextMultistring &
CTextMultistring::operator=(
    const CTextMultistring &tz)
{
    CTextString::operator=((const CTextString &)tz);
    return *this;
}

LPCSTR
CTextMultistring::operator=(
    LPCSTR sz)
{
    return CTextString::operator=(sz);
}

LPCWSTR
CTextMultistring::operator=(
    LPCWSTR wsz)
{
    return CTextString::operator=(wsz);
}


/*++

operator+=:

    These methods append values to the MultiString object.

Arguments:

    tz supplies the value to be appended as a CTextMultistring
    sz supplies the value to be appended as an ANSI string
    wsz supplies the value to be appended as a UNICODE string

Return Value:

    The concatenated string, in the form of the appended string.

Author:

    Doug Barlow (dbarlow) 2/25/1998

--*/

CTextMultistring &
CTextMultistring::operator+=(
    const CTextMultistring &tz)
{
    CTextString::operator+=((const CTextString &)tz);
    return *this;
}

LPCSTR
CTextMultistring::operator+=(
    LPCSTR sz)
{
    return CTextString::operator+=(sz);
}

LPCWSTR
CTextMultistring::operator+=(
    LPCWSTR wsz)
{
    return CTextString::operator+=(wsz);
}

