[dev] [st][PATCH] Cancel DCS with SUB, CAN, ESC or any CC1 code

From: Roberto E. Vargas Caballero <k0ga_AT_shike2.com>
Date: Sat, 26 Apr 2014 08:45:01 +0200

From http://www.vt100.net/docs/vt510-rm/chapter4:

        *The VT510 ignores all following characters until it receives a
         SUB, ST, or any other C1 control character.

So OSC, PM and APC sequence ends with a SUB (it cancels the sequence
and show a question mark as error), ST or any another C1 (8 bits)
code, or their C0 (7 bits) equivalent sequences (at this moment we
do not handle C1 codes, but we should). But it is also said that:

        Cancel CAN
        1/8 Immediately cancels an escape sequence, control sequence,
                or device control string in progress. In this case, the
                VT510 does not display any error character.

        Escape ESC
        1/11 Introduces an escape sequence. ESC also cancels any escape
                sequence, control sequence, or device control string in
                progress.
---
 st.c | 139 ++++++++++++++++++++++++++++++++++++++++++-------------------------
 1 file changed, 88 insertions(+), 51 deletions(-)
diff --git a/st.c b/st.c
index d2261e2..6cce36a 100644
--- a/st.c
+++ b/st.c
_AT_@ -399,6 +399,7 @@ static void ttyread(void);
 static void ttyresize(void);
 static void ttysend(char *, size_t);
 static void ttywrite(const char *, size_t);
+static bool iscontrol(int c);
 
 static void xdraws(char *, Glyph, int, int, int, int);
 static void xhints(void);
