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