1 /* $OpenBSD: deroff.c,v 1.7 2005/07/05 18:27:41 jmc Exp $ */ 2 3 /*- 4 * Copyright (c) 1988, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 /* 32 * Copyright (C) Caldera International Inc. 2001-2002. 33 * All rights reserved. 34 * 35 * Redistribution and use in source and binary forms, with or without 36 * modification, are permitted provided that the following conditions 37 * are met: 38 * 1. Redistributions of source code and documentation must retain the above 39 * copyright notice, this list of conditions and the following disclaimer. 40 * 2. Redistributions in binary form must reproduce the above copyright 41 * notice, this list of conditions and the following disclaimer in the 42 * documentation and/or other materials provided with the distribution. 43 * 3. All advertising materials mentioning features or use of this software 44 * must display the following acknowledgement: 45 * This product includes software developed or owned by Caldera 46 * International, Inc. 47 * 4. Neither the name of Caldera International, Inc. nor the names of other 48 * contributors may be used to endorse or promote products derived from 49 * this software without specific prior written permission. 50 * 51 * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA 52 * INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR 53 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 54 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 55 * IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE FOR ANY DIRECT, 56 * INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 57 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 58 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 59 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 60 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 61 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 62 * POSSIBILITY OF SUCH DAMAGE. 63 */ 64 65 #ifndef lint 66 static const char copyright[] = 67 "@(#) Copyright (c) 1988, 1993\n\ 68 The Regents of the University of California. All rights reserved.\n"; 69 #endif /* not lint */ 70 71 #ifndef lint 72 #if 0 73 static const char sccsid[] = "@(#)deroff.c 8.1 (Berkeley) 6/6/93"; 74 #else 75 static const char rcsid[] = "$OpenBSD: deroff.c,v 1.7 2005/07/05 18:27:41 jmc Exp $"; 76 #endif 77 #endif /* not lint */ 78 79 #include <err.h> 80 #include <limits.h> 81 #include <stdio.h> 82 #include <stdlib.h> 83 #include <string.h> 84 #include <unistd.h> 85 86 /* 87 * Deroff command -- strip troff, eqn, and Tbl sequences from 88 * a file. Has two flags argument, -w, to cause output one word per line 89 * rather than in the original format. 90 * -mm (or -ms) causes the corresponding macro's to be interpreted 91 * so that just sentences are output 92 * -ml also gets rid of lists. 93 * Deroff follows .so and .nx commands, removes contents of macro 94 * definitions, equations (both .EQ ... .EN and $...$), 95 * Tbl command sequences, and Troff backslash constructions. 96 * 97 * All input is through the Cget macro; 98 * the most recently read character is in c. 99 * 100 * Modified by Robert Henry to process -me and -man macros. 101 */ 102 103 #define Cget ( (c=getc(infile)) == EOF ? eof() : ((c==ldelim)&&(filesp==files) ? skeqn() : c) ) 104 #define C1get ( (c=getc(infile)) == EOF ? eof() : c) 105 106 #ifdef DEBUG 107 # define C _C() 108 # define C1 _C1() 109 #else /* not DEBUG */ 110 # define C Cget 111 # define C1 C1get 112 #endif /* not DEBUG */ 113 114 #define SKIP while (C != '\n') 115 #define SKIP_TO_COM SKIP; SKIP; pc=c; while (C != '.' || pc != '\n' || C > 'Z')pc=c 116 117 #define YES 1 118 #define NO 0 119 #define MS 0 /* -ms */ 120 #define MM 1 /* -mm */ 121 #define ME 2 /* -me */ 122 #define MA 3 /* -man */ 123 124 #ifdef DEBUG 125 char *mactab[] = { "-ms", "-mm", "-me", "-ma" }; 126 #endif /* DEBUG */ 127 128 #define ONE 1 129 #define TWO 2 130 131 #define NOCHAR -2 132 #define SPECIAL 0 133 #define APOS 1 134 #define PUNCT 2 135 #define DIGIT 3 136 #define LETTER 4 137 138 #define MAXFILES 20 139 140 int iflag; 141 int wordflag; 142 int msflag; /* processing a source written using a mac package */ 143 int mac; /* which package */ 144 int disp; 145 int parag; 146 int inmacro; 147 int intable; 148 int keepblock; /* keep blocks of text; normally false when msflag */ 149 150 char chars[128]; /* SPECIAL, PUNCT, APOS, DIGIT, or LETTER */ 151 152 char line[LINE_MAX]; 153 char *lp; 154 155 int c; 156 int pc; 157 int ldelim; 158 int rdelim; 159 160 char fname[PATH_MAX]; 161 FILE *files[MAXFILES]; 162 FILE **filesp; 163 FILE *infile; 164 165 int argc; 166 char **argv; 167 168 /* 169 * Macro processing 170 * 171 * Macro table definitions 172 */ 173 typedef int pacmac; /* compressed macro name */ 174 int argconcat = 0; /* concat arguments together (-me only) */ 175 176 #define tomac(c1, c2) ((((c1) & 0xFF) << 8) | ((c2) & 0xFF)) 177 #define frommac(src, c1, c2) (((c1)=((src)>>8)&0xFF),((c2) =(src)&0xFF)) 178 179 struct mactab{ 180 int condition; 181 pacmac macname; 182 int (*func)(); /* XXX - args */ 183 }; 184 185 struct mactab troffmactab[]; 186 struct mactab ppmactab[]; 187 struct mactab msmactab[]; 188 struct mactab mmmactab[]; 189 struct mactab memactab[]; 190 struct mactab manmactab[]; 191 192 /* 193 * Macro table initialization 194 */ 195 #define M(cond, c1, c2, func) {cond, tomac(c1, c2), func} 196 197 /* 198 * Flags for matching conditions other than 199 * the macro name 200 */ 201 #define NONE 0 202 #define FNEST 1 /* no nested files */ 203 #define NOMAC 2 /* no macro */ 204 #define MAC 3 /* macro */ 205 #define PARAG 4 /* in a paragraph */ 206 #define MSF 5 /* msflag is on */ 207 #define NBLK 6 /* set if no blocks to be kept */ 208 209 /* 210 * Return codes from macro minions, determine where to jump, 211 * how to repeat/reprocess text 212 */ 213 #define COMX 1 /* goto comx */ 214 #define COM 2 /* goto com */ 215 216 int skeqn(void); 217 int eof(void); 218 int _C1(void); 219 int _C(void); 220 int EQ(void); 221 int domacro(void); 222 int PS(void); 223 int skip(void); 224 int intbl(void); 225 int outtbl(void); 226 int so(void); 227 int nx(void); 228 int skiptocom(void); 229 int PP(pacmac); 230 int AU(void); 231 int SH(pacmac); 232 int UX(void); 233 int MMHU(pacmac); 234 int mesnblock(pacmac); 235 int mssnblock(pacmac); 236 int nf(void); 237 int ce(void); 238 int meip(pacmac); 239 int mepp(pacmac); 240 int mesh(pacmac); 241 int mefont(pacmac); 242 int manfont(pacmac); 243 int manpp(pacmac); 244 int macsort(const void *, const void *); 245 int sizetab(struct mactab *); 246 void getfname(void); 247 void textline(char *, int); 248 void work(void); 249 void regline(void (*)(char *, int), int); 250 void macro(void); 251 void tbl(void); 252 void stbl(void); 253 void eqn(void); 254 void backsl(void); 255 void sce(void); 256 void refer(int); 257 void inpic(void); 258 void msputmac(char *, int); 259 void msputwords(int); 260 void meputmac(char *, int); 261 void meputwords(int); 262 void noblock(char, char); 263 void defcomline(pacmac); 264 void comline(void); 265 void buildtab(struct mactab **, int *); 266 FILE *opn(char *); 267 struct mactab *macfill(struct mactab *, struct mactab *); 268 __dead void usage(void); 269 270 int 271 main(int ac, char **av) 272 { 273 int i, ch; 274 int errflg = 0; 275 int kflag = NO; 276 277 iflag = NO; 278 wordflag = NO; 279 msflag = NO; 280 mac = ME; 281 disp = NO; 282 parag = NO; 283 inmacro = NO; 284 intable = NO; 285 ldelim = NOCHAR; 286 rdelim = NOCHAR; 287 keepblock = YES; 288 289 while ((ch = getopt(ac, av, "ikpwm:")) != -1) { 290 switch (ch) { 291 case 'i': 292 iflag = YES; 293 break; 294 case 'k': 295 kflag = YES; 296 break; 297 case 'm': 298 msflag = YES; 299 keepblock = NO; 300 switch (optarg[0]) { 301 case 'm': 302 mac = MM; 303 break; 304 case 's': 305 mac = MS; 306 break; 307 case 'e': 308 mac = ME; 309 break; 310 case 'a': 311 mac = MA; 312 break; 313 case 'l': 314 disp = YES; 315 break; 316 default: 317 errflg++; 318 break; 319 } 320 if (errflg == 0 && optarg[1] != '\0') 321 errflg++; 322 break; 323 case 'p': 324 parag = YES; 325 break; 326 case 'w': 327 wordflag = YES; 328 kflag = YES; 329 break; 330 default: 331 errflg++; 332 } 333 } 334 argc = ac - optind; 335 argv = av + optind; 336 337 if (kflag) 338 keepblock = YES; 339 if (errflg) 340 usage(); 341 342 #ifdef DEBUG 343 printf("msflag = %d, mac = %s, keepblock = %d, disp = %d\n", 344 msflag, mactab[mac], keepblock, disp); 345 #endif /* DEBUG */ 346 if (argc == 0) { 347 infile = stdin; 348 } else { 349 infile = opn(argv[0]); 350 --argc; 351 ++argv; 352 } 353 files[0] = infile; 354 filesp = &files[0]; 355 356 for (i = 'a'; i <= 'z' ; ++i) 357 chars[i] = LETTER; 358 for (i = 'A'; i <= 'Z'; ++i) 359 chars[i] = LETTER; 360 for (i = '0'; i <= '9'; ++i) 361 chars[i] = DIGIT; 362 chars['\''] = APOS; 363 chars['&'] = APOS; 364 chars['.'] = PUNCT; 365 chars[','] = PUNCT; 366 chars[';'] = PUNCT; 367 chars['?'] = PUNCT; 368 chars[':'] = PUNCT; 369 work(); 370 exit(0); 371 } 372 373 int 374 skeqn(void) 375 { 376 377 while ((c = getc(infile)) != rdelim) { 378 if (c == EOF) 379 c = eof(); 380 else if (c == '"') { 381 while ((c = getc(infile)) != '"') { 382 if (c == EOF || 383 (c == '\\' && (c = getc(infile)) == EOF)) 384 c = eof(); 385 } 386 } 387 } 388 if (msflag) 389 return((c = 'x')); 390 return((c = ' ')); 391 } 392 393 FILE * 394 opn(char *p) 395 { 396 FILE *fd; 397 398 if ((fd = fopen(p, "r")) == NULL) 399 err(1, "fopen %s", p); 400 401 return(fd); 402 } 403 404 int 405 eof(void) 406 { 407 408 if (infile != stdin) 409 fclose(infile); 410 if (filesp > files) 411 infile = *--filesp; 412 else if (argc > 0) { 413 infile = opn(argv[0]); 414 --argc; 415 ++argv; 416 } else 417 exit(0); 418 return(C); 419 } 420 421 void 422 getfname(void) 423 { 424 char *p; 425 struct chain { 426 struct chain *nextp; 427 char *datap; 428 } *q; 429 static struct chain *namechain= NULL; 430 431 while (C == ' ') 432 ; /* nothing */ 433 434 for (p = fname ; p - fname < sizeof(fname) && (*p = c) != '\n' && 435 c != ' ' && c != '\t' && c != '\\'; ++p) 436 C; 437 *p = '\0'; 438 while (c != '\n') 439 C; 440 441 /* see if this name has already been used */ 442 for (q = namechain ; q; q = q->nextp) 443 if (strcmp(fname, q->datap) == 0) { 444 fname[0] = '\0'; 445 return; 446 } 447 448 q = (struct chain *) malloc(sizeof(struct chain)); 449 if (q == NULL) 450 err(1, NULL); 451 q->nextp = namechain; 452 q->datap = strdup(fname); 453 if (q->datap == NULL) 454 err(1, NULL); 455 namechain = q; 456 } 457 458 /*ARGSUSED*/ 459 void 460 textline(char *str, int constant) 461 { 462 463 if (wordflag) { 464 msputwords(0); 465 return; 466 } 467 puts(str); 468 } 469 470 void 471 work(void) 472 { 473 474 for (;;) { 475 C; 476 #ifdef FULLDEBUG 477 printf("Starting work with `%c'\n", c); 478 #endif /* FULLDEBUG */ 479 if (c == '.' || c == '\'') 480 comline(); 481 else 482 regline(textline, TWO); 483 } 484 } 485 486 void 487 regline(void (*pfunc)(char *, int), int constant) 488 { 489 490 line[0] = c; 491 lp = line; 492 while (lp - line < sizeof(line)) { 493 if (c == '\\') { 494 *lp = ' '; 495 backsl(); 496 } 497 if (c == '\n') 498 break; 499 if (intable && c == 'T') { 500 *++lp = C; 501 if (c == '{' || c == '}') { 502 lp[-1] = ' '; 503 *lp = C; 504 } 505 } else { 506 *++lp = C; 507 } 508 } 509 *lp = '\0'; 510 511 if (line[0] != '\0') 512 (*pfunc)(line, constant); 513 } 514 515 void 516 macro(void) 517 { 518 519 if (msflag) { 520 do { 521 SKIP; 522 } while (C!='.' || C!='.' || C=='.'); /* look for .. */ 523 if (c != '\n') 524 SKIP; 525 return; 526 } 527 SKIP; 528 inmacro = YES; 529 } 530 531 void 532 tbl(void) 533 { 534 535 while (C != '.') 536 ; /* nothing */ 537 SKIP; 538 intable = YES; 539 } 540 541 void 542 stbl(void) 543 { 544 545 while (C != '.') 546 ; /* nothing */ 547 SKIP_TO_COM; 548 if (c != 'T' || C != 'E') { 549 SKIP; 550 pc = c; 551 while (C != '.' || pc != '\n' || C != 'T' || C != 'E') 552 pc = c; 553 } 554 } 555 556 void 557 eqn(void) 558 { 559 int c1, c2; 560 int dflg; 561 char last; 562 563 last=0; 564 dflg = 1; 565 SKIP; 566 567 for (;;) { 568 if (C1 == '.' || c == '\'') { 569 while (C1 == ' ' || c == '\t') 570 ; 571 if (c == 'E' && C1 == 'N') { 572 SKIP; 573 if (msflag && dflg) { 574 putchar('x'); 575 putchar(' '); 576 if (last) { 577 putchar(last); 578 putchar('\n'); 579 } 580 } 581 return; 582 } 583 } else if (c == 'd') { 584 /* look for delim */ 585 if (C1 == 'e' && C1 == 'l') 586 if (C1 == 'i' && C1 == 'm') { 587 while (C1 == ' ') 588 ; /* nothing */ 589 590 if ((c1 = c) == '\n' || 591 (c2 = C1) == '\n' || 592 (c1 == 'o' && c2 == 'f' && C1=='f')) { 593 ldelim = NOCHAR; 594 rdelim = NOCHAR; 595 } else { 596 ldelim = c1; 597 rdelim = c2; 598 } 599 } 600 dflg = 0; 601 } 602 603 if (c != '\n') 604 while (C1 != '\n') { 605 if (chars[c] == PUNCT) 606 last = c; 607 else if (c != ' ') 608 last = 0; 609 } 610 } 611 } 612 613 /* skip over a complete backslash construction */ 614 void 615 backsl(void) 616 { 617 int bdelim; 618 619 sw: 620 switch (C) { 621 case '"': 622 SKIP; 623 return; 624 625 case 's': 626 if (C == '\\') 627 backsl(); 628 else { 629 while (C >= '0' && c <= '9') 630 ; /* nothing */ 631 ungetc(c, infile); 632 c = '0'; 633 } 634 --lp; 635 return; 636 637 case 'f': 638 case 'n': 639 case '*': 640 if (C != '(') 641 return; 642 643 case '(': 644 if (msflag) { 645 if (C == 'e') { 646 if (C == 'm') { 647 *lp = '-'; 648 return; 649 } 650 } 651 else if (c != '\n') 652 C; 653 return; 654 } 655 if (C != '\n') 656 C; 657 return; 658 659 case '$': 660 C; /* discard argument number */ 661 return; 662 663 case 'b': 664 case 'x': 665 case 'v': 666 case 'h': 667 case 'w': 668 case 'o': 669 case 'l': 670 case 'L': 671 if ((bdelim = C) == '\n') 672 return; 673 while (C != '\n' && c != bdelim) 674 if (c == '\\') 675 backsl(); 676 return; 677 678 case '\\': 679 if (inmacro) 680 goto sw; 681 682 default: 683 return; 684 } 685 } 686 687 void 688 sce(void) 689 { 690 char *ap; 691 int n, i; 692 char a[10]; 693 694 for (ap = a; C != '\n'; ap++) { 695 *ap = c; 696 if (ap == &a[9]) { 697 SKIP; 698 ap = a; 699 break; 700 } 701 } 702 if (ap != a) 703 n = atoi(a); 704 else 705 n = 1; 706 for (i = 0; i < n;) { 707 if (C == '.') { 708 if (C == 'c') { 709 if (C == 'e') { 710 while (C == ' ') 711 ; /* nothing */ 712 if (c == '0') { 713 SKIP; 714 break; 715 } else 716 SKIP; 717 } 718 else 719 SKIP; 720 } else if (c == 'P' || C == 'P') { 721 if (c != '\n') 722 SKIP; 723 break; 724 } else if (c != '\n') 725 SKIP; 726 } else { 727 SKIP; 728 i++; 729 } 730 } 731 } 732 733 void 734 refer(int c1) 735 { 736 int c2; 737 738 if (c1 != '\n') 739 SKIP; 740 741 for (c2 = -1;;) { 742 if (C != '.') 743 SKIP; 744 else { 745 if (C != ']') 746 SKIP; 747 else { 748 while (C != '\n') 749 c2 = c; 750 if (c2 != -1 && chars[c2] == PUNCT) 751 putchar(c2); 752 return; 753 } 754 } 755 } 756 } 757 758 void 759 inpic(void) 760 { 761 int c1; 762 char *p1; 763 764 SKIP; 765 p1 = line; 766 c = '\n'; 767 for (;;) { 768 c1 = c; 769 if (C == '.' && c1 == '\n') { 770 if (C != 'P') { 771 if (c == '\n') 772 continue; 773 else { 774 SKIP; 775 c = '\n'; 776 continue; 777 } 778 } 779 if (C != 'E') { 780 if (c == '\n') 781 continue; 782 else { 783 SKIP; 784 c = '\n'; 785 continue; 786 } 787 } 788 SKIP; 789 return; 790 } 791 else if (c == '\"') { 792 while (C != '\"') { 793 if (c == '\\') { 794 if (C == '\"') 795 continue; 796 ungetc(c, infile); 797 backsl(); 798 } else 799 *p1++ = c; 800 } 801 *p1++ = ' '; 802 } 803 else if (c == '\n' && p1 != line) { 804 *p1 = '\0'; 805 if (wordflag) 806 msputwords(NO); 807 else { 808 puts(line); 809 putchar('\n'); 810 } 811 p1 = line; 812 } 813 } 814 } 815 816 #ifdef DEBUG 817 int 818 _C1(void) 819 { 820 821 return(C1get); 822 } 823 824 int 825 _C(void) 826 { 827 828 return(Cget); 829 } 830 #endif /* DEBUG */ 831 832 /* 833 * Put out a macro line, using ms and mm conventions. 834 */ 835 void 836 msputmac(char *s, int constant) 837 { 838 char *t; 839 int found; 840 int last; 841 842 last = 0; 843 found = 0; 844 if (wordflag) { 845 msputwords(YES); 846 return; 847 } 848 while (*s) { 849 while (*s == ' ' || *s == '\t') 850 putchar(*s++); 851 for (t = s ; *t != ' ' && *t != '\t' && *t != '\0' ; ++t) 852 ; /* nothing */ 853 if (*s == '\"') 854 s++; 855 if (t > s + constant && chars[(unsigned char)s[0]] == LETTER && 856 chars[(unsigned char)s[1]] == LETTER) { 857 while (s < t) 858 if (*s == '\"') 859 s++; 860 else 861 putchar(*s++); 862 last = *(t-1); 863 found++; 864 } else if (found && chars[(unsigned char)s[0]] == PUNCT && 865 s[1] == '\0') { 866 putchar(*s++); 867 } else { 868 last = *(t - 1); 869 s = t; 870 } 871 } 872 putchar('\n'); 873 if (msflag && chars[last] == PUNCT) { 874 putchar(last); 875 putchar('\n'); 876 } 877 } 878 879 /* 880 * put out words (for the -w option) with ms and mm conventions 881 */ 882 void 883 msputwords(int macline) 884 { 885 char *p, *p1; 886 int i, nlet; 887 888 for (p1 = line;;) { 889 /* 890 * skip initial specials ampersands and apostrophes 891 */ 892 while (chars[(unsigned char)*p1] < DIGIT) 893 if (*p1++ == '\0') 894 return; 895 nlet = 0; 896 for (p = p1 ; (i = chars[(unsigned char)*p]) != SPECIAL ; ++p) 897 if (i == LETTER) 898 ++nlet; 899 900 if (nlet > 1 && chars[(unsigned char)p1[0]] == LETTER) { 901 /* 902 * delete trailing ampersands and apostrophes 903 */ 904 while ((i = chars[(unsigned char)p[-1]]) == PUNCT || 905 i == APOS ) 906 --p; 907 while (p1 < p) 908 putchar(*p1++); 909 putchar('\n'); 910 } else { 911 p1 = p; 912 } 913 } 914 } 915 916 /* 917 * put out a macro using the me conventions 918 */ 919 #define SKIPBLANK(cp) while (*cp == ' ' || *cp == '\t') { cp++; } 920 #define SKIPNONBLANK(cp) while (*cp !=' ' && *cp !='\cp' && *cp !='\0') { cp++; } 921 922 void 923 meputmac(char *cp, int constant) 924 { 925 char *np; 926 int found; 927 int argno; 928 int last; 929 int inquote; 930 931 last = 0; 932 found = 0; 933 if (wordflag) { 934 meputwords(YES); 935 return; 936 } 937 for (argno = 0; *cp; argno++) { 938 SKIPBLANK(cp); 939 inquote = (*cp == '"'); 940 if (inquote) 941 cp++; 942 for (np = cp; *np; np++) { 943 switch (*np) { 944 case '\n': 945 case '\0': 946 break; 947 948 case '\t': 949 case ' ': 950 if (inquote) 951 continue; 952 else 953 goto endarg; 954 955 case '"': 956 if (inquote && np[1] == '"') { 957 memmove(np, np + 1, strlen(np)); 958 np++; 959 continue; 960 } else { 961 *np = ' '; /* bye bye " */ 962 goto endarg; 963 } 964 965 default: 966 continue; 967 } 968 } 969 endarg: ; 970 /* 971 * cp points at the first char in the arg 972 * np points one beyond the last char in the arg 973 */ 974 if ((argconcat == 0) || (argconcat != argno)) 975 putchar(' '); 976 #ifdef FULLDEBUG 977 { 978 char *p; 979 printf("[%d,%d: ", argno, np - cp); 980 for (p = cp; p < np; p++) { 981 putchar(*p); 982 } 983 printf("]"); 984 } 985 #endif /* FULLDEBUG */ 986 /* 987 * Determine if the argument merits being printed 988 * 989 * constant is the cut off point below which something 990 * is not a word. 991 */ 992 if (((np - cp) > constant) && 993 (inquote || (chars[(unsigned char)cp[0]] == LETTER))) { 994 for (cp = cp; cp < np; cp++) 995 putchar(*cp); 996 last = np[-1]; 997 found++; 998 } else if (found && (np - cp == 1) && 999 chars[(unsigned char)*cp] == PUNCT) { 1000 putchar(*cp); 1001 } else { 1002 last = np[-1]; 1003 } 1004 cp = np; 1005 } 1006 if (msflag && chars[last] == PUNCT) 1007 putchar(last); 1008 putchar('\n'); 1009 } 1010 1011 /* 1012 * put out words (for the -w option) with ms and mm conventions 1013 */ 1014 void 1015 meputwords(int macline) 1016 { 1017 1018 msputwords(macline); 1019 } 1020 1021 /* 1022 * 1023 * Skip over a nested set of macros 1024 * 1025 * Possible arguments to noblock are: 1026 * 1027 * fi end of unfilled text 1028 * PE pic ending 1029 * DE display ending 1030 * 1031 * for ms and mm only: 1032 * KE keep ending 1033 * 1034 * NE undocumented match to NS (for mm?) 1035 * LE mm only: matches RL or *L (for lists) 1036 * 1037 * for me: 1038 * ([lqbzcdf] 1039 */ 1040 void 1041 noblock(char a1, char a2) 1042 { 1043 int c1,c2; 1044 int eqnf; 1045 int lct; 1046 1047 lct = 0; 1048 eqnf = 1; 1049 SKIP; 1050 for (;;) { 1051 while (C != '.') 1052 if (c == '\n') 1053 continue; 1054 else 1055 SKIP; 1056 if ((c1 = C) == '\n') 1057 continue; 1058 if ((c2 = C) == '\n') 1059 continue; 1060 if (c1 == a1 && c2 == a2) { 1061 SKIP; 1062 if (lct != 0) { 1063 lct--; 1064 continue; 1065 } 1066 if (eqnf) 1067 putchar('.'); 1068 putchar('\n'); 1069 return; 1070 } else if (a1 == 'L' && c2 == 'L') { 1071 lct++; 1072 SKIP; 1073 } 1074 /* 1075 * equations (EQ) nested within a display 1076 */ 1077 else if (c1 == 'E' && c2 == 'Q') { 1078 if ((mac == ME && a1 == ')') 1079 || (mac != ME && a1 == 'D')) { 1080 eqn(); 1081 eqnf=0; 1082 } 1083 } 1084 /* 1085 * turning on filling is done by the paragraphing 1086 * macros 1087 */ 1088 else if (a1 == 'f') { /* .fi */ 1089 if ((mac == ME && (c2 == 'h' || c2 == 'p')) 1090 || (mac != ME && (c1 == 'P' || c2 == 'P'))) { 1091 SKIP; 1092 return; 1093 } 1094 } else { 1095 SKIP; 1096 } 1097 } 1098 } 1099 1100 int 1101 EQ(void) 1102 { 1103 1104 eqn(); 1105 return(0); 1106 } 1107 1108 int 1109 domacro(void) 1110 { 1111 1112 macro(); 1113 return(0); 1114 } 1115 1116 int 1117 PS(void) 1118 { 1119 1120 for (C; c == ' ' || c == '\t'; C) 1121 ; /* nothing */ 1122 1123 if (c == '<') { /* ".PS < file" -- don't expect a .PE */ 1124 SKIP; 1125 return(0); 1126 } 1127 if (!msflag) 1128 inpic(); 1129 else 1130 noblock('P', 'E'); 1131 return(0); 1132 } 1133 1134 int 1135 skip(void) 1136 { 1137 1138 SKIP; 1139 return(0); 1140 } 1141 1142 int 1143 intbl(void) 1144 { 1145 1146 if (msflag) 1147 stbl(); 1148 else 1149 tbl(); 1150 return(0); 1151 } 1152 1153 int 1154 outtbl(void) 1155 { 1156 1157 intable = NO; 1158 return(0); 1159 } 1160 1161 int 1162 so(void) 1163 { 1164 1165 if (!iflag) { 1166 getfname(); 1167 if (fname[0]) { 1168 if (++filesp - &files[0] > MAXFILES) 1169 err(1, "too many nested files (max %d)", 1170 MAXFILES); 1171 infile = *filesp = opn(fname); 1172 } 1173 } 1174 return(0); 1175 } 1176 1177 int 1178 nx(void) 1179 { 1180 1181 if (!iflag) { 1182 getfname(); 1183 if (fname[0] == '\0') 1184 exit(0); 1185 if (infile != stdin) 1186 fclose(infile); 1187 infile = *filesp = opn(fname); 1188 } 1189 return(0); 1190 } 1191 1192 int 1193 skiptocom(void) 1194 { 1195 1196 SKIP_TO_COM; 1197 return(COMX); 1198 } 1199 1200 int 1201 PP(pacmac c12) 1202 { 1203 int c1, c2; 1204 1205 frommac(c12, c1, c2); 1206 printf(".%c%c", c1, c2); 1207 while (C != '\n') 1208 putchar(c); 1209 putchar('\n'); 1210 return(0); 1211 } 1212 1213 int 1214 AU(void) 1215 { 1216 1217 if (mac == MM) 1218 return(0); 1219 SKIP_TO_COM; 1220 return(COMX); 1221 } 1222 1223 int 1224 SH(pacmac c12) 1225 { 1226 int c1, c2; 1227 1228 frommac(c12, c1, c2); 1229 1230 if (parag) { 1231 printf(".%c%c", c1, c2); 1232 while (C != '\n') 1233 putchar(c); 1234 putchar(c); 1235 putchar('!'); 1236 for (;;) { 1237 while (C != '\n') 1238 putchar(c); 1239 putchar('\n'); 1240 if (C == '.') 1241 return(COM); 1242 putchar('!'); 1243 putchar(c); 1244 } 1245 /*NOTREACHED*/ 1246 } else { 1247 SKIP_TO_COM; 1248 return(COMX); 1249 } 1250 } 1251 1252 int 1253 UX(void) 1254 { 1255 1256 if (wordflag) 1257 printf("UNIX\n"); 1258 else 1259 printf("UNIX "); 1260 return(0); 1261 } 1262 1263 int 1264 MMHU(pacmac c12) 1265 { 1266 int c1, c2; 1267 1268 frommac(c12, c1, c2); 1269 if (parag) { 1270 printf(".%c%c", c1, c2); 1271 while (C != '\n') 1272 putchar(c); 1273 putchar('\n'); 1274 } else { 1275 SKIP; 1276 } 1277 return(0); 1278 } 1279 1280 int 1281 mesnblock(pacmac c12) 1282 { 1283 int c1, c2; 1284 1285 frommac(c12, c1, c2); 1286 noblock(')', c2); 1287 return(0); 1288 } 1289 1290 int 1291 mssnblock(pacmac c12) 1292 { 1293 int c1, c2; 1294 1295 frommac(c12, c1, c2); 1296 noblock(c1, 'E'); 1297 return(0); 1298 } 1299 1300 int 1301 nf(void) 1302 { 1303 1304 noblock('f', 'i'); 1305 return(0); 1306 } 1307 1308 int 1309 ce(void) 1310 { 1311 1312 sce(); 1313 return(0); 1314 } 1315 1316 int 1317 meip(pacmac c12) 1318 { 1319 1320 if (parag) 1321 mepp(c12); 1322 else if (wordflag) /* save the tag */ 1323 regline(meputmac, ONE); 1324 else 1325 SKIP; 1326 return(0); 1327 } 1328 1329 /* 1330 * only called for -me .pp or .sh, when parag is on 1331 */ 1332 int 1333 mepp(pacmac c12) 1334 { 1335 1336 PP(c12); /* eats the line */ 1337 return(0); 1338 } 1339 1340 /* 1341 * Start of a section heading; output the section name if doing words 1342 */ 1343 int 1344 mesh(pacmac c12) 1345 { 1346 1347 if (parag) 1348 mepp(c12); 1349 else if (wordflag) 1350 defcomline(c12); 1351 else 1352 SKIP; 1353 return(0); 1354 } 1355 1356 /* 1357 * process a font setting 1358 */ 1359 int 1360 mefont(pacmac c12) 1361 { 1362 1363 argconcat = 1; 1364 defcomline(c12); 1365 argconcat = 0; 1366 return(0); 1367 } 1368 1369 int 1370 manfont(pacmac c12) 1371 { 1372 1373 return(mefont(c12)); 1374 } 1375 1376 int 1377 manpp(pacmac c12) 1378 { 1379 1380 return(mepp(c12)); 1381 } 1382 1383 void 1384 defcomline(pacmac c12) 1385 { 1386 int c1, c2; 1387 1388 frommac(c12, c1, c2); 1389 if (msflag && mac == MM && c2 == 'L') { 1390 if (disp || c1 == 'R') { 1391 noblock('L', 'E'); 1392 } else { 1393 SKIP; 1394 putchar('.'); 1395 } 1396 } 1397 else if (c1 == '.' && c2 == '.') { 1398 if (msflag) { 1399 SKIP; 1400 return; 1401 } 1402 while (C == '.') 1403 /*VOID*/; 1404 } 1405 ++inmacro; 1406 /* 1407 * Process the arguments to the macro 1408 */ 1409 switch (mac) { 1410 default: 1411 case MM: 1412 case MS: 1413 if (c1 <= 'Z' && msflag) 1414 regline(msputmac, ONE); 1415 else 1416 regline(msputmac, TWO); 1417 break; 1418 case ME: 1419 regline(meputmac, ONE); 1420 break; 1421 } 1422 --inmacro; 1423 } 1424 1425 void 1426 comline(void) 1427 { 1428 int c1; 1429 int c2; 1430 pacmac c12; 1431 int mid; 1432 int lb, ub; 1433 int hit; 1434 static int tabsize = 0; 1435 static struct mactab *mactab = (struct mactab *)0; 1436 struct mactab *mp; 1437 1438 if (mactab == 0) 1439 buildtab(&mactab, &tabsize); 1440 com: 1441 while (C == ' ' || c == '\t') 1442 ; 1443 comx: 1444 if ((c1 = c) == '\n') 1445 return; 1446 c2 = C; 1447 if (c1 == '.' && c2 != '.') 1448 inmacro = NO; 1449 if (msflag && c1 == '[') { 1450 refer(c2); 1451 return; 1452 } 1453 if (parag && mac==MM && c1 == 'P' && c2 == '\n') { 1454 printf(".P\n"); 1455 return; 1456 } 1457 if (c2 == '\n') 1458 return; 1459 /* 1460 * Single letter macro 1461 */ 1462 if (mac == ME && (c2 == ' ' || c2 == '\t') ) 1463 c2 = ' '; 1464 c12 = tomac(c1, c2); 1465 /* 1466 * binary search through the table of macros 1467 */ 1468 lb = 0; 1469 ub = tabsize - 1; 1470 while (lb <= ub) { 1471 mid = (ub + lb) / 2; 1472 mp = &mactab[mid]; 1473 if (mp->macname < c12) 1474 lb = mid + 1; 1475 else if (mp->macname > c12) 1476 ub = mid - 1; 1477 else { 1478 hit = 1; 1479 #ifdef FULLDEBUG 1480 printf("preliminary hit macro %c%c ", c1, c2); 1481 #endif /* FULLDEBUG */ 1482 switch (mp->condition) { 1483 case NONE: 1484 hit = YES; 1485 break; 1486 case FNEST: 1487 hit = (filesp == files); 1488 break; 1489 case NOMAC: 1490 hit = !inmacro; 1491 break; 1492 case MAC: 1493 hit = inmacro; 1494 break; 1495 case PARAG: 1496 hit = parag; 1497 break; 1498 case NBLK: 1499 hit = !keepblock; 1500 break; 1501 default: 1502 hit = 0; 1503 } 1504 1505 if (hit) { 1506 #ifdef FULLDEBUG 1507 printf("MATCH\n"); 1508 #endif /* FULLDEBUG */ 1509 switch ((*(mp->func))(c12)) { 1510 default: 1511 return; 1512 case COMX: 1513 goto comx; 1514 case COM: 1515 goto com; 1516 } 1517 } 1518 #ifdef FULLDEBUG 1519 printf("FAIL\n"); 1520 #endif /* FULLDEBUG */ 1521 break; 1522 } 1523 } 1524 defcomline(c12); 1525 } 1526 1527 int 1528 macsort(const void *p1, const void *p2) 1529 { 1530 struct mactab *t1 = (struct mactab *)p1; 1531 struct mactab *t2 = (struct mactab *)p2; 1532 1533 return(t1->macname - t2->macname); 1534 } 1535 1536 int 1537 sizetab(struct mactab *mp) 1538 { 1539 int i; 1540 1541 i = 0; 1542 if (mp) { 1543 for (; mp->macname; mp++, i++) 1544 /*VOID*/ ; 1545 } 1546 return(i); 1547 } 1548 1549 struct mactab * 1550 macfill(struct mactab *dst, struct mactab *src) 1551 { 1552 1553 if (src) { 1554 while (src->macname) 1555 *dst++ = *src++; 1556 } 1557 return(dst); 1558 } 1559 1560 __dead void 1561 usage(void) 1562 { 1563 extern char *__progname; 1564 1565 fprintf(stderr, "usage: %s [-ikpw] [-m a | e | l | m | s] [file ...]\n", __progname); 1566 exit(1); 1567 } 1568 1569 void 1570 buildtab(struct mactab **r_back, int *r_size) 1571 { 1572 int size; 1573 struct mactab *p, *p1, *p2; 1574 struct mactab *back; 1575 1576 size = sizetab(troffmactab) + sizetab(ppmactab); 1577 p1 = p2 = NULL; 1578 if (msflag) { 1579 switch (mac) { 1580 case ME: 1581 p1 = memactab; 1582 break; 1583 case MM: 1584 p1 = msmactab; 1585 p2 = mmmactab; 1586 break; 1587 case MS: 1588 p1 = msmactab; 1589 break; 1590 case MA: 1591 p1 = manmactab; 1592 break; 1593 default: 1594 break; 1595 } 1596 } 1597 size += sizetab(p1); 1598 size += sizetab(p2); 1599 back = (struct mactab *)calloc(size+2, sizeof(struct mactab)); 1600 if (back == NULL) 1601 err(1, NULL); 1602 1603 p = macfill(back, troffmactab); 1604 p = macfill(p, ppmactab); 1605 p = macfill(p, p1); 1606 p = macfill(p, p2); 1607 1608 qsort(back, size, sizeof(struct mactab), macsort); 1609 *r_size = size; 1610 *r_back = back; 1611 } 1612 1613 /* 1614 * troff commands 1615 */ 1616 struct mactab troffmactab[] = { 1617 M(NONE, '\\','"', skip), /* comment */ 1618 M(NOMAC, 'd','e', domacro), /* define */ 1619 M(NOMAC, 'i','g', domacro), /* ignore till .. */ 1620 M(NOMAC, 'a','m', domacro), /* append macro */ 1621 M(NBLK, 'n','f', nf), /* filled */ 1622 M(NBLK, 'c','e', ce), /* centered */ 1623 1624 M(NONE, 's','o', so), /* source a file */ 1625 M(NONE, 'n','x', nx), /* go to next file */ 1626 1627 M(NONE, 't','m', skip), /* print string on tty */ 1628 M(NONE, 'h','w', skip), /* exception hyphen words */ 1629 M(NONE, 0,0, 0) 1630 }; 1631 1632 /* 1633 * Preprocessor output 1634 */ 1635 struct mactab ppmactab[] = { 1636 M(FNEST, 'E','Q', EQ), /* equation starting */ 1637 M(FNEST, 'T','S', intbl), /* table starting */ 1638 M(FNEST, 'T','C', intbl), /* alternative table? */ 1639 M(FNEST, 'T','&', intbl), /* table reformatting */ 1640 M(NONE, 'T','E', outtbl),/* table ending */ 1641 M(NONE, 'P','S', PS), /* picture starting */ 1642 M(NONE, 0,0, 0) 1643 }; 1644 1645 /* 1646 * Particular to ms and mm 1647 */ 1648 struct mactab msmactab[] = { 1649 M(NONE, 'T','L', skiptocom), /* title follows */ 1650 M(NONE, 'F','S', skiptocom), /* start footnote */ 1651 M(NONE, 'O','K', skiptocom), /* Other kws */ 1652 1653 M(NONE, 'N','R', skip), /* undocumented */ 1654 M(NONE, 'N','D', skip), /* use supplied date */ 1655 1656 M(PARAG, 'P','P', PP), /* begin parag */ 1657 M(PARAG, 'I','P', PP), /* begin indent parag, tag x */ 1658 M(PARAG, 'L','P', PP), /* left blocked parag */ 1659 1660 M(NONE, 'A','U', AU), /* author */ 1661 M(NONE, 'A','I', AU), /* authors institution */ 1662 1663 M(NONE, 'S','H', SH), /* section heading */ 1664 M(NONE, 'S','N', SH), /* undocumented */ 1665 M(NONE, 'U','X', UX), /* unix */ 1666 1667 M(NBLK, 'D','S', mssnblock), /* start display text */ 1668 M(NBLK, 'K','S', mssnblock), /* start keep */ 1669 M(NBLK, 'K','F', mssnblock), /* start float keep */ 1670 M(NONE, 0,0, 0) 1671 }; 1672 1673 struct mactab mmmactab[] = { 1674 M(NONE, 'H',' ', MMHU), /* -mm ? */ 1675 M(NONE, 'H','U', MMHU), /* -mm ? */ 1676 M(PARAG, 'P',' ', PP), /* paragraph for -mm */ 1677 M(NBLK, 'N','S', mssnblock), /* undocumented */ 1678 M(NONE, 0,0, 0) 1679 }; 1680 1681 struct mactab memactab[] = { 1682 M(PARAG, 'p','p', mepp), 1683 M(PARAG, 'l','p', mepp), 1684 M(PARAG, 'n','p', mepp), 1685 M(NONE, 'i','p', meip), 1686 1687 M(NONE, 's','h', mesh), 1688 M(NONE, 'u','h', mesh), 1689 1690 M(NBLK, '(','l', mesnblock), 1691 M(NBLK, '(','q', mesnblock), 1692 M(NBLK, '(','b', mesnblock), 1693 M(NBLK, '(','z', mesnblock), 1694 M(NBLK, '(','c', mesnblock), 1695 1696 M(NBLK, '(','d', mesnblock), 1697 M(NBLK, '(','f', mesnblock), 1698 M(NBLK, '(','x', mesnblock), 1699 1700 M(NONE, 'r',' ', mefont), 1701 M(NONE, 'i',' ', mefont), 1702 M(NONE, 'b',' ', mefont), 1703 M(NONE, 'u',' ', mefont), 1704 M(NONE, 'q',' ', mefont), 1705 M(NONE, 'r','b', mefont), 1706 M(NONE, 'b','i', mefont), 1707 M(NONE, 'b','x', mefont), 1708 M(NONE, 0,0, 0) 1709 }; 1710 1711 struct mactab manmactab[] = { 1712 M(PARAG, 'B','I', manfont), 1713 M(PARAG, 'B','R', manfont), 1714 M(PARAG, 'I','B', manfont), 1715 M(PARAG, 'I','R', manfont), 1716 M(PARAG, 'R','B', manfont), 1717 M(PARAG, 'R','I', manfont), 1718 1719 M(PARAG, 'P','P', manpp), 1720 M(PARAG, 'L','P', manpp), 1721 M(PARAG, 'H','P', manpp), 1722 M(NONE, 0,0, 0) 1723 }; 1724