/* * n1.c * * consume options, initialization, main loop, * input routines, escape function calling */ #include "tdef.h" #include "fns.h" #include "ext.h" #include "dwbinit.h" #include #include char *Version = "March 11, 1994"; #ifndef DWBVERSION #define DWBVERSION "???" #endif char *DWBfontdir = FONTDIR; char *DWBntermdir = NTERMDIR; char *DWBalthyphens = ALTHYPHENS; char *DWBhomedir = ""; dwbinit dwbpaths[] = { &DWBfontdir, NULL, 0, &DWBntermdir, NULL, 0, &DWBalthyphens, NULL, 0, &DWBhomedir, NULL, 0, NULL, nextf, NS, NULL, NULL, 0 }; int TROFF = 1; /* assume we started in troff... */ jmp_buf sjbuf; Offset ipl[NSO]; static FILE *ifile = stdin; static FILE *ifl[NSO]; /* open input file pointers */ char cfname[NSO+1][NS] = { "stdin" }; /* file name stack */ int cfline[NSO]; /* input line count stack */ char *progname; /* program name (troff or nroff) */ int trace = 0; /* tracing mode: default off */ int trace1 = 0; main(int argc, char *argv[]) { char *p; int j; Tchar i; char buf[100]; buf[0] = '\0'; /* make sure it's empty (silly 3b2) */ progname = argv[0]; if ((p = strrchr(progname, '/')) == NULL) p = progname; else p++; DWBinit(progname, dwbpaths); if (strcmp(p, "nroff") == 0) TROFF = 0; #ifdef UNICODE alphabet = 128; /* unicode for plan 9 */ #endif /*UNICODE*/ mnspace(); nnspace(); mrehash(); nrehash(); numtabp[NL].val = -1; while (--argc > 0 && (++argv)[0][0] == '-') switch (argv[0][1]) { case 'N': /* ought to be used first... */ TROFF = 0; break; case 'd': fprintf(stderr, "troff/nroff version %s\n", Version); break; case 'F': /* switch font tables from default */ if (argv[0][2] != '\0') { strcpy(termtab, &argv[0][2]); strcpy(fontdir, &argv[0][2]); } else { argv++; argc--; strcpy(termtab, argv[0]); strcpy(fontdir, argv[0]); } break; case 0: goto start; case 'i': stdi++; break; case 'n': npn = atoi(&argv[0][2]); break; case 'u': /* set emboldening amount */ bdtab[3] = atoi(&argv[0][2]); if (bdtab[3] < 0 || bdtab[3] > 50) bdtab[3] = 0; break; case 's': if (!(stop = atoi(&argv[0][2]))) stop++; break; case 'r': sprintf(buf + strlen(buf), ".nr %c %s\n", argv[0][2], &argv[0][3]); /* not yet cpushback(buf);*/ /* dotnr(&argv[0][2], &argv[0][3]); */ break; case 'm': if (mflg++ >= NMF) { ERROR "Too many macro packages: %s", argv[0] WARN; break; } strcpy(mfiles[nmfi], nextf); strcat(mfiles[nmfi++], &argv[0][2]); break; case 'o': getpn(&argv[0][2]); break; case 'T': strcpy(devname, &argv[0][2]); dotT++; break; case 'a': ascii = 1; break; case 'h': hflg++; break; case 'e': eqflg++; break; case 'q': quiet++; save_tty(); break; case 'V': fprintf(stdout, "%croff: DWB %s\n", TROFF ? 't' : 'n', DWBVERSION); exit(0); case 't': if (argv[0][2] != '\0') trace = trace1 = argv[0][2]; break; /* for the sake of compatibility */ default: ERROR "unknown option %s", argv[0] WARN; done(02); } start: /* * cpushback maintains a LIFO, so push pack the -r arguments * in reverse order to maintain a FIFO in case someone did -rC1 -rC3 */ if (buf[0]) { char *p = buf; while(*p++) ; while(p > buf) { while(strncmp(p, ".nr", 3) != 0) p--; cpushback(p); *p-- = '\0'; } } argp = argv; rargc = argc; nmfi = 0; init2(); setjmp(sjbuf); loop: copyf = lgf = nb = nflush = nlflg = 0; if (ip && rbf0(ip) == 0 && ejf && frame->pframe <= ejl && dip == d) { nflush++; trap = 0; eject((Stack *)0); goto loop; } i = getch(); if (pendt) goto Lt; if ((j = cbits(i)) == XPAR) { copyf++; tflg++; while (cbits(i) != '\n') pchar(i = getch()); tflg = 0; copyf--; /* pointless */ goto loop; } if (j == cc || j == c2) { if (j == c2) nb++; copyf++; while ((j = cbits(i = getch())) == ' ' || j == '\t') ; ch = i; copyf--; control(getrq(), 1); flushi(); goto loop; } Lt: ch = i; text(); if (nlflg) numtabp[HP].val = 0; goto loop; } void init2(void) { int i; char buf[100]; for (i = NTRTAB; --i; ) trtab[i] = i; trtab[UNPAD] = ' '; iflg = 0; obufp = obuf; if (TROFF) t_ptinit(); else n_ptinit(); mchbits(); cvtime(); numtabp[PID].val = getpid(); numtabp[HP].val = init = 0; numtabp[NL].val = -1; nfo = 0; copyf = raw = 0; sprintf(buf, ".ds .T %s\n", devname); cpushback(buf); sprintf(buf, ".ds .P %s\n", DWBhomedir); cpushback(buf); numtabp[CD].val = -1; /* compensation */ nx = mflg; frame = stk = (Stack *)setbrk(STACKSIZE); dip = &d[0]; nxf = frame + 1; for (i = 1; i < NEV; i++) /* propagate the environment */ envcopy(&env[i], &env[0]); for (i = 0; i < NEV; i++) { if ((env[i]._word._bufp = (Tchar *)calloc(WDSIZE, sizeof(Tchar))) == NULL) { ERROR "not enough room for word buffers" WARN; done2(1); } env[i]._word._size = WDSIZE; if ((env[i]._line._bufp = (Tchar *)calloc(LNSIZE, sizeof(Tchar))) == NULL) { ERROR "not enough room for line buffers" WARN; done2(1); } env[i]._line._size = LNSIZE; } if ((oline = (Tchar *)calloc(OLNSIZE, sizeof(Tchar))) == NULL) { ERROR "not enough room for line buffers" WARN; done2(1); } olinep = oline; olnsize = OLNSIZE; blockinit(); } void cvtime(void) { long tt; struct tm *ltime; time(&tt); ltime = localtime(&tt); numtabp[YR].val = ltime->tm_year % 100; numtabp[YR].fmt = 2; numtabp[MO].val = ltime->tm_mon + 1; /* troff uses 1..12 */ numtabp[DY].val = ltime->tm_mday; numtabp[DW].val = ltime->tm_wday + 1; /* troff uses 1..7 */ } char errbuf[200]; void errprint(void) /* error message printer */ { int savecd = numtabp[CD].val; if (!nlflg) numtabp[CD].val++; fprintf(stderr, "%s: ", progname); fputs(errbuf, stderr); if (cfname[ifi][0]) fprintf(stderr, "; %s:%d", cfname[ifi], numtabp[CD].val); fputs("\n", stderr); if (cfname[ifi][0]) stackdump(); numtabp[CD].val = savecd; } int control(int a, int b) { int j, k; extern Contab *contabp; numerr.type = RQERR; numerr.req = a; if (a == 0 || (j = findmn(a)) == -1) return(0); if (contabp[j].f == 0) { if (trace & TRMAC) fprintf(stderr, "invoke macro %s\n", unpair(a)); if (dip != d) for (k = dilev; k; k--) if (d[k].curd == a) { ERROR "diversion %s invokes itself during diversion", unpair(a) WARN; edone(0100); } nxf->nargs = 0; if (b) collect(); flushi(); return pushi(contabp[j].mx, a); /* BUG??? all that matters is 0/!0 */ } if (b) { if (trace & TRREQ) fprintf(stderr, "invoke request %s\n", unpair(a)); (*contabp[j].f)(); } return(0); } void casept(void) { int i; noscale++; if (skip()) i = trace1; else { i = max(inumb(&trace), 0); if (nonumb) i = trace1; } trace1 = trace; trace = i; noscale = 0; } int getrq(void) { int i, j; if ((i = getach()) == 0 || (j = getach()) == 0) goto rtn; i = PAIR(i, j); rtn: return(i); } /* * table encodes some special characters, to speed up tests * in getch, viz FLSS, RPT, f, \b, \n, fc, tabch, ldrch */ char gchtab[NCHARS] = { 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,001,000,000,000,000, /* FLSS, ESC */ 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, }; int realcbits(Tchar c) /* return character bits, or MOTCH if motion */ { if (ismot(c)) return MOTCH; else return c & 0xFFFF; } Tchar getch(void) { int k; Tchar i, j; g0: if (ch) { i = ch; if (cbits(i) == '\n') nlflg++; ch = 0; return(i); } if (nlflg) return('\n'); i = getch0(); if (ismot(i)) return(i); k = cbits(i); if (k >= sizeof(gchtab)/sizeof(gchtab[0]) || gchtab[k] == 0) /* nothing special */ return(i); if (k != ESC) { if (k == '\n') { nlflg++; if (ip == 0) numtabp[CD].val++; /* line number */ 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 (k == 'f' && lg && !lgf) { i = getlg(i); return(i); } 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); } } return(i); } k = cbits(j = getch0()); if (ismot(j)) return(j); switch (k) { case 'n': /* number register */ setn(); goto g0; case '$': /* argument indicator */ seta(); goto g0; case '*': /* string indicator */ setstr(); goto g0; case '{': /* LEFT */ i = LEFT; goto gx; case '}': /* RIGHT */ i = RIGHT; goto gx; case '"': /* comment */ while (cbits(i = getch0()) != '\n') ; if (ip == 0) numtabp[CD].val++; /* line number */ nlflg++; return(i); /* experiment: put it here instead of copy mode */ case '(': /* special char name \(xx */ case 'C': /* \C'...' */ if ((i = setch(k)) == 0) goto g0; goto gx; case ESC: /* double backslash */ i = eschar; goto gx; case 'e': /* printable version of current eschar */ i = PRESC; goto gx; case '\n': /* concealed newline */ numtabp[CD].val++; goto g0; case ' ': /* unpaddable space */ i = UNPAD; 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 '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) */ /* old: *pbp++ = LEADER; goto g0; */ i = LEADER; return i; case '%': /* ohc */ i = OHC; return(i); case 'g': /* return format of a number register */ setaf(); /* should this really be in copy mode??? */ goto g0; case '.': /* . */ i = '.'; gx: setsfbits(i, sfbits(j)); return(i); } if (copyf) { *pbp++ = j; return(eschar); } switch (k) { case 'f': /* font indicator */ setfont(0); goto g0; case 's': /* size indicator */ setps(); goto g0; case 'v': /* vert mot */ numerr.type = numerr.escarg = 0; numerr.esc = k; if (i = vmot()) { return(i); } goto g0; case 'h': /* horiz mot */ numerr.type = numerr.escarg = 0; numerr.esc = k; if (i = hmot()) return(i); goto g0; case '|': /* narrow space */ if (NROFF) goto g0; return(makem((int)(EM)/6)); case '^': /* half narrow space */ if (NROFF) goto g0; return(makem((int)(EM)/12)); case 'w': /* width function */ setwd(); goto g0; case 'p': /* spread */ spread++; goto g0; case 'N': /* absolute character number */ numerr.type = numerr.escarg = 0; numerr.esc = k; if ((i = setabs()) == 0) goto g0; return i; case 'H': /* character height */ numerr.type = numerr.escarg = 0; numerr.esc = k; return(setht()); case 'S': /* slant */ numerr.type = numerr.escarg = 0; numerr.esc = k; return(setslant()); case 'z': /* zero with char */ return(setz()); case 'l': /* hor line */ numerr.type = numerr.escarg = 0; numerr.esc = k; setline(); goto g0; case 'L': /* vert line */ numerr.type = numerr.escarg = 0; numerr.esc = k; setvline(); goto g0; case 'D': /* drawing function */ numerr.type = numerr.escarg = 0; numerr.esc = k; setdraw(); goto g0; case 'X': /* \X'...' for copy through */ setxon(); goto g0; case 'b': /* bracket */ setbra(); goto g0; case 'o': /* overstrike */ setov(); goto g0; case 'k': /* mark hor place */ if ((k = findr(getsn())) != -1) { numtabp[k].val = numtabp[HP].val; } goto g0; case '0': /* number space */ return(makem(width('0' | chbits))); case 'x': /* extra line space */ numerr.type = numerr.escarg = 0; numerr.esc = k; 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)); default: return(j); } /* NOTREACHED */ } void setxon(void) /* \X'...' for copy through */ { Tchar xbuf[NC]; Tchar *i; Tchar c; int delim, k; if (ismot(c = getch())) return; delim = cbits(c); i = xbuf; *i++ = XON | chbits; while ((k = cbits(c = getch())) != delim && k != '\n' && i < xbuf+NC-1) { if (k == ' ') setcbits(c, WORDSP); *i++ = c | ZBIT; } *i++ = XOFF | chbits; *i = 0; pushback(xbuf); } char ifilt[32] = { 0, 001, 002, 003, 0, 005, 006, 007, 010, 011, 012 }; Tchar getch0(void) { Tchar i; again: if (pbp > lastpbp) i = *--pbp; else if (ip) { /* i = rbf(); */ i = rbf0(ip); if (i == 0) i = rbf(); else { ++ip; if (pastend(ip)) { --ip; rbf(); } } } else { if (donef || ndone) done(0); if (nx || 1) { /* BUG: was ibufp >= eibuf, so EOF test is wrong */ if (nfo < 0) ERROR "in getch0, nfo = %d", nfo WARN; if (nfo == 0) { g0: if (nextfile()) { if (ip) goto again; } } nx = 0; #ifdef UNICODE if (MB_CUR_MAX > 1) i = get1ch(ifile); else #endif /*UNICODE*/ i = getc(ifile); if (i == EOF) goto g0; if (ip) goto again; } if (i >= 040) /* zapped: && i < 0177 */ goto g4; i = ifilt[i]; } if (cbits(i) == IMP && !raw) goto again; if (i == 0 && !init && !raw) { /* zapped: || i == 0177 */ goto again; } g4: if (ismot(i)) return i; if (copyf == 0 && sfbits(i) == 0) i |= chbits; if (cbits(i) == eschar && !raw) setcbits(i, ESC); return(i); } #ifdef UNICODE Tchar get1ch(FILE *fp) /* get one "character" from input, figure out what alphabet */ { wchar_t wc; char buf[100], *p; int i, n, c; n = c = 0; for (i = 0, p = buf; i < MB_CUR_MAX; i++) { if ((c = getc(fp)) == EOF) return c; *p++ = c; if ((n = mbtowc(&wc, buf, p-buf)) >= 0) break; } if (n == 1) /* real ascii, presumably */ return wc; if (n == 0) return p[-1]; /* illegal, but what else to do? */ if (c == EOF) return EOF; *p = 0; return chadd(buf, MBchar, Install); /* add name even if haven't seen it */ } #endif /*UNICODE*/ void pushback(Tchar *b) { Tchar *ob = b; while (*b++) ; b--; while (b > ob && pbp < &pbbuf[NC-3]) *pbp++ = *--b; if (pbp >= &pbbuf[NC-3]) { ERROR "pushback overflow" WARN; done(2); } } void cpushback(char *b) { char *ob = b; while (*b++) ; b--; while (b > ob && pbp < &pbbuf[NC-3]) *pbp++ = *--b; if (pbp >= &pbbuf[NC-3]) { ERROR "cpushback overflow" WARN; done(2); } } int nextfile(void) { char *p; n0: if (ifile != stdin) fclose(ifile); if (ifi > 0 && !nx) { if (popf()) goto n0; /* popf error */ return(1); /* popf ok */ } if (nx || nmfi < mflg) { p = mfiles[nmfi++]; if (*p != 0) goto n1; } if (rargc-- <= 0) { if ((nfo -= mflg) && !stdi) { done(0); } nfo++; numtabp[CD].val = stdi = mflg = 0; ifile = stdin; strcpy(cfname[ifi], "stdin"); return(0); } p = (argp++)[0]; if (rargc >= 0) cfname[ifi][0] = 0; n1: numtabp[CD].val = 0; if (p[0] == '-' && p[1] == 0) { ifile = stdin; strcpy(cfname[ifi], "stdin"); } else if ((ifile = fopen(p, "r")) == NULL) { ERROR "cannot open file %s", p WARN; nfo -= mflg; done(02); } else strcpy(cfname[ifi],p); nfo++; return(0); } popf(void) { --ifi; if (ifi < 0) { ERROR "popf went negative" WARN; return 1; } numtabp[CD].val = cfline[ifi]; /* restore line counter */ ip = ipl[ifi]; /* input pointer */ ifile = ifl[ifi]; /* input FILE * */ return(0); } void flushi(void) { if (nflush) return; ch = 0; copyf++; while (!nlflg) { if (donef && frame == stk) break; getch(); } copyf--; } /* * return 16-bit, ascii/alphabetic character, ignore chars with more bits, * (internal names), spaces and special cookies (below 040). * Leave STX ETX ENQ ACK and BELL in to maintain compatibility with v7 troff. */ getach(void) { Tchar i; int j; lgf++; j = cbits(i = getch()); if (ismot(i) || j > SHORTMASK || (j <= 040 && j != 002 /*STX*/ && j != 003 /*ETX*/ && j != 005 /*ENQ*/ && j != 006 /*ACK*/ && j != 007)) { /*BELL*/ ch = i; j = 0; } lgf--; return j; } void casenx(void) { lgf++; skip(); getname(); nx++; if (nmfi > 0) nmfi--; strcpy(mfiles[nmfi], nextf); nextfile(); nlflg++; ip = 0; pendt = 0; frame = stk; nxf = frame + 1; } getname(void) { int j, k; lgf++; for (k = 0; k < NS - 1; k++) { j = getach(); if (!j) break; nextf[k] = j; } nextf[k] = 0; lgf--; return(nextf[0]); } void caseso(void) { FILE *fp; lgf++; nextf[0] = 0; fp = NULL; if (skip() || !getname() || (fp = fopen(nextf, "r")) == NULL || ifi >= NSO) { ERROR "can't open file %s", nextf WARN; done(02); } strcpy(cfname[ifi+1], nextf); cfline[ifi] = numtabp[CD].val; /*hold line counter*/ numtabp[CD].val = 0; flushi(); ifl[ifi] = ifile; ifile = fp; ipl[ifi] = ip; ip = 0; nx++; nflush++; ifi++; } void caself(void) /* set line number and file */ { int n; if (skip()) return; n = atoi0(); if (!nonumb) cfline[ifi] = numtabp[CD].val = n - 1; if (!skip()) if (getname()) { /* eats '\n' ? */ strcpy(cfname[ifi], nextf); if (!nonumb) numtabp[CD].val--; } } void cpout(FILE *fin, char *token) { int n; char buf[1024]; if (token) { /* BUG: There should be no NULL bytes in input */ char *newl = buf; while ((fgets(buf, sizeof buf, fin)) != NULL) { if (newl) { numtabp[CD].val++; /* line number */ if (strcmp(token, buf) == 0) return; } newl = strchr(buf, '\n'); fputs(buf, ptid); } } else { while ((n = fread(buf, sizeof *buf, sizeof buf, fin)) > 0) fwrite(buf, n, 1, ptid); fclose(fin); } } void casecf(void) { /* copy file without change */ FILE *fd; char *eof, *p; extern int hpos, esc, po; /* this may not make much sense in nroff... */ lgf++; nextf[0] = 0; if (!skip() && getname()) { if (strncmp("<<", nextf, 2) != 0) { if ((fd = fopen(nextf, "r")) == NULL) { ERROR "can't open file %s", nextf WARN; done(02); } eof = (char *) NULL; } else { /* current file */ if (pbp > lastpbp || ip) { ERROR "casecf: not reading from file" WARN; done(02); } eof = &nextf[2]; if (!*eof) { ERROR "casecf: missing end of input token" WARN; done(02); } p = eof; while(*++p) ; *p++ = '\n'; *p = 0; fd = ifile; } } else { ERROR "casecf: no argument" WARN; lgf--; return; } lgf--; /* make it into a clean state, be sure that everything is out */ tbreak(); hpos = po; esc = 0; ptesc(); /* to left margin */ esc = un; ptesc(); ptlead(); ptps(); ptfont(); flusho(); cpout(fd, eof); ptps(); ptfont(); } void getline(char *s, int n) /* get rest of input line into s */ { int i; lgf++; copyf++; skip(); for (i = 0; i < n-1; i++) if ((s[i] = cbits(getch())) == '\n' || s[i] == RIGHT) break; s[i] = 0; copyf--; lgf--; } void casesy(void) /* call system */ { char sybuf[NTM]; getline(sybuf, NTM); system(sybuf); } void getpn(char *a) { 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(*a)) { do n = 10 * n + *a++ - '0'; while (isdigit(*a)); a--; } else n = 9999; *pnp++ = neg ? -n : n; neg = 0; if (pnp >= &pnlist[NPN-2]) { ERROR "too many page numbers" WARN; done3(-3); } } if (neg) *pnp++ = -9999; *pnp = -INT_MAX; print = 0; pnp = pnlist; if (*pnp != -INT_MAX) chkpn(); } void setrpt(void) { Tchar i, j; copyf++; raw++; i = getch0(); copyf--; raw--; if ((long) i < 0 || cbits(j = getch0()) == RPT) return; while (i > 0 && pbp < &pbbuf[NC-3]) { i--; *pbp++ = j; } }