/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
/*	  All Rights Reserved  	*/


/*	from OpenSolaris "n1.c	1.25	05/06/08 SMI"	*/

/*
 * Portions Copyright (c) 2005 Gunnar Ritter, Freiburg i. Br., Germany
 *
 * Sccsid @(#)n1.c	1.144 (gritter) 8/19/08
 */

/*
 * Changes Copyright (c) 2014 Carsten Kunze <carsten.kunze at arcor.de>
 */

/*
 * University Copyright- Copyright (c) 1982, 1986, 1988
 * The Regents of the University of California
 * All Rights Reserved
 *
 * University Acknowledgment- Portions of this document are derived from
 * software developed by the University of California, Berkeley, and its
 * contributors.
 */

/* const char *xxxvers = "@(#)roff:n1.c	2.13"; */
/*
 * n1.c
 *
 *	consume options, initialization, main loop,
 *	input routines, escape function calling
 */

#include <stdio.h>
#include <ctype.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <setjmp.h>
#include <time.h>
#include <stdarg.h>
#include <locale.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <unistd.h>
#ifdef 	EUC
#include <stddef.h>
#include <limits.h>
#include <wchar.h>
#include <wctype.h>
#endif	/* EUC */

#include "tdef.h"
#include "ext.h"

#ifdef NROFF
#include "tw.h"
#include "draw.h"
#endif
#include "pt.h"

#define	MAX_RECURSION_DEPTH	512
static int	max_recursion_depth = MAX_RECURSION_DEPTH;
static int	max_tail_depth;

jmp_buf sjbuf;
static filep	ipl[NSO];
static long	offl[NSO];
static long	ioff;
static const char	*ttyp;
char	*cfname[NSO+1];		/*file name stack*/
static int	cfline[NSO];		/*input line count stack*/
static int	cfpid[NSO+1];	/* .pso process IDs */
char	*progname;	/* program name (troff) */
#ifdef	EUC
char	mbbuf1[MB_LEN_MAX + 1];
char	*mbbuf1p = mbbuf1;
wchar_t	twc = 0;
#endif	/* EUC */
static unsigned char escoff[126-31];

static void	initg(void);
#ifndef	NROFF
static void	printn(long, long);
static void	printlong(long, int);
#define	vfdprintf	xxvfdprintf
static void	vfdprintf(int fd, const char *fmt, va_list ap);
#endif
static tchar	setyon(void);
static void	_setenv(void);
static tchar	setZ(void);
static int	setgA(void);
static int	setB(void);
static void	_caseesc(int);

#ifdef	DEBUG
int	debug = 0;	/*debug flag*/
#endif	/* DEBUG */

static int	_xflag;
int	bol;
int	noschr;
int	prdblesc;

int
main(int argc, char **argv)
{
	register char	*p;
	register int j;
	char	**oargv;
	size_t l;

	setlocale(LC_CTYPE, "");
	mb_cur_max = MB_CUR_MAX;
	progname = argv[0];
	nextf = calloc(1, NS = 1);
	d = calloc(NDI = 5, sizeof *d);
	growblist();
	growcontab();
	grownumtab();
	growpbbuf();
	morechars(1);
	initg();
	for (j = 0; j <= NSO; j++)
		cfpid[j] = -1;
	if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
		signal(SIGHUP, catch);
	if (signal(SIGINT, catch) == SIG_IGN) {
		signal(SIGHUP, SIG_IGN);
		signal(SIGINT, SIG_IGN);
		signal(SIGQUIT, SIG_IGN);
	}
	signal(SIGPIPE, catch);
	signal(SIGTERM, kcatch);
	oargv = argv;
	cfname[0] = strdup("<standard input>");
	init0();
#ifdef EUC
	localize();
#endif /* EUC */
	if ((p = getenv("TYPESETTER")) != 0)
		n_strcpy(devname, p, sizeof(devname));
	while (--argc > 0 && (++argv)[0][0] == '-')
		switch (argv[0][1]) {

		case 'F':	/* switch font tables from default */
			if (argv[0][2] != '\0') {
				termtab = &argv[0][2];
				fontfile = &argv[0][2];
			} else {
				argv++; argc--;
				if (argv[0] != '\0') {
					termtab = argv[0];
					fontfile = argv[0];
				} else
					errprint("missing the font directory");
			}
			continue;
		case 0:
			goto start;
		case 'i':
			stdi++;
			continue;
		case 'q':
#ifdef	NROFF
			quiet++;
			save_tty();
#else
			errprint("-q option ignored in troff");
#endif	/* NROFF */
			continue;
		case 'n':
			npn = ctoi(&argv[0][2]);
			continue;
		case 'u':	/* set emboldening amount */
			initbdtab[3] = ctoi(&argv[0][2]);
			if (initbdtab[3] < 0 || initbdtab[3] > 50)
				initbdtab[3] = 0;
			continue;
		case 's':
			if (!(stop = ctoi(&argv[0][2])))
				stop++;
			continue;
		case 't':
			ptid = 1;
			continue;
		case 'r':
		case 'd':
			if (&argv[0][2] != '\0' && strlen(&argv[0][2]) >= 2 && &argv[0][3] != '\0') {
			if ((p = strchr(&argv[0][3], '=')) != NULL) {
				*p = 0;
				l = strlen(ibuf);
				eibuf = roff_sprintf(ibuf + l, sizeof(ibuf) - l,
						".do %s %s %s%s\n",
					argv[0][1] == 'd' ? "ds" : "nr",
					&argv[0][2],
					argv[0][1] == 'd' ? "\"" : "",
					&p[1]);
				*p = '=';
			} else {
				l = strlen(ibuf);
				eibuf = roff_sprintf(ibuf + l, sizeof(ibuf) - l,
						".%s %c %s%s\n",
					argv[0][1] == 'd' ? "ds" : "nr",
					argv[0][2],
					argv[0][1] == 'd' ? "\"" : "",
					&argv[0][3]); 
			}
			} else 
				errprint("wrong options");
			continue;
		case 'c':
		case 'm':
			if (mflg++ >= NMF && (mfiles = realloc(mfiles,
						++NMF * sizeof *mfiles)) == 0) {
				errprint("Too many macro packages: %s",
					 argv[0]);
				continue;
			}
		        if (argv[0][2] == '\0') {
				errprint("No library provided with -m");
				done(02);
			}
			if (getenv("TROFFMACS") != '\0') {
			     if (tryfile(getenv("TROFFMACS"), &argv[0][2], nmfi))
			       nmfi++;
			} else
			  if (tryfile(MACDIR "/", &argv[0][2], nmfi)
			  || tryfile(MACDIR "/tmac.", &argv[0][2], nmfi))
				nmfi++;
			  else {
				errprint("Cannot find library %s\n", argv[0]);
				done(02);
			  } 
			continue;
		case 'o':
			getpn(&argv[0][2]);
			continue;
		case 'T':
			n_strcpy(devname, &argv[0][2], sizeof(devname));
			dotT++;
			continue;
		case 'x':
			if (argv[0][2])
				xflag = strtol(&argv[0][2], NULL, 10);
			else
				xflag = 2;
			continue;
		case 'X':
			xflag = 0;
			continue;
#ifdef NROFF
		case 'h':
			hflg++;
			continue;
		case 'z':
			no_out++;
			continue;
		case 'e':
			eqflg++;
			continue;
#endif
#ifndef NROFF
		case 'z':
			no_out++;
		case 'a':
			ascii = 1;
			nofeed++;
			continue;
		case 'f':
			nofeed++;
			continue;
#endif
		case '#':
#ifdef	DEBUG
			debug = ctoi(&argv[0][2]);
#else
			errprint("DEBUG not enabled");
#endif	/* DEBUG */
			continue;
		case 'V':
			fprintf(stdout, "Heirloom doctools %croff, " RELEASE
			    "\n",
#ifdef NROFF
			    'n'
#else
			    't'
#endif
			);
			exit(0);
		default:
			errprint("unknown option %s", argv[0]);
			done(02);
		}

start:
	init1(oargv[0][0]);
	argp = argv;
	rargc = argc;
	nmfi = 0;
	init2();
	mainloop();
	/*NOTREACHED*/
	return(0);
}

