summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2020-05-19 21:20:04 +0200
committerBram Moolenaar <Bram@vim.org>2020-05-19 21:20:04 +0200
commitbe593bf135f6967335b14ba188bd5f8f32175c75 (patch)
tree18fcdef9c8d095ecd3a6b4b934b658d83f9a92ce /src
parent904e48a22be7e227a73c70e520cf9d8bc50fc1b2 (diff)
downloadvim-git-be593bf135f6967335b14ba188bd5f8f32175c75.tar.gz
patch 8.2.0798: libvterm code lags behind the upstream versionv8.2.0798
Problem: Libvterm code lags behind the upstream version. Solution: Include revisions 755 - 758.
Diffstat (limited to 'src')
-rw-r--r--src/libvterm/include/vterm.h13
-rw-r--r--src/libvterm/src/parser.c278
-rw-r--r--src/libvterm/src/screen.c4
-rw-r--r--src/libvterm/src/state.c208
-rw-r--r--src/libvterm/src/vterm.c25
-rw-r--r--src/libvterm/src/vterm_internal.h47
-rw-r--r--src/libvterm/t/02parser.test26
-rw-r--r--src/libvterm/t/18state_termprops.test8
-rw-r--r--src/libvterm/t/29state_fallback.test4
-rw-r--r--src/libvterm/t/68screen_termprops.test2
-rw-r--r--src/libvterm/t/harness.c38
-rw-r--r--src/libvterm/t/run-test.pl17
-rw-r--r--src/terminal.c36
-rw-r--r--src/version.c2
14 files changed, 396 insertions, 312 deletions
diff --git a/src/libvterm/include/vterm.h b/src/libvterm/include/vterm.h
index ea8f8cd10..8b6a64d96 100644
--- a/src/libvterm/include/vterm.h
+++ b/src/libvterm/include/vterm.h
@@ -107,10 +107,17 @@ typedef enum {
VTERM_N_VALUETYPES
} VTermValueType;
+typedef struct {
+ const char *str;
+ size_t len : 30;
+ unsigned int initial : 1;
+ unsigned int final : 1;
+} VTermStringFragment;
+
typedef union {
int boolean;
int number;
- char *string;
+ VTermStringFragment string;
VTermColor color;
} VTermValue;
@@ -257,8 +264,8 @@ typedef struct {
int (*control)(unsigned char control, void *user);
int (*escape)(const char *bytes, size_t len, void *user);
int (*csi)(const char *leader, const long args[], int argcount, const char *intermed, char command, void *user);
- int (*osc)(const char *command, size_t cmdlen, void *user);
- int (*dcs)(const char *command, size_t cmdlen, void *user);
+ int (*osc)(int command, VTermStringFragment frag, void *user);
+ int (*dcs)(const char *command, size_t commandlen, VTermStringFragment frag, void *user);
int (*resize)(int rows, int cols, void *user);
} VTermParserCallbacks;
diff --git a/src/libvterm/src/parser.c b/src/libvterm/src/parser.c
index 8a06c427b..520ec2cdc 100644
--- a/src/libvterm/src/parser.c
+++ b/src/libvterm/src/parser.c
@@ -23,10 +23,10 @@ static void do_csi(VTerm *vt, char command)
{
#ifdef DEBUG_PARSER
printf("Parsed CSI args as:\n", arglen, args);
- printf(" leader: %s\n", vt->parser.csi_leader);
- for(int argi = 0; argi < vt->parser.csi_argi; argi++) {
- printf(" %lu", CSI_ARG(vt->parser.csi_args[argi]));
- if(!CSI_ARG_HAS_MORE(vt->parser.csi_args[argi]))
+ printf(" leader: %s\n", vt->parser.v.csi.leader);
+ for(int argi = 0; argi < vt->parser.v.csi.argi; argi++) {
+ printf(" %lu", CSI_ARG(vt->parser.v.csi.args[argi]));
+ if(!CSI_ARG_HAS_MORE(vt->parser.v.csi.args[argi]))
printf("\n");
printf(" intermed: %s\n", vt->parser.intermed);
}
@@ -34,9 +34,9 @@ static void do_csi(VTerm *vt, char command)
if(vt->parser.callbacks && vt->parser.callbacks->csi)
if((*vt->parser.callbacks->csi)(
- vt->parser.csi_leaderlen ? vt->parser.csi_leader : NULL,
- vt->parser.csi_args,
- vt->parser.csi_argi,
+ vt->parser.v.csi.leaderlen ? vt->parser.v.csi.leader : NULL,
+ vt->parser.v.csi.args,
+ vt->parser.v.csi.argi,
vt->parser.intermedlen ? vt->parser.intermed : NULL,
command,
vt->parser.cbdata))
@@ -61,65 +61,36 @@ static void do_escape(VTerm *vt, char command)
DEBUG_LOG1("libvterm: Unhandled escape ESC 0x%02x\n", command);
}
-static void append_strbuffer(VTerm *vt, const char *str, size_t len)
+static void string_fragment(VTerm *vt, const char *str, size_t len, int final)
{
- if(len > vt->parser.strbuffer_len - vt->parser.strbuffer_cur) {
- len = vt->parser.strbuffer_len - vt->parser.strbuffer_cur;
- DEBUG_LOG1("Truncating strbuffer preserve to %zu bytes\n", len);
- }
-
- if(len > 0) {
- strncpy(vt->parser.strbuffer + vt->parser.strbuffer_cur, str, len);
- vt->parser.strbuffer_cur += len;
- }
-}
-
-static void start_string(VTerm *vt, VTermParserStringType type)
-{
- vt->parser.stringtype = type;
+ VTermStringFragment frag;
- vt->parser.strbuffer_cur = 0;
-}
+ frag.str = str;
+ frag.len = len;
+ frag.initial = vt->parser.string_initial;
+ frag.final = final;
-static void more_string(VTerm *vt, const char *str, size_t len)
-{
- append_strbuffer(vt, str, len);
-}
+ switch(vt->parser.state) {
+ case OSC:
+ if(vt->parser.callbacks && vt->parser.callbacks->osc)
+ (*vt->parser.callbacks->osc)(vt->parser.v.osc.command, frag, vt->parser.cbdata);
+ break;
-static void done_string(VTerm *vt, const char *str, size_t len)
-{
- if(vt->parser.strbuffer_cur) {
- if(str)
- append_strbuffer(vt, str, len);
+ case DCS:
+ if(len && vt->parser.callbacks && vt->parser.callbacks->dcs)
+ (*vt->parser.callbacks->dcs)(vt->parser.v.dcs.command, vt->parser.v.dcs.commandlen, frag, vt->parser.cbdata);
+ break;
- str = vt->parser.strbuffer;
- len = vt->parser.strbuffer_cur;
- }
- else if(!str) {
- DEBUG_LOG("parser.c: TODO: No strbuffer _and_ no final fragment???\n");
- len = 0;
+ case NORMAL:
+ case CSI_LEADER:
+ case CSI_ARGS:
+ case CSI_INTERMED:
+ case OSC_COMMAND:
+ case DCS_COMMAND:
+ break;
}
- switch(vt->parser.stringtype) {
- case VTERM_PARSER_OSC:
- if(vt->parser.callbacks && vt->parser.callbacks->osc)
- if((*vt->parser.callbacks->osc)(str, len, vt->parser.cbdata))
- return;
-
- DEBUG_LOG2("libvterm: Unhandled OSC %.*s\n", (int)len, str);
- return;
-
- case VTERM_PARSER_DCS:
- if(vt->parser.callbacks && vt->parser.callbacks->dcs)
- if((*vt->parser.callbacks->dcs)(str, len, vt->parser.cbdata))
- return;
-
- DEBUG_LOG2("libvterm: Unhandled DCS %.*s\n", (int)len, str);
- return;
-
- case VTERM_N_PARSER_TYPES:
- return;
- }
+ vt->parser.string_initial = FALSE;
}
size_t vterm_input_write(VTerm *vt, const char *bytes, size_t len)
@@ -135,43 +106,45 @@ size_t vterm_input_write(VTerm *vt, const char *bytes, size_t len)
case CSI_LEADER:
case CSI_ARGS:
case CSI_INTERMED:
- case ESC:
+ case OSC_COMMAND:
+ case DCS_COMMAND:
string_start = NULL;
break;
- case STRING:
- case ESC_IN_STRING:
+ case OSC:
+ case DCS:
string_start = bytes;
break;
}
-#define ENTER_STRING_STATE() do { vt->parser.state = STRING; string_start = bytes + pos + 1; } while(0)
#define ENTER_STATE(st) do { vt->parser.state = st; string_start = NULL; } while(0)
#define ENTER_NORMAL_STATE() ENTER_STATE(NORMAL)
for( ; pos < len; pos++) {
unsigned char c = bytes[pos];
+ int c1_allowed = !vt->mode.utf8;
+ size_t string_len;
if(c == 0x00 || c == 0x7f) { // NUL, DEL
- if(vt->parser.state >= STRING) {
- more_string(vt, string_start, bytes + pos - string_start);
+ if(vt->parser.state >= OSC) {
+ string_fragment(vt, string_start, bytes + pos - string_start, FALSE);
string_start = bytes + pos + 1;
}
continue;
}
if(c == 0x18 || c == 0x1a) { // CAN, SUB
+ vt->parser.in_esc = FALSE;
ENTER_NORMAL_STATE();
continue;
}
else if(c == 0x1b) { // ESC
vt->parser.intermedlen = 0;
- if(vt->parser.state == STRING)
- vt->parser.state = ESC_IN_STRING;
- else
- ENTER_STATE(ESC);
+ if(vt->parser.state < OSC)
+ vt->parser.state = NORMAL;
+ vt->parser.in_esc = TRUE;
continue;
}
else if(c == 0x07 && // BEL, can stand for ST in OSC or DCS state
- vt->parser.state == STRING) {
+ vt->parser.state >= OSC) {
// fallthrough
}
else if(c < 0x20) { // other C0
@@ -182,96 +155,72 @@ size_t vterm_input_write(VTerm *vt, const char *bytes, size_t len)
if(pos + 2 < len && bytes[pos + 1] == 0x20 && bytes[pos + 2] == 0x08)
vt->in_backspace = 2; // Trigger when count down to 1
}
- if(vt->parser.state >= STRING)
- more_string(vt, string_start, bytes + pos - string_start);
+ if(vt->parser.state >= OSC)
+ string_fragment(vt, string_start, bytes + pos - string_start, FALSE);
do_control(vt, c);
- if(vt->parser.state >= STRING)
+ if(vt->parser.state >= OSC)
string_start = bytes + pos + 1;
continue;
}
// else fallthrough
- switch(vt->parser.state) {
- case ESC_IN_STRING:
- if(c == 0x5c) { // ST
- vt->parser.state = STRING;
- done_string(vt, string_start, bytes + pos - string_start - 1);
- ENTER_NORMAL_STATE();
- break;
+ string_len = bytes + pos - string_start;
+
+ if(vt->parser.in_esc) {
+ // Hoist an ESC letter into a C1 if we're not in a string mode
+ // Always accept ESC \ == ST even in string mode
+ if(!vt->parser.intermedlen &&
+ c >= 0x40 && c < 0x60 &&
+ ((vt->parser.state < OSC || c == 0x5c))) {
+ c += 0x40;
+ c1_allowed = TRUE;
+ string_len -= 1;
+ vt->parser.in_esc = FALSE;
}
- vt->parser.state = ESC;
- // else fallthrough
-
- case ESC:
- switch(c) {
- case 0x50: // DCS
- start_string(vt, VTERM_PARSER_DCS);
- ENTER_STRING_STATE();
- break;
- case 0x5b: // CSI
- vt->parser.csi_leaderlen = 0;
- ENTER_STATE(CSI_LEADER);
- break;
- case 0x5d: // OSC
- start_string(vt, VTERM_PARSER_OSC);
- ENTER_STRING_STATE();
- break;
- default:
- if(is_intermed(c)) {
- if(vt->parser.intermedlen < INTERMED_MAX-1)
- vt->parser.intermed[vt->parser.intermedlen++] = c;
- }
- else if(!vt->parser.intermedlen && c >= 0x40 && c < 0x60) {
- do_control(vt, c + 0x40);
- ENTER_NORMAL_STATE();
- }
- else if(c >= 0x30 && c < 0x7f) {
- do_escape(vt, c);
- ENTER_NORMAL_STATE();
- }
- else {
- DEBUG_LOG1("TODO: Unhandled byte %02x in Escape\n", c);
- }
+ else {
+ string_start = NULL;
+ vt->parser.state = NORMAL;
}
- break;
+ }
+ switch(vt->parser.state) {
case CSI_LEADER:
// Extract leader bytes 0x3c to 0x3f
if(c >= 0x3c && c <= 0x3f) {
- if(vt->parser.csi_leaderlen < CSI_LEADER_MAX-1)
- vt->parser.csi_leader[vt->parser.csi_leaderlen++] = c;
+ if(vt->parser.v.csi.leaderlen < CSI_LEADER_MAX-1)
+ vt->parser.v.csi.leader[vt->parser.v.csi.leaderlen++] = c;
break;
}
// else fallthrough
- vt->parser.csi_leader[vt->parser.csi_leaderlen] = 0;
+ vt->parser.v.csi.leader[vt->parser.v.csi.leaderlen] = 0;
- vt->parser.csi_argi = 0;
- vt->parser.csi_args[0] = CSI_ARG_MISSING;
+ vt->parser.v.csi.argi = 0;
+ vt->parser.v.csi.args[0] = CSI_ARG_MISSING;
vt->parser.state = CSI_ARGS;
// fallthrough
case CSI_ARGS:
// Numerical value of argument
if(c >= '0' && c <= '9') {
- if(vt->parser.csi_args[vt->parser.csi_argi] == CSI_ARG_MISSING)
- vt->parser.csi_args[vt->parser.csi_argi] = 0;
- vt->parser.csi_args[vt->parser.csi_argi] *= 10;
- vt->parser.csi_args[vt->parser.csi_argi] += c - '0';
+ if(vt->parser.v.csi.args[vt->parser.v.csi.argi] == CSI_ARG_MISSING)
+ vt->parser.v.csi.args[vt->parser.v.csi.argi] = 0;
+ vt->parser.v.csi.args[vt->parser.v.csi.argi] *= 10;
+ vt->parser.v.csi.args[vt->parser.v.csi.argi] += c - '0';
break;
}
if(c == ':') {
- vt->parser.csi_args[vt->parser.csi_argi] |= CSI_ARG_FLAG_MORE;
+ vt->parser.v.csi.args[vt->parser.v.csi.argi] |= CSI_ARG_FLAG_MORE;
c = ';';
}
if(c == ';') {
- vt->parser.csi_argi++;
- vt->parser.csi_args[vt->parser.csi_argi] = CSI_ARG_MISSING;
+ vt->parser.v.csi.argi++;
+ vt->parser.v.csi.args[vt->parser.v.csi.argi] = CSI_ARG_MISSING;
break;
}
// else fallthrough
- vt->parser.csi_argi++;
+ vt->parser.v.csi.argi++;
vt->parser.intermedlen = 0;
vt->parser.state = CSI_INTERMED;
// fallthrough
@@ -293,31 +242,77 @@ size_t vterm_input_write(VTerm *vt, const char *bytes, size_t len)
ENTER_NORMAL_STATE();
break;
- case STRING:
- if(c == 0x07 || (c == 0x9c && !vt->mode.utf8)) {
- done_string(vt, string_start, bytes + pos - string_start);
- ENTER_NORMAL_STATE();
+ case OSC_COMMAND:
+ /* Numerical value of command */
+ if(c >= '0' && c <= '9') {
+ if(vt->parser.v.osc.command == -1)
+ vt->parser.v.osc.command = 0;
+ else
+ vt->parser.v.osc.command *= 10;
+ vt->parser.v.osc.command += c - '0';
+ break;
}
- else if (pos + 1 == len) {
- // end of input but OSC string isn't finished yet, copy it to
- // vt->parser.strbuffer to continue it later
- more_string(vt, string_start, bytes + pos + 1 - string_start);
+ if(c == ';') {
+ vt->parser.state = OSC;
+ string_start = bytes + pos + 1;
+ break;
+ }
+
+ /* else fallthrough */
+ string_start = bytes + pos;
+ vt->parser.state = OSC;
+ goto string_state;
+
+ case DCS_COMMAND:
+ if(vt->parser.v.dcs.commandlen < CSI_LEADER_MAX)
+ vt->parser.v.dcs.command[vt->parser.v.dcs.commandlen++] = c;
+
+ if(c >= 0x40 && c<= 0x7e) {
+ string_start = bytes + pos + 1;
+ vt->parser.state = DCS;
+ }
+ break;
+
+string_state:
+ case OSC:
+ case DCS:
+ if(c == 0x07 || (c1_allowed && c == 0x9c)) {
+ string_fragment(vt, string_start, string_len, TRUE);
+ ENTER_NORMAL_STATE();
}
break;
case NORMAL:
- if(c >= 0x80 && c < 0xa0 && !vt->mode.utf8) {
+ if(vt->parser.in_esc) {
+ if(is_intermed(c)) {
+ if(vt->parser.intermedlen < INTERMED_MAX-1)
+ vt->parser.intermed[vt->parser.intermedlen++] = c;
+ }
+ else if(c >= 0x30 && c < 0x7f) {
+ do_escape(vt, c);
+ vt->parser.in_esc = 0;
+ ENTER_NORMAL_STATE();
+ }
+ else {
+ DEBUG_LOG1("TODO: Unhandled byte %02x in Escape\n", c);
+ }
+ break;
+ }
+ if(c1_allowed && c >= 0x80 && c < 0xa0) {
switch(c) {
case 0x90: // DCS
- start_string(vt, VTERM_PARSER_DCS);
- ENTER_STRING_STATE();
+ vt->parser.string_initial = TRUE;
+ vt->parser.v.dcs.commandlen = 0;
+ ENTER_STATE(DCS_COMMAND);
break;
case 0x9b: // CSI
+ vt->parser.v.csi.leaderlen = 0;
ENTER_STATE(CSI_LEADER);
break;
case 0x9d: // OSC
- start_string(vt, VTERM_PARSER_OSC);
- ENTER_STRING_STATE();
+ vt->parser.v.osc.command = -1;
+ vt->parser.string_initial = TRUE;
+ ENTER_STATE(OSC_COMMAND);
break;
default:
do_control(vt, c);
@@ -341,6 +336,9 @@ size_t vterm_input_write(VTerm *vt, const char *bytes, size_t len)
}
}
+ if(string_start)
+ string_fragment(vt, string_start, bytes + pos - string_start, FALSE);
+
return len;
}
diff --git a/src/libvterm/src/screen.c b/src/libvterm/src/screen.c
index 1da2b72ae..0dd6276a1 100644
--- a/src/libvterm/src/screen.c
+++ b/src/libvterm/src/screen.c
@@ -533,7 +533,7 @@ static void resize_buffer(VTermScreen *screen, int bufidx, int new_rows, int new
ScreenCell *dst = &new_buffer[pos.row * new_cols + pos.col];
int i;
- for(i = 0; ; i++) {
+ for(i = 0; i < VTERM_MAX_CHARS_PER_CELL; i++) {
dst->chars[i] = src->chars[i];
if(!src->chars[i])
break;
@@ -804,7 +804,7 @@ int vterm_screen_get_cell(const VTermScreen *screen, VTermPos pos, VTermScreenCe
if(!intcell)
return 0;
- for(i = 0; ; i++) {
+ for(i = 0; i < VTERM_MAX_CHARS_PER_CELL; i++) {
cell->chars[i] = intcell->chars[i];
if(!intcell->chars[i])
break;
diff --git a/src/libvterm/src/state.c b/src/libvterm/src/state.c
index 1be1dae85..6a5c265ab 100644
--- a/src/libvterm/src/state.c
+++ b/src/libvterm/src/state.c
@@ -582,19 +582,12 @@ static int settermprop_int(VTermState *state, VTermProp prop, int v)
return vterm_state_set_termprop(state, prop, &val);
}
-static int settermprop_string(VTermState *state, VTermProp prop, const char *str, size_t len)
+static int settermprop_string(VTermState *state, VTermProp prop, VTermStringFragment frag)
{
- char *strvalue;
- int r;
VTermValue val;
- strvalue = vterm_allocator_malloc(state->vt, (len+1) * sizeof(char));
- strncpy(strvalue, str, len);
- strvalue[len] = 0;
-
- val.string = strvalue;
- r = vterm_state_set_termprop(state, prop, &val);
- vterm_allocator_free(state->vt, strvalue);
- return r;
+
+ val.string = frag;
+ return vterm_state_set_termprop(state, prop, &val);
}
static void savecursor(VTermState *state, int save)
@@ -1602,100 +1595,121 @@ static int on_csi(const char *leader, const long args[], int argcount, const cha
return 1;
}
-static int on_osc(const char *command, size_t cmdlen, void *user)
+static int on_osc(int command, VTermStringFragment frag, void *user)
{
VTermState *state = user;
- if(cmdlen < 2)
- return 0;
+ switch(command) {
+ case 0:
+ settermprop_string(state, VTERM_PROP_ICONNAME, frag);
+ settermprop_string(state, VTERM_PROP_TITLE, frag);
+ return 1;
- if(strneq(command, "0;", 2)) {
- settermprop_string(state, VTERM_PROP_ICONNAME, command + 2, cmdlen - 2);
- settermprop_string(state, VTERM_PROP_TITLE, command + 2, cmdlen - 2);
- return 1;
- }
- else if(strneq(command, "1;", 2)) {
- settermprop_string(state, VTERM_PROP_ICONNAME, command + 2, cmdlen - 2);
- return 1;
- }
- else if(strneq(command, "2;", 2)) {
- settermprop_string(state, VTERM_PROP_TITLE, command + 2, cmdlen - 2);
- return 1;
- }
- else if(strneq(command, "10;", 3)) {
- // request foreground color: <Esc>]10;?<0x07>
- int red = state->default_fg.red;
- int blue = state->default_fg.blue;
- int green = state->default_fg.green;
- vterm_push_output_sprintf_ctrl(state->vt, C1_OSC, "10;rgb:%02x%02x/%02x%02x/%02x%02x\x07", red, red, green, green, blue, blue);
- return 1;
- }
- else if(strneq(command, "11;", 3)) {
- // request background color: <Esc>]11;?<0x07>
- int red = state->default_bg.red;
- int blue = state->default_bg.blue;
- int green = state->default_bg.green;
- vterm_push_output_sprintf_ctrl(state->vt, C1_OSC, "11;rgb:%02x%02x/%02x%02x/%02x%02x\x07", red, red, green, green, blue, blue);
- return 1;
- }
- else if(strneq(command, "12;", 3)) {
- settermprop_string(state, VTERM_PROP_CURSORCOLOR, command + 3, cmdlen - 3);
- return 1;
- }
- else if(state->fallbacks && state->fallbacks->osc)
- if((*state->fallbacks->osc)(command, cmdlen, state->fbdata))
+ case 1:
+ settermprop_string(state, VTERM_PROP_ICONNAME, frag);
+ return 1;
+
+ case 2:
+ settermprop_string(state, VTERM_PROP_TITLE, frag);
return 1;
+ case 10:
+ {
+ // request foreground color: <Esc>]10;?<0x07>
+ int red = state->default_fg.red;
+ int blue = state->default_fg.blue;
+ int green = state->default_fg.green;
+ vterm_push_output_sprintf_ctrl(state->vt, C1_OSC, "10;rgb:%02x%02x/%02x%02x/%02x%02x\x07", red, red, green, green, blue, blue);
+ return 1;
+ }
+
+ case 11:
+ {
+ // request background color: <Esc>]11;?<0x07>
+ int red = state->default_bg.red;
+ int blue = state->default_bg.blue;
+ int green = state->default_bg.green;
+ vterm_push_output_sprintf_ctrl(state->vt, C1_OSC, "11;rgb:%02x%02x/%02x%02x/%02x%02x\x07", red, red, green, green, blue, blue);
+ return 1;
+ }
+ case 12:
+ settermprop_string(state, VTERM_PROP_CURSORCOLOR, frag);
+ return 1;
+
+ default:
+ if(state->fallbacks && state->fallbacks->osc)
+ if((*state->fallbacks->osc)(command, frag, state->fbdata))
+ return 1;
+ }
+
return 0;
}
-static void request_status_string(VTermState *state, const char *command, size_t cmdlen)
+static void request_status_string(VTermState *state, VTermStringFragment frag)
{
VTerm *vt = state->vt;
- if(cmdlen == 1)
- switch(command[0]) {
- case 'm': // Query SGR
- {
- long args[20];
- int argc = vterm_state_getpen(state, args, sizeof(args)/sizeof(args[0]));
- int argi;
- size_t cur = 0;
-
- cur += SNPRINTF(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
- vt->mode.ctrl8bit ? "\x90" "1$r" : ESC_S "P" "1$r"); // DCS 1$r ...
- if(cur >= vt->tmpbuffer_len)
- return;
-
- for(argi = 0; argi < argc; argi++) {
- cur += SNPRINTF(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
- argi == argc - 1 ? "%ld" :
- CSI_ARG_HAS_MORE(args[argi]) ? "%ld:" :
- "%ld;",
- CSI_ARG(args[argi]));
-
- if(cur >= vt->tmpbuffer_len)
- return;
- }
-
- cur += SNPRINTF(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
- vt->mode.ctrl8bit ? "m" "\x9C" : "m" ESC_S "\\"); // ... m ST
- if(cur >= vt->tmpbuffer_len)
- return;
-
- vterm_push_output_bytes(vt, vt->tmpbuffer, cur);
- }
- return;
- case 'r': // Query DECSTBM
- vterm_push_output_sprintf_dcs(vt, "1$r%d;%dr", state->scrollregion_top+1, SCROLLREGION_BOTTOM(state));
+ char *tmp = state->tmp.decrqss;
+ size_t i = 0;
+
+ if(frag.initial)
+ tmp[0] = tmp[1] = tmp[2] = tmp[3] = 0;
+
+ while(i < sizeof(state->tmp.decrqss)-1 && tmp[i])
+ i++;
+ while(i < sizeof(state->tmp.decrqss)-1 && frag.len--)
+ tmp[i++] = (frag.str++)[0];
+ tmp[i] = 0;
+
+ if(!frag.final)
+ return;
+
+ fprintf(stderr, "DECRQSS on <%s>\n", tmp);
+
+ switch(tmp[0] | tmp[1]<<8 | tmp[2]<<16) {
+ case 'm': {
+ // Query SGR
+ long args[20];
+ int argc = vterm_state_getpen(state, args, sizeof(args)/sizeof(args[0]));
+ size_t cur = 0;
+ int argi;
+
+ cur += snprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
+ vt->mode.ctrl8bit ? "\x90" "1$r" : ESC_S "P" "1$r"); // DCS 1$r ...
+ if(cur >= vt->tmpbuffer_len)
return;
- case 's': // Query DECSLRM
- vterm_push_output_sprintf_dcs(vt, "1$r%d;%ds", SCROLLREGION_LEFT(state)+1, SCROLLREGION_RIGHT(state));
+
+ for(argi = 0; argi < argc; argi++) {
+ cur += snprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
+ argi == argc - 1 ? "%ld" :
+ CSI_ARG_HAS_MORE(args[argi]) ? "%ld:" :
+ "%ld;",
+ CSI_ARG(args[argi]));
+ if(cur >= vt->tmpbuffer_len)
+ return;
+ }
+
+ cur += snprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
+ vt->mode.ctrl8bit ? "m" "\x9C" : "m" ESC_S "\\"); // ... m ST
+ if(cur >= vt->tmpbuffer_len)
return;
+
+ vterm_push_output_bytes(vt, vt->tmpbuffer, cur);
+ return;
}
- if(cmdlen == 2) {
- if(strneq(command, " q", 2)) {
+ case 'r':
+ // Query DECSTBM
+ vterm_push_output_sprintf_dcs(vt, "1$r%d;%dr", state->scrollregion_top+1, SCROLLREGION_BOTTOM(state));
+ return;
+
+ case 's':
+ // Query DECSLRM
+ vterm_push_output_sprintf_dcs(vt, "1$r%d;%ds", SCROLLREGION_LEFT(state)+1, SCROLLREGION_RIGHT(state));
+ return;
+
+ case ' '|('q'<<8): {
+ // Query DECSCUSR
int reply;
switch(state->mode.cursor_shape) {
case VTERM_PROP_CURSORSHAPE_BLOCK: reply = 2; break;
@@ -1707,27 +1721,29 @@ static void request_status_string(VTermState *state, const char *command, size_t
vterm_push_output_sprintf_dcs(vt, "1$r%d q", reply);
return;
}
- else if(strneq(command, "\"q", 2)) {
+
+ case '\"'|('q'<<8):
+ // Query DECSCA
vterm_push_output_sprintf_dcs(vt, "1$r%d\"q", state->protected_cell ? 1 : 2);
return;
- }
}
- vterm_push_output_sprintf_dcs(state->vt, "0$r%.s", (int)cmdlen, command);
+ vterm_push_output_sprintf_dcs(state->vt, "0$r%s", tmp);
}
-static int on_dcs(const char *command, size_t cmdlen, void *user)
+static int on_dcs(const char *command, size_t commandlen, VTermStringFragment frag, void *user)
{
VTermState *state = user;
- if(cmdlen >= 2 && strneq(command, "$q", 2)) {
- request_status_string(state, command+2, cmdlen-2);
+ if(commandlen == 2 && strneq(command, "$q", 2)) {
+ request_status_string(state, frag);
return 1;
}
else if(state->fallbacks && state->fallbacks->dcs)
- if((*state->fallbacks->dcs)(command, cmdlen, state->fbdata))
+ if((*state->fallbacks->dcs)(command, commandlen, frag, state->fbdata))
return 1;
+ DEBUG_LOG2("libvterm: Unhandled DCS %.*s\n", (int)commandlen, command);
return 0;
}
diff --git a/src/libvterm/src/vterm.c b/src/libvterm/src/vterm.c
index 991286ee9..d03b7b4d7 100644
--- a/src/libvterm/src/vterm.c
+++ b/src/libvterm/src/vterm.c
@@ -55,35 +55,23 @@ VTerm *vterm_new_with_allocator(int rows, int cols, VTermAllocatorFunctions *fun
vt->parser.callbacks = NULL;
vt->parser.cbdata = NULL;
- vt->parser.strbuffer_len = 500; // should be able to hold an OSC string
- vt->parser.strbuffer_cur = 0;
- vt->parser.strbuffer = vterm_allocator_malloc(vt, vt->parser.strbuffer_len);
- if (vt->parser.strbuffer == NULL)
- {
- vterm_allocator_free(vt, vt);
- return NULL;
- }
-
vt->outfunc = NULL;
vt->outdata = NULL;
vt->outbuffer_len = 200;
vt->outbuffer_cur = 0;
vt->outbuffer = vterm_allocator_malloc(vt, vt->outbuffer_len);
- if (vt->outbuffer == NULL)
- {
- vterm_allocator_free(vt, vt->parser.strbuffer);
- vterm_allocator_free(vt, vt);
- return NULL;
- }
vt->tmpbuffer_len = 64;
vt->tmpbuffer = vterm_allocator_malloc(vt, vt->tmpbuffer_len);
- if (vt->tmpbuffer == NULL)
+
+ if (vt->tmpbuffer == NULL
+ || vt->outbuffer == NULL
+ || vt->tmpbuffer == NULL)
{
- vterm_allocator_free(vt, vt->parser.strbuffer);
- vterm_allocator_free(vt, vt);
vterm_allocator_free(vt, vt->outbuffer);
+ vterm_allocator_free(vt, vt->tmpbuffer);
+ vterm_allocator_free(vt, vt);
return NULL;
}
@@ -98,7 +86,6 @@ void vterm_free(VTerm *vt)
if(vt->state)
vterm_state_free(vt->state);
- vterm_allocator_free(vt, vt->parser.strbuffer);
vterm_allocator_free(vt, vt->outbuffer);
vterm_allocator_free(vt, vt->tmpbuffer);
diff --git a/src/libvterm/src/vterm_internal.h b/src/libvterm/src/vterm_internal.h
index 19b00c96c..7321d0d85 100644
--- a/src/libvterm/src/vterm_internal.h
+++ b/src/libvterm/src/vterm_internal.h
@@ -160,14 +160,12 @@ struct VTermState
unsigned int cursor_shape:2;
} mode;
} saved;
-};
-
-typedef enum {
- VTERM_PARSER_OSC,
- VTERM_PARSER_DCS,
- VTERM_N_PARSER_TYPES
-} VTermParserStringType;
+ /* Temporary state for DECRQSS parsing */
+ union {
+ char decrqss[4];
+ } tmp;
+};
struct VTerm
{
@@ -188,28 +186,39 @@ struct VTerm
CSI_LEADER,
CSI_ARGS,
CSI_INTERMED,
- ESC,
+ OSC_COMMAND,
+ DCS_COMMAND,
// below here are the "string states"
- STRING,
- ESC_IN_STRING,
+ OSC,
+ DCS,
} state;
+ unsigned int in_esc : 1;
+
int intermedlen;
char intermed[INTERMED_MAX];
- int csi_leaderlen;
- char csi_leader[CSI_LEADER_MAX];
-
- int csi_argi;
- long csi_args[CSI_ARGS_MAX];
+ union {
+ struct {
+ int leaderlen;
+ char leader[CSI_LEADER_MAX];
+
+ int argi;
+ long args[CSI_ARGS_MAX];
+ } csi;
+ struct {
+ int command;
+ } osc;
+ struct {
+ int commandlen;
+ char command[CSI_LEADER_MAX];
+ } dcs;
+ } v;
const VTermParserCallbacks *callbacks;
void *cbdata;
- VTermParserStringType stringtype;
- char *strbuffer;
- size_t strbuffer_len;
- size_t strbuffer_cur;
+ int string_initial;
} parser;
// len == malloc()ed size; cur == number of valid bytes
diff --git a/src/libvterm/t/02parser.test b/src/libvterm/t/02parser.test
index 66d487dad..4a4a65bca 100644
--- a/src/libvterm/t/02parser.test
+++ b/src/libvterm/t/02parser.test
@@ -132,15 +132,23 @@ PUSH "\e[12\n;3X"
!OSC BEL
PUSH "\e]1;Hello\x07"
- osc "1;Hello"
+ osc [1 "Hello"]
!OSC ST (7bit)
PUSH "\e]1;Hello\e\\"
- osc "1;Hello"
+ osc [1 "Hello"]
!OSC ST (8bit)
PUSH "\x{9d}1;Hello\x9c"
- osc "1;Hello"
+ osc [1 "Hello"]
+
+!OSC in parts
+PUSH "\e]52;abc"
+ osc [52 "abc"
+PUSH "def"
+ osc "def"
+PUSH "ghi\e\\"
+ osc "ghi"]
!Escape cancels OSC, starts Escape
PUSH "\e]Something\e9"
@@ -152,20 +160,21 @@ PUSH "\e]12\x{18}AB"
!C0 in OSC interrupts and continues
PUSH "\e]2;\nBye\x07"
+ osc [2 ""
control 10
- osc "2;Bye"
+ osc "Bye"]
!DCS BEL
PUSH "\ePHello\x07"
- dcs "Hello"
+ dcs ["Hello"]
!DCS ST (7bit)
PUSH "\ePHello\e\\"
- dcs "Hello"
+ dcs ["Hello"]
!DCS ST (8bit)
PUSH "\x{90}Hello\x9c"
- dcs "Hello"
+ dcs ["Hello"]
!Escape cancels DCS, starts Escape
PUSH "\ePSomething\e9"
@@ -177,8 +186,9 @@ PUSH "\eP12\x{18}AB"
!C0 in OSC interrupts and continues
PUSH "\ePBy\ne\x07"
+ dcs ["By"
control 10
- dcs "Bye"
+ dcs "e"]
!NUL ignored
PUSH "\x{00}"
diff --git a/src/libvterm/t/18state_termprops.test b/src/libvterm/t/18state_termprops.test
index 9e6928a21..83c333f3f 100644
--- a/src/libvterm/t/18state_termprops.test
+++ b/src/libvterm/t/18state_termprops.test
@@ -33,4 +33,10 @@ PUSH "\e[3 q"
!Title
PUSH "\e]2;Here is my title\a"
- settermprop 4 "Here is my title"
+ settermprop 4 ["Here is my title"]
+
+!Title split write
+PUSH "\e]2;Here is"
+ settermprop 4 ["Here is"
+PUSH " another title\a"
+ settermprop 4 " another title"]
diff --git a/src/libvterm/t/29state_fallback.test b/src/libvterm/t/29state_fallback.test
index adf1c233a..7995dd1b6 100644
--- a/src/libvterm/t/29state_fallback.test
+++ b/src/libvterm/t/29state_fallback.test
@@ -12,8 +12,8 @@ PUSH "\e[?15;2z"
!Unrecognised OSC
PUSH "\e]27;Something\e\\"
- osc "27;Something"
+ osc [27 "Something"]
!Unrecognised DCS
PUSH "\ePz123\e\\"
- dcs "z123"
+ dcs ["z123"]
diff --git a/src/libvterm/t/68screen_termprops.test b/src/libvterm/t/68screen_termprops.test
index adf7ec2f7..bba6660e3 100644
--- a/src/libvterm/t/68screen_termprops.test
+++ b/src/libvterm/t/68screen_termprops.test
@@ -14,4 +14,4 @@ PUSH "\e[?25l"
!Title
PUSH "\e]2;Here is my title\a"
- settermprop 4 "Here is my title"
+ settermprop 4 ["Here is my title"]
diff --git a/src/libvterm/t/harness.c b/src/libvterm/t/harness.c
index 289d82928..92882fdfd 100644
--- a/src/libvterm/t/harness.c
+++ b/src/libvterm/t/harness.c
@@ -153,21 +153,44 @@ static int parser_csi(const char *leader, const long args[], int argcount, const
return 1;
}
-static int parser_osc(const char *command, size_t cmdlen, void *user UNUSED)
+static int parser_osc(int command, VTermStringFragment frag, void *user UNUSED)
{
printf("osc ");
- printhex(command, cmdlen);
+
+ if(frag.initial) {
+ if(command == -1)
+ printf("[");
+ else
+ printf("[%d;", command);
+ }
+
+ printhex(frag.str, frag.len);
+
+ if(frag.final)
+ printf("]");
+
printf("\n");
return 1;
}
-static int parser_dcs(const char *command, size_t cmdlen, void *user UNUSED)
+static int parser_dcs(const char *command, size_t commandlen, VTermStringFragment frag, void *user UNUSED)
{
-
printf("dcs ");
- printhex(command, cmdlen);
+
+ if(frag.initial) {
+ size_t i;
+ printf("[");
+ for(i = 0; i < commandlen; i++)
+ printf("%02x", command[i]);
+ }
+
+ printhex(frag.str, frag.len);
+
+ if(frag.final)
+ printf("]");
+
printf("\n");
return 1;
@@ -239,7 +262,8 @@ static int settermprop(VTermProp prop, VTermValue *val, void *user UNUSED)
printf("settermprop %d %d\n", prop, val->number);
return 1;
case VTERM_VALUETYPE_STRING:
- printf("settermprop %d \"%s\"\n", prop, val->string);
+ printf("settermprop %d %s\"%.*s\"%s\n", prop,
+ val->string.initial ? "[" : "", val->string.len, val->string.str, val->string.final ? "]" : "");
return 1;
case VTERM_VALUETYPE_COLOR:
printf("settermprop %d rgb(%d,%d,%d)\n", prop, val->color.red, val->color.green, val->color.blue);
@@ -262,7 +286,7 @@ static int state_putglyph(VTermGlyphInfo *info, VTermPos pos, void *user UNUSED)
return 1;
printf("putglyph ");
- for(i = 0; info->chars[i]; i++)
+ for(i = 0; i < VTERM_MAX_CHARS_PER_CELL && info->chars[i]; i++)
printf(i ? ",%x" : "%x", info->chars[i]);
printf(" %d %d,%d", info->width, pos.row, pos.col);
if(info->protected_cell)
diff --git a/src/libvterm/t/run-test.pl b/src/libvterm/t/run-test.pl
index 5f3c78d70..9f48c5cad 100644
--- a/src/libvterm/t/run-test.pl
+++ b/src/libvterm/t/run-test.pl
@@ -11,7 +11,8 @@ my $VALGRIND = 0;
my $EXECUTABLE = "t/.libs/harness";
GetOptions(
'valgrind|v+' => \$VALGRIND,
- 'executable|e=s' => \$EXECUTABLE
+ 'executable|e=s' => \$EXECUTABLE,
+ 'fail-early|F' => \(my $FAIL_EARLY),
) or exit 1;
my ( $hin, $hout, $hpid );
@@ -65,6 +66,7 @@ sub do_onetest
}
$exitcode = 1 if $fail_printed;
+ exit $exitcode if $exitcode and $FAIL_EARLY;
}
sub do_line
@@ -105,8 +107,15 @@ sub do_line
elsif( $line =~ m/^csi (\S+) (.*)$/ ) {
$line = sprintf "csi %02x %s", eval($1), $2; # TODO
}
- elsif( $line =~ m/^(escape|osc|dcs) (.*)$/ ) {
- $line = "$1 " . join "", map sprintf("%02x", $_), unpack "C*", eval($2);
+ elsif( $line =~ m/^(osc) (\[\d+)? *(.*?)(\]?)$/ ) {
+ my ( $cmd, $initial, $data, $final ) = ( $1, $2, $3, $4 );
+ $initial //= "";
+ $initial .= ";" if $initial =~ m/\d+/;
+
+ $line = "$cmd $initial" . join( "", map sprintf("%02x", $_), unpack "C*", eval($data) ) . "$final";
+ }
+ elsif( $line =~ m/^(escape|dcs) (\[?)(.*?)(\]?)$/ ) {
+ $line = "$1 $2" . join( "", map sprintf("%02x", $_), unpack "C*", eval($3) ) . "$4";
}
elsif( $line =~ m/^putglyph (\S+) (.*)$/ ) {
$line = "putglyph " . join( ",", map sprintf("%x", $_), eval($1) ) . " $2";
@@ -139,6 +148,7 @@ sub do_line
"# Expected: $want\n" .
"# Actual: $response\n";
$exitcode = 1;
+ exit $exitcode if $exitcode and $FAIL_EARLY;
}
}
# Assertions start with '?'
@@ -162,6 +172,7 @@ sub do_line
"# Expected: $expectation\n" .
"# Actual: $response\n";
$exitcode = 1;
+ exit $exitcode if $exitcode and $FAIL_EARLY;
}
}
# Test controls start with '$'
diff --git a/src/terminal.c b/src/terminal.c
index fa18d7022..5774dba9a 100644
--- a/src/terminal.c
+++ b/src/terminal.c
@@ -2998,22 +2998,27 @@ handle_settermprop(
void *user)
{
term_T *term = (term_T *)user;
+ char_u *strval = NULL;
switch (prop)
{
case VTERM_PROP_TITLE:
+ strval = vim_strnsave((char_u *)value->string.str,
+ (int)value->string.len);
+ if (strval == NULL)
+ break;
vim_free(term->tl_title);
// a blank title isn't useful, make it empty, so that "running" is
// displayed
- if (*skipwhite((char_u *)value->string) == NUL)
+ if (*skipwhite(strval) == NUL)
term->tl_title = NULL;
// Same as blank
else if (term->tl_arg0_cmd != NULL
- && STRNCMP(term->tl_arg0_cmd, (char_u *)value->string,
+ && STRNCMP(term->tl_arg0_cmd, strval,
(int)STRLEN(term->tl_arg0_cmd)) == 0)
term->tl_title = NULL;
// Empty corrupted data of winpty
- else if (STRNCMP(" - ", (char_u *)value->string, 4) == 0)
+ else if (STRNCMP(" - ", strval, 4) == 0)
term->tl_title = NULL;
#ifdef MSWIN
else if (!enc_utf8 && enc_codepage > 0)
@@ -3022,8 +3027,8 @@ handle_settermprop(
int length = 0;
MultiByteToWideChar_alloc(CP_UTF8, 0,
- (char*)value->string, (int)STRLEN(value->string),
- &ret, &length);
+ (char*)value->string.str,
+ (int)value->string.len, &ret, &length);
if (ret != NULL)
{
WideCharToMultiByte_alloc(enc_codepage, 0,
@@ -3034,7 +3039,10 @@ handle_settermprop(
}
#endif
else
- term->tl_title = vim_strsave((char_u *)value->string);
+ {
+ term->tl_title = vim_strsave(strval);
+ strval = NULL;
+ }
VIM_CLEAR(term->tl_status_text);
if (term == curbuf->b_term)
maketitle();
@@ -3057,7 +3065,11 @@ handle_settermprop(
break;
case VTERM_PROP_CURSORCOLOR:
- cursor_color_copy(&term->tl_cursor_color, (char_u*)value->string);
+ strval = vim_strnsave((char_u *)value->string.str,
+ (int)value->string.len);
+ if (strval == NULL)
+ break;
+ cursor_color_copy(&term->tl_cursor_color, strval);
may_set_cursor_props(term);
break;
@@ -3069,6 +3081,8 @@ handle_settermprop(
default:
break;
}
+ vim_free(strval);
+
// Always return 1, otherwise vterm doesn't store the value internally.
return 1;
}
@@ -4181,7 +4195,7 @@ handle_call_command(term_T *term, channel_T *channel, listitem_T *item)
* We recognize a terminal API command.
*/
static int
-parse_osc(const char *command, size_t cmdlen, void *user)
+parse_osc(int command, VTermStringFragment frag, void *user)
{
term_T *term = (term_T *)user;
js_read_T reader;
@@ -4190,10 +4204,10 @@ parse_osc(const char *command, size_t cmdlen, void *user)
: term->tl_job->jv_channel;
// We recognize only OSC 5 1 ; {command}
- if (cmdlen < 3 || STRNCMP(command, "51;", 3) != 0)
- return 0; // not handled
+ if (command != 51)
+ return 0;
- reader.js_buf = vim_strnsave((char_u *)command + 3, (int)(cmdlen - 3));
+ reader.js_buf = vim_strnsave((char_u *)frag.str, (int)(frag.len));
if (reader.js_buf == NULL)
return 1;
reader.js_fill = NULL;
diff --git a/src/version.c b/src/version.c
index 9c1f6f180..4c3cab753 100644
--- a/src/version.c
+++ b/src/version.c
@@ -747,6 +747,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 798,
+/**/
797,
/**/
796,