Annotation of parser3/src/classes/file.C, revision 1.112
1.17 paf 1: /** @file
2: Parser: @b file parser class.
3:
1.111 paf 4: Copyright (c) 2001-2003 ArtLebedev Group (http://www.artlebedev.com)
1.72 paf 5: Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)
1.91 paf 6: */
1.17 paf 7:
1.112 ! paf 8: static const char* IDENT_FILE_C="$Date: 2003/09/24 14:32:05 $";
1.47 parser 9:
10: #include "pa_config_includes.h"
11:
12: #include "pcre.h"
1.1 paf 13:
1.35 paf 14: #include "classes.h"
1.111 paf 15: #include "pa_vmethod_frame.h"
16:
1.1 paf 17: #include "pa_request.h"
18: #include "pa_vfile.h"
1.11 paf 19: #include "pa_table.h"
1.21 paf 20: #include "pa_vint.h"
1.24 paf 21: #include "pa_exec.h"
1.40 parser 22: #include "pa_vdate.h"
1.47 parser 23: #include "pa_dir.h"
24: #include "pa_vtable.h"
1.67 paf 25: #include "pa_charset.h"
1.109 paf 26: #include "pa_charsets.h"
1.1 paf 27:
1.32 paf 28: // defines
29:
1.48 parser 30: #define TEXT_MODE_NAME "text"
1.90 paf 31: #define STDIN_EXEC_PARAM_NAME "stdin"
1.109 paf 32: #define CHARSET_EXEC_PARAM_NAME "charset"
1.48 parser 33:
1.111 paf 34: // class
35:
36: class MFile: public Methoded {
37: public: // VStateless_class
38:
39: Value* create_new_value() { return new VFile(); }
40:
41: public: // Methoded
42: bool used_directly() { return true; }
43:
44: public:
45: MFile();
46:
47: };
48:
49: // global variable
50:
51: DECLARE_CLASS_VAR(file, new MFile, 0);
52:
1.83 paf 53: // consts
54:
55: /// from apache-1.3|src|support|suexec.c
1.111 paf 56: static const char* suexec_safe_env_lst[]={
1.83 paf 57: "AUTH_TYPE",
58: "CONTENT_LENGTH",
59: "CONTENT_TYPE",
60: "DATE_GMT",
61: "DATE_LOCAL",
62: "DOCUMENT_NAME",
63: "DOCUMENT_PATH_INFO",
64: "DOCUMENT_ROOT",
65: "DOCUMENT_URI",
66: "FILEPATH_INFO",
67: "GATEWAY_INTERFACE",
68: "LAST_MODIFIED",
69: "PATH_INFO",
70: "PATH_TRANSLATED",
71: "QUERY_STRING",
72: "QUERY_STRING_UNESCAPED",
73: "REMOTE_ADDR",
74: "REMOTE_HOST",
75: "REMOTE_IDENT",
76: "REMOTE_PORT",
77: "REMOTE_USER",
78: "REDIRECT_QUERY_STRING",
79: "REDIRECT_STATUS",
80: "REDIRECT_URL",
81: "REQUEST_METHOD",
82: "REQUEST_URI",
83: "SCRIPT_FILENAME",
84: "SCRIPT_NAME",
85: "SCRIPT_URI",
86: "SCRIPT_URL",
87: "SERVER_ADMIN",
88: "SERVER_NAME",
89: "SERVER_ADDR",
90: "SERVER_PORT",
91: "SERVER_PROTOCOL",
92: "SERVER_SOFTWARE",
93: "UNIQUE_ID",
94: "USER_NAME",
95: "TZ",
96: NULL
97: };
98:
1.111 paf 99: // statics
1.33 paf 100:
1.112 ! paf 101: static const String::Body adate_name("adate");
! 102: static const String::Body mdate_name("mdate");
! 103: static const String::Body cdate_name("cdate");
1.32 paf 104:
1.1 paf 105: // methods
106:
1.111 paf 107: static void _save(Request& r, MethodParams& params) {
108: Value& vmode_name=params. as_no_junction(0, "mode must not be code");
109: Value& vfile_name=params.as_no_junction(1, "file name must not be code");
1.4 paf 110:
1.7 paf 111: // save
1.111 paf 112: GET_SELF(r, VFile).save(r.absolute(vfile_name.as_string()),
1.48 parser 113: vmode_name.as_string()==TEXT_MODE_NAME);
1.7 paf 114: }
115:
1.111 paf 116: static void _delete(Request& r, MethodParams& params) {
117: Value& vfile_name=params.as_no_junction(0, "file name must not be code");
1.7 paf 118:
119: // unlink
1.68 paf 120: file_delete(r.absolute(vfile_name.as_string()));
1.1 paf 121: }
122:
1.111 paf 123: static void _move(Request& r, MethodParams& params) {
124: Value& vfrom_file_name=params.as_no_junction(0, "from file name must not be code");
125: Value& vto_file_name=params.as_no_junction(1, "to file name must not be code");
1.45 parser 126:
1.51 parser 127: // move
1.68 paf 128: file_move(
1.45 parser 129: r.absolute(vfrom_file_name.as_string()),
130: r.absolute(vto_file_name.as_string()));
131: }
132:
1.111 paf 133: static void _load_pass_param(
134: HashStringValue::key_type key,
135: HashStringValue::value_type value,
136: HashStringValue *dest) {
137: dest->put(key, value);
138: }
139: static void _load(Request& r, MethodParams& params) {
140: Value& vmode_name=params. as_no_junction(0, "mode must not be code");
141: const String& lfile_name=r.absolute(params.as_no_junction(1, "file name must not be code").as_string());
142: Value* third_param=params.count()>2?¶ms.as_no_junction(2, "filename or options must not be code")
143: :0;
144: HashStringValue* third_param_hash=third_param?third_param->get_hash():0;
145: size_t alt_filename_param_index=2;
1.104 paf 146: if(third_param_hash)
147: alt_filename_param_index++;
1.9 paf 148:
1.111 paf 149: File_read_result file=file_read(r.charsets, lfile_name,
1.104 paf 150: vmode_name.as_string()==TEXT_MODE_NAME,
1.111 paf 151: third_param_hash
1.104 paf 152: );
1.9 paf 153:
1.111 paf 154: const char *user_file_name=params.count()>alt_filename_param_index?
155: params.as_string(alt_filename_param_index, "filename must be string").cstr()
156: :lfile_name.cstr(String::L_FILE_SPEC);
157:
158: Value* vcontent_type=0;
159: if(file.headers)
160: vcontent_type=file.headers->get(content_type_name);
1.104 paf 161: if(!vcontent_type)
1.111 paf 162: vcontent_type=new VString(r.mime_type_of(user_file_name));
1.10 paf 163:
1.111 paf 164: VFile& self=GET_SELF(r, VFile);
165: self.set(true/*tainted*/, file.str, file.length, user_file_name, vcontent_type);
166: if(file.headers)
167: file.headers->for_each(_load_pass_param, &self.fields());
1.9 paf 168: }
169:
1.111 paf 170: static void _stat(Request& r, MethodParams& params) {
171: Value& vfile_name=params.as_no_junction(0, "file name must not be code");
1.25 paf 172:
173: const String& lfile_name=vfile_name.as_string();
174:
1.40 parser 175: size_t size;
176: time_t atime, mtime, ctime;
177: file_stat(r.absolute(lfile_name),
178: size,
179: atime, mtime, ctime);
1.25 paf 180:
1.111 paf 181: VFile& self=GET_SELF(r, VFile);
182: self.set(true/*tainted*/, 0/*no bytes*/, size);
183: HashStringValue& ff=self.fields();
184: ff.put(adate_name, new VDate(atime));
185: ff.put(mdate_name, new VDate(mtime));
186: ff.put(cdate_name, new VDate(ctime));
187: ff.put(content_type_name, new VString(r.mime_type_of(lfile_name.cstr(String::L_FILE_SPEC))));
1.25 paf 188: }
189:
1.111 paf 190: static bool is_safe_env_key(const char* key) {
191: for(const char* validator=key; *validator; validator++) {
192: char c=*validator;
193: if(!(c>='A' && c<='Z' || c>='0' && c<='9' || c=='_' || c=='-'))
194: return false;
195: }
1.88 paf 196: if(strncasecmp(key, "HTTP_", 5)==0)
1.83 paf 197: return true;
1.87 paf 198: if(strncasecmp(key, "CGI_", 4)==0)
1.83 paf 199: return true;
200: for(int i=0; suexec_safe_env_lst[i]; i++) {
1.87 paf 201: if(strcasecmp(key, suexec_safe_env_lst[i])==0)
1.83 paf 202: return true;
203: }
204: return false;
205: }
1.90 paf 206: #ifndef DOXYGEN
207: struct Append_env_pair_info {
1.111 paf 208: HashStringString* env;
1.100 paf 209: Value* vstdin;
1.109 paf 210: Value* vcharset;
1.90 paf 211: };
212: #endif
1.111 paf 213: static void append_env_pair(
214: HashStringValue::key_type akey,
215: HashStringValue::value_type avalue,
216: Append_env_pair_info *info) {
217: if(akey==STDIN_EXEC_PARAM_NAME) {
218: info->vstdin=avalue;
219: } else if(akey==CHARSET_EXEC_PARAM_NAME) {
220: info->vcharset=avalue;
1.90 paf 221: } else {
1.111 paf 222: if(!is_safe_env_key(akey.cstr()))
1.90 paf 223: throw Exception("parser.runtime",
1.111 paf 224: new String(akey, String::L_TAINTED),
1.90 paf 225: "not safe environment variable");
1.111 paf 226: info->env->put(akey, avalue->as_string());
1.90 paf 227: }
1.22 paf 228: }
1.94 paf 229: #ifndef DOXYGEN
230: struct Pass_cgi_header_attribute_info {
1.111 paf 231: Charset* charset;
232: HashStringValue* fields;
233: Value* content_type;
1.94 paf 234: };
235: #endif
1.111 paf 236: static void pass_cgi_header_attribute(
237: ArrayString::element_type astring,
238: Pass_cgi_header_attribute_info* info) {
239: size_t colon_pos=astring->pos(':');
240: if(colon_pos==STRING_NOT_FOUND) {
241: const String& key=astring->mid(0, colon_pos).change_case(
242: *info->charset, String::CC_UPPER);
243: Value* value=new VString(astring->mid(colon_pos+1, astring->length()));
244: info->fields->put(key, value);
1.94 paf 245: if(key=="CONTENT-TYPE")
1.111 paf 246: info->content_type=value;
1.94 paf 247: }
1.29 paf 248: }
1.90 paf 249: /// @todo fix `` in perl - they produced flipping consoles and no output to perl
1.111 paf 250: static void _exec_cgi(Request& r, MethodParams& params,
1.41 parser 251: bool cgi) {
1.21 paf 252:
1.111 paf 253: Value& vfile_name=params.as_no_junction(0, "file name must not be code");
1.21 paf 254:
1.23 paf 255: const String& script_name=r.absolute(vfile_name.as_string());
256:
1.111 paf 257: HashStringString env;
1.62 paf 258: #define ECSTR(name, value_cstr) \
1.111 paf 259: if(value_cstr) \
260: env.put( \
1.112 ! paf 261: String::Body(#name), \
! 262: String::Body(value_cstr, 0)); \
1.82 paf 263: // passing SAPI::environment
1.111 paf 264: if(const char *const *pairs=SAPI::environment(r.sapi_info)) {
265: while(const char* pair=*pairs++)
266: if(const char* eq_at=strchr(pair, '='))
267: if(eq_at[1]) // has value
268: env.put(
269: pa_strdup(pair, eq_at-pair),
270: pa_strdup(eq_at+1, 0));
1.82 paf 271: }
272:
1.23 paf 273: // const
1.63 paf 274: ECSTR(GATEWAY_INTERFACE, "CGI/1.1");
1.23 paf 275: // from Request.info
1.111 paf 276: ECSTR(DOCUMENT_ROOT, r.request_info.document_root);
277: ECSTR(PATH_TRANSLATED, r.request_info.path_translated);
278: ECSTR(REQUEST_METHOD, r.request_info.method);
279: ECSTR(QUERY_STRING, r.request_info.query_string);
280: ECSTR(REQUEST_URI, r.request_info.uri);
281: ECSTR(CONTENT_TYPE, r.request_info.content_type);
1.23 paf 282: char content_length_cstr[MAX_NUMBER];
1.111 paf 283: snprintf(content_length_cstr, MAX_NUMBER, "%u", r.request_info.content_length);
284: //String content_length(content_length_cstr);
1.62 paf 285: ECSTR(CONTENT_LENGTH, content_length_cstr);
1.82 paf 286: // SCRIPT_*
1.112 ! paf 287: env.put(String::Body("SCRIPT_NAME"), script_name);
! 288: //env.put(String::Body("SCRIPT_FILENAME"), ??&script_name);
1.23 paf 289:
1.111 paf 290: bool stdin_specified=false;
1.90 paf 291: // environment & stdin from param
1.111 paf 292: String *in=new String();
1.109 paf 293: Charset *charset=0; // default script works raw_in 'source' charset = no transcoding needed
1.111 paf 294: if(params.count()>1) {
295: Value& venv=params.as_no_junction(1, "env must not be code");
296: if(HashStringValue* user_env=venv.get_hash()) {
1.90 paf 297: Append_env_pair_info info={&env};
298: user_env->for_each(append_env_pair, &info);
1.109 paf 299: // $.stdin
1.103 paf 300: if(info.vstdin) {
1.111 paf 301: stdin_specified=true;
302: if(const String* sstdin=info.vstdin->get_string()) {
303: in->append(*sstdin, String::L_CLEAN, true);
1.103 paf 304: } else
1.111 paf 305: if(VFile* vfile=static_cast<VFile *>(info.vstdin->as("file", false)))
306: in->append_know_length((const char* )vfile->value_ptr(), vfile->value_size(), String::L_TAINTED);
1.100 paf 307: else
308: throw Exception("parser.runtime",
1.111 paf 309: 0,
1.100 paf 310: STDIN_EXEC_PARAM_NAME " parameter must be string or file");
1.103 paf 311: }
1.109 paf 312: // $.charset
313: if(info.vcharset)
1.111 paf 314: charset=&charsets.get(info.vcharset->as_string()
315: .change_case(r.charsets.source(), String::CC_UPPER));
1.90 paf 316: }
1.21 paf 317: }
318:
1.90 paf 319: // argv from params
1.111 paf 320: ArrayString argv;
321: if(params.count()>2) {
322: for(size_t i=2; i<params.count(); i++)
323: argv+=¶ms.as_string(i, "parameter must be string");
1.21 paf 324: }
1.90 paf 325:
1.109 paf 326: // transcode if necessary
327: if(charset) {
1.111 paf 328: Charset::transcode(env, r.charsets.source(), *charset);
329: Charset::transcode(argv, r.charsets.source(), *charset);
330: in=&Charset::transcode(*in, r.charsets.source(), *charset);
331: }
332: // @todo
333: // ifdef WIN32 do OEM->ANSI transcode on some(.cmd?) programs to
334: // match silent conversion in OS
335:
336: // exec!
337: PA_exec_result execution=
338: pa_exec(false/*forced_allow*/, script_name, &env, argv, *in);
339:
340: String *real_out=&execution.out;
341: String *real_err=&execution.err;
342: // transcode if necessary
343: if(charset) {
344: real_out=&Charset::transcode(*real_out, *charset, r.charsets.source());
345: real_err=&Charset::transcode(*real_err, *charset, r.charsets.source());
1.109 paf 346: }
347:
1.111 paf 348: VFile& self=GET_SELF(r, VFile);
1.109 paf 349:
1.111 paf 350: const String* body=real_out; // ^file:exec
351: Value* content_type=0;
352: const char* eol_marker=0; size_t eol_marker_size;
353: const String* header=0;
1.41 parser 354: if(cgi) { // ^file:cgi
1.111 paf 355: // construct with 'out' body and header
356: size_t dos_pos=real_out->pos("\r\n\r\n", 4);
357: size_t unix_pos=real_out->pos("\n\n", 2);
358:
359: bool unix_header_break;
360: switch((dos_pos!=STRING_NOT_FOUND?10:00) + (unix_pos!=STRING_NOT_FOUND?01:00)) {
361: case 10: // dos
362: unix_header_break=false;
363: break;
364: case 01: // unix
365: unix_header_break=true;
366: break;
367: case 11: // dos & unix
368: unix_header_break=unix_pos<dos_pos;
369: break;
370: default: // 00
371: unix_header_break=false; // calm down, compiler
1.74 paf 372: throw Exception(0,
1.111 paf 373: 0,
1.90 paf 374: "output does not contain CGI header; "
375: "exit status=%d; stdoutsize=%u; stdout: \"%s\"; stderrsize=%u; stderr: \"%s\"",
1.111 paf 376: execution.status,
377: (uint)real_out->length(), real_out->cstr(),
378: (uint)real_err->length(), real_err->cstr());
379: break; //never reached
380: }
381:
382: int header_break_pos;
383: if(unix_header_break) {
384: header_break_pos=unix_pos;
385: eol_marker="\n"; eol_marker_size=1;
386: } else {
387: header_break_pos=dos_pos;
388: eol_marker="\r\n"; eol_marker_size=2;
389: }
1.21 paf 390:
1.109 paf 391: header=&real_out->mid(0, header_break_pos);
1.111 paf 392: body=&real_out->mid(header_break_pos+eol_marker_size*2, real_out->length());
1.29 paf 393: }
1.41 parser 394: // body
1.111 paf 395: self.set(false/*not tainted*/, body->cstr(), body->length());
1.94 paf 396:
397: // $fields << header
1.98 paf 398: if(header && eol_marker) {
1.111 paf 399: ArrayString rows;
400: size_t pos_after=0;
401: header->split(rows, pos_after, eol_marker);
402: Pass_cgi_header_attribute_info info={0};
403: info.charset=&r.charsets.source();
404: info.fields=&self.fields();
1.94 paf 405: rows.for_each(pass_cgi_header_attribute, &info);
406: if(info.content_type)
1.111 paf 407: self.fields().put(content_type_name, info.content_type);
1.94 paf 408: }
1.21 paf 409:
1.42 parser 410: // $status
1.111 paf 411: self.fields().put(file_status_name, new VInt(execution.status));
1.21 paf 412:
413: // $stderr
1.111 paf 414: if(real_err->length())
1.21 paf 415: self.fields().put(
1.112 ! paf 416: String::Body("stderr"),
1.111 paf 417: new VString(*real_err));
1.21 paf 418: }
1.111 paf 419: static void _exec(Request& r, MethodParams& params) {
420: _exec_cgi(r, params, false);
1.41 parser 421: }
1.111 paf 422: static void _cgi(Request& r, MethodParams& params) {
423: _exec_cgi(r, params, true);
1.41 parser 424: }
425:
1.111 paf 426: static void _list(Request& r, MethodParams& params) {
427: Value& relative_path=params.as_no_junction(0, "path must not be code");
1.47 parser 428:
1.111 paf 429: const String* regexp;
1.47 parser 430: pcre *regexp_code;
1.81 paf 431: const int ovecsize=(1/*match*/)*3;
432: int ovector[ovecsize];
1.111 paf 433: if(params.count()>1) {
434: regexp=¶ms.as_no_junction(1, "regexp must not be code").as_string();
1.47 parser 435:
1.111 paf 436: const char* pattern=regexp->cstr();
437: const char* errptr;
1.47 parser 438: int erroffset;
439: regexp_code=pcre_compile(pattern, PCRE_EXTRA | PCRE_DOTALL,
440: &errptr, &erroffset,
1.111 paf 441: r.charsets.source().pcre_tables);
1.47 parser 442:
443: if(!regexp_code)
1.74 paf 444: throw Exception(0,
1.111 paf 445: ®exp->mid(erroffset, regexp->length()),
1.47 parser 446: "regular expression syntax error - %s", errptr);
447: } else
448: regexp_code=0;
449:
450:
1.111 paf 451: const char* absolute_path_cstr=r.absolute(relative_path.as_string()).cstr(String::L_FILE_SPEC);
1.47 parser 452:
1.111 paf 453: Table::columns_type columns(new ArrayString);
454: *columns+=new String("name");
455: Table& table=*new Table(columns);
1.47 parser 456:
457: LOAD_DIR(absolute_path_cstr,
1.111 paf 458: const char* file_name_cstr=ffblk.ff_name;
459: size_t file_name_size=strlen(file_name_cstr);
1.47 parser 460: bool suits=true;
461: if(regexp_code) {
462: int exec_result=pcre_exec(regexp_code, 0,
463: ffblk.ff_name, file_name_size, 0,
464: 0, ovector, ovecsize);
465:
466: if(exec_result==PCRE_ERROR_NOMATCH)
467: suits=false;
468: else if(exec_result<0) {
469: (*pcre_free)(regexp_code);
1.74 paf 470: throw Exception(0,
1.47 parser 471: regexp,
472: "regular expression execute (%d)",
473: exec_result);
474: }
475: }
476:
477: if(suits) {
1.111 paf 478: Table::element_type row(new ArrayString);
479: *row+=new String(pa_strdup(file_name_cstr, file_name_size), file_name_size, true);
480: table+=row;
1.47 parser 481: }
482: );
483:
484: if(regexp_code)
1.111 paf 485: pcre_free(regexp_code);
1.47 parser 486:
1.60 parser 487: // write out result
1.111 paf 488: r.write_no_lang(*new VTable(&table));
1.47 parser 489: }
1.21 paf 490:
1.69 paf 491: #ifndef DOXYGEN
492: struct Lock_execute_body_info {
1.111 paf 493: Request* r;
494: Value* body_code;
1.69 paf 495: };
496: #endif
1.111 paf 497: static void lock_execute_body(int , void *ainfo) {
498: Lock_execute_body_info& info=*static_cast<Lock_execute_body_info *>(ainfo);
1.69 paf 499: // execute body
1.78 paf 500: info.r->write_assign_lang(info.r->process(*info.body_code));
1.69 paf 501: };
1.111 paf 502: static void _lock(Request& r, MethodParams& params) {
503: Lock_execute_body_info info={0};
504: info.r=&r;
505: const String& file_spec=r.absolute(params.as_string(0, "file name must be string"));
506: info.body_code=¶ms.as_junction(1, "body must be code");
1.69 paf 507:
1.70 paf 508: file_write_action_under_lock(file_spec, "lock", lock_execute_body, &info);
1.69 paf 509: }
510:
1.111 paf 511: static int lastposafter(const String& s, size_t after, const char* substr, size_t substr_size, bool beforelast=false) {
1.89 paf 512: size_t size;
513: if(beforelast)
1.111 paf 514: size=s.length();
1.89 paf 515: int at;
1.112 ! paf 516: while((at=s.pos(String::Body(substr, substr_size), after))!=STRING_NOT_FOUND) {
1.89 paf 517: size_t newafter=at+substr_size/*skip substr*/;
518: if(beforelast && newafter==size)
519: break;
520: after=newafter;
521: }
522:
523: return after;
524: }
525:
1.111 paf 526: static void _find(Request& r, MethodParams& params) {
527: const String& file_name=params.as_no_junction(0, "file name must not be code").as_string();
528: const String* file_spec;
1.90 paf 529: if(file_name.first_char()=='/')
530: file_spec=&file_name;
531: else
1.111 paf 532: file_spec=&r.relative(r.request_info.uri, file_name);
1.90 paf 533:
534: // easy way
535: if(file_readable(r.absolute(*file_spec))) {
1.96 paf 536: r.write_assign_lang(*file_spec);
1.90 paf 537: return;
538: }
539:
540: // monkey way
541: int after_base_slash=lastposafter(*file_spec, 0, "/", 1);
1.111 paf 542: const String* dirname=&file_spec->mid(0, after_base_slash);
543: const String& basename=file_spec->mid(after_base_slash, file_spec->length());
1.90 paf 544:
545: int after_monkey_slash;
546: while((after_monkey_slash=lastposafter(*dirname, 0, "/", 1, true))>0) {
1.111 paf 547: String test_name;
548: test_name<<*(dirname=&dirname->mid(0, after_monkey_slash));
549: test_name<<basename;
550: if(file_readable(r.absolute(test_name))) {
551: r.write_assign_lang(test_name);
1.90 paf 552: return;
553: }
554: }
555:
556: // no way, not found
1.111 paf 557: if(params.count()==2) {
558: Value& not_found_code=params.as_junction(1, "not-found param must be code");
1.90 paf 559: r.write_pass_lang(r.process(not_found_code));
560: }
561: }
562:
1.111 paf 563: static void _dirname(Request& r, MethodParams& params) {
564: const String& file_spec=params.as_string(0, "file name must be string");
1.89 paf 565: // /a/some.tar.gz > /a
566: // /a/b/ > /a
567: int afterslash=lastposafter(file_spec, 0, "/", 1, true);
568: if(afterslash>0)
569: r.write_assign_lang(file_spec.mid(0, afterslash==1?1:afterslash-1));
570: else
1.111 paf 571: r.write_assign_lang(String(".", 1));
1.89 paf 572: }
573:
1.111 paf 574: static void _basename(Request& r, MethodParams& params) {
575: const String& file_spec=params.as_string(0, "file name must be string");
1.89 paf 576: // /a/some.tar.gz > some.tar.gz
577: int afterslash=lastposafter(file_spec, 0, "/", 1);
1.111 paf 578: r.write_assign_lang(file_spec.mid(afterslash, file_spec.length()));
1.89 paf 579: }
580:
1.111 paf 581: static void _justname(Request& r, MethodParams& params) {
582: const String& file_spec=params.as_string(0, "file name must be string");
1.89 paf 583: // /a/some.tar.gz > some.tar
584: int afterslash=lastposafter(file_spec, 0, "/", 1);
585: int afterdot=lastposafter(file_spec, afterslash, ".", 1);
1.111 paf 586: r.write_assign_lang(file_spec.mid(afterslash, afterdot!=afterslash?afterdot-1:file_spec.length()));
1.89 paf 587: }
1.111 paf 588: static void _justext(Request& r, MethodParams& params) {
589: const String& file_spec=params.as_string(0, "file name must be string");
1.89 paf 590: // /a/some.tar.gz > gz
591: int afterdot=lastposafter(file_spec, 0, ".", 1);
592: if(afterdot>0)
1.111 paf 593: r.write_assign_lang(file_spec.mid(afterdot, file_spec.length()));
1.89 paf 594: }
595:
1.111 paf 596: static void _fullpath(Request& r, MethodParams& params) {
597: const String& file_spec=params.as_string(0, "file name must be string");
598: const String* result;
1.102 paf 599: if(file_spec.first_char()=='/')
600: result=&file_spec;
601: else {
602: // /some/page.html: ^file:fullpath[a.gif] => /some/a.gif
603: const String& full_disk_path=r.absolute(file_spec);
1.111 paf 604: size_t document_root_length=strlen(r.request_info.document_root);
1.106 paf 605:
606: if(document_root_length>0) {
1.111 paf 607: char last_char=r.request_info.document_root[document_root_length-1];
1.106 paf 608: if(last_char == '/' || last_char == '\\')
609: --document_root_length;
610: }
1.111 paf 611: result=&full_disk_path.mid(document_root_length, full_disk_path.length());
1.102 paf 612: }
613: r.write_assign_lang(*result);
614: }
615:
1.89 paf 616:
1.32 paf 617: // constructor
618:
1.111 paf 619: MFile::MFile(): Methoded("file") {
1.48 parser 620: // ^save[mode;file-name]
621: add_native_method("save", Method::CT_DYNAMIC, _save, 2, 2);
1.7 paf 622:
623: // ^delete[file-name]
1.32 paf 624: add_native_method("delete", Method::CT_STATIC, _delete, 1, 1);
1.45 parser 625:
626: // ^move[from-file-name;to-file-name]
627: add_native_method("move", Method::CT_STATIC, _move, 2, 2);
1.8 paf 628:
1.48 parser 629: // ^load[mode;disk-name]
630: // ^load[mode;disk-name;user-name]
631: add_native_method("load", Method::CT_DYNAMIC, _load, 2, 3);
1.25 paf 632:
633: // ^stat[disk-name]
1.32 paf 634: add_native_method("stat", Method::CT_DYNAMIC, _stat, 1, 1);
1.21 paf 635:
1.36 paf 636: // ^cgi[file-name]
637: // ^cgi[file-name;env hash]
638: // ^cgi[file-name;env hash;1cmd;2line;3ar;4g;5s]
1.41 parser 639: add_native_method("cgi", Method::CT_DYNAMIC, _cgi, 1, 2+10);
640:
641: // ^exec[file-name]
642: // ^exec[file-name;env hash]
643: // ^exec[file-name;env hash;1cmd;2line;3ar;4g;5s]
644: add_native_method("exec", Method::CT_DYNAMIC, _exec, 1, 2+10);
1.47 parser 645:
646: // ^file:list[path]
647: // ^file:list[path][regexp]
648: add_native_method("list", Method::CT_STATIC, _list, 1, 2);
1.69 paf 649:
650: // ^file:lock[path]{code}
651: add_native_method("lock", Method::CT_STATIC, _lock, 2, 2);
1.90 paf 652:
653: // ^find[file-name]
654: // ^find[file-name]{when-not-found}
655: add_native_method("find", Method::CT_STATIC, _find, 1, 2);
1.47 parser 656:
1.89 paf 657: // ^file:dirname[/a/some.tar.gz]=/a
658: // ^file:dirname[/a/b/]=/a
659: add_native_method("dirname", Method::CT_STATIC, _dirname, 1, 1);
660: // ^file:basename[/a/some.tar.gz]=some.tar.gz
661: add_native_method("basename", Method::CT_STATIC, _basename, 1, 1);
662: // ^file:justname[/a/some.tar.gz]=some.tar
663: add_native_method("justname", Method::CT_STATIC, _justname, 1, 1);
664: // ^file:justext[/a/some.tar.gz]=gz
665: add_native_method("justext", Method::CT_STATIC, _justext, 1, 1);
1.102 paf 666: // /some/page.html: ^file:fullpath[a.gif] => /some/a.gif
667: add_native_method("fullpath", Method::CT_STATIC, _fullpath, 1, 1);
1.1 paf 668: }
E-mail: