| /* |
| * 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], "%ld", 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 "%s", 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); |
| } |