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