No empty .Rs/.Re
[netbsd-mini2440.git] / usr.bin / deroff / deroff.c
blob38f855f17af147bab05683b89750fa79ee300044
1 /* $NetBSD: deroff.c,v 1.6 2009/04/11 13:01:27 lukem Exp $ */
3 /* taken from: OpenBSD: deroff.c,v 1.6 2004/06/02 14:58:46 tom Exp */
5 /*-
6 * Copyright (c) 1988, 1993
7 * The Regents of the University of California. All rights reserved.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
34 * Copyright (C) Caldera International Inc. 2001-2002.
35 * All rights reserved.
37 * Redistribution and use in source and binary forms, with or without
38 * modification, are permitted provided that the following conditions
39 * are met:
40 * 1. Redistributions of source code and documentation must retain the above
41 * copyright notice, this list of conditions and the following disclaimer.
42 * 2. Redistributions in binary form must reproduce the above copyright
43 * notice, this list of conditions and the following disclaimer in the
44 * documentation and/or other materials provided with the distribution.
45 * 3. All advertising materials mentioning features or use of this software
46 * must display the following acknowledgement:
47 * This product includes software developed or owned by Caldera
48 * International, Inc.
49 * 4. Neither the name of Caldera International, Inc. nor the names of other
50 * contributors may be used to endorse or promote products derived from
51 * this software without specific prior written permission.
53 * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
54 * INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
55 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
56 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
57 * IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE FOR ANY DIRECT,
58 * INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
59 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
60 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
61 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
62 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
63 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
64 * POSSIBILITY OF SUCH DAMAGE.
67 #ifndef lint
68 static const char copyright[] =
69 "@(#) Copyright (c) 1988, 1993\n\
70 The Regents of the University of California. All rights reserved.\n";
71 #endif /* not lint */
73 #ifndef lint
74 #if 0
75 static const char sccsid[] = "@(#)deroff.c 8.1 (Berkeley) 6/6/93";
76 #else
77 static const char rcsid[] = "$NetBSD: deroff.c,v 1.6 2009/04/11 13:01:27 lukem Exp $";
78 #endif
79 #endif /* not lint */
81 #include <sys/cdefs.h>
82 #include <err.h>
83 #include <limits.h>
84 #include <stddef.h>
85 #include <stdio.h>
86 #include <stdlib.h>
87 #include <string.h>
88 #include <unistd.h>
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.
101 * All input is through the Cget macro;
102 * the most recently read character is in c.
104 * Modified by Robert Henry to process -me and -man macros.
107 #define Cget ( (c=getc(infile)) == EOF ? eof() : ((c==ldelim)&&(filesp==files) ? skeqn() : c) )
108 #define C1get ( (c=getc(infile)) == EOF ? eof() : c)
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 */
118 #define SKIP while (C != '\n')
119 #define SKIP_TO_COM SKIP; SKIP; pc=c; while (C != '.' || pc != '\n' || C > 'Z')pc=c
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 */
128 #ifdef DEBUG
129 char *mactab[] = { "-ms", "-mm", "-me", "-ma" };
130 #endif /* DEBUG */
132 #define ONE 1
133 #define TWO 2
135 #define NOCHAR -2
136 #define SPECIAL 0
137 #define APOS 1
138 #define PUNCT 2
139 #define DIGIT 3
140 #define LETTER 4
142 #define MAXFILES 20
144 static int iflag;
145 static int wordflag;
146 static int msflag; /* processing a source written using a mac package */
147 static int mac; /* which package */
148 static int disp;
149 static int parag;
150 static int inmacro;
151 static int intable;
152 static int keepblock; /* keep blocks of text; normally false when msflag */
154 static char chars[128]; /* SPECIAL, PUNCT, APOS, DIGIT, or LETTER */
156 static char line[LINE_MAX];
157 static char *lp;
159 static int c;
160 static int pc;
161 static int ldelim;
162 static int rdelim;
164 static char fname[PATH_MAX];
165 static FILE *files[MAXFILES];
166 static FILE **filesp;
167 static FILE *infile;
169 static int argc;
170 static char **argv;
173 * Macro processing
175 * Macro table definitions
177 typedef int pacmac; /* compressed macro name */
178 static int argconcat = 0; /* concat arguments together (-me only) */
180 #define tomac(c1, c2) ((((c1) & 0xFF) << 8) | ((c2) & 0xFF))
181 #define frommac(src, c1, c2) (((c1)=((src)>>8)&0xFF),((c2) =(src)&0xFF))
183 struct mactab {
184 int condition;
185 pacmac macname;
186 int (*func)(pacmac);
189 static const struct mactab troffmactab[];
190 static const struct mactab ppmactab[];
191 static const struct mactab msmactab[];
192 static const struct mactab mmmactab[];
193 static const struct mactab memactab[];
194 static const struct mactab manmactab[];
197 * Macro table initialization
199 #define M(cond, c1, c2, func) {cond, tomac(c1, c2), func}
202 * Flags for matching conditions other than
203 * the macro name
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 */
214 * Return codes from macro minions, determine where to jump,
215 * how to repeat/reprocess text
217 #define COMX 1 /* goto comx */
218 #define COM 2 /* goto com */
220 static int skeqn(void);
221 static int eof(void);
222 #ifdef DEBUG
223 static int _C1(void);
224 static int _C(void);
225 #endif
226 static int EQ(pacmac);
227 static int domacro(pacmac);
228 static int PS(pacmac);
229 static int skip(pacmac);
230 static int intbl(pacmac);
231 static int outtbl(pacmac);
232 static int so(pacmac);
233 static int nx(pacmac);
234 static int skiptocom(pacmac);
235 static int PP(pacmac);
236 static int AU(pacmac);
237 static int SH(pacmac);
238 static int UX(pacmac);
239 static int MMHU(pacmac);
240 static int mesnblock(pacmac);
241 static int mssnblock(pacmac);
242 static int nf(pacmac);
243 static int ce(pacmac);
244 static int meip(pacmac);
245 static int mepp(pacmac);
246 static int mesh(pacmac);
247 static int mefont(pacmac);
248 static int manfont(pacmac);
249 static int manpp(pacmac);
250 static int macsort(const void *, const void *);
251 static int sizetab(const struct mactab *);
252 static void getfname(void);
253 static void textline(char *, int);
254 static void work(void);
255 static void regline(void (*)(char *, int), int);
256 static void macro(void);
257 static void tbl(void);
258 static void stbl(void);
259 static void eqn(void);
260 static void backsl(void);
261 static void sce(void);
262 static void refer(int);
263 static void inpic(void);
264 static void msputmac(char *, int);
265 static void msputwords(int);
266 static void meputmac(char *, int);
267 static void meputwords(int);
268 static void noblock(char, char);
269 static void defcomline(pacmac);
270 static void comline(void);
271 static void buildtab(const struct mactab **, int *);
272 static FILE *opn(char *);
273 static struct mactab *macfill(struct mactab *, const struct mactab *);
274 static void usage(void) __dead;
277 main(int ac, char **av)
279 int i, ch;
280 int errflg = 0;
281 int kflag = NO;
283 iflag = NO;
284 wordflag = NO;
285 msflag = NO;
286 mac = ME;
287 disp = NO;
288 parag = NO;
289 inmacro = NO;
290 intable = NO;
291 ldelim = NOCHAR;
292 rdelim = NOCHAR;
293 keepblock = YES;
295 while ((ch = getopt(ac, av, "ikpwm:")) != -1) {
296 switch (ch) {
297 case 'i':
298 iflag = YES;
299 break;
300 case 'k':
301 kflag = YES;
302 break;
303 case 'm':
304 msflag = YES;
305 keepblock = NO;
306 switch (optarg[0]) {
307 case 'm':
308 mac = MM;
309 break;
310 case 's':
311 mac = MS;
312 break;
313 case 'e':
314 mac = ME;
315 break;
316 case 'a':
317 mac = MA;
318 break;
319 case 'l':
320 disp = YES;
321 break;
322 default:
323 errflg++;
324 break;
326 if (errflg == 0 && optarg[1] != '\0')
327 errflg++;
328 break;
329 case 'p':
330 parag = YES;
331 break;
332 case 'w':
333 wordflag = YES;
334 kflag = YES;
335 break;
336 default:
337 errflg++;
340 argc = ac - optind;
341 argv = av + optind;
343 if (kflag)
344 keepblock = YES;
345 if (errflg)
346 usage();
348 #ifdef DEBUG
349 printf("msflag = %d, mac = %s, keepblock = %d, disp = %d\n",
350 msflag, mactab[mac], keepblock, disp);
351 #endif /* DEBUG */
352 if (argc == 0) {
353 infile = stdin;
354 } else {
355 infile = opn(argv[0]);
356 --argc;
357 ++argv;
359 files[0] = infile;
360 filesp = &files[0];
362 for (i = 'a'; i <= 'z' ; ++i)
363 chars[i] = LETTER;
364 for (i = 'A'; i <= 'Z'; ++i)
365 chars[i] = LETTER;
366 for (i = '0'; i <= '9'; ++i)
367 chars[i] = DIGIT;
368 chars['\''] = APOS;
369 chars['&'] = APOS;
370 chars['.'] = PUNCT;
371 chars[','] = PUNCT;
372 chars[';'] = PUNCT;
373 chars['?'] = PUNCT;
374 chars[':'] = PUNCT;
375 work();
376 return 0;
379 static int
380 skeqn(void)
383 while ((c = getc(infile)) != rdelim) {
384 if (c == EOF)
385 c = eof();
386 else if (c == '"') {
387 while ((c = getc(infile)) != '"') {
388 if (c == EOF ||
389 (c == '\\' && (c = getc(infile)) == EOF))
390 c = eof();
394 if (msflag)
395 return c == 'x';
396 return c == ' ';
399 static FILE *
400 opn(char *p)
402 FILE *fd;
404 if ((fd = fopen(p, "r")) == NULL)
405 err(1, "fopen %s", p);
407 return fd;
410 static int
411 eof(void)
414 if (infile != stdin)
415 fclose(infile);
416 if (filesp > files)
417 infile = *--filesp;
418 else if (argc > 0) {
419 infile = opn(argv[0]);
420 --argc;
421 ++argv;
422 } else
423 exit(0);
424 return C;
427 static void
428 getfname(void)
430 char *p;
431 struct chain {
432 struct chain *nextp;
433 char *datap;
434 } *q;
435 static struct chain *namechain= NULL;
437 while (C == ' ')
438 ; /* nothing */
440 for (p = fname ; p - fname < (ptrdiff_t)sizeof(fname) &&
441 (*p = c) != '\n' &&
442 c != ' ' && c != '\t' && c != '\\'; ++p)
444 *p = '\0';
445 while (c != '\n')
448 /* see if this name has already been used */
449 for (q = namechain ; q; q = q->nextp)
450 if (strcmp(fname, q->datap) == 0) {
451 fname[0] = '\0';
452 return;
455 q = (struct chain *) malloc(sizeof(struct chain));
456 if (q == NULL)
457 err(1, NULL);
458 q->nextp = namechain;
459 q->datap = strdup(fname);
460 if (q->datap == NULL)
461 err(1, NULL);
462 namechain = q;
465 /*ARGSUSED*/
466 static void
467 textline(char *str, int constant)
470 if (wordflag) {
471 msputwords(0);
472 return;
474 puts(str);
477 void
478 work(void)
481 for (;;) {
483 #ifdef FULLDEBUG
484 printf("Starting work with `%c'\n", c);
485 #endif /* FULLDEBUG */
486 if (c == '.' || c == '\'')
487 comline();
488 else
489 regline(textline, TWO);
493 static void
494 regline(void (*pfunc)(char *, int), int constant)
497 line[0] = c;
498 lp = line;
499 while (lp - line < (ptrdiff_t)sizeof(line)) {
500 if (c == '\\') {
501 *lp = ' ';
502 backsl();
504 if (c == '\n')
505 break;
506 if (intable && c == 'T') {
507 *++lp = C;
508 if (c == '{' || c == '}') {
509 lp[-1] = ' ';
510 *lp = C;
512 } else {
513 *++lp = C;
516 *lp = '\0';
518 if (line[0] != '\0')
519 (*pfunc)(line, constant);
522 static void
523 macro(void)
526 if (msflag) {
527 do {
528 SKIP;
529 } while (C!='.' || C!='.' || C=='.'); /* look for .. */
530 if (c != '\n')
531 SKIP;
532 return;
534 SKIP;
535 inmacro = YES;
538 static void
539 tbl(void)
542 while (C != '.')
543 ; /* nothing */
544 SKIP;
545 intable = YES;
548 static void
549 stbl(void)
552 while (C != '.')
553 ; /* nothing */
554 SKIP_TO_COM;
555 if (c != 'T' || C != 'E') {
556 SKIP;
557 pc = c;
558 while (C != '.' || pc != '\n' || C != 'T' || C != 'E')
559 pc = c;
563 static void
564 eqn(void)
566 int c1, c2;
567 int dflg;
568 char last;
570 last=0;
571 dflg = 1;
572 SKIP;
574 for (;;) {
575 if (C1 == '.' || c == '\'') {
576 while (C1 == ' ' || c == '\t')
578 if (c == 'E' && C1 == 'N') {
579 SKIP;
580 if (msflag && dflg) {
581 putchar('x');
582 putchar(' ');
583 if (last) {
584 putchar(last);
585 putchar('\n');
588 return;
590 } else if (c == 'd') {
591 /* look for delim */
592 if (C1 == 'e' && C1 == 'l')
593 if (C1 == 'i' && C1 == 'm') {
594 while (C1 == ' ')
595 ; /* nothing */
597 if ((c1 = c) == '\n' ||
598 (c2 = C1) == '\n' ||
599 (c1 == 'o' && c2 == 'f' && C1=='f')) {
600 ldelim = NOCHAR;
601 rdelim = NOCHAR;
602 } else {
603 ldelim = c1;
604 rdelim = c2;
607 dflg = 0;
610 if (c != '\n')
611 while (C1 != '\n') {
612 if (chars[c] == PUNCT)
613 last = c;
614 else if (c != ' ')
615 last = 0;
620 /* skip over a complete backslash construction */
621 static void
622 backsl(void)
624 int bdelim;
627 switch (C) {
628 case '"':
629 SKIP;
630 return;
632 case 's':
633 if (C == '\\')
634 backsl();
635 else {
636 while (C >= '0' && c <= '9')
637 ; /* nothing */
638 ungetc(c, infile);
639 c = '0';
641 --lp;
642 return;
644 case 'f':
645 case 'n':
646 case '*':
647 if (C != '(')
648 return;
650 case '(':
651 if (msflag) {
652 if (C == 'e') {
653 if (C == 'm') {
654 *lp = '-';
655 return;
658 else if (c != '\n')
660 return;
662 if (C != '\n')
664 return;
666 case '$':
667 C; /* discard argument number */
668 return;
670 case 'b':
671 case 'x':
672 case 'v':
673 case 'h':
674 case 'w':
675 case 'o':
676 case 'l':
677 case 'L':
678 if ((bdelim = C) == '\n')
679 return;
680 while (C != '\n' && c != bdelim)
681 if (c == '\\')
682 backsl();
683 return;
685 case '\\':
686 if (inmacro)
687 goto sw;
689 default:
690 return;
694 static void
695 sce(void)
697 char *ap;
698 int n, i;
699 char a[10];
701 for (ap = a; C != '\n'; ap++) {
702 *ap = c;
703 if (ap == &a[9]) {
704 SKIP;
705 ap = a;
706 break;
709 if (ap != a)
710 n = atoi(a);
711 else
712 n = 1;
713 for (i = 0; i < n;) {
714 if (C == '.') {
715 if (C == 'c') {
716 if (C == 'e') {
717 while (C == ' ')
718 ; /* nothing */
719 if (c == '0') {
720 SKIP;
721 break;
722 } else
723 SKIP;
725 else
726 SKIP;
727 } else if (c == 'P' || C == 'P') {
728 if (c != '\n')
729 SKIP;
730 break;
731 } else if (c != '\n')
732 SKIP;
733 } else {
734 SKIP;
735 i++;
740 static void
741 refer(int c1)
743 int c2;
745 if (c1 != '\n')
746 SKIP;
748 for (c2 = -1;;) {
749 if (C != '.')
750 SKIP;
751 else {
752 if (C != ']')
753 SKIP;
754 else {
755 while (C != '\n')
756 c2 = c;
757 if (c2 != -1 && chars[c2] == PUNCT)
758 putchar(c2);
759 return;
765 static void
766 inpic(void)
768 int c1;
769 char *p1;
771 SKIP;
772 p1 = line;
773 c = '\n';
774 for (;;) {
775 c1 = c;
776 if (C == '.' && c1 == '\n') {
777 if (C != 'P') {
778 if (c == '\n')
779 continue;
780 else {
781 SKIP;
782 c = '\n';
783 continue;
786 if (C != 'E') {
787 if (c == '\n')
788 continue;
789 else {
790 SKIP;
791 c = '\n';
792 continue;
795 SKIP;
796 return;
798 else if (c == '\"') {
799 while (C != '\"') {
800 if (c == '\\') {
801 if (C == '\"')
802 continue;
803 ungetc(c, infile);
804 backsl();
805 } else
806 *p1++ = c;
808 *p1++ = ' ';
810 else if (c == '\n' && p1 != line) {
811 *p1 = '\0';
812 if (wordflag)
813 msputwords(NO);
814 else {
815 puts(line);
816 putchar('\n');
818 p1 = line;
823 #ifdef DEBUG
824 static int
825 _C1(void)
828 return C1get;
831 static int
832 _C(void)
835 return Cget;
837 #endif /* DEBUG */
840 * Put out a macro line, using ms and mm conventions.
842 static void
843 msputmac(char *s, int constant)
845 char *t;
846 int found;
847 int last;
849 last = 0;
850 found = 0;
851 if (wordflag) {
852 msputwords(YES);
853 return;
855 while (*s) {
856 while (*s == ' ' || *s == '\t')
857 putchar(*s++);
858 for (t = s ; *t != ' ' && *t != '\t' && *t != '\0' ; ++t)
859 ; /* nothing */
860 if (*s == '\"')
861 s++;
862 if (t > s + constant && chars[(unsigned char)s[0]] == LETTER &&
863 chars[(unsigned char)s[1]] == LETTER) {
864 while (s < t)
865 if (*s == '\"')
866 s++;
867 else
868 putchar(*s++);
869 last = *(t-1);
870 found++;
871 } else if (found && chars[(unsigned char)s[0]] == PUNCT &&
872 s[1] == '\0') {
873 putchar(*s++);
874 } else {
875 last = *(t - 1);
876 s = t;
879 putchar('\n');
880 if (msflag && chars[last] == PUNCT) {
881 putchar(last);
882 putchar('\n');
887 * put out words (for the -w option) with ms and mm conventions
889 static void
890 msputwords(int macline)
892 char *p, *p1;
893 int i, nlet;
895 for (p1 = line;;) {
897 * skip initial specials ampersands and apostrophes
899 while (chars[(unsigned char)*p1] < DIGIT)
900 if (*p1++ == '\0')
901 return;
902 nlet = 0;
903 for (p = p1 ; (i = chars[(unsigned char)*p]) != SPECIAL ; ++p)
904 if (i == LETTER)
905 ++nlet;
907 if (nlet > 1 && chars[(unsigned char)p1[0]] == LETTER) {
909 * delete trailing ampersands and apostrophes
911 while ((i = chars[(unsigned char)p[-1]]) == PUNCT ||
912 i == APOS )
913 --p;
914 while (p1 < p)
915 putchar(*p1++);
916 putchar('\n');
917 } else {
918 p1 = p;
924 * put out a macro using the me conventions
926 #define SKIPBLANK(cp) while (*cp == ' ' || *cp == '\t') { cp++; }
927 #define SKIPNONBLANK(cp) while (*cp !=' ' && *cp !='\cp' && *cp !='\0') { cp++; }
929 static void
930 meputmac(char *cp, int constant)
932 char *np;
933 int found;
934 int argno;
935 int last;
936 int inquote;
938 last = 0;
939 found = 0;
940 if (wordflag) {
941 meputwords(YES);
942 return;
944 for (argno = 0; *cp; argno++) {
945 SKIPBLANK(cp);
946 inquote = (*cp == '"');
947 if (inquote)
948 cp++;
949 for (np = cp; *np; np++) {
950 switch (*np) {
951 case '\n':
952 case '\0':
953 break;
955 case '\t':
956 case ' ':
957 if (inquote)
958 continue;
959 else
960 goto endarg;
962 case '"':
963 if (inquote && np[1] == '"') {
964 memmove(np, np + 1, strlen(np));
965 np++;
966 continue;
967 } else {
968 *np = ' '; /* bye bye " */
969 goto endarg;
972 default:
973 continue;
976 endarg: ;
978 * cp points at the first char in the arg
979 * np points one beyond the last char in the arg
981 if ((argconcat == 0) || (argconcat != argno))
982 putchar(' ');
983 #ifdef FULLDEBUG
985 char *p;
986 printf("[%d,%d: ", argno, np - cp);
987 for (p = cp; p < np; p++) {
988 putchar(*p);
990 printf("]");
992 #endif /* FULLDEBUG */
994 * Determine if the argument merits being printed
996 * constant is the cut off point below which something
997 * is not a word.
999 if (((np - cp) > constant) &&
1000 (inquote || (chars[(unsigned char)cp[0]] == LETTER))) {
1001 for (cp = cp; cp < np; cp++)
1002 putchar(*cp);
1003 last = np[-1];
1004 found++;
1005 } else if (found && (np - cp == 1) &&
1006 chars[(unsigned char)*cp] == PUNCT) {
1007 putchar(*cp);
1008 } else {
1009 last = np[-1];
1011 cp = np;
1013 if (msflag && chars[last] == PUNCT)
1014 putchar(last);
1015 putchar('\n');
1019 * put out words (for the -w option) with ms and mm conventions
1021 static void
1022 meputwords(int macline)
1025 msputwords(macline);
1030 * Skip over a nested set of macros
1032 * Possible arguments to noblock are:
1034 * fi end of unfilled text
1035 * PE pic ending
1036 * DE display ending
1038 * for ms and mm only:
1039 * KE keep ending
1041 * NE undocumented match to NS (for mm?)
1042 * LE mm only: matches RL or *L (for lists)
1044 * for me:
1045 * ([lqbzcdf]
1047 static void
1048 noblock(char a1, char a2)
1050 int c1,c2;
1051 int eqnf;
1052 int lct;
1054 lct = 0;
1055 eqnf = 1;
1056 SKIP;
1057 for (;;) {
1058 while (C != '.')
1059 if (c == '\n')
1060 continue;
1061 else
1062 SKIP;
1063 if ((c1 = C) == '\n')
1064 continue;
1065 if ((c2 = C) == '\n')
1066 continue;
1067 if (c1 == a1 && c2 == a2) {
1068 SKIP;
1069 if (lct != 0) {
1070 lct--;
1071 continue;
1073 if (eqnf)
1074 putchar('.');
1075 putchar('\n');
1076 return;
1077 } else if (a1 == 'L' && c2 == 'L') {
1078 lct++;
1079 SKIP;
1082 * equations (EQ) nested within a display
1084 else if (c1 == 'E' && c2 == 'Q') {
1085 if ((mac == ME && a1 == ')')
1086 || (mac != ME && a1 == 'D')) {
1087 eqn();
1088 eqnf=0;
1092 * turning on filling is done by the paragraphing
1093 * macros
1095 else if (a1 == 'f') { /* .fi */
1096 if ((mac == ME && (c2 == 'h' || c2 == 'p'))
1097 || (mac != ME && (c1 == 'P' || c2 == 'P'))) {
1098 SKIP;
1099 return;
1101 } else {
1102 SKIP;
1107 static int
1108 /*ARGSUSED*/
1109 EQ(pacmac unused)
1112 eqn();
1113 return 0;
1116 static int
1117 /*ARGSUSED*/
1118 domacro(pacmac unused)
1121 macro();
1122 return 0;
1125 static int
1126 /*ARGSUSED*/
1127 PS(pacmac unused)
1130 for (C; c == ' ' || c == '\t'; C)
1131 ; /* nothing */
1133 if (c == '<') { /* ".PS < file" -- don't expect a .PE */
1134 SKIP;
1135 return 0;
1137 if (!msflag)
1138 inpic();
1139 else
1140 noblock('P', 'E');
1141 return 0;
1144 static int
1145 /*ARGSUSED*/
1146 skip(pacmac unused)
1149 SKIP;
1150 return 0;
1153 static int
1154 /*ARGSUSED*/
1155 intbl(pacmac unused)
1158 if (msflag)
1159 stbl();
1160 else
1161 tbl();
1162 return 0;
1165 static int
1166 /*ARGSUSED*/
1167 outtbl(pacmac unused)
1170 intable = NO;
1171 return 0;
1175 /*ARGSUSED*/
1176 so(pacmac unused)
1179 if (!iflag) {
1180 getfname();
1181 if (fname[0]) {
1182 if (++filesp - &files[0] > MAXFILES)
1183 err(1, "too many nested files (max %d)",
1184 MAXFILES);
1185 infile = *filesp = opn(fname);
1188 return 0;
1191 static int
1192 /*ARGSUSED*/
1193 nx(pacmac unused)
1196 if (!iflag) {
1197 getfname();
1198 if (fname[0] == '\0')
1199 exit(0);
1200 if (infile != stdin)
1201 fclose(infile);
1202 infile = *filesp = opn(fname);
1204 return 0;
1207 static int
1208 /*ARGSUSED*/
1209 skiptocom(pacmac unused)
1212 SKIP_TO_COM;
1213 return COMX;
1216 static int
1217 PP(pacmac c12)
1219 int c1, c2;
1221 frommac(c12, c1, c2);
1222 printf(".%c%c", c1, c2);
1223 while (C != '\n')
1224 putchar(c);
1225 putchar('\n');
1226 return 0;
1229 static int
1230 /*ARGSUSED*/
1231 AU(pacmac unused)
1234 if (mac == MM)
1235 return 0;
1236 SKIP_TO_COM;
1237 return COMX;
1240 static int
1241 SH(pacmac c12)
1243 int c1, c2;
1245 frommac(c12, c1, c2);
1247 if (parag) {
1248 printf(".%c%c", c1, c2);
1249 while (C != '\n')
1250 putchar(c);
1251 putchar(c);
1252 putchar('!');
1253 for (;;) {
1254 while (C != '\n')
1255 putchar(c);
1256 putchar('\n');
1257 if (C == '.')
1258 return COM;
1259 putchar('!');
1260 putchar(c);
1262 /*NOTREACHED*/
1263 } else {
1264 SKIP_TO_COM;
1265 return COMX;
1269 static int
1270 /*ARGSUSED*/
1271 UX(pacmac unused)
1274 if (wordflag)
1275 printf("UNIX\n");
1276 else
1277 printf("UNIX ");
1278 return 0;
1281 static int
1282 MMHU(pacmac c12)
1284 int c1, c2;
1286 frommac(c12, c1, c2);
1287 if (parag) {
1288 printf(".%c%c", c1, c2);
1289 while (C != '\n')
1290 putchar(c);
1291 putchar('\n');
1292 } else {
1293 SKIP;
1295 return 0;
1298 static int
1299 mesnblock(pacmac c12)
1301 int c1, c2;
1303 frommac(c12, c1, c2);
1304 noblock(')', c2);
1305 return 0;
1308 static int
1309 mssnblock(pacmac c12)
1311 int c1, c2;
1313 frommac(c12, c1, c2);
1314 noblock(c1, 'E');
1315 return 0;
1318 static int
1319 /*ARGUSED*/
1320 nf(pacmac unused)
1323 noblock('f', 'i');
1324 return 0;
1327 static int
1328 /*ARGUSED*/
1329 ce(pacmac unused)
1332 sce();
1333 return 0;
1336 static int
1337 meip(pacmac c12)
1340 if (parag)
1341 mepp(c12);
1342 else if (wordflag) /* save the tag */
1343 regline(meputmac, ONE);
1344 else
1345 SKIP;
1346 return 0;
1350 * only called for -me .pp or .sh, when parag is on
1352 static int
1353 mepp(pacmac c12)
1356 PP(c12); /* eats the line */
1357 return 0;
1361 * Start of a section heading; output the section name if doing words
1363 static int
1364 mesh(pacmac c12)
1367 if (parag)
1368 mepp(c12);
1369 else if (wordflag)
1370 defcomline(c12);
1371 else
1372 SKIP;
1373 return 0;
1377 * process a font setting
1379 static int
1380 mefont(pacmac c12)
1383 argconcat = 1;
1384 defcomline(c12);
1385 argconcat = 0;
1386 return 0;
1389 static int
1390 manfont(pacmac c12)
1393 return mefont(c12);
1396 static int
1397 manpp(pacmac c12)
1400 return mepp(c12);
1403 static void
1404 defcomline(pacmac c12)
1406 int c1, c2;
1408 frommac(c12, c1, c2);
1409 if (msflag && mac == MM && c2 == 'L') {
1410 if (disp || c1 == 'R') {
1411 noblock('L', 'E');
1412 } else {
1413 SKIP;
1414 putchar('.');
1417 else if (c1 == '.' && c2 == '.') {
1418 if (msflag) {
1419 SKIP;
1420 return;
1422 while (C == '.')
1423 /*VOID*/;
1425 ++inmacro;
1427 * Process the arguments to the macro
1429 switch (mac) {
1430 default:
1431 case MM:
1432 case MS:
1433 if (c1 <= 'Z' && msflag)
1434 regline(msputmac, ONE);
1435 else
1436 regline(msputmac, TWO);
1437 break;
1438 case ME:
1439 regline(meputmac, ONE);
1440 break;
1442 --inmacro;
1445 static void
1446 comline(void)
1448 int c1;
1449 int c2;
1450 pacmac c12;
1451 int mid;
1452 int lb, ub;
1453 int hit;
1454 static int tabsize = 0;
1455 static const struct mactab *mactab = NULL;
1456 const struct mactab *mp;
1458 if (mactab == 0)
1459 buildtab(&mactab, &tabsize);
1460 com:
1461 while (C == ' ' || c == '\t')
1463 comx:
1464 if ((c1 = c) == '\n')
1465 return;
1466 c2 = C;
1467 if (c1 == '.' && c2 != '.')
1468 inmacro = NO;
1469 if (msflag && c1 == '[') {
1470 refer(c2);
1471 return;
1473 if (parag && mac==MM && c1 == 'P' && c2 == '\n') {
1474 printf(".P\n");
1475 return;
1477 if (c2 == '\n')
1478 return;
1480 * Single letter macro
1482 if (mac == ME && (c2 == ' ' || c2 == '\t') )
1483 c2 = ' ';
1484 c12 = tomac(c1, c2);
1486 * binary search through the table of macros
1488 lb = 0;
1489 ub = tabsize - 1;
1490 while (lb <= ub) {
1491 mid = (ub + lb) / 2;
1492 mp = &mactab[mid];
1493 if (mp->macname < c12)
1494 lb = mid + 1;
1495 else if (mp->macname > c12)
1496 ub = mid - 1;
1497 else {
1498 hit = 1;
1499 #ifdef FULLDEBUG
1500 printf("preliminary hit macro %c%c ", c1, c2);
1501 #endif /* FULLDEBUG */
1502 switch (mp->condition) {
1503 case NONE:
1504 hit = YES;
1505 break;
1506 case FNEST:
1507 hit = (filesp == files);
1508 break;
1509 case NOMAC:
1510 hit = !inmacro;
1511 break;
1512 case MAC:
1513 hit = inmacro;
1514 break;
1515 case PARAG:
1516 hit = parag;
1517 break;
1518 case NBLK:
1519 hit = !keepblock;
1520 break;
1521 default:
1522 hit = 0;
1525 if (hit) {
1526 #ifdef FULLDEBUG
1527 printf("MATCH\n");
1528 #endif /* FULLDEBUG */
1529 switch ((*(mp->func))(c12)) {
1530 default:
1531 return;
1532 case COMX:
1533 goto comx;
1534 case COM:
1535 goto com;
1538 #ifdef FULLDEBUG
1539 printf("FAIL\n");
1540 #endif /* FULLDEBUG */
1541 break;
1544 defcomline(c12);
1547 static int
1548 macsort(const void *p1, const void *p2)
1550 const struct mactab *t1 = p1;
1551 const struct mactab *t2 = p2;
1553 return t1->macname - t2->macname;
1556 static int
1557 sizetab(const struct mactab *mp)
1559 int i;
1561 i = 0;
1562 if (mp) {
1563 for (; mp->macname; mp++, i++)
1564 /*VOID*/ ;
1566 return i;
1569 static struct mactab *
1570 macfill(struct mactab *dst, const struct mactab *src)
1573 if (src) {
1574 while (src->macname)
1575 *dst++ = *src++;
1577 return dst;
1580 static void
1581 usage(void)
1583 extern char *__progname;
1585 fprintf(stderr, "usage: %s [-ikpw ] [ -m a | e | l | m | s] [file ...]\n", __progname);
1586 exit(1);
1589 static void
1590 buildtab(const struct mactab **r_back, int *r_size)
1592 size_t size;
1593 const struct mactab *p1, *p2;
1594 struct mactab *back, *p;
1596 size = sizetab(troffmactab) + sizetab(ppmactab);
1597 p1 = p2 = NULL;
1598 if (msflag) {
1599 switch (mac) {
1600 case ME:
1601 p1 = memactab;
1602 break;
1603 case MM:
1604 p1 = msmactab;
1605 p2 = mmmactab;
1606 break;
1607 case MS:
1608 p1 = msmactab;
1609 break;
1610 case MA:
1611 p1 = manmactab;
1612 break;
1613 default:
1614 break;
1617 size += sizetab(p1);
1618 size += sizetab(p2);
1619 back = calloc(size + 2, sizeof(struct mactab));
1620 if (back == NULL)
1621 err(1, NULL);
1623 p = macfill(back, troffmactab);
1624 p = macfill(p, ppmactab);
1625 p = macfill(p, p1);
1626 p = macfill(p, p2);
1628 qsort(back, size, sizeof(struct mactab), macsort);
1629 *r_size = size;
1630 *r_back = back;
1634 * troff commands
1636 static const struct mactab troffmactab[] = {
1637 M(NONE, '\\','"', skip), /* comment */
1638 M(NOMAC, 'd','e', domacro), /* define */
1639 M(NOMAC, 'i','g', domacro), /* ignore till .. */
1640 M(NOMAC, 'a','m', domacro), /* append macro */
1641 M(NBLK, 'n','f', nf), /* filled */
1642 M(NBLK, 'c','e', ce), /* centered */
1644 M(NONE, 's','o', so), /* source a file */
1645 M(NONE, 'n','x', nx), /* go to next file */
1647 M(NONE, 't','m', skip), /* print string on tty */
1648 M(NONE, 'h','w', skip), /* exception hyphen words */
1649 M(NONE, 0,0, 0)
1653 * Preprocessor output
1655 static const struct mactab ppmactab[] = {
1656 M(FNEST, 'E','Q', EQ), /* equation starting */
1657 M(FNEST, 'T','S', intbl), /* table starting */
1658 M(FNEST, 'T','C', intbl), /* alternative table? */
1659 M(FNEST, 'T','&', intbl), /* table reformatting */
1660 M(NONE, 'T','E', outtbl),/* table ending */
1661 M(NONE, 'P','S', PS), /* picture starting */
1662 M(NONE, 0,0, 0)
1666 * Particular to ms and mm
1668 static const struct mactab msmactab[] = {
1669 M(NONE, 'T','L', skiptocom), /* title follows */
1670 M(NONE, 'F','S', skiptocom), /* start footnote */
1671 M(NONE, 'O','K', skiptocom), /* Other kws */
1673 M(NONE, 'N','R', skip), /* undocumented */
1674 M(NONE, 'N','D', skip), /* use supplied date */
1676 M(PARAG, 'P','P', PP), /* begin parag */
1677 M(PARAG, 'I','P', PP), /* begin indent parag, tag x */
1678 M(PARAG, 'L','P', PP), /* left blocked parag */
1680 M(NONE, 'A','U', AU), /* author */
1681 M(NONE, 'A','I', AU), /* authors institution */
1683 M(NONE, 'S','H', SH), /* section heading */
1684 M(NONE, 'S','N', SH), /* undocumented */
1685 M(NONE, 'U','X', UX), /* unix */
1687 M(NBLK, 'D','S', mssnblock), /* start display text */
1688 M(NBLK, 'K','S', mssnblock), /* start keep */
1689 M(NBLK, 'K','F', mssnblock), /* start float keep */
1690 M(NONE, 0,0, 0)
1693 static const struct mactab mmmactab[] = {
1694 M(NONE, 'H',' ', MMHU), /* -mm ? */
1695 M(NONE, 'H','U', MMHU), /* -mm ? */
1696 M(PARAG, 'P',' ', PP), /* paragraph for -mm */
1697 M(NBLK, 'N','S', mssnblock), /* undocumented */
1698 M(NONE, 0,0, 0)
1701 static const struct mactab memactab[] = {
1702 M(PARAG, 'p','p', mepp),
1703 M(PARAG, 'l','p', mepp),
1704 M(PARAG, 'n','p', mepp),
1705 M(NONE, 'i','p', meip),
1707 M(NONE, 's','h', mesh),
1708 M(NONE, 'u','h', mesh),
1710 M(NBLK, '(','l', mesnblock),
1711 M(NBLK, '(','q', mesnblock),
1712 M(NBLK, '(','b', mesnblock),
1713 M(NBLK, '(','z', mesnblock),
1714 M(NBLK, '(','c', mesnblock),
1716 M(NBLK, '(','d', mesnblock),
1717 M(NBLK, '(','f', mesnblock),
1718 M(NBLK, '(','x', mesnblock),
1720 M(NONE, 'r',' ', mefont),
1721 M(NONE, 'i',' ', mefont),
1722 M(NONE, 'b',' ', mefont),
1723 M(NONE, 'u',' ', mefont),
1724 M(NONE, 'q',' ', mefont),
1725 M(NONE, 'r','b', mefont),
1726 M(NONE, 'b','i', mefont),
1727 M(NONE, 'b','x', mefont),
1728 M(NONE, 0,0, 0)
1731 static const struct mactab manmactab[] = {
1732 M(PARAG, 'B','I', manfont),
1733 M(PARAG, 'B','R', manfont),
1734 M(PARAG, 'I','B', manfont),
1735 M(PARAG, 'I','R', manfont),
1736 M(PARAG, 'R','B', manfont),
1737 M(PARAG, 'R','I', manfont),
1739 M(PARAG, 'P','P', manpp),
1740 M(PARAG, 'L','P', manpp),
1741 M(PARAG, 'H','P', manpp),
1742 M(NONE, 0,0, 0)