#!/usr/local/bin/pike import Stdio; import spider; import Array; mapping(int:mapping) links=([]); string p1(string data); /* Note: destructive ! */ string *srt(string *foo) { sort(map(foo,lower_case),foo); return foo; } string quote_param(string s) { string ret=""; while(sscanf(s,"%[-a-zA-Z0-9/()*,.]%c%s",string a, int c,s)==3) ret+=sprintf("%s%%02x",a,c); return ret+s; } string unquote_param(string s) { string *tmp=s/"%"; for(int e=1;e<sizeof(tmp);e++) if(sscanf(tmp[e],"%2x%s",int x, string rest)) tmp[e]=sprintf("%c%s",x,rest); return tmp*""; } string code_params(mapping s) { string ret=""; foreach(indices(s), string data) ret+=" "+data+"="+quote_param(s[data]); return ret; } /* Index handling */ void _add_to(mapping foo, string bar, string full) { mapping m; if(!bar) { if(foo[0]) { foo[0]|=({full}); }else{ foo[0]=({full}); } return; } sscanf(bar,"%s.%s",bar,string rest); if(!(m=foo[bar])) m=foo[bar]=([]); _add_to(m, rest,full); } void add_to(string bar, string full) { sscanf(lower_case(unquote_param(bar)),"%*[_ ]%c",int c); mapping m; if(!(m=links[c])) m=links[c]=([]); _add_to(m, bar, full); } string anchor(mixed a, mixed b, mixed c) { if(b->name) { if(c[strlen(c)-10..]=="<!--END-->") werror("Warning: Anchor not ended "+b->name+".\n"); add_to(b->name, b->name); string *foo=b->name/"."; for(int e=1;e<sizeof(foo);e++) add_to(foo[e], foo[..e]*"."); } if (a=="anchor") return "<a name="+b->name+">"+c+"</a name="+b->name+">"; return 0; } string mklink(string lnk) { return "<a href=#"+lnk+">"+unquote_param(lnk)+"</a>"; } string genindex(mapping m, string prefix) { string ret="<dl>\n"; if(m[0]) { foreach(srt(m[0]), string lnk) ret+="<dt>"+mklink(lnk)+"\n"; } m_delete(m,0); foreach(srt(indices(m)), string ind) { if(m[ind][0] && sizeof(m[ind])==1 && sizeof(m[ind][0])==1 && m[ind][0][0]==prefix+ind) { foreach(srt(m[ind][0]), string lnk) ret+="<dt>"+mklink(lnk)+"\n"; }else{ ret+="<dt>"+unquote_param(prefix+ind)+"\n"+ "<dd>\n"+ genindex(m[ind], prefix+ind+"."); } } ret+="</dl>\n"; return ret; } /* chapter handling */ mixed *tod=({}); string prefix=""; string chapter(string tag, mapping params, string data); string section(string tag, mapping params, string data); string appendix(string tag, mapping params, string data); string appendixes(string tag, mapping params, string data); #define TAGS \ (["chapter":chapter,"section":section,\ "appendix":appendix,"appendixes":appendixes]) mapping aliases=([]); string rtag(string name, string title, string outtitle, string xlink, string sectionname, mapping params, string data) { if(data[strlen(data)-10..]=="<!--END-->") werror("Warning: "+name+" tag not ended "+title+".\n"); string *save=tod; string save_prefix=prefix; tod=({}); prefix+=xlink+"."; data+="\n<!--END-->"; data=parse_html(data,([]),TAGS); prefix=save_prefix; tod=save+({prefix+xlink,sectionname||(prefix+xlink),title,tod}); if(params->alias) { aliases[params->alias]= ({ prefix+xlink, sectionname || ( name + " " + prefix+xlink ) }) ; } if(data[strlen(data)-10..]=="<!--END-->") data=data[..strlen(data)-11]; string tmp; switch(sizeof(prefix/".")) { case 0: case 1: tmp="h1"; break; case 2: tmp="h2"; break; case 3: default: tmp="h3"; break; } return "<a name="+ prefix+xlink +">\n"+ "<"+tmp+">"+outtitle+"</"+tmp+">"+ data+"\n"+ "</a> <!-- "+name+" "+prefix+xlink+" -->\n"+ "<p>\n"; } string section(string tag, mapping params, string data) { int c = sizeof(tod)/4 +1; return rtag("section", params->title, prefix+c+" "+params->title, (string)c, 0, params, data); } string chapter(string tag, mapping params, string data) { int c = sizeof(tod)/4+1; return rtag("chapter", params->title, "Chapter "+prefix+c+", "+params->title, (string)c, 0, params, data); } string appendixes(string tag, mapping params, string data) { return rtag("appendixes", "", "Appendixes", "Appendix", "Appendixes", params, data); } int appnum = 'A'; string appendix(string tag, mapping params, string data) { string c=sprintf("%c",appnum++); return rtag("appendix", params->title, "Appendix "+c+", "+params->title, c, "Appendix "+c, params, data); } string gencontents(mixed *tod) { if(!sizeof(tod)) return ""; string ret="<dl>\n"; for(int e;e<sizeof(tod);e+=4) { ret+="<dt><b><a href=#"+tod[e]+">"+tod[e+1]+"</b> "+tod[e+2]+"</a>\n"; string tmp=gencontents(tod[e+3]); if(strlen(tmp)) ret+="<dd>\n"+tmp; } ret+="</dl>"; return ret; } /* Stage 2 */ mapping replacements=([]); string do_replace(string what) { return replacements[lower_case(what)]; } string encaps1(string word) { return "<font size=+1>"+word[0..0]+"</font><font size=-1>"+word[1..]+"</font>"; } string encaps(string tag, mapping params, string data) { return "<b>"+map((data/" ")-({""}),encaps1)*" "+"</b>"; } string link(string tag, mapping params, string data) { mixed tmp=aliases[params->to]; if(!tmp) { werror("Unresolved <link> :"+params->to+"\n"); return 0; } return "<a href=#"+tmp[0]+">"+tmp[1]+"</a>"; } string box(string tag, mapping params, string data) { return "<center><table border=1>\n"+ data+ "</table></center>\n"; } string do_include(string tag, mapping params, string data) { return Stdio.read_file(params->file); } string end_img_tag(string ret, mapping params) { if(params->align) ret+=" align="+quote_param(params->align); if(params->alt) ret+=" alt="+quote_param(params->align); return ret+">"; } int gifnum; mapping gifcache=([]); string mkgif(object o) { string g=o->togif(); if(gifcache[g]) return gifcache[g]; gifnum++; string gifname="illustration"+gifnum+".gif"; rm(gifname); write_file(gifname,g); gifcache[g]=gifname; return gifname; } mapping gifcache2=([]); mapping srccache=([]); string illustration(string tag, mapping params, string data) { string src=params->src; object img=Image.image(); float dpi=75.0; if(params->dpi) dpi=(float)params->dpi; if(params->scale) dpi/=(float)params->scale; float scale=75.0/dpi; string key=encode_value(({src,scale,data})); string file; if(!(file=gifcache2[key])) { mixed e; if(params->src) img=srccache[src] || (srccache[src]=img->fromppm(Process.popen("anytopnm "+src))); if(scale!=1.0) img=img->scale(scale); if (e=catch { img=compile_string("import Image;\n" "object `()(object src){ "+data+" ; }")()(img); file=mkgif(img); gifcache2[key]=file; }) { werror("error while compiling and running\n"+data+"\n"); if (params->__from__) werror("from "+params->__from__+":\n"); werror(master()->describe_backtrace(e)+"\n"); return "<!-- failed to illustrate -->"; } params-=(["__from__":0]); } return end_img_tag("<img src="+file,params); } string image(string tag, mapping params, string data) { string ret; if(params->xfig) { Process.system("fig2dev -L ps "+params->xfig+".fig ___tmp.ps;echo showpage >>___tmp.ps"); object o=Image.image()->fromppm( Process.popen("gs -q -sDEVICE=pbmraw -r225 -g2500x2500 -sOutputFile=- ___tmp.ps")); o=o->autocrop()->scale(1.0/3)->rotate(-90); o=Image.image(o->xsize()+40, o->ysize()+40, 255,255,255)->paste(o,20,20); string gifname=mkgif(o); rm("___tmp.ps"); ret="<img src="+quote_param(gifname); } else if(params->src) { return illustration(tag,params,"return src"); } else { return ""; werror("Unknown image type\n"); } return end_img_tag(ret,params); } int main() { string data=Stdio.stdin.read(0x7fffffff); data+="\n<!--END-->"; /* Preprocess */ data=parse_html(data,(["include":do_include]),([])); /* Pass 1, find links for the index */ data=parse_html(data, ([]) , (["a":anchor,"anchor":anchor])); /* Pass 2, parse chapters and sections */ data=parse_html(data, ([]) , TAGS); /* Build index */ string index="<dl>\n"; foreach(sort(indices(links)), int letter) { index+="<dt><font size=+2>"+ (letter=='`'?"Operators": upper_case(sprintf("%c",letter)))+ "</font>\n"+ "<dd>\n"+ genindex(links[letter],""); } index+="</dl>\n"; replacements->index=index; /* Build table of contents */ replacements["table-of-contents"]=gencontents(tod); /* Pass 3, insert generated data */ write(parse_html(data, ([ "index":do_replace, "table-of-contents":do_replace, "link":link, "image":image, ]), ([ "box":box, "encaps":encaps, "illustration":illustration, ]))); }