| #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); |
| } |
| |