Subversion Repositories planix.SVN

Rev

Rev 2 | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 * troff3.c
 * 
 * macro and string routines, storage allocation
 */

#include "tdef.h"
#include "fns.h"
#include "ext.h"

Tchar   *argtop;
int     pagech = '%';
int     strflg;

#define MHASHSIZE       128     /* must be 2**n */
#define MHASH(x)        ((x>>6)^x) & (MHASHSIZE-1)
Contab  *mhash[MHASHSIZE];


Blockp  *blist;         /* allocated blocks for macros and strings */
int     nblist;         /* how many there are */
int     bfree = -1;     /* first (possible) free block in the list */

Contab *contabp = NULL;
#define MDELTA 500
int     nm = 0;

int savname;            /* name of macro/string being defined */
int savslot;            /* place in Contab of savname */
int freeslot = -1;      /* first (possible) free slot in contab */

void prcontab(Contab *p)
{
        int i;
        for (i = 0; i < nm; i++)
                if (p)
                        if (p[i].rq != 0)
                                fprintf(stderr, "slot %d, %-2.2s\n", i, unpair(p[i].rq));
                        else
                                fprintf(stderr, "slot %d empty\n", i);
                else
                        fprintf(stderr, "slot %d empty\n", i);
}


void blockinit(void)
{
        blist = (Blockp *) calloc(NBLIST, sizeof(Blockp));
        if (blist == NULL) {
                ERROR "not enough room for %d blocks", NBLIST WARN;
                done2(1);
        }
        nblist = NBLIST;
        blist[0].nextoff = blist[1].nextoff = -1;
        blist[0].bp = (Tchar *) calloc(BLK, sizeof(Tchar));
        blist[1].bp = (Tchar *) calloc(BLK, sizeof(Tchar));
                /* -1 prevents blist[0] from being used; temporary fix */
                /* for a design botch: offset==0 is overloaded. */
                /* blist[1] reserved for .rd indicator -- also unused. */
                /* but someone unwittingly looks at these, so allocate something */
        bfree = 2;
}


char *grow(char *ptr, int num, int size)        /* make array bigger */
{
        char *p;

        if (ptr == NULL)
                p = (char *) calloc(num, size);
        else
                p = (char *) realloc(ptr, num * size);
        return p;
}

void mnspace(void)
{
        nm = sizeof(contab)/sizeof(Contab) + MDELTA;
        freeslot = sizeof(contab)/sizeof(Contab) + 1;
        contabp = (Contab *) grow((char *) contabp, nm, sizeof(Contab));
        if (contabp == NULL) {
                ERROR "not enough memory for namespace of %d marcos", nm WARN;
                exit(1);
        }
        contabp = (Contab *) memcpy((char *) contabp, (char *)contab,
                                                        sizeof(contab));
        if (contabp == NULL) {
                ERROR "Cannot reinitialize macro/request name list" WARN;
                exit(1);
        }

}

void caseig(void)
{
        int i;
        Offset oldoff = offset;

        offset = 0;
        i = copyb();
        offset = oldoff;
        if (i != '.')
                control(i, 1);
}


void casern(void)
{
        int i, j, k;

        lgf++;
        skip();
        if ((i = getrq()) == 0 || (oldmn = findmn(i)) < 0)
                return;
        skip();
        clrmn(findmn(j = getrq()));
        if (j) {
                munhash(&contabp[oldmn]);
                contabp[oldmn].rq = j;
                maddhash(&contabp[oldmn]);
                if (dip != d )
                        for (k = dilev; k; k--)
                                if (d[k].curd == i)
                                        d[k].curd = j;
        }
}

void maddhash(Contab *rp)
{
        Contab **hp;

        if (rp->rq == 0)
                return;
        hp = &mhash[MHASH(rp->rq)];
        rp->link = *hp;
        *hp = rp;
}

