plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

t7.c (8615B)


      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
     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
     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
     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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    325 emitdi(void)
    326 {
    327 	flushdi();
    328 	runefmtstrinit(&difmt);
    329 	difmtinit = 1;
    330 	fmtrune(&difmt, Uformatted);
    331 }
    332 
    333 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
    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
    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
    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
    430 itrap(void)
    431 {
    432 	itrapset();
    433 	if(itrapwaiting){
    434 		itrapwaiting = 0;
    435 		runmacro1(itrapname);
    436 	}
    437 }
    438 
    439 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
    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
    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
    469 e_t(void)
    470 {
    471 	if(inputmode&CopyMode)
    472 		return '\t';
    473 	return 0;
    474 }
    475 
    476 int
    477 e_a(void)
    478 {
    479 	if(inputmode&CopyMode)
    480 		return '\a';
    481 	return 0;
    482 }
    483 
    484 int
    485 e_backslash(void)
    486 {
    487 	if(inputmode&ArgMode)
    488 		ungetrune('\\');
    489 	return backslash;
    490 }
    491 
    492 int
    493 e_dot(void)
    494 {
    495 	return '.';
    496 }
    497 
    498 int
    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
    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 }