shithub: aplenty

Download patch

ref: 82e4bed4981039799aae9c21dcc8f07d3aa3dde5
parent: 666191d45413d79a7a462998ebfd300e12af6f13
author: B. Wilson <x@wilsonb.com>
date: Sat Jul 19 10:46:33 EDT 2025

Support multi-line execute and paste

--- a/aplenty.c
+++ b/aplenty.c
@@ -6,16 +6,28 @@
 #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;
-vlong caddr;
 int debug;
 
 void
-hevride(int in, int ev, int out, int addr){
+hride(int in, int ev, int out, int addr){
 	Biobuf *bev;
-	char *b, s[32], k;   /* event buffer, kind */
-	long t, n, o; /*       type, length, offset */
+	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);
@@ -24,65 +36,68 @@
 		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);
 
-		qlock(&lwin);
+		/* Usual prompt signals start of new I/O round */
+		if(k == 'p' &&  t == 1)
+			fndx = fndx == lndx ? fndx : NXT(fndx);
 
-		if(k == 'p'){
-			fprint(addr, "#%lld,$", caddr);
-			write(out, b, n);
-		}
+		/* Target address range */
+		fprint(addr, "#%lld", iaddr[fndx]);
+		if(k == 'o' && t == 14)
+			fprint(addr, ".,.-+");
 
-		if(k == 'o'){
-			b = malloc(n);
-			pread(in, b, n, o);
-			if(t == 14)
-				fprint(addr, "#%lld,$", caddr);
-			else
-				fprint(addr, "$");
-			write(out, b, n);
-			free(b);
-		}
+		/* Write response data */
+		write(out, d, n);
 
-		write(addr, "$-/^/", 5);
-		pread(addr, s, 12, 0);
-		caddr = atol(strtok(s, " "));
+		/* 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
-hevroot(int wid, int in, int out){
-	Biobuf *bin, *bxdata, *btag;
-	int fdctl, fdevent, fdaddr, fdbody;
+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 */
-	Rune *r;          /*       runes */
-	long c;
-	char *ln, s[256];
+	char *b;          /*       buffer */
+	char *ln, *e, s[256];
 	int i;
 
-	/* setup files */
+	/* Setup files */
 	snprintf(s, sizeof(s), "/mnt/acme/%i", wid);
 	chdir(s);
 	fdctl   = open("ctl", OWRITE);
-	fdevent = open("event", OWRITE);
 	fdaddr  = open("addr", ORDWR);
-	fdbody  = open("body", OWRITE);
+	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);
 
-	/* initial prompt not setup by ride */
-	fprint(fdctl, "name %s/-aplenty\n", rdir);
-	fprint(fdbody, "      ");
-	caddr = 0;
+	/* 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 */
-	r = malloc((2+4*12 + 256)*sizeof(*r));
+	/* 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, ' '));
@@ -91,49 +106,57 @@
 		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);
 
-		qlock(&lwin);
-
-		/* trailing event text may be elided; read canonical source */
-		if(!strchr("Dd", t)){
-			if(strchr("ILX", t)){
-				fprint(fdaddr, "#%ld,#%ld", n, m);
-				bxdata = Bopen("xdata", OREAD);
-				for(i = 0, c = Bgetrune(bxdata); c >= 0; i++, c = Bgetrune(bxdata))
-					r[i] = c;
-				Bterm(bxdata);
-			} else if(strchr("ilx", t)) {
-				btag = Bopen("tag", OREAD);
-				for(i = 0, c = Bgetrune(btag); c >= 0; i++, c = Bgetrune(btag))
-					r[i] = c;
-				Bterm(btag);
-			}
+		/* 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';
 		}
 
-		/* Ride-triggered edits */
-		if(strchr("EF", o))
-			goto end;
+		/* Execute target lines */
+		if(b){
+			for(i = lndx, ln = b; e = strchr(ln, '\n'); i = NXT(i), ln += l){
+				l = 1 + e-ln;
 
-		/* XXX: Only execute first line if multiline input */
-		if(t == 'I' && n >= caddr || t == 'X'){
-			fprint(fdaddr, "#%lld,#%ld", caddr, m);
-			bxdata  = Bopen("xdata", OREAD);
-			ln = Brdline(bxdata, '\n');
-			if(ln)
-				write(out, ln, Blinelen(bxdata));
-			Bterm(bxdata);
+				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))
-		if((t == 'D') + m < caddr)
-			caddr += t == 'D' ? n-m : m-n;
+			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);
 
-		end:
 		qunlock(&lwin);
 	}
 }
@@ -188,7 +211,7 @@
 	/* new acme window */
 	if((wctl = open("/mnt/acme/new/ctl", OREAD)) < 0)
 		exits(errmsg(err));
-	readn(wctl, b, 12);
+	pread(wctl, b, 12, 0);
 	wid = atoi(strtok(b, " "));
 
 	JSONfmtinstall();
@@ -195,7 +218,7 @@
 	rfork(RFNOTEG);
 
 	switch(rfork(RFPROC|RFMEM)){
-	case -1: err = "unable to start ride message handler"; break;
+	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 */
@@ -206,13 +229,13 @@
 		snprintf(p, sizeof(p), "/mnt/acme/%i/addr", wid);
 		waddr = open(p, ORDWR);
 	
-		hevride(rin, revent, wout, waddr);
+		hride(rin, revent, wout, waddr);
 		exits(nil);
 	default: break;
 	}
 
 	switch(rfork(RFPROC|RFMEM)){
-	case -1: err = "unable to start root window event handler"; break;
+	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);
@@ -219,10 +242,12 @@
 		snprintf(p, sizeof(p), "/mnt/ride/%i/text", rid);
 		rout = open(p, OWRITE);
 
-		hevroot(wid, win, rout);
+		hsession(wid, win, rout);
 		exits(nil);
 	default: break;
 	}
+
+	print("%d\n", wid);
 
 	wait();
 	postnote(PNGROUP, getpid(), "exit");
--