Introduce gdb-specific %p format suffixes
authorPedro Alves <palves@redhat.com>
Wed, 5 Jun 2019 08:17:16 +0000 (09:17 +0100)
committerTom Tromey <tom@tromey.com>
Tue, 1 Oct 2019 21:12:38 +0000 (15:12 -0600)
This introduces a few gdb-specific %p format suffixes.  This is useful
for emitting gdb-specific output in an ergonomic way.  It also yields
code that is more i18n-friendly.

The comment before ui_out::message explains the details.

Note that the tests had to change a little.  When using one of the gdb
printf functions with styling, there can be spurious style changes
emitted to the output.  This did not seem worthwhile to fix, as the
low-level output functions are rather spaghetti-ish already, and I
didn't want to make them even worse.

This change also necessitated adding support for "*" as precision and
width in format_pieces.  These are used in various spots in gdb, and
it seemed better to me to implement them than to remove the uses.

gdb/ChangeLog
2019-10-01  Pedro Alves  <palves@redhat.com>
    Tom Tromey  <tom@tromey.com>

* unittests/format_pieces-selftests.c: Add gdb_format parameter.
(test_gdb_formats): New function.
(run_tests): Call it.
(test_format_specifier): Update.
* utils.h (fputs_filtered): Update comment.
(vfprintf_styled, vfprintf_styled_no_gdbfmt)
(fputs_styled_unfiltered): Declare.
* utils.c (fputs_styled_unfiltered): New function.
(vfprintf_maybe_filtered): Add gdbfmt parameter.
(vfprintf_filtered): Update.
(vfprintf_unfiltered, vprintf_filtered): Update.
(vfprintf_styled, vfprintf_styled_no_gdbfmt): New functions.
* ui-out.h (enum ui_out_flag) <unfiltered_output,
disallow_ui_out_field>: New constants.
(enum class field_kind): New.
(struct base_field_s, struct signed_field_s): New.
(signed_field): New function.
(struct string_field_s): New.
(string_field): New function.
(struct styled_string_s): New.
(styled_string): New function.
(class ui_out) <message>: Add comment.
<vmessage, call_do_message>: New methods.
<do_message>: Add style parameter.
* ui-out.c (ui_out::call_do_message, ui_out::vmessage): New
methods.
(ui_out::message): Rewrite.
* mi/mi-out.h (class mi_ui_out) <do_message>: Add style
parameter.
* mi/mi-out.c (mi_ui_out::do_message): Add style parameter.
* gdbsupport/format.h (class format_pieces) <format_pieces>: Add
gdb_extensions parameter.
(class format_piece): Add parameter to constructor.
(n_int_args): New field.
* gdbsupport/format.c (format_pieces::format_pieces): Add
gdb_extensions parameter.  Handle '*'.
* cli-out.h (class cli_ui_out) <do_message>: Add style parameter.
* cli-out.c (cli_ui_out::do_message): Add style parameter.  Call
vfprintf_styled_no_gdbfmt.
(cli_ui_out::do_field_string, cli_ui_out::do_spaces)
(cli_ui_out::do_text, cli_ui_out::field_separator): Allow
unfiltered output.
* ui-style.h (struct ui_file_style) <ptr>: New method.

gdb/testsuite/ChangeLog
2019-10-01  Tom Tromey  <tom@tromey.com>

* gdb.base/style.exp: Update tests.

15 files changed:
gdb/ChangeLog
gdb/cli-out.c
gdb/cli-out.h
gdb/gdbsupport/format.c
gdb/gdbsupport/format.h
gdb/mi/mi-out.c
gdb/mi/mi-out.h
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.base/style.exp
gdb/ui-out.c
gdb/ui-out.h
gdb/ui-style.h
gdb/unittests/format_pieces-selftests.c
gdb/utils.c
gdb/utils.h

index 5c3dec35790fafdc4b5dbbe3ffec9c036935c655..ff336c86f0e6b23e9a5ba3cb71e27b4766778b3a 100644 (file)
@@ -1,3 +1,50 @@
+2019-10-01  Pedro Alves  <palves@redhat.com>
+           Tom Tromey  <tom@tromey.com>
+
+       * unittests/format_pieces-selftests.c: Add gdb_format parameter.
+       (test_gdb_formats): New function.
+       (run_tests): Call it.
+       (test_format_specifier): Update.
+       * utils.h (fputs_filtered): Update comment.
+       (vfprintf_styled, vfprintf_styled_no_gdbfmt)
+       (fputs_styled_unfiltered): Declare.
+       * utils.c (fputs_styled_unfiltered): New function.
+       (vfprintf_maybe_filtered): Add gdbfmt parameter.
+       (vfprintf_filtered): Update.
+       (vfprintf_unfiltered, vprintf_filtered): Update.
+       (vfprintf_styled, vfprintf_styled_no_gdbfmt): New functions.
+       * ui-out.h (enum ui_out_flag) <unfiltered_output,
+       disallow_ui_out_field>: New constants.
+       (enum class field_kind): New.
+       (struct base_field_s, struct signed_field_s): New.
+       (signed_field): New function.
+       (struct string_field_s): New.
+       (string_field): New function.
+       (struct styled_string_s): New.
+       (styled_string): New function.
+       (class ui_out) <message>: Add comment.
+       <vmessage, call_do_message>: New methods.
+       <do_message>: Add style parameter.
+       * ui-out.c (ui_out::call_do_message, ui_out::vmessage): New
+       methods.
+       (ui_out::message): Rewrite.
+       * mi/mi-out.h (class mi_ui_out) <do_message>: Add style
+       parameter.
+       * mi/mi-out.c (mi_ui_out::do_message): Add style parameter.
+       * gdbsupport/format.h (class format_pieces) <format_pieces>: Add
+       gdb_extensions parameter.
+       (class format_piece): Add parameter to constructor.
+       (n_int_args): New field.
+       * gdbsupport/format.c (format_pieces::format_pieces): Add
+       gdb_extensions parameter.  Handle '*'.
+       * cli-out.h (class cli_ui_out) <do_message>: Add style parameter.
+       * cli-out.c (cli_ui_out::do_message): Add style parameter.  Call
+       vfprintf_styled_no_gdbfmt.
+       (cli_ui_out::do_field_string, cli_ui_out::do_spaces)
+       (cli_ui_out::do_text, cli_ui_out::field_separator): Allow
+       unfiltered output.
+       * ui-style.h (struct ui_file_style) <ptr>: New method.
+
 2019-10-01  Tom Tromey  <tom@tromey.com>
 
        * unittests/format_pieces-selftests.c: Update.  Add final format.
