Diff for /parser3/src/classes/op.C between versions 1.127.2.22.2.3 and 1.189

version 1.127.2.22.2.3, 2003/03/19 13:16:55 version 1.189, 2009/06/04 12:38:46
Line 1 Line 1
 /** @file  /** @file
         Parser: parser @b operators.          Parser: parser @b operators.
   
         Copyright (c) 2001-2003 ArtLebedev Group (http://www.artlebedev.com)          Copyright (c) 2001-2009 ArtLebedev Group (http://www.artlebedev.com)
         Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)          Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)
 */  */
   
 static const char* IDENT_OP_C="$Date$";  static const char * const IDENT_OP_C="$Date$";
   
 #include "classes.h"  #include "classes.h"
 #include "pa_vmethod_frame.h"  #include "pa_vmethod_frame.h"
   
 #include "pa_common.h"  #include "pa_common.h"
   #include "pa_os.h"
 #include "pa_request.h"  #include "pa_request.h"
 #include "pa_vint.h"  #include "pa_vint.h"
 #include "pa_sql_connection.h"  #include "pa_sql_connection.h"
 #include "pa_vdate.h"  #include "pa_vdate.h"
 #include "pa_vmethod_frame.h"  #include "pa_vmethod_frame.h"
 #include "pa_vclass.h"  #include "pa_vclass.h"
   #include "pa_charset.h"
   
 // limits  // limits
   
 #define MAX_LOOPS 10000  #define MAX_LOOPS 20000
   
 // defines  // defines
   
 #define CASE_DEFAULT_VALUE "DEFAULT"  #define CASE_DEFAULT_VALUE "DEFAULT"
   #define PROCESS_MAIN_OPTION_NAME "main"
   #define PROCESS_FILE_OPTION_NAME "file"
   #define PROCESS_LINENO_OPTION_NAME "lineno"
   
 // class  // class
   
 class VClassMAIN: public VClass {  class VClassMAIN: public VClass {
 public:  public:
         VClassMAIN();          VClassMAIN();
         override ~VClassMAIN() {}  
 };  };
   
   // defines for globals
   
   #define CYCLE_DATA_NAME "CYCLE-DATA"
   
   // globals
   
   //^for & co
   String cycle_data_name(CYCLE_DATA_NAME);
   
 // defines for statics  // defines for statics
   
 #define SWITCH_DATA_NAME "SWITCH-DATA"  #define SWITCH_DATA_NAME "SWITCH-DATA"
 #define CACHE_DATA_NAME "CACHE-DATA"  #define CACHE_DATA_NAME "CACHE-DATA"
   
 #define EXCEPTION_VAR_NAME "exception"  #define EXCEPTION_VAR_NAME "exception"
 #define EXCEPTION_TYPE_PART_NAME "type"  
 #define EXCEPTION_SOURCE_PART_NAME "source"  
 #define EXCEPTION_COMMENT_PART_NAME "comment"  
 #define EXCEPTION_HANDLED_PART_NAME "handled"  
   
 // statics  // statics
   
 //^switch ^case  //^switch ^case
 static const String& switch_data_name(new String(SWITCH_DATA_NAME));  static const String switch_data_name(SWITCH_DATA_NAME);
 //^cache  //^cache
 static const String& cache_data_name(new String(CACHE_DATA_NAME));  static const String cache_data_name(CACHE_DATA_NAME);
   
 static const String& exception_var_name(new String(EXCEPTION_VAR_NAME));  static const String exception_var_name(EXCEPTION_VAR_NAME);
 static const String& exception_type_part_name(new String(EXCEPTION_TYPE_PART_NAME));  
 static const String& exception_source_part_name(new String(EXCEPTION_SOURCE_PART_NAME));  
 static const String& exception_comment_part_name(new String(EXCEPTION_COMMENT_PART_NAME));  // local defines
 static const String& exception_handled_part_name(new String(EXCEPTION_HANDLED_PART_NAME));  
   #define CACHE_EXCEPTION_HANDLED_CACHE_NAME "cache"
   
 // helpers  // helpers
   
 class Untaint_lang_name2enum: public Hash<String* , int> {  class Untaint_lang_name2enum: public HashString<String::Language> {
 public:  public:
         Untaint_lang_name2enum() {          Untaint_lang_name2enum() {
                 #define ULN(name, LANG) \                  #define ULN(name, LANG) \
                         put(String* (new String(name)), (value_type)String::UL_##LANG);                          put(String::Body(name), (value_type)(String::L_##LANG));
                 ULN("as-is", AS_IS);                  ULN("as-is", AS_IS);
                 ULN("optimized-as-is", AS_IS|String::UL_OPTIMIZE_BIT);                  ULN("optimized-as-is", AS_IS|String::L_OPTIMIZE_BIT);
                 ULN("file-spec", FILE_SPEC);                  ULN("file-spec", FILE_SPEC);
                 ULN("http-header", HTTP_HEADER);                  ULN("http-header", HTTP_HEADER);
                 ULN("mail-header", MAIL_HEADER);                  ULN("mail-header", MAIL_HEADER);
                 ULN("uri", URI);                  ULN("uri", URI);
                 ULN("table", TABLE);  
                 ULN("sql", SQL);                  ULN("sql", SQL);
                 ULN("js", JS);                  ULN("js", JS);
                 ULN("xml", XML);                  ULN("xml", XML);
                 ULN("optimized-xml", XML|String::UL_OPTIMIZE_BIT);                  ULN("optimized-xml", XML|String::L_OPTIMIZE_BIT);
                 ULN("html", HTML);                  ULN("html", HTML);
                 ULN("optimized-html", HTML|String::UL_OPTIMIZE_BIT);                  ULN("optimized-html", HTML|String::L_OPTIMIZE_BIT);
                   ULN("regex", REGEX);
                 #undef ULN                  #undef ULN
         }          }
 } untaint_lang_name2enum;  } untaint_lang_name2enum;
   
 // methods  // methods
   
 static void _if(Request& r, const String& /*method_name*/, MethodParams* params) {  static void _if(Request& r, MethodParams& params) {
         ValuePtr condition_code=params->as_junction(0, "condition must be expression");          bool condition=params.as_bool(0, "condition must be expression", r);
   
         bool condition=r.process_to_value(condition_code,   
                 /*0/*no name* /,*/  
                 false/*don't intercept string*/)->as_bool();  
         if(condition)          if(condition)
                 r.write_pass_lang(r.process(params->as_junction(1, "'then' parameter must be code")));                  r.process_write(*params.get(1));
         else if(params->count()>2)          else if(params.count()>2)
                 r.write_pass_lang(r.process(params->as_junction(2, "'else' parameter must be code")));                  r.process_write(*params.get(2));
 }  }
   
 static void _untaint(Request& r, const String& method_name, MethodParams* params) {  static String::Language get_untaint_lang(MethodParams& params, int index){
           const String& lang_name=params.as_string(index, "lang must be string");
         String_UL lang;          String::Language lang=untaint_lang_name2enum.get(lang_name);
         if(params->count()==1)          if(!lang)
                 lang=String::UL_AS_IS; // mark as simply 'tainted'. useful in html from sql                   throw Exception(PARSER_RUNTIME,
         else {                          &lang_name,
                 const String& lang_name=params->as_string(0, "lang must be string");                          "invalid taint language");
                 lang=untaint_lang_name2enum.get(lang_name);          return lang;
                 if(!lang)  }
                         throw Exception(0,  
                                 lang_name,  static void _untaint(Request& r, MethodParams& params) {
                                 "invalid taint language");          String::Language lang;
         }          if(params.count()==1)
                   lang=String::L_AS_IS; // mark as simply 'as-is'. useful in html from sql 
           else
                   lang=get_untaint_lang(params, 0);
   
         {          {
                 ValuePtr vbody=params->as_junction(params->count()-1, "body must be code");                  Value& vbody=params.as_junction(params.count()-1, "body must be code");
                                   
                 Temp_lang temp_lang(r, lang); // set temporarily specified ^untaint[language;                  StringOrValue result;
                 r.write_pass_lang(r.process(vbody)); // process marking tainted with that lang                  {
                           Temp_lang temp_lang(r, lang); // set temporarily specified ^untaint[language;
                           result=r.process(vbody); // process marking tainted with that lang
                   }
                   r.write_assign_lang(result);
         }          }
 }  }
   
 static void _taint(Request& r, const String& /*method_name*/, MethodParams* params) {  static void _taint(Request& r, MethodParams& params) {
           String::Language lang;
         String_UL lang;          if(params.count()==1)
         if(params->count()==1)                  lang=String::L_TAINTED; // mark as simply 'tainted'. useful in table:create
                 lang=String::UL_TAINTED; // mark as simply 'tainted'. useful in table:set          else
         else {                  lang=get_untaint_lang(params, 0);
                 const String& lang_name=params->as_string(0, "lang must be string");  
                 lang=untaint_lang_name2enum.get(lang_name);  
                 if(!lang)  
                         throw Exception(0,  
                                 lang_name,  
                                 "invalid taint language");  
         }  
   
         {          {
                 ValuePtr vbody=params->as_no_junction(params->count()-1, "body must not be code");                  Value& vbody=params.as_no_junction(params.count()-1, "body must not be code");
                                   
                 String result;                  String result;
                 result.append(                  result.append(
                         *vbody->as_string(&pool),  // process marking tainted with that lang                          vbody.as_string(),  // process marking tainted with that lang
                         lang, true);  // force result language to specified                          lang, true);  // force result language to specified
                 r.write_pass_lang(result);                  r.write_assign_lang(result);
         }          }
 }  }
   
 static void _process(Request& r, const String& method_name, MethodParams* params) {  static void _process(Request& r, MethodParams& params) {
         const String* source; // must be declared here to prevent premature destruction          Method* main_method;
         MethodPtr main_method;  
         ValuePtr target_self=params->count()>1?  
                 params->as_no_junction(0, "target must not be code")  
                 :ValuePtr(&r.get_method_frame()->caller()->self());  
         {  
                 ValuePtr vjunction=params->as_junction(params->count()-1, "body must be code");  
   
           size_t index=0;
           Value* target_self;
           Value& maybe_target_self=params[index];
           if(maybe_target_self.get_string() || maybe_target_self.get_junction())
                   target_self=&r.get_method_frame()->caller()->self();
           else {
                   target_self=&maybe_target_self;  index++;
           }
   
           {
                 VStateless_class *target_class=target_self->get_last_derived_class();                  VStateless_class *target_class=target_self->get_last_derived_class();
                 if(!target_class)                  if(!target_class)
                         throw Exception("parser.runtime",                          throw Exception(PARSER_RUNTIME,
                                 method_name,                                  0,
                                 "no target class");                                  "no target class");
                   
                 // evaluate source to process  
                 source=r.process_to_string(vjunction);  
   
                 // temporary remove language change                  // temporary remove language change
                 Temp_lang temp_lang(r, String::UL_PASS_APPENDED);                  Temp_lang temp_lang(r, String::L_PASS_APPENDED);
                 // temporary zero @main so to maybe-replace it in processed code                  // temporary zero @main so to maybe-replace it in processed code
                 Temp_method temp_method_main(*target_class, main_method_name, MethodPtr(0));                  Temp_method temp_method_main(*target_class, main_method_name, 0);
                 // temporary zero @auto so it wouldn't be auto-called in Request::use_buf                  // temporary zero @auto so it wouldn't be auto-called in Request::use_buf
                 Temp_method temp_method_auto(*target_class, auto_method_name, MethodPtr(0));                  Temp_method temp_method_auto(*target_class, auto_method_name, 0);
                   
                 // calculate pseudo file name of processed chars                  size_t options_index=index+1;
                 // would be something like "/some/file(4) process"                  HashStringValue* options=0;
                 char local_place[MAX_STRING];                  if(options_index<params.count()) {
 #ifndef NO_STRING_ORIGIN                          Value& voptions=params.as_no_junction(options_index, "options must not be code");
                 const String_fragment::Origin& source_origin=source->origin();                          options=voptions.get_hash();
                 const String_fragment::Origin& method_origin=method_name->origin();                          if(!options)
                 size_t place_size;                                  throw Exception(PARSER_RUNTIME,
                 if(source_origin.file==method_origin.file)                                          0,
                         place_size=snprintf(local_place, MAX_STRING, "%s(%d) %s",  // same file                                          "options must be hash");
                                 source_origin.file?source_origin.file:"unknown_file", 1+source_origin.line,                   }
                                 method_name->cstr().get())+1;  
                 else // different files ^process{external__file_text__or__sql}                  const String* main_alias=0;
                         place_size=snprintf(local_place, MAX_STRING, "%s",                   const String* file_alias=0;
                                 source_origin.file?source_origin.file:"unknown_file")+1;                  int line_no_alias_offset=0;
 #else                  if(options) {
                 strncpy(local_place, method_name->cstr(), MAX_STRING-1); place[MAX_STRING-1]=0;                          int valid_options=0;
                 size_t place_size=strlen(local_place)+1;                          if(Value* vmain_alias=options->get(PROCESS_MAIN_OPTION_NAME)) {
 #endif                                  valid_options++;
                 char *heap_place=pool.copy(local_place, place_size);                                  main_alias=&vmain_alias->as_string();
                           }
                           if(Value* vfile_alias=options->get(PROCESS_FILE_OPTION_NAME)) {
                                   valid_options++;
                                   file_alias=&vfile_alias->as_string();
                           }
                           if(Value* vline_no_alias_offset=options->get(PROCESS_LINENO_OPTION_NAME)) {
                                   valid_options++;
                                   line_no_alias_offset=vline_no_alias_offset->as_int();
                           }
           
                           if(valid_options!=options->count())
                                   throw Exception(PARSER_RUNTIME,
                                           0,
                                           "called with invalid option");
                   }
   
                 // process source code, append processed methods to 'self' class                  uint processe_file_no=file_alias?
                 // maybe-define new @main                          r.register_file(r.absolute(*file_alias))
                           : pseudo_file_no__process;
                   // process...{string}
                   Value& vjunction=params.as_junction(index, "body must be code");
                   // evaluate source to process
                   const String& source=r.process_to_string(vjunction);
                 r.use_buf(*target_class,                  r.use_buf(*target_class,
                         source->cstr(pool, String::UL_UNSPECIFIED, r.connection(0)),                           source.cstr(String::L_UNSPECIFIED, r.connection(false)),
                         String* (new String(heap_place, place_size, true /*tainted*/)),                          main_alias,
                         heap_place);                          processe_file_no,
                                           line_no_alias_offset);
   
                 // main_method                  // main_method
                 main_method=target_class->get_method(main_method_name);                  main_method=target_class->get_method(main_method_name);
         }          }
Line 201  static void _process(Request& r, const S Line 230  static void _process(Request& r, const S
         // maybe-execute @main[]          // maybe-execute @main[]
         if(main_method) {          if(main_method) {
                 // temporarily set method_frame's self to target_self                  // temporarily set method_frame's self to target_self
                 Temp_method_frame_self tmfs(*r.get_method_frame(), target_self);                  Temp_method_frame_self tmfs(*r.get_method_frame(), *target_self);
                 // execute!                       // execute!     
                 r.execute(*main_method->parser_code);                  r.execute(*main_method->parser_code);
         }          }
 }  }
                   
 static void _rem(Request& r, const String& /*method_name*/, MethodParams* params) {  static void _rem(Request&, MethodParams& params) {
         params->as_junction(0, "body must be code");          params.as_junction(0, "body must be code");
 }  }
   
 static void _while(Request& r, const String& method_name, MethodParams* params) {  static void _while(Request& r, MethodParams& params) {
           Temp_hash_value<const String::Body, void*> 
                   cycle_data_setter(r.classes_conf, cycle_data_name, /*any not null flag*/&r);
   
         ValuePtr vcondition=params->as_junction(0, "condition must be expression");          Value& vcondition=params.as_expression(0, "condition must be number, bool or expression");
         ValuePtr body=params->as_junction(1, "body must be code");  
           Value& body_code=params.as_junction(1, "body must be code");
           Value* delim_maybe_code=params.count()>2?&params[2]:0;
   
         // while...          // while...
         int endless_loop_count=0;          int endless_loop_count=0;
         while(true) {          if(delim_maybe_code){ // delimiter set
                 if(++endless_loop_count>=MAX_LOOPS) // endless loop?                  bool need_delim=false;
                         throw Exception("parser.runtime",                  while(true) {
                                 method_name,                          if(++endless_loop_count>=MAX_LOOPS) // endless loop?
                                 "endless loop detected");                                  throw Exception(PARSER_RUNTIME,
                                           0,
                 bool condition=r.process_to_value(vcondition,                                           "endless loop detected");
                                 /*0/*no name* /,*/  
                                 false/*don't intercept string*/)->as_bool();                          if(!r.process_to_value(vcondition, false/*don't intercept string*/).as_bool())
                 if(!condition) // ...condition is true                                  break;
                         break;  
                           StringOrValue sv_processed=r.process(body_code);
                           Request::Skip lskip=r.get_skip(); r.set_skip(Request::SKIP_NOTHING);
   
                           const String* s_processed=sv_processed.get_string();
                           if(s_processed && !s_processed->is_empty()) { // we have body
                                   if(need_delim) // need delim & iteration produced string?
                                           r.write_pass_lang(r.process(*delim_maybe_code));
                                   else
                                           need_delim=true;
                           }
                           r.write_pass_lang(sv_processed);
   
                           if(lskip==Request::SKIP_BREAK)
                                   break;
                   }
           } else {
                   while(true) {
                           if(++endless_loop_count>=MAX_LOOPS) // endless loop?
                                   throw Exception(PARSER_RUNTIME,
                                           0,
                                           "endless loop detected");
   
                           if(!r.process_to_value(vcondition, false/*don't intercept string*/).as_bool())
                                   break;
   
                           r.process_write(body_code);
                           Request::Skip lskip=r.get_skip(); r.set_skip(Request::SKIP_NOTHING);
   
                 // write processed body                          if(lskip==Request::SKIP_BREAK)
                 r.write_pass_lang(r.process(body));                                  break;
                   }
         }          }
 }  }
   
 static void _use(Request& r, const String& method_name, MethodParams* params) {  static void _use(Request& r, MethodParams& params) {
         ValuePtr vfile=params->as_no_junction(0, "file name must not be code");          Value& vfile=params.as_no_junction(0, FILE_NAME_MUST_NOT_BE_CODE);
         r.use_file(*r.main_class, vfile->as_string(&pool));          r.use_file(r.main_class, vfile.as_string());
   }
   
   static void set_skip(Request& r, Request::Skip askip) {
           void* data=r.classes_conf.get(cycle_data_name);
           if(!data)
                   throw Exception(PARSER_RUNTIME,
                           0,
                           "without cycle");
   
           r.set_skip(askip);
   }
   
   static void _break(Request& r, MethodParams&) {
           set_skip(r, Request::SKIP_BREAK);
 }  }
   
 static void _for(Request& r, const String& method_name, MethodParams* params) {  static void _continue(Request& r, MethodParams&) {
         const String& var_name=params->as_string(0, "var name must be string");          set_skip(r, Request::SKIP_CONTINUE);
         int from=params->as_int(1, "from must be int", r);  }
         int to=params->as_int(2, "to must be int", r);  
         ValuePtr  body_code=params->as_junction(3, "body must be code");  static void _for(Request& r, MethodParams& params) {
         ValuePtr delim_maybe_code=params->count()>4?(*params)[4]:ValuePtr(0);          Temp_hash_value<const String::Body, void*> 
                   cycle_data_setter(r.classes_conf, cycle_data_name, /*any not null flag*/&r);
   
           const String& var_name=params.as_string(0, "var name must be string");
           int from=params.as_int(1, "from must be int", r);
           int to=params.as_int(2, "to must be int", r);
           Value& body_code=params.as_junction(3, "body must be code");
           Value* delim_maybe_code=params.count()>4?&params[4]:0;
   
         if(to-from>=MAX_LOOPS) // too long loop?          if(to-from>=MAX_LOOPS) // too long loop?
                 throw Exception("parser.runtime",                  throw Exception(PARSER_RUNTIME,
                         method_name,                          0,
                         "endless loop detected");                          "endless loop detected");
   
         bool need_delim=false;          VInt* vint=new VInt(0);
         VIntPtr vint(new VInt(0));  
         r.get_method_frame()->caller()->put_element(var_name, vint, false);          VMethodFrame& caller=*r.get_method_frame()->caller();
         for(int i=from; i<=to; i++) {          caller.put_element(caller, var_name, vint, false);
                 vint->set_int(i);          if(delim_maybe_code){ // delimiter set 
                   bool need_delim=false;
   
                   for(int i=from; i<=to; i++) {
                           vint->set_int(i);
   
                           StringOrValue sv_processed=r.process(body_code);
                           Request::Skip lskip=r.get_skip(); r.set_skip(Request::SKIP_NOTHING);
   
                           const String* s_processed=sv_processed.get_string();
                           if(s_processed && !s_processed->is_empty()) { // we have body
                                   if(need_delim) // need delim & iteration produced string?
                                           r.write_pass_lang(r.process(*delim_maybe_code));
                                   else
                                           need_delim=true;
                           }
                           r.write_pass_lang(sv_processed);
   
                           if(lskip==Request::SKIP_BREAK)
                                   break;
                   }
           } else {
                   for(int i=from; i<=to; i++) {
                           vint->set_int(i);
    
                           r.process_write(body_code);
                           Request::Skip lskip=r.get_skip(); r.set_skip(Request::SKIP_NOTHING);
   
                 StringOrValue sv_processed=r.process(body_code);                          if(lskip==Request::SKIP_BREAK)
                 const String& s_processed=sv_processed.get_string();                                  break;
                 if(delim_maybe_code && s_processed && s_processed->size()) { // delimiter set and we have body  
                         if(need_delim) // need delim & iteration produced string?  
                                 r.write_pass_lang(r.process(delim_maybe_code));  
                         need_delim=true;  
                 }                  }
                 r.write_pass_lang(sv_processed);  
         }          }
 }  }
   
 static void _eval(Request& r, const String& method_name, MethodParams* params) {  static void _eval(Request& r, MethodParams& params) {
         ValuePtr expr=params->as_junction(0, "need expression");          Value& expr=params.as_junction(0, "need expression");
         // evaluate expresion          // evaluate expresion
         ValuePtr value_result=r.process_to_value(expr,           Value& value_result=r.process_to_value(expr, 
                 /*0/*no name YET* /,*/                  false/*don't intercept string*/).as_expr_result();
                 true/*don't intercept string*/)->as_expr_result();          if(params.count()>1) {
         if(params->count()>1) {                  const String& fmt=params.as_string(1, "fmt must be string").trim();
                 ValuePtr fmt=params->as_no_junction(1, "fmt must not be code");                  if(fmt.is_empty()){
                 String string;                          r.write_no_lang(value_result);
                 string.APPEND_CONST(                  } else {
                         format(pool, value_result->as_double(), fmt->as_string(&pool)->cstr()));                          r.write_no_lang(String(format(value_result.as_double(), fmt.cstrm())));
                 r.write_no_lang(string);                  }
         } else          } else
                 r.write_no_lang(value_result);                  r.write_no_lang(value_result);
 }  }
   
 static void _connect(Request& r, const String& method_name, MethodParams* params) {  static void _connect(Request& r, MethodParams& params) {
 #ifdef RESOURCES_DEBUG  #ifdef RESOURCES_DEBUG
 struct timeval mt[2];  struct timeval mt[2];
 #endif  #endif
         ValuePtr url=params->as_no_junction(0, "url must not be code");          Value& url=params.as_no_junction(0, "url must not be code");
         ValuePtr body_code=params->as_junction(1, "body must be code");          Value& body_code=params.as_junction(1, "body must be code");
   
         Table* protocol2driver_and_client=0;          Table* protocol2driver_and_client=0;
         if(ValuePtr sql=r.main_class->get_element(String* (new String(MAIN_SQL_NAME)), *r.main_class, false)) {          if(Value* sql=r.main_class.get_element(String(MAIN_SQL_NAME), r.main_class, false)) {
                 if(ValuePtr element=sql->get_element(String* (new String(MAIN_SQL_DRIVERS_NAME)), *sql, false)) {                  if(Value* element=sql->get_element(String(MAIN_SQL_DRIVERS_NAME), *sql, false)) {
                         protocol2driver_and_client=element->get_table();                          protocol2driver_and_client=element->get_table();
                 }                  }
         }          }
Line 304  struct timeval mt[2]; Line 407  struct timeval mt[2];
 gettimeofday(&mt[0],NULL);  gettimeofday(&mt[0],NULL);
 #endif  #endif
         // connect          // connect
         SQL_ConnectionPtr connection=SQL_driver_manager.get_connection(pool,          SQL_Connection* connection=SQL_driver_manager->get_connection(url.as_string(), 
                 url->as_string(&pool), method_name, protocol2driver_and_client);                  protocol2driver_and_client,
                   r.charsets.source().NAME().cstr(),
                   r.request_info.document_root);
   
 #ifdef RESOURCES_DEBUG  #ifdef RESOURCES_DEBUG
 //measure:after connect  //measure:after connect
Line 317  for(int i=0;i<2;i++) Line 422  for(int i=0;i<2;i++)
   
 r.sql_connect_time+=t[1]-t[0];  r.sql_connect_time+=t[1]-t[0];
 #endif  #endif
         Temp_connection temp_connection(r, connection.get());          Temp_connection temp_connection(r, connection);
         // execute body          // execute body
         try {          try {
                 r.write_assign_lang(r.process(body_code));                  r.process_write(body_code);
                   connection->commit();
                   connection->close();
         } catch(...) { // process problem          } catch(...) { // process problem
                 connection->mark_to_rollback();                  connection->rollback();
                 rethrow;                   connection->close();
                   rethrow;
         }          }
 }  }
   
Line 331  r.sql_connect_time+=t[1]-t[0]; Line 439  r.sql_connect_time+=t[1]-t[0];
 class Switch_data: public PA_Object {  class Switch_data: public PA_Object {
 public:  public:
         Request& r;          Request& r;
         ValuePtr searching;          const String* searching_string;
         ValuePtr found;          double searching_double;
         ValuePtr _default;          Value* found;
           Value* _default;
 public:  public:
         Switch_data(Request& ar, ValuePtr asearching):           Switch_data(Request& ar, Value& asearching): 
           r(ar), searching(asearching) {}                  r(ar)
           {
                   if(asearching.is_string() || asearching.is_void()){
                           searching_string=&asearching.as_string();
                           searching_double=0;
                   } else {
                           searching_string=0;
                           searching_double=asearching.as_double();
                   }
           }
 };  };
 DECLARE_OBJECT_PTR(Switch_data);  
 #endif  #endif
 static void _switch(Request& r, const String& /*method_name*/, MethodParams* params) {  static void _switch(Request& r, MethodParams& params) {
         Switch_dataPtr data(new Switch_data(r, r.process_to_value(params->get(0))));          Switch_data* data=new Switch_data(r, r.process_to_value(params[0]));
         Temp_hash_value<String* , PA_Object* >           Temp_hash_value<const String::Body, void*> 
                 switch_data_setter(r.classes_conf, switch_data_name, data);                  switch_data_setter(r.classes_conf, switch_data_name, data);
   
         ValuePtr cases_code=params->as_junction(1, "switch cases must be code");          Value& cases_code=params.as_junction(1, "switch cases must be code");
         // execution of found ^case[...]{code} must be in context of ^switch[...]{code}          // execution of found ^case[...]{code} must be in context of ^switch[...]{code}
         // because of stacked WWrapper used there as wcontext          // because of stacked WWrapper used there as wcontext
         r.process(cases_code, true/*intercept_string*/);          r.process(cases_code, true/*intercept_string*/);
         if(ValuePtr selected_code=data->found? data->found: data->_default) {          if(Value* selected_code=data->found? data->found: data->_default)
                 // setting code context, would execute in ^switch[...]{>>context<<}                  r.write_pass_lang(r.process(*selected_code));
                 //selected_code->get_junction()->change_context(cases_code.get_junction());  
                 r.write_pass_lang(r.process(selected_code));  
         }  
 }  }
   
 static void _case(Request& r, const String& method_name, MethodParams* params) {  static void _case(Request& r, MethodParams& params) {
           Switch_data* data=static_cast<Switch_data*>(r.classes_conf.get(switch_data_name));
         Switch_data* data=static_cast<Switch_data*>(r.classes_conf.get(switch_data_name).get());  
         if(!data)          if(!data)
                 throw Exception("parser.runtime",                  throw Exception(PARSER_RUNTIME,
                         method_name,                          0,
                         "without switch");                          "without switch");
   
         int count=params->count();          if(data->found) // matches already was found
         ValuePtr code=params->as_junction(--count, "case result must be code");                  return;
           
         // killing context for safety, would execute in ^switch[...]{>>context<<}          int count=params.count();
         // reason: context is stacked, and it would become invalid afterwards          Value* code=&params.as_expression(--count, "case result must be code");
         //code->get_junction()->change_context(0);  
   
         for(int i=0; i<count; i++) {  #ifdef USE_DESTRUCTORS
                 ValuePtr value=r.process_to_value(params->get(i));          Junction *j=code->get_junction();
           if (j){
                   code=new VJunction(j->self,j->method,j->method_frame,j->rcontext,j->wcontext,j->code);
                   if (j->wcontext) j->wcontext->attach_junction((VJunction *)code);
           }
   #endif
           
           for(int i=0; i<count; i++){
                   Value& value=r.process_to_value(params[i]);
   
                 if(*value->as_string(&pool) == CASE_DEFAULT_VALUE) {                  if(value.is_string() && value.as_string() == CASE_DEFAULT_VALUE){
                         data->_default=code;                          data->_default=code;
                         break;                          continue;
                 }                  }
   
                 bool matches;                  bool matches;
                 if(data->searching->is_string())                  if(data->searching_string)
                         matches=*data->searching->as_string(&pool) == *value->as_string(&pool);                          matches=(*data->searching_string) == value.as_string();
                 else                  else
                         matches=data->searching->as_double() == value->as_double();                          matches=data->searching_double == value.as_double();
   
                 if(matches) {  
                         if(data->found)  
                                 throw Exception("parser.runtime",  
                                         method_name,  
                                         "duplicate found");  
   
                   if(matches){
                         data->found=code;                          data->found=code;
                         break;                          break;
                 }                  }
         }          }
 }  }
   #ifndef DOXYGEN
   struct Try_catch_result {
           StringOrValue processed_code;
           const String* exception_should_be_handled;
   
           Try_catch_result(): exception_should_be_handled(0) {}
   };
   #endif
   
   /// used by ^try and ^cache, @returns $exception.handled[string] if any
   template<class I>
   static Try_catch_result try_catch(Request& r, 
                     StringOrValue body_code(Request&, I), I info, 
                     Value* catch_code,
                     bool could_be_handled_by_caller=false) 
   {
           Try_catch_result result;
   
           if(!catch_code) {
                   result.processed_code=body_code(r, info);
                   return result;
           }
   
           // taking snapshot of try-context
           Request_context_saver try_context(r);
           try {
                   result.processed_code=body_code(r, info);
           } catch(const Exception& e) {
                   Request_context_saver throw_context(r); // taking snapshot of throw-context [stack trace contains error]
                   Request::Exception_details details=r.get_details(e);
                   try_context.restore(); // restoring try-context to perform catch-code
   
                   Junction* junction=catch_code->get_junction();
                   Value* method_frame=junction->method_frame;
                   Value* saved_exception_var_value=method_frame->get_element(exception_var_name, *method_frame, false);
                   VMethodFrame& frame=*junction->method_frame;
                   frame.put_element(frame, exception_var_name, &details.vhash, false);
   
                   result.processed_code=r.process(*catch_code);
                   
                   // retriving $exception.handled, restoring $exception var
                   Value* vhandled=details.vhash.hash().get(exception_handled_part_name);
                   frame.put_element(frame, exception_var_name, saved_exception_var_value, false);
   
                   bool bhandled=false;
                   if(vhandled) {
                           if(vhandled->is_string()) { // not simple $exception.handled(1/0)?
                                   if(could_be_handled_by_caller) { // and we can possibly handle it
                                           result.exception_should_be_handled=vhandled->get_string(); // considering 'recovered' and let the caller recover
                                           return result;
                                   }
                                   
                                   bhandled=false;
                           } else
                                   bhandled=vhandled->as_bool();
                   }
   
                   if(!bhandled) {
                           throw_context.restore(); // restoring throw-context [exception were not handled]
                           rethrow;
                   }
           }
   
           return result;
   }
   
 // cache--  // cache--
   
 // consts  // consts
   
 const int DATA_STRING_SERIALIZED_VERSION=0x0004;  const int DATA_STRING_SERIALIZED_VERSION=0x0006;
   
 // helper types  // helper types
   
Line 413  struct Data_string_serialized_prolog { Line 593  struct Data_string_serialized_prolog {
 #endif  #endif
   
 void cache_delete(const String& file_spec) {  void cache_delete(const String& file_spec) {
         file_delete(file_spec, false/*fail_on_read_problem*/);          file_delete(file_spec, false/*fail_on_problem*/);
 }  }
   
 #ifndef DOXYGEN  #ifndef DOXYGEN
 class Cache_data: public PA_Object {  struct Cache_scope {
 public:  public:
         time_t expires;          time_t expires;
           const String* body_from_disk;
 };  };
 DECLARE_OBJECT_PTR(Cache_data);  
 struct Locked_process_and_cache_put_action_info {  struct Locked_process_and_cache_put_action_info {
         Request *r;          Request *r;
         Cache_data *data;          Cache_scope *scope;
         ValuePtr body_code; const String& evaluated_body;          Value* body_code;
           Value* catch_code; 
           const String* processed_code;
 };  };
 #endif  #endif
   
   
   
   static StringOrValue process_cache_body_code(Request& r, Value* body_code) {
           return StringOrValue(r.process_to_string(*body_code));
   }
   
 /* @todo maybe network order worth spending some effort?  /* @todo maybe network order worth spending some effort?
         don't bothering myself with network byte order,          don't bothering myself with network byte order,
         am not planning to be able to move resulting file across platforms          am not planning to be able to move resulting file across platforms
Line 436  static void locked_process_and_cache_put Line 625  static void locked_process_and_cache_put
         Locked_process_and_cache_put_action_info& info=          Locked_process_and_cache_put_action_info& info=
                 *static_cast<Locked_process_and_cache_put_action_info *>(context);                  *static_cast<Locked_process_and_cache_put_action_info *>(context);
   
         =info.r->pool();          const String* body_from_disk=info.scope->body_from_disk;
           
         // body->process           // body->process 
         info.evaluated_body=info.r->process_to_string(info.body_code);          Try_catch_result result=try_catch(*info.r, 
                   process_cache_body_code, info.body_code,
                   info.catch_code, body_from_disk!=0 /*we have something old=we can handle=recover later*/);
   
           if(result.exception_should_be_handled) {
                   if(*result.exception_should_be_handled==CACHE_EXCEPTION_HANDLED_CACHE_NAME) {
                           assert(body_from_disk);
                           info.processed_code=body_from_disk;
                   } else
                           throw Exception(PARSER_RUNTIME,
                                   result.exception_should_be_handled,
                                   "$"EXCEPTION_VAR_NAME"."EXCEPTION_HANDLED_PART_NAME" value must be "
                                   "either boolean or string '"CACHE_EXCEPTION_HANDLED_CACHE_NAME"'");
           } else
                   info.processed_code=&result.processed_code.as_string();
   
         // expiration time not spoiled by ^cache(0) or something?          // expiration time not spoiled by ^cache(0) or something?
         if(info.data->expires > time(0)) {          if(info.scope->expires > time(0)) {
                 // string -serialize> buffer                  // string -serialize> buffer
                 char *data; size_t data_size;                  String::Cm serialized=info.processed_code->serialize(
                 info.evaluated_body->serialize(pool,                           sizeof(Data_string_serialized_prolog));
                         sizeof(Data_string_serialized_prolog),   
                         data, data_size);  
                 Data_string_serialized_prolog& prolog=                  Data_string_serialized_prolog& prolog=
                         *reinterpret_cast<Data_string_serialized_prolog *>(data);                          *reinterpret_cast<Data_string_serialized_prolog *>(serialized.str);
                 prolog.version=DATA_STRING_SERIALIZED_VERSION;                  prolog.version=DATA_STRING_SERIALIZED_VERSION;
                 prolog.expires=info.data->expires;                  prolog.expires=info.scope->expires;
                                   
                 // buffer -write> file                  // buffer -write> file
                 write(f, data, data_size);                  write(f, serialized.str, serialized.length);
         } else // expired!          } else // expired!
                 info.data->expires=0; // flag it so that could be easily checked by caller                  info.scope->expires=0; // flag it so that could be easily checked by caller
 }  }
 const String& locked_process_and_cache_put(Request& r,   const String* locked_process_and_cache_put(Request& r, 
                                                                         ValuePtr body_code,                                             Value& body_code,
                                                                         Cache_data& data,                                             Value* catch_code,
                                                                         const String& file_spec) {                                             Cache_scope& scope,
         Locked_process_and_cache_put_action_info info;                                             const String& file_spec) 
         info.r=&r;  {
         info.data=&data;          Locked_process_and_cache_put_action_info info={&r, &scope, &body_code, catch_code, 0};
         info.body_code=body_code;  
   
         const String& result=file_write_action_under_lock(          const String* result=file_write_action_under_lock(
                 file_spec,                   file_spec, 
                 "cache_put", locked_process_and_cache_put_action, &info,                  "cache_put",
                   locked_process_and_cache_put_action,
                   &info,
                 false/*as_text*/,                  false/*as_text*/,
                 false/*do_append*/,                  false/*do_append*/,
                 false/*block*/,                  false/*block == don't wait till other thread release lock*/,
                 false/*fail on lock problem*/) ? info.evaluated_body: 0;                  false/*dun throw exception if lock failed*/) ? info.processed_code: 0;
   
         time_t now=time(0);          time_t now=time(0);
         if(data.expires<=now)          if(scope.expires<=now)
                 cache_delete(file_spec);                  cache_delete(file_spec);
         return result;          return result;
 }  }
 const String& cache_getCharset& charset, const String& file_spec, time_t now) {  #ifndef DOXYGEN
         File_read_result file=file_read(pool, charset, file_spec,   struct Cache_get_result {
           const String* body;
           bool expired;
   };
   #endif
   static Cache_get_result cache_get(Request_charsets& charsets, const String& file_spec, time_t now) {
           Cache_get_result result={0, false};
   
           File_read_result file=file_read(charsets, file_spec, 
                            false/*as_text*/,                              false/*as_text*/, 
                            0, //no params                             0, //no params
                            false/*fail_on_read_problem*/);                             false/*fail_on_read_problem*/);
         if(file.success && file.size/* ignore reads which are empty due to           if(file.success && file.length/* ignore reads which are empty due to 
                         non-unary open+lockEX conflict with lockSH */) {                          non-unary open+lockEX conflict with lockSH */) {
                                                   
                 Data_string_serialized_prolog& prolog=                  Data_string_serialized_prolog& prolog=
                         *reinterpret_cast<Data_string_serialized_prolog *>(file.data);                          *reinterpret_cast<Data_string_serialized_prolog *>(file.str);
   
                 const String& result(new String);                  String* body=new String;
                 if(                  if(
                         file.size>=sizeof(Data_string_serialized_prolog)                          file.length>=sizeof(Data_string_serialized_prolog)
                         && prolog.version==DATA_STRING_SERIALIZED_VERSION                          && prolog.version==DATA_STRING_SERIALIZED_VERSION) {
                         && prolog.expires > now                          if(body->deserialize(sizeof(Data_string_serialized_prolog),  file.str, file.length)) {
                         && result->deserialize(                                  result.body=body;
                                 sizeof(Data_string_serialized_prolog),  file.data, file.size, file_spec->cstr()))                                  result.expired=prolog.expires <= now;
                         return result;                          }
                   }
         }          }
   
         return 0;          return result;
 }  }
 static time_t as_expires(Request& r, const String& method_name, MethodParams* params,   static time_t as_expires(Request& r, MethodParams& params, 
                                                 int index, time_t now) {                                                  int index, time_t now) {
         time_t result;          time_t result;
         if(Value* vdate=(*params)[index]->as(VDATE_TYPE, false))          if(Value* vdate=params[index].as(VDATE_TYPE, false))
                 result=static_cast<VDate*>(vdate)->get_time();                  result=static_cast<VDate*>(vdate)->get_time();
         else          else
                 result=now+(time_t)params->as_double(index, "lifespan must be date or number", r);                  result=now+(time_t)params.as_double(index, "lifespan must be date or number", r);
                   
         return result;          return result;
 }  }
 static const String& as_file_spec(Request& r, MethodParams* params, int index) {  static const String& as_file_spec(Request& r, MethodParams& params, int index) {
         return r.absolute(params->as_string(index, "filespec must be string"));          return r.absolute(params.as_string(index, "filespec must be string"));
 }  }
 static void _cache(Request& r, const String& method_name, MethodParams* params) {  static void _cache(Request& r, MethodParams& params) {
           if(params.count()==0) {
                   // ^cache[] -- return current expiration time
                   Cache_scope* scope=static_cast<Cache_scope*>(r.classes_conf.get(cache_data_name));
                   if(!scope)
                           throw Exception(PARSER_RUNTIME,
                                   0,
                                   "expire-time get without cache");
                   r.write_no_lang(*new VDate(scope->expires));
                   return;
           }
   
         time_t now=time(0);          time_t now=time(0);
   
         // ^cache[filename] ^cache(seconds) ^cache[expires date]          if(params.count()==1) {
         if(params->count()==1) {                  // ^cache[filename] ^cache(seconds) ^cache[expires date]
                 if((*params)[0]->is_string()) { // filename?                  if(params[0].is_string()) { // filename?
                         cache_delete(as_file_spec(r, params, 0));                          cache_delete(as_file_spec(r, params, 0));
                         return;                          return;
                 }                  }
   
                 // secods|expires date                  // secods|expires date
                 Cache_data* data=static_cast<Cache_data*>(r.classes_conf.get(cache_data_name).get());                  Cache_scope* scope=static_cast<Cache_scope*>(r.classes_conf.get(cache_data_name));
                 if(!data)                  if(!scope)
                         throw Exception("parser.runtime",                          throw Exception(PARSER_RUNTIME,
                                 method_name,                                  0,
                                 "expire-time reducing instruction without cache");                                  "expire-time reducing instruction without cache");
                                   
                 time_t expires=as_expires(r, method_name, params, 0, now);                  time_t expires=as_expires(r, params, 0, now);
                 if(expires < data->expires)                  if(expires < scope->expires)
                         data->expires=expires;                          scope->expires=expires;
   
                 return;                  return;
         }          } else if(params.count()<3)
                   throw Exception(PARSER_RUNTIME,
                           0,
                           "invalid number of parameters"); 
                   
         // file_spec, expires, body code          // file_spec, expires, body code
         const String& file_spec=r.absolute(params->as_string(0, "filespec must be string"));          const String& file_spec=as_file_spec(r, params, 0);
   
         Cache_dataPtr data(new Cache_data);          Cache_scope scope={as_expires(r, params, 1, now), 0};
         Temp_hash_value<String* , PA_Object* >   
                 cache_data_setter(r.classes_conf, cache_data_name, data);  
         data->expires=as_expires(r, method_name, params, 1, now);  
         ValuePtr body_code=params->as_junction(2, "body must be code");  
   
         if(data->expires>now) { // valid 'expires' specified? try cached copy...  
                 // hence we don't hope to have unary create/lockEX  
                 // we need some plan to live in a life like that, so...   
                 // worst races plan:  
                 // A        B  
                 // open  
                 //          |open  
                 // lockSH  
                 //          |nonblocking-lockEX fails  
                 // unlockSH  
                 // close, cache_get returns 0  
                 // open  
                 // nonblocking-lockEX succeeds; process, write, close  
                 //          |retry1: open  
                 // ...  
                 //          |lockSH succeeds; ...  
   
                 for(int retry=0; retry<2; retry++) {  
                         if(const String& cached_body=cache_get(pool, r.charsets.source(), file_spec, now)) { // have cached copy?  
                                 // write it out   
                                 r.write_assign_lang(*cached_body);  
                                 // happy with it  
                                 return;  
                         }  
   
                         // non-blocked lock; process; cache it          Temp_hash_value<const String::Body, void*> 
                         if(const String& processed_body=                  cache_scope_setter(r.classes_conf, cache_data_name, &scope);
                                 locked_process_and_cache_put(r, body_code, *data, file_spec)) {          Value& body_code=params.as_junction(2, "body_code must be code");
                                 // write it out           Value* catch_code=0;
                                 r.write_assign_lang(*processed_body);          if(params.count()>3)
                                 // happy with it                  catch_code=&params.as_junction(3, "catch_code must be code");
                                 return;  
                         } else { // somebody writing result right now          if(scope.expires>now) {
                                 pa_sleep(0, 500000); // waiting half a second                  Cache_get_result cached=cache_get(r.charsets, file_spec, now);
                                 retry=0; // prolonging our wait, than could cache_get it, without processing body_code  
                         }                  if(cached.body) { // have cached copy
                           if(cached.expired) {
                                   scope.body_from_disk=cached.body; // storing for user to retrive it with ^cache[]
                           } else {
                                   // and it's not expired yet write it out 
                                   r.write_assign_lang(*cached.body);
                                   // happy with it
                                   return;
                           }
                 }                  }
                 throw Exception(0,  
                         file_spec,  
                         "locking problem");  
         } else {   
                 // instructed not to cache; forget cached copy  
                 cache_delete(file_spec);  
                 // process  
                 const String& processed_body=r.process_to_string(body_code);  
                 // write it out   
                 r.write_assign_lang(*processed_body);  
                 // happy with it  
                 return;  
         }  
         // never reached  
 }  
   
 // also used in pa_request.C to pass param to @unhandled_exception  
 VHashPtr exception2vhashconst Exception& e) {  
         VHashPtr result(new VHash);  
         HashStringValue& hash=result->hash(Exception::undefined_source);  
         if(const char* type=e.type(true))  
                 hash.put(exception_type_part_name, ValuePtr(new VString(String* (new String(type)))));  
         if(const String& asource=e.problem_source()) {  
                 const String* source(new String);   
                 source->append(*asource, String::UL_TAINTED, true/*forced*/);  
   
                 hash.put(exception_source_part_name, ValuePtr(new VString(source)));  
 #ifndef NO_STRING_ORIGIN  
                 const String_fragment::Origin& origin=source->origin();  
                 hash.put(String* (new String("file", 0, true)),  
                         ValuePtr(new VString(String* (new String(origin.file)))));  
                 hash.put(String* (new String("lineno")),  
                         ValuePtr(new VInt(1+origin.line)));  
 #endif  
         }  
         if(const char* ecomment=e.comment(true)) {  
                 char *pcomment=pool.copy(ecomment);  
                 hash.put(exception_comment_part_name,   
                         ValuePtr(new VString(String* (new String(pcomment, 0, true/*tainted*/)))));  
         }  
         hash.put(exception_handled_part_name,   
                 ValuePtr(new VBool(false)));  
   
         return result;  
 }  
   
 static void _try_operator(Request& r, const String& method_name, MethodParams* params) {  
   
         ValuePtr body_code=params->as_junction(0, "body_code must be code");  
         ValuePtr catch_code=params->as_junction(1, "catch_code must be code");  
   
         StringOrValue result;  
         // taking snapshot of try-context  
         Request_context_saver try_context(r);  
         try {  
                 result=r.process(body_code);  
         } catch(const Exception& e) {  
                 Request_context_saver throw_context(r); // taking snapshot of throw-context [stack trace contains error]  
                 try_context.restore(); // restoring try-context to perform catch-code  
   
                 VHashPtr vhash=exception2vhash(pool, e);  
   
                 JunctionPtr junction=catch_code->get_junction();                  // no cached info or it's already expired
                 Value* method_frame=junction->method_frame;  
                 ValuePtr saved_exception_var_value=method_frame->get_element(exception_var_name, *method_frame, false);  
                 junction->method_frame->put_element(exception_var_name, vhash, false);  
                 result=r.process(catch_code);  
                 bool handled=false;  
                 if(ValuePtr value=  
                         vhash->hash(Exception::undefined_source).get(exception_handled_part_name))  
                         handled=value->as_bool();                 
                 junction->method_frame->put_element(exception_var_name, saved_exception_var_value, false);  
   
                 if(!handled) {                  // trying to process it under lock and store result in file
                         throw_context.restore(); // restoring throw-context [exception were not handled]                  const String* processed_body=locked_process_and_cache_put(r, body_code, catch_code, scope, file_spec);
                         throw(e); // rethrow                  if(processed_body){
                           // write it out 
                           r.write_assign_lang(*processed_body);
                           // happy with it
                           return;
                   } else {
                           // we fail while get exclusive lock. nvm, we just execute body_code a bit later
                 }                  }
           } else { 
                   // instructed not to cache; forget cached copy
                   cache_delete(file_spec);
         }          }
         // write out result          
         r.write_pass_lang(result);          // process without cacheing
 }          const String& processed_body=r.process_to_string(body_code);
           // write it out 
 static void _throw_operator(Request& r, const String& method_name, MethodParams* params) {          r.write_assign_lang(processed_body);
   }
         if(params->count()==1) {  
                 ValuePtr param0=(*params)[0];  static StringOrValue process_try_body_code(Request& r, Value* body_code) {
                 if(HashStringValue *hash=param0->get_hash(method_name)) {          return r.process(*body_code);
   }
   static void _try_operator(Request& r, MethodParams& params) {
           Value& body_code=params.as_junction(0, "body_code must be code");
           Value& catch_code=params.as_junction(1, "catch_code must be code");
           Value* finally_code=(params.count()==3) ? &params.as_junction(2, "finally_code must be code") : 0;
   
           Try_catch_result result;
           StringOrValue finally_result;
           try{
                   result=try_catch(r, 
                           process_try_body_code, &body_code,
                           &catch_code);
                   if(result.exception_should_be_handled)
                           throw Exception(PARSER_RUNTIME,
                                   result.exception_should_be_handled,
                                   "catch block must set $exception.handled to some boolean value, not string");
           } catch(...){
                   if(finally_code)
                           finally_result=r.process(*finally_code);
                   rethrow;
           }
   
           if(finally_code)
                   finally_result=r.process(*finally_code);
   
           // write out processed body_code or catch_code
           r.write_pass_lang(result.processed_code);
   
           // write out processed finally code
           if(finally_code)
                   r.write_pass_lang(finally_result);
   }
   
   static void _throw_operator(Request&, MethodParams& params) {
           if(params.count()==1 && !params[0].is_string()) {
                   if(HashStringValue *hash=params[0].get_hash()) {
                         const char* type=0;                          const char* type=0;
                         if(ValuePtr value=hash->get(exception_type_part_name))                          if(Value* value=hash->get(exception_type_part_name))
                                 type=value->as_string(&pool)->cstr();                                  type=value->as_string().cstr();
                         const String* source;                          const String* source=0;
                         if(ValuePtr value=hash->get(exception_source_part_name))                          if(Value* value=hash->get(exception_source_part_name))
                                 source=value->as_string(&pool);                                  source=&value->as_string();
                         const char* comment;                          const char* comment=0;
                         if(ValuePtr value=hash->get(exception_comment_part_name))                          if(Value* value=hash->get(exception_comment_part_name))
                                 comment=value->as_string(&pool)->cstr();                                  comment=value->as_string().cstr();
   
                         throw Exception(type,                          throw Exception(type,
                                 source?source:method_name,                                  source?source:0,
                                 "%s", comment?comment.get():"");                                  "%s", comment?comment:"");
                 } else                  } else
                         throw Exception("parser.runtime",                          throw Exception(PARSER_RUNTIME,
                                 method_name,                                  0,
                                 "one-param version has hash param");                                  "one-param version has hash or string param");
         } else {          } else {
                 const char* type=params->as_string(0, "type must be string")->cstr();                  const char* type=params.as_string(0, "type must be string").cstr();
                 const String* source=params->as_string(1, "source must be string");                  const String* source=params.count()>1? &params.as_string(1, "source must be string"):0;
                 const char* comment=params->count()>2? params->as_string(2, "comment must be string")->cstr()                  const char* comment=params.count()>2? params.as_string(2, "comment must be string").cstr():0;
                         :CharPtr(0);                  throw Exception(type, source, "%s", comment?comment:"");
                 throw Exception(type, source, "%s", comment?comment.get():"");          }
         }   }
 }  
   static void _sleep_operator(Request& r, MethodParams& params) {
           double seconds=params.as_double(0, "seconds must be double", r);
           pa_sleep((int)trunc(seconds), (int)trunc(seconds*1000));
    }
   
 #if defined(WIN32) && defined(_DEBUG)  #if defined(WIN32) && defined(_DEBUG)
 #       define PA_BPT  #       define PA_BPT
 static void _bpt(Request& r, const String& method_name, MethodParams* params) {  static void _bpt(Request&, MethodParams&) {
         _asm int 3;          _asm int 3;
 }  }
 #endif  #endif
