Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

#include "a.h"
/*
 * 8. Number Registers
 * (Reg register implementation is also here.)
 */

/*
 *      \nx             N
 *      \n(xx   N
 *      \n+x            N+=M
 *      \n-x            N-=M
 *
 *      .nr R ±N M
 *      .af R c
 *
 *      formats
 *              1       0, 1, 2, 3, ...
 *              001     001, 002, 003, ...
 *              i       0, i, ii, iii, iv, v, ...
 *              I       0, I, II, III, IV, V, ...
 *              a       0, a, b, ..., aa, ab, ..., zz, aaa, ...
 *              A       0, A, B, ..., AA, AB, ..., ZZ, AAA, ...
 *
 *      \gx \g(xx return format of number register
 *
 *      .rr R
 */

typedef struct Reg Reg;
struct Reg
{
        Reg *next;
        Rune *name;
        Rune *val;
        Rune *fmt;
        int inc;
};

Reg *dslist;
Reg *nrlist;

/*
 * Define strings and numbers.
 */
void
dsnr(Rune *name, Rune *val, Reg **l)
{
        Reg *s;

        for(s = *l; s != nil; s = *l){
                if(runestrcmp(s->name, name) == 0)
                        break;
                l = &s->next;
        }
        if(val == nil){
                if(s){
                        *l = s->next;
                        free(s->val);
                        free(s->fmt);
                        free(s);
                }
                return;
        }
        if(s == nil){
                s = emalloc(sizeof(Reg));
                *l = s;
                s->name = erunestrdup(name);
        }else
                free(s->val);
        s->val = erunestrdup(val);
}

Rune*
getdsnr(Rune *name, Reg *list)
{
        Reg *s;
        
        for(s=list; s; s=s->next)
                if(runestrcmp(name, s->name) == 0)
                        return s->val;
        return nil;
}

void
ds(Rune *name, Rune *val)
{
        dsnr(name, val, &dslist);
}

void
as(Rune *name, Rune *val)
{
        Rune *p, *q;
        
        p = getds(name);
        if(p == nil)
                p = L("");
        q = runemalloc(runestrlen(p)+runestrlen(val)+1);
        runestrcpy(q, p);
        runestrcat(q, val);
        ds(name, q);
        free(q);
}

Rune*
getds(Rune *name)
{
        return getdsnr(name, dslist);
}

void
printds(int t)
{
        int n, total;
        Reg *s;
        
        total = 0;
        for(s=dslist; s; s=s->next){
                if(s->val)
                        n = runestrlen(s->val);
                else
                        n = 0;
                total += n;
                if(!t)
                        fprint(2, "%S\t%d\n", s->name, n);
        }
        fprint(2, "total\t%d\n", total);
}

void
nr(Rune *name, int val)
{
        Rune buf[20];
        
        runesnprint(buf, nelem(buf), "%d", val);
        _nr(name, buf);
}

void
af(Rune *name, Rune *fmt)
{
        Reg *s;

        if(_getnr(name) == nil)
                _nr(name, L("0"));
        for(s=nrlist; s; s=s->next)
                if(runestrcmp(s->name, name) == 0)
                        s->fmt = erunestrdup(fmt);
}

Rune*
getaf(Rune *name)
{
        Reg *s;
        
        for(s=nrlist; s; s=s->next)
                if(runestrcmp(s->name, name) == 0)
                        return s->fmt;
        return nil;
}

void
printnr(void)
{
        Reg *r;
        
        for(r=nrlist; r; r=r->next)
                fprint(2, "%S %S %d\n", r->name, r->val, r->inc);
}

/*
 * Some internal number registers are actually strings,
 * so provide _ versions to get at them.
 */
void
_nr(Rune *name, Rune *val)
{
        dsnr(name, val, &nrlist);
}

Rune*
_getnr(Rune *name)
{
        return getdsnr(name, nrlist);
}

int
getnr(Rune *name)
{
        Rune *p;

        p = _getnr(name);
        if(p == nil)
                return 0;
        return eval(p);
}

/* new register */
void
r_nr(int argc, Rune **argv)
{
        Reg *s;

        if(argc < 2)
                return;
        if(argc < 3)
                nr(argv[1], 0);
        else{
                if(argv[2][0] == '+')
                        nr(argv[1], getnr(argv[1])+eval(argv[2]+1));
                else if(argv[2][0] == '-')
                        nr(argv[1], getnr(argv[1])-eval(argv[2]+1));
                else
                        nr(argv[1], eval(argv[2]));
        }
        if(argc > 3){
                for(s=nrlist; s; s=s->next)
                        if(runestrcmp(s->name, argv[1]) == 0)
                                s->inc = eval(argv[3]);
        }
}

/* assign format */
void
r_af(int argc, Rune **argv)
{
        USED(argc);
        
        af(argv[1], argv[2]);
}

/* remove register */
void
r_rr(int argc, Rune **argv)
{
        int i;
        
        for(i=1; i<argc; i++)
                _nr(argv[i], nil);
}

/* fmt integer in base 26 */
void
alpha(Rune *buf, int n, int a)
{
        int i, v;
        
        i = 1;
        for(v=n; v>0; v/=26)
                i++;
        if(i == 0)
                i = 1;
        buf[i] = 0;
        while(i > 0){
                buf[--i] = a+n%26;
                n /= 26;
        }
}

struct romanv {
        char *s;
        int v;
} romanv[] =
{
        "m",    1000,
        "cm", 900,
        "d", 500,
        "cd", 400,
        "c", 100,
        "xc", 90,
        "l", 50,
        "xl", 40,
        "x", 10,
        "ix", 9,
        "v", 5,
        "iv", 4,
        "i", 1
};

/* fmt integer in roman numerals! */
void
roman(Rune *buf, int n, int upper)
{
        Rune *p;
        char *q;
        struct romanv *r;
        
        if(upper)
                upper = 'A' - 'a';
        if(n >= 5000 || n <= 0){
                runestrcpy(buf, L("-"));
                return;
        }
        p = buf;
        r = romanv;
        while(n > 0){
                while(n >= r->v){
                        for(q=r->s; *q; q++)
                                *p++ = *q + upper;
                        n -= r->v;
                }
                r++;
        }
        *p = 0;
}

Rune*
getname(void)
{
        int i, c, cc;
        static Rune buf[100];
        
        /* XXX add [name] syntax as in groff */
        c = getnext();
        if(c < 0)
                return L("");
        if(c == '\n'){
                warn("newline in name\n");
                ungetnext(c);
                return L("");
        }
        if(c == '['){
                for(i=0; i<nelem(buf)-1; i++){
                        if((c = getrune()) < 0)
                                return L("");
                        if(c == ']'){
                                buf[i] = 0;
                                return buf;
                        }
                        buf[i] = c;
                }
                return L("");
        }
        if(c != '('){
                buf[0] = c;
                buf[1] = 0;
                return buf;
        }
        c = getnext();
        cc = getnext();
        if(c < 0 || cc < 0)
                return L("");
        if(c == '\n' | cc == '\n'){
                warn("newline in \\n");
                ungetnext(cc);
                if(c == '\n')
                        ungetnext(c);
        }
        buf[0] = c;
        buf[1] = cc;
        buf[2] = 0;
        return buf;
}

/* \n - return number register */
int
e_n(void)
{
        int inc, v, l;
        Rune *name, *fmt, buf[100];
        Reg *s;
        
        inc = getnext();
        if(inc < 0)
                return -1;
        if(inc != '+' && inc != '-'){
                ungetnext(inc);
                inc = 0;
        }
        name = getname();
        if(_getnr(name) == nil)
                _nr(name, L("0"));
        for(s=nrlist; s; s=s->next){
                if(runestrcmp(s->name, name) == 0){
                        if(s->fmt == nil && !inc && s->val[0]){
                                /* might be a string! */
                                pushinputstring(s->val);
                                return 0;
                        }
                        v = eval(s->val);
                        if(inc){
                                if(inc == '+')
                                        v += s->inc;
                                else
                                        v -= s->inc;
                                runesnprint(buf, nelem(buf), "%d", v);
                                free(s->val);
                                s->val = erunestrdup(buf);
                        }
                        fmt = s->fmt;
                        if(fmt == nil)
                                fmt = L("1");
                        switch(fmt[0]){
                        case 'i':
                        case 'I':
                                roman(buf, v, fmt[0]=='I');
                                break;
                        case 'a':
                        case 'A':
                                alpha(buf, v, fmt[0]);
                                break;
                        default:
                                l = runestrlen(fmt);
                                if(l == 0)
                                        l = 1;
                                runesnprint(buf, sizeof buf, "%0*d", l, v);
                                break;
                        }
                        pushinputstring(buf);
                        return 0;
                }
        }
        pushinputstring(L(""));
        return 0;
}

/* \g - number register format */
int
e_g(void)
{
        Rune *p;

        p = getaf(getname());
        if(p == nil)
                p = L("1");
        pushinputstring(p);
        return 0;
}

void
r_pnr(int argc, Rune **argv)
{
        USED(argc);
        USED(argv);
        printnr();
}

void
t8init(void)
{
        addreq(L("nr"), r_nr, -1);
        addreq(L("af"), r_af, 2);
        addreq(L("rr"), r_rr, -1);
        addreq(L("pnr"), r_pnr, 0);
        
        addesc('n', e_n, CopyMode|ArgMode|HtmlMode);
        addesc('g', e_g, 0);
}