patch 8.2.1978: making a mapping work in all modes is complicated

Problem:    Making a mapping work in all modes is complicated.
Solution:   Add the <Cmd> special key. (Yegappan Lakshmanan, closes #7282,
            closes 4784, based on patch by Bjorn Linse)
diff --git a/src/getchar.c b/src/getchar.c
index b0cc8c2..3d73a6c 100644
--- a/src/getchar.c
+++ b/src/getchar.c
@@ -3619,3 +3619,96 @@
 	    );
 }
 #endif
+
+/*
+ * Function passed to do_cmdline() to get the command after a <Cmd> key from
+ * typeahead.
+ */
+    char_u *
+getcmdkeycmd(
+	int		promptc UNUSED,
+	void		*cookie UNUSED,
+	int		indent UNUSED,
+	getline_opt_T	do_concat UNUSED)
+{
+    garray_T	line_ga;
+    int		c1 = -1;
+    int		c2;
+    int		cmod = 0;
+    int		aborted = FALSE;
+
+    ga_init2(&line_ga, 1, 32);
+
+    // no mapping for these characters
+    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_cmd_mapping_must_end_with_cr));
+	    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);
+	    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)
+	{
+	    // give a nicer error message for this special case
+	    emsg(_(e_cmd_mapping_must_end_with_cr_before_second_cmd));
+	    aborted = TRUE;
+	}
+	else if (IS_SPECIAL(c1))
+	{
+	    if (c1 == K_SNR)
+	    {
+		ga_append(&line_ga, (char)K_SPECIAL);
+		ga_append(&line_ga, (char)KS_EXTRA);
+		ga_append(&line_ga, (char)KE_SNR);
+	    }
+	    else
+	    {
+		semsg(e_cmd_maping_must_not_include_str_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;
+}