9base

revived minimalist port of Plan 9 userland to Unix
git clone git://git.suckless.org/9base
Log | Files | Refs | README | LICENSE

n1.c (20331B)


      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 ' ':	/* unpaddable space */
    516 		i = UNPAD;
    517 		goto gx;
    518 	case '\'':	/* \(aa */
    519 		i = ACUTE;
    520 		goto gx;
    521 	case '`':	/* \(ga */
    522 		i = GRAVE;
    523 		goto gx;
    524 	case '_':	/* \(ul */
    525 		i = UNDERLINE;
    526 		goto gx;
    527 	case '-':	/* current font minus */
    528 		i = MINUS;
    529 		goto gx;
    530 	case '&':	/* filler */
    531 		i = FILLER;
    532 		goto gx;
    533 	case 'c':	/* to be continued */
    534 		i = CONT;
    535 		goto gx;
    536 	case '!':	/* transparent indicator */
    537 		i = XPAR;
    538 		goto gx;
    539 	case 't':	/* tab */
    540 		i = '\t';
    541 		return(i);
    542 	case 'a':	/* leader (SOH) */
    543 /* old:		*pbp++ = LEADER; goto g0; */
    544 		i = LEADER;
    545 		return i;
    546 	case '%':	/* ohc */
    547 		i = OHC;
    548 		return(i);
    549 	case 'g':	/* return format of a number register */
    550 		setaf();	/* should this really be in copy mode??? */
    551 		goto g0;
    552 	case '.':	/* . */
    553 		i = '.';
    554 gx:
    555 		setsfbits(i, sfbits(j));
    556 		return(i);
    557 	}
    558 	if (copyf) {
    559 		*pbp++ = j;
    560 		return(eschar);
    561 	}
    562 	switch (k) {
    563 
    564 	case 'f':	/* font indicator */
    565 		setfont(0);
    566 		goto g0;
    567 	case 's':	/* size indicator */
    568 		setps();
    569 		goto g0;
    570 	case 'v':	/* vert mot */
    571 		numerr.type = numerr.escarg = 0; numerr.esc = k;
    572 		if (i = vmot()) {
    573 			return(i);
    574 		}
    575 		goto g0;
    576 	case 'h': 	/* horiz mot */
    577 		numerr.type = numerr.escarg = 0; numerr.esc = k;
    578 		if (i = hmot())
    579 			return(i);
    580 		goto g0;
    581 	case '|':	/* narrow space */
    582 		if (NROFF)
    583 			goto g0;
    584 		return(makem((int)(EM)/6));
    585 	case '^':	/* half narrow space */
    586 		if (NROFF)
    587 			goto g0;
    588 		return(makem((int)(EM)/12));
    589 	case 'w':	/* width function */
    590 		setwd();
    591 		goto g0;
    592 	case 'p':	/* spread */
    593 		spread++;
    594 		goto g0;
    595 	case 'N':	/* absolute character number */
    596 		numerr.type = numerr.escarg = 0; numerr.esc = k;
    597 		if ((i = setabs()) == 0)
    598 			goto g0;
    599 		return i;
    600 	case 'H':	/* character height */
    601 		numerr.type = numerr.escarg = 0; numerr.esc = k;
    602 		return(setht());
    603 	case 'S':	/* slant */
    604 		numerr.type = numerr.escarg = 0; numerr.esc = k;
    605 		return(setslant());
    606 	case 'z':	/* zero with char */
    607 		return(setz());
    608 	case 'l':	/* hor line */
    609 		numerr.type = numerr.escarg = 0; numerr.esc = k;
    610 		setline();
    611 		goto g0;
    612 	case 'L':	/* vert line */
    613 		numerr.type = numerr.escarg = 0; numerr.esc = k;
    614 		setvline();
    615 		goto g0;
    616 	case 'D':	/* drawing function */
    617 		numerr.type = numerr.escarg = 0; numerr.esc = k;
    618 		setdraw();
    619 		goto g0;
    620 	case 'X':	/* \X'...' for copy through */
    621 		setxon();
    622 		goto g0;
    623 	case 'b':	/* bracket */
    624 		setbra();
    625 		goto g0;
    626 	case 'o':	/* overstrike */
    627 		setov();
    628 		goto g0;
    629 	case 'k':	/* mark hor place */
    630 		if ((k = findr(getsn())) != -1) {
    631 			numtabp[k].val = numtabp[HP].val;
    632 		}
    633 		goto g0;
    634 	case '0':	/* number space */
    635 		return(makem(width('0' | chbits)));
    636 	case 'x':	/* extra line space */
    637 		numerr.type = numerr.escarg = 0; numerr.esc = k;
    638 		if (i = xlss())
    639 			return(i);
    640 		goto g0;
    641 	case 'u':	/* half em up */
    642 	case 'r':	/* full em up */
    643 	case 'd':	/* half em down */
    644 		return(sethl(k));
    645 	default:
    646 		return(j);
    647 	}
    648 	/* NOTREACHED */
    649 }
    650 
    651 void setxon(void)	/* \X'...' for copy through */
    652 {
    653 	Tchar xbuf[NC];
    654 	Tchar *i;
    655 	Tchar c;
    656 	int delim, k;
    657 
    658 	if (ismot(c = getch()))
    659 		return;
    660 	delim = cbits(c);
    661 	i = xbuf;
    662 	*i++ = XON | chbits;
    663 	while ((k = cbits(c = getch())) != delim && k != '\n' && i < xbuf+NC-1) {
    664 		if (k == ' ')
    665 			setcbits(c, WORDSP);
    666 		*i++ = c | ZBIT;
    667 	}
    668 	*i++ = XOFF | chbits;
    669 	*i = 0;
    670 	pushback(xbuf);
    671 }
    672 
    673 
    674 char	ifilt[32] = { 0, 001, 002, 003, 0, 005, 006, 007, 010, 011, 012 };
    675 
    676 Tchar getch0(void)
    677 {
    678 	Tchar i;
    679 
    680 again:
    681 	if (pbp > lastpbp)
    682 		i = *--pbp;
    683 	else if (ip) {
    684 		/* i = rbf(); */
    685 		i = rbf0(ip);
    686 		if (i == 0)
    687 			i = rbf();
    688 		else {
    689 			++ip;
    690 			if (pastend(ip)) {
    691 				--ip;
    692 				rbf();
    693 			}
    694 		}
    695 	} else {
    696 		if (donef || ndone)
    697 			done(0);
    698 		if (nx || 1) {	/* BUG: was ibufp >= eibuf, so EOF test is wrong */
    699 			if (nfo < 0)
    700 				ERROR "in getch0, nfo = %d", nfo WARN;
    701 			if (nfo == 0) {
    702 g0:
    703 				if (nextfile()) {
    704 					if (ip)
    705 						goto again;
    706 				}
    707 			}
    708 			nx = 0;
    709 #ifdef UNICODE
    710 			if (MB_CUR_MAX > 1)
    711 				i = get1ch(ifile);
    712 			else
    713 #endif	/*UNICODE*/
    714 				i = getc(ifile);
    715 			if (i == EOF)
    716 				goto g0;
    717 			if (ip)
    718 				goto again;
    719 		}
    720 /*g2: */
    721 		if (i >= 040)			/* zapped: && i < 0177 */
    722 			goto g4;
    723 		i = ifilt[i];
    724 	}
    725 	if (cbits(i) == IMP && !raw)
    726 		goto again;
    727 	if (i == 0 && !init && !raw) {		/* zapped:  || i == 0177 */
    728 		goto again;
    729 	}
    730 g4:
    731 	if (ismot(i))
    732 		return i;
    733 	if (copyf == 0 && sfbits(i) == 0)
    734 		i |= chbits;
    735 	if (cbits(i) == eschar && !raw)
    736 		setcbits(i, ESC);
    737 	return(i);
    738 }
    739 
    740 
    741 #ifdef UNICODE
    742 Tchar get1ch(FILE *fp)	/* get one "character" from input, figure out what alphabet */
    743 {
    744 	wchar_t wc;
    745 	char buf[100], *p;
    746 	int i, n, c;
    747 
    748 	for (i = 0, p = buf; i < MB_CUR_MAX; i++) {
    749 		if ((c = getc(fp)) == EOF)
    750 			return c;
    751 		*p++ = c;
    752 		if ((n = mbtowc(&wc, buf, p-buf)) >= 0)
    753 			break;
    754 	}
    755 
    756 	if (n == 1)	/* real ascii, presumably */
    757 		return wc;
    758 	if (n == 0)
    759 		return p[-1];	/* illegal, but what else to do? */
    760 	if (c == EOF)
    761 		return EOF;
    762 	*p = 0;
    763 	return chadd(buf, MBchar, Install);	/* add name even if haven't seen it */
    764 }
    765 #endif	/*UNICODE*/
    766 
    767 void pushback(Tchar *b)
    768 {
    769 	Tchar *ob = b;
    770 
    771 	while (*b++)
    772 		;
    773 	b--;
    774 	while (b > ob && pbp < &pbbuf[NC-3])
    775 		*pbp++ = *--b;
    776 	if (pbp >= &pbbuf[NC-3]) {
    777 		ERROR "pushback overflow" WARN;
    778 		done(2);
    779 	}
    780 }
    781 
    782 void cpushback(char *b)
    783 {
    784 	char *ob = b;
    785 
    786 	while (*b++)
    787 		;
    788 	b--;
    789 	while (b > ob && pbp < &pbbuf[NC-3])
    790 		*pbp++ = *--b;
    791 	if (pbp >= &pbbuf[NC-3]) {
    792 		ERROR "cpushback overflow" WARN;
    793 		done(2);
    794 	}
    795 }
    796 
    797 int nextfile(void)
    798 {
    799 	char *p;
    800 
    801 n0:
    802 	if (ifile != stdin)
    803 		fclose(ifile);
    804 	if (ifi > 0 && !nx) {
    805 		if (popf())
    806 			goto n0; /* popf error */
    807 		return(1);	 /* popf ok */
    808 	}
    809 	if (nx || nmfi < mflg) {
    810 		p = mfiles[nmfi++];
    811 		if (*p != 0)
    812 			goto n1;
    813 	}
    814 	if (rargc-- <= 0) {
    815 		if ((nfo -= mflg) && !stdi) {
    816 			done(0);
    817 }
    818 		nfo++;
    819 		numtabp[CD].val = stdi = mflg = 0;
    820 		ifile = stdin;
    821 		strcpy(cfname[ifi], "stdin");
    822 		return(0);
    823 	}
    824 	p = (argp++)[0];
    825 	if (rargc >= 0)
    826 		cfname[ifi][0] = 0;
    827 n1:
    828 	numtabp[CD].val = 0;
    829 	if (p[0] == '-' && p[1] == 0) {
    830 		ifile = stdin;
    831 		strcpy(cfname[ifi], "stdin");
    832 	} else if ((ifile = fopen(unsharp(p), "r")) == NULL) {
    833 		ERROR "cannot open file %s", p WARN;
    834 		nfo -= mflg;
    835 		done(02);
    836 	} else
    837 		strcpy(cfname[ifi],p);
    838 	nfo++;
    839 	return(0);
    840 }
    841 
    842 int
    843 popf(void)
    844 {
    845 	--ifi;
    846 	if (ifi < 0) {
    847 		ERROR "popf went negative" WARN;
    848 		return 1;
    849 	}
    850 	numtabp[CD].val = cfline[ifi];	/* restore line counter */
    851 	ip = ipl[ifi];			/* input pointer */
    852 	ifile = ifl[ifi];		/* input FILE * */
    853 	return(0);
    854 }
    855 
    856 
    857 void flushi(void)
    858 {
    859 	if (nflush)
    860 		return;
    861 	ch = 0;
    862 	copyf++;
    863 	while (!nlflg) {
    864 		if (donef && frame == stk)
    865 			break;
    866 		getch();
    867 	}
    868 	copyf--;
    869 }
    870 
    871 /*
    872  * return 16-bit, ascii/alphabetic character, ignore chars with more bits,
    873  * (internal names), spaces and special cookies (below 040).
    874  * Leave STX ETX ENQ ACK and BELL in to maintain compatibility with v7 troff.
    875  */
    876 int
    877 getach(void)
    878 {
    879 	Tchar i;
    880 	int j;
    881 
    882 	lgf++;
    883 	j = cbits(i = getch());
    884         if (ismot(i)
    885 	    || j > SHORTMASK
    886 	    || (j <= 040 && j != 002	/*STX*/
    887 			&& j != 003	/*ETX*/
    888 			&& j != 005	/*ENQ*/
    889 			&& j != 006	/*ACK*/
    890 			&& j != 007)) {	/*BELL*/
    891 		ch = i;
    892 		j = 0;
    893 	}
    894 	lgf--;
    895 	return j;
    896 }
    897 
    898 
    899 void casenx(void)
    900 {
    901 	lgf++;
    902 	skip();
    903 	getname();
    904 	nx++;
    905 	if (nmfi > 0)
    906 		nmfi--;
    907 	strcpy(mfiles[nmfi], nextf);
    908 	nextfile();
    909 	nlflg++;
    910 	ip = 0;
    911 	pendt = 0;
    912 	frame = stk;
    913 	nxf = frame + 1;
    914 }
    915 
    916 int
    917 getname(void)
    918 {
    919 	int j, k;
    920 
    921 	lgf++;
    922 	for (k = 0; k < NS - 1; k++) {
    923 		j = getach();
    924 		if (!j)
    925 			break;
    926 		nextf[k] = j;
    927 	}
    928 	nextf[k] = 0;
    929 	lgf--;
    930 	return(nextf[0]);
    931 }
    932 
    933 
    934 void caseso(void)
    935 {
    936 	FILE *fp = 0;
    937 
    938 	lgf++;
    939 	nextf[0] = 0;
    940 	if (skip() || !getname() || (fp = fopen(unsharp(nextf), "r")) == NULL || ifi >= NSO) {
    941 		ERROR "can't open file %s", nextf WARN;
    942 		done(02);
    943 	}
    944 	strcpy(cfname[ifi+1], nextf);
    945 	cfline[ifi] = numtabp[CD].val;		/*hold line counter*/
    946 	numtabp[CD].val = 0;
    947 	flushi();
    948 	ifl[ifi] = ifile;
    949 	ifile = fp;
    950 	ipl[ifi] = ip;
    951 	ip = 0;
    952 	nx++;
    953 	nflush++;
    954 	ifi++;
    955 }
    956 
    957 void caself(void)	/* set line number and file */
    958 {
    959 	int n;
    960 
    961 	if (skip())
    962 		return;
    963 	n = atoi0();
    964 	if (!nonumb)
    965 		cfline[ifi] = numtabp[CD].val = n - 1;
    966 	if (!skip())
    967 		if (getname()) {	/* eats '\n' ? */
    968 			strcpy(cfname[ifi], nextf);
    969 			if (!nonumb)
    970 				numtabp[CD].val--;
    971 		}
    972 }
    973 
    974 void cpout(FILE *fin, char *token)
    975 {
    976 	int n;
    977 	char buf[1024];
    978 
    979 	if (token) {	/* BUG: There should be no NULL bytes in input */
    980 		char *newl = buf;
    981 		while ((fgets(buf, sizeof buf, fin)) != NULL) {
    982 			if (newl) {
    983 				numtabp[CD].val++; /* line number */
    984 				if (strcmp(token, buf) == 0)
    985 					return;
    986 			}
    987 			newl = strchr(buf, '\n');
    988 			fputs(buf, ptid);
    989 		}
    990 	} else {
    991 		while ((n = fread(buf, sizeof *buf, sizeof buf, fin)) > 0)
    992 			fwrite(buf, n, 1, ptid);
    993 		fclose(fin);
    994 	}
    995 }
    996 
    997 void casecf(void)
    998 {	/* copy file without change */
    999 	FILE *fd;
   1000 	char *eof, *p;
   1001 	extern int hpos, esc, po;
   1002 
   1003 	/* this may not make much sense in nroff... */
   1004 
   1005 	lgf++;
   1006 	nextf[0] = 0;
   1007 	if (!skip() && getname()) {
   1008 		if (strncmp("<<", nextf, 2) != 0) {
   1009 			if ((fd = fopen(unsharp(nextf), "r")) == NULL) {
   1010 				ERROR "can't open file %s", nextf WARN;
   1011 				done(02);
   1012 			}
   1013 			eof = (char *) NULL;
   1014 		} else {	/* current file */
   1015 			if (pbp > lastpbp || ip) {
   1016 				ERROR "casecf: not reading from file" WARN;
   1017 				done(02);
   1018 			}
   1019 			eof = &nextf[2];
   1020 			if (!*eof)  {
   1021 				ERROR "casecf: missing end of input token" WARN;
   1022 				done(02);
   1023 			}
   1024 			p = eof;
   1025 			while(*++p)
   1026 				;
   1027 			*p++ = '\n';
   1028 			*p = 0;
   1029 			fd = ifile;
   1030 		}
   1031 	} else {
   1032 		ERROR "casecf: no argument" WARN;
   1033 		lgf--;
   1034 		return;
   1035 	}
   1036 	lgf--;
   1037 
   1038 	/* make it into a clean state, be sure that everything is out */
   1039 	tbreak();
   1040 	hpos = po;
   1041 	esc = 0;
   1042 	ptesc();	/* to left margin */
   1043 	esc = un;
   1044 	ptesc();
   1045 	ptlead();
   1046 	ptps();
   1047 	ptfont();
   1048 	flusho();
   1049 	cpout(fd, eof);
   1050 	ptps();
   1051 	ptfont();
   1052 }
   1053 
   1054 void getline(char *s, int n)	/* get rest of input line into s */
   1055 {
   1056 	int i;
   1057 
   1058 	lgf++;
   1059 	copyf++;
   1060 	skip();
   1061 	for (i = 0; i < n-1; i++)
   1062 		if ((s[i] = cbits(getch())) == '\n' || s[i] == RIGHT)
   1063 			break;
   1064 	s[i] = 0;
   1065 	copyf--;
   1066 	lgf--;
   1067 }
   1068 
   1069 void casesy(void)	/* call system */
   1070 {
   1071 	char sybuf[NTM];
   1072 
   1073 	getline(sybuf, NTM);
   1074 	system(sybuf);
   1075 }
   1076 
   1077 
   1078 void getpn(char *a)
   1079 {
   1080 	int n, neg;
   1081 
   1082 	if (*a == 0)
   1083 		return;
   1084 	neg = 0;
   1085 	for ( ; *a; a++)
   1086 		switch (*a) {
   1087 		case '+':
   1088 		case ',':
   1089 			continue;
   1090 		case '-':
   1091 			neg = 1;
   1092 			continue;
   1093 		default:
   1094 			n = 0;
   1095 			if (isdigit((uchar)*a)) {
   1096 				do
   1097 					n = 10 * n + *a++ - '0';
   1098 				while (isdigit((uchar)*a));
   1099 				a--;
   1100 			} else
   1101 				n = 9999;
   1102 			*pnp++ = neg ? -n : n;
   1103 			neg = 0;
   1104 			if (pnp >= &pnlist[NPN-2]) {
   1105 				ERROR "too many page numbers" WARN;
   1106 				done3(-3);
   1107 			}
   1108 		}
   1109 	if (neg)
   1110 		*pnp++ = -9999;
   1111 	*pnp = -INT_MAX;
   1112 	print = 0;
   1113 	pnp = pnlist;
   1114 	if (*pnp != -INT_MAX)
   1115 		chkpn();
   1116 }
   1117 
   1118 
   1119 void setrpt(void)
   1120 {
   1121 	Tchar i, j;
   1122 
   1123 	copyf++;
   1124 	raw++;
   1125 	i = getch0();
   1126 	copyf--;
   1127 	raw--;
   1128 	if ((long) i < 0 || cbits(j = getch0()) == RPT)
   1129 		return;
   1130 	while (i > 0 && pbp < &pbbuf[NC-3]) {
   1131 		i--;
   1132 		*pbp++ = j;
   1133 	}
   1134 }