Annotation of src/usr.bin/deroff/deroff.c, Revision 1.6
1.6 ! tom 1: /* $OpenBSD: deroff.c,v 1.5 2003/06/03 02:56:07 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.
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: #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
1.6 ! tom 75: static const char rcsid[] = "$OpenBSD: deroff.c,v 1.5 2003/06/03 02:56:07 millert Exp $";
1.1 millert 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()
1.4 danh 109: #else /* not DEBUG */
1.1 millert 110: # define C Cget
111: # define C1 C1get
1.4 danh 112: #endif /* not DEBUG */
1.1 millert 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" };
1.4 danh 126: #endif /* DEBUG */
1.1 millert 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);
1.4 danh 345: #endif /* DEBUG */
1.1 millert 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)
1.6 ! tom 450: err(1, NULL);
1.1 millert 451: q->nextp = namechain;
452: q->datap = strdup(fname);
453: if (q->datap == NULL)
1.6 ! tom 454: err(1, NULL);
1.1 millert 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);
1.4 danh 478: #endif /* FULLDEBUG */
1.1 millert 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: }
1.4 danh 830: #endif /* DEBUG */
1.1 millert 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] == '"') {
1.3 millert 957: memmove(np, np + 1, strlen(np));
1.1 millert 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: }
1.4 danh 985: #endif /* FULLDEBUG */
1.1 millert 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);
1.4 danh 1481: #endif /* FULLDEBUG */
1.1 millert 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");
1.4 danh 1508: #endif /* FULLDEBUG */
1.1 millert 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");
1.4 danh 1520: #endif /* FULLDEBUG */
1.1 millert 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 | m | s | l ) ] [ filename ] ... \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)
1.6 ! tom 1601: err(1, NULL);
1.1 millert 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: };