|
|
| version 1.284, 2024/10/22 10:34:00 | version 1.297, 2025/08/30 01:12:31 |
|---|---|
| Line 1 | Line 1 |
| /** @file | /** @file |
| Parser: @b file parser class. | Parser: @b file parser class. |
| Copyright (c) 2001-2023 Art. Lebedev Studio (http://www.artlebedev.com) | Copyright (c) 2001-2024 Art. Lebedev Studio (http://www.artlebedev.com) |
| Authors: Konstantin Morshnev <moko@design.ru>, Alexandr Petrosian <paf@design.ru> | Authors: Konstantin Morshnev <moko@design.ru>, Alexandr Petrosian <paf@design.ru> |
| */ | */ |
| Line 35 volatile const char * IDENT_FILE_C="$Id$ | Line 35 volatile const char * IDENT_FILE_C="$Id$ |
| #define CHARSET_EXEC_PARAM_NAME "charset" | #define CHARSET_EXEC_PARAM_NAME "charset" |
| #define NAME_NAME "name" | #define NAME_NAME "name" |
| #define MODE_APPEND "append" | |
| #define KEEP_EMPTY_DIRS_NAME "keep-empty-dirs" | #define KEEP_EMPTY_DIRS_NAME "keep-empty-dirs" |
| #define SUPPRESS_EXCEPTION_NAME "exception" | #define SUPPRESS_EXCEPTION_NAME "exception" |
| Line 57 public: | Line 58 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 | // class |
| Line 128 static const String::Body cdate_name("cd | Line 134 static const String::Body cdate_name("cd |
| // methods | // methods |
| static void _save(Request& r, MethodParams& params) { | static void _save(Request& r, MethodParams& params) { |
| bool is_text=VFile::is_text_mode(params.as_string(0, MODE_MUST_NOT_BE_CODE)); | bool do_append=false; |
| Value& vfile_name=params.as_no_junction(1, FILE_NAME_MUST_NOT_BE_CODE); | 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; | Charset* asked_charset=0; |
| if(params.count()>2) | if(params.count()>2) |
| Line 139 static void _save(Request& r, MethodPara | Line 146 static void _save(Request& r, MethodPara |
| asked_charset=&pa_charsets.get(vcharset_name->as_string()); | asked_charset=&pa_charsets.get(vcharset_name->as_string()); |
| valid_options++; | valid_options++; |
| } | } |
| if(Value* vappend=options->get(MODE_APPEND)){ | |
| do_append=vappend->as_bool(); | |
| valid_options++; | |
| } | |
| if(valid_options != options->count()) | if(valid_options != options->count()) |
| throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION); | throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION); |
| } | } |
| // save | // 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) { | 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 keep_empty_dirs=false; |
| bool fail_on_problem=true; | bool fail_on_problem=true; |
| Line 203 static void copy_process_source(struct s | Line 214 static void copy_process_source(struct s |
| nCount = file_block_read(from_file, buffer, sizeof(buffer)); | nCount = file_block_read(from_file, buffer, sizeof(buffer)); |
| int written=write(to_file, buffer, nCount); | int written=write(to_file, buffer, nCount); |
| if( written < 0 ) | if( written < 0 ) |
| throw Exception("file.access", | throw Exception("file.write", 0, "write failed: %s (%d)", strerror(errno), errno); |
| 0, | |
| "write failed: %s (%d)", strerror(errno), errno); | |
| } while(nCount > 0); | } while(nCount > 0); |
| } | } |
| Line 223 static void _copy(Request& r, MethodPara | Line 232 static void _copy(Request& r, MethodPara |
| if(params.count()>2) | if(params.count()>2) |
| if(HashStringValue* options=params.as_hash(2)){ | if(HashStringValue* options=params.as_hash(2)){ |
| int valid_options=0; | int valid_options=0; |
| if(Value* vappend=options->get("append")){ | if(Value* vappend=options->get(MODE_APPEND)){ |
| append=r.process(*vappend).as_bool(); | append=r.process(*vappend).as_bool(); |
| valid_options++; | valid_options++; |
| } | } |
| Line 251 static void _load_pass_param( | Line 260 static void _load_pass_param( |
| } | } |
| static void _load(Request& r, MethodParams& params) { | static void _load(Request& r, MethodParams& params) { |
| bool as_text=VFile::is_text_mode(params.as_string(0, MODE_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_string(1, FILE_NAME_MUST_NOT_BE_CODE)); | const String& lfile_name=r.full_disk_path(params.as_file_name(1)); |
| size_t param_index=params.count()-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; | Value* param_value=param_index>1?¶ms.as_no_junction(param_index, "file name or options must not be code"):0; |
| Line 311 static void _create(Request& r, MethodPa | Line 320 static void _create(Request& r, MethodPa |
| if(params.count()>=3){ | if(params.count()>=3){ |
| // old format: ^file::create[text|binary;file-name;string-or-file-content[;options]] | // 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); | 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; | content_index=2; |
| options_index=3; | options_index=3; |
| extended_options=false; | extended_options=false; |
| Line 385 static void _create(Request& r, MethodPa | Line 394 static void _create(Request& r, MethodPa |
| } | } |
| static void _stat(Request& r, MethodParams& params) { | 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; | uint64_t size; |
| time_t atime, mtime, ctime; | time_t atime, mtime, ctime; |
| Line 473 static void append_to_argv(Request& r, A | Line 482 static void append_to_argv(Request& r, A |
| static void _exec_cgi(Request& r, MethodParams& params, bool cgi) { | static void _exec_cgi(Request& r, MethodParams& params, bool cgi) { |
| bool is_text=true; | bool is_text=true; |
| size_t param_index=0; | 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)) { | if(VFile::is_valid_mode(mode)) { |
| is_text=VFile::is_text_mode(mode); | is_text=VFile::is_text_mode(mode); |
| param_index++; | param_index++; |
| Line 482 static void _exec_cgi(Request& r, Method | Line 491 static void _exec_cgi(Request& r, Method |
| if(param_index>=params.count()) | if(param_index>=params.count()) |
| throw Exception(PARSER_RUNTIME, 0, FILE_NAME_MUST_BE_SPECIFIED); | 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; | 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 | // passing environment |
| for(SAPI::Env::Iterator i(r.sapi_info); i; i.next() ) | 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 | // const |
| ECSTR(GATEWAY_INTERFACE, "CGI/1.1"); | ECSTR(GATEWAY_INTERFACE, "CGI/1.1"); |
| Line 563 static void _exec_cgi(Request& r, Method | Line 572 static void _exec_cgi(Request& r, Method |
| throw Exception(PARSER_RUNTIME, 0, "param must be string or table or array of strings"); | throw Exception(PARSER_RUNTIME, 0, "param must be string or table or array of strings"); |
| } | } |
| } | } |
| // remove trailing empty arguments for backward compatibility | |
| for(ArrayString::ReverseIterator i(argv); i;){ | |
| if(i.prev()->is_empty()){ // here for correct i.index() | |
| argv.remove(i.index()); | |
| } else { | |
| break; | |
| } | |
| } | |
| } | } |
| // transcode if necessary | // transcode if necessary |
| Line 728 static void _list(Request& r, MethodPara | Line 746 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); | 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::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; | const int ovector_size=(1/*match*/)*3; |
| int ovector[ovector_size]; | int ovector[ovector_size]; |
| Line 769 static void lock_execute_body(int , void | Line 787 static void lock_execute_body(int , void |
| } | } |
| static void _lock(Request& r, MethodParams& params) { | 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={ | Lock_execute_body_info info={ |
| &r, | &r, |
| ¶ms.as_junction(1, "body must be code") | ¶ms.as_junction(1, "body must be code") |
| Line 793 static size_t afterlastslash(const Strin | Line 811 static size_t afterlastslash(const Strin |
| } | } |
| static void _find(Request& r, MethodParams& params) { | 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; | Value* not_found_code=(params.count()==2)?¶ms.as_junction(1, "not-found param must be code"):0; |
| Line 835 static void _find(Request& r, MethodPara | Line 853 static void _find(Request& r, MethodPara |
| } | } |
| static void _dirname(Request& r, MethodParams& params) { | 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 | // works as *nix dirname |
| // empty > . | // empty > . |
| Line 869 static void _dirname(Request& r, MethodP | Line 887 static void _dirname(Request& r, MethodP |
| } | } |
| static void _basename(Request& r, MethodParams& params) { | 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 | // works as *nix basename |
| // empty > . | // empty > . |
| Line 895 static void _basename(Request& r, Method | Line 913 static void _basename(Request& r, Method |
| } | } |
| static void _justname(Request& r, MethodParams& params) { | 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/some.tar.gz > some.tar |
| // /a/b.c/ > empty | // /a/b.c/ > empty |
| // /a/b.c > b | // /a/b.c > b |
| Line 905 static void _justname(Request& r, Method | Line 923 static void _justname(Request& r, Method |
| } | } |
| static void _justext(Request& r, MethodParams& params) { | 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/some.tar.gz > gz |
| // /a/b.c/ > empty | // /a/b.c/ > empty |
| size_t pos=afterlastslash(file_spec); | size_t pos=afterlastslash(file_spec); |
| Line 915 static void _justext(Request& r, MethodP | Line 933 static void _justext(Request& r, MethodP |
| } | } |
| static void _fullpath(Request& r, MethodParams& params) { | 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; | const String* result; |
| if(file_spec.first_char()=='/') | if(file_spec.first_char()=='/') |
| result=&file_spec; | result=&file_spec; |
| Line 1097 static void _base64(Request& r, MethodPa | Line 1115 static void _base64(Request& r, MethodPa |
| if(params.count() < 3) | 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) | 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); | user_file_name=¶ms.as_string(1, FILE_NAME_MUST_BE_STRING); |
| if(params.count() == 4) | if(params.count() == 4) |
| Line 1123 static void _base64(Request& r, MethodPa | Line 1141 static void _base64(Request& r, MethodPa |
| if(params.count() > 2) | if(params.count() > 2) |
| throw Exception(PARSER_RUNTIME, 0, "accepts maximum 2 parameter(s) (has %d parameters)", params.count()); | 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*/); | 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); | Base64Options options = base64_encode_options(r, params.count() > 1 ? params.as_hash(1) : NULL); |
| Line 1137 static void _crc32(Request& r, MethodPar | Line 1155 static void _crc32(Request& r, MethodPar |
| if(&r.get_self() == file_class) { | if(&r.get_self() == file_class) { |
| // ^file:crc32[file-name] | // ^file:crc32[file-name] |
| if(params.count()) { | 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)); | crc32=pa_crc32(r.full_disk_path(file_spec)); |
| } else { | } else { |
| throw Exception(PARSER_RUNTIME, 0, FILE_NAME_MUST_BE_SPECIFIED); | throw Exception(PARSER_RUNTIME, 0, FILE_NAME_MUST_BE_SPECIFIED); |
| Line 1193 static void _md5(Request& r, MethodParam | Line 1211 static void _md5(Request& r, MethodParam |
| if(&r.get_self() == file_class) { | if(&r.get_self() == file_class) { |
| // ^file:md5[file-name] | // ^file:md5[file-name] |
| if(params.count()) { | 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)); | md5=pa_md5(r.full_disk_path(file_spec)); |
| } else { | } else { |
| throw Exception(PARSER_RUNTIME, 0, FILE_NAME_MUST_BE_SPECIFIED); | throw Exception(PARSER_RUNTIME, 0, FILE_NAME_MUST_BE_SPECIFIED); |
| Line 1215 MFile::MFile(): Methoded("file") { | Line 1233 MFile::MFile(): Methoded("file") { |
| add_native_method("create", Method::CT_DYNAMIC, _create, 1, 4); | add_native_method("create", Method::CT_DYNAMIC, _create, 1, 4); |
| // ^file.save[mode;file-name] | // ^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); | add_native_method("save", Method::CT_DYNAMIC, _save, 2, 3); |
| // ^file:delete[file-name] | // ^file:delete[file-name] |