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 }