Line 708  static void _bpt(Request& r, const Strin Line 891  static void _bpt(Request& r, const Strin
 // constructor  // constructor
   
 VClassMAIN::VClassMAIN(): VClass() {  VClassMAIN::VClassMAIN(): VClass() {
         set_name(String* (new String(MAIN_CLASS_NAME)));          set_name(*new String(MAIN_CLASS_NAME));
   
 #ifdef PA_BPT  #ifdef PA_BPT
         // ^bpt[]          // ^bpt[]
Line 717  VClassMAIN::VClassMAIN(): VClass() { Line 900  VClassMAIN::VClassMAIN(): VClass() {
   
         // ^if(condition){code-when-true}          // ^if(condition){code-when-true}
         // ^if(condition){code-when-true}{code-when-false}          // ^if(condition){code-when-true}{code-when-false}
         add_native_method("if", Method::CT_ANY, _if, 2, 3);          add_native_method("if", Method::CT_ANY, _if, 2, 3, Method::CO_WITHOUT_FRAME);
   
         // ^untaint[as-is|uri|sql|js|html|html-typo]{code}          // ^untaint[as-is|uri|sql|js|html|html-typo|regex]{code}
         add_native_method("untaint", Method::CT_ANY, _untaint, 1, 2);          add_native_method("untaint", Method::CT_ANY, _untaint, 1, 2);
   
         // ^taint[as-is|uri|sql|js|html|html-typo]{code}          // ^taint[as-is|uri|sql|js|html|html-typo|regex]{code}
         add_native_method("taint", Method::CT_ANY, _taint, 1, 2);          add_native_method("taint", Method::CT_ANY, _taint, 1, 2);
   
         // ^process[code]          // ^process[code]
         add_native_method("process", Method::CT_ANY, _process, 1, 2);          add_native_method("process", Method::CT_ANY, _process, 1, 3);
   
         // ^rem{code}          // ^rem{code}
         add_native_method("rem", Method::CT_ANY, _rem, 1, 10000);          add_native_method("rem", Method::CT_ANY, _rem, 1, 10000, Method::CO_WITHOUT_FRAME);
   
         // ^while(condition){code}          // ^while(condition){code}
         add_native_method("while", Method::CT_ANY, _while, 2, 2);          add_native_method("while", Method::CT_ANY, _while, 2, 3, Method::CO_WITHOUT_FRAME);
   
         // ^use[file]          // ^use[file]
         add_native_method("use", Method::CT_ANY, _use, 1, 1);          add_native_method("use", Method::CT_ANY, _use, 1, 1);
   
           // ^break[]
           add_native_method("break", Method::CT_ANY, _break, 0, 0, Method::CO_WITHOUT_FRAME);
   
           // ^continue[]
           add_native_method("continue", Method::CT_ANY, _continue, 0, 0, Method::CO_WITHOUT_FRAME);
   
         // ^for[i](from-number;to-number-inclusive){code}[delim]          // ^for[i](from-number;to-number-inclusive){code}[delim]
         add_native_method("for", Method::CT_ANY, _for, 3+1, 3+1+1);          add_native_method("for", Method::CT_ANY, _for, 3+1, 3+1+1, Method::CO_WITHOUT_WCONTEXT);
   
         // ^eval(expr)          // ^eval(expr)
         // ^eval(expr)[format]          // ^eval(expr)[format]
         add_native_method("eval", Method::CT_ANY, _eval, 1, 2);          add_native_method("eval", Method::CT_ANY, _eval, 1, 2, Method::CO_WITHOUT_FRAME);
   
         // ^connect[protocol://user:pass@host[:port]/database]{code with ^sql-s}          // ^connect[protocol://user:pass@host[:port]/database]{code with ^sql-s}
         add_native_method("connect", Method::CT_ANY, _connect, 2, 2);          add_native_method("connect", Method::CT_ANY, _connect, 2, 2);
   
   
           // ^cache[file_spec](time){code}[{catch code}] time=0 no cache
         // ^cache[file_spec] delete cache          // ^cache[file_spec] delete cache
         // ^cache[file_spec](time){code} time=0 no cache          // ^cache[] get current expiration time
         add_native_method("cache", Method::CT_ANY, _cache, 1, 3);          add_native_method("cache", Method::CT_ANY, _cache, 0, 4);
                   
         // switch          // switch
   
         // ^switch[value]{cases}          // ^switch[value]{cases}
         add_native_method("switch", Method::CT_ANY, _switch, 2, 2);          add_native_method("switch", Method::CT_ANY, _switch, 2, 2, Method::CO_WITHOUT_FRAME);
   
         // ^case[value]{code}          // ^case[value]{code}
         add_native_method("case", Method::CT_ANY, _case, 2, 10000);          add_native_method("case", Method::CT_ANY, _case, 2, 10000, Method::CO_WITHOUT_FRAME);
   
         // try-catch          // try-catch
   
         // ^try{code}{catch code}          // ^try{code}{catch code}
         add_native_method("try", Method::CT_ANY, _try_operator, 2, 2);          add_native_method("try", Method::CT_ANY, _try_operator, 2, 3, Method::CO_WITHOUT_FRAME);
         // ^throw[$exception hash]          // ^throw[$exception hash]
         // ^throw[type;source;comment]          // ^throw[type;source;comment]
         add_native_method("throw", Method::CT_ANY, _throw_operator, 1, 3);          add_native_method("throw", Method::CT_ANY, _throw_operator, 1, 3);
   
           add_native_method("sleep", Method::CT_ANY, _sleep_operator, 1, 1);
 }  }
   
 // constructor & configurator  // constructor & configurator
   
 VStateless_classPtr VClassMAIN_create() {  VStateless_class& VClassMAIN_create() {
                   return *new VClassMAIN;
         return VStateless_classPtr(new VClassMAIN);  
 }  }

Removed from v.1.127.2.22.2.3  
changed lines
  Added in v.1.189


E-mail: