xref: /plan9/sys/src/cmd/htmlroff/t7.c (revision 426d2b71458df9b491ba6c167f699b3f1f7b0428)
1 /*
2  * 7.  Macros, strings, diversion, and position traps.
3  *
4  * 	macros can override builtins
5  *	builtins can be renamed or removed!
6  */
7 
8 #include "a.h"
9 
10 enum
11 {
12 	MAXARG = 10,
13 	MAXMSTACK = 40
14 };
15 
16 /* macro invocation frame */
17 typedef struct Mac Mac;
18 struct Mac
19 {
20 	int argc;
21 	Rune *argv[MAXARG];
22 };
23 
24 Mac		mstack[MAXMSTACK];
25 int		nmstack;
26 void		emitdi(void);
27 void		flushdi(void);
28 
29 /*
30  * Run a user-defined macro.
31  */
32 void popmacro(void);
33 int
runmacro(int dot,int argc,Rune ** argv)34 runmacro(int dot, int argc, Rune **argv)
35 {
36 	Rune *p;
37 	int i;
38 	Mac *m;
39 
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 }
68 
69 void
popmacro(void)70 popmacro(void)
71 {
72 	int i;
73 	Mac *m;
74 
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 }
87 
88 void popmacro1(void);
89 jmp_buf runjb[10];
90 int nrunjb;
91 
92 void
runmacro1(Rune * name)93 runmacro1(Rune *name)
94 {
95 	Rune *argv[2];
96 	int obol;
97 
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);
109 	}
110 	bol = obol;
111 }
112 
113 void
popmacro1(void)114 popmacro1(void)
115 {
116 	popmacro();
117 	if(nrunjb >= 0)
118 		longjmp(runjb[--nrunjb], 1);
119 }
120 
121 /*
122  * macro arguments
123  *
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
130  *
131  *	arguments copied in copy mode
132  */
133 
134 /*
135  * diversions
136  *
137  *	processed output diverted
138  *	dn dl registers vertical and horizontal size of last diversion
139  *	.z - current diversion name
140  */
141 
142 /*
143  * traps
144  *
145  *	skip most
146  *	.t register - distance to next trap
147  */
148 static Rune *trap0;
149 
150 void
outtrap(void)151 outtrap(void)
152 {
153 	Rune *t;
154 
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);
163 	}
164 }
165 
166 /* .wh - install trap */
167 void
r_wh(int argc,Rune ** argv)168 r_wh(int argc, Rune **argv)
169 {
170 	int i;
171 
172 	if(argc < 2)
173 		return;
174 
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);
183 	}
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);
191 	}
192 }
193 
194 void
r_ch(int argc,Rune ** argv)195 r_ch(int argc, Rune **argv)
196 {
197 	int i;
198 
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;
207 	}
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);
216 	}
217 }
218 
219 void
r_dt(int argc,Rune ** argv)220 r_dt(int argc, Rune **argv)
221 {
222 	USED(argc);
223 	USED(argv);
224 	warn("ignoring diversion trap");
225 }
226 
227 /* define macro - .de, .am, .ig */
228 void
r_de(int argc,Rune ** argv)229 r_de(int argc, Rune **argv)
230 {
231 	Rune *end, *p;
232 	Fmt fmt;
233 	int ignore, len;
234 
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;
252 		}
253 		if(!ignore)
254 			fmtprint(&fmt, "%S\n", p);
255 		free(p);
256 	}
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);
266 }
267 
268 /* define string .ds .as */
269 void
r_ds(Rune * cmd)270 r_ds(Rune *cmd)
271 {
272 	Rune *name, *line, *p;
273 
274 	name = copyarg();
275 	line = readline(CopyMode);
276 	if(name == nil || line == nil){
277 		free(name);
278 		return;
279 	}
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);
289 }
290 
291 /* remove request, macro, or string */
292 void
r_rm(int argc,Rune ** argv)293 r_rm(int argc, Rune **argv)
294 {
295 	int i;
296 
297 	emitdi();
298 	for(i=1; i<argc; i++){
299 		delreq(argv[i]);
300 		delraw(argv[i]);
301 		ds(argv[i], nil);
302 	}
303 }
304 
305 /* .rn - rename request, macro, or string */
306 void
r_rn(int argc,Rune ** argv)307 r_rn(int argc, Rune **argv)
308 {
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);
314 }
315 
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;
323 
324 void
emitdi(void)325 emitdi(void)
326 {
327 	flushdi();
328 	runefmtstrinit(&difmt);
329 	difmtinit = 1;
330 	fmtrune(&difmt, Uformatted);
331 }
332 
333 void
flushdi(void)334 flushdi(void)
335 {
336 	int n;
337 	Rune *p;
338 
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;
353 		}
354 	}
355 	as(di[ndi-1], p);
356 	free(p);
357 }
358 
359 void
outdi(Rune r)360 outdi(Rune r)
361 {
362 if(!difmtinit) abort();
363 	if(r == Uempty)
364 		return;
365 	fmtrune(&difmt, r);
366 }
367 
368 /* .di, .da */
369 void
r_di(int argc,Rune ** argv)370 r_di(int argc, Rune **argv)
371 {
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;
380 		}
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;
390 		}
391 		return;
392 	}
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;
406 }
407 
408 /* .wh - install trap */
409 /* .ch - change trap */
410 /* .dt - install diversion trap */
411 
412 /* set input-line count trap */
413 int itrapcount;
414 int itrapwaiting;
415 Rune *itrapname;
416 
417 void
r_it(int argc,Rune ** argv)418 r_it(int argc, Rune **argv)
419 {
420 	if(argc < 3){
421 		itrapcount = 0;
422 		return;
423 	}
424 	itrapcount = eval(argv[1]);
425 	free(itrapname);
426 	itrapname = erunestrdup(argv[2]);
427 }
428 
429 void
itrap(void)430 itrap(void)
431 {
432 	itrapset();
433 	if(itrapwaiting){
434 		itrapwaiting = 0;
435 		runmacro1(itrapname);
436 	}
437 }
438 
439 void
itrapset(void)440 itrapset(void)
441 {
442 	if(itrapcount > 0 && --itrapcount == 0)
443 		itrapwaiting = 1;
444 }
445 
446 /* .em - invoke macro when all input is over */
447 void
r_em(int argc,Rune ** argv)448 r_em(int argc, Rune **argv)
449 {
450 	Rune buf[20];
451 
452 	USED(argc);
453 	runesnprint(buf, nelem(buf), ".%S\n", argv[1]);
454 	as(L("eof"), buf);
455 }
456 
457 int
e_star(void)458 e_star(void)
459 {
460 	Rune *p;
461 
462 	p = getds(getname());
463 	if(p)
464 		pushinputstring(p);
465 	return 0;
466 }
467 
468 int
e_t(void)469 e_t(void)
470 {
471 	if(inputmode&CopyMode)
472 		return '\t';
473 	return 0;
474 }
475 
476 int
e_a(void)477 e_a(void)
478 {
479 	if(inputmode&CopyMode)
480 		return '\a';
481 	return 0;
482 }
483 
484 int
e_backslash(void)485 e_backslash(void)
486 {
487 	if(inputmode&ArgMode)
488 		ungetrune('\\');
489 	return backslash;
490 }
491 
492 int
e_dot(void)493 e_dot(void)
494 {
495 	return '.';
496 }
497 
498 int
e_dollar(void)499 e_dollar(void)
500 {
501 	int c;
502 
503 	c = getnext();
504 	if(c < '1' || c > '9'){
505 		ungetnext(c);
506 		return 0;
507 	}
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;
513 }
514 
515 void
t7init(void)516 t7init(void)
517 {
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);
532 
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);
539 
540 	ds(L("eof"), L(".sp 0.5i\n"));
541 	ds(L(".."), L(""));
542 }
543 
544