Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

/*
 * 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);
}