ref: 7e3533cf6041b0c950a0161c11f391617f1aa21d
dir: /engine.c/
#include <u.h>
#include <libc.h>
#include <bio.h>
#include "spread.h"
char Einitengine[] = "initengine: %r";
int p[3];
char *preamble;
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);
}
}
char Hfunc[] = "func %s() { return %s }\n";
char Hstring[] = "func %s() { print \"%s\" }\n";
static void
sendctohoc(Cell *c, void*)
{
char *h;
char *buf;
switch (c->type) {
case FUNCTION:
h = Hfunc;
break;
case STRING:
return;
break;
default:
sysfatal("code error");
}
buf = smprint(h, ptoa(c->p), c->value);
hocwrite(buf, nil);
free(buf);
}
static void
eaddcell(char *s, int type)
{
P p;
char *a;
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++;
p = atop(s);
addcell(p, a, type);
}
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();
foreachcell(sendctohoc, nil);
return 1;
}
int
updatecells()
{
if (!sortcells())
return 0;
foreachcell(sendctohoc, nil);
return 1;
}
static void
writecell(Cell *c, void *aux)
{
Biobuf *b = (Biobuf*)aux;
switch (c->type) {
case FUNCTION:
Bprint(b, "%s=%s\n", ptoa(c->p), c->value);
break;
case STRING:
if (!c->value || !c->value[0])
break;
Bprint(b, "%s;%s\n", 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;
}
o.msg = nil;
s = smprint("%s()\n", ptoa(cell));
hocwrite(s, &o);
free(s);
return o;
}