void
mainloop(void)
{
	register int j;
	register tchar i;
	int eileenct;		/*count to test for "Eileen's loop"*/
#ifdef NROFF
	int ndo = 0;
#endif

	_xflag = xflag;
	setjmp(sjbuf);
	eileenct = 0;		/*reset count for "Eileen's loop"*/
loop:
#ifdef NROFF
	if (ndo) {
		ndo = 0;
		npic(0);
	}
#endif
	xflag = _xflag;
	defcf = charf = clonef = copyf = lgf = nb = nflush = nlflg = 0;
	if (ip && rbf0(ip) == 0 && dip == d && ejf &&
			frame->pframe->tail_cnt <= ejl) {
		nflush++;
		trap = 0;
		eject((struct s *)0);
#ifdef	DEBUG
		if (debug & DB_LOOP)
			fprintf(stderr, "loop: NL=%d, ejf=%d, lss=%d, "
			    "eileenct=%d\n", numtab[NL].val, ejf, lss,
			    eileenct);
#endif	/* DEBUG */
		if (eileenct > 20) {
			errprint("job looping; check abuse of macros");
			ejf = 0;	/*try to break Eileen's loop*/
			eileenct = 0;
		} else
			eileenct++;
		goto loop;
	}
	eileenct = 0;		/*reset count for "Eileen's loop"*/
	bol = 1;
	i = getch();
	bol = 0;
	if (!i) /* CK: Bugfix: .bp followed by .. */
		goto loop;
	if (pendt)
		goto Lt;
	if ((j = cbits(i)) == XPAR) {
		copyf++;
		tflg++;
		while (cbits(i) != '\n')
			pchar(i = getch());
		tflg = 0;
		copyf--;
		goto loop;
	}
	if (j == cc || j == c2 || isxfunc(i, CC)) {
		if (gflag && isdi(i))
			goto Lt;
		if (j == c2)
			nb++;
		copyf++;
		while ((j = cbits(i = getch())) == ' ' || j == '\t')
			;
		ch = i;
		copyf--;
		j = getrq(4);
#ifdef NROFF
		if (j == PAIR('P', 'S')) npic(1);
		else if (ndraw && j == PAIR('d', 'o')) ndo = 1;
		else
#endif
		if (xflag != 0 && j == PAIR('d', 'o')) {
			xflag = 3;
			skip(1);
			j = getrq(4);
		}
		noschr = 1;
		control(j, 1);
		noschr = 0;
		flushi();
		goto loop;
	}
Lt:
	ch = i;
	text();
	if (nlflg)
		numtab[HP].val = 0;
	goto loop;
}


int
tryfile(register const char *pat, register char *fn, int idx)
{
	size_t l = strlen(pat) + strlen(fn) + 1;
	mfiles[idx] = malloc(l);
	n_strcpy(mfiles[idx], pat, l);
	n_strcat(mfiles[idx], fn, l);
	if (access(mfiles[idx], 4) == -1)
		return(0);
	else return(1);
}	

void catch(int unused __unused)
{
	done3(01);
}


void kcatch(int unused __unused)
{
	signal(SIGTERM, SIG_IGN);
	done3(01);
}


void
init0(void)
{
	eibuf = ibufp = ibuf;
	ibuf[0] = 0;
	numtab[NL].val = -1;
}


void
init1(char a __unused)
{
	register int i;

	for (i = NTRTAB; --i; )
		trnttab[i] = trtab[i] = i;
	trnttab[UNPAD] = trtab[UNPAD] = ' ';
	trnttab[STRETCH] = trtab[STRETCH] = ' ';
}


void
init2(void)
{
	register int i, j;
	size_t l;

	ttyod = 2;
	if ((ttyp=ttyname(j=0)) != 0 || (ttyp=ttyname(j=1)) != 0 || (ttyp=ttyname(j=2)) != 0)
		;
	else 
		ttyp = "notty";
	iflg = j;
	if (ascii)
		mesg(0);
	obufp = obuf;
	ptinit();
	mchbits();
	cvtime();
	setnr(".g", gflag, 0);
	numtab[PID].val = getpid();
	spreadlimit = 3*EM;
	olinesz = LNSIZE;
	oline = malloc(olinesz * sizeof *oline);
	olinep = oline;
	ioff = 0;
	numtab[HP].val = init = 0;
	numtab[NL].val = -1;
	nfo = 0;
	ifile = 0;
	copyf = raw = 0;
	l = strlen(ibuf);
	eibuf = roff_sprintf(ibuf + l, sizeof(ibuf) - l, ".ds .T %s\n",
	    devname);
	numtab[CD].val = -1;	/* compensation */
	cpushback(ibuf);
	ibufp = ibuf;
	nx = mflg;
	frame = stk = calloc(1, sizeof *stk);
	stk->frame_cnt = 0;
	dip = &d[0];
	nxf = calloc(1, sizeof *nxf);
	initenv = env;
	for (i = 0; i < NEV; i++) {
		extern tchar *corebuf;
		((struct env *)corebuf)[i] = env;
	}
}


void
cvtime(void)
{
	time_t	tt;
	register struct tm *tm;

	tt = time((time_t *) 0);
	tm = localtime(&tt);
	numtab[DY].val = tm->tm_mday;
	numtab[DW].val = tm->tm_wday + 1;
	numtab[YR].val = tm->tm_year;
	numtab[MO].val = tm->tm_mon + 1;
	setnr("hours", tm->tm_hour, 0);
	setnr("minutes", tm->tm_min, 0);
	setnr("seconds", tm->tm_sec, 0);
	setnr("year", tm->tm_year + 1900, 0);

}


int
ctoi(register char *s)
{
	register int n;

	while (*s == ' ')
		s++;
	n = 0;
	while (isdigit((unsigned char)*s))
		n = 10 * n + *s++ - '0';
	return n;
}


void
mesg(int f)
{
	static int	mode;
	struct stat stbuf;

	if (!f) {
		stat(ttyp, &stbuf);
		mode = stbuf.st_mode;
		chmod(ttyp, mode & ~0122);	/* turn off writing for others */
	} else {
		if (ttyp && *ttyp && mode)
			chmod(ttyp, mode);
	}
}

static void
verrprint(const char *s, va_list ap)
{
	fprintf(stderr, "%s: ", progname);
	vfprintf(stderr, s, ap);
	if (numtab[CD].val > 0)
		fprintf(stderr, "; line %d, file %s",
			 numtab[CD].val + (nlflg == 0 && frame == stk),
			 cfname[ifi] ? cfname[ifi] : "");
	if (xflag && realpage)
		fprintf(stderr, "; page %ld", realpage);
	fprintf(stderr, "\n");
	stackdump();
#ifdef	DEBUG
	if (debug & DB_ABRT)
		abort();
#endif	/* DEBUG */
}

void
errprint(const char *s, ...)	/* error message printer */
{
	va_list	ap;

	va_start(ap, s);
	verrprint(s, ap);
	va_end(ap);
}

