aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/htmlroff/t7.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/htmlroff/t7.c')
-rw-r--r--src/cmd/htmlroff/t7.c543
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(""));
+}
+