summary refs log tree commit diff
diff options
context:
space:
mode:
authorJune McEnroe <june@causal.agency>2019-08-14 14:31:34 -0400
committerJune McEnroe <june@causal.agency>2019-08-14 14:31:34 -0400
commit68845f0403fce8f45a88915e5a12c752adcc0258 (patch)
treedb983bc42732b1092a55eb58e7bb751439c72a4e
parentAllow :<=>? in CSI params (diff)
downloadstream-68845f0403fce8f45a88915e5a12c752adcc0258.tar.gz
stream-68845f0403fce8f45a88915e5a12c752adcc0258.zip
Handle state transitions just much better
-rw-r--r--Makefile2
-rw-r--r--term.c287
-rw-r--r--term.h1
3 files changed, 169 insertions, 121 deletions
diff --git a/Makefile b/Makefile
index 0cae642..d280a66 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
 CHROOT_USER = stream
 CHROOT_GROUP = ${CHROOT_USER}
 
-CFLAGS += -Wall -Wextra -Wpedantic
+CFLAGS += -Wall -Wextra -Wpedantic -Wno-gnu-case-range
 LDFLAGS = -static
 LDLIBS = -lcurses -lutil
 
diff --git a/term.c b/term.c
index 2a8960a..9910889 100644
--- a/term.c
+++ b/term.c
@@ -83,59 +83,23 @@ static void scrollDown(struct Term *term, uint top, uint n) {
 	);
 }
 
-typedef void Action(struct Term *, wchar_t);
-
 #define ACTION(name) \
-	static void name(struct Term *t, wchar_t _ch __attribute__((unused)))
+	static void name(struct Term *t, wchar_t ch __attribute__((unused)))
 
-enum {
-	Def,
-	Esc,
-	G0,
-	CSI,
-	OSC,
-	OSCEsc,
-};
+ACTION(nop) { (void)t; }
 
-ACTION(nop)    { (void)t; }
-ACTION(esc)    { t->state = Esc; }
-ACTION(g0)     { t->state = G0; }
-ACTION(osc)    { t->state = OSC; }
-ACTION(oscEsc) { t->state = OSCEsc; }
 ACTION(csi) {
-	t->state = CSI;
 	memset(&t->param, 0, sizeof(t->param));
 }
