diff options
Diffstat (limited to 'src/cmd/htmlroff/t7.c')
-rw-r--r-- | src/cmd/htmlroff/t7.c | 543 |
1 files changed, 543 insertions, 0 deletions
diff --git a/src/cmd/htmlroff/t7.c b/src/cmd/htmlroff/t7.c new file mode 100644 index 00000000..19b77ec1 --- /dev/null +++ b/src/cmd/htmlroff/t7.c @@ -0,0 +1,543 @@ +/* + * 7. Macros, strings, diversion, and position traps. + * + * macros can override builtins + * builtins can be renamed or removed! + */ + +#include "a.h" + +enum +{ + MAXARG = 10, + MAXMSTACK = 40 +}; + +/* macro invocation frame */ +typedef struct Mac Mac; +struct Mac +{ + int argc; + Rune *argv[MAXARG]; +}; + +Mac mstack[MAXMSTACK]; +int nmstack; +void emitdi(void); +void flushdi(void); + +/* + * Run a user-defined macro. + */ +void popmacro(void); +int +runmacro(int dot, int argc, Rune **argv) +{ + Rune *p; + int i; + Mac *m; + +if(verbose && isupperrune(argv[0][0])) fprint(2, "run: %S\n", argv[0]); + p = getds(argv[0]); + if(p == nil){ + if(verbose) + warn("ignoring unknown request %C%S", dot, argv[0]); + if(verbose > 1){ + for(i=0; i<argc; i++) + fprint(2, " %S", argv[i]); + fprint(2, "\n"); + } + return -1; + } + if(nmstack >= nelem(mstack)){ + fprint(2, "%L: macro stack overflow:"); + for(i=0; i<nmstack; i++) + fprint(2, " %S", mstack[i].argv[0]); + fprint(2, "\n"); + return -1; + } + m = &mstack[nmstack++]; + m->argc = argc; + for(i=0; i<argc; i++) + m->argv[i] = erunestrdup(argv[i]); + pushinputstring(p); + nr(L(".$"), argc-1); + inputnotify(popmacro); + return 0; +} + +void +popmacro(void) +{ + int i; + Mac *m; + + if(--nmstack < 0){ + fprint(2, "%L: macro stack underflow\n"); + return; + } + m = &mstack[nmstack]; + for(i=0; i<m->argc; i++) + free(m->argv[i]); + if(nmstack > 0) + nr(L(".$"), mstack[nmstack-1].argc-1); + else + nr(L(".$"), 0); +} + +void popmacro1(void); +jmp_buf runjb[10]; +int nrunjb; + +void +runmacro1(Rune *name) +{ + Rune *argv[2]; + int obol; + +if(verbose) fprint(2, "outcb %p\n", outcb); + obol = bol; + argv[0] = name; + argv[1] = nil; + bol = 1; + if(runmacro('.', 1, argv) >= 0){ + inputnotify(popmacro1); + if(!setjmp(runjb[nrunjb++])) + runinput(); + else + if(verbose) fprint(2, "finished %S\n", name); + } + bol = obol; +} + +void +popmacro1(void) +{ + popmacro(); + if(nrunjb >= 0) + longjmp(runjb[--nrunjb], 1); +} + +/* + * macro arguments + * + * "" means " inside " " + * "" empty string + * \newline can be done + * argument separator is space (not tab) + * number register .$ = number of arguments + * no arguments outside macros or in strings + * + * arguments copied in copy mode + */ + +/* + * diversions + * + * processed output diverted + * dn dl registers vertical and horizontal size of last diversion + * .z - current diversion name + */ + +/* + * traps + * + * skip most + * .t register - distance to next trap + */ +static Rune *trap0; + +void +outtrap(void) +{ + Rune *t; + + if(outcb) + return; + if(trap0){ +if(verbose) fprint(2, "trap: %S\n", trap0); + t = trap0; + trap0 = nil; + runmacro1(t); + free(t); + } +} + +/* .wh - install trap */ +void +r_wh(int argc, Rune **argv) +{ + int i; + + if(argc < 2) + return; + + i = eval(argv[1]); + if(argc == 2){ + if(i == 0){ + free(trap0); + trap0 = nil; + }else + if(verbose) + warn("not removing trap at %d", i); + } + if(argc > 2){ + if(i == 0){ + free(trap0); + trap0 = erunestrdup(argv[2]); + }else + if(verbose) + warn("not installing %S trap at %d", argv[2], i); + } +} + +void +r_ch(int argc, Rune **argv) +{ + int i; + + if(argc == 2){ + if(trap0 && runestrcmp(argv[1], trap0) == 0){ + free(trap0); + trap0 = nil; + }else + if(verbose) + warn("not removing %S trap", argv[1]); + return; + } + if(argc >= 3){ + i = eval(argv[2]); + if(i == 0){ + free(trap0); + trap0 = erunestrdup(argv[1]); + }else + if(verbose) + warn("not moving %S trap to %d", argv[1], i); + } +} + +void +r_dt(int argc, Rune **argv) +{ + USED(argc); + USED(argv); + warn("ignoring diversion trap"); +} + +/* define macro - .de, .am, .ig */ +void +r_de(int argc, Rune **argv) +{ + Rune *end, *p; + Fmt fmt; + int ignore, len; + + delreq(argv[1]); + delraw(argv[1]); + ignore = runestrcmp(argv[0], L("ig")) == 0; + if(!ignore) + runefmtstrinit(&fmt); + end = L(".."); + if(argc >= 3) + end = argv[2]; + if(runestrcmp(argv[0], L("am")) == 0 && (p=getds(argv[1])) != nil) + fmtrunestrcpy(&fmt, p); + len = runestrlen(end); + while((p = readline(CopyMode)) != nil){ + if(runestrncmp(p, end, len) == 0 + && (p[len]==' ' || p[len]==0 || p[len]=='\t' + || (p[len]=='\\' && p[len+1]=='}'))){ + free(p); + goto done; + } + if(!ignore) + fmtprint(&fmt, "%S\n", p); + free(p); + } + warn("eof in %C%S %S - looking for %#Q", dot, argv[0], argv[1], end); +done: + if(ignore) + return; + p = runefmtstrflush(&fmt); + if(p == nil) + sysfatal("out of memory"); + ds(argv[1], p); + free(p); +} + +/* define string .ds .as */ +void +r_ds(Rune *cmd) +{ + Rune *name, *line, *p; + + name = copyarg(); + line = readline(CopyMode); + if(name == nil || line == nil){ + free(name); + return; + } + p = line; + if(*p == '"') + p++; + if(cmd[0] == 'd') + ds(name, p); + else + as(name, p); + free(name); + free(line); +} + +/* remove request, macro, or string */ +void +r_rm(int argc, Rune **argv) +{ + int i; + + emitdi(); + for(i=1; i<argc; i++){ + delreq(argv[i]); + delraw(argv[i]); + ds(argv[i], nil); + } +} + +/* .rn - rename request, macro, or string */ +void +r_rn(int argc, Rune **argv) +{ + USED(argc); + renreq(argv[1], argv[2]); + renraw(argv[1], argv[2]); + ds(argv[2], getds(argv[1])); + ds(argv[1], nil); +} + +/* .di - divert output to macro xx */ +/* .da - divert, appending to macro */ +/* page offsetting is not done! */ +Fmt difmt; +int difmtinit; +Rune di[20][100]; +int ndi; + +void +emitdi(void) +{ + flushdi(); + runefmtstrinit(&difmt); + difmtinit = 1; + fmtrune(&difmt, Uformatted); +} + +void +flushdi(void) +{ + int n; + Rune *p; + + if(ndi == 0 || difmtinit == 0) + return; + fmtrune(&difmt, Uunformatted); + p = runefmtstrflush(&difmt); + memset(&difmt, 0, sizeof difmt); + difmtinit = 0; + if(p == nil) + warn("out of memory in diversion %C%S", dot, di[ndi-1]); + else{ + n = runestrlen(p); + if(n > 0 && p[n-1] != '\n'){ + p = runerealloc(p, n+2); + p[n] = '\n'; + p[n+1] = 0; + } + } + as(di[ndi-1], p); + free(p); +} + +void +outdi(Rune r) +{ +if(!difmtinit) abort(); + if(r == Uempty) + return; + fmtrune(&difmt, r); +} + +/* .di, .da */ +void +r_di(int argc, Rune **argv) +{ + br(); + if(argc > 2) + warn("extra arguments to %C%S", dot, argv[0]); + if(argc == 1){ + /* end diversion */ + if(ndi <= 0){ + // warn("unmatched %C%S", dot, argv[0]); + return; + } + flushdi(); + if(--ndi == 0){ + _nr(L(".z"), nil); + outcb = nil; + }else{ + _nr(L(".z"), di[ndi-1]); + runefmtstrinit(&difmt); + fmtrune(&difmt, Uformatted); + difmtinit = 1; + } + return; + } + /* start diversion */ + /* various register state should be saved, but it's all useless to us */ + flushdi(); + if(ndi >= nelem(di)) + sysfatal("%Cdi overflow", dot); + if(argv[0][1] == 'i') + ds(argv[1], nil); + _nr(L(".z"), argv[1]); + runestrcpy(di[ndi++], argv[1]); + runefmtstrinit(&difmt); + fmtrune(&difmt, Uformatted); + difmtinit = 1; + outcb = outdi; +} + +/* .wh - install trap */ +/* .ch - change trap */ +/* .dt - install diversion trap */ + +/* set input-line count trap */ +int itrapcount; +int itrapwaiting; +Rune *itrapname; + +void +r_it(int argc, Rune **argv) +{ + if(argc < 3){ + itrapcount = 0; + return; + } + itrapcount = eval(argv[1]); + free(itrapname); + itrapname = erunestrdup(argv[2]); +} + +void +itrap(void) +{ + itrapset(); + if(itrapwaiting){ + itrapwaiting = 0; + runmacro1(itrapname); + } +} + +void +itrapset(void) +{ + if(itrapcount > 0 && --itrapcount == 0) + itrapwaiting = 1; +} + +/* .em - invoke macro when all input is over */ +void +r_em(int argc, Rune **argv) +{ + Rune buf[20]; + + USED(argc); + runesnprint(buf, nelem(buf), ".%S\n", argv[1]); + as(L("eof"), buf); +} + +int +e_star(void) +{ + Rune *p; + + p = getds(getname()); + if(p) + pushinputstring(p); + return 0; +} + +int +e_t(void) +{ + if(inputmode&CopyMode) + return '\t'; + return 0; +} + +int +e_a(void) +{ + if(inputmode&CopyMode) + return '\a'; + return 0; +} + +int +e_backslash(void) +{ + if(inputmode&ArgMode) + ungetrune('\\'); + return backslash; +} + +int +e_dot(void) +{ + return '.'; +} + +int +e_dollar(void) +{ + int c; + + c = getnext(); + if(c < '1' || c > '9'){ + ungetnext(c); + return 0; + } + c -= '0'; + if(nmstack <= 0 || mstack[nmstack-1].argc <= c) + return 0; + pushinputstring(mstack[nmstack-1].argv[c]); + return 0; +} + +void +t7init(void) +{ + addreq(L("de"), r_de, -1); + addreq(L("am"), r_de, -1); + addreq(L("ig"), r_de, -1); + addraw(L("ds"), r_ds); + addraw(L("as"), r_ds); + addreq(L("rm"), r_rm, -1); + addreq(L("rn"), r_rn, -1); + addreq(L("di"), r_di, -1); + addreq(L("da"), r_di, -1); + addreq(L("it"), r_it, -1); + addreq(L("em"), r_em, 1); + addreq(L("wh"), r_wh, -1); + addreq(L("ch"), r_ch, -1); + addreq(L("dt"), r_dt, -1); + + addesc('$', e_dollar, CopyMode|ArgMode|HtmlMode); + addesc('*', e_star, CopyMode|ArgMode|HtmlMode); + addesc('t', e_t, CopyMode|ArgMode); + addesc('a', e_a, CopyMode|ArgMode); + addesc('\\', e_backslash, ArgMode|CopyMode); + addesc('.', e_dot, CopyMode|ArgMode); + + ds(L("eof"), L(".sp 0.5i\n")); + ds(L(".."), L("")); +} + |