]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
[gdb] Add c_ctrl/c_unctrl
authorTom de Vries <tdevries@suse.de>
Thu, 26 Mar 2026 09:45:59 +0000 (10:45 +0100)
committerTom de Vries <tdevries@suse.de>
Thu, 26 Mar 2026 09:45:59 +0000 (10:45 +0100)
Readline exports macros CTRL/UNCTRL (compatible with readline macro
CTRL_CHAR), such that:
- CTRL_CHAR ('C') == 0
- CTRL_CHAR (0x03 /* ^C */) == 1
- CTRL ('C') == 0x03 /* ^C */
- CTRL ('c') == 0x03 /* ^C */
- UNCTRL (0x03 /* ^C */) == 'C'

Add c_ctrl/c_unctrl, a variant of CTRL/UNCTRL that's compatible with gnulib's
c_iscntrl.

While c_iscntrl (0x7f /* ^? */) == 1, CTRL_CHAR (0x7f /* ^? */) == 0.

Consequently, the current code using CTRL_CHAR also explicitly handles RUBOUT
(which is readline's way of representing ^?).

Use c_iscntrl/c_ctrl/c_unctrl instead of CTRL_CHAR/CTRL/UNCTRL, removing
redundant RUBOUT handling code.

Tested on x86_64-linux.

A v1 was submitted here [2].

Changes in v2:
- change $subject to use gdb instead of gdbsupport since we're adding the
  new functions in gdb/utils.c
- change parameter and result type of c_unctrl to unsigned char
- avoid using readline macros in c_unctrl implementation
- rewrite c_unctrl to be compatible with c_iscntrl, making sure to handle
  ^?.
- add c_ctrl
- use c_ctrl/c_iscntrl instead of CTRL/CTRL_CHAR.

Suggested-By: Tom Tromey <tom@tromey.com> [1]
Approved-By: Tom Tromey <tom@tromey.com>
[1] https://sourceware.org/pipermail/gdb-patches/2026-March/226136.html
[2] https://sourceware.org/pipermail/gdb-patches/2026-March/226171.html

gdb/completer.c
gdb/tui/tui-io.c
gdb/tui/tui.c
gdb/utils.c
gdb/utils.h

index 8c70a61cdec6c768b7babee605829dc69cbec0db..49189ac183f2c12df37286e97f9e9e60b264f097 100644 (file)
@@ -3044,7 +3044,7 @@ gdb_fnwidth (const char *string)
   width = pos = 0;
   while (string[pos])
     {
-      if (CTRL_CHAR (string[pos]) || string[pos] == RUBOUT)
+      if (c_iscntrl (string[pos]))
        {
          width += 2;
          pos++;
@@ -3118,20 +3118,10 @@ gdb_fnprint (const char *to_print, int prefix_bytes,
   s = to_print + prefix_bytes;
   while (*s)
     {
-      if (CTRL_CHAR (*s))
+      if (c_iscntrl (*s))
        {
          displayer->putch (displayer, '^');
-         displayer->putch (displayer, UNCTRL (*s));
-         printed_len += 2;
-         s++;
-#if defined (HANDLE_MULTIBYTE)
-         memset (&ps, 0, sizeof (mbstate_t));
-#endif
-       }
-      else if (*s == RUBOUT)
-       {
-         displayer->putch (displayer, '^');
-         displayer->putch (displayer, '?');
+         displayer->putch (displayer, c_unctrl (*s));
          printed_len += 2;
          s++;
 #if defined (HANDLE_MULTIBYTE)
index 6b94fa2e8b0f8c3b37ed49d8dd363c25761566a7..e0892a40c0d7e286a1e227d119e68fc1635cb6e9 100644 (file)
@@ -631,10 +631,10 @@ tui_redisplay_readline (void)
        break;
 
       c = (unsigned char) rl_line_buffer[in];
-      if (CTRL_CHAR (c) || c == RUBOUT)
+      if (c_iscntrl (c))
        {
          waddch (w, '^');
-         waddch (w, CTRL_CHAR (c) ? UNCTRL (c) : '?');
+         waddch (w, c_unctrl (c));
        }
       else if (c == '\t')
        {
index b6b6fb7acf515eecc97173be3adb4b7eac027f7c..9cf21f390c8049352ea92f5d6b310772fc6752ec 100644 (file)
@@ -364,8 +364,8 @@ tui_ensure_readline_initialized ()
   rl_bind_key_in_map ('a', tui_rl_switch_mode, tui_ctlx_keymap);
   rl_bind_key_in_map ('A', tui_rl_switch_mode, emacs_ctlx_keymap);
   rl_bind_key_in_map ('A', tui_rl_switch_mode, tui_ctlx_keymap);
-  rl_bind_key_in_map (CTRL ('A'), tui_rl_switch_mode, emacs_ctlx_keymap);
-  rl_bind_key_in_map (CTRL ('A'), tui_rl_switch_mode, tui_ctlx_keymap);
+  rl_bind_key_in_map (c_ctrl ('A'), tui_rl_switch_mode, emacs_ctlx_keymap);
+  rl_bind_key_in_map (c_ctrl ('A'), tui_rl_switch_mode, tui_ctlx_keymap);
   rl_bind_key_in_map ('1', tui_rl_delete_other_windows, emacs_ctlx_keymap);
   rl_bind_key_in_map ('1', tui_rl_delete_other_windows, tui_ctlx_keymap);
   rl_bind_key_in_map ('2', tui_rl_change_windows, emacs_ctlx_keymap);
index 6908256de4d4a4d9b863940d8cba374d827d30f1..b073bb9fdf30b6e5a26a6da5f5c50851367502de 100644 (file)
@@ -3733,6 +3733,85 @@ extract_single_filename_arg (const char *args)
   return filename;
 }
 
+/* See utils.h.  */
+
+unsigned char
+c_unctrl (unsigned char c)
+{
+  if (!c_iscntrl (c))
+    return c;
+
+  unsigned char res = c;
+  if (res >= 0x40)
+    {
+      /* Map 0x7f (^?) to 0x3f (?).  */
+      res -= 0x40;
+    }
+  else
+    {
+      /* Map 0x03 (^C) to 0x43 (C).  */
+      res += 0x40;
+    }
+
+  return res;
+}
+
+/* See utils.h.  */
+
+unsigned char
+c_ctrl (unsigned char c)
+{
+  unsigned char res = c;
+  if (res < 0x40)
+    {
+      /* Map 0x3f (?) to 0x7f (^?).  */
+      res += 0x40;
+    }
+  else
+    {
+      res = c_toupper (res);
+
+      /* Map 0x43 (C) to 0x03 (^C).  */
+      res -= 0x40;
+    }
+
+  return c_iscntrl (res) ? res : c;
+}
+
+#if GDB_SELF_TEST
+static void
+test_c_ctrl_unctrl ()
+{
+  /* Basic check.  */
+  SELF_CHECK (c_ctrl ('C') == 0x03);
+  SELF_CHECK (c_ctrl ('c') == 0x03);
+  SELF_CHECK (c_unctrl (0x03) == 'C');
+
+  /* Function c_iscntrl considers ^? to be a control character, but for some
+     reason, readline's CTRL_CHAR doesn't, so CTRL/UNCTRL don't handle it.
+     Check that c_ctrl/c_unctrl do handle it.  */
+  SELF_CHECK (c_ctrl ('?') == 0x7f);
+  SELF_CHECK (c_unctrl (0x7f) == '?');
+
+  /* Consistency check.  */
+  for (unsigned int i = 0; i < 0x100; i++)
+    {
+      unsigned char ch = i;
+      unsigned char unctrl_ch = c_unctrl (ch);
+      if (!c_iscntrl (ch))
+       {
+         SELF_CHECK (unctrl_ch == ch);
+         continue;
+       }
+
+      SELF_CHECK (!c_iscntrl (unctrl_ch));
+      SELF_CHECK (!c_islower (unctrl_ch));
+      SELF_CHECK (c_ctrl (unctrl_ch) == ch);
+      SELF_CHECK (c_ctrl (c_tolower (unctrl_ch)) == ch);
+    }
+}
+#endif
+
 #if GDB_SELF_TEST
 static void
 test_assign_set_return_if_changed ()
@@ -3834,5 +3913,6 @@ When set, debugging messages will be marked with seconds and microseconds."),
   selftests::register_test ("pager", test_pager);
   selftests::register_test ("assign_set_return_if_changed",
                            test_assign_set_return_if_changed);
+  selftests::register_test ("c_ctrl_unctrl", test_c_ctrl_unctrl);
 #endif
 }
index 7cab021e7a91526a866633863cbb17663d3e406c..ecef7d13c20a5460677604d438173d04c203d91b 100644 (file)
@@ -515,4 +515,12 @@ private:
   std::vector<string_file> m_warnings;
 };
 
+/* Re-implementation of readline's CTRL/UNCTRL (compatible with readline's
+   CTRL_CHAR), designed to be compatible with gnulib's c_iscntrl.
+   While gnulib's c-ctype.h still uses int for character parameters and
+   results, we use unsigned char here for maximum clarity.  */
+
+extern unsigned char c_ctrl (unsigned char c);
+extern unsigned char c_unctrl (unsigned char c);
+
 #endif /* GDB_UTILS_H */