ref: c8d87b22d50a17b8832019a93b9293fef19fbd02
dir: /sys/src/cmd/troff/n4.c/
/* * 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); }