]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
gdb: make get_chars_per_line return an unsigned value
authorAndrew Burgess <aburgess@redhat.com>
Thu, 4 Dec 2025 15:19:37 +0000 (15:19 +0000)
committerAndrew Burgess <aburgess@redhat.com>
Mon, 15 Dec 2025 14:56:45 +0000 (14:56 +0000)
I noticed that get_chars_per_line currently returns an 'int', but the
value it is returning, from utils.c, is an 'unsigned int'.  In some
cases this can cause weird behaviour as an unlimited width terminal
will have UINT_MAX characters per line, which will appear as negative
when returned from get_chars_per_line.

This has been the case since get_chars_per_line was added in commit:

  commit 2f2287318b33ddf855a692fcc191f6b25caf4644
  Date:   Wed Dec 16 18:18:40 2020 +0100

      [gdb/cli] Add a progress meter

Lets make get_chars_per_line return an unsigned value, and update all
the uses of this function to hold the result in an unsigned variable.

I ran into this issue when looking at print_gdb_hints (from top.c)
where a very large get_chars_per_line() value would appear negative,
and so the startup hints would be printed without a box when really
they should have been boxed.  Someone else noticed this problem while
I was building this patch, and pushed commit:

  commit 06e470d8fc0ae0e83fe0977fdf8c011998980891
  Date:   Sat Nov 29 15:48:55 2025 +0100

      gdb: handle unlimited screen width case in print_gdb_hints

This commit works around the signed / unsigned confusion entirely
within print_gdb_hints by adding a case to 'int' in one place.  The
change I present here reverts parts of 06e470d8fc0a in favour of
fixing the type of WIDTH within print_gdb_hints.

It is worth nothing that there are other bugs in print_gdb_hints
relating to how WIDTH is handled, but these are fixed in the next
commit.

By updating the return type of get_chars_per_line, I ran into some
issues in cli-out.c relating to how progress bars are handled.  The
existing code includes code like:

   if (!stream->isatty ()
       || !current_ui->input_interactive_p ()
       || chars_per_line < MIN_CHARS_PER_LINE)
     return;

The early return here triggers when progress bars should not be
printed.  Notice that when the terminal width is unlimited,
CHARS_PER_LINE will appear negative, and so the early return will
trigger.

It turns out that our testsuite depends on this; the debuginfod tests
don't expect to see a progress bar, and they don't because within the
tests we set the width to unlimited.

By "fixing" the type of CHARS_PER_LINE to 'unsigned int', an unlimited
width terminal no longer triggers the early return, and GDB starts
trying to print a progress bar in our debuginfod tests, which cause
the tests to fail.

I think the real fix is to add a new flag to allow progress bars to be
disabled, the tests can then use this.  I will add just such a flag at
the end of this series.

For now though, I propose adding a bodge.  If CHARS_PER_LINE is
UINT_MAX, then we should act as if progress bars are disabled.  The
above code now becomes:

   if (!stream->isatty ()
       || !current_ui->input_interactive_p ()
       || chars_per_line < MIN_CHARS_PER_LINE
       || chars_per_line == UINT_MAX)
     return;

This change is in cli_ui_out::clear_progress_notify.  There is a
similar change in cli_ui_out::do_progress_notify.  With these two
changes, the debuginfod tests are working again.  This bodge will be
removed by the last patch in this series.

There's no tests with this commit yet as print_gdb_hints has other
bugs which will be fixed in the next commit.  At this point I'll add
some tests.

Reviewed-By: Guinevere Larsen <guinevere@redhat.com>
Approved-By: Tom Tromey <tom@tromey.com>
gdb/cli-out.c
gdb/top.c
gdb/utils.c
gdb/utils.h

