ref: 82e4bed4981039799aae9c21dcc8f07d3aa3dde5
dir: /aplenty.c/
#include <u.h>
#include <libc.h>
#include <stdio.h>
#include <bio.h>
#include <json.h>
#include <regexp.h>
QLock lwin;
vlong iaddr[256]; /* input line addresses */
int fndx, lndx; /* first, last indices */
#define NXT(i) ((i+1) % sizeof(iaddr))
char *rdir;
int debug;
void
hride(int in, int ev, int out, int addr){
Biobuf *bev;
char k, *b, *d; /* event kind, buffer, data */
long t, n, o; /* type, length, offset */
int i;
/* Session I/O is line-oriented and sequential. The event response chain
* to an execute request happens in the following order:
*
* 1. k == 'o' && t == 14: Input line echo;
* 2. k == 'p' && t != 1: Result start, if applicable;
* 3. k == 'o' && t != 14: Result output, if applicable;
* 4. k == 'p' && t == 1: Begin new input line.
*/
bev = Bfdopen(ev, OREAD);
while(b = Brdline(bev, '\n')){
Bseek(bev, Blinelen(bev), 1);
k = b[0]; b++;
t = atol(strtok(b, " \n"));
n = atol(strtok(nil, " \n"));
o = atol(b = strtok(nil, " \n"));
for(; *b != '\0'; b++); b++;
d = malloc(n);
switch(k){
case 'p': memmove(d, b, n); break; /* prompt */
case 'o': pread(in, d, n, o); break; /* output */
}
qlock(&lwin); /* window resources shared */
if(debug)
fprintf(stderr, "ride: %c %ld %ld %ld\n", k, t, n, o);
/* Usual prompt signals start of new I/O round */
if(k == 'p' && t == 1)
fndx = fndx == lndx ? fndx : NXT(fndx);
/* Target address range */
fprint(addr, "#%lld", iaddr[fndx]);
if(k == 'o' && t == 14)
fprint(addr, ".,.-+");
/* Write response data */
write(out, d, n);
/* Update input addresses */
o = iaddr[NXT(fndx)] - iaddr[fndx];
if(k == 'o') iaddr[fndx] += n = utfnlen(d, n);
for(i = NXT(fndx); i != NXT(lndx); i = NXT(i))
iaddr[i] += n - o;
free(d);
qunlock(&lwin);
}
}
void
hsession(int wid, int in, int out){
Biobuf *bin;
int fdctl, fdaddr, fddata, fdxdata, fdevent;
char o, t; /* event origin, type */
long n, m, f, l; /* addr n, addr m, flag, len */
char *b; /* buffer */
char *ln, *e, s[256];
int i;
/* Setup files */
snprintf(s, sizeof(s), "/mnt/acme/%i", wid);
chdir(s);
fdctl = open("ctl", OWRITE);
fdaddr = open("addr", ORDWR);
fddata = open("data", OWRITE);
fdxdata = open("xdata", OREAD);
fdevent = open("event", OWRITE);
bin = Bfdopen(in, OREAD);
rerrstr(s, sizeof(s));
if(s[0] != 0)
exits(s);
/* Initialize window */
fprint(fdctl, "name %s/-%s\n", rdir, argv0);
fprint(fddata, " "); /* first prompt not written by ride */
/* Event handle loop: cf acme(4):/event */
iaddr[0] = 0; fndx = lndx = 0;
while((o = Bgetc(bin)) != EOF){
t = Bgetc(bin);
n = atol(Brdline(bin, ' '));
m = atol(Brdline(bin, ' '));
f = atol(Brdline(bin, ' '));
l = atol(Brdline(bin, ' '));
for(i = 0; i < l+1; i++) /* trailing LF not counted by l */
Bgetrune(bin);
/* Ignore self-triggered edits */
if(strchr("F", o))
continue;
qlock(&lwin); /* window resources shared */
if(debug)
fprintf(stderr, "acme: %c%c%ld %ld %ld %ld\n", o, t, n , m, f, l);
/* Find executable input */
b = nil;
if(t == 'I' && n >= iaddr[lndx] || t == 'X'){
if(t == 'I') n = iaddr[lndx];
fprint(fdaddr, "#%ld,#%ld", n, m);
l = UTFmax*(m - n); /* n, m count runes */
b = malloc(l+1);
l = read(fdxdata, b, l);
b[l] = '\0';
}
/* Execute target lines */
if(b){
for(i = lndx, ln = b; e = strchr(ln, '\n'); i = NXT(i), ln += l){
l = 1 + e-ln;
fprint(fdaddr, "#%ld,#%ld-+", n, n);
write(out, ln, l);
pread(fdaddr, s, 24, 0);
n = atol(strtok(s, " "));
m = atol(strtok(nil, " "));
iaddr[i] = t == 'X' ? iaddr[lndx] : n;
n = m;
lndx = i;
if(NXT(i) == fndx) break; /* XXX: excess lines simply ignored */
}
free(b);
}
/* Update input addresses */
if(strchr("ID", t))
for(i = fndx; i != NXT(lndx); i = NXT(i))
if(n < iaddr[i])
iaddr[i] += t == 'D' ? n-m : m-n;
/* Let acme handle non-repl events */
if(f%2 == 0 && strchr("Lidlx", t))
fprint(fdevent, "%c%c%ld %ld\n", o, t, n, m);
qunlock(&lwin);
}
}
char*
errmsg(char *err){
if(err == nil){
err = malloc(ERRMAX);
rerrstr(err, ERRMAX);
}
fprintf(stderr, "%s\n", err);
return err;
}
void
usage(void){
fprintf(stderr, "Usage: %s [-d] addr\n", argv0);
}
void
main(int argc, char **argv){
char *err, *addr, b[256], p[256];
int rid, rctl, rin, rout, revent; /* ride */
int wid, wctl, win, wout, waddr; /* root window */
ARGBEGIN{
case 'd': debug++; break;
case 'h': usage(); exits(nil);
}ARGEND
if(argc == 0)
addr = getenv("rideaddr");
else
addr = argv[0];
if(addr == nil){
usage();
exits(errmsg("no ride address"));
}
err = nil;
/* new ride connection */
if((rctl = open("/mnt/ride/clone", ORDWR)) < 0)
exits(errmsg(err));
sprintf(b, "connect %s\n", addr);
write(rctl, b, strlen(b));
pread(rctl, b, sizeof(b), 0);
sprintf(p, "\n");
rid = atoi(strtok(b, p));
rdir = smprint("/mnt/ride/%d", rid);
/* new acme window */
if((wctl = open("/mnt/acme/new/ctl", OREAD)) < 0)
exits(errmsg(err));
pread(wctl, b, 12, 0);
wid = atoi(strtok(b, " "));
JSONfmtinstall();
rfork(RFNOTEG);
switch(rfork(RFPROC|RFMEM)){
case -1: err = "unable to start ride event handler"; break;
case 0:
snprintf(p, sizeof(p), "/mnt/ride/%i/text", rid);
rin = open(p, OREAD); /* establishes connection */
snprintf(p, sizeof(p), "/mnt/ride/%i/event", rid);
revent = open(p, OREAD);
snprintf(p, sizeof(p), "/mnt/acme/%i/data", wid);
wout = open(p, OWRITE);
snprintf(p, sizeof(p), "/mnt/acme/%i/addr", wid);
waddr = open(p, ORDWR);
hride(rin, revent, wout, waddr);
exits(nil);
default: break;
}
switch(rfork(RFPROC|RFMEM)){
case -1: err = "unable to start session window event handler"; break;
case 0:
snprintf(p, sizeof(p), "/mnt/acme/%i/event", wid);
win = open(p, OREAD);
snprintf(p, sizeof(p), "/mnt/ride/%i/text", rid);
rout = open(p, OWRITE);
hsession(wid, win, rout);
exits(nil);
default: break;
}
print("%d\n", wid);
wait();
postnote(PNGROUP, getpid(), "exit");
exits(errmsg(err));
}