diff options
author | rsc <devnull@localhost> | 2006-02-21 18:37:05 +0000 |
---|---|---|
committer | rsc <devnull@localhost> | 2006-02-21 18:37:05 +0000 |
commit | c42a1d3d6168df56f966ea1f3ba3ef39ebbff4e4 (patch) | |
tree | 400f263e56681842ba1e6e1fdd8be453856474ef /src/cmd/htmlroff/roff.c | |
parent | 49a1496cbbb871bc623cfd0925566628e246c9ba (diff) | |
download | plan9port-c42a1d3d6168df56f966ea1f3ba3ef39ebbff4e4.tar.gz plan9port-c42a1d3d6168df56f966ea1f3ba3ef39ebbff4e4.tar.bz2 plan9port-c42a1d3d6168df56f966ea1f3ba3ef39ebbff4e4.zip |
add
Diffstat (limited to 'src/cmd/htmlroff/roff.c')
-rw-r--r-- | src/cmd/htmlroff/roff.c | 750 |
1 files changed, 750 insertions, 0 deletions
diff --git a/src/cmd/htmlroff/roff.c b/src/cmd/htmlroff/roff.c new file mode 100644 index 00000000..6a7cd09e --- /dev/null +++ b/src/cmd/htmlroff/roff.c @@ -0,0 +1,750 @@ +#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<nraw; i++){ + if(runestrcmp(raw[i].name, name) == 0){ + if(i != --nraw){ + free(raw[i].name); + raw[i] = raw[nraw]; + } + return; + } + } +} + +void +renraw(Rune *from, Rune *to) +{ + int i; + + delraw(to); + for(i=0; i<nraw; i++) + if(runestrcmp(raw[i].name, from) == 0){ + free(raw[i].name); + raw[i].name = erunestrdup(to); + return; + } +} + + +void +addreq(Rune *s, void (*f)(int, Rune**), int argc) +{ + Req *r; + + if(nreq >= 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<nreq; i++){ + if(runestrcmp(req[i].name, name) == 0){ + if(i != --nreq){ + free(req[i].name); + req[i] = req[nreq]; + } + return; + } + } +} + +void +renreq(Rune *from, Rune *to) +{ + int i; + + delreq(to); + for(i=0; i<nreq; i++) + if(runestrcmp(req[i].name, from) == 0){ + free(req[i].name); + req[i].name = erunestrdup(to); + return; + } +} + +void +addesc(Rune r, int (*f)(void), int mode) +{ + Esc *e; + + if(nesc >= 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<nesc; i++){ + if(r == esc[i].r && (inputmode&esc[i].mode)==inputmode){ + if(esc[i].f == e_warn) + warn("ignoring %C%C", backslash, r); + r = esc[i].f(); + if(r <= 0) + goto next; + return r; + } + } + if(inputmode&(ArgMode|CopyMode)){ + ungetrune(r); + r = backslash; + } + } + return r; +} + +void +ungetnext(Rune r) +{ + /* + * really we want to undo the getrunes that led us here, + * since the call after ungetnext might be getrune! + */ + ungetrune(r); +} + +int +_readx(Rune *p, int n, int nmode, int line) +{ + int c, omode; + Rune *e; + + while((c = getrune()) == ' ' || c == '\t') + ; + ungetrune(c); + omode = inputmode; + inputmode = nmode; + e = p+n-1; + for(c=getnext(); p<e; c=getnext()){ + if(c < 0) + break; + if(!line && (c == ' ' || c == '\t')) + break; + if(c == '\n'){ + if(!line) + ungetnext(c); + break; + } + *p++ = c; + } + inputmode = omode; + *p = 0; + if(c < 0) + return -1; + return 0; +} + +/* + * Get the next argument from the current line. + */ +Rune* +copyarg(void) +{ + static Rune buf[MaxLine]; + int c; + Rune *r; + + if(_readx(buf, sizeof buf, ArgMode, 0) < 0) + return nil; + r = runestrstr(buf, L("\\\"")); + if(r){ + *r = 0; + while((c = getrune()) >= 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<MAXARG; argc++){ + while(*p == ' ' || *p == '\t') + p++; + if(*p == 0) + break; + argv[argc] = p; + if(*p == '"'){ + /* quoted argument */ + if(*(p+1) == '"'){ + /* empty argument */ + *p = 0; + p += 2; + }else{ + /* parse quoted string */ + w = p++; + for(; *p; p++){ + if(*p == '"' && *(p+1) == '"') + *w++ = '"'; + else if(*p == '"'){ + p++; + break; + }else + *w++ = *p; + } + *w = 0; + } + }else{ + /* unquoted argument - need to watch out for \" comment */ + for(; *p; p++){ + if(*p == ' ' || *p == '\t'){ + *p++ = 0; + break; + } + if(*p == '\\' && *(p+1) == '"'){ + *p = 0; + if(p != argv[argc]) + argc++; + return argc; + } + } + } + } + return argc; +} + +/* + * Process a dot line. The dot has been read. + */ +void +dotline(int dot) +{ + int argc, i; + Rune *a, *argv[1+MAXARG]; + + /* + * Read request/macro name + */ + a = copyarg(); + if(a == nil || a[0] == 0){ + free(a); + getrune(); /* \n */ + return; + } + argv[0] = a; + /* + * Check for .if, .ie, and others with special parsing. + */ + for(i=0; i<nraw; i++){ + if(runestrcmp(raw[i].name, a) == 0){ + raw[i].f(raw[i].name); + free(a); + return; + } + } + + /* + * Read rest of line in copy mode, invoke regular request. + */ + a = readline(ArgMode); + if(a == nil){ + free(argv[0]); + return; + } + argc = 1+parseargs(a, argv+1); + for(i=0; i<nreq; i++){ + if(runestrcmp(req[i].name, argv[0]) == 0){ + if(req[i].argc != -1){ + if(argc < 1+req[i].argc){ + warn("not enough arguments for %C%S", dot, req[i].name); + free(argv[0]); + free(a); + return; + } + if(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), "<p style=\"line-height: %.1fem; text-indent: %.2fin; margin-top: 0; margin-bottom: 0; text-align: %s;\">\n", + vs, ti, align); + else + runesnprint(buf, nelem(buf), "<p style=\"line-height: %.1fem; margin-left: %.2fin; text-indent: %.2fin; margin-right: %.2fin; margin-top: 0; margin-bottom: 0; text-align: %s;\">\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("</p>")); +} + +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, "<sup>"); + Brune(r); + Bprint(&bout, "</sup>"); + 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; +} |