#ifndef	NROFF
/*
 * Scaled down version of C Library printf.
 * Only %s %u %d (==%u) %o %c %x %D are recognized.
 */
#undef putchar
#define	putchar(n)	(*pfbp++ = (n))	/* NO CHECKING! */

static char	pfbuf[NTM];
static char	*pfbp = pfbuf;

void
fdprintf(int fd, const char *fmt, ...)
{
	va_list	ap;

	va_start(ap, fmt);
	vfdprintf(fd, fmt, ap);
	va_end(ap);
}

static void
vfdprintf(int fd, const char *fmt, va_list ap)
{
	register int c;
	char	*s;
	register int i;

	pfbp = pfbuf;
loop:
	while ((c = *fmt++) != '%') {
		if (c == '\0') {
			if (fd == 2)
				write(STDERR_FILENO, pfbuf, pfbp - pfbuf);
			else {
				*pfbp = 0;
				pfbp = pfbuf;
				while (*pfbp) {
					*obufp++ = *pfbp++;
					if (obufp >= &obuf[OBUFSZ])
						flusho();
				}
			}
			return;
		}
		putchar(c);
	}
	c = *fmt++;
	if (c == 'd' || c == 'u' || c == 'o' || c == 'x') {
		i = va_arg(ap, int);
		printlong(i, c);
	} else if (c == 'c') {
		if (c > 0177 || c < 040)
			putchar('\\');
		putchar(va_arg(ap, int) & 0177);
	} else if (c == 's') {
		s = va_arg(ap, char *);
		while ((c = *s++))
			putchar(c);
	} else if (c == 'D') {
		printn(va_arg(ap, long), 10);
	} else if (c == 'O') {
		printn(va_arg(ap, long), 8);
	} else if (c == 'e' || c == 'E' ||
			c == 'f' || c == 'F' ||
			c == 'g' || c == 'G') {
		char	tmp[40];
		char	fmt[] = "%%";
		fmt[1] = c;
		snprintf(s = tmp, sizeof(tmp), fmt, va_arg(ap, double));
		while ((c = *s++))
			putchar(c);
	} else if (c == 'p') {
		i = (intptr_t)va_arg(ap, void *);
		putchar('0');
		putchar('x');
		printlong(i, 'x');
	} else if (c == 'l') {
		c = *fmt++;
		if (c == 'd' || c == 'u' || c == 'o' || c == 'x') {
			i = va_arg(ap, long);
			printlong(i, c);
		} else if (c == 'c') {
			i = va_arg(ap, int);
			if (c & ~0177) {
#ifdef	EUC
				char	mb[MB_LEN_MAX];
				int	j, n;
				n = wctomb(mb, i);
				for (j = 0; j < n; j++)
					putchar(mb[j]&0377);
#endif	/* EUC */
			} else
				putchar(i);
		}
	} else if (c == 'C') {
		extern int	nchtab;
		tchar	t = va_arg(ap, tchar);
		if ((i = cbits(t)) < 0177) {
			putchar(i);
		} else if (i < 128 + nchtab) {
			putchar('\\');
			putchar('(');
			putchar(chname[chtab[i-128]]);
			putchar(chname[chtab[i-128]+1]);
		}
		else if ((i = tr2un(i, fbits(t))) != -1)
			goto U;
	} else if (c == 'U') {
		i = va_arg(ap, int);
	U:
		putchar('U');
		putchar('+');
		if (i < 0x1000)
			putchar('0');
		if (i < 0x100)
			putchar('0');
		if (i < 0x10)
			putchar('0');
		printn((long)i, 16);
#ifdef	EUC
		if (iswprint(i)) {
			char	mb[MB_LEN_MAX];
			int	j, n;
			n = wctomb(mb, i);
			putchar(' ');
			putchar('(');
			for (j = 0; j < n; j++)
				putchar(mb[j]&0377);
			putchar(')');
		}
#endif	/* EUC */
	}
	goto loop;
}


static void
printlong(long i, int fmt)
{
	switch (fmt) {
	case 'd':
		if (i < 0) {
			putchar('-');
			i = -i;
		}
		/*FALLTHRU*/
	case 'u':
		printn(i, 10);
		break;
	case 'o':
		printn(i, 8);
		break;
	case 'x':
		printn(i, 16);
		break;
	}
}

/*
 * Print an unsigned integer in base b.
 */
static void printn(register long n, register long b)
{
	register long	a;

	if (n < 0) {	/* shouldn't happen */
		putchar('-');
		n = -n;
	}
	if ((a = n / b))
		printn(a, b);
	putchar("0123456789ABCDEF"[(int)(n%b)]);
}
#endif	/* !NROFF */

/* returns pointer to \0 that ends the string */

/* VARARGS2 */
char *roff_sprintf(char *str, size_t size, const char *fmt, ...)
{
	int ret;
	va_list ap;

	va_start(ap, fmt);
	ret = vsnprintf(str, size, fmt, ap);
	va_end(ap);

	return (str + ret);
}

int
control(register int a, register int b)
{
	struct contab	*contp;
	int	newip;
	struct s	*p;

	if (a == 0 || (contp = findmx(a)) == NULL) {
		nosuch(a);
		return(0);
	}

	/*
	 * Attempt to find endless recursion at runtime. Arbitrary
	 * recursion limit of MAX_RECURSION_DEPTH was chosen as
	 * it is extremely unlikely that a correct nroff/troff
	 * invocation would exceed this value.
	 *
	 * The depth of tail-recursive macro calls is not limited
	 * by default.
	 */

	if (max_recursion_depth > 0 && frame->frame_cnt > max_recursion_depth) {
		errprint(
		    "Exceeded maximum stack size (%d) when "
		    "executing macro %s. Stack dump follows",
		    max_recursion_depth, macname(frame->mname));
		edone(02);
	}
	if (max_tail_depth > 0 && frame->tail_cnt > max_tail_depth) {
		errprint(
		    "Exceeded maximum recursion depth (%d) when "
		    "executing macro %s. Stack dump follows",
		    max_tail_depth, macname(frame->mname));
		edone(02);
	}

	lastrq = a;

#ifdef	DEBUG
	if (debug & DB_MAC)
		fprintf(stderr, "control: macro %s, contab[%d]\n",
			macname(a), contp - contab);
#endif	/* DEBUG */
	if (contp->f == 0) {
		nxf->nargs = 0;
		tailflg = 0;
		if (b)
			collect();
		flushi();
		newip = pushi((filep)contp->mx, a, contp->flags);
		p = frame->pframe;
		if (tailflg && b && p != stk &&
				p->ppendt == 0 &&
				p->pch == 0 &&
				p->pip == frame->pip &&
				p->lastpbp == frame->lastpbp) {
			frame->pframe = p->pframe;
			frame->frame_cnt--;
			sfree(p);
			*p = *frame;
			free(frame);
			frame = p;
		}
		contp->flags |= FLAG_USED;
		frame->contp = contp;
		tailflg = 0;
		return newip;
	} else if (b) {
		(*contp->f)(0);
		return 0;
	} else
		return(0);
}


int
rgetach(void)
{
	extern const char	nmctab[];
	int	i;

	if ((i = getach()) == 0 || (xflag && i < ' ' && nmctab[i]))
		return(0);
	return(i);
}

int
getrq2(void)
{
	register int i, j;

	if (((i = rgetach()) == 0) || ((j = rgetach()) == 0))
		goto rtn;
	i = PAIR(i, j);
rtn:
	return(i);
}

int
getrq(int flags)
{
	int	i;

	if ((i = getrq2()) >= 256)
		i = maybemore(i, flags);
	return(i);
}

