ref: 28539de81a1a7beb10e8281a9825b9c39e8482cf
dir: /engine.c/
#include <u.h> #include <libc.h> #include <bio.h> #include <draw.h> #include "spread.h" char Einitengine[] = "initengine: %r"; int p[3]; char *preamble; Width *firstwidth = nil; Width *lastwidth = nil; typedef struct Strchan Strchan; struct Strchan { /* data */ QLock l; Rendez full; Rendez empty; char *s; Response r; /* want */ QLock w; int want; }; Strchan* mkchan(void) { Strchan *c; c = mallocz(sizeof(Strchan), 1); c->full.l = &c->l; c->empty.l = &c->l; return c; } void request(Strchan *c, int want) { qlock(&c->w); c->want = want; qunlock(&c->w); } void send(Strchan *c, char *s, int error) { qlock(&c->w); if (!c->want) { qunlock(&c->w); return; } c->want = 0; qunlock(&c->w); qlock(&c->l); while (c->r.msg) { rsleep(&c->full); } c->r.msg = strdup(s); c->r.error = error; rwakeup(&c->empty); qunlock(&c->l); } Response recv(Strchan *c) { Response r; qlock(&c->l); while (!c->r.msg) { rsleep(&c->empty); } r = c->r; c->r.msg = nil; c->r.error = 0; rwakeup(&c->full); qunlock(&c->l); return r; } void freeresponse(Response *r) { if (r->msg) free(r->msg); r->msg = nil; r->error = 0; } Strchan *outchan; enum { L_INVALID = 0, L_FUNC = FUNCTION, L_STRING = STRING, }; int spawnerrrdr(void) { Biobuf *bin; char *s; switch (rfork(RFPROC|RFMEM|RFFDG)) { case -1: sysfatal(Einitengine); case 0: /* child */ break; default: /* parent */ return 1; } bin = Bfdopen(p[2], OREAD); if (!bin) sysfatal(Einitengine); while (s = Brdstr(bin, '\n', 1)) { if (debug) fprint(2, "←hoc: × %s\n", s); send(outchan, s, 1); free(s); } Bterm(bin); exits(nil); } int inithoc(void) { int in[2]; int out[2]; int err[2]; Biobuf *bin; char *s; if (pipe(in) < 0) sysfatal(Einitengine); if (pipe(out) < 0) sysfatal(Einitengine); if (pipe(err) < 0) sysfatal(Einitengine); switch (fork()) { case -1: /* error */ sysfatal(Einitengine); break; case 0: /* child hoc */ dup(out[1], 1); dup(in[0], 0); dup(err[1], 2); close(out[1]); close(out[0]); close(in[0]); close(in[1]); close(err[1]); close(err[0]); execl("/bin/hoc", "hoc", nil); sysfatal(Einitengine); break; default: /* parent */ close(out[1]); close(in[0]); close(out[1]); p[0] = in[1]; p[1] = out[0]; p[2] = err[0]; break; } outchan = mkchan(); switch (rfork(RFPROC|RFMEM|RFFDG)) { case -1: /* error */ sysfatal(Einitengine); case 0: /* child reader */ break; default: /* parent */ return spawnerrrdr(); } bin = Bfdopen(p[1], OREAD); if (!bin) sysfatal(Einitengine); while (s = Brdstr(bin, '\n', 1)) { if (debug) fprint(2, "←hoc: ← %s\n", s); send(outchan, s, 0); free(s); } Bterm(bin); exits(nil); } static void hocwrite(char *s, Response *o) { request(outchan, !!o); fprint(p[0], "%s", s); if (debug) fprint(2, "→hoc: %s", s); if (o) { *o = recv(outchan); } } static int linetype(char *s) { char *c; int xalpha, xnum, falpha, fnum, found; xalpha = 1; xnum = 0; falpha = 0; fnum = 0; found = L_INVALID; while ((c = s++) && *c) { if (*c != 0 && found) return found; switch (*c) { case 0: /* end of line */ return L_INVALID; case ';': /* string line */ if (!falpha || !fnum) return L_INVALID; found = L_STRING; continue; case '=': /* hoc function line */ if (!falpha || !fnum) return L_INVALID; found = L_FUNC; continue; } if (*c >= '0' && *c <= '9') { if (!xnum || !falpha) return L_INVALID; fnum = 1; xalpha = L_INVALID; } if ((*c >= 'a' && *c <= 'z') || (*c >= 'A' && *c <= 'Z') ) { if (!xalpha) return L_INVALID; falpha = 1; xnum = 1; } } return found; } void interactivehoc() { Response o; char *s; Biobuf *bin; bin = Bfdopen(0, OREAD); while (s = Brdstr(bin, '\n', 0)) { if (s[0] == '\n') continue; hocwrite(s, &o); free(s); if (o.msg) print("%s %s\n", o.error ? "×" : "→", o.msg); } } static void calcwidth(Cell *c) { Width *w; int tw; char *s; if (!font) return; if (!(c->buffered || c->value)) return; s = c->buffered ? c->buffered : c->value; if (!firstwidth) { firstwidth = mallocz(sizeof(Width), 1); lastwidth = firstwidth; firstwidth->column = c->p.x; firstwidth->width = stringwidth(font, s); return; } for (w = firstwidth; w; w = w->next) { if (w->column != c->p.x) continue; w->column = c->p.x; tw = stringwidth(font, s); if (tw > w->width) w->width = tw; return; } lastwidth->next = mallocz(sizeof(Width), 1); lastwidth = lastwidth->next; lastwidth->column = c->p.x; tw = stringwidth(font, s); if (tw > lastwidth->width) lastwidth->width = tw; } static void resetwidths(void) { Width *w, *ow; for (w = firstwidth; w;) { ow = w->next; free(w); w = ow; } firstwidth = lastwidth = nil; } int getwidth(int c) { Width *w; for (w = firstwidth; w; w = w->next) if (w->column == c) return w->width; return -1; } char Hfunc[] = "func %s() { return %s }\n"; char Hstring[] = "func %s() { print \"%s\" }\n"; static void sendctohoc(Cell *c, void*) { char *h; char *buf; Response r; switch (c->type) { case FUNCTION: h = Hfunc; break; case STRING: calcwidth(c); return; default: sysfatal("code error"); } buf = smprint(h, ptoa(c->p), c->procvalue); hocwrite(buf, nil); free(buf); r = getvalue(c->p); if (r.msg) { c->buffered = strdup(r.msg); } else { if (c->buffered) free(c->buffered); c->buffered = nil; } if (!r.error) calcwidth(c); freeresponse(&r); } static void eaddcell(char *s, int type) { P p; char *a; int align; switch (type) { case L_FUNC: a = strchr(s, '='); break; case L_STRING: a = strchr(s, ';'); break; default: sysfatal("bad type: %d not in [1,2]", type); } *a = 0; a++; align = Aleft; switch (*s) { case '<': align = Aleft; s++; break; case '>': align = Aright; s++; break; case '|': align = Acenter; s++; break; case '.': align = Adot; s++; break; } p = atop(s); addcell(p, a, type, align); } static void appendpreamble(char *s) { char *t; if (!preamble) { preamble = strdup(s); return; } t = preamble; preamble = smprint("%s%s", preamble, s); free(t); } int loadfile(char *file) { Biobuf *bin; char *s; int type; bin = Bopen(file, OREAD); if (!bin) { werrstr("open: %r"); return 0; } while (s = Brdstr(bin, '\n', 0)) { if (strcmp("%%%\n", s) == 0) { free(s); break; } appendpreamble(s); hocwrite(s, nil); free(s); } while (s = Brdstr(bin, '\n', 1)) { type = linetype(s); switch(type) { case L_INVALID: if (debug) { fprint(2, "!hoc: %s\n", s); } break; case L_FUNC: case L_STRING: eaddcell(s, type); break; } free(s); } Bterm(bin); sortcells(); resetwidths(); foreachcell(sendctohoc, nil); return 1; } static void unbuffer(Cell *c, void*) { if (c->buffered) free(c->buffered); c->buffered = nil; } int updatecells() { if (!sortcells()) return 0; resetwidths(); foreachcell(unbuffer, nil); foreachcell(sendctohoc, nil); return 1; } static char alignchar(int align) { switch (align) { default: case Aleft: return '<'; case Aright: return '>'; case Acenter: return '|'; case Adot: return '.'; } } static void writecell(Cell *c, void *aux) { Biobuf *b = (Biobuf*)aux; switch (c->type) { case FUNCTION: Bprint(b, "%c%s=%s\n", alignchar(c->align), ptoa(c->p), c->value); break; case STRING: if (!c->value || !c->value[0]) break; Bprint(b, "%c%s;%s\n", alignchar(c->align), ptoa(c->p), c->value); break; } } int writefile(char *file) { Biobuf *bout; int fd; bout = Bopen(file, OWRITE); if (!bout) { fd = create(file, OWRITE, 0666); if (fd < 0) { werrstr("unable to create file: %r"); return 0; } bout = Bfdopen(fd, OWRITE); if (!bout) { werrstr("error: %r"); return 0; } } if (preamble) Bprint(bout, "%s", preamble); Bprint(bout, "%%%%%%\n"); foreachcell(writecell, bout); Bterm(bout); return 1; } Response getvalue(P cell) { Cell *c; char *s; Response o; c = getcell(cell); if (c && c->type == STRING) { o.msg = strdup(c->value); o.error = 0; return o; } if (c && c->buffered) { o.msg = strdup(c->buffered); o.error = 0; return o; } o.msg = nil; s = smprint("%s()\n", ptoa(cell)); hocwrite(s, &o); free(s); return o; }