diff options
-rw-r--r-- | src/nvim/api/vim.c | 10 | ||||
-rw-r--r-- | src/nvim/charset.c | 7 | ||||
-rw-r--r-- | src/nvim/eval.c | 81 | ||||
-rw-r--r-- | src/nvim/ex_docmd.c | 3 | ||||
-rw-r--r-- | src/nvim/ex_getln.c | 94 | ||||
-rw-r--r-- | src/nvim/ex_getln.h | 12 | ||||
-rw-r--r-- | src/nvim/getchar.c | 40 | ||||
-rw-r--r-- | src/nvim/keymap.c | 178 | ||||
-rw-r--r-- | src/nvim/keymap.h | 10 | ||||
-rw-r--r-- | src/nvim/menu.c | 10 | ||||
-rw-r--r-- | src/nvim/option.c | 104 | ||||
-rw-r--r-- | src/nvim/os/input.c | 5 |
12 files changed, 325 insertions, 229 deletions
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 243806b3f1..46ac3c9022 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -116,8 +116,14 @@ String vim_replace_termcodes(String str, Boolean from_part, Boolean do_lt, } char *ptr = NULL; - replace_termcodes((char_u *)str.data, (char_u **)&ptr, - from_part, do_lt, special); + // Set 'cpoptions' the way we want it. + // FLAG_CPO_BSLASH set - backslashes are *not* treated specially + // FLAG_CPO_KEYCODE set - keycodes are *not* reverse-engineered + // FLAG_CPO_SPECI unset - <Key> sequences *are* interpreted + // The third from end parameter of replace_termcodes() is true so that the + // <lt> sequence is recognised - needed for a real backslash. + replace_termcodes((char_u *)str.data, str.size, (char_u **)&ptr, + from_part, do_lt, special, CPO_TO_CPO_FLAGS); return cstr_as_string(ptr); } diff --git a/src/nvim/charset.c b/src/nvim/charset.c index 83e2aaa6e6..d0dc7b66fc 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -1794,10 +1794,11 @@ bool vim_isblankline(char_u *lbuf) /// @param nptr Returns the signed result. /// @param unptr Returns the unsigned result. /// @param maxlen Max length of string to check. -void vim_str2nr(char_u *start, int *prep, int *len, int what, - long *nptr, unsigned long *unptr, int maxlen) +void vim_str2nr(const char_u *const start, int *const prep, int *const len, + const int what, long *const nptr, unsigned long *const unptr, + const int maxlen) { - char_u *ptr = start; + const char_u *ptr = start; int pre = 0; // default is decimal bool negative = false; unsigned long un = 0; diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 9553c7a7ed..8b04d9afa4 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -4740,13 +4740,14 @@ static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate) ++name; break; - /* Special key, e.g.: "\<C-W>" */ - case '<': extra = trans_special(&p, name, TRUE); + // Special key, e.g.: "\<C-W>" + case '<': + extra = trans_special((const char_u **) &p, STRLEN(p), name, true); if (extra != 0) { name += extra; break; } - /* FALLTHROUGH */ + // FALLTHROUGH default: MB_COPY_CHAR(p, name); break; @@ -10866,21 +10867,22 @@ static void f_hasmapto(typval_T *argvars, typval_T *rettv) */ static void f_histadd(typval_T *argvars, typval_T *rettv) { - int histype; + HistoryType histype; char_u *str; char_u buf[NUMBUFLEN]; - rettv->vval.v_number = FALSE; - if (check_restricted() || check_secure()) + rettv->vval.v_number = false; + if (check_restricted() || check_secure()) { return; - str = get_tv_string_chk(&argvars[0]); /* NULL on type error */ - histype = str != NULL ? get_histtype(str) : -1; - if (histype >= 0) { + } + str = get_tv_string_chk(&argvars[0]); // NULL on type error + histype = str != NULL ? get_histtype(str, STRLEN(str), false) : HIST_INVALID; + if (histype != HIST_INVALID) { str = get_tv_string_buf(&argvars[1], buf); if (*str != NUL) { init_history(); - add_to_history(histype, str, FALSE, NUL); - rettv->vval.v_number = TRUE; + add_to_history(histype, str, false, NUL); + rettv->vval.v_number = true; return; } } @@ -10895,20 +10897,21 @@ static void f_histdel(typval_T *argvars, typval_T *rettv) char_u buf[NUMBUFLEN]; char_u *str; - str = get_tv_string_chk(&argvars[0]); /* NULL on type error */ - if (str == NULL) + str = get_tv_string_chk(&argvars[0]); // NULL on type error + if (str == NULL) { n = 0; - else if (argvars[1].v_type == VAR_UNKNOWN) - /* only one argument: clear entire history */ - n = clr_history(get_histtype(str)); - else if (argvars[1].v_type == VAR_NUMBER) - /* index given: remove that entry */ - n = del_history_idx(get_histtype(str), - (int)get_tv_number(&argvars[1])); - else - /* string given: remove all matching entries */ - n = del_history_entry(get_histtype(str), - get_tv_string_buf(&argvars[1], buf)); + } else if (argvars[1].v_type == VAR_UNKNOWN) { + // only one argument: clear entire history + n = clr_history(get_histtype(str, STRLEN(str), false)); + } else if (argvars[1].v_type == VAR_NUMBER) { + // index given: remove that entry + n = del_history_idx(get_histtype(str, STRLEN(str), false), + (int) get_tv_number(&argvars[1])); + } else { + // string given: remove all matching entries + n = del_history_entry(get_histtype(str, STRLEN(str), false), + get_tv_string_buf(&argvars[1], buf)); + } rettv->vval.v_number = n; } @@ -10917,20 +10920,21 @@ static void f_histdel(typval_T *argvars, typval_T *rettv) */ static void f_histget(typval_T *argvars, typval_T *rettv) { - int type; + HistoryType type; int idx; char_u *str; - str = get_tv_string_chk(&argvars[0]); /* NULL on type error */ - if (str == NULL) + str = get_tv_string_chk(&argvars[0]); // NULL on type error + if (str == NULL) { rettv->vval.v_string = NULL; - else { - type = get_histtype(str); - if (argvars[1].v_type == VAR_UNKNOWN) + } else { + type = get_histtype(str, STRLEN(str), false); + if (argvars[1].v_type == VAR_UNKNOWN) { idx = get_history_idx(type); - else + } else { idx = (int)get_tv_number_chk(&argvars[1], NULL); - /* -1 on type error */ + } + // -1 on type error rettv->vval.v_string = vim_strsave(get_history_entry(type, idx)); } rettv->v_type = VAR_STRING; @@ -10945,11 +10949,13 @@ static void f_histnr(typval_T *argvars, typval_T *rettv) char_u *history = get_tv_string_chk(&argvars[0]); - i = history == NULL ? HIST_CMD - 1 : get_histtype(history); - if (i >= HIST_CMD && i < HIST_COUNT) + i = history == NULL ? HIST_CMD - 1 : get_histtype(history, STRLEN(history), + false); + if (i != HIST_INVALID) { i = get_history_idx(i); - else + } else { i = -1; + } rettv->vval.v_number = i; } @@ -12086,8 +12092,9 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) mode = get_map_mode(&which, 0); - keys = replace_termcodes(keys, &keys_buf, TRUE, TRUE, FALSE); - rhs = check_map(keys, mode, exact, FALSE, abbr, &mp, &buffer_local); + keys = replace_termcodes(keys, STRLEN(keys), &keys_buf, true, true, false, + CPO_TO_CPO_FLAGS); + rhs = check_map(keys, mode, exact, false, abbr, &mp, &buffer_local); xfree(keys_buf); if (!get_dict) { diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 2d316cb106..648a3a8487 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -4556,7 +4556,8 @@ static int uc_add_command(char_u *name, size_t name_len, char_u *rep, char_u *rep_buf = NULL; garray_T *gap; - replace_termcodes(rep, &rep_buf, FALSE, FALSE, FALSE); + replace_termcodes(rep, STRLEN(rep), &rep_buf, false, false, false, + CPO_TO_CPO_FLAGS); if (rep_buf == NULL) { /* Can't replace termcodes - try using the string as is */ rep_buf = vim_strsave(rep); diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index cffda1ca55..a9e9ee76d5 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -4272,20 +4272,33 @@ void globpath(char_u *path, char_u *file, garray_T *ga, int expand_options) * Command line history stuff * *********************************/ -/* - * Translate a history character to the associated type number. - */ -static int hist_char2type(int c) +/// Translate a history character to the associated type number +static HistoryType hist_char2type(const int c) + FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT { - if (c == ':') - return HIST_CMD; - if (c == '=') - return HIST_EXPR; - if (c == '@') - return HIST_INPUT; - if (c == '>') - return HIST_DEBUG; - return HIST_SEARCH; /* must be '?' or '/' */ + switch (c) { + case ':': { + return HIST_CMD; + } + case '=': { + return HIST_EXPR; + } + case '@': { + return HIST_INPUT; + } + case '>': { + return HIST_DEBUG; + } + case '/': + case '?': { + return HIST_SEARCH; + } + default: { + assert(false); + } + } + // Silence -Wreturn-type + return 0; } /* @@ -4454,28 +4467,38 @@ in_history ( return false; } -/* - * Convert history name (from table above) to its HIST_ equivalent. - * When "name" is empty, return "cmd" history. - * Returns -1 for unknown history name. - */ -int get_histtype(char_u *name) +/// Convert history name to its HIST_ equivalent +/// +/// Names are taken from the table above. When `name` is empty returns currently +/// active history or HIST_DEFAULT, depending on `return_default` argument. +/// +/// @param[in] name Converted name. +/// @param[in] len Name length. +/// @param[in] return_default Determines whether HIST_DEFAULT should be +/// returned or value based on `ccline.cmdfirstc`. +/// +/// @return Any value from HistoryType enum, including HIST_INVALID. May not +/// return HIST_DEFAULT unless return_default is true. +HistoryType get_histtype(const char_u *const name, const size_t len, + const bool return_default) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - int i; - int len = (int)STRLEN(name); - - /* No argument: use current history. */ - if (len == 0) - return hist_char2type(ccline.cmdfirstc); + // No argument: use current history. + if (len == 0) { + return return_default ? HIST_DEFAULT :hist_char2type(ccline.cmdfirstc); + } - for (i = 0; history_names[i] != NULL; ++i) - if (STRNICMP(name, history_names[i], len) == 0) + for (HistoryType i = 0; history_names[i] != NULL; i++) { + if (STRNICMP(name, history_names[i], len) == 0) { return i; + } + } - if (vim_strchr((char_u *)":=@>?/", name[0]) != NULL && name[1] == NUL) + if (vim_strchr((char_u *)":=@>?/", name[0]) != NULL && len == 1) { return hist_char2type(name[0]); + } - return -1; + return HIST_INVALID; } static int last_maptick = -1; /* last seen maptick */ @@ -4847,23 +4870,20 @@ void ex_history(exarg_T *eap) while (ASCII_ISALPHA(*end) || vim_strchr((char_u *)":=@>/?", *end) != NULL) end++; - i = *end; - *end = NUL; - histype1 = get_histtype(arg); - if (histype1 == -1) { - if (STRNICMP(arg, "all", STRLEN(arg)) == 0) { + histype1 = get_histtype(arg, end - arg, false); + if (histype1 == HIST_INVALID) { + if (STRNICMP(arg, "all", end - arg) == 0) { histype1 = 0; histype2 = HIST_COUNT-1; } else { - *end = i; EMSG(_(e_trailing)); return; } } else histype2 = histype1; - *end = i; - } else + } else { end = arg; + } if (!get_list_range(&end, &hisidx1, &hisidx2) || *end != NUL) { EMSG(_(e_trailing)); return; diff --git a/src/nvim/ex_getln.h b/src/nvim/ex_getln.h index 21da8b9d42..24eebdc303 100644 --- a/src/nvim/ex_getln.h +++ b/src/nvim/ex_getln.h @@ -27,11 +27,13 @@ /// Present history tables typedef enum { - HIST_CMD, ///< Colon commands. - HIST_SEARCH, ///< Search commands. - HIST_EXPR, ///< Expressions (e.g. from entering = register). - HIST_INPUT, ///< input() lines. - HIST_DEBUG, ///< Debug commands. + HIST_DEFAULT = -2, ///< Default (current) history. + HIST_INVALID = -1, ///< Unknown history. + HIST_CMD = 0, ///< Colon commands. + HIST_SEARCH, ///< Search commands. + HIST_EXPR, ///< Expressions (e.g. from entering = register). + HIST_INPUT, ///< input() lines. + HIST_DEBUG, ///< Debug commands. } HistoryType; /// Number of history tables diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 437495faa4..dba83d3684 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -2688,22 +2688,24 @@ do_map ( goto theend; } - /* - * If mapping has been given as ^V<C_UP> say, then replace the term codes - * with the appropriate two bytes. If it is a shifted special key, unshift - * it too, giving another two bytes. - * replace_termcodes() may move the result to allocated memory, which - * needs to be freed later (*keys_buf and *arg_buf). - * replace_termcodes() also removes CTRL-Vs and sometimes backslashes. - */ - if (haskey) - keys = replace_termcodes(keys, &keys_buf, TRUE, TRUE, special); + // If mapping has been given as ^V<C_UP> say, then replace the term codes + // with the appropriate two bytes. If it is a shifted special key, unshift + // it too, giving another two bytes. + // replace_termcodes() may move the result to allocated memory, which + // needs to be freed later (*keys_buf and *arg_buf). + // replace_termcodes() also removes CTRL-Vs and sometimes backslashes. + if (haskey) { + keys = replace_termcodes(keys, STRLEN(keys), &keys_buf, true, true, special, + CPO_TO_CPO_FLAGS); + } orig_rhs = rhs; if (hasarg) { - if (STRICMP(rhs, "<nop>") == 0) /* "<Nop>" means nothing */ + if (STRICMP(rhs, "<nop>") == 0) { // "<Nop>" means nothing rhs = (char_u *)""; - else - rhs = replace_termcodes(rhs, &arg_buf, FALSE, TRUE, special); + } else { + rhs = replace_termcodes(rhs, STRLEN(rhs), &arg_buf, false, true, special, + CPO_TO_CPO_FLAGS); + } } /* @@ -3270,7 +3272,8 @@ int map_to_exists(char_u *str, char_u *modechars, int abbr) char_u *buf; int retval; - rhs = replace_termcodes(str, &buf, FALSE, TRUE, FALSE); + rhs = replace_termcodes(str, STRLEN(str), &buf, false, true, false, + CPO_TO_CPO_FLAGS); if (vim_strchr(modechars, 'n') != NULL) mode |= NORMAL; @@ -3465,7 +3468,7 @@ int ExpandMappings(regmatch_T *regmatch, int *num_file, char_u ***file) mp = maphash[hash]; for (; mp; mp = mp->m_next) { if (mp->m_mode & expand_mapmodes) { - p = translate_mapping(mp->m_keys, TRUE); + p = translate_mapping(mp->m_keys, true, CPO_TO_CPO_FLAGS); if (p != NULL && vim_regexec(regmatch, p, (colnr_T)0)) { if (round == 1) ++count; @@ -4190,14 +4193,15 @@ void add_map(char_u *map, int mode) // Returns NULL when there is a problem. static char_u * translate_mapping ( char_u *str, - int expmap // TRUE when expanding mappings on command-line + int expmap, // True when expanding mappings on command-line + int cpo_flags // Value of various flags present in &cpo ) { garray_T ga; ga_init(&ga, 1, 40); - int cpo_bslash = (vim_strchr(p_cpo, CPO_BSLASH) != NULL); - int cpo_special = (vim_strchr(p_cpo, CPO_SPECI) != NULL); + bool cpo_bslash = !(cpo_flags&FLAG_CPO_BSLASH); + bool cpo_special = !(cpo_flags&FLAG_CPO_SPECI); for (; *str; ++str) { int c = *str; diff --git a/src/nvim/keymap.c b/src/nvim/keymap.c index 6c75d8bdf4..99e94fc60f 100644 --- a/src/nvim/keymap.c +++ b/src/nvim/keymap.c @@ -482,26 +482,28 @@ char_u *get_special_key_name(int c, int modifiers) return string; } -/* - * Try translating a <> name at (*srcp)[] to dst[]. - * Return the number of characters added to dst[], zero for no match. - * If there is a match, srcp is advanced to after the <> name. - * dst[] must be big enough to hold the result (up to six characters)! - */ -unsigned int -trans_special ( - char_u **srcp, - char_u *dst, - int keycode /* prefer key code, e.g. K_DEL instead of DEL */ -) +/// Try translating a <> name +/// +/// @param[in,out] srcp Source from which <> are translated. Is advanced to +/// after the <> name if there is a match. +/// @param[in] src_len Length of the srcp. +/// @param[out] dst Location where translation result will be kept. Must have +/// at least six bytes. +/// @param[in] keycode Prefer key code, e.g. K_DEL in place of DEL. +/// +/// @return Number of characters added to dst, zero for no match. +unsigned int trans_special(const char_u **srcp, const size_t src_len, + char_u *const dst, const bool keycode) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { int modifiers = 0; int key; unsigned int dlen = 0; - key = find_special_key(srcp, &modifiers, keycode, FALSE); - if (key == 0) + key = find_special_key(srcp, src_len, &modifiers, keycode, false); + if (key == 0) { return 0; + } /* Put the appropriate modifier in a string */ if (modifiers != 0) { @@ -514,69 +516,78 @@ trans_special ( dst[dlen++] = K_SPECIAL; dst[dlen++] = (char_u)KEY2TERMCAP0(key); dst[dlen++] = KEY2TERMCAP1(key); - } else if (has_mbyte && !keycode) + } else if (has_mbyte && !keycode) { dlen += (unsigned int)(*mb_char2bytes)(key, dst + dlen); - else if (keycode) { + } else if (keycode) { char_u *after = add_char2buf(key, dst + dlen); assert(after >= dst && (uintmax_t)(after - dst) <= UINT_MAX); dlen = (unsigned int)(after - dst); - } - else + } else { dst[dlen++] = (char_u)key; + } return dlen; } -// Try translating a <> name at (*srcp)[], return the key and modifiers. -// srcp is advanced to after the <> name. -// returns 0 if there is no match. -int find_special_key( - char_u **srcp, - int *modp, - int keycode, // prefer key code, e.g. K_DEL instead of DEL - int keep_x_key // don't translate xHome to Home key -) +/// Try translating a <> name +/// +/// @param[in,out] srcp Translated <> name. Is advanced to after the <> name. +/// @param[in] src_len srcp length. +/// @param[out] modp Location where information about modifiers is saved. +/// @param[in] keycode Prefer key code, e.g. K_DEL in place of DEL. +/// @param[in] keep_x_key Don’t translate xHome to Home key. +/// +/// @return Key and modifiers or 0 if there is no match. +int find_special_key(const char_u **srcp, const size_t src_len, int *const modp, + const bool keycode, const bool keep_x_key) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { - char_u *last_dash; - char_u *end_of_name; - char_u *src; - char_u *bp; + const char_u *last_dash; + const char_u *end_of_name; + const char_u *src; + const char_u *bp; + const char_u *const end = *srcp + src_len - 1; int modifiers; int bit; int key; unsigned long n; int l; + if (src_len == 0) { + return 0; + } + src = *srcp; - if (src[0] != '<') + if (src[0] != '<') { return 0; + } // Find end of modifier list last_dash = src; - for (bp = src + 1; *bp == '-' || vim_isIDc(*bp); bp++) { + for (bp = src + 1; bp <= end && (*bp == '-' || vim_isIDc(*bp)); bp++) { if (*bp == '-') { last_dash = bp; - if (bp[1] != NUL) { + if (bp + 1 <= end) { if (has_mbyte) { - l = mb_ptr2len(bp + 1); + l = mb_ptr2len_len(bp + 1, (int) (end - bp) + 1); } else { l = 1; } - if (bp[l + 1] == '>') { - bp += l; // anything accepted, like <C-?> + if (end - bp > l && bp[l + 1] == '>') { + bp += l; // anything accepted, like <C-?> } } } - if (bp[0] == 't' && bp[1] == '_' && bp[2] && bp[3]) { - bp += 3; // skip t_xx, xx may be '-' or '>' - } else if (STRNICMP(bp, "char-", 5) == 0) { + if (end - bp > 3 && bp[0] == 't' && bp[1] == '_') { + bp += 3; // skip t_xx, xx may be '-' or '>' + } else if (end - bp > 4 && STRNICMP(bp, "char-", 5) == 0) { vim_str2nr(bp + 5, NULL, &l, STR2NR_ALL, NULL, NULL, 0); bp += l + 5; break; } } - if (*bp == '>') { /* found matching '>' */ + if (bp <= end && *bp == '>') { // found matching '>' end_of_name = bp + 1; /* Which modifiers are given? */ @@ -696,7 +707,7 @@ int find_special_key_in_table(int c) * termcap name. * Return the key code, or 0 if not found. */ -int get_special_key_code(char_u *name) +int get_special_key_code(const char_u *name) { char_u *table_name; int i, j; @@ -730,50 +741,58 @@ int get_mouse_button(int code, bool *is_click, bool *is_drag) return 0; /* Shouldn't get here */ } -// Replace any terminal code strings in from[] with the equivalent internal -// vim representation. This is used for the "from" and "to" part of a -// mapping, and the "to" part of a menu command. -// Any strings like "<C-UP>" are also replaced, unless 'cpoptions' contains -// '<'. -// K_SPECIAL by itself is replaced by K_SPECIAL KS_SPECIAL KE_FILLER. -// -// The replacement is done in result[] and finally copied into allocated -// memory. If this all works well *bufp is set to the allocated memory and a -// pointer to it is returned. If something fails *bufp is set to NULL and from -// is returned. -// -// CTRL-V characters are removed. When "from_part" is TRUE, a trailing CTRL-V -// is included, otherwise it is removed (for ":map xx ^V", maps xx to -// nothing). When 'cpoptions' does not contain 'B', a backslash can be used -// instead of a CTRL-V. -char_u * replace_termcodes ( - char_u *from, - char_u **bufp, - int from_part, - int do_lt, // also translate <lt> - int special // always accept <key> notation -) +/// Replace any terminal code strings with the equivalent internal +/// representation +/// +/// This is used for the "from" and "to" part of a mapping, and the "to" part of +/// a menu command. Any strings like "<C-UP>" are also replaced, unless +/// 'cpoptions' contains '<'. K_SPECIAL by itself is replaced by K_SPECIAL +/// KS_SPECIAL KE_FILLER. +/// +/// @param[in] from What characters to replace. +/// @param[in] from_len Length of the "from" argument. +/// @param[out] bufp Location where results were saved in case of success +/// (allocated). Will be set to NULL in case of failure. +/// @param[in] do_lt If true, also translate <lt>. +/// @param[in] from_part If true, trailing <C-v> is included, otherwise it is +/// removed (to make ":map xx ^V" map xx to nothing). +/// When cpo_flags contains #FLAG_CPO_BSLASH, a backslash +/// can be used in place of <C-v>. All other <C-v> +/// characters are removed. +/// @param[in] special If true, always accept <key> notation. +/// @param[in] cpo_flags Relevant flags derived from p_cpo, see +/// #CPO_TO_CPO_FLAGS. +/// +/// @return Pointer to an allocated memory in case of success, "from" in case of +/// failure. In case of success returned pointer is also saved to +/// "bufp". +char_u *replace_termcodes(const char_u *from, const size_t from_len, + char_u **bufp, const bool from_part, const bool do_lt, + const bool special, int cpo_flags) + FUNC_ATTR_NONNULL_ALL { ssize_t i; size_t slen; char_u key; size_t dlen = 0; - char_u *src; + const char_u *src; + const char_u *const end = from + from_len - 1; int do_backslash; // backslash is a special character int do_special; // recognize <> key codes char_u *result; // buffer for resulting string - do_backslash = (vim_strchr(p_cpo, CPO_BSLASH) == NULL); - do_special = (vim_strchr(p_cpo, CPO_SPECI) == NULL) || special; + do_backslash = !(cpo_flags&FLAG_CPO_BSLASH); + do_special = !(cpo_flags&FLAG_CPO_SPECI) || special; // Allocate space for the translation. Worst case a single character is // replaced by 6 bytes (shifted special key), plus a NUL at the end. - result = xmalloc(STRLEN(from) * 6 + 1); + result = xmalloc(from_len * 6 + 1); src = from; // Check for #n at start only: function key n - if (from_part && src[0] == '#' && ascii_isdigit(src[1])) { // function key + if (from_part && from_len > 1 && src[0] == '#' + && ascii_isdigit(src[1])) { // function key result[dlen++] = K_SPECIAL; result[dlen++] = 'k'; if (src[1] == '0') { @@ -785,13 +804,14 @@ char_u * replace_termcodes ( } // Copy each byte from *from to result[dlen] - while (*src != NUL) { + while (src <= end) { // If 'cpoptions' does not contain '<', check for special key codes, // like "<C-S-LeftMouse>" - if (do_special && (do_lt || STRNCMP(src, "<lt>", 4) != 0)) { + if (do_special && (do_lt || ((end - src) >= 3 + && STRNCMP(src, "<lt>", 4) != 0))) { // Replace <SID> by K_SNR <script-nr> _. // (room: 5 * 6 = 30 bytes; needed: 3 + <nr> + 1 <= 14) - if (STRNICMP(src, "<SID>", 5) == 0) { + if (end - src >= 4 && STRNICMP(src, "<SID>", 5) == 0) { if (current_SID <= 0) { EMSG(_(e_usingsid)); } else { @@ -806,7 +826,7 @@ char_u * replace_termcodes ( } } - slen = trans_special(&src, result + dlen, TRUE); + slen = trans_special(&src, (size_t) (end - src) + 1, result + dlen, true); if (slen) { dlen += slen; continue; @@ -819,10 +839,10 @@ char_u * replace_termcodes ( // Replace <Leader> by the value of "mapleader". // Replace <LocalLeader> by the value of "maplocalleader". // If "mapleader" or "maplocalleader" isn't set use a backslash. - if (STRNICMP(src, "<Leader>", 8) == 0) { + if (end - src >= 7 && STRNICMP(src, "<Leader>", 8) == 0) { len = 8; p = get_var_value((char_u *)"g:mapleader"); - } else if (STRNICMP(src, "<LocalLeader>", 13) == 0) { + } else if (end - src >= 12 && STRNICMP(src, "<LocalLeader>", 13) == 0) { len = 13; p = get_var_value((char_u *)"g:maplocalleader"); } else { @@ -851,8 +871,8 @@ char_u * replace_termcodes ( // If 'cpoptions' does not contain 'B', also accept a backslash. key = *src; if (key == Ctrl_V || (do_backslash && key == '\\')) { - ++src; // skip CTRL-V or backslash - if (*src == NUL) { + src++; // skip CTRL-V or backslash + if (src > end) { if (from_part) { result[dlen++] = key; } @@ -861,7 +881,7 @@ char_u * replace_termcodes ( } // skip multibyte char correctly - for (i = (*mb_ptr2len)(src); i > 0; --i) { + for (i = (*mb_ptr2len_len)(src, (int) (end - src) + 1); i > 0; i--) { // If the character is K_SPECIAL, replace it with K_SPECIAL // KS_SPECIAL KE_FILLER. // If compiled with the GUI replace CSI with K_CSI. diff --git a/src/nvim/keymap.h b/src/nvim/keymap.h index 8f9980c6b4..bb8ba84a6a 100644 --- a/src/nvim/keymap.h +++ b/src/nvim/keymap.h @@ -1,6 +1,8 @@ #ifndef NVIM_KEYMAP_H #define NVIM_KEYMAP_H +#include "nvim/strings.h" + /* * Keycode definitions for special keys. * @@ -461,6 +463,14 @@ enum key_extra { // This is a total of 6 tokens, and is currently the longest one possible. #define MAX_KEY_CODE_LEN 6 +#define FLAG_CPO_BSLASH 0x01 +#define FLAG_CPO_SPECI 0x02 +#define CPO_TO_CPO_FLAGS (((vim_strchr(p_cpo, CPO_BSLASH) == NULL) \ + ? 0 \ + : FLAG_CPO_BSLASH)| \ + (vim_strchr(p_cpo, CPO_SPECI) == NULL \ + ? 0 \ + : FLAG_CPO_SPECI)) #ifdef INCLUDE_GENERATED_DECLARATIONS # include "keymap.h.generated.h" diff --git a/src/nvim/menu.c b/src/nvim/menu.c index 91a72abfc5..3c2394d579 100644 --- a/src/nvim/menu.c +++ b/src/nvim/menu.c @@ -215,10 +215,12 @@ ex_menu ( if (STRICMP(map_to, "<nop>") == 0) { /* "<Nop>" means nothing */ map_to = (char_u *)""; map_buf = NULL; - } else if (modes & MENU_TIP_MODE) - map_buf = NULL; /* Menu tips are plain text. */ - else - map_to = replace_termcodes(map_to, &map_buf, FALSE, TRUE, special); + } else if (modes & MENU_TIP_MODE) { + map_buf = NULL; // Menu tips are plain text. + } else { + map_to = replace_termcodes(map_to, STRLEN(map_to), &map_buf, false, true, + special, CPO_TO_CPO_FLAGS); + } menuarg.modes = modes; menuarg.noremap[0] = noremap; menuarg.silent[0] = silent; diff --git a/src/nvim/option.c b/src/nvim/option.c index 3376348d6f..b255d47c18 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -1177,28 +1177,27 @@ do_set ( errmsg = e_invarg; goto skip; } - arg[len] = NUL; /* put NUL after name */ - if (arg[1] == 't' && arg[2] == '_') /* could be term code */ - opt_idx = findoption(arg + 1); - arg[len++] = '>'; /* restore '>' */ - if (opt_idx == -1) + if (arg[1] == 't' && arg[2] == '_') { // could be term code + opt_idx = findoption_len(arg + 1, (size_t) (len - 1)); + } + len++; + if (opt_idx == -1) { key = find_key_option(arg + 1); + } } else { len = 0; - /* - * The two characters after "t_" may not be alphanumeric. - */ - if (arg[0] == 't' && arg[1] == '_' && arg[2] && arg[3]) + // The two characters after "t_" may not be alphanumeric. + if (arg[0] == 't' && arg[1] == '_' && arg[2] && arg[3]) { len = 4; - else - while (ASCII_ISALNUM(arg[len]) || arg[len] == '_') - ++len; - nextchar = arg[len]; - arg[len] = NUL; /* put NUL after name */ - opt_idx = findoption(arg); - arg[len] = nextchar; /* restore nextchar */ - if (opt_idx == -1) + } else { + while (ASCII_ISALNUM(arg[len]) || arg[len] == '_') { + len++; + } + } + opt_idx = findoption_len(arg, (size_t) len); + if (opt_idx == -1) { key = find_key_option(arg); + } } /* remember character after option name */ @@ -2965,7 +2964,8 @@ did_set_string_option ( /* 'pastetoggle': translate key codes like in a mapping */ else if (varp == &p_pt) { if (*p_pt) { - (void)replace_termcodes(p_pt, &p, TRUE, TRUE, FALSE); + (void)replace_termcodes(p_pt, STRLEN(p_pt), &p, true, true, false, + CPO_TO_CPO_FLAGS); if (p != NULL) { if (new_value_alloced) free_string_option(p_pt); @@ -4303,14 +4303,16 @@ static void check_redraw(uint32_t flags) redraw_all_later(NOT_VALID); } -/* - * Find index for option 'arg'. - * Return -1 if not found. - */ -static int findoption(char_u *arg) +/// Find index for named option +/// +/// @param[in] arg Option to find index for. +/// @param[in] len Length of the option. +/// +/// @return Index of the option or -1 if option was not found. +int findoption_len(const char_u *const arg, const size_t len) { - char *s, *p; - static short quick_tab[27] = {0, 0}; /* quick access table */ + char *s, *p; + static int quick_tab[27] = { 0, 0 }; // quick access table int is_term_opt; /* @@ -4334,25 +4336,31 @@ static int findoption(char_u *arg) /* * Check for name starting with an illegal character. */ - if (arg[0] < 'a' || arg[0] > 'z') + if (len == 0 || arg[0] < 'a' || arg[0] > 'z') { return -1; + } int opt_idx; - is_term_opt = (arg[0] == 't' && arg[1] == '_'); - if (is_term_opt) + is_term_opt = (len > 2 && arg[0] == 't' && arg[1] == '_'); + if (is_term_opt) { opt_idx = quick_tab[26]; - else + } else { opt_idx = quick_tab[CharOrdLow(arg[0])]; + } + // Match full name for (; (s = options[opt_idx].fullname) != NULL; opt_idx++) { - if (STRCMP(arg, s) == 0) /* match full name */ + if (STRNCMP(arg, s, len) == 0 && s[len] == NUL) { break; + } } if (s == NULL && !is_term_opt) { opt_idx = quick_tab[CharOrdLow(arg[0])]; + // Match short name for (; options[opt_idx].fullname != NULL; opt_idx++) { s = options[opt_idx].shortname; - if (s != NULL && STRCMP(arg, s) == 0) /* match short name */ + if (s != NULL && STRNCMP(arg, s, len) == 0 && s[len] == NUL) { break; + } s = NULL; } } @@ -4420,6 +4428,15 @@ bool set_tty_option(char *name, char *value) } /* + * Find index for option 'arg'. + * Return -1 if not found. + */ +static int findoption(char_u *arg) +{ + return findoption_len(arg, STRLEN(arg)); +} + +/* * Get the value for an option. * * Returns: @@ -4675,27 +4692,32 @@ char_u *get_highlight_default(void) /* * Translate a string like "t_xx", "<t_xx>" or "<S-Tab>" to a key number. */ -static int find_key_option(char_u *arg) +int find_key_option_len(const char_u *arg, size_t len) { int key; int modifiers; - /* - * Don't use get_special_key_code() for t_xx, we don't want it to call - * add_termcap_entry(). - */ - if (arg[0] == 't' && arg[1] == '_' && arg[2] && arg[3]) + // Don't use get_special_key_code() for t_xx, we don't want it to call + // add_termcap_entry(). + if (len >= 4 && arg[0] == 't' && arg[1] == '_') { key = TERMCAP2KEY(arg[2], arg[3]); - else { - --arg; /* put arg at the '<' */ + } else { + arg--; // put arg at the '<' modifiers = 0; - key = find_special_key(&arg, &modifiers, TRUE, TRUE); - if (modifiers) /* can't handle modifiers here */ + key = find_special_key(&arg, len + 1, &modifiers, true, true); + if (modifiers) { // can't handle modifiers here key = 0; + } } return key; } +static int find_key_option(const char_u *arg) +{ + return find_key_option_len(arg, STRLEN(arg)); +} + + /* * if 'all' == 0: show changed options * if 'all' == 1: show all normal options diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c index f317fd6b5a..e0826aa272 100644 --- a/src/nvim/os/input.c +++ b/src/nvim/os/input.c @@ -175,8 +175,9 @@ size_t input_enqueue(String keys) char *ptr = keys.data, *end = ptr + keys.size; while (rbuffer_space(input_buffer) >= 6 && ptr < end) { - uint8_t buf[6] = {0}; - unsigned int new_size = trans_special((uint8_t **)&ptr, buf, true); + uint8_t buf[6] = { 0 }; + unsigned int new_size = trans_special((const uint8_t **)&ptr, keys.size, + buf, true); if (new_size) { new_size = handle_mouse_event(&ptr, buf, new_size); |