Fix some terminal encoding issues
- fix `buf_put_key()` for control keys - add command line argument -mk=X for xterm modifyOtherKeys setting (0,1,2) - improve argument parsing to avoid seg fault on bogus arg spec - report command line argument errors in popup window - improve escape sequence handling in tty.c - support bogus iTerm2 encoding of option/command/control key combinations
This commit is contained in:
parent
9815df4c68
commit
7a94d82d6c
5 changed files with 67 additions and 53 deletions
1
buffer.c
1
buffer.c
|
|
@ -954,7 +954,6 @@ void eb_trace_bytes(const void *buf, int size, int state)
|
|||
endp = p0 + size;
|
||||
|
||||
#define MAX_TRACE_WIDTH 98
|
||||
// XXX: should encode into a local buffer by chunks
|
||||
for (p = p0; p0 < endp; p++) {
|
||||
while (p >= endp || *p < 32 || *p >= 127 || *p == '\\') {
|
||||
if (p0 >= endp)
|
||||
|
|
|
|||
41
qe.c
41
qe.c
|
|
@ -61,6 +61,7 @@ static int screen_height = 0;
|
|||
static int no_init_file;
|
||||
static int single_window;
|
||||
int force_tty;
|
||||
int tty_mk = 2;
|
||||
int disable_crc;
|
||||
#ifdef CONFIG_SESSION
|
||||
int use_session_file;
|
||||
|
|
@ -10374,19 +10375,19 @@ static void show_usage(void)
|
|||
|
||||
static int parse_command_line(int argc, char **argv)
|
||||
{
|
||||
int _optind;
|
||||
int optind_;
|
||||
|
||||
for (_optind = 1; _optind < argc;) {
|
||||
const char *arg, *r, *_optarg;
|
||||
for (optind_ = 1; optind_ < argc;) {
|
||||
const char *arg, *r, *optarg_;
|
||||
CmdLineOptionDef *p;
|
||||
bstr_t opt1;
|
||||
bstr_t opt2;
|
||||
|
||||
r = arg = argv[_optind];
|
||||
r = arg = argv[optind_];
|
||||
/* stop before first non option */
|
||||
if (r[0] != '-')
|
||||
break;
|
||||
_optind++;
|
||||
optind_++;
|
||||
|
||||
opt1.s = opt2.s = r + 1;
|
||||
if (r[1] == '-') {
|
||||
|
|
@ -10396,10 +10397,10 @@ static int parse_command_line(int argc, char **argv)
|
|||
break;
|
||||
}
|
||||
/* parse optional argument specified with opt=arg or opt:arg syntax */
|
||||
_optarg = NULL;
|
||||
optarg_ = NULL;
|
||||
while (*r) {
|
||||
if (*r == ':' || *r == '=') {
|
||||
_optarg = r + 1;
|
||||
optarg_ = r + 1;
|
||||
break;
|
||||
}
|
||||
r++;
|
||||
|
|
@ -10414,30 +10415,30 @@ static int parse_command_line(int argc, char **argv)
|
|||
bstr_t name = bstr_token(s, '|', &s);
|
||||
bstr_t argname = bstr_token(s, '|', &s);
|
||||
if (bstr_equal(opt1, shortname) || bstr_equal(opt2, name)) {
|
||||
if (argname.len && _optarg == NULL) {
|
||||
if (_optind >= argc) {
|
||||
put_status(NULL,
|
||||
"cmdline argument %.*s expected for --%.*s",
|
||||
argname.len, argname.s, name.len, name.s);
|
||||
if (p->need_arg && optarg_ == NULL) {
|
||||
if (optind_ >= argc) {
|
||||
put_error(NULL,
|
||||
"cmdline argument %.*s expected for --%.*s",
|
||||
argname.len, argname.s, name.len, name.s);
|
||||
goto next_cmd;
|
||||
}
|
||||
_optarg = argv[_optind++];
|
||||
optarg_ = argv[optind_++];
|
||||
}
|
||||
switch (p->type) {
|
||||
case CMD_LINE_TYPE_BOOL:
|
||||
*p->u.int_ptr = qe_strtobool(_optarg, 1);
|
||||
*p->u.int_ptr = qe_strtobool(optarg_, 1);
|
||||
break;
|
||||
case CMD_LINE_TYPE_INT:
|
||||
*p->u.int_ptr = strtol(_optarg, NULL, 0);
|
||||
*p->u.int_ptr = strtol(optarg_, NULL, 0);
|
||||
break;
|
||||
case CMD_LINE_TYPE_STRING:
|
||||
*p->u.string_ptr = _optarg;
|
||||
*p->u.string_ptr = optarg_;
|
||||
break;
|
||||
case CMD_LINE_TYPE_FVOID:
|
||||
p->u.func_noarg();
|
||||
break;
|
||||
case CMD_LINE_TYPE_FARG:
|
||||
p->u.func_arg(_optarg);
|
||||
p->u.func_arg(optarg_);
|
||||
break;
|
||||
case CMD_LINE_TYPE_NONE:
|
||||
case CMD_LINE_TYPE_NEXT:
|
||||
|
|
@ -10448,11 +10449,11 @@ static int parse_command_line(int argc, char **argv)
|
|||
p++;
|
||||
}
|
||||
}
|
||||
put_status(NULL, "unknown cmdline option '%s'", arg);
|
||||
put_error(NULL, "unknown cmdline option '%s'", arg);
|
||||
next_cmd: ;
|
||||
}
|
||||
|
||||
return _optind;
|
||||
return optind_;
|
||||
}
|
||||
|
||||
void do_add_resource_path(EditState *s, const char *path)
|
||||
|
|
@ -10541,6 +10542,8 @@ static CmdLineOptionDef cmd_options[] = {
|
|||
CMD_LINE_BOOL("", "free-all", &free_everything,
|
||||
"free all structures upon exit"),
|
||||
#endif
|
||||
CMD_LINE_INT("mk", "modify-other-keys", "VAL", &tty_mk,
|
||||
"set the modifyOtherKeys tty configuration (0,1,2)"),
|
||||
CMD_LINE_LINK()
|
||||
};
|
||||
|
||||
|
|
|
|||
16
qe.h
16
qe.h
|
|
@ -152,6 +152,7 @@ enum CmdLineOptionType {
|
|||
typedef struct CmdLineOptionDef {
|
||||
const char *desc;
|
||||
enum CmdLineOptionType type;
|
||||
BOOL need_arg;
|
||||
union {
|
||||
int *int_ptr;
|
||||
const char **string_ptr;
|
||||
|
|
@ -161,13 +162,13 @@ typedef struct CmdLineOptionDef {
|
|||
} u;
|
||||
} CmdLineOptionDef;
|
||||
|
||||
#define CMD_LINE_NONE() { NULL, CMD_LINE_TYPE_NONE, { NULL }}
|
||||
#define CMD_LINE_BOOL(s,n,p,h) { s "|" n "||" h, CMD_LINE_TYPE_BOOL, { .int_ptr = p }}
|
||||
#define CMD_LINE_INT(s,n,a,p,h) { s "|" n "|" a "|" h, CMD_LINE_TYPE_INT, { .int_ptr = p }}
|
||||
#define CMD_LINE_STRING(s,n,a,p,h) { s "|" n "|" a "|" h, CMD_LINE_TYPE_STRING, { .string_ptr = p }}
|
||||
#define CMD_LINE_FVOID(s,n,p,h) { s "|" n "||" h, CMD_LINE_TYPE_FVOID, { .func_noarg = p }}
|
||||
#define CMD_LINE_FARG(s,n,a,p,h) { s "|" n "|" a "|" h, CMD_LINE_TYPE_FARG, { .func_arg = p }}
|
||||
#define CMD_LINE_LINK() { NULL, CMD_LINE_TYPE_NEXT, { NULL }}
|
||||
#define CMD_LINE_NONE() { NULL, CMD_LINE_TYPE_NONE, FALSE, { NULL }}
|
||||
#define CMD_LINE_BOOL(s,n,p,h) { s "|" n "||" h, CMD_LINE_TYPE_BOOL, TRUE, { .int_ptr = p }}
|
||||
#define CMD_LINE_INT(s,n,a,p,h) { s "|" n "|" a "|" h, CMD_LINE_TYPE_INT, TRUE, { .int_ptr = p }}
|
||||
#define CMD_LINE_STRING(s,n,a,p,h) { s "|" n "|" a "|" h, CMD_LINE_TYPE_STRING, TRUE, { .string_ptr = p }}
|
||||
#define CMD_LINE_FVOID(s,n,p,h) { s "|" n "||" h, CMD_LINE_TYPE_FVOID, FALSE, { .func_noarg = p }}
|
||||
#define CMD_LINE_FARG(s,n,a,p,h) { s "|" n "|" a "|" h, CMD_LINE_TYPE_FARG, TRUE, { .func_arg = p }}
|
||||
#define CMD_LINE_LINK() { NULL, CMD_LINE_TYPE_NEXT, FALSE, { NULL }}
|
||||
|
||||
void qe_register_cmd_line_options(CmdLineOptionDef *table);
|
||||
|
||||
|
|
@ -890,6 +891,7 @@ int qe_free_mode_data(QEModeData *md);
|
|||
/* from tty.c */
|
||||
/* set from command line option to prevent GUI such as X11 */
|
||||
extern int force_tty;
|
||||
extern int tty_mk;
|
||||
|
||||
enum QEStyle {
|
||||
#define STYLE_DEF(constant, name, fg_color, bg_color, \
|
||||
|
|
|
|||
52
tty.c
52
tty.c
|
|
@ -322,6 +322,10 @@ static int tty_dpy_init(QEditScreen *s,
|
|||
"\033[39;49m" /* orig_pair */
|
||||
"\033[?1h\033=" /* keypad_xmit */
|
||||
);
|
||||
if (tty_mk) {
|
||||
/* modifyOtherKeys: report shift states */
|
||||
TTY_FPRINTF(s->STDOUT, "\033[>4;%dm", tty_mk);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Get charset from command line option */
|
||||
|
|
@ -417,6 +421,7 @@ static int tty_dpy_init(QEditScreen *s,
|
|||
sigemptyset(&sig.sa_mask);
|
||||
sig.sa_flags = 0;
|
||||
sigaction(SIGWINCH, &sig, NULL);
|
||||
|
||||
fcntl(fileno(s->STDIN), F_SETFL, O_NONBLOCK);
|
||||
/* If stdout is to a pty, make sure we aren't in nonblocking mode.
|
||||
* Otherwise, the printf()s in term_flush() can fail with EAGAIN,
|
||||
|
|
@ -454,7 +459,11 @@ static void tty_dpy_close(QEditScreen *s)
|
|||
"\033[?1l\033>" /* keypad_local */
|
||||
"\033[?25h" /* show cursor */
|
||||
"\r\033[m\033[K" /* return erase eol */
|
||||
);
|
||||
);
|
||||
if (tty_mk) {
|
||||
/* modifyOtherKeys: report shift states */
|
||||
TTY_FPRINTF(s->STDOUT, "\033[>4m");
|
||||
}
|
||||
#endif
|
||||
fflush(s->STDOUT);
|
||||
tcsetattr(fileno(s->STDIN), TCSANOW, &ts->oldtty);
|
||||
|
|
@ -657,7 +666,7 @@ static void tty_read_handler(void *opaque)
|
|||
* keys transmitting escape sequences starting with \033
|
||||
* but followed immediately by more characters.
|
||||
*/
|
||||
goto the_end;
|
||||
goto the_end_meta;
|
||||
}
|
||||
ts->input_state = IS_ESC;
|
||||
break;
|
||||
|
|
@ -675,11 +684,10 @@ static void tty_read_handler(void *opaque)
|
|||
if (!pending) {
|
||||
/* Distinguish alt-ESC from ESC prefix applied to other keys */
|
||||
ch = KEY_META(KEY_ESC);
|
||||
ts->input_state = IS_NORM;
|
||||
goto the_end;
|
||||
}
|
||||
/* cygwin A-right transmit ESC ESC[C ... */
|
||||
ts->has_meta = 8;
|
||||
ts->has_meta = KEY_STATE_META;
|
||||
break;
|
||||
}
|
||||
if (ch == '[' && pending) { // CSI
|
||||
|
|
@ -708,7 +716,6 @@ static void tty_read_handler(void *opaque)
|
|||
}
|
||||
/* FIXME: handle terminal answers */
|
||||
ch = KEY_META(ch);
|
||||
ts->input_state = IS_NORM;
|
||||
goto the_end;
|
||||
|
||||
case IS_CSI:
|
||||
|
|
@ -731,6 +738,7 @@ static void tty_read_handler(void *opaque)
|
|||
if (ts->interm) {
|
||||
/* syntax error: ignore CSI sequence */
|
||||
ts->input_state = IS_NORM;
|
||||
ts->has_meta = 0;
|
||||
break;
|
||||
}
|
||||
if (ts->nb_params < countof(ts->params)) {
|
||||
|
|
@ -754,7 +762,6 @@ static void tty_read_handler(void *opaque)
|
|||
/* FIXME: Should only accept final bytes in range 40..7E,
|
||||
* and ignore other bytes.
|
||||
*/
|
||||
ts->input_state = IS_NORM;
|
||||
n1 = ts->params[0] >= 0 ? ts->params[0] : 0;
|
||||
n2 = ts->params[1] >= 0 ? ts->params[1] : 1;
|
||||
switch ((ts->leader << 16) | (ts->interm << 8) | ch) {
|
||||
|
|
@ -774,8 +781,13 @@ static void tty_read_handler(void *opaque)
|
|||
ch = ts->params[2];
|
||||
if (ch == 8 && (ts->term_flags |= KBS_CONTROL_H))
|
||||
ch = KEY_DEL;
|
||||
// XXX: iTerm2 3.14.19 encoding bug for M-C-a to M-C-z
|
||||
if (n2 == 4 && ch >= 'A' && ch <= 'Z') {
|
||||
n2 |= 2;
|
||||
ch += 'a' - 'A';
|
||||
}
|
||||
ch = get_modified_key(ch, n2);
|
||||
goto the_end;
|
||||
goto the_end_meta;
|
||||
}
|
||||
if (n1 < countof(csi_lookup)) {
|
||||
ch = csi_lookup[n1];
|
||||
|
|
@ -789,7 +801,7 @@ static void tty_read_handler(void *opaque)
|
|||
* supported by xterm if formatOtherKeys resource is selected
|
||||
*/
|
||||
ch = get_modified_key(n1, n2 - 1);
|
||||
goto the_end;
|
||||
goto the_end_meta;
|
||||
/* All these for ansi|cygwin */
|
||||
default:
|
||||
/* n2 contains the shift status + 1:
|
||||
|
|
@ -834,19 +846,17 @@ static void tty_read_handler(void *opaque)
|
|||
case 'Z': ch = KEY_SHIFT_TAB; goto the_end_modified; // kcbt
|
||||
}
|
||||
ch = KEY_UNKNOWN;
|
||||
ts->has_meta = 0;
|
||||
goto the_end;
|
||||
}
|
||||
break;
|
||||
case IS_CSI2:
|
||||
/* cygwin/linux terminal */
|
||||
ts->input_state = IS_NORM;
|
||||
switch (ch) {
|
||||
case 'A': ch = KEY_F1; goto the_end;
|
||||
case 'B': ch = KEY_F2; goto the_end;
|
||||
case 'C': ch = KEY_F3; goto the_end;
|
||||
case 'D': ch = KEY_F4; goto the_end;
|
||||
case 'E': ch = KEY_F5; goto the_end;
|
||||
case 'A': ch = KEY_F1; goto the_end_meta;
|
||||
case 'B': ch = KEY_F2; goto the_end_meta;
|
||||
case 'C': ch = KEY_F3; goto the_end_meta;
|
||||
case 'D': ch = KEY_F4; goto the_end_meta;
|
||||
case 'E': ch = KEY_F5; goto the_end_meta;
|
||||
}
|
||||
ch = KEY_UNKNOWN;
|
||||
goto the_end;
|
||||
|
|
@ -859,7 +869,6 @@ static void tty_read_handler(void *opaque)
|
|||
break;
|
||||
}
|
||||
n2 = ts->params[0] > 0 ? ts->params[0] - 1 : 0;
|
||||
ts->input_state = IS_NORM;
|
||||
switch (ch) {
|
||||
case 'A': ch = KEY_UP; goto the_end_modified;
|
||||
case 'B': ch = KEY_DOWN; goto the_end_modified;
|
||||
|
|
@ -888,14 +897,14 @@ static void tty_read_handler(void *opaque)
|
|||
ch = KEY_META(ch);
|
||||
if (n2 & 4)
|
||||
ch = KEY_CONTROL(ch);
|
||||
goto the_end;
|
||||
|
||||
the_end_meta:
|
||||
if (ts->has_meta) {
|
||||
ts->has_meta = 0;
|
||||
if (ts->has_meta)
|
||||
ch = KEY_META(ch);
|
||||
}
|
||||
|
||||
the_end:
|
||||
ts->input_state = IS_NORM;
|
||||
ts->has_meta = 0;
|
||||
// XXX: should add custom key sequences, especially for KEY_UNKNOWN
|
||||
ev->key_event.type = QE_KEY_EVENT;
|
||||
ev->key_event.key = ch;
|
||||
|
|
@ -905,7 +914,7 @@ static void tty_read_handler(void *opaque)
|
|||
case IS_OSC:
|
||||
/* OSC syntax is: OSC string 07 or OSC string ST (ESC \) */
|
||||
if (ch == 27) {
|
||||
ts->has_meta = 1;
|
||||
ts->has_meta = KEY_STATE_META;
|
||||
break;
|
||||
}
|
||||
if (ts->has_meta && ch == '\\') {
|
||||
|
|
@ -915,6 +924,7 @@ static void tty_read_handler(void *opaque)
|
|||
break;
|
||||
// FIXME: handle terminal response
|
||||
ts->input_state = IS_NORM;
|
||||
ts->has_meta = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
10
util.c
10
util.c
|
|
@ -1946,8 +1946,8 @@ int buf_put_key(buf_t *out, int key) {
|
|||
buf_put_key(out, key & 0xff);
|
||||
} else
|
||||
// FIXME: handle @-_ @@@
|
||||
if (key >= KEY_CTRL('a') && key <= KEY_CTRL('z')) {
|
||||
buf_printf(out, "C-%c", key + 'a' - 1);
|
||||
if (key >= KEY_CTRL('@') && key <= KEY_CTRL('_')) {
|
||||
buf_printf(out, "C-%c", qe_tolower(key + 'A' - 1));
|
||||
} else {
|
||||
buf_putc_utf8(out, key);
|
||||
}
|
||||
|
|
@ -2410,9 +2410,9 @@ bstr_t bstr_token(const char *s, int sep, const char **pp) {
|
|||
continue;
|
||||
|
||||
bs.len = s - bs.s;
|
||||
if (pp) {
|
||||
*pp = *s ? s + 1 : NULL;
|
||||
}
|
||||
}
|
||||
if (pp) {
|
||||
*pp = (s && *s) ? s + 1 : NULL;
|
||||
}
|
||||
return bs;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue