829 lines
12 KiB
C
829 lines
12 KiB
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);
|
|
}
|