#include "a.h" enum { MAXREQ = 100, MAXRAW = 40, MAXESC = 60, MAXLINE = 1024, MAXIF = 20, MAXARG = 10, }; typedef struct Esc Esc; typedef struct Req Req; typedef struct Raw Raw; /* escape sequence handler, like for \c */ struct Esc { Rune r; int (*f)(void); int mode; }; /* raw request handler, like for .ie */ struct Raw { Rune *name; void (*f)(Rune*); }; /* regular request handler, like for .ft */ struct Req { int argc; Rune *name; void (*f)(int, Rune**); }; int dot = '.'; int tick = '\''; int backslash = '\\'; int inputmode; Req req[MAXREQ]; int nreq; Raw raw[MAXRAW]; int nraw; Esc esc[MAXESC]; int nesc; int iftrue[MAXIF]; int niftrue; int isoutput; int linepos; void addraw(Rune *name, void (*f)(Rune*)) { Raw *r; if(nraw >= nelem(raw)){ fprint(2, "too many raw requets\n"); return; } r = &raw[nraw++]; r->name = erunestrdup(name); r->f = f; } void delraw(Rune *name) { int i; for(i=0; i= nelem(req)){ fprint(2, "too many requests\n"); return; } r = &req[nreq++]; r->name = erunestrdup(s); r->f = f; r->argc = argc; } void delreq(Rune *name) { int i; for(i=0; i= nelem(esc)){ fprint(2, "too many escapes\n"); return; } e = &esc[nesc++]; e->r = r; e->f = f; e->mode = mode; } /* * Get the next logical character in the input stream. */ int getnext(void) { int i, r; next: r = getrune(); if(r < 0) return -1; if(r == Uformatted){ br(); assert(!isoutput); while((r = getrune()) >= 0 && r != Uunformatted){ if(r == Uformatted) continue; outrune(r); } goto next; } if(r == Uunformatted) goto next; if(r == backslash){ r = getrune(); if(r < 0) return -1; for(i=0; i= 0 && c != '\n') ; ungetrune('\n'); } r = erunestrdup(buf); return r; } /* * Read the current line in given mode. Newline not kept. * Uses different buffer from copyarg! */ Rune* readline(int m) { static Rune buf[MaxLine]; Rune *r; if(_readx(buf, sizeof buf, m, 1) < 0) return nil; r = erunestrdup(buf); return r; } /* * Given the argument line (already read in copy+arg mode), * parse into arguments. Note that \" has been left in place * during copy+arg mode parsing, so comments still need to be stripped. */ int parseargs(Rune *p, Rune **argv) { int argc; Rune *w; for(argc=0; argc 1+req[i].argc) warn("too many arguments for %C%S", dot, req[i].name); } req[i].f(argc, argv); free(argv[0]); free(a); return; } } /* * Invoke user-defined macros. */ runmacro(dot, argc, argv); free(argv[0]); free(a); } /* * newlines are magical in various ways. */ int bol; void newline(void) { int n; if(bol) sp(eval(L("1v"))); bol = 1; if((n=getnr(L(".ce"))) > 0){ nr(L(".ce"), n-1); br(); } if(getnr(L(".fi")) == 0) br(); outrune('\n'); } void startoutput(void) { char *align; double ps, vs, lm, rm, ti; Rune buf[200]; if(isoutput) return; isoutput = 1; if(getnr(L(".paragraph")) == 0) return; nr(L(".ns"), 0); isoutput = 1; ps = getnr(L(".s")); if(ps <= 1) ps = 10; ps /= 72.0; USED(ps); vs = getnr(L(".v"))*getnr(L(".ls")) * 1.0/UPI; vs /= (10.0/72.0); /* ps */ if(vs == 0) vs = 1.2; lm = (getnr(L(".o"))+getnr(L(".i"))) * 1.0/UPI; ti = getnr(L(".ti")) * 1.0/UPI; nr(L(".ti"), 0); rm = 8.0 - getnr(L(".l"))*1.0/UPI - getnr(L(".o"))*1.0/UPI; if(rm < 0) rm = 0; switch(getnr(L(".j"))){ default: case 0: align = "left"; break; case 1: align = "justify"; break; case 3: align = "center"; break; case 5: align = "right"; break; } if(getnr(L(".ce"))) align = "center"; if(!getnr(L(".margin"))) runesnprint(buf, nelem(buf), "

