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