index fa72a1d344f41afc281d8414aa0544a2626fc0f7..c713607e06800201b6b25149ff44ea95360b6fb7 100644 (file)
@@ -170,7 +170,12 @@ cli_ui_out::do_field_string (int fldno, int width, ui_align align,
     spaces (before);
 
   if (string)
-    fputs_styled (string, style, m_streams.back ());
+    {
+      if (test_flags (unfiltered_output))
+       fputs_styled_unfiltered (string, style, m_streams.back ());
+      else
+       fputs_styled (string, style, m_streams.back ());
+    }
 
   if (after)
     spaces (after);
@@ -201,7 +206,10 @@ cli_ui_out::do_spaces (int numspaces)
   if (m_suppress_output)
     return;
 
-  print_spaces_filtered (numspaces, m_streams.back ());
+  if (test_flags (unfiltered_output))
+    print_spaces (numspaces, m_streams.back ());
+  else
+    print_spaces_filtered (numspaces, m_streams.back ());
 }
 
 void
@@ -210,16 +218,24 @@ cli_ui_out::do_text (const char *string)
   if (m_suppress_output)
     return;
 
-  fputs_filtered (string, m_streams.back ());
+  if (test_flags (unfiltered_output))
+    fputs_unfiltered (string, m_streams.back ());
+  else
+    fputs_filtered (string, m_streams.back ());
 }
 
 void
-cli_ui_out::do_message (const char *format, va_list args)
+cli_ui_out::do_message (const ui_file_style &style,
+                       const char *format, va_list args)
 {
   if (m_suppress_output)
     return;
 
-  vfprintf_unfiltered (m_streams.back (), format, args);
+  /* Use the "no_gdbfmt" variant here to avoid recursion.
+     vfprintf_styled calls into cli_ui_out::message to handle the
+     gdb-specific printf formats.  */
+  vfprintf_styled_no_gdbfmt (m_streams.back (), style,
+                            !test_flags (unfiltered_output), format, args);
 }
 
 void
@@ -255,7 +271,10 @@ cli_ui_out::do_redirect (ui_file *outstream)
 void
 cli_ui_out::field_separator ()
 {
-  fputc_filtered (' ', m_streams.back ());
+  if (test_flags (unfiltered_output))
+    fputc_unfiltered (' ', m_streams.back ());
+  else
+    fputc_filtered (' ', m_streams.back ());
 }
 
 /* Constructor for cli_ui_out.  */
index bc8b781d605d749adb28a4224b985ba87da08d0f..d7bd23b0ef22ae7560866dc82452bfd0f4091f63 100644 (file)
@@ -64,8 +64,9 @@ protected:
     override ATTRIBUTE_PRINTF (6,0);
   virtual void do_spaces (int numspaces) override;
   virtual void do_text (const char *string) override;
-  virtual void do_message (const char *format, va_list args) override
-    ATTRIBUTE_PRINTF (2,0);
+  virtual void do_message (const ui_file_style &style,
+                          const char *format, va_list args) override
+    ATTRIBUTE_PRINTF (3,0);
   virtual void do_wrap_hint (const char *identstring) override;
   virtual void do_flush () override;
   virtual void do_redirect (struct ui_file *outstream) override;
index a5a367015f1695ab3e1893579356a89125d73d7a..1e803501ae67ea965812149175e357b2bb1c22f2 100644 (file)
 #include "common-defs.h"
 #include "format.h"
 
