plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

n1.c (20409B)


      1 /*
      2  * n1.c
      3  *
      4  *	consume options, initialization, main loop,
      5  *	input routines, escape function calling
      6  */
      7 
      8 #include <u.h>
      9 #include "tdef.h"
     10 #include "fns.h"
     11 #include "ext.h"
     12 #include "dwbinit.h"
     13 
     14 #include <setjmp.h>
     15 #include <time.h>
     16 
     17 char	*Version	= "March 11, 1994";
     18 
     19 #ifndef DWBVERSION
     20 #define DWBVERSION      "???"
     21 #endif
     22 
     23 char	*DWBfontdir = FONTDIR;
     24 char	*DWBntermdir = NTERMDIR;
     25 char	*DWBalthyphens = ALTHYPHENS;
     26 char	*DWBhomedir = "";
     27 
     28 dwbinit dwbpaths[] = {
     29 	&DWBfontdir, NULL, 0,
     30 	&DWBntermdir, NULL, 0,
     31 	&DWBalthyphens, NULL, 0,
     32 	&DWBhomedir, NULL, 0,
     33 	NULL, nextf, NS,
     34 	NULL, NULL, 0
     35 };
     36 
     37 int	TROFF	= 1;	/* assume we started in troff... */
     38 
     39 jmp_buf sjbuf;
     40 Offset	ipl[NSO];
     41 
     42 static	FILE	*ifile;
     43 static	FILE	*ifl[NSO];	/* open input file pointers */
     44 char	cfname[NSO+1][NS] = {  "stdin" };	/* file name stack */
     45 int	cfline[NSO];		/* input line count stack */
     46 char	*progname;		/* program name (troff or nroff) */
     47 
     48 int	trace = 0;	/* tracing mode: default off */
     49 int	trace1 = 0;
     50 
     51 int
     52 main(int argc, char *argv[])
     53 {
     54 	char *p;
     55 	int j;
     56 	Tchar i;
     57 	char buf[100];
     58 
     59 	ifile = stdin;		/* gcc */
     60 	ptid = stdout;
     61 
     62 	buf[0] = '\0';		/* make sure it's empty (silly 3b2) */
     63 	progname = argv[0];
     64 	if ((p = strrchr(progname, '/')) == NULL)
     65 		p = progname;
     66 	else
     67 		p++;
     68 	DWBinit(progname, dwbpaths);
     69 	if (strcmp(p, "nroff") == 0)
     70 		TROFF = 0;
     71 #ifdef UNICODE
     72 	alphabet = 128;	/* unicode for plan 9 */
     73 #endif	/*UNICODE*/
     74 	mnspace();
     75 	nnspace();
     76 	mrehash();
     77 	nrehash();
     78 	numtabp[NL].val = -1;
     79 
     80 	while (--argc > 0 && (++argv)[0][0] == '-')
     81 		switch (argv[0][1]) {
     82 
     83 		case 'N':	/* ought to be used first... */
     84 			TROFF = 0;
     85 			break;
     86 		case 'd':
     87 			fprintf(stderr, "troff/nroff version %s\n", Version);
     88 			break;
     89 		case 'F':	/* switch font tables from default */
     90 			if (argv[0][2] != '\0') {
     91 				strcpy(termtab, &argv[0][2]);
     92 				strcpy(fontdir, &argv[0][2]);
     93 			} else {
     94 				argv++; argc--;
     95 				strcpy(termtab, argv[0]);
     96 				strcpy(fontdir, argv[0]);
     97 			}
     98 			break;
     99 		case 0:
    100 			goto start;
    101 		case 'i':
    102 			stdi++;
    103 			break;
    104 		case 'n':
    105 			npn = atoi(&argv[0][2]);
    106 			break;
    107 		case 'u':	/* set emboldening amount */
    108 			bdtab[3] = atoi(&argv[0][2]);
    109 			if (bdtab[3] < 0 || bdtab[3] > 50)
    110 				bdtab[3] = 0;
    111 			break;
    112 		case 's':
    113 			if (!(stop = atoi(&argv[0][2])))
    114 				stop++;
    115 			break;
    116 		case 'r':
    117 			sprintf(buf + strlen(buf), ".nr %c %s\n",
    118 				argv[0][2], &argv[0][3]);
    119 			/* not yet cpushback(buf);*/
    120 			/* dotnr(&argv[0][2], &argv[0][3]); */
    121 			break;
    122 		case 'm':
    123 			if (mflg++ >= NMF) {
    124 				ERROR "Too many macro packages: %s", argv[0] WARN;
    125 				break;
    126 			}
    127 			strcpy(mfiles[nmfi], nextf);
    128 			strcat(mfiles[nmfi++], &argv[0][2]);
    129 			break;
    130 		case 'o':
    131 			getpn(&argv[0][2]);
    132 			break;
    133 		case 'T':
    134 			strcpy(devname, &argv[0][2]);
    135 			dotT++;
    136 			break;
    137 		case 'a':
    138 			ascii = 1;
    139 			break;
    140 		case 'h':
    141 			hflg++;
    142 			break;
    143 		case 'e':
    144 			eqflg++;
    145 			break;
    146 		case 'q':
    147 			quiet++;
    148 			save_tty();
    149 			break;
    150 		case 'V':
    151 			fprintf(stdout, "%croff: DWB %s\n",
    152 					TROFF ? 't' : 'n', DWBVERSION);
    153 			exit(0);
    154 		case 't':
    155 			if (argv[0][2] != '\0')
    156 				trace = trace1 = argv[0][2];
    157 			break;		/* for the sake of compatibility */
    158 		default:
    159 			ERROR "unknown option %s", argv[0] WARN;
    160 			done(02);
    161 		}
    162 
    163 start:
    164 	/*
    165 	 * cpushback maintains a LIFO, so push pack the -r arguments
    166 	 * in reverse order to maintain a FIFO in case someone did -rC1 -rC3
    167 	 */
    168 	if (buf[0]) {
    169 		char *p = buf;
    170 		while(*p++)
    171 			;
    172 		while(p > buf) {
    173 			while(strncmp(p, ".nr", 3) != 0)
    174 				p--;
    175 			cpushback(p);
    176 			*p-- = '\0';
    177 		}
    178 	}
    179 	argp = argv;
    180 	rargc = argc;
    181 	nmfi = 0;
    182 	init2();
    183 	setjmp(sjbuf);
    184 loop:
    185 	copyf = lgf = nb = nflush = nlflg = 0;
    186 	if (ip && rbf0(ip) == 0 && ejf && frame->pframe <= ejl && dip == d) {
    187 		nflush++;
    188 		trap = 0;
    189 		eject((Stack *)0);
    190 		goto loop;
    191 	}
    192 	i = getch();
    193 	if (pendt)
    194 		goto Lt;
    195 	if ((j = cbits(i)) == XPAR) {
    196 		copyf++;
    197 		tflg++;
    198 		while (cbits(i) != '\n')
    199 			pchar(i = getch());
    200 		tflg = 0;
    201 		copyf--;
    202 		goto loop;
    203 	}
    204 	if (j == cc || j == c2) {
    205 		if (j == c2)
    206 			nb++;
    207 		copyf++;
    208 		while ((j = cbits(i = getch())) == ' ' || j == '\t')
    209 			;
    210 		ch = i;
    211 		copyf--;
    212 		control(getrq(), 1);
    213 		flushi();
    214 		goto loop;
    215 	}
    216 Lt:
    217 	ch = i;
    218 	text();
    219 	if (nlflg)
    220 		numtabp[HP].val = 0;
    221 	goto loop;
    222 }
    223 
    224 
    225 
    226 void init2(void)
    227 {
    228 	int i;
    229 	char buf[100];
    230 
    231 	for (i = NTRTAB; --i; )
    232 		trtab[i] = i;
    233 	trtab[UNPAD] = ' ';
    234 	iflg = 0;
    235 	obufp = obuf;
    236 	if (TROFF)
    237 		t_ptinit();
    238 	else
    239 		n_ptinit();
    240 	mchbits();
    241 	cvtime();
    242 	numtabp[PID].val = getpid();
    243 	numtabp[HP].val = init = 0;
    244 	numtabp[NL].val = -1;
    245 	nfo = 0;
    246 	copyf = raw = 0;
    247 	sprintf(buf, ".ds .T %s\n", devname);
    248 	cpushback(buf);
    249 	sprintf(buf, ".ds .P %s\n", DWBhomedir);
    250 	cpushback(buf);
    251 	numtabp[CD].val = -1;	/* compensation */
    252 	nx = mflg;
    253 	frame = stk = (Stack *)setbrk(STACKSIZE);
    254 	dip = &d[0];
    255 	nxf = frame + 1;
    256 	for (i = 1; i < NEV; i++)	/* propagate the environment */
    257 		envcopy(&env[i], &env[0]);
    258 	for (i = 0; i < NEV; i++) {
    259 		if ((env[i]._word._bufp = (Tchar *)calloc(WDSIZE, sizeof(Tchar))) == NULL) {
    260 			ERROR "not enough room for word buffers" WARN;
    261 			done2(1);
    262 		}
    263 		env[i]._word._size = WDSIZE;
    264 		if ((env[i]._line._bufp = (Tchar *)calloc(LNSIZE, sizeof(Tchar))) == NULL) {
    265 			ERROR "not enough room for line buffers" WARN;
    266 			done2(1);
    267 		}
    268 		env[i]._line._size = LNSIZE;
    269 	}
    270 	if ((oline = (Tchar *)calloc(OLNSIZE, sizeof(Tchar))) == NULL) {
    271 		ERROR "not enough room for line buffers" WARN;
    272 		done2(1);
    273 	}
    274 	olinep = oline;
    275 	olnsize = OLNSIZE;
    276 	blockinit();
    277 }
    278 
    279 void cvtime(void)
    280 {
    281 	time_t tt;
    282 	struct tm *ltime;
    283 
    284 	time(&tt);
    285 	ltime = localtime(&tt);
    286 	numtabp[YR].val = ltime->tm_year % 100;
    287 	numtabp[YR].fmt = 2;
    288 	numtabp[MO].val = ltime->tm_mon + 1;	/* troff uses 1..12 */
    289 	numtabp[DY].val = ltime->tm_mday;
    290 	numtabp[DW].val = ltime->tm_wday + 1;	/* troff uses 1..7 */
    291 }
    292 
    293 
    294 
    295 char	errbuf[200];
    296 
    297 void errprint(void)	/* error message printer */
    298 {
    299 	int savecd = numtabp[CD].val;
    300 
    301 	if (!nlflg)
    302 		numtabp[CD].val++;
    303 
    304 	fprintf(stderr, "%s: ", progname);
    305 	fputs(errbuf, stderr);
    306 	if (cfname[ifi][0])
    307 		fprintf(stderr, "; %s:%d", cfname[ifi], numtabp[CD].val);
    308 	fputs("\n", stderr);
    309 	if (cfname[ifi][0])
    310 		stackdump();
    311 	numtabp[CD].val = savecd;
    312 }
    313 
    314 
    315 int control(int a, int b)
    316 {
    317 	int j, k;
    318 	extern Contab *contabp;
    319 
    320 	numerr.type = RQERR;
    321 	numerr.req = a;
    322 	if (a == 0 || (j = findmn(a)) == -1)
    323 		return(0);
    324 	if (contabp[j].f == 0) {
    325 		if (trace & TRMAC)
    326 			fprintf(stderr, "invoke macro %s\n", unpair(a));
    327 		if (dip != d)
    328 			for (k = dilev; k; k--)
    329 				if (d[k].curd == a) {
    330 					ERROR "diversion %s invokes itself during diversion",
    331 								unpair(a) WARN;
    332 					edone(0100);
    333 				}
    334 		nxf->nargs = 0;
    335 		if (b)
    336 			collect();
    337 		flushi();
    338 		return pushi(contabp[j].mx, a);	/* BUG??? all that matters is 0/!0 */
    339 	}
    340 	if (b) {
    341 		if (trace & TRREQ)
    342 			fprintf(stderr, "invoke request %s\n", unpair(a));
    343 		 (*contabp[j].f)();
    344 	}
    345 	return(0);
    346 }
    347 
    348 void casept(void)
    349 {
    350 	int i;
    351 
    352 	noscale++;
    353 	if (skip())
    354 		i = trace1;
    355 	else {
    356 		i = max(inumb(&trace), 0);
    357 		if (nonumb)
    358 			i = trace1;
    359 	}
    360 	trace1 = trace;
    361 	trace = i;
    362 	noscale = 0;
    363 }
    364 
    365 
    366 int getrq(void)
    367 {
    368 	int i, j;
    369 
    370 	if ((i = getach()) == 0 || (j = getach()) == 0)
    371 		goto rtn;
    372 	i = PAIR(i, j);
    373 rtn:
    374 	return(i);
    375 }
    376 
    377 /*
    378  * table encodes some special characters, to speed up tests
    379  * in getch, viz FLSS, RPT, f, \b, \n, fc, tabch, ldrch
    380  */
    381 
    382 char gchtab[NCHARS] = {
    383 	000,004,000,000,010,000,000,000, /* fc, ldr */
    384 	001,002,001,000,001,000,000,000, /* \b, tab, nl, RPT */
    385 	000,000,000,000,000,000,000,000,
    386 	000,001,000,001,000,000,000,000, /* FLSS, ESC */
    387 	000,000,000,000,000,000,000,000,
    388 	000,000,000,000,000,000,000,000,
    389 	000,000,000,000,000,000,000,000,
    390 	000,000,000,000,000,000,000,000,
    391 	000,000,000,000,000,000,000,000,
    392 	000,000,000,000,000,000,000,000,
    393 	000,000,000,000,000,000,000,000,
    394 	000,000,000,000,000,000,000,000,
    395 	000,000,000,000,000,000,001,000, /* f */
    396 	000,000,000,000,000,000,000,000,
    397 	000,000,000,000,000,000,000,000,
    398 	000,000,000,000,000,000,000,000
    399 };
    400 
    401 int realcbits(Tchar c)	/* return character bits, or MOTCH if motion */
    402 {
    403 	if (ismot(c))
    404 		return MOTCH;
    405 	else
    406 		return c & 0xFFFF;
    407 }
    408 
    409 Tchar getch(void)
    410 {
    411 	int k;
    412 	Tchar i, j;
    413 
    414 g0:
    415 	if (ch) {
    416 		i = ch;
    417 		if (cbits(i) == '\n')
    418 			nlflg++;
    419 		ch = 0;
    420 		return(i);
    421 	}
    422 
    423 	if (nlflg)
    424 		return('\n');
    425 	i = getch0();
    426 	if (ismot(i))
    427 		return(i);
    428 	k = cbits(i);
    429 	if (k >= sizeof(gchtab)/sizeof(gchtab[0]) || gchtab[k] == 0)	/* nothing special */
    430 		return(i);
    431 	if (k != ESC) {
    432 		if (k == '\n') {
    433 			nlflg++;
    434 			if (ip == 0)
    435 				numtabp[CD].val++; /* line number */
    436 			return(k);
    437 		}
    438 		if (k == FLSS) {
    439 			copyf++;
    440 			raw++;
    441 			i = getch0();
    442 			if (!fi)
    443 				flss = i;
    444 			copyf--;
    445 			raw--;
    446 			goto g0;
    447 		}
    448 		if (k == RPT) {
    449 			setrpt();
    450 			goto g0;
    451 		}
    452 		if (!copyf) {
    453 			if (k == 'f' && lg && !lgf) {
    454 				i = getlg(i);
    455 				return(i);
    456 			}
    457 			if (k == fc || k == tabch || k == ldrch) {
    458 				if ((i = setfield(k)) == 0)
    459 					goto g0;
    460 				else
    461 					return(i);
    462 			}
    463 			if (k == '\b') {
    464 				i = makem(-width(' ' | chbits));
    465 				return(i);
    466 			}
    467 		}
    468 		return(i);
    469 	}
    470 
    471 	k = cbits(j = getch0());
    472 	if (ismot(j))
    473 		return(j);
    474 
    475 	switch (k) {
    476 	case 'n':	/* number register */
    477 		setn();
    478 		goto g0;
    479 	case '$':	/* argument indicator */
    480 		seta();
    481 		goto g0;
    482 	case '*':	/* string indicator */
    483 		setstr();
    484 		goto g0;
    485 	case '{':	/* LEFT */
    486 		i = LEFT;
    487 		goto gx;
    488 	case '}':	/* RIGHT */
    489 		i = RIGHT;
    490 		goto gx;
    491 	case '"':	/* comment */
    492 		while (cbits(i = getch0()) != '\n')
    493 			;
    494 		if (ip == 0)
    495 			numtabp[CD].val++; /* line number */
    496 		nlflg++;
    497 		return(i);
    498 
    499 /* experiment: put it here instead of copy mode */
    500 	case '(':	/* special char name \(xx */
    501 	case 'C':	/* 		\C'...' */
    502 		if ((i = setch(k)) == 0)
    503 			goto g0;
    504 		goto gx;
    505 
    506 	case ESC:	/* double backslash */
    507 		i = eschar;
    508 		goto gx;
    509 	case 'e':	/* printable version of current eschar */
    510 		i = PRESC;
    511 		goto gx;
    512 	case '\n':	/* concealed newline */
    513 		numtabp[CD].val++;
    514 		goto g0;
    515 	case '~':	/* Heirloom/groff/neatroff: unbreakable space */
    516 		; /* fall through */
    517 	case ' ':	/* unpaddable space */
    518 		i = UNPAD;
    519 		goto gx;
    520 	case '\'':	/* \(aa */
    521 		i = ACUTE;
    522 		goto gx;
    523 	case '`':	/* \(ga */
    524 		i = GRAVE;
    525 		goto gx;
    526 	case '_':	/* \(ul */
    527 		i = UNDERLINE;
    528 		goto gx;
    529 	case '-':	/* current font minus */
    530 		i = MINUS;
    531 		goto gx;
    532 	case '&':	/* filler */
    533 		i = FILLER;
    534 		goto gx;
    535 	case 'c':	/* to be continued */
    536 		i = CONT;
    537 		goto gx;
    538 	case '!':	/* transparent indicator */
    539 		i = XPAR;
    540 		goto gx;
    541 	case 't':	/* tab */
    542 		i = '\t';
    543 		return(i);
    544 	case 'a':	/* leader (SOH) */
    545 /* old:		*pbp++ = LEADER; goto g0; */
    546 		i = LEADER;
    547 		return i;
    548 	case '%':	/* ohc */
    549 		i = OHC;
    550 		return(i);
    551 	case 'g':	/* return format of a number register */
    552 		setaf();	/* should this really be in copy mode??? */
    553 		goto g0;
    554 	case '.':	/* . */
    555 		i = '.';
    556 gx:
    557 		setsfbits(i, sfbits(j));
    558 		return(i);
    559 	}
    560 	if (copyf) {
    561 		*pbp++ = j;
    562 		return(eschar);
    563 	}
    564 	switch (k) {
    565 
    566 	case 'f':	/* font indicator */
    567 		setfont(0);
    568 		goto g0;
    569 	case 's':	/* size indicator */
    570 		setps();
    571 		goto g0;
    572 	case 'v':	/* vert mot */
    573 		numerr.type = numerr.escarg = 0; numerr.esc = k;
    574 		if (i = vmot()) {
    575 			return(i);
    576 		}
    577 		goto g0;
    578 	case 'h': 	/* horiz mot */
    579 		numerr.type = numerr.escarg = 0; numerr.esc = k;
    580 		if (i = hmot())
    581 			return(i);
    582 		goto g0;
    583 	case '|':	/* narrow space */
    584 		if (NROFF)
    585 			goto g0;
    586 		return(makem((int)(EM)/6));
    587 	case '^':	/* half narrow space */
    588 		if (NROFF)
    589 			goto g0;
    590 		return(makem((int)(EM)/12));
    591 	case 'w':	/* width function */
    592 		setwd();
    593 		goto g0;
    594 	case 'p':	/* spread */
    595 		spread++;
    596 		goto g0;
    597 	case 'N':	/* absolute character number */
    598 		numerr.type = numerr.escarg = 0; numerr.esc = k;
    599 		if ((i = setabs()) == 0)
    600 			goto g0;
    601 		return i;
    602 	case 'H':	/* character height */
    603 		numerr.type = numerr.escarg = 0; numerr.esc = k;
    604 		return(setht());
    605 	case 'S':	/* slant */
    606 		numerr.type = numerr.escarg = 0; numerr.esc = k;
    607 		return(setslant());
    608 	case 'z':	/* zero with char */
    609 		return(setz());
    610 	case 'l':	/* hor line */
    611 		numerr.type = numerr.escarg = 0; numerr.esc = k;
    612 		setline();
    613 		goto g0;
    614 	case 'L':	/* vert line */
    615 		numerr.type = numerr.escarg = 0; numerr.esc = k;
    616 		setvline();
    617 		goto g0;
    618 	case 'D':	/* drawing function */
    619 		numerr.type = numerr.escarg = 0; numerr.esc = k;
    620 		setdraw();
    621 		goto g0;
    622 	case 'X':	/* \X'...' for copy through */
    623 		setxon();
    624 		goto g0;
    625 	case 'b':	/* bracket */
    626 		setbra();
    627 		goto g0;
    628 	case 'o':	/* overstrike */
    629 		setov();
    630 		goto g0;
    631 	case 'k':	/* mark hor place */
    632 		if ((k = findr(getsn())) != -1) {
    633 			numtabp[k].val = numtabp[HP].val;
    634 		}
    635 		goto g0;
    636 	case '0':	/* number space */
    637 		return(makem(width('0' | chbits)));
    638 	case 'x':	/* extra line space */
    639 		numerr.type = numerr.escarg = 0; numerr.esc = k;
    640 		if (i = xlss())
    641 			return(i);
    642 		goto g0;
    643 	case 'u':	/* half em up */
    644 	case 'r':	/* full em up */
    645 	case 'd':	/* half em down */
    646 		return(sethl(k));
    647 	default:
    648 		return(j);
    649 	}
    650 	/* NOTREACHED */
    651 }
    652 
    653 void setxon(void)	/* \X'...' for copy through */
    654 {
    655 	Tchar xbuf[NC];
    656 	Tchar *i;
    657 	Tchar c;
    658 	int delim, k;
    659 
    660 	if (ismot(c = getch()))
    661 		return;
    662 	delim = cbits(c);
    663 	i = xbuf;
    664 	*i++ = XON | chbits;
    665 	while ((k = cbits(c = getch())) != delim && k != '\n' && i < xbuf+NC-1) {
    666 		if (k == ' ')
    667 			setcbits(c, WORDSP);
    668 		*i++ = c | ZBIT;
    669 	}
    670 	*i++ = XOFF | chbits;
    671 	*i = 0;
    672 	pushback(xbuf);
    673 }
    674 
    675 
    676 char	ifilt[32] = { 0, 001, 002, 003, 0, 005, 006, 007, 010, 011, 012 };
    677 
    678 Tchar getch0(void)
    679 {
    680 	Tchar i;
    681 
    682 again:
    683 	if (pbp > lastpbp)
    684 		i = *--pbp;
    685 	else if (ip) {
    686 		/* i = rbf(); */
    687 		i = rbf0(ip);
    688 		if (i == 0)
    689 			i = rbf();
    690 		else {
    691 			++ip;
    692 			if (pastend(ip)) {
    693 				--ip;
    694 				rbf();
    695 			}
    696 		}
    697 	} else {
    698 		if (donef || ndone)
    699 			done(0);
    700 		if (nx || 1) {	/* BUG: was ibufp >= eibuf, so EOF test is wrong */
    701 			if (nfo < 0)
    702 				ERROR "in getch0, nfo = %d", nfo WARN;
    703 			if (nfo == 0) {
    704 g0:
    705 				if (nextfile()) {
    706 					if (ip)
    707 						goto again;
    708 				}
    709 			}
    710 			nx = 0;
    711 #ifdef UNICODE
    712 			if (MB_CUR_MAX > 1)
    713 				i = get1ch(ifile);
    714 			else
    715 #endif	/*UNICODE*/
    716 				i = getc(ifile);
    717 			if (i == EOF)
    718 				goto g0;
    719 			if (ip)
    720 				goto again;
    721 		}
    722 /*g2: */
    723 		if (i >= 040)			/* zapped: && i < 0177 */
    724 			goto g4;
    725 		i = ifilt[i];
    726 	}
    727 	if (cbits(i) == IMP && !raw)
    728 		goto again;
    729 	if (i == 0 && !init && !raw) {		/* zapped:  || i == 0177 */
    730 		goto again;
    731 	}
    732 g4:
    733 	if (ismot(i))
    734 		return i;
    735 	if (copyf == 0 && sfbits(i) == 0)
    736 		i |= chbits;
    737 	if (cbits(i) == eschar && !raw)
    738 		setcbits(i, ESC);
    739 	return(i);
    740 }
    741 
    742 
    743 #ifdef UNICODE
    744 Tchar get1ch(FILE *fp)	/* get one "character" from input, figure out what alphabet */
    745 {
    746 	wchar_t wc;
    747 	char buf[100], *p;
    748 	int i, n, c;
    749 
    750 	for (i = 0, p = buf; i < MB_CUR_MAX; i++) {
    751 		if ((c = getc(fp)) == EOF)
    752 			return c;
    753 		*p++ = c;
    754 		if ((n = mbtowc(&wc, buf, p-buf)) >= 0)
    755 			break;
    756 	}
    757 
    758 	if (n == 1)	/* real ascii, presumably */
    759 		return wc;
    760 	if (n == 0)
    761 		return p[-1];	/* illegal, but what else to do? */
    762 	if (c == EOF)
    763 		return EOF;
    764 	*p = 0;
    765 	return chadd(buf, MBchar, Install);	/* add name even if haven't seen it */
    766 }
    767 #endif	/*UNICODE*/
    768 
    769 void pushback(Tchar *b)
    770 {
    771 	Tchar *ob = b;
    772 
    773 	while (*b++)
    774 		;
    775 	b--;
    776 	while (b > ob && pbp < &pbbuf[NC-3])
    777 		*pbp++ = *--b;
    778 	if (pbp >= &pbbuf[NC-3]) {
    779 		ERROR "pushback overflow" WARN;
    780 		done(2);
    781 	}
    782 }
    783 
    784 void cpushback(char *b)
    785 {
    786 	char *ob = b;
    787 
    788 	while (*b++)
    789 		;
    790 	b--;
    791 	while (b > ob && pbp < &pbbuf[NC-3])
    792 		*pbp++ = *--b;
    793 	if (pbp >= &pbbuf[NC-3]) {
    794 		ERROR "cpushback overflow" WARN;
    795 		done(2);
    796 	}
    797 }
    798 
    799 int nextfile(void)
    800 {
    801 	char *p;
    802 
    803 n0:
    804 	if (ifile != stdin)
    805 		fclose(ifile);
    806 	if (ifi > 0 && !nx) {
    807 		if (popf())
    808 			goto n0; /* popf error */
    809 		return(1);	 /* popf ok */
    810 	}
    811 	if (nx || nmfi < mflg) {
    812 		p = mfiles[nmfi++];
    813 		if (*p != 0)
    814 			goto n1;
    815 	}
    816 	if (rargc-- <= 0) {
    817 		if ((nfo -= mflg) && !stdi) {
    818 			done(0);
    819 }
    820 		nfo++;
    821 		numtabp[CD].val = stdi = mflg = 0;
    822 		ifile = stdin;
    823 		strcpy(cfname[ifi], "stdin");
    824 		return(0);
    825 	}
    826 	p = (argp++)[0];
    827 	if (rargc >= 0)
    828 		cfname[ifi][0] = 0;
    829 n1:
    830 	numtabp[CD].val = 0;
    831 	if (p[0] == '-' && p[1] == 0) {
    832 		ifile = stdin;
    833 		strcpy(cfname[ifi], "stdin");
    834 	} else if ((ifile = fopen(unsharp(p), "r")) == NULL) {
    835 		ERROR "cannot open file %s", p WARN;
    836 		nfo -= mflg;
    837 		done(02);
    838 	} else
    839 		strcpy(cfname[ifi],p);
    840 	nfo++;
    841 	return(0);
    842 }
    843 
    844 int
    845 popf(void)
    846 {
    847 	--ifi;
    848 	if (ifi < 0) {
    849 		ERROR "popf went negative" WARN;
    850 		return 1;
    851 	}
    852 	numtabp[CD].val = cfline[ifi];	/* restore line counter */
    853 	ip = ipl[ifi];			/* input pointer */
    854 	ifile = ifl[ifi];		/* input FILE * */
    855 	return(0);
    856 }
    857 
    858 
    859 void flushi(void)
    860 {
    861 	if (nflush)
    862 		return;
    863 	ch = 0;
    864 	copyf++;
    865 	while (!nlflg) {
    866 		if (donef && frame == stk)
    867 			break;
    868 		getch();
    869 	}
    870 	copyf--;
    871 }
    872 
    873 /*
    874  * return 16-bit, ascii/alphabetic character, ignore chars with more bits,
    875  * (internal names), spaces and special cookies (below 040).
    876  * Leave STX ETX ENQ ACK and BELL in to maintain compatibility with v7 troff.
    877  */
    878 int
    879 getach(void)
    880 {
    881 	Tchar i;
    882 	int j;
    883 
    884 	lgf++;
    885 	j = cbits(i = getch());
    886         if (ismot(i)
    887 	    || j > SHORTMASK
    888 	    || (j <= 040 && j != 002	/*STX*/
    889 			&& j != 003	/*ETX*/
    890 			&& j != 005	/*ENQ*/
    891 			&& j != 006	/*ACK*/
    892 			&& j != 007)) {	/*BELL*/
    893 		ch = i;
    894 		j = 0;
    895 	}
    896 	lgf--;
    897 	return j;
    898 }
    899 
    900 
    901 void casenx(void)
    902 {
    903 	lgf++;
    904 	skip();
    905 	getname();
    906 	nx++;
    907 	if (nmfi > 0)
    908 		nmfi--;
    909 	strcpy(mfiles[nmfi], nextf);
    910 	nextfile();
    911 	nlflg++;
    912 	ip = 0;
    913 	pendt = 0;
    914 	frame = stk;
    915 	nxf = frame + 1;
    916 }
    917 
    918 int
    919 getname(void)
    920 {
    921 	int j, k;
    922 
    923 	lgf++;
    924 	for (k = 0; k < NS - 1; k++) {
    925 		j = getach();
    926 		if (!j)
    927 			break;
    928 		nextf[k] = j;
    929 	}
    930 	nextf[k] = 0;
    931 	lgf--;
    932 	return(nextf[0]);
    933 }
    934 
    935 
    936 void caseso(void)
    937 {
    938 	FILE *fp = 0;
    939 
    940 	lgf++;
    941 	nextf[0] = 0;
    942 	if (skip() || !getname() || (fp = fopen(unsharp(nextf), "r")) == NULL || ifi >= NSO) {
    943 		ERROR "can't open file %s", nextf WARN;
    944 		done(02);
    945 	}
    946 	strcpy(cfname[ifi+1], nextf);
    947 	cfline[ifi] = numtabp[CD].val;		/*hold line counter*/
    948 	numtabp[CD].val = 0;
    949 	flushi();
    950 	ifl[ifi] = ifile;
    951 	ifile = fp;
    952 	ipl[ifi] = ip;
    953 	ip = 0;
    954 	nx++;
    955 	nflush++;
    956 	ifi++;
    957 }
    958 
    959 void caself(void)	/* set line number and file */
    960 {
    961 	int n;
    962 
    963 	if (skip())
    964 		return;
    965 	n = atoi0();
    966 	if (!nonumb)
    967 		cfline[ifi] = numtabp[CD].val = n - 1;
    968 	if (!skip())
    969 		if (getname()) {	/* eats '\n' ? */
    970 			strcpy(cfname[ifi], nextf);
    971 			if (!nonumb)
    972 				numtabp[CD].val--;
    973 		}
    974 }
    975 
    976 void cpout(FILE *fin, char *token)
    977 {
    978 	int n;
    979 	char buf[1024];
    980 
    981 	if (token) {	/* BUG: There should be no NULL bytes in input */
    982 		char *newl = buf;
    983 		while ((fgets(buf, sizeof buf, fin)) != NULL) {
    984 			if (newl) {
    985 				numtabp[CD].val++; /* line number */
    986 				if (strcmp(token, buf) == 0)
    987 					return;
    988 			}
    989 			newl = strchr(buf, '\n');
    990 			fputs(buf, ptid);
    991 		}
    992 	} else {
    993 		while ((n = fread(buf, sizeof *buf, sizeof buf, fin)) > 0)
    994 			fwrite(buf, n, 1, ptid);
    995 		fclose(fin);
    996 	}
    997 }
    998 
    999 void casecf(void)
   1000 {	/* copy file without change */
   1001 	FILE *fd;
   1002 	char *eof, *p;
   1003 	extern int hpos, esc, po;
   1004 
   1005 	/* this may not make much sense in nroff... */
   1006 
   1007 	lgf++;
   1008 	nextf[0] = 0;
   1009 	if (!skip() && getname()) {
   1010 		if (strncmp("<<", nextf, 2) != 0) {
   1011 			if ((fd = fopen(unsharp(nextf), "r")) == NULL) {
   1012 				ERROR "can't open file %s", nextf WARN;
   1013 				done(02);
   1014 			}
   1015 			eof = (char *) NULL;
   1016 		} else {	/* current file */
   1017 			if (pbp > lastpbp || ip) {
   1018 				ERROR "casecf: not reading from file" WARN;
   1019 				done(02);
   1020 			}
   1021 			eof = &nextf[2];
   1022 			if (!*eof)  {
   1023 				ERROR "casecf: missing end of input token" WARN;
   1024 				done(02);
   1025 			}
   1026 			p = eof;
   1027 			while(*++p)
   1028 				;
   1029 			*p++ = '\n';
   1030 			*p = 0;
   1031 			fd = ifile;
   1032 		}
   1033 	} else {
   1034 		ERROR "casecf: no argument" WARN;
   1035 		lgf--;
   1036 		return;
   1037 	}
   1038 	lgf--;
   1039 
   1040 	/* make it into a clean state, be sure that everything is out */
   1041 	tbreak();
   1042 	hpos = po;
   1043 	esc = 0;
   1044 	ptesc();	/* to left margin */
   1045 	esc = un;
   1046 	ptesc();
   1047 	ptlead();
   1048 	ptps();
   1049 	ptfont();
   1050 	flusho();
   1051 	cpout(fd, eof);
   1052 	ptps();
   1053 	ptfont();
   1054 }
   1055 
   1056 void getline(char *s, int n)	/* get rest of input line into s */
   1057 {
   1058 	int i;
   1059 
   1060 	lgf++;
   1061 	copyf++;
   1062 	skip();
   1063 	for (i = 0; i < n-1; i++)
   1064 		if ((s[i] = cbits(getch())) == '\n' || s[i] == RIGHT)
   1065 			break;
   1066 	s[i] = 0;
   1067 	copyf--;
   1068 	lgf--;
   1069 }
   1070 
   1071 void casesy(void)	/* call system */
   1072 {
   1073 	char sybuf[NTM];
   1074 
   1075 	getline(sybuf, NTM);
   1076 	system(sybuf);
   1077 }
   1078 
   1079 
   1080 void getpn(char *a)
   1081 {
   1082 	int n, neg;
   1083 
   1084 	if (*a == 0)
   1085 		return;
   1086 	neg = 0;
   1087 	for ( ; *a; a++)
   1088 		switch (*a) {
   1089 		case '+':
   1090 		case ',':
   1091 			continue;
   1092 		case '-':
   1093 			neg = 1;
   1094 			continue;
   1095 		default:
   1096 			n = 0;
   1097 			if (isdigit((uchar)*a)) {
   1098 				do
   1099 					n = 10 * n + *a++ - '0';
   1100 				while (isdigit((uchar)*a));
   1101 				a--;
   1102 			} else
   1103 				n = 9999;
   1104 			*pnp++ = neg ? -n : n;
   1105 			neg = 0;
   1106 			if (pnp >= &pnlist[NPN-2]) {
   1107 				ERROR "too many page numbers" WARN;
   1108 				done3(-3);
   1109 			}
   1110 		}
   1111 	if (neg)
   1112 		*pnp++ = -9999;
   1113 	*pnp = -INT_MAX;
   1114 	print = 0;
   1115 	pnp = pnlist;
   1116 	if (*pnp != -INT_MAX)
   1117 		chkpn();
   1118 }
   1119 
   1120 
   1121 void setrpt(void)
   1122 {
   1123 	Tchar i, j;
   1124 
   1125 	copyf++;
   1126 	raw++;
   1127 	i = getch0();
   1128 	copyf--;
   1129 	raw--;
   1130 	if ((long) i < 0 || cbits(j = getch0()) == RPT)
   1131 		return;
   1132 	while (i > 0 && pbp < &pbbuf[NC-3]) {
   1133 		i--;
   1134 		*pbp++ = j;
   1135 	}
   1136 }