Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
From: Tom de Vries <tdevries@suse.de>
To: gdb-patches@sourceware.org
Subject: [PATCH v2] [gdb] Add c_ctrl/c_unctrl
Date: Wed, 25 Mar 2026 13:39:46 +0100	[thread overview]
Message-ID: <20260325123946.4072546-1-tdevries@suse.de> (raw)

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]

[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  | 16 ++--------
 gdb/tui/tui-io.c |  4 +--
 gdb/tui/tui.c    |  4 +--
 gdb/utils.c      | 80 ++++++++++++++++++++++++++++++++++++++++++++++++
 gdb/utils.h      |  8 +++++
 5 files changed, 95 insertions(+), 17 deletions(-)

diff --git a/gdb/completer.c b/gdb/completer.c
index 8c70a61cdec..49189ac183f 100644
--- a/gdb/completer.c
+++ b/gdb/completer.c
@@ -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)
diff --git a/gdb/tui/tui-io.c b/gdb/tui/tui-io.c
index 6b94fa2e8b0..e0892a40c0d 100644
--- a/gdb/tui/tui-io.c
+++ b/gdb/tui/tui-io.c
@@ -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')
 	{
diff --git a/gdb/tui/tui.c b/gdb/tui/tui.c
index b6b6fb7acf5..9cf21f390c8 100644
--- a/gdb/tui/tui.c
+++ b/gdb/tui/tui.c
@@ -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);
diff --git a/gdb/utils.c b/gdb/utils.c
index 6908256de4d..b073bb9fdf3 100644
--- a/gdb/utils.c
+++ b/gdb/utils.c
@@ -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
 }
diff --git a/gdb/utils.h b/gdb/utils.h
index 7cab021e7a9..ecef7d13c20 100644
--- a/gdb/utils.h
+++ b/gdb/utils.h
@@ -515,4 +515,12 @@ struct deferred_warnings final : public warning_hook_handler_type
   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 */

base-commit: 4054f280399225fc40ac42423246ac9c5de6f857
-- 
2.51.0


             reply	other threads:[~2026-03-25 12:40 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-03-25 12:39 Tom de Vries [this message]
2026-03-25 17:40 ` Tom Tromey
2026-03-25 18:12   ` Tom de Vries
2026-03-25 19:45     ` Tom Tromey
2026-03-25 20:50       ` Tom Tromey
2026-03-26  9:57         ` Tom de Vries
2026-03-26 14:07           ` Tom de Vries

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260325123946.4072546-1-tdevries@suse.de \
    --to=tdevries@suse.de \
    --cc=gdb-patches@sourceware.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox