roff.c (10822B)
1 #include "a.h" 2 3 enum 4 { 5 MAXREQ = 100, 6 MAXRAW = 40, 7 MAXESC = 60, 8 MAXLINE = 1024, 9 MAXIF = 20, 10 MAXARG = 10 11 }; 12 13 typedef struct Esc Esc; 14 typedef struct Req Req; 15 typedef struct Raw Raw; 16 17 /* escape sequence handler, like for \c */ 18 struct Esc 19 { 20 Rune r; 21 int (*f)(void); 22 int mode; 23 }; 24 25 /* raw request handler, like for .ie */ 26 struct Raw 27 { 28 Rune *name; 29 void (*f)(Rune*); 30 }; 31 32 /* regular request handler, like for .ft */ 33 struct Req 34 { 35 int argc; 36 Rune *name; 37 void (*f)(int, Rune**); 38 }; 39 40 int dot = '.'; 41 int tick = '\''; 42 int backslash = '\\'; 43 44 int inputmode; 45 Req req[MAXREQ]; 46 int nreq; 47 Raw raw[MAXRAW]; 48 int nraw; 49 Esc esc[MAXESC]; 50 int nesc; 51 int iftrue[MAXIF]; 52 int niftrue; 53 54 int isoutput; 55 int linepos; 56 57 58 void 59 addraw(Rune *name, void (*f)(Rune*)) 60 { 61 Raw *r; 62 63 if(nraw >= nelem(raw)){ 64 fprint(2, "too many raw requets\n"); 65 return; 66 } 67 r = &raw[nraw++]; 68 r->name = erunestrdup(name); 69 r->f = f; 70 } 71 72 void 73 delraw(Rune *name) 74 { 75 int i; 76 77 for(i=0; i<nraw; i++){ 78 if(runestrcmp(raw[i].name, name) == 0){ 79 if(i != --nraw){ 80 free(raw[i].name); 81 raw[i] = raw[nraw]; 82 } 83 return; 84 } 85 } 86 } 87 88 void 89 renraw(Rune *from, Rune *to) 90 { 91 int i; 92 93 delraw(to); 94 for(i=0; i<nraw; i++) 95 if(runestrcmp(raw[i].name, from) == 0){ 96 free(raw[i].name); 97 raw[i].name = erunestrdup(to); 98 return; 99 } 100 } 101 102 103 void 104 addreq(Rune *s, void (*f)(int, Rune**), int argc) 105 { 106 Req *r; 107 108 if(nreq >= nelem(req)){ 109 fprint(2, "too many requests\n"); 110 return; 111 } 112 r = &req[nreq++]; 113 r->name = erunestrdup(s); 114 r->f = f; 115 r->argc = argc; 116 } 117 118 void 119 delreq(Rune *name) 120 { 121 int i; 122 123 for(i=0; i<nreq; i++){ 124 if(runestrcmp(req[i].name, name) == 0){ 125 if(i != --nreq){ 126 free(req[i].name); 127 req[i] = req[nreq]; 128 } 129 return; 130 } 131 } 132 } 133 134 void 135 renreq(Rune *from, Rune *to) 136 { 137 int i; 138 139 delreq(to); 140 for(i=0; i<nreq; i++) 141 if(runestrcmp(req[i].name, from) == 0){ 142 free(req[i].name); 143 req[i].name = erunestrdup(to); 144 return; 145 } 146 } 147 148 void 149 addesc(Rune r, int (*f)(void), int mode) 150 { 151 Esc *e; 152 153 if(nesc >= nelem(esc)){ 154 fprint(2, "too many escapes\n"); 155 return; 156 } 157 e = &esc[nesc++]; 158 e->r = r; 159 e->f = f; 160 e->mode = mode; 161 } 162 163 /* 164 * Get the next logical character in the input stream. 165 */ 166 int 167 getnext(void) 168 { 169 int i, r; 170 171 next: 172 r = getrune(); 173 if(r < 0) 174 return -1; 175 if(r == Uformatted){ 176 br(); 177 assert(!isoutput); 178 while((r = getrune()) >= 0 && r != Uunformatted){ 179 if(r == Uformatted) 180 continue; 181 outrune(r); 182 } 183 goto next; 184 } 185 if(r == Uunformatted) 186 goto next; 187 if(r == backslash){ 188 r = getrune(); 189 if(r < 0) 190 return -1; 191 for(i=0; i<nesc; i++){ 192 if(r == esc[i].r && (inputmode&esc[i].mode)==inputmode){ 193 if(esc[i].f == e_warn) 194 warn("ignoring %C%C", backslash, r); 195 r = esc[i].f(); 196 if(r <= 0) 197 goto next; 198 return r; 199 } 200 } 201 if(inputmode&(ArgMode|CopyMode)){ 202 ungetrune(r); 203 r = backslash; 204 } 205 } 206 return r; 207 } 208 209 void 210 ungetnext(Rune r) 211 { 212 /* 213 * really we want to undo the getrunes that led us here, 214 * since the call after ungetnext might be getrune! 215 */ 216 ungetrune(r); 217 } 218 219 int 220 _readx(Rune *p, int n, int nmode, int line) 221 { 222 int c, omode; 223 Rune *e; 224 225 while((c = getrune()) == ' ' || c == '\t') 226 ; 227 ungetrune(c); 228 omode = inputmode; 229 inputmode = nmode; 230 e = p+n-1; 231 for(c=getnext(); p<e; c=getnext()){ 232 if(c < 0) 233 break; 234 if(!line && (c == ' ' || c == '\t')) 235 break; 236 if(c == '\n'){ 237 if(!line) 238 ungetnext(c); 239 break; 240 } 241 *p++ = c; 242 } 243 inputmode = omode; 244 *p = 0; 245 if(c < 0) 246 return -1; 247 return 0; 248 } 249 250 /* 251 * Get the next argument from the current line. 252 */ 253 Rune* 254 copyarg(void) 255 { 256 static Rune buf[MaxLine]; 257 int c; 258 Rune *r; 259 260 if(_readx(buf, MaxLine, ArgMode, 0) < 0) 261 return nil; 262 r = runestrstr(buf, L("\\\"")); 263 if(r){ 264 *r = 0; 265 while((c = getrune()) >= 0 && c != '\n') 266 ; 267 ungetrune('\n'); 268 } 269 r = erunestrdup(buf); 270 return r; 271 } 272 273 /* 274 * Read the current line in given mode. Newline not kept. 275 * Uses different buffer from copyarg! 276 */ 277 Rune* 278 readline(int m) 279 { 280 static Rune buf[MaxLine]; 281 Rune *r; 282 283 if(_readx(buf, MaxLine, m, 1) < 0) 284 return nil; 285 r = erunestrdup(buf); 286 return r; 287 } 288 289 /* 290 * Given the argument line (already read in copy+arg mode), 291 * parse into arguments. Note that \" has been left in place 292 * during copy+arg mode parsing, so comments still need to be stripped. 293 */ 294 int 295 parseargs(Rune *p, Rune **argv) 296 { 297 int argc; 298 Rune *w; 299 300 for(argc=0; argc<MAXARG; argc++){ 301 while(*p == ' ' || *p == '\t') 302 p++; 303 if(*p == 0) 304 break; 305 argv[argc] = p; 306 if(*p == '"'){ 307 /* quoted argument */ 308 if(*(p+1) == '"'){ 309 /* empty argument */ 310 *p = 0; 311 p += 2; 312 }else{ 313 /* parse quoted string */ 314 w = p++; 315 for(; *p; p++){ 316 if(*p == '"' && *(p+1) == '"') 317 *w++ = '"'; 318 else if(*p == '"'){ 319 p++; 320 break; 321 }else 322 *w++ = *p; 323 } 324 *w = 0; 325 } 326 }else{ 327 /* unquoted argument - need to watch out for \" comment */ 328 for(; *p; p++){ 329 if(*p == ' ' || *p == '\t'){ 330 *p++ = 0; 331 break; 332 } 333 if(*p == '\\' && *(p+1) == '"'){ 334 *p = 0; 335 if(p != argv[argc]) 336 argc++; 337 return argc; 338 } 339 } 340 } 341 } 342 return argc; 343 } 344 345 /* 346 * Process a dot line. The dot has been read. 347 */ 348 void 349 dotline(int dot) 350 { 351 int argc, i; 352 Rune *a, *argv[1+MAXARG]; 353 354 /* 355 * Read request/macro name 356 */ 357 a = copyarg(); 358 if(a == nil || a[0] == 0){ 359 free(a); 360 getrune(); /* \n */ 361 return; 362 } 363 argv[0] = a; 364 /* 365 * Check for .if, .ie, and others with special parsing. 366 */ 367 for(i=0; i<nraw; i++){ 368 if(runestrcmp(raw[i].name, a) == 0){ 369 raw[i].f(raw[i].name); 370 free(a); 371 return; 372 } 373 } 374 375 /* 376 * Read rest of line in copy mode, invoke regular request. 377 */ 378 a = readline(ArgMode); 379 if(a == nil){ 380 free(argv[0]); 381 return; 382 } 383 argc = 1+parseargs(a, argv+1); 384 for(i=0; i<nreq; i++){ 385 if(runestrcmp(req[i].name, argv[0]) == 0){ 386 if(req[i].argc != -1){ 387 if(argc < 1+req[i].argc){ 388 warn("not enough arguments for %C%S", dot, req[i].name); 389 free(argv[0]); 390 free(a); 391 return; 392 } 393 if(argc > 1+req[i].argc) 394 warn("too many arguments for %C%S", dot, req[i].name); 395 } 396 req[i].f(argc, argv); 397 free(argv[0]); 398 free(a); 399 return; 400 } 401 } 402 403 /* 404 * Invoke user-defined macros. 405 */ 406 runmacro(dot, argc, argv); 407 free(argv[0]); 408 free(a); 409 } 410 411 /* 412 * newlines are magical in various ways. 413 */ 414 int bol; 415 void 416 newline(void) 417 { 418 int n; 419 420 if(bol) 421 sp(eval(L("1v"))); 422 bol = 1; 423 if((n=getnr(L(".ce"))) > 0){ 424 nr(L(".ce"), n-1); 425 br(); 426 } 427 if(getnr(L(".fi")) == 0) 428 br(); 429 outrune('\n'); 430 } 431 432 void 433 startoutput(void) 434 { 435 char *align; 436 double ps, vs, lm, rm, ti; 437 Rune buf[200]; 438 439 if(isoutput) 440 return; 441 isoutput = 1; 442 443 if(getnr(L(".paragraph")) == 0) 444 return; 445 446 nr(L(".ns"), 0); 447 isoutput = 1; 448 ps = getnr(L(".s")); 449 if(ps <= 1) 450 ps = 10; 451 ps /= 72.0; 452 USED(ps); 453 454 vs = getnr(L(".v"))*getnr(L(".ls")) * 1.0/UPI; 455 vs /= (10.0/72.0); /* ps */ 456 if(vs == 0) 457 vs = 1.2; 458 459 lm = (getnr(L(".o"))+getnr(L(".i"))) * 1.0/UPI; 460 ti = getnr(L(".ti")) * 1.0/UPI; 461 nr(L(".ti"), 0); 462 463 rm = 8.0 - getnr(L(".l"))*1.0/UPI - getnr(L(".o"))*1.0/UPI; 464 if(rm < 0) 465 rm = 0; 466 switch(getnr(L(".j"))){ 467 default: 468 case 0: 469 align = "left"; 470 break; 471 case 1: 472 align = "justify"; 473 break; 474 case 3: 475 align = "center"; 476 break; 477 case 5: 478 align = "right"; 479 break; 480 } 481 if(getnr(L(".ce"))) 482 align = "center"; 483 if(!getnr(L(".margin"))) 484 runesnprint(buf, nelem(buf), "<p style=\"line-height: %.1fem; text-indent: %.2fin; margin-top: 0; margin-bottom: 0; text-align: %s;\">\n", 485 vs, ti, align); 486 else 487 runesnprint(buf, nelem(buf), "<p style=\"line-height: %.1fem; margin-left: %.2fin; text-indent: %.2fin; margin-right: %.2fin; margin-top: 0; margin-bottom: 0; text-align: %s;\">\n", 488 vs, lm, ti, rm, align); 489 outhtml(buf); 490 } 491 void 492 br(void) 493 { 494 if(!isoutput) 495 return; 496 isoutput = 0; 497 498 nr(L(".dv"), 0); 499 dv(0); 500 hideihtml(); 501 if(getnr(L(".paragraph"))) 502 outhtml(L("</p>")); 503 } 504 505 void 506 r_margin(int argc, Rune **argv) 507 { 508 USED(argc); 509 510 nr(L(".margin"), eval(argv[1])); 511 } 512 513 int inrequest; 514 void 515 runinput(void) 516 { 517 int c; 518 519 bol = 1; 520 for(;;){ 521 c = getnext(); 522 if(c < 0) 523 break; 524 if((c == dot || c == tick) && bol){ 525 inrequest = 1; 526 dotline(c); 527 bol = 1; 528 inrequest = 0; 529 }else if(c == '\n'){ 530 newline(); 531 itrap(); 532 linepos = 0; 533 }else{ 534 outtrap(); 535 startoutput(); 536 showihtml(); 537 if(c == '\t'){ 538 /* XXX do better */ 539 outrune(' '); 540 while(++linepos%4) 541 outrune(' '); 542 }else{ 543 outrune(c); 544 linepos++; 545 } 546 bol = 0; 547 } 548 } 549 } 550 551 void 552 run(void) 553 { 554 t1init(); 555 t2init(); 556 t3init(); 557 t4init(); 558 t5init(); 559 t6init(); 560 t7init(); 561 t8init(); 562 /* t9init(); t9.c */ 563 t10init(); 564 t11init(); 565 /* t12init(); t12.c */ 566 t13init(); 567 t14init(); 568 t15init(); 569 t16init(); 570 t17init(); 571 t18init(); 572 t19init(); 573 t20init(); 574 htmlinit(); 575 hideihtml(); 576 577 addreq(L("margin"), r_margin, 1); 578 nr(L(".margin"), 1); 579 nr(L(".paragraph"), 1); 580 581 runinput(); 582 while(popinput()) 583 ; 584 dot = '.'; 585 if(verbose) 586 fprint(2, "eof\n"); 587 runmacro1(L("eof")); 588 closehtml(); 589 } 590 591 void 592 out(Rune *s) 593 { 594 if(s == nil) 595 return; 596 for(; *s; s++) 597 outrune(*s); 598 } 599 600 void (*outcb)(Rune); 601 602 void 603 inroman(Rune r) 604 { 605 int f; 606 607 f = getnr(L(".f")); 608 nr(L(".f"), 1); 609 runmacro1(L("font")); 610 outrune(r); 611 nr(L(".f"), f); 612 runmacro1(L("font")); 613 } 614 615 void 616 Brune(Rune r) 617 { 618 if(r == '&') 619 Bprint(&bout, "&"); 620 else if(r == '<') 621 Bprint(&bout, "<"); 622 else if(r == '>') 623 Bprint(&bout, ">"); 624 else if(r < Runeself || utf8) 625 Bprint(&bout, "%C", r); 626 else 627 Bprint(&bout, "%S", rune2html(r)); 628 } 629 630 void 631 outhtml(Rune *s) 632 { 633 Rune r; 634 635 for(; *s; s++){ 636 switch(r = *s){ 637 case '<': 638 r = Ult; 639 break; 640 case '>': 641 r = Ugt; 642 break; 643 case '&': 644 r = Uamp; 645 break; 646 case ' ': 647 r = Uspace; 648 break; 649 } 650 outrune(r); 651 } 652 } 653 654 void 655 outrune(Rune r) 656 { 657 switch(r){ 658 case ' ': 659 if(getnr(L(".fi")) == 0) 660 r = Unbsp; 661 break; 662 case Uformatted: 663 case Uunformatted: 664 abort(); 665 } 666 if(outcb){ 667 if(r == ' ') 668 r = Uspace; 669 outcb(r); 670 return; 671 } 672 /* writing to bout */ 673 switch(r){ 674 case Uempty: 675 return; 676 case Upl: 677 inroman('+'); 678 return; 679 case Ueq: 680 inroman('='); 681 return; 682 case Umi: 683 inroman(0x2212); 684 return; 685 case Utick: 686 r = '\''; 687 break; 688 case Ubtick: 689 r = '`'; 690 break; 691 case Uminus: 692 r = '-'; 693 break; 694 case '\'': 695 Bprint(&bout, "’"); 696 return; 697 case '`': 698 Bprint(&bout, "‘"); 699 return; 700 case Uamp: 701 Bputrune(&bout, '&'); 702 return; 703 case Ult: 704 Bputrune(&bout, '<'); 705 return; 706 case Ugt: 707 Bputrune(&bout, '>'); 708 return; 709 case Uspace: 710 Bputrune(&bout, ' '); 711 return; 712 case 0x2032: 713 /* 714 * In Firefox, at least, the prime is not 715 * a superscript by default. 716 */ 717 Bprint(&bout, "<sup>"); 718 Brune(r); 719 Bprint(&bout, "</sup>"); 720 return; 721 } 722 Brune(r); 723 } 724 725 void 726 r_nop(int argc, Rune **argv) 727 { 728 USED(argc); 729 USED(argv); 730 } 731 732 void 733 r_warn(int argc, Rune **argv) 734 { 735 USED(argc); 736 warn("ignoring %C%S", dot, argv[0]); 737 } 738 739 int 740 e_warn(void) 741 { 742 /* dispatch loop prints a warning for us */ 743 return 0; 744 } 745 746 int 747 e_nop(void) 748 { 749 return 0; 750 }