aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/getchar.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/getchar.c')
-rw-r--r--src/nvim/getchar.c395
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;
+}