neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

keycodes.c (30047B)


      1 #include <assert.h>
      2 #include <inttypes.h>
      3 #include <limits.h>
      4 #include <stdbool.h>
      5 #include <stdio.h>
      6 #include <string.h>
      7 #include <uv.h>
      8 
      9 #include "nvim/api/private/defs.h"
     10 #include "nvim/ascii_defs.h"
     11 #include "nvim/charset.h"
     12 #include "nvim/errors.h"
     13 #include "nvim/eval/typval_defs.h"
     14 #include "nvim/eval/vars.h"
     15 #include "nvim/gettext_defs.h"
     16 #include "nvim/globals.h"
     17 #include "nvim/keycodes.h"
     18 #include "nvim/macros_defs.h"
     19 #include "nvim/mbyte.h"
     20 #include "nvim/mbyte_defs.h"
     21 #include "nvim/memory.h"
     22 #include "nvim/message.h"
     23 #include "nvim/mouse.h"
     24 #include "nvim/option_vars.h"
     25 #include "nvim/strings.h"
     26 
     27 #include "keycode_names.generated.h"
     28 #include "keycodes.c.generated.h"
     29 
     30 // Some useful tables.
     31 
     32 static const struct modmasktable {
     33  uint16_t mod_mask;  ///< Bit-mask for particular key modifier.
     34  uint16_t mod_flag;  ///< Bit(s) for particular key modifier.
     35  char name;  ///< Single letter name of modifier.
     36 } mod_mask_table[] = {
     37  { MOD_MASK_ALT,              MOD_MASK_ALT,           'M' },
     38  { MOD_MASK_META,             MOD_MASK_META,          'T' },
     39  { MOD_MASK_CTRL,             MOD_MASK_CTRL,          'C' },
     40  { MOD_MASK_SHIFT,            MOD_MASK_SHIFT,         'S' },
     41  { MOD_MASK_MULTI_CLICK,      MOD_MASK_2CLICK,        '2' },
     42  { MOD_MASK_MULTI_CLICK,      MOD_MASK_3CLICK,        '3' },
     43  { MOD_MASK_MULTI_CLICK,      MOD_MASK_4CLICK,        '4' },
     44  { MOD_MASK_CMD,              MOD_MASK_CMD,           'D' },
     45  // 'A' must be the last one
     46  { MOD_MASK_ALT,              MOD_MASK_ALT,           'A' },
     47  { 0, 0, NUL }
     48  // NOTE: when adding an entry, update MAX_KEY_NAME_LEN!
     49 };
     50 
     51 // Shifted key terminal codes and their unshifted equivalent.
     52 // Don't add mouse codes here, they are handled separately!
     53 
     54 #define MOD_KEYS_ENTRY_SIZE 5
     55 
     56 static uint8_t modifier_keys_table[] = {
     57  //  mod mask      with modifier               without modifier
     58  MOD_MASK_SHIFT, '&', '9',                   '@', '1',         // begin
     59  MOD_MASK_SHIFT, '&', '0',                   '@', '2',         // cancel
     60  MOD_MASK_SHIFT, '*', '1',                   '@', '4',         // command
     61  MOD_MASK_SHIFT, '*', '2',                   '@', '5',         // copy
     62  MOD_MASK_SHIFT, '*', '3',                   '@', '6',         // create
     63  MOD_MASK_SHIFT, '*', '4',                   'k', 'D',         // delete char
     64  MOD_MASK_SHIFT, '*', '5',                   'k', 'L',         // delete line
     65  MOD_MASK_SHIFT, '*', '7',                   '@', '7',         // end
     66  MOD_MASK_CTRL,  KS_EXTRA, KE_C_END,         '@', '7',         // end
     67  MOD_MASK_SHIFT, '*', '9',                   '@', '9',         // exit
     68  MOD_MASK_SHIFT, '*', '0',                   '@', '0',         // find
     69  MOD_MASK_SHIFT, '#', '1',                   '%', '1',         // help
     70  MOD_MASK_SHIFT, '#', '2',                   'k', 'h',         // home
     71  MOD_MASK_CTRL,  KS_EXTRA, KE_C_HOME,        'k', 'h',         // home
     72  MOD_MASK_SHIFT, '#', '3',                   'k', 'I',         // insert
     73  MOD_MASK_SHIFT, '#', '4',                   'k', 'l',         // left arrow
     74  MOD_MASK_CTRL,  KS_EXTRA, KE_C_LEFT,        'k', 'l',         // left arrow
     75  MOD_MASK_SHIFT, '%', 'a',                   '%', '3',         // message
     76  MOD_MASK_SHIFT, '%', 'b',                   '%', '4',         // move
     77  MOD_MASK_SHIFT, '%', 'c',                   '%', '5',         // next
     78  MOD_MASK_SHIFT, '%', 'd',                   '%', '7',         // options
     79  MOD_MASK_SHIFT, '%', 'e',                   '%', '8',         // previous
     80  MOD_MASK_SHIFT, '%', 'f',                   '%', '9',         // print
     81  MOD_MASK_SHIFT, '%', 'g',                   '%', '0',         // redo
     82  MOD_MASK_SHIFT, '%', 'h',                   '&', '3',         // replace
     83  MOD_MASK_SHIFT, '%', 'i',                   'k', 'r',         // right arr.
     84  MOD_MASK_CTRL,  KS_EXTRA, KE_C_RIGHT,       'k', 'r',         // right arr.
     85  MOD_MASK_SHIFT, '%', 'j',                   '&', '5',         // resume
     86  MOD_MASK_SHIFT, '!', '1',                   '&', '6',         // save
     87  MOD_MASK_SHIFT, '!', '2',                   '&', '7',         // suspend
     88  MOD_MASK_SHIFT, '!', '3',                   '&', '8',         // undo
     89  MOD_MASK_SHIFT, KS_EXTRA, KE_S_UP,          'k', 'u',         // up arrow
     90  MOD_MASK_SHIFT, KS_EXTRA, KE_S_DOWN,        'k', 'd',         // down arrow
     91 
     92  // vt100 F1
     93  MOD_MASK_SHIFT, KS_EXTRA, KE_S_XF1,         KS_EXTRA, KE_XF1,
     94  MOD_MASK_SHIFT, KS_EXTRA, KE_S_XF2,         KS_EXTRA, KE_XF2,
     95  MOD_MASK_SHIFT, KS_EXTRA, KE_S_XF3,         KS_EXTRA, KE_XF3,
     96  MOD_MASK_SHIFT, KS_EXTRA, KE_S_XF4,         KS_EXTRA, KE_XF4,
     97 
     98  MOD_MASK_SHIFT, KS_EXTRA, KE_S_F1,          'k', '1',         // F1
     99  MOD_MASK_SHIFT, KS_EXTRA, KE_S_F2,          'k', '2',
    100  MOD_MASK_SHIFT, KS_EXTRA, KE_S_F3,          'k', '3',
    101  MOD_MASK_SHIFT, KS_EXTRA, KE_S_F4,          'k', '4',
    102  MOD_MASK_SHIFT, KS_EXTRA, KE_S_F5,          'k', '5',
    103  MOD_MASK_SHIFT, KS_EXTRA, KE_S_F6,          'k', '6',
    104  MOD_MASK_SHIFT, KS_EXTRA, KE_S_F7,          'k', '7',
    105  MOD_MASK_SHIFT, KS_EXTRA, KE_S_F8,          'k', '8',
    106  MOD_MASK_SHIFT, KS_EXTRA, KE_S_F9,          'k', '9',
    107  MOD_MASK_SHIFT, KS_EXTRA, KE_S_F10,         'k', ';',         // F10
    108 
    109  MOD_MASK_SHIFT, KS_EXTRA, KE_S_F11,         'F', '1',
    110  MOD_MASK_SHIFT, KS_EXTRA, KE_S_F12,         'F', '2',
    111  MOD_MASK_SHIFT, KS_EXTRA, KE_S_F13,         'F', '3',
    112  MOD_MASK_SHIFT, KS_EXTRA, KE_S_F14,         'F', '4',
    113  MOD_MASK_SHIFT, KS_EXTRA, KE_S_F15,         'F', '5',
    114  MOD_MASK_SHIFT, KS_EXTRA, KE_S_F16,         'F', '6',
    115  MOD_MASK_SHIFT, KS_EXTRA, KE_S_F17,         'F', '7',
    116  MOD_MASK_SHIFT, KS_EXTRA, KE_S_F18,         'F', '8',
    117  MOD_MASK_SHIFT, KS_EXTRA, KE_S_F19,         'F', '9',
    118  MOD_MASK_SHIFT, KS_EXTRA, KE_S_F20,         'F', 'A',
    119 
    120  MOD_MASK_SHIFT, KS_EXTRA, KE_S_F21,         'F', 'B',
    121  MOD_MASK_SHIFT, KS_EXTRA, KE_S_F22,         'F', 'C',
    122  MOD_MASK_SHIFT, KS_EXTRA, KE_S_F23,         'F', 'D',
    123  MOD_MASK_SHIFT, KS_EXTRA, KE_S_F24,         'F', 'E',
    124  MOD_MASK_SHIFT, KS_EXTRA, KE_S_F25,         'F', 'F',
    125  MOD_MASK_SHIFT, KS_EXTRA, KE_S_F26,         'F', 'G',
    126  MOD_MASK_SHIFT, KS_EXTRA, KE_S_F27,         'F', 'H',
    127  MOD_MASK_SHIFT, KS_EXTRA, KE_S_F28,         'F', 'I',
    128  MOD_MASK_SHIFT, KS_EXTRA, KE_S_F29,         'F', 'J',
    129  MOD_MASK_SHIFT, KS_EXTRA, KE_S_F30,         'F', 'K',
    130 
    131  MOD_MASK_SHIFT, KS_EXTRA, KE_S_F31,         'F', 'L',
    132  MOD_MASK_SHIFT, KS_EXTRA, KE_S_F32,         'F', 'M',
    133  MOD_MASK_SHIFT, KS_EXTRA, KE_S_F33,         'F', 'N',
    134  MOD_MASK_SHIFT, KS_EXTRA, KE_S_F34,         'F', 'O',
    135  MOD_MASK_SHIFT, KS_EXTRA, KE_S_F35,         'F', 'P',
    136  MOD_MASK_SHIFT, KS_EXTRA, KE_S_F36,         'F', 'Q',
    137  MOD_MASK_SHIFT, KS_EXTRA, KE_S_F37,         'F', 'R',
    138 
    139  // TAB pseudo code
    140  MOD_MASK_SHIFT, 'k', 'B',                   KS_EXTRA, KE_TAB,
    141 
    142  NUL
    143 };
    144 
    145 static struct mousetable {
    146  int pseudo_code;            // Code for pseudo mouse event
    147  int button;                 // Which mouse button is it?
    148  bool is_click;              // Is it a mouse button click event?
    149  bool is_drag;               // Is it a mouse drag event?
    150 } mouse_table[] = {
    151  { KE_LEFTMOUSE,        MOUSE_LEFT,     true,   false },
    152  { KE_LEFTDRAG,         MOUSE_LEFT,     false,  true },
    153  { KE_LEFTRELEASE,      MOUSE_LEFT,     false,  false },
    154  { KE_MIDDLEMOUSE,      MOUSE_MIDDLE,   true,   false },
    155  { KE_MIDDLEDRAG,       MOUSE_MIDDLE,   false,  true },
    156  { KE_MIDDLERELEASE,    MOUSE_MIDDLE,   false,  false },
    157  { KE_RIGHTMOUSE,       MOUSE_RIGHT,    true,   false },
    158  { KE_RIGHTDRAG,        MOUSE_RIGHT,    false,  true },
    159  { KE_RIGHTRELEASE,     MOUSE_RIGHT,    false,  false },
    160  { KE_X1MOUSE,          MOUSE_X1,       true,   false },
    161  { KE_X1DRAG,           MOUSE_X1,       false,  true },
    162  { KE_X1RELEASE,        MOUSE_X1,       false,  false },
    163  { KE_X2MOUSE,          MOUSE_X2,       true,   false },
    164  { KE_X2DRAG,           MOUSE_X2,       false,  true },
    165  { KE_X2RELEASE,        MOUSE_X2,       false,  false },
    166  // DRAG without CLICK
    167  { KE_MOUSEMOVE,        MOUSE_RELEASE,  false,  true },
    168  // RELEASE without CLICK
    169  { KE_IGNORE,           MOUSE_RELEASE,  false,  false },
    170  { 0,                   0,              0,      0 },
    171 };
    172 
    173 /// Return the modifier mask bit (#MOD_MASK_*) corresponding to mod name
    174 ///
    175 /// E.g. 'S' for shift, 'C' for ctrl.
    176 int name_to_mod_mask(int c)
    177  FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT
    178 {
    179  c = TOUPPER_ASC(c);
    180  for (size_t i = 0; mod_mask_table[i].mod_mask != 0; i++) {
    181    if (c == (uint8_t)mod_mask_table[i].name) {
    182      return mod_mask_table[i].mod_flag;
    183    }
    184  }
    185  return 0;
    186 }
    187 
    188 /// Check if there is a special key code for "key" with specified modifiers
    189 ///
    190 /// @param[in]  key  Initial key code.
    191 /// @param[in,out]  modifiers  Initial modifiers, is adjusted to have simplified
    192 ///                            modifiers.
    193 ///
    194 /// @return Simplified key code.
    195 int simplify_key(const int key, int *modifiers)
    196  FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
    197 {
    198  if (!(*modifiers & (MOD_MASK_SHIFT | MOD_MASK_CTRL))) {
    199    return key;
    200  }
    201 
    202  // TAB is a special case.
    203  if (key == TAB && (*modifiers & MOD_MASK_SHIFT)) {
    204    *modifiers &= ~MOD_MASK_SHIFT;
    205    return K_S_TAB;
    206  }
    207  const int key0 = KEY2TERMCAP0(key);
    208  const int key1 = KEY2TERMCAP1(key);
    209  for (int i = 0; modifier_keys_table[i] != NUL; i += MOD_KEYS_ENTRY_SIZE) {
    210    if (key0 == modifier_keys_table[i + 3]
    211        && key1 == modifier_keys_table[i + 4]
    212        && (*modifiers & modifier_keys_table[i])) {
    213      *modifiers &= ~modifier_keys_table[i];
    214      return TERMCAP2KEY(modifier_keys_table[i + 1],
    215                         modifier_keys_table[i + 2]);
    216    }
    217  }
    218  return key;
    219 }
    220 
    221 /// Change <xKey> to <Key>
    222 int handle_x_keys(const int key)
    223  FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT
    224 {
    225  switch (key) {
    226  case K_XUP:
    227    return K_UP;
    228  case K_XDOWN:
    229    return K_DOWN;
    230  case K_XLEFT:
    231    return K_LEFT;
    232  case K_XRIGHT:
    233    return K_RIGHT;
    234  case K_XHOME:
    235    return K_HOME;
    236  case K_ZHOME:
    237    return K_HOME;
    238  case K_XEND:
    239    return K_END;
    240  case K_ZEND:
    241    return K_END;
    242  case K_XF1:
    243    return K_F1;
    244  case K_XF2:
    245    return K_F2;
    246  case K_XF3:
    247    return K_F3;
    248  case K_XF4:
    249    return K_F4;
    250  case K_S_XF1:
    251    return K_S_F1;
    252  case K_S_XF2:
    253    return K_S_F2;
    254  case K_S_XF3:
    255    return K_S_F3;
    256  case K_S_XF4:
    257    return K_S_F4;
    258  }
    259  return key;
    260 }
    261 
    262 /// @return  a string which contains the name of the given key when the given modifiers are down.
    263 char *get_special_key_name(int c, int modifiers)
    264 {
    265  static char string[MAX_KEY_NAME_LEN + 1];
    266 
    267  string[0] = '<';
    268  int idx = 1;
    269 
    270  // Key that stands for a normal character.
    271  if (IS_SPECIAL(c) && KEY2TERMCAP0(c) == KS_KEY) {
    272    c = KEY2TERMCAP1(c);
    273  }
    274 
    275  // Translate shifted special keys into unshifted keys and set modifier.
    276  // Same for CTRL and ALT modifiers.
    277  if (IS_SPECIAL(c)) {
    278    for (int i = 0; modifier_keys_table[i] != 0; i += MOD_KEYS_ENTRY_SIZE) {
    279      if (KEY2TERMCAP0(c) == (int)modifier_keys_table[i + 1]
    280          && (int)KEY2TERMCAP1(c) == (int)modifier_keys_table[i + 2]) {
    281        modifiers |= modifier_keys_table[i];
    282        c = TERMCAP2KEY(modifier_keys_table[i + 3],
    283                        modifier_keys_table[i + 4]);
    284        break;
    285      }
    286    }
    287  }
    288 
    289  // try to find the key in the special key table
    290  int table_idx = find_special_key_in_table(c);
    291 
    292  // When not a known special key, and not a printable character, try to
    293  // extract modifiers.
    294  if (c > 0
    295      && utf_char2len(c) == 1) {
    296    if (table_idx < 0
    297        && (!vim_isprintc(c) || (c & 0x7f) == ' ')
    298        && (c & 0x80)) {
    299      c &= 0x7f;
    300      modifiers |= MOD_MASK_ALT;
    301      // try again, to find the un-alted key in the special key table
    302      table_idx = find_special_key_in_table(c);
    303    }
    304    if (table_idx < 0 && !vim_isprintc(c) && c < ' ') {
    305      c += '@';
    306      modifiers |= MOD_MASK_CTRL;
    307    }
    308  }
    309 
    310  // translate the modifier into a string
    311  for (int i = 0; mod_mask_table[i].name != 'A'; i++) {
    312    if ((modifiers & mod_mask_table[i].mod_mask)
    313        == mod_mask_table[i].mod_flag) {
    314      string[idx++] = mod_mask_table[i].name;
    315      string[idx++] = '-';
    316    }
    317  }
    318 
    319  if (table_idx < 0) {          // unknown special key, may output t_xx
    320    if (IS_SPECIAL(c)) {
    321      string[idx++] = 't';
    322      string[idx++] = '_';
    323      string[idx++] = (char)(uint8_t)KEY2TERMCAP0(c);
    324      string[idx++] = (char)(uint8_t)KEY2TERMCAP1(c);
    325    } else {
    326      // Not a special key, only modifiers, output directly.
    327      int len = utf_char2len(c);
    328      if (len == 1 && vim_isprintc(c)) {
    329        string[idx++] = (char)(uint8_t)c;
    330      } else if (len > 1) {
    331        idx += utf_char2bytes(c, string + idx);
    332      } else {
    333        char *s = transchar(c);
    334        while (*s) {
    335          string[idx++] = *s++;
    336        }
    337      }
    338    }
    339  } else {            // use name of special key
    340    const String *s = &key_names_table[table_idx].name;
    341    if ((int)s->size + idx + 2 <= MAX_KEY_NAME_LEN) {
    342      STRCPY(string + idx, s->data);
    343      idx += (int)s->size;
    344    }
    345  }
    346  string[idx++] = '>';
    347  string[idx] = NUL;
    348 
    349  return string;
    350 }
    351 
    352 /// Try translating a <> name ("keycode").
    353 ///
    354 /// @param[in,out]  srcp  Source from which <> are translated. Is advanced to
    355 ///                       after the <> name if there is a match.
    356 /// @param[in]  src_len  Length of the srcp.
    357 /// @param[out]  dst  Location where translation result will be kept. It must
    358 //                    be at least 19 bytes per "<x>" form.
    359 /// @param[in]  flags  FSK_ values
    360 /// @param[in]  escape_ks  escape K_SPECIAL bytes in the character
    361 /// @param[out]  did_simplify  found <C-H>, etc.
    362 ///
    363 /// @return Number of characters added to dst, zero for no match.
    364 unsigned trans_special(const char **const srcp, const size_t src_len, char *const dst,
    365                       const int flags, const bool escape_ks, bool *const did_simplify)
    366  FUNC_ATTR_NONNULL_ARG(1, 3) FUNC_ATTR_WARN_UNUSED_RESULT
    367 {
    368  int modifiers = 0;
    369  int key = find_special_key(srcp, src_len, &modifiers, flags, did_simplify);
    370  if (key == 0) {
    371    return 0;
    372  }
    373 
    374  return special_to_buf(key, modifiers, escape_ks, dst);
    375 }
    376 
    377 /// Put the character sequence for "key" with "modifiers" into "dst" and return
    378 /// the resulting length.
    379 /// When "escape_ks" is true escape K_SPECIAL bytes in the character.
    380 /// The sequence is not NUL terminated.
    381 /// This is how characters in a string are encoded.
    382 unsigned special_to_buf(int key, int modifiers, bool escape_ks, char *dst)
    383 {
    384  unsigned dlen = 0;
    385 
    386  // Put the appropriate modifier in a string.
    387  if (modifiers != 0) {
    388    dst[dlen++] = (char)(uint8_t)K_SPECIAL;
    389    dst[dlen++] = (char)(uint8_t)KS_MODIFIER;
    390    dst[dlen++] = (char)(uint8_t)modifiers;
    391  }
    392 
    393  if (IS_SPECIAL(key)) {
    394    dst[dlen++] = (char)(uint8_t)K_SPECIAL;
    395    dst[dlen++] = (char)(uint8_t)KEY2TERMCAP0(key);
    396    dst[dlen++] = (char)(uint8_t)KEY2TERMCAP1(key);
    397  } else if (escape_ks) {
    398    char *after = add_char2buf(key, dst + dlen);
    399    assert(after >= dst && (uintmax_t)(after - dst) <= UINT_MAX);
    400    dlen = (unsigned)(after - dst);
    401  } else {
    402    dlen += (unsigned)utf_char2bytes(key, dst + dlen);
    403  }
    404 
    405  return dlen;
    406 }
    407 
    408 /// Try translating a <> name
    409 ///
    410 /// @param[in,out]  srcp  Translated <> name. Is advanced to after the <> name.
    411 /// @param[in]  src_len  srcp length.
    412 /// @param[out]  modp  Location where information about modifiers is saved.
    413 /// @param[in]  flags  FSK_ values
    414 /// @param[out]  did_simplify  FSK_SIMPLIFY and found <C-H>, etc.
    415 ///
    416 /// @return Key and modifiers or 0 if there is no match.
    417 int find_special_key(const char **const srcp, const size_t src_len, int *const modp,
    418                     const int flags, bool *const did_simplify)
    419  FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1, 3)
    420 {
    421  const char *bp;
    422  const char *const end = *srcp + src_len - 1;
    423  const bool in_string = flags & FSK_IN_STRING;
    424  uvarnumber_T n;
    425  int l;
    426 
    427  if (src_len == 0) {
    428    return 0;
    429  }
    430 
    431  const char *src = *srcp;
    432  if (src[0] != '<') {
    433    return 0;
    434  }
    435  if (src[1] == '*') {  // <*xxx>: do not simplify
    436    src++;
    437  }
    438 
    439  // Find end of modifier list
    440  const char *last_dash = src;
    441  for (bp = src + 1; bp <= end && (*bp == '-' || ascii_isident(*bp)); bp++) {
    442    if (*bp == '-') {
    443      last_dash = bp;
    444      if (bp + 1 <= end) {
    445        l = utfc_ptr2len_len(bp + 1, (int)(end - bp) + 1);
    446        // Anything accepted, like <C-?>.
    447        // <C-"> or <M-"> are not special in strings as " is
    448        // the string delimiter. With a backslash it works: <M-\">
    449        if (end - bp > l && !(in_string && bp[1] == '"') && bp[l + 1] == '>') {
    450          bp += l;
    451        } else if (end - bp > 2 && in_string && bp[1] == '\\'
    452                   && bp[2] == '"' && bp[3] == '>') {
    453          bp += 2;
    454        }
    455      }
    456    }
    457    if (end - bp > 3 && bp[0] == 't' && bp[1] == '_') {
    458      bp += 3;  // skip t_xx, xx may be '-' or '>'
    459    } else if (end - bp > 4 && STRNICMP(bp, "char-", 5) == 0) {
    460      vim_str2nr(bp + 5, NULL, &l, STR2NR_ALL, NULL, NULL, 0, true, NULL);
    461      if (l == 0) {
    462        emsg(_(e_invarg));
    463        return 0;
    464      }
    465      bp += l + 5;
    466      break;
    467    }
    468  }
    469 
    470  if (bp <= end && *bp == '>') {  // found matching '>'
    471    int key;
    472    const char *end_of_name = bp + 1;
    473 
    474    // Which modifiers are given?
    475    int modifiers = 0x0;
    476    for (bp = src + 1; bp < last_dash; bp++) {
    477      if (*bp != '-') {
    478        int bit = name_to_mod_mask((uint8_t)(*bp));
    479        if (bit == 0x0) {
    480          break;                // Illegal modifier name
    481        }
    482        modifiers |= bit;
    483      }
    484    }
    485 
    486    // Legal modifier name.
    487    if (bp >= last_dash) {
    488      if (STRNICMP(last_dash + 1, "char-", 5) == 0
    489          && ascii_isdigit(last_dash[6])) {
    490        // <Char-123> or <Char-033> or <Char-0x33>
    491        vim_str2nr(last_dash + 6, NULL, &l, STR2NR_ALL, NULL, &n, 0, true, NULL);
    492        if (l == 0) {
    493          emsg(_(e_invarg));
    494          return 0;
    495        }
    496        key = (int)n;
    497      } else {
    498        int off = 1;
    499 
    500        // Modifier with single letter, or special key name.
    501        if (in_string && last_dash[1] == '\\' && last_dash[2] == '"') {
    502          // Special case for a double-quoted string
    503          off = l = 2;
    504        } else {
    505          l = utfc_ptr2len(last_dash + 1);
    506        }
    507        if (modifiers != 0 && last_dash[l + 1] == '>') {
    508          key = utf_ptr2char(last_dash + off);
    509        } else {
    510          key = get_special_key_code(last_dash + off);
    511          if (!(flags & FSK_KEEP_X_KEY)) {
    512            key = handle_x_keys(key);
    513          }
    514        }
    515      }
    516 
    517      // get_special_key_code() may return NUL for invalid
    518      // special key name.
    519      if (key != NUL) {
    520        // Only use a modifier when there is no special key code that
    521        // includes the modifier.
    522        key = simplify_key(key, &modifiers);
    523 
    524        if (!(flags & FSK_KEYCODE)) {
    525          // don't want keycode, use single byte code
    526          if (key == K_BS) {
    527            key = BS;
    528          } else if (key == K_DEL || key == K_KDEL) {
    529            key = DEL;
    530          }
    531        }
    532 
    533        // Normal Key with modifier:
    534        // Try to make a single byte code (except for Alt/Meta modifiers).
    535        if (!IS_SPECIAL(key)) {
    536          key = extract_modifiers(key, &modifiers, flags & FSK_SIMPLIFY, did_simplify);
    537        }
    538 
    539        *modp = modifiers;
    540        *srcp = end_of_name;
    541        return key;
    542      }  // else { ELOG("unknown key: '%s'", src); }
    543    }
    544  }
    545  return 0;
    546 }
    547 
    548 /// Try to include modifiers (except alt/meta) in the key.
    549 /// Changes "Shift-a" to 'A', "Ctrl-@" to <Nul>, etc.
    550 /// @param[in]  simplify  if false, don't do Ctrl
    551 /// @param[out]  did_simplify  set when it is not NULL and "simplify" is true and
    552 ///                            Ctrl is removed from modifiers
    553 static int extract_modifiers(int key, int *modp, const bool simplify, bool *const did_simplify)
    554 {
    555  int modifiers = *modp;
    556 
    557  if ((modifiers & MOD_MASK_SHIFT) && ASCII_ISALPHA(key)) {
    558    key = TOUPPER_ASC(key);
    559    // With <C-S-a> we keep the shift modifier.
    560    // With <S-a>, <A-S-a> and <S-A> we don't keep the shift modifier.
    561    if (!(modifiers & MOD_MASK_CTRL)) {
    562      modifiers &= ~MOD_MASK_SHIFT;
    563    }
    564  }
    565 
    566  // <C-H> and <C-h> mean the same thing, always use "H"
    567  if ((modifiers & MOD_MASK_CTRL) && ASCII_ISALPHA(key)) {
    568    key = TOUPPER_ASC(key);
    569  }
    570 
    571  if (simplify && (modifiers & MOD_MASK_CTRL)
    572      && ((key >= '?' && key <= '_') || ASCII_ISALPHA(key))) {
    573    key = CTRL_CHR(key);
    574    modifiers &= ~MOD_MASK_CTRL;
    575    if (key == NUL) {  // <C-@> is <Nul>
    576      key = K_ZERO;
    577    }
    578    if (did_simplify != NULL) {
    579      *did_simplify = true;
    580    }
    581  }
    582 
    583  *modp = modifiers;
    584  return key;
    585 }
    586 
    587 /// Try to find key "c" in the special key table.
    588 /// @return  the index when found, -1 when not found.
    589 int find_special_key_in_table(int c)
    590 {
    591  for (int i = 0; i < (int)ARRAY_SIZE(key_names_table); i++) {
    592    if (c == key_names_table[i].key && !key_names_table[i].is_alt) {
    593      return i;
    594    }
    595  }
    596 
    597  return -1;
    598 }
    599 
    600 /// Find the special key with the given name
    601 ///
    602 /// @param[in]  name  Name of the special. Does not have to end with NUL, it is
    603 ///                   assumed to end before the first non-idchar. If name starts
    604 ///                   with "t_" the next two characters are interpreted as
    605 ///                   a termcap name.
    606 ///
    607 /// @return Key code or 0 if not found.
    608 int get_special_key_code(const char *name)
    609  FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
    610 {
    611  if (name[0] == 't' && name[1] == '_' && name[2] != NUL && name[3] != NUL) {
    612    return TERMCAP2KEY((uint8_t)name[2], (uint8_t)name[3]);
    613  }
    614 
    615  const char *name_end = name;
    616  while (ascii_isident(*name_end)) {
    617    name_end++;
    618  }
    619 
    620  int idx = get_special_key_code_hash(name, (size_t)(name_end - name));
    621  return idx >= 0 ? key_names_table[idx].key : 0;
    622 }
    623 
    624 /// Look up the given mouse code to return the relevant information in the other arguments.
    625 /// @return  which button is down or was released.
    626 int get_mouse_button(int code, bool *is_click, bool *is_drag)
    627 {
    628  for (int i = 0; mouse_table[i].pseudo_code; i++) {
    629    if (code == mouse_table[i].pseudo_code) {
    630      *is_click = mouse_table[i].is_click;
    631      *is_drag = mouse_table[i].is_drag;
    632      return mouse_table[i].button;
    633    }
    634  }
    635  return 0;         // Shouldn't get here
    636 }
    637 
    638 /// Replace any terminal code strings with the equivalent internal representation.
    639 ///
    640 /// Used for the "from" and "to" part of a mapping, and the "to" part of a menu command.
    641 /// Any strings like "<C-UP>" are also replaced, unless `special` is false.
    642 /// K_SPECIAL by itself is replaced by K_SPECIAL KS_SPECIAL KE_FILLER.
    643 ///
    644 /// When "flags" has REPTERM_FROM_PART, trailing <C-v> is included, otherwise it is removed (to make
    645 /// ":map xx ^V" map xx to nothing). When cpo_val contains CPO_BSLASH, a backslash can be used in
    646 /// place of <C-v>. All other <C-v> characters are removed.
    647 ///
    648 /// @param[in]  from  What characters to replace.
    649 /// @param[in]  from_len  Length of the "from" argument.
    650 /// @param[out]  bufp  Location where results were saved in case of success (allocated).
    651 ///                    If `*bufp` is non-NULL, it will be used directly,
    652 ///                    and is assumed to be 128 bytes long (enough for transcoding LHS of mapping),
    653 ///                    and will be set to NULL in case of failure.
    654 /// @param[in]  sid_arg  Script ID to use for <SID>, or 0 to use current_sctx
    655 /// @param[in]  flags  REPTERM_FROM_PART    see above
    656 ///                    REPTERM_DO_LT        also translate <lt>
    657 ///                    REPTERM_NO_SPECIAL   do not accept <key> notation
    658 ///                    REPTERM_NO_SIMPLIFY  do not simplify <C-H> into 0x08, etc.
    659 /// @param[out]  did_simplify  set when some <C-H> code was simplified, unless it is NULL.
    660 /// @param[in]  cpo_val  The value of 'cpoptions' to use. Only CPO_BSLASH matters.
    661 ///
    662 /// @return  The same as what `*bufp` is set to.
    663 char *replace_termcodes(const char *const from, const size_t from_len, char **const bufp,
    664                        const scid_T sid_arg, const int flags, bool *const did_simplify,
    665                        const char *const cpo_val)
    666  FUNC_ATTR_NONNULL_ARG(1, 3, 7)
    667 {
    668  size_t dlen = 0;
    669  const char *const end = from + from_len - 1;
    670 
    671  // backslash is a special character
    672  const bool do_backslash = (vim_strchr(cpo_val, CPO_BSLASH) == NULL);
    673  const bool do_special = !(flags & REPTERM_NO_SPECIAL);
    674 
    675  bool allocated = (*bufp == NULL);
    676 
    677  // Allocate space for the translation.  Worst case a single character is
    678  // replaced by 6 bytes (shifted special key), plus a NUL at the end.
    679  const size_t buf_len = allocated ? from_len * 6 + 1 : 128;
    680  char *result = allocated ? xmalloc(buf_len) : *bufp;  // buffer for resulting string
    681 
    682  const char *src = from;
    683 
    684  // Copy each byte from *from to result[dlen]
    685  while (src <= end) {
    686    if (!allocated && dlen + 64 > buf_len) {
    687      return NULL;
    688    }
    689    // Check for special <> keycodes, like "<C-S-LeftMouse>"
    690    if (do_special && ((flags & REPTERM_DO_LT) || ((end - src) >= 3
    691                                                   && strncmp(src, "<lt>", 4) != 0))) {
    692      // Change <SID>Func to K_SNR <script-nr> _Func.  This name is used
    693      // for script-local user functions.
    694      // (room: 5 * 6 = 30 bytes; needed: 3 + <nr> + 1 <= 14)
    695      if (end - src >= 4 && STRNICMP(src, "<SID>", 5) == 0) {
    696        if (sid_arg < 0 || (sid_arg == 0 && current_sctx.sc_sid <= 0)) {
    697          emsg(_(e_usingsid));
    698        } else {
    699          const scid_T sid = sid_arg != 0 ? sid_arg : current_sctx.sc_sid;
    700          src += 5;
    701          result[dlen++] = (char)K_SPECIAL;
    702          result[dlen++] = (char)KS_EXTRA;
    703          result[dlen++] = KE_SNR;
    704          snprintf(result + dlen, buf_len - dlen, "%" PRIdSCID, sid);
    705          dlen += strlen(result + dlen);
    706          result[dlen++] = '_';
    707          continue;
    708        }
    709      }
    710 
    711      size_t slen = trans_special(&src, (size_t)(end - src) + 1, result + dlen,
    712                                  FSK_KEYCODE | ((flags & REPTERM_NO_SIMPLIFY) ? 0 : FSK_SIMPLIFY),
    713                                  true, did_simplify);
    714      if (slen) {
    715        dlen += slen;
    716        continue;
    717      }
    718    }
    719 
    720    if (do_special) {
    721      char *p, *s;
    722      int len;
    723 
    724      // Replace <Leader> by the value of "mapleader".
    725      // Replace <LocalLeader> by the value of "maplocalleader".
    726      // If "mapleader" or "maplocalleader" isn't set use a backslash.
    727      if (end - src >= 7 && STRNICMP(src, "<Leader>", 8) == 0) {
    728        len = 8;
    729        p = get_var_value("g:mapleader");
    730      } else if (end - src >= 12 && STRNICMP(src, "<LocalLeader>", 13) == 0) {
    731        len = 13;
    732        p = get_var_value("g:maplocalleader");
    733      } else {
    734        len = 0;
    735        p = NULL;
    736      }
    737 
    738      if (len != 0) {
    739        // Allow up to 8 * 6 characters for "mapleader".
    740        if (p == NULL || *p == NUL || strlen(p) > 8 * 6) {
    741          s = "\\";
    742        } else {
    743          s = p;
    744        }
    745        while (*s != NUL) {
    746          result[dlen++] = *s++;
    747        }
    748        src += len;
    749        continue;
    750      }
    751    }
    752 
    753    // Remove CTRL-V and ignore the next character.
    754    // For "from" side the CTRL-V at the end is included, for the "to"
    755    // part it is removed.
    756    // If 'cpoptions' does not contain 'B', also accept a backslash.
    757    char key = *src;
    758    if (key == Ctrl_V || (do_backslash && key == '\\')) {
    759      src++;  // skip CTRL-V or backslash
    760      if (src > end) {
    761        if (flags & REPTERM_FROM_PART) {
    762          result[dlen++] = key;
    763        }
    764        break;
    765      }
    766    }
    767 
    768    // skip multibyte char correctly
    769    for (ssize_t i = utfc_ptr2len_len(src, (int)(end - src) + 1); i > 0; i--) {
    770      // If the character is K_SPECIAL, replace it with K_SPECIAL
    771      // KS_SPECIAL KE_FILLER.
    772      if (*src == (char)K_SPECIAL) {
    773        result[dlen++] = (char)K_SPECIAL;
    774        result[dlen++] = (char)KS_SPECIAL;
    775        result[dlen++] = KE_FILLER;
    776      } else {
    777        result[dlen++] = *src;
    778      }
    779      src++;
    780    }
    781  }
    782  result[dlen] = NUL;
    783 
    784  if (allocated) {
    785    *bufp = xrealloc(result, dlen + 1);
    786  }
    787 
    788  return *bufp;
    789 }
    790 
    791 /// Add character "c" to buffer "s"
    792 ///
    793 /// Escapes the special meaning of K_SPECIAL, handles multi-byte
    794 /// characters.
    795 ///
    796 /// @param[in]  c  Character to add.
    797 /// @param[out]  s  Buffer to add to. Must have at least MB_MAXBYTES + 1 bytes.
    798 ///
    799 /// @return Pointer to after the added bytes.
    800 char *add_char2buf(int c, char *s)
    801  FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
    802 {
    803  char temp[MB_MAXBYTES + 1];
    804  const int len = utf_char2bytes(c, temp);
    805  for (int i = 0; i < len; i++) {
    806    c = (uint8_t)temp[i];
    807    // Need to escape K_SPECIAL like in the typeahead buffer.
    808    if (c == K_SPECIAL) {
    809      *s++ = (char)(uint8_t)K_SPECIAL;
    810      *s++ = (char)(uint8_t)KS_SPECIAL;
    811      *s++ = KE_FILLER;
    812    } else {
    813      *s++ = (char)(uint8_t)c;
    814    }
    815  }
    816  return s;
    817 }
    818 
    819 /// Copy "p" to allocated memory, escaping K_SPECIAL so that the result
    820 /// can be put in the typeahead buffer.
    821 char *vim_strsave_escape_ks(char *p)
    822 {
    823  // Need a buffer to hold up to three times as much.  Four in case of an
    824  // illegal utf-8 byte:
    825  // 0xc0 -> 0xc3 - 0x80 -> 0xc3 K_SPECIAL KS_SPECIAL KE_FILLER
    826  char *res = xmalloc(strlen(p) * 4 + 1);
    827  char *d = res;
    828  for (char *s = p; *s != NUL;) {
    829    if ((uint8_t)s[0] == K_SPECIAL && s[1] != NUL && s[2] != NUL) {
    830      // Copy special key unmodified.
    831      *d++ = *s++;
    832      *d++ = *s++;
    833      *d++ = *s++;
    834    } else {
    835      // Add character, possibly multi-byte to destination, escaping
    836      // K_SPECIAL. Be careful, it can be an illegal byte!
    837      d = add_char2buf(utf_ptr2char(s), d);
    838      s += utf_ptr2len(s);
    839    }
    840  }
    841  *d = NUL;
    842 
    843  return res;
    844 }
    845 
    846 /// Remove escaping from K_SPECIAL characters.  Reverse of
    847 /// vim_strsave_escape_ks().  Works in-place.
    848 void vim_unescape_ks(char *p)
    849 {
    850  uint8_t *s = (uint8_t *)p;
    851  uint8_t *d = (uint8_t *)p;
    852 
    853  while (*s != NUL) {
    854    if (s[0] == K_SPECIAL && s[1] == KS_SPECIAL && s[2] == KE_FILLER) {
    855      *d++ = K_SPECIAL;
    856      s += 3;
    857    } else {
    858      *d++ = *s++;
    859    }
    860  }
    861  *d = NUL;
    862 }