]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/timepic.c
grep: error if sbrk fails
[plan9front.git] / sys / src / cmd / timepic.c
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <ctype.h>
5
6 typedef struct Symbol Symbol;
7 typedef struct Event Event;
8 typedef struct Signal Signal;
9
10 struct Symbol {
11         char *name;
12         int t;
13         double e;
14         Symbol *next;
15 };
16 struct Event {
17         double t;
18         int val;
19         Event *next;
20         char *data;
21         int line;
22 };
23 struct Signal {
24         char *name;
25         Event *ev;
26         Signal *next;
27 };
28
29 int lineno, lastc, peeked;
30 char sname[512];
31 double sval;
32 double width, rheight;
33 double mint, maxt;
34 double left, right;
35 int nsig;
36 Biobuf *bp;
37 Symbol *stab[256];
38 Signal *sigfirst, **siglast = &sigfirst;
39
40 enum {
41         SYMFREE,
42         SYMNUM,
43 };
44
45 enum {
46         VZ,
47         VX,
48         VL,
49         VH,
50         VMULT,
51 };
52
53 enum {
54         CMD = -1,
55         SYM = -2,
56         NUM = -3,
57         EOF = -4,
58         STR = -5,
59 };
60
61 static int
62 Tfmt(Fmt *f)
63 {
64         int n;
65         
66         n = va_arg(f->args, int);
67         if(n >= 0 && n < 0x80 && isprint(n))
68                 return fmtprint(f, "'%c'", n);
69         else
70                 switch(n){
71                 case CMD: return fmtprint(f, ".%s", sname);
72                 case SYM: return fmtprint(f, "'%s'", sname);
73                 case NUM: return fmtprint(f, "%g", sval);
74                 case EOF: return fmtprint(f, "EOF");
75                 case STR: return fmtprint(f, "%#q", sname);
76                 default: return fmtprint(f, "%d", n);
77                 }
78 }
79
80 static void *
81 emalloc(int n)
82 {
83         void *v;
84         
85         v = malloc(n);
86         if(v == nil)
87                 sysfatal("malloc: %r");
88         memset(v, 0, n);
89         setmalloctag(v, getcallerpc(&n));
90         return v;
91 }
92
93 static void
94 error(int l, char *fmt, ...)
95 {
96         va_list va;
97         char *s;
98         
99         va_start(va, fmt);
100         s = vsmprint(fmt, va);
101         fprint(2, "%d %s\n", l, s);
102         free(s);
103         va_end(va);
104 }
105
106 static int
107 hash(char *s)
108 {
109         int c;
110
111         for(c = 0; *s != 0; s++)
112                 c += *s;
113         return c;
114 }
115
116 static Symbol *
117 getsym(char *st)
118 {
119         Symbol *s, **p;
120         
121         for(p = &stab[hash(st)%nelem(stab)]; s = *p, s != nil; p = &s->next){
122                 if(strcmp(s->name, st) == 0)
123                         return s;
124         }
125         s = emalloc(sizeof(Symbol));
126         s->name = strdup(st);
127         *p = s;
128         return s;
129 }
130
131 static int
132 issym(int c)
133 {
134         return isalnum(c) || c == '_' || c >= 0x80;
135 }
136
137 static int
138 numfsm(int c, int *st)
139 {
140         enum {
141                 PREDOT,
142                 POSTDOT,
143                 ESIGN,
144                 EDIG
145         };
146         
147         switch(*st){
148         case PREDOT:
149                 if(c == '.')
150                         *st = POSTDOT;
151                 if(c == 'e')
152                         *st = ESIGN;
153                 return isdigit(c) || c == '.' || c == 'e';
154         case POSTDOT:
155                 if(c == 'e')
156                         *st = ESIGN;
157                 return isdigit(c) || c == 'e';
158         case ESIGN:
159                 *st = EDIG;
160                 return isdigit(c) || c == '+' || c == '-';
161         case EDIG:
162                 return isdigit(c);
163         }
164         return 0;
165 }
166
167 static int
168 lex(void)
169 {
170         int c;
171         char *p;
172         int st;
173
174         do{
175                 c = Bgetc(bp);
176                 if(c < 0)
177                         return EOF;
178                 if(!isspace(c))
179                         break;
180                 if(c == '\n')
181                         lineno++;
182                 lastc = c;
183         }while(1);
184         
185         if(lastc == 10 && c == '.'){
186                 for(p = sname; c = Bgetc(bp), issym(c); )
187                         if(p < sname + sizeof(sname) - 1)
188                                 *p++ = c;
189                 Bungetc(bp);
190                 *p = 0;
191                 return CMD;
192         }
193         if(isdigit(c) || c == '.'){
194                 st = 0;
195                 for(p = sname; numfsm(c, &st); c = Bgetc(bp))
196                         if(p < sname + sizeof(sname) - 1)
197                                 *p++ = c;
198                 Bungetc(bp);
199                 *p = 0;
200                 sval = strtol(sname, &p, 0);
201                 if(*p != 0)
202                         sval = strtod(sname, &p);
203                 if(*p != 0)
204                         error(lineno, "invalid number %s", sname);
205                 return NUM;
206         }
207         if(issym(c)){
208                 for(p = sname; issym(c); c = Bgetc(bp))
209                         if(p < sname + sizeof(sname) - 1)
210                                 *p++ = c;
211                 Bungetc(bp);
212                 *p = 0;
213                 return SYM;
214         }
215         if(c == '\''){
216                 for(p = sname; c = Bgetc(bp), c != '\'' || Bgetc(bp) == '\''; )
217                         if(p < sname + sizeof(sname) - 1)
218                                 *p++ = c;
219                 Bungetc(bp);
220                 *p = 0;
221                 return STR;
222         }
223         return c;
224 }
225
226 static int
227 next(void)
228 {
229         int rc;
230
231         if(peeked != 0){
232                 rc = peeked;
233                 peeked = 0;
234                 return rc;
235         }
236         return lex();
237 }
238
239 static int
240 peek(void)
241 {
242         if(peeked == 0)
243                 peeked = lex();
244         return peeked;
245 }
246
247 static void
248 expect(int n)
249 {
250         int s;
251
252         if((s = peek()) != n)
253                 error(lineno, "expected %T, got %T", n, s);
254         else
255                 next();
256 }
257
258 static double expr(void);
259
260 static double
261 factor(void)
262 {
263         int t;
264         double g;
265         Symbol *s;
266
267         switch(t = next()){
268         case NUM:
269                 return sval;
270         case SYM:
271                 s = getsym(sname);
272                 if(s->t != SYMNUM)
273                         error(lineno, "not a number: %s", s->name);
274                 return s->e;
275         case '(':
276                 g = expr();
277                 expect(')');
278                 return g;
279         default:
280                 error(lineno, "factor: unexpected %T", t);
281                 return 0;
282         }
283 }
284
285 static double
286 term(void)
287 {
288         double g;
289
290         g = factor();
291         while(peek() == '*' || peek() == '/'){
292                 switch(next()){
293                 case '*': g *= factor(); break;
294                 case '/': g /= factor(); break;
295                 }
296         }
297         return g;
298 }
299
300 static double
301 expr(void)
302 {
303         int s;
304         double g;
305         
306         s = 1;
307         if(peek() == '+' || peek() == '-')
308                 s = next() == '-' ? -1 : 1;
309         g = s * term();
310         while(peek() == '+' || peek() == '-'){
311                 s = next() == '-' ? -1 : 1;
312                 g += s * term();
313         }
314         return g;
315 }
316
317 static void
318 assign(Symbol *s)
319 {
320         next();
321         if(s->t != SYMFREE){
322                 error(lineno, "symbol already exists: %s", s->name);
323                 return;
324         }
325         s->t = SYMNUM;
326         s->e = expr();
327         expect(';');
328 }
329
330 static int
331 parseval(Event *e)
332 {
333         int t;
334         
335         switch(t = next()){
336         case SYM:
337                 if(strcmp(sname, "x") == 0)
338                         return VX;
339                 if(strcmp(sname, "z") == 0)
340                         return VZ;
341                 e->data = strdup(sname);
342                 return VMULT;
343         case NUM:
344                 if(sval == 0)
345                         return VL;
346                 if(sval == 1)
347                         return VH;
348                 e->data = smprint("%g", sval);
349                 return VMULT;
350         case STR:
351                 e->data = strdup(sname);
352                 return VMULT;
353         default:
354                 error(lineno, "unexpected %T", t);
355                 return VZ;
356         }
357 }
358
359 static void
360 append(double off, Event ***ep, Event *e)
361 {
362         Event *f;
363         
364         for(; e != nil; e = e->next){
365                 f = emalloc(sizeof(Event));
366                 f->t = e->t + off;
367                 f->val = e->val;
368                 f->line = e->line;
369                 if(e->data != nil)
370                         f->data = strdup(e->data);
371                 **ep = f;
372                 *ep = &f->next;
373         }
374 }
375
376 static void
377 freeev(Event *e)
378 {
379         Event *en;
380         
381         for(; e != nil; e = en){
382                 en = e->next;
383                 free(e->data);
384                 free(e);
385         }
386 }
387
388 static Event *
389 events(double *off, int ronly)
390 {
391         int rela;
392         Event *e, *ev, **ep;
393         int i, n;
394         double f;
395         Symbol *sy;
396         double len;
397         int line;
398         
399         rela = 0;
400         line = 0;
401         ev = nil;
402         ep = &ev;
403         for(;;)
404                 switch(peek()){
405                 case '+':
406                         rela = 1;
407                         next();
408                         break;
409                 case '}':
410                 case ';':
411                         return ev;
412                 case ':':
413                         next();
414                         e = emalloc(sizeof(Event));
415                         e->t = *off;
416                         e->val = parseval(e);
417                         e->line = line;
418                         line = 0;
419                         *ep = e;
420                         ep = &e->next;
421                         break;
422                 case '|':
423                         line = 1;
424                         next();
425                         break;
426                 default:
427                         f = expr();
428                         if(peek() == '{'){
429                                 next();
430                                 if(f < 0 || isNaN(f) || f >= 0x7fffffff){
431                                         error(lineno, "invalid repeat count");
432                                         f = 0;
433                                 }
434                                 len = 0;
435                                 e = events(&len, 1);
436                                 expect('}');
437                                 n = f + 0.5;
438                                 for(i = 0; i < n; i++){
439                                         append(*off, &ep, e);
440                                         *off += len;
441                                 }
442                                 if(*off > maxt) maxt = *off;
443                                 freeev(e);
444                                 break;
445                         }
446                         if(ronly && !rela){
447                                 error(lineno, "only relative addressing allowed");
448                                 rela = 1;
449                         }
450                         if(peek() == SYM){
451                                 next();
452                                 sy = getsym(sname);
453                                 if(sy->t == SYMFREE)
454                                         error(lineno, "undefined %s", sy->name);
455                                 else
456                                         f *= sy->e;
457                         }
458                         if(rela)
459                                 *off += f;
460                         else
461                                 *off = f;
462                         if(peek() == '['){
463                                 next();
464                                 sy = getsym(sname);
465                                 if(sy->t != SYMFREE && sy->t != SYMNUM)
466                                         error(lineno, "already defined %s", sy->name);
467                                 else{
468                                         sy->t = SYMNUM;
469                                         sy->e = *off;
470                                 }
471                                 expect(']');
472                         }
473                         if(*off < mint) mint = *off;
474                         if(*off > maxt) maxt = *off;
475                         rela = 0;
476                         break;
477                 }
478         
479 }
480
481 static void
482 signal(char *st)
483 {
484         Signal *s;
485         double off;
486
487         s = emalloc(sizeof(Signal));
488         s->name = strdup(st);
489         s->ev = events(&off, 0);
490         expect(';');
491         *siglast = s;
492         siglast = &s->next;
493         nsig++;
494 }
495
496 static void
497 slantfill(double x1, double x2, double t, double b, double tw)
498 {
499         double x;
500         double sw = 0.05;
501         double soff = 0.05;
502
503         for(x = x1; x < x2 - sw; x += soff)
504                 print("line from %g,%g to %g,%g\n", x, t, x+sw, b);
505         if(x < x2)
506                 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));
507 }
508
509 static void
510 sigout(Signal *s, double top)
511 {
512         Event *e, *n;
513         double l, w, t, b, x1, x2, tw, m;
514         
515         for(e = s->ev; e != nil && e->next != nil && e->next->t < left; e = e->next)
516                 ;
517         if(e == nil)
518                 return;
519         b = top - rheight * 0.75;
520         t = top - rheight * 0.25;
521         tw = width * 0.003;
522         m = (t+b)/2;
523         l = width * 0.2;
524         w = width * 0.8 / (right - left);
525         x1 = l;
526         print("\"%s\" ljust at %g,%g\n", s->name, width * 0.1, m);
527         while(n = e->next, n != nil && n->t < right){
528                 x2 = (n->t - left) * w + l;
529                 if(n->line)
530                         print("line from %g,%g to %g,%g dashed\n", x2, 0.0, x2, nsig*rheight);
531                 switch(e->val){
532                 case VZ:
533                         print("line from %g,%g to %g,%g\n", x1, m, x2, m);
534                         break;
535                 case VL:
536                         print("line from %g,%g to %g,%g\n", x1, b, x2-tw, b);
537                         print("line from %g,%g to %g,%g\n", x2-tw, b, x2, m);
538                         break;
539                 case VH:
540                         print("line from %g,%g to %g,%g\n", x1, t, x2-tw, t);
541                         print("line from %g,%g to %g,%g\n", x2-tw, t, x2, m);
542                         break;
543                 case VMULT:
544                         print("\"%s\" at %g,%g\n", e->data, (x1+x2)/2, m);
545                         if(0){
546                 case VX:
547                                 slantfill(x1, x2-tw, t, b, tw);
548                         }
549                         print("line from %g,%g to %g,%g\n", x1, b, x2-tw, b);
550                         print("line from %g,%g to %g,%g\n", x2-tw, b, x2, m);
551                         print("line from %g,%g to %g,%g\n", x1, t, x2-tw, t);
552                         print("line from %g,%g to %g,%g\n", x2-tw, t, x2, m);
553                         break;
554                 default:
555                         fprint(2, "unknown event type %d\n", e->val);
556                 }
557                 switch(n->val){
558                 case VL:
559                         print("line from %g,%g to %g,%g\n", x2, m, x2+tw, b);
560                         break;
561                 case VH:
562                         print("line from %g,%g to %g,%g\n", x2, m, x2+tw, t);
563                         break;
564                 case VMULT:
565                 case VX:
566                         print("line from %g,%g to %g,%g\n", x2, m, x2+tw, b);
567                         print("line from %g,%g to %g,%g\n", x2, m, x2+tw, t);
568                         break;
569                 }
570                 e = e->next;
571                 if(e->val == VZ)
572                         x1 = x2;
573                 else
574                         x1 = x2 + tw;
575         }
576         x2 = (right - left) * w + l;
577         switch(e->val){
578         case VZ:
579                 print("line from %g,%g to %g,%g\n", x1, m, x2, m);
580                 break;
581         case VL:
582                 print("line from %g,%g to %g,%g\n", x1, b, x2, b);
583                 break;
584         case VH:
585                 print("line from %g,%g to %g,%g\n", x1, t, x2, t);
586                 break;
587         case VMULT:
588                 print("\"%s\" at %g,%g\n", e->data, (x1+x2)/2, m);
589                 if(0){
590         case VX:
591                         slantfill(x1, x2, t, b, tw);
592                 }
593                 print("line from %g,%g to %g,%g\n", x1, b, x2, b);
594                 print("line from %g,%g to %g,%g\n", x1, t, x2, t);
595                 break;
596         default:
597                 fprint(2, "unknown event type %d\n", e->val);
598         }
599 }
600
601 static void
602 parseopts(char *l)
603 {
604         char *f[3];
605         int rc;
606
607         rc = tokenize(l, f, nelem(f));
608         if(rc != 3){
609                 error(lineno, ".TPS wrong syntax");
610                 return;
611         }
612         width = strtod(f[1], 0);
613         rheight = strtod(f[2], 0);
614 }
615
616 static void
617 cleansym(void)
618 {
619         Symbol **p, *s, *sn;
620         Signal *si, *sin;
621         Event *e, *en;
622         
623         for(p = stab; p < stab + nelem(stab); p++)
624                 for(s = *p, *p = nil; s != nil; s = sn){
625
626                         free(s->name);
627                         sn = s->next;
628                         free(s);
629                 }
630         memset(stab, 0, sizeof(stab));
631         
632         for(si = sigfirst; si != nil; si = sin){
633                 for(e = si->ev; e != nil; e = en){
634                         en = e->next;
635                         free(e);
636                 }
637                 free(si->name);
638                 sin = si->next;
639                 free(si);
640         }
641         siglast = &sigfirst;
642 }
643
644 static void
645 diagram(char *l)
646 {
647         Symbol *s;
648         Signal *si;
649         int t;
650         double top;
651         
652         lastc = 10;
653         mint = 0;
654         maxt = 0;
655         nsig = 0;
656         parseopts(l);
657
658         for(;;){
659                 switch(t = next()){
660                 case SYM:
661                         s = getsym(sname);
662                         if(peek() == '=')
663                                 assign(s);
664                         else
665                                 signal(s->name);
666                         break;
667                 case STR:
668                         signal(sname);
669                         break;
670                 case CMD:
671                         if(strcmp(sname, "TPE") == 0)
672                                 goto end;
673                         error(lineno, "unknown command %s", sname);
674                         break;
675                 default:
676                         error(lineno, "unexpected %T", t);
677                 }
678         }
679 end:
680
681         print(".PS %g %g\n", width, rheight * nsig);
682         left = mint;
683         right = maxt;
684         top = rheight * nsig;
685         for(si = sigfirst; si != nil; si = si->next, top -= rheight)
686                 sigout(si, top);
687         print(".PE\n");
688         
689         cleansym();
690 }
691
692 static void
693 run(char *f)
694 {
695         char *l;
696
697         if(f == nil)
698                 bp = Bfdopen(0, OREAD);
699         else
700                 bp = Bopen(f, OREAD);
701         if(bp == nil)
702                 sysfatal("open: %r");
703         Blethal(bp, nil);
704         lineno = 1;
705         
706         for(;;){
707                 l = Brdstr(bp, '\n', 1);
708                 if(l == nil)
709                         break;
710                 lineno++;
711                 if(strncmp(l, ".TPS", 4) == 0 && (!l[4] || isspace(l[4])))
712                         diagram(l);
713                 else
714                         print("%s\n", l);
715                 free(l);
716         }
717         Bterm(bp);
718 }
719
720 static void
721 usage(void)
722 {
723         fprint(2, "usage: %s [ files ]\n", argv0);
724         exits("usage");
725 }
726
727 void
728 main(int argc, char **argv)
729 {
730         int i;
731
732         fmtinstall('T', Tfmt);
733         quotefmtinstall();
734
735         ARGBEGIN {
736         default: usage();
737         } ARGEND;
738         
739         if(argc == 0)
740                 run(nil);
741         else
742                 for(i = 0; i < argc; i++)
743                         run(argv[i]);
744         
745         exits(nil);
746 }