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