void munhash(Contab *mp)
{       
        Contab *p;
        Contab **lp;

        if (mp->rq == 0)
                return;
        lp = &mhash[MHASH(mp->rq)];
        p = *lp;
        while (p) {
                if (p == mp) {
                        *lp = p->link;
                        p->link = 0;
                        return;
                }
                lp = &p->link;
                p = p->link;
        }
}

void mrehash(void)
{
        Contab *p;
        int i;

        for (i=0; i < MHASHSIZE; i++)
                mhash[i] = 0;
        for (p=contabp; p < &contabp[nm]; p++)
                p->link = 0;
        for (p=contabp; p < &contabp[nm]; p++) {
                if (p->rq == 0)
                        continue;
                i = MHASH(p->rq);
                p->link = mhash[i];
                mhash[i] = p;
        }
}

void caserm(void)
{
        int j, k;

        lgf++;
g0:
        while (!skip() && (j = getrq()) != 0) {
                if (dip != d)
                        for (k = dilev; k; k--)
                                if (d[k].curd == j) {
                                        ERROR "cannot remove diversion %s during definition",
                                                                unpair(j) WARN;
                                        goto g0;
                                }
                clrmn(findmn(j));
        }
        lgf--;
}


void caseas(void)
{
        app++;
        caseds();
}


void caseds(void)
{
        ds++;
        casede();
}


void caseam(void)
{
        app++;
        casede();
}


void casede(void)
{
        int i, req;
        Offset savoff;

        req = '.';
        lgf++;
        skip();
        if ((i = getrq()) == 0)
                goto de1;
        if ((offset = finds(i)) == 0)
                goto de1;
        if (newmn)
                savslot = newmn;
        else
                savslot = findmn(i);
        savname = i;
        if (ds)
                copys();
        else
                req = copyb();
        clrmn(oldmn);
        if (newmn) {
                if (contabp[newmn].rq)
                        munhash(&contabp[newmn]);
                contabp[newmn].rq = i;
                maddhash(&contabp[newmn]);

        }
        if (apptr) {
                savoff = offset;
                offset = apptr;
                wbf((Tchar) IMP);
                offset = savoff;        /* pointless */
        }
        offset = dip->op;
        if (req != '.')
                control(req, 1);
de1:
        ds = app = 0;
}


int findmn(int i)
{
        Contab *p;

        for (p = mhash[MHASH(i)]; p; p = p->link)
                if (i == p->rq)
                        return(p - contabp);
        return(-1);
}


void clrmn(int i)
{
        if (i >= 0) {
                if (contabp[i].mx)
                        ffree(contabp[i].mx);
                munhash(&contabp[i]);
                contabp[i].rq = 0;
                contabp[i].mx = 0;
                contabp[i].emx = 0;
                contabp[i].f = 0;
                if (contabp[i].divsiz != NULL) {
                        free(contabp[i].divsiz);
                        contabp[i].divsiz = NULL;
                }
                if (freeslot > i)
                        freeslot = i;
        }
}

void growcontab(void)
{
        nm += MDELTA;
        contabp = (Contab *) grow((char *) contabp , nm, sizeof(Contab));
        if (contabp == NULL) {
                ERROR "Too many (%d) string/macro names", nm WARN;
                done2(02);
        } else {
                memset((char *)(contabp) + (nm - MDELTA) * sizeof(Contab),
                                                0, MDELTA * sizeof(Contab));
                mrehash();
        }
}


Offset finds(int mn)
{
        int i;
        Offset savip;

        oldmn = findmn(mn);
        newmn = 0;
        apptr = 0;
        if (app && oldmn >= 0 && contabp[oldmn].mx) {
                savip = ip;
                ip = contabp[oldmn].emx;
                oldmn = -1;
                apptr = ip;
                if (!diflg)
                        ip = incoff(ip);
                nextb = ip;
                ip = savip;
        } else {
                for (i = freeslot; i < nm; i++) {
                        if (contabp[i].rq == 0)
                                break;
                }
                if (i == nm) 
                        growcontab();
                freeslot = i + 1;
                if ((nextb = alloc()) == -1) {
                        app = 0;
                        if (macerr++ > 1)
                                done2(02);
                        if (nextb == 0)
                                ERROR "Not enough space for string/macro names" WARN;
                        edone(04);
                        return(offset = 0);
                }
                contabp[i].mx = nextb;
                if (!diflg) {
                        newmn = i;
                        if (oldmn == -1)
                                contabp[i].rq = -1;
                } else {
                        contabp[i].rq = mn;
                        maddhash(&contabp[i]);
                }
        }
        app = 0;
        return(offset = nextb);
}

int skip(void)
{
        Tchar i;

        while (cbits(i = getch()) == ' ' || ismot(i))
                ;
        ch = i;
        return(nlflg);
}


int copyb(void)
{
        int i, j, state;
        Tchar ii;
        int req, k;
        Offset savoff;
        Uchar *p;

        if (skip() || !(j = getrq()))
                j = '.';
        req = j;
        p = unpair(j);
        /* was: k = j >> BYTE; j &= BYTEMASK; */
        j = p[0];
        k = p[1];
        copyf++;
        flushi();
        nlflg = 0;
        state = 1;
        savoff = 0;

/* state 0      eat up
 * state 1      look for .
 * state 2      look for first char of end macro
 * state 3      look for second char of end macro
 */

        while (1) {
                i = cbits(ii = getch());
                if (state == 3) {
                        if (i == k)
                                break;
                        if (!k) {
                                ch = ii;
                                i = getach();
                                ch = ii;
                                if (!i)
                                        break;
                        }
                        state = 0;
                        goto c0;
                }
                if (i == '\n') {
                        state = 1;
                        nlflg = 0;
                        goto c0;
                }
                if (state == 1 && i == '.') {
                        state++;
                        savoff = offset;
                        goto c0;
                }
                if (state == 2 && i == j) {
                        state++;
                        goto c0;
                }
                state = 0;
c0:
                if (offset)
                        wbf(ii);
        }
        if (offset) {
                offset = savoff;
                wbf((Tchar)0);
        }
        copyf--;
        return(req);
}


void copys(void)
{
        Tchar i;

        copyf++;
        if (skip())
                goto c0;
        if (cbits(i = getch()) != '"')
                wbf(i);
        while (cbits(i = getch()) != '\n')
                wbf(i);
c0:
        wbf((Tchar)0);
        copyf--;
}


Offset alloc(void)      /* return free Offset in nextb */
{
        int i, j;

        for (i = bfree; i < nblist; i++)
                if (blist[i].nextoff == 0)
                        break;
        if (i == nblist) {
                blist = (Blockp *) realloc((char *) blist, 2 * nblist * sizeof(Blockp));
                if (blist == NULL) {
                        ERROR "can't grow blist for string/macro defns" WARN;
                        done2(2);
                }
                nblist *= 2;
                for (j = i; j < nblist; j++) {
                        blist[j].nextoff = 0;
                        blist[j].bp = 0;
                }
        }
        blist[i].nextoff = -1;  /* this block is the end */
        bfree = i + 1;
        if (blist[i].bp == 0)
                blist[i].bp = (Tchar *) calloc(BLK, sizeof(Tchar));
        if (blist[i].bp == NULL) {
                ERROR "can't allocate memory for string/macro definitions" WARN;
                done2(2);
        }
        nextb = (Offset) i * BLK;
        return nextb;
}


void ffree(Offset i)    /* free list of blocks starting at blist(o) */
{                       /* (doesn't actually free the blocks, just the pointers) */
        int j;

        for ( ; blist[j = bindex(i)].nextoff != -1; ) {
                if (bfree > j)
                        bfree = j;
                i = blist[j].nextoff;
                blist[j].nextoff = 0;
        }
        blist[j].nextoff = 0;
}


void wbf(Tchar i)       /* store i into offset, get ready for next one */
{
        int j, off;

        if (!offset)
                return;
        j = bindex(offset);
        if (i == 0)
                contabp[savslot].emx = offset;
        off = boffset(offset);
        blist[j].bp[off] = i;
        offset++;
        if (pastend(offset)) {  /* off the end of this block */
                if (blist[j].nextoff == -1) {
                        if ((nextb = alloc()) == -1) {
                                ERROR "Out of temp file space" WARN;
                                done2(01);
                        }
                        blist[j].nextoff = nextb;
                }
                offset = blist[j].nextoff;
        }
}


Tchar rbf(void) /* return next char from blist[] block */
{
        Tchar i, j;

        if (ip == RD_OFFSET) {          /* for rdtty */
                if (j = rdtty())
                        return(j);
                else
                        return(popi());
        }
        
        i = rbf0(ip);
        if (i == 0) {
                if (!app)
                        i = popi();
                return(i);
        }
        ip = incoff(ip);
        return(i);
}


Offset xxxincoff(Offset p)              /* get next blist[] block */
{
        p++;
        if (pastend(p)) {               /* off the end of this block */
                if ((p = blist[bindex(p-1)].nextoff) == -1) {   /* and nothing was allocated after it */
                        ERROR "Bad storage allocation" WARN;
                        done2(-5);
                }
        }
        return(p);
}


Tchar popi(void)
{
        Stack *p;

        if (frame == stk)
                return(0);
        if (strflg)
                strflg--;
        p = nxf = frame;
        p->nargs = 0;
        frame = p->pframe;
        ip = p->pip;
        pendt = p->ppendt;
        lastpbp = p->lastpbp;
        return(p->pch);
}

/*
 *      test that the end of the allocation is above a certain location
 *      in memory
 */
#define SPACETEST(base, size) \
        if ((char*)base + size >= (char*)stk+STACKSIZE) \
                ERROR "Stacksize overflow in n3" WARN

Offset pushi(Offset newip, int  mname)
{
        Stack *p;

        SPACETEST(nxf, sizeof(Stack));
        p = nxf;
        p->pframe = frame;
        p->pip = ip;
        p->ppendt = pendt;
        p->pch = ch;
        p->lastpbp = lastpbp;
        p->mname = mname;
        lastpbp = pbp;
        pendt = ch = 0;
        frame = nxf;
        if (nxf->nargs == 0) 
                nxf += 1;
        else 
                nxf = (Stack *)argtop;
        return(ip = newip);
}


void *setbrk(int x)
{
        char *i;

        if ((i = (char *) calloc(x, 1)) == 0) {
                ERROR "Core limit reached" WARN;
                edone(0100);
        }
        return(i);
}


int getsn(void)
{
        int i;

        if ((i = getach()) == 0)
                return(0);
        if (i == '(')
                return(getrq());
        else 
                return(i);
}


Offset setstr(void)
{
        int i, j;

        lgf++;
        if ((i = getsn()) == 0 || (j = findmn(i)) == -1 || !contabp[j].mx) {
                lgf--;
                return(0);
        } else {
                SPACETEST(nxf, sizeof(Stack));
                nxf->nargs = 0;
                strflg++;
                lgf--;
                return pushi(contabp[j].mx, i);
        }
}



void collect(void)
{
        int j;
        Tchar i, *strp, *lim, **argpp, **argppend;
        int quote;
        Stack *savnxf;

        copyf++;
        nxf->nargs = 0;
        savnxf = nxf;
        if (skip())
                goto rtn;

        {
                char *memp;
                memp = (char *)savnxf;
                /*
                 *      1 s structure for the macro descriptor
                 *      APERMAC Tchar *'s for pointers into the strings
                 *      space for the Tchar's themselves
                 */
                memp += sizeof(Stack);
                /*
                 *      CPERMAC = the total # of characters for ALL arguments
                 */
#define CPERMAC 200
#define APERMAC 9
                memp += APERMAC * sizeof(Tchar *);
                memp += CPERMAC * sizeof(Tchar);
                nxf = (Stack *)memp;
        }
        lim = (Tchar *)nxf;
        argpp = (Tchar **)(savnxf + 1);
        argppend = &argpp[APERMAC];
        SPACETEST(argppend, sizeof(Tchar *));
        strp = (Tchar *)argppend;
        /*
         *      Zero out all the string pointers before filling them in.
         */
        for (j = 0; j < APERMAC; j++)
                argpp[j] = 0;
        /* ERROR "savnxf=0x%x,nxf=0x%x,argpp=0x%x,strp=argppend=0x%x, lim=0x%x",
         *      savnxf, nxf, argpp, strp, lim WARN;
         */
        strflg = 0;
        while (argpp != argppend && !skip()) {
                *argpp++ = strp;
                quote = 0;
                if (cbits(i = getch()) == '"')
                        quote++;
                else 
                        ch = i;
                while (1) {
                        i = getch();
/* fprintf(stderr, "collect %c %d\n", cbits(i), cbits(i)); */
                        if (nlflg || (!quote && argpp != argppend && cbits(i) == ' '))
                                break;  /* collects rest into $9 */
                        if (   quote
                            && cbits(i) == '"'
                            && cbits(i = getch()) != '"') {
                                ch = i;
                                break;
                        }
                        *strp++ = i;
                        if (strflg && strp >= lim) {
                                /* ERROR "strp=0x%x, lim = 0x%x", strp, lim WARN; */
                                ERROR "Macro argument too long" WARN;
                                copyf--;
                                edone(004);
                        }
                        SPACETEST(strp, 3 * sizeof(Tchar));
                }
                *strp++ = 0;
        }
        nxf = savnxf;
        nxf->nargs = argpp - (Tchar **)(savnxf + 1);
        argtop = strp;
rtn:
        copyf--;
}


void seta(void)
{
        int i;

        i = cbits(getch()) - '0';
        if (i > 0 && i <= APERMAC && i <= frame->nargs)
                pushback(*(((Tchar **)(frame + 1)) + i - 1));
}


void caseda(void)
{
        app++;
        casedi();
}

void casegd(void)
{
        int i, j;

        skip();
        if ((i = getrq()) == 0)
                return;
        if ((j = findmn(i)) >= 0) {
                if (contabp[j].divsiz != NULL) {
                        numtabp[DN].val = contabp[j].divsiz->dix;
                        numtabp[DL].val = contabp[j].divsiz->diy;
                }
        }
}

#define FINDDIV(o) if ((o =  findmn(dip->curd)) < 0) \
                        ERROR "lost diversion %s", unpair(dip->curd) WARN

void casedi(void)
{
        int i, j, *k;

        lgf++;
        if (skip() || (i = getrq()) == 0) {
                if (dip != d) {
                        FINDDIV(savslot);
                        wbf((Tchar)0);
                }
                if (dilev > 0) {
                        numtabp[DN].val = dip->dnl;
                        numtabp[DL].val = dip->maxl;
                        FINDDIV(j);
                        if ((contabp[j].divsiz = (Divsiz *) malloc(sizeof(Divsiz))) == NULL) {
                                ERROR "Cannot alloc diversion size" WARN;
                                done2(1);
                        } else {
                                contabp[j].divsiz->dix = numtabp[DN].val;
                                contabp[j].divsiz->diy = numtabp[DL].val;
                        }
                        dip = &d[--dilev];
                        offset = dip->op;
                }
                goto rtn;
        }
        if (++dilev == NDI) {
                --dilev;
                ERROR "Diversions nested too deep" WARN;
                edone(02);
        }
        if (dip != d) {
                FINDDIV(j);
                savslot = j;
                wbf((Tchar)0);
        }
        diflg++;
        dip = &d[dilev];
        dip->op = finds(i);
        dip->curd = i;
        clrmn(oldmn);
        k = (int *) & dip->dnl;
        for (j = 0; j < 10; j++)
                k[j] = 0;       /*not op and curd*/
rtn:
        app = 0;
        diflg = 0;
}


