GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/deroff/deroff.c Lines: 0 601 0.0 %
Date: 2016-12-06 Branches: 0 1924 0.0 %

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