-
-static void csiParam(struct Term *t, wchar_t ch) {
-	switch (ch) {
-		case L'0': case L'1': case L'2': case L'3': case L'4':
-		case L'5': case L'6': case L'7': case L'8': case L'9': {
-			t->param.s[t->param.i] *= 10;
-			t->param.s[t->param.i] += ch - L'0';
-			if (!t->param.n) t->param.n++;
-		}
-		break; case L':': // ignore
-		break; case L';': {
-			if (t->param.n == ParamCap) break;
-			t->param.n++;
-			t->param.i++;
-		}
-		break; case L'<': t->param.lt = true;
-		break; case L'=': t->param.eq = true;
-		break; case L'>': t->param.gt = true;
-		break; case L'?': t->param.qm = true;
-		break; default: {
-			unhandled("CSI %lc", ch);
-			return;
-		}
-	}
-	t->state = CSI;
+ACTION(csiSep) {
+	if (t->param.n == ParamCap) return;
+	t->param.n++;
+	t->param.i++;
 }
-
-static void escUnhandled(struct Term *t, wchar_t ch) {
-	(void)t;
-	unhandled("ESC %lc", ch);
+ACTION(csiDigit) {
+	t->param.s[t->param.i] *= 10;
+	t->param.s[t->param.i] += ch - L'0';
+	if (!t->param.n) t->param.n++;
 }
 
 #define Y t->y
@@ -152,8 +116,8 @@ ACTION(cuu) { Y -= MIN(P(0, 1), Y); }
 ACTION(cud) { Y  = MIN(Y + P(0, 1), B); }
 ACTION(cuf) { X  = MIN(X + P(0, 1), R); }
 ACTION(cub) { X -= MIN(P(0, 1), X); }
-ACTION(cnl) { X = 0; cud(t, 0); }
-ACTION(cpl) { X = 0; cuu(t, 0); }
+ACTION(cnl) { X = 0; cud(t, ch); }
+ACTION(cpl) { X = 0; cuu(t, ch); }
 ACTION(cha) { X = MIN(P(0, 1) - 1, R); }
 ACTION(vpa) { Y = MIN(P(0, 1) - 1, B); }
 ACTION(cup) {
@@ -232,29 +196,46 @@ enum {
 	DECTCEM = 25,
 };
 
-static void mode(struct Term *t, wchar_t ch) {
+static enum Mode paramMode(struct Term *t) {
+	enum Mode mode = 0;
+	for (uint i = 0; i < t->param.n; ++i) {
+		switch (t->param.s[i]) {
+			break; case IRM: mode |= Insert;
+			break; default: unhandled("SM/RM %u", t->param.s[i]);
+		}
+	}
+	return mode;
+}
+
+static enum Mode paramDECMode(struct Term *t) {
 	enum Mode mode = 0;
 	for (uint i = 0; i < t->param.n; ++i) {
-		if (t->param.qm) {
-			switch (t->param.s[i]) {
-				break; case 1: // DECCKM
-				break; case DECAWM: mode |= Wrap;
-				break; case 12: // "Start Blinking Cursor"
-				break; case DECTCEM: mode |= Cursor;
-				break; default: {
-					if (t->param.s[i] < 1000) {
-						unhandled("DECSET/DECRST %u", t->param.s[i]);
-					}
+		switch (t->param.s[i]) {
+			break; case 1: // DECCKM
+			break; case DECAWM: mode |= Wrap;
+			break; case 12: // "Start Blinking Cursor"
+			break; case DECTCEM: mode |= Cursor;
+			break; default: {
+				if (t->param.s[i] < 1000) {
+					unhandled("DECSET/DECRST %u", t->param.s[i]);
 				}
 			}
-		} else {
-			switch (t->param.s[i]) {
-				break; case IRM: mode |= Insert;
-				break; default: unhandled("SM/RM %u", t->param.s[i]);
-			}
 		}
 	}
-	t->mode = (ch == L'h' ? t->mode | mode : t->mode & ~mode);
+	return mode;
+}
+
+ACTION(sm) {
+	t->mode |= paramMode(t);
+}
+ACTION(decset) {
+	t->mode |= paramDECMode(t);
+}
+ACTION(rm) {
+	t->mode &= ~paramMode(t);
+}
+ACTION(decrst) {
+	t->mode &= ~paramDECMode(t);
 }
 
 enum {
@@ -339,7 +320,7 @@ ACTION(sgr) {
 	}
 }
 
-static void add(struct Term *t, wchar_t ch) {
+ACTION(add) {
 	int width = wcwidth(ch);
 	if (width < 0) {
 		unhandled("\\u%02X", ch);
@@ -369,75 +350,143 @@ static void add(struct Term *t, wchar_t ch) {
 	}
 }
 
-#define S(s) break; case (s): switch (ch)
-#define A(c, a) break; case (c): (a)(term, ch)
-#define D(a) break; default: (a)(term, ch)
+enum {
+	Data,
+	Esc,
+	G0,
+	CSI,
+	CSILt,
+	CSIEq,
+	CSIGt,
+	CSIQm,
+	CSIInter,
+	OSC,
+	OSCEsc,
+};
+
+ACTION(escDefault) {
+	(void)t;
+	unhandled("ESC %lc", ch);
+}
+
+ACTION(csiInter) {
+	switch (t->state) {
+		break; case CSI: unhandled("CSI %lc ...", ch);
+		break; case CSILt: unhandled("CSI < %lc ...", ch);
+		break; case CSIEq: unhandled("CSI = %lc ...", ch);
+		break; case CSIGt: unhandled("CSI > %lc ...", ch);
+		break; case CSIQm: unhandled("CSI ? %lc ...", ch);
+	}
+}
+
+ACTION(csiFinal) {
+	switch (t->state) {
+		break; case CSI: unhandled("CSI %lc", ch);
+		break; case CSILt: unhandled("CSI < %lc", ch);
+		break; case CSIEq: unhandled("CSI = %lc", ch);
+		break; case CSIGt: unhandled("CSI > %lc", ch);
+		break; case CSIQm: unhandled("CSI ? %lc", ch);
+		break; case CSIInter: unhandled("CSI ... %lc", ch);
+	}
+}
+
+#define S(s) break; case s: switch (ch)
+#define A(c, a, s) break; case c: a(term, ch); term->state = s
+#define D(a, s) break; default: a(term, ch); term->state = s
 void termUpdate(struct Term *term, wchar_t ch) {
-	uint state = term->state;
-	term->state = Def;
-	switch (state) {
+	switch (term->state) {
 		default: abort();
 
-		S(Def) {
-			A(BEL, nop);
-			A(BS,  bs);
-			A(HT,  ht);
-			A(NL,  nl);
-			A(CR,  cr);
-			A(ESC, esc);
-			D(add);
+		S(Data) {
+			A(BEL, nop, Data);
+			A(BS,  bs,  Data);
+			A(HT,  ht,  Data);
+			A(NL,  nl,  Data);
+			A(CR,  cr,  Data);
+			A(ESC, nop, Esc);
+			D(add, Data);
 		}
 
 		S(Esc) {
-			A('(', g0);
-			A('7', decsc);
-			A('8', decrc);
-			A('=', nop);
-			A('>', nop);
-			A('M', ri);
-			A('[', csi);
-			A(']', osc);
-			D(escUnhandled);
+			A('(', nop, G0);
+			A('7', decsc, Data);
+			A('8', decrc, Data);
+			A('=', nop, Data);
+			A('>', nop, Data);
+			A('M', ri,  Data);
+			A('[', csi, CSI);
+			A(']', nop, OSC);
+			D(escDefault, Data);
 		}
 		S(G0) {
-			D(nop);
+			D(nop, Data);
 		}
 
 		S(CSI) {
-			A('@', ich);
-			A('A', cuu);
-			A('B', cud);
-			A('C', cuf);
-			A('D', cub);
-			A('E', cnl);
-			A('F', cpl);
-			A('G', cha);
-			A('H', cup);
-			A('J', ed);
-			A('K', el);
-			A('L', il);
-			A('M', dl);
-			A('P', dch);
-			A('S', su);
-			A('T', sd);
-			A('X', ech);
-			A('d', vpa);
-			A('h', mode);
-			A('l', mode);
-			A('m', sgr);
-			A('r', decstbm);
-			A('t', nop);
-			D(csiParam);
+			A(' ' ... '/', csiInter, CSIInter);
+			A('0' ... '9', csiDigit, CSI);
+			A(':', nop, CSI);
+			A(';', csiSep, CSI);
+			A('<', nop, CSILt);
+			A('=', nop, CSIEq);
+			A('>', nop, CSIGt);
+			A('?', nop, CSIQm);
+			A('@', ich, Data);
+			A('A', cuu, Data);
+			A('B', cud, Data);
+			A('C', cuf, Data);
+			A('D', cub, Data);
+			A('E', cnl, Data);
+			A('F', cpl, Data);
+			A('G', cha, Data);
+			A('H', cup, Data);
+			A('J', ed,  Data);
+			A('K', el,  Data);
+			A('L', il,  Data);
+			A('M', dl,  Data);
+			A('P', dch, Data);
+			A('S', su,  Data);
+			A('T', sd,  Data);
+			A('X', ech, Data);
+			A('d', vpa, Data);
+			A('h', sm,  Data);
+			A('l', rm,  Data);
+			A('m', sgr, Data);
+			A('r', decstbm, Data);
+			A('t', nop, Data);
+			D(csiFinal, Data);
+		}
+
+		S(CSILt ... CSIGt) {
+			A(' ' ... '/', csiInter, CSIInter);
+			A('0' ... '9', csiDigit, term->state);
+			A(':', nop, term->state);
+			A(';', csiSep, term->state);
+			D(csiFinal, Data);
+		}
+
+		S(CSIQm) {
+			A(' ' ... '/', csiInter, CSIInter);
+			A('0' ... '9', csiDigit, CSIQm);
+			A(':', nop, CSIQm);
+			A(';', csiSep, CSIQm);
+			A('h', decset, Data);
+			A('l', decrst, Data);
+			D(csiFinal, Data);
+		}
+
+		S(CSIInter) {
+			D(csiFinal, Data);
 		}
 
 		S(OSC) {
-			A(BEL, nop);
-			A(ESC, oscEsc);
-			D(osc);
+			A(BEL, nop, Data);
+			A(ESC, nop, OSCEsc);
+			D(nop, OSC);
 		}
 		S(OSCEsc) {
-			A('\\', nop);
-			D(osc);
+			A('\\', nop, Data);
+			D(nop, OSC);
 		}
 	}
 }
diff --git a/term.h b/term.h
index 19ebb95..f2df1fa 100644
--- a/term.h
+++ b/term.h
@@ -58,7 +58,6 @@ struct Term {
 	uint rows, cols;
 	uint state;
 	struct {
-		bool lt, eq, gt, qm;
 		uint s[ParamCap];
 		uint n, i;
 	} param;