polserver/pol-core/clib/strutil.cpp

284 lines
6.2 KiB
C++

/** @file
*
* @par History
* - 2009/09/12 MuadDib: Disabled 4244 in this file due to it being on a string iter. Makes no
* sense.
* - 2014/06/10 Nando: Removed pragma that disabled 4244. (tolower()/toupper() used ints because -1
* is a valid output).
*/
#include "strutil.h"
#include <boost/algorithm/string/case_conv.hpp>
#include <utf8/utf8.h>
#include "logfacility.h"
#include "stlutil.h"
#include "unittest.h"
namespace Pol
{
namespace Clib
{
void splitnamevalue( const std::string& istr, std::string& propname, std::string& propvalue )
{
std::string::size_type start = istr.find_first_not_of( " \t\r\n" );
if ( start != std::string::npos )
{
std::string::size_type delimpos = istr.find_first_of( " \t\r\n=", start + 1 );
if ( delimpos != std::string::npos )
{
std::string::size_type valuestart = istr.find_first_not_of( " \t\r\n", delimpos + 1 );
std::string::size_type valueend = istr.find_last_not_of( " \t\r\n" );
propname = istr.substr( start, delimpos - start );
if ( valuestart != std::string::npos && valueend != std::string::npos )
{
propvalue = istr.substr( valuestart, valueend - valuestart + 1 );
}
else
{
propvalue = "";
}
}
else
{
propname = istr.substr( start, std::string::npos );
propvalue = "";
}
}
else
{
propname = "";
propvalue = "";
}
}
void test_splitnamevalue( const std::string& istr, const std::string& exp_pn,
const std::string& exp_pv )
{
std::string pn, pv;
splitnamevalue( istr, pn, pv );
if ( pn != exp_pn || pv != exp_pv )
{
INFO_PRINT << "splitnamevalue( \"" << istr << "\" ) fails!\n";
}
}
void test_splitnamevalue()
{
test_splitnamevalue( "a b", "a", "b" );
test_splitnamevalue( "av bx", "av", "bx" );
test_splitnamevalue( "nm=valu", "nm", "valu" );
test_splitnamevalue( "nm:valu", "nm:valu", "" );
test_splitnamevalue( "nm", "nm", "" );
test_splitnamevalue( " nm", "nm", "" );
test_splitnamevalue( " nm ", "nm", "" );
test_splitnamevalue( " nm valu", "nm", "valu" );
test_splitnamevalue( " nm value ", "nm", "value" );
test_splitnamevalue( " nm value is multiple words", "nm", "value is multiple words" );
test_splitnamevalue( " nm value is multiple words\t ", "nm", "value is multiple words" );
}
UnitTest test_splitnamevalue_obj( test_splitnamevalue );
void decodequotedstring( std::string& str )
{
std::string tmp;
tmp.swap( str );
const char* s = tmp.c_str();
str.reserve( tmp.size() );
++s;
while ( *s )
{
char ch = *s++;
switch ( ch )
{
case '\\':
ch = *s++;
switch ( ch )
{
case '\0':
return;
case 'n': // newline
str += "\n";
break;
default: // slash, quote, etc
str += ch;
break;
}
break;
case '\"':
return;
default:
str += ch;
break;
}
}
}
void encodequotedstring( std::string& str )
{
std::string tmp;
tmp.swap( str );
const char* s = tmp.c_str();
str.reserve( tmp.size() + 2 );
str += "\"";
while ( *s )
{
char ch = *s++;
switch ( ch )
{
case '\\':
str += "\\\\";
break;
case '\"':
str += "\\\"";
break;
case '\n':
str += "\\n";
break;
default:
str += ch;
break;
}
}
str += "\"";
}
std::string getencodedquotedstring( const std::string& in )
{
std::string tmp = in;
encodequotedstring( tmp );
return tmp;
}
void test_dqs( const std::string& in, const std::string& out )
{
std::string tmp = in;
decodequotedstring( tmp );
if ( tmp != out )
{
INFO_PRINT << "decodequotedstring( " << in << " ) fails!\n";
}
encodequotedstring( tmp );
if ( tmp != in )
{
INFO_PRINT << "encodequotedstring( " << out << " ) fails!\n";
}
}
void test_convertquotedstring()
{
test_dqs( "\"hi\"", "hi" );
test_dqs( "\"hi \"", "hi " );
test_dqs( "\" hi \"", " hi " );
test_dqs( "\" \\\"hi\"", " \"hi" );
}
UnitTest test_convertquotedstring_obj( test_convertquotedstring );
// If we have boost, I think we should use it...
void mklowerASCII( std::string& str )
{
boost::to_lower( str );
}
void mkupperASCII( std::string& str )
{
boost::to_upper( str );
}
std::string strlowerASCII( const std::string& str )
{
return boost::to_lower_copy( str );
}
std::string strupperASCII( const std::string& str )
{
return boost::to_upper_copy( str );
}
bool isValidUnicode( const std::string& str )
{
return utf8::find_invalid( str.begin(), str.end() ) == str.end();
}
void sanitizeUnicodeWithIso( std::string* str )
{
if ( isValidUnicode( *str ) )
return;
// assume iso8859
std::string utf8( "" );
utf8.reserve( 2 * str->size() + 1 );
for ( const auto& s : *str )
{
if ( !( s & 0x80 ) )
{
utf8.push_back( s );
}
else
{
utf8.push_back( 0xc2 | ( (unsigned char)( s ) >> 6 ) );
utf8.push_back( 0xbf & s );
}
}
*str = utf8;
}
void sanitizeUnicode( std::string* str )
{
if ( !isValidUnicode( *str ) )
{
try
{
std::string new_s;
utf8::replace_invalid( str->begin(), str->end(), std::back_inserter( new_s ) );
*str = new_s;
}
catch ( utf8::exception& )
{
*str = "Invalid unicode";
}
}
auto begin = str->begin();
auto end = str->end();
auto invalid_chr = []( u32 c ) {
return ( c >= 0x1u && c < 0x9u ) /*0x9 \t 0x10 \n*/ ||
( c >= 0x11u && c < 0x13u ) /*0x13 \r*/ || ( c >= 0x14u && c < 0x20u ) || c == 0x7Fu ||
( c >= 0x80u && c <= 0x9Fu );
};
while ( begin != end )
{
auto c = utf8::unchecked::next( begin );
if ( invalid_chr( c ) )
{
// control character found build new string skipping them
std::string new_s;
begin = str->begin();
while ( begin != end )
{
c = utf8::unchecked::next( begin );
if ( invalid_chr( c ) )
continue;
utf8::unchecked::append( c, std::back_inserter( new_s ) );
}
*str = new_s;
break;
}
}
}
void remove_bom( std::string* strbuf )
{
if ( strbuf->size() >= 3 )
{
if ( utf8::starts_with_bom( strbuf->cbegin(), strbuf->cend() ) )
strbuf->erase( 0, 3 );
}
}
} // namespace Clib
} // namespace Pol