//
// The developer of the original code and/or files is Tripwire, Inc.
// Portions created by Tripwire, Inc. are copyright (C) 2000-2018 Tripwire,
// Inc. Tripwire is a registered trademark of Tripwire, Inc.  All rights
// reserved.
//
// This program is free software.  The contents of this file are subject
// to the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 2 of the License, or (at your
// option) any later version.  You may redistribute it and/or modify it
// only in compliance with the GNU General Public License.
//
// This program is distributed in the hope that it will be useful.
// However, this program is distributed AS-IS WITHOUT ANY
// WARRANTY; INCLUDING THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS
// FOR A PARTICULAR PURPOSE.  Please see the GNU General Public License
// for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
// USA.
//
// Nothing in the GNU General Public License or any other license to use
// the code or files shall permit you to use Tripwire's trademarks,
// service marks, or other intellectual property without Tripwire's
// prior written consent.
//
// If you have any questions, please contact Tripwire, Inc. at either
// info@tripwire.org or www.tripwire.org.
//
///////////////////////////////////////////////////////////////////////////////
// displayencoder.cpp
//

//=========================================================================
// INCLUDES
//=========================================================================

#include "stdcore.h"
#include "displayencoder.h"
#include "charutil.h"
#include "debug.h"
#include "twlocale.h"
#include "stringutil.h"
#include "errorutil.h"
#include "ntmbs.h"
#include "codeconvert.h"

//=========================================================================
// STANDARD LIBRARY INCLUDES
//=========================================================================

#include <iomanip>
#include <iterator>

//=========================================================================
// DEFINES AND MACROS
//=========================================================================

// uncomment this to test schema
// #define TSS_DO_SCHEMA_VALIDATION

//////////////////////////////////////////////////////////////////////////////
// ENCODER UTILITIES
//////////////////////////////////////////////////////////////////////////////


inline bool IsSingleTCHAR(TSTRING::const_iterator first, TSTRING::const_iterator last)
{
    return (first + 1 == last);
}

//////////////////////////////////////////////////////////////////////////////
// CHAR ENCODER INTERFACE
//////////////////////////////////////////////////////////////////////////////

// all derived classes should encode a char to "EscapeChar() + Identifier() + Encode( char ) [ + Identifier() ]"

class iCharEncoder
{
public:
    virtual ~iCharEncoder(){};

    virtual bool NeedsEncoding(TSTRING::const_iterator first, TSTRING::const_iterator last) const = 0;
    // Determines if character identified by [first,last) needs encoding.
    // Returns true if it does.

    virtual TSTRING EncodeRoundtrip(TSTRING::const_iterator first, TSTRING::const_iterator last) const = 0;
    // Encodes character identified by [first,last) in such a way that it
    // can be decoded by Decode().  Returns encoded character sequence.

    virtual TSTRING EncodePretty(TSTRING::const_iterator first, TSTRING::const_iterator last) const = 0;
    // Encodes character identified by [first,last) in a manner that is not roundtrip,
    // but looks good.  Returns encoded character sequence.

    virtual TSTRING Decode(TSTRING::const_iterator* pcur, const TSTRING::const_iterator end) const = 0;
    // Decodes character sequence beginning with '*pcur' and ending before 'end'.
    // Returns decoded character or sequence of characters.  Advances *pcur beyond
    // the last character decoded.


    virtual TCHAR Identifier() const = 0;

    static TCHAR EscapeChar()
    {
        return char_escape;
    }

protected:
    static TCHAR char_escape;
};


class cNonNarrowableCharEncoder : public iCharEncoder
{
public:
    virtual ~cNonNarrowableCharEncoder()
    {
    }

    virtual bool NeedsEncoding(TSTRING::const_iterator first, TSTRING::const_iterator last) const;

    virtual TSTRING EncodeRoundtrip(TSTRING::const_iterator first, TSTRING::const_iterator last) const;

    virtual TSTRING EncodePretty(TSTRING::const_iterator first, TSTRING::const_iterator last) const;

    virtual TSTRING Decode(TSTRING::const_iterator* cur, const TSTRING::const_iterator end) const;

    virtual TCHAR Identifier() const;

private:
    static TCHAR char_identifier;
    static TCHAR char_replace;
};


class cNonPrintableCharEncoder : public iCharEncoder
{
public:
    cNonPrintableCharEncoder(bool f_allowWS) : m_allowWS(f_allowWS){};

