/** @file * * @par History * - 2005/11/24 Shinigami: added itoa for Linux because it is not ANSI C/C++ * - 2005/11/29 Shinigami: mf_SplitWords will now accept each type of to-split-value as same as in * the past * - 2006/10/07 Shinigami: FreeBSD fix - changed __linux__ to __unix__ * - 2006/12/29 Shinigami: mf_SplitWords will not hang server on queue of delimiter */ #include "basicmod.h" #include "../../clib/clib.h" #include "../../clib/rawtypes.h" #include "../../clib/stlutil.h" #include "../../clib/strutil.h" #include "../../bscript/berror.h" #include "../../bscript/bobject.h" #include "../../bscript/dict.h" #include "../../bscript/executor.h" #include "../../bscript/impstr.h" #include #include #include #ifdef _MSC_VER #pragma warning( disable : 4996 ) // stricmp, itoa and sprintf warnings #endif namespace Pol { namespace Module { using namespace Bscript; BasicExecutorModule::BasicExecutorModule( Executor& exec ) : Bscript::TmplExecutorModule( "Basic", exec ) { } Bscript::BObjectImp* BasicExecutorModule::len() { Bscript::BObjectImp* imp = exec.getParamImp( 0 ); if ( imp->isa( Bscript::BObjectImp::OTArray ) ) { Bscript::ObjArray* arr = static_cast( imp ); return new BLong( static_cast( arr->ref_arr.size() ) ); } else if ( imp->isa( Bscript::BObjectImp::OTString ) ) { return new BLong( static_cast( imp->getStringRep().length() ) ); } else if ( imp->isa( Bscript::BObjectImp::OTError ) ) { BError* err = static_cast( imp ); return new BLong( static_cast( err->mapcount() ) ); } else { return new BLong( 0 ); } } Bscript::BObjectImp* BasicExecutorModule::find() { exec.makeString( 0 ); String* str = static_cast( exec.getParamImp( 0 ) ); const char* s = exec.paramAsString( 1 ); int d = static_cast( exec.paramAsLong( 2 ) ); int posn = str->find( d ? ( d - 1 ) : 0, s ) + 1; return new BLong( posn ); } Bscript::BObjectImp* BasicExecutorModule::mf_substr() { exec.makeString( 0 ); String* str = static_cast( exec.getParamImp( 0 ) ); int start = static_cast( exec.paramAsLong( 1 ) ); int length = static_cast( exec.paramAsLong( 2 ) ); return str->StrStr( start, length ); } Bscript::BObjectImp* BasicExecutorModule::mf_Trim() { Bscript::BObjectImp* imp = exec.getParamImp( 0 ); if ( !( imp->isa( Bscript::BObjectImp::OTString ) ) ) { return new BError( "Param 1 must be a string." ); } String* string = static_cast( imp ); int type = static_cast( exec.paramAsLong( 1 ) ); const char* cset = exec.paramAsString( 2 ); if ( type > 3 ) type = 3; if ( type < 1 ) type = 1; return string->ETrim( cset, type ); } /* eScript Function Useage StrReplace(str, to_replace, replace_with); */ Bscript::BObjectImp* BasicExecutorModule::mf_StrReplace() { Bscript::BObjectImp* imp = exec.getParamImp( 0 ); std::unique_ptr string( new String( imp->getStringRep().c_str() ) ); String* to_replace = static_cast( exec.getParamImp( 1, Bscript::BObjectImp::OTString ) ); if ( !to_replace ) return new BError( "Invalid parameter type" ); String* replace_with = static_cast( exec.getParamImp( 2, Bscript::BObjectImp::OTString ) ); if ( !replace_with ) return new BError( "Invalid parameter type" ); if ( string->length() < 1 ) return new BError( "Cannot use empty string for string haystack." ); if ( to_replace->length() < 1 ) return new BError( "Cannot use empty string for string needle." ); string->EStrReplace( to_replace, replace_with ); return string.release(); } // SubStrReplace(str, replace_with, start, length:=0); Bscript::BObjectImp* BasicExecutorModule::mf_SubStrReplace() { Bscript::BObjectImp* imp = exec.getParamImp( 0 ); std::unique_ptr string( new String( imp->getStringRep().c_str() ) ); String* replace_with = static_cast( exec.getParamImp( 1, Bscript::BObjectImp::OTString ) ); if ( !replace_with ) return new BError( "Invalid parameter type" ); int index = static_cast( exec.paramAsLong( 2 ) ); int len = static_cast( exec.paramAsLong( 3 ) ); if ( index < 0 ) return new BError( "Index must not be negative" ); if ( static_cast( index - 1 ) > string->length() ) return new BError( "Index out of range" ); // We set it to 1 because of doing -1 later to stay with eScript handling. if ( !index ) index = 1; if ( static_cast( len ) > ( string->length() - index ) ) return new BError( "Length out of range" ); if ( len < 0 ) return new BError( "Length must not be negative" ); if ( !len ) len = static_cast( replace_with->length() - index ); string->ESubStrReplace( replace_with, static_cast( index ), static_cast( len ) ); return string.release(); } // OMG I HATED THIS REQUEST. Ugly code, but necessary for all the checks // just in case someone's code is bugged :( Bscript::BObjectImp* BasicExecutorModule::mf_Compare() { std::string str1 = exec.paramAsString( 0 ); std::string str2 = exec.paramAsString( 1 ); int pos1_index = static_cast( exec.paramAsLong( 2 ) ); int pos1_len = static_cast( exec.paramAsLong( 3 ) ); int pos2_index = static_cast( exec.paramAsLong( 4 ) ); int pos2_len = static_cast( exec.paramAsLong( 5 ) ); if ( pos1_index != 0 ) { if ( pos1_index < 0 ) return new BError( "Index must not be negative for param 1" ); if ( static_cast( pos1_index - 1 ) > str1.length() ) return new BError( "Index out of range for param 1" ); } if ( pos2_index != 0 ) { if ( pos2_index < 0 ) return new BError( "Index must not be negative for param 2" ); if ( static_cast( pos2_index - 1 ) > str2.length() ) return new BError( "Index out of range for param 2" ); } if ( pos1_len < 0 ) return new BError( "Length must not be negative for param 1" ); if ( static_cast( pos1_len ) > ( str1.length() - pos1_index ) ) return new BError( "Length out of range for param 1" ); if ( pos2_len < 0 ) return new BError( "Length must not be negative for param 2" ); if ( static_cast( pos2_len ) > ( str2.length() - pos2_index ) ) return new BError( "Length out of range for param 2" ); if ( pos1_index == 0 ) { unsigned int result = str1.compare( str2 ); if ( result != 0 ) return new BLong( 0 ); else return new BLong( 1 ); } else if ( pos1_index > 0 && pos2_index == 0 ) { unsigned int result = str1.compare( pos1_index - 1, pos1_len, str2 ); if ( result != 0 ) return new BLong( 0 ); else return new BLong( 1 ); } else { unsigned int result = str1.compare( pos1_index - 1, pos1_len, str2, pos2_index - 1, pos2_len ); if ( result != 0 ) return new BLong( 0 ); else return new BLong( 1 ); } } Bscript::BObjectImp* BasicExecutorModule::lower() { String* string = new String( exec.paramAsString( 0 ) ); string->toLower(); return string; } Bscript::BObjectImp* BasicExecutorModule::upper() { String* string = new String( exec.paramAsString( 0 ) ); string->toUpper(); return string; } Bscript::BObjectImp* BasicExecutorModule::mf_CInt() { Bscript::BObjectImp* imp = exec.getParamImp( 0 ); if ( imp->isa( Bscript::BObjectImp::OTLong ) ) { return imp->copy(); } else if ( imp->isa( Bscript::BObjectImp::OTString ) ) { String* str = static_cast( imp ); return new BLong( strtoul( str->data(), NULL, 0 ) ); } else if ( imp->isa( Bscript::BObjectImp::OTDouble ) ) { Double* dbl = static_cast( imp ); return new BLong( static_cast( dbl->value() ) ); } else { return new BLong( 0 ); } } Bscript::BObjectImp* BasicExecutorModule::mf_CDbl() { Bscript::BObjectImp* imp = exec.getParamImp( 0 ); if ( imp->isa( Bscript::BObjectImp::OTLong ) ) { BLong* lng = static_cast( imp ); return new Double( lng->value() ); } else if ( imp->isa( Bscript::BObjectImp::OTString ) ) { String* str = static_cast( imp ); return new Double( strtod( str->data(), NULL ) ); } else if ( imp->isa( Bscript::BObjectImp::OTDouble ) ) { return imp->copy(); } else { return new Double( 0 ); } } Bscript::BObjectImp* BasicExecutorModule::mf_CStr() { Bscript::BObjectImp* imp = exec.getParamImp( 0 ); return new String( imp->getStringRep() ); } Bscript::BObjectImp* BasicExecutorModule::mf_CAsc() { Bscript::BObjectImp* imp = exec.getParamImp( 0 ); if ( imp->isa( Bscript::BObjectImp::OTString ) ) { String* str = static_cast( imp ); return new BLong( static_cast( str->data()[0] ) ); } else { return new BError( "Invalid parameter type" ); } } Bscript::BObjectImp* BasicExecutorModule::mf_CAscZ() { Bscript::BObjectImp* imp = exec.getParamImp( 0 ); std::string tmp = imp->getStringRep(); int nullterm = static_cast( exec.paramAsLong( 1 ) ); std::unique_ptr arr( new Bscript::ObjArray ); for ( size_t i = 0; i < tmp.size(); ++i ) { arr->addElement( new BLong( static_cast( tmp[i] ) ) ); } if ( nullterm ) arr->addElement( new BLong( 0 ) ); return arr.release(); } Bscript::BObjectImp* BasicExecutorModule::mf_CChr() { int val; if ( getParam( 0, val ) ) { char s[2]; s[0] = static_cast( val ); s[1] = '\0'; return new String( s ); } else { return new BError( "Invalid parameter type" ); } } Bscript::BObjectImp* BasicExecutorModule::mf_CChrZ() { std::string res; Bscript::ObjArray* arr = static_cast( exec.getParamImp( 0, Bscript::BObjectImp::OTArray ) ); int break_first_null = static_cast( exec.paramAsLong( 1 ) ); if ( !arr ) return new BError( "Invalid parameter type" ); for ( Bscript::ObjArray::const_iterator itr = arr->ref_arr.begin(), itrend = arr->ref_arr.end(); itr != itrend; ++itr ) { BObject* bo = ( itr->get() ); if ( bo == NULL ) continue; Bscript::BObjectImp* imp = bo->impptr(); if ( imp ) { if ( imp->isa( Bscript::BObjectImp::OTLong ) ) { BLong* blong = static_cast( imp ); if ( break_first_null && blong->value() == 0 ) break; char s[2]; s[0] = static_cast( blong->value() ); s[1] = '\0'; res += s; } } } return new String( res ); } Bscript::BObjectImp* BasicExecutorModule::mf_Hex() { Bscript::BObjectImp* imp = exec.getParamImp( 0 ); if ( imp->isa( Bscript::BObjectImp::OTLong ) ) { BLong* plong = static_cast( imp ); char s[20]; sprintf( s, "0x%X", static_cast( plong->value() ) ); return new String( s ); } else if ( imp->isa( Bscript::BObjectImp::OTDouble ) ) { Double* pdbl = static_cast( imp ); char s[20]; sprintf( s, "0x%X", static_cast( pdbl->value() ) ); return new String( s ); } else if ( imp->isa( Bscript::BObjectImp::OTString ) ) { String* str = static_cast( imp ); char s[20]; sprintf( s, "0x%X", static_cast( strtoul( str->data(), NULL, 0 ) ) ); return new String( s ); } else { return new BError( "Hex() expects an Integer, Real, or String" ); } } #ifdef __unix__ char* itoa( int value, char* result, int base ) { // check that the base is valid if ( base < 2 || base > 16 ) { *result = 0; return result; } char* out = result; int quotient = value; do { *out = "0123456789abcdef"[std::abs( quotient % base )]; ++out; quotient /= base; } while ( quotient ); // Only apply negative sign for base 10 if ( value < 0 && base == 10 ) *out++ = '-'; std::reverse( result, out ); *out = 0; return result; } #endif Bscript::BObjectImp* BasicExecutorModule::mf_Bin() { Bscript::BObjectImp* imp = exec.getParamImp( 0 ); if ( imp->isa( Bscript::BObjectImp::OTLong ) ) { BLong* plong = static_cast( imp ); int number = plong->value(); char buffer[sizeof( int ) * 8 + 1]; return new String( itoa( number, buffer, 2 ) ); } else { return new BError( "Bin() expects an Integer" ); } } Bscript::BObjectImp* BasicExecutorModule::mf_SplitWords() { Bscript::BObjectImp* bimp_split = exec.getParamImp( 0 ); std::string source = bimp_split->getStringRep(); const String* bimp_delimiter; if ( !exec.getStringParam( 1, bimp_delimiter ) ) { return new BError( "Invalid parameter type." ); } std::string delimiter = bimp_delimiter->getStringRep(); // max_split parameter int max_split = -1; int count = 0; if ( exec.hasParams( 3 ) ) { max_split = static_cast( exec.paramAsLong( 2 ) ); } std::unique_ptr objarr( new Bscript::ObjArray ); // Support for how it previously worked. // Kept to support spaces and tabs as the same. if ( delimiter == " " ) { ISTRINGSTREAM is( source ); std::string tmp; std::streamoff tellg = -1; bool splitted = false; while ( is >> tmp ) { tellg = is.tellg(); if ( count == max_split && tellg != -1 ) { // added max_split parameter splitted = true; break; } objarr->addElement( new String( tmp ) ); tmp = ""; count += 1; } // Merges the remaining of the string if ( splitted ) { std::string remaining_string; remaining_string = source.substr( tellg - tmp.length(), source.length() ); objarr->addElement( new String( remaining_string ) ); } return objarr.release(); } // New delimiter support. std::string new_string = source; std::string::size_type found; do { found = new_string.find( delimiter, 0 ); if ( found == std::string::npos ) break; else if ( count == max_split ) { // added max_split parameter break; } std::string add_string = new_string.substr( 0, found ); // Shinigami: will not hang server on queue of delimiter // if ( add_string.empty() ) // continue; objarr->addElement( new String( add_string ) ); std::string tmp_string = new_string.substr( found + delimiter.length(), new_string.length() ); new_string = tmp_string; count += 1; } while ( found != std::string::npos ); // Catch leftovers here. if ( !new_string.empty() ) objarr->addElement( new String( new_string ) ); return objarr.release(); } Bscript::BObjectImp* BasicExecutorModule::mf_Pack() { Bscript::BObjectImp* imp = exec.getParamImp( 0 ); return new String( imp->pack() ); } Bscript::BObjectImp* BasicExecutorModule::mf_Unpack() { const String* str; if ( exec.getStringParam( 0, str ) ) { return Bscript::BObjectImp::unpack( str->data() ); } else { return new BError( "Invalid parameter type" ); } } Bscript::BObjectImp* BasicExecutorModule::mf_TypeOf() { Bscript::BObjectImp* imp = exec.getParamImp( 0 ); return new String( imp->typeOf() ); } Bscript::BObjectImp* BasicExecutorModule::mf_SizeOf() { Bscript::BObjectImp* imp = exec.getParamImp( 0 ); return new BLong( static_cast( imp->sizeEstimate() ) ); } Bscript::BObjectImp* BasicExecutorModule::mf_TypeOfInt() { BObjectImp* imp = exec.getParamImp( 0 ); return new BLong( imp->typeOfInt() ); } picojson::value recurseE2J(BObjectImp* v) { if (v->isa(BObjectImp::OTString)) { return picojson::value(v->getStringRep()); } else if (v->isa(BObjectImp::OTLong)) { int intVal = static_cast(v)->value(); return picojson::value(static_cast(intVal)); } else if (v->isa(BObjectImp::OTDouble)) { return picojson::value(static_cast(v)->value()); } else if (v->isa(BObjectImp::OTBoolean)) { return picojson::value(static_cast(v)->value()); } else if (v->isa(BObjectImp::OTArray)) { ObjArray* arr = static_cast(v); picojson::array jsonArr; for (const auto &elem : arr->ref_arr) { BObject* bo = elem.get(); if (bo == nullptr) continue; BObjectImp* imp = bo->impptr(); jsonArr.push_back(recurseE2J(imp)); } return picojson::value(jsonArr); } else if (v->isa(BObjectImp::OTStruct)) { BStruct *bstruct = static_cast(v); picojson::object jsonObj; for (const auto& content : bstruct->contents()) { BObjectImp* imp = content.second->impptr(); jsonObj.insert(std::pair( content.first , recurseE2J(imp) )); } return picojson::value(jsonObj); } else if (v->isa(BObjectImp::OTDictionary)) { BDictionary* cpropdict = static_cast(v); picojson::object jsonObj; for (const auto& content : cpropdict->contents()) { BObjectImp* imp = content.second->impptr(); jsonObj.insert(std::pair(content.first->getStringRep(), recurseE2J(imp))); } return picojson::value(jsonObj); } return picojson::value(); } Bscript::BObjectImp* BasicExecutorModule::mf_PackJSON() { BObjectImp* imp = exec.getParamImp(0); return new String(recurseE2J(imp).serialize()); } Bscript::BObjectImp* recurseJ2E(const picojson::value& v) { if (v.is()) { return new String(v.get()); } else if (v.is()) { // Possible improvement: separate into BLong and Double return new Double(v.get()); } else if (v.is()) { return new BBoolean(v.get()); } else if (v.is()) { std::unique_ptr objarr(new ObjArray); const picojson::array& arr = v.get(); for (const auto& elem : arr) { objarr->addElement(recurseJ2E(elem)); } return objarr.release(); } else if (v.is()) { std::unique_ptr objstruct(new BStruct); for (const auto& content : v.get()) { objstruct->addMember(content.first.c_str(), recurseJ2E(content.second) ); } return objstruct.release(); } else return UninitObject::create(); } Bscript::BObjectImp* BasicExecutorModule::mf_UnpackJSON() { const String* str; if (exec.getStringParam(0, str)) { picojson::value v; std::string err = picojson::parse(v, str->data() ); if (!err.empty()) { return new BError(err); } return recurseJ2E(v); } else { return new BError("Invalid parameter type"); } } Bscript::BObjectImp* BasicExecutorModule::mf_Boolean() { Bscript::BObjectImp* imp = exec.getParamImp( 0 ); if ( imp->isa( Bscript::BObjectImp::OTLong ) ) { BLong* plong = static_cast( imp ); return new BBoolean(plong->value() != 0); } else if ( imp->isa( Bscript::BObjectImp::OTBoolean ) ) { return new BBoolean(*static_cast(imp)); } else { return new BError( "Boolean() expects an Integer or Boolean" ); } } } // namespace Module namespace Bscript { using namespace Module; template <> TmplExecutorModule::FunctionTable TmplExecutorModule::function_table = { {"find", &BasicExecutorModule::find}, {"len", &BasicExecutorModule::len}, {"upper", &BasicExecutorModule::upper}, {"lower", &BasicExecutorModule::lower}, {"Substr", &BasicExecutorModule::mf_substr}, {"Trim", &BasicExecutorModule::mf_Trim}, {"StrReplace", &BasicExecutorModule::mf_StrReplace}, {"SubStrReplace", &BasicExecutorModule::mf_SubStrReplace}, {"Compare", &BasicExecutorModule::mf_Compare}, {"CInt", &BasicExecutorModule::mf_CInt}, {"CStr", &BasicExecutorModule::mf_CStr}, {"CDbl", &BasicExecutorModule::mf_CDbl}, {"CAsc", &BasicExecutorModule::mf_CAsc}, {"CChr", &BasicExecutorModule::mf_CChr}, {"CAscZ", &BasicExecutorModule::mf_CAscZ}, {"CChrZ", &BasicExecutorModule::mf_CChrZ}, {"Bin", &BasicExecutorModule::mf_Bin}, {"Hex", &BasicExecutorModule::mf_Hex}, {"SplitWords", &BasicExecutorModule::mf_SplitWords}, {"Pack", &BasicExecutorModule::mf_Pack}, {"Unpack", &BasicExecutorModule::mf_Unpack}, {"TypeOf", &BasicExecutorModule::mf_TypeOf}, {"SizeOf", &BasicExecutorModule::mf_SizeOf}, {"TypeOfInt", &BasicExecutorModule::mf_TypeOfInt}, {"Boolean", &BasicExecutorModule::mf_Boolean}, {"PackJSON", &BasicExecutorModule::mf_PackJSON}, {"UnpackJSON", &BasicExecutorModule::mf_UnpackJSON }}; } }