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