_AT_@ -2134,6 +2135,7 @@ strhandle(void) {
 	char *p = NULL;
 	int j, narg, par;
 
+	term.esc &= ~(ESC_STR_END|ESC_STR);
 	strparse();
 	narg = strescseq.narg;
 	par = atoi(strescseq.args[0]);
_AT_@ -2293,13 +2295,22 @@ tputtab(int n) {
 	tmoveto(x, term.c.y);
 }
 
+bool
+iscontrol(int c) {
+	return c < 0x20 || c == 0177 || BETWEEN(c, 0x80, 0x9f);
+}
+
 void
 techo(char *buf, int len) {
 	for(; len > 0; buf++, len--) {
 		char c = *buf;
 
-		if(BETWEEN(c, 0x00, 0x1f) || c == 0x7f) { /* control code */
-			if(c != '\n' && c != '\r' && c != '\t') {
+		if(iscontrol(c)) { /* control code */
+			if(c & 0x80) {
+				c &= 0x7f;
+				tputc("^", 1);
+				tputc("[", 1);
+			} else if(c != '\n' && c != '\r' && c != '\t') {
 				c ^= '\x40';
 				tputc("^", 1);
 			}
_AT_@ -2340,55 +2351,59 @@ tselcs(void) {
 
 void
 tputc(char *c, int len) {
-	uchar ascii = *c;
-	bool control = ascii < '\x20' || ascii == 0177;
+	uchar ascii;
+	bool control;
+	bool dcs = term.esc & ESC_STR;
 	long unicodep;
 	int width;
+	char s[UTF_SIZ] = "?";
 
 	if(len == 1) {
 		width = 1;
+		ascii = *c;
 	} else {
 		utf8decode(c, &unicodep, UTF_SIZ);
 		width = wcwidth(unicodep);
+		ascii = unicodep;
 	}
 
+	control = iscontrol(ascii) && width == 1;
 	if(IS_SET(MODE_PRINT))
 		tprinter(c, len);
 
 	/*
-	 * STR sequences must be checked before anything else
-	 * because it can use some control codes as part of the sequence.
+	 * STR sequence must be checked before anything else
+	 * because it uses all following characters until it
+	 * receives a ESC, a SUB, a ST or any other C1 control
+	 * character.
 	 */
-	if(term.esc & ESC_STR) {
-		switch(ascii) {
-		case '\033':
-			term.esc = ESC_START | ESC_STR_END;
-			break;
-		case '\a': /* backwards compatibility to xterm */
-			term.esc = 0;
-			strhandle();
-			break;
-		default:
-			if(strescseq.len + len < sizeof(strescseq.buf) - 1) {
-				memmove(&strescseq.buf[strescseq.len], c, len);
-				strescseq.len += len;
-			} else {
-			/*
-			 * Here is a bug in terminals. If the user never sends
-			 * some code to stop the str or esc command, then st
-			 * will stop responding. But this is better than
-			 * silently failing with unknown characters. At least
-			 * then users will report back.
-			 *
-			 * In the case users ever get fixed, here is the code:
-			 */
-			/*
-			 * term.esc = 0;
-			 * strhandle();
-			 */
-			}
+	if(dcs) {
+		if(width == 1 &&
+		   (ascii == '\a' || ascii == 030 ||
+		    ascii == 032  || ascii == 033 ||
+		    BETWEEN(ascii, 0x80, 0x9f))) {
+			term.esc &= ~ESC_STR;
+			term.esc |= ESC_STR_END;
+		} else if(strescseq.len + len < sizeof(strescseq.buf) - 1) {
+			memmove(&strescseq.buf[strescseq.len], c, len);
+			strescseq.len += len;
+			return;
+		} else {
+		/*
+		 * Here is a bug in terminals. If the user never sends
+		 * some code to stop the str or esc command, then st
+		 * will stop responding. But this is better than
+		 * silently failing with unknown characters. At least
+		 * then users will report back.
+		 *
+		 * In the case users ever get fixed, here is the code:
+		 */
+		/*
+		 * term.esc = 0;
+		 * strhandle();
+		 */
+			return;
 		}
-		return;
 	}
 
 	/*
_AT_@ -2400,48 +2415,70 @@ tputc(char *c, int len) {
 		switch(ascii) {
 		case '\t':   /* HT */
 			tputtab(1);
-			return;
+			break;
 		case '\b':   /* BS */
 			tmoveto(term.c.x-1, term.c.y);
-			return;
+			break;
 		case '\r':   /* CR */
 			tmoveto(0, term.c.y);
-			return;
+			break;
 		case '\f':   /* LF */
 		case '\v':   /* VT */
 		case '\n':   /* LF */
 			/* go to first col if the mode is set */
 			tnewline(IS_SET(MODE_CRLF));
-			return;
+			break;
 		case '\a':   /* BEL */
-			if(!(xw.state & WIN_FOCUSED))
-				xseturgency(1);
-			if (bellvolume)
-				XBell(xw.dpy, bellvolume);
-			return;
+			if(term.esc & ESC_STR_END) {
+				/* backwards compatibility to xterm */
+				strhandle();
+			} else {
+				if(!(xw.state & WIN_FOCUSED))
+					xseturgency(1);
+				if (bellvolume)
+					XBell(xw.dpy, bellvolume);
+			}
+			break;
 		case '\033': /* ESC */
 			csireset();
-			term.esc = ESC_START;
+			term.esc |= ESC_START;
 			return;
 		case '\016': /* SO */
 			term.charset = 0;
 			tselcs();
-			return;
+			break;
 		case '\017': /* SI */
 			term.charset = 1;
 			tselcs();
-			return;
+			break;
 		case '\032': /* SUB */
+			tsetchar(s, &term.c.attr, term.c.x, term.c.y);
 		case '\030': /* CAN */
 			csireset();
-			return;
+			break;
 		case '\005': /* ENQ (IGNORED) */
 		case '\000': /* NUL (IGNORED) */
 		case '\021': /* XON (IGNORED) */
 		case '\023': /* XOFF (IGNORED) */
 		case 0177:   /* DEL (IGNORED) */
-			return;
+		case 0x84:   /* TODO: IND */
+		case 0x85:   /* TODO: NEL */
+		case 0x88:   /* TODO: HTS */
+		case 0x8d:   /* TODO: RI */
+		case 0x8e:   /* TODO: SS2 */
+		case 0x8f:   /* TODO: SS3 */
+		case 0x90:   /* TODO: DCS */
+		case 0x98:   /* TODO: SOS */
+		case 0x9a:   /* TODO: DECID */
+		case 0x9b:   /* TODO: CSI */
+		case 0x9c:   /* TODO: ST */
+		case 0x9d:   /* TODO: OSC */
+		case 0x9e:   /* TODO: PM */
+		case 0x9f:   /* TODO: APC */
+			break;
 		}
+		term.esc &= ~(ESC_STR_END|ESC_STR);
+		return;
 	} else if(term.esc & ESC_START) {
 		if(term.esc & ESC_CSI) {
 			csiescseq.buf[csiescseq.len++] = ascii;
_AT_@ -2458,12 +2495,12 @@ tputc(char *c, int len) {
 			term.esc = 0;
 		} else if(term.esc & ESC_TEST) {
 			if(ascii == '8') { /* DEC screen alignment test. */
-				char E[UTF_SIZ] = "E";
 				int x, y;
+				s[0] = 'E';
 
 				for(x = 0; x < term.col; ++x) {
 					for(y = 0; y < term.row; ++y)
-						tsetchar(E, &term.c.attr, x, y);
+						tsetchar(s, &term.c.attr, x, y);
 				}
 			}
 			term.esc = 0;
-- 
1.8.5.3
Received on Sat Apr 26 2014 - 08:45:01 CEST

This archive was generated by hypermail 2.3.0 : Sat Apr 26 2014 - 08:48:06 CEST