Annotation of parser3/src/classes/file.C, revision 1.117
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.117 ! paf 8: static const char * const IDENT_FILE_C="$Date: 2003/11/20 17:07:43 $";
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:
1.114 paf 39: Value* create_new_value(Pool&) { return new VFile(); }
1.111 paf 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.116 paf 297: Append_env_pair_info info={&env, 0, 0};
1.90 paf 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: const char* eol_marker=0; size_t eol_marker_size;
352: const String* header=0;
1.41 parser 353: if(cgi) { // ^file:cgi
1.111 paf 354: // construct with 'out' body and header
355: size_t dos_pos=real_out->pos("\r\n\r\n", 4);
356: size_t unix_pos=real_out->pos("\n\n", 2);
357:
358: bool unix_header_break;
359: switch((dos_pos!=STRING_NOT_FOUND?10:00) + (unix_pos!=STRING_NOT_FOUND?01:00)) {
360: case 10: // dos
361: unix_header_break=false;
362: break;
363: case 01: // unix
364: unix_header_break=true;
365: break;
366: case 11: // dos & unix
367: unix_header_break=unix_pos<dos_pos;
368: break;
369: default: // 00
370: unix_header_break=false; // calm down, compiler
1.74 paf 371: throw Exception(0,
1.111 paf 372: 0,
1.90 paf 373: "output does not contain CGI header; "
374: "exit status=%d; stdoutsize=%u; stdout: \"%s\"; stderrsize=%u; stderr: \"%s\"",
1.111 paf 375: execution.status,
376: (uint)real_out->length(), real_out->cstr(),
377: (uint)real_err->length(), real_err->cstr());
378: break; //never reached
379: }
380:
381: int header_break_pos;
382: if(unix_header_break) {
383: header_break_pos=unix_pos;
384: eol_marker="\n"; eol_marker_size=1;
385: } else {
386: header_break_pos=dos_pos;
387: eol_marker="\r\n"; eol_marker_size=2;
388: }
1.21 paf 389:
1.109 paf 390: header=&real_out->mid(0, header_break_pos);
1.111 paf 391: body=&real_out->mid(header_break_pos+eol_marker_size*2, real_out->length());
1.29 paf 392: }
1.41 parser 393: // body
1.111 paf 394: self.set(false/*not tainted*/, body->cstr(), body->length());
1.94 paf 395:
396: // $fields << header
1.98 paf 397: if(header && eol_marker) {
1.111 paf 398: ArrayString rows;
399: size_t pos_after=0;
400: header->split(rows, pos_after, eol_marker);
1.116 paf 401: Pass_cgi_header_attribute_info info={0, 0, 0};
1.111 paf 402: info.charset=&r.charsets.source();
403: info.fields=&self.fields();
1.94 paf 404: rows.for_each(pass_cgi_header_attribute, &info);
405: if(info.content_type)
1.111 paf 406: self.fields().put(content_type_name, info.content_type);
1.94 paf 407: }
1.21 paf 408:
1.42 parser 409: // $status
1.111 paf 410: self.fields().put(file_status_name, new VInt(execution.status));
1.21 paf 411:
412: // $stderr
1.111 paf 413: if(real_err->length())
1.21 paf 414: self.fields().put(
1.112 paf 415: String::Body("stderr"),
1.111 paf 416: new VString(*real_err));
1.21 paf 417: }
1.111 paf 418: static void _exec(Request& r, MethodParams& params) {
419: _exec_cgi(r, params, false);
1.41 parser 420: }
1.111 paf 421: static void _cgi(Request& r, MethodParams& params) {
422: _exec_cgi(r, params, true);
1.41 parser 423: }
424:
1.111 paf 425: static void _list(Request& r, MethodParams& params) {
426: Value& relative_path=params.as_no_junction(0, "path must not be code");
1.47 parser 427:
1.111 paf 428: const String* regexp;
1.47 parser 429: pcre *regexp_code;
1.81 paf 430: const int ovecsize=(1/*match*/)*3;
431: int ovector[ovecsize];
1.111 paf 432: if(params.count()>1) {
433: regexp=¶ms.as_no_junction(1, "regexp must not be code").as_string();
1.47 parser 434:
1.111 paf 435: const char* pattern=regexp->cstr();
436: const char* errptr;
1.47 parser 437: int erroffset;
438: regexp_code=pcre_compile(pattern, PCRE_EXTRA | PCRE_DOTALL,
439: &errptr, &erroffset,
1.111 paf 440: r.charsets.source().pcre_tables);
1.47 parser 441:
442: if(!regexp_code)
1.74 paf 443: throw Exception(0,
1.111 paf 444: ®exp->mid(erroffset, regexp->length()),
1.47 parser 445: "regular expression syntax error - %s", errptr);
1.114 paf 446: } else {
447: regexp=0; // not used, just to calm down compiler
1.47 parser 448: regexp_code=0;
1.114 paf 449: }
1.47 parser 450:
451:
1.111 paf 452: const char* absolute_path_cstr=r.absolute(relative_path.as_string()).cstr(String::L_FILE_SPEC);
1.47 parser 453:
1.111 paf 454: Table::columns_type columns(new ArrayString);
455: *columns+=new String("name");
456: Table& table=*new Table(columns);
1.47 parser 457:
458: LOAD_DIR(absolute_path_cstr,
1.111 paf 459: const char* file_name_cstr=ffblk.ff_name;
460: size_t file_name_size=strlen(file_name_cstr);
1.47 parser 461: bool suits=true;
462: if(regexp_code) {
463: int exec_result=pcre_exec(regexp_code, 0,
464: ffblk.ff_name, file_name_size, 0,
465: 0, ovector, ovecsize);
466:
467: if(exec_result==PCRE_ERROR_NOMATCH)
468: suits=false;
469: else if(exec_result<0) {
470: (*pcre_free)(regexp_code);
1.74 paf 471: throw Exception(0,
1.47 parser 472: regexp,
473: "regular expression execute (%d)",
474: exec_result);
475: }
476: }
477:
478: if(suits) {
1.111 paf 479: Table::element_type row(new ArrayString);
480: *row+=new String(pa_strdup(file_name_cstr, file_name_size), file_name_size, true);
481: table+=row;
1.47 parser 482: }
483: );
484:
485: if(regexp_code)
1.111 paf 486: pcre_free(regexp_code);
1.47 parser 487:
1.60 parser 488: // write out result
1.111 paf 489: r.write_no_lang(*new VTable(&table));
1.47 parser 490: }
1.21 paf 491:
1.69 paf 492: #ifndef DOXYGEN
493: struct Lock_execute_body_info {
1.111 paf 494: Request* r;
495: Value* body_code;
1.69 paf 496: };
497: #endif
1.111 paf 498: static void lock_execute_body(int , void *ainfo) {
499: Lock_execute_body_info& info=*static_cast<Lock_execute_body_info *>(ainfo);
1.69 paf 500: // execute body
1.78 paf 501: info.r->write_assign_lang(info.r->process(*info.body_code));
1.69 paf 502: };
1.111 paf 503: static void _lock(Request& r, MethodParams& params) {
504: const String& file_spec=r.absolute(params.as_string(0, "file name must be string"));
1.116 paf 505: Lock_execute_body_info info={
506: &r,
1.117 ! paf 507: ¶ms.as_junction(1, "body must be code")
1.116 paf 508: };
1.69 paf 509:
1.70 paf 510: file_write_action_under_lock(file_spec, "lock", lock_execute_body, &info);
1.69 paf 511: }
512:
1.111 paf 513: static int lastposafter(const String& s, size_t after, const char* substr, size_t substr_size, bool beforelast=false) {
1.114 paf 514: size_t size=0; // just to calm down compiler
1.89 paf 515: if(beforelast)
1.111 paf 516: size=s.length();
1.116 paf 517: size_t at;
1.112 paf 518: while((at=s.pos(String::Body(substr, substr_size), after))!=STRING_NOT_FOUND) {
1.89 paf 519: size_t newafter=at+substr_size/*skip substr*/;
520: if(beforelast && newafter==size)
521: break;
522: after=newafter;
523: }
524:
525: return after;
526: }
527:
1.111 paf 528: static void _find(Request& r, MethodParams& params) {
529: const String& file_name=params.as_no_junction(0, "file name must not be code").as_string();
530: const String* file_spec;
1.90 paf 531: if(file_name.first_char()=='/')
532: file_spec=&file_name;
533: else
1.111 paf 534: file_spec=&r.relative(r.request_info.uri, file_name);
1.90 paf 535:
536: // easy way
537: if(file_readable(r.absolute(*file_spec))) {
1.96 paf 538: r.write_assign_lang(*file_spec);
1.90 paf 539: return;
540: }
541:
542: // monkey way
543: int after_base_slash=lastposafter(*file_spec, 0, "/", 1);
1.111 paf 544: const String* dirname=&file_spec->mid(0, after_base_slash);
545: const String& basename=file_spec->mid(after_base_slash, file_spec->length());
1.90 paf 546:
547: int after_monkey_slash;
548: while((after_monkey_slash=lastposafter(*dirname, 0, "/", 1, true))>0) {
1.111 paf 549: String test_name;
550: test_name<<*(dirname=&dirname->mid(0, after_monkey_slash));
551: test_name<<basename;
552: if(file_readable(r.absolute(test_name))) {
553: r.write_assign_lang(test_name);
1.90 paf 554: return;
555: }
556: }
557:
558: // no way, not found
1.111 paf 559: if(params.count()==2) {
560: Value& not_found_code=params.as_junction(1, "not-found param must be code");
1.90 paf 561: r.write_pass_lang(r.process(not_found_code));
562: }
563: }
564:
1.111 paf 565: static void _dirname(Request& r, MethodParams& params) {
566: const String& file_spec=params.as_string(0, "file name must be string");
1.89 paf 567: // /a/some.tar.gz > /a
568: // /a/b/ > /a
569: int afterslash=lastposafter(file_spec, 0, "/", 1, true);
570: if(afterslash>0)
571: r.write_assign_lang(file_spec.mid(0, afterslash==1?1:afterslash-1));
572: else
1.111 paf 573: r.write_assign_lang(String(".", 1));
1.89 paf 574: }
575:
1.111 paf 576: static void _basename(Request& r, MethodParams& params) {
577: const String& file_spec=params.as_string(0, "file name must be string");
1.89 paf 578: // /a/some.tar.gz > some.tar.gz
579: int afterslash=lastposafter(file_spec, 0, "/", 1);
1.111 paf 580: r.write_assign_lang(file_spec.mid(afterslash, file_spec.length()));
1.89 paf 581: }
582:
1.111 paf 583: static void _justname(Request& r, MethodParams& params) {
584: const String& file_spec=params.as_string(0, "file name must be string");
1.89 paf 585: // /a/some.tar.gz > some.tar
586: int afterslash=lastposafter(file_spec, 0, "/", 1);
587: int afterdot=lastposafter(file_spec, afterslash, ".", 1);
1.111 paf 588: r.write_assign_lang(file_spec.mid(afterslash, afterdot!=afterslash?afterdot-1:file_spec.length()));
1.89 paf 589: }
1.111 paf 590: static void _justext(Request& r, MethodParams& params) {
591: const String& file_spec=params.as_string(0, "file name must be string");
1.89 paf 592: // /a/some.tar.gz > gz
593: int afterdot=lastposafter(file_spec, 0, ".", 1);
594: if(afterdot>0)
1.111 paf 595: r.write_assign_lang(file_spec.mid(afterdot, file_spec.length()));
1.89 paf 596: }
597:
1.111 paf 598: static void _fullpath(Request& r, MethodParams& params) {
599: const String& file_spec=params.as_string(0, "file name must be string");
600: const String* result;
1.102 paf 601: if(file_spec.first_char()=='/')
602: result=&file_spec;
603: else {
604: // /some/page.html: ^file:fullpath[a.gif] => /some/a.gif
605: const String& full_disk_path=r.absolute(file_spec);
1.111 paf 606: size_t document_root_length=strlen(r.request_info.document_root);
1.106 paf 607:
608: if(document_root_length>0) {
1.111 paf 609: char last_char=r.request_info.document_root[document_root_length-1];
1.106 paf 610: if(last_char == '/' || last_char == '\\')
611: --document_root_length;
612: }
1.111 paf 613: result=&full_disk_path.mid(document_root_length, full_disk_path.length());
1.102 paf 614: }
615: r.write_assign_lang(*result);
616: }
617:
1.89 paf 618:
1.32 paf 619: // constructor
620:
1.111 paf 621: MFile::MFile(): Methoded("file") {
1.48 parser 622: // ^save[mode;file-name]
623: add_native_method("save", Method::CT_DYNAMIC, _save, 2, 2);
1.7 paf 624:
625: // ^delete[file-name]
1.32 paf 626: add_native_method("delete", Method::CT_STATIC, _delete, 1, 1);
1.45 parser 627:
628: // ^move[from-file-name;to-file-name]
629: add_native_method("move", Method::CT_STATIC, _move, 2, 2);
1.8 paf 630:
1.48 parser 631: // ^load[mode;disk-name]
632: // ^load[mode;disk-name;user-name]
633: add_native_method("load", Method::CT_DYNAMIC, _load, 2, 3);
1.25 paf 634:
635: // ^stat[disk-name]
1.32 paf 636: add_native_method("stat", Method::CT_DYNAMIC, _stat, 1, 1);
1.21 paf 637:
1.36 paf 638: // ^cgi[file-name]
639: // ^cgi[file-name;env hash]
640: // ^cgi[file-name;env hash;1cmd;2line;3ar;4g;5s]
1.41 parser 641: add_native_method("cgi", Method::CT_DYNAMIC, _cgi, 1, 2+10);
642:
643: // ^exec[file-name]
644: // ^exec[file-name;env hash]
645: // ^exec[file-name;env hash;1cmd;2line;3ar;4g;5s]
646: add_native_method("exec", Method::CT_DYNAMIC, _exec, 1, 2+10);
1.47 parser 647:
648: // ^file:list[path]
649: // ^file:list[path][regexp]
650: add_native_method("list", Method::CT_STATIC, _list, 1, 2);
1.69 paf 651:
652: // ^file:lock[path]{code}
653: add_native_method("lock", Method::CT_STATIC, _lock, 2, 2);
1.90 paf 654:
655: // ^find[file-name]
656: // ^find[file-name]{when-not-found}
657: add_native_method("find", Method::CT_STATIC, _find, 1, 2);
1.47 parser 658:
1.89 paf 659: // ^file:dirname[/a/some.tar.gz]=/a
660: // ^file:dirname[/a/b/]=/a
661: add_native_method("dirname", Method::CT_STATIC, _dirname, 1, 1);
662: // ^file:basename[/a/some.tar.gz]=some.tar.gz
663: add_native_method("basename", Method::CT_STATIC, _basename, 1, 1);
664: // ^file:justname[/a/some.tar.gz]=some.tar
665: add_native_method("justname", Method::CT_STATIC, _justname, 1, 1);
666: // ^file:justext[/a/some.tar.gz]=gz
667: add_native_method("justext", Method::CT_STATIC, _justext, 1, 1);
1.102 paf 668: // /some/page.html: ^file:fullpath[a.gif] => /some/a.gif
669: add_native_method("fullpath", Method::CT_STATIC, _fullpath, 1, 1);
1.1 paf 670: }
E-mail: