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>
24de1e2001-08-28Per Hedbor constant cvs_version = "$Id: piketag.pike,v 2.32 2001/08/28 18:09:43 per Exp $";
ea72a61999-11-27Per Hedbor constant thread_safe=1; inherit "module";
e123c72000-07-17Johan Sundström #include <module.h>
ea72a61999-11-27Per Hedbor 
b3281f2000-09-10Martin Nilsson constant module_type = MODULE_TAG;
bc0fa02001-03-08Per Hedbor constant module_name = "Scripting: Pike tag";
29c6412000-08-11Martin Stjernholm constant module_doc = #"
2da1e82000-09-15Johan Sundström <p>This module adds a processing instruction tag, <code>&lt;?pike ... ?&gt;</code>, for evaluating Pike code directly in the document.</p>
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
2da1e82000-09-15Johan Sundström run programs with the same right as the server!</p>
29c6412000-08-11Martin Stjernholm 
2da1e82000-09-15Johan Sundström <p>Example:</p>
29c6412000-08-11Martin Stjernholm 
2da1e82000-09-15Johan Sundström <pre>&lt;?pike write (\"Hello world!\\n\"); ?&gt;\n</pre>
29c6412000-08-11Martin Stjernholm 
2da1e82000-09-15Johan Sundström <p>There are a few helper functions available:</p>
29c6412000-08-11Martin Stjernholm  <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
2da1e82000-09-15Johan Sundström inserted into the page. It is not reparsed with the RXML parser.</p>
29c6412000-08-11Martin Stjernholm 
2da1e82000-09-15Johan Sundström <p>These special constructs are also recognized:</p>
29c6412000-08-11Martin Stjernholm  <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
2da1e82000-09-15Johan Sundström parsing the pike tag was encountered. Entities and scopes are available as variables named like the entity/scope itself. The RequestID object is available as <code>id</code>.</p>
d2f6692000-08-11Martin Stjernholm 
29c6412000-08-11Martin Stjernholm <p>Note that every RXML fragment is parsed by itself, so you can't
2da1e82000-09-15Johan Sundström have unmatched RXML tags in them. E.g. the following does not work:</p>
29c6412000-08-11Martin Stjernholm  <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
2da1e82000-09-15Johan Sundström works:</p>
29c6412000-08-11Martin Stjernholm 
2da1e82000-09-15Johan Sundström <pre>&lt;?pike
29c6412000-08-11Martin Stjernholm  //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
2da1e82000-09-15Johan Sundström does not apply to it. The use of the container tag is deprecated.</p>";
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())
99dc3f2000-09-25Per Hedbor #define SPLIT(X,FN) Parser.Pike.hide_whitespaces(Parser.Pike.tokenize(Parser.Pike.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"; \ }
99dc3f2000-09-25Per Hedbor #define R(X) Parser.Pike.reconstitute_with_line_numbers(X)
29c6412000-08-11Martin Stjernholm 
24de1e2001-08-28Per Hedbor array helpers(function add_constant)
29c6412000-08-11Martin Stjernholm { 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(); if(err=catch {
24de1e2001-08-28Per Hedbor  p = my_compile_string( s,id,1,"pike-tag("+id->not_query+")",e );
29c6412000-08-11Martin Stjernholm  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);
24de1e2001-08-28Per Hedbor  if (e->get() && strlen(e->get())) RXML.parse_error ("Error while compiling Pike code:\n%s", e->get());
29c6412000-08-11Martin Stjernholm  else throw (err); } master()->set_inhibit_compile_errors(0); if(err = catch{ (o=p())->parse(id);
26a0e12000-10-18Martin Stjernholm  }) { catch { err = ({err[0], err[1][sizeof (backtrace())..]}); err[1][0] = ({"top level", err[1][0][1]}); }; RXML.run_error ("Error in Pike code: %s", describe_backtrace (err)); }
29c6412000-08-11Martin Stjernholm  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 
24de1e2001-08-28Per Hedbor program my_compile_string(string s, object id, int dom, string fn, ErrorContainer e)
6540372000-08-09Per Hedbor { if( program_cache[ s ] ) return program_cache[ s ];
24de1e2001-08-28Per Hedbor  object compile_handler = class( ErrorContainer e ) { mapping constants = all_constants()+([]); void add_constant( string x, mixed v ) { constants[x] = v; } mapping(string:mixed) get_default_module() { return constants; } mixed resolv(string id, void|string fn, void|string ch) { return constants[id] || master()->resolv( id, fn, ch ); } void compile_error(string a, int b, string c) { e->compile_error( a, b, c ); } void compile_warning(string a, int b, string c) { e->compile_warning( a, b, c ); } }(e); object key = Roxen.add_scope_constants( 0, compile_handler->add_constant ); [array ip, array data] = parse_magic(s,id,dom,fn,e);
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;
24de1e2001-08-28Per Hedbor  pre = helpers(compile_handler->add_constant);
6540372000-08-09Per Hedbor  } else
24de1e2001-08-28Per Hedbor  { helpers(compile_handler->add_constant);
6540372000-08-09Per Hedbor  pre = helper_prototypes();
24de1e2001-08-28Per Hedbor  }
6540372000-08-09Per Hedbor  foreach( ip, program ipc ) {
24de1e2001-08-28Per Hedbor  compile_handler->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;
24de1e2001-08-28Per Hedbor  werror( "Compiling:\n%s\n\n", R(pre) ); program p = compile_string( R(pre), fn, compile_handler );
75612c2001-01-26Martin Stjernholm  if (query ("program_cache_limit") > 0) program_cache[ s ] = p;
3aca382000-08-09Per Hedbor 
6540372000-08-09Per Hedbor  cnt=0; return p; }
ea72a61999-11-27Per Hedbor 
24de1e2001-08-28Per Hedbor program handle_inherit( string what, RequestID id, ErrorContainer e )
6540372000-08-09Per Hedbor { // ouch ouch ouch.
24de1e2001-08-28Per Hedbor  return my_compile_string( read_roxen_file( what, id ),id,0,what,e);
6540372000-08-09Per Hedbor }
24de1e2001-08-28Per Hedbor array parse_magic( string data, RequestID id, int add_md, string filename, ErrorContainer e )
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 '/':
24de1e2001-08-28Per Hedbor  if( strlen(flat[i]->text)>2 && (flat[i]->text[1]=='/' || flat[i]->text[1]=='*') ) { if( flat[i]->text[2] == 'X' ) { if (flat[i]->text[1] == '*') flat[i]->text = flat[i]->text[..sizeof (flat[i]->text) - 3]; OCIPUP(); CIP( cip ); } else if( flat[i]->text[2] == 'O' ) { if (flat[i]->text[1] == '*') flat[i]->text = flat[i]->text[..sizeof (flat[i]->text) - 3]; OCIP(); CIP( cipup ); } else { OCIPUP(); OCIP(); } } else { OCIPUP(); OCIP(); } break;
6540372000-08-09Per Hedbor  case '#': OCIP(); OCIPUP();
24de1e2001-08-28Per Hedbor  werror("preproc: %O", flat[i] );
29c6412000-08-11Martin Stjernholm  if( sscanf( flat[i]->text, "#%*[ \t]inherit%[ \t]%s", string ws, string fn) == 3 && sizeof (ws)) {
24de1e2001-08-28Per Hedbor  flat[i]->text=""; inherits += ({ handle_inherit( PS(fn), id, e ) });
29c6412000-08-11Martin Stjernholm  } 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 );
24de1e2001-08-28Per Hedbor  [array ih,flat[i]] = parse_magic(read_roxen_file(fn,id), id, 0, fn,e);
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 {
24de1e2001-08-28Per Hedbor  // COMPATIBILITY CODE, DO NOT CHANGE SEMANTICS
6540372000-08-09Per Hedbor  program p; object o; string res; mixed err;
3257512000-08-09Per Hedbor 
24de1e2001-08-28Per Hedbor  werror("Foo\n");
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))]); }
75612c2001-01-26Martin Stjernholm  if (query ("program_cache_limit") > 0) program_cache[s] = p;
ea72a61999-11-27Per Hedbor  } }) { master()->set_inhibit_compile_errors(0);
24de1e2001-08-28Per Hedbor  if (e->get() && strlen(e->get()))
15a1852000-08-16Per Hedbor  {
24de1e2001-08-28Per Hedbor  RXML.parse_error ("Error while compiling Pike code:\n%s", Roxen.html_encode_string( 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=([
9b03652001-03-07Kenneth Johansson "?pike":#"<desc pi='pi'><p><short hide='hide'>
1748b32000-09-20Kenneth Johansson  Pike processing instruction tag.</short>This processing intruction
2da1e82000-09-15Johan Sundström  tag allows for evaluating Pike code directly in the document.</p>
3a20972000-08-29Kenneth Johansson  <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
2da1e82000-09-15Johan Sundström  inserted into the page. It is not reparsed with the RXML parser.</p>
bd57ab2000-09-18Kenneth Johansson  <p>Use entities within the Pike code, scope.variable is handled like <ent>scope.variable</ent> in RXML.</p> <p>Note: It is still possible to use the <tag>pike</tag>...<tag>/pike</tag> container tag, though it behaves exactly as it did in Roxen 2.0 and earlier and the functionality mentioned below does not apply to it. The use of the container tag is deprecated.</p> <p>Below is a list of special helper functions and constructs which are only available within the <tag>?pike ?</tag> tag.</p>
3a20972000-08-29Kenneth Johansson </desc>
9b03652001-03-07Kenneth Johansson <attr name='write'><p>
bd57ab2000-09-18Kenneth Johansson  write(string fmt, mixed ... args) 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
9b03652001-03-07Kenneth Johansson  output buffer without being interpreted as a format specifier.</p>
3a20972000-08-29Kenneth Johansson </attr>
9b03652001-03-07Kenneth Johansson <attr name='flush'><p>
3a20972000-08-29Kenneth Johansson  flush() is a helper function. It returns the contents of the output
9b03652001-03-07Kenneth Johansson  buffer and resets it.</p>
3a20972000-08-29Kenneth Johansson </attr>
9b03652001-03-07Kenneth Johansson <attr name='rxml'><p>
bd57ab2000-09-18Kenneth Johansson  rxml(string rxmlcode) is a helper function. It parses the string with
9b03652001-03-07Kenneth Johansson  the RXML parser.</p>
bd57ab2000-09-18Kenneth Johansson </attr>
9b03652001-03-07Kenneth Johansson <attr name='\"//O ...\" or \"/*O ... */\"'><p>
bd57ab2000-09-18Kenneth Johansson  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
9b03652001-03-07Kenneth Johansson  text that's written directly to the output buffer.</p>
bd57ab2000-09-18Kenneth Johansson </attr>
9b03652001-03-07Kenneth Johansson <attr name='\"//X ...\" or \"/*X ... */\"'><p>
bd57ab2000-09-18Kenneth Johansson  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 executed by the
9b03652001-03-07Kenneth Johansson  RXML parser and then written to the output buffer.</p>
bd57ab2000-09-18Kenneth Johansson </attr>
9b03652001-03-07Kenneth Johansson <attr name='#include \"...\"'><p> An #include preprocessor directive includes the specified file.</p>
bd57ab2000-09-18Kenneth Johansson </attr>
9b03652001-03-07Kenneth Johansson <attr name='#inherit \"...\"'><p>
bd57ab2000-09-18Kenneth Johansson  An #inherit preprocessor directive puts a corresponding inherit declaration in the class that's generated to contain the Pike code in
9b03652001-03-07Kenneth Johansson  the tag, i.e. it inherits a specified file from the Roxen filesystem.</p>
bd57ab2000-09-18Kenneth Johansson  <ex type='box'> <?pike //O <pre> int first = 1; for( var.counter=100; var.counter>1; var.counter--,first=0 ) { if( !first ) { //X &var.counter; bottles of beer on the wall //O } //X &var.counter; bottles of beer on the wall //X &var.counter; bottles of beer //O take one down, pass it around } //O one bottle of beer on the wall //O one bottle of beer //O take it down, pass it around //O no bottles of beer on the wall //O </pre> ?> </ex>
2da1e82000-09-15Johan Sundström </attr>",
bd57ab2000-09-18Kenneth Johansson 
3a20972000-08-29Kenneth Johansson  ]); #endif