1430c02000-03-16Martin Nilsson // This is a roxen module. Copyright © 1996 - 2000, Roxen IS.
ea72a61999-11-27Per Hedbor // // Adds support for inline pike in documents. // // Example: // <pike> // return "Hello world!\n"; // </pike>
29c6412000-08-11Martin Stjernholm constant cvs_version = "$Id: piketag.pike,v 2.14 2000/08/10 22:45:52 mast Exp $";
ea72a61999-11-27Per Hedbor constant thread_safe=1;
e1e93c2000-08-09Per Hedbor  #if constant(Parser.C) #define PARSER_C Parser.C #else #define PARSER_C Roxen.Parser.C #endif
ea72a61999-11-27Per Hedbor inherit "module";
e123c72000-07-17Johan Sundström #include <module.h>
ea72a61999-11-27Per Hedbor  constant module_type = MODULE_PARSER; constant module_name = "Pike tag";
29c6412000-08-11Martin Stjernholm constant module_doc = #" This module adds a processing instruction tag, <code>&lt;?pike ... ?&gt;</code>, for evaluating Pike code directly in the document.
ea72a61999-11-27Per Hedbor 
29c6412000-08-11Martin Stjernholm <p><img src=\"internal-roxen-err_2\" align=\"left\" alt=\"Warning\"> NOTE: Enabling this module is the same thing as letting your users run programs with the same right as the server! <p>Example: <p><pre>&lt;?pike write (\"Hello world!\\n\"); ?&gt;\n</pre> <p>There are a few helper functions available: <dl> <dt><code>write(string fmt, mixed ... args)</code></dt> <dd>Formats and appends a string to the output buffer.</dd> <dt><code>flush()</code></dt> <dd>Returns the contents of the output buffer and resets it.</dd> <dt><code>rxml(string rxmlcode)</code></dt> <dd>Parses the string with the rxml parser.</dd> </dl> <p>When the pike tag returns, the contents of the output buffer is inserted into the page. It is not reparsed with the RXML parser. <p>These special constructs are also recognized: <dl> <dt><code>//O ...</code> or <code>/*O ... */</code></dt> <dd>A Pike comment with an 'O' (the letter, not the number) as the very first character treats the rest of the text in the comment as output text that's written directly to the output buffer.</dd> <dt><code>//X ...</code> or <code>/*X ... */</code></dt> <dd>A Pike comment with an 'X' as the very first character treats the rest of the text in the comment as RXML code that's parsed with the RXML parser and then written to the output buffer.</dd> <dt><code>#include \"...\"</code></dt> <dd>An <code>#include</code> preprocessor directive includes the specified file in the virtual filesystem.</dd> <dt><code>#inherit \"...\"</code></dt> <dd>An <code>#inherit</code> preprocessor directive puts a corresponding inherit declaration in the class that's generated by the Pike code in the tag.</dd> </dl> <p>Note that every RXML fragment is parsed by itself, so you can't have unmatched RXML tags in them. E.g. the following does not work: <p><pre>&lt;?pike //X &lt;gtext&gt; write (\"Foo\"); //X &lt;/gtext&gt; ?&gt;\n</pre> <p>Adjacent 'X' comments are concatenated however, so the following works: <p><pre>&lt;?pike //X &lt;gtext&gt; //X Foo //X &lt;/gtext&gt; ?&gt;\n</pre> <p>For compatibility this module also adds the Pike container tag, <code>&lt;pike&gt;...&lt;/pike&gt;</code>. It behaves exactly as it did in Roxen 2.0 and earlier and the functionality mentioned above does not apply to it. The use of the container tag is deprecated.";
6540372000-08-09Per Hedbor 
ea72a61999-11-27Per Hedbor void create() { defvar("program_cache_limit", 256, "Program cache limit", TYPE_INT|VAR_MORE, "Maximum size of the cache for compiled programs."); } // Helper functions, to be used in the pike script. class Helpers { string data = ""; void output(mixed ... args) {
6540372000-08-09Per Hedbor  write( @args );
ea72a61999-11-27Per Hedbor  }
3257512000-08-09Per Hedbor  void write(mixed ... args) { if(!sizeof(args)) return; if(sizeof(args) > 1) data += sprintf(@args); else data += args[0]; }
ea72a61999-11-27Per Hedbor  string flush() { string r = data; data =""; return r; }
6540372000-08-09Per Hedbor  string rxml( string what )
3aca382000-08-09Per Hedbor  {
6540372000-08-09Per Hedbor  return Roxen.parse_rxml( what, RXML.get_context()->id );
3aca382000-08-09Per Hedbor  }
ea72a61999-11-27Per Hedbor  constant seteuid=0; constant setegid=0; constant setuid=0; constant setgid=0; constant call_out=0; constant all_constants=0; constant Privs=0; }
6540372000-08-09Per Hedbor class HProtos
ea72a61999-11-27Per Hedbor {
6540372000-08-09Per Hedbor  void output(mixed ... args); void write(mixed ... args); string flush(); string rxml( string what, object id ); constant seteuid=0; constant setegid=0; constant setuid=0; constant setgid=0; constant call_out=0; constant all_constants=0; constant Privs=0;
ea72a61999-11-27Per Hedbor }
29c6412000-08-11Martin Stjernholm #define PREFN "pike-tag(preamble)" #define POSTFN "pike-tag(postamble)"
6540372000-08-09Per Hedbor #define PS(X) (compile_string( "mixed foo(){ return "+(X)+";}")()->foo())
29c6412000-08-11Martin Stjernholm #define SPLIT(X,FN) PARSER_C.hide_whitespaces(PARSER_C.tokenize(PARSER_C.split(X),FN))
3aca382000-08-09Per Hedbor #define OCIP( ) \ if( cip ) \ { \
6540372000-08-09Per Hedbor  cip->text=sprintf("write(rxml(%O));",cip->text); \
3aca382000-08-09Per Hedbor  cip = 0; \ } #define OCIPUP( ) \ if( cipup ) \ { \ cipup->text=sprintf("write(%O);",cipup->text); \ cipup = 0; \ }
b191f02000-08-09Per Hedbor #define CIP(X) if( X ) \
3aca382000-08-09Per Hedbor  { \ X->text += flat[i]->text[3..]+"\n"; \
b191f02000-08-09Per Hedbor  flat[i]->text=flat[i]->trailing_whitespaces=""; \
3aca382000-08-09Per Hedbor  } \ else \ { \ X = flat[i]; \ flat[i]->text = flat[i]->text[3..]+"\n"; \ }
29c6412000-08-11Martin Stjernholm #define R(X) PARSER_C.reconstitute_with_line_numbers(X) array helpers() { add_constant( "__ps_magic_helpers", Helpers ); add_constant( "__ps_magic_protos", HProtos ); return SPLIT("inherit __ps_magic_helpers;\nimport Roxen;\n",PREFN); } array helper_prototypes( ) { return SPLIT("inherit __ps_magic_protos;\nimport Roxen;\n",PREFN); } private static mapping(string:program) program_cache = ([]); string simple_pi_tag_pike( string tag, mapping m, string s,RequestID id ) { program p; object o; string res; mixed err; id->misc->__added_helpers_in_tree=0; id->misc->cacheable=0; object e = ErrorContainer(); master()->set_inhibit_compile_errors(e); if(err=catch { p = my_compile_string( s,id,1,"pike-tag("+id->not_query+")" ); if (sizeof(program_cache) > query("program_cache_limit")) { array a = indices(program_cache); int i; // Zap somewhere between 25 & 50% of the cache. for(i = query("program_cache_limit")/2; i > 0; i--) m_delete(program_cache, a[random(sizeof(a))]); } }) { master()->set_inhibit_compile_errors(0); if (e->get()) RXML.parse_error ("Error compiling Pike code:\n%s", e->get()); else throw (err); } master()->set_inhibit_compile_errors(0); if(err = catch{ (o=p())->parse(id); }) RXML.run_error ("Error in Pike code: %s\n", describe_error (err)); res = (o && o->flush() || ""); if(o) destruct(o); return res; } string read_roxen_file( string what, object id ) { // let there be magic return id->conf->open_file(Roxen.fix_relative(what,id),"rR",id,1) ->read()[0]; }
9266a32000-08-09Per Hedbor 
6540372000-08-09Per Hedbor program my_compile_string(string s, object id, int dom, string fn) { if( program_cache[ s ] ) return program_cache[ s ]; object key = Roxen.add_scope_constants();
29c6412000-08-11Martin Stjernholm  [array ip, array data] = parse_magic(s,id,dom,fn);
6540372000-08-09Per Hedbor  int cnt;
29c6412000-08-11Martin Stjernholm  array pre;
6540372000-08-09Per Hedbor  if( !id->misc->__added_helpers_in_tree && !sizeof(ip)) { id->misc->__added_helpers_in_tree=1; pre = helpers(); } else pre = helper_prototypes(); foreach( ip, program ipc ) { add_constant( "____ih_"+cnt, ipc );
29c6412000-08-11Martin Stjernholm  pre += SPLIT("inherit ____ih_"+cnt++ + ";\n",PREFN);
6540372000-08-09Per Hedbor  }
29c6412000-08-11Martin Stjernholm  if (dom) pre += SPLIT("string|int parse(RequestID id)\n{\n",PREFN) + data + SPLIT("}",POSTFN); else pre += data; program p = compile_string(R(pre), fn);
6540372000-08-09Per Hedbor  program_cache[ s ] = p;
3aca382000-08-09Per Hedbor 
6540372000-08-09Per Hedbor  cnt=0; foreach( ip, program ipc ) add_constant( "____ih_"+cnt++, 0 ); destruct( key ); return p; }
ea72a61999-11-27Per Hedbor 
6540372000-08-09Per Hedbor program handle_inherit( string what, RequestID id ) { array err; // ouch ouch ouch.
29c6412000-08-11Martin Stjernholm  return my_compile_string( read_roxen_file( what, id ),id,0,what);
6540372000-08-09Per Hedbor }
29c6412000-08-11Martin Stjernholm array parse_magic( string data, RequestID id, int add_md, string filename )
6540372000-08-09Per Hedbor {
29c6412000-08-11Martin Stjernholm  array flat=SPLIT(data,filename);
6540372000-08-09Per Hedbor  object cip, cipup; array inherits = ({}); for( int i = 0; i<sizeof( flat ); i++ ) { switch( strlen(flat[i]->text) && flat[i]->text[0] )
ea72a61999-11-27Per Hedbor  {
6540372000-08-09Per Hedbor  case '.': OCIP(); OCIPUP();
e1e93c2000-08-09Per Hedbor  if( flat[i] == "." ) flat[i]->text = "->";
6540372000-08-09Per Hedbor  break; case '/':
29c6412000-08-11Martin Stjernholm  if( flat[i]->text[2..2] == "X" )
6540372000-08-09Per Hedbor  {
29c6412000-08-11Martin Stjernholm  if (flat[i]->text[1] == '*') flat[i]->text = flat[i]->text[..sizeof (flat[i]->text) - 3];
6540372000-08-09Per Hedbor  OCIPUP(); CIP( cip ); }
29c6412000-08-11Martin Stjernholm  else if( flat[i]->text[2..2] == "O" )
6540372000-08-09Per Hedbor  {
29c6412000-08-11Martin Stjernholm  if (flat[i]->text[1] == '*') flat[i]->text = flat[i]->text[..sizeof (flat[i]->text) - 3];
6540372000-08-09Per Hedbor  OCIP(); CIP( cipup ); } else { OCIPUP(); OCIP(); } break; case '#': OCIP(); OCIPUP();
29c6412000-08-11Martin Stjernholm  if( sscanf( flat[i]->text, "#%*[ \t]inherit%[ \t]%s", string ws, string fn) == 3 && sizeof (ws)) { inherits += ({ handle_inherit( PS(fn), id ) }); } else if( sscanf( flat[i]->text, "#%*[ \t]include%[ \t]%s", string ws, string fn) == 3 && sizeof (ws))
6540372000-08-09Per Hedbor  { sscanf( fn, "%*s<%s>", fn ); sscanf( fn, "%*s\"%s\"", fn );
29c6412000-08-11Martin Stjernholm  [array ih,flat[i]] = parse_magic(read_roxen_file(fn,id), id, 0, fn);
6540372000-08-09Per Hedbor  inherits += ih; } break; default: OCIP(); OCIPUP(); } } OCIP(); OCIPUP();
29c6412000-08-11Martin Stjernholm  return ({ inherits, flat }); } // ------------------------------------------------------------------- // The old <pike> container, for compatibility. // Helper functions, to be used in the pike script. class CompatHelpers { inherit "roxenlib"; string data = ""; void output(mixed ... args) { if(!sizeof(args)) return; if(sizeof(args) > 1) data += sprintf(@args); else data += args[0]; } string flush()
6540372000-08-09Per Hedbor  {
29c6412000-08-11Martin Stjernholm  string r = data; data =""; return r;
6540372000-08-09Per Hedbor  }
29c6412000-08-11Martin Stjernholm  constant seteuid=0; constant setegid=0; constant setuid=0; constant setgid=0; constant call_out=0; constant all_constants=0; constant Privs=0; } string functions(string page, int line) { add_constant( "__magic_helpers", CompatHelpers ); return "inherit __magic_helpers;\n" "#"+line+" \""+replace(page,"\"","\\\"")+"\"\n"; } // Preamble string pre(string what, object id) { if(search(what, "parse(") != -1) return functions(id->not_query, id->misc->line); if(search(what, "return") != -1) return functions(id->not_query, id->misc->line) + "string|int parse(RequestID id, mapping defines, object file, mapping args) { "; else return functions(id->not_query, id->misc->line) + "string|int parse(RequestID id, mapping defines, object file, mapping args) { return "; } // Will be added at the end... string post(string what) { if(search(what, "parse(") != -1) return ""; if (!strlen(what) || what[-1] != ';') return ";}"; else return "}";
6540372000-08-09Per Hedbor }
9266a32000-08-09Per Hedbor 
6540372000-08-09Per Hedbor // Compile and run the contents of the tag (in s) as a pike // program.
29c6412000-08-11Martin Stjernholm string container_pike(string tag, mapping m, string s, RequestID request_id, object file, mapping defs)
6540372000-08-09Per Hedbor { program p; object o; string res; mixed err;
3257512000-08-09Per Hedbor 
6540372000-08-09Per Hedbor  request_id->misc->cacheable=0;
ea72a61999-11-27Per Hedbor 
6540372000-08-09Per Hedbor  object e = ErrorContainer(); master()->set_inhibit_compile_errors(e); if(err=catch {
29c6412000-08-11Martin Stjernholm  s = pre(s,request_id)+s+post(s); p = program_cache[s];
6540372000-08-09Per Hedbor 
29c6412000-08-11Martin Stjernholm  if (!p) { // Not in the program cache. p = compile_string(s, "Pike-tag("+request_id->not_query+":"+ request_id->misc->line+")"); if (sizeof(program_cache) > query("program_cache_limit")) { array a = indices(program_cache); int i; // Zap somewhere between 25 & 50% of the cache. for(i = query("program_cache_limit")/2; i > 0; i--) m_delete(program_cache, a[random(sizeof(a))]); } program_cache[s] = p;
ea72a61999-11-27Per Hedbor  } }) { master()->set_inhibit_compile_errors(0);
29c6412000-08-11Martin Stjernholm  if (e->get()) RXML.parse_error ("Error compiling Pike code:\n%s", e->get()); else throw (err);
ea72a61999-11-27Per Hedbor  } master()->set_inhibit_compile_errors(0); if(err = catch{
29c6412000-08-11Martin Stjernholm  res = (o=p())->parse(request_id, defs, file, m);
ea72a61999-11-27Per Hedbor  })
29c6412000-08-11Martin Stjernholm  RXML.run_error ("Error in Pike code: %s\n", describe_error (err));
ea72a61999-11-27Per Hedbor  res = (res || "") + (o && o->flush() || ""); if(o) destruct(o); return res; }