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>
b3281f2000-09-10Martin Nilsson constant cvs_version = "$Id: piketag.pike,v 2.21 2000/09/10 16:47:49 nilsson Exp $";
ea72a61999-11-27Per Hedbor constant thread_safe=1;
e1e93c2000-08-09Per Hedbor  #if constant(Parser.C) #define PARSER_C Parser.C #else
776f872000-08-14Martin Stjernholm #define PARSER_C Roxen._Parser.C
e1e93c2000-08-09Per Hedbor #endif
ea72a61999-11-27Per Hedbor inherit "module";
e123c72000-07-17Johan Sundström #include <module.h>
ea72a61999-11-27Per Hedbor 
b3281f2000-09-10Martin Nilsson constant module_type = MODULE_TAG;
ea72a61999-11-27Per Hedbor 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>
d2f6692000-08-11Martin Stjernholm  <dd>Formats a string in the same way as <code>printf</code> and appends it to the output buffer. If given only one string argument, it's written directly to the output buffer without being interpreted as a format specifier.</dd>
29c6412000-08-11Martin Stjernholm  <dt><code>flush()</code></dt> <dd>Returns the contents of the output buffer and resets it.</dd> <dt><code>rxml(string rxmlcode)</code></dt>
d2f6692000-08-11Martin Stjernholm  <dd>Parses the string with the RXML parser.</dd>
29c6412000-08-11Martin Stjernholm </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>
d2f6692000-08-11Martin Stjernholm  <dt><code>//O ... </code> or <code>/*O ... */</code></dt>
29c6412000-08-11Martin Stjernholm  <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>
d2f6692000-08-11Martin Stjernholm  <dt><code>//X ... </code> or <code>/*X ... */</code></dt>
29c6412000-08-11Martin Stjernholm  <dd>A Pike comment with an 'X' as the very first character treats
d2f6692000-08-11Martin Stjernholm  the rest of the text in the comment as RXML code that's executed by the RXML parser and then written to the output buffer.</dd>
29c6412000-08-11Martin Stjernholm  <dt><code>#include \"...\"</code></dt> <dd>An <code>#include</code> preprocessor directive includes the
d2f6692000-08-11Martin Stjernholm  specified file.</dd>
29c6412000-08-11Martin Stjernholm  <dt><code>#inherit \"...\"</code></dt> <dd>An <code>#inherit</code> preprocessor directive puts a
d2f6692000-08-11Martin Stjernholm  corresponding inherit declaration in the class that's generated to contain the Pike code in the tag.</dd>
29c6412000-08-11Martin Stjernholm </dl>
d2f6692000-08-11Martin Stjernholm <p>When files are included or inherited, they will be read from the virtual filesystem in Roxen, relative to the location during whose parsing the pike tag was encountered.
29c6412000-08-11Martin Stjernholm <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)
d2f6692000-08-11Martin Stjernholm  pre += SPLIT("void parse(RequestID id)\n{\n",PREFN) +
29c6412000-08-11Martin Stjernholm  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] == "." )
1343ce2000-08-11Per Hedbor  { flat[i]->text = "["; flat[i+1]->text = "\"" + 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())
15a1852000-08-16Per Hedbor  {
29c6412000-08-11Martin Stjernholm  RXML.parse_error ("Error compiling Pike code:\n%s", e->get());
15a1852000-08-16Per Hedbor  } 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; }
3a20972000-08-29Kenneth Johansson  // --------------------- Documentation ----------------------- TAGDOCUMENTATION; #ifdef manual constant tagdoc=([
c84cdf2000-08-30Martin Nilsson "<?pike":#"<desc pi><short hide>
3a20972000-08-29Kenneth Johansson  Pike processing instruction tag.</short>This processing intruction tag allows for evaluating Pike code directly in the document. <p>Note: With this tag, users are able to run programs with the same right as the server. This is a serious security hasard.</p> <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. </desc> <attr name='write' value='(string fmt, mixed ... args)'> write() is a helper function. It formats a string in the same way as printf and appends it to the output buffer. If given only one string argument, it's written directly to the output buffer without being interpreted as a format specifier. </attr> <attr name='flush' value='()'> flush() is a helper function. It returns the contents of the output buffer and resets it. </attr> <attr name='rxml' value='(string rxmlcode)'> rxml() is a helper function. It parses the string with the RXML parser. </attr> ", ]); #endif