--- parser3/src/classes/file.C 2024/11/06 21:56:23 1.286 +++ parser3/src/classes/file.C 2025/10/05 19:41:27 1.298 @@ -27,7 +27,7 @@ #include "pa_vregex.h" #include "pa_version.h" -volatile const char * IDENT_FILE_C="$Id: file.C,v 1.286 2024/11/06 21:56:23 moko Exp $"; +volatile const char * IDENT_FILE_C="$Id: file.C,v 1.298 2025/10/05 19:41:27 moko Exp $"; // defines @@ -35,6 +35,7 @@ volatile const char * IDENT_FILE_C="$Id: #define CHARSET_EXEC_PARAM_NAME "charset" #define NAME_NAME "name" +#define MODE_APPEND "append" #define KEEP_EMPTY_DIRS_NAME "keep-empty-dirs" #define SUPPRESS_EXCEPTION_NAME "exception" @@ -57,7 +58,12 @@ public: } }; -Table file_list_table_template(new File_list_table_template_columns); +static Table &file_list_table_template(){ + static Table *singleton=NULL; + if(!singleton) + singleton=new Table(new File_list_table_template_columns); + return *singleton; +} // class @@ -128,8 +134,9 @@ static const String::Body cdate_name("cd // methods static void _save(Request& r, MethodParams& params) { - bool is_text=VFile::is_text_mode(params.as_string(0, MODE_MUST_NOT_BE_CODE)); - Value& vfile_name=params.as_no_junction(1, FILE_NAME_MUST_NOT_BE_CODE); + bool do_append=false; + bool is_text=VFile::is_text_mode(params.as_string(0, MODE_MUST_BE_STRING)); + const String& file_name=params.as_file_name(1); Charset* asked_charset=0; if(params.count()>2) @@ -139,16 +146,20 @@ static void _save(Request& r, MethodPara asked_charset=&pa_charsets.get(vcharset_name->as_string()); valid_options++; } + if(Value* vappend=options->get(MODE_APPEND)){ + do_append=vappend->as_bool(); + valid_options++; + } if(valid_options != options->count()) throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION); } // save - GET_SELF(r, VFile).save(r.charsets, r.full_disk_path(vfile_name.as_string()), is_text, asked_charset); + GET_SELF(r, VFile).save(r.charsets, r.full_disk_path(file_name), is_text, do_append, asked_charset); } static void _delete(Request& r, MethodParams& params) { - const String& file_name=params.as_string(0, FILE_NAME_MUST_NOT_BE_CODE); + const String& file_name=params.as_file_name(0); bool keep_empty_dirs=false; bool fail_on_problem=true; @@ -203,9 +214,7 @@ static void copy_process_source(struct s nCount = file_block_read(from_file, buffer, sizeof(buffer)); int written=write(to_file, buffer, nCount); if( written < 0 ) - throw Exception("file.access", - 0, - "write failed: %s (%d)", strerror(errno), errno); + throw Exception("file.write", 0, "write failed: %s (%d)", strerror(errno), errno); } while(nCount > 0); } @@ -223,7 +232,7 @@ static void _copy(Request& r, MethodPara if(params.count()>2) if(HashStringValue* options=params.as_hash(2)){ int valid_options=0; - if(Value* vappend=options->get("append")){ + if(Value* vappend=options->get(MODE_APPEND)){ append=r.process(*vappend).as_bool(); valid_options++; } @@ -251,8 +260,8 @@ static void _load_pass_param( } static void _load(Request& r, MethodParams& params) { - bool as_text=VFile::is_text_mode(params.as_string(0, MODE_MUST_NOT_BE_CODE)); - const String& lfile_name=r.full_disk_path(params.as_string(1, FILE_NAME_MUST_NOT_BE_CODE)); + bool as_text=VFile::is_text_mode(params.as_string(0, MODE_MUST_BE_STRING)); + const String& lfile_name=r.full_disk_path(params.as_file_name(1)); size_t param_index=params.count()-1; Value* param_value=param_index>1?¶ms.as_no_junction(param_index, "file name or options must not be code"):0; @@ -311,9 +320,9 @@ static void _create(Request& r, MethodPa if(params.count()>=3){ // old format: ^file::create[text|binary;file-name;string-or-file-content[;options]] - mode=¶ms.as_string(0, MODE_MUST_NOT_BE_CODE); + mode=¶ms.as_string(0, MODE_MUST_BE_STRING); is_text=VFile::is_text_mode(*mode); - file_name=¶ms.as_string(1, FILE_NAME_MUST_NOT_BE_CODE); + file_name=¶ms.as_string(1, FILE_NAME_MUST_BE_STRING); content_index=2; options_index=3; extended_options=false; @@ -385,7 +394,7 @@ static void _create(Request& r, MethodPa } static void _stat(Request& r, MethodParams& params) { - const String& lfile_name=params.as_string(0, FILE_NAME_MUST_NOT_BE_CODE); + const String& lfile_name=params.as_file_name(0); uint64_t size; time_t atime, mtime, ctime; @@ -473,7 +482,7 @@ static void append_to_argv(Request& r, A static void _exec_cgi(Request& r, MethodParams& params, bool cgi) { bool is_text=true; size_t param_index=0; - const String& mode=params.as_string(0, FIRST_ARG_MUST_NOT_BE_CODE); + const String& mode=params.as_string(0, "mode must be string"); if(VFile::is_valid_mode(mode)) { is_text=VFile::is_text_mode(mode); param_index++; @@ -482,13 +491,13 @@ static void _exec_cgi(Request& r, Method if(param_index>=params.count()) throw Exception(PARSER_RUNTIME, 0, FILE_NAME_MUST_BE_SPECIFIED); - const String& script_name=r.full_disk_path(params.as_string(param_index++, FILE_NAME_MUST_NOT_BE_CODE)); + const String& script_name=r.full_disk_path(params.as_file_name(param_index++)); HashStringString env; - #define ECSTR(name, value_cstr) if(value_cstr) env.put(#name, value_cstr); + #define ECSTR(name, value_cstr) if(value_cstr) env.put(#name, String::Body(value_cstr)); // passing environment for(SAPI::Env::Iterator i(r.sapi_info); i; i.next() ) - env.put(i.key(), i.value() ); + env.put(i.key(), String::Body(i.value()) ); // const ECSTR(GATEWAY_INTERFACE, "CGI/1.1"); @@ -737,7 +746,7 @@ static void _list(Request& r, MethodPara const char* absolute_path_cstr=r.full_disk_path(relative_path.as_string()).taint_cstr(String::L_FILE_SPEC); Table::Action_options table_options; - Table& table=*new Table(file_list_table_template, table_options); + Table& table=*new Table(file_list_table_template(), table_options); const int ovector_size=(1/*match*/)*3; int ovector[ovector_size]; @@ -778,7 +787,7 @@ static void lock_execute_body(int , void } static void _lock(Request& r, MethodParams& params) { - const String& file_spec=r.full_disk_path(params.as_string(0, FILE_NAME_MUST_BE_STRING)); + const String& file_spec=r.full_disk_path(params.as_file_name(0)); Lock_execute_body_info info={ &r, ¶ms.as_junction(1, "body must be code") @@ -802,7 +811,7 @@ static size_t afterlastslash(const Strin } static void _find(Request& r, MethodParams& params) { - const String& file_name=params.as_string(0, FILE_NAME_MUST_NOT_BE_CODE); + const String& file_name=params.as_string(0, FILE_NAME_MUST_BE_STRING); Value* not_found_code=(params.count()==2)?¶ms.as_junction(1, "not-found param must be code"):0; @@ -844,7 +853,7 @@ static void _find(Request& r, MethodPara } static void _dirname(Request& r, MethodParams& params) { - const String& file_spec=params.as_string(0, FILE_NAME_MUST_BE_STRING); + const String& file_spec=params.as_file_spec(0); // works as *nix dirname // empty > . @@ -878,7 +887,7 @@ static void _dirname(Request& r, MethodP } static void _basename(Request& r, MethodParams& params) { - const String& file_spec=params.as_string(0, FILE_NAME_MUST_BE_STRING); + const String& file_spec=params.as_file_spec(0); // works as *nix basename // empty > . @@ -904,7 +913,7 @@ static void _basename(Request& r, Method } static void _justname(Request& r, MethodParams& params) { - const String& file_spec=params.as_string(0, FILE_NAME_MUST_BE_STRING); + const String& file_spec=params.as_file_spec(0); // /a/some.tar.gz > some.tar // /a/b.c/ > empty // /a/b.c > b @@ -914,7 +923,7 @@ static void _justname(Request& r, Method } static void _justext(Request& r, MethodParams& params) { - const String& file_spec=params.as_string(0, FILE_NAME_MUST_BE_STRING); + const String& file_spec=params.as_file_spec(0); // /a/some.tar.gz > gz // /a/b.c/ > empty size_t pos=afterlastslash(file_spec); @@ -924,7 +933,7 @@ static void _justext(Request& r, MethodP } static void _fullpath(Request& r, MethodParams& params) { - const String& file_spec=params.as_string(0, FILE_NAME_MUST_BE_STRING); + const String& file_spec=params.as_file_spec(0); const String* result; if(file_spec.first_char()=='/') result=&file_spec; @@ -1061,28 +1070,21 @@ extern Base64Options base64_encode_optio Base64Options base64_decode_options(Request& r, HashStringValue* options, VString** vcontent_type) { 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 == CONTENT_TYPE_NAME) { *vcontent_type=new VString(value->as_string()); - valid_options++; } else if(key == "url-safe") { if(r.process(*value).as_bool()) result.set_url_safe_abc(); - valid_options++; - } + } else + throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION); } - - if(valid_options != options->count()) - throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION); } return result; } @@ -1106,7 +1108,7 @@ static void _base64(Request& r, MethodPa if(params.count() < 3) throw Exception(PARSER_RUNTIME, 0, "constructor cannot have less than 3 parameters (has %d parameters)", params.count()); // actually it accepts 1 parameter (backward) - is_text=VFile::is_text_mode(params.as_string(0, MODE_MUST_NOT_BE_CODE)); + is_text=VFile::is_text_mode(params.as_string(0, MODE_MUST_BE_STRING)); user_file_name=¶ms.as_string(1, FILE_NAME_MUST_BE_STRING); if(params.count() == 4) @@ -1132,7 +1134,7 @@ static void _base64(Request& r, MethodPa if(params.count() > 2) throw Exception(PARSER_RUNTIME, 0, "accepts maximum 2 parameter(s) (has %d parameters)", params.count()); - const String& file_spec = params.as_string(0, FILE_NAME_MUST_BE_STRING); + const String& file_spec = params.as_file_name(0); File_read_result data = file_read_binary(r.full_disk_path(file_spec), true /*fail on problem*/); Base64Options options = base64_encode_options(r, params.count() > 1 ? params.as_hash(1) : NULL); @@ -1146,7 +1148,7 @@ static void _crc32(Request& r, MethodPar if(&r.get_self() == file_class) { // ^file:crc32[file-name] if(params.count()) { - const String& file_spec=params.as_string(0, FILE_NAME_MUST_BE_STRING); + const String& file_spec=params.as_file_name(0); crc32=pa_crc32(r.full_disk_path(file_spec)); } else { throw Exception(PARSER_RUNTIME, 0, FILE_NAME_MUST_BE_SPECIFIED); @@ -1202,7 +1204,7 @@ static void _md5(Request& r, MethodParam if(&r.get_self() == file_class) { // ^file:md5[file-name] if(params.count()) { - const String& file_spec=params.as_string(0, FILE_NAME_MUST_BE_STRING); + const String& file_spec=params.as_file_name(0); md5=pa_md5(r.full_disk_path(file_spec)); } else { throw Exception(PARSER_RUNTIME, 0, FILE_NAME_MUST_BE_SPECIFIED); @@ -1224,7 +1226,7 @@ MFile::MFile(): Methoded("file") { add_native_method("create", Method::CT_DYNAMIC, _create, 1, 4); // ^file.save[mode;file-name] - // ^file.save[mode;file-name;$.charset[...]] + // ^file.save[mode;file-name;$.charset[...]$.append(false)] add_native_method("save", Method::CT_DYNAMIC, _save, 2, 3); // ^file:delete[file-name]