/*
 * table encodes some special characters, to speed up tests
 * in getchar, viz FLSS, RPT, f, \b, \n, fc, tabch, ldrch
 */

static char
_gchtab[] = {
	000,004,000,000,010,000,000,000, /* fc, ldr */
	001,002,001,000,001,000,000,000, /* \b, tab, nl, RPT */
	000,000,000,000,000,000,000,000,
	000,001,000,000,000,000,000,000, /* FLSS */
	000,000,000,000,000,000,000,000,
	000,000,000,000,000,000,000,000,
	000,000,000,000,000,000,000,000,
	000,000,000,000,000,000,000,000,
	000,000,000,000,000,000,000,000,
	000,000,000,000,000,000,000,000,
	000,000,000,000,000,000,000,000,
	000,000,000,000,000,000,000,000,
	000,000,000,000,000,000,001,000, /* f */
	000,000,000,000,000,000,000,000,
	000,000,000,000,000,000,000,000,
	000,000,000,000,000,000,000,000,
};

static void
initg(void)
{
	memcpy(gchtab, _gchtab, sizeof _gchtab);
}

tchar
getch(void)
{
	register int	k;
	register tchar i, j;
	struct numtab	*np;

g0:
	if ((i = ch)) {
		if (cbits(i) == '\n') {
			nlflg++;
			tailflg = istail(i);
		}
		ch = 0;
		return(i);
	}

	if (nlflg)
		return('\n');
	i = getch0();
	if (ismot(i))
		return(i);
	k = cbits(i);
	if (k != ESC) {
		if (k >= NCHARS || gchtab[k]==0)
			return(i);
		if (k == '\n') {
		nl:
			if (cbits(i) == '\n') {
				nlflg++;
				tailflg = istail(i);
			}
			return(k);
		}
		if (k == FLSS) {
			copyf++; 
			raw++;
			i = getch0();
			if (!fi)
				flss = i;
			copyf--; 
			raw--;
			goto g0;
		}
		if (k == RPT) {
			setrpt();
			goto g0;
		}
		if (!copyf) {
			if (gchtab[k]&LGBIT && !isdi(i) && lg && !lgf) {
				k = cbits(i = getlg(i));
				goto chartest;
			}
			if (k == fc || k == tabch || k == ldrch) {
				if ((i = setfield(k)) == 0)
					goto g0; 
				else
					return(i);
			}
			if (k == '\b') {
				i = makem(-width(' ' | chbits));
				return(i);
			}
chartest:
			if (
#ifndef NROFF
			    (!html || k < NCHARS) &&
#endif
			    !lgf && !charf && chartab[trtab[k]] != NULL &&
			    !noschr && (!argdelim || k != argdelim) &&
			    !(bol && (k == cc || k == c2)))
				i = setchar(i);
			return(i);
		}
		return(i);
	}
ge:
	k = cbits(j = getch0());
	if (ismot(j))
		return(j);
	if (k >= 32 && k <= 126 && escoff[k-32]) {
		if (clonef || copyf || tryglf) {
			pbbuf[pbp++] = j;
			return eschar;
		}
		return j;
	}
	switch (k) {

	case '\n':	/* concealed newline */
		if (fmtchar)
			goto nl;
		goto g0;
	case '{':	/* LEFT */
		i = LEFT;
		goto gx;
	case '}':	/* RIGHT */
		i = RIGHT;
		goto gx;
	case '#':	/* comment including newline */
		if (xflag == 0)
			break;
		/*FALLTHRU*/
	case '"':	/* comment */
		while (cbits(i = getch0()) != '\n')
			;
		if (k == '#')
			goto g0;
		nlflg++;
		tailflg = istail(i);
		return(i);
	case 'e':	/* printable version of current eschar */
		i = PRESC;
		goto gx;
	case ' ':	/* unpaddable space */
		i = UNPAD;
		goto gx;
	case '~':	/* stretchable but unbreakable space */
		if (xflag == 0)
			break;
		i = STRETCH;
		goto gx;
	case '\'':	/* \(aa */
		i = ACUTE;
		goto gx;
	case '`':	/* \(ga */
		i = GRAVE;
		goto gx;
	case '_':	/* \(ul */
		i = UNDERLINE;
		goto gx;
	case '-':	/* current font minus */
		i = MINUS;
		goto gx;
	case '&':	/* filler */
		i = FILLER;
		goto gx;
	case ')':	/* transparent filler */
		if (xflag == 0)
			break;
		i = FILLER|TRANBIT;
		goto gx;
	case 'c':	/* to be continued */
		i = CONT;
		goto gx;
	case '!':	/* transparent indicator */
		i = XPAR;
		goto gx;
	case 't':	/* tab */
		i = '\t';
		return(i);
	case 'a':	/* leader (SOH) */
		i = LEADER;
		return(i);
	case '%':	/* ohc */
		i = OHC;
		return(i);
	case ':':	/* optional line break but no hyphenation */
		if (xflag == 0)
			break;
		i = OHC | BLBIT;
		return(i);
	}
	if (clonef) {
		pbbuf[pbp++] = j;
		return(eschar);
	}
	switch (k) {

	case 'n':	/* number register */
		setn();
		goto g0;
	case '*':	/* string indicator */
		setstr();
		goto g0;
	case '$':	/* argument indicator */
		seta();
		goto g0;
	case ESC:	/* double backslash */
		if (prdblesc || dilev)
			i = PRESC;
		else
			i = eschar;
		goto gx;
	case 'g':	/* return format of a number register */
		setaf();
		goto g0;
	case 'P':	/* output line trap */
		if (xflag == 0)
			break;
		i = setolt();
		return(i);
	case 'V':	/* environment variable */
		if (xflag == 0)
			break;
		_setenv();
		goto g0;
	case '.':	/* . */
		i = '.';
gx:
		setsfbits(i, sfbits(j));
		return(i);
	}
	if (copyf) {
copy:
		pbbuf[pbp++] = j;
		return(eschar);
	}
	switch (k) {

	case '[':
		if (defcf)
			goto copy;
		if (xflag == 0)
			goto dfl;
		/*FALLTHRU*/
	case 'C':
	case '(':	/* special char name */
		if (defcf)
			goto copy;
		if ((i = setch(k)) == 0 && !tryglf)
			goto g0;
		k = cbits(i);
		goto chartest;
	case 'U':	/* Unicode character */
		if (xflag == 0)
			goto dfl;
		if ((i = setuc()) == 0 && !tryglf)
			goto g0;
		return(i);
	case 'N':	/* absolute character number */
		i = setabs();
		goto gx;
	case 'E':	/* eschar out of copy mode */
		if (xflag == 0)
			goto dfl;
		goto ge;
	}
	if (tryglf) {
		pbbuf[pbp++] = j;
		return(eschar);
	}
	switch (k) {

	case 'X':	/* \X'...' for copy through */
		setxon();
		goto g0;
	case 'Y':	/* \Y(xx for indirect copy through */
		if (xflag == 0)
			goto dfl;
		i = setyon();
		return(i);
	case 'p':	/* spread */
		spread = 1;
		goto g0;
	case 's':	/* size indicator */
		setps();
		goto g0;
	case 'H':	/* character height */
		return(setht());
	case 'S':	/* slant */
		return(setslant());
	case 'f':	/* font indicator */
		setfont(0);
		goto g0;
	case 'w':	/* width function */
		setwd();
		goto g0;
	case 'v':	/* vert mot */
		if ((i = vmot()))
			return(i);
		goto g0;
	case 'h': 	/* horiz mot */
		if ((i = hmot()))
			return(i);
		goto g0;
	case 'z':	/* zero with char */
		return(setz());
	case 'l':	/* hor line */
		setline();
		goto g0;
	case 'L':	/* vert line */
		setvline();
		goto g0;
	case 'D':	/* drawing function */
		setdraw();
		goto g0;
	case 'b':	/* bracket */
		setbra();
		goto g0;
	case 'o':	/* overstrike */
		setov();
		goto g0;
	case 'k':	/* mark hor place */
		if ((np = findr(getsn(1))) != NULL) {
			np->val = numtab[HP].val;
			prwatchn(np);
		}
		goto g0;
	case '0':	/* number space */
		return(makem(width('0' | chbits)));
#ifdef NROFF
	case '/':
	case ',':
		if (!(gflag || gemu))
			goto dfl;
		goto g0;
	case '|':
	case '^':
		goto g0;
#else
	case '/':
		if (gflag == 0)
			goto dfl;
		return(makem((int)(EM)/12));	/* italic correction */
	case ',':
		if (!(gflag || gemu))
			goto dfl;
		return(makem(0));	/* left italic correction */
	case '|':	/* narrow space */
		return(makem((int)(EM)/6));
	case '^':	/* half narrow space */
		return(makem((int)(EM)/12));
#endif
	case 'x':	/* extra line space */
		if ((i = xlss()))
			return(i);
		goto g0;
	case 'u':	/* half em up */
	case 'r':	/* full em up */
	case 'd':	/* half em down */
		return(sethl(k));
	case 'I':
		if (xflag) {
			i = setgA() + '0';
			goto gx;
		}
		goto dfl;
	case 'A':	/* set anchor */
		if (gflag) {	/* acceptable as name */
			i = setgA() + '0';
			goto gx;
		}
		if (xflag == 0)
			goto dfl;
		if ((j = setanchor()) == 0)
			goto g0;
		return(j);
	case 'B':	/* acceptable as expression */
		if (xflag) {
			i = setB() + '0';
			goto gx;
		}
		goto dfl;
	case 'F':
	case 'm':
	case 'M':
		if (gflag || gemu) {	/* font family, color */
			if ((i = getsn(0)) > 0 && warn & WARN_ESCAPE)
				errprint("\\%c[%s] unimplemented",
						k, macname(i));
			goto g0;
		}
		goto dfl;
	case 'T':
		if (xflag == 0)
			goto dfl;
		if ((j = setlink()) == 0)
			goto g0;
		return(j);
	case 'R':
		if (xflag) {
			setr();
			goto g0;
		}
		goto dfl;
	case 'W':	/* URI link */
		if (xflag == 0)
			goto dfl;
		if ((j = setulink()) == 0)
			goto g0;
		return(j);
	case 'Z':
		if (xflag == 0)
			goto dfl;
		if ((j = setZ()) != 0)
			return(j);
		goto g0;
	case 'j':
		if (xflag == 0)
			goto dfl;
		if ((j = setpenalty()) != 0)
			return(j);
		goto g0;
	case 'J':
		if (xflag == 0)
			goto dfl;
		if ((j = setdpenal()) != 0)
			return(j);
		goto g0;
	case '@':
		if (xflag == 0)
			goto dfl;
		k = cbits(i = getch0());
		switch (k) {
		case '{':
			pushinlev();
			break;
		case '}':
			if ((i = popinlev()) != 0)
				return(i);
			break;
		default:
			if (warn & WARN_ESCAPE)
				errprint("undefined inline environment "
				         "function \\@%c", k);
			pbbuf[pbp++] = i;
			goto dfl;
		}
		goto g0;
	case ';':	/* ligature suppressor (only) */
		if (xflag)
			goto g0;
		/*FALLTHRU*/
	default:
	dfl:	if (defcf)
			goto copy;
		if (warn & WARN_ESCAPE)
			errprint("undefined escape sequence \\%c", k);
		return(j);
	}
	/* NOTREACHED */
}