-format_pieces::format_pieces (const char **arg)
+format_pieces::format_pieces (const char **arg, bool gdb_extensions)
 {
   const char *s;
-  char *f, *string;
+  const char *string;
   const char *prev_start;
   const char *percent_loc;
   char *sub_start, *current_substring;
@@ -31,70 +31,79 @@ format_pieces::format_pieces (const char **arg)
 
   s = *arg;
 
-  /* Parse the format-control string and copy it into the string STRING,
-     processing some kinds of escape sequence.  */
+  if (gdb_extensions)
+    {
+      string = *arg;
+      *arg += strlen (*arg);
+    }
+  else
+    {
+      /* Parse the format-control string and copy it into the string STRING,
+        processing some kinds of escape sequence.  */
 
-  f = string = (char *) alloca (strlen (s) + 1);
+      char *f = (char *) alloca (strlen (s) + 1);
+      string = f;
 
-  while (*s != '"' && *s != '\0')
-    {
-      int c = *s++;
-      switch (c)
+      while ((gdb_extensions || *s != '"') && *s != '\0')
        {
-       case '\0':
-         continue;
-
-       case '\\':
-         switch (c = *s++)
+         int c = *s++;
+         switch (c)
            {
+           case '\0':
+             continue;
+
            case '\\':
-             *f++ = '\\';
-             break;
-           case 'a':
-             *f++ = '\a';
-             break;
-           case 'b':
-             *f++ = '\b';
-             break;
-           case 'e':
-             *f++ = '\e';
-             break;
-           case 'f':
-             *f++ = '\f';
-             break;
-           case 'n':
-             *f++ = '\n';
-             break;
-           case 'r':
-             *f++ = '\r';
-             break;
-           case 't':
-             *f++ = '\t';
-             break;
-           case 'v':
-             *f++ = '\v';
-             break;
-           case '"':
-             *f++ = '"';
+             switch (c = *s++)
+               {
+               case '\\':
+                 *f++ = '\\';
+                 break;
+               case 'a':
+                 *f++ = '\a';
+                 break;
+               case 'b':
+                 *f++ = '\b';
+                 break;
+               case 'e':
+                 *f++ = '\e';
+                 break;
+               case 'f':
+                 *f++ = '\f';
+                 break;
+               case 'n':
+                 *f++ = '\n';
+                 break;
+               case 'r':
+                 *f++ = '\r';
+                 break;
+               case 't':
+                 *f++ = '\t';
+                 break;
+               case 'v':
+                 *f++ = '\v';
+                 break;
+               case '"':
+                 *f++ = '"';
+                 break;
+               default:
+                 /* ??? TODO: handle other escape sequences.  */
+                 error (_("Unrecognized escape character \\%c in format string."),
+                        c);
+               }
              break;
+
            default:
-             /* ??? TODO: handle other escape sequences.  */
-             error (_("Unrecognized escape character \\%c in format string."),
-                    c);
+             *f++ = c;
            }
-         break;
-
-       default:
-         *f++ = c;
        }
-    }
 
-  /* Terminate our escape-processed copy.  */
-  *f++ = '\0';
+      /* Terminate our escape-processed copy.  */
+      *f++ = '\0';
 
-  /* Whether the format string ended with double-quote or zero, we're
-     done with it; it's up to callers to complain about syntax.  */
-  *arg = s;
+      /* Whether the format string ended with double-quote or zero, we're
+        done with it; it's up to callers to complain about syntax.  */
+      *arg = s;
+    }
 
   /* Need extra space for the '\0's.  Doubling the size is sufficient.  */
 
@@ -105,7 +114,7 @@ format_pieces::format_pieces (const char **arg)
      argclass classifies the %-specs so we can give printf-type functions
      something of the right size.  */
 
-  f = string;
+  const char *f = string;
   prev_start = string;
   while (*f)
     if (*f++ == '%')
@@ -115,6 +124,7 @@ format_pieces::format_pieces (const char **arg)
        int seen_big_l = 0, seen_h = 0, seen_big_h = 0;
        int seen_big_d = 0, seen_double_big_d = 0;
        int bad = 0;
+       int n_int_args = 0;
 
        /* Skip over "%%", it will become part of a literal piece.  */
        if (*f == '%')
@@ -130,7 +140,7 @@ format_pieces::format_pieces (const char **arg)
        *current_substring++ = '\0';
 
        if (*sub_start != '\0')
-         m_pieces.emplace_back (sub_start, literal_piece);
+         m_pieces.emplace_back (sub_start, literal_piece, 0);
 
        percent_loc = f - 1;
 
@@ -155,16 +165,32 @@ format_pieces::format_pieces (const char **arg)
          }
 
        /* The next part of a format specifier is a width.  */
-       while (*f != '\0' && strchr ("0123456789", *f))
-         f++;
+       if (gdb_extensions && *f == '*')
+         {
+           ++f;
+           ++n_int_args;
+         }
+       else
+         {
+           while (*f != '\0' && strchr ("0123456789", *f))
+             f++;
+         }
 
        /* The next part of a format specifier is a precision.  */
        if (*f == '.')
          {
            seen_prec = 1;
            f++;
-           while (*f != '\0' && strchr ("0123456789", *f))
-             f++;
+           if (gdb_extensions && *f == '*')
+             {
+               ++f;
+               ++n_int_args;
+             }
+           else
+             {
+               while (*f != '\0' && strchr ("0123456789", *f))
+                 f++;
+             }
          }
 
        /* The next part of a format specifier is a length modifier.  */
@@ -252,6 +278,20 @@ format_pieces::format_pieces (const char **arg)
              bad = 1;
            if (seen_hash || seen_zero || seen_space || seen_plus)
              bad = 1;
+
+           if (gdb_extensions)
+             {
+               switch (f[1])
+                 {
+                 case 's':
+                 case 'F':
+                 case '[':
+                 case ']':
+                   f++;
+                   break;
+                 }
+             }
+
            break;
 
          case 's':
@@ -336,7 +376,7 @@ format_pieces::format_pieces (const char **arg)
 
        prev_start = f;
 
