aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto E. Vargas Caballero <k0ga@shike2.com>2014-04-26 01:34:46 +0200
committerRoberto E. Vargas Caballero <k0ga@shike2.com>2014-04-27 11:30:13 +0200
commitaa35bbd7a16c6c210a7574a8c45bbe939d5b2922 (patch)
tree12217256ce4bf8d2260f029599e74066df8620c5
parent704d12442e85ded011f71c95e90534ebacc81692 (diff)
downloadst-aa35bbd7a16c6c210a7574a8c45bbe939d5b2922.tar.gz
st-aa35bbd7a16c6c210a7574a8c45bbe939d5b2922.zip
Cancel DCS with SUB, CAN, ESC or any CC1 code
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.
-rw-r--r--st.c206
1 files changed, 127 insertions, 79 deletions
diff --git a/st.c b/st.c
index 49df792..124c047 100644
--- a/st.c
+++ b/st.c
@@ -70,6 +70,8 @@ char *argv0;
70#define LEN(a) (sizeof(a) / sizeof(a)[0]) 70#define LEN(a) (sizeof(a) / sizeof(a)[0])
71#define DEFAULT(a, b) (a) = (a) ? (a) : (b) 71#define DEFAULT(a, b) (a) = (a) ? (a) : (b)
72#define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b)) 72#define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b))
73#define ISCONTROLC0(c) (BETWEEN((uchar) (c), 0, 0x1f))
74#define ISCONTROLC1(c) (BETWEEN((uchar) (c), 0x80, 0x9f))
73#define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x) 75#define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x)
74#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).fg || (a).bg != (b).bg) 76#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).fg || (a).bg != (b).bg)
75#define IS_SET(flag) ((term.mode & (flag)) != 0) 77#define IS_SET(flag) ((term.mode & (flag)) != 0)
@@ -390,6 +392,7 @@ static void tsetdirtattr(int);
390static void tsetmode(bool, bool, int *, int); 392static void tsetmode(bool, bool, int *, int);
391static void tfulldirt(void); 393static void tfulldirt(void);
392static void techo(char *, int); 394static void techo(char *, int);
395static bool tcontrolcode(uchar );
393static int32_t tdefcolor(int *, int *, int); 396static int32_t tdefcolor(int *, int *, int);
394static void tselcs(void); 397static void tselcs(void);
395static void tdeftran(char); 398static void tdeftran(char);
@@ -399,6 +402,7 @@ static void ttyread(void);
399static void ttyresize(void); 402static void ttyresize(void);
400static void ttysend(char *, size_t); 403static void ttysend(char *, size_t);
401static void ttywrite(const char *, size_t); 404static void ttywrite(const char *, size_t);
405static inline bool iscontrol(char);
402 406
403static void xdraws(char *, Glyph, int, int, int, int); 407static void xdraws(char *, Glyph, int, int, int, int);
404static void xhints(void); 408static void xhints(void);
@@ -2136,6 +2140,7 @@ strhandle(void) {
2136 char *p = NULL; 2140 char *p = NULL;
2137 int j, narg, par; 2141 int j, narg, par;
2138 2142
2143 term.esc &= ~(ESC_STR_END|ESC_STR);
2139 strparse(); 2144 strparse();
2140 narg = strescseq.narg; 2145 narg = strescseq.narg;
2141 par = atoi(strescseq.args[0]); 2146 par = atoi(strescseq.args[0]);
@@ -2295,13 +2300,22 @@ tputtab(int n) {
2295 tmoveto(x, term.c.y); 2300 tmoveto(x, term.c.y);
2296} 2301}
2297 2302
2303static inline bool
2304iscontrol(char c) {
2305 return ISCONTROLC0(c) || ISCONTROLC1(c);
2306}
2307
2298void 2308void
2299techo(char *buf, int len) { 2309techo(char *buf, int len) {
2300 for(; len > 0; buf++, len--) { 2310 for(; len > 0; buf++, len--) {
2301 char c = *buf; 2311 char c = *buf;
2302 2312
2303 if(BETWEEN(c, 0x00, 0x1f) || c == 0x7f) { /* control code */ 2313 if(iscontrol(c)) { /* control code */
2304 if(c != '\n' && c != '\r' && c != '\t') { 2314 if(c & 0x80) {
2315 c &= 0x7f;
2316 tputc("^", 1);
2317 tputc("[", 1);
2318 } else if(c != '\n' && c != '\r' && c != '\t') {
2305 c ^= '\x40'; 2319 c ^= '\x40';
2306 tputc("^", 1); 2320 tputc("^", 1);
2307 } 2321 }
@@ -2340,58 +2354,135 @@ tselcs(void) {
2340 ATTR_GFX); 2354 ATTR_GFX);
2341} 2355}
2342 2356
2357bool
2358tcontrolcode(uchar ascii) {
2359 static char question[UTF_SIZ] = "?";
2360
2361 switch(ascii) {
2362 case '\t': /* HT */
2363 tputtab(1);
2364 break;
2365 case '\b': /* BS */
2366 tmoveto(term.c.x-1, term.c.y);
2367 break;
2368 case '\r': /* CR */
2369 tmoveto(0, term.c.y);
2370 break;
2371 case '\f': /* LF */
2372 case '\v': /* VT */
2373 case '\n': /* LF */
2374 /* go to first col if the mode is set */
2375 tnewline(IS_SET(MODE_CRLF));
2376 break;
2377 case '\a': /* BEL */
2378 if(term.esc & ESC_STR_END) {
2379 /* backwards compatibility to xterm */
2380 strhandle();
2381 } else {
2382 if(!(xw.state & WIN_FOCUSED))
2383 xseturgency(1);
2384 if (bellvolume)
2385 XBell(xw.dpy, bellvolume);
2386 }
2387 break;
2388 case '\033': /* ESC */
2389 csireset();
2390 term.esc &= ~(ESC_CSI|ESC_ALTCHARSET|ESC_TEST);
2391 term.esc |= ESC_START;
2392 return 1;
2393 case '\016': /* SO */
2394 term.charset = 0;
2395 tselcs();
2396 break;
2397 case '\017': /* SI */
2398 term.charset = 1;
2399 tselcs();
2400 break;
2401 case '\032': /* SUB */
2402 tsetchar(question, &term.c.attr, term.c.x, term.c.y);
2403 case '\030': /* CAN */
2404 csireset();
2405 break;
2406 case '\005': /* ENQ (IGNORED) */
2407 case '\000': /* NUL (IGNORED) */
2408 case '\021': /* XON (IGNORED) */
2409 case '\023': /* XOFF (IGNORED) */
2410 case 0177: /* DEL (IGNORED) */
2411 case 0x84: /* TODO: IND */
2412 case 0x85: /* TODO: NEL */
2413 case 0x88: /* TODO: HTS */
2414 case 0x8d: /* TODO: RI */
2415 case 0x8e: /* TODO: SS2 */
2416 case 0x8f: /* TODO: SS3 */
2417 case 0x90: /* TODO: DCS */
2418 case 0x98: /* TODO: SOS */
2419 case 0x9a: /* TODO: DECID */
2420 case 0x9b: /* TODO: CSI */
2421 case 0x9c: /* TODO: ST */
2422 case 0x9d: /* TODO: OSC */
2423 case 0x9e: /* TODO: PM */
2424 case 0x9f: /* TODO: APC */
2425 break;
2426 default:
2427 return 0;
2428 }
2429 term.esc &= ~(ESC_STR_END|ESC_STR);
2430 return 1;
2431}
2432
2343void 2433void
2344tputc(char *c, int len) { 2434tputc(char *c, int len) {
2345 uchar ascii = *c; 2435 uchar ascii;
2346 bool control = ascii < '\x20' || ascii == 0177; 2436 bool control;
2347 long unicodep; 2437 long unicodep;
2348 int width; 2438 int width;
2349 2439
2350 if(len == 1) { 2440 if(len == 1) {
2351 width = 1; 2441 width = 1;
2442 ascii = *c;
2352 } else { 2443 } else {
2353 utf8decode(c, &unicodep, UTF_SIZ); 2444 utf8decode(c, &unicodep, UTF_SIZ);
2354 width = wcwidth(unicodep); 2445 width = wcwidth(unicodep);
2446 ascii = unicodep;
2355 } 2447 }
2356 2448
2449 control = iscontrol(ascii) && width == 1;
2357 if(IS_SET(MODE_PRINT)) 2450 if(IS_SET(MODE_PRINT))
2358 tprinter(c, len); 2451 tprinter(c, len);
2359 2452
2360 /* 2453 /*
2361 * STR sequences must be checked before anything else 2454 * STR sequence must be checked before anything else
2362 * because it can use some control codes as part of the sequence. 2455 * because it uses all following characters until it
2456 * receives a ESC, a SUB, a ST or any other C1 control
2457 * character.
2363 */ 2458 */
2364 if(term.esc & ESC_STR) { 2459 if(term.esc & ESC_STR) {
2365 switch(ascii) { 2460 if(width == 1 &&
2366 case '\033': 2461 (ascii == '\a' || ascii == 030 ||
2367 term.esc = ESC_START | ESC_STR_END; 2462 ascii == 032 || ascii == 033 ||
2368 break; 2463 ISCONTROLC1(ascii))) {
2369 case '\a': /* backwards compatibility to xterm */ 2464 term.esc &= ~ESC_STR;
2370 term.esc = 0; 2465 term.esc |= ESC_STR_END;
2371 strhandle(); 2466 } else if(strescseq.len + len < sizeof(strescseq.buf) - 1) {
2372 break; 2467 memmove(&strescseq.buf[strescseq.len], c, len);
2373 default: 2468 strescseq.len += len;
2374 if(strescseq.len + len < sizeof(strescseq.buf) - 1) { 2469 return;
2375 memmove(&strescseq.buf[strescseq.len], c, len); 2470 } else {
2376 strescseq.len += len; 2471 /*
2377 } else { 2472 * Here is a bug in terminals. If the user never sends
2378 /* 2473 * some code to stop the str or esc command, then st
2379 * Here is a bug in terminals. If the user never sends 2474 * will stop responding. But this is better than
2380 * some code to stop the str or esc command, then st 2475 * silently failing with unknown characters. At least
2381 * will stop responding. But this is better than 2476 * then users will report back.
2382 * silently failing with unknown characters. At least 2477 *
2383 * then users will report back. 2478 * In the case users ever get fixed, here is the code:
2384 * 2479 */
2385 * In the case users ever get fixed, here is the code: 2480 /*
2386 */ 2481 * term.esc = 0;
2387 /* 2482 * strhandle();
2388 * term.esc = 0; 2483 */
2389 * strhandle(); 2484 return;
2390 */
2391 }
2392 break;
2393 } 2485 }
2394 return;
2395 } 2486 }
2396 2487
2397 /* 2488 /*
@@ -2400,51 +2491,8 @@ tputc(char *c, int len) {
2400 * they must not cause conflicts with sequences. 2491 * they must not cause conflicts with sequences.
2401 */ 2492 */
2402 if(control) { 2493 if(control) {
2403 switch(ascii) { 2494 if (tcontrolcode(ascii))
2404 case '\t': /* HT */
2405 tputtab(1);
2406 return; 2495 return;
2407 case '\b': /* BS */
2408 tmoveto(term.c.x-1, term.c.y);
2409 return;
2410 case '\r': /* CR */
2411 tmoveto(0, term.c.y);
2412 return;
2413 case '\f': /* LF */
2414 case '\v': /* VT */
2415 case '\n': /* LF */
2416 /* go to first col if the mode is set */
2417 tnewline(IS_SET(MODE_CRLF));
2418 return;
2419 case '\a': /* BEL */
2420 if(!(xw.state & WIN_FOCUSED))
2421 xseturgency(1);
2422 if (bellvolume)
2423 XBell(xw.dpy, bellvolume);
2424 return;
2425 case '\033': /* ESC */
2426 csireset();
2427 term.esc = ESC_START;
2428 return;
2429 case '\016': /* SO */
2430 term.charset = 0;
2431 tselcs();
2432 return;
2433 case '\017': /* SI */
2434 term.charset = 1;
2435 tselcs();
2436 return;
2437 case '\032': /* SUB */
2438 case '\030': /* CAN */
2439 csireset();
2440 return;
2441 case '\005': /* ENQ (IGNORED) */
2442 case '\000': /* NUL (IGNORED) */
2443 case '\021': /* XON (IGNORED) */
2444 case '\023': /* XOFF (IGNORED) */
2445 case 0177: /* DEL (IGNORED) */
2446 return;
2447 }
2448 } else if(term.esc & ESC_START) { 2496 } else if(term.esc & ESC_START) {
2449 if(term.esc & ESC_CSI) { 2497 if(term.esc & ESC_CSI) {
2450 csiescseq.buf[csiescseq.len++] = ascii; 2498 csiescseq.buf[csiescseq.len++] = ascii;