902 lines
28 KiB
C++
902 lines
28 KiB
C++
//
|
|
// The developer of the original code and/or files is Tripwire, Inc.
|
|
// Portions created by Tripwire, Inc. are copyright (C) 2000-2017 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!
|
|
}
|
|
|