Fixed Xml encoding
- don't encode apostrophes - only encode quotes in attributes - encode control characters (as in PR #465)
This commit is contained in:
parent
6de135c63a
commit
d6e59cd56f
6 changed files with 762 additions and 453 deletions
|
@ -14,9 +14,65 @@
|
|||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <iomanip>
|
||||
|
||||
namespace Catch {
|
||||
|
||||
class XmlEncode {
|
||||
public:
|
||||
enum ForWhat { ForTextNodes, ForAttributes };
|
||||
|
||||
XmlEncode( std::string const& str, ForWhat forWhat = ForTextNodes )
|
||||
: m_str( str ),
|
||||
m_forWhat( forWhat )
|
||||
{}
|
||||
|
||||
void encodeTo( std::ostream& os ) const {
|
||||
|
||||
// Apostrophe escaping not necessary if we always use " to write attributes
|
||||
// (see: http://www.w3.org/TR/xml/#syntax)
|
||||
|
||||
for( std::size_t i = 0; i < m_str.size(); ++ i ) {
|
||||
char c = m_str[i];
|
||||
switch( c ) {
|
||||
case '<': os << "<"; break;
|
||||
case '&': os << "&"; break;
|
||||
|
||||
case '>':
|
||||
// See: http://www.w3.org/TR/xml/#syntax
|
||||
if( i > 2 && m_str[i-1] == ']' && m_str[i-2] == ']' )
|
||||
os << ">";
|
||||
else
|
||||
os << c;
|
||||
break;
|
||||
|
||||
case '\"':
|
||||
if( m_forWhat == ForAttributes )
|
||||
os << """;
|
||||
else
|
||||
os << c;
|
||||
break;
|
||||
|
||||
default:
|
||||
// Escape control chars - based on contribution by @espenalb in PR #465
|
||||
if ( ( c < '\x09' ) || ( c > '\x0D' && c < '\x20') || c=='\x7F' )
|
||||
os << "&#x" << std::uppercase << std::hex << static_cast<int>( c );
|
||||
else
|
||||
os << c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) {
|
||||
xmlEncode.encodeTo( os );
|
||||
return os;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_str;
|
||||
ForWhat m_forWhat;
|
||||
};
|
||||
|
||||
class XmlWriter {
|
||||
public:
|
||||
|
||||
|
@ -99,11 +155,8 @@ namespace Catch {
|
|||
}
|
||||
|
||||
XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ) {
|
||||
if( !name.empty() && !attribute.empty() ) {
|
||||
stream() << " " << name << "=\"";
|
||||
writeEncodedText( attribute );
|
||||
stream() << "\"";
|
||||
}
|
||||
if( !name.empty() && !attribute.empty() )
|
||||
stream() << " " << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << "\"";
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
@ -114,9 +167,9 @@ namespace Catch {
|
|||
|
||||
template<typename T>
|
||||
XmlWriter& writeAttribute( std::string const& name, T const& attribute ) {
|
||||
if( !name.empty() )
|
||||
stream() << " " << name << "=\"" << attribute << "\"";
|
||||
return *this;
|
||||
std::ostringstream oss;
|
||||
oss << attribute;
|
||||
return writeAttribute( name, oss.str() );
|
||||
}
|
||||
|
||||
XmlWriter& writeText( std::string const& text, bool indent = true ) {
|
||||
|
@ -125,7 +178,7 @@ namespace Catch {
|
|||
ensureTagClosed();
|
||||
if( tagWasOpen && indent )
|
||||
stream() << m_indent;
|
||||
writeEncodedText( text );
|
||||
stream() << XmlEncode( text );
|
||||
m_needsNewline = true;
|
||||
}
|
||||
return *this;
|
||||
|
@ -170,30 +223,6 @@ namespace Catch {
|
|||
}
|
||||
}
|
||||
|
||||
void writeEncodedText( std::string const& text ) {
|
||||
static const char* charsToEncode = "<&\"";
|
||||
std::string mtext = text;
|
||||
std::string::size_type pos = mtext.find_first_of( charsToEncode );
|
||||
while( pos != std::string::npos ) {
|
||||
stream() << mtext.substr( 0, pos );
|
||||
|
||||
switch( mtext[pos] ) {
|
||||
case '<':
|
||||
stream() << "<";
|
||||
break;
|
||||
case '&':
|
||||
stream() << "&";
|
||||
break;
|
||||
case '\"':
|
||||
stream() << """;
|
||||
break;
|
||||
}
|
||||
mtext = mtext.substr( pos+1 );
|
||||
pos = mtext.find_first_of( charsToEncode );
|
||||
}
|
||||
stream() << mtext;
|
||||
}
|
||||
|
||||
bool m_tagIsOpen;
|
||||
bool m_needsNewline;
|
||||
std::vector<std::string> m_tags;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue