mirror of
https://github.com/neovim/neovim
synced 2025-07-15 16:51:49 +00:00
fix: stack overflow when too many csi args Problem: Crash when csi contains > 16 args. It's easy to trigger when you attempt to pipe external terminal scrollback buffer. ```sh nvim --clean --cmd "term printf '\e[38:2:59:66:97;48:2:36:40:59;58:2:224:175:104;4:3m'" ``` Solution: Increase buffer size.
293 lines
6.5 KiB
C
293 lines
6.5 KiB
C
#pragma once
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include "nvim/mbyte_defs.h"
|
|
#include "nvim/vterm/vterm_defs.h"
|
|
|
|
#ifdef DEBUG
|
|
# define DEBUG_LOG(...) fprintf(stderr, __VA_ARGS__)
|
|
#else
|
|
# define DEBUG_LOG(...)
|
|
#endif
|
|
|
|
#define ESC_S "\x1b"
|
|
|
|
#define INTERMED_MAX 16
|
|
|
|
#define CSI_ARGS_MAX 32
|
|
#define CSI_LEADER_MAX 16
|
|
|
|
#define BUFIDX_PRIMARY 0
|
|
#define BUFIDX_ALTSCREEN 1
|
|
|
|
#define KEY_ENCODING_DISAMBIGUATE 0x1
|
|
#define KEY_ENCODING_REPORT_EVENTS 0x2
|
|
#define KEY_ENCODING_REPORT_ALTERNATE 0x4
|
|
#define KEY_ENCODING_REPORT_ALL_KEYS 0x8
|
|
#define KEY_ENCODING_REPORT_ASSOCIATED 0x10
|
|
|
|
typedef struct VTermEncoding VTermEncoding;
|
|
typedef struct VTermKeyEncodingFlags VTermKeyEncodingFlags;
|
|
|
|
typedef struct {
|
|
VTermEncoding *enc;
|
|
|
|
// This size should be increased if required by other stateful encodings
|
|
char data[4 * sizeof(uint32_t)];
|
|
} VTermEncodingInstance;
|
|
|
|
struct VTermPen {
|
|
VTermColor fg;
|
|
VTermColor bg;
|
|
int uri;
|
|
unsigned bold:1;
|
|
unsigned underline:2;
|
|
unsigned italic:1;
|
|
unsigned blink:1;
|
|
unsigned reverse:1;
|
|
unsigned conceal:1;
|
|
unsigned strike:1;
|
|
unsigned font:4; // To store 0-9
|
|
unsigned small:1;
|
|
unsigned baseline:2;
|
|
};
|
|
|
|
// https://sw.kovidgoyal.net/kitty/keyboard-protocol/#progressive-enhancement
|
|
struct VTermKeyEncodingFlags {
|
|
bool disambiguate:1;
|
|
bool report_events:1;
|
|
bool report_alternate:1;
|
|
bool report_all_keys:1;
|
|
bool report_associated:1;
|
|
};
|
|
|
|
struct VTermKeyEncodingStack {
|
|
VTermKeyEncodingFlags items[16];
|
|
uint8_t size; ///< Number of items in the stack. This is at least 1 and at
|
|
///< most the length of the "items" array.
|
|
};
|
|
|
|
struct VTermState {
|
|
VTerm *vt;
|
|
|
|
const VTermStateCallbacks *callbacks;
|
|
void *cbdata;
|
|
|
|
const VTermStateFallbacks *fallbacks;
|
|
void *fbdata;
|
|
|
|
int rows;
|
|
int cols;
|
|
|
|
// Current cursor position
|
|
VTermPos pos;
|
|
|
|
int at_phantom; // True if we're on the "81st" phantom column to defer a wraparound
|
|
|
|
int scrollregion_top;
|
|
int scrollregion_bottom; // -1 means unbounded
|
|
#define SCROLLREGION_BOTTOM(state) ((state)->scrollregion_bottom > \
|
|
-1 ? (state)->scrollregion_bottom : (state)->rows)
|
|
int scrollregion_left;
|
|
#define SCROLLREGION_LEFT(state) ((state)->mode.leftrightmargin ? (state)->scrollregion_left : 0)
|
|
int scrollregion_right; // -1 means unbounded
|
|
#define SCROLLREGION_RIGHT(state) ((state)->mode.leftrightmargin \
|
|
&& (state)->scrollregion_right > \
|
|
-1 ? (state)->scrollregion_right : (state)->cols)
|
|
|
|
// Bitvector of tab stops
|
|
uint8_t *tabstops;
|
|
|
|
// Primary and Altscreen; lineinfos[1] is lazily allocated as needed
|
|
VTermLineInfo *lineinfos[2];
|
|
|
|
// lineinfo will == lineinfos[0] or lineinfos[1], depending on altscreen
|
|
VTermLineInfo *lineinfo;
|
|
#define ROWWIDTH(state, \
|
|
row) ((state)->lineinfo[(row)].doublewidth ? ((state)->cols / 2) : (state)->cols)
|
|
#define THISROWWIDTH(state) ROWWIDTH(state, (state)->pos.row)
|
|
|
|
// Mouse state
|
|
int mouse_col, mouse_row;
|
|
int mouse_buttons;
|
|
int mouse_flags;
|
|
#define MOUSE_WANT_CLICK 0x01
|
|
#define MOUSE_WANT_DRAG 0x02
|
|
#define MOUSE_WANT_MOVE 0x04
|
|
|
|
enum { MOUSE_X10, MOUSE_UTF8, MOUSE_SGR, MOUSE_RXVT, } mouse_protocol;
|
|
|
|
// Last glyph output, for Unicode recombining purposes
|
|
char grapheme_buf[MAX_SCHAR_SIZE];
|
|
size_t grapheme_len;
|
|
uint32_t grapheme_last; // last added UTF-32 char
|
|
GraphemeState grapheme_state;
|
|
int combine_width; // The width of the glyph above
|
|
VTermPos combine_pos; // Position before movement
|
|
|
|
struct {
|
|
unsigned keypad:1;
|
|
unsigned cursor:1;
|
|
unsigned autowrap:1;
|
|
unsigned insert:1;
|
|
unsigned newline:1;
|
|
unsigned cursor_visible:1;
|
|
unsigned cursor_blink:1;
|
|
unsigned cursor_shape:2;
|
|
unsigned alt_screen:1;
|
|
unsigned origin:1;
|
|
unsigned screen:1;
|
|
unsigned leftrightmargin:1;
|
|
unsigned bracketpaste:1;
|
|
unsigned report_focus:1;
|
|
unsigned theme_updates:1;
|
|
} mode;
|
|
|
|
VTermEncodingInstance encoding[4], encoding_utf8;
|
|
int gl_set, gr_set, gsingle_set;
|
|
|
|
struct VTermPen pen;
|
|
|
|
VTermColor default_fg;
|
|
VTermColor default_bg;
|
|
VTermColor colors[16]; // Store the 8 ANSI and the 8 ANSI high-brights only
|
|
|
|
int bold_is_highbright;
|
|
|
|
unsigned protected_cell : 1;
|
|
|
|
// Saved state under DEC mode 1048/1049
|
|
struct {
|
|
VTermPos pos;
|
|
struct VTermPen pen;
|
|
|
|
struct {
|
|
unsigned cursor_visible:1;
|
|
unsigned cursor_blink:1;
|
|
unsigned cursor_shape:2;
|
|
} mode;
|
|
} saved;
|
|
|
|
// Temporary state for DECRQSS parsing
|
|
union {
|
|
char decrqss[4];
|
|
struct {
|
|
uint16_t mask;
|
|
enum {
|
|
SELECTION_INITIAL,
|
|
SELECTION_SELECTED,
|
|
SELECTION_QUERY,
|
|
SELECTION_SET_INITIAL,
|
|
SELECTION_SET,
|
|
SELECTION_INVALID,
|
|
} state : 8;
|
|
uint32_t recvpartial;
|
|
uint32_t sendpartial;
|
|
} selection;
|
|
} tmp;
|
|
|
|
struct {
|
|
const VTermSelectionCallbacks *callbacks;
|
|
void *user;
|
|
char *buffer;
|
|
size_t buflen;
|
|
} selection;
|
|
|
|
// Maintain two stacks, one for primary screen and one for altscreen
|
|
struct VTermKeyEncodingStack key_encoding_stacks[2];
|
|
};
|
|
|
|
struct VTerm {
|
|
const VTermAllocatorFunctions *allocator;
|
|
void *allocdata;
|
|
|
|
int rows;
|
|
int cols;
|
|
|
|
struct {
|
|
unsigned utf8:1;
|
|
unsigned ctrl8bit:1;
|
|
} mode;
|
|
|
|
struct {
|
|
enum VTermParserState {
|
|
NORMAL,
|
|
CSI_LEADER,
|
|
CSI_ARGS,
|
|
CSI_INTERMED,
|
|
DCS_COMMAND,
|
|
// below here are the "string states"
|
|
OSC_COMMAND,
|
|
OSC,
|
|
DCS_VTERM,
|
|
APC,
|
|
PM,
|
|
SOS,
|
|
} state;
|
|
|
|
bool in_esc : 1;
|
|
|
|
int intermedlen;
|
|
char intermed[INTERMED_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;
|
|
|
|
bool string_initial;
|
|
|
|
bool emit_nul;
|
|
} parser;
|
|
|
|
// len == malloc()ed size; cur == number of valid bytes
|
|
|
|
VTermOutputCallback *outfunc;
|
|
void *outdata;
|
|
|
|
char *outbuffer;
|
|
size_t outbuffer_len;
|
|
size_t outbuffer_cur;
|
|
|
|
char *tmpbuffer;
|
|
size_t tmpbuffer_len;
|
|
|
|
VTermState *state;
|
|
VTermScreen *screen;
|
|
};
|
|
|
|
struct VTermEncoding {
|
|
void (*init)(VTermEncoding *enc, void *data);
|
|
void (*decode)(VTermEncoding *enc, void *data, uint32_t cp[], int *cpi, int cplen,
|
|
const char bytes[], size_t *pos, size_t len);
|
|
};
|
|
|
|
typedef enum {
|
|
ENC_UTF8,
|
|
ENC_SINGLE_94,
|
|
} VTermEncodingType;
|
|
|
|
enum {
|
|
C1_SS3 = 0x8f,
|
|
C1_DCS = 0x90,
|
|
C1_CSI = 0x9b,
|
|
C1_ST = 0x9c,
|
|
C1_OSC = 0x9d,
|
|
};
|