void
setxon(void)	/* \X'...' for copy through */
{
	tchar _xbuf[NC];
	register tchar *i;
	tchar c, delim;
	int k;

	if (ismot(c = getch()))
		return;
	delim = c;
	i = _xbuf;
	*i++ = XON;
	charf++;
	while (k = cbits(c = getch()), !issame(c, delim) && k != '\n' && i < _xbuf+NC-1) {
		if (k == ' ')
			setcbits(c, UNPAD);
		*i++ = c | ZBIT;
	}
	if (!issame(c, delim))
		nodelim(delim);
	charf--;
	*i++ = XOFF;
	*i = 0;
	pushback(_xbuf);
}

static tchar
setyon(void)	/* \Y(xx for indirect copy through */
{
	storerq(getsn(0));
	return mkxfunc(YON, 0);
}


char	ifilt[32] = {
	0, 001, 002, 003, 0, 005, 006, 007, 010, 011, 012};

tchar getch0(void)
{
	register int	j;
	register tchar i;
#ifdef	EUC
	size_t	n;
#endif	/* EUC */

again:
	if (pbp > lastpbp)
		i = pbbuf[--pbp];
	else if (ip) {
		extern tchar *corebuf;
		i = corebuf[ip];
		if (i == 0)
		{
			/* CK: Bugfix: .bp followed by ..
			 * The "<" is questionable */
			if (ejf && frame->pframe->tail_cnt < ejl && dip == d)
				goto r;
			i = rbf();
		}
		else {
			if ((++ip & (BLK - 1)) == 0) {
				--ip;
				(void)rbf();
			}
		}
	} else {
		if (donef || ndone)
			done(0);
		if (nx || ibufp >= eibuf) {
			if (nfo==0) {
g0:
				if (nextfile()) {
					if (ip)
						goto again;
					if (ibufp < eibuf)
						goto g2;
				}
			}
			nx = 0;
			if ((j = read(ifile, ibuf, IBUFSZ)) <= 0)
				goto g0;
			ibufp = ibuf;
			eibuf = ibuf + j;
			if (ip)
				goto again;
		}
g2:
#ifndef	EUC
		i = *ibufp++ & 0177;
		ioff++;
		if (i >= 040 && i < 0177)
#else	/* EUC */
		i = *ibufp++ & 0377;
		ioff++;
		*mbbuf1p++ = i;
		*mbbuf1p = 0;
		if (multi_locale && (*mbbuf1&~(wchar_t)0177)) {
			mbstate_t	state;
			memset(&state, 0, sizeof state);
			if ((n = mbrtowc(&twc, mbbuf1, mbbuf1p-mbbuf1, &state))
					== -1 ||
					twc & ~(wchar_t)0x1FFFFF) {
				illseq(-1, mbbuf1, mbbuf1p-mbbuf1);
				mbbuf1p = mbbuf1;
				*mbbuf1p = 0;
				i &= 0177;
			} else if (n == -2)
				goto again;
			else {
				mbbuf1p = mbbuf1;
				*mbbuf1p = 0;
				i = twc | COPYBIT;
				goto g4;
			}
		} else {
			mbbuf1p = mbbuf1;
			*mbbuf1p = 0;
			if (!raw)
				i &= 0177;
		}
		if (i >= 040 && i < 0177)
#endif	/* EUC */
			goto g4;
		if (i != 0177) {
			if (i != ifilt[i])
				illseq(i, NULL, 0);
			i = ifilt[i];
		} else
			illseq(i, NULL, 0);
		if (i == '\n')
			numtab[CD].val++; /* line number */
	}
	if (cbits(i) == IMP && !raw)
		goto again;
	if ((i == 0 || i == 0177) && !init && !raw) {
		goto again;
	}
g4:
	if (!copyf && iscopy(i))
		i = setuc0(cbits(i));
	if (copyf == 0 && (i & ~BYTEMASK) == 0)
		i |= chbits;
	if (cbits(i) == eschar && !raw) {
		if (gflag && isdi(i))
			setcbits(i, PRESC);
		else
			setcbits(i, ESC);
	}
r:
	return i;
}