-       m_pieces.emplace_back (sub_start, this_argclass);
+       m_pieces.emplace_back (sub_start, this_argclass, n_int_args);
       }
 
   /* Record the remainder of the string.  */
@@ -349,6 +389,6 @@ format_pieces::format_pieces (const char **arg)
       current_substring += f - prev_start;
       *current_substring++ = '\0';
 
-      m_pieces.emplace_back (sub_start, literal_piece);
+      m_pieces.emplace_back (sub_start, literal_piece, 0);
     }
 }
index 08ef66a760211b1bca1b9ff6521f9e7ac114a9b6..e2a47ba5187d951c91e318af4ea1224c5b5fecd2 100644 (file)
@@ -50,9 +50,10 @@ enum argclass
 
 struct format_piece
 {
-  format_piece (const char *str, enum argclass argc)
+  format_piece (const char *str, enum argclass argc, int n)
     : string (str),
-      argclass (argc)
+      argclass (argc),
+      n_int_args (n)
   {
   }
 
@@ -64,13 +65,17 @@ struct format_piece
 
   const char *string;
   enum argclass argclass;
+  /* Count the number of preceding 'int' arguments that must be passed
+     along.  This is used for a width or precision of '*'.  Note that
+     this feature is only available in "gdb_extensions" mode.  */
+  int n_int_args;
 };
 
 class format_pieces
 {
 public:
 
-  format_pieces (const char **arg);
+  format_pieces (const char **arg, bool gdb_extensions = false);
   ~format_pieces () = default;
 
   DISABLE_COPY_AND_ASSIGN (format_pieces);
index 0b930738f1dd5dbda118f441e5f24cde42ab5ebf..71af4865e9745e2f3cad112552c02ef389a6cdbf 100644 (file)
@@ -166,7 +166,8 @@ mi_ui_out::do_text (const char *string)
 }
 
 void
-mi_ui_out::do_message (const char *format, va_list args)
+mi_ui_out::do_message (const ui_file_style &style,
+                      const char *format, va_list args)
 {
 }
 
index 90528fd4e84d270ca4d877270130c50a6bbe031c..9393809b5f20c296afe93b308fb1834ae5bd95bb 100644 (file)
@@ -72,8 +72,9 @@ protected:
     override ATTRIBUTE_PRINTF (6,0);
   virtual void do_spaces (int numspaces) override;
   virtual void do_text (const char *string) override;
-  virtual void do_message (const char *format, va_list args) override
-    ATTRIBUTE_PRINTF (2,0);
+  virtual void do_message (const ui_file_style &style,
+                          const char *format, va_list args) override
+    ATTRIBUTE_PRINTF (3,0);
   virtual void do_wrap_hint (const char *identstring) override;
   virtual void do_flush () override;
   virtual void do_redirect (struct ui_file *outstream) override;
index 1a20cdaa29511c51408c25865ab294ee927dff60..b9a320c981151f4dd58793fcb4e907ffcdea5eef 100644 (file)
@@ -1,3 +1,7 @@
+2019-10-01  Tom Tromey  <tom@tromey.com>
+
+       * gdb.base/style.exp: Update tests.
+
 2019-10-01  Andreas Arnez  <arnez@linux.ibm.com>
 
        * gdb.base/pretty-print.c (struct s1_t): Change fields 'three' and
