/* * 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= nelem(mstack)){ fprint(2, "%L: macro stack overflow:"); for(i=0; iargc = argc; for(i=0; iargv[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; iargc; 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 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("")); }