void
pushback(register tchar *b)
{
	register tchar *ob = b;

	while (*b++)
		;
	b--;
	while (b > ob) {
		if (pbp >= pbsize-3)
			if (growpbbuf() == NULL) {
				errprint("pushback overflow");
				done(2);
			}
		pbbuf[pbp++] = *--b;
	}
}

void
cpushback(register const char *b)
{
	register const char *ob = b;

	while (*b++)
		;
	b--;
	while (b > ob) {
		if (pbp >= pbsize-3)
			if (growpbbuf() == NULL) {
				errprint("cpushback overflow");
				done(2);
			}
		pbbuf[pbp++] = *--b;
	}
}

tchar *
growpbbuf(void)
{
	tchar	*npb;
	int	inc = NC;

	if ((npb = realloc(pbbuf, (pbsize + inc) * sizeof *pbbuf)) == NULL)
		return NULL;
	pbsize += inc;
	return pbbuf = npb;
}

int
nextfile(void)
{
	register char	*p;

n0:
	if (ifile)
		close(ifile);
	if (nx  ||  nmfi < mflg) {
		p = mfiles[nmfi++];
		if (*p != 0)
			goto n1;
	}
	if (ifi > 0) {
		if (popf())
			goto n0; /* popf error */
		return(1); /* popf ok */
	}
	if (rargc-- <= 0) {
		if ((nfo -= mflg) && !stdi)
			done(0);
		nfo++;
		numtab[CD].val = ifile = stdi = mflg = 0;
		free(cfname[ifi]);
		cfname[ifi] = strdup("<standard input>");
		ioff = 0;
		return(0);
	}
	p = (argp++)[0];
n1:
	numtab[CD].val = 0;
	if (p[0] == '-' && p[1] == 0) {
		ifile = 0;
		free(cfname[ifi]);
		cfname[ifi] = strdup("<standard input>");
	} else if ((ifile = open(p, O_RDONLY)) < 0) {
		errprint("cannot open file %s", p);
		nfo -= mflg;
		done(02);
	} else {
		free(cfname[ifi]);
		cfname[ifi] = strdup(p);
	}
	nfo++;
	ioff = 0;
	return(0);
}


int
popf(void)
{
	register int i;
	register char	*p, *q;

	if (cfpid[ifi] != -1) {
		while (waitpid(cfpid[ifi], NULL, 0) != cfpid[ifi]);
		cfpid[ifi] = -1;
	}
	ioff = offl[--ifi];
	numtab[CD].val = cfline[ifi];		/*restore line counter*/
	ip = ipl[ifi];
	if ((ifile = ifl[ifi]) == 0) {
		p = xbuf;
		q = ibuf;
		ibufp = xbufp;
		eibuf = xeibuf;
		while (q < eibuf)
			*q++ = *p++;
		return(0);
	}
	if (lseek(ifile, ioff & ~(IBUFSZ-1), SEEK_SET) == -1
	   || (i = read(ifile, ibuf, IBUFSZ)) < 0)
		return(1);
	eibuf = ibuf + i;
	ibufp = ibuf;
	if (ttyname(ifile) == 0)
		/* was >= ... */
		if ((ibufp = ibuf + (ioff & (IBUFSZ - 1))) > eibuf)
			return(1);
	return(0);
}


void
flushi(void)
{
	if (nflush)
		return;
	ch = 0;
	copyf++;
	while (!nlflg) {
		if (donef && (frame == stk))
			break;
		getch();
	}
	copyf--;
}


int
getach(void)
{
	register tchar i;
	register int j;

	lgf++;
	i = getch();
	while (isxfunc(i, CHAR))
		i = charout[sbits(i)].ch;
	j = cbits(i);
	if (ismot(i) || (j == XFUNC && fbits(i)) || j == ' ' || j == '\n' ||
			j & 0200) {
		if (!ismot(i) && j >= 0200)
			illseq(j, NULL, -3);
		else if (WARN_INPUT) {
			if (ismot(i) && !isadjmot(i))
				errprint("motion terminates name");
			else if (j == XFUNC && fbits(i))
				errprint("illegal character terminates name");
		}

		ch = i;
		j = 0;
	}
	lgf--;
	return(j & 0177);
}


int
getxch(void)
{
	register tchar i, k;
	register int j;

	lgf++;
	k = i = getch();
	while (isxfunc(i, CHAR))
		i = charout[sbits(i)].ch;
	j = cbits(i);
	if (ismot(i) || (j == XFUNC && fbits(i)) || j == ' ' || j == '\n' ||
			j & 0200) {
		if (!ismot(i) && j >= 0200)
			illseq(j, NULL, -3);
		else if (WARN_INPUT) {
			if (ismot(i) && !isadjmot(i))
				errprint("motion terminates name");
			else if (j == XFUNC && fbits(i))
				errprint("illegal character terminates name");
		}
	}
	lgf--;
	return k;
}


void
casenx(void)
{
	struct s *pp;
	lgf++;
	skip(0);
	getname();
	nx++;
	if (nmfi > 0)
		nmfi--;
	if (mfiles == NULL)
		mfiles = calloc(1, sizeof *mfiles);
	free(mfiles[nmfi]);
	mfiles[nmfi] = malloc(NS);
	n_strcpy(mfiles[nmfi], nextf, NS);
	nextfile();
	nlflg++;
	tailflg = 0;
	ip = 0;
	pendt = 0;
	while (frame != stk) {
		pp = frame;
		frame = frame->pframe;
		sfree(pp);
		free(pp);
	}
	nxf = calloc(1, sizeof *nxf);
}


int
getname(void)
{
	register int	j, k;
	tchar i;
	int delim = ' ';

	lgf++;
	k = 0;
	while (1) {
		if ((j = cbits(i = getch())) < 32 || j == delim || (!xflag
		    && j > 0176))
			break;
		if (xflag && !k && j == '"') {
			delim = j;
			continue;
		}
		if (k + 1 >= NS)
			nextf = realloc(nextf, NS += 14);
		nextf[k++] = j & BYTEMASK;
	}
	nextf[k] = 0;
	ch = i;
	lgf--;
	return(nextf[0]);
}

tchar
setuc(void)
{
	char	c, _d, b[NC], *bp;
	int	n;
	size_t	i = 0;
	tchar	r = 0;
#ifndef NROFF
	extern int nchtab;
#endif

	_d = getach();
	do {
		c = getach();
		if (i >= sizeof b)
			goto rtn;
		b[i++] = c;
	} while (c && c != _d);
	b[--i] = 0;
	if (i == 0 || c != _d)
		goto rtn;
	n = strtol(b, &bp, 16);
	if (n == 0 || *bp != '\0')
		goto rtn;
#ifndef NROFF
	switch (n) {
	case '\'':
		bp = "aq";
		break;
	case '`':
		bp = "ga";
		break;
	case '-':
		r = MINUS;
		goto rtn;
	default:
		goto uc;
	}
	for (i = 0; i < nchtab; i++)
		if (strcmp(&chname[chtab[i]], bp) == 0) {
			r = (i + 128) | chbits;
			break;
		}
	goto rtn;
uc:
#endif
	r = setuc0(n);
rtn:
	return r;
}


