|
|
| version 1.252, 2019/11/22 22:37:30 | version 1.274, 2025/01/10 20:16:33 |
|---|---|
| Line 1 | Line 1 |
| /** @file | /** @file |
| Parser: parser @b operators. | Parser: parser @b operators. |
| Copyright (c) 2001-2017 Art. Lebedev Studio (http://www.artlebedev.com) | Copyright (c) 2001-2024 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" |
| Line 18 | Line 18 |
| #include "pa_vclass.h" | #include "pa_vclass.h" |
| #include "pa_charset.h" | #include "pa_charset.h" |
| #ifdef HAVE_SYSLOG | |
| #include "syslog.h" | |
| #endif | |
| volatile const char * IDENT_OP_C="$Id$"; | volatile const char * IDENT_OP_C="$Id$"; |
| // defines | // defines |
| Line 76 public: | Line 80 public: |
| ULN("json", JSON); | ULN("json", JSON); |
| #undef ULN | #undef ULN |
| } | } |
| } untaint_lang_name2enum; | |
| static Untaint_lang_name2enum &instance(){ | |
| static Untaint_lang_name2enum *singleton=NULL; | |
| if(!singleton) | |
| singleton=new Untaint_lang_name2enum; | |
| return *singleton; | |
| } | |
| }; | |
| // methods | // methods |
| Line 97 static void _if(Request& r, MethodParams | Line 109 static void _if(Request& r, MethodParams |
| } | } |
| String::Language get_untaint_lang(const String& lang_name){ | String::Language get_untaint_lang(const String& lang_name){ |
| String::Language lang=untaint_lang_name2enum.get(lang_name); | String::Language lang=Untaint_lang_name2enum::instance().get(lang_name); |
| if(!lang) | if(!lang) |
| throw Exception(PARSER_RUNTIME, &lang_name, "invalid taint language"); | throw Exception(PARSER_RUNTIME, &lang_name, "invalid taint language"); |
| return lang; | return lang; |
| Line 200 static void _process(Request& r, MethodP | Line 212 static void _process(Request& r, MethodP |
| throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION); | throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION); |
| } | } |
| uint processe_file_no=file_alias ? r.register_file(r.absolute(*file_alias)) : pseudo_file_no__process; | uint processe_file_no=file_alias ? r.register_file(r.full_disk_path(*file_alias)) : pseudo_file_no__process; |
| // process...{string} | // process...{string} |
| Value& vjunction=params.as_junction(index, "body must be code"); | Value& vjunction=params.as_junction(index, "body must be code"); |
| // evaluate source to process | // evaluate source to process |
| Line 279 static void _while(Request& r, MethodPar | Line 291 static void _while(Request& r, MethodPar |
| } | } |
| static void _use(Request& r, MethodParams& params) { | static void _use(Request& r, MethodParams& params) { |
| Value& vfile=params.as_no_junction(0, FILE_NAME_MUST_NOT_BE_CODE); | const String& file_name=params.as_file_name(0); |
| bool allow_class_replace=false; | |
| const String* use_origin=0; | const String* use_origin=0; |
| bool allow_class_replace=false; | |
| bool load_auto_p=false; | |
| if(params.count()==2) | if(params.count()==2) |
| if(HashStringValue* options=params.as_hash(1)) { | if(HashStringValue* options=params.as_hash(1)) { |
| Line 292 static void _use(Request& r, MethodParam | Line 305 static void _use(Request& r, MethodParam |
| String::Body key=i.key(); | String::Body key=i.key(); |
| Value* value=i.value(); | Value* value=i.value(); |
| if(key == "origin") { | |
| valid_options++; | |
| use_origin=&value->as_string(); | |
| } | |
| if(key == "replace") { | if(key == "replace") { |
| valid_options++; | valid_options++; |
| allow_class_replace=r.process(*value).as_bool(); | allow_class_replace=r.process(*value).as_bool(); |
| } | } |
| if(key == "origin") { | if(key == "main") { |
| valid_options++; | valid_options++; |
| use_origin=&value->as_string(); | load_auto_p=r.process(*value).as_bool(); |
| } | } |
| if(valid_options!=options->count()) | if(valid_options!=options->count()) |
| Line 313 static void _use(Request& r, MethodParam | Line 331 static void _use(Request& r, MethodParam |
| Temp_class_replace class_replace(r, allow_class_replace); | Temp_class_replace class_replace(r, allow_class_replace); |
| r.use_file(r.main_class, vfile.as_string(), use_origin); | r.use_file(file_name, use_origin, load_auto_p); |
| } | } |
| static void set_skip(Request& r, Request::Skip askip) { | static void set_skip(Request& r, Request::Skip askip) { |
| Line 333 static void _continue(Request& r, Method | Line 351 static void _continue(Request& r, Method |
| static void _return(Request& r, MethodParams& params) { | static void _return(Request& r, MethodParams& params) { |
| VMethodFrame& caller=*r.get_method_frame()->caller(); | VMethodFrame& caller=*r.get_method_frame()->caller(); |
| if(params.count()) | if(params.count()) |
| r.put_element(caller, Symbols::RESULT_SYMBOL, ¶ms[0]); | r.put_element(caller, Symbols::RESULT_SYMBOL, &r.process(params[0])); |
| r.set_skip_return(caller); | r.set_skip_return(caller); |
| } | } |
| Line 395 static void _eval(Request& r, MethodPara | Line 413 static void _eval(Request& r, MethodPara |
| if(fmt.is_empty()){ | if(fmt.is_empty()){ |
| r.write(value_result); | r.write(value_result); |
| } else { | } else { |
| r.write(String(format(value_result.as_double(), fmt.cstrm()))); | r.write(String(format_double(value_result.as_double(), fmt.cstrm()))); |
| } | } |
| } else | } else |
| r.write(value_result); | r.write(value_result); |
| Line 666 static void locked_process_and_cache_put | Line 684 static void locked_process_and_cache_put |
| prolog.expires=info.scope->expires; | prolog.expires=info.scope->expires; |
| // buffer -write> file | // buffer -write> file |
| write(f, serialized.str, serialized.length); | if(write(f, serialized.str, serialized.length)<0) |
| throw Exception("file.write", 0, "write failed: %s (%d)", strerror(errno), errno); | |
| } else // expired! | } else // expired! |
| info.scope->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 |
| } | } |
| Line 701 struct Cache_get_result { | Line 720 struct Cache_get_result { |
| }; | }; |
| #endif | #endif |
| static Cache_get_result cache_get(Request_charsets& charsets, const String& file_spec, time_t now) { | static Cache_get_result cache_get(const String& file_spec, time_t now) { |
| Cache_get_result result={0, false}; | Cache_get_result result={0, false}; |
| File_read_result file=file_read(charsets, file_spec, false /*as_text*/, 0, /*no params*/ false /*fail_on_read_problem*/); | File_read_result file=file_read_binary(file_spec, false /*fail_on_read_problem*/); |
| if(file.success && file.length /*ignore reads which are empty due to non-unary open+lockEX conflict with lockSH*/) { | if(file.success && file.length /*ignore reads which are empty due to non-unary open+lockEX conflict with lockSH*/) { |
| Data_string_serialized_prolog& prolog = *reinterpret_cast<Data_string_serialized_prolog *>(file.str); | Data_string_serialized_prolog& prolog = *reinterpret_cast<Data_string_serialized_prolog *>(file.str); |
| Line 723 static Cache_get_result cache_get(Reques | Line 742 static Cache_get_result cache_get(Reques |
| static time_t as_expires(Request& r, MethodParams& params, int index, time_t now) { | static time_t as_expires(Request& r, MethodParams& params, int index, time_t now) { |
| time_t result; | time_t result; |
| if(Value* vdate=params[index].as(VDATE_TYPE)) | if(VDate* vdate=dynamic_cast<VDate*>(¶ms[index])) |
| result=(time_t)(static_cast<VDate*>(vdate)->get_time()); | result=(time_t)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); |
| Line 732 static time_t as_expires(Request& r, Met | Line 751 static time_t as_expires(Request& r, Met |
| } | } |
| 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.full_disk_path(params.as_string(index, "filespec must be string")); |
| } | } |
| static void _cache(Request& r, MethodParams& params) { | static void _cache(Request& r, MethodParams& params) { |
| Line 783 static void _cache(Request& r, MethodPar | Line 802 static void _cache(Request& r, MethodPar |
| catch_code=¶ms.as_junction(3, "catch_code must be code"); | catch_code=¶ms.as_junction(3, "catch_code must be code"); |
| if(scope.expires>now) { | if(scope.expires>now) { |
| Cache_get_result cached=cache_get(r.charsets, file_spec, now); | Cache_get_result cached=cache_get(file_spec, now); |
| if(cached.body) { // have cached copy | if(cached.body) { // have cached copy |
| if(cached.expired) { | if(cached.expired) { |
| Line 855 static void _try_operator(Request& r, Me | Line 874 static void _try_operator(Request& r, Me |
| // no exception in try/catch, writing processed body_code or catch_code | // no exception in try/catch, writing processed body_code or catch_code |
| r.write(result.processed_code); | r.write(result.processed_code); |
| } | } |
| } | } |
| static void _throw_operator(Request&, MethodParams& params) { | static void _throw_operator(Request&, MethodParams& params) { |
| Line 871 static void _throw_operator(Request&, Me | Line 889 static void _throw_operator(Request&, Me |
| if(Value* value=hash->get(exception_comment_part_name)) | if(Value* value=hash->get(exception_comment_part_name)) |
| comment=value->as_string().cstr(); | comment=value->as_string().cstr(); |
| throw Exception(type, | throw Exception(type, source, "%s", comment); // to avoid MAX_STRING limit |
| source?source:0, | |
| "%s", comment?comment:""); | |
| } else | } else |
| throw Exception(PARSER_RUNTIME, | throw Exception(PARSER_RUNTIME, 0, "one-param version has hash or string param"); |
| 0, | |
| "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.count()>1? ¶ms.as_string(1, "source must be string"):0; | const String* source=params.count()>1 ? ¶ms.as_string(1, "source must be string") : 0; |
| const char* comment=params.count()>2? params.as_string(2, "comment must be string").cstr():0; | const char* comment=params.count()>2 ? params.as_string(2, "comment must be string").cstr() : 0; |
| throw Exception(type, source, "%s", comment?comment:""); | throw Exception(type, source, "%s", comment); // to avoid MAX_STRING limit |
| } | } |
| } | } |
| static void _sleep_operator(Request& r, MethodParams& params) { | static void _sleep_operator(Request& r, MethodParams& params) { |
| double seconds=params.as_double(0, "seconds must be double", r); | double seconds=params.as_double(0, "seconds must be double", r); |
| if(seconds>0) | if(seconds>0) |
| pa_sleep((int)trunc(seconds), (int)trunc((seconds-trunc(seconds))*1000000)); | pa_sleep((int)trunc(seconds), (int)trunc((seconds-trunc(seconds))*1000000)); |
| } | } |
| #if defined(WIN32) && defined(_DEBUG) | #ifdef HAVE_SYSLOG |
| static int log_level(const String &name){ | |
| if(name.is_empty()) return LOG_INFO; | |
| const char *sname = str_upper(name.cstr()); | |
| if(!strcmp(sname,"INFO")) return LOG_INFO; | |
| if(!strcmp(sname,"WARNING")) return LOG_WARNING; | |
| if(!strcmp(sname,"ERROR")) return LOG_ERR; | |
| if(!strcmp(sname,"DEBUG")) return LOG_DEBUG; | |
| throw Exception("syslog", &name, "invalid log level value"); | |
| } | |
| #endif | |
| static void _syslog_operator(Request& r, MethodParams& params) { | |
| const char* ident=params.as_string(0, "ident must be string").cstr(); | |
| const char* message=params.as_string(1, "message must be string").cstr(); | |
| #ifdef HAVE_SYSLOG | |
| int level=params.count()>2 ? log_level(params.as_string(2, "level must be string")) : LOG_INFO; | |
| openlog(*ident ? ident : "parser3", LOG_PID, LOG_USER); | |
| syslog(level, "%s", message); | |
| closelog(); | |
| #else | |
| SAPI::log(r.sapi_info, "syslog: %s", message); | |
| #endif | |
| } | |
| #if defined(WIN32) && defined(_DEBUG) && !defined(_WIN64) | |
| # define PA_BPT | # define PA_BPT |
| static void _bpt(Request&, MethodParams&) { | static void _bpt(Request&, MethodParams&) { |
| _asm int 3; | _asm int 3; |
| Line 980 VClassMAIN::VClassMAIN(): VClass(MAIN_CL | Line 1021 VClassMAIN::VClassMAIN(): VClass(MAIN_CL |
| 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); | add_native_method("sleep", Method::CT_ANY, _sleep_operator, 1, 1); |
| // ^syslog[ident;message[;info|warning|error|debug]] | |
| add_native_method("syslog", Method::CT_ANY, _syslog_operator, 2, 3); | |
| } | } |
| // constructor & configurator | // constructor & configurator |