Standalone PSP

[ Start > PikeApps > Standalone PSP ] [ Edit this Page | Viewing Version 1 ]


This is the Web.PikeServerPages.PSPCompiler class from Pike ScriptRunner, modified to run as a standalone CGI script, so no server-specific customization at all is needed. Place it somewhere (/usr/local/bin/pikesp for example), and put the following at the top of your PSP page:

  #! /path/to/pike /usr/local/bin/pikesp

This line will be removed from the output. Make sure your PSP page ends with a CGI extension (.cgi for example). Note that each invocation will launch a new pike process, so be careful not to use complex modules like Calendar or your pages per second rate will drop like a stone. Because of this, the following functions from Parser.HTML (which includes Calendar) are included directly in the script:

- html_encode_string

  • http_decode_string
  • http_encode_query
  • http_decode_urlencoded_query

#! /usr/local/bin/pike

constant TYPE_SCRIPTLET = 1; constant TYPE_DECLARATION = 2; constant TYPE_INLINE = 3; constant TYPE_DIRECTIVE = 4;

string document_root = "";

private int includes = 0;

// should this be configurable? int max_includes = 100;

array(Block) psp_to_blocks(string file, string realfile) { int file_len = strlen(file); int in_tag = 0; int sp = 0; int old_sp = 0; int start_line = 1; array contents = ({});

/* remove #! line if it exists */ if (file[0..1] == "#!") { old_sp = search(file,"n") + 1; start_line++; }

do { #ifdef DEBUG werror("starting point: %O, len: %O&#110;", sp, file_len); #endif sp = search(file, "<% , sp); if(sp= = -1) { sp=file_len; if(old_sp!=sp) { string s=file[old_sp..sp-1]; int l=sizeof(s) - sizeof(s- &#110; ); Block b=TextBlock(s, realfile); b->start = start_line; b->end = (start_line+=l); contents += ({b}); } }// no starting point, skip to the end.

else if(sp >= 0) // have a starting code. { int end; if(in_tag) { error("invalid format: nested tags!&#110;"); } if(old_sp>=0) { string s = file[old_sp..sp-1]; int l = sizeof(s) - sizeof(s-"&#110;"); Block b = TextBlock(s, realfile); b->start = start_line; b->end = (start_line+=l); contents += ({b}); } if((sp == 0) || (sp > 0 && file[sp-1] != '<')) { in_tag = 1; end = find_end(file, sp); } else { sp = sp + 2; continue; } // the start was escaped.

if(end == -1) error("invalid format: missing end tag.&#110;");

else { in_tag = 0; string s = file[sp..end]; Block b = PikeBlock(s, realfile); int l = sizeof(s) - sizeof(s-"&#110;"); b->start = start_line; b->end = (start_line+=l); contents += ({b});

sp = end + 1; old_sp = sp; } } } while (sp < file_len);

return contents; }

string parse_psp(string file, string realname) { // now, let's render some pike! string pikescript = ""; string header = ""; pikescript+="#line "+(LINE+1)+ &#32; +FILE+ :(preamble)&#32;&#110; ; pikescript+="inherit __psphelpers;&#110;"; pikescript+="mapping headers = ([ &#34;Content-Type&#32; : &#32;text/html&#32; ]);&#110; ; pikescript+="String.Buffer out = String.Buffer();&#110;"; pikescript+="int write(mixed … args) { out->add(sprintf(@args)); };&#110;"; pikescript+="void main(int argc, array(string)argv){&#110;";

array(Block) contents = psp_to_blocks(file, realname); /* foreach(contents, Block b) { werror( %O&#110; , b); } */ string ps, h;

[ps, h] = render_psp(contents, , );

pikescript += ps;

header += h;

pikescript += "Stdio.stdout->write(&#34;%s&#92;n&#92;n%s&#32;,((array)headers)[*]*&#32;: &#32;*&#32;&#92;n&#32;,out->get());&#110; }&#110;";

return header + "&#110;&#110;" + pikescript; }

array render_psp(array(Block) contents, string pikescript, string header) { foreach(contents, object e) { if(e->get_type() == TYPE_DECLARATION) header += e->render(); else if(e->get_type() == TYPE_DIRECTIVE) { mixed ren = e->render(); if(arrayp(ren)) [pikescript, header] = render_psp(ren, pikescript, header); } else pikescript += e->render(); }

return ({pikescript, header}); }