static void
_setenv(void)
{
	int	a = 0, i = 0, c, delim;
	char	*np = NULL, *vp;

	if ((delim = getach()) == 0)
		return;
	switch (delim) {
	case '[':
		for (;;) {
			if (i + 1 >= a)
				np = realloc(np, a += 32);
			if ((c = getach()) == 0) {
				nodelim(']');
				break;
			}
			if (c == ']')
				break;
			np[i++] = c;
		}
		np[i] = 0;
		break;
	case '(':
		np = malloc(a = 3);
		np[0] = delim;
		np[1] = getach();
		np[2] = 0;
		break;
	default:
		np = malloc(a = 2);
		np[0] = delim;
		np[1] = 0;
	}
	if ((vp = getenv(np)) != NULL)
		cpushback(vp);
	free(np);
}


static void
sopso(int i, pid_t pid)
{
	register char	*p, *q;

	free(cfname[ifi+1]);
	cfname[ifi+1] = malloc(NS);
	n_strcpy(cfname[ifi+1], nextf, NS);
	cfline[ifi] = numtab[CD].val;		/*hold line counter*/
	numtab[CD].val = 0;
	flushi();
	cfpid[ifi+1] = pid;
	ifl[ifi] = ifile;
	ifile = i;
	offl[ifi] = ioff;
	ioff = 0;
	ipl[ifi] = ip;
	ip = 0;
	nx++;
	nflush++;
	if (!ifl[ifi++]) {
		p = ibuf;
		q = xbuf;
		xbufp = ibufp;
		xeibuf = eibuf;
		while (p < eibuf)
			*q++ = *p++;
	}
}

void
caseso(void)
{
	register int i = 0;

	lgf++;
	nextf[0] = 0;
	if (skip(1))
		done(02);
	if (!getname() || ((i = open(nextf, O_RDONLY)) < 0) ||
			(ifi >= NSO)) {
		errprint("can't open file %s", nextf);
		if (gflag)
			return;
		done(02);
	}
	sopso(i, -1);
}

void
casepso(void)
{
	int	pd[2];
	int	c, i, k;
	pid_t	pid;

	lgf++;
	nextf[0] = 0;
	if (skip(1))
		done(02);
	if (ifi >= NSO || pipe(pd) < 0) {
		errprint("can't .pso");
		done(02);
	}
	for (k = 0; ; k++) {
		if ((c = cbits(i = getch())) == '\n' || c == 0)
			break;
		if (k + 1 >= NS)
			nextf = realloc(nextf, NS += 14);
		nextf[k] = c & BYTEMASK;
	}
	nextf[k] = 0;
	switch (pid = fork()) {
	case 0:
		close(pd[0]);
		close(1);
		dup(pd[1]);
		close(pd[1]);
		execl(SHELL, "sh", "-c", nextf, NULL);
		_exit(0177);
		/*NOTREACHED*/
	case -1:
		errprint("can't fork");
		done(02);
		/*NOTREACHED*/
	}
	close(pd[1]);
	sopso(pd[0], pid);
}

void
caself(void)	/* set line number and file */
{
	int n;

	if (skip(1))
		return;
	n = hatoi();
	cfline[ifi] = numtab[CD].val = n - 2;
	if (skip(0))
		return;
	if (getname()) {
		free(cfname[ifi]);
		cfname[ifi] = malloc(NS);
		n_strcpy(cfname[ifi], nextf, NS);
	}
}


void
casecf(void)
{	/* copy file without change */
#ifndef NROFF
	int	fd = -1, n;
	char	buf[512];
	extern int hpos, esc, po;
	nextf[0] = 0;
	if (skip(1))
		return;
	if (!getname() || (fd = open(nextf, O_RDONLY)) < 0) {
		errprint("can't open file %s", nextf);
		done(02);
	}
	tbreak();
	/* make it into a clean state, be sure that everything is out */
	hpos = po;
	esc = un;
	ptesc();
	ptlead();
	ptps();
	ptfont();
	flusho();
	while ((n = read(fd, buf, sizeof buf)) > 0)
		write(ptid, buf, n);
	close(fd);
#endif
}

void
casesy(void)	/* call system */
{
	char	sybuf[NTM];
	int	i;

	lgf++;
	copyf++;
	skip(1);
	for (i = 0; i < NTM - 2; i++)
		if ((sybuf[i] = getch()) == '\n')
			break;
	sybuf[i] = 0;
	system(sybuf);
	copyf--;
	lgf--;
}


void
getpn(register char *a)
{
	register int n, neg;

	if (*a == 0)
		return;
	neg = 0;
	for ( ; *a; a++)
		switch (*a) {
		case '+':
		case ',':
			continue;
		case '-':
			neg = 1;
			continue;
		default:
			n = 0;
			if (isdigit((unsigned char)*a)) {
				do
					n = 10 * n + *a++ - '0';
				while (isdigit((unsigned char)*a));
				a--;
			} else
				n = 9999;
			*pnp++ = neg ? -n : n;
			neg = 0;
			if (pnp >= &pnlist[NPN-2]) {
				errprint("too many page numbers");
				done3(-3);
			}
		}
	if (neg)
		*pnp++ = -9999;
	*pnp = -32767;
	print = 0;
	pnp = pnlist;
	if (*pnp != -32767)
		chkpn();
}


void
setrpt(void)
{
	tchar i, j;

	copyf++;
	raw++;
	i = getch0();
	copyf--;
	raw--;
	if (i < 0 || cbits(j = getch0()) == RPT)
		return;
	i &= BYTEMASK;
	while (i>0) {
		if (pbp >= pbsize-3)
			if (growpbbuf() == NULL)
				break;
		i--;
		pbbuf[pbp++] = j;
	}
}


void
casedb(void)
{
#ifdef	DEBUG
	debug = 0;
	if (skip(1))
		return;
	noscale++;
	debug = max(hatoi(), 0);
	noscale = 0;
#endif	/* DEBUG */
}

void
casexflag(void)
{
	int	i;

#ifndef	NROFF
	if (gflag == 1)
		zapwcache(1);
#endif
	gflag = 0;
	setnr(".g", gflag, 0);
	gemu = 0;
	skip(1);
	noscale++;
	i = hatoi();
	noscale--;
	if (!nonumb)
		_xflag = xflag = i & 3;
}

void
casecp(void)
{
	if (xflag) {
#ifndef	NROFF
		if (gflag == 0)
			zapwcache(1);
#endif
		gflag = 1;
		noscale++;
		if (skip(1) || (hatoi() && !nonumb))
			xflag = 1;
		else
			xflag = 3;
		noscale--;
		_xflag = xflag;
		setnr(".g", gflag, 0);
		setnr(".C", xflag == 1, 0);
		setnr(".x", 1, 0);
		setnr(".y", 18, 0);
	}
}

void
caserecursionlimit(void)
{
	skip(1);
	noscale++;
	max_recursion_depth = hatoi();
	skip(0);
	max_tail_depth = hatoi();
	noscale--;
}

