--- parser3/src/classes/string.C 2024/09/28 14:37:53 1.255 +++ parser3/src/classes/string.C 2025/12/15 23:07:29 1.264 @@ -1,7 +1,7 @@ /** @file Parser: @b string parser class. - Copyright (c) 2001-2023 Art. Lebedev Studio (http://www.artlebedev.com) + Copyright (c) 2001-2024 Art. Lebedev Studio (http://www.artlebedev.com) Authors: Konstantin Morshnev , Alexandr Petrosian */ @@ -15,13 +15,14 @@ #include "pa_vtable.h" #include "pa_vbool.h" #include "pa_string.h" +#include "pa_varray.h" #include "pa_sql_connection.h" #include "pa_dictionary.h" #include "pa_vmethod_frame.h" #include "pa_vregex.h" #include "pa_charsets.h" -volatile const char * IDENT_STRING_C="$Id: string.C,v 1.255 2024/09/28 14:37:53 moko Exp $"; +volatile const char * IDENT_STRING_C="$Id: string.C,v 1.264 2025/12/15 23:07:29 moko Exp $"; // class @@ -70,25 +71,22 @@ static void _length(Request& r, MethodPa static void _int(Request& r, MethodParams& params) { const String& self_string=GET_SELF(r, VString).string(); - int converted; if(self_string.is_empty()) { if(params.count()>0) - converted=params.as_int(0, "default must be int", r); // (default) + r.write(*new VInt(params.as_int(0, "default must be int", r))); // (default) else throw Exception(PARSER_RUNTIME, 0, "unable to convert empty string without default specified"); } else { try { - converted=self_string.as_int(); + r.write(*new VInt(self_string.as_int())); } catch(...) { // convert problem if(params.count()>0) - converted=params.as_int(0, "default must be int", r); // (default) + r.write(*new VInt(params.as_int(0, "default must be int", r))); // (default) else rethrow; // we have a problem when no default } } - - r.write(*new VInt(converted)); } static void _double(Request& r, MethodParams& params) { @@ -147,7 +145,7 @@ static void _bool(Request& r, MethodPara // for some time due to stupid {} in original design const String& fmt=r.process_to_string(fmt_maybe_code); - const char* buf=format(r.get_self().as_double(), fmt.trim().cstrm()); + const char* buf=format_double(r.get_self().as_double(), fmt.trim().cstrm()); r.write(String(buf)); } @@ -208,6 +206,7 @@ static void _pos(Request& r, MethodParam struct Split_action_info { const String& src; ArrayString &result; + Split_action_info(const String& s, ArrayString& r) : src(s), result(r) {} }; static void split_action(Table& , ArrayString* row, int prestart, int prefinish, int poststart, int postfinish, void *info) { @@ -225,7 +224,7 @@ static void split_list(Value& delim_valu vregex->study(); int matches_count=0; - Split_action_info ai = { string, result }; + Split_action_info ai(string, result); string.match(vregex, split_action, &ai, matches_count); } else @@ -236,6 +235,7 @@ static void split_list(Value& delim_valu #define SPLIT_RIGHT 0x0010 #define SPLIT_HORIZONTAL 0x0100 #define SPLIT_VERTICAL 0x1000 +#define SPLIT_ARRAY 0x10000 static int split_options(const String* options) { struct Split_option { @@ -244,10 +244,11 @@ static int split_options(const String* o int setBit; int checkBit; } split_option[]={ - {"l", "L", SPLIT_LEFT, SPLIT_RIGHT}, // 0xVHRL + {"l", "L", SPLIT_LEFT, SPLIT_RIGHT}, // 0xAVHRL {"r", "R", SPLIT_RIGHT, SPLIT_LEFT}, - {"h", "H", SPLIT_HORIZONTAL, SPLIT_VERTICAL}, - {"v", "V", SPLIT_VERTICAL, SPLIT_HORIZONTAL}, + {"h", "H", SPLIT_HORIZONTAL, SPLIT_VERTICAL | SPLIT_ARRAY}, + {"v", "V", SPLIT_VERTICAL, SPLIT_HORIZONTAL | SPLIT_ARRAY}, + {"a", "A", SPLIT_ARRAY, SPLIT_VERTICAL | SPLIT_HORIZONTAL}, {0, 0, 0, 0} }; @@ -302,6 +303,19 @@ static Table& split_horizontal(ArrayStri return table; } +static VArray& split_array(ArrayString& pieces, bool right) { + VArray& result=*new VArray(pieces.count()); + ArrayValue &array=result.array(); + if(right) { // right + for(int i=pieces.count(); --i>=0; ) + array+=new VString(*pieces[i]); + } else { // left + for(ArrayString::Iterator i(pieces); i; ) + array+=new VString(*i.next()); + } + return result; +} + static void split_with_options(Request& r, MethodParams& params, int bits) { const String& string=GET_SELF(r, VString).string(); size_t params_count=params.count(); @@ -318,19 +332,25 @@ static void split_with_options(Request& bool right=(bits & SPLIT_RIGHT) != 0; bool horizontal=(bits & SPLIT_HORIZONTAL) !=0; + bool array=(bits & SPLIT_ARRAY) !=0; const String* column_name=0; if(params_count>2){ column_name=¶ms.as_string(2, COLUMN_NAME_MUST_BE_STRING); - if (horizontal && !column_name->is_empty()) - throw Exception(PARSER_RUNTIME, column_name, "column name can't be specified with horisontal split"); - } - if(!column_name || column_name->is_empty()) - column_name=new String("piece"); - - Table& table=horizontal?split_horizontal(pieces, right):split_vertical(pieces, right, column_name); + if (horizontal && !column_name->is_empty()) + throw Exception(PARSER_RUNTIME, column_name, "column name cannot be specified when splitting into a table horizontal row"); + if (array && !column_name->is_empty()) + throw Exception(PARSER_RUNTIME, column_name, "column name cannot be specified when splitting into an array"); + } - r.write(*new VTable(&table)); + if(array){ + r.write(split_array(pieces, right)); + } else { + if(!column_name || column_name->is_empty()) + column_name=new String("piece"); + Table& table=horizontal ? split_horizontal(pieces, right) : split_vertical(pieces, right, column_name); + r.write(*new VTable(&table)); + } } static void _split(Request& r, MethodParams& params) { split_with_options(r, params, 0 /* maybe-determine from param #2 */); @@ -618,7 +638,7 @@ static void _save(Request& r, MethodPara } } - const String& file_name=params.as_string(file_name_index, FILE_NAME_MUST_BE_STRING); + const String& file_name=params.as_file_name(file_name_index); const String& src=GET_SELF(r, VString).string(); String::Body sbody=src.cstr_to_string_body_untaint(String::L_AS_IS, r.connection(false), &r.charsets); @@ -667,25 +687,19 @@ static void _trim(Request& r, MethodPara Base64Options base64_encode_options(Request& r, HashStringValue* options) { Base64Options result; if(options) { - int valid_options=0; for(HashStringValue::Iterator i(*options); i; i.next()) { String::Body key=i.key(); Value* value=i.value(); if(key == "pad") { result.pad=r.process(*value).as_bool(); - valid_options++; } else if(key == "wrap") { result.wrap=r.process(*value).as_bool(); - valid_options++; } else if(key == "url-safe") { if(r.process(*value).as_bool()) result.set_url_safe_abc(); - valid_options++; - } + } else + throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION); } - - if(valid_options != options->count()) - throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION); } return result; } @@ -693,25 +707,19 @@ Base64Options base64_encode_options(Requ Base64Options base64_decode_options(Request& r, HashStringValue* options) { Base64Options result; if(options) { - int valid_options=0; for(HashStringValue::Iterator i(*options); i; i.next() ) { String::Body key=i.key(); Value* value=i.value(); if(key == "pad") { result.pad=r.process(*value).as_bool(); - valid_options++; } else if(key == "strict") { result.strict=r.process(*value).as_bool(); - valid_options++; } else if(key == "url-safe") { if(r.process(*value).as_bool()) result.set_url_safe_abc(); - valid_options++; - } + } else + throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION); } - - if(valid_options != options->count()) - throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION); } return result; } @@ -769,7 +777,7 @@ static void _js_unescape(Request& r, Met } static void _unescape(Request& r, MethodParams& params){ - const String& mode=params.as_string(0, MODE_MUST_NOT_BE_CODE); + const String& mode=params.as_string(0, MODE_MUST_BE_STRING); const String& src=params.as_string(1, PARAMETER_MUST_BE_STRING); Charset* from_charset=&r.charsets.client(); @@ -791,7 +799,7 @@ static void _unescape(Request& r, Method } else if(mode==UNESCAPE_MODE_URI){ mode_js=false; } else { - throw Exception(PARSER_RUNTIME, &mode, "is invalid mode, must be either '" UNESCAPE_MODE_JS "' or '" UNESCAPE_MODE_URI "'"); + throw Exception(PARSER_RUNTIME, &mode, "is an invalid mode, must be either '" UNESCAPE_MODE_JS "' or '" UNESCAPE_MODE_URI "'"); } const char* unescaped=unescape_chars(src.cstr(), src.length(), from_charset, mode_js);