    virtual ~cNonPrintableCharEncoder()
    {
    }

    virtual bool NeedsEncoding(TSTRING::const_iterator first, TSTRING::const_iterator last) const;

    virtual TSTRING EncodeRoundtrip(TSTRING::const_iterator first, TSTRING::const_iterator last) const;

    virtual TSTRING EncodePretty(TSTRING::const_iterator first, TSTRING::const_iterator last) const;

    virtual TSTRING Decode(TSTRING::const_iterator* cur, const TSTRING::const_iterator end) const;

    virtual TCHAR Identifier() const;

private:
    static TCHAR char_identifier;
    static TCHAR char_replace;

    bool m_allowWS;
};

class cQuoteCharEncoder : public iCharEncoder
{
public:
    virtual ~cQuoteCharEncoder()
    {
    }

    virtual bool NeedsEncoding(TSTRING::const_iterator first, TSTRING::const_iterator last) const;

    virtual TSTRING EncodeRoundtrip(TSTRING::const_iterator first, TSTRING::const_iterator last) const;

    virtual TSTRING EncodePretty(TSTRING::const_iterator first, TSTRING::const_iterator last) const;

    virtual TSTRING Decode(TSTRING::const_iterator* cur, const TSTRING::const_iterator end) const;

    virtual TCHAR Identifier() const;

private:
    static TCHAR char_test;
    static TCHAR char_identifier;
    static TCHAR char_replace;
};


class cBackslashCharEncoder : public iCharEncoder
{
public:
    virtual ~cBackslashCharEncoder()
    {
    }

    virtual bool NeedsEncoding(TSTRING::const_iterator first, TSTRING::const_iterator last) const;

    virtual TSTRING EncodeRoundtrip(TSTRING::const_iterator first, TSTRING::const_iterator last) const;

    virtual TSTRING EncodePretty(TSTRING::const_iterator first, TSTRING::const_iterator last) const;

    virtual TSTRING Decode(TSTRING::const_iterator* cur, const TSTRING::const_iterator end) const;

    virtual TCHAR Identifier() const;

private:
    static TCHAR char_test;
    static TCHAR char_identifier;
    static TCHAR char_replace;
};


//////////////////////////////////////////////////////////////////////////////
// CHARACTER SPECIALIZATIONS
//////////////////////////////////////////////////////////////////////////////

TCHAR iCharEncoder::char_escape = _T('\\');

TCHAR cNonNarrowableCharEncoder::char_identifier = _T('x');
TCHAR cNonPrintableCharEncoder::char_identifier  = _T('x');
TCHAR cQuoteCharEncoder::char_identifier         = _T('\"');
TCHAR cBackslashCharEncoder::char_identifier     = _T('\\');

TCHAR cBackslashCharEncoder::char_test = cBackslashCharEncoder::char_identifier;
TCHAR cQuoteCharEncoder::char_test     = cQuoteCharEncoder::char_identifier;

TCHAR cBackslashCharEncoder::char_replace = cBackslashCharEncoder::char_identifier;
TCHAR cQuoteCharEncoder::char_replace     = cQuoteCharEncoder::char_identifier;

TCHAR cNonNarrowableCharEncoder::char_replace = _T('?');
TCHAR cNonPrintableCharEncoder::char_replace  = _T('?');

//////////////////////////////////////////////////////////////////////////////
// TESTS
//////////////////////////////////////////////////////////////////////////////

bool cNonNarrowableCharEncoder::NeedsEncoding(TSTRING::const_iterator first, TSTRING::const_iterator last) const
{
    return false; // all chars are narrow
}


bool cNonPrintableCharEncoder::NeedsEncoding(TSTRING::const_iterator first, TSTRING::const_iterator last) const
{
    // TODO:BAM -- handle this with mb chars
    // std::isprint<wchar_t> does a wctob() on the wchar!!?!?!
    // what's up with that?  Maybe ignore this all together and
    // just do a isprint like KAI does?

    // HYPOTHESIS: all mb characters are printable.  only sb ASCII
    // chars that would have C isprint() return false actually aren't printable
    // So escape chars, and tabs and such are only in sb chars that C isprint() would check.
    // HMMMM: true in all locales, though? (LC_CTYPE is checked for C isprint(), though...)

    // Sooooo... it should be something like
    //
    //  #ifdef _UNICODE
    //      char nch = wctob( ch );
    //      return( nch != EOF && ! isprint( nch ) );
    //  #else
    //      return( ! isprint( ch ) );
    //  #endif
    //

    // assuming all unprintable chars are one TCHAR long
    if (!IsSingleTCHAR(first, last))
        return false;

    if (m_allowWS && cCharEncoderUtil::IsWhiteSpace(*first))
        return false;

    return cCharEncoderUtil::IsPrintable(*first);
}

