blob: a1abe943d96493a7e25e41cd76035ba14c552ce4 [file] [log] [blame]
/*
* 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);
}