Blob


1 /*
2 * 7. Macros, strings, diversion, and position traps.
3 *
4 * macros can override builtins
5 * builtins can be renamed or removed!
6 */
8 #include "a.h"
10 enum
11 {
12 MAXARG = 10,
13 MAXMSTACK = 40
14 };
16 /* macro invocation frame */
17 typedef struct Mac Mac;
18 struct Mac
19 {
20 int argc;
21 Rune *argv[MAXARG];
22 };
24 Mac mstack[MAXMSTACK];
25 int nmstack;
26 void emitdi(void);
27 void flushdi(void);
29 /*
30 * Run a user-defined macro.
31 */
32 void popmacro(void);
33 int
34 runmacro(int dot, int argc, Rune **argv)
35 {
36 Rune *p;
37 int i;
38 Mac *m;
40 if(verbose && isupperrune(argv[0][0])) fprint(2, "run: %S\n", argv[0]);
41 p = getds(argv[0]);
42 if(p == nil){
43 if(verbose)
44 warn("ignoring unknown request %C%S", dot, argv[0]);
45 if(verbose > 1){
46 for(i=0; i<argc; i++)
47 fprint(2, " %S", argv[i]);
48 fprint(2, "\n");
49 }
50 return -1;
51 }
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]);
56 fprint(2, "\n");
57 return -1;
58 }
59 m = &mstack[nmstack++];
60 m->argc = argc;
61 for(i=0; i<argc; i++)
62 m->argv[i] = erunestrdup(argv[i]);
63 pushinputstring(p);
64 nr(L(".$"), argc-1);
65 inputnotify(popmacro);
66 return 0;
67 }
69 void
70 popmacro(void)
71 {
72 int i;
73 Mac *m;
75 if(--nmstack < 0){
76 fprint(2, "%L: macro stack underflow\n");
77 return;
78 }
79 m = &mstack[nmstack];
80 for(i=0; i<m->argc; i++)
81 free(m->argv[i]);
82 if(nmstack > 0)
83 nr(L(".$"), mstack[nmstack-1].argc-1);
84 else
85 nr(L(".$"), 0);
86 }
88 void popmacro1(void);
89 jmp_buf runjb[10];
90 int nrunjb;
92 void
93 runmacro1(Rune *name)
94 {
95 Rune *argv[2];
96 int obol;
98 if(verbose) fprint(2, "outcb %p\n", outcb);
99 obol = bol;
100 argv[0] = name;
101 argv[1] = nil;
102 bol = 1;
103 if(runmacro('.', 1, argv) >= 0){
104 inputnotify(popmacro1);
105 if(!setjmp(runjb[nrunjb++]))
106 runinput();
107 else
108 if(verbose) fprint(2, "finished %S\n", name);
110 bol = obol;
113 void
114 popmacro1(void)
116 popmacro();
117 if(nrunjb >= 0)
118 longjmp(runjb[--nrunjb], 1);
121 /*
122 * macro arguments
124 * "" means " inside " "
125 * "" empty string
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
132 */
134 /*
135 * diversions
137 * processed output diverted
138 * dn dl registers vertical and horizontal size of last diversion
139 * .z - current diversion name
140 */
142 /*
143 * traps
145 * skip most
146 * .t register - distance to next trap
147 */
148 static Rune *trap0;
150 void
151 outtrap(void)
153 Rune *t;
155 if(outcb)
156 return;
157 if(trap0){
158 if(verbose) fprint(2, "trap: %S\n", trap0);
159 t = trap0;
160 trap0 = nil;
161 runmacro1(t);
162 free(t);
166 /* .wh - install trap */
167 void
168 r_wh(int argc, Rune **argv)
170 int i;
172 if(argc < 2)
173 return;
175 i = eval(argv[1]);
176 if(argc == 2){
177 if(i == 0){
178 free(trap0);
179 trap0 = nil;
180 }else
181 if(verbose)
182 warn("not removing trap at %d", i);
184 if(argc > 2){
185 if(i == 0){
186 free(trap0);
187 trap0 = erunestrdup(argv[2]);
188 }else
189 if(verbose)
190 warn("not installing %S trap at %d", argv[2], i);
194 void
195 r_ch(int argc, Rune **argv)
197 int i;
199 if(argc == 2){
200 if(trap0 && runestrcmp(argv[1], trap0) == 0){
201 free(trap0);
202 trap0 = nil;
203 }else
204 if(verbose)
205 warn("not removing %S trap", argv[1]);
206 return;
208 if(argc >= 3){
209 i = eval(argv[2]);
210 if(i == 0){
211 free(trap0);
212 trap0 = erunestrdup(argv[1]);
213 }else
214 if(verbose)
215 warn("not moving %S trap to %d", argv[1], i);
219 void
220 r_dt(int argc, Rune **argv)
222 USED(argc);
223 USED(argv);
224 warn("ignoring diversion trap");
227 /* define macro - .de, .am, .ig */
228 void
229 r_de(int argc, Rune **argv)
231 Rune *end, *p;
232 Fmt fmt;
233 int ignore, len;
235 delreq(argv[1]);
236 delraw(argv[1]);
237 ignore = runestrcmp(argv[0], L("ig")) == 0;
238 if(!ignore)
239 runefmtstrinit(&fmt);
240 end = L("..");
241 if(argc >= 3)
242 end = argv[2];
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]=='}'))){
250 free(p);
251 goto done;
253 if(!ignore)
254 fmtprint(&fmt, "%S\n", p);
255 free(p);
257 warn("eof in %C%S %S - looking for %#Q", dot, argv[0], argv[1], end);
258 done:
259 if(ignore)
260 return;
261 p = runefmtstrflush(&fmt);
262 if(p == nil)
263 sysfatal("out of memory");
264 ds(argv[1], p);
265 free(p);
268 /* define string .ds .as */
269 void
270 r_ds(Rune *cmd)
272 Rune *name, *line, *p;
274 name = copyarg();
275 line = readline(CopyMode);
276 if(name == nil || line == nil){
277 free(name);
278 return;
280 p = line;
281 if(*p == '"')
282 p++;
283 if(cmd[0] == 'd')
284 ds(name, p);
285 else
286 as(name, p);
287 free(name);
288 free(line);
291 /* remove request, macro, or string */
292 void
293 r_rm(int argc, Rune **argv)
295 int i;
297 emitdi();
298 for(i=1; i<argc; i++){
299 delreq(argv[i]);
300 delraw(argv[i]);
301 ds(argv[i], nil);
305 /* .rn - rename request, macro, or string */
306 void
307 r_rn(int argc, Rune **argv)
309 USED(argc);
310 renreq(argv[1], argv[2]);
311 renraw(argv[1], argv[2]);
312 ds(argv[2], getds(argv[1]));
313 ds(argv[1], nil);
316 /* .di - divert output to macro xx */
317 /* .da - divert, appending to macro */
318 /* page offsetting is not done! */
319 Fmt difmt;
320 int difmtinit;
321 Rune di[20][100];
322 int ndi;
324 void
325 emitdi(void)
327 flushdi();
328 runefmtstrinit(&difmt);
329 difmtinit = 1;
330 fmtrune(&difmt, Uformatted);
333 void
334 flushdi(void)
336 int n;
337 Rune *p;
339 if(ndi == 0 || difmtinit == 0)
340 return;
341 fmtrune(&difmt, Uunformatted);
342 p = runefmtstrflush(&difmt);
343 memset(&difmt, 0, sizeof difmt);
344 difmtinit = 0;
345 if(p == nil)
346 warn("out of memory in diversion %C%S", dot, di[ndi-1]);
347 else{
348 n = runestrlen(p);
349 if(n > 0 && p[n-1] != '\n'){
350 p = runerealloc(p, n+2);
351 p[n] = '\n';
352 p[n+1] = 0;
355 as(di[ndi-1], p);
356 free(p);
359 void
360 outdi(Rune r)
362 if(!difmtinit) abort();
363 if(r == Uempty)
364 return;
365 fmtrune(&difmt, r);
368 /* .di, .da */
369 void
370 r_di(int argc, Rune **argv)
372 br();
373 if(argc > 2)
374 warn("extra arguments to %C%S", dot, argv[0]);
375 if(argc == 1){
376 /* end diversion */
377 if(ndi <= 0){
378 /* warn("unmatched %C%S", dot, argv[0]); */
379 return;
381 flushdi();
382 if(--ndi == 0){
383 _nr(L(".z"), nil);
384 outcb = nil;
385 }else{
386 _nr(L(".z"), di[ndi-1]);
387 runefmtstrinit(&difmt);
388 fmtrune(&difmt, Uformatted);
389 difmtinit = 1;
391 return;
393 /* start diversion */
394 /* various register state should be saved, but it's all useless to us */
395 flushdi();
396 if(ndi >= nelem(di))
397 sysfatal("%Cdi overflow", dot);
398 if(argv[0][1] == 'i')
399 ds(argv[1], nil);
400 _nr(L(".z"), argv[1]);
401 runestrcpy(di[ndi++], argv[1]);
402 runefmtstrinit(&difmt);
403 fmtrune(&difmt, Uformatted);
404 difmtinit = 1;
405 outcb = outdi;
408 /* .wh - install trap */
409 /* .ch - change trap */
410 /* .dt - install diversion trap */
412 /* set input-line count trap */
413 int itrapcount;
414 int itrapwaiting;
415 Rune *itrapname;
417 void
418 r_it(int argc, Rune **argv)
420 if(argc < 3){
421 itrapcount = 0;
422 return;
424 itrapcount = eval(argv[1]);
425 free(itrapname);
426 itrapname = erunestrdup(argv[2]);
429 void
430 itrap(void)
432 itrapset();
433 if(itrapwaiting){
434 itrapwaiting = 0;
435 runmacro1(itrapname);
439 void
440 itrapset(void)
442 if(itrapcount > 0 && --itrapcount == 0)
443 itrapwaiting = 1;
446 /* .em - invoke macro when all input is over */
447 void
448 r_em(int argc, Rune **argv)
450 Rune buf[20];
452 USED(argc);
453 runesnprint(buf, nelem(buf), ".%S\n", argv[1]);
454 as(L("eof"), buf);
457 int
458 e_star(void)
460 Rune *p;
462 p = getds(getname());
463 if(p)
464 pushinputstring(p);
465 return 0;
468 int
469 e_t(void)
471 if(inputmode&CopyMode)
472 return '\t';
473 return 0;
476 int
477 e_a(void)
479 if(inputmode&CopyMode)
480 return '\a';
481 return 0;
484 int
485 e_backslash(void)
487 if(inputmode&ArgMode)
488 ungetrune('\\');
489 return backslash;
492 int
493 e_dot(void)
495 return '.';
498 int
499 e_dollar(void)
501 int c;
503 c = getnext();
504 if(c < '1' || c > '9'){
505 ungetnext(c);
506 return 0;
508 c -= '0';
509 if(nmstack <= 0 || mstack[nmstack-1].argc <= c)
510 return 0;
511 pushinputstring(mstack[nmstack-1].argv[c]);
512 return 0;
515 void
516 t7init(void)
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"));
541 ds(L(".."), L(""));