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