--- parser3/src/classes/string.C 2020/12/17 20:01:03 1.249 +++ parser3/src/classes/string.C 2025/10/05 19:41:27 1.263 @@ -1,8 +1,8 @@ /** @file Parser: @b string parser class. - Copyright (c) 2001-2020 Art. Lebedev Studio (http://www.artlebedev.com) - Author: Alexandr Petrosian (http://paf.design.ru) + Copyright (c) 2001-2024 Art. Lebedev Studio (http://www.artlebedev.com) + Authors: Konstantin Morshnev , Alexandr Petrosian */ #include "classes.h" @@ -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.249 2020/12/17 20:01:03 moko Exp $"; +volatile const char * IDENT_STRING_C="$Id: string.C,v 1.263 2025/10/05 19:41:27 moko Exp $"; // class @@ -147,7 +148,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)); } @@ -205,14 +206,39 @@ static void _pos(Request& r, MethodParam r.write(*new VInt((int)string.pos(r.charsets.source(), substr.as_string(), offset))); } +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) { + Split_action_info& ai=*static_cast(info); + if(row) { // begin&middle + // piece from last match['prestart'] to beginning of this match['prefinish'] + ai.result += &ai.src.mid(prestart, prefinish); + } else // end + if(poststart != postfinish) + ai.result += &ai.src.mid(poststart, postfinish); +} + static void split_list(Value& delim_value, const String& string, ArrayString& result) { - string.split(result, 0, delim_value.as_string()); + if(VRegex *vregex=dynamic_cast(&delim_value)){ + vregex->study(); + + int matches_count=0; + Split_action_info ai(string, result); + + string.match(vregex, split_action, &ai, matches_count); + } else + string.split(result, 0, delim_value.as_string()); } #define SPLIT_LEFT 0x0001 #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 { @@ -221,10 +247,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} }; @@ -254,8 +281,7 @@ static Table& split_vertical(ArrayString table+=row; } } else { // left - Array_iterator i(pieces); - while(i.has_next()) { + for(ArrayString::Iterator i(pieces); i; ) { Table::element_type row(new ArrayString); *row+=i.next(); table+=row; @@ -272,7 +298,7 @@ static Table& split_horizontal(ArrayStri for(int i=pieces.count(); --i>=0; ) *row+=pieces[i]; } else { // left - for(Array_iterator i(pieces); i.has_next(); ) + for(ArrayString::Iterator i(pieces); i; ) *row+=i.next(); } table+=row; @@ -280,6 +306,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(); @@ -296,19 +335,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 */); @@ -363,13 +408,12 @@ static void _match(Request& r, MethodPar Value& regexp=params.as_no_junction(0, "regexp must not be code"); Value* options=(params_count>1)?¶ms.as_no_junction(1, OPTIONS_MUST_NOT_BE_CODE):0; - VRegex* vregex; + VRegex* vregex=dynamic_cast(®exp); VRegexCleaner vrcleaner; - if(Value* value=regexp.as(VREGEX_TYPE)){ + if(vregex){ if(options && options->is_defined()) - throw Exception(PARSER_RUNTIME, 0, "you can not specify regex-object and options together"); - vregex=static_cast(value); + throw Exception(PARSER_RUNTIME, 0, "you cannot specify regex-object and options together"); } else { vregex=new VRegex(r.charsets.source(), ®exp.as_string(), (options) ? (&options->as_string()) : 0); vregex->study(); @@ -465,7 +509,7 @@ public: bool add_row(SQL_Error& /*error*/) { /* ignore */ return false; } bool add_row_cell(SQL_Error& error, const char* str, size_t) { if(got_cell) { - error=SQL_Error("result must not contain more then one row"); + error=SQL_Error("result must contain no more than one row"); return true; } try { @@ -597,7 +641,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); @@ -646,25 +690,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; } @@ -672,25 +710,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; } @@ -748,7 +780,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(); @@ -770,7 +802,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);