Diff for /parser3/src/classes/string.C between versions 1.232 and 1.253

version 1.232, 2016/10/26 15:44:49 version 1.253, 2024/09/07 16:30:26
Line 1 Line 1
 /** @file  /** @file
         Parser: @b string parser class.          Parser: @b string parser class.
   
         Copyright (c) 2001-2015 Art. Lebedev Studio (http://www.artlebedev.com)          Copyright (c) 2001-2023 Art. Lebedev Studio (http://www.artlebedev.com)
         Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)          Authors: Konstantin Morshnev <moko@design.ru>, Alexandr Petrosian <paf@design.ru>
 */  */
   
 #include "classes.h"  #include "classes.h"
 #include "pa_vmethod_frame.h"  #include "pa_vmethod_frame.h"
   #include "pa_base64.h"
   
 #include "pa_request.h"  #include "pa_request.h"
 #include "pa_vdouble.h"  #include "pa_vdouble.h"
Line 64  static const String match_var_name(MATCH Line 65  static const String match_var_name(MATCH
   
 static void _length(Request& r, MethodParams&) {  static void _length(Request& r, MethodParams&) {
         double result=GET_SELF(r, VString).string().length(r.charsets.source());          double result=GET_SELF(r, VString).string().length(r.charsets.source());
         r.write_no_lang(*new VDouble(result));          r.write(*new VDouble(result));
 }  }
   
 static void _int(Request& r, MethodParams& params) {  static void _int(Request& r, MethodParams& params) {
Line 87  static void _int(Request& r, MethodParam Line 88  static void _int(Request& r, MethodParam
                 }                  }
         }          }
   
         r.write_no_lang(*new VInt(converted));          r.write(*new VInt(converted));
 }  }
   
 static void _double(Request& r, MethodParams& params) {  static void _double(Request& r, MethodParams& params) {
Line 95  static void _double(Request& r, MethodPa Line 96  static void _double(Request& r, MethodPa
   
         if(self_string.is_empty()) {          if(self_string.is_empty()) {
                 if(params.count()>0)                  if(params.count()>0)
                         r.write_no_lang(*new VDouble(params.as_double(0, "default must be double", r))); // (default)                          r.write(*new VDouble(params.as_double(0, "default must be double", r))); // (default)
                 else                  else
                         throw Exception(PARSER_RUNTIME, 0, "unable to convert empty string without default specified");                          throw Exception(PARSER_RUNTIME, 0, "unable to convert empty string without default specified");
         } else {          } else {
                 try {                  try {
                         r.write_no_lang(*new VDouble(self_string.as_double()));                          r.write(*new VDouble(self_string.as_double()));
                 } catch(...) { // convert problem                  } catch(...) { // convert problem
                         if(params.count()>0)                          if(params.count()>0)
                                 r.write_no_lang(*new VDouble(params.as_double(0, "default must be double", r))); // (default)                                  r.write(*new VDouble(params.as_double(0, "default must be double", r))); // (default)
                         else                          else
                                 rethrow; // we have a problem when no default                                  rethrow; // we have a problem when no default
                 }                  }
Line 137  static void _bool(Request& r, MethodPara Line 138  static void _bool(Request& r, MethodPara
                 }                  }
         }          }
   
         r.write_no_lang(VBool::get(converted));          r.write(VBool::get(converted));
 }  }
   
 /*not static*/void _string_format(Request& r, MethodParams& params) {  /*not static*/void _string_format(Request& r, MethodParams& params) {
Line 148  static void _bool(Request& r, MethodPara Line 149  static void _bool(Request& r, MethodPara
   
         const char* buf=format(r.get_self().as_double(), fmt.trim().cstrm());          const char* buf=format(r.get_self().as_double(), fmt.trim().cstrm());
   
         r.write_no_lang(String(buf));          r.write(String(buf));
 }  }
   
 static void _left(Request& r, MethodParams& params) {  static void _left(Request& r, MethodParams& params) {
         ssize_t sn=params.as_int(0, "n must be int", r);          int sn=params.as_int(0, "n must be int", r);
         const String& string=GET_SELF(r, VString).string();          const String& string=GET_SELF(r, VString).string();
         r.write_pass_lang(sn<0 ? string : string.mid(r.charsets.source(), 0, (size_t)sn));          r.write(sn<0 ? string : string.mid(r.charsets.source(), 0, sn));
 }  }
   
 static void _right(Request& r, MethodParams& params) {  static void _right(Request& r, MethodParams& params) {
         ssize_t sn=params.as_int(0, "n must be int", r);          int sn=params.as_int(0, "n must be int", r);
         if(sn>0){          if(sn>0){
                 size_t n=(size_t)sn;                  size_t n=sn;
                 const String& string=GET_SELF(r, VString).string();                  const String& string=GET_SELF(r, VString).string();
                 size_t length=string.length(r.charsets.source());                  size_t length=string.length(r.charsets.source());
                 r.write_pass_lang(n<length ? string.mid(r.charsets.source(), length-n, length, length) : string);                  r.write(n<length ? string.mid(r.charsets.source(), length-n, length, length) : string);
         }          }
 }  }
   
 static void _mid(Request& r, MethodParams& params) {  static void _mid(Request& r, MethodParams& params) {
         const String& string=GET_SELF(r, VString).string();          const String& string=GET_SELF(r, VString).string();
   
         ssize_t sbegin=params.as_int(0, "p must be int", r);          int begin=params.as_int(0, "p must be int", r);
         if(sbegin<0)          if(begin<0)
                 throw Exception(PARSER_RUNTIME,                  throw Exception(PARSER_RUNTIME, 0,  "p(%d) must be >=0", begin);
                         0,   
                         "p(%d) must be >=0", sbegin);  
         size_t begin=(size_t)sbegin;  
   
         size_t end;          size_t end;
         size_t length=0;          size_t length=0;
   
         if(params.count()>1) {          if(params.count()>1) {
                 ssize_t sn=params.as_int(1, "n must be int", r);                  int n=params.as_int(1, "n must be int", r);
                 if(sn<0)                  if(n<0)
                         throw Exception(PARSER_RUNTIME,                          throw Exception(PARSER_RUNTIME, 0, "n(%d) must be >=0", n);
                                 0,                   end=begin+n;
                                 "n(%d) must be >=0", sn);  
                 end=begin+(size_t)sn;  
         } else {          } else {
                 length=string.length(r.charsets.source());                  length=string.length(r.charsets.source());
                 end=length;                  end=length;
         }          }
   
         r.write_pass_lang(string.mid(r.charsets.source(), begin, end, length));          r.write(string.mid(r.charsets.source(), begin, end, length));
 }  }
   
 static void _pos(Request& r, MethodParams& params) {  static void _pos(Request& r, MethodParams& params) {
         Value& substr=params.as_no_junction(0, "substr must not be code");          Value& substr=params.as_no_junction(0, "substr must not be code");
                   
         const String& string=GET_SELF(r, VString).string();          const String& string=GET_SELF(r, VString).string();
         ssize_t offset=0;          int offset=0;
         if(params.count()>1){          if(params.count()>1){
                 offset=params.as_int(1, "n must be int", r);                  offset=params.as_int(1, "n must be int", r);
                 if(offset<0)                  if(offset<0)
                         throw Exception(PARSER_RUNTIME, 0, "n(%d) must be >=0", offset);                          throw Exception(PARSER_RUNTIME, 0, "n(%d) must be >=0", offset);
         }          }
   
         r.write_no_lang(*new VInt((int)string.pos(r.charsets.source(), substr.as_string(), (size_t)offset)));          r.write(*new VInt((int)string.pos(r.charsets.source(), substr.as_string(), offset)));
 }  }
   
 static void split_list(MethodParams& params, int paramIndex, const String& string, ArrayString& result) {  struct Split_action_info {
         Value& delim_value=params.as_no_junction(paramIndex, "delimiter must not be code");          const String& src;
         string.split(result, 0, delim_value.as_string());          ArrayString &result;
   };
   
   static void split_action(Table& , ArrayString* row, int prestart, int prefinish, int poststart, int postfinish, void *info) {
           Split_action_info& ai=*static_cast<Split_action_info *>(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) {
           if(Value* value=delim_value.as(VREGEX_TYPE)){
                   VRegex *vregex=static_cast<VRegex*>(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_LEFT 0x0001
Line 238  static int split_options(const String* o Line 258  static int split_options(const String* o
                         if(options->pos(o->keyL)!=STRING_NOT_FOUND                           if(options->pos(o->keyL)!=STRING_NOT_FOUND 
                                 || (o->keyU && options->pos(o->keyU)!=STRING_NOT_FOUND)) {                                  || (o->keyU && options->pos(o->keyU)!=STRING_NOT_FOUND)) {
                                 if(result & o->checkBit)                                  if(result & o->checkBit)
                                         throw Exception(PARSER_RUNTIME,                                          throw Exception(PARSER_RUNTIME, options, "conflicting split options");
                                                 options,  
                                                 "conflicting split options");  
                                 result |= o->setBit;                                  result |= o->setBit;
                         }                          }
         }          }
Line 260  static Table& split_vertical(ArrayString Line 278  static Table& split_vertical(ArrayString
                         table+=row;                          table+=row;
                 }                  }
         } else { // left          } else { // left
                 Array_iterator<const String*> i(pieces);                  for(ArrayString::Iterator i(pieces); i; ) {
                 while(i.has_next()) {  
                         Table::element_type row(new ArrayString);                          Table::element_type row(new ArrayString);
                         *row+=i.next();                          *row+=i.next();
                         table+=row;                          table+=row;
Line 278  static Table& split_horizontal(ArrayStri Line 295  static Table& split_horizontal(ArrayStri
                 for(int i=pieces.count(); --i>=0; )                  for(int i=pieces.count(); --i>=0; )
                         *row+=pieces[i];                          *row+=pieces[i];
         } else { // left          } else { // left
                 for(Array_iterator<const String*> i(pieces); i.has_next(); )                  for(ArrayString::Iterator i(pieces); i; )
                         *row+=i.next();                          *row+=i.next();
         }          }
         table+=row;          table+=row;
Line 286  static Table& split_horizontal(ArrayStri Line 303  static Table& split_horizontal(ArrayStri
         return table;          return table;
 }  }
   
 static void split_with_options(Request& r, MethodParams& params,  static void split_with_options(Request& r, MethodParams& params, int bits) {
                                                            int bits) {  
         const String& string=GET_SELF(r, VString).string();          const String& string=GET_SELF(r, VString).string();
         size_t params_count=params.count();          size_t params_count=params.count();
   
         ArrayString pieces;          ArrayString pieces;
         split_list(params, 0, string, pieces);          split_list(params.as_no_junction(0, "delimiter must not be code"), string, pieces);
   
         if(!bits) {          if(!bits) {
                 const String* options=0;                  const String* options=0;
                 if(params_count>1)                  if(params_count>1)
                         options=&params.as_string(1, OPTIONS_MUST_NOT_BE_CODE);                          options=&params.as_string(1, OPTIONS_MUST_NOT_BE_CODE);
                   
                 bits=split_options(options);                  bits=split_options(options);
         }          }
   
Line 309  static void split_with_options(Request& Line 324  static void split_with_options(Request&
         if(params_count>2){          if(params_count>2){
                 column_name=&params.as_string(2, COLUMN_NAME_MUST_BE_STRING);                  column_name=&params.as_string(2, COLUMN_NAME_MUST_BE_STRING);
                 if (horizontal && !column_name->is_empty())                   if (horizontal && !column_name->is_empty()) 
                         throw Exception(PARSER_RUNTIME,                          throw Exception(PARSER_RUNTIME, column_name, "column name can't be specified with horisontal split");
                                 column_name,  
                                 "column name can't be specified with horisontal split");  
         }           } 
         if(!column_name || column_name->is_empty())          if(!column_name || column_name->is_empty())
                 column_name=new String("piece");                  column_name=new String("piece");
   
         Table& table=horizontal?split_horizontal(pieces, right):split_vertical(pieces, right, column_name);          Table& table=horizontal?split_horizontal(pieces, right):split_vertical(pieces, right, column_name);
   
         r.write_no_lang(*new VTable(&table));          r.write(*new VTable(&table));
 }  }
 static void _split(Request& r, MethodParams& params) {  static void _split(Request& r, MethodParams& params) {
         split_with_options(r, params, 0 /* maybe-determine from param #2 */);          split_with_options(r, params, 0 /* maybe-determine from param #2 */);
Line 345  struct Replace_action_info { Line 358  struct Replace_action_info {
 };  };
 #endif  #endif
 /// @todo they can do $global[$result] there, getting pointer to later-invalid local var, kill this  /// @todo they can do $global[$result] there, getting pointer to later-invalid local var, kill this
 static void replace_action(Table& table, ArrayString* row,   static void replace_action(Table& table, ArrayString* row, int prestart, int prefinish, int poststart, int postfinish, void *info) {
                                 int prestart, int prefinish,   
                                 int poststart, int postfinish,  
                                 void *info) {  
         Replace_action_info& ai=*static_cast<Replace_action_info *>(info);          Replace_action_info& ai=*static_cast<Replace_action_info *>(info);
         if(row) { // begin&middle          if(row) { // begin&middle
                 // piece from last match['prestart'] to beginning of this match['prefinish']                  // piece from last match['prestart'] to beginning of this match['prefinish']
Line 381  static void _match(Request& r, MethodPar Line 391  static void _match(Request& r, MethodPar
   
         if(Value* value=regexp.as(VREGEX_TYPE)){          if(Value* value=regexp.as(VREGEX_TYPE)){
                 if(options && options->is_defined())                  if(options && options->is_defined())
                         throw Exception(PARSER_RUNTIME,                          throw Exception(PARSER_RUNTIME, 0, "you can not specify regex-object and options together");
                                 0,  
                                 "you can not specify regex-object and options together"  
                         );  
                 vregex=static_cast<VRegex*>(value);                  vregex=static_cast<VRegex*>(value);
         } else {          } else {
                 vregex=new VRegex(r.charsets.source(),                  vregex=new VRegex(r.charsets.source(), &regexp.as_string(), (options) ? (&options->as_string()) : 0);
                         &regexp.as_string(),  
                         (options)?(&options->as_string()):0);  
                 vregex->study();                  vregex->study();
                 vrcleaner.vregex=vregex;                  vrcleaner.vregex=vregex;
         }          }
Line 398  static void _match(Request& r, MethodPar Line 403  static void _match(Request& r, MethodPar
         int matches_count=0;          int matches_count=0;
   
         if(params_count<3) { // search          if(params_count<3) { // search
                 Table* table=src.match(vregex,                  Table* table=src.match(vregex, search_action, 0, matches_count);
                         search_action, 0,  
                         matches_count);  
   
                 if(table){                  if(table){
                         r.write_no_lang(*new VTable(table));                          r.write(*new VTable(table));
                 } else {                  } else {
                         r.write_no_lang(*new VInt(matches_count));                          r.write(*new VInt(matches_count));
                 }                  }
   
         } else { // replace          } else { // replace
Line 421  static void _match(Request& r, MethodPar Line 424  static void _match(Request& r, MethodPar
                         if(replacement->is_defined())                          if(replacement->is_defined())
                                 replacement_code=replacement;                                  replacement_code=replacement;
                 } else if(!replacement->is_void())                  } else if(!replacement->is_void())
                         throw Exception(PARSER_RUNTIME,                          throw Exception(PARSER_RUNTIME, 0, "replacement option should be junction or string");
                                 0,  
                                 "replacement option should be junction or string");  
   
                 Value* default_code=(params_count==4)?&params.as_junction(3, "default value must be code"):0;                  Value* default_code=(params_count==4) ? &params.as_junction(3, "default value must be code") : 0;
   
                 String result;                  String result;
                 VTable* vtable=new VTable;                  VTable* vtable=new VTable;
Line 447  static void _match(Request& r, MethodPar Line 448  static void _match(Request& r, MethodPar
                 if(!matches_count && default_code)                  if(!matches_count && default_code)
                         r.process_write(*default_code);                          r.process_write(*default_code);
                 else                  else
                         r.write_pass_lang(result);                          r.write(result);
         }          }
 }  }
   
 static void change_case(Request& r, MethodParams&,   static void change_case(Request& r, MethodParams&, String::Change_case_kind kind) {
                                                 String::Change_case_kind kind) {  
         const String& src=GET_SELF(r, VString).string();          const String& src=GET_SELF(r, VString).string();
   
         r.write_pass_lang(src.change_case(r.charsets.source(), kind));          r.write(src.change_case(r.charsets.source(), kind));
 }  }
 static void _upper(Request& r, MethodParams& params) {  static void _upper(Request& r, MethodParams& params) {
         change_case(r, params, String::CC_UPPER);          change_case(r, params, String::CC_UPPER);
Line 466  static void _lower(Request& r, MethodPar Line 466  static void _lower(Request& r, MethodPar
   
 #ifndef DOXYGEN  #ifndef DOXYGEN
 class String_sql_event_handlers: public SQL_Driver_query_event_handlers {  class String_sql_event_handlers: public SQL_Driver_query_event_handlers {
         const String& statement_string; const char* statement_cstr;  
         bool got_column;          bool got_column;
 public:  public:
         bool got_cell;          bool got_cell;
         const String* result;          const String* result;
 public:  public:
         String_sql_event_handlers(          String_sql_event_handlers():
                 const String& astatement_string, const char* astatement_cstr):  
                 statement_string(astatement_string), statement_cstr(astatement_cstr),  
                 got_column(false),                  got_column(false),
                 got_cell(false),                  got_cell(false),
                 result(&String::Empty) {}                  result(&String::Empty) {}
   
         bool add_column(SQL_Error& error, const char* /*str*/, size_t /*length*/) {          bool add_column(SQL_Error& error, const char* /*str*/, size_t /*length*/) {
                 if(got_column) {                  if(got_column) {
                         error=SQL_Error(PARSER_RUNTIME,                          error=SQL_Error("result must contain exactly one column");
                                 //statement_string,  
                                 "result must contain exactly one column");  
                         return true;                          return true;
                 }                  }
                 got_column=true;                  got_column=true;
Line 493  public: Line 488  public:
         bool add_row(SQL_Error& /*error*/) { /* ignore */ return false; }          bool add_row(SQL_Error& /*error*/) { /* ignore */ return false; }
         bool add_row_cell(SQL_Error& error, const char* str, size_t) {          bool add_row_cell(SQL_Error& error, const char* str, size_t) {
                 if(got_cell) {                  if(got_cell) {
                         error=SQL_Error(PARSER_RUNTIME,                          error=SQL_Error("result must not contain more then one row");
                                 //statement_string,  
                                 "result must not contain more then one row");  
                         return true;                          return true;
                 }                  }
   
                 try {                  try {
                         got_cell=true;                          got_cell=true;
                         result=new String(str, String::L_TAINTED /* no length as 0x00 can be inside */ );                          result=new String(str, String::L_TAINTED /* no length as 0x00 can be inside */ );
                         return false;                          return false;
                 } catch(...) {                  } catch(...) {
                         error=SQL_Error("exception occured in String_sql_event_handlers::add_row_cell");                          error=SQL_Error("exception occurred in String_sql_event_handlers::add_row_cell");
                         return true;                          return true;
                 }                  }
         }          }
 };  };
 #endif  #endif
 extern String sql_bind_name;  
 extern String sql_limit_name;  
 extern String sql_offset_name;  
 extern String sql_default_name;  
 extern String sql_distinct_name;  
 extern int marshal_binds(HashStringValue& hash, SQL_Driver::Placeholder*& placeholders);  extern int marshal_binds(HashStringValue& hash, SQL_Driver::Placeholder*& placeholders);
 extern void unmarshal_bind_updates(HashStringValue& hash, int placeholder_count, SQL_Driver::Placeholder* placeholders);  extern void unmarshal_bind_updates(HashStringValue& hash, int placeholder_count, SQL_Driver::Placeholder* placeholders);
   
Line 555  const String* sql_result_string(Request& Line 543  const String* sql_result_string(Request&
         const String& statement_string=r.process_to_string(statement);          const String& statement_string=r.process_to_string(statement);
         const char* statement_cstr=statement_string.untaint_cstr(String::L_SQL, r.connection());          const char* statement_cstr=statement_string.untaint_cstr(String::L_SQL, r.connection());
   
         String_sql_event_handlers handlers(statement_string, statement_cstr);          String_sql_event_handlers handlers;
   
         r.connection()->query(          r.connection()->query(statement_cstr, placeholders_count, placeholders, offset, limit, handlers, statement_string);
                 statement_cstr,   
                 placeholders_count, placeholders,  
                 offset, limit,   
                 handlers,  
                 statement_string);  
                   
         if(bind)          if(bind)
                 unmarshal_bind_updates(*bind, placeholders_count, placeholders);                  unmarshal_bind_updates(*bind, placeholders_count, placeholders);
Line 581  static void _sql(Request& r, MethodParam Line 564  static void _sql(Request& r, MethodParam
                 if(default_code) {                  if(default_code) {
                         string=&r.process_to_string(*default_code);                          string=&r.process_to_string(*default_code);
                 } else                  } else
                         throw Exception(PARSER_RUNTIME,                          throw Exception(PARSER_RUNTIME, 0, "produced no result, but no default option specified");
                                 0,  
                                 "produced no result, but no default option specified");  
         }          }
   
         r.write_pass_lang(*string);          r.write(*string);
 }  }
   
 static void _replace(Request& r, MethodParams& params) {  static void _replace(Request& r, MethodParams& params) {
Line 594  static void _replace(Request& r, MethodP Line 575  static void _replace(Request& r, MethodP
   
         if(params.count()==1) {          if(params.count()==1) {
                 // ^string.replace[table]                  // ^string.replace[table]
                 Table* table=params.as_table(0, "param");                  if(Table* table=params.as_table(0, "param")){
                 Dictionary dict(*table);                          Dictionary dict(*table);
                 r.write_pass_lang(src.replace(dict));                          r.write(src.replace(dict));
                   } else
                           r.write(src);
         } else {          } else {
                 // ^string.replace[from-string;to-string]                  // ^string.replace[from-string;to-string]
                 Dictionary dict(                  Dictionary dict(params.as_string(0, "from must be string"), params.as_string(1, "to must be string"));
                                                 params.as_string(0, "from must be string"),                  r.write(src.replace(dict));
                                                 params.as_string(1, "to must be string")  
                                         );  
                 r.write_pass_lang(src.replace(dict));  
         }          }
   
 }  }
Line 646  static void _save(Request& r, MethodPara Line 626  static void _save(Request& r, MethodPara
         String::Body sbody=src.cstr_to_string_body_untaint(String::L_AS_IS, r.connection(false), &r.charsets);          String::Body sbody=src.cstr_to_string_body_untaint(String::L_AS_IS, r.connection(false), &r.charsets);
   
         // write          // write
         file_write(r.charsets, r.absolute(file_name), sbody.cstr(), sbody.length(), true, do_append, asked_charset);          file_write(r.charsets, r.full_disk_path(file_name), sbody.cstr(), sbody.length(), true, do_append, asked_charset);
 }  }
   
 static void _normalize(Request& r, MethodParams&) {  static void _normalize(Request& r, MethodParams&) {
         const String& src=GET_SELF(r, VString).string();          const String& src=GET_SELF(r, VString).string();
   
         r.write_pass_lang(src);          r.write(src);
 }  }
   
 static void _trim(Request& r, MethodParams& params) {  static void _trim(Request& r, MethodParams& params) {
Line 673  static void _trim(Request& r, MethodPara Line 653  static void _trim(Request& r, MethodPara
                         else if(params_count==1)                          else if(params_count==1)
                                 chars=skind.cstr();                                  chars=skind.cstr();
                         else                          else
                                 throw Exception(PARSER_RUNTIME,                                  throw Exception(PARSER_RUNTIME, &skind, "'kind' must be one of " TRIM_START_OPTION ", " TRIM_BOTH_OPTION ", " TRIM_END_OPTION);
                                         &skind,  
                                         "'kind' must be one of " TRIM_START_OPTION ", " TRIM_BOTH_OPTION ", " TRIM_END_OPTION);  
                 }                  }
   
                 if(params_count>1) {                  if(params_count>1) {
Line 685  static void _trim(Request& r, MethodPara Line 663  static void _trim(Request& r, MethodPara
                 }                  }
         }          }
   
         r.write_pass_lang(src.trim(kind, chars, &r.charsets.source()));          r.write(src.trim(kind, chars, &r.charsets.source()));
   }
   
   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++;
                           }
                   }
   
                   if(valid_options != options->count())
                           throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION);
           }
           return result;
   }
   
   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++;
                           }
                   }
   
                   if(valid_options != options->count())
                           throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION);
           }
           return result;
 }  }
   
 static void _base64(Request& r, MethodParams& params) {  static void _base64(Request& r, MethodParams& params) {
         if(&r.get_self() == string_class) {          if(&r.get_self() == string_class) {
                 // decode: ^string:base64[encoded[;$.strict(true|false)]]                  // decode: ^string:base64[encoded[;options]]
                 const char* cstr=params.count() ? params.as_string(0, PARAMETER_MUST_BE_STRING).cstr() : "";                  const char* cstr=params.count() ? params.as_string(0, PARAMETER_MUST_BE_STRING).cstr() : "";
                 char* decoded=0;                  Base64Options options = base64_decode_options(r, params.count() > 1 ? params.as_hash(1) : NULL);
                 size_t length=0;  
   
                 bool strict=false;                  char* decoded=0;
                 if(params.count() > 1)                  size_t length=pa_base64_decode(cstr, strlen(cstr), decoded, options);
                         if(HashStringValue* options=params.as_hash(1)) {  
                                 int valid_options=0;  
                                 if(Value* vstrict=options->get(BASE64_STRICT_OPTION_NAME)) {  
                                         strict=r.process(*vstrict).as_bool();  
                                         valid_options++;  
                                 }  
                                 if(valid_options!=options->count())  
                                         throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION);  
                         }  
   
                 pa_base64_decode(cstr, strlen(cstr), decoded, length, strict);  
                 if(decoded && length){                  if(decoded && length){
                         if(memchr((const char*)decoded, 0, length))                          if(memchr(decoded, 0, length))
                                 throw Exception(PARSER_RUNTIME,                                  throw Exception(PARSER_RUNTIME, 0, "Invalid \\x00 character found while decoding to string. Decode to file instead.");
                                         0,  
                                         "Invalid \\x00 character found while decode to string. Decode it to file instead.");  
   
                         fix_line_breaks(decoded, length);                          fix_line_breaks(decoded, length);
   
                         if(length)                          if(length)
                                 r.write_pass_lang(*new String(decoded, String::L_TAINTED));                                  r.write(*new String(decoded, String::L_TAINTED));
                 }                  }
         } else {          } else {
                 // encode: ^str.base64[]                  // encode: ^str.base64[options]
                 VString& self=GET_SELF(r, VString);                  VString& self=GET_SELF(r, VString);
                 const char* cstr=self.string().cstr();                  const char* cstr=self.string().cstr();
                 const char* encoded=pa_base64_encode(cstr, strlen(cstr));                  Base64Options options = base64_encode_options(r, params.count() ? params.as_hash(0) : NULL);
                 r.write_pass_lang(*new String(encoded, String::L_TAINTED/*once ?param=base64(something) was needed*/));                  const char* encoded=pa_base64_encode(cstr, strlen(cstr), options);
                   r.write(*new String(encoded, String::L_TAINTED /*once ?param=base64(something) was needed*/ ));
         }          }
 }  }
   
Line 731  static void _idna(Request& r, MethodPara Line 750  static void _idna(Request& r, MethodPara
         if(&r.get_self() == string_class) {          if(&r.get_self() == string_class) {
                 // decode: ^string:idna[encoded]                  // decode: ^string:idna[encoded]
                 const char* cstr=params.count() ? params.as_string(0, PARAMETER_MUST_BE_STRING).cstr() : "";                  const char* cstr=params.count() ? params.as_string(0, PARAMETER_MUST_BE_STRING).cstr() : "";
                 r.write_pass_lang(*new String(pa_idna_decode(cstr, r.charsets.source()), String::L_TAINTED));                  r.write(*new String(pa_idna_decode(cstr, r.charsets.source()), String::L_TAINTED));
         } else {          } else {
                 // encode: ^str.idna[]                  // encode: ^str.idna[]
                 VString& self=GET_SELF(r, VString);                  VString& self=GET_SELF(r, VString);
                 const char* cstr=self.string().cstr();                  const char* cstr=self.string().cstr();
                 r.write_pass_lang(*new String(pa_idna_encode(cstr, r.charsets.source()), String::L_TAINTED));                  r.write(*new String(pa_idna_encode(cstr, r.charsets.source()), String::L_TAINTED));
         }          }
 }  }
   
 static void _js_escape(Request& r, MethodParams&){  static void _js_escape(Request& r, MethodParams&){
         const String& src=GET_SELF(r, VString).string();          const String& src=GET_SELF(r, VString).string();
         r.write_pass_lang(src.escape(r.charsets.source()));          r.write(src.escape(r.charsets.source()));
 }  }
   
 static void _js_unescape(Request& r, MethodParams& params){  static void _js_unescape(Request& r, MethodParams& params){
         const String& src=params.as_string(0, PARAMETER_MUST_BE_STRING);          const String& src=params.as_string(0, PARAMETER_MUST_BE_STRING);
         if(const char* result=unescape_chars(src.cstr(), src.length(), &r.charsets.source(), true))          if(const char* result=unescape_chars(src.cstr(), src.length(), &r.charsets.source(), true))
                 r.write_pass_lang(*new String(result, String::L_TAINTED));                  r.write(*new String(result, String::L_TAINTED));
 }  }
   
 static void _unescape(Request& r, MethodParams& params){  static void _unescape(Request& r, MethodParams& params){
Line 780  static void _unescape(Request& r, Method Line 799  static void _unescape(Request& r, Method
         const char* unescaped=unescape_chars(src.cstr(), src.length(), from_charset, mode_js);          const char* unescaped=unescape_chars(src.cstr(), src.length(), from_charset, mode_js);
         if(*unescaped){          if(*unescaped){
                 const String* result=new String(Charset::transcode(unescaped, *from_charset, r.charsets.source()), String::L_TAINTED);                  const String* result=new String(Charset::transcode(unescaped, *from_charset, r.charsets.source()), String::L_TAINTED);
                 r.write_pass_lang(*result);                  r.write(*result);
         }          }
 }  }
   
Line 788  static void _contains(Request& r, Method Line 807  static void _contains(Request& r, Method
         // empty or whitespace string is hash compatible          // empty or whitespace string is hash compatible
         GET_SELF(r, VString).get_element(params.as_string(0, "key must be string"));          GET_SELF(r, VString).get_element(params.as_string(0, "key must be string"));
         // ignoring result as it allways null          // ignoring result as it allways null
         r.write_no_lang(VBool::get(false));          r.write(VBool::get(false));
 }  }
   
 // constructor  // constructor
Line 826  MString::MString(): Methoded("string") { Line 845  MString::MString(): Methoded("string") {
         // ^string.split[delim][options]          // ^string.split[delim][options]
         // ^string.split[delim][options][column name]          // ^string.split[delim][options][column name]
         add_native_method("split", Method::CT_DYNAMIC, _split, 1, 3);          add_native_method("split", Method::CT_DYNAMIC, _split, 1, 3);
                 // old names for backward compatibility          // old names for backward compatibility
                 // ^string.lsplit[delim]          // ^string.lsplit[delim]
                 add_native_method("lsplit", Method::CT_DYNAMIC, _lsplit, 1, 1);          add_native_method("lsplit", Method::CT_DYNAMIC, _lsplit, 1, 1);
                 // ^string.rsplit[delim]          // ^string.rsplit[delim]
                 add_native_method("rsplit", Method::CT_DYNAMIC, _rsplit, 1, 1);          add_native_method("rsplit", Method::CT_DYNAMIC, _rsplit, 1, 1);
           
         // ^string.match[regexp][options]          // ^string.match[regexp][options]
         // ^string.match[regexp][options]{replacement-code}          // ^string.match[regexp][options]{replacement-code}
         // ^string.match[regexp][options]{replacement-code}{code-if-nothing-is-found}          // ^string.match[regexp][options]{replacement-code}{code-if-nothing-is-found}

Removed from v.1.232  
changed lines
  Added in v.1.253


E-mail: