ref: 920783505c3b91624200f144b2cbdd78dc348ca5
parent: 4c841d0b46720390df695dab404e6c82cdd25ef6
author: aiju <devnull@localhost>
date: Sun Sep 27 09:50:18 EDT 2015
add timepic(1)
--- /dev/null
+++ b/sys/src/cmd/timepic.c
@@ -1,0 +1,729 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <ctype.h>
+
+typedef struct Symbol Symbol;
+typedef struct Event Event;
+typedef struct Signal Signal;
+
+struct Symbol {
+ char *name;
+ int t;
+ double e;
+ Symbol *next;
+};
+struct Event {
+ double t;
+ int val;
+ Event *next;
+ char *data;
+ int line;
+};
+struct Signal {
+ char *name;
+ Event *ev;
+ Signal *next;
+};
+
+int lineno, lastc, peeked;
+char sname[512];
+double sval;
+double width, rheight;
+double mint, maxt;
+double left, right;
+int nsig;
+Biobuf *bp;
+Symbol *stab[256];
+Signal *sigfirst, **siglast = &sigfirst;
+
+enum {
+ SYMFREE,
+ SYMNUM,
+};
+
+enum {
+ VZ,
+ VX,
+ VL,
+ VH,
+ VMULT,
+};
+
+enum {
+ CMD = -1,
+ SYM = -2,
+ NUM = -3,
+ EOF = -4,
+};
+
+static int
+Tfmt(Fmt *f)
+{
+ int n;
+
+ n = va_arg(f->args, int);
+ if(n >= 0 && n < 0x80 && isprint(n))
+ return fmtprint(f, "'%c'", n);
+ else
+ switch(n){
+ case CMD: return fmtprint(f, ".%s", sname);
+ case SYM: return fmtprint(f, "'%s'", sname);
+ case NUM: return fmtprint(f, "%g", sval);
+ case EOF: return fmtprint(f, "EOF");
+ default: return fmtprint(f, "%d", n);
+ }
+}
+
+static void *
+emalloc(int n)
+{
+ void *v;
+
+ v = malloc(n);
+ if(v == nil)
+ sysfatal("malloc: %r");
+ memset(v, 0, n);
+ setmalloctag(v, getcallerpc(&n));
+ return v;
+}
+
+static void
+error(int l, char *fmt, ...)
+{
+ va_list va;
+ char *s;
+
+ va_start(va, fmt);
+ s = vsmprint(fmt, va);
+ fprint(2, "%d %s\n", l, s);
+ free(s);
+ va_end(va);
+}
+
+static int
+hash(char *s)
+{
+ int c;
+
+ for(c = 0; *s != 0; s++)
+ c += *s;
+ return c;
+}
+
+static Symbol *
+getsym(char *st)
+{
+ Symbol *s, **p;
+
+ for(p = &stab[hash(st)%nelem(stab)]; s = *p, s != nil; p = &s->next){
+ if(strcmp(s->name, st) == 0)
+ return s;
+ }
+ s = emalloc(sizeof(Symbol));
+ s->name = strdup(st);
+ *p = s;
+ return s;
+}
+
+static int
+issym(int c)
+{
+ return isalnum(c) || c == '_' || c >= 0x80;
+}
+
+static int
+numfsm(int c, int *st)
+{
+ enum {
+ PREDOT,
+ POSTDOT,
+ ESIGN,
+ EDIG
+ };
+
+ switch(*st){
+ case PREDOT:
+ if(c == '.')
+ *st = POSTDOT;
+ if(c == 'e')
+ *st = ESIGN;
+ return isdigit(c) || c == '.' || c == 'e';
+ case POSTDOT:
+ if(c == 'e')
+ *st = ESIGN;
+ return isdigit(c) || c == 'e';
+ case ESIGN:
+ *st = EDIG;
+ return isdigit(c) || c == '+' || c == '-';
+ case EDIG:
+ return isdigit(c);
+ }
+ return 0;
+}
+
+static int
+lex(void)
+{
+ int c;
+ char *p;
+ int st;
+
+ do{
+ c = Bgetc(bp);
+ if(c < 0)
+ return EOF;
+ if(!isspace(c))
+ break;
+ if(c == '\n')
+ lineno++;
+ lastc = c;
+ }while(1);
+
+ if(lastc == 10 && c == '.'){
+ for(p = sname; c = Bgetc(bp), issym(c); )
+ if(p < sname + sizeof(sname) - 1)
+ *p++ = c;
+ Bungetc(bp);
+ *p = 0;
+ return CMD;
+ }
+ if(isdigit(c) || c == '.'){
+ st = 0;
+ for(p = sname; numfsm(c, &st); c = Bgetc(bp))
+ if(p < sname + sizeof(sname) - 1)
+ *p++ = c;
+ Bungetc(bp);
+ *p = 0;
+ sval = strtol(sname, &p, 0);
+ if(*p != 0)
+ sval = strtod(sname, &p);
+ if(*p != 0)
+ error(lineno, "invalid number %s", sname);
+ return NUM;
+ }
+ if(issym(c)){
+ for(p = sname; issym(c); c = Bgetc(bp))
+ if(p < sname + sizeof(sname) - 1)
+ *p++ = c;
+ Bungetc(bp);
+ *p = 0;
+ return SYM;
+ }
+ return c;
+}
+
+static int
+next(void)
+{
+ int rc;
+
+ if(peeked != 0){
+ rc = peeked;
+ peeked = 0;
+ return rc;
+ }
+ return lex();
+}
+
+static int
+peek(void)
+{
+ if(peeked == 0)
+ peeked = lex();
+ return peeked;
+}
+
+static void
+expect(int n)
+{
+ int s;
+
+ if((s = peek()) != n)
+ error(lineno, "expected %T, got %T", n, s);
+ else
+ next();
+}
+
+static double expr(void);
+
+static double
+factor(void)
+{
+ int t;
+ double g;
+ Symbol *s;
+
+ switch(t = next()){
+ case NUM:
+ return sval;
+ case SYM:
+ s = getsym(sname);
+ if(s->t != SYMNUM)
+ error(lineno, "not a number: %s", s->name);
+ return s->e;
+ case '(':
+ g = expr();
+ expect(')');
+ return g;
+ default:
+ error(lineno, "factor: unexpected %T", t);
+ return 0;
+ }
+}
+
+static double
+term(void)
+{
+ double g;
+
+ g = factor();
+ while(peek() == '*' || peek() == '/'){
+ switch(next()){
+ case '*': g *= factor(); break;
+ case '/': g /= factor(); break;
+ }
+ }
+ return g;
+}
+
+static double
+expr(void)
+{
+ int s;
+ double g;
+
+ s = 1;
+ if(peek() == '+' || peek() == '-')
+ s = next() == '-' ? -1 : 1;
+ g = s * term();
+ while(peek() == '+' || peek() == '-'){
+ s = next() == '-' ? -1 : 1;
+ g += s * term();
+ }
+ return g;
+}
+
+static void
+assign(Symbol *s)
+{
+ next();
+ if(s->t != SYMFREE){
+ error(lineno, "symbol already exists: %s", s->name);
+ return;
+ }
+ s->t = SYMNUM;
+ s->e = expr();
+ expect(';');
+}
+
+static int
+parseval(Event *e)
+{
+ int t;
+
+ switch(t = next()){
+ case SYM:
+ if(strcmp(sname, "x") == 0)
+ return VX;
+ if(strcmp(sname, "z") == 0)
+ return VZ;
+ e->data = strdup(sname);
+ return VMULT;
+ case NUM:
+ if(sval == 0)
+ return VL;
+ if(sval == 1)
+ return VH;
+ e->data = smprint("%g", sval);
+ return VMULT;
+ default:
+ error(lineno, "unexpected %T", t);
+ return VZ;
+ }
+}
+
+static void
+append(double off, Event ***ep, Event *e)
+{
+ Event *f;
+
+ for(; e != nil; e = e->next){
+ f = emalloc(sizeof(Event));
+ f->t = e->t + off;
+ f->val = e->val;
+ f->line = e->line;
+ if(e->data != nil)
+ f->data = strdup(e->data);
+ **ep = f;
+ *ep = &f->next;
+ }
+}
+
+static void
+freeev(Event *e)
+{
+ Event *en;
+
+ for(; e != nil; e = en){
+ en = e->next;
+ free(e->data);
+ free(e);
+ }
+}
+
+static Event *
+events(double *off, int ronly)
+{
+ int rela;
+ Event *e, *ev, **ep;
+ int i, n;
+ double f;
+ Symbol *sy;
+ double len;
+ int line;
+
+ rela = 0;
+ line = 0;
+ ev = nil;
+ ep = &ev;
+ for(;;)
+ switch(peek()){
+ case '+':
+ rela = 1;
+ next();
+ break;
+ case '}':
+ case ';':
+ return ev;
+ case ':':
+ next();
+ e = emalloc(sizeof(Event));
+ e->t = *off;
+ e->val = parseval(e);
+ e->line = line;
+ line = 0;
+ *ep = e;
+ ep = &e->next;
+ break;
+ case '|':
+ line = 1;
+ next();
+ break;
+ default:
+ f = expr();
+ if(peek() == '{'){
+ next();
+ if(f < 0 || isNaN(f) || f >= 0x7fffffff){
+ error(lineno, "invalid repeat count");
+ f = 0;
+ }
+ len = 0;
+ e = events(&len, 1);
+ expect('}');
+ n = f + 0.5;
+ for(i = 0; i < n; i++){
+ append(*off, &ep, e);
+ *off += len;
+ }
+ if(*off > maxt) maxt = *off;
+ freeev(e);
+ break;
+ }
+ if(ronly && !rela){
+ error(lineno, "only relative addressing allowed");
+ rela = 1;
+ }
+ if(peek() == SYM){
+ next();
+ sy = getsym(sname);
+ if(sy->t == SYMFREE)
+ error(lineno, "undefined %s", sy->name);
+ else
+ f *= sy->e;
+ }
+ if(rela)
+ *off += f;
+ else
+ *off = f;
+ if(peek() == '['){
+ next();
+ sy = getsym(sname);
+ if(sy->t != SYMFREE && sy->t != SYMNUM)
+ error(lineno, "already defined %s", sy->name);
+ else{
+ sy->t = SYMNUM;
+ sy->e = *off;
+ }
+ expect(']');
+ }
+ if(*off < mint) mint = *off;
+ if(*off > maxt) maxt = *off;
+ rela = 0;
+ break;
+ }
+
+}
+
+static void
+signal(char *st)
+{
+ Signal *s;
+ double off;
+
+ s = emalloc(sizeof(Signal));
+ s->name = strdup(st);
+ s->ev = events(&off, 0);
+ expect(';');
+ *siglast = s;
+ siglast = &s->next;
+ nsig++;
+}
+
+static void
+slantfill(double x1, double x2, double t, double b, double tw)
+{
+ double x;
+ double sw = 0.05;
+ double soff = 0.05;
+
+ for(x = x1; x < x2 - sw; x += soff)
+ print("line from %g,%g to %g,%g\n", x, t, x+sw, b);
+ if(x < x2)
+ print("line from %g,%g to %g,%g\n", x, t, (2*sw*tw+2*tw*x+sw*x2)/(sw+2*tw), (sw*t+t*(x-x2)+b*(2*tw-x+x2))/(sw+2*tw));
+}
+
+static void
+sigout(Signal *s, double top)
+{
+ Event *e, *n;
+ double l, w, t, b, x1, x2, tw, m;
+
+ for(e = s->ev; e != nil && e->next != nil && e->next->t < left; e = e->next)
+ ;
+ if(e == nil)
+ return;
+ b = top - rheight * 0.75;
+ t = top - rheight * 0.25;
+ tw = width * 0.003;
+ m = (t+b)/2;
+ l = width * 0.2;
+ w = width * 0.8 / (right - left);
+ x1 = l;
+ print("\"%s\" ljust at %g,%g\n", s->name, width * 0.1, m);
+ while(n = e->next, n != nil && n->t < right){
+ x2 = (n->t - left) * w + l;
+ if(n->line)
+ print("line from %g,%g to %g,%g dashed\n", x2, 0.0, x2, nsig*rheight);
+ switch(e->val){
+ case VZ:
+ print("line from %g,%g to %g,%g\n", x1, m, x2, m);
+ break;
+ case VL:
+ print("line from %g,%g to %g,%g\n", x1, b, x2-tw, b);
+ print("line from %g,%g to %g,%g\n", x2-tw, b, x2, m);
+ break;
+ case VH:
+ print("line from %g,%g to %g,%g\n", x1, t, x2-tw, t);
+ print("line from %g,%g to %g,%g\n", x2-tw, t, x2, m);
+ break;
+ case VMULT:
+ print("\"%s\" at %g,%g\n", e->data, (x1+x2)/2, m);
+ if(0){
+ case VX:
+ slantfill(x1, x2-tw, t, b, tw);
+ }
+ print("line from %g,%g to %g,%g\n", x1, b, x2-tw, b);
+ print("line from %g,%g to %g,%g\n", x2-tw, b, x2, m);
+ print("line from %g,%g to %g,%g\n", x1, t, x2-tw, t);
+ print("line from %g,%g to %g,%g\n", x2-tw, t, x2, m);
+ break;
+ default:
+ fprint(2, "unknown event type %d\n", e->val);
+ }
+ switch(n->val){
+ case VL:
+ print("line from %g,%g to %g,%g\n", x2, m, x2+tw, b);
+ break;
+ case VH:
+ print("line from %g,%g to %g,%g\n", x2, m, x2+tw, t);
+ break;
+ case VMULT:
+ case VX:
+ print("line from %g,%g to %g,%g\n", x2, m, x2+tw, b);
+ print("line from %g,%g to %g,%g\n", x2, m, x2+tw, t);
+ break;
+ }
+ e = e->next;
+ if(e->val == VZ)
+ x1 = x2;
+ else
+ x1 = x2 + tw;
+ }
+ x2 = (right - left) * w + l;
+ switch(e->val){
+ case VZ:
+ print("line from %g,%g to %g,%g\n", x1, m, x2, m);
+ break;
+ case VL:
+ print("line from %g,%g to %g,%g\n", x1, b, x2, b);
+ break;
+ case VH:
+ print("line from %g,%g to %g,%g\n", x1, t, x2, t);
+ break;
+ case VMULT:
+ print("\"%s\" at %g,%g\n", e->data, (x1+x2)/2, m);
+ if(0){
+ case VX:
+ slantfill(x1, x2, t, b, tw);
+ }
+ print("line from %g,%g to %g,%g\n", x1, b, x2, b);
+ print("line from %g,%g to %g,%g\n", x1, t, x2, t);
+ break;
+ default:
+ fprint(2, "unknown event type %d\n", e->val);
+ }
+}
+
+static void
+parseopts(char *l)
+{
+ char *f[3];
+ int rc;
+
+ rc = tokenize(l, f, nelem(f));
+ if(rc != 3){
+ error(lineno, ".TPS wrong syntax");
+ return;
+ }
+ width = strtod(f[1], 0);
+ rheight = strtod(f[2], 0);
+}
+
+static void
+cleansym(void)
+{
+ Symbol **p, *s, *sn;
+ Signal *si, *sin;
+ Event *e, *en;
+
+ for(p = stab; p < stab + nelem(stab); p++)
+ for(s = *p, *p = nil; s != nil; s = sn){
+
+ free(s->name);
+ sn = s->next;
+ free(s);
+ }
+ memset(stab, 0, sizeof(stab));
+
+ for(si = sigfirst; si != nil; si = sin){
+ for(e = si->ev; e != nil; e = en){
+ en = e->next;
+ free(e);
+ }
+ free(si->name);
+ sin = si->next;
+ free(si);
+ }
+ siglast = &sigfirst;
+}
+
+static void
+diagram(char *l)
+{
+ Symbol *s;
+ Signal *si;
+ int t;
+ double top;
+
+ lastc = 10;
+ mint = 0;
+ maxt = 0;
+ nsig = 0;
+ parseopts(l);
+
+ for(;;){
+ switch(t = next()){
+ case SYM:
+ s = getsym(sname);
+ if(peek() == '=')
+ assign(s);
+ else
+ signal(s->name);
+ break;
+ case CMD:
+ if(strcmp(sname, "TPE") == 0)
+ goto end;
+ error(lineno, "unknown command %s", sname);
+ break;
+ default:
+ error(lineno, "unexpected %T", t);
+ }
+ }
+end:
+
+ print(".PS %g %g\n", width, rheight * nsig);
+ left = mint;
+ right = maxt;
+ top = rheight * nsig;
+ for(si = sigfirst; si != nil; si = si->next, top -= rheight)
+ sigout(si, top);
+ print(".PE\n");
+
+ cleansym();
+}
+
+static void
+run(char *f)
+{
+ char *l;
+
+ if(f == nil)
+ bp = Bfdopen(0, OREAD);
+ else
+ bp = Bopen(f, OREAD);
+ if(bp == nil)
+ sysfatal("open: %r");
+ Blethal(bp, nil);
+ lineno = 1;
+
+ for(;;){
+ l = Brdstr(bp, '\n', 1);
+ if(l == nil)
+ break;
+ lineno++;
+ if(strncmp(l, ".TPS", 4) == 0 && (!l[4] || isspace(l[4])))
+ diagram(l);
+ else
+ print("%s\n", l);
+ free(l);
+ }
+ Bterm(bp);
+}
+
+static void
+usage(void)
+{
+ fprint(2, "usage: %s [ files ]\n", argv0);
+ exits("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+ int i;
+
+ fmtinstall('T', Tfmt);
+
+ ARGBEGIN {
+ default: usage();
+ } ARGEND;
+
+ if(argc == 0)
+ run(nil);
+ else
+ for(i = 0; i < argc; i++)
+ run(argv[i]);
+
+ exits(nil);
+}