From 0519ce00394474055bd58c089ea90a19986443eb Mon Sep 17 00:00:00 2001
From: zeertzjq <zeertzjq@outlook.com>
Date: Mon, 9 May 2022 12:16:19 +0100
Subject: [PATCH] patch 8.2.4924: maparg() may return a string that cannot be
 reused

Problem:    maparg() may return a string that cannot be reused.
Solution:   use msg_outtrans_special() instead of str2special().
(closes #10384)
---
 src/message.c                | 35 +++++++++++++++--------------------
 src/option.c                 |  2 ++
 src/testdir/test_mapping.vim |  7 +++++++
 src/testdir/test_options.vim | 20 ++++++++++++++++++++
 4 files changed, 44 insertions(+), 20 deletions(-)

diff --git a/src/message.c b/src/message.c
index 0898d90..363dbe1 100644
--- a/src/message.c
+++ b/src/message.c
@@ -1637,6 +1637,9 @@ msg_outtrans_special(
 	}
 	else
 	    text = (char *)str2special(&str, from);
+	if (text[0] != NUL && text[1] == NUL)
+	    // single-byte character or illegal byte
+	    text = (char *)transchar_byte((char_u)text[0]);
 	len = vim_strsize((char_u *)text);
 	if (maxlen > 0 && retval + len >= maxlen)
 	    break;
@@ -1671,6 +1674,7 @@ str2special_save(
 
 /*
  * Return the printable string for the key codes at "*sp".
+ * On illegal byte return a string with only that byte.
  * Used for translating the lhs or rhs of a mapping to printable chars.
  * Advances "sp" to the next code.
  */
@@ -1714,7 +1718,7 @@ str2special(
 	    special = TRUE;
     }
 
-    if (has_mbyte && !IS_SPECIAL(c))
+    if (has_mbyte && !IS_SPECIAL(c) && MB_BYTE2LEN(c) > 1)
     {
         char_u  *p;
 
@@ -1722,30 +1726,21 @@ str2special(
         // Try to un-escape a multi-byte character after modifiers.
         p = mb_unescape(sp);
 
-        if (p == NULL)
-	{
-           int len = (*mb_ptr2len)(str);
-
-           // Check for an illegal byte.
-           if (MB_BYTE2LEN(*str) > len)
-           {
-               transchar_nonprint(curbuf, buf, c);
-               *sp = str + 1;
-               return buf;
-           }
-           *sp = str + len;
-           p = str;
-	}
-	/* Since 'special' is TRUE the multi-byte character 'c' will be
-	 * processed by get_special_key_name() */
-	c = (*mb_ptr2char)(p);
+       if (p != NULL)
+           // Since 'special' is TRUE the multi-byte character 'c' will be
+           // processed by get_special_key_name()
+           c = (*mb_ptr2char)(p);
+       else
+           // illegal byte
+           *sp = str + 1;
     }
     else
+        // single-byte character or illegal byte
 	*sp = str + 1;
 
-    /* Make unprintable characters in <> form, also <M-Space> and <Tab>.
+    /* Make special keys and C0 control characters in <> form, also <M-Space>. 
      * Use <Space> only for lhs of a mapping. */
-    if (special || char2cells(c) > 1 || (from && c == ' '))
+    if (special || c < ' ' || (from && c == ' '))
 	return get_special_key_name(c, modifiers);
     buf[0] = c;
     buf[1] = NUL;
diff --git a/src/option.c b/src/option.c
index eb610dd..6a93a7b 100644
--- a/src/option.c
+++ b/src/option.c
@@ -3878,6 +3878,8 @@ get_option_value(
 	    if ((char_u **)varp == &curbuf->b_p_key
 						&& **(char_u **)(varp) != NUL)
 		*stringval = vim_strsave((char_u *)"*****");
+	    else if ((char_u **)varp == &p_pt)	// 'pastetoggle'
+		*stringval = str2special_save(*(char_u **)(varp), FALSE);
 	    else
 #endif
 		*stringval = vim_strsave(*(char_u **)(varp));
diff --git a/src/testdir/test_mapping.vim b/src/testdir/test_mapping.vim
index 58c284d..9e32b38 100644
--- a/src/testdir/test_mapping.vim
+++ b/src/testdir/test_mapping.vim
@@ -480,6 +480,13 @@ func Test_list_mappings()
   nmap <M-…> foo
   call assert_equal(['n  <M-…>         foo'],
         \ execute('nmap <M-…>')->trim()->split("\n"))
+  
+  " illegal bytes
+  let str = ":\x7f:\x80:\x90:\xd0:"
+  exe 'nmap foo ' .. str
+  call assert_equal(['n  foo           ' .. strtrans(str)],
+        \ execute('nmap foo')->trim()->split("\n"))
+  unlet str
 
 endfunc
 
diff --git a/src/testdir/test_options.vim b/src/testdir/test_options.vim
index 7584465..e494cd9 100644
--- a/src/testdir/test_options.vim
+++ b/src/testdir/test_options.vim
@@ -31,6 +31,26 @@ func Test_isfname()
   set isfname&
 endfunc
 
+" Test for getting the value of 'pastetoggle'
+func Test_pastetoggle()
+  " character with K_SPECIAL byte
+  let &pastetoggle = '…'
+  call assert_equal('…', &pastetoggle)
+  call assert_equal("\n  pastetoggle=…", execute('set pastetoggle?'))
+
+  " modified character with K_SPECIAL byte
+  let &pastetoggle = '<M-…>'
+  call assert_equal('<M-…>', &pastetoggle)
+  call assert_equal("\n  pastetoggle=<M-…>", execute('set pastetoggle?'))
+
+  " illegal bytes
+  let str = ":\x7f:\x80:\x90:\xd0:"
+  let &pastetoggle = str
+  call assert_equal(str, &pastetoggle)
+  call assert_equal("\n  pastetoggle=" .. strtrans(str), execute('set pastetoggle?'))
+  unlet str
+endfunc
+
 func Test_wildchar()
   " Empty 'wildchar' used to access invalid memory.
   call assert_fails('set wildchar=', 'E521:')
-- 
1.8.3.1

