shithub: aplenty

ref: ccddc176a6d18d2d965334f731a2c838d1f39e59
dir: /aplenty.c/

View raw version
#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;
	}

	wait();
	postnote(PNGROUP, getpid(), "exit");

	exits(errmsg(err));
}