index 41c43dc8f7c877beffd23eeec7bd40bae1d90134..d2c3105bb9fd8d72547c8f983266adcf124b744b 100644 (file)
@@ -94,13 +94,13 @@ save_vars { env(TERM) } {
     gdb_test "" "${vers}.*" \
        "version is styled"
 
-    set address_style_expr [style "\"address\" style" address]
+    set address_style_expr [style ".*\".*address.*\".*style.*" address]
     gdb_test "show style address foreground" \
        "The ${address_style_expr} foreground color is: blue" \
        "style name and style word styled using its own style in show style"
 
-    set aliases_expr [style "aliases" title]
-    set breakpoints_expr [style "breakpoints" title]
+    set aliases_expr [style ".*aliases.*" title]
+    set breakpoints_expr [style ".*breakpoints.*" title]
     gdb_test "help" \
        [multi_line \
             "List of classes of commands:" \
@@ -111,8 +111,8 @@ save_vars { env(TERM) } {
            ] \
        "help classes of commands styled with title"
 
-    set taas_expr  [style "taas" title]
-    set tfaas_expr  [style "tfaas" title]
+    set taas_expr  [style ".*taas.*" title]
+    set tfaas_expr  [style ".*tfaas.*" title]
     set cut_for_thre_expr [style "cut for 'thre" highlight]
     gdb_test "apropos -v cut for 'thre" \
        [multi_line \
index e8fe44c8268e31ab0a3818581a4a4bc0c832d8f4..8cbaa4e0bc19a42010544cf2a3b2cdbf1b0ea9f2 100644 (file)
@@ -563,12 +563,190 @@ ui_out::text (const char *string)
 }
 
 void
-ui_out::message (const char *format, ...)
+ui_out::call_do_message (const ui_file_style &style, const char *format,
+                        ...)
 {
   va_list args;
 
   va_start (args, format);
-  do_message (format, args);
+  do_message (style, format, args);
+  va_end (args);
+}
+
+void
+ui_out::vmessage (const ui_file_style &in_style, const char *format,
+                 va_list args)
+{
+  format_pieces fpieces (&format, true);
+
+  ui_file_style style = in_style;
+
+  for (auto &&piece : fpieces)
+    {
+      const char *current_substring = piece.string;
+
+      gdb_assert (piece.n_int_args >= 0 && piece.n_int_args <= 2);
+      int intvals[2] = { 0, 0 };
+      for (int i = 0; i < piece.n_int_args; ++i)
+       intvals[i] = va_arg (args, int);
+
+      /* The only ones we support for now.  */
+      gdb_assert (piece.n_int_args == 0
+                 || piece.argclass == string_arg
+                 || piece.argclass == int_arg
+                 || piece.argclass == long_arg);
+
+      switch (piece.argclass)
+       {
+       case string_arg:
+         {
+           const char *str = va_arg (args, const char *);
+           switch (piece.n_int_args)
+             {
+             case 0:
+               call_do_message (style, current_substring, str);
+               break;
+             case 1:
+               call_do_message (style, current_substring, intvals[0], str);
+               break;
+             case 2:
+               call_do_message (style, current_substring,
+                                intvals[0], intvals[1], str);
+               break;
+             }
+         }
+         break;
+       case wide_string_arg:
+         gdb_assert_not_reached (_("wide_string_arg not supported in vmessage"));
+         break;
+       case wide_char_arg:
+         gdb_assert_not_reached (_("wide_char_arg not supported in vmessage"));
+         break;
+       case long_long_arg:
+         call_do_message (style, current_substring, va_arg (args, long long));
+         break;
+       case int_arg:
+         {
+           int val = va_arg (args, int);
+           switch (piece.n_int_args)
+             {
+             case 0:
+               call_do_message (style, current_substring, val);
+               break;
+             case 1:
+               call_do_message (style, current_substring, intvals[0], val);
+               break;
+             case 2:
+               call_do_message (style, current_substring,
+                                intvals[0], intvals[1], val);
+               break;
+             }
+         }
+         break;
+       case long_arg:
+         {
+           long val = va_arg (args, long);
+           switch (piece.n_int_args)
+             {
+             case 0:
+               call_do_message (style, current_substring, val);
+               break;
+             case 1:
+               call_do_message (style, current_substring, intvals[0], val);
+               break;
+             case 2:
+               call_do_message (style, current_substring,
+                                intvals[0], intvals[1], val);
+               break;
+             }
+         }
+         break;
+       case double_arg:
+         call_do_message (style, current_substring, va_arg (args, double));
+         break;
+       case long_double_arg:
+         gdb_assert_not_reached (_("long_double_arg not supported in vmessage"));
+         break;
+       case dec32float_arg:
+         gdb_assert_not_reached (_("dec32float_arg not supported in vmessage"));
+         break;
+       case dec64float_arg:
+         gdb_assert_not_reached (_("dec64float_arg not supported in vmessage"));
+         break;
+       case dec128float_arg:
+         gdb_assert_not_reached (_("dec128float_arg not supported in vmessage"));
+         break;
+       case ptr_arg:
+         switch (current_substring[2])
+           {
+           case 'F':
+             {
+               gdb_assert (!test_flags (disallow_ui_out_field));
+               base_field_s *bf = va_arg (args, base_field_s *);
+               switch (bf->kind)
+                 {
+                 case field_kind::SIGNED:
+                   {
+                     auto *f = (signed_field_s *) bf;
+                     field_signed (f->name, f->val);
+                   }
+                   break;
+                 case field_kind::STRING:
+                   {
+                     auto *f = (string_field_s *) bf;
+                     field_string (f->name, f->str);
+                   }
+                   break;
+                 }
+             }
+             break;
+           case 's':
+             {
+               styled_string_s *ss = va_arg (args, styled_string_s *);
+               call_do_message (ss->style, "%s", ss->str);
+             }
+             break;
+           case '[':
+             style = *va_arg (args, const ui_file_style *);
+             break;
+           case ']':
+             {
+               void *arg = va_arg (args, void *);
+               gdb_assert (arg == nullptr);
+
+               style = {};
+             }
+             break;
+           default:
+             call_do_message (style, current_substring, va_arg (args, void *));
+             break;
+           }
+         break;
+       case literal_piece:
+         /* Print a portion of the format string that has no
+            directives.  Note that this will not include any ordinary
+            %-specs, but it might include "%%".  That is why we use
+            call_do_message here.  Also, we pass a dummy argument
+            because some platforms have modified GCC to include
+            -Wformat-security by default, which will warn here if
+            there is no argument.  */
+         call_do_message (style, current_substring, 0);
+         break;
+       default:
+         internal_error (__FILE__, __LINE__,
+                         _("failed internal consistency check"));
+       }
+    }
+}
+
+void
+ui_out::message (const char *format, ...)
+{
+  va_list args;
+  va_start (args, format);
+
+  vmessage (ui_file_style (), format, args);
+
   va_end (args);
 }
 
index 6732f04671915bdede5b83de8df580c1e70b5819..0bba1280fcc672ed05121dde597b14409223fb8c 100644 (file)
@@ -53,6 +53,12 @@ enum ui_out_flag
 {
   ui_source_list = (1 << 0),
   fix_multi_location_breakpoint_output = (1 << 1),
+  /* For CLI output, this flag is set if unfiltered output is desired.
+     This should only be used by low-level formatting functions.  */
+  unfiltered_output = (1 << 2),
+  /* This indicates that %pF should be disallowed in a printf format
+     string.  */
+  disallow_ui_out_field = (1 << 3)
 };
 
 DEF_ENUM_FLAGS_TYPE (ui_out_flag, ui_out_flags);
@@ -68,6 +74,87 @@ enum ui_out_type
     ui_out_type_list
   };
 
+/* The possible kinds of fields.  */
+enum class field_kind
+  {
+    SIGNED,
+    STRING,
+  };
+
+/* The base type of all fields that can be emitted using %pF.  */
+
+struct base_field_s
+{
+  const char *name;
+  field_kind kind;
+};
+
+/* A signed integer field, to be passed to %pF in format strings.  */
+
+struct signed_field_s : base_field_s
+{
+  LONGEST val;
+};
+
+/* Construct a temporary signed_field_s on the caller's stack and
+   return a pointer to the constructed object.  We use this because
+   it's not possible to pass a reference via va_args.  */
+
+static inline signed_field_s *
+signed_field (const char *name, LONGEST val,
+             signed_field_s &&tmp = {})
+{
+  tmp.name = name;
+  tmp.kind = field_kind::SIGNED;
+  tmp.val = val;
+  return &tmp;
+}
+
+/* A string field, to be passed to %pF in format strings.  */
+
+struct string_field_s : base_field_s
+{
+  const char *str;
+};
+
+/* Construct a temporary string_field_s on the caller's stack and
+   return a pointer to the constructed object.  We use this because
+   it's not possible to pass a reference via va_args.  */
+
+static inline string_field_s *
+string_field (const char *name, const char *str,
+             string_field_s &&tmp = {})
+{
+  tmp.name = name;
+  tmp.kind = field_kind::STRING;
+  tmp.str = str;
+  return &tmp;
+}
+
+/* A styled string.  */
+
+struct styled_string_s
+{
+  /* The style.  */
+  ui_file_style style;
+
+  /* The string.  */
+  const char *str;
+};
+
+/* Construct a temporary styled_string_s on the caller's stack and
+   return a pointer to the constructed object.  We use this because
+   it's not possible to pass a reference via va_args.  */
+
+static inline styled_string_s *
+styled_string (const ui_file_style &style, const char *str,
+              styled_string_s &&tmp = {})
+{
+  tmp.style = style;
+  tmp.str = str;
+  return &tmp;
+}
+
 class ui_out
 {
  public:
@@ -110,7 +197,55 @@ class ui_out
 
   void spaces (int numspaces);
   void text (const char *string);
+
+  /* Output a printf-style formatted string.  In addition to the usual
+     printf format specs, this supports a few GDB-specific
+     formatters:
+
+     - '%pF' - output a field.
+
+       The argument is a field, wrapped in any of the base_field_s
+       subclasses.  signed_field for integer fields, string_field for
+       string fields.  This is preferred over separate
+       uiout->field_signed(), uiout_>field_string() etc. calls when
+       the formatted message is translatable.  E.g.:
+
+         uiout->message (_("\nWatchpoint %pF deleted because the program has "
+                         "left the block in\n"
+                         "which its expression is valid.\n"),
+                         signed_field ("wpnum", b->number));
+
+     - '%p[' - output the following text in a specified style.
+       '%p]' - output the following text in the default style.
+
+       The argument to '%p[' is a ui_file_style pointer.  The argument
+       to '%p]' must be nullptr.
+
+       This is useful when you want to output some portion of a string
+       literal in some style.  E.g.:
+
+        uiout->message (_(" %p[<repeats %u times>%p]"),
+                        metadata_style.style ().ptr (),
+                        reps, repeats, nullptr);
+
+     - '%ps' - output a styled string.
+
+       The argument is the result of a call to styled_string.  This is
+       useful when you want to output some runtime-generated string in
+       some style.  E.g.:
+
+        uiout->message (_("this is a target address %ps.\n"),
+                        styled_string (address_style.style (),
+                                       paddress (gdbarch, pc)));
+
+     Note that these all "abuse" the %p printf format spec, in order
+     to be compatible with GCC's printf format checking.  This is OK
+     because code in GDB that wants to print a host address should use
+     host_address_to_string instead of %p.  */
   void message (const char *format, ...) ATTRIBUTE_PRINTF (2, 3);
+  void vmessage (const ui_file_style &in_style,
+                const char *format, va_list args) ATTRIBUTE_PRINTF (3, 0);
+
   void wrap_hint (const char *identstring);
 
   void flush ();
@@ -161,8 +296,9 @@ class ui_out
     ATTRIBUTE_PRINTF (6,0) = 0;
   virtual void do_spaces (int numspaces) = 0;
   virtual void do_text (const char *string) = 0;
-  virtual void do_message (const char *format, va_list args)
-    ATTRIBUTE_PRINTF (2,0) = 0;
+  virtual void do_message (const ui_file_style &style,
+                          const char *format, va_list args)
+    ATTRIBUTE_PRINTF (3,0) = 0;
   virtual void do_wrap_hint (const char *identstring) = 0;
   virtual void do_flush () = 0;
   virtual void do_redirect (struct ui_file *outstream) = 0;
@@ -174,6 +310,8 @@ class ui_out
   { return false; }
 
  private:
+  void call_do_message (const ui_file_style &style, const char *format,
+                       ...);
 
   ui_out_flags m_flags;
 
index 24b4b59ed9f55477a5b151c9a88552efe787e200..d2e9c1362631ac872c9b423c0f682a95ff15443f 100644 (file)
@@ -223,6 +223,12 @@ struct ui_file_style
      BUF.  */
   bool parse (const char *buf, size_t *n_read);
 
+  /* We need this because we can't pass a reference via va_args.  */
+  const ui_file_style *ptr () const
+  {
+    return this;
+  }
+
 private:
 
   color m_foreground = NONE;
index 862b2da0f48a795e405120684dacadcaa48f6233..ed83d9670fd1d36c33bb07497fb46e68fea94314 100644 (file)
@@ -27,9 +27,10 @@ namespace format_pieces {
 /* Verify that parsing STR gives pieces equal to EXPECTED_PIECES.  */
 
 static void
-check (const char *str, const std::vector<format_piece> &expected_pieces)
+check (const char *str, const std::vector<format_piece> &expected_pieces,
+       bool gdb_format = false)
 {
-  ::format_pieces pieces (&str);
+  ::format_pieces pieces (&str, gdb_format);
 
   SELF_CHECK ((pieces.end () - pieces.begin ()) == expected_pieces.size ());
   SELF_CHECK (std::equal (pieces.begin (), pieces.end (),
@@ -41,7 +42,7 @@ test_escape_sequences ()
 {
   check ("This is an escape sequence: \\e",
     {
-      format_piece ("This is an escape sequence: \e", literal_piece),
+      format_piece ("This is an escape sequence: \e", literal_piece, 0),
     });
 }
 
@@ -50,21 +51,37 @@ test_format_specifier ()
 {
   /* The format string here ends with a % sequence, to ensure we don't
      see a trailing empty literal piece.  */
-  check ("Hello %d%llx%%d%d", /* ARI: %ll */
+  check ("Hello\\t %d%llx%%d%d", /* ARI: %ll */
     {
-      format_piece ("Hello ", literal_piece),
-      format_piece ("%d", int_arg),
-      format_piece ("%llx", long_long_arg), /* ARI: %ll */
-      format_piece ("%%d", literal_piece),
-      format_piece ("%d", int_arg),
+      format_piece ("Hello\t ", literal_piece, 0),
+      format_piece ("%d", int_arg, 0),
+      format_piece ("%llx", long_long_arg, 0), /* ARI: %ll */
+      format_piece ("%%d", literal_piece, 0),
+      format_piece ("%d", int_arg, 0),
     });
 }
 
+static void
+test_gdb_formats ()
+{
+  check ("Hello\\t \"%p[%pF%ps%*.*d%p]\"",
+    {
+      format_piece ("Hello\\t \"", literal_piece, 0),
+      format_piece ("%p[", ptr_arg, 0),
+      format_piece ("%pF", ptr_arg, 0),
+      format_piece ("%ps", ptr_arg, 0),
+      format_piece ("%*.*d", int_arg, 2),
+      format_piece ("%p]", ptr_arg, 0),
+      format_piece ("\"", literal_piece, 0),
+    }, true);
+}
+
 static void
 run_tests ()
 {
   test_escape_sequences ();
   test_format_specifier ();
+  test_gdb_formats ();
 }
 
 } /* namespace format_pieces */
index b7d380073ff52ee9dcbea3c6e9c131a870ec6182..e685cc2084733b3411489f80d23242f7556d20a4 100644 (file)
 #include "cli/cli-style.h"
 #include "gdbsupport/scope-exit.h"
 #include "gdbarch.h"
+#include "cli-out.h"
 
 void (*deprecated_error_begin_hook) (void);
 
 /* Prototypes for local functions */
 
 static void vfprintf_maybe_filtered (struct ui_file *, const char *,
-                                    va_list, int) ATTRIBUTE_PRINTF (2, 0);
+                                    va_list, bool, bool)
+  ATTRIBUTE_PRINTF (2, 0);
 
 static void fputs_maybe_filtered (const char *, struct ui_file *, int);
 
@@ -1854,6 +1856,24 @@ fputs_styled (const char *linebuffer, const ui_file_style &style,
 
 /* See utils.h.  */
 
+void
+fputs_styled_unfiltered (const char *linebuffer, const ui_file_style &style,
+                        struct ui_file *stream)
+{
+  /* This just makes it so we emit somewhat fewer escape
+     sequences.  */
+  if (style.is_default ())
+    fputs_maybe_filtered (linebuffer, stream, 0);
+  else
+    {
+      set_output_style (stream, style);
+      fputs_maybe_filtered (linebuffer, stream, 0);
+      set_output_style (stream, ui_file_style ());
+    }
+}
+
+/* See utils.h.  */
+
 void
 fputs_highlighted (const char *str, const compiled_regex &highlight,
                   struct ui_file *stream)
@@ -2021,34 +2041,46 @@ puts_debug (char *prefix, char *string, char *suffix)
    We implement three variants, vfprintf (takes a vararg list and stream),
    fprintf (takes a stream to write on), and printf (the usual).
 
-   Note also that a longjmp to top level may occur in this routine
-   (since prompt_for_continue may do so) so this routine should not be
-   called when cleanups are not in place.  */
+   Note also that this may throw a quit (since prompt_for_continue may
+   do so).  */
 
 static void
 vfprintf_maybe_filtered (struct ui_file *stream, const char *format,
-                        va_list args, int filter)
+                        va_list args, bool filter, bool gdbfmt)
 {
-  std::string linebuffer = string_vprintf (format, args);
-  fputs_maybe_filtered (linebuffer.c_str (), stream, filter);
+  if (gdbfmt)
+    {
+      ui_out_flags flags = disallow_ui_out_field;
+      if (!filter)
+       flags |= unfiltered_output;
+      cli_ui_out (stream, flags).vmessage (applied_style, format, args);
+    }
+  else
+    {
+      std::string str = string_vprintf (format, args);
+      fputs_maybe_filtered (str.c_str (), stream, filter);
+    }
 }
 
 
 void
 vfprintf_filtered (struct ui_file *stream, const char *format, va_list args)
 {
-  vfprintf_maybe_filtered (stream, format, args, 1);
+  vfprintf_maybe_filtered (stream, format, args, true, true);
 }
 
 void
 vfprintf_unfiltered (struct ui_file *stream, const char *format, va_list args)
 {
-  std::string linebuffer = string_vprintf (format, args);
   if (debug_timestamp && stream == gdb_stdlog)
     {
       using namespace std::chrono;
       int len, need_nl;
 
+      string_file sfile;
+      cli_ui_out (&sfile, 0).vmessage (ui_file_style (), format, args);
+      std::string linebuffer = std::move (sfile.string ());
+
       steady_clock::time_point now = steady_clock::now ();
       seconds s = duration_cast<seconds> (now.time_since_epoch ());
       microseconds us = duration_cast<microseconds> (now.time_since_epoch () - s);
@@ -2064,13 +2096,13 @@ vfprintf_unfiltered (struct ui_file *stream, const char *format, va_list args)
       fputs_unfiltered (timestamp.c_str (), stream);
     }
   else
-    fputs_unfiltered (linebuffer.c_str (), stream);
+    vfprintf_maybe_filtered (stream, format, args, false, true);
 }
 
 void
 vprintf_filtered (const char *format, va_list args)
 {
-  vfprintf_maybe_filtered (gdb_stdout, format, args, 1);
+  vfprintf_maybe_filtered (gdb_stdout, format, args, true, false);
 }
 
 void
@@ -2130,6 +2162,33 @@ fprintf_styled (struct ui_file *stream, const ui_file_style &style,
   set_output_style (stream, ui_file_style ());
 }
 
+/* See utils.h.  */
+
+void
+vfprintf_styled (struct ui_file *stream, const ui_file_style &style,
+                const char *format, va_list args)
+{
+  set_output_style (stream, style);
+  vfprintf_filtered (stream, format, args);
+  set_output_style (stream, ui_file_style ());
+}
+
+/* See utils.h.  */
+
+void
+vfprintf_styled_no_gdbfmt (struct ui_file *stream, const ui_file_style &style,
+                          bool filter, const char *format, va_list args)
+{
+  std::string str = string_vprintf (format, args);
+  if (!str.empty ())
+    {
+      if (!style.is_default ())
+       set_output_style (stream, style);
+      fputs_maybe_filtered (str.c_str (), stream, filter);
+      if (!style.is_default ())
+       set_output_style (stream, ui_file_style ());
+    }
+}
 
 void
 printf_filtered (const char *format, ...)
index 7df86beec4714edd2ce54be49fdf4796f74df303..76f0da69f712bce7f5559a37c4182d83d5352d8c 100644 (file)
@@ -350,7 +350,10 @@ extern struct ui_file *gdb_stdtargin;
 extern void set_screen_width_and_height (int width, int height);
 
 /* More generic printf like operations.  Filtered versions may return
-   non-locally on error.  */
+   non-locally on error.  As an extension over plain printf, these
+   support some GDB-specific format specifiers.  Particularly useful
+   here are the styling formatters: '%p[', '%p]' and '%ps'.  See
+   ui_out::message for details.  */
 
 extern void fputs_filtered (const char *, struct ui_file *);
 
@@ -430,6 +433,20 @@ extern void fprintf_styled (struct ui_file *stream,
                            ...)
   ATTRIBUTE_PRINTF (3, 4);
 
+extern void vfprintf_styled (struct ui_file *stream,
+                            const ui_file_style &style,
+                            const char *fmt,
+                            va_list args)
+  ATTRIBUTE_PRINTF (3, 0);
+
+/* Like vfprintf_styled, but do not process gdb-specific format
+   specifiers.  */
+extern void vfprintf_styled_no_gdbfmt (struct ui_file *stream,
+                                      const ui_file_style &style,
+                                      bool filter,
+                                      const char *fmt, va_list args)
+  ATTRIBUTE_PRINTF (4, 0);
+
 /* Like fputs_filtered, but styles the output according to STYLE, when
    appropriate.  */
 
@@ -437,6 +454,12 @@ extern void fputs_styled (const char *linebuffer,
                          const ui_file_style &style,
                          struct ui_file *stream);
 
+/* Unfiltered variant of fputs_styled.  */
+
+extern void fputs_styled_unfiltered (const char *linebuffer,
+                                    const ui_file_style &style,
+                                    struct ui_file *stream);
+
 /* Like fputs_styled, but uses highlight_style to highlight the
    parts of STR that match HIGHLIGHT.  */
 
This page took 0.044863 seconds and 4 git commands to generate.