bool cQuoteCharEncoder::NeedsEncoding(TSTRING::const_iterator first, TSTRING::const_iterator last) const
{
    return (IsSingleTCHAR(first, last) && (*first == char_test));
}

bool cBackslashCharEncoder::NeedsEncoding(TSTRING::const_iterator first, TSTRING::const_iterator last) const
{
    return (IsSingleTCHAR(first, last) && (*first == char_test));
}

//////////////////////////////////////////////////////////////////////////////
// ROUNDTRIP ENCODINGS
//////////////////////////////////////////////////////////////////////////////


TSTRING cNonNarrowableCharEncoder::EncodeRoundtrip(TSTRING::const_iterator first, TSTRING::const_iterator last) const
{
    TSTRING str;

    str += char_escape;
    str += char_identifier;
    str += cCharEncoderUtil::CharStringToHexValue(TSTRING(first, last));

    return str;
}


TSTRING cNonPrintableCharEncoder::EncodeRoundtrip(TSTRING::const_iterator first, TSTRING::const_iterator last) const
{
    ASSERT(IsSingleTCHAR(first, last)); // non-prints are single char (see NOTE above)

    TSTRING str;

    str += char_escape;
    str += char_identifier;
    str += cCharEncoderUtil::CharStringToHexValue(TSTRING(first, last));

    return str;
}


TSTRING cQuoteCharEncoder::EncodeRoundtrip(TSTRING::const_iterator first, TSTRING::const_iterator last) const
{
    // should just be a quote
    ASSERT(IsSingleTCHAR(first, last));
    ASSERT(*first == char_test);

    TSTRING str;

    str += char_escape;
    str += char_identifier;

    return str;
}


TSTRING cBackslashCharEncoder::EncodeRoundtrip(TSTRING::const_iterator first, TSTRING::const_iterator last) const
{
    // should just be a backslash
    ASSERT(IsSingleTCHAR(first, last));
    ASSERT(*first == char_test);

    TSTRING str;

    str += char_escape;
    str += char_identifier;

    return str;
}

//////////////////////////////////////////////////////////////////////////////
// NON-ROUNDTRIP ENCODINGS
//////////////////////////////////////////////////////////////////////////////


TSTRING cNonNarrowableCharEncoder::EncodePretty(TSTRING::const_iterator first, TSTRING::const_iterator last) const
{
    return EncodeRoundtrip(first, last);
}


TSTRING cNonPrintableCharEncoder::EncodePretty(TSTRING::const_iterator first, TSTRING::const_iterator last) const
{
    return EncodeRoundtrip(first, last);
}


TSTRING cQuoteCharEncoder::EncodePretty(TSTRING::const_iterator first, TSTRING::const_iterator last) const
{
    // should just be a quote
    ASSERT(IsSingleTCHAR(first, last));
    ASSERT(*first == char_test);

    return TSTRING(1, char_replace);
}


TSTRING cBackslashCharEncoder::EncodePretty(TSTRING::const_iterator first, TSTRING::const_iterator last) const
{
    // should just be a backslash
    ASSERT(IsSingleTCHAR(first, last));
    ASSERT(*first == char_test);

    return TSTRING(1, char_replace);
}

//////////////////////////////////////////////////////////////////////////////
// DECODINGS
//////////////////////////////////////////////////////////////////////////////


TSTRING cNonNarrowableCharEncoder::Decode(TSTRING::const_iterator* pcur, const TSTRING::const_iterator end) const
{
    // check preconditions
    if ((*pcur) >= end || *(*pcur) != Identifier())
        ThrowAndAssert(eBadDecoderInput());

    return (cCharEncoderUtil::DecodeHexToChar(pcur, end));
}


TSTRING cNonPrintableCharEncoder::Decode(TSTRING::const_iterator* pcur, const TSTRING::const_iterator end) const
{
    // check preconditions
    if ((*pcur) >= end || *(*pcur) != Identifier())
        ThrowAndAssert(eBadDecoderInput());

    return (cCharEncoderUtil::DecodeHexToChar(pcur, end));
}