void casedt(void)
{
        lgf++;
        dip->dimac = dip->ditrap = dip->ditf = 0;
        skip();
        dip->ditrap = vnumb((int *)0);
        if (nonumb)
                return;
        skip();
        dip->dimac = getrq();
}

#define LNSIZE 4000
void casetl(void)
{
        int j;
        int w[3];
        Tchar buf[LNSIZE];
        Tchar *tp;
        Tchar i, delim;

        /*
         * bug fix
         *
         * if .tl is the first thing in the file, the p1
         * doesn't come out, also the pagenumber will be 0
         *
         * tends too confuse the device filter (and the user as well)
         */
        if (dip == d && numtabp[NL].val == -1)
                newline(1);
        dip->nls = 0;
        skip();
        if (ismot(delim = getch())) {
                ch = delim;
                delim = '\'';
        } else 
                delim = cbits(delim);
        tp = buf;
        numtabp[HP].val = 0;
        w[0] = w[1] = w[2] = 0;
        j = 0;
        while (cbits(i = getch()) != '\n') {
                if (cbits(i) == cbits(delim)) {
                        if (j < 3)
                                w[j] = numtabp[HP].val;
                        numtabp[HP].val = 0;
                        if (w[j] != 0)
                                *tp++ = WORDSP;
                        j++;
                        *tp++ = 0;
                } else {
                        if (cbits(i) == pagech) {
                                setn1(numtabp[PN].val, numtabp[findr('%')].fmt,
                                      i&SFMASK);
                                continue;
                        }
                        numtabp[HP].val += width(i);
                        if (tp < &buf[LNSIZE-10]) {
                                if (cbits(i) == ' ' && *tp != WORDSP)
                                        *tp++ = WORDSP;
                                *tp++ = i;
                        } else {
                                ERROR "Overflow in casetl" WARN;
                        }
                }
        }
        if (j<3)
                w[j] = numtabp[HP].val;
        *tp++ = 0;
        *tp++ = 0;
        *tp = 0;
        tp = buf;
        if (NROFF)
                horiz(po);
        while (i = *tp++)
                pchar(i);
        if (w[1] || w[2])
                horiz(j = quant((lt - w[1]) / 2 - w[0], HOR));
        while (i = *tp++)
                pchar(i);
        if (w[2]) {
                horiz(lt - w[0] - w[1] - w[2] - j);
                while (i = *tp++)
                        pchar(i);
        }
        newline(0);
        if (dip != d) {
                if (dip->dnl > dip->hnl)
                        dip->hnl = dip->dnl;
        } else {
                if (numtabp[NL].val > dip->hnl)
                        dip->hnl = numtabp[NL].val;
        }
}


void casepc(void)
{
        pagech = chget(IMP);
}


void casepm(void)
{
        int i, k;
        int xx, cnt, tcnt, kk, tot;
        Offset j;

        kk = cnt = tcnt = 0;
        tot = !skip();
        stackdump();
        for (i = 0; i < nm; i++) {
                if ((xx = contabp[i].rq) == 0 || contabp[i].mx == 0)
                        continue;
                tcnt++;
                j = contabp[i].mx;
                for (k = 1; (j = blist[bindex(j)].nextoff) != -1; )
                        k++; 
                cnt++;
                kk += k;
                if (!tot)
                        fprintf(stderr, "%-2.2s %d\n", unpair(xx), k);
        }
        fprintf(stderr, "pm: total %d, macros %d, space %d\n", tcnt, cnt, kk);
}

void stackdump(void)    /* dumps stack of macros in process */
{
        Stack *p;

        if (frame != stk) {
                fprintf(stderr, "stack: ");
                for (p = frame; p != stk; p = p->pframe)
                        fprintf(stderr, "%s ", unpair(p->mname));
                fprintf(stderr, "\n");
        }
}