index 27a82f607eb3d6abcd9867880e99f824a0392312..5aa13a64271db06c6c8e9a9359f76cf60a1d0b9c 100644 (file)
@@ -298,18 +298,16 @@ cli_ui_out::do_progress_notify (const std::string &msg,
                                const char *unit,
                                double howmuch, double total)
 {
-  int chars_per_line = get_chars_per_line ();
+  unsigned int chars_per_line = get_chars_per_line ();
   struct ui_file *stream = get_unbuffered (m_streams.back ());
   cli_progress_info &info (m_progress_info.back ());
 
-  if (chars_per_line > MAX_CHARS_PER_LINE)
-    chars_per_line = MAX_CHARS_PER_LINE;
-
   if (info.state == progress_update::START)
     {
       if (stream->isatty ()
          && current_ui->input_interactive_p ()
-         && chars_per_line >= MIN_CHARS_PER_LINE)
+         && chars_per_line >= MIN_CHARS_PER_LINE
+         && chars_per_line != UINT_MAX)
        {
          gdb_printf (stream, "%s\n", msg.c_str ());
          info.state = progress_update::BAR;
@@ -321,10 +319,12 @@ cli_ui_out::do_progress_notify (const std::string &msg,
        }
     }
 
-  if (info.state != progress_update::BAR
-      || chars_per_line < MIN_CHARS_PER_LINE)
+  if (info.state != progress_update::BAR)
     return;
 
+  if (chars_per_line > MAX_CHARS_PER_LINE)
+    chars_per_line = MAX_CHARS_PER_LINE;
+
   if (total > 0 && howmuch >= 0 && howmuch <= 1.0)
     {
       std::string progress = string_printf (" %3.f%% (%.2f %s)",
@@ -385,14 +385,15 @@ void
 cli_ui_out::clear_progress_notify ()
 {
   struct ui_file *stream = get_unbuffered (m_streams.back ());
-  int chars_per_line = get_chars_per_line ();
+  unsigned int chars_per_line = get_chars_per_line ();
 
   scoped_restore save_pagination
     = make_scoped_restore (&pagination_enabled, false);
 
   if (!stream->isatty ()
       || !current_ui->input_interactive_p ()
-      || chars_per_line < MIN_CHARS_PER_LINE)
+      || chars_per_line < MIN_CHARS_PER_LINE
+      || chars_per_line == UINT_MAX)
     return;
 
   if (chars_per_line > MAX_CHARS_PER_LINE)
index 4f689255907d575a0474ce690d902f6669f77252..adc0596a504fd1f6e26b13597016c83da4b7138d 100644 (file)
--- a/gdb/top.c
+++ b/gdb/top.c
@@ -1413,7 +1413,7 @@ box_one_message (ui_file *stream, std::string message, int width)
 void
 print_gdb_hints (struct ui_file *stream)
 {
-  int width = get_chars_per_line ();
+  unsigned int width = get_chars_per_line ();
 
   /* Arbitrarily setting maximum width to 80 characters, so that
      things are big and visible but not overwhelming.  */
@@ -1438,10 +1438,10 @@ print_gdb_hints (struct ui_file *stream)
              styled_string (command_style.style (), "apropos <word>"));
 
   /* If there isn't enough space to display the longest URL in a boxed
-     style or if screen width is unlimited, use the simple styling of a
-     singular visual break.  The longest URL is used because the other
-     messages may be broken into multiple lines, but URLs can't.  */
-  if (width - 3 <= (int) docs_url.length ())
+     style, use the simple styling of a singular visual break.  The longest
+     URL is used because the other messages may be broken into multiple
+     lines, but URLs can't.  */
+  if (width - 3 <= docs_url.length ())
     {
       for (string_file &msg : styled_msg)
        gdb_printf (stream, "%s\n", msg.c_str ());
index d06ebc0014b87958e1bafc7fd71ea74aa75b0a81..57b8d44259266d8a20aa1bdec2aa64bd52baa629 100644 (file)
@@ -1546,9 +1546,10 @@ gdb_flush (struct ui_file *stream)
 
 /* See utils.h.  */
 
-int
+unsigned int
 get_chars_per_line ()
 {
+  gdb_assert (chars_per_line > 0);
   return chars_per_line;
 }
 
index 0e28f9424e5aa6631d3a76ca5eb7c96d486ed835..6316547653eec3a9fdd2b527f82430efa9e0de46 100644 (file)
@@ -157,9 +157,10 @@ extern void wrap_here (int);
 
 extern void reinitialize_more_filter (void);
 
-/* Return the number of characters in a line.  */
+/* Return the number of characters in a line.  Will never be zero, but can
+   be UINT_MAX, which indicates unlimited characters per line.  */
 
-extern int get_chars_per_line ();
+extern unsigned int get_chars_per_line ();
 
 extern bool pagination_enabled;