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