TSTRING cQuoteCharEncoder::Decode(TSTRING::const_iterator* pcur, const TSTRING::const_iterator end) const
{
    if ((*pcur) >= end || *(*pcur) != Identifier())
        ThrowAndAssert(eBadDecoderInput());

    (*pcur)++; // advance past part decoded

    return TSTRING(1, Identifier());
}


TSTRING cBackslashCharEncoder::Decode(TSTRING::const_iterator* pcur, const TSTRING::const_iterator end) const
{
    if ((*pcur) >= end || *(*pcur) != Identifier())
        ThrowAndAssert(eBadDecoderInput());

    (*pcur)++; // advance past part decoded

    return TSTRING(1, Identifier());
}

//////////////////////////////////////////////////////////////////////////////
// IDENTIFIERS
//////////////////////////////////////////////////////////////////////////////


TCHAR cNonNarrowableCharEncoder::Identifier() const
{
    return char_identifier;
}


TCHAR cNonPrintableCharEncoder::Identifier() const
{
    return char_identifier;
}


TCHAR cQuoteCharEncoder::Identifier() const
{
    return char_identifier;
}

TCHAR cBackslashCharEncoder::Identifier() const
{
    return char_identifier;
}

//////////////////////////////////////////////////////////////////////////////
// UTILITIES
//////////////////////////////////////////////////////////////////////////////

bool cCharEncoderUtil::IsWhiteSpace(TCHAR ch)
{
    return (ch == '\r' || ch == '\n' || ch == '\t' || ch == '\v' || ch == ' ');
}

bool cCharEncoderUtil::IsPrintable(TCHAR ch)
{
#if USE_CLIB_LOCALE && !defined(__APPLE__)

    return (!isprint(ch)); // kludge for KAI

#else // USE_CLIB_LOCALE

    return (!std::isprint<TCHAR>(ch, std::locale()));

#endif // USE_CLIB_LOCALE
}

TSTRING cCharEncoderUtil::CharStringToHexValue(const TSTRING& str)
{
    TSTRING                 strOut;
    TSTRING::const_iterator at;

    for (at = str.begin(); at < str.end(); at++)
    {
        strOut += char_to_hex(*at);
    }

    return strOut;
}


TSTRING cCharEncoderUtil::HexValueToCharString(const TSTRING& str)
{
    TSTRING                 strOut;
    TSTRING::const_iterator at;

    for (at = str.begin(); at < str.end(); at += TCHAR_AS_HEX__IN_TCHARS)
    {
        strOut += hex_to_char(at, at + TCHAR_AS_HEX__IN_TCHARS);
    }

    return strOut;
}

TCHAR cCharEncoderUtil::hex_to_char(TSTRING::const_iterator first, TSTRING::const_iterator last)
{
    static const TCHAR max_char = std::numeric_limits<TCHAR>::max();
    static const TCHAR min_char = std::numeric_limits<TCHAR>::min();

    if (first + TCHAR_AS_HEX__IN_TCHARS != last)
        ThrowAndAssert(eBadHexConversion());

    TISTRINGSTREAM ss(TSTRING(first, last));
    ss.imbue(std::locale::classic());
    ss.fill(_T('0'));
    ss.setf(std::ios_base::hex, std::ios_base::basefield);

    unsigned long ch;
    ss >> ch;

    if (ss.bad() || ss.fail())
        ThrowAndAssert(eBadHexConversion(TSTRING(first, last)));
    if ((TCHAR)ch > max_char || (TCHAR)ch < min_char)
        ThrowAndAssert(eBadHexConversion(TSTRING(first, last)));

    return (TCHAR)ch;
}


TSTRING cCharEncoderUtil::char_to_hex(TCHAR ch)
{
    TOSTRINGSTREAM ss;

    ss.imbue(std::locale::classic());
    ss.fill(_T('0'));
    ss.width(TCHAR_AS_HEX__IN_TCHARS);
    ss.setf(std::ios_base::hex, std::ios_base::basefield);

    ss << tss::util::char_to_size(ch);

    if (ss.bad() || ss.fail() || ss.str().length() != TCHAR_AS_HEX__IN_TCHARS)
        ThrowAndAssert(eBadHexConversion(TSTRING(1, ch)));
    return ss.str();
}

