2 * 7. Macros, strings, diversion, and position traps.
4 * macros can override builtins
5 * builtins can be renamed or removed!
16 /* macro invocation frame */
17 typedef struct Mac Mac;
24 Mac mstack[MAXMSTACK];
30 * Run a user-defined macro.
34 runmacro(int dot, int argc, Rune **argv)
40 if(verbose && isupperrune(argv[0][0])) fprint(2, "run: %S\n", argv[0]);
44 warn("ignoring unknown request %C%S", dot, argv[0]);
47 fprint(2, " %S", argv[i]);
52 if(nmstack >= nelem(mstack)){
53 fprint(2, "%L: macro stack overflow:");
54 for(i=0; i<nmstack; i++)
55 fprint(2, " %S", mstack[i].argv[0]);
59 m = &mstack[nmstack++];
62 m->argv[i] = erunestrdup(argv[i]);
65 inputnotify(popmacro);
76 fprint(2, "%L: macro stack underflow\n");
80 for(i=0; i<m->argc; i++)
83 nr(L(".$"), mstack[nmstack-1].argc-1);
98 if(verbose) fprint(2, "outcb %p\n", outcb);
103 if(runmacro('.', 1, argv) >= 0){
104 inputnotify(popmacro1);
105 if(!setjmp(runjb[nrunjb++]))
108 if(verbose) fprint(2, "finished %S\n", name);
118 longjmp(runjb[--nrunjb], 1);
124 * "" means " inside " "
126 * \newline can be done
127 * argument separator is space (not tab)
128 * number register .$ = number of arguments
129 * no arguments outside macros or in strings
131 * arguments copied in copy mode
137 * processed output diverted
138 * dn dl registers vertical and horizontal size of last diversion
139 * .z - current diversion name
146 * .t register - distance to next trap
158 if(verbose) fprint(2, "trap: %S\n", trap0);
166 /* .wh - install trap */
168 r_wh(int argc, Rune **argv)
182 warn("not removing trap at %d", i);
187 trap0 = erunestrdup(argv[2]);
190 warn("not installing %S trap at %d", argv[2], i);
195 r_ch(int argc, Rune **argv)
200 if(trap0 && runestrcmp(argv[1], trap0) == 0){
205 warn("not removing %S trap", argv[1]);
212 trap0 = erunestrdup(argv[1]);
215 warn("not moving %S trap to %d", argv[1], i);
220 r_dt(int argc, Rune **argv)
224 warn("ignoring diversion trap");
227 /* define macro - .de, .am, .ig */
229 r_de(int argc, Rune **argv)
237 ignore = runestrcmp(argv[0], L("ig")) == 0;
239 runefmtstrinit(&fmt);
243 if(runestrcmp(argv[0], L("am")) == 0 && (p=getds(argv[1])) != nil)
244 fmtrunestrcpy(&fmt, p);
245 len = runestrlen(end);
246 while((p = readline(CopyMode)) != nil){
247 if(runestrncmp(p, end, len) == 0
248 && (p[len]==' ' || p[len]==0 || p[len]=='\t'
249 || (p[len]=='\\' && p[len+1]=='}'))){
254 fmtprint(&fmt, "%S\n", p);
257 warn("eof in %C%S %S - looking for %#Q", dot, argv[0], argv[1], end);
261 p = runefmtstrflush(&fmt);
263 sysfatal("out of memory");
268 /* define string .ds .as */
272 Rune *name, *line, *p;
275 line = readline(CopyMode);
276 if(name == nil || line == nil){
291 /* remove request, macro, or string */
293 r_rm(int argc, Rune **argv)
298 for(i=1; i<argc; i++){
305 /* .rn - rename request, macro, or string */
307 r_rn(int argc, Rune **argv)
310 renreq(argv[1], argv[2]);
311 renraw(argv[1], argv[2]);
312 ds(argv[2], getds(argv[1]));
316 /* .di - divert output to macro xx */
317 /* .da - divert, appending to macro */
318 /* page offsetting is not done! */
328 runefmtstrinit(&difmt);
330 fmtrune(&difmt, Uformatted);
339 if(ndi == 0 || difmtinit == 0)
341 fmtrune(&difmt, Uunformatted);
342 p = runefmtstrflush(&difmt);
343 memset(&difmt, 0, sizeof difmt);
346 warn("out of memory in diversion %C%S", dot, di[ndi-1]);
349 if(n > 0 && p[n-1] != '\n'){
350 p = runerealloc(p, n+2);
362 if(!difmtinit) abort();
370 r_di(int argc, Rune **argv)
374 warn("extra arguments to %C%S", dot, argv[0]);
378 /* warn("unmatched %C%S", dot, argv[0]); */
386 _nr(L(".z"), di[ndi-1]);
387 runefmtstrinit(&difmt);
388 fmtrune(&difmt, Uformatted);
393 /* start diversion */
394 /* various register state should be saved, but it's all useless to us */
397 sysfatal("%Cdi overflow", dot);
398 if(argv[0][1] == 'i')
400 _nr(L(".z"), argv[1]);
401 runestrcpy(di[ndi++], argv[1]);
402 runefmtstrinit(&difmt);
403 fmtrune(&difmt, Uformatted);
408 /* .wh - install trap */
409 /* .ch - change trap */
410 /* .dt - install diversion trap */
412 /* set input-line count trap */
418 r_it(int argc, Rune **argv)
424 itrapcount = eval(argv[1]);
426 itrapname = erunestrdup(argv[2]);
435 runmacro1(itrapname);
442 if(itrapcount > 0 && --itrapcount == 0)
446 /* .em - invoke macro when all input is over */
448 r_em(int argc, Rune **argv)
453 runesnprint(buf, nelem(buf), ".%S\n", argv[1]);
462 p = getds(getname());
471 if(inputmode&CopyMode)
479 if(inputmode&CopyMode)
487 if(inputmode&ArgMode)
504 if(c < '1' || c > '9'){
509 if(nmstack <= 0 || mstack[nmstack-1].argc <= c)
511 pushinputstring(mstack[nmstack-1].argv[c]);
518 addreq(L("de"), r_de, -1);
519 addreq(L("am"), r_de, -1);
520 addreq(L("ig"), r_de, -1);
521 addraw(L("ds"), r_ds);
522 addraw(L("as"), r_ds);
523 addreq(L("rm"), r_rm, -1);
524 addreq(L("rn"), r_rn, -1);
525 addreq(L("di"), r_di, -1);
526 addreq(L("da"), r_di, -1);
527 addreq(L("it"), r_it, -1);
528 addreq(L("em"), r_em, 1);
529 addreq(L("wh"), r_wh, -1);
530 addreq(L("ch"), r_ch, -1);
531 addreq(L("dt"), r_dt, -1);
533 addesc('$', e_dollar, CopyMode|ArgMode|HtmlMode);
534 addesc('*', e_star, CopyMode|ArgMode|HtmlMode);
535 addesc('t', e_t, CopyMode|ArgMode);
536 addesc('a', e_a, CopyMode|ArgMode);
537 addesc('\\', e_backslash, ArgMode|CopyMode);
538 addesc('.', e_dot, CopyMode|ArgMode);
540 ds(L("eof"), L(".sp 0.5i\n"));