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