TSTRING cCharEncoderUtil::DecodeHexToChar(TSTRING::const_iterator* pcur, const TSTRING::const_iterator end)
{
    // get hex numbers -- 2 chars
    TSTRING str;
    size_t  n = 0;
    for ((*pcur)++; n < TCHAR_AS_HEX__IN_TCHARS && (*pcur) != end; n++, (*pcur)++)
    {
        str += *(*pcur);
    }

    if (n != TCHAR_AS_HEX__IN_TCHARS)
        ThrowAndAssert(eBadDecoderInput());

    // convert hex numbers
    return HexValueToCharString(str);
}

//////////////////////////////////////////////////////////////////////////////
// ENCODER MEMBERS
//////////////////////////////////////////////////////////////////////////////


cEncoder::cEncoder(int e, int f) : m_fFlags(f)
{
    // add encodings
    if (e & NON_NARROWABLE)
        m_encodings.push_back(new cNonNarrowableCharEncoder);
    if (e & NON_PRINTABLE)
        m_encodings.push_back(new cNonPrintableCharEncoder(AllowWhiteSpace()));
    if (e & BACKSLASH)
        m_encodings.push_back(new cBackslashCharEncoder);
    if (e & DBL_QUOTE)
        m_encodings.push_back(new cQuoteCharEncoder);

    // assert that we weren't passed anything freaky
    ASSERT(0 == (e & ~(NON_NARROWABLE | NON_PRINTABLE | BACKSLASH | DBL_QUOTE)));

    // add flags
    ASSERT(!((m_fFlags & ROUNDTRIP) && (m_fFlags & NON_ROUNDTRIP)));

#ifdef TSS_DO_SCHEMA_VALIDATION

    // check assumptions about encodings
    ValidateSchema();

#endif
}


cEncoder::~cEncoder()
{
    sack_type::iterator itr;
    for (itr = m_encodings.begin(); itr != m_encodings.end(); ++itr)
        delete *itr;
}

bool cEncoder::RoundTrip() const
{
    return (0 != (m_fFlags & ROUNDTRIP));
}

bool cEncoder::AllowWhiteSpace() const
{
    return (0 != (m_fFlags & ALLOW_WHITESPACE));
}

//////////////////////////////////////////////////////////////////////////////
// ENCODER BASIC FUNCTIONALITY
//////////////////////////////////////////////////////////////////////////////

void cEncoder::Encode(TSTRING& strIn) const
{
    // TODO:BAM -- reserve space for strOut as an optimization?
    TSTRING strOut; // encoded string we will build up

    TSTRING::const_iterator       cur   = strIn.begin(); // pointer to working position in strIn
    const TSTRING::const_iterator end   = strIn.end();   // end of strIn
    TSTRING::const_iterator       first = end;           // identifies beginning of current character
    TSTRING::const_iterator       last  = end;           // identifies end of current character

    // while get next char (updates cur)
    while (cCharUtil::PopNextChar(cur, end, first, last))
    {
        bool                      fCharEncoded = false; // answers: did char need encoding?
        sack_type::const_iterator atE;

        // for all encoders
        for (atE = m_encodings.begin(); atE != m_encodings.end(); atE++)
        {
            // does char need encoding?
            if ((*atE)->NeedsEncoding(first, last))
            {
                strOut += Encode(first, last, atE);
                fCharEncoded = true;
                break; // each char should only fail at most one
                       // encoding test, so it should be cool to quit
            }
        }

        if (!fCharEncoded)
        {
            strOut.append(first, last); // simply add current char to output since it needed no encoding
        }
    }

    // pass back encoded string
    strIn = strOut;
}

TSTRING
cEncoder::Encode(TSTRING::const_iterator first, TSTRING::const_iterator last, sack_type::const_iterator encoding) const
{
    // encode it
    if (RoundTrip())
        return (*encoding)->EncodeRoundtrip(first, last);
    else
        return (*encoding)->EncodePretty(first, last);
}