void
casechar(int flag __unused)
{
#ifndef	NROFF
	extern int	ps2cc(const char *);
	extern int	nchtab;
#endif
	char	name[NC];
	int	i, k, size = 0;
	tchar	c, *tp = NULL;

	defcf++;
	charf++;
	lgf++;
	if (skip(1))
		return;
	c = getch();
	while (isxfunc(c, CHAR))
		c = charout[sbits(c)].ch;
	if ((k = cbits(c)) == eschar || k == WORDSP) {
		switch (cbits(c = getch())) {
		case '(':
			name[0] = getch();
			name[1] = getch();
			name[2] = 0;
			break;
		case '[':
			for (i = 0; cbits(c = getch()) != ']'; i++)
				if (i < sizeof name - 1)
					name[i] = c;
			name[i] = 0;
			break;
		default:
			errprint("mapping of escape sequences not permitted");
			return;
		}
#ifndef	NROFF
		k = ps2cc(name) + nchtab + 128 + 32 + 128 - 32 + nchtab;
#else
		if (!(k = findch(name)))
			k = addch(name);
#endif
	} else if (iscopy(c))
		k = cbits(c = setuc0(k));
	if (k <= ' ') {
		errprint("mapping of special characters not permitted");
		return;
	}
	defcf--;
	charf--;
	copyf++;
	size = 10;
	tp = malloc(size * sizeof *tp);
	i = 0;
	if (skip(0))
		tp[i++] = FILLER;
	else {
		if (cbits(c = getch()) != '"')
			ch = c;
		while (c = getch(), !nlflg) {
			if (i + 3 >= size) {
				size += 10;
				tp = realloc(tp, size * sizeof *tp);
			}
			tp[i++] = c;
		}
	}
	tp[i++] = '\n';
	tp[i] = 0;
	i = k;
	if (++i >= NCHARS)
		morechars(i);
	free(chartab[k]);
	chartab[k] = tp;
	gchtab[k] |= CHBIT;
	copyf--;
#ifndef	NROFF
	if (flag)
		fchartab[k] = 1;
	else
		fchartab[k] = 0;
#endif
}

void
casefchar(void)
{
#ifndef	NROFF
	casechar(1);
#endif
}

void
caserchar(void)
{
	tchar	c;
	int	k;

	lgf++;
	if (skip(1))
		return;
	do {
		c = getch();
		k = cbits(c);
		free(chartab[k]);
		chartab[k] = NULL;
		gchtab[k] &= ~CHBIT;
	} while (!skip(0));
}

struct fmtchar {
	struct d	newd, *savedip;
	struct env	saveev;
	int	savvflag;
	int	savvpt;
	int	savhp;
	int	savnflush;
	tchar	*csp;
	int	charcount;
};

static int
prepchar(struct fmtchar *fp)
{
	static int	charcount;
	filep	startb;
	tchar	_t;

	if ((startb = alloc()) == 0) {
		errprint("out of space");
		return -1;
	}
	_t = 0;
	setsbits(_t, charcount);
	charcount = sbits(_t);
	if (dip != d)
		wbt(0);
	if (charcount >= charoutsz) {
		charoutsz += 32;
		charout = realloc(charout, charoutsz * sizeof *charout);
	}
	memset(&charout[charcount], 0, sizeof *charout);
	fp->savedip = dip;
	memset(&fp->newd, 0, sizeof fp->newd);
	dip = &fp->newd;
	offset = dip->op = startb;
	charout[charcount].op = startb;
	fp->savnflush = nflush;
	fp->savvflag = vflag;
	vflag = 0;
	fp->savvpt = vpt;
	vpt = 0;
	fp->savhp = numtab[HP].val;
	fp->saveev = env;
	evc(&env, &env);
	in = in1 = 0;
	fi = 0;
	return charcount++;
}

static void
restchar(struct fmtchar *fp, int keepf)
{
	wbt(0);
	dip = fp->savedip;
	offset = dip->op;
	relsev(&env);
	if (keepf) {
		fp->saveev._apts = apts;
		fp->saveev._apts1 = apts1;
		fp->saveev._pts = pts;
		fp->saveev._pts1 = pts1;
		fp->saveev._font = font;
		fp->saveev._font1 = font1;
		fp->saveev._chbits = chbits;
		fp->saveev._spbits = spbits;
	}
	env = fp->saveev;
	nflush = fp->savnflush;
	vflag = fp->savvflag;
	vpt = fp->savvpt;
	numtab[HP].val = fp->savhp;
}

tchar
setchar(tchar c)
{
	struct fmtchar	f;
	int	k = trtab[cbits(c)];
	tchar	*csp;
	int	charcount;
	int	savxflag;
	int	saveschar;

#ifndef	NROFF
	if (fchartab[k] && onfont(c))
		return c;
#endif
	if (iszbit(c))
		return c;
	if ((charcount = prepchar(&f)) < 0)
		return ' ';
	fmtchar++;
	savxflag = xflag;
	xflag = 3;
	saveschar = eschar;
	eschar = '\\';
	csp = chartab[k];
	chartab[k] = NULL;
	pushback(csp);
	text();
	tbreak();
	nlflg = 0;
	charout[charcount].ch = c;
	if (iszbit(c))
		charout[charcount].width = 0;
	else {
		charout[charcount].width = dip->maxl - lasttrack;
		width(' ' | sfmask(c));
		charout[charcount].width += lasttrack;
	}
	charout[charcount].height = maxcht;
	charout[charcount].depth = maxcdp;
	restchar(&f, 0);
	chartab[k] = csp;
	eschar = saveschar;
	xflag = savxflag;
	fmtchar--;
	return mkxfunc(CHAR, charcount);
}

static tchar
setZ(void)
{
	struct fmtchar	f;
	int	charcount;
	tchar	i;

	if (ismot(i = getch()))
		return 0;
	if ((charcount = prepchar(&f)) < 0)
		return 0;
	stopch = i;
	charout[charcount].ch = FILLER | sfmask(stopch);
	text();
	if (nlflg)
		nodelim(stopch);
	charout[charcount].ch = 0;
	restchar(&f, 1);
	return mkxfunc(CHAR, charcount);
}

tchar
sfmask(tchar _t)
{
	while (isxfunc(_t, CHAR))
		_t = charout[sbits(_t)].ch;
	if (_t == XFUNC || _t == SLANT || (_t & SFMASK) == 0)
		return chbits;
	return _t & SFMASK;
}

int
issame(tchar c, tchar _d)
{
	if (ismot(c) || ismot(_d))
		return 0;
	while (isxfunc(c, CHAR))
		c = charout[sbits(c)].ch;
	while (isxfunc(_d, CHAR))
		_d = charout[sbits(_d)].ch;
	if (cbits(c) != cbits(_d))
		return 0;
	if (cbits(c) == XFUNC && cbits(_d) == XFUNC)
		return fbits(c) == fbits(_d);
	return 1;
}

static int
setgA(void)
{
	extern const char	nmctab[];
	tchar	c, delim;
	int	k, y = 1;

	lgf++;
	delim = getch();
	if (ismot(delim)) {
		lgf--;
		return 0;
	}
	while (k = cbits(c = getch()), !issame(c, delim) && !nlflg)
		if (ismot(c) || (k < ' ' && nmctab[k]) || k == ' ' || k >= 0200)
			y = 0;
	if (nlflg)
		y = 0;
	lgf--;
	return y;
}

static int
setB(void)
{
	tchar	c, delim;
	int	y = 1;

	lgf++;
	delim = getch();
	if (ismot(delim)) {
		lgf--;
		return 0;
	}
	atoi0();
	if (nonumb)
		y = 0;
	do {
		c = getch();
		if (!ismot(c) && issame(c, delim))
			break;
		y = 0;
	} while (!nlflg);
	lgf--;
	return y;
}

void
caseescoff(void) {
	_caseesc(1);
}

void
caseescon(void) {
	_caseesc(0);
}

static void
_caseesc(int off) {
	int c;
	if (skip(1)) return;
	while (1) {
		c = cbits(getch());
		if (c < 32 || c > 126)
			errprint("Invalid character '%c' for .esc%s\n",
			    c, off ? "off" : "on");
		else
			escoff[c-32] = (unsigned char)off;
		if (skip(0))
			return;
	}
}
