xref: /illumos-gate/usr/src/cmd/troff/n1.c (revision 3d393ee6c37fa10ac512ed6d36109ad616dc7c1a)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved  	*/
29 
30 /*
31  * University Copyright- Copyright (c) 1982, 1986, 1988
32  * The Regents of the University of California
33  * All Rights Reserved
34  *
35  * University Acknowledgment- Portions of this document are derived from
36  * software developed by the University of California, Berkeley, and its
37  * contributors.
38  */
39 
40 #pragma ident	"%Z%%M%	%I%	%E% SMI"
41 
42 char *xxxvers = "@(#)roff:n1.c	2.13";
43 /*
44  * n1.c
45  *
46  *	consume options, initialization, main loop,
47  *	input routines, escape function calling
48  */
49 
50 #include <ctype.h>
51 #include <signal.h>
52 #include <sys/types.h>
53 #include <sys/stat.h>
54 #include <setjmp.h>
55 #include <time.h>
56 #include <stdarg.h>
57 #include <locale.h>
58 #include <fcntl.h>
59 #include <stdlib.h>
60 #include <string.h>
61 #ifdef 	EUC
62 #ifdef	NROFF
63 #include <stddef.h>
64 #include <limits.h>
65 #endif	/* NROFF */
66 #endif	/* EUC */
67 
68 #include "tdef.h"
69 #include "ext.h"
70 
71 #ifdef NROFF
72 #include "tw.h"
73 #endif
74 
75 #define	MAX_RECURSION_DEPTH	512
76 
77 jmp_buf sjbuf;
78 extern	void	fdprintf(int, char *, ...);
79 extern	char	*roff_sprintf(char *, char *, ...);
80 filep	ipl[NSO];
81 long	offl[NSO];
82 long	ioff;
83 char	*ttyp;
84 char	cfname[NSO+1][NS];	/*file name stack*/
85 int	cfline[NSO];		/*input line count stack*/
86 char	*progname;	/* program name (troff) */
87 #ifdef	EUC
88 #ifdef	NROFF
89 char	mbbuf1[MB_LEN_MAX + 1];
90 char	*mbbuf1p = mbbuf1;
91 wchar_t	twc = 0;
92 #endif	/* NROFF */
93 #endif	/* EUC */
94 
95 #ifdef	DEBUG
96 int	debug = 0;	/*debug flag*/
97 #endif	/* DEBUG */
98 
99 static char *sprintn(char *, long, int);
100 static int printn(long, int);
101 
102 int
103 main(int argc, char **argv)
104 {
105 	char	*p, *q;
106 	int	j;
107 	tchar i;
108 	int eileenct;		/*count to test for "Eileen's loop"*/
109 	extern void catch(), kcatch();
110 	char	**oargv, *getenv();
111 
112 	(void)setlocale(LC_ALL, "");
113 #if !defined(TEXT_DOMAIN)
114 #define TEXT_DOMAIN "SYS_TEST"
115 #endif
116 	(void)textdomain(TEXT_DOMAIN);
117 	progname = argv[0];
118 	if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
119 		signal(SIGHUP, catch);
120 	if (signal(SIGINT, catch) == SIG_IGN) {
121 		signal(SIGHUP, SIG_IGN);
122 		signal(SIGINT, SIG_IGN);
123 		signal(SIGQUIT, SIG_IGN);
124 	}
125 	signal(SIGPIPE, catch);
126 	signal(SIGTERM, kcatch);
127 	oargv = argv;
128 	strcpy(cfname[0], "<standard input>");
129 	mrehash();
130 	nrehash();
131 	init0();
132 #ifdef EUC
133 #ifdef NROFF
134 	(void)localize();
135 #endif /* NROFF */
136 #endif /* EUC */
137 	if ((p = getenv("TYPESETTER")) != 0)
138 		strcpy(devname, p);
139 	while (--argc > 0 && (++argv)[0][0] == '-')
140 		switch (argv[0][1]) {
141 
142 		case 'F':	/* switch font tables from default */
143 			if (argv[0][2] != '\0') {
144 				strcpy(termtab, &argv[0][2]);
145 				strcpy(fontfile, &argv[0][2]);
146 			} else {
147 				argv++; argc--;
148 				if (argv[0] != '\0') {
149 					strcpy(termtab, argv[0]);
150 					strcpy(fontfile, argv[0]);
151 				} else
152 					errprint(gettext("missing the font directory"));
153 			}
154 			continue;
155 		case 0:
156 			goto start;
157 		case 'i':
158 			stdi++;
159 			continue;
160 		case 'q':
161 #ifdef	NROFF
162 			quiet++;
163 			save_tty();
164 #else
165 			errprint(gettext("-q option ignored in troff"));
166 #endif	/* NROFF */
167 			continue;
168 		case 'n':
169 			npn = ctoi(&argv[0][2]);
170 			continue;
171 		case 'u':	/* set emboldening amount */
172 			bdtab[3] = ctoi(&argv[0][2]);
173 			if (bdtab[3] < 0 || bdtab[3] > 50)
174 				bdtab[3] = 0;
175 			continue;
176 		case 's':
177 			if (!(stop = ctoi(&argv[0][2])))
178 				stop++;
179 			continue;
180 		case 't':
181 			ptid = 1;
182 			continue;
183 		case 'r':
184 			if (&argv[0][2] != '\0' && strlen(&argv[0][2]) >= 2 && &argv[0][3] != '\0')
185 			eibuf = roff_sprintf(ibuf+strlen(ibuf), ".nr %c %s\n",
186 				argv[0][2], &argv[0][3]);
187 			else
188 				errprint(gettext("wrong options"));
189 			continue;
190 		case 'c':
191 		case 'm':
192 			if (mflg++ >= NMF) {
193 				errprint(gettext("Too many macro packages: %s"),
194 					 argv[0]);
195 				continue;
196 			}
197 		        if (argv[0][2] == '\0') {
198 				errprint(gettext("No library provided with -m"));
199 				done(02);
200 			}
201 			if (getenv("TROFFMACS") != '\0') {
202 			     if (tryfile(getenv("TROFFMACS"), &argv[0][2], nmfi))
203 			       nmfi++;
204 			} else
205 			  if (tryfile("/usr/share/lib/tmac/", &argv[0][2], nmfi)
206 			  || tryfile("/usr/share/lib/tmac/tmac.", &argv[0][2], nmfi))
207 				nmfi++;
208 			  else {
209 				errprint(gettext("Cannot find library %s\n"),
210 					argv[0]);
211 				done(02);
212 			  }
213 			continue;
214 		case 'o':
215 			getpn(&argv[0][2]);
216 			continue;
217 		case 'T':
218 			strcpy(devname, &argv[0][2]);
219 			dotT++;
220 			continue;
221 #ifdef NROFF
222 		case 'h':
223 			hflg++;
224 			continue;
225 		case 'z':
226 			no_out++;
227 			continue;
228 		case 'e':
229 			eqflg++;
230 			continue;
231 #endif
232 #ifndef NROFF
233 		case 'z':
234 			no_out++;
235 		case 'a':
236 			ascii = 1;
237 			nofeed++;
238 			continue;
239 		case 'f':
240 			nofeed++;
241 			continue;
242 #endif
243 		case '#':
244 #ifdef	DEBUG
245 			debug = ctoi(&argv[0][2]);
246 #else
247 			errprint("DEBUG not enabled");
248 #endif	/* DEBUG */
249 			continue;
250 		default:
251 			errprint(gettext("unknown option %s"), argv[0]);
252 			done(02);
253 		}
254 
255 start:
256 	init1(oargv[0][0]);
257 	argp = argv;
258 	rargc = argc;
259 	nmfi = 0;
260 	init2();
261 	setjmp(sjbuf);
262 	eileenct = 0;		/*reset count for "Eileen's loop"*/
263 loop:
264 	copyf = lgf = nb = nflush = nlflg = 0;
265 	if (ip && rbf0(ip) == 0 && ejf && frame->pframe <= ejl) {
266 		nflush++;
267 		trap = 0;
268 		eject((struct s *)0);
269 #ifdef	DEBUG
270 	if (debug & DB_LOOP)
271 		fdprintf(stderr, "loop: NL=%d, ejf=%d, lss=%d, eileenct=%d\n",
272 			numtab[NL].val, ejf, lss, eileenct);
273 #endif	/* DEBUG */
274 		if (eileenct > 20) {
275 			errprint(gettext("job looping; check abuse of macros"));
276 			ejf = 0;	/*try to break Eileen's loop*/
277 			eileenct = 0;
278 		} else
279 			eileenct++;
280 		goto loop;
281 	}
282 	eileenct = 0;		/*reset count for "Eileen's loop"*/
283 	i = getch();
284 	if (pendt)
285 		goto Lt;
286 	if ((j = cbits(i)) == XPAR) {
287 		copyf++;
288 		tflg++;
289 		while (cbits(i) != '\n')
290 			pchar(i = getch());
291 		tflg = 0;
292 		copyf--;
293 		goto loop;
294 	}
295 	if (j == cc || j == c2) {
296 		if (j == c2)
297 			nb++;
298 		copyf++;
299 		while ((j = cbits(i = getch())) == ' ' || j == '\t')
300 			;
301 		ch = i;
302 		copyf--;
303 		control(getrq(), 1);
304 		flushi();
305 		goto loop;
306 	}
307 Lt:
308 	ch = i;
309 	text();
310 	if (nlflg)
311 		numtab[HP].val = 0;
312 	goto loop;
313 }
314 
315 
316 int
317 tryfile(pat, fn, idx)
318 char *pat, *fn;
319 int idx;
320 {
321 	strcpy(mfiles[idx], pat);
322 	strcat(mfiles[idx], fn);
323 	if (access(mfiles[idx], 4) == -1)
324 		return(0);
325 	else return(1);
326 }
327 
328 void catch()
329 {
330 	done3(01);
331 }
332 
333 
334 void kcatch()
335 {
336 	signal(SIGTERM, SIG_IGN);
337 	done3(01);
338 }
339 
340 int
341 init0()
342 {
343 	eibuf = ibufp = ibuf;
344 	ibuf[0] = 0;
345 	numtab[NL].val = -1;
346 	return (0);
347 }
348 
349 
350 int
351 init1(a)
352 char	a;
353 {
354 	char	*p;
355 	int i;
356 
357 	p = tmp_name;
358 	if (a == 'a')
359 		p = &p[9];
360 	if ((ibf = mkstemp(p)) == -1) {
361 		errprint(gettext("cannot create temp file."));
362 		exit(-1);
363 	}
364 	unlkp = p;
365 	for (i = NTRTAB; --i; )
366 		trtab[i] = i;
367 	trtab[UNPAD] = ' ';
368 	return (0);
369 }
370 
371 
372 int
373 init2()
374 {
375 	int	i, j;
376 	extern char	*setbrk();
377 	extern char	*ttyname();
378 
379 	ttyod = 2;
380 	if ((ttyp=ttyname(j=0)) != 0 || (ttyp=ttyname(j=1)) != 0 || (ttyp=ttyname(j=2)) != 0)
381 		;
382 	else
383 		ttyp = "notty";
384 	iflg = j;
385 	if (ascii)
386 		mesg(0);
387 	obufp = obuf;
388 	ptinit();
389 	mchbits();
390 	cvtime();
391 	numtab[PID].val = getpid();
392 	olinep = oline;
393 	ioff = 0;
394 	numtab[HP].val = init = 0;
395 	numtab[NL].val = -1;
396 	nfo = 0;
397 	ifile = 0;
398 	copyf = raw = 0;
399 	eibuf = roff_sprintf(ibuf+strlen(ibuf), ".ds .T %s\n", devname);
400 	numtab[CD].val = -1;	/* compensation */
401 	cpushback(ibuf);
402 	ibufp = ibuf;
403 	nx = mflg;
404 	frame = stk = (struct s *)setbrk(DELTA);
405 	dip = &d[0];
406 	nxf = frame + 1;
407 #ifdef INCORE
408 	for (i = 0; i < NEV; i++) {
409 		extern tchar corebuf[];
410 		*(struct env *)&corebuf[i * sizeof(env)/sizeof(tchar)] = env;
411 	}
412 #else
413 	for (i = NEV; i--; )
414 		write(ibf, (char *) & env, sizeof(env));
415 #endif
416 	return (0);
417 }
418 
419 int
420 cvtime()
421 {
422 	time_t	tt;
423 	struct tm *tm;
424 
425 	tt = time((time_t *) 0);
426 	tm = localtime(&tt);
427 	numtab[DY].val = tm->tm_mday;
428 	numtab[DW].val = tm->tm_wday + 1;
429 	numtab[YR].val = tm->tm_year;
430 	numtab[MO].val = tm->tm_mon + 1;
431 
432 	return (0);
433 }
434 
435 
436 int
437 ctoi(s)
438 	char *s;
439 {
440 	int	n;
441 
442 	while (*s == ' ')
443 		s++;
444 	n = 0;
445 	while (isdigit((unsigned char)*s))
446 		n = 10 * n + *s++ - '0';
447 	return n;
448 }
449 
450 
451 int
452 mesg(f)
453 int	f;
454 {
455 	static int	mode;
456 	struct stat stbuf;
457 
458 	if (!f) {
459 		stat(ttyp, &stbuf);
460 		mode = stbuf.st_mode;
461 		chmod(ttyp, mode & ~0122);	/* turn off writing for others */
462 	} else {
463 		if (ttyp && *ttyp && mode)
464 			chmod(ttyp, mode);
465 	}
466 
467 	return (0);
468 }
469 
470 int
471 errprint(s, s1, s2, s3, s4, s5)	/* error message printer */
472 	char *s, *s1, *s2, *s3, *s4, *s5;
473 {
474 	fdprintf(stderr, "%s: ", progname);
475 	fdprintf(stderr, s, s1, s2, s3, s4, s5);
476 	if (numtab[CD].val > 0)
477 		fdprintf(stderr, gettext("; line %d, file %s"), numtab[CD].val,
478 			 cfname[ifi]);
479 	fdprintf(stderr, "\n");
480 	stackdump();
481 #ifdef	DEBUG
482 	if (debug)
483 		abort();
484 #endif	/* DEBUG */
485 	return (0);
486 }
487 
488 
489 /*
490  * Scaled down version of C Library printf.
491  * Only %s %u %d (==%u) %o %c %x %D are recognized.
492  */
493 #undef putchar
494 #define	putchar(n)	(*pfbp++ = (n))	/* NO CHECKING! */
495 
496 static char	pfbuf[NTM];
497 static char	*pfbp = pfbuf;
498 int	stderr	 = 2;	/* NOT stdio value */
499 
500 void
501 fdprintf(int fd, char *fmt, ...)
502 {
503 	int	c;
504 	char	*s;
505 	int	i;
506 	va_list	ap;
507 
508 	pfbp = pfbuf;
509 	va_start(ap, fmt);
510 loop:
511 	while ((c = *fmt++) != '%') {
512 		if (c == '\0') {
513 			if (fd == stderr)
514 				write(stderr, pfbuf, pfbp - pfbuf);
515 			else {
516 				*pfbp = 0;
517 				pfbp = pfbuf;
518 				while (*pfbp) {
519 					*obufp++ = *pfbp++;
520 					if (obufp >= &obuf[OBUFSZ])
521 						flusho();
522 				}
523 			}
524 			va_end(ap);
525 			return;
526 		}
527 		putchar(c);
528 	}
529 	c = *fmt++;
530 	if (c == 'd') {
531 		i = va_arg(ap, int);
532 		if (i < 0) {
533 			putchar('-');
534 			i = -i;
535 		}
536 		printn((long)i, 10);
537 	} else if (c == 'u' || c == 'o' || c == 'x')
538 		printn(va_arg(ap, long), c == 'o' ? 8 : (c == 'x' ? 16 : 10));
539 	else if (c == 'c') {
540 		if (c > 0177 || c < 040)
541 			putchar('\\');
542 		putchar(va_arg(ap, int) & 0177);
543 	} else if (c == 's') {
544 		s = va_arg(ap, char *);
545 		while (c = *s++)
546 			putchar(c);
547 	} else if (c == 'D') {
548 		printn(va_arg(ap, long), 10);
549 	} else if (c == 'O') {
550 		printn(va_arg(ap, long), 8);
551 	}
552 	goto loop;
553 }
554 
555 
556 /*
557  * Print an unsigned integer in base b.
558  */
559 static int
560 printn(n, b)
561 	long	n;
562 	int	b;
563 {
564 	long	a;
565 
566 	if (n < 0) {	/* shouldn't happen */
567 		putchar('-');
568 		n = -n;
569 	}
570 	if (a = n / b)
571 		printn(a, b);
572 	putchar("0123456789ABCDEF"[(int)(n%b)]);
573 
574 	return (0);
575 }
576 
577 /* scaled down version of library roff_sprintf */
578 /* same limits as fdprintf */
579 /* returns pointer to \0 that ends the string */
580 
581 /* VARARGS2 */
582 char *roff_sprintf(char *str, char *fmt, ...)
583 {
584 	int	c;
585 	char	*s;
586 	int	i;
587 	va_list ap;
588 
589 	va_start(ap, fmt);
590 loop:
591 	while ((c = *fmt++) != '%') {
592 		if (c == '\0') {
593 			*str = 0;
594 			va_end(ap);
595 			return str;
596 		}
597 		*str++ = c;
598 	}
599 	c = *fmt++;
600 	if (c == 'd') {
601 		i = va_arg(ap, int);
602 		if (i < 0) {
603 			*str++ = '-';
604 			i = -i;
605 		}
606 		str = sprintn(str, (long)i, 10);
607 	} else if (c == 'u' || c == 'o' || c == 'x')
608 		str = sprintn(str, va_arg(ap, long), c == 'o' ? 8 : (c == 'x' ? 16 : 10));
609 	else if (c == 'c') {
610 		if (c > 0177 || c < 040)
611 			*str++ = '\\';
612 		*str++ = va_arg(ap, int) & 0177;
613 	} else if (c == 's') {
614 		s = va_arg(ap, char *);
615 		while (c = *s++)
616 			*str++ = c;
617 	} else if (c == 'D') {
618 		str = sprintn(str, va_arg(ap, long), 10);
619 	} else if (c == 'O') {
620 		str = sprintn(str, va_arg(ap, unsigned) , 8);
621 	}
622 	goto loop;
623 }
624 
625 /*
626  * Print an unsigned integer in base b.
627  */
628 static char *sprintn(s, n, b)
629 	char *s;
630 	long n;
631 	int b;
632 {
633 	long	a;
634 
635 	if (n < 0) {	/* shouldn't happen */
636 		*s++ = '-';
637 		n = -n;
638 	}
639 	if (a = n / b)
640 		s = sprintn(s, a, b);
641 	*s++ = "0123456789ABCDEF"[(int)(n%b)];
642 	return s;
643 }
644 
645 
646 int
647 control(a, b)
648 int	a, b;
649 {
650 	int	j;
651 
652 	if (a == 0 || (j = findmn(a)) == -1)
653 		return(0);
654 
655 	/*
656 	 * Attempt to find endless recursion at runtime. Arbitrary
657 	 * recursion limit of MAX_RECURSION_DEPTH was chosen as
658 	 * it is extremely unlikely that a correct nroff/troff
659 	 * invocation would exceed this value.
660 	 */
661 
662 	if (frame != stk) {
663 		int frame_cnt = 0;
664 		struct s *p;
665 
666 		for (p = frame; p != stk; p = p->pframe)
667 			frame_cnt++;
668 		if (frame_cnt > MAX_RECURSION_DEPTH) {
669 			errprint(
670 			    gettext("Exceeded maximum stack size (%d) when "
671 			    "executing macro %c%c. Stack dump follows"),
672 			    MAX_RECURSION_DEPTH,
673 			    frame->mname & 0177, (frame->mname >> BYTE) & 0177);
674 			edone(02);
675 		}
676 	}
677 
678 #ifdef	DEBUG
679 	if (debug & DB_MAC)
680 		fdprintf(stderr, "control: macro %c%c, contab[%d]\n",
681 			a&0177, (a>>BYTE)&0177 ? (a>>BYTE)&0177 : ' ', j);
682 #endif	/* DEBUG */
683 	if (contab[j].f == 0) {
684 		nxf->nargs = 0;
685 		if (b)
686 			collect();
687 		flushi();
688 		return pushi((filep)contab[j].mx, a);
689 	} else if (b)
690 		return((*contab[j].f)(0));
691 	else
692 		return(0);
693 }
694 
695 int
696 getrq()
697 {
698 	int	i, j;
699 
700 	if (((i = getach()) == 0) || ((j = getach()) == 0))
701 		goto rtn;
702 	i = PAIR(i, j);
703 rtn:
704 	return(i);
705 }
706 
707 /*
708  * table encodes some special characters, to speed up tests
709  * in getchar, viz FLSS, RPT, f, \b, \n, fc, tabch, ldrch
710  */
711 
712 char
713 gchtab[] = {
714 	000,004,000,000,010,000,000,000, /* fc, ldr */
715 	001,002,001,000,001,000,000,000, /* \b, tab, nl, RPT */
716 	000,000,000,000,000,000,000,000,
717 	000,001,000,000,000,000,000,000, /* FLSS */
718 	000,000,000,000,000,000,000,000,
719 	000,000,000,000,000,000,000,000,
720 	000,000,000,000,000,000,000,000,
721 	000,000,000,000,000,000,000,000,
722 	000,000,000,000,000,000,000,000,
723 	000,000,000,000,000,000,000,000,
724 	000,000,000,000,000,000,000,000,
725 	000,000,000,000,000,000,000,000,
726 	000,000,000,000,000,000,001,000, /* f */
727 	000,000,000,000,000,000,000,000,
728 	000,000,000,000,000,000,000,000,
729 	000,000,000,000,000,000,000,000,
730 };
731 
732 tchar
733 getch()
734 {
735 	int	k;
736 	tchar i, j;
737 	tchar setht(), setslant();
738 
739 g0:
740 	if (i = ch) {
741 #ifdef	DEBUG
742 		if (debug & DB_GETC)
743 			fdprintf(stderr, "getch: ch is %x (%c)\n",
744 				ch, (ch&0177) < 040 ? 0177 : ch&0177);
745 #endif	/* DEBUG */
746 		if (cbits(i) == '\n')
747 			nlflg++;
748 		ch = 0;
749 		return(i);
750 	}
751 
752 #ifdef	DEBUG
753 	if (nlflg)
754 		if (debug & DB_GETC)
755 			fdprintf(stderr,"getch: nlflg is %x\n", nlflg);
756 #endif	/* DEBUG */
757 	if (nlflg)
758 		return('\n');
759 	i = getch0();
760 #ifdef	DEBUG
761 	if (debug & DB_GETC)
762 		fdprintf(stderr, "getch: getch0 returns %x (%c)\n",
763 			i, (i&0177) < 040 ? 0177 : i&0177);
764 #endif	/* DEBUG */
765 	if (ismot(i))
766 		return(i);
767 	k = cbits(i);
768 	if (k != ESC) {
769 		/*
770 		 * gchtab[] has only 128 entries
771 		 * if k is out of the range, it should be
772 		 * handled as gchtab[k] == 0
773 		 */
774 		if (!isascii(k) || gchtab[k]==0)
775 			return(i);
776 		if (k == '\n') {
777 			if (cbits(i) == '\n') {
778 				nlflg++;
779 				if (ip == 0)
780 					numtab[CD].val++; /* line number */
781 			}
782 			return(k);
783 		}
784 		if (k == FLSS) {
785 			copyf++;
786 			raw++;
787 			i = getch0();
788 			if (!fi)
789 				flss = i;
790 			copyf--;
791 			raw--;
792 			goto g0;
793 		}
794 		if (k == RPT) {
795 			setrpt();
796 			goto g0;
797 		}
798 		if (!copyf) {
799 			if (k == 'f' && lg && !lgf) {
800 				i = getlg(i);
801 				return(i);
802 			}
803 			if (k == fc || k == tabch || k == ldrch) {
804 				if ((i = setfield(k)) == 0)
805 					goto g0;
806 				else
807 					return(i);
808 			}
809 			if (k == '\b') {
810 				i = makem(-width(' ' | chbits));
811 				return(i);
812 			}
813 		}
814 		return(i);
815 	}
816 	k = cbits(j = getch0());
817 	if (ismot(j))
818 		return(j);
819 	switch (k) {
820 
821 	case 'X':	/* \X'...' for copy through */
822 		setxon();
823 		goto g0;
824 	case '\n':	/* concealed newline */
825 		goto g0;
826 	case 'n':	/* number register */
827 		setn();
828 		goto g0;
829 	case '*':	/* string indicator */
830 		setstr();
831 		goto g0;
832 	case '$':	/* argument indicator */
833 		seta();
834 		goto g0;
835 	case '{':	/* LEFT */
836 		i = LEFT;
837 		goto gx;
838 	case '}':	/* RIGHT */
839 		i = RIGHT;
840 		goto gx;
841 	case '"':	/* comment */
842 		while (cbits(i = getch0()) != '\n')
843 			;
844 		nlflg++;
845 		if (ip == 0)
846 			numtab[CD].val++;
847 		return(i);
848 	case ESC:	/* double backslash */
849 		i = eschar;
850 		goto gx;
851 	case 'e':	/* printable version of current eschar */
852 		i = PRESC;
853 		goto gx;
854 	case ' ':	/* unpaddable space */
855 		i = UNPAD;
856 		goto gx;
857 	case '\'':	/* \(aa */
858 		i = ACUTE;
859 		goto gx;
860 	case '`':	/* \(ga */
861 		i = GRAVE;
862 		goto gx;
863 	case '_':	/* \(ul */
864 		i = UNDERLINE;
865 		goto gx;
866 	case '-':	/* current font minus */
867 		i = MINUS;
868 		goto gx;
869 	case '&':	/* filler */
870 		i = FILLER;
871 		goto gx;
872 	case 'c':	/* to be continued */
873 		i = CONT;
874 		goto gx;
875 	case '!':	/* transparent indicator */
876 		i = XPAR;
877 		goto gx;
878 	case 't':	/* tab */
879 		i = '\t';
880 		return(i);
881 	case 'a':	/* leader (SOH) */
882 		i = LEADER;
883 		return(i);
884 	case '%':	/* ohc */
885 		i = OHC;
886 		return(i);
887 	case 'g':	/* return format of a number register */
888 		setaf();
889 		goto g0;
890 	case 'N':	/* absolute character number */
891 		i = setabs();
892 		goto gx;
893 	case '.':	/* . */
894 		i = '.';
895 gx:
896 		setsfbits(i, sfbits(j));
897 		return(i);
898 	}
899 	if (copyf) {
900 		*pbp++ = j;
901 		return(eschar);
902 	}
903 	switch (k) {
904 
905 	case 'p':	/* spread */
906 		spread++;
907 		goto g0;
908 	case '(':	/* special char name */
909 		if ((i = setch()) == 0)
910 			goto g0;
911 		return(i);
912 	case 's':	/* size indicator */
913 		setps();
914 		goto g0;
915 	case 'H':	/* character height */
916 		return(setht());
917 	case 'S':	/* slant */
918 		return(setslant());
919 	case 'f':	/* font indicator */
920 		setfont(0);
921 		goto g0;
922 	case 'w':	/* width function */
923 		setwd();
924 		goto g0;
925 	case 'v':	/* vert mot */
926 		if (i = vmot())
927 			return(i);
928 		goto g0;
929 	case 'h': 	/* horiz mot */
930 		if (i = hmot())
931 			return(i);
932 		goto g0;
933 	case 'z':	/* zero with char */
934 		return(setz());
935 	case 'l':	/* hor line */
936 		setline();
937 		goto g0;
938 	case 'L':	/* vert line */
939 		setvline();
940 		goto g0;
941 	case 'D':	/* drawing function */
942 		setdraw();
943 		goto g0;
944 	case 'b':	/* bracket */
945 		setbra();
946 		goto g0;
947 	case 'o':	/* overstrike */
948 		setov();
949 		goto g0;
950 	case 'k':	/* mark hor place */
951 		if ((k = findr(getsn())) != -1) {
952 			numtab[k].val = numtab[HP].val;
953 		}
954 		goto g0;
955 	case '0':	/* number space */
956 		return(makem(width('0' | chbits)));
957 #ifdef NROFF
958 	case '|':
959 	case '^':
960 		goto g0;
961 #else
962 	case '|':	/* narrow space */
963 		return(makem((int)(EM)/6));
964 	case '^':	/* half narrow space */
965 		return(makem((int)(EM)/12));
966 #endif
967 	case 'x':	/* extra line space */
968 		if (i = xlss())
969 			return(i);
970 		goto g0;
971 	case 'u':	/* half em up */
972 	case 'r':	/* full em up */
973 	case 'd':	/* half em down */
974 		return(sethl(k));
975 	default:
976 		return(j);
977 	}
978 	/* NOTREACHED */
979 }
980 
981 int
982 setxon()	/* \X'...' for copy through */
983 {
984 	tchar xbuf[NC];
985 	tchar *i;
986 	tchar c;
987 	int delim, k;
988 
989 	if (ismot(c = getch()))
990 		return (0);
991 	delim = cbits(c);
992 	i = xbuf;
993 	*i++ = XON;
994 	while ((k = cbits(c = getch())) != delim && k != '\n' && i < xbuf+NC-1) {
995 		if (k == ' ')
996 			setcbits(c, UNPAD);
997 		*i++ = c | ZBIT;
998 	}
999 	*i++ = XOFF;
1000 	*i = 0;
1001 	pushback(xbuf);
1002 
1003 	return (0);
1004 }
1005 
1006 
1007 char	ifilt[32] = {
1008 	0, 001, 002, 003, 0, 005, 006, 007, 010, 011, 012};
1009 
1010 tchar getch0()
1011 {
1012 	int	j;
1013 	tchar i;
1014 #ifdef	EUC
1015 #ifdef	NROFF
1016 	int	n;
1017 	int col_index;
1018 #endif	/* NROFF */
1019 #endif	/* EUC */
1020 
1021 again:
1022 	if (pbp > lastpbp)
1023 		i = *--pbp;
1024 	else if (ip) {
1025 #ifdef INCORE
1026 		extern tchar corebuf[];
1027 		i = corebuf[ip];
1028 		if (i == 0)
1029 			i = rbf();
1030 		else {
1031 			if ((++ip & (BLK - 1)) == 0) {
1032 				--ip;
1033 				(void)rbf();
1034 			}
1035 		}
1036 #else
1037 		i = rbf();
1038 #endif
1039 	} else {
1040 		if (donef || ndone)
1041 			done(0);
1042 		if (nx || ibufp >= eibuf) {
1043 			if (nfo==0) {
1044 g0:
1045 				if (nextfile()) {
1046 					if (ip)
1047 						goto again;
1048 					if (ibufp < eibuf)
1049 						goto g2;
1050 				}
1051 			}
1052 			nx = 0;
1053 			if ((j = read(ifile, ibuf, IBUFSZ)) <= 0)
1054 				goto g0;
1055 			ibufp = ibuf;
1056 			eibuf = ibuf + j;
1057 			if (ip)
1058 				goto again;
1059 		}
1060 g2:
1061 #ifndef	EUC
1062 		i = *ibufp++ & 0177;
1063 		ioff++;
1064 		if (i >= 040 && i < 0177)
1065 #else
1066 #ifndef	NROFF
1067 		i = *ibufp++ & 0177;
1068 		ioff++;
1069 		if (i >= 040 && i < 0177)
1070 #else
1071 		i = *ibufp++ & 0377;
1072 		*mbbuf1p++ = i;
1073 		*mbbuf1p = 0;
1074 		if (!multi_locale) {
1075 			twc = 0;
1076 			mbbuf1p = mbbuf1;
1077 		} else if ((n = mbtowc(&twc, mbbuf1, MB_CUR_MAX)) <= 0) {
1078 			if (mbbuf1p >= mbbuf1 + MB_CUR_MAX) {
1079 				i &= ~(MBMASK | CSMASK);
1080 				twc = 0;
1081 				mbbuf1p = mbbuf1;
1082 				*mbbuf1p = 0;
1083 			} else {
1084 				i |= (MIDDLEOFMB);
1085 			}
1086 		} else {
1087 			if (n > 1)
1088 				i |= (LASTOFMB);
1089 			else
1090 				i |= (BYTE_CHR);
1091 			if (isascii(twc)) {
1092 				col_index = 0;
1093 			} else {
1094 				if ((col_index = wcwidth(twc)) < 0)
1095 					col_index = 0;
1096 			}
1097 			setcsbits(i, col_index);
1098 			twc = 0;
1099 			mbbuf1p = mbbuf1;
1100 		}
1101 		ioff++;
1102 		if (i >= 040 && i != 0177)
1103 #endif	/* NROFF */
1104 #endif	/* EUC */
1105 			goto g4;
1106 		if (i != 0177)
1107 			i = ifilt[i];
1108 	}
1109 	if (cbits(i) == IMP && !raw)
1110 		goto again;
1111 	if ((i == 0 || i == 0177) && !init && !raw) {
1112 		goto again;
1113 	}
1114 g4:
1115 #ifndef EUC
1116 	if (copyf == 0 && (i & ~BYTEMASK) == 0)
1117 #else
1118 #ifndef NROFF
1119 	if (copyf == 0 && (i & ~BYTEMASK) == 0)
1120 #else
1121 	if (copyf == 0 && (i & ~CHMASK) == 0)
1122 #endif /* NROFF */
1123 #endif /* EUC */
1124 		i |= chbits;
1125 #ifdef EUC
1126 #ifdef NROFF
1127 	if (multi_locale)
1128 		if (i & MBMASK1)
1129 			i |= ZBIT;
1130 #endif /* NROFF */
1131 #endif /* EUC */
1132 	if (cbits(i) == eschar && !raw)
1133 		setcbits(i, ESC);
1134 	return(i);
1135 }
1136 
1137 int
1138 pushback(b)
1139 tchar *b;
1140 {
1141 	tchar *ob = b;
1142 
1143 	while (*b++)
1144 		;
1145 	b--;
1146 	while (b > ob && pbp < &pbbuf[NC-3])
1147 		*pbp++ = *--b;
1148 	if (pbp >= &pbbuf[NC-3]) {
1149 		errprint(gettext("pushback overflow"));
1150 		done(2);
1151 	}
1152 
1153 	return (0);
1154 }
1155 
1156 int
1157 cpushback(b)
1158 char *b;
1159 {
1160 	char *ob = b;
1161 
1162 	while (*b++)
1163 		;
1164 	b--;
1165 	while (b > ob && pbp < &pbbuf[NC-3])
1166 		*pbp++ = *--b;
1167 	if (pbp >= &pbbuf[NC-3]) {
1168 		errprint(gettext("cpushback overflow"));
1169 		done(2);
1170 	}
1171 
1172 	return (0);
1173 }
1174 
1175 int
1176 nextfile()
1177 {
1178 	char	*p;
1179 
1180 n0:
1181 	if (ifile)
1182 		close(ifile);
1183 	if (nx  ||  nmfi < mflg) {
1184 		p = mfiles[nmfi++];
1185 		if (*p != 0)
1186 			goto n1;
1187 	}
1188 	if (ifi > 0) {
1189 		if (popf())
1190 			goto n0; /* popf error */
1191 		return(1); /* popf ok */
1192 	}
1193 	if (rargc-- <= 0) {
1194 		if ((nfo -= mflg) && !stdi)
1195 			done(0);
1196 		nfo++;
1197 		numtab[CD].val = ifile = stdi = mflg = 0;
1198 		strcpy(cfname[ifi], "<standard input>");
1199 		ioff = 0;
1200 		return(0);
1201 	}
1202 	p = (argp++)[0];
1203 n1:
1204 	numtab[CD].val = 0;
1205 	if (p[0] == '-' && p[1] == 0) {
1206 		ifile = 0;
1207 		strcpy(cfname[ifi], "<standard input>");
1208 	} else if ((ifile = open(p, 0)) < 0) {
1209 		errprint(gettext("cannot open file %s"), p);
1210 		nfo -= mflg;
1211 		done(02);
1212 	} else
1213 		strcpy(cfname[ifi],p);
1214 	nfo++;
1215 	ioff = 0;
1216 	return(0);
1217 }
1218 
1219 
1220 int
1221 popf()
1222 {
1223 	int	i;
1224 	char	*p, *q;
1225 	extern char	*ttyname();
1226 
1227 	ioff = offl[--ifi];
1228 	numtab[CD].val = cfline[ifi];		/*restore line counter*/
1229 	ip = ipl[ifi];
1230 	if ((ifile = ifl[ifi]) == 0) {
1231 		p = xbuf;
1232 		q = ibuf;
1233 		ibufp = xbufp;
1234 		eibuf = xeibuf;
1235 		while (q < eibuf)
1236 			*q++ = *p++;
1237 		return(0);
1238 	}
1239 	if (lseek(ifile, (long)(ioff & ~(IBUFSZ-1)), 0) == (long) -1
1240 	   || (i = read(ifile, ibuf, IBUFSZ)) < 0)
1241 		return(1);
1242 	eibuf = ibuf + i;
1243 	ibufp = ibuf;
1244 	if (ttyname(ifile) == 0)
1245 		/* was >= ... */
1246 		if ((ibufp = ibuf + (int)(ioff & (IBUFSZ - 1))) > eibuf)
1247 			return(1);
1248 	return(0);
1249 }
1250 
1251 
1252 int
1253 flushi()
1254 {
1255 	if (nflush)
1256 		return (0);
1257 	ch = 0;
1258 	copyf++;
1259 	while (!nlflg) {
1260 		if (donef && (frame == stk))
1261 			break;
1262 		getch();
1263 	}
1264 	copyf--;
1265 
1266 	return (0);
1267 }
1268 
1269 
1270 int
1271 getach()
1272 {
1273 	tchar i;
1274 	int	j;
1275 
1276 	lgf++;
1277 	j = cbits(i = getch());
1278 #ifndef	EUC
1279 	if (ismot(i) || j == ' ' || j == '\n' || j & 0200) {
1280 #else
1281 #ifndef	NROFF
1282 	if (ismot(i) || j == ' ' || j == '\n' || j & 0200) {
1283 #else
1284 	if (ismot(i) || j == ' ' || j == '\n' || j > 0200) {
1285 #endif	/* NROFF */
1286 #endif	/* EUC */
1287 
1288 		ch = i;
1289 		j = 0;
1290 	}
1291 	lgf--;
1292 	return(j & 0177);
1293 }
1294 
1295 
1296 int
1297 casenx()
1298 {
1299 	lgf++;
1300 	skip();
1301 	getname();
1302 	nx++;
1303 	if (nmfi > 0)
1304 		nmfi--;
1305 	strcpy(mfiles[nmfi], nextf);
1306 	nextfile();
1307 	nlflg++;
1308 	ip = 0;
1309 	pendt = 0;
1310 	frame = stk;
1311 	nxf = frame + 1;
1312 
1313 	return (0);
1314 }
1315 
1316 
1317 int
1318 getname()
1319 {
1320 	int	j, k;
1321 	tchar i;
1322 
1323 	lgf++;
1324 	for (k = 0; k < (NS - 1); k++) {
1325 #ifndef EUC
1326 		if (((j = cbits(i = getch())) <= ' ') || (j > 0176))
1327 #else
1328 #ifndef NROFF
1329 		if (((j = cbits(i = getch())) <= ' ') || (j > 0176))
1330 #else
1331 		if (((j = cbits(i = getch())) <= ' ') || (j == 0177))
1332 #endif /* NROFF */
1333 #endif /* EUC */
1334 			break;
1335 		nextf[k] = j & BYTEMASK;
1336 	}
1337 	nextf[k] = 0;
1338 	ch = i;
1339 	lgf--;
1340 	return((int)nextf[0]);
1341 }
1342 
1343 
1344 int
1345 caseso()
1346 {
1347 	int	i;
1348 	char	*p, *q;
1349 
1350 	lgf++;
1351 	nextf[0] = 0;
1352 	if (skip() || !getname() || ((i = open(nextf, 0)) < 0) || (ifi >= NSO)) {
1353 		errprint(gettext("can't open file %s"), nextf);
1354 		done(02);
1355 	}
1356 	strcpy(cfname[ifi+1], nextf);
1357 	cfline[ifi] = numtab[CD].val;		/*hold line counter*/
1358 	numtab[CD].val = 0;
1359 	flushi();
1360 	ifl[ifi] = ifile;
1361 	ifile = i;
1362 	offl[ifi] = ioff;
1363 	ioff = 0;
1364 	ipl[ifi] = ip;
1365 	ip = 0;
1366 	nx++;
1367 	nflush++;
1368 	if (!ifl[ifi++]) {
1369 		p = ibuf;
1370 		q = xbuf;
1371 		xbufp = ibufp;
1372 		xeibuf = eibuf;
1373 		while (p < eibuf)
1374 			*q++ = *p++;
1375 	}
1376 
1377 	return (0);
1378 }
1379 
1380 int
1381 caself()	/* set line number and file */
1382 {
1383 	int n;
1384 
1385 	if (skip())
1386 		return (0);
1387 	n = atoi();
1388 	cfline[ifi] = numtab[CD].val = n - 2;
1389 	if (skip())
1390 		return (0);
1391 	if (getname())
1392 		strcpy(cfname[ifi], nextf);
1393 
1394 	return (0);
1395 }
1396 
1397 
1398 int
1399 casecf()
1400 {	/* copy file without change */
1401 #ifndef NROFF
1402 	int	fd, n;
1403 	char	buf[512];
1404 	extern int hpos, esc, po;
1405 	nextf[0] = 0;
1406 	if (skip() || !getname() || (fd = open(nextf, 0)) < 0) {
1407 		errprint(gettext("can't open file %s"), nextf);
1408 		done(02);
1409 	}
1410 	tbreak();
1411 	/* make it into a clean state, be sure that everything is out */
1412 	hpos = po;
1413 	esc = un;
1414 	ptesc();
1415 	ptlead();
1416 	ptps();
1417 	ptfont();
1418 	flusho();
1419 	while ((n = read(fd, buf, sizeof buf)) > 0)
1420 		write(ptid, buf, n);
1421 	close(fd);
1422 #endif
1423 	return (0);
1424 }
1425 
1426 
1427 int
1428 casesy()	/* call system */
1429 {
1430 	char	sybuf[NTM];
1431 	int	i;
1432 
1433 	lgf++;
1434 	copyf++;
1435 	skip();
1436 	for (i = 0; i < NTM - 2; i++)
1437 		if ((sybuf[i] = getch()) == '\n')
1438 			break;
1439 	sybuf[i] = 0;
1440 	system(sybuf);
1441 	copyf--;
1442 	lgf--;
1443 
1444 	return (0);
1445 }
1446 
1447 
1448 int
1449 getpn(a)
1450 	char *a;
1451 {
1452 	int n, neg;
1453 
1454 	if (*a == 0)
1455 		return (0);
1456 	neg = 0;
1457 	for ( ; *a; a++)
1458 		switch (*a) {
1459 		case '+':
1460 		case ',':
1461 			continue;
1462 		case '-':
1463 			neg = 1;
1464 			continue;
1465 		default:
1466 			n = 0;
1467 			if (isdigit((unsigned char)*a)) {
1468 				do
1469 					n = 10 * n + *a++ - '0';
1470 				while (isdigit((unsigned char)*a));
1471 				a--;
1472 			} else
1473 				n = 9999;
1474 			*pnp++ = neg ? -n : n;
1475 			neg = 0;
1476 			if (pnp >= &pnlist[NPN-2]) {
1477 				errprint(gettext("too many page numbers"));
1478 				done3(-3);
1479 			}
1480 		}
1481 	if (neg)
1482 		*pnp++ = -9999;
1483 	*pnp = -32767;
1484 	print = 0;
1485 	pnp = pnlist;
1486 	if (*pnp != -32767)
1487 		chkpn();
1488 
1489 	return (0);
1490 }
1491 
1492 
1493 int
1494 setrpt()
1495 {
1496 	tchar i, j;
1497 
1498 	copyf++;
1499 	raw++;
1500 	i = getch0();
1501 	copyf--;
1502 	raw--;
1503 	if (i < 0 || cbits(j = getch0()) == RPT)
1504 		return (0);
1505 	i &= BYTEMASK;
1506 	while (i>0 && pbp < &pbbuf[NC-3]) {
1507 		i--;
1508 		*pbp++ = j;
1509 	}
1510 
1511 	return (0);
1512 }
1513 
1514 
1515 int
1516 casedb()
1517 {
1518 #ifdef	DEBUG
1519 	debug = 0;
1520 	if (skip())
1521 		return (0);
1522 	noscale++;
1523 	debug = max(atoi(), 0);
1524 	noscale = 0;
1525 #endif	/* DEBUG */
1526 
1527 	return (0);
1528 }
1529