#! /usr/local/bin/pikeconstant 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: %On", 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- n ); 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!n"); }
if(old_sp>=0)
{
string s = file[old_sp..sp-1];
int l = sizeof(s) - sizeof(s-"n");
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.n"); else
{
in_tag = 0;
string s = file[sp..end];
Block b = PikeBlock(s, realfile);
int l = sizeof(s) - sizeof(s-"n");
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)+   +FILE+ :(preamble) n ;
pikescript+="inherit __psphelpers;n";
pikescript+="mapping headers = ([ "Content-Type  :  text/html  ]);n ;
pikescript+="String.Buffer out = String.Buffer();n";
pikescript+="int write(mixed … args) { out->add(sprintf(@args)); };n";
pikescript+="void main(int argc, array(string)argv){n"; array(Block) contents = psp_to_blocks(file, realname);
/*
foreach(contents, Block b)
{
werror( %On , b);
}
*/
string ps, h; [ps, h] = render_psp(contents, , ); pikescript += ps; header += h; pikescript += "Stdio.stdout->write("%s\n\n%s ,((array)headers)[*]* :  * \n ,out->get());n }n"; return header + "nn" + 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.n", 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: %On", p);
#endif
if(p == -1) return 0;
else if(f[p-1] == '%') {
#ifdef DEBUG
werror("escaped!n");
#endif
start = start + 2; continue; } // (escaped!)
else {
#ifdef DEBUG
werror("got the end!n");
#endif
ret = p + 1;}
} while(!ret);
#ifdef DEBUG
werror("returning: %On", 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 = ({"\", """, "n"}); array out = ({"\\", "\\"", "\n"}); string render()
{
return "{n" + escape_string(contents) + "}n";
}
string escape_string(string c)
{
string retval = "";
int cl = start;
int atend=0;
int current=0;
do
{
string line;
int end = search(c, "n", 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 + " "" + filename + ""n out->add("" + line + "");n");
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 + n#line + start +  + filename +  n + 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 + n#line + start +  + filename +  nout->add((string)(" + expr + "));");
} else
{
string expr = contents[2..strlen(contents)-3];
return "// "+ start + " - " + end + "n#line " + start + " "" + filename + ""n" + expr + "n";
}
} string|array(Block) parse_directive(string exp)
{
exp = String.trim_all_whites(exp); if(search(exp, "n")!=-1)
throw(Error.Generic("PSP format error: invalid directive format.n")); // format of a directive is: keyword option="value" ... string keyword; int r = sscanf(exp, "%[A-Za-z0-9-] %s", keyword, exp); switch(keyword)
{
case "include":
return process_include(exp);
break; default:
throw(Error.Generic("PSP format error: unknown directive " + keyword + ".n")); }
} // 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!n")); includes++; int r = sscanf(exp, "%*sfile="%s"%*s", file); if(r != 3)
throw(Error.Generic("PSP format error: unknown include format.n")); string realfile; if(file[0] = '/')
{
realfile = Stdio.append_path(document_root, (file/"/" - ({""})) * "/");
}
else
{
realfile = file;
}
contents = Stdio.read_file(realfile); //werror("contents: %On", contents); if(contents)
{
array x = psp_to_blocks(contents, file);
//werror("blocks: %On", 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,
({ 000 , 001 , 002 , 003 , 004 , 005 , 006 , 007 ,
010 , 011 , 012 , 013 , 014 , 015 , 016 , 017 ,
020 , 021 , 022 , 023 , 024 , 025 , 026 , 027 ,
030 , 031 , 032 , 033 , 034 , 035 , 036 , 037 ,
177 ,
200 , 201 , 202 , 203 , 204 , 205 , 206 , 207 ,
210 , 211 , 212 , 213 , 214 , 215 , 216 , 217 ,
220 , 221 , 222 , 223 , 224 , 225 , 226 , 227 ,
230 , 231 , 232 , 233 , 234 , 235 , 236 , 237 ,
, % , '", """, "+", "&", "=", "/",
"#", ";", "\", "<", ">", "t", "n", "r", "@" }),
({
"%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" }));
}
}