X-Git-Url: https://jasonwoof.com/gitweb/?a=blobdiff_plain;f=st.c;h=124c04702bd754533702c17f7c4ba003fa0f8463;hb=aa35bbd7a16c6c210a7574a8c45bbe939d5b2922;hp=b37069c6934e69b3007a350b7d77f8147002ab3a;hpb=8f11e1cd034ff28ca47bb4955505db7fa8016ba8;p=st.git diff --git a/st.c b/st.c index b37069c..124c047 100644 --- a/st.c +++ b/st.c @@ -70,6 +70,8 @@ char *argv0; #define LEN(a) (sizeof(a) / sizeof(a)[0]) #define DEFAULT(a, b) (a) = (a) ? (a) : (b) #define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b)) +#define ISCONTROLC0(c) (BETWEEN((uchar) (c), 0, 0x1f)) +#define ISCONTROLC1(c) (BETWEEN((uchar) (c), 0x80, 0x9f)) #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x) #define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).fg || (a).bg != (b).bg) #define IS_SET(flag) ((term.mode & (flag)) != 0) @@ -149,7 +151,7 @@ enum charset { enum escape_state { ESC_START = 1, ESC_CSI = 2, - ESC_STR = 4, /* DSC, OSC, PM, APC */ + ESC_STR = 4, /* DCS, OSC, PM, APC */ ESC_ALTCHARSET = 8, ESC_STR_END = 16, /* a final string was encountered */ ESC_TEST = 32, /* Enter in test mode */ @@ -390,6 +392,7 @@ static void tsetdirtattr(int); static void tsetmode(bool, bool, int *, int); static void tfulldirt(void); static void techo(char *, int); +static bool tcontrolcode(uchar ); static int32_t tdefcolor(int *, int *, int); static void tselcs(void); static void tdeftran(char); @@ -399,6 +402,7 @@ static void ttyread(void); static void ttyresize(void); static void ttysend(char *, size_t); static void ttywrite(const char *, size_t); +static inline bool iscontrol(char); static void xdraws(char *, Glyph, int, int, int, int); static void xhints(void); @@ -452,7 +456,7 @@ static char utf8encodebyte(long, size_t); static size_t utf8len(char *); static size_t utf8validate(long *, size_t); -static ssize_t xwrite(int, char *, size_t); +static ssize_t xwrite(int, const char *, size_t); static void *xmalloc(size_t); static void *xrealloc(void *, size_t); static char *xstrdup(char *); @@ -518,7 +522,7 @@ static Fontcache frc[16]; static int frclen = 0; ssize_t -xwrite(int fd, char *s, size_t len) { +xwrite(int fd, const char *s, size_t len) { size_t aux = len; while(len > 0) { @@ -992,7 +996,7 @@ selnotify(XEvent *e) { } /* - * As seen in selcopy: + * As seen in getsel: * Line endings are inconsistent in the terminal and GUI world * copy and pasting. When receiving some selection data, * replace all '\n' with '\r'. @@ -1228,6 +1232,7 @@ ttynew(void) { opt_io, strerror(errno)); } } + break; } } @@ -1270,7 +1275,7 @@ ttyread(void) { void ttywrite(const char *s, size_t n) { - if(write(cmdfd, s, n) == -1) + if(xwrite(cmdfd, s, n) == -1) die("write error on tty: %s\n", strerror(errno)); } @@ -1673,6 +1678,7 @@ tdefcolor(int *attr, int *npar, int l) { default: fprintf(stderr, "erresc(38): gfx attr %d unknown\n", attr[*npar]); + break; } return idx; @@ -1781,7 +1787,6 @@ tsetmode(bool priv, bool set, int *args, int narg) { for(lim = args + narg; args < lim; ++args) { if(priv) { switch(*args) { - break; case 1: /* DECCKM -- Cursor key */ MODBIT(term.mode, set, MODE_APPCURSOR); break; @@ -2135,6 +2140,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]); @@ -2170,7 +2176,7 @@ strhandle(void) { case 'k': /* old title set compatibility */ xsettitle(strescseq.args[0]); return; - case 'P': /* DSC -- Device Control String */ + case 'P': /* DCS -- Device Control String */ case '_': /* APC -- Application Program Command */ case '^': /* PM -- Privacy Message */ return; @@ -2294,13 +2300,22 @@ tputtab(int n) { tmoveto(x, term.c.y); } +static inline bool +iscontrol(char c) { + return ISCONTROLC0(c) || ISCONTROLC1(c); +} + void techo(char *buf, int len) { for(; len > 0; buf++, len--) { char c = *buf; - if(c < 0x20 || c == 0177) { /* 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); } @@ -2339,57 +2354,135 @@ tselcs(void) { ATTR_GFX); } +bool +tcontrolcode(uchar ascii) { + static char question[UTF_SIZ] = "?"; + + switch(ascii) { + case '\t': /* HT */ + tputtab(1); + break; + case '\b': /* BS */ + tmoveto(term.c.x-1, term.c.y); + break; + case '\r': /* CR */ + tmoveto(0, term.c.y); + break; + case '\f': /* LF */ + case '\v': /* VT */ + case '\n': /* LF */ + /* go to first col if the mode is set */ + tnewline(IS_SET(MODE_CRLF)); + break; + case '\a': /* BEL */ + 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_CSI|ESC_ALTCHARSET|ESC_TEST); + term.esc |= ESC_START; + return 1; + case '\016': /* SO */ + term.charset = 0; + tselcs(); + break; + case '\017': /* SI */ + term.charset = 1; + tselcs(); + break; + case '\032': /* SUB */ + tsetchar(question, &term.c.attr, term.c.x, term.c.y); + case '\030': /* CAN */ + csireset(); + break; + case '\005': /* ENQ (IGNORED) */ + case '\000': /* NUL (IGNORED) */ + case '\021': /* XON (IGNORED) */ + case '\023': /* XOFF (IGNORED) */ + case 0177: /* DEL (IGNORED) */ + 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; + default: + return 0; + } + term.esc &= ~(ESC_STR_END|ESC_STR); + return 1; +} + void tputc(char *c, int len) { - uchar ascii = *c; - bool control = ascii < '\x20' || ascii == 0177; + uchar ascii; + bool control; long unicodep; int width; 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(width == 1 && + (ascii == '\a' || ascii == 030 || + ascii == 032 || ascii == 033 || + ISCONTROLC1(ascii))) { + 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; } /* @@ -2398,51 +2491,8 @@ tputc(char *c, int len) { * they must not cause conflicts with sequences. */ if(control) { - switch(ascii) { - case '\t': /* HT */ - tputtab(1); - return; - case '\b': /* BS */ - tmoveto(term.c.x-1, term.c.y); + if (tcontrolcode(ascii)) return; - case '\r': /* CR */ - tmoveto(0, term.c.y); - return; - case '\f': /* LF */ - case '\v': /* VT */ - case '\n': /* LF */ - /* go to first col if the mode is set */ - tnewline(IS_SET(MODE_CRLF)); - return; - case '\a': /* BEL */ - if(!(xw.state & WIN_FOCUSED)) - xseturgency(1); - if (bellvolume) - XBell(xw.dpy, bellvolume); - return; - case '\033': /* ESC */ - csireset(); - term.esc = ESC_START; - return; - case '\016': /* SO */ - term.charset = 0; - tselcs(); - return; - case '\017': /* SI */ - term.charset = 1; - tselcs(); - return; - case '\032': /* SUB */ - case '\030': /* CAN */ - csireset(); - return; - case '\005': /* ENQ (IGNORED) */ - case '\000': /* NUL (IGNORED) */ - case '\021': /* XON (IGNORED) */ - case '\023': /* XOFF (IGNORED) */ - case 0177: /* DEL (IGNORED) */ - return; - } } else if(term.esc & ESC_START) { if(term.esc & ESC_CSI) { csiescseq.buf[csiescseq.len++] = ascii; @@ -2453,10 +2503,6 @@ tputc(char *c, int len) { csiparse(); csihandle(); } - } else if(term.esc & ESC_STR_END) { - term.esc = 0; - if(ascii == '\\') - strhandle(); } else if(term.esc & ESC_ALTCHARSET) { tdeftran(ascii); tselcs(); @@ -2546,13 +2592,16 @@ tputc(char *c, int len) { tcursor(CURSOR_LOAD); term.esc = 0; break; - case '\\': /* ST -- Stop */ + case '\\': /* ST -- String Terminator */ + if(term.esc & ESC_STR_END) + strhandle(); term.esc = 0; break; default: fprintf(stderr, "erresc: unknown sequence ESC 0x%02X '%c'\n", (uchar) ascii, isprint(ascii)? ascii:'.'); term.esc = 0; + break; } } /*