diff options
Diffstat (limited to 'src/nvim/getchar.c')
| -rw-r--r-- | src/nvim/getchar.c | 395 | 
1 files changed, 239 insertions, 156 deletions
| diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index b83681ad01..e2566c8c66 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -1,3 +1,6 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check +// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com +  /*   * getchar.c   * @@ -15,6 +18,7 @@  #include "nvim/vim.h"  #include "nvim/ascii.h"  #include "nvim/getchar.h" +#include "nvim/buffer_defs.h"  #include "nvim/charset.h"  #include "nvim/cursor.h"  #include "nvim/edit.h" @@ -44,6 +48,13 @@  #include "nvim/event/loop.h"  #include "nvim/os/input.h"  #include "nvim/os/os.h" +#include "nvim/os/fileio.h" +#include "nvim/api/private/handle.h" + + +/// Index in scriptin +static int curscript = 0; +FileDescriptor *scriptin[NSCRIPT] = { NULL };  /*   * These buffers are used for storing: @@ -87,23 +98,20 @@ static int typeahead_char = 0;          /* typeahead char that's not flushed */   */  static int block_redo = FALSE; -/* - * Make a hash value for a mapping. - * "mode" is the lower 4 bits of the State for the mapping. - * "c1" is the first character of the "lhs". - * Returns a value between 0 and 255, index in maphash. - * Put Normal/Visual mode mappings mostly separately from Insert/Cmdline mode. - */ +// Make a hash value for a mapping. +// "mode" is the lower 4 bits of the State for the mapping. +// "c1" is the first character of the "lhs". +// Returns a value between 0 and 255, index in maphash. +// Put Normal/Visual mode mappings mostly separately from Insert/Cmdline mode.  #define MAP_HASH(mode, \                   c1) (((mode) & \                         (NORMAL + VISUAL + SELECTMODE + \ -                        OP_PENDING)) ? (c1) : ((c1) ^ 0x80)) +                        OP_PENDING + TERM_FOCUS)) ? (c1) : ((c1) ^ 0x80)) -/* - * Each mapping is put in one of the 256 hash lists, to speed up finding it. - */ -static mapblock_T       *(maphash[256]); -static int maphash_valid = FALSE; +// Each mapping is put in one of the MAX_MAPHASH hash lists, +// to speed up finding it. +static mapblock_T *(maphash[MAX_MAPHASH]); +static bool maphash_valid = false;  /*   * List used for abbreviations. @@ -252,16 +260,17 @@ static void add_buff(buffheader_T *const buf, const char *const s,      return;    } -  if (buf->bh_first.b_next == NULL) {   /* first add to list */ +  if (buf->bh_first.b_next == NULL) {  // first add to list      buf->bh_space = 0;      buf->bh_curr = &(buf->bh_first); -  } else if (buf->bh_curr == NULL) {  /* buffer has already been read */ -    EMSG(_("E222: Add to read buffer")); +  } else if (buf->bh_curr == NULL) {  // buffer has already been read +    IEMSG(_("E222: Add to read buffer"));      return; -  } else if (buf->bh_index != 0) +  } else if (buf->bh_index != 0) {      memmove(buf->bh_first.b_next->b_str, -        buf->bh_first.b_next->b_str + buf->bh_index, -        STRLEN(buf->bh_first.b_next->b_str + buf->bh_index) + 1); +            buf->bh_first.b_next->b_str + buf->bh_index, +            STRLEN(buf->bh_first.b_next->b_str + buf->bh_index) + 1); +  }    buf->bh_index = 0;    size_t len; @@ -302,13 +311,13 @@ static void add_num_buff(buffheader_T *buf, long n)   */  static void add_char_buff(buffheader_T *buf, int c)  { -  char bytes[MB_MAXBYTES + 1]; +  uint8_t bytes[MB_MAXBYTES + 1];    int len;    if (IS_SPECIAL(c)) {      len = 1;    } else { -    len = (*mb_char2bytes)(c, (char_u *)bytes); +    len = mb_char2bytes(c, bytes);    }    for (int i = 0; i < len; i++) { @@ -866,20 +875,15 @@ int ins_typebuf(char_u *str, int noremap, int offset, int nottyped, bool silent)    addlen = (int)STRLEN(str); -  /* -   * Easy case: there is room in front of typebuf.tb_buf[typebuf.tb_off] -   */    if (offset == 0 && addlen <= typebuf.tb_off) { +    // Easy case: there is room in front of typebuf.tb_buf[typebuf.tb_off]      typebuf.tb_off -= addlen;      memmove(typebuf.tb_buf + typebuf.tb_off, str, (size_t)addlen); -  } -  /* -   * Need to allocate a new buffer. -   * In typebuf.tb_buf there must always be room for 3 * MAXMAPLEN + 4 -   * characters.  We add some extra room to avoid having to allocate too -   * often. -   */ -  else { +  } else { +    // Need to allocate a new buffer. +    // In typebuf.tb_buf there must always be room for 3 * MAXMAPLEN + 4 +    // characters.  We add some extra room to avoid having to allocate too +    // often.      newoff = MAXMAPLEN + 4;      newlen = typebuf.tb_len + addlen + newoff + 4 * (MAXMAPLEN + 4);      if (newlen < 0) {               /* string is getting too long */ @@ -1155,14 +1159,16 @@ void alloc_typebuf(void)   */  void free_typebuf(void)  { -  if (typebuf.tb_buf == typebuf_init) -    EMSG2(_(e_intern2), "Free typebuf 1"); -  else +  if (typebuf.tb_buf == typebuf_init) { +    internal_error("Free typebuf 1"); +  } else {      xfree(typebuf.tb_buf); -  if (typebuf.tb_noremap == noremapbuf_init) -    EMSG2(_(e_intern2), "Free typebuf 2"); -  else +  } +  if (typebuf.tb_noremap == noremapbuf_init) { +    internal_error("Free typebuf 2"); +  } else {      xfree(typebuf.tb_noremap); +  }  }  /* @@ -1243,10 +1249,13 @@ openscript (      ++curscript;    /* use NameBuff for expanded name */    expand_env(name, NameBuff, MAXPATHL); -  if ((scriptin[curscript] = mch_fopen((char *)NameBuff, READBIN)) == NULL) { -    EMSG2(_(e_notopen), name); -    if (curscript) -      --curscript; +  int error; +  if ((scriptin[curscript] = file_open_new(&error, (char *)NameBuff, +                                           kFileReadOnly, 0)) == NULL) { +    emsgf(_(e_notopen_2), name, os_strerror(error)); +    if (curscript) { +      curscript--; +    }      return;    }    save_typebuf(); @@ -1296,7 +1305,7 @@ static void closescript(void)    free_typebuf();    typebuf = saved_typebuf[curscript]; -  fclose(scriptin[curscript]); +  file_free(scriptin[curscript], false);    scriptin[curscript] = NULL;    if (curscript > 0)      --curscript; @@ -1319,10 +1328,8 @@ int using_script(void)    return scriptin[curscript] != NULL;  } -/* - * This function is called just before doing a blocking wait.  Thus after - * waiting 'updatetime' for a character to arrive. - */ +/// This function is called just before doing a blocking wait.  Thus after +/// waiting 'updatetime' for a character to arrive.  void before_blocking(void)  {    updatescript(0); @@ -1331,21 +1338,22 @@ void before_blocking(void)    }  } -/* - * updatescipt() is called when a character can be written into the script file - * or when we have waited some time for a character (c == 0) - * - * All the changed memfiles are synced if c == 0 or when the number of typed - * characters reaches 'updatecount' and 'updatecount' is non-zero. - */ -void updatescript(int c) +/// updatescript() is called when a character can be written to the script file +/// or when we have waited some time for a character (c == 0). +/// +/// All the changed memfiles are synced if c == 0 or when the number of typed +/// characters reaches 'updatecount' and 'updatecount' is non-zero. +static void updatescript(int c)  {    static int count = 0; -  if (c && scriptout) +  if (c && scriptout) {      putc(c, scriptout); -  if (c == 0 || (p_uc > 0 && ++count >= p_uc)) { -    ml_sync_all(c == 0, TRUE); +  } +  bool idle = (c == 0); +  if (idle || (p_uc > 0 && ++count >= p_uc)) { +    ml_sync_all(idle, true, +                (!!p_fs || idle));  // Always fsync at idle (CursorHold).      count = 0;    }  } @@ -1577,7 +1585,7 @@ vungetc ( /* unget one character (can only be done once!) */    old_mouse_col = mouse_col;  } -/// get a character: +/// Gets a character:  /// 1. from the stuffbuffer  ///    This is used for abbreviated commands like "D" -> "d$".  ///    Also used to redo a command for ".". @@ -1589,13 +1597,13 @@ vungetc ( /* unget one character (can only be done once!) */  ///    This may do a blocking wait if "advance" is TRUE.  ///  /// if "advance" is TRUE (vgetc()): -///    really get the character. +///    Really get the character.  ///    KeyTyped is set to TRUE in the case the user typed the key.  ///    KeyStuffed is TRUE if the character comes from the stuff buffer.  /// if "advance" is FALSE (vpeekc()):  ///    just look whether there is a character available.  /// -/// When "no_mapping" is zero, checks for mappings in the current mode. +/// When `no_mapping` (global) is zero, checks for mappings in the current mode.  /// Only returns one byte (of a multi-byte character).  /// K_SPECIAL and CSI may be escaped, need to get two more bytes then.  static int vgetorpeek(int advance) @@ -1661,10 +1669,10 @@ static int vgetorpeek(int advance)      }      if (c != NUL && !got_int) {        if (advance) { -        /* KeyTyped = FALSE;  When the command that stuffed something -         * was typed, behave like the stuffed command was typed. -         * needed for CTRL-W CTRl-] to open a fold, for example. */ -        KeyStuffed = TRUE; +        // KeyTyped = FALSE;  When the command that stuffed something +        // was typed, behave like the stuffed command was typed. +        // needed for CTRL-W CTRL-] to open a fold, for example. +        KeyStuffed = true;        }        if (typebuf.tb_no_abbr_cnt == 0)          typebuf.tb_no_abbr_cnt = 1;             /* no abbreviations now */ @@ -1802,7 +1810,7 @@ static int vgetorpeek(int advance)                   * <M-a> and then changing 'encoding'. Beware                   * that 0x80 is escaped. */                  char_u *p1 = mp->m_keys; -                char_u *p2 = mb_unescape(&p1); +                char_u *p2 = (char_u *)mb_unescape((const char **)&p1);                  if (has_mbyte && p2 != NULL && MB_BYTE2LEN(c1) > MB_PTR2LEN(p2))                    mlen = 0; @@ -1849,11 +1857,12 @@ static int vgetorpeek(int advance)                      mp_match = mp;                      mp_match_len = keylen;                    } -                } else -                /* No match; may have to check for -                 * termcode at next character. */ -                if (max_mlen < mlen) -                  max_mlen = mlen; +                } else { +                  // No match; may have to check for termcode at next character. +                  if (max_mlen < mlen) { +                    max_mlen = mlen; +                  } +                }                }              } @@ -1903,7 +1912,7 @@ static int vgetorpeek(int advance)            }            if ((mp == NULL || max_mlen >= mp_match_len) -              && keylen != KEYLEN_PART_MAP && keylen != KEYLEN_PART_KEY) { +              && keylen != KEYLEN_PART_MAP) {              // No matching mapping found or found a non-matching mapping that              // matches at least what the matching mapping matched              keylen = 0; @@ -2335,9 +2344,8 @@ inchar (      int tb_change_cnt  )  { -  int len = 0;                      /* init for GCC */ -  int retesc = FALSE;               /* return ESC with gotint */ -  int script_char; +  int len = 0;  // Init for GCC. +  int retesc = false;  // Return ESC with gotint.    if (wait_time == -1L || wait_time > 100L) {      // flush output before waiting @@ -2355,45 +2363,38 @@ inchar (    }    undo_off = FALSE;                 /* restart undo now */ -  /* -   * Get a character from a script file if there is one. -   * If interrupted: Stop reading script files, close them all. -   */ -  script_char = -1; -  while (scriptin[curscript] != NULL && script_char < 0 -         && !ignore_script -         ) { - - -    if (got_int || (script_char = getc(scriptin[curscript])) < 0) { -      /* Reached EOF. -       * Careful: closescript() frees typebuf.tb_buf[] and buf[] may -       * point inside typebuf.tb_buf[].  Don't use buf[] after this! */ +  // Get a character from a script file if there is one. +  // If interrupted: Stop reading script files, close them all. +  ptrdiff_t read_size = -1; +  while (scriptin[curscript] != NULL && read_size <= 0 && !ignore_script) { +    char script_char; +    if (got_int +        || (read_size = file_read(scriptin[curscript], &script_char, 1)) != 1) { +      // Reached EOF or some error occurred. +      // Careful: closescript() frees typebuf.tb_buf[] and buf[] may +      // point inside typebuf.tb_buf[].  Don't use buf[] after this!        closescript(); -      /* -       * When reading script file is interrupted, return an ESC to get -       * back to normal mode. -       * Otherwise return -1, because typebuf.tb_buf[] has changed. -       */ -      if (got_int) -        retesc = TRUE; -      else +      // When reading script file is interrupted, return an ESC to get +      // back to normal mode. +      // Otherwise return -1, because typebuf.tb_buf[] has changed. +      if (got_int) { +        retesc = true; +      } else {          return -1; +      }      } else {        buf[0] = (char_u)script_char;        len = 1;      }    } -  if (script_char < 0) {        /* did not get a character from script */ -    /* -     * If we got an interrupt, skip all previously typed characters and -     * return TRUE if quit reading script file. -     * Stop reading typeahead when a single CTRL-C was read, -     * fill_input_buf() returns this when not able to read from stdin. -     * Don't use buf[] here, closescript() may have freed typebuf.tb_buf[] -     * and buf may be pointing inside typebuf.tb_buf[]. -     */ +  if (read_size <= 0) {  // Did not get a character from script. +    // If we got an interrupt, skip all previously typed characters and +    // return TRUE if quit reading script file. +    // Stop reading typeahead when a single CTRL-C was read, +    // fill_input_buf() returns this when not able to read from stdin. +    // Don't use buf[] here, closescript() may have freed typebuf.tb_buf[] +    // and buf may be pointing inside typebuf.tb_buf[].      if (got_int) {  #define DUM_LEN MAXMAPLEN * 3 + 3        char_u dum[DUM_LEN + 1]; @@ -2406,21 +2407,18 @@ inchar (        return retesc;      } -    /* -     * Always flush the output characters when getting input characters -     * from the user. -     */ +    // Always flush the output characters when getting input characters +    // from the user.      ui_flush(); -    /* -     * Fill up to a third of the buffer, because each character may be -     * tripled below. -     */ +    // Fill up to a third of the buffer, because each character may be +    // tripled below.      len = os_inchar(buf, maxlen / 3, (int)wait_time, tb_change_cnt);    } -  if (typebuf_changed(tb_change_cnt)) +  if (typebuf_changed(tb_change_cnt)) {      return 0; +  }    return fix_input_buffer(buf, len);  } @@ -2532,7 +2530,6 @@ do_map (    bool unique = false;    bool nowait = false;    bool silent = false; -  bool special = false;    bool expr = false;    int noremap;    char_u      *orig_rhs; @@ -2578,12 +2575,9 @@ do_map (        continue;      } -    /* -     * Check for "<special>": accept special keys in <> -     */ +    // Ignore obsolete "<special>" modifier.      if (STRNCMP(keys, "<special>", 9) == 0) {        keys = skipwhite(keys + 9); -      special = true;        continue;      } @@ -2652,7 +2646,7 @@ do_map (    // 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, +    keys = replace_termcodes(keys, STRLEN(keys), &keys_buf, true, true, true,                               CPO_TO_CPO_FLAGS);    }    orig_rhs = rhs; @@ -2660,7 +2654,7 @@ do_map (      if (STRICMP(rhs, "<nop>") == 0) {  // "<Nop>" means nothing        rhs = (char_u *)"";      } else { -      rhs = replace_termcodes(rhs, STRLEN(rhs), &arg_buf, false, true, special, +      rhs = replace_termcodes(rhs, STRLEN(rhs), &arg_buf, false, true, true,                                CPO_TO_CPO_FLAGS);      }    } @@ -3164,6 +3158,10 @@ showmap (  {    size_t len = 1; +  if (message_filtered(mp->m_keys) && message_filtered(mp->m_str)) { +    return; +  } +    if (msg_didout || msg_silent != 0) {      msg_putchar('\n');      if (got_int)            /* 'q' typed at MORE prompt */ @@ -3236,7 +3234,7 @@ bool map_to_exists(const char *const str, const char *const modechars,    char_u *buf;    char_u *const rhs = replace_termcodes((const char_u *)str, strlen(str), &buf, -                                        false, true, false, +                                        false, true, true,                                          CPO_TO_CPO_FLAGS);  #define MAPMODE(mode, modechars, chr, modeflags) \ @@ -3365,6 +3363,10 @@ set_context_in_map_cmd (          arg = skipwhite(arg + 8);          continue;        } +      if (STRNCMP(arg, "<special>", 9) == 0) { +        arg = skipwhite(arg + 9); +        continue; +      }        if (STRNCMP(arg, "<script>", 8) == 0) {          arg = skipwhite(arg + 8);          continue; @@ -3407,21 +3409,24 @@ int ExpandMappings(regmatch_T *regmatch, int *num_file, char_u ***file)    for (round = 1; round <= 2; ++round) {      count = 0; -    for (i = 0; i < 6; ++i) { -      if (i == 0) +    for (i = 0; i < 7; i++) { +      if (i == 0) {          p = (char_u *)"<silent>"; -      else if (i == 1) +      } else if (i == 1) {          p = (char_u *)"<unique>"; -      else if (i == 2) +      } else if (i == 2) {          p = (char_u *)"<script>"; -      else if (i == 3) +      } else if (i == 3) {          p = (char_u *)"<expr>"; -      else if (i == 4 && !expand_buffer) +      } else if (i == 4 && !expand_buffer) {          p = (char_u *)"<buffer>"; -      else if (i == 5) +      } else if (i == 5) {          p = (char_u *)"<nowait>"; -      else +      } else if (i == 6) { +        p = (char_u *)"<special>"; +      } else {          continue; +      }        if (vim_regexec(regmatch, p, (colnr_T)0)) {          if (round == 1) @@ -3588,8 +3593,8 @@ int check_abbr(int c, char_u *ptr, int col, int mincol)        char_u *q = mp->m_keys;        int match; -      if (vim_strbyte(mp->m_keys, K_SPECIAL) != NULL) { -        /* might have CSI escaped mp->m_keys */ +      if (strchr((const char *)mp->m_keys, K_SPECIAL) != NULL) { +        // Might have CSI escaped mp->m_keys.          q = vim_strsave(mp->m_keys);          vim_unescape_csi(q);          qlen = (int)STRLEN(q); @@ -3786,8 +3791,7 @@ makemap (    char        *cmd;    int abbr;    int hash; -  int did_cpo = FALSE; -  int i; +  bool did_cpo = false;    validate_maphash(); @@ -3908,20 +3912,22 @@ makemap (            c1 = 't';            break;          default: -          EMSG(_("E228: makemap: Illegal mode")); +          IEMSG(_("E228: makemap: Illegal mode"));            return FAIL;          }          do {            /* do this twice if c2 is set, 3 times with c3 */            /* When outputting <> form, need to make sure that 'cpo'             * is set to the Vim default. */            if (!did_cpo) { -            if (*mp->m_str == NUL)                      /* will use <Nop> */ -              did_cpo = TRUE; -            else -              for (i = 0; i < 2; ++i) -                for (p = (i ? mp->m_str : mp->m_keys); *p; ++p) -                  if (*p == K_SPECIAL || *p == NL) -                    did_cpo = TRUE; +            if (*mp->m_str == NUL) {  // Will use <Nop>. +              did_cpo = true; +            } else { +              const char specials[] = { (char)(uint8_t)K_SPECIAL, NL, NUL }; +              if (strpbrk((const char *)mp->m_str, specials) != NULL +                  || strpbrk((const char *)mp->m_keys, specials) != NULL) { +                did_cpo = true; +              } +            }              if (did_cpo) {                if (fprintf(fd, "let s:cpo_save=&cpo") < 0                    || put_eol(fd) < 0 @@ -3989,12 +3995,10 @@ int put_escstr(FILE *fd, char_u *strstart, int what)      return OK;    } -  for (; *str != NUL; ++str) { -    char_u  *p; - -    /* Check for a multi-byte character, which may contain escaped -     * K_SPECIAL and CSI bytes */ -    p = mb_unescape(&str); +  for (; *str != NUL; str++) { +    // Check for a multi-byte character, which may contain escaped +    // K_SPECIAL and CSI bytes. +    const char *p = mb_unescape((const char **)&str);      if (p != NULL) {        while (*p != NUL)          if (fputc(*p++, fd) < 0) @@ -4150,8 +4154,7 @@ void add_map(char_u *map, int mode)  }  // Translate an internal mapping/abbreviation representation into the -// corresponding external one recognized by :map/:abbrev commands; -// respects the current B/k/< settings of 'cpoption'. +// corresponding external one recognized by :map/:abbrev commands.  //  // This function is called when expanding mappings/abbreviations on the  // command-line, and for building the "Ambiguous mapping..." error message. @@ -4171,7 +4174,6 @@ static char_u * translate_mapping (    ga_init(&ga, 1, 40);    bool cpo_bslash = !(cpo_flags&FLAG_CPO_BSLASH); -  bool cpo_special = !(cpo_flags&FLAG_CPO_SPECI);    for (; *str; ++str) {      int c = *str; @@ -4184,7 +4186,7 @@ static char_u * translate_mapping (        }        if (c == K_SPECIAL && str[1] != NUL && str[2] != NUL) { -        if (expmap && cpo_special) { +        if (expmap) {            ga_clear(&ga);            return NULL;          } @@ -4195,8 +4197,8 @@ static char_u * translate_mapping (          }          str += 2;        } -      if (IS_SPECIAL(c) || modifiers) {         /* special key */ -        if (expmap && cpo_special) { +      if (IS_SPECIAL(c) || modifiers) {         // special key +        if (expmap) {            ga_clear(&ga);            return NULL;          } @@ -4206,7 +4208,7 @@ static char_u * translate_mapping (      }      if (c == ' ' || c == '\t' || c == Ctrl_J || c == Ctrl_V -        || (c == '<' && !cpo_special) || (c == '\\' && !cpo_bslash)) { +        || (c == '\\' && !cpo_bslash)) {        ga_append(&ga, cpo_bslash ? Ctrl_V : '\\');      } @@ -4228,3 +4230,84 @@ static bool typebuf_match_len(const uint8_t *str, int *mlen)    *mlen = i;    return str[i] == NUL;  // matched the whole string  } + +/// Retrieve the mapblock at the index either globally or for a certain buffer +/// +/// @param  index  The index in the maphash[] +/// @param  buf  The buffer to get the maphash from. NULL for global +mapblock_T *get_maphash(int index, buf_T *buf) +    FUNC_ATTR_PURE +{ +  if (index > MAX_MAPHASH) { +    return NULL; +  } + +  return (buf == NULL) ? maphash[index] : buf->b_maphash[index]; +} + +/// Get command argument for <Cmd> key +char_u * getcmdkeycmd(int promptc, void *cookie, int indent) +{ +  garray_T line_ga; +  int c1 = -1, c2; +  int cmod = 0; +  bool aborted = false; + +  ga_init(&line_ga, 1, 32); + +  no_mapping++; + +  got_int = false; +  while (c1 != NUL && !aborted) { +    ga_grow(&line_ga, 32); + +    if (vgetorpeek(false) == NUL) { +      // incomplete <Cmd> is an error, because there is not much the user +      // could do in this state. +      EMSG(e_cmdmap_err); +      aborted = true; +      break; +    } + +    // Get one character at a time. +    c1 = vgetorpeek(true); +    // Get two extra bytes for special keys +    if (c1 == K_SPECIAL) { +      c1 = vgetorpeek(true);          // no mapping for these chars +      c2 = vgetorpeek(true); +      if (c1 == KS_MODIFIER) { +        cmod = c2; +        continue; +      } +      c1 = TO_SPECIAL(c1, c2); +    } + + +    if (got_int) { +      aborted = true; +    } else if (c1 == '\r' || c1 == '\n') { +      c1 = NUL;  // end the line +    } else if (c1 == ESC) { +      aborted = true; +    } else if (c1 == K_COMMAND) { +      // special case to give nicer error message +      EMSG(e_cmdmap_repeated); +      aborted = true; +    } else if (IS_SPECIAL(c1)) { +      EMSG2(e_cmdmap_key, get_special_key_name(c1, cmod)); +      aborted = true; +    } else { +      ga_append(&line_ga, (char)c1); +    } + +    cmod = 0; +  } + +  no_mapping--; + +  if (aborted) { +    ga_clear(&line_ga); +  } + +  return (char_u *)line_ga.ga_data; +} | 
