aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/troff/n4.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/troff/n4.c')
-rw-r--r--src/cmd/troff/n4.c828
1 files changed, 828 insertions, 0 deletions
diff --git a/src/cmd/troff/n4.c b/src/cmd/troff/n4.c
new file mode 100644
index 00000000..3b3698e4
--- /dev/null
+++ b/src/cmd/troff/n4.c
@@ -0,0 +1,828 @@
+/*
+ * troff4.c
+ *
+ * number registers, conversion, arithmetic
+ */
+
+#include "tdef.h"
+#include "fns.h"
+#include "ext.h"
+
+
+int regcnt = NNAMES;
+int falsef = 0; /* on if inside false branch of if */
+
+#define NHASHSIZE 128 /* must be 2**n */
+#define NHASH(i) ((i>>6)^i) & (NHASHSIZE-1)
+Numtab *nhash[NHASHSIZE];
+
+Numtab *numtabp = NULL;
+#define NDELTA 400
+int ncnt = 0;
+
+void setn(void)
+{
+ int i, j, f;
+ Tchar ii;
+ Uchar *p;
+ char buf[NTM]; /* for \n(.S */
+
+ f = nform = 0;
+ if ((i = cbits(ii = getach())) == '+')
+ f = 1;
+ else if (i == '-')
+ f = -1;
+ else if (ii) /* don't put it back if it's already back (thanks to jaap) */
+ ch = ii;
+ if (falsef)
+ f = 0;
+ if ((i = getsn()) == 0)
+ return;
+ p = unpair(i);
+ if (p[0] == '.')
+ switch (p[1]) {
+ case 's':
+ i = pts;
+ break;
+ case 'v':
+ i = lss;
+ break;
+ case 'f':
+ i = font;
+ break;
+ case 'p':
+ i = pl;
+ break;
+ case 't':
+ i = findt1();
+ break;
+ case 'o':
+ i = po;
+ break;
+ case 'l':
+ i = ll;
+ break;
+ case 'i':
+ i = in;
+ break;
+ case '$':
+ i = frame->nargs;
+ break;
+ case 'A':
+ i = ascii;
+ break;
+ case 'c':
+ i = numtabp[CD].val;
+ break;
+ case 'n':
+ i = lastl;
+ break;
+ case 'a':
+ i = ralss;
+ break;
+ case 'h':
+ i = dip->hnl;
+ break;
+ case 'd':
+ if (dip != d)
+ i = dip->dnl;
+ else
+ i = numtabp[NL].val;
+ break;
+ case 'u':
+ i = fi;
+ break;
+ case 'j':
+ i = ad + 2 * admod;
+ break;
+ case 'w':
+ i = widthp;
+ break;
+ case 'x':
+ i = nel;
+ break;
+ case 'y':
+ i = un;
+ break;
+ case 'T':
+ i = dotT;
+ break; /* -Tterm used in nroff */
+ case 'V':
+ i = VERT;
+ break;
+ case 'H':
+ i = HOR;
+ break;
+ case 'k':
+ i = ne;
+ break;
+ case 'P':
+ i = print;
+ break;
+ case 'L':
+ i = ls;
+ break;
+ case 'R': /* maximal # of regs that can be addressed */
+ i = 255*256 - regcnt;
+ break;
+ case 'z':
+ p = unpair(dip->curd);
+ *pbp++ = p[1]; /* watch order */
+ *pbp++ = p[0];
+ return;
+ case 'b':
+ i = bdtab[font];
+ break;
+ case 'F':
+ cpushback(cfname[ifi]);
+ return;
+ case 'S':
+ buf[0] = j = 0;
+ for( i = 0; tabtab[i] != 0 && i < NTAB; i++) {
+ if (i > 0)
+ buf[j++] = ' ';
+ sprintf(&buf[j], "%d", tabtab[i] & TABMASK);
+ j = strlen(buf);
+ if ( tabtab[i] & RTAB)
+ sprintf(&buf[j], "uR");
+ else if (tabtab[i] & CTAB)
+ sprintf(&buf[j], "uC");
+ else
+ sprintf(&buf[j], "uL");
+ j += 2;
+ }
+ cpushback(buf);
+ return;
+ default:
+ goto s0;
+ }
+ else {
+s0:
+ if ((j = findr(i)) == -1)
+ i = 0;
+ else {
+ i = numtabp[j].val = numtabp[j].val + numtabp[j].inc * f;
+ nform = numtabp[j].fmt;
+ }
+ }
+ setn1(i, nform, (Tchar) 0);
+}
+
+Tchar numbuf[25];
+Tchar *numbufp;
+
+int wrc(Tchar i)
+{
+ if (numbufp >= &numbuf[24])
+ return(0);
+ *numbufp++ = i;
+ return(1);
+}
+
+
+
+/* insert into input number i, in format form, with size-font bits bits */
+void setn1(int i, int form, Tchar bits)
+{
+ numbufp = numbuf;
+ nrbits = bits;
+ nform = form;
+ fnumb(i, wrc);
+ *numbufp = 0;
+ pushback(numbuf);
+}
+
+void prnumtab(Numtab *p)
+{
+ int i;
+ for (i = 0; i < ncnt; i++)
+ if (p)
+ if (p[i].r != 0)
+ fprintf(stderr, "slot %d, %s, val %d\n", i, unpair(p[i].r), p[i].val);
+ else
+ fprintf(stderr, "slot %d empty\n", i);
+ else
+ fprintf(stderr, "slot %d empty\n", i);
+}
+
+void nnspace(void)
+{
+ ncnt = sizeof(numtab)/sizeof(Numtab) + NDELTA;
+ numtabp = (Numtab *) grow((char *)numtabp, ncnt, sizeof(Numtab));
+ if (numtabp == NULL) {
+ ERROR "not enough memory for registers (%d)", ncnt WARN;
+ exit(1);
+ }
+ numtabp = (Numtab *) memcpy((char *)numtabp, (char *)numtab,
+ sizeof(numtab));
+ if (numtabp == NULL) {
+ ERROR "Cannot initialize registers" WARN;
+ exit(1);
+ }
+}
+
+void grownumtab(void)
+{
+ ncnt += NDELTA;
+ numtabp = (Numtab *) grow((char *) numtabp, ncnt, sizeof(Numtab));
+ if (numtabp == NULL) {
+ ERROR "Too many number registers (%d)", ncnt WARN;
+ done2(04);
+ } else {
+ memset((char *)(numtabp) + (ncnt - NDELTA) * sizeof(Numtab),
+ 0, NDELTA * sizeof(Numtab));
+ nrehash();
+ }
+}
+
+void nrehash(void)
+{
+ Numtab *p;
+ int i;
+
+ for (i=0; i<NHASHSIZE; i++)
+ nhash[i] = 0;
+ for (p=numtabp; p < &numtabp[ncnt]; p++)
+ p->link = 0;
+ for (p=numtabp; p < &numtabp[ncnt]; p++) {
+ if (p->r == 0)
+ continue;
+ i = NHASH(p->r);
+ p->link = nhash[i];
+ nhash[i] = p;
+ }
+}
+
+void nunhash(Numtab *rp)
+{
+ Numtab *p;
+ Numtab **lp;
+
+ if (rp->r == 0)
+ return;
+ lp = &nhash[NHASH(rp->r)];
+ p = *lp;
+ while (p) {
+ if (p == rp) {
+ *lp = p->link;
+ p->link = 0;
+ return;
+ }
+ lp = &p->link;
+ p = p->link;
+ }
+}
+
+int findr(int i)
+{
+ Numtab *p;
+ int h = NHASH(i);
+
+ if (i == 0)
+ return(-1);
+a0:
+ for (p = nhash[h]; p; p = p->link)
+ if (i == p->r)
+ return(p - numtabp);
+ for (p = numtabp; p < &numtabp[ncnt]; p++) {
+ if (p->r == 0) {
+ p->r = i;
+ p->link = nhash[h];
+ nhash[h] = p;
+ regcnt++;
+ return(p - numtabp);
+ }
+ }
+ grownumtab();
+ goto a0;
+}
+
+int usedr(int i) /* returns -1 if nr i has never been used */
+{
+ Numtab *p;
+
+ if (i == 0)
+ return(-1);
+ for (p = nhash[NHASH(i)]; p; p = p->link)
+ if (i == p->r)
+ return(p - numtabp);
+ return -1;
+}
+
+
+int fnumb(int i, int (*f)(Tchar))
+{
+ int j;
+
+ j = 0;
+ if (i < 0) {
+ j = (*f)('-' | nrbits);
+ i = -i;
+ }
+ switch (nform) {
+ default:
+ case '1':
+ case 0:
+ return decml(i, f) + j;
+ case 'i':
+ case 'I':
+ return roman(i, f) + j;
+ case 'a':
+ case 'A':
+ return abc(i, f) + j;
+ }
+}
+
+
+int decml(int i, int (*f)(Tchar))
+{
+ int j, k;
+
+ k = 0;
+ nform--;
+ if ((j = i / 10) || (nform > 0))
+ k = decml(j, f);
+ return(k + (*f)((i % 10 + '0') | nrbits));
+}
+
+
+int roman(int i, int (*f)(Tchar))
+{
+
+ if (!i)
+ return((*f)('0' | nrbits));
+ if (nform == 'i')
+ return(roman0(i, f, "ixcmz", "vldw"));
+ else
+ return(roman0(i, f, "IXCMZ", "VLDW"));
+}
+
+
+int roman0(int i, int (*f)(Tchar), char *onesp, char *fivesp)
+{
+ int q, rem, k;
+
+ if (!i)
+ return(0);
+ k = roman0(i / 10, f, onesp + 1, fivesp + 1);
+ q = (i = i % 10) / 5;
+ rem = i % 5;
+ if (rem == 4) {
+ k += (*f)(*onesp | nrbits);
+ if (q)
+ i = *(onesp + 1);
+ else
+ i = *fivesp;
+ return(k += (*f)(i | nrbits));
+ }
+ if (q)
+ k += (*f)(*fivesp | nrbits);
+ while (--rem >= 0)
+ k += (*f)(*onesp | nrbits);
+ return(k);
+}
+
+
+int abc(int i, int (*f)(Tchar))
+{
+ if (!i)
+ return((*f)('0' | nrbits));
+ else
+ return(abc0(i - 1, f));
+}
+
+
+int abc0(int i, int (*f)(Tchar))
+{
+ int j, k;
+
+ k = 0;
+ if (j = i / 26)
+ k = abc0(j - 1, f);
+ return(k + (*f)((i % 26 + nform) | nrbits));
+}
+
+long atoi0(void)
+{
+ int c, k, cnt;
+ Tchar ii;
+ long i, acc;
+
+ acc = 0;
+ nonumb = 0;
+ cnt = -1;
+a0:
+ cnt++;
+ ii = getch();
+ c = cbits(ii);
+ switch (c) {
+ default:
+ ch = ii;
+ if (cnt)
+ break;
+ case '+':
+ i = ckph();
+ if (nonumb)
+ break;
+ acc += i;
+ goto a0;
+ case '-':
+ i = ckph();
+ if (nonumb)
+ break;
+ acc -= i;
+ goto a0;
+ case '*':
+ i = ckph();
+ if (nonumb)
+ break;
+ acc *= i;
+ goto a0;
+ case '/':
+ i = ckph();
+ if (nonumb)
+ break;
+ if (i == 0) {
+ flusho();
+ ERROR "divide by zero." WARN;
+ acc = 0;
+ } else
+ acc /= i;
+ goto a0;
+ case '%':
+ i = ckph();
+ if (nonumb)
+ break;
+ acc %= i;
+ goto a0;
+ case '&': /*and*/
+ i = ckph();
+ if (nonumb)
+ break;
+ if ((acc > 0) && (i > 0))
+ acc = 1;
+ else
+ acc = 0;
+ goto a0;
+ case ':': /*or*/
+ i = ckph();
+ if (nonumb)
+ break;
+ if ((acc > 0) || (i > 0))
+ acc = 1;
+ else
+ acc = 0;
+ goto a0;
+ case '=':
+ if (cbits(ii = getch()) != '=')
+ ch = ii;
+ i = ckph();
+ if (nonumb) {
+ acc = 0;
+ break;
+ }
+ if (i == acc)
+ acc = 1;
+ else
+ acc = 0;
+ goto a0;
+ case '>':
+ k = 0;
+ if (cbits(ii = getch()) == '=')
+ k++;
+ else
+ ch = ii;
+ i = ckph();
+ if (nonumb) {
+ acc = 0;
+ break;
+ }
+ if (acc > (i - k))
+ acc = 1;
+ else
+ acc = 0;
+ goto a0;
+ case '<':
+ k = 0;
+ if (cbits(ii = getch()) == '=')
+ k++;
+ else
+ ch = ii;
+ i = ckph();
+ if (nonumb) {
+ acc = 0;
+ break;
+ }
+ if (acc < (i + k))
+ acc = 1;
+ else
+ acc = 0;
+ goto a0;
+ case ')':
+ break;
+ case '(':
+ acc = atoi0();
+ goto a0;
+ }
+ return(acc);
+}
+
+
+long ckph(void)
+{
+ Tchar i;
+ long j;
+
+ if (cbits(i = getch()) == '(')
+ j = atoi0();
+ else {
+ j = atoi1(i);
+ }
+ return(j);
+}
+
+
+/*
+ * print error about illegal numeric argument;
+ */
+void prnumerr(void)
+{
+ char err_buf[40];
+ static char warn[] = "Numeric argument expected";
+ int savcd = numtabp[CD].val;
+
+ if (numerr.type == RQERR)
+ sprintf(err_buf, "%c%s: %s", nb ? cbits(c2) : cbits(cc),
+ unpair(numerr.req), warn);
+ else
+ sprintf(err_buf, "\\%c'%s': %s", numerr.esc, &numerr.escarg,
+ warn);
+ if (frame != stk) /* uncertainty correction */
+ numtabp[CD].val--;
+ ERROR err_buf WARN;
+ numtabp[CD].val = savcd;
+}
+
+
+long atoi1(Tchar ii)
+{
+ int i, j, digits;
+ double acc; /* this is the only double in troff! */
+ int neg, abs, field, decpnt;
+ extern int ifnum;
+
+
+ neg = abs = field = decpnt = digits = 0;
+ acc = 0;
+ for (;;) {
+ i = cbits(ii);
+ switch (i) {
+ default:
+ break;
+ case '+':
+ ii = getch();
+ continue;
+ case '-':
+ neg = 1;
+ ii = getch();
+ continue;
+ case '|':
+ abs = 1 + neg;
+ neg = 0;
+ ii = getch();
+ continue;
+ }
+ break;
+ }
+a1:
+ while (i >= '0' && i <= '9') {
+ field++;
+ digits++;
+ acc = 10 * acc + i - '0';
+ ii = getch();
+ i = cbits(ii);
+ }
+ if (i == '.' && !decpnt++) {
+ field++;
+ digits = 0;
+ ii = getch();
+ i = cbits(ii);
+ goto a1;
+ }
+ if (!field) {
+ ch = ii;
+ goto a2;
+ }
+ switch (i) {
+ case 'u':
+ i = j = 1; /* should this be related to HOR?? */
+ break;
+ case 'v': /*VSs - vert spacing*/
+ j = lss;
+ i = 1;
+ break;
+ case 'm': /*Ems*/
+ j = EM;
+ i = 1;
+ break;
+ case 'n': /*Ens*/
+ j = EM;
+ if (TROFF)
+ i = 2;
+ else
+ i = 1; /*Same as Ems in NROFF*/
+ break;
+ case 'p': /*Points*/
+ j = INCH;
+ i = 72;
+ break;
+ case 'i': /*Inches*/
+ j = INCH;
+ i = 1;
+ break;
+ case 'c': /*Centimeters*/
+ /* if INCH is too big, this will overflow */
+ j = INCH * 50;
+ i = 127;
+ break;
+ case 'P': /*Picas*/
+ j = INCH;
+ i = 6;
+ break;
+ default:
+ j = dfact;
+ ch = ii;
+ i = dfactd;
+ }
+ if (neg)
+ acc = -acc;
+ if (!noscale) {
+ acc = (acc * j) / i;
+ }
+ if (field != digits && digits > 0)
+ while (digits--)
+ acc /= 10;
+ if (abs) {
+ if (dip != d)
+ j = dip->dnl;
+ else
+ j = numtabp[NL].val;
+ if (!vflag) {
+ j = numtabp[HP].val;
+ }
+ if (abs == 2)
+ j = -j;
+ acc -= j;
+ }
+a2:
+ nonumb = (!field || field == decpnt);
+ if (nonumb && (trace & TRNARGS) && !ismot(ii) && !nlflg && !ifnum) {
+ if (cbits(ii) != RIGHT ) /* Too painful to do right */
+ prnumerr();
+ }
+ return(acc);
+}
+
+
+void caserr(void)
+{
+ int i, j;
+ Numtab *p;
+
+ lgf++;
+ while (!skip() && (i = getrq()) ) {
+ j = usedr(i);
+ if (j < 0)
+ continue;
+ p = &numtabp[j];
+ nunhash(p);
+ p->r = p->val = p->inc = p->fmt = 0;
+ regcnt--;
+ }
+}
+
+/*
+ * .nr request; if tracing, don't check optional
+ * 2nd argument because tbl generates .in 1.5n
+ */
+void casenr(void)
+{
+ int i, j;
+ int savtr = trace;
+
+ lgf++;
+ skip();
+ if ((i = findr(getrq())) == -1)
+ goto rtn;
+ skip();
+ j = inumb(&numtabp[i].val);
+ if (nonumb)
+ goto rtn;
+ numtabp[i].val = j;
+ skip();
+ trace = 0;
+ j = atoi0(); /* BUG??? */
+ trace = savtr;
+ if (nonumb)
+ goto rtn;
+ numtabp[i].inc = j;
+rtn:
+ return;
+}
+
+void caseaf(void)
+{
+ int i, k;
+ Tchar j;
+
+ lgf++;
+ if (skip() || !(i = getrq()) || skip())
+ return;
+ k = 0;
+ j = getch();
+ if (!isalpha(cbits(j))) {
+ ch = j;
+ while ((j = cbits(getch())) >= '0' && j <= '9')
+ k++;
+ }
+ if (!k)
+ k = j;
+ numtabp[findr(i)].fmt = k; /* was k & BYTEMASK */
+}
+
+void setaf(void) /* return format of number register */
+{
+ int i, j;
+
+ i = usedr(getsn());
+ if (i == -1)
+ return;
+ if (numtabp[i].fmt > 20) /* it was probably a, A, i or I */
+ *pbp++ = numtabp[i].fmt;
+ else
+ for (j = (numtabp[i].fmt ? numtabp[i].fmt : 1); j; j--)
+ *pbp++ = '0';
+}
+
+
+int vnumb(int *i)
+{
+ vflag++;
+ dfact = lss;
+ res = VERT;
+ return(inumb(i));
+}
+
+
+int hnumb(int *i)
+{
+ dfact = EM;
+ res = HOR;
+ return(inumb(i));
+}
+
+
+int inumb(int *n)
+{
+ int i, j, f;
+ Tchar ii;
+
+ f = 0;
+ if (n) {
+ if ((j = cbits(ii = getch())) == '+')
+ f = 1;
+ else if (j == '-')
+ f = -1;
+ else
+ ch = ii;
+ }
+ i = atoi0();
+ if (n && f)
+ i = *n + f * i;
+ i = quant(i, res);
+ vflag = 0;
+ res = dfactd = dfact = 1;
+ if (nonumb)
+ i = 0;
+ return(i);
+}
+
+
+int quant(int n, int m)
+{
+ int i, neg;
+
+ neg = 0;
+ if (n < 0) {
+ neg++;
+ n = -n;
+ }
+ /* better as i = ((n + m/2)/m)*m */
+ i = n / m;
+ if (n - m * i > m / 2)
+ i += 1;
+ i *= m;
+ if (neg)
+ i = -i;
+ return(i);
+}