void cEncoder::Decode(TSTRING& strIn) const
{
    // TODO:BAM -- reserve space for strOut as an optimization?
    TSTRING strOut; // decoded string we will build up

    TSTRING::const_iterator       cur   = strIn.begin(); // pointer to working position in strIn
    const TSTRING::const_iterator end   = strIn.end();   // end of strIn
    TSTRING::const_iterator       first = end;           // identifies beginning of current character
    TSTRING::const_iterator       last  = end;           // identifies end of current character


    // while get next char (updates cur)
    while (cCharUtil::PopNextChar(cur, end, first, last))
    {
        // is this char the escape character?
        if (IsSingleTCHAR(first, last) && *first == iCharEncoder::EscapeChar())
        {
            // get to identifier
            if (!cCharUtil::PopNextChar(cur, end, first, last))
                ThrowAndAssert(eBadDecoderInput());

            // this algorithm assumes that all identifiers are single char
            // so anything following the escape char should be a
            // single-char identifier
            if (!IsSingleTCHAR(first, last))
                THROW_INTERNAL("displayencoder.cpp");

            // determine to which encoding the identifier belongs
            bool                      fFoundEncoding = false;
            sack_type::const_iterator atE;
            for (atE = m_encodings.begin(); atE != m_encodings.end(); atE++)
            {
                // is this the right encoding?
                if (*first == (*atE)->Identifier())
                {
                    // this is the correct encoding....
                    fFoundEncoding = true;

                    // ...so decode char
                    strOut += (*atE)->Decode(&first, end); // should modify cur

                    cur = first; // advance current char pointer

                    break; // no need to run other tests after
                           // this because all identifiers should be unique
                }
            }

            if (!fFoundEncoding)
                ThrowAndAssert(eUnknownEscapeEncoding(TSTRING(1, *first)));
        }
        else
        {
            strOut.append(first, last);
        }
    }

    strIn = strOut;
}

//////////////////////////////////////////////////////////////////////////////
// ENCODER SCHEMA VALIDATION
//////////////////////////////////////////////////////////////////////////////

void cEncoder::ValidateSchema() const
{
    ASSERT(OnlyOneCatagoryPerChar());
    ASSERT(AllIdentifiersUnique());
}


// only tests single TCHAR characters (but of those, tests all of them)
bool cEncoder::OnlyOneCatagoryPerChar() const
{
    // TODO:BAM - man, is there a better way to do this?
    TCHAR   ch = std::numeric_limits<TCHAR>::min();
    TSTRING ach(1, ch);

    if (ch != std::numeric_limits<TCHAR>::max())
    {
        do
        {
            bool fFailedATest = false;

            ach[0] = ch;
            for (sack_type::const_iterator atE = m_encodings.begin(); atE != m_encodings.end(); atE++)
            {
                if ((*atE)->NeedsEncoding(ach.begin(), ach.end()))
                {
                    if (fFailedATest)
                        return false; // each char can only fail one test
                    else
                        fFailedATest = true;
                }
            }
            ch++;
        } while (ch != std::numeric_limits<TCHAR>::max());
    }
    return true;
}


bool cEncoder::AllIdentifiersUnique() const
{
    TSTRING chars;
    for (sack_type::const_iterator atE = m_encodings.begin(); atE != m_encodings.end(); atE++)
    {
        TCHAR chID = (*atE)->Identifier();
        if (chars.find(chID) == TSTRING::npos)
            chars += chID;
        else
            return false;
    }
    return true;
}


bool cEncoder::AllTestsRunOnEncodedString(const TSTRING& s) const
{
    TSTRING::const_iterator       cur   = s.begin(); // pointer to working position in s
    const TSTRING::const_iterator end   = s.end();   // end of s
    TSTRING::const_iterator       first = end;       // identifies beginning of current character
    TSTRING::const_iterator       last  = end;       // identifies end of current character


    // while get next char (updates cur)
    while (cCharUtil::PopNextChar(cur, end, first, last))
    {
        sack_type::const_iterator atE;
        for (atE = m_encodings.begin(); atE != m_encodings.end(); atE++)
        {
            if ((*atE)->NeedsEncoding(first, last))
            {
                return false;
            }
        }
    }

    return true;
}


//////////////////////////////////////////////////////////////////////////////
// cDisplayEncoder MEMBERS
//////////////////////////////////////////////////////////////////////////////


cDisplayEncoder::cDisplayEncoder(Flags f) : cEncoder(NON_NARROWABLE | NON_PRINTABLE | BACKSLASH | DBL_QUOTE, f)
{
}

void cDisplayEncoder::Encode(TSTRING& str) const
{
    cEncoder::Encode(str);
}

bool cDisplayEncoder::Decode(TSTRING& str) const
{
    cEncoder::Decode(str);
    return true; // TODO:BAM -- throw error!
}