int main(int argc, array(string) argv) {

string file = Stdio.read_file(argv[1]); if(!file) { werror("input file %s does not exist.&#110;", argv[1]); return 1;}

string pikescript = parse_psp(file, argv[1]);

// write(pikescript);exit(0);

add_constant("__psphelpers", Helpers);

program p = compile_string(pikescript, argv[1]);

add_constant("__psphelpers");

p()->main(argc, argv);

return 0; }

int find_end(string f, int start) { int ret;

do { int p = search(f, "%>", start); #ifdef DEBUG werror("p: %O&#110;", p); #endif if(p == -1) return 0; else if(f[p-1] == '%') { #ifdef DEBUG werror("escaped!&#110;"); #endif start = start + 2; continue; } // (escaped!) else { #ifdef DEBUG werror("got the end!&#110;"); #endif ret = p + 1;} } while(!ret); #ifdef DEBUG werror("returning: %O&#110;", ret); #endif return ret; }

class Block(string contents, string filename) { int start; int end;

int get_type() { return 0; }

string _sprintf(mixed type) { return "Block(" + contents + ")"; }

array(Block) | string render(); }

class TextBlock { inherit Block;

array in = ({"&#92;", "&#34;", "&#110;"});

array out = ({"&#92;&#92;&#34;, "&#92;&#92;"", "&#92;n"});

string render() { return "{&#110;" + escape_string(contents) + "}&#110;"; }

string escape_string(string c) { string retval = ""; int cl = start; int atend=0; int current=0; do { string line; int end = search(c, "&#110;", current); if(end != -1) { line = c[current..end]; if(end == (strlen(c) -1)) atend = 1; else current = end + 1; } if(end == -1) { line = c[current..]; atend = 1; } line = replace(line, in, out); if(strlen(line)) { retval+=("#line " + cl + " &#34;" + filename + "&#34;&#110; out->add(&#34;" + line + "&#34;);&#110;"); cl++; } } while(!atend);

return retval;

}

}

class PikeBlock { inherit Block;

int get_type() { if(has_prefix(contents, "<% = )) return TYPE_INLINE; if(has_prefix(contents, %! )) return TYPE_DECLARATION; if(has_prefix(contents, %@ )) return TYPE_DIRECTIVE; else return TYPE_SCRIPTLET; } array(Block) | string render() { if(has_prefix(contents, %! )) { string expr=contents[3..strlen(contents)-3]; return( // + start + - + end + &#110;#line + start + &#32;+ filename + &#32;&#110; + expr); } else if(has_prefix(contents, %@ )) { string expr=contents[3..strlen(contents)-3]; return parse_directive(expr); } else if(has_prefix(contents, %=")) { string expr = String.trim_all_whites(contents[3..strlen(contents)-3]); return(" // + start + - + end + &#110;#line + start + &#32;+ filename + &#32;&#110;out->add((string)(" + expr + "));"); }

else { string expr = contents[2..strlen(contents)-3]; return "// "+ start + " - " + end + "&#110;#line " + start + " &#34;" + filename + "&#34;&#110;" + expr + "&#110;"; } }

string|array(Block) parse_directive(string exp) { exp = String.trim_all_whites(exp);

if(search(exp, "&#110;")!=-1) throw(Error.Generic("PSP format error: invalid directive format.&#110;"));

// format of a directive is: keyword option="value" ...

string keyword;

int r = sscanf(exp, "%[A-Za-z0-9&#45;] %s", keyword, exp);

switch(keyword) { case "include": return process_include(exp); break;

default: throw(Error.Generic("PSP format error: unknown directive " + keyword + ".&#110;"));

} }

// we don't handle absolute includes yet. array(Block) process_include(string exp) { string file; string contents;

if(includes > max_includes) throw(Error.Generic("PSP Error: too many includes, possible recursion!&#110;"));

includes++;

int r = sscanf(exp, "%*sfile=&#34;%s&#34;%*s", file);

if(r != 3) throw(Error.Generic("PSP format error: unknown include format.&#110;"));

string realfile;

if(file[0] = '/') { realfile = Stdio.append_path(document_root, (file/"/" - ({""})) * "/"); } else { realfile = file; } contents = Stdio.read_file(realfile);

//werror("contents: %O&#110;", contents);

if(contents) { array x = psp_to_blocks(contents, file); //werror("blocks: %O&#110;", x); return x; }

}

}

class Helpers { function html_encode_string = _Roxen.html_encode_string; function http_decode_string = _Roxen.http_decode_string;

//! method mapping(string:string|array(string)) http_decode_urlencoded_query(string query,void|mapping dest) //! Decodes an URL-encoded query into a mapping.

mapping(string:string|array(string)) http_decode_urlencoded_query(string query, void|mapping dest) { if (!dest) dest=([]);

foreach (query/"&",string s) { string i,v; if (sscanf(s,"%s=%s",i,v)<2) v=i=http_decode_string(s); else i=http_decode_string(replace(i, + , )),v=http_decode_string(replace(v, + , )); if (dest[i]) if (arrayp(dest[i])) dest[i]+=({v}); else dest[i]=({dest[i],v}); else dest[i]=v; }

return dest; }

string http_encode_query(mapping(string:int|string) variables) { return Array.map((array)variables, lambda(array(string|int|array(string)) v) { if (intp(v[1])) return http_encode_string(v[0]); if (arrayp(v[1])) return map(v[1], lambda (string val) { return http_encode_string(v[0])+ = + http_encode_string(val); })* & ; return http_encode_string(v[0])+ = + http_encode_string(v[1]); })* & ; }

//! This protects all odd - see @[http_encode_query()] - //! characters for transfer in HTTP. //! //! Do not use this function to protect URLs, since //! it will protect URL characters like @expr{'/'@} and @expr{'?'@}. //! @param in //! The string to encode //! @returns //! The HTTP encoded string string http_encode_string(string in) { return replace( in, ({ &#48;00 , &#48;01 , &#48;02 , &#48;03 , &#48;04 , &#48;05 , &#48;06 , &#48;07 , &#48;10 , &#48;11 , &#48;12 , &#48;13 , &#48;14 , &#48;15 , &#48;16 , &#48;17 , &#48;20 , &#48;21 , &#48;22 , &#48;23 , &#48;24 , &#48;25 , &#48;26 , &#48;27 , &#48;30 , &#48;31 , &#48;32 , &#48;33 , &#48;34 , &#48;35 , &#48;36 , &#48;37 , &#49;77 , &#50;00 , &#50;01 , &#50;02 , &#50;03 , &#50;04 , &#50;05 , &#50;06 , &#50;07 , &#50;10 , &#50;11 , &#50;12 , &#50;13 , &#50;14 , &#50;15 , &#50;16 , &#50;17 , &#50;20 , &#50;21 , &#50;22 , &#50;23 , &#50;24 , &#50;25 , &#50;26 , &#50;27 , &#50;30 , &#50;31 , &#50;32 , &#50;33 , &#50;34 , &#50;35 , &#50;36 , &#50;37 , , % , '", "&#34;", "+", "&", "=", "/", "#", ";", "&#92;", "<", ">", "&#116;", "&#110;", "&#114;", "@" }), ({ "%00", "%01", "%02", "%03", "%04", "%05", "%06", "%07", "%08", "%09", "%0a", "%0b", "%0c", "%0d", "%0e", "%0f", "%10", "%11", "%12", "%13", "%14", "%15", "%16", "%17", "%18", "%19", "%1a", "%1b", "%1c", "%1d", "%1e", "%1f", "%7f", "%80", "%81", "%82", "%83", "%84", "%85", "%86", "%87", "%88", "%89", "%8a", "%8b", "%8c", "%8d", "%8e", "%8f", "%90", "%91", "%92", "%93", "%94", "%95", "%96", "%97", "%98", "%99", "%9a", "%9b", "%9c", "%9d", "%9e", "%9f", "%20", "%25", "%27", "%22", "%2b", "%26", "%3d", "%2f", "%23", "%3b", "%5c", "%3c", "%3e", "%09", "%0a", "%0d", "%40" })); } }


Powered by PikeWiki2

 
gotpike.org | Copyright © 2004 - 2009 | Pike is a trademark of Department of Computer and Information Science, Linköping University