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