\n", vs, ti, align); else runesnprint(buf, nelem(buf), "

\n", vs, lm, ti, rm, align); outhtml(buf); } void br(void) { if(!isoutput) return; isoutput = 0; nr(L(".dv"), 0); dv(0); hideihtml(); if(getnr(L(".paragraph"))) outhtml(L("

")); } void r_margin(int argc, Rune **argv) { USED(argc); nr(L(".margin"), eval(argv[1])); } int inrequest; void runinput(void) { int c; bol = 1; for(;;){ c = getnext(); if(c < 0) break; if((c == dot || c == tick) && bol){ inrequest = 1; dotline(c); bol = 1; inrequest = 0; }else if(c == '\n'){ newline(); itrap(); linepos = 0; }else{ outtrap(); startoutput(); showihtml(); if(c == '\t'){ /* XXX do better */ outrune(' '); while(++linepos%4) outrune(' '); }else{ outrune(c); linepos++; } bol = 0; } } } void run(void) { t1init(); t2init(); t3init(); t4init(); t5init(); t6init(); t7init(); t8init(); /* t9init(); t9.c */ t10init(); t11init(); /* t12init(); t12.c */ t13init(); t14init(); t15init(); t16init(); t17init(); t18init(); t19init(); t20init(); htmlinit(); hideihtml(); addreq(L("margin"), r_margin, 1); nr(L(".margin"), 1); nr(L(".paragraph"), 1); runinput(); while(popinput()) ; dot = '.'; if(verbose) fprint(2, "eof\n"); runmacro1(L("eof")); closehtml(); } void out(Rune *s) { if(s == nil) return; for(; *s; s++) outrune(*s); } void (*outcb)(Rune); void inroman(Rune r) { int f; f = getnr(L(".f")); nr(L(".f"), 1); runmacro1(L("font")); outrune(r); nr(L(".f"), f); runmacro1(L("font")); } void Brune(Rune r) { if(r == '&') Bprint(&bout, "&"); else if(r == '<') Bprint(&bout, "<"); else if(r == '>') Bprint(&bout, ">"); else if(r < Runeself || utf8) Bprint(&bout, "%C", r); else Bprint(&bout, "%S", rune2html(r)); } void outhtml(Rune *s) { Rune r; for(; *s; s++){ switch(r = *s){ case '<': r = Ult; break; case '>': r = Ugt; break; case '&': r = Uamp; break; case ' ': r = Uspace; break; } outrune(r); } } void outrune(Rune r) { switch(r){ case ' ': if(getnr(L(".fi")) == 0) r = Unbsp; break; case Uformatted: case Uunformatted: abort(); } if(outcb){ if(r == ' ') r = Uspace; outcb(r); return; } /* writing to bout */ switch(r){ case Uempty: return; case Upl: inroman('+'); return; case Ueq: inroman('='); return; case Umi: inroman(0x2212); return; case Utick: r = '\''; break; case Ubtick: r = '`'; break; case Uminus: r = '-'; break; case '\'': Bprint(&bout, "’"); return; case '`': Bprint(&bout, "‘"); return; case Uamp: Bputrune(&bout, '&'); return; case Ult: Bputrune(&bout, '<'); return; case Ugt: Bputrune(&bout, '>'); return; case Uspace: Bputrune(&bout, ' '); return; case 0x2032: /* * In Firefox, at least, the prime is not * a superscript by default. */ Bprint(&bout, ""); Brune(r); Bprint(&bout, ""); return; } Brune(r); } void r_nop(int argc, Rune **argv) { USED(argc); USED(argv); } void r_warn(int argc, Rune **argv) { USED(argc); warn("ignoring %C%S", dot, argv[0]); } int e_warn(void) { /* dispatch loop prints a warning for us */ return 0; } int e_nop(void) { return 0; }