public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH v2 00/18] Refactor character printing
@ 2022-02-17 22:05 Tom Tromey
  2022-02-17 22:05 ` [PATCH v2 01/18] Fix latent quote char bug in generic_printstr Tom Tromey
                   ` (18 more replies)
  0 siblings, 19 replies; 30+ messages in thread
From: Tom Tromey @ 2022-02-17 22:05 UTC (permalink / raw)
  To: gdb-patches

Here's v2 of the series to refactor character printing.
v1 is here:

https://sourceware.org/pipermail/gdb-patches/2022-February/185918.html

I believe this version addresses all the review comments.  The big
changes are that the emission API is changed as Andrew suggested, and
the latent bug fix with escaping is now done only for hex escapes.

Regression tested on x86-64 Fedora 34.

Tom



^ permalink raw reply	[flat|nested] 30+ messages in thread

* [PATCH v2 01/18] Fix latent quote char bug in generic_printstr
  2022-02-17 22:05 [PATCH v2 00/18] Refactor character printing Tom Tromey
@ 2022-02-17 22:05 ` Tom Tromey
  2022-02-17 22:05 ` [PATCH v2 02/18] Boolify need_escape in generic_emit_char Tom Tromey
                   ` (17 subsequent siblings)
  18 siblings, 0 replies; 30+ messages in thread
From: Tom Tromey @ 2022-02-17 22:05 UTC (permalink / raw)
  To: gdb-patches; +Cc: Tom Tromey, Andrew Burgess

generic_printstr prints an empty string like:

      fputs_filtered ("\"\"", stream);

However, this seems wrong to me if the quote character is something
other than double quote.  This patch fixes this latent bug.  Thanks to
Andrew for the test case.

Co-authored-by: Andrew Burgess <aburgess@redhat.com>
---
 gdb/testsuite/gdb.fortran/empty-string.exp | 33 ++++++++++++++++++++++
 gdb/testsuite/gdb.fortran/empty-string.f90 | 30 ++++++++++++++++++++
 gdb/valprint.c                             |  2 +-
 3 files changed, 64 insertions(+), 1 deletion(-)
 create mode 100644 gdb/testsuite/gdb.fortran/empty-string.exp
 create mode 100644 gdb/testsuite/gdb.fortran/empty-string.f90

diff --git a/gdb/testsuite/gdb.fortran/empty-string.exp b/gdb/testsuite/gdb.fortran/empty-string.exp
new file mode 100644
index 00000000000..892f189784a
--- /dev/null
+++ b/gdb/testsuite/gdb.fortran/empty-string.exp
@@ -0,0 +1,33 @@
+# Copyright 2022 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/> .
+
+# Test printing of an empty Fortran string.
+
+if {[skip_fortran_tests]} { return -1 }
+
+standard_testfile ".f90"
+load_lib fortran.exp
+
+if {[prepare_for_testing "failed to prepare" $testfile $srcfile {debug f90}]} {
+    return -1
+}
+
+if ![fortran_runto_main] {
+    return -1
+}
+
+gdb_breakpoint [gdb_get_line_number "Break here"]
+gdb_continue_to_breakpoint "run to test location"
+gdb_test "print string" " = ''"
diff --git a/gdb/testsuite/gdb.fortran/empty-string.f90 b/gdb/testsuite/gdb.fortran/empty-string.f90
new file mode 100644
index 00000000000..574ed7fb7aa
--- /dev/null
+++ b/gdb/testsuite/gdb.fortran/empty-string.f90
@@ -0,0 +1,30 @@
+! Copyright 2022 Free Software Foundation, Inc.
+!
+! This program is free software; you can redistribute it and/or modify
+! it under the terms of the GNU General Public License as published by
+! the Free Software Foundation; either version 3 of the License, or
+! (at your option) any later version.
+!
+! This program is distributed in the hope that it will be useful,
+! but WITHOUT ANY WARRANTY; without even the implied warranty of
+! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+! GNU General Public License for more details.
+!
+! You should have received a copy of the GNU General Public License
+! along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+program empty_string
+  implicit none
+
+  integer :: ret
+
+  ret = string_length('')
+
+contains
+
+  integer(kind=4) function string_length(string)
+    character*(*) :: string
+    string_length = len(string)		! Break here.
+  end function string_length
+
+end program empty_string
diff --git a/gdb/valprint.c b/gdb/valprint.c
index d6ec64845f4..e4d68381189 100644
--- a/gdb/valprint.c
+++ b/gdb/valprint.c
@@ -2632,7 +2632,7 @@ generic_printstr (struct ui_file *stream, struct type *type,
 
   if (length == 0)
     {
-      fputs_filtered ("\"\"", stream);
+      fprintf_filtered (stream, "%c%c", quote_char, quote_char);
       return;
     }
 
-- 
2.31.1


^ permalink raw reply	[flat|nested] 30+ messages in thread

* [PATCH v2 02/18] Boolify need_escape in generic_emit_char
  2022-02-17 22:05 [PATCH v2 00/18] Refactor character printing Tom Tromey
  2022-02-17 22:05 ` [PATCH v2 01/18] Fix latent quote char bug in generic_printstr Tom Tromey
@ 2022-02-17 22:05 ` Tom Tromey
  2022-02-17 22:05 ` [PATCH v2 03/18] Remove c_emit_char Tom Tromey
                   ` (16 subsequent siblings)
  18 siblings, 0 replies; 30+ messages in thread
From: Tom Tromey @ 2022-02-17 22:05 UTC (permalink / raw)
  To: gdb-patches; +Cc: Tom Tromey

This changes 'need_escape' in generic_emit_char to be of type bool,
rather than int.
---
 gdb/valprint.c | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/gdb/valprint.c b/gdb/valprint.c
index e4d68381189..e758c1d1066 100644
--- a/gdb/valprint.c
+++ b/gdb/valprint.c
@@ -2191,11 +2191,11 @@ print_wchar (gdb_wint_t w, const gdb_byte *orig,
 	     int orig_len, int width,
 	     enum bfd_endian byte_order,
 	     struct obstack *output,
-	     int quoter, int *need_escapep)
+	     int quoter, bool *need_escapep)
 {
-  int need_escape = *need_escapep;
+  bool need_escape = *need_escapep;
 
-  *need_escapep = 0;
+  *need_escapep = false;
 
   /* iswprint implementation on Windows returns 1 for tab character.
      In order to avoid different printout on this host, we explicitly
@@ -2265,7 +2265,7 @@ print_wchar (gdb_wint_t w, const gdb_byte *orig,
 		  ++i;
 		}
 
-	      *need_escapep = 1;
+	      *need_escapep = true;
 	    }
 	  break;
 	}
@@ -2283,7 +2283,7 @@ generic_emit_char (int c, struct type *type, struct ui_file *stream,
   enum bfd_endian byte_order
     = type_byte_order (type);
   gdb_byte *c_buf;
-  int need_escape = 0;
+  bool need_escape = false;
 
   c_buf = (gdb_byte *) alloca (TYPE_LENGTH (type));
   pack_long (c_buf, type, c);
@@ -2448,7 +2448,7 @@ print_converted_chars_to_obstack (struct obstack *obstack,
   const converted_character *elem;
   enum {START, SINGLE, REPEAT, INCOMPLETE, FINISH} state, last;
   gdb_wchar_t wide_quote_char = gdb_btowc (quote_char);
-  int need_escape = 0;
+  bool need_escape = false;
 
   /* Set the start state.  */
   idx = 0;
-- 
2.31.1


^ permalink raw reply	[flat|nested] 30+ messages in thread

* [PATCH v2 03/18] Remove c_emit_char
  2022-02-17 22:05 [PATCH v2 00/18] Refactor character printing Tom Tromey
  2022-02-17 22:05 ` [PATCH v2 01/18] Fix latent quote char bug in generic_printstr Tom Tromey
  2022-02-17 22:05 ` [PATCH v2 02/18] Boolify need_escape in generic_emit_char Tom Tromey
@ 2022-02-17 22:05 ` Tom Tromey
  2022-02-17 22:05 ` [PATCH v2 04/18] Remove c_printstr Tom Tromey
                   ` (15 subsequent siblings)
  18 siblings, 0 replies; 30+ messages in thread
From: Tom Tromey @ 2022-02-17 22:05 UTC (permalink / raw)
  To: gdb-patches; +Cc: Tom Tromey

This renames c_emit_char, removing a layer of indirection.
---
 gdb/c-lang.c   | 4 ++--
 gdb/c-lang.h   | 3 ---
 gdb/language.c | 9 ---------
 3 files changed, 2 insertions(+), 14 deletions(-)

diff --git a/gdb/c-lang.c b/gdb/c-lang.c
index 1f7cac7bef1..342109c94ef 100644
--- a/gdb/c-lang.c
+++ b/gdb/c-lang.c
@@ -144,8 +144,8 @@ classify_type (struct type *elttype, struct gdbarch *gdbarch,
    for printing characters and strings is language specific.  */
 
 void
-c_emit_char (int c, struct type *type,
-	     struct ui_file *stream, int quoter)
+language_defn::emitchar (int c, struct type *type,
+			 struct ui_file *stream, int quoter) const
 {
   const char *encoding;
 
diff --git a/gdb/c-lang.h b/gdb/c-lang.h
index 46e562df055..16c9b116393 100644
--- a/gdb/c-lang.h
+++ b/gdb/c-lang.h
@@ -103,9 +103,6 @@ extern void c_printstr (struct ui_file * stream,
 extern void c_language_arch_info (struct gdbarch *gdbarch,
 				  struct language_arch_info *lai);
 
-extern void c_emit_char (int c, struct type *type,
-			 struct ui_file *stream, int quoter);
-
 /* These are in c-typeprint.c: */
 
 extern void c_type_print_base (struct type *, struct ui_file *,
diff --git a/gdb/language.c b/gdb/language.c
index 69c73b0318e..931abcd3076 100644
--- a/gdb/language.c
+++ b/gdb/language.c
@@ -635,15 +635,6 @@ language_defn::value_print_inner
 
 /* See language.h.  */
 
-void
-language_defn::emitchar (int ch, struct type *chtype,
-			 struct ui_file * stream, int quoter) const
-{
-  c_emit_char (ch, chtype, stream, quoter);
-}
-
-/* See language.h.  */
-
 void
 language_defn::printstr (struct ui_file *stream, struct type *elttype,
 			 const gdb_byte *string, unsigned int length,
-- 
2.31.1


^ permalink raw reply	[flat|nested] 30+ messages in thread

* [PATCH v2 04/18] Remove c_printstr
  2022-02-17 22:05 [PATCH v2 00/18] Refactor character printing Tom Tromey
                   ` (2 preceding siblings ...)
  2022-02-17 22:05 ` [PATCH v2 03/18] Remove c_emit_char Tom Tromey
@ 2022-02-17 22:05 ` Tom Tromey
  2022-02-17 22:05 ` [PATCH v2 05/18] Don't use wchar_printable in print_wchar Tom Tromey
                   ` (14 subsequent siblings)
  18 siblings, 0 replies; 30+ messages in thread
From: Tom Tromey @ 2022-02-17 22:05 UTC (permalink / raw)
  To: gdb-patches; +Cc: Tom Tromey

This renames c_printstr, removing a layer of indirection.
---
 gdb/c-lang.c    |  8 ++++----
 gdb/c-lang.h    |  8 --------
 gdb/language.c  | 12 ------------
 gdb/rust-lang.c |  5 +++--
 4 files changed, 7 insertions(+), 26 deletions(-)

diff --git a/gdb/c-lang.c b/gdb/c-lang.c
index 342109c94ef..562736a5ee8 100644
--- a/gdb/c-lang.c
+++ b/gdb/c-lang.c
@@ -190,10 +190,10 @@ language_defn::printchar (int c, struct type *type,
    characters, or if FORCE_ELLIPSES.  */
 
 void
-c_printstr (struct ui_file *stream, struct type *type, 
-	    const gdb_byte *string, unsigned int length, 
-	    const char *user_encoding, int force_ellipses,
-	    const struct value_print_options *options)
+language_defn::printstr (struct ui_file *stream, struct type *type,
+			 const gdb_byte *string, unsigned int length,
+			 const char *user_encoding, int force_ellipses,
+			 const struct value_print_options *options) const
 {
   c_string_type str_type;
   const char *type_encoding;
diff --git a/gdb/c-lang.h b/gdb/c-lang.h
index 16c9b116393..5441bfe10c7 100644
--- a/gdb/c-lang.h
+++ b/gdb/c-lang.h
@@ -92,14 +92,6 @@ extern void c_value_print (struct value *, struct ui_file *,
 
 extern void c_printchar (int, struct type *, struct ui_file *);
 
-extern void c_printstr (struct ui_file * stream,
-			struct type *elttype,
-			const gdb_byte *string,
-			unsigned int length,
-			const char *user_encoding,
-			int force_ellipses,
-			const struct value_print_options *options);
-
 extern void c_language_arch_info (struct gdbarch *gdbarch,
 				  struct language_arch_info *lai);
 
diff --git a/gdb/language.c b/gdb/language.c
index 931abcd3076..20b6d8ccf9b 100644
--- a/gdb/language.c
+++ b/gdb/language.c
@@ -635,18 +635,6 @@ language_defn::value_print_inner
 
 /* See language.h.  */
 
-void
-language_defn::printstr (struct ui_file *stream, struct type *elttype,
-			 const gdb_byte *string, unsigned int length,
-			 const char *encoding, int force_ellipses,
-			 const struct value_print_options *options) const
-{
-  c_printstr (stream, elttype, string, length, encoding, force_ellipses,
-	      options);
-}
-
-/* See language.h.  */
-
 void
 language_defn::print_typedef (struct type *type, struct symbol *new_symbol,
 			      struct ui_file *stream) const
diff --git a/gdb/rust-lang.c b/gdb/rust-lang.c
index 7584d2572fa..d2960d5dc62 100644
--- a/gdb/rust-lang.c
+++ b/gdb/rust-lang.c
@@ -283,8 +283,9 @@ rust_language::printstr (struct ui_file *stream, struct type *type,
 	{
 	  /* This is probably some C string, so let's let C deal with
 	     it.  */
-	  c_printstr (stream, type, string, length, user_encoding,
-		      force_ellipses, options);
+	  language_defn::printstr (stream, type, string, length,
+				   user_encoding, force_ellipses,
+				   options);
 	  return;
 	}
     }
-- 
2.31.1


^ permalink raw reply	[flat|nested] 30+ messages in thread

* [PATCH v2 05/18] Don't use wchar_printable in print_wchar
  2022-02-17 22:05 [PATCH v2 00/18] Refactor character printing Tom Tromey
                   ` (3 preceding siblings ...)
  2022-02-17 22:05 ` [PATCH v2 04/18] Remove c_printstr Tom Tromey
@ 2022-02-17 22:05 ` Tom Tromey
  2022-02-22 15:36   ` Andrew Burgess
  2022-02-17 22:05 ` [PATCH v2 06/18] Fix a latent bug " Tom Tromey
                   ` (13 subsequent siblings)
  18 siblings, 1 reply; 30+ messages in thread
From: Tom Tromey @ 2022-02-17 22:05 UTC (permalink / raw)
  To: gdb-patches; +Cc: Tom Tromey

print_wchar uses wchar_printable, but this isn't needed -- all the
relevant cases are already handled by the 'switch'.  This changes the
code to use gdb_iswprint, and removes a somewhat confusing comment
related to this code.
---
 gdb/valprint.c | 9 +++------
 1 file changed, 3 insertions(+), 6 deletions(-)

diff --git a/gdb/valprint.c b/gdb/valprint.c
index e758c1d1066..17ad46c87b5 100644
--- a/gdb/valprint.c
+++ b/gdb/valprint.c
@@ -2197,9 +2197,6 @@ print_wchar (gdb_wint_t w, const gdb_byte *orig,
 
   *need_escapep = false;
 
-  /* iswprint implementation on Windows returns 1 for tab character.
-     In order to avoid different printout on this host, we explicitly
-     use wchar_printable function.  */
   switch (w)
     {
       case LCST ('\a'):
@@ -2225,9 +2222,9 @@ print_wchar (gdb_wint_t w, const gdb_byte *orig,
 	break;
       default:
 	{
-	  if (wchar_printable (w) && (!need_escape || (!gdb_iswdigit (w)
-						       && w != LCST ('8')
-						       && w != LCST ('9'))))
+	  if (gdb_iswprint (w) && (!need_escape || (!gdb_iswdigit (w)
+						    && w != LCST ('8')
+						    && w != LCST ('9'))))
 	    {
 	      gdb_wchar_t wchar = w;
 
-- 
2.31.1


^ permalink raw reply	[flat|nested] 30+ messages in thread

* [PATCH v2 06/18] Fix a latent bug in print_wchar
  2022-02-17 22:05 [PATCH v2 00/18] Refactor character printing Tom Tromey
                   ` (4 preceding siblings ...)
  2022-02-17 22:05 ` [PATCH v2 05/18] Don't use wchar_printable in print_wchar Tom Tromey
@ 2022-02-17 22:05 ` Tom Tromey
  2022-02-17 22:05 ` [PATCH v2 07/18] Remove language_defn::emitchar Tom Tromey
                   ` (12 subsequent siblings)
  18 siblings, 0 replies; 30+ messages in thread
From: Tom Tromey @ 2022-02-17 22:05 UTC (permalink / raw)
  To: gdb-patches; +Cc: Tom Tromey

print_wchar keeps track of when escape sequences are emitted, to force
an escape sequence if needed by a subsequent character.  For example
for the string concatenation "\0" "1", gdb will print "\000\061" --
because printing "\0001" might be confusing.

However, this code has two errors.  First, this logic is not needed
for octal escapes, because there is a length limit of 3 for octal
escapes, and gdb always prints these with "%.3o".  Second, though,
this *is* needed for hex escapes, because those do not have a length
limit.

This patch fixes these problems and adds the appropriate tests.
---
 gdb/gdb_wchar.h                    |  4 ++--
 gdb/testsuite/gdb.base/charset.exp |  8 ++++++++
 gdb/valprint.c                     | 22 ++++++++++++++--------
 3 files changed, 24 insertions(+), 10 deletions(-)

diff --git a/gdb/gdb_wchar.h b/gdb/gdb_wchar.h
index ba5baf3a2a0..8c6e4fc9fd6 100644
--- a/gdb/gdb_wchar.h
+++ b/gdb/gdb_wchar.h
@@ -66,7 +66,7 @@ typedef wint_t gdb_wint_t;
 
 #define gdb_wcslen wcslen
 #define gdb_iswprint iswprint
-#define gdb_iswdigit iswdigit
+#define gdb_iswxdigit iswxdigit
 #define gdb_btowc btowc
 #define gdb_WEOF WEOF
 
@@ -103,7 +103,7 @@ typedef int gdb_wint_t;
 
 #define gdb_wcslen strlen
 #define gdb_iswprint isprint
-#define gdb_iswdigit isdigit
+#define gdb_iswxdigit isxdigit
 #define gdb_btowc /* empty */
 #define gdb_WEOF EOF
 
diff --git a/gdb/testsuite/gdb.base/charset.exp b/gdb/testsuite/gdb.base/charset.exp
index 5df2ec1a8de..359968df696 100644
--- a/gdb/testsuite/gdb.base/charset.exp
+++ b/gdb/testsuite/gdb.base/charset.exp
@@ -503,6 +503,11 @@ gdb_test "print '\\9'" " = \[0-9\]+ '9'"
 # An octal escape can only be 3 digits.
 gdb_test "print \"\\1011\"" " = \"A1\""
 
+# The final digit does not need to be escaped here.
+foreach val {0 1 2 3 4 5 6 7 8 9 a b c d e f} {
+    gdb_test "print \"\\0\" \"${val}\"" " = \"\\\\000${val}\""
+}
+
 # Tests for wide- or unicode- strings.  L is the prefix letter to use,
 # either "L" (for wide strings), "u" (for UTF-16), or "U" (for UTF-32).
 # NAME is used in the test names and should be related to the prefix
@@ -519,6 +524,9 @@ proc test_wide_or_unicode {L name} {
     gdb_test "print $L\"\" \"abcdef\" \"g\"" \
       "$L\"abcdefg\"" \
       "concatenate three strings with empty $name string"
+    gdb_test "print $L\"\\xffef\" $L\"f\"" \
+	"$L\"\\\\xffef\\\\146\"" \
+	"test multi-char escape sequence case for $name"
 
     gdb_test "print $L'a'" "= \[0-9\]+ $L'a'" \
       "basic $name character"
diff --git a/gdb/valprint.c b/gdb/valprint.c
index 17ad46c87b5..545dfbca73f 100644
--- a/gdb/valprint.c
+++ b/gdb/valprint.c
@@ -2222,9 +2222,7 @@ print_wchar (gdb_wint_t w, const gdb_byte *orig,
 	break;
       default:
 	{
-	  if (gdb_iswprint (w) && (!need_escape || (!gdb_iswdigit (w)
-						    && w != LCST ('8')
-						    && w != LCST ('9'))))
+	  if (gdb_iswprint (w) && !(need_escape && gdb_iswxdigit (w)))
 	    {
 	      gdb_wchar_t wchar = w;
 
@@ -2246,10 +2244,19 @@ print_wchar (gdb_wint_t w, const gdb_byte *orig,
 		  /* If the value fits in 3 octal digits, print it that
 		     way.  Otherwise, print it as a hex escape.  */
 		  if (value <= 0777)
-		    xsnprintf (octal, sizeof (octal), "\\%.3o",
-			       (int) (value & 0777));
+		    {
+		      xsnprintf (octal, sizeof (octal), "\\%.3o",
+				 (int) (value & 0777));
+		      *need_escapep = false;
+		    }
 		  else
-		    xsnprintf (octal, sizeof (octal), "\\x%lx", (long) value);
+		    {
+		      xsnprintf (octal, sizeof (octal), "\\x%lx", (long) value);
+		      /* A hex escape might require the next character
+			 to be escaped, because, unlike with octal,
+			 hex escapes have no length limit.  */
+		      *need_escapep = true;
+		    }
 		  append_string_as_wide (octal, output);
 		}
 	      /* If we somehow have extra bytes, print them now.  */
@@ -2258,11 +2265,10 @@ print_wchar (gdb_wint_t w, const gdb_byte *orig,
 		  char octal[5];
 
 		  xsnprintf (octal, sizeof (octal), "\\%.3o", orig[i] & 0xff);
+		  *need_escapep = false;
 		  append_string_as_wide (octal, output);
 		  ++i;
 		}
-
-	      *need_escapep = true;
 	    }
 	  break;
 	}
-- 
2.31.1


^ permalink raw reply	[flat|nested] 30+ messages in thread

* [PATCH v2 07/18] Remove language_defn::emitchar
  2022-02-17 22:05 [PATCH v2 00/18] Refactor character printing Tom Tromey
                   ` (5 preceding siblings ...)
  2022-02-17 22:05 ` [PATCH v2 06/18] Fix a latent bug " Tom Tromey
@ 2022-02-17 22:05 ` Tom Tromey
  2022-02-17 22:05 ` [PATCH v2 08/18] Add gdb_iswcntrl Tom Tromey
                   ` (11 subsequent siblings)
  18 siblings, 0 replies; 30+ messages in thread
From: Tom Tromey @ 2022-02-17 22:05 UTC (permalink / raw)
  To: gdb-patches; +Cc: Tom Tromey

Nothing outside of the specific language implementations ever calls
language_defn::emitchar.  This patch removes this method and updates
the rest of the code.  In some spots, the method is entirely removed;
in others, just the 'override' is removed.
---
 gdb/ada-lang.c  |  8 --------
 gdb/c-lang.c    | 19 +++----------------
 gdb/f-lang.h    | 12 ++----------
 gdb/language.c  |  9 ---------
 gdb/language.h  |  6 ------
 gdb/m2-lang.h   |  2 +-
 gdb/p-lang.h    |  2 +-
 gdb/rust-lang.c | 10 ++++++----
 gdb/rust-lang.h | 12 +-----------
 9 files changed, 14 insertions(+), 66 deletions(-)

diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c
index d2f620cbb04..e3bd1fa7c17 100644
--- a/gdb/ada-lang.c
+++ b/gdb/ada-lang.c
@@ -13317,14 +13317,6 @@ class ada_language : public language_defn
 
   /* See language.h.  */
 
-  void emitchar (int ch, struct type *chtype,
-		 struct ui_file *stream, int quoter) const override
-  {
-    ada_emit_char (ch, chtype, stream, quoter, 1);
-  }
-
-  /* See language.h.  */
-
   void printchar (int ch, struct type *chtype,
 		  struct ui_file *stream) const override
   {
diff --git a/gdb/c-lang.c b/gdb/c-lang.c
index 562736a5ee8..f7dee8c31d8 100644
--- a/gdb/c-lang.c
+++ b/gdb/c-lang.c
@@ -139,20 +139,6 @@ classify_type (struct type *elttype, struct gdbarch *gdbarch,
   return result;
 }
 
-/* Print the character C on STREAM as part of the contents of a
-   literal string whose delimiter is QUOTER.  Note that that format
-   for printing characters and strings is language specific.  */
-
-void
-language_defn::emitchar (int c, struct type *type,
-			 struct ui_file *stream, int quoter) const
-{
-  const char *encoding;
-
-  classify_type (type, type->arch (), &encoding);
-  generic_emit_char (c, type, stream, quoter, encoding);
-}
-
 /* See language.h.  */
 
 void
@@ -160,8 +146,9 @@ language_defn::printchar (int c, struct type *type,
 			  struct ui_file * stream) const
 {
   c_string_type str_type;
+  const char *encoding;
 
-  str_type = classify_type (type, type->arch (), NULL);
+  str_type = classify_type (type, type->arch (), &encoding);
   switch (str_type)
     {
     case C_CHAR:
@@ -178,7 +165,7 @@ language_defn::printchar (int c, struct type *type,
     }
 
   fputc_filtered ('\'', stream);
-  emitchar (c, type, stream, '\'');
+  generic_emit_char (c, type, stream, '\'', encoding);
   fputc_filtered ('\'', stream);
 }
 
diff --git a/gdb/f-lang.h b/gdb/f-lang.h
index 11debd5569f..388c832dcdb 100644
--- a/gdb/f-lang.h
+++ b/gdb/f-lang.h
@@ -149,20 +149,12 @@ class f_language : public language_defn
 
   /* See language.h.  */
 
-  void emitchar (int ch, struct type *chtype,
-		 struct ui_file *stream, int quoter) const override
-  {
-    const char *encoding = get_encoding (chtype);
-    generic_emit_char (ch, chtype, stream, quoter, encoding);
-  }
-
-  /* See language.h.  */
-
   void printchar (int ch, struct type *chtype,
 		  struct ui_file *stream) const override
   {
     fputs_filtered ("'", stream);
-    emitchar (ch, chtype, stream, '\'');
+    const char *encoding = get_encoding (chtype);
+    generic_emit_char (ch, chtype, stream, '\'', encoding);
     fputs_filtered ("'", stream);
   }
 
diff --git a/gdb/language.c b/gdb/language.c
index 20b6d8ccf9b..dff9f8bdaf9 100644
--- a/gdb/language.c
+++ b/gdb/language.c
@@ -786,15 +786,6 @@ class auto_or_unknown_language : public language_defn
 
   /* See language.h.  */
 
-  void emitchar (int ch, struct type *chtype,
-		 struct ui_file *stream, int quoter) const override
-  {
-    error (_("emit character not implemented for language \"%s\""),
-	   natural_name ());
-  }
-
-  /* See language.h.  */
-
   void printchar (int ch, struct type *chtype,
 		  struct ui_file *stream) const override
   {
diff --git a/gdb/language.h b/gdb/language.h
index f2885000259..2820eca0050 100644
--- a/gdb/language.h
+++ b/gdb/language.h
@@ -524,12 +524,6 @@ struct language_defn
 
   virtual int parser (struct parser_state *ps) const;
 
-  /* Print the character CH (of type CHTYPE) on STREAM as part of the
-     contents of a literal string whose delimiter is QUOTER.  */
-
-  virtual void emitchar (int ch, struct type *chtype,
-			 struct ui_file *stream, int quoter) const;
-
   virtual void printchar (int ch, struct type *chtype,
 			  struct ui_file * stream) const;
 
diff --git a/gdb/m2-lang.h b/gdb/m2-lang.h
index 86a093e5f1b..ee2fc6fb8bc 100644
--- a/gdb/m2-lang.h
+++ b/gdb/m2-lang.h
@@ -93,7 +93,7 @@ class m2_language : public language_defn
   /* See language.h.  */
 
   void emitchar (int ch, struct type *chtype,
-		 struct ui_file *stream, int quoter) const override;
+		 struct ui_file *stream, int quoter) const;
 
   /* See language.h.  */
 
diff --git a/gdb/p-lang.h b/gdb/p-lang.h
index 429ef23aea4..b831bde0af6 100644
--- a/gdb/p-lang.h
+++ b/gdb/p-lang.h
@@ -110,7 +110,7 @@ class pascal_language : public language_defn
   /* See language.h.  */
 
   void emitchar (int ch, struct type *chtype,
-		 struct ui_file *stream, int quoter) const override
+		 struct ui_file *stream, int quoter) const
   {
     int in_quotes = 0;
 
diff --git a/gdb/rust-lang.c b/gdb/rust-lang.c
index d2960d5dc62..a6094cd3b25 100644
--- a/gdb/rust-lang.c
+++ b/gdb/rust-lang.c
@@ -1592,13 +1592,14 @@ rust_language::print_type (struct type *type, const char *varstring,
 /* See language.h.  */
 
 void
-rust_language::emitchar (int ch, struct type *chtype,
-			 struct ui_file *stream, int quoter) const
+rust_language::printchar (int ch, struct type *chtype,
+			  struct ui_file *stream) const
 {
+  fputs_filtered ("'", stream);
   if (!rust_chartype_p (chtype))
-    generic_emit_char (ch, chtype, stream, quoter,
+    generic_emit_char (ch, chtype, stream, '\'',
 		       target_charset (chtype->arch ()));
-  else if (ch == '\\' || ch == quoter)
+  else if (ch == '\\')
     fprintf_filtered (stream, "\\%c", ch);
   else if (ch == '\n')
     fputs_filtered ("\\n", stream);
@@ -1614,6 +1615,7 @@ rust_language::emitchar (int ch, struct type *chtype,
     fprintf_filtered (stream, "\\x%02x", ch);
   else
     fprintf_filtered (stream, "\\u{%06x}", ch);
+  fputs_filtered ("'", stream);
 }
 
 /* See language.h.  */
diff --git a/gdb/rust-lang.h b/gdb/rust-lang.h
index 60a1967ff45..1db83fe75ae 100644
--- a/gdb/rust-lang.h
+++ b/gdb/rust-lang.h
@@ -170,18 +170,8 @@ class rust_language : public language_defn
 
   /* See language.h.  */
 
-  void emitchar (int ch, struct type *chtype,
-		 struct ui_file *stream, int quoter) const override;
-
-  /* See language.h.  */
-
   void printchar (int ch, struct type *chtype,
-		  struct ui_file *stream) const override
-  {
-    fputs_filtered ("'", stream);
-    emitchar (ch, chtype, stream, '\'');
-    fputs_filtered ("'", stream);
-  }
+		  struct ui_file *stream) const override;
 
   /* See language.h.  */
 
-- 
2.31.1


^ permalink raw reply	[flat|nested] 30+ messages in thread

* [PATCH v2 08/18] Add gdb_iswcntrl
  2022-02-17 22:05 [PATCH v2 00/18] Refactor character printing Tom Tromey
                   ` (6 preceding siblings ...)
  2022-02-17 22:05 ` [PATCH v2 07/18] Remove language_defn::emitchar Tom Tromey
@ 2022-02-17 22:05 ` Tom Tromey
  2022-02-17 22:05 ` [PATCH v2 09/18] Include \0 in printable wide characters Tom Tromey
                   ` (10 subsequent siblings)
  18 siblings, 0 replies; 30+ messages in thread
From: Tom Tromey @ 2022-02-17 22:05 UTC (permalink / raw)
  To: gdb-patches; +Cc: Tom Tromey

A future patch will need 'iswcntrl', so introduce the appropriate
wrappers in gdb_wchar.h.
---
 gdb/gdb_wchar.h | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/gdb/gdb_wchar.h b/gdb/gdb_wchar.h
index 8c6e4fc9fd6..47b5329dda9 100644
--- a/gdb/gdb_wchar.h
+++ b/gdb/gdb_wchar.h
@@ -67,6 +67,7 @@ typedef wint_t gdb_wint_t;
 #define gdb_wcslen wcslen
 #define gdb_iswprint iswprint
 #define gdb_iswxdigit iswxdigit
+#define gdb_iswcntrl iswcntrl
 #define gdb_btowc btowc
 #define gdb_WEOF WEOF
 
@@ -104,6 +105,7 @@ typedef int gdb_wint_t;
 #define gdb_wcslen strlen
 #define gdb_iswprint isprint
 #define gdb_iswxdigit isxdigit
+#define gdb_iswcntrl iscntrl
 #define gdb_btowc /* empty */
 #define gdb_WEOF EOF
 
-- 
2.31.1


^ permalink raw reply	[flat|nested] 30+ messages in thread

* [PATCH v2 09/18] Include \0 in printable wide characters
  2022-02-17 22:05 [PATCH v2 00/18] Refactor character printing Tom Tromey
                   ` (7 preceding siblings ...)
  2022-02-17 22:05 ` [PATCH v2 08/18] Add gdb_iswcntrl Tom Tromey
@ 2022-02-17 22:05 ` Tom Tromey
  2022-02-23 13:49   ` Andrew Burgess
  2022-02-17 22:05 ` [PATCH v2 10/18] Use a ui_file in print_wchar Tom Tromey
                   ` (9 subsequent siblings)
  18 siblings, 1 reply; 30+ messages in thread
From: Tom Tromey @ 2022-02-17 22:05 UTC (permalink / raw)
  To: gdb-patches; +Cc: Tom Tromey

print_wchar can already display \0, so include it in the list of
"printable" characters in wchar_printable.  This is useful in a coming
patch to change Rust to use the generic character-printing code.
---
 gdb/valprint.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/gdb/valprint.c b/gdb/valprint.c
index 545dfbca73f..57201164c87 100644
--- a/gdb/valprint.c
+++ b/gdb/valprint.c
@@ -2160,7 +2160,7 @@ wchar_printable (gdb_wchar_t w)
 	  || w == LCST ('\a') || w == LCST ('\b')
 	  || w == LCST ('\f') || w == LCST ('\n')
 	  || w == LCST ('\r') || w == LCST ('\t')
-	  || w == LCST ('\v'));
+	  || w == LCST ('\v') || w == LCST ('\0'));
 }
 
 /* A helper function that converts the contents of STRING to wide
-- 
2.31.1


^ permalink raw reply	[flat|nested] 30+ messages in thread

* [PATCH v2 10/18] Use a ui_file in print_wchar
  2022-02-17 22:05 [PATCH v2 00/18] Refactor character printing Tom Tromey
                   ` (8 preceding siblings ...)
  2022-02-17 22:05 ` [PATCH v2 09/18] Include \0 in printable wide characters Tom Tromey
@ 2022-02-17 22:05 ` Tom Tromey
  2022-02-17 22:05 ` [PATCH v2 11/18] Add an emitter callback to generic_printstr and generic_emit_char Tom Tromey
                   ` (8 subsequent siblings)
  18 siblings, 0 replies; 30+ messages in thread
From: Tom Tromey @ 2022-02-17 22:05 UTC (permalink / raw)
  To: gdb-patches; +Cc: Tom Tromey

This introduces a new ui_file subclass, that turns a host string into
a wide string and appends it to an obstack.  print_wchar is rewritten
to use this new ui_file.  This will be more useful in a later patch.
---
 gdb/valprint.c | 52 ++++++++++++++++++++++++++------------------------
 1 file changed, 27 insertions(+), 25 deletions(-)

diff --git a/gdb/valprint.c b/gdb/valprint.c
index 57201164c87..2d90f61e297 100644
--- a/gdb/valprint.c
+++ b/gdb/valprint.c
@@ -2163,19 +2163,29 @@ wchar_printable (gdb_wchar_t w)
 	  || w == LCST ('\v') || w == LCST ('\0'));
 }
 
-/* A helper function that converts the contents of STRING to wide
-   characters and then appends them to OUTPUT.  */
-
-static void
-append_string_as_wide (const char *string,
-		       struct obstack *output)
-{
-  for (; *string; ++string)
-    {
-      gdb_wchar_t w = gdb_btowc (*string);
-      obstack_grow (output, &w, sizeof (gdb_wchar_t));
-    }
-}
+/* A ui_file that writes wide characters to an obstack.  */
+class obstack_wide_file : public ui_file
+{
+public:
+  explicit obstack_wide_file (struct obstack *output)
+    : m_output (output)
+  {
+  }
+
+  ~obstack_wide_file () = default;
+
+  void write (const char *buf, long length_buf) override
+  {
+    for (long i = 0; i < length_buf; ++i)
+      {
+	gdb_wchar_t w = gdb_btowc (buf[i]);
+	obstack_grow (m_output, &w, sizeof (gdb_wchar_t));
+      }
+  }
+
+private:
+  struct obstack *m_output;
+};
 
 /* Print a wide character W to OUTPUT.  ORIG is a pointer to the
    original (target) bytes representing the character, ORIG_LEN is the
@@ -2233,10 +2243,10 @@ print_wchar (gdb_wint_t w, const gdb_byte *orig,
 	  else
 	    {
 	      int i;
+	      obstack_wide_file file (output);
 
 	      for (i = 0; i + width <= orig_len; i += width)
 		{
-		  char octal[30];
 		  ULONGEST value;
 
 		  value = extract_unsigned_integer (&orig[i], width,
@@ -2245,28 +2255,20 @@ print_wchar (gdb_wint_t w, const gdb_byte *orig,
 		     way.  Otherwise, print it as a hex escape.  */
 		  if (value <= 0777)
 		    {
-		      xsnprintf (octal, sizeof (octal), "\\%.3o",
-				 (int) (value & 0777));
+		      fprintf_filtered (&file, "\\%.3o", (int) (value & 0777));
 		      *need_escapep = false;
 		    }
 		  else
 		    {
-		      xsnprintf (octal, sizeof (octal), "\\x%lx", (long) value);
-		      /* A hex escape might require the next character
-			 to be escaped, because, unlike with octal,
-			 hex escapes have no length limit.  */
+		      fprintf_filtered (&file, "\\x%lx", (long) value);
 		      *need_escapep = true;
 		    }
-		  append_string_as_wide (octal, output);
 		}
 	      /* If we somehow have extra bytes, print them now.  */
 	      while (i < orig_len)
 		{
-		  char octal[5];
-
-		  xsnprintf (octal, sizeof (octal), "\\%.3o", orig[i] & 0xff);
+		  fprintf_filtered (&file, "\\%.3o", orig[i] & 0xff);
 		  *need_escapep = false;
-		  append_string_as_wide (octal, output);
 		  ++i;
 		}
 	    }
-- 
2.31.1


^ permalink raw reply	[flat|nested] 30+ messages in thread

* [PATCH v2 11/18] Add an emitter callback to generic_printstr and generic_emit_char
  2022-02-17 22:05 [PATCH v2 00/18] Refactor character printing Tom Tromey
                   ` (9 preceding siblings ...)
  2022-02-17 22:05 ` [PATCH v2 10/18] Use a ui_file in print_wchar Tom Tromey
@ 2022-02-17 22:05 ` Tom Tromey
  2022-02-17 22:05 ` [PATCH v2 12/18] Add a default encoding to generic_emit_char and generic_printstr Tom Tromey
                   ` (7 subsequent siblings)
  18 siblings, 0 replies; 30+ messages in thread
From: Tom Tromey @ 2022-02-17 22:05 UTC (permalink / raw)
  To: gdb-patches; +Cc: Tom Tromey

This adds an emitter callback to generic_printstr and
generic_emit_char.  print_wchar is renamed, exported, and modified to
be suitable for use as the default emitter.  This will be used to let
languages override the way that escape sequences are emitted.  Nothing
uses this yet, that comes later in the series.
---
 gdb/valprint.c | 134 ++++++++++++++++++++++---------------------------
 gdb/valprint.h |  65 +++++++++++++++++++++++-
 2 files changed, 122 insertions(+), 77 deletions(-)

diff --git a/gdb/valprint.c b/gdb/valprint.c
index 2d90f61e297..ecb9b3c9871 100644
--- a/gdb/valprint.c
+++ b/gdb/valprint.c
@@ -2163,45 +2163,16 @@ wchar_printable (gdb_wchar_t w)
 	  || w == LCST ('\v') || w == LCST ('\0'));
 }
 
-/* A ui_file that writes wide characters to an obstack.  */
-class obstack_wide_file : public ui_file
-{
-public:
-  explicit obstack_wide_file (struct obstack *output)
-    : m_output (output)
-  {
-  }
-
-  ~obstack_wide_file () = default;
-
-  void write (const char *buf, long length_buf) override
-  {
-    for (long i = 0; i < length_buf; ++i)
-      {
-	gdb_wchar_t w = gdb_btowc (buf[i]);
-	obstack_grow (m_output, &w, sizeof (gdb_wchar_t));
-      }
-  }
-
-private:
-  struct obstack *m_output;
-};
-
-/* Print a wide character W to OUTPUT.  ORIG is a pointer to the
-   original (target) bytes representing the character, ORIG_LEN is the
-   number of valid bytes.  WIDTH is the number of bytes in a base
-   characters of the type.  OUTPUT is an obstack to which wide
-   characters are emitted.  QUOTER is a (narrow) character indicating
-   the style of quotes surrounding the character to be printed.
-   NEED_ESCAPE is an in/out flag which is used to track numeric
-   escapes across calls.  */
+/* See valprint.h.  */
 
-static void
-print_wchar (gdb_wint_t w, const gdb_byte *orig,
-	     int orig_len, int width,
-	     enum bfd_endian byte_order,
-	     struct obstack *output,
-	     int quoter, bool *need_escapep)
+void
+default_emit_wchar (obstack_wide_file *stream,
+		    gdb_wint_t w,
+		    gdb::array_view<const gdb_byte> orig,
+		    int width,
+		    enum bfd_endian byte_order,
+		    int quoter,
+		    bool *need_escapep)
 {
   bool need_escape = *need_escapep;
 
@@ -2210,64 +2181,61 @@ print_wchar (gdb_wint_t w, const gdb_byte *orig,
   switch (w)
     {
       case LCST ('\a'):
-	obstack_grow_wstr (output, LCST ("\\a"));
+	fputs_filtered ("\\a", stream);
 	break;
       case LCST ('\b'):
-	obstack_grow_wstr (output, LCST ("\\b"));
+	fputs_filtered ("\\b", stream);
 	break;
       case LCST ('\f'):
-	obstack_grow_wstr (output, LCST ("\\f"));
+	fputs_filtered ("\\f", stream);
 	break;
       case LCST ('\n'):
-	obstack_grow_wstr (output, LCST ("\\n"));
+	fputs_filtered ("\\n", stream);
 	break;
       case LCST ('\r'):
-	obstack_grow_wstr (output, LCST ("\\r"));
+	fputs_filtered ("\\r", stream);
 	break;
       case LCST ('\t'):
-	obstack_grow_wstr (output, LCST ("\\t"));
+	fputs_filtered ("\\t", stream);
 	break;
       case LCST ('\v'):
-	obstack_grow_wstr (output, LCST ("\\v"));
+	fputs_filtered ("\\v", stream);
 	break;
       default:
 	{
 	  if (gdb_iswprint (w) && !(need_escape && gdb_iswxdigit (w)))
 	    {
-	      gdb_wchar_t wchar = w;
-
 	      if (w == gdb_btowc (quoter) || w == LCST ('\\'))
-		obstack_grow_wstr (output, LCST ("\\"));
-	      obstack_grow (output, &wchar, sizeof (gdb_wchar_t));
+		fputs_filtered ("\\", stream);
+	      stream->write_wide_char (w);
 	    }
 	  else
 	    {
 	      int i;
-	      obstack_wide_file file (output);
 
-	      for (i = 0; i + width <= orig_len; i += width)
+	      for (i = 0; i + width <= orig.size (); i += width)
 		{
 		  ULONGEST value;
 
 		  value = extract_unsigned_integer (&orig[i], width,
-						  byte_order);
+						    byte_order);
 		  /* If the value fits in 3 octal digits, print it that
 		     way.  Otherwise, print it as a hex escape.  */
 		  if (value <= 0777)
 		    {
-		      fprintf_filtered (&file, "\\%.3o", (int) (value & 0777));
+		      fprintf_filtered (stream, "\\%.3o", (int) (value & 0777));
 		      *need_escapep = false;
 		    }
 		  else
 		    {
-		      fprintf_filtered (&file, "\\x%lx", (long) value);
+		      fprintf_filtered (stream, "\\x%lx", (long) value);
 		      *need_escapep = true;
 		    }
 		}
 	      /* If we somehow have extra bytes, print them now.  */
-	      while (i < orig_len)
+	      while (i < orig.size ())
 		{
-		  fprintf_filtered (&file, "\\%.3o", orig[i] & 0xff);
+		  fprintf_filtered (stream, "\\%.3o", orig[i] & 0xff);
 		  *need_escapep = false;
 		  ++i;
 		}
@@ -2283,7 +2251,8 @@ print_wchar (gdb_wint_t w, const gdb_byte *orig,
 
 void
 generic_emit_char (int c, struct type *type, struct ui_file *stream,
-		   int quoter, const char *encoding)
+		   int quoter, const char *encoding,
+		   emit_char_ftype emitter)
 {
   enum bfd_endian byte_order
     = type_byte_order (type);
@@ -2297,6 +2266,7 @@ generic_emit_char (int c, struct type *type, struct ui_file *stream,
 
   /* This holds the printable form of the wchar_t data.  */
   auto_obstack wchar_buf;
+  obstack_wide_file wchar_stream (&wchar_buf);
 
   while (1)
     {
@@ -2330,16 +2300,17 @@ generic_emit_char (int c, struct type *type, struct ui_file *stream,
 	  if (!print_escape)
 	    {
 	      for (i = 0; i < num_chars; ++i)
-		print_wchar (chars[i], buf, buflen,
-			     TYPE_LENGTH (type), byte_order,
-			     &wchar_buf, quoter, &need_escape);
+		emitter (&wchar_stream, chars[i],
+			 gdb::make_array_view (buf, buflen),
+			 TYPE_LENGTH (type), byte_order,
+			 quoter, &need_escape);
 	    }
 	}
 
       /* This handles the NUM_CHARS == 0 case as well.  */
       if (print_escape)
-	print_wchar (gdb_WEOF, buf, buflen, TYPE_LENGTH (type),
-		     byte_order, &wchar_buf, quoter, &need_escape);
+	emitter (&wchar_stream, gdb_WEOF, gdb::make_array_view (buf, buflen),
+		 TYPE_LENGTH (type), byte_order, quoter, &need_escape);
     }
 
   /* The output in the host encoding.  */
@@ -2447,7 +2418,8 @@ print_converted_chars_to_obstack (struct obstack *obstack,
 				  const std::vector<converted_character> &chars,
 				  int quote_char, int width,
 				  enum bfd_endian byte_order,
-				  const struct value_print_options *options)
+				  const struct value_print_options *options,
+				  emit_char_ftype emitter)
 {
   unsigned int idx;
   const converted_character *elem;
@@ -2483,15 +2455,19 @@ print_converted_chars_to_obstack (struct obstack *obstack,
 		  obstack_grow_wstr (obstack, LCST (", "));
 		obstack_grow (obstack, &wide_quote_char, sizeof (gdb_wchar_t));
 	      }
+
+	    obstack_wide_file wchar_stream (obstack);
 	    /* Output the character.  */
 	    for (j = 0; j < elem->repeat_count; ++j)
 	      {
 		if (elem->result == wchar_iterate_ok)
-		  print_wchar (elem->chars[0], elem->buf, elem->buflen, width,
-			       byte_order, obstack, quote_char, &need_escape);
+		  emitter (&wchar_stream, elem->chars[0],
+			   gdb::make_array_view (elem->buf, elem->buflen),
+			   width, byte_order, quote_char, &need_escape);
 		else
-		  print_wchar (gdb_WEOF, elem->buf, elem->buflen, width,
-			       byte_order, obstack, quote_char, &need_escape);
+		  emitter (&wchar_stream, gdb_WEOF,
+			   gdb::make_array_view (elem->buf, elem->buflen),
+			   width, byte_order, quote_char, &need_escape);
 	      }
 	  }
 	  break;
@@ -2514,12 +2490,15 @@ print_converted_chars_to_obstack (struct obstack *obstack,
 
 	    /* Output the character and repeat string.  */
 	    obstack_grow_wstr (obstack, LCST ("'"));
+	    obstack_wide_file wchar_stream (obstack);
 	    if (elem->result == wchar_iterate_ok)
-	      print_wchar (elem->chars[0], elem->buf, elem->buflen, width,
-			   byte_order, obstack, quote_char, &need_escape);
+	      emitter (&wchar_stream, elem->chars[0],
+		       gdb::make_array_view (elem->buf, elem->buflen),
+		       width, byte_order, quote_char, &need_escape);
 	    else
-	      print_wchar (gdb_WEOF, elem->buf, elem->buflen, width,
-			   byte_order, obstack, quote_char, &need_escape);
+	      emitter (&wchar_stream, gdb_WEOF,
+		       gdb::make_array_view (elem->buf, elem->buflen),
+		       width, byte_order, quote_char, &need_escape);
 	    obstack_grow_wstr (obstack, LCST ("'"));
 	    std::string s = string_printf (_(" <repeats %u times>"),
 					   elem->repeat_count);
@@ -2544,8 +2523,12 @@ print_converted_chars_to_obstack (struct obstack *obstack,
 
 	  /* Output the incomplete sequence string.  */
 	  obstack_grow_wstr (obstack, LCST ("<incomplete sequence "));
-	  print_wchar (gdb_WEOF, elem->buf, elem->buflen, width, byte_order,
-		       obstack, 0, &need_escape);
+	  {
+	    obstack_wide_file wchar_stream (obstack);
+	    emitter (&wchar_stream, gdb_WEOF,
+		     gdb::make_array_view (elem->buf, elem->buflen),
+		     width, byte_order, 0, &need_escape);
+	  }
 	  obstack_grow_wstr (obstack, LCST (">"));
 
 	  /* We do not attempt to output anything after this.  */
@@ -2604,7 +2587,8 @@ generic_printstr (struct ui_file *stream, struct type *type,
 		  const gdb_byte *string, unsigned int length, 
 		  const char *encoding, int force_ellipses,
 		  int quote_char, int c_style_terminator,
-		  const struct value_print_options *options)
+		  const struct value_print_options *options,
+		  emit_char_ftype emitter)
 {
   enum bfd_endian byte_order = type_byte_order (type);
   unsigned int i;
@@ -2680,7 +2664,7 @@ generic_printstr (struct ui_file *stream, struct type *type,
 
   /* Print the output string to the obstack.  */
   print_converted_chars_to_obstack (&wchar_buf, converted_chars, quote_char,
-				    width, byte_order, options);
+				    width, byte_order, options, emitter);
 
   if (force_ellipses || !finished)
     obstack_grow_wstr (&wchar_buf, LCST ("..."));
diff --git a/gdb/valprint.h b/gdb/valprint.h
index 0586836f9e6..0dbdef06c4c 100644
--- a/gdb/valprint.h
+++ b/gdb/valprint.h
@@ -233,14 +233,75 @@ extern void generic_value_print (struct value *val, struct ui_file *stream,
 				 const struct value_print_options *options,
 				 const struct generic_val_print_decorations *d);
 
+/* A ui_file that writes wide characters to an obstack.  */
+class obstack_wide_file : public ui_file
+{
+public:
+  explicit obstack_wide_file (struct obstack *output)
+    : m_output (output)
+  {
+  }
+
+  ~obstack_wide_file () = default;
+
+  void write (const char *buf, long length_buf) override
+  {
+    for (long i = 0; i < length_buf; ++i)
+      {
+	gdb_wchar_t w = gdb_btowc (buf[i]);
+	obstack_grow (m_output, &w, sizeof (gdb_wchar_t));
+      }
+  }
+
+  void write_wide_char (gdb_wchar_t w)
+  {
+    obstack_grow (m_output, &w, sizeof (gdb_wchar_t));
+  }
+
+private:
+  struct obstack *m_output;
+};
+
+/* A callback that can be used to print a representation of a wide
+   character to a stream.
+   STREAM is the stream to write to.
+   W is the character.  It might be gdb_WEOF, meaning an unconvertible
+   sequence.
+   ORIG is the original (target) bytes corresponding to W.
+   WIDTH is the width of a base character in the encoding.
+   BYTE_ORDER is the character type's byte order.
+   QUOTER is the quote character used -- this is a host character.
+   NEED_ESCAPEP is used to track whether emitting this character may
+   require a subsequent character to be escaped.  */
+typedef gdb::function_view<void (obstack_wide_file *stream,
+				 gdb_wint_t w,
+				 gdb::array_view<const gdb_byte> orig,
+				 int width,
+				 enum bfd_endian byte_order,
+				 int quoter,
+				 bool *need_escapep)> emit_char_ftype;
+
+/* A function suitable for use as a character emitter, that emits
+   characters in the C style.  */
+
+extern void default_emit_wchar (obstack_wide_file *stream,
+				gdb_wint_t w,
+				gdb::array_view<const gdb_byte> orig,
+				int width,
+				enum bfd_endian byte_order,
+				int quoter,
+				bool *need_escapep);
+
 extern void generic_emit_char (int c, struct type *type, struct ui_file *stream,
-			       int quoter, const char *encoding);
+			       int quoter, const char *encoding,
+			       emit_char_ftype emitter = default_emit_wchar);
 
 extern void generic_printstr (struct ui_file *stream, struct type *type, 
 			      const gdb_byte *string, unsigned int length, 
 			      const char *encoding, int force_ellipses,
 			      int quote_char, int c_style_terminator,
-			      const struct value_print_options *options);
+			      const struct value_print_options *options,
+			      emit_char_ftype emitter = default_emit_wchar);
 
 /* Run the "output" command.  ARGS and FROM_TTY are the usual
    arguments passed to all command implementations, except ARGS is
-- 
2.31.1


^ permalink raw reply	[flat|nested] 30+ messages in thread

* [PATCH v2 12/18] Add a default encoding to generic_emit_char and generic_printstr
  2022-02-17 22:05 [PATCH v2 00/18] Refactor character printing Tom Tromey
                   ` (10 preceding siblings ...)
  2022-02-17 22:05 ` [PATCH v2 11/18] Add an emitter callback to generic_printstr and generic_emit_char Tom Tromey
@ 2022-02-17 22:05 ` Tom Tromey
  2022-02-17 22:05 ` [PATCH v2 13/18] Change generic_emit_char to print the quotes Tom Tromey
                   ` (6 subsequent siblings)
  18 siblings, 0 replies; 30+ messages in thread
From: Tom Tromey @ 2022-02-17 22:05 UTC (permalink / raw)
  To: gdb-patches; +Cc: Tom Tromey

This adds a default encoding to generic_emit_char and
generic_printstr.  The default is pretty basic: use the target charset
for single-byte characters, use the wide charset for wchar_t, and
assume UTF-16/32 for the appropriately-sized other characters.
Languages for which these do not hold can be modified to do something
else if need be.
---
 gdb/valprint.c | 35 +++++++++++++++++++++++++++++++++++
 1 file changed, 35 insertions(+)

diff --git a/gdb/valprint.c b/gdb/valprint.c
index ecb9b3c9871..39c75e82a71 100644
--- a/gdb/valprint.c
+++ b/gdb/valprint.c
@@ -2245,6 +2245,37 @@ default_emit_wchar (obstack_wide_file *stream,
     }
 }
 
+/* Helper function to get the default encoding, given a type.  */
+static const char *
+get_default_encoding (struct type *chtype)
+{
+  const char *encoding;
+  if (TYPE_LENGTH (chtype) == 1)
+    encoding = target_charset (chtype->arch ());
+  else if (streq (chtype->name (), "wchar_t"))
+    encoding = target_wide_charset (chtype->arch ());
+  else if (TYPE_LENGTH (chtype) == 2)
+    {
+      if (type_byte_order (chtype) == BFD_ENDIAN_BIG)
+	encoding = "UTF-16BE";
+      else
+	encoding = "UTF-16LE";
+    }
+  else if (TYPE_LENGTH (chtype) == 4)
+    {
+      if (type_byte_order (chtype) == BFD_ENDIAN_BIG)
+	encoding = "UTF-32BE";
+      else
+	encoding = "UTF-32LE";
+    }
+  else
+    {
+      /* No idea.  */
+      encoding = target_charset (chtype->arch ());
+    }
+  return encoding;
+}
+
 /* Print the character C on STREAM as part of the contents of a
    literal string whose delimiter is QUOTER.  ENCODING names the
    encoding of C.  */
@@ -2254,6 +2285,8 @@ generic_emit_char (int c, struct type *type, struct ui_file *stream,
 		   int quoter, const char *encoding,
 		   emit_char_ftype emitter)
 {
+  if (encoding == nullptr)
+    encoding = get_default_encoding (type);
   enum bfd_endian byte_order
     = type_byte_order (type);
   gdb_byte *c_buf;
@@ -2590,6 +2623,8 @@ generic_printstr (struct ui_file *stream, struct type *type,
 		  const struct value_print_options *options,
 		  emit_char_ftype emitter)
 {
+  if (encoding == nullptr)
+    encoding = get_default_encoding (type);
   enum bfd_endian byte_order = type_byte_order (type);
   unsigned int i;
   int width = TYPE_LENGTH (type);
-- 
2.31.1


^ permalink raw reply	[flat|nested] 30+ messages in thread

* [PATCH v2 13/18] Change generic_emit_char to print the quotes
  2022-02-17 22:05 [PATCH v2 00/18] Refactor character printing Tom Tromey
                   ` (11 preceding siblings ...)
  2022-02-17 22:05 ` [PATCH v2 12/18] Add a default encoding to generic_emit_char and generic_printstr Tom Tromey
@ 2022-02-17 22:05 ` Tom Tromey
  2022-02-17 22:05 ` [PATCH v2 14/18] Use generic_emit_char in Rust Tom Tromey
                   ` (5 subsequent siblings)
  18 siblings, 0 replies; 30+ messages in thread
From: Tom Tromey @ 2022-02-17 22:05 UTC (permalink / raw)
  To: gdb-patches; +Cc: Tom Tromey

All callers of generic_emit_char print the quotes around the
character, then pass the quote character to the function.  It seemed
better to just have generic_emit_char print the quotes itself.
---
 gdb/c-lang.c   | 2 --
 gdb/f-lang.h   | 2 --
 gdb/valprint.c | 2 ++
 3 files changed, 2 insertions(+), 4 deletions(-)

diff --git a/gdb/c-lang.c b/gdb/c-lang.c
index f7dee8c31d8..b8097eb8c07 100644
--- a/gdb/c-lang.c
+++ b/gdb/c-lang.c
@@ -164,9 +164,7 @@ language_defn::printchar (int c, struct type *type,
       break;
     }
 
-  fputc_filtered ('\'', stream);
   generic_emit_char (c, type, stream, '\'', encoding);
-  fputc_filtered ('\'', stream);
 }
 
 /* Print the character string STRING, printing at most LENGTH
diff --git a/gdb/f-lang.h b/gdb/f-lang.h
index 388c832dcdb..fc2cdc1786b 100644
--- a/gdb/f-lang.h
+++ b/gdb/f-lang.h
@@ -152,10 +152,8 @@ class f_language : public language_defn
   void printchar (int ch, struct type *chtype,
 		  struct ui_file *stream) const override
   {
-    fputs_filtered ("'", stream);
     const char *encoding = get_encoding (chtype);
     generic_emit_char (ch, chtype, stream, '\'', encoding);
-    fputs_filtered ("'", stream);
   }
 
   /* See language.h.  */
diff --git a/gdb/valprint.c b/gdb/valprint.c
index 39c75e82a71..f873377fc8c 100644
--- a/gdb/valprint.c
+++ b/gdb/valprint.c
@@ -2295,6 +2295,7 @@ generic_emit_char (int c, struct type *type, struct ui_file *stream,
   c_buf = (gdb_byte *) alloca (TYPE_LENGTH (type));
   pack_long (c_buf, type, c);
 
+  fputc_filtered (quoter, stream);
   wchar_iterator iter (c_buf, TYPE_LENGTH (type), encoding, TYPE_LENGTH (type));
 
   /* This holds the printable form of the wchar_t data.  */
@@ -2356,6 +2357,7 @@ generic_emit_char (int c, struct type *type, struct ui_file *stream,
   obstack_1grow (&output, '\0');
 
   fputs_filtered ((const char *) obstack_base (&output), stream);
+  fputc_filtered (quoter, stream);
 }
 
 /* Return the repeat count of the next character/byte in ITER,
-- 
2.31.1


^ permalink raw reply	[flat|nested] 30+ messages in thread

* [PATCH v2 14/18] Use generic_emit_char in Rust
  2022-02-17 22:05 [PATCH v2 00/18] Refactor character printing Tom Tromey
                   ` (12 preceding siblings ...)
  2022-02-17 22:05 ` [PATCH v2 13/18] Change generic_emit_char to print the quotes Tom Tromey
@ 2022-02-17 22:05 ` Tom Tromey
  2022-02-17 22:05 ` [PATCH v2 15/18] Use generic_emit_char in Ada Tom Tromey
                   ` (4 subsequent siblings)
  18 siblings, 0 replies; 30+ messages in thread
From: Tom Tromey @ 2022-02-17 22:05 UTC (permalink / raw)
  To: gdb-patches; +Cc: Tom Tromey

This changes the Rust code to use generic_emit_char, passing in a
function to handle Rust escape sequences correctly.  This is
PR rust/20164.

Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=20164
---
 gdb/rust-lang.c                    | 80 ++++++++++++++++++------------
 gdb/testsuite/gdb.rust/expr.exp    |  6 +--
 gdb/testsuite/gdb.rust/unicode.exp |  3 +-
 3 files changed, 50 insertions(+), 39 deletions(-)

diff --git a/gdb/rust-lang.c b/gdb/rust-lang.c
index a6094cd3b25..0699e3a0468 100644
--- a/gdb/rust-lang.c
+++ b/gdb/rust-lang.c
@@ -218,16 +218,6 @@ rust_u8_type_p (struct type *type)
 	  && TYPE_LENGTH (type) == 1);
 }
 
-/* Return true if TYPE is a Rust character type.  */
-
-static bool
-rust_chartype_p (struct type *type)
-{
-  return (type->code () == TYPE_CODE_CHAR
-	  && TYPE_LENGTH (type) == 4
-	  && type->is_unsigned ());
-}
-
 /* If VALUE represents a trait object pointer, return the underlying
    pointer with the correct (i.e., runtime) type.  Otherwise, return
    NULL.  */
@@ -263,6 +253,51 @@ rust_get_trait_object_pointer (struct value *value)
 
 \f
 
+/* A callback function for generic_emit_char and generic_printstr that
+   escapes characters Rust-style.  */
+static void
+rust_emit_char (obstack_wide_file *stream,
+		gdb_wint_t w,
+		gdb::array_view<const gdb_byte> orig,
+		int width,
+		enum bfd_endian byte_order,
+		int quoter,
+		bool *need_escapep)
+{
+  if (gdb_iswprint (w) && !gdb_iswcntrl (w))
+    default_emit_wchar (stream, w, orig, width, byte_order, quoter,
+			need_escapep);
+  else if (w == LCST ('\n'))
+    fputs_filtered ("\\n", stream);
+  else if (w == LCST ('\r'))
+    fputs_filtered ("\\r", stream);
+  else if (w == LCST ('\t'))
+    fputs_filtered ("\\t", stream);
+  else if (w == LCST ('\0'))
+    fputs_filtered ("\\0", stream);
+  else
+    {
+      int i;
+
+      for (i = 0; i + width <= orig.size (); i += width)
+	{
+	  ULONGEST value = extract_unsigned_integer (&orig[i], width,
+						     byte_order);
+	  if (value <= 255)
+	    fprintf_filtered (stream, "\\x%02x", (int) value);
+	  else
+	    fprintf_filtered (stream, "\\u{%06lx}", (unsigned long) value);
+	}
+
+      /* If we somehow have extra bytes, print them now.  */
+      while (i < orig.size ())
+	{
+	  fprintf_filtered (stream, "\\x%02x", orig[i] & 0xff);
+	  ++i;
+	}
+    }
+}
+
 /* See language.h.  */
 
 void
@@ -290,9 +325,8 @@ rust_language::printstr (struct ui_file *stream, struct type *type,
 	}
     }
 
-  /* This is not ideal as it doesn't use our character printer.  */
   generic_printstr (stream, type, string, length, encoding, force_ellipses,
-		    '"', 0, options);
+		    '"', 0, options, rust_emit_char);
 }
 
 \f
@@ -1595,27 +1629,7 @@ void
 rust_language::printchar (int ch, struct type *chtype,
 			  struct ui_file *stream) const
 {
-  fputs_filtered ("'", stream);
-  if (!rust_chartype_p (chtype))
-    generic_emit_char (ch, chtype, stream, '\'',
-		       target_charset (chtype->arch ()));
-  else if (ch == '\\')
-    fprintf_filtered (stream, "\\%c", ch);
-  else if (ch == '\n')
-    fputs_filtered ("\\n", stream);
-  else if (ch == '\r')
-    fputs_filtered ("\\r", stream);
-  else if (ch == '\t')
-    fputs_filtered ("\\t", stream);
-  else if (ch == '\0')
-    fputs_filtered ("\\0", stream);
-  else if (ch >= 32 && ch <= 127 && isprint (ch))
-    fputc_filtered (ch, stream);
-  else if (ch <= 255)
-    fprintf_filtered (stream, "\\x%02x", ch);
-  else
-    fprintf_filtered (stream, "\\u{%06x}", ch);
-  fputs_filtered ("'", stream);
+  generic_emit_char (ch, chtype, stream, '\'', nullptr, rust_emit_char);
 }
 
 /* See language.h.  */
diff --git a/gdb/testsuite/gdb.rust/expr.exp b/gdb/testsuite/gdb.rust/expr.exp
index 0c445897338..34eba4bf997 100644
--- a/gdb/testsuite/gdb.rust/expr.exp
+++ b/gdb/testsuite/gdb.rust/expr.exp
@@ -115,10 +115,8 @@ gdb_test "print \[1,2 3" "',' or ']' expected"
 gdb_test "print \[1 2" "',', ';', or ']' expected"
 
 gdb_test "print b\"hi rust\"" " = b\"hi rust\""
-# This isn't rusty syntax yet, but that's another bug -- this is just
-# testing that byte escapes work properly.
-gdb_test "print b\"\\xddhi bob\"" " = b\"\\\\335hi bob\""
-gdb_test "print b\"has\\0nul\"" " = b\"has\\\\000nul\""
+gdb_test "print b\"\\xddhi bob\"" " = b\"\\\\xddhi bob\""
+gdb_test "print b\"has\\0nul\"" " = b\"has\\\\0nul\""
 
 gdb_test "print br##\"hi\"##" " = b\"hi\""
 gdb_test "print br##\"hi" "Unexpected EOF in string"
diff --git a/gdb/testsuite/gdb.rust/unicode.exp b/gdb/testsuite/gdb.rust/unicode.exp
index 9de0a0e724f..8378195bf5d 100644
--- a/gdb/testsuite/gdb.rust/unicode.exp
+++ b/gdb/testsuite/gdb.rust/unicode.exp
@@ -43,8 +43,7 @@ if {![runto ${srcfile}:$line]} {
 
 gdb_test "print 𝕯" " = 98" "print D"
 gdb_test "print \"𝕯\"" " = \"𝕯\"" "print D in string"
-# This output is maybe not ideal, but it also isn't incorrect.
-gdb_test "print '𝕯'" " = 120175 '\\\\u\\\{01d56f\\\}'" \
+gdb_test "print '𝕯'" " = 120175 '𝕯'" \
     "print D as char"
 gdb_test "print cç" " = 97" "print cc"
 
-- 
2.31.1


^ permalink raw reply	[flat|nested] 30+ messages in thread

* [PATCH v2 15/18] Use generic_emit_char in Ada
  2022-02-17 22:05 [PATCH v2 00/18] Refactor character printing Tom Tromey
                   ` (13 preceding siblings ...)
  2022-02-17 22:05 ` [PATCH v2 14/18] Use generic_emit_char in Rust Tom Tromey
@ 2022-02-17 22:05 ` Tom Tromey
  2022-02-17 22:05 ` [PATCH v2 16/18] Use generic_emit_char in Modula-2 Tom Tromey
                   ` (3 subsequent siblings)
  18 siblings, 0 replies; 30+ messages in thread
From: Tom Tromey @ 2022-02-17 22:05 UTC (permalink / raw)
  To: gdb-patches; +Cc: Tom Tromey

This changes the Ada code to use generic_emit_char and
generic_printstr.  This simplifies gdb somewhat.
---
 gdb/ada-lang.c     |   7 +-
 gdb/ada-lang.h     |  14 ++--
 gdb/ada-valprint.c | 159 ++++++++++-----------------------------------
 3 files changed, 44 insertions(+), 136 deletions(-)

diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c
index e3bd1fa7c17..35474909f20 100644
--- a/gdb/ada-lang.c
+++ b/gdb/ada-lang.c
@@ -59,6 +59,7 @@
 #include "gdbsupport/byte-vector.h"
 #include <algorithm>
 #include "ada-exp.h"
+#include "charset.h"
 
 /* Define whether or not the C operator '/' truncates towards zero for
    differently signed operands (truncation direction is undefined in C).
@@ -13320,7 +13321,7 @@ class ada_language : public language_defn
   void printchar (int ch, struct type *chtype,
 		  struct ui_file *stream) const override
   {
-    ada_printchar (ch, chtype, stream);
+    generic_emit_char (ch, chtype, stream, '\'', nullptr, ada_emit_char);
   }
 
   /* See language.h.  */
@@ -13330,8 +13331,8 @@ class ada_language : public language_defn
 		 const char *encoding, int force_ellipses,
 		 const struct value_print_options *options) const override
   {
-    ada_printstr (stream, elttype, string, length, encoding,
-		  force_ellipses, options);
+    generic_printstr (stream, elttype, string, length, encoding,
+		      force_ellipses, '"', 0, options, ada_emit_char);
   }
 
   /* See language.h.  */
diff --git a/gdb/ada-lang.h b/gdb/ada-lang.h
index a6caf35b826..a593a44daa7 100644
--- a/gdb/ada-lang.h
+++ b/gdb/ada-lang.h
@@ -28,6 +28,7 @@ struct parser_state;
 #include "value.h"
 #include "gdbtypes.h"
 #include "breakpoint.h"
+#include "valprint.h"
 
 /* Names of specific files known to be part of the runtime
    system and that might consider (confusing) debugging information.
@@ -172,13 +173,12 @@ extern void ada_value_print (struct value *, struct ui_file *,
 
 				/* Defined in ada-lang.c */
 
-extern void ada_emit_char (int, struct type *, struct ui_file *, int, int);
-
-extern void ada_printchar (int, struct type *, struct ui_file *);
-
-extern void ada_printstr (struct ui_file *, struct type *, const gdb_byte *,
-			  unsigned int, const char *, int,
-			  const struct value_print_options *);
+extern void ada_emit_char (obstack_wide_file *stream, gdb_wint_t w,
+			   gdb::array_view<const gdb_byte> orig,
+			   int width,
+			   enum bfd_endian byte_order,
+			   int quoter,
+			   bool *need_escapep);
 
 struct value *ada_convert_actual (struct value *actual,
 				  struct type *formal_type0);
diff --git a/gdb/ada-valprint.c b/gdb/ada-valprint.c
index a59c392bef4..cf3145fcc40 100644
--- a/gdb/ada-valprint.c
+++ b/gdb/ada-valprint.c
@@ -255,29 +255,41 @@ val_print_packed_array_elements (struct type *type, const gdb_byte *valaddr,
   value_free_to_mark (mark);
 }
 
-/* Print the character C on STREAM as part of the contents of a literal
-   string whose delimiter is QUOTER.  TYPE_LEN is the length in bytes
-   of the character.  */
+/* A callback for generic_emit_char and generic_printstr that escapes
+   characters Ada-style.  */
 
 void
-ada_emit_char (int c, struct type *type, struct ui_file *stream,
-	       int quoter, int type_len)
+ada_emit_char (obstack_wide_file *stream, gdb_wint_t w,
+	       gdb::array_view<const gdb_byte> orig,
+	       int width,
+	       enum bfd_endian byte_order,
+	       int quoter,
+	       bool *need_escapep)
 {
-  /* If this character fits in the normal ASCII range, and is
-     a printable character, then print the character as if it was
-     an ASCII character, even if this is a wide character.
-     The UCHAR_MAX check is necessary because the isascii function
-     requires that its argument have a value of an unsigned char,
-     or EOF (EOF is obviously not printable).  */
-  if (c <= UCHAR_MAX && isascii (c) && isprint (c))
+  if (quoter == '"' && w == LCST ('"'))
+    fprintf_filtered (stream, "\"\"");
+  else if (gdb_iswprint (w) && !gdb_iswcntrl (w))
+    default_emit_wchar (stream, w, orig, width, byte_order, quoter,
+			need_escapep);
+  else
     {
-      if (c == quoter && c == '"')
-	fprintf_filtered (stream, "\"\"");
-      else
-	fprintf_filtered (stream, "%c", c);
+      int i;
+
+      for (i = 0; i + width <= orig.size (); i += width)
+	{
+	  ULONGEST value = extract_unsigned_integer (&orig[i], width,
+						     byte_order);
+	  fprintf_filtered (stream, "[\"%0*lx\"]", width * 2,
+			    (unsigned long) value);
+	}
+
+      /* If we somehow have extra bytes, print them now.  */
+      while (i < orig.size ())
+	{
+	  fprintf_filtered (stream, "[\"%02x\"]", orig[i] & 0xff);
+	  ++i;
+	}
     }
-  else
-    fprintf_filtered (stream, "[\"%0*x\"]", type_len * 2, c);
 }
 
 /* Character #I of STRING, given that TYPE_LEN is the size in bytes
@@ -348,14 +360,6 @@ ada_print_floating (const gdb_byte *valaddr, struct type *type,
     fprintf_filtered (stream, "%s", &s[skip_count]);
 }
 
-void
-ada_printchar (int c, struct type *type, struct ui_file *stream)
-{
-  fputs_filtered ("'", stream);
-  ada_emit_char (c, type, stream, '\'', TYPE_LENGTH (type));
-  fputs_filtered ("'", stream);
-}
-
 /* [From print_type_scalar in typeprint.c].   Print VAL on STREAM in a
    form appropriate for TYPE, if non-NULL.  If TYPE is NULL, print VAL
    like a default signed integer.  */
@@ -436,103 +440,6 @@ ada_print_scalar (struct type *type, LONGEST val, struct ui_file *stream)
     }
 }
 
-/* Print the character string STRING, printing at most LENGTH characters.
-   Printing stops early if the number hits print_max; repeat counts
-   are printed as appropriate.  Print ellipses at the end if we
-   had to stop before printing LENGTH characters, or if FORCE_ELLIPSES.
-   TYPE_LEN is the length (1 or 2) of the character type.  */
-
-static void
-printstr (struct ui_file *stream, struct type *elttype, const gdb_byte *string,
-	  unsigned int length, int force_ellipses, int type_len,
-	  const struct value_print_options *options)
-{
-  enum bfd_endian byte_order = type_byte_order (elttype);
-  unsigned int i;
-  unsigned int things_printed = 0;
-  int in_quotes = 0;
-  int need_comma = 0;
-
-  if (length == 0)
-    {
-      fputs_filtered ("\"\"", stream);
-      return;
-    }
-
-  for (i = 0; i < length && things_printed < options->print_max; i += 1)
-    {
-      /* Position of the character we are examining
-	 to see whether it is repeated.  */
-      unsigned int rep1;
-      /* Number of repetitions we have detected so far.  */
-      unsigned int reps;
-
-      QUIT;
-
-      if (need_comma)
-	{
-	  fputs_filtered (", ", stream);
-	  need_comma = 0;
-	}
-
-      rep1 = i + 1;
-      reps = 1;
-      while (rep1 < length
-	     && char_at (string, rep1, type_len, byte_order)
-		== char_at (string, i, type_len, byte_order))
-	{
-	  rep1 += 1;
-	  reps += 1;
-	}
-
-      if (reps > options->repeat_count_threshold)
-	{
-	  if (in_quotes)
-	    {
-	      fputs_filtered ("\", ", stream);
-	      in_quotes = 0;
-	    }
-	  fputs_filtered ("'", stream);
-	  ada_emit_char (char_at (string, i, type_len, byte_order),
-			 elttype, stream, '\'', type_len);
-	  fputs_filtered ("'", stream);
-	  fprintf_filtered (stream, _(" %p[<repeats %u times>%p]"),
-			    metadata_style.style ().ptr (), reps, nullptr);
-	  i = rep1 - 1;
-	  things_printed += options->repeat_count_threshold;
-	  need_comma = 1;
-	}
-      else
-	{
-	  if (!in_quotes)
-	    {
-	      fputs_filtered ("\"", stream);
-	      in_quotes = 1;
-	    }
-	  ada_emit_char (char_at (string, i, type_len, byte_order),
-			 elttype, stream, '"', type_len);
-	  things_printed += 1;
-	}
-    }
-
-  /* Terminate the quotes if necessary.  */
-  if (in_quotes)
-    fputs_filtered ("\"", stream);
-
-  if (force_ellipses || i < length)
-    fputs_filtered ("...", stream);
-}
-
-void
-ada_printstr (struct ui_file *stream, struct type *type,
-	      const gdb_byte *string, unsigned int length,
-	      const char *encoding, int force_ellipses,
-	      const struct value_print_options *options)
-{
-  printstr (stream, type, string, length, force_ellipses, TYPE_LENGTH (type),
-	    options);
-}
-
 static int
 print_variant_part (struct value *value, int field_num,
 		    struct value *outer_value,
@@ -707,8 +614,8 @@ ada_val_print_string (struct type *type, const gdb_byte *valaddr,
       len = temp_len;
     }
 
-  printstr (stream, elttype, valaddr + offset_aligned, len, 0,
-	    eltlen, options);
+  current_language->printstr (stream, elttype, valaddr + offset_aligned,
+			      len, nullptr, 0, options);
 }
 
 /* Implement Ada value_print'ing for the case where TYPE is a
@@ -802,7 +709,7 @@ ada_value_print_num (struct value *val, struct ui_file *stream, int recurse,
 
 	      fputs_filtered (" ", stream);
 	      c = unpack_long (type, valaddr);
-	      ada_printchar (c, type, stream);
+	      current_language->printchar (c, type, stream);
 	    }
 	}
       return;
-- 
2.31.1


^ permalink raw reply	[flat|nested] 30+ messages in thread

* [PATCH v2 16/18] Use generic_emit_char in Modula-2
  2022-02-17 22:05 [PATCH v2 00/18] Refactor character printing Tom Tromey
                   ` (14 preceding siblings ...)
  2022-02-17 22:05 ` [PATCH v2 15/18] Use generic_emit_char in Ada Tom Tromey
@ 2022-02-17 22:05 ` Tom Tromey
  2022-02-23 20:17   ` Gaius Mulley
                     ` (2 more replies)
  2022-02-17 22:05 ` [PATCH v2 17/18] Use generic_emit_char in Pascal Tom Tromey
                   ` (2 subsequent siblings)
  18 siblings, 3 replies; 30+ messages in thread
From: Tom Tromey @ 2022-02-17 22:05 UTC (permalink / raw)
  To: gdb-patches; +Cc: Tom Tromey

This changes the Modula-2 code to use generic_emit_char and
generic_printstr.  I have no way to test this.  However, the Modula-2
code seems to be pretty much a copy of the old C code from before the
charset work.
---
 gdb/m2-lang.c | 144 +++++++++-----------------------------------------
 gdb/m2-lang.h |   5 --
 2 files changed, 26 insertions(+), 123 deletions(-)

diff --git a/gdb/m2-lang.c b/gdb/m2-lang.c
index 7673426b7a8..29aaec87405 100644
--- a/gdb/m2-lang.c
+++ b/gdb/m2-lang.c
@@ -139,137 +139,45 @@ m2_language::language_arch_info (struct gdbarch *gdbarch,
   lai->set_bool_type (builtin->builtin_bool, "BOOLEAN");
 }
 
+/* A callback function for generic_emit_char and generic_printstr that
+   escapes characters Modula 2-style.  */
+
+static void
+m2_emit_char (obstack_wide_file *stream,
+	      gdb_wint_t w,
+	      gdb::array_view<const gdb_byte> orig,
+	      int width,
+	      enum bfd_endian byte_order,
+	      int quoter,
+	      bool *need_escapep)
+{
+  /* Historically the Modula-2 code in gdb handled \e as well.  */
+  if (w == LCST ('\033'))
+    fputs_filtered ("\\e", stream);
+  else
+    default_emit_wchar (stream, w, orig, width, byte_order, quoter,
+			need_escapep);
+}
+
 /* See languge.h.  */
 
 void
 m2_language::printchar (int c, struct type *type,
 			struct ui_file *stream) const
 {
-  fputs_filtered ("'", stream);
-  emitchar (c, type, stream, '\'');
-  fputs_filtered ("'", stream);
+  generic_emit_char (c, type, stream, '\'', nullptr, m2_emit_char);
 }
 
 /* See language.h.  */
 
 void
 m2_language::printstr (struct ui_file *stream, struct type *elttype,
-			const gdb_byte *string, unsigned int length,
-			const char *encoding, int force_ellipses,
-			const struct value_print_options *options) const
-{
-  unsigned int i;
-  unsigned int things_printed = 0;
-  int in_quotes = 0;
-  int need_comma = 0;
-
-  if (length == 0)
-    {
-      puts_filtered ("\"\"");
-      return;
-    }
-
-  for (i = 0; i < length && things_printed < options->print_max; ++i)
-    {
-      /* Position of the character we are examining
-	 to see whether it is repeated.  */
-      unsigned int rep1;
-      /* Number of repetitions we have detected so far.  */
-      unsigned int reps;
-
-      QUIT;
-
-      if (need_comma)
-	{
-	  fputs_filtered (", ", stream);
-	  need_comma = 0;
-	}
-
-      rep1 = i + 1;
-      reps = 1;
-      while (rep1 < length && string[rep1] == string[i])
-	{
-	  ++rep1;
-	  ++reps;
-	}
-
-      if (reps > options->repeat_count_threshold)
-	{
-	  if (in_quotes)
-	    {
-	      fputs_filtered ("\", ", stream);
-	      in_quotes = 0;
-	    }
-	  printchar (string[i], elttype, stream);
-	  fprintf_filtered (stream, " <repeats %u times>", reps);
-	  i = rep1 - 1;
-	  things_printed += options->repeat_count_threshold;
-	  need_comma = 1;
-	}
-      else
-	{
-	  if (!in_quotes)
-	    {
-	      fputs_filtered ("\"", stream);
-	      in_quotes = 1;
-	    }
-	  emitchar (string[i], elttype, stream, '"');
-	  ++things_printed;
-	}
-    }
-
-  /* Terminate the quotes if necessary.  */
-  if (in_quotes)
-    fputs_filtered ("\"", stream);
-
-  if (force_ellipses || i < length)
-    fputs_filtered ("...", stream);
-}
-
-/* See language.h.  */
-
-void
-m2_language::emitchar (int ch, struct type *chtype,
-		       struct ui_file *stream, int quoter) const
+		       const gdb_byte *string, unsigned int length,
+		       const char *encoding, int force_ellipses,
+		       const struct value_print_options *options) const
 {
-  ch &= 0xFF;			/* Avoid sign bit follies.  */
-
-  if (PRINT_LITERAL_FORM (ch))
-    {
-      if (ch == '\\' || ch == quoter)
-	fputs_filtered ("\\", stream);
-      fprintf_filtered (stream, "%c", ch);
-    }
-  else
-    {
-      switch (ch)
-	{
-	case '\n':
-	  fputs_filtered ("\\n", stream);
-	  break;
-	case '\b':
-	  fputs_filtered ("\\b", stream);
-	  break;
-	case '\t':
-	  fputs_filtered ("\\t", stream);
-	  break;
-	case '\f':
-	  fputs_filtered ("\\f", stream);
-	  break;
-	case '\r':
-	  fputs_filtered ("\\r", stream);
-	  break;
-	case '\033':
-	  fputs_filtered ("\\e", stream);
-	  break;
-	case '\007':
-	  fputs_filtered ("\\a", stream);
-	  break;
-	default:
-	  fprintf_filtered (stream, "\\%.3o", (unsigned int) ch);
-	  break;
-	}
-    }
+  generic_printstr (stream, elttype, string, length, encoding, force_ellipses,
+		    '"', 0, options, m2_emit_char);
 }
 
 /* Called during architecture gdbarch initialisation to create language
diff --git a/gdb/m2-lang.h b/gdb/m2-lang.h
index ee2fc6fb8bc..2f516bc6427 100644
--- a/gdb/m2-lang.h
+++ b/gdb/m2-lang.h
@@ -92,11 +92,6 @@ class m2_language : public language_defn
 
   /* See language.h.  */
 
-  void emitchar (int ch, struct type *chtype,
-		 struct ui_file *stream, int quoter) const;
-
-  /* See language.h.  */
-
   void printchar (int ch, struct type *chtype,
 		  struct ui_file *stream) const override;
 
-- 
2.31.1


^ permalink raw reply	[flat|nested] 30+ messages in thread

* [PATCH v2 17/18] Use generic_emit_char in Pascal
  2022-02-17 22:05 [PATCH v2 00/18] Refactor character printing Tom Tromey
                   ` (15 preceding siblings ...)
  2022-02-17 22:05 ` [PATCH v2 16/18] Use generic_emit_char in Modula-2 Tom Tromey
@ 2022-02-17 22:05 ` Tom Tromey
  2022-02-17 22:05 ` [PATCH v2 18/18] Simplify Fortran string printing Tom Tromey
  2022-10-10 17:37 ` [PATCH v2 00/18] Refactor character printing Tom Tromey
  18 siblings, 0 replies; 30+ messages in thread
From: Tom Tromey @ 2022-02-17 22:05 UTC (permalink / raw)
  To: gdb-patches; +Cc: Tom Tromey

This changes the Pascal to use generic_emit_char and generic_printstr.
The output isn't identical, though it does pass the Pascal tests
(during which I learned that FPC does not work with gold...).

I think this is an improvement, nevertheless, because the Pascal
expression parser accepts the C-style output that is now generated.
---
 gdb/p-lang.c | 134 +++++++--------------------------------------------
 gdb/p-lang.h |  12 -----
 2 files changed, 18 insertions(+), 128 deletions(-)

diff --git a/gdb/p-lang.c b/gdb/p-lang.c
index 70ff404bac3..6afaada6a68 100644
--- a/gdb/p-lang.c
+++ b/gdb/p-lang.c
@@ -143,31 +143,23 @@ pascal_is_string_type (struct type *type,int *length_pos, int *length_size,
   return 0;
 }
 
-/* See p-lang.h.  */
-
-void
-pascal_language::print_one_char (int c, struct ui_file *stream,
-				 int *in_quotes) const
+/* A callback function for generic_emit_char and generic_printstr that
+   escapes characters Pascal style.  */
+
+static void
+pascal_emit_char (obstack_wide_file *stream,
+		  gdb_wint_t w,
+		  gdb::array_view<const gdb_byte> orig,
+		  int width,
+		  enum bfd_endian byte_order,
+		  int quoter,
+		  bool *need_escapep)
 {
-  if (c == '\'' || ((unsigned int) c <= 0xff && (PRINT_LITERAL_FORM (c))))
-    {
-      if (!(*in_quotes))
-	fputs_filtered ("'", stream);
-      *in_quotes = 1;
-      if (c == '\'')
-	{
-	  fputs_filtered ("''", stream);
-	}
-      else
-	fprintf_filtered (stream, "%c", c);
-    }
+  if (w == LCST ('\''))
+    fputs_filtered ("''", stream);
   else
-    {
-      if (*in_quotes)
-	fputs_filtered ("'", stream);
-      *in_quotes = 0;
-      fprintf_filtered (stream, "#%d", (unsigned int) c);
-    }
+    default_emit_wchar (stream, w, orig, width, byte_order, quoter,
+			need_escapep);
 }
 
 /* See language.h.  */
@@ -176,11 +168,7 @@ void
 pascal_language::printchar (int c, struct type *type,
 			    struct ui_file *stream) const
 {
-  int in_quotes = 0;
-
-  print_one_char (c, stream, &in_quotes);
-  if (in_quotes)
-    fputs_filtered ("'", stream);
+  generic_emit_char (c, type, stream, '\'', nullptr, pascal_emit_char);
 }
 
 \f
@@ -228,94 +216,8 @@ pascal_language::printstr (struct ui_file *stream, struct type *elttype,
 			   const char *encoding, int force_ellipses,
 			   const struct value_print_options *options) const
 {
-  enum bfd_endian byte_order = type_byte_order (elttype);
-  unsigned int i;
-  unsigned int things_printed = 0;
-  int in_quotes = 0;
-  int need_comma = 0;
-  int width;
-
-  /* Preserve ELTTYPE's original type, just set its LENGTH.  */
-  check_typedef (elttype);
-  width = TYPE_LENGTH (elttype);
-
-  /* If the string was not truncated due to `set print elements', and
-     the last byte of it is a null, we don't print that, in traditional C
-     style.  */
-  if ((!force_ellipses) && length > 0
-      && extract_unsigned_integer (string + (length - 1) * width, width,
-				   byte_order) == 0)
-    length--;
-
-  if (length == 0)
-    {
-      fputs_filtered ("''", stream);
-      return;
-    }
-
-  for (i = 0; i < length && things_printed < options->print_max; ++i)
-    {
-      /* Position of the character we are examining
-	 to see whether it is repeated.  */
-      unsigned int rep1;
-      /* Number of repetitions we have detected so far.  */
-      unsigned int reps;
-      unsigned long int current_char;
-
-      QUIT;
-
-      if (need_comma)
-	{
-	  fputs_filtered (", ", stream);
-	  need_comma = 0;
-	}
-
-      current_char = extract_unsigned_integer (string + i * width, width,
-					       byte_order);
-
-      rep1 = i + 1;
-      reps = 1;
-      while (rep1 < length
-	     && extract_unsigned_integer (string + rep1 * width, width,
-					  byte_order) == current_char)
-	{
-	  ++rep1;
-	  ++reps;
-	}
-
-      if (reps > options->repeat_count_threshold)
-	{
-	  if (in_quotes)
-	    {
-	      fputs_filtered ("', ", stream);
-	      in_quotes = 0;
-	    }
-	  printchar (current_char, elttype, stream);
-	  fprintf_filtered (stream, " %p[<repeats %u times>%p]",
-			    metadata_style.style ().ptr (),
-			    reps, nullptr);
-	  i = rep1 - 1;
-	  things_printed += options->repeat_count_threshold;
-	  need_comma = 1;
-	}
-      else
-	{
-	  if ((!in_quotes) && (PRINT_LITERAL_FORM (current_char)))
-	    {
-	      fputs_filtered ("'", stream);
-	      in_quotes = 1;
-	    }
-	  print_one_char (current_char, stream, &in_quotes);
-	  ++things_printed;
-	}
-    }
-
-  /* Terminate the quotes if necessary.  */
-  if (in_quotes)
-    fputs_filtered ("'", stream);
-
-  if (force_ellipses || i < length)
-    fputs_filtered ("...", stream);
+  generic_printstr (stream, elttype, string, length, encoding, force_ellipses,
+		    '\'', 0, options, pascal_emit_char);
 }
 
 /* Single instance of the Pascal language class.  */
diff --git a/gdb/p-lang.h b/gdb/p-lang.h
index b831bde0af6..79f8fd7c28d 100644
--- a/gdb/p-lang.h
+++ b/gdb/p-lang.h
@@ -109,18 +109,6 @@ class pascal_language : public language_defn
 
   /* See language.h.  */
 
-  void emitchar (int ch, struct type *chtype,
-		 struct ui_file *stream, int quoter) const
-  {
-    int in_quotes = 0;
-
-    print_one_char (ch, stream, &in_quotes);
-    if (in_quotes)
-      fputs_filtered ("'", stream);
-  }
-
-  /* See language.h.  */
-
   void printchar (int ch, struct type *chtype,
 		  struct ui_file *stream) const override;
 
-- 
2.31.1


^ permalink raw reply	[flat|nested] 30+ messages in thread

* [PATCH v2 18/18] Simplify Fortran string printing
  2022-02-17 22:05 [PATCH v2 00/18] Refactor character printing Tom Tromey
                   ` (16 preceding siblings ...)
  2022-02-17 22:05 ` [PATCH v2 17/18] Use generic_emit_char in Pascal Tom Tromey
@ 2022-02-17 22:05 ` Tom Tromey
  2022-10-10 17:37 ` [PATCH v2 00/18] Refactor character printing Tom Tromey
  18 siblings, 0 replies; 30+ messages in thread
From: Tom Tromey @ 2022-02-17 22:05 UTC (permalink / raw)
  To: gdb-patches; +Cc: Tom Tromey

Now that generic_emit_char has a default encoding, Fortran can remove
its get_encoding method and just rely on the default.
---
 gdb/f-lang.c | 27 ---------------------------
 gdb/f-lang.h | 13 +------------
 2 files changed, 1 insertion(+), 39 deletions(-)

diff --git a/gdb/f-lang.c b/gdb/f-lang.c
index eaeda884aef..7fdc767ccdb 100644
--- a/gdb/f-lang.c
+++ b/gdb/f-lang.c
@@ -74,33 +74,6 @@ static value *fortran_prepare_argument (struct expression *exp,
 					int arg_num, bool is_internal_call_p,
 					struct type *func_type, enum noside noside);
 
-/* Return the encoding that should be used for the character type
-   TYPE.  */
-
-const char *
-f_language::get_encoding (struct type *type)
-{
-  const char *encoding;
-
-  switch (TYPE_LENGTH (type))
-    {
-    case 1:
-      encoding = target_charset (type->arch ());
-      break;
-    case 4:
-      if (type_byte_order (type) == BFD_ENDIAN_BIG)
-	encoding = "UTF-32BE";
-      else
-	encoding = "UTF-32LE";
-      break;
-
-    default:
-      error (_("unrecognized character type"));
-    }
-
-  return encoding;
-}
-
 \f
 
 /* A helper function for the "bound" intrinsics that checks that TYPE
diff --git a/gdb/f-lang.h b/gdb/f-lang.h
index fc2cdc1786b..c3c76193dc4 100644
--- a/gdb/f-lang.h
+++ b/gdb/f-lang.h
@@ -152,8 +152,7 @@ class f_language : public language_defn
   void printchar (int ch, struct type *chtype,
 		  struct ui_file *stream) const override
   {
-    const char *encoding = get_encoding (chtype);
-    generic_emit_char (ch, chtype, stream, '\'', encoding);
+    generic_emit_char (ch, chtype, stream, '\'', nullptr);
   }
 
   /* See language.h.  */
@@ -163,14 +162,9 @@ class f_language : public language_defn
 		 const char *encoding, int force_ellipses,
 		 const struct value_print_options *options) const override
   {
-    const char *type_encoding = get_encoding (elttype);
-
     if (TYPE_LENGTH (elttype) == 4)
       fputs_filtered ("4_", stream);
 
-    if (!encoding || !*encoding)
-      encoding = type_encoding;
-
     generic_printstr (stream, elttype, string, length, encoding,
 		      force_ellipses, '\'', 0, options);
   }
@@ -223,11 +217,6 @@ class f_language : public language_defn
 	(const lookup_name_info &lookup_name) const override;
 
 private:
-  /* Return the encoding that should be used for the character type
-     TYPE.  */
-
-  static const char *get_encoding (struct type *type);
-
   /* Print any asterisks or open-parentheses needed before the variable
      name (to describe its type).
 
-- 
2.31.1


^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v2 05/18] Don't use wchar_printable in print_wchar
  2022-02-17 22:05 ` [PATCH v2 05/18] Don't use wchar_printable in print_wchar Tom Tromey
@ 2022-02-22 15:36   ` Andrew Burgess
  2022-10-10 16:39     ` Tom Tromey
  0 siblings, 1 reply; 30+ messages in thread
From: Andrew Burgess @ 2022-02-22 15:36 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

* Tom Tromey <tom@tromey.com> [2022-02-17 15:05:33 -0700]:

> print_wchar uses wchar_printable, but this isn't needed -- all the
> relevant cases are already handled by the 'switch'.  This changes the
> code to use gdb_iswprint, and removes a somewhat confusing comment
> related to this code.
> ---
>  gdb/valprint.c | 9 +++------
>  1 file changed, 3 insertions(+), 6 deletions(-)
> 
> diff --git a/gdb/valprint.c b/gdb/valprint.c
> index e758c1d1066..17ad46c87b5 100644
> --- a/gdb/valprint.c
> +++ b/gdb/valprint.c
> @@ -2197,9 +2197,6 @@ print_wchar (gdb_wint_t w, const gdb_byte *orig,
>  
>    *need_escapep = false;
>  
> -  /* iswprint implementation on Windows returns 1 for tab character.
> -     In order to avoid different printout on this host, we explicitly
> -     use wchar_printable function.  */

I wonder if we should leave some comment here that just says something
like:

  /* If any additional cases are added to this switch block, then the
     function wchar_printable will likely need updating too.  */

Though I guess we'd figure out soon enough if the two came out of
step, so it's up to you.

Otherwise, this, and all the patches up to this point, LGTM.

Thanks,
Andrew

>    switch (w)
>      {
>        case LCST ('\a'):
> @@ -2225,9 +2222,9 @@ print_wchar (gdb_wint_t w, const gdb_byte *orig,
>  	break;
>        default:
>  	{
> -	  if (wchar_printable (w) && (!need_escape || (!gdb_iswdigit (w)
> -						       && w != LCST ('8')
> -						       && w != LCST ('9'))))
> +	  if (gdb_iswprint (w) && (!need_escape || (!gdb_iswdigit (w)
> +						    && w != LCST ('8')
> +						    && w != LCST ('9'))))
>  	    {
>  	      gdb_wchar_t wchar = w;
>  
> -- 
> 2.31.1
> 


^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v2 09/18] Include \0 in printable wide characters
  2022-02-17 22:05 ` [PATCH v2 09/18] Include \0 in printable wide characters Tom Tromey
@ 2022-02-23 13:49   ` Andrew Burgess
  2022-02-23 22:28     ` Tom Tromey
  0 siblings, 1 reply; 30+ messages in thread
From: Andrew Burgess @ 2022-02-23 13:49 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

* Tom Tromey <tom@tromey.com> [2022-02-17 15:05:37 -0700]:

> print_wchar can already display \0, so include it in the list of
> "printable" characters in wchar_printable.  This is useful in a coming
> patch to change Rust to use the generic character-printing code.
> ---
>  gdb/valprint.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/gdb/valprint.c b/gdb/valprint.c
> index 545dfbca73f..57201164c87 100644
> --- a/gdb/valprint.c
> +++ b/gdb/valprint.c
> @@ -2160,7 +2160,7 @@ wchar_printable (gdb_wchar_t w)
>  	  || w == LCST ('\a') || w == LCST ('\b')
>  	  || w == LCST ('\f') || w == LCST ('\n')
>  	  || w == LCST ('\r') || w == LCST ('\t')
> -	  || w == LCST ('\v'));
> +	  || w == LCST ('\v') || w == LCST ('\0'));
>  }

I'm not convinced that this change is OK.

Unfortunately, I don't an example that shows this change causing a
failure, but I can explain my thinking for why this bothers me.

My first thought was how can you add this to wchar_printable, without
also adding something to print_wchar.  Your commit message says that
print_wchar already handles \0, but I feel that's a little misleading,
the code path that "handles" \0 is (I think) the path that prints
values as escape sequences, so surely your commit could actually just
be:

  static int
  wchar_printable (gdb_wchar_t w)
  {
    /* print_wchar handles everything!  */
    return true;
  }

So, I tried completely removing wchar_printable, and all its one user,
and now the core of generic_emit_char looks like this:

  while (1)
    {
      int num_chars;
      gdb_wchar_t *chars;
      const gdb_byte *buf;
      size_t buflen;
      enum wchar_iterate_result result;

      num_chars = iter.iterate (&result, &chars, &buf, &buflen);
      if (num_chars < 0)
	break;
      if (num_chars > 0)
	{
	  for (int i = 0; i < num_chars; ++i)
	    print_wchar (chars[i], buf, buflen,
			 TYPE_LENGTH (type), byte_order,
			 &wchar_buf, quoter, &need_escape);
	}
      else
	print_wchar (gdb_WEOF, buf, buflen, TYPE_LENGTH (type),
		     byte_order, &wchar_buf, quoter, &need_escape);
    }

With that change there's no regressions on upstream/master.  And if I
apply this series completely, and make a similar change to
generic_emit_char, I still see no regressions.

So, does this mean that wchar_printable is not needed?

I don't think so.  I think there is a problem with the above change,
and its covered by this comment, which I found slightly cryptic, but I
think I eventually figured out:

  /* If all characters are printable, print them.  Otherwise,
     we're going to have to print an escape sequence.  We
     check all characters because we want to print the target
     bytes in the escape sequence, and we don't know character
     boundaries there.  */

My confusion here is that I initially thought; if we have multiple
characters, some that are printable, and some that are not, then
surely, we would want to print the initial printable ones for real,
and only later switch to escape sequences, right?

Except, that's not what we do.

And the reason (probably obvious to quicker minds than mine) is that
characters might have different widths, so we can't "just" print the
initial characters, and then print the unprintable as escape
sequences, as we wouldn't know where in BUF the unprintable character
actually starts.

OK, so my idea of removing wchar_printable is clearly a bad idea, but
how does this relate to your change?

Well, prior to this patch, if we had 3 characters, the first two are
printable, and the third was \0, we would spot the non-printable \0,
and so print the whole buffer, all 3 characters, as escape sequences.

With this patch, all 3 characters will appear to be printable.  So now
we will print the first character, just fine.  Then print the second
character just fine.  Now for the third character, the \0, we call to
print_wchar.  The \0 is not handled by anything but the 'default' case
of the switch.

In the default case, the \0 is non-printable, so we end up in the
escape sequence printing code, which then tries to load bytes starting
from BUF - which isn't going to be correct.

Now, this is where things are a bit weird.  The code in
generic_emit_char is clearly written to handle multiple characters,
but, I've only ever seen it print 1 character, which is why, I claim,
your above change to wchar_printable works.

I'm of the opinion that passing buf, buflen, the TYPE_LENGTH(type),
and byte_order to the first print_wchar call (in generic_emit_char) is
a bad idea, if we hadn't done that then your proposed change would
never have worked.

I propose that there should be three tightly coupled, and related
functions:

  bool wchar_printable (gdb_wchar_t w);
  void print_wchar (gdb_wint_t w, struct obstack *output, int quoter, int *need_escapep);
  void print_wchar_escape (const gdb_byte *orig, int orig_len, int width,
			   enum bfd_endian byte_order, struct obstack *output,
			   int quoter, int *need_escapep);

With the third one of these being entirely new.  I think that the
wchar_printable _must_ return true only for characters that
print_wchar can handle.  If print_wchar sees something it can't
handle, then this is an internal_error situation.

As for the impact on your patch series, my thinking is that you might
need to consider replacing emit_char_ftype with an actual
emit_char_handler object reference.  We'd then have:

  struct emit_wchar_handler
  {
    virtual bool is_printable (gdb_wchar_t w)
    { .... }

    void print (gdb_wint_t w, struct obstack *output,
                int quoter, int *need_escapep)
    { ... }

    virtual void print_wchar_escape (const gdb_byte *orig, int orig_len, int width,
			             enum bfd_endian byte_order, struct obstack *output,
			             int quoter, int *need_escapep)
    { ... }
  };

an instance of this would replace default_emit_wchar in the valprint.h
API.  And languages can create a sub-class of this if they need too.

Have I missed something here?

Thanks,
Andrew


^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v2 16/18] Use generic_emit_char in Modula-2
  2022-02-17 22:05 ` [PATCH v2 16/18] Use generic_emit_char in Modula-2 Tom Tromey
@ 2022-02-23 20:17   ` Gaius Mulley
  2022-03-16 12:29   ` [PATCH] Additional modula2 tests Gaius Mulley
  2022-04-11 19:45   ` [PATCH v1] Array access in Modula-2 Gaius Mulley
  2 siblings, 0 replies; 30+ messages in thread
From: Gaius Mulley @ 2022-02-23 20:17 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches


Tom Tromey <tom@tromey.com> writes:

> This changes the Modula-2 code to use generic_emit_char and
> generic_printstr.  I have no way to test this.  However, the Modula-2
> code seems to be pretty much a copy of the old C code from before the
> charset work.
> ---
>  gdb/m2-lang.c | 144 +++++++++-----------------------------------------
>  gdb/m2-lang.h |   5 --
>  2 files changed, 26 insertions(+), 123 deletions(-)
>
> diff --git a/gdb/m2-lang.c b/gdb/m2-lang.c
> index 7673426b7a8..29aaec87405 100644
> --- a/gdb/m2-lang.c
> +++ b/gdb/m2-lang.c
> @@ -139,137 +139,45 @@ m2_language::language_arch_info (struct gdbarch *gdbarch,
>    lai->set_bool_type (builtin->builtin_bool, "BOOLEAN");
>  }
>  
> +/* A callback function for generic_emit_char and generic_printstr that
> +   escapes characters Modula 2-style.  */
> +
> +static void
> +m2_emit_char (obstack_wide_file *stream,
> +	      gdb_wint_t w,
> +	      gdb::array_view<const gdb_byte> orig,
> +	      int width,
> +	      enum bfd_endian byte_order,
> +	      int quoter,
> +	      bool *need_escapep)
> +{
> +  /* Historically the Modula-2 code in gdb handled \e as well.  */
> +  if (w == LCST ('\033'))
> +    fputs_filtered ("\\e", stream);
> +  else
> +    default_emit_wchar (stream, w, orig, width, byte_order, quoter,
> +			need_escapep);
> +}
> +
>  /* See languge.h.  */
>  
>  void
>  m2_language::printchar (int c, struct type *type,
>  			struct ui_file *stream) const
>  {
> -  fputs_filtered ("'", stream);
> -  emitchar (c, type, stream, '\'');
> -  fputs_filtered ("'", stream);
> +  generic_emit_char (c, type, stream, '\'', nullptr, m2_emit_char);
>  }
>  
>  /* See language.h.  */
>  
>  void
>  m2_language::printstr (struct ui_file *stream, struct type *elttype,
> -			const gdb_byte *string, unsigned int length,
> -			const char *encoding, int force_ellipses,
> -			const struct value_print_options *options) const
> -{
> -  unsigned int i;
> -  unsigned int things_printed = 0;
> -  int in_quotes = 0;
> -  int need_comma = 0;
> -
> -  if (length == 0)
> -    {
> -      puts_filtered ("\"\"");
> -      return;
> -    }
> -
> -  for (i = 0; i < length && things_printed < options->print_max; ++i)
> -    {
> -      /* Position of the character we are examining
> -	 to see whether it is repeated.  */
> -      unsigned int rep1;
> -      /* Number of repetitions we have detected so far.  */
> -      unsigned int reps;
> -
> -      QUIT;
> -
> -      if (need_comma)
> -	{
> -	  fputs_filtered (", ", stream);
> -	  need_comma = 0;
> -	}
> -
> -      rep1 = i + 1;
> -      reps = 1;
> -      while (rep1 < length && string[rep1] == string[i])
> -	{
> -	  ++rep1;
> -	  ++reps;
> -	}
> -
> -      if (reps > options->repeat_count_threshold)
> -	{
> -	  if (in_quotes)
> -	    {
> -	      fputs_filtered ("\", ", stream);
> -	      in_quotes = 0;
> -	    }
> -	  printchar (string[i], elttype, stream);
> -	  fprintf_filtered (stream, " <repeats %u times>", reps);
> -	  i = rep1 - 1;
> -	  things_printed += options->repeat_count_threshold;
> -	  need_comma = 1;
> -	}
> -      else
> -	{
> -	  if (!in_quotes)
> -	    {
> -	      fputs_filtered ("\"", stream);
> -	      in_quotes = 1;
> -	    }
> -	  emitchar (string[i], elttype, stream, '"');
> -	  ++things_printed;
> -	}
> -    }
> -
> -  /* Terminate the quotes if necessary.  */
> -  if (in_quotes)
> -    fputs_filtered ("\"", stream);
> -
> -  if (force_ellipses || i < length)
> -    fputs_filtered ("...", stream);
> -}
> -
> -/* See language.h.  */
> -
> -void
> -m2_language::emitchar (int ch, struct type *chtype,
> -		       struct ui_file *stream, int quoter) const
> +		       const gdb_byte *string, unsigned int length,
> +		       const char *encoding, int force_ellipses,
> +		       const struct value_print_options *options) const
>  {
> -  ch &= 0xFF;			/* Avoid sign bit follies.  */
> -
> -  if (PRINT_LITERAL_FORM (ch))
> -    {
> -      if (ch == '\\' || ch == quoter)
> -	fputs_filtered ("\\", stream);
> -      fprintf_filtered (stream, "%c", ch);
> -    }
> -  else
> -    {
> -      switch (ch)
> -	{
> -	case '\n':
> -	  fputs_filtered ("\\n", stream);
> -	  break;
> -	case '\b':
> -	  fputs_filtered ("\\b", stream);
> -	  break;
> -	case '\t':
> -	  fputs_filtered ("\\t", stream);
> -	  break;
> -	case '\f':
> -	  fputs_filtered ("\\f", stream);
> -	  break;
> -	case '\r':
> -	  fputs_filtered ("\\r", stream);
> -	  break;
> -	case '\033':
> -	  fputs_filtered ("\\e", stream);
> -	  break;
> -	case '\007':
> -	  fputs_filtered ("\\a", stream);
> -	  break;
> -	default:
> -	  fprintf_filtered (stream, "\\%.3o", (unsigned int) ch);
> -	  break;
> -	}
> -    }
> +  generic_printstr (stream, elttype, string, length, encoding, force_ellipses,
> +		    '"', 0, options, m2_emit_char);
>  }
>  
>  /* Called during architecture gdbarch initialisation to create language
> diff --git a/gdb/m2-lang.h b/gdb/m2-lang.h
> index ee2fc6fb8bc..2f516bc6427 100644
> --- a/gdb/m2-lang.h
> +++ b/gdb/m2-lang.h
> @@ -92,11 +92,6 @@ class m2_language : public language_defn
>  
>    /* See language.h.  */
>  
> -  void emitchar (int ch, struct type *chtype,
> -		 struct ui_file *stream, int quoter) const;
> -
> -  /* See language.h.  */
> -
>    void printchar (int ch, struct type *chtype,
>  		  struct ui_file *stream) const override;

Hi Tom,

all looks fine - but I'll test this and report back.  It might be a few
days as my Internet connection is down - storm damage on the North
Cornwall coast,

regards,
Gaius

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v2 09/18] Include \0 in printable wide characters
  2022-02-23 13:49   ` Andrew Burgess
@ 2022-02-23 22:28     ` Tom Tromey
  2022-02-23 23:59       ` Tom Tromey
  0 siblings, 1 reply; 30+ messages in thread
From: Tom Tromey @ 2022-02-23 22:28 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: Tom Tromey, gdb-patches

>>>>> "Andrew" == Andrew Burgess <aburgess@redhat.com> writes:

Andrew> My confusion here is that I initially thought; if we have multiple
Andrew> characters, some that are printable, and some that are not, then
Andrew> surely, we would want to print the initial printable ones for real,
Andrew> and only later switch to escape sequences, right?

Andrew> Except, that's not what we do.

Andrew> And the reason (probably obvious to quicker minds than mine) is that
Andrew> characters might have different widths, so we can't "just" print the
Andrew> initial characters, and then print the unprintable as escape
Andrew> sequences, as we wouldn't know where in BUF the unprintable character
Andrew> actually starts.

Yeah, that's my understanding as well.

Andrew> OK, so my idea of removing wchar_printable is clearly a bad idea, but
Andrew> how does this relate to your change?

Andrew> Well, prior to this patch, if we had 3 characters, the first two are
Andrew> printable, and the third was \0, we would spot the non-printable \0,
Andrew> and so print the whole buffer, all 3 characters, as escape sequences.

Andrew> With this patch, all 3 characters will appear to be printable.  So now
Andrew> we will print the first character, just fine.  Then print the second
Andrew> character just fine.  Now for the third character, the \0, we call to
Andrew> print_wchar.  The \0 is not handled by anything but the 'default' case
Andrew> of the switch.

Andrew> In the default case, the \0 is non-printable, so we end up in the
Andrew> escape sequence printing code, which then tries to load bytes starting
Andrew> from BUF - which isn't going to be correct.

I think the idea behind this is that only a real \0 in the input will
really ever turn into a L'\0' in the wchar_t form.  It seems to me that
an L'\0' pretty much has to correspond exactly to a target \0, just
because C is pervasive and an encoding where stray \0 bytes can appear
would break everything.

Andrew> Now, this is where things are a bit weird.  The code in
Andrew> generic_emit_char is clearly written to handle multiple characters,
Andrew> but, I've only ever seen it print 1 character, which is why, I claim,
Andrew> your above change to wchar_printable works.

That's most likely because you are trying this on Linux.  Linux uses
UTF-32 for wchar_t, and so there aren't target characters that can't be
converted to a single wchar_t -- because UTF-32 is pretty much designed
to round-trip everything else.  So, on Linux hosts, I think some of
these loops aren't really needed.

However, Windows uses UTF-16 and a single target character can be
converted to two wchar_t, via surrogate pairs.

On Solaris and (IIRC) NetBSD, wchar_t is even weirder, though I don't
recall whether it is a variable-length encoding.

Anyway the \0 case is only really here for Rust.  So maybe another idea
is to handle it exactly there, somehow.  The Rust printer can assume the
use of UTF-32 on the target, so that would all work out fine.

Tom

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v2 09/18] Include \0 in printable wide characters
  2022-02-23 22:28     ` Tom Tromey
@ 2022-02-23 23:59       ` Tom Tromey
  0 siblings, 0 replies; 30+ messages in thread
From: Tom Tromey @ 2022-02-23 23:59 UTC (permalink / raw)
  To: Tom Tromey; +Cc: Andrew Burgess, gdb-patches

Tom> I think the idea behind this is that only a real \0 in the input will
Tom> really ever turn into a L'\0' in the wchar_t form.  It seems to me that
Tom> an L'\0' pretty much has to correspond exactly to a target \0, just
Tom> because C is pervasive and an encoding where stray \0 bytes can appear
Tom> would break everything.

I went for a short walk and, naturally, realized this is only half
right.

An L'\0' can come from a non-zero target encoding in the Java flavor of
UTF-8, which exists precisely to smuggle a wide '\0' through a
multi-byte encoding.

https://en.wikipedia.org/wiki/UTF-8#Modified_UTF-8

So while I still believe that a target '\0' will always map to a L'\0',
it's not the case that an L'\0' necessarily came from one such, with the
Java-style 0xc0 0x80 being a counter-example.

In this case it's not 100% clear what is the best thing to do.  Possibly
iconv will just give an encoding error, as that is an overlong sequence.
Anyway maybe the right thing to do is print \xc0\x80 or the like, to
make it clear that something unusual is going on.

Tom> That's most likely because you are trying this on Linux.  Linux uses
Tom> UTF-32 for wchar_t, and so there aren't target characters that can't be
Tom> converted to a single wchar_t

I now wonder if this is true as well, because you might see a "CESU-8"
encoding:

https://en.wikipedia.org/wiki/CESU-8

... where surrogate pairs are represented as two UTF-8 sequences.  This
could show up as a target program decision to use this encoding,
combined with using UTF-8 in gdb.  I didn't experiment to see what iconv
does for this sort of thing.

I'll look into a Rust-specific fix and just drop this patch.

Tom

^ permalink raw reply	[flat|nested] 30+ messages in thread

* [PATCH] Additional modula2 tests.
  2022-02-17 22:05 ` [PATCH v2 16/18] Use generic_emit_char in Modula-2 Tom Tromey
  2022-02-23 20:17   ` Gaius Mulley
@ 2022-03-16 12:29   ` Gaius Mulley
  2022-04-07 14:21     ` Tom Tromey
  2022-04-11 19:45   ` [PATCH v1] Array access in Modula-2 Gaius Mulley
  2 siblings, 1 reply; 30+ messages in thread
From: Gaius Mulley @ 2022-03-16 12:29 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

[-- Attachment #1: Type: text/plain, Size: 3588 bytes --]

Tom Tromey <tom@tromey.com> writes:

> This changes the Modula-2 code to use generic_emit_char and
> generic_printstr.  I have no way to test this.

Hi Tom and gdb developers,

here is a proposed patch for gdb and some new files which enable testing
of binaries generated from Modula-2 source files.  breakpointm2 and
printformatted are included as a start.  These patches and new files
have been tested against the gdb git repro of Tue Mar 15 08:53:27 2022
and no new failures occur after the patches have been applied and new
files added.  It was tested on a Debian Bullseye amd64 machine with
regular packages (gcc-10 and gm2-10 etc).


2022-03-16  Gaius Mulley  <gaiusmod2@gmail.com>

	Adding some modula-2 test cases and the tcl infastructure to
	support modula-2.

	* configure.ac (GM2_FOR_BUILD): Added.  (GM2_FOR_TARGET): Added.
	* gdb/testsuite/lib/gdb.exp (skip_modula2_tests): New function.
	* gdb/testsuite/lib/modula2.exp: New file.
	* gdb/testsuite/gdb.modula2/breakpointm2.mod: New file.
	* gdb/testsuite/gdb.modula2/breakpointm2.exp: New file.
	* gdb/testsuite/gdb.modula2/printformattedm2.exp: New file.
	* gdb/testsuite/gdb.modula2/printformattedm2.mod: New file.


diff --git a/configure.ac b/configure.ac
index da4e41d7247..351d3f9655b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1270,6 +1270,7 @@ if test "${build}" != "${host}" ; then
   CC_FOR_BUILD=${CC_FOR_BUILD-gcc}
   CXX_FOR_BUILD=${CXX_FOR_BUILD-g++}
   GFORTRAN_FOR_BUILD=${GFORTRAN_FOR_BUILD-gfortran}
+  GM2_FOR_BUILD=${GM2_FOR_BUILD-gccm2}
   GOC_FOR_BUILD=${GOC_FOR_BUILD-gccgo}
   DLLTOOL_FOR_BUILD=${DLLTOOL_FOR_BUILD-dlltool}
   LD_FOR_BUILD=${LD_FOR_BUILD-ld}
@@ -1283,6 +1284,7 @@ else
   CC_FOR_BUILD="\$(CC)"
   CXX_FOR_BUILD="\$(CXX)"
   GFORTRAN_FOR_BUILD="\$(GFORTRAN)"
+  GM2_FOR_BUILD="\$(GM2)"
   GOC_FOR_BUILD="\$(GOC)"
   DLLTOOL_FOR_BUILD="\$(DLLTOOL)"
   LD_FOR_BUILD="\$(LD)"
@@ -3374,6 +3376,7 @@ AC_SUBST(CXXFLAGS_FOR_BUILD)
 AC_SUBST(CXX_FOR_BUILD)
 AC_SUBST(DLLTOOL_FOR_BUILD)
 AC_SUBST(GFORTRAN_FOR_BUILD)
+AC_SUBST(GM2_FOR_BUILD)
 AC_SUBST(GOC_FOR_BUILD)
 AC_SUBST(LDFLAGS_FOR_BUILD)
 AC_SUBST(LD_FOR_BUILD)
@@ -3501,6 +3504,7 @@ NCN_STRICT_CHECK_TARGET_TOOLS(CC_FOR_TARGET, cc gcc)
 NCN_STRICT_CHECK_TARGET_TOOLS(CXX_FOR_TARGET, c++ g++ cxx gxx)
 NCN_STRICT_CHECK_TARGET_TOOLS(GCC_FOR_TARGET, gcc, ${CC_FOR_TARGET})
 NCN_STRICT_CHECK_TARGET_TOOLS(GFORTRAN_FOR_TARGET, gfortran)
+NCN_STRICT_CHECK_TARGET_TOOLS(GM2_FOR_TARGET, gccm2)
 NCN_STRICT_CHECK_TARGET_TOOLS(GOC_FOR_TARGET, gccgo)
 
 ACX_CHECK_INSTALLED_TARGET_TOOL(AR_FOR_TARGET, ar)
@@ -3533,6 +3537,8 @@ GCC_TARGET_TOOL(dlltool, DLLTOOL_FOR_TARGET, DLLTOOL, [binutils/dlltool])
 GCC_TARGET_TOOL(gcc, GCC_FOR_TARGET, , [gcc/xgcc -B$$r/$(HOST_SUBDIR)/gcc/])
 GCC_TARGET_TOOL(gfortran, GFORTRAN_FOR_TARGET, GFORTRAN,
 		[gcc/gfortran -B$$r/$(HOST_SUBDIR)/gcc/], fortran)
+GCC_TARGET_TOOL(gccm2, GM2_FOR_TARGET, GM2,
+		[gcc/gm2 -B$$r/$(HOST_SUBDIR)/gcc/], modula-2)
 GCC_TARGET_TOOL(gccgo, GOC_FOR_TARGET, GOC,
 		[gcc/gccgo -B$$r/$(HOST_SUBDIR)/gcc/], go)
 GCC_TARGET_TOOL(ld, LD_FOR_TARGET, LD, [ld/ld-new])
diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index a35d08a05de..e368a0dfccb 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -2295,6 +2295,12 @@ proc skip_d_tests {} {
     return 0
 }
 
+# Return a 1 if I don't even want to try to test Modula2.
+
+proc skip_modula2_tests {} {
+    return 0
+}
+
 # Return 1 to skip Rust tests, 0 to try them.
 proc skip_rust_tests {} {
     if { ![isnative] } {


new Modula-2 source tests and associated tcl driver scripts:


[-- Attachment #2: breakpointm2 and printformattedm2 --]
[-- Type: application/gzip, Size: 2785 bytes --]

[-- Attachment #3: Type: text/plain, Size: 16 bytes --]


regards,
Gaius

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH] Additional modula2 tests.
  2022-03-16 12:29   ` [PATCH] Additional modula2 tests Gaius Mulley
@ 2022-04-07 14:21     ` Tom Tromey
  2022-04-09 23:16       ` Gaius Mulley
  0 siblings, 1 reply; 30+ messages in thread
From: Tom Tromey @ 2022-04-07 14:21 UTC (permalink / raw)
  To: Gaius Mulley via Gdb-patches; +Cc: Tom Tromey, Gaius Mulley

Gaius> 2022-03-16  Gaius Mulley  <gaiusmod2@gmail.com>

Gaius> 	Adding some modula-2 test cases and the tcl infastructure to
Gaius> 	support modula-2.

Gaius> 	* configure.ac (GM2_FOR_BUILD): Added.  (GM2_FOR_TARGET): Added.
Gaius> 	* gdb/testsuite/lib/gdb.exp (skip_modula2_tests): New function.
Gaius> 	* gdb/testsuite/lib/modula2.exp: New file.
Gaius> 	* gdb/testsuite/gdb.modula2/breakpointm2.mod: New file.
Gaius> 	* gdb/testsuite/gdb.modula2/breakpointm2.exp: New file.
Gaius> 	* gdb/testsuite/gdb.modula2/printformattedm2.exp: New file.
Gaius> 	* gdb/testsuite/gdb.modula2/printformattedm2.mod: New file.

Top-level changes have to go via gcc-patches first.
This code is canonically maintained there.

gdb doesn't use a ChangeLog any more, so those entries can just be
dropped.

I didn't really look at the rest of the patch yet.

Tom

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH] Additional modula2 tests.
  2022-04-07 14:21     ` Tom Tromey
@ 2022-04-09 23:16       ` Gaius Mulley
  0 siblings, 0 replies; 30+ messages in thread
From: Gaius Mulley @ 2022-04-09 23:16 UTC (permalink / raw)
  To: Tom Tromey; +Cc: Gaius Mulley via Gdb-patches

Tom Tromey <tom@tromey.com> writes:

> Gaius> 2022-03-16  Gaius Mulley  <gaiusmod2@gmail.com>
>
> Gaius> 	Adding some modula-2 test cases and the tcl infastructure to
> Gaius> 	support modula-2.
>
> Gaius> 	* configure.ac (GM2_FOR_BUILD): Added.  (GM2_FOR_TARGET): Added.
> Gaius> 	* gdb/testsuite/lib/gdb.exp (skip_modula2_tests): New function.
> Gaius> 	* gdb/testsuite/lib/modula2.exp: New file.
> Gaius> 	* gdb/testsuite/gdb.modula2/breakpointm2.mod: New file.
> Gaius> 	* gdb/testsuite/gdb.modula2/breakpointm2.exp: New file.
> Gaius> 	* gdb/testsuite/gdb.modula2/printformattedm2.exp: New file.
> Gaius> 	* gdb/testsuite/gdb.modula2/printformattedm2.mod: New file.
>
> Top-level changes have to go via gcc-patches first.
> This code is canonically maintained there.
>
> gdb doesn't use a ChangeLog any more, so those entries can just be
> dropped.
>
> I didn't really look at the rest of the patch yet.

Thanks for the feedback - will repost the top level on the gcc-patches
mailing list.

I've been working on further improvements to the Modula-2 mode within
gdb which I'll post in the next day or so.  Static array and unbounded
array data types can be ptyped/HIGH/SIZE/TSIZE showing their dimensions.
Multidimensional unbounded arrays can be accessed cleanly

regards,
Gaius

^ permalink raw reply	[flat|nested] 30+ messages in thread

* [PATCH v1] Array access in Modula-2
  2022-02-17 22:05 ` [PATCH v2 16/18] Use generic_emit_char in Modula-2 Tom Tromey
  2022-02-23 20:17   ` Gaius Mulley
  2022-03-16 12:29   ` [PATCH] Additional modula2 tests Gaius Mulley
@ 2022-04-11 19:45   ` Gaius Mulley
  2 siblings, 0 replies; 30+ messages in thread
From: Gaius Mulley @ 2022-04-11 19:45 UTC (permalink / raw)
  To: gdb-patches

[-- Attachment #1: Type: text/plain, Size: 2657 bytes --]



Hi,

here are some patches to enhance the gdb Modula-2 language mode.  In
summary arrays (both static and unbounded) are now handled correctly
within expressions and data type description.  Unbounded arrays also
have their dynamic bounds displayed and HIGH and SIZE also understand
unbounded arrays.

I've updated the gdb.texinfo to reflect the changes and also include a
number of dejagnu test cases which minic two of the documentation
examples and 11 other test cases.  I've tested gdb and the changes
introduce no further failures (and it implements a commented out test
in the repro (gdb.modula2/unbounded-array.exp ptype).

Here is an example of usage - taken from the new documentation:

The following example shows a static @code{ARRAY} being passed into a
procedure as an unbounded @code{ARRAY}.  The @code{ptype} command on
the unbounded @code{ARRAY} reveals its dynamic bounds.  @value{GDBN}
can be requested to @code{print} the entire @code{ARRAY} or rows or
elements of the dynamic array.

@smallexample
TYPE
   pos = RECORD
            x, y: CARDINAL ;
         END ;

PROCEDURE foo (a: ARRAY OF ARRAY OF pos) ;
BEGIN

END foo ;

VAR
   b: ARRAY [1..2] OF ARRAY [2..3] OF pos ;
BEGIN
   b[1][2].x := 4 ;
   b[1][2].y := 2 ;
   foo (b)
@end smallexample

@smallexample
(@value{GDBP}) break foo
(@value{GDBP}) run
(@value{GDBP}) set lang modula-2
(@value{GDBP}) ptype b
type = ARRAY [1..2] OF ARRAY [2..3] OF RECORD
    x : CARDINAL;
    y : CARDINAL;
END
(@value{GDBP}) ptype a
type = ARRAY <0..1> OF ARRAY <0..1> OF RECORD
    x : CARDINAL;
    y : CARDINAL;
END
(@value{GDBP}) print a[0]
$1 = @{@{x = 4, y = 2@}, @{x = 0, y = 0@}, @}
(@value{GDBP}) print a[0][0]
$2 = @{x = 4, y = 2@}
(@value{GDBP}) print a[0][0].x
$3 = 4
(@value{GDBP}) print HIGH(a)
$4 = 1
(@value{GDBP}) print HIGH(a[0])
$5 = 1
(@value{GDBP}) print SIZE(a)
$6 = 32
(@value{GDBP}) print SIZE(a[0])
$7 = 16
@end smallexample

The instrinsic procedure functions @code{SIZE} and @code{HIGH} will
return the last index into the @code{ARRAY} and the number of bytes of
data used by the @code{ARRAY} respectively.  The range of an unbounded
@code{ARRAY} is shown inside @code{<} and @code{>} to differentiate
from static array bounds @code{[} and @code{]}.

Hope these patches are useful and perhaps they might be applied when
at a convenient point in the correct development phase.  I acknowledge
that the top level diffs need to go to the gcc mailing list - and will
post them there - I thought it useful to post the complete set here as
well

regards,
Gaius

----------------------------------------------------------------------


here is a tar archive with the new files


[-- Attachment #2: archive of new test programs and tcl scripts --]
[-- Type: application/gzip, Size: 5202 bytes --]

[-- Attachment #3: Type: text/plain, Size: 128984 bytes --]


and below are the diffs

----------------------------------------------------------------------


diff --git a/configure b/configure
index 26935ebda24..3d9d4542462 100755
--- a/configure
+++ b/configure
@@ -612,6 +612,7 @@ DLLTOOL_FOR_TARGET
 AS_FOR_TARGET
 AR_FOR_TARGET
 GOC_FOR_TARGET
+GM2_FOR_TARGET
 GFORTRAN_FOR_TARGET
 GCC_FOR_TARGET
 CXX_FOR_TARGET
@@ -646,6 +647,7 @@ NM_FOR_BUILD
 LD_FOR_BUILD
 LDFLAGS_FOR_BUILD
 GOC_FOR_BUILD
+GM2_FOR_BUILD
 GFORTRAN_FOR_BUILD
 DLLTOOL_FOR_BUILD
 CXX_FOR_BUILD
@@ -755,6 +757,7 @@ infodir
 docdir
 oldincludedir
 includedir
+runstatedir
 localstatedir
 sharedstatedir
 sysconfdir
@@ -868,6 +871,7 @@ CC_FOR_TARGET
 CXX_FOR_TARGET
 GCC_FOR_TARGET
 GFORTRAN_FOR_TARGET
+GM2_FOR_TARGET
 GOC_FOR_TARGET
 AR_FOR_TARGET
 AS_FOR_TARGET
@@ -920,6 +924,7 @@ datadir='${datarootdir}'
 sysconfdir='${prefix}/etc'
 sharedstatedir='${prefix}/com'
 localstatedir='${prefix}/var'
+runstatedir='${localstatedir}/run'
 includedir='${prefix}/include'
 oldincludedir='/usr/include'
 docdir='${datarootdir}/doc/${PACKAGE}'
@@ -1172,6 +1177,15 @@ do
   | -silent | --silent | --silen | --sile | --sil)
     silent=yes ;;

+  -runstatedir | --runstatedir | --runstatedi | --runstated \
+  | --runstate | --runstat | --runsta | --runst | --runs \
+  | --run | --ru | --r)
+    ac_prev=runstatedir ;;
+  -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
+  | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
+  | --run=* | --ru=* | --r=*)
+    runstatedir=$ac_optarg ;;
+
   -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
     ac_prev=sbindir ;;
   -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
@@ -1309,7 +1323,7 @@ fi
 for ac_var in	exec_prefix prefix bindir sbindir libexecdir datarootdir \
 		datadir sysconfdir sharedstatedir localstatedir includedir \
 		oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
-		libdir localedir mandir
+		libdir localedir mandir runstatedir
 do
   eval ac_val=\$$ac_var
   # Remove trailing slashes.
@@ -1469,6 +1483,7 @@ Fine tuning of the installation directories:
   --sysconfdir=DIR        read-only single-machine data [PREFIX/etc]
   --sharedstatedir=DIR    modifiable architecture-independent data [PREFIX/com]
   --localstatedir=DIR     modifiable single-machine data [PREFIX/var]
+  --runstatedir=DIR       modifiable per-process data [LOCALSTATEDIR/run]
   --libdir=DIR            object code libraries [EPREFIX/lib]
   --includedir=DIR        C header files [PREFIX/include]
   --oldincludedir=DIR     C header files for non-gcc [/usr/include]
@@ -1655,6 +1670,8 @@ Some influential environment variables:
               GCC for the target
   GFORTRAN_FOR_TARGET
               GFORTRAN for the target
+  GM2_FOR_TARGET
+              GM2 for the target
   GOC_FOR_TARGET
               GOC for the target
   AR_FOR_TARGET
@@ -4010,6 +4027,7 @@ if test "${build}" != "${host}" ; then
   CC_FOR_BUILD=${CC_FOR_BUILD-gcc}
   CXX_FOR_BUILD=${CXX_FOR_BUILD-g++}
   GFORTRAN_FOR_BUILD=${GFORTRAN_FOR_BUILD-gfortran}
+  GM2_FOR_BUILD=${GM2_FOR_BUILD-gccm2}
   GOC_FOR_BUILD=${GOC_FOR_BUILD-gccgo}
   DLLTOOL_FOR_BUILD=${DLLTOOL_FOR_BUILD-dlltool}
   LD_FOR_BUILD=${LD_FOR_BUILD-ld}
@@ -4023,6 +4041,7 @@ else
   CC_FOR_BUILD="\$(CC)"
   CXX_FOR_BUILD="\$(CXX)"
   GFORTRAN_FOR_BUILD="\$(GFORTRAN)"
+  GM2_FOR_BUILD="\$(GM2)"
   GOC_FOR_BUILD="\$(GOC)"
   DLLTOOL_FOR_BUILD="\$(DLLTOOL)"
   LD_FOR_BUILD="\$(LD)"
@@ -8075,6 +8094,7 @@ done



+
 # Generate default definitions for YACC, M4, LEX and other programs that run
 # on the build machine.  These are used if the Makefile can't locate these
 # programs in objdir.
@@ -11112,6 +11132,167 @@ fi



+if test -n "$GM2_FOR_TARGET"; then
+  ac_cv_prog_GM2_FOR_TARGET=$GM2_FOR_TARGET
+elif test -n "$ac_cv_prog_GM2_FOR_TARGET"; then
+  GM2_FOR_TARGET=$ac_cv_prog_GM2_FOR_TARGET
+fi
+
+if test -n "$ac_cv_prog_GM2_FOR_TARGET"; then
+  for ncn_progname in gccm2; do
+    # Extract the first word of "${ncn_progname}", so it can be a program name with args.
+set dummy ${ncn_progname}; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_GM2_FOR_TARGET+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$GM2_FOR_TARGET"; then
+  ac_cv_prog_GM2_FOR_TARGET="$GM2_FOR_TARGET" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_GM2_FOR_TARGET="${ncn_progname}"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+GM2_FOR_TARGET=$ac_cv_prog_GM2_FOR_TARGET
+if test -n "$GM2_FOR_TARGET"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $GM2_FOR_TARGET" >&5
+$as_echo "$GM2_FOR_TARGET" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  done
+fi
+
+if test -z "$ac_cv_prog_GM2_FOR_TARGET" && test -n "$with_build_time_tools"; then
+  for ncn_progname in gccm2; do
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${ncn_progname} in $with_build_time_tools" >&5
+$as_echo_n "checking for ${ncn_progname} in $with_build_time_tools... " >&6; }
+    if test -x $with_build_time_tools/${ncn_progname}; then
+      ac_cv_prog_GM2_FOR_TARGET=$with_build_time_tools/${ncn_progname}
+      { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+      break
+    else
+      { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+    fi
+  done
+fi
+
+if test -z "$ac_cv_prog_GM2_FOR_TARGET"; then
+  for ncn_progname in gccm2; do
+    if test -n "$ncn_target_tool_prefix"; then
+      # Extract the first word of "${ncn_target_tool_prefix}${ncn_progname}", so it can be a program name with args.
+set dummy ${ncn_target_tool_prefix}${ncn_progname}; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_GM2_FOR_TARGET+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$GM2_FOR_TARGET"; then
+  ac_cv_prog_GM2_FOR_TARGET="$GM2_FOR_TARGET" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_GM2_FOR_TARGET="${ncn_target_tool_prefix}${ncn_progname}"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+GM2_FOR_TARGET=$ac_cv_prog_GM2_FOR_TARGET
+if test -n "$GM2_FOR_TARGET"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $GM2_FOR_TARGET" >&5
+$as_echo "$GM2_FOR_TARGET" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+    fi
+    if test -z "$ac_cv_prog_GM2_FOR_TARGET" && test $build = $target ; then
+      # Extract the first word of "${ncn_progname}", so it can be a program name with args.
+set dummy ${ncn_progname}; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_GM2_FOR_TARGET+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$GM2_FOR_TARGET"; then
+  ac_cv_prog_GM2_FOR_TARGET="$GM2_FOR_TARGET" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_GM2_FOR_TARGET="${ncn_progname}"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+GM2_FOR_TARGET=$ac_cv_prog_GM2_FOR_TARGET
+if test -n "$GM2_FOR_TARGET"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $GM2_FOR_TARGET" >&5
+$as_echo "$GM2_FOR_TARGET" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+    fi
+    test -n "$ac_cv_prog_GM2_FOR_TARGET" && break
+  done
+fi
+
+if test -z "$ac_cv_prog_GM2_FOR_TARGET" ; then
+  set dummy gccm2
+  if test $build = $target ; then
+    GM2_FOR_TARGET="$2"
+  else
+    GM2_FOR_TARGET="${ncn_target_tool_prefix}$2"
+  fi
+else
+  GM2_FOR_TARGET="$ac_cv_prog_GM2_FOR_TARGET"
+fi
+
+
+
 if test -n "$GOC_FOR_TARGET"; then
   ac_cv_prog_GOC_FOR_TARGET=$GOC_FOR_TARGET
 elif test -n "$ac_cv_prog_GOC_FOR_TARGET"; then
@@ -14624,6 +14805,51 @@ $as_echo "pre-installed" >&6; }
   fi
 fi

+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking where to find the target gccm2" >&5
+$as_echo_n "checking where to find the target gccm2... " >&6; }
+if test "x${build}" != "x${host}" ; then
+  if expr "x$GM2_FOR_TARGET" : "x/" > /dev/null; then
+    # We already found the complete path
+    ac_dir=`dirname $GM2_FOR_TARGET`
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: pre-installed in $ac_dir" >&5
+$as_echo "pre-installed in $ac_dir" >&6; }
+  else
+    # Canadian cross, just use what we found
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: pre-installed" >&5
+$as_echo "pre-installed" >&6; }
+  fi
+else
+  ok=yes
+  case " ${configdirs} " in
+    *" gcc "*) ;;
+    *) ok=no ;;
+  esac
+  case ,${enable_languages}, in
+    *,modula-2,*) ;;
+    *) ok=no ;;
+  esac
+  if test $ok = yes; then
+    # An in-tree tool is available and we can use it
+    GM2_FOR_TARGET='$$r/$(HOST_SUBDIR)/gcc/gm2 -B$$r/$(HOST_SUBDIR)/gcc/'
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: just compiled" >&5
+$as_echo "just compiled" >&6; }
+  elif expr "x$GM2_FOR_TARGET" : "x/" > /dev/null; then
+    # We already found the complete path
+    ac_dir=`dirname $GM2_FOR_TARGET`
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: pre-installed in $ac_dir" >&5
+$as_echo "pre-installed in $ac_dir" >&6; }
+  elif test "x$target" = "x$host"; then
+    # We can use an host tool
+    GM2_FOR_TARGET='$(GM2)'
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: host tool" >&5
+$as_echo "host tool" >&6; }
+  else
+    # We need a cross tool
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: pre-installed" >&5
+$as_echo "pre-installed" >&6; }
+  fi
+fi
+
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking where to find the target gccgo" >&5
 $as_echo_n "checking where to find the target gccgo... " >&6; }
 if test "x${build}" != "x${host}" ; then
diff --git a/configure.ac b/configure.ac
index da4e41d7247..351d3f9655b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1270,6 +1270,7 @@ if test "${build}" != "${host}" ; then
   CC_FOR_BUILD=${CC_FOR_BUILD-gcc}
   CXX_FOR_BUILD=${CXX_FOR_BUILD-g++}
   GFORTRAN_FOR_BUILD=${GFORTRAN_FOR_BUILD-gfortran}
+  GM2_FOR_BUILD=${GM2_FOR_BUILD-gccm2}
   GOC_FOR_BUILD=${GOC_FOR_BUILD-gccgo}
   DLLTOOL_FOR_BUILD=${DLLTOOL_FOR_BUILD-dlltool}
   LD_FOR_BUILD=${LD_FOR_BUILD-ld}
@@ -1283,6 +1284,7 @@ else
   CC_FOR_BUILD="\$(CC)"
   CXX_FOR_BUILD="\$(CXX)"
   GFORTRAN_FOR_BUILD="\$(GFORTRAN)"
+  GM2_FOR_BUILD="\$(GM2)"
   GOC_FOR_BUILD="\$(GOC)"
   DLLTOOL_FOR_BUILD="\$(DLLTOOL)"
   LD_FOR_BUILD="\$(LD)"
@@ -3374,6 +3376,7 @@ AC_SUBST(CXXFLAGS_FOR_BUILD)
 AC_SUBST(CXX_FOR_BUILD)
 AC_SUBST(DLLTOOL_FOR_BUILD)
 AC_SUBST(GFORTRAN_FOR_BUILD)
+AC_SUBST(GM2_FOR_BUILD)
 AC_SUBST(GOC_FOR_BUILD)
 AC_SUBST(LDFLAGS_FOR_BUILD)
 AC_SUBST(LD_FOR_BUILD)
@@ -3501,6 +3504,7 @@ NCN_STRICT_CHECK_TARGET_TOOLS(CC_FOR_TARGET, cc gcc)
 NCN_STRICT_CHECK_TARGET_TOOLS(CXX_FOR_TARGET, c++ g++ cxx gxx)
 NCN_STRICT_CHECK_TARGET_TOOLS(GCC_FOR_TARGET, gcc, ${CC_FOR_TARGET})
 NCN_STRICT_CHECK_TARGET_TOOLS(GFORTRAN_FOR_TARGET, gfortran)
+NCN_STRICT_CHECK_TARGET_TOOLS(GM2_FOR_TARGET, gccm2)
 NCN_STRICT_CHECK_TARGET_TOOLS(GOC_FOR_TARGET, gccgo)

 ACX_CHECK_INSTALLED_TARGET_TOOL(AR_FOR_TARGET, ar)
@@ -3533,6 +3537,8 @@ GCC_TARGET_TOOL(dlltool, DLLTOOL_FOR_TARGET, DLLTOOL, [binutils/dlltool])
 GCC_TARGET_TOOL(gcc, GCC_FOR_TARGET, , [gcc/xgcc -B$$r/$(HOST_SUBDIR)/gcc/])
 GCC_TARGET_TOOL(gfortran, GFORTRAN_FOR_TARGET, GFORTRAN,
 		[gcc/gfortran -B$$r/$(HOST_SUBDIR)/gcc/], fortran)
+GCC_TARGET_TOOL(gccm2, GM2_FOR_TARGET, GM2,
+		[gcc/gm2 -B$$r/$(HOST_SUBDIR)/gcc/], modula-2)
 GCC_TARGET_TOOL(gccgo, GOC_FOR_TARGET, GOC,
 		[gcc/gccgo -B$$r/$(HOST_SUBDIR)/gcc/], go)
 GCC_TARGET_TOOL(ld, LD_FOR_TARGET, LD, [ld/ld-new])
diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c
index d819038e63b..0298654b88f 100644
--- a/gdb/ada-lang.c
+++ b/gdb/ada-lang.c
@@ -13582,7 +13582,8 @@ class ada_language : public language_defn

   void print_type (struct type *type, const char *varstring,
 		   struct ui_file *stream, int show, int level,
-		   const struct type_print_options *flags) const override
+		   const struct type_print_options *flags,
+		   struct value *val) const override
   {
     ada_print_type (type, varstring, stream, show, level, flags);
   }
diff --git a/gdb/c-lang.c b/gdb/c-lang.c
index 014dbefb8e2..f980d0cb793 100644
--- a/gdb/c-lang.c
+++ b/gdb/c-lang.c
@@ -818,7 +818,8 @@ class c_language : public language_defn

   void print_type (struct type *type, const char *varstring,
 		   struct ui_file *stream, int show, int level,
-		   const struct type_print_options *flags) const override
+		   const struct type_print_options *flags,
+		   struct value *val = NULL) const override
   {
     c_print_type (type, varstring, stream, show, level, flags);
   }
@@ -964,7 +965,8 @@ class cplus_language : public language_defn

   void print_type (struct type *type, const char *varstring,
 		   struct ui_file *stream, int show, int level,
-		   const struct type_print_options *flags) const override
+		   const struct type_print_options *flags,
+		   struct value *val = NULL) const override
   {
     c_print_type (type, varstring, stream, show, level, flags);
   }
@@ -1064,7 +1066,8 @@ class asm_language : public language_defn

   void print_type (struct type *type, const char *varstring,
 		   struct ui_file *stream, int show, int level,
-		   const struct type_print_options *flags) const override
+		   const struct type_print_options *flags,
+		   struct value *val = NULL) const override
   {
     c_print_type (type, varstring, stream, show, level, flags);
   }
@@ -1116,7 +1119,8 @@ class minimal_language : public language_defn

   void print_type (struct type *type, const char *varstring,
 		   struct ui_file *stream, int show, int level,
-		   const struct type_print_options *flags) const override
+		   const struct type_print_options *flags,
+		   struct value *val = NULL) const override
   {
     c_print_type (type, varstring, stream, show, level, flags);
   }
diff --git a/gdb/d-lang.c b/gdb/d-lang.c
index ec4a80a3223..25d1b97ba51 100644
--- a/gdb/d-lang.c
+++ b/gdb/d-lang.c
@@ -146,7 +146,8 @@ class d_language : public language_defn

   void print_type (struct type *type, const char *varstring,
 		   struct ui_file *stream, int show, int level,
-		   const struct type_print_options *flags) const override
+		   const struct type_print_options *flags,
+		   struct value *val) const override
   {
     c_print_type (type, varstring, stream, show, level, flags);
   }
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index e4685cd995b..99279383f22 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -3494,7 +3494,7 @@ programs:
 @item @samp{thread apply [@var{thread-id-list} | all] @var{args}},
 a command to apply a command to a list of threads
 @item thread-specific breakpoints
-@item @samp{set print thread-events}, which controls printing of
+@item @samp{set print thread-events}, which controls printing of
 messages on thread start and exit.
 @item @samp{set libthread-db-search-path @var{path}}, which lets
 the user specify which @code{libthread_db} to use if the default choice
@@ -3817,8 +3817,8 @@ system-give name, and removing the user-specified name will cause
 Search for and display thread ids whose name or @var{systag}
 matches the supplied regular expression.

-As well as being the complement to the @samp{thread name} command,
-this command also allows you to identify a thread by its target
+As well as being the complement to the @samp{thread name} command,
+this command also allows you to identify a thread by its target
 @var{systag}.  For instance, on @sc{gnu}/Linux, the target @var{systag}
 is the LWP id.

@@ -3826,7 +3826,7 @@ is the LWP id.
 (@value{GDBN}) thread find 26688
 Thread 4 has target id 'Thread 0x41e02940 (LWP 26688)'
 (@value{GDBN}) info thread 4
-  Id   Target Id         Frame
+  Id   Target Id         Frame
   4    Thread 0x41e02940 (LWP 26688) 0x00000031ca6cd372 in select ()
 @end smallexample

@@ -3894,8 +3894,8 @@ If none of @code{libthread_db} libraries initialize successfully,
 Setting @code{libthread-db-search-path} is currently implemented
 only on some platforms.

-@kindex show libthread-db-search-path
-@item show libthread-db-search-path
+@kindex show libthread-db-search-path
+@item show libthread-db-search-path
 Display current libthread_db search path.

 @kindex set debug libthread-db
@@ -3991,14 +3991,14 @@ retain debugger control over them both.
 @table @code
 @item on
 The child process (or parent process, depending on the value of
-@code{follow-fork-mode}) will be detached and allowed to run
+@code{follow-fork-mode}) will be detached and allowed to run
 independently.  This is the default.

 @item off
 Both processes will be held under the control of @value{GDBN}.
-One process (child or parent, depending on the value of
+One process (child or parent, depending on the value of
 @code{follow-fork-mode}) is debugged as usual, while the other
-is held suspended.
+is held suspended.

 @end table

@@ -4120,15 +4120,15 @@ includes changes in memory, registers, and even (within some limits)
 system state.  Effectively, it is like going back in time to the
 moment when the checkpoint was saved.

-Thus, if you're stepping thru a program and you think you're
+Thus, if you're stepping thru a program and you think you're
 getting close to the point where things go wrong, you can save
 a checkpoint.  Then, if you accidentally go too far and miss
 the critical statement, instead of having to restart your program
 from the beginning, you can just go back to the checkpoint and
 start again from there.

-This can be especially useful if it takes a lot of time or
-steps to reach the point where you think the bug occurs.
+This can be especially useful if it takes a lot of time or
+steps to reach the point where you think the bug occurs.

 To use the @code{checkpoint}/@code{restart} method of debugging:

@@ -4194,7 +4194,7 @@ different execution path this time.
 @cindex checkpoints and process id
 Finally, there is one bit of internal program state that will be
 different when you return to a checkpoint --- the program's process
-id.  Each checkpoint will have a unique process id (or @var{pid}),
+id.  Each checkpoint will have a unique process id (or @var{pid}),
 and each will be different from the program's original @var{pid}.
 If your program has saved a local copy of its process id, this could
 potentially pose a problem.
@@ -4202,15 +4202,15 @@ potentially pose a problem.
 @subsection A Non-obvious Benefit of Using Checkpoints

 On some systems such as @sc{gnu}/Linux, address space randomization
-is performed on new processes for security reasons.  This makes it
+is performed on new processes for security reasons.  This makes it
 difficult or impossible to set a breakpoint, or watchpoint, on an
-absolute address if you have to restart the program, since the
+absolute address if you have to restart the program, since the
 absolute location of a symbol will change from one execution to the
 next.

-A checkpoint, however, is an @emph{identical} copy of a process.
-Therefore if you create a checkpoint at (eg.@:) the start of main,
-and simply return to that checkpoint instead of restarting the
+A checkpoint, however, is an @emph{identical} copy of a process.
+Therefore if you create a checkpoint at (eg.@:) the start of main,
+and simply return to that checkpoint instead of restarting the
 process, you can avoid the effects of address randomization and
 your symbols will all stay in the same place.

@@ -5931,7 +5931,7 @@ for more information on how to add @code{SystemTap} @acronym{SDT}
 probes in your applications.}.  @code{SystemTap} probes are usable
 from assembly, C and C@t{++} languages@footnote{See
 @uref{http://sourceware.org/systemtap/wiki/UserSpaceProbeImplementation}
-for a good reference on how the @acronym{SDT} probes are implemented.}.
+for a good reference on how the @acronym{SDT} probes are implemented.}.

 @item @code{DTrace} (@uref{http://oss.oracle.com/projects/DTrace})
 @acronym{USDT} probes.  @code{DTrace} probes are usable from C and
@@ -6841,9 +6841,9 @@ Bounds: [lower = 0x7fffffffc390, upper = 0x7fffffffc3a3]
 (@pxref{Threads,, Debugging Programs with Multiple Threads}).  There
 are two modes of controlling execution of your program within the
 debugger.  In the default mode, referred to as @dfn{all-stop mode},
-when any thread in your program stops (for example, at a breakpoint
-or while being stepped), all other threads in the program are also stopped by
-@value{GDBN}.  On some targets, @value{GDBN} also supports
+when any thread in your program stops (for example, at a breakpoint
+or while being stepped), all other threads in the program are also stopped by
+@value{GDBN}.  On some targets, @value{GDBN} also supports
 @dfn{non-stop mode}, in which other threads can continue to run freely while
 you examine the stopped thread in the debugger.

@@ -6891,7 +6891,7 @@ Whenever @value{GDBN} stops your program, due to a breakpoint or a
 signal, it automatically selects the thread where that breakpoint or
 signal happened.  @value{GDBN} alerts you to the context switch with a
 message such as @samp{[Switching to Thread @var{n}]} to identify the
-thread.
+thread.

 On some OSes, you can modify @value{GDBN}'s default behavior by
 locking the OS scheduler to allow only a single thread to run.
@@ -7173,7 +7173,7 @@ explictly asks for the thread list with the @code{info threads}
 command.

 @node Interrupted System Calls
-@subsection Interrupted System Calls
+@subsection Interrupted System Calls

 @cindex thread breakpoints and system calls
 @cindex system calls and thread breakpoints
@@ -7347,7 +7347,7 @@ targets may be able undo things like device I/O, and some may not.

 The contract between @value{GDBN} and the reverse executing target
 requires only that the target do something reasonable when
-@value{GDBN} tells it to execute backwards, and then report the
+@value{GDBN} tells it to execute backwards, and then report the
 results back to @value{GDBN}.  Whatever the target reports back to
 @value{GDBN}, @value{GDBN} will report back to the user.  @value{GDBN}
 assumes that the memory and registers that the target reports are in a
@@ -7406,7 +7406,7 @@ the current (innermost) stack frame.  If the line contains function
 calls, they will be ``un-executed'' without stopping.  Starting from
 the first line of a function, @code{reverse-next} will take you back
 to the caller of that function, @emph{before} the function was called,
-just as the normal @code{next} command would take you from the last
+just as the normal @code{next} command would take you from the last
 line of a function back to its return to its caller
 @footnote{Unless the code is too heavily optimized.}.

@@ -10212,7 +10212,7 @@ Enter the index of the element you want to explore in `cs.arr': 5

 (cs.arr)[5] = 4

-Press enter to return to parent value:
+Press enter to return to parent value:
 @end smallexample

 In general, at any stage of exploration, you can go deeper towards the
@@ -12002,7 +12002,7 @@ with the corresponding objfile (e.g., shared library).
 @xref{Objfiles In Python}, for more details on objfiles in Python.
 @end itemize

-@xref{Selecting Pretty-Printers}, for further information on how
+@xref{Selecting Pretty-Printers}, for further information on how
 pretty-printers are selected,

 @xref{Writing a Pretty-Printer}, for implementing pretty printers
@@ -12016,7 +12016,7 @@ Here is how a C@t{++} @code{std::string} looks without a pretty-printer:
 @smallexample
 (@value{GDBP}) print s
 $1 = @{
-  static npos = 4294967295,
+  static npos = 4294967295,
   _M_dataplus = @{
     <std::allocator<char>> = @{
       <__gnu_cxx::new_allocator<char>> = @{
@@ -14984,7 +14984,7 @@ Num     Type           Disp Enb Address    What
         end
         collect globfoo2
         end
-        pass count 1200
+        pass count 1200
 2       tracepoint     keep y   <MULTIPLE>
         collect $eip
 2.1                         y     0x0804859c in func4 at change-loc.h:35
@@ -17622,7 +17622,9 @@ ordered type, which include integral, character and enumerated types.

 @item SIZE(@var{x})
 Returns the size of its argument.  The argument @var{x} can be a
-variable or a type.
+variable or a type.  If the argument is an unbounded @code{ARRAY} then
+the number of data bytes used by the array is returned rather than the
+size of the @code{ARRAY} descriptor.

 @item TRUNC(@var{r})
 Returns the integral part of @var{r}.
@@ -17756,13 +17758,9 @@ VAR
 @smallexample
 (@value{GDBP}) ptype s
 ARRAY [-10..10] OF CHAR
+(@value{GDBP}) print s[-10]
 @end smallexample

-Note that the array handling is not yet complete and although the type
-is printed correctly, expression handling still assumes that all
-arrays have a lower bound of zero and not @code{-10} as in the example
-above.
-
 Here are some more type related Modula-2 examples:

 @smallexample
@@ -17852,9 +17850,70 @@ type = POINTER TO ARRAY [-2..2] OF foo = RECORD
     f1 : CARDINAL;
     f2 : CHAR;
     f3 : ARRAY [-2..2] OF CARDINAL;
-END
+END
+@end smallexample
+
+The following example shows a static @code{ARRAY} being passed into a
+procedure as an unbounded @code{ARRAY}.  The @code{ptype} command on
+the unbounded @code{ARRAY} reveals its dynamic bounds.  @value{GDBN}
+can be requested to @code{print} the entire @code{ARRAY} or rows or
+elements of the dynamic array.
+
+@smallexample
+TYPE
+   pos = RECORD
+            x, y: CARDINAL ;
+         END ;
+
+PROCEDURE foo (a: ARRAY OF ARRAY OF pos) ;
+BEGIN
+
+END foo ;
+
+VAR
+   b: ARRAY [1..2] OF ARRAY [2..3] OF pos ;
+BEGIN
+   b[1][2].x := 4 ;
+   b[1][2].y := 2 ;
+   foo (b)
 @end smallexample

+@smallexample
+(@value{GDBP}) break foo
+(@value{GDBP}) run
+(@value{GDBP}) set lang modula-2
+(@value{GDBP}) ptype b
+type = ARRAY [1..2] OF ARRAY [2..3] OF RECORD
+    x : CARDINAL;
+    y : CARDINAL;
+END
+(@value{GDBP}) ptype a
+type = ARRAY <0..1> OF ARRAY <0..1> OF RECORD
+    x : CARDINAL;
+    y : CARDINAL;
+END
+(@value{GDBP}) print a[0]
+$1 = @{@{x = 4, y = 2@}, @{x = 0, y = 0@}, @}
+(@value{GDBP}) print a[0][0]
+$2 = @{x = 4, y = 2@}
+(@value{GDBP}) print a[0][0].x
+$3 = 4
+(@value{GDBP}) print HIGH(a)
+$4 = 1
+(@value{GDBP}) print HIGH(a[0])
+$5 = 1
+(@value{GDBP}) print SIZE(a)
+$6 = 32
+(@value{GDBP}) print SIZE(a[0])
+$7 = 16
+@end smallexample
+
+The instrinsic procedure functions @code{SIZE} and @code{HIGH} will
+return the last index into the @code{ARRAY} and the number of bytes of
+data used by the @code{ARRAY} respectively.  The range of an unbounded
+@code{ARRAY} is shown inside @code{<} and @code{>} to differentiate
+from static array bounds @code{[} and @code{]}.
+
 @node M2 Defaults
 @subsubsection Modula-2 Defaults
 @cindex Modula-2 defaults
@@ -18001,8 +18060,8 @@ to be difficult.

 @cindex expressions in Ada
 @menu
-* Ada Mode Intro::              General remarks on the Ada syntax
-                                   and semantics supported by Ada mode
+* Ada Mode Intro::              General remarks on the Ada syntax
+                                   and semantics supported by Ada mode
                                    in @value{GDBN}.
 * Omissions from Ada::          Restrictions on the Ada expression syntax.
 * Additions to Ada::            Extensions of the Ada expression syntax.
@@ -18023,22 +18082,22 @@ to be difficult.
 @subsubsection Introduction
 @cindex Ada mode, general

-The Ada mode of @value{GDBN} supports a fairly large subset of Ada expression
+The Ada mode of @value{GDBN} supports a fairly large subset of Ada expression
 syntax, with some extensions.
-The philosophy behind the design of this subset is
+The philosophy behind the design of this subset is

 @itemize @bullet
 @item
-That @value{GDBN} should provide basic literals and access to operations for
-arithmetic, dereferencing, field selection, indexing, and subprogram calls,
+That @value{GDBN} should provide basic literals and access to operations for
+arithmetic, dereferencing, field selection, indexing, and subprogram calls,
 leaving more sophisticated computations to subprograms written into the
 program (which therefore may be called from @value{GDBN}).

-@item
+@item
 That type safety and strict adherence to Ada language restrictions
 are not particularly important to the @value{GDBN} user.

-@item
+@item
 That brevity is important to the @value{GDBN} user.
 @end itemize

@@ -18048,13 +18107,13 @@ according to Ada rules, thus making it unnecessary to fully qualify most
 names with their packages, regardless of context.  Where this causes
 ambiguity, @value{GDBN} asks the user's intent.

-The debugger will start in Ada mode if it detects an Ada main program.
+The debugger will start in Ada mode if it detects an Ada main program.
 As for other languages, it will enter Ada mode when stopped in a program that
 was translated from an Ada source file.

-While in Ada mode, you may use `@t{--}' for comments.  This is useful
-mostly for documenting command files.  The standard @value{GDBN} comment
-(@samp{#}) still works at the beginning of a line in Ada mode, but not in the
+While in Ada mode, you may use `@t{--}' for comments.  This is useful
+mostly for documenting command files.  The standard @value{GDBN} comment
+(@samp{#}) still works at the beginning of a line in Ada mode, but not in the
 middle (to allow based literals).

 @node Omissions from Ada
@@ -18073,10 +18132,10 @@ Only a subset of the attributes are supported:
  on array objects (not on types and subtypes).

 @item
-@t{'Min} and @t{'Max}.
+@t{'Min} and @t{'Max}.

-@item
-@t{'Pos} and @t{'Val}.
+@item
+@t{'Pos} and @t{'Val}.

 @item
 @t{'Tag}.
@@ -18085,8 +18144,8 @@ Only a subset of the attributes are supported:
 @t{'Range} on array objects (not subtypes), but only as the right
 operand of the membership (@code{in}) operator.

-@item
-@t{'Access}, @t{'Unchecked_Access}, and
+@item
+@t{'Access}, @t{'Unchecked_Access}, and
 @t{'Unrestricted_Access} (a GNAT extension).

 @item
@@ -18101,20 +18160,20 @@ Equality tests (@samp{=} and @samp{/=}) on arrays test for bitwise
 equality of representations.  They will generally work correctly
 for strings and arrays whose elements have integer or enumeration types.
 They may not work correctly for arrays whose element
-types have user-defined equality, for arrays of real values
+types have user-defined equality, for arrays of real values
 (in particular, IEEE-conformant floating point, because of negative
 zeroes and NaNs), and for arrays whose elements contain unused bits with
-indeterminate values.
+indeterminate values.

 @item
-The other component-by-component array operations (@code{and}, @code{or},
+The other component-by-component array operations (@code{and}, @code{or},
 @code{xor}, @code{not}, and relational tests other than equality)
-are not implemented.
+are not implemented.

-@item
+@item
 @cindex array aggregates (Ada)
 @cindex record aggregates (Ada)
-@cindex aggregates (Ada)
+@cindex aggregates (Ada)
 There is limited support for array and record aggregates.  They are
 permitted only on the right sides of assignments, as in these examples:

@@ -18132,7 +18191,7 @@ discriminant's value by assigning an aggregate has an
 undefined effect if that discriminant is used within the record.
 However, you can first modify discriminants by directly assigning to
 them (which normally would not be allowed in Ada), and then performing an
-aggregate assignment.  For example, given a variable @code{A_Rec}
+aggregate assignment.  For example, given a variable @code{A_Rec}
 declared to have a type such as:

 @smallexample
@@ -18152,7 +18211,7 @@ assignments:

 As this example also illustrates, @value{GDBN} is very loose about the usual
 rules concerning aggregates.  You may leave out some of the
-components of an array or record aggregate (such as the @code{Len}
+components of an array or record aggregate (such as the @code{Len}
 component in the assignment to @code{A_Rec} above); they will retain their
 original values upon assignment.  You may freely use dynamic values as
 indices in component associations.  You may even use overlapping or
@@ -18176,16 +18235,16 @@ The @code{new} operator is not implemented.
 @item
 Entry calls are not implemented.

-@item
-Aside from printing, arithmetic operations on the native VAX floating-point
+@item
+Aside from printing, arithmetic operations on the native VAX floating-point
 formats are not supported.

 @item
 It is not possible to slice a packed array.

 @item
-The names @code{True} and @code{False}, when not part of a qualified name,
-are interpreted as if implicitly prefixed by @code{Standard}, regardless of
+The names @code{True} and @code{False}, when not part of a qualified name,
+are interpreted as if implicitly prefixed by @code{Standard}, regardless of
 context.
 Should your program
 redefine these names in a package or procedure (at best a dubious practice),
@@ -18197,7 +18256,7 @@ Based real literals are not implemented.

 @node Additions to Ada
 @subsubsection Additions to Ada
-@cindex Ada, deviations from
+@cindex Ada, deviations from

 As it does for other languages, @value{GDBN} makes certain generic
 extensions to Ada (@pxref{Expressions}):
@@ -18218,12 +18277,12 @@ which certain debugging information has been optimized away.
 appears in function or file @var{B}.''  When @var{B} is a file name,
 you must typically surround it in single quotes.

-@item
+@item
 The expression @code{@{@var{type}@} @var{addr}} means ``the variable of type
 @var{type} that appears at address @var{addr}.''

 @item
-A name starting with @samp{$} is a convenience variable
+A name starting with @samp{$} is a convenience variable
 (@pxref{Convenience Vars}) or a machine register (@pxref{Registers}).
 @end itemize

@@ -18231,7 +18290,7 @@ In addition, @value{GDBN} provides a few other shortcuts and outright
 additions specific to Ada:

 @itemize @bullet
-@item
+@item
 The assignment statement is allowed as an expression, returning
 its right-hand operand as its value.  Thus, you may enter

@@ -18240,8 +18299,8 @@ its right-hand operand as its value.  Thus, you may enter
 (@value{GDBP}) print A(tmp := y + 1)
 @end smallexample

-@item
-The semicolon is allowed as an ``operator,''  returning as its value
+@item
+The semicolon is allowed as an ``operator,''  returning as its value
 the value of its right-hand operand.
 This allows, for example,
 complex conditional breaks:
@@ -18264,13 +18323,13 @@ constant: zero means @code{Float} is used, one means
 $1 = 23.0
 @end smallexample

-@item
-Rather than use catenation and symbolic character names to introduce special
-characters into strings, one may instead use a special bracket notation,
-which is also used to print strings.  A sequence of characters of the form
-@samp{["@var{XX}"]} within a string or character literal denotes the
+@item
+Rather than use catenation and symbolic character names to introduce special
+characters into strings, one may instead use a special bracket notation,
+which is also used to print strings.  A sequence of characters of the form
+@samp{["@var{XX}"]} within a string or character literal denotes the
 (single) character whose numeric encoding is @var{XX} in hexadecimal.  The
-sequence of characters @samp{["""]} also denotes a single quotation mark
+sequence of characters @samp{["""]} also denotes a single quotation mark
 in strings.   For example,
 @smallexample
    "One line.["0a"]Next line.["0a"]"
@@ -18289,7 +18348,7 @@ to write
 @end smallexample

 @item
-When printing arrays, @value{GDBN} uses positional notation when the
+When printing arrays, @value{GDBN} uses positional notation when the
 array has a lower bound of 1, and uses a modified named notation otherwise.
 For example, a one-dimensional array of three integers with a lower bound
 of 3 might print as
@@ -18299,30 +18358,30 @@ of 3 might print as
 @end smallexample

 @noindent
-That is, in contrast to valid Ada, only the first component has a @code{=>}
+That is, in contrast to valid Ada, only the first component has a @code{=>}
 clause.

 @item
 You may abbreviate attributes in expressions with any unique,
-multi-character subsequence of
+multi-character subsequence of
 their names (an exact match gets preference).
 For example, you may use @t{a'len}, @t{a'gth}, or @t{a'lh}
 in place of  @t{a'length}.

 @item
 @cindex quoting Ada internal identifiers
-Since Ada is case-insensitive, the debugger normally maps identifiers you type
-to lower case.  The GNAT compiler uses upper-case characters for
+Since Ada is case-insensitive, the debugger normally maps identifiers you type
+to lower case.  The GNAT compiler uses upper-case characters for
 some of its internal identifiers, which are normally of no interest to users.
 For the rare occasions when you actually have to look at them,
-enclose them in angle brackets to avoid the lower-case mapping.
+enclose them in angle brackets to avoid the lower-case mapping.
 For example,
 @smallexample
 (@value{GDBP}) print <JMPBUF_SAVE>[0]
 @end smallexample

 @item
-Printing an object of class-wide type or dereferencing an
+Printing an object of class-wide type or dereferencing an
 access-to-class-wide value will display all the components of the object's
 specific type (as indicated by its run-time tag).  Likewise, component
 selection on such a value will operate on the specific type of the
@@ -18351,7 +18410,7 @@ Multiple matches for f
 [0] cancel
 [1] foo.f (integer) return boolean at foo.adb:23
 [2] foo.f (foo.new_integer) return boolean at foo.adb:28
->
+>
 @end smallexample

 In this case, just select one menu entry either to cancel expression evaluation
@@ -18808,12 +18867,12 @@ Show the current source character set for Ada.
 Besides the omissions listed previously (@pxref{Omissions from Ada}),
 we know of several problems with and limitations of Ada mode in
 @value{GDBN},
-some of which will be fixed with planned future releases of the debugger
+some of which will be fixed with planned future releases of the debugger
 and the GNU Ada compiler.

 @itemize @bullet
-@item
-Static constants that the compiler chooses not to materialize as objects in
+@item
+Static constants that the compiler chooses not to materialize as objects in
 storage are invisible to the debugger.

 @item
@@ -18824,22 +18883,22 @@ argument lists are treated as positional).
 Many useful library packages are currently invisible to the debugger.

 @item
-Fixed-point arithmetic, conversions, input, and output is carried out using
-floating-point arithmetic, and may give results that only approximate those on
+Fixed-point arithmetic, conversions, input, and output is carried out using
+floating-point arithmetic, and may give results that only approximate those on
 the host machine.

 @item
-The GNAT compiler never generates the prefix @code{Standard} for any of
-the standard symbols defined by the Ada language.  @value{GDBN} knows about
+The GNAT compiler never generates the prefix @code{Standard} for any of
+the standard symbols defined by the Ada language.  @value{GDBN} knows about
 this: it will strip the prefix from names when you use it, and will never
 look for a name you have so qualified among local symbols, nor match against
-symbols in other packages or subprograms.  If you have
-defined entities anywhere in your program other than parameters and
-local variables whose simple names match names in @code{Standard},
+symbols in other packages or subprograms.  If you have
+defined entities anywhere in your program other than parameters and
+local variables whose simple names match names in @code{Standard},
 GNAT's lack of qualification here can cause confusion.  When this happens,
-you can usually resolve the confusion
+you can usually resolve the confusion
 by qualifying the problematic names with package
-@code{Standard} explicitly.
+@code{Standard} explicitly.
 @end itemize

 Older versions of the compiler sometimes generate erroneous debugging
@@ -22526,7 +22585,7 @@ current exec-file loaded by @value{GDBN} (@pxref{set exec-file-mismatch}).
 @cindex symbol files, remote debugging

 @value{GDBN}, running on the host, needs access to symbol and debugging
-information for your program running on the target.  This requires
+information for your program running on the target.  This requires
 access to an unstripped copy of your program, and possibly any associated
 symbol files.  Note that this section applies equally to both @code{target
 remote} mode and @code{target extended-remote} mode.
@@ -23363,7 +23422,7 @@ debugging agent is launched in parallel with @value{GDBN}; there is a race
 condition because the agent may not become ready to accept the connection
 before @value{GDBN} attempts to connect.  When auto-retry is
 enabled, if the initial attempt to connect fails, @value{GDBN} reattempts
-to establish the connection using the timeout specified by
+to establish the connection using the timeout specified by
 @code{set tcp connect-timeout}.

 @item set tcp auto-retry off
@@ -23377,7 +23436,7 @@ Show the current auto-retry setting.
 @cindex connection timeout, for remote TCP target
 @cindex timeout, for remote target connection
 Set the timeout for establishing a TCP connection to the remote target to
-@var{seconds}.  The timeout affects both polling to retry failed connections
+@var{seconds}.  The timeout affects both polling to retry failed connections
 (enabled by @code{set tcp auto-retry on}) and waiting for connections
 that are merely slow to complete, and represents an approximate cumulative
 value.  If @var{seconds} is @code{unlimited}, there is no timeout and
@@ -25051,7 +25110,7 @@ Show whether ARM-specific debugging messages are enabled.
 @end table

 @table @code
-@item target sim @r{[}@var{simargs}@r{]} @dots{}
+@item target sim @r{[}@var{simargs}@r{]} @dots{}
 The @value{GDBN} ARM simulator accepts the following optional arguments.

 @table @code
@@ -25104,8 +25163,8 @@ This host system is used to download the configuration bitstream to
 the target FPGA.  The Xilinx Microprocessor Debugger (XMD) program
 communicates with the target board using the JTAG interface and
 presents a @code{gdbserver} interface to the board.  By default
-@code{xmd} uses port @code{1234}.  (While it is possible to change
-this default port, it requires the use of undocumented @code{xmd}
+@code{xmd} uses port @code{1234}.  (While it is possible to change
+this default port, it requires the use of undocumented @code{xmd}
 commands.  Contact Xilinx support if you need to do this.)

 Use these GDB commands to connect to the MicroBlaze target processor.
@@ -25312,7 +25371,7 @@ Show the current CRIS version.
 @item set cris-dwarf2-cfi
 @cindex DWARF-2 CFI and CRIS
 Set the usage of DWARF-2 CFI for CRIS debugging.  The default is @samp{on}.
-Change to @samp{off} when using @code{gcc-cris} whose version is below
+Change to @samp{off} when using @code{gcc-cris} whose version is below
 @code{R59}.

 @item show cris-dwarf2-cfi
@@ -25321,7 +25380,7 @@ Show the current state of using DWARF-2 CFI.
 @item set cris-mode @var{mode}
 @cindex CRIS mode
 Set the current CRIS mode to @var{mode}.  It should only be changed when
-debugging in guru mode, in which case it should be set to
+debugging in guru mode, in which case it should be set to
 @samp{guru} (the default is @samp{normal}).

 @item show cris-mode
@@ -25723,7 +25782,7 @@ given @var{address}.
 @subsection PowerPC
 @cindex PowerPC architecture

-When @value{GDBN} is debugging the PowerPC architecture, it provides a set of
+When @value{GDBN} is debugging the PowerPC architecture, it provides a set of
 pseudo-registers to enable inspection of 128-bit wide Decimal Floating Point
 numbers stored in the floating point registers. These values must be stored
 in two consecutive registers, always starting at an even register like
@@ -25761,38 +25820,38 @@ Show the current setting of Nios II debugging messages.
 @cindex Application Data Integrity
 @subsubsection ADI Support

-The M7 processor supports an Application Data Integrity (ADI) feature that
-detects invalid data accesses.  When software allocates memory and enables
-ADI on the allocated memory, it chooses a 4-bit version number, sets the
-version in the upper 4 bits of the 64-bit pointer to that data, and stores
-the 4-bit version in every cacheline of that data.  Hardware saves the latter
-in spare bits in the cache and memory hierarchy.  On each load and store,
-the processor compares the upper 4 VA (virtual address) bits to the
-cacheline's version.  If there is a mismatch, the processor generates a
-version mismatch trap which can be either precise or disrupting.  The trap
-is an error condition which the kernel delivers to the process as a SIGSEGV
+The M7 processor supports an Application Data Integrity (ADI) feature that
+detects invalid data accesses.  When software allocates memory and enables
+ADI on the allocated memory, it chooses a 4-bit version number, sets the
+version in the upper 4 bits of the 64-bit pointer to that data, and stores
+the 4-bit version in every cacheline of that data.  Hardware saves the latter
+in spare bits in the cache and memory hierarchy.  On each load and store,
+the processor compares the upper 4 VA (virtual address) bits to the
+cacheline's version.  If there is a mismatch, the processor generates a
+version mismatch trap which can be either precise or disrupting.  The trap
+is an error condition which the kernel delivers to the process as a SIGSEGV
 signal.

 Note that only 64-bit applications can use ADI and need to be built with
 ADI-enabled.

-Values of the ADI version tags, which are in granularity of a
-cacheline (64 bytes), can be viewed or modified.
+Values of the ADI version tags, which are in granularity of a
+cacheline (64 bytes), can be viewed or modified.


 @table @code
 @kindex adi examine
 @item adi (examine | x) [ / @var{n} ] @var{addr}

-The @code{adi examine} command displays the value of one ADI version tag per
-cacheline.
+The @code{adi examine} command displays the value of one ADI version tag per
+cacheline.

-@var{n} is a decimal integer specifying the number in bytes; the default
-is 1.  It specifies how much ADI version information, at the ratio of 1:ADI
-block size, to display.
+@var{n} is a decimal integer specifying the number in bytes; the default
+is 1.  It specifies how much ADI version information, at the ratio of 1:ADI
+block size, to display.

-@var{addr} is the address in user address space where you want @value{GDBN}
-to begin displaying the ADI version tags.
+@var{addr} is the address in user address space where you want @value{GDBN}
+to begin displaying the ADI version tags.

 Below is an example of displaying ADI versions of variable "shmaddr".

@@ -25804,19 +25863,19 @@ Below is an example of displaying ADI versions of variable "shmaddr".
 @kindex adi assign
 @item adi (assign | a) [ / @var{n} ] @var{addr} = @var{tag}

-The @code{adi assign} command is used to assign new ADI version tag
-to an address.
+The @code{adi assign} command is used to assign new ADI version tag
+to an address.

-@var{n} is a decimal integer specifying the number in bytes;
-the default is 1.  It specifies how much ADI version information, at the
-ratio of 1:ADI block size, to modify.
+@var{n} is a decimal integer specifying the number in bytes;
+the default is 1.  It specifies how much ADI version information, at the
+ratio of 1:ADI block size, to modify.

-@var{addr} is the address in user address space where you want @value{GDBN}
-to begin modifying the ADI version tags.
+@var{addr} is the address in user address space where you want @value{GDBN}
+to begin modifying the ADI version tags.

 @var{tag} is the new ADI version tag.

-For example, do the following to modify then verify ADI versions of
+For example, do the following to modify then verify ADI versions of
 variable "shmaddr":

 @smallexample
@@ -27128,7 +27187,7 @@ Show the current state of @sc{gnu}/Hurd debugging messages.
 @item set debug infrun
 @cindex inferior debugging info
 Turns on or off display of @value{GDBN} debugging info for running the inferior.
-The default is off.  @file{infrun.c} contains GDB's runtime state machine used
+The default is off.  @file{infrun.c} contains GDB's runtime state machine used
 for implementing operations such as single-stepping the inferior.
 @item show debug infrun
 Displays the current state of @value{GDBN} inferior debugging.
@@ -29418,7 +29477,7 @@ in the form of a reference manual.

 Note that @sc{gdb/mi} is still under construction, so some of the
 features described below are incomplete and subject to change
-(@pxref{GDB/MI Development and Front Ends, , @sc{gdb/mi} Development and Front Ends}).
+(@pxref{GDB/MI Development and Front Ends, , @sc{gdb/mi} Development and Front Ends}).

 @unnumberedsec Notation and Terminology

@@ -29500,7 +29559,7 @@ a command and reported as part of that command response.
 The important examples of notifications are:
 @itemize @bullet

-@item
+@item
 Exec notifications.  These are used to report changes in
 target state---when a target is resumed, or stopped.  It would not
 be feasible to include this information in response of resuming
@@ -29509,7 +29568,7 @@ different threads.  Also, quite some time may pass before any event
 happens in the target, while a frontend needs to know whether the resuming
 command itself was successfully executed.

-@item
+@item
 Console output, and status notifications.  Console output
 notifications are used to report output of CLI commands, as well as
 diagnostics for other commands.  Status notifications are used to
@@ -29529,8 +29588,8 @@ orthogonal frontend design.
 There's no guarantee that whenever an MI command reports an error,
 @value{GDBN} or the target are in any specific state, and especially,
 the state is not reverted to the state before the MI command was
-processed.  Therefore, whenever an MI command results in an error,
-we recommend that the frontend refreshes all the information shown in
+processed.  Therefore, whenever an MI command results in an error,
+we recommend that the frontend refreshes all the information shown in
 the user interface.


@@ -29552,7 +29611,7 @@ be specified.  The CLI interface maintains the selected thread and frame,
 and supplies them to target on each command.  This is convenient,
 because a command line user would not want to specify that information
 explicitly on each command, and because user interacts with
-@value{GDBN} via a single terminal, so no confusion is possible as
+@value{GDBN} via a single terminal, so no confusion is possible as
 to what thread and frame are the current ones.

 In the case of MI, the concept of selected thread and frame is less
@@ -29578,7 +29637,7 @@ frontend's selection to the one specified by user.  @value{GDBN}
 communicates the suggestion to change current thread and frame using the
 @samp{=thread-selected} notification.

-Note that historically, MI shares the selected thread with CLI, so
+Note that historically, MI shares the selected thread with CLI, so
 frontends used the @code{-thread-select} to execute commands in the
 right context.  However, getting this to work right is cumbersome.  The
 simplest way is for frontend to emit @code{-thread-select} command
@@ -29607,7 +29666,7 @@ For instance:
 @smallexample
 -data-evaluate-expression --language c "sizeof (void*)"
 ^done,value="4"
-(gdb)
+(gdb)
 @end smallexample

 The valid language names are the same names accepted by the
@@ -29688,8 +29747,8 @@ hardware systems, each one having several cores with several different
 processes running on each core.  This section describes the MI
 mechanism to support such debugging scenarios.

-The key observation is that regardless of the structure of the
-target, MI can have a global list of threads, because most commands that
+The key observation is that regardless of the structure of the
+target, MI can have a global list of threads, because most commands that
 accept the @samp{--thread} option do not need to know what process that
 thread belongs to.  Therefore, it is not necessary to introduce
 neither additional @samp{--process} option, nor an notion of the
@@ -30210,7 +30269,7 @@ A breakpoint was reached.
 A watchpoint was triggered.
 @item read-watchpoint-trigger
 A read watchpoint was triggered.
-@item access-watchpoint-trigger
+@item access-watchpoint-trigger
 An access watchpoint was triggered.
 @item function-finished
 An -exec-finish or similar CLI command was accomplished.
@@ -30219,15 +30278,15 @@ An -exec-until or similar CLI command was accomplished.
 @item watchpoint-scope
 A watchpoint has gone out of scope.
 @item end-stepping-range
-An -exec-next, -exec-next-instruction, -exec-step, -exec-step-instruction or
+An -exec-next, -exec-next-instruction, -exec-step, -exec-step-instruction or
 similar CLI command was accomplished.
-@item exited-signalled
+@item exited-signalled
 The inferior exited because of a signal.
-@item exited
+@item exited
 The inferior exited.
-@item exited-normally
+@item exited-normally
 The inferior exited normally.
-@item signal-received
+@item signal-received
 A signal was received by the inferior.
 @item solib-event
 The inferior has stopped due to a library being loaded or unloaded.
@@ -32274,8 +32333,8 @@ other cases.
 @end smallexample

 Resumes the execution of the inferior program, which will continue
-to execute until it reaches a debugger stop event.  If the
-@samp{--reverse} option is specified, execution resumes in reverse until
+to execute until it reaches a debugger stop event.  If the
+@samp{--reverse} option is specified, execution resumes in reverse until
 it reaches a stop event.  Stop events may include
 @itemize @bullet
 @item
@@ -33256,10 +33315,10 @@ object, or to change display format.
 Variable objects have hierarchical tree structure.  Any variable object
 that corresponds to a composite type, such as structure in C, has
 a number of child variable objects, for example corresponding to each
-element of a structure.  A child variable object can itself have
-children, recursively.  Recursion ends when we reach
+element of a structure.  A child variable object can itself have
+children, recursively.  Recursion ends when we reach
 leaf variable objects, which always have built-in types.  Child variable
-objects are created only by explicit request, so if a frontend
+objects are created only by explicit request, so if a frontend
 is not interested in the children of a particular variable object, no
 child will be created.

@@ -33268,7 +33327,7 @@ string, or set the value from a string.  String value can be also
 obtained for a non-leaf variable object, but it's generally a string
 that only indicates the type of the object, and does not list its
 contents.  Assignment to a non-leaf variable object is not allowed.
-
+
 A frontend does not need to read the values of all variable objects each time
 the program stops.  Instead, MI provides an update command that lists all
 variable objects whose values has changed since the last update
@@ -33285,7 +33344,7 @@ relatively slow for embedded targets, so a frontend might want
 to disable automatic update for the variables that are either not
 visible on the screen, or ``closed''.  This is possible using so
 called ``frozen variable objects''.  Such variable objects are never
-implicitly updated.
+implicitly updated.

 Variable objects can be either @dfn{fixed} or @dfn{floating}.  For the
 fixed variable object, the expression is parsed when the variable
@@ -33528,8 +33587,8 @@ but with padding zeroes to the left of the value.  For example, a 32-bit
 hexadecimal value of 0x1234 would be represented as 0x00001234 in the
 zero-hexadecimal format.

-For a variable with children, the format is set only on the
-variable itself, and the children are not affected.
+For a variable with children, the format is set only on the
+variable itself, and the children are not affected.

 @subheading The @code{-var-show-format} Command
 @findex -var-show-format
@@ -33745,7 +33804,7 @@ Returns an expression that can be evaluated in the current
 context and will yield the same value that a variable object has.
 Compare this with the @code{-var-info-expression} command, which
 result can be used only for UI presentation.  Typical use of
-the @code{-var-info-path-expression} command is creating a
+the @code{-var-info-path-expression} command is creating a
 watchpoint from a variable object.

 This command is currently not valid for children of a dynamic varobj,
@@ -33790,10 +33849,10 @@ where @var{attr} is @code{@{ @{ editable | noneditable @} | TBD @}}.

 Evaluates the expression that is represented by the specified variable
 object and returns its value as a string.  The format of the string
-can be specified with the @samp{-f} option.  The possible values of
-this option are the same as for @code{-var-set-format}
+can be specified with the @samp{-f} option.  The possible values of
+this option are the same as for @code{-var-set-format}
 (@pxref{-var-set-format}).  If the @samp{-f} option is not specified,
-the current display format will be used.  The current display format
+the current display format will be used.  The current display format
 can be changed using the @code{-var-set-format} command.

 @smallexample
@@ -33970,12 +34029,12 @@ type_changed="false"@}]
 Set the frozenness flag on the variable object @var{name}.  The
 @var{flag} parameter should be either @samp{1} to make the variable
 frozen or @samp{0} to make it unfrozen.  If a variable object is
-frozen, then neither itself, nor any of its children, are
-implicitly updated by @code{-var-update} of
+frozen, then neither itself, nor any of its children, are
+implicitly updated by @code{-var-update} of
 a parent variable or by @code{-var-update *}.  Only
 @code{-var-update} of the variable itself will update its value and
 values of its children.  After a variable object is unfrozen, it is
-implicitly updated by all subsequent @code{-var-update} operations.
+implicitly updated by all subsequent @code{-var-update} operations.
 Unfreezing a variable does not update it, only subsequent
 @code{-var-update} does.

@@ -36252,7 +36311,7 @@ Signal handling commands are not implemented.

 Attach to a process @var{pid} or a file @var{file} outside of
 @value{GDBN}, or a thread group @var{gid}.  If attaching to a thread
-group, the id previously returned by
+group, the id previously returned by
 @samp{-list-thread-groups --available} must be used.

 @subsubheading @value{GDBN} Command
@@ -36853,7 +36912,7 @@ option (@pxref{GDB/MI Data Manipulation}).
 @findex -list-target-features

 Returns a list of particular features that are supported by the
-target.  Those features affect the permitted MI commands, but
+target.  Those features affect the permitted MI commands, but
 unlike the features reported by the @code{-list-features} command, the
 features depend on which target GDB is using at the moment.  Whenever
 a target can change, due to commands such as @code{-target-select},
@@ -37279,7 +37338,7 @@ The corresponding @value{GDBN} command is @samp{add-inferior}
 @smallexample
 -interpreter-exec @var{interpreter} @var{command}
 @end smallexample
-@anchor{-interpreter-exec}
+@anchor{-interpreter-exec}

 Execute the specified @var{command} in the given @var{interpreter}.

@@ -37797,7 +37856,7 @@ depend on the language).
 This chapter documents @value{GDBN}'s @dfn{just-in-time} (JIT) compilation
 interface.  A JIT compiler is a program or library that generates native
 executable code at runtime and executes it, usually in order to achieve good
-performance while maintaining platform independence.
+performance while maintaining platform independence.

 Programs that use JIT compilation are normally difficult to debug because
 portions of their code are generated at runtime, instead of being loaded from
@@ -38759,7 +38818,7 @@ directory in which Python is installed, to choose a particular
 installation of Python.

 @item zlib
-@cindex compressed debug sections
+@cindex compressed debug sections
 @value{GDBN} will use the @samp{zlib} library, if available, to read
 compressed debug sections.  Some linkers, such as GNU gold, are capable
 of producing binaries with compressed debug sections.  If @value{GDBN}
@@ -39224,7 +39283,7 @@ If @value{GDBN} has been configured with the option @option{--prefix=$prefix},
 they may be subject to relocation.  Two possible cases:

 @itemize @bullet
-@item
+@item
 If the default location of this init file/directory contains @file{$prefix},
 it will be subject to relocation.  Suppose that the configure options
 are @option{--prefix=$prefix --with-system-gdbinit=$prefix/etc/gdbinit};
@@ -40254,7 +40313,7 @@ debugging stub incorporated in your program) sends a @var{response}.  In
 the case of step and continue @var{command}s, the response is only sent
 when the operation has completed, and the target has again stopped all
 threads in all attached processes.  This is the default all-stop mode
-behavior, but the remote protocol also supports @value{GDBN}'s non-stop
+behavior, but the remote protocol also supports @value{GDBN}'s non-stop
 execution mode; see @ref{Remote Non-Stop}, for details.

 @var{packet-data} consists of a sequence of characters with the
@@ -40348,7 +40407,7 @@ bytes @samp{foo}, followed by a @var{bar}, followed directly by a
 @var{baz}.

 @cindex @var{thread-id}, in remote protocol
-@anchor{thread-id syntax}
+@anchor{thread-id syntax}
 Several packets and replies include a @var{thread-id} field to identify
 a thread.  Normally these are positive numbers with a target-specific
 interpretation, formatted as big-endian hex strings.  A @var{thread-id}
@@ -40492,7 +40551,7 @@ Don't use this packet; instead, define a general set packet
 @item D
 @itemx D;@var{pid}
 @cindex @samp{D} packet
-The first form of the packet is used to detach @value{GDBN} from the
+The first form of the packet is used to detach @value{GDBN} from the
 remote system.  It is sent to the remote target
 before @value{GDBN} disconnects via the @code{detach} command.

@@ -40776,10 +40835,10 @@ attached without being stopped if that is supported by the target.
 @c In non-stop mode, on a successful vAttach, the stub should set the
 @c current thread to a thread of the newly-attached process.  After
 @c attaching, GDB queries for the attached process's thread ID with qC.
-@c Also note that, from a user perspective, whether or not the
-@c target is stopped on attach in non-stop mode depends on whether you
-@c use the foreground or background version of the attach command, not
-@c on what vAttach does; GDB does the right thing with respect to either
+@c Also note that, from a user perspective, whether or not the
+@c target is stopped on attach in non-stop mode depends on whether you
+@c use the foreground or background version of the attach command, not
+@c on what vAttach does; GDB does the right thing with respect to either
 @c stopping or restarting threads.

 This packet is only available in extended mode (@pxref{extended mode}).
@@ -40840,7 +40899,7 @@ in a degenerate way as a single instruction step operation.)

 @end table

-The optional argument @var{addr} normally associated with the
+The optional argument @var{addr} normally associated with the
 @samp{c}, @samp{C}, @samp{s}, and @samp{S} packets is
 not supported in @samp{vCont}.

@@ -41272,10 +41331,10 @@ list of loaded libraries.  The @var{r} part is ignored.

 @cindex replay log events, remote reply
 @item replaylog
-The packet indicates that the target cannot continue replaying
+The packet indicates that the target cannot continue replaying
 logged execution events, because it has reached the end (or the
 beginning when executing backward) of the log.  The value of @var{r}
-will be either @samp{begin} or @samp{end}.  @xref{Reverse Execution},
+will be either @samp{begin} or @samp{end}.  @xref{Reverse Execution},
 for more information.

 @item swbreak
@@ -41517,7 +41576,7 @@ Return the current thread ID.
 Reply:
 @table @samp
 @item QC @var{thread-id}
-Where @var{thread-id} is a thread ID as documented in
+Where @var{thread-id} is a thread ID as documented in
 @ref{thread-id syntax}.
 @item @r{(anything else)}
 Any other reply implies the old thread ID.
@@ -41783,7 +41842,7 @@ information associated with the variable.)
 @var{lm} is the (big endian, hex encoded) OS/ABI-specific encoding of the
 load module associated with the thread local storage.  For example,
 a @sc{gnu}/Linux system will pass the link map address of the shared
-object associated with the thread local storage under consideration.
+object associated with the thread local storage under consideration.
 Other operating environments may choose to represent the load module
 differently, so the precise meaning of this parameter will vary.

@@ -41957,7 +42016,7 @@ kept at fixed offsets relative to the last relocated segment.
 @cindex thread information, remote request
 @cindex @samp{qP} packet
 Returns information on @var{thread-id}.  Where: @var{mode} is a hex
-encoded 32 bit mode; @var{thread-id} is a thread ID
+encoded 32 bit mode; @var{thread-id} is a thread ID
 (@pxref{thread-id syntax}).

 Don't use this packet; use the @samp{qThreadExtraInfo} query instead
@@ -41988,7 +42047,7 @@ the stub.

 This packet is not probed by default; the remote stub must request it,
 by supplying an appropriate @samp{qSupported} response (@pxref{qSupported}).
-Use of this packet is controlled by the @code{set non-stop} command;
+Use of this packet is controlled by the @code{set non-stop} command;
 @pxref{Non-Stop Mode}.

 @item QCatchSyscalls:1 @r{[};@var{sysno}@r{]}@dots{}
@@ -42038,7 +42097,7 @@ by supplying an appropriate @samp{qSupported} response (@pxref{qSupported}).
 @cindex pass signals to inferior, remote request
 @cindex @samp{QPassSignals} packet
 @anchor{QPassSignals}
-Each listed @var{signal} should be passed directly to the inferior process.
+Each listed @var{signal} should be passed directly to the inferior process.
 Signals are numbered identically to continue packets and stop replies
 (@pxref{Stop Reply Packets}).  Each @var{signal} list item should be
 strictly greater than the previous item.  These signals do not need to stop
@@ -42268,11 +42327,11 @@ state, even if the stub had previously been communicating with
 a different version of @value{GDBN}.

 The following values of @var{gdbfeature} (for the packet sent by @value{GDBN})
-are defined:
+are defined:

 @table @samp
 @item multiprocess
-This feature indicates whether @value{GDBN} supports multiprocess
+This feature indicates whether @value{GDBN} supports multiprocess
 extensions to the remote protocol.  @value{GDBN} does not use such
 extensions unless the stub also reports that it supports them by
 including @samp{multiprocess+} in its @samp{qSupported} reply.
@@ -42903,13 +42962,13 @@ packets.)
 @itemx QTSave
 @itemx qTsP
 @itemx qTsV
-@itemx QTStart
-@itemx QTStop
+@itemx QTStart
+@itemx QTStop
 @itemx QTEnable
 @itemx QTDisable
-@itemx QTinit
-@itemx QTro
-@itemx qTStatus
+@itemx QTinit
+@itemx QTro
+@itemx qTStatus
 @itemx qTV
 @itemx qTfSTM
 @itemx qTsSTM
@@ -43048,7 +43107,7 @@ stub indicated it supports the augmented form of this packet
 by supplying an appropriate @samp{qSupported} response
 (@pxref{qXfer read}, @ref{qSupported}).

-This packet is optional for better performance on SVR4 targets.
+This packet is optional for better performance on SVR4 targets.
 @value{GDBN} uses memory read packets to read the SVR4 library list otherwise.

 This packet is not probed by default; the remote stub must request it,
@@ -44087,7 +44146,7 @@ packet}) with the usual packet framing instead of the single byte
 Stubs are not required to recognize these interrupt mechanisms and the
 precise meaning associated with receipt of the interrupt is
 implementation defined.  If the target supports debugging of multiple
-threads and/or processes, it should attempt to interrupt all
+threads and/or processes, it should attempt to interrupt all
 currently-executing threads and processes.
 If the stub is successful at interrupting the
 running program, it should send one of the stop
@@ -44219,7 +44278,7 @@ The following notifications are defined:
 @tab vStopped
 @tab @var{reply}.  The @var{reply} has the form of a stop reply, as
 described in @ref{Stop Reply Packets}.  Refer to @ref{Remote Non-Stop},
-for information on how these notifications are acknowledged by
+for information on how these notifications are acknowledged by
 @value{GDBN}.
 @tab Report an asynchronous stop event in non-stop mode.

@@ -44253,7 +44312,7 @@ to run.  When reporting a @samp{W} or @samp{X} response, all running
 threads belonging to other attached processes continue to run.

 In non-stop mode, the target shall respond to the @samp{?} packet as
-follows.  First, any incomplete stop reply notification/@samp{vStopped}
+follows.  First, any incomplete stop reply notification/@samp{vStopped}
 sequence in progress is abandoned.  The target must begin a new
 sequence reporting stop events for all stopped threads, whether or not
 it has previously reported those events to @value{GDBN}.  The first
@@ -44388,12 +44447,12 @@ It uses its own internal representation of datatypes and values.  Both
 translating the system-dependent value representations into the internal
 protocol representations when data is transmitted.

-The communication is synchronous.  A system call is possible only when
-@value{GDBN} is waiting for a response from the @samp{C}, @samp{c}, @samp{S}
+The communication is synchronous.  A system call is possible only when
+@value{GDBN} is waiting for a response from the @samp{C}, @samp{c}, @samp{S}
 or @samp{s} packets.  While @value{GDBN} handles the request for a system call,
 the target is stopped to allow deterministic access to the target's
 memory.  Therefore File-I/O is not interruptible by target signals.  On
-the other hand, it is possible to interrupt File-I/O by a user interrupt
+the other hand, it is possible to interrupt File-I/O by a user interrupt
 (@samp{Ctrl-C}) within @value{GDBN}.

 The target's request to perform a host system call does not finish
@@ -44411,7 +44470,7 @@ request from @value{GDBN} is required.
   <- target hits breakpoint and sends a Txx packet
 @end smallexample

-The protocol only supports I/O on the console and to regular files on
+The protocol only supports I/O on the console and to regular files on
 the host file system.  Character or block special devices, pipes,
 named pipes, sockets or any other communication method on the host
 system are not supported by this protocol.
@@ -44424,7 +44483,7 @@ File I/O is not supported in non-stop mode.

 The File-I/O protocol uses the @code{F} packet as the request as well
 as reply packet.  Since a File-I/O system call can only occur when
-@value{GDBN} is waiting for a response from the continuing or stepping target,
+@value{GDBN} is waiting for a response from the continuing or stepping target,
 the File-I/O request is a reply that @value{GDBN} has to expect as a result
 of a previous @samp{C}, @samp{c}, @samp{S} or @samp{s} packet.
 This @code{F} packet contains all information needed to allow @value{GDBN}
@@ -44446,7 +44505,7 @@ At this point, @value{GDBN} has to perform the following actions.

 @itemize @bullet
 @item
-If the parameters include pointer values to data needed as input to a
+If the parameters include pointer values to data needed as input to a
 system call, @value{GDBN} requests this data from the target with a
 standard @code{m} packet request.  This additional communication has to be
 expected by the target implementation and is handled as any other @code{m}
@@ -44502,11 +44561,11 @@ The @code{F} request packet has the following format:
 @var{call-id} is the identifier to indicate the host system call to be called.
 This is just the name of the function.

-@var{parameter@dots{}} are the parameters to the system call.
+@var{parameter@dots{}} are the parameters to the system call.
 Parameters are hexadecimal integer values, either the actual values in case
 of scalar datatypes, pointers to target buffer space in case of compound
 datatypes and unspecified memory areas, or pointer/length pairs in case
-of string parameters.  These are appended to the @var{call-id} as a
+of string parameters.  These are appended to the @var{call-id} as a
 comma-delimited list.  All values are transmitted in ASCII
 string representation, pointer/length pairs separated by a slash.

@@ -45174,10 +45233,10 @@ The call was interrupted by the user.

 @end table

-@value{GDBN} takes over the full task of calling the necessary host calls
-to perform the @code{system} call.  The return value of @code{system} on
+@value{GDBN} takes over the full task of calling the necessary host calls
+to perform the @code{system} call.  The return value of @code{system} on
 the host is simplified before it's returned
-to the target.  Any termination signal information from the child process
+to the target.  Any termination signal information from the child process
 is discarded, and the return value consists
 entirely of the exit status of the called command.

@@ -45213,9 +45272,9 @@ protocol.
 @unnumberedsubsubsec Integral Datatypes
 @cindex integral datatypes, in file-i/o protocol

-The integral datatypes used in the system calls are @code{int},
+The integral datatypes used in the system calls are @code{int},
 @code{unsigned int}, @code{long}, @code{unsigned long},
-@code{mode_t}, and @code{time_t}.
+@code{mode_t}, and @code{time_t}.

 @code{int}, @code{unsigned int}, @code{mode_t} and @code{time_t} are
 implemented as 32 bit values in this protocol.
@@ -45259,10 +45318,10 @@ at address 0x123456 is transmitted as
 @cindex memory transfer, in file-i/o protocol

 Structured data which is transferred using a memory read or write (for
-example, a @code{struct stat}) is expected to be in a protocol-specific format
+example, a @code{struct stat}) is expected to be in a protocol-specific format
 with all scalar multibyte datatypes being big endian.  Translation to
-this representation needs to be done both by the target before the @code{F}
-packet is sent, and by @value{GDBN} before
+this representation needs to be done both by the target before the @code{F}
+packet is sent, and by @value{GDBN} before
 it transfers memory to the target.  Transferred pointers to structured
 data should point to the already-coerced data at any time.

@@ -45271,7 +45330,7 @@ data should point to the already-coerced data at any time.
 @unnumberedsubsubsec struct stat
 @cindex struct stat, in file-i/o protocol

-The buffer of type @code{struct stat} used by the target and @value{GDBN}
+The buffer of type @code{struct stat} used by the target and @value{GDBN}
 is defined as follows:

 @smallexample
@@ -46626,9 +46685,9 @@ targets.  It should describe the following registers:
 @item
 @samp{eflags}, @samp{cs}, @samp{ss}, @samp{ds}, @samp{es},
 @samp{fs}, @samp{gs}
-@item
+@item
 @samp{st0} through @samp{st7}
-@item
+@item
 @samp{fctrl}, @samp{fstat}, @samp{ftag}, @samp{fiseg}, @samp{fioff},
 @samp{foseg}, @samp{fooff} and @samp{fop}
 @end itemize
@@ -46643,7 +46702,7 @@ describe registers:
 @samp{xmm0} through @samp{xmm7} for i386
 @item
 @samp{xmm0} through @samp{xmm15} for amd64
-@item
+@item
 @samp{mxcsr}
 @end itemize

@@ -46769,7 +46828,7 @@ Linux kernel to control restartable syscalls.
 @item @samp{org.gnu.gdb.m68k.core}
 @itemx @samp{org.gnu.gdb.coldfire.core}
 @itemx @samp{org.gnu.gdb.fido.core}
-One of those features must be always present.
+One of those features must be always present.
 The feature that is present determines which flavor of m68k is
 used.  The feature that is present should contain registers
 @samp{d0} through @samp{d7}, @samp{a0} through @samp{a5}, @samp{fp},
@@ -47087,7 +47146,7 @@ contain registers @samp{TSR}, @samp{ILC} and @samp{RILC}.
 Users of @value{GDBN} often wish to obtain information about the state of
 the operating system running on the target---for example the list of
 processes, or the list of open files.  This section describes the
-mechanism that makes it possible.  This mechanism is similar to the
+mechanism that makes it possible.  This mechanism is similar to the
 target features mechanism (@pxref{Target Descriptions}), but focuses
 on a different aspect of target.

diff --git a/gdb/eval.c b/gdb/eval.c
index ce1d883aa86..7ce248b966f 100644
--- a/gdb/eval.c
+++ b/gdb/eval.c
@@ -2181,7 +2181,7 @@ eval_op_objc_msgcall (struct type *expect_type, struct expression *exp,

 /* Helper function for MULTI_SUBSCRIPT.  */

-static struct value *
+struct value *
 eval_multi_subscript (struct type *expect_type, struct expression *exp,
 		      enum noside noside, value *arg1,
 		      gdb::array_view<value *> args)
@@ -2191,6 +2191,7 @@ eval_multi_subscript (struct type *expect_type, struct expression *exp,
       if (binop_user_defined_p (MULTI_SUBSCRIPT, arg1, arg2))
 	{
 	  arg1 = value_x_binop (arg1, arg2, MULTI_SUBSCRIPT, OP_NULL, noside);
+	  arg1 = value_inc_subscript_level (arg1);
 	}
       else
 	{
diff --git a/gdb/expop.h b/gdb/expop.h
index a17311f74e5..999d22ce077 100644
--- a/gdb/expop.h
+++ b/gdb/expop.h
@@ -194,6 +194,11 @@ extern struct value *eval_binop_assign_modify (struct type *expect_type,
 					       enum exp_opcode op,
 					       struct value *arg1,
 					       struct value *arg2);
+extern struct value *
+eval_multi_subscript (struct type *expect_type, struct expression *exp,
+		      enum noside noside, value *arg1,
+		      gdb::array_view<value *> args);
+

 namespace expr
 {
@@ -2097,6 +2102,22 @@ class multi_subscript_operation
   { return MULTI_SUBSCRIPT; }
 };

+/* Multi-dimensional subscripting.  */
+class m2_multi_subscript_operation
+  : public tuple_holding_operation<operation_up, std::vector<operation_up>>
+{
+public:
+
+  using tuple_holding_operation::tuple_holding_operation;
+
+  value *evaluate (struct type *expect_type,
+		   struct expression *exp,
+		   enum noside noside) override;
+
+  enum exp_opcode opcode () const override
+  { return MULTI_SUBSCRIPT; }
+};
+
 /* The "&&" operator.  */
 class logical_and_operation
   : public maybe_constant_operation<operation_up, operation_up>
diff --git a/gdb/f-lang.h b/gdb/f-lang.h
index f92d3b01c78..259493f720e 100644
--- a/gdb/f-lang.h
+++ b/gdb/f-lang.h
@@ -88,7 +88,8 @@ class f_language : public language_defn

   void print_type (struct type *type, const char *varstring,
 		   struct ui_file *stream, int show, int level,
-		   const struct type_print_options *flags) const override;
+		   const struct type_print_options *flags,
+		   struct value *val = NULL) const override;

   /* See language.h.  This just returns default set of word break
      characters but with the modules separator `::' removed.  */
diff --git a/gdb/f-typeprint.c b/gdb/f-typeprint.c
index 170187c6749..b75633b0662 100644
--- a/gdb/f-typeprint.c
+++ b/gdb/f-typeprint.c
@@ -48,7 +48,8 @@ f_language::print_typedef (struct type *type, struct symbol *new_symbol,
 void
 f_language::print_type (struct type *type, const char *varstring,
 			struct ui_file *stream, int show, int level,
-			const struct type_print_options *flags) const
+			const struct type_print_options *flags,
+			struct value *val) const
 {
   enum type_code code;

diff --git a/gdb/go-lang.h b/gdb/go-lang.h
index fb57dd7dacc..ee291929b8f 100644
--- a/gdb/go-lang.h
+++ b/gdb/go-lang.h
@@ -111,7 +111,8 @@ class go_language : public language_defn

   void print_type (struct type *type, const char *varstring,
 		   struct ui_file *stream, int show, int level,
-		   const struct type_print_options *flags) const override;
+		   const struct type_print_options *flags,
+		   struct value *val = NULL) const override;

   /* See language.h.  */

diff --git a/gdb/go-typeprint.c b/gdb/go-typeprint.c
index f8f155fbb64..d04782e2c42 100644
--- a/gdb/go-typeprint.c
+++ b/gdb/go-typeprint.c
@@ -44,7 +44,8 @@
 void
 go_language::print_type (struct type *type, const char *varstring,
 			 struct ui_file *stream, int show, int level,
-			 const struct type_print_options *flags) const
+			 const struct type_print_options *flags,
+			 struct value *val) const
 {
   /* Borrowed from c-typeprint.c.  */
   if (show > 0)
diff --git a/gdb/language.c b/gdb/language.c
index af58a374dd6..f251f4d74cb 100644
--- a/gdb/language.c
+++ b/gdb/language.c
@@ -763,7 +763,8 @@ class auto_or_unknown_language : public language_defn

   void print_type (struct type *type, const char *varstring,
 		   struct ui_file *stream, int show, int level,
-		   const struct type_print_options *flags) const override
+		   const struct type_print_options *flags,
+		   struct value *val) const override
   {
     error (_("type printing not implemented for language \"%s\""),
 	   natural_name ());
diff --git a/gdb/language.h b/gdb/language.h
index f2885000259..472f4e80fb7 100644
--- a/gdb/language.h
+++ b/gdb/language.h
@@ -451,7 +451,8 @@ struct language_defn

   virtual void print_type (struct type *type, const char *varstring,
 			   struct ui_file *stream, int show, int level,
-			   const struct type_print_options *flags) const = 0;
+			   const struct type_print_options *flags,
+			   struct value *val = NULL) const = 0;

   /* PC is possibly an unknown languages trampoline.
      If that PC falls in a trampoline belonging to this language, return
diff --git a/gdb/m2-exp.h b/gdb/m2-exp.h
index cf233e66009..8aa879ec6b4 100644
--- a/gdb/m2-exp.h
+++ b/gdb/m2-exp.h
@@ -20,22 +20,48 @@
 #ifndef M2_EXP_H
 #define M2_EXP_H

+#include "defs.h"
+#include <ctype.h>
+#include "expression.h"
+#include "value.h"
+#include "parser-defs.h"
+#include "language.h"
+#include "c-lang.h"
+#include "m2-lang.h"
+#include "bfd.h" /* Required by objfiles.h.  */
+#include "symfile.h" /* Required by objfiles.h.  */
+#include "objfiles.h" /* For have_full_symbols and have_partial_symbols */
+#include "charset.h"
+#include "block.h"
+#include "type-stack.h"
 #include "expop.h"
+#include "cp-abi.h"
+#include "gdbsupport/traits.h"
+#include "gdbsupport/enum-flags.h"

 extern struct value *eval_op_m2_high (struct type *expect_type,
 				      struct expression *exp,
 				      enum noside noside,
 				      struct value *arg1);
+extern struct value *eval_op_m2_size (struct type *expect_type,
+				      struct expression *exp,
+				      enum noside noside,
+				      struct value *arg1);
 extern struct value *eval_op_m2_subscript (struct type *expect_type,
 					   struct expression *exp,
 					   enum noside noside,
 					   struct value *arg1,
 					   struct value *arg2);

+
+
 namespace expr
 {

-/* The Modula-2 "HIGH" operation.  */
+/* The PIM/ISO Modula-2 "HIGH (array)" operation which returns
+   the indice to the last element in a single dimension
+   array.  */
+
 class m2_unop_high_operation
   : public tuple_holding_operation<operation_up>
 {
@@ -56,9 +82,12 @@ class m2_unop_high_operation
   { return UNOP_HIGH; }
 };

-/* Subscripting for Modula-2.  */
-class m2_binop_subscript_operation
-  : public tuple_holding_operation<operation_up, operation_up>
+/* The PIM/ISO Modula-2 "SIZE (object)" operation which returns
+   the indice to the last element in a single dimension
+   array.  */
+
+class m2_unop_size_operation
+  : public tuple_holding_operation<operation_up>
 {
 public:

@@ -70,13 +99,11 @@ class m2_binop_subscript_operation
   {
     value *arg1 = std::get<0> (m_storage)->evaluate_with_coercion (exp,
 								   noside);
-    value *arg2 = std::get<1> (m_storage)->evaluate_with_coercion (exp,
-								   noside);
-    return eval_op_m2_subscript (expect_type, exp, noside, arg1, arg2);
+    return eval_op_m2_size (expect_type, exp, noside, arg1);
   }

   enum exp_opcode opcode () const override
-  { return BINOP_SUBSCRIPT; }
+  { return UNOP_SIZEOF; }
 };

 } /* namespace expr */
diff --git a/gdb/m2-exp.y b/gdb/m2-exp.y
index 02e3cf1b4e7..7d1cabbd75b 100644
--- a/gdb/m2-exp.y
+++ b/gdb/m2-exp.y
@@ -228,14 +228,13 @@ exp	:	TRUNC '(' exp ')'
 	;

 exp	:	TSIZE '(' exp ')'
-			{ pstate->wrap<unop_sizeof_operation> (); }
+			{ pstate->wrap<m2_unop_size_operation> (); }
 	;

 exp	:	SIZE exp       %prec UNARY
-			{ pstate->wrap<unop_sizeof_operation> (); }
+			{ pstate->wrap<m2_unop_size_operation> (); }
 	;

-
 exp	:	INC '(' exp ')'
 			{ pstate->wrap<preinc_operation> (); }
 	;
@@ -302,7 +301,7 @@ exp     :       exp '['
 			  gdb_assert (pstate->arglist_len > 0);
 			  std::vector<operation_up> args
 			    = pstate->pop_vector (pstate->end_arglist ());
-			  pstate->push_new<multi_subscript_operation>
+			  pstate->push_new<expr::m2_multi_subscript_operation>
 			    (pstate->pop (), std::move (args));
 			}
 	;
diff --git a/gdb/m2-lang.c b/gdb/m2-lang.c
index 17b21af5d95..3e3794c4e2c 100644
--- a/gdb/m2-lang.c
+++ b/gdb/m2-lang.c
@@ -24,11 +24,50 @@
 #include "parser-defs.h"
 #include "language.h"
 #include "varobj.h"
+
+#include "defs.h"
+#include "symtab.h"
+#include "gdbtypes.h"
+#include "value.h"
+#include "expression.h"
+#include "target.h"
+#include "frame.h"
+#include "gdbthread.h"
+#include "language.h"		/* For CAST_IS_CONVERSION.  */
+#include "cp-abi.h"
+#include "infcall.h"
+#include "objc-lang.h"
+#include "block.h"
+#include "parser-defs.h"
+#include "cp-support.h"
+#include "ui-out.h"
+#include "regcache.h"
+#include "user-regs.h"
+#include "valprint.h"
+#include "gdbsupport/gdb_obstack.h"
+#include "objfiles.h"
+#include "typeprint.h"
+#include <ctype.h>
+#include "expop.h"
+#include "c-exp.h"
+#include "inferior.h"
 #include "m2-lang.h"
 #include "c-lang.h"
 #include "valprint.h"
 #include "gdbarch.h"
 #include "m2-exp.h"
+#include "gdbsupport/traits.h"
+#include "gdbsupport/enum-flags.h"
+
+
+struct type *get_type (const char *name);
+struct value *
+m2_check_unbounded_array_end (value *arg1,
+			      struct type *unbounded_array_type,
+			      struct type *elttype);
+struct value *
+m2_get_unbounded_data (value *unbounded, struct type *unbounded_array_type);
+

 /* A helper function for UNOP_HIGH.  */

@@ -44,23 +83,173 @@ eval_op_m2_high (struct type *expect_type, struct expression *exp,
       arg1 = coerce_ref (arg1);
       struct type *type = check_typedef (value_type (arg1));

-      if (m2_is_unbounded_array (type))
+      if (m2_is_unbounded_array_single (type))
+	{
+	  if (value_subscript_level (arg1) > 0)
+	    error (_("unbounded array only has a single dimension"));
+	  else
+	    {
+	      struct value *temp = arg1;
+
+	      type = type->field (1).type ();
+	      /* i18n: Do not translate the "_m2_high" part!  */
+	      arg1 = value_struct_elt (&temp, {}, "_m2_high", NULL,
+				       _("unbounded structure "
+					 "missing _m2_high field"));
+
+	      if (value_type (arg1) != type)
+		arg1 = value_cast (type, arg1);
+	    }
+	}
+      else if (m2_is_unbounded_array_multiple (type))
 	{
 	  struct value *temp = arg1;
+	  int total_dim = m2_unbounded_array_dimensions (type);
+	  int dim = value_subscript_level (arg1) + 1;

-	  type = type->field (1).type ();
-	  /* i18n: Do not translate the "_m2_high" part!  */
-	  arg1 = value_struct_elt (&temp, {}, "_m2_high", NULL,
-				   _("unbounded structure "
-				     "missing _m2_high field"));
+	  if (dim <= m2_unbounded_array_dimensions (type))
+	    {
+	      char high_field_name[30];
+
+	      /* i18n: Do not translate the "_m2_high_%d" part!  */
+	      snprintf (high_field_name, sizeof (high_field_name),
+			"_m2_high_%d", dim);
+
+	      type = type->field (dim).type ();
+	      arg1 = value_struct_elt (&temp, {}, high_field_name, NULL,
+				       _("unbounded structure "
+					 "missing _m2_high field"));

-	  if (value_type (arg1) != type)
-	    arg1 = value_cast (type, arg1);
+	      if (value_type (arg1) != type)
+		arg1 = value_cast (type, arg1);
+	    }
+	  else
+	    {
+	      if (total_dim == 1)
+		error (_("unbounded array only has a single dimension"));
+	      else
+		error (_("unbounded array has fewer dimensions than"
+			 " HIGH is attempting to access"));
+	    }
 	}
     }
   return arg1;
 }

+struct value *
+m2_eval_multi_subscript_unbounded (struct type *expect_type, struct expression *exp,
+				   enum noside noside, value *arg1,
+				   gdb::array_view<value *> args,
+				   struct type *unbounded_array_type,
+				   struct type *elttype);
+
+
+static LONGEST
+m2_dim_multi (value *arg1,
+	      struct type *unbounded_array_type,
+	      int dimension)
+{
+  LONGEST multiplier = 1;
+  int total_dim = m2_unbounded_array_dimensions (unbounded_array_type);
+
+  for (int i = dimension + 1; i <= total_dim; i++)
+    multiplier *= (m2_unbounded_type_high (arg1, unbounded_array_type, i) + 1);
+  return multiplier;
+}
+
+struct value *
+m2_get_unbounded_data (value *unbounded, struct type *unbounded_array_type)
+{
+  const gdb_byte *valaddr = value_contents_for_printing (unbounded).data ();
+  CORE_ADDR addr = unpack_pointer (unbounded_array_type->field (0).type (),
+				   (unbounded_array_type->field (0).loc_bitpos () / 8) +
+				   valaddr);
+  return value_at_lazy (TYPE_TARGET_TYPE (unbounded_array_type->field (0).type ()),
+			addr);
+}
+
+
+/* Check to see if the unbounded array subscription has completed
+   the array data structure and if so return the data element.  */
+
+struct value *
+m2_check_unbounded_array_end (value *arg1,
+			      struct type *unbounded_array_type,
+			      struct type *elttype)
+{
+  int dimension = value_subscript_level (arg1);
+  if (dimension == m2_unbounded_array_dimensions (unbounded_array_type))
+    {
+      struct value *array_data = m2_get_unbounded_data (arg1, unbounded_array_type);
+      CORE_ADDR address = value_address (array_data);
+      LONGEST offset = value_unbounded_array_offset (arg1);
+      unsigned int eltlen = type_length_units (check_typedef (elttype));
+      struct value *element = value_at_lazy (elttype, address + offset * eltlen);
+      return element;
+    }
+  /* More dimensions to be accessed.  */
+  return arg1;
+}
+
+
+/* Helper function for MULTI_SUBSCRIPT.  */
+
+struct value *
+m2_eval_multi_subscript_unbounded (struct type *expect_type, struct expression *exp,
+				   enum noside noside, value *arg1,
+				   gdb::array_view<value *> args,
+				   struct type *unbounded_array_type,
+				   struct type *elttype)
+{
+  for (value *arg2 : args)
+    if (arg2 != NULL)
+      {
+	arg1 = value_inc_subscript_level (arg1);
+	int dimension = value_subscript_level (arg1);
+	LONGEST offset = value_unbounded_array_offset (arg1);
+	LONGEST dimension_multiplier = m2_dim_multi (arg1,
+						     unbounded_array_type,
+						     dimension);
+	LONGEST inner = value_as_long (arg2) * dimension_multiplier;
+        set_value_unbounded_array_offset (arg1, offset + inner);
+      }
+  return m2_check_unbounded_array_end (arg1, unbounded_array_type, elttype);
+}
+
+namespace expr
+{
+
+value *
+m2_multi_subscript_operation::evaluate (struct type *expect_type,
+					struct expression *exp,
+					enum noside noside)
+{
+  value *arg1 = std::get<0> (m_storage)->evaluate_with_coercion (exp, noside);
+  struct type *type = check_typedef (value_type (coerce_ref (arg1)));
+  arg1 = std::get<0> (m_storage)->evaluate_with_coercion (exp, noside);
+  std::vector<operation_up> &values = std::get<1> (m_storage);
+  value **argvec = XALLOCAVEC (struct value *, values.size ());
+  for (int ix = 0; ix < values.size (); ++ix)
+    argvec[ix] = values[ix]->evaluate_with_coercion (exp, noside);
+
+  if (m2_is_unbounded_array (type))
+    {
+      struct type *first_field_type = check_typedef (type->field (0).type ());  // re-factor
+      struct type *unresolved_elttype = TYPE_TARGET_TYPE (first_field_type);
+      struct type *elttype = check_typedef (unresolved_elttype);
+
+      return m2_eval_multi_subscript_unbounded (expect_type, exp, noside, arg1,
+						gdb::make_array_view (argvec,
+								      values.size ()),
+						type, elttype);
+    }
+  else
+    return eval_multi_subscript (expect_type, exp, noside, arg1,
+				 gdb::make_array_view (argvec, values.size ()));
+}
+
+}
+
 /* A helper function for BINOP_SUBSCRIPT.  */

 struct value *
@@ -86,7 +275,7 @@ eval_op_m2_subscript (struct type *expect_type, struct expression *exp,
       arg1 = value_struct_elt (&temp, {}, "_m2_contents", NULL,
 			       _("unbounded structure "
 				 "missing _m2_contents field"));
-
+
       if (value_type (arg1) != type)
 	arg1 = value_cast (type, arg1);

@@ -109,6 +298,55 @@ eval_op_m2_subscript (struct type *expect_type, struct expression *exp,
     return value_subscript (arg1, value_as_long (arg2));
 }

+/* A helper to look up a Modula-2 type, or fail.  This only works for
+   types defined by build_m2_types.  */
+
+struct type *get_type (const char *name)
+{
+  struct type *type;
+
+  type = language_lookup_primitive_type (language_def (language_m2),
+					 target_gdbarch (), name);
+  if (type == NULL)
+    error (_("Could not find Modula-2 base type %s"), name);
+  return type;
+}
+
+/* A helper function for UNOP_SIZE.  */
+
+struct value *
+eval_op_m2_size (struct type *expect_type, struct expression *exp,
+		 enum noside noside,
+		 struct value *arg1)
+{
+  if (noside == EVAL_AVOID_SIDE_EFFECTS)
+    return arg1;
+  else
+    {
+      arg1 = coerce_ref (arg1);
+      struct type *type = check_typedef (value_type (arg1));
+
+      if (m2_is_unbounded_array_single (type)
+	  || m2_is_unbounded_array_multiple (type))
+	{
+	  LONGEST element_size = TYPE_LENGTH (TYPE_TARGET_TYPE (type->field (0).type ()));
+	  struct type *result_type = type->field (1).type ();
+	  int total_dim = m2_unbounded_array_dimensions (type);
+	  int dim = value_subscript_level (arg1) + 1;
+	  while (dim <= total_dim)
+	    {
+	      LONGEST dimension_high = m2_unbounded_type_high (arg1, type, dim);
+	      element_size *= (dimension_high + 1);
+	      dim += 1;
+	    }
+	  return value_from_longest (result_type, element_size);
+	}
+      else
+	arg1 = value_from_longest (get_type ("CARDINAL"), TYPE_LENGTH (type));
+    }
+  return arg1;
+}
+
 \f

 /* Single instance of the M2 language.  */
@@ -154,7 +392,7 @@ m2_language::printchar (int c, struct type *type,

 void
 m2_language::printstr (struct ui_file *stream, struct type *elttype,
-			const gdb_byte *string, unsigned int length,
+			const gdb_byte *str, unsigned int length,
 			const char *encoding, int force_ellipses,
 			const struct value_print_options *options) const
 {
@@ -187,7 +425,7 @@ m2_language::printstr (struct ui_file *stream, struct type *elttype,

       rep1 = i + 1;
       reps = 1;
-      while (rep1 < length && string[rep1] == string[i])
+      while (rep1 < length && str[rep1] == str[i])
 	{
 	  ++rep1;
 	  ++reps;
@@ -200,7 +438,7 @@ m2_language::printstr (struct ui_file *stream, struct type *elttype,
 	      gdb_puts ("\", ", stream);
 	      in_quotes = 0;
 	    }
-	  printchar (string[i], elttype, stream);
+	  printchar (str[i], elttype, stream);
 	  gdb_printf (stream, " <repeats %u times>", reps);
 	  i = rep1 - 1;
 	  things_printed += options->repeat_count_threshold;
@@ -213,7 +451,7 @@ m2_language::printstr (struct ui_file *stream, struct type *elttype,
 	      gdb_puts ("\"", stream);
 	      in_quotes = 1;
 	    }
-	  emitchar (string[i], elttype, stream, '"');
+	  emitchar (str[i], elttype, stream, '"');
 	  ++things_printed;
 	}
     }
diff --git a/gdb/m2-lang.h b/gdb/m2-lang.h
index 86a093e5f1b..bbb0dcbe6a5 100644
--- a/gdb/m2-lang.h
+++ b/gdb/m2-lang.h
@@ -25,10 +25,15 @@ struct parser_state;

 /* Defined in m2-typeprint.c */
 extern void m2_print_type (struct type *, const char *, struct ui_file *, int,
-			   int, const struct type_print_options *);
+			   int, const struct type_print_options *,
+			   struct value *val = NULL);

 extern int m2_is_long_set (struct type *type);
 extern int m2_is_unbounded_array (struct type *type);
+extern int m2_is_unbounded_array_single (struct type *type);
+extern int m2_is_unbounded_array_multiple (struct type *type);
+extern int m2_unbounded_array_dimensions (type* type);
+extern LONGEST m2_unbounded_type_high (struct value *val, struct type *type, int dim);

 extern int get_long_set_bounds (struct type *type, LONGEST *low,
 				LONGEST *high);
@@ -75,9 +80,10 @@ class m2_language : public language_defn

   void print_type (struct type *type, const char *varstring,
 		   struct ui_file *stream, int show, int level,
-		   const struct type_print_options *flags) const override
+		   const struct type_print_options *flags,
+		   struct value *val) const override
   {
-    m2_print_type (type, varstring, stream, show, level, flags);
+    m2_print_type (type, varstring, stream, show, level, flags, val);
   }

   /* See language.h.  */
diff --git a/gdb/m2-typeprint.c b/gdb/m2-typeprint.c
index cbd35df0190..15e1ef92f2f 100644
--- a/gdb/m2-typeprint.c
+++ b/gdb/m2-typeprint.c
@@ -33,9 +33,17 @@
 #include "cp-abi.h"
 #include "cli/cli-style.h"

+static ULONGEST umax_of_size (int size);
+static LONGEST max_of_type (struct type *t);
+static LONGEST min_of_type (struct type *t);
+
+static void m2_print_array_bounds (struct type *type,
+				   struct ui_file *stream,
+				   int show, int level);
+
 static void m2_print_bounds (struct type *type,
 			     struct ui_file *stream, int show, int level,
-			     int print_high);
+			     bool print_high);

 static void m2_typedef (struct type *, struct ui_file *, int, int,
 			const struct type_print_options *);
@@ -56,7 +64,8 @@ static void m2_short_set (struct type *type, struct ui_file *stream,
 			  int show, int level);
 static int m2_long_set (struct type *type, struct ui_file *stream,
 			int show, int level, const struct type_print_options *flags);
-static int m2_unbounded_array (struct type *type, struct ui_file *stream,
+static int m2_unbounded_array (struct value *val,
+			       struct type *type, struct ui_file *stream,
 			       int show, int level,
 			       const struct type_print_options *flags);
 static void m2_record_fields (struct type *type, struct ui_file *stream,
@@ -67,16 +76,19 @@ static void m2_unknown (const char *s, struct type *type,
 int m2_is_long_set (struct type *type);
 int m2_is_long_set_of_type (struct type *type, struct type **of_type);
 int m2_is_unbounded_array (struct type *type);
+int m2_is_unbounded_array_single (struct type *type);
+int m2_is_unbounded_array_multiple (struct type *type);
+int m2_unbounded_array_dimensions (type* type);


 void
 m2_print_type (struct type *type, const char *varstring,
 	       struct ui_file *stream,
 	       int show, int level,
-	       const struct type_print_options *flags)
+	       const struct type_print_options *flags,
+	       struct value *val)
 {
   type = check_typedef (type);
-
   QUIT;

   stream->wrap_here (4);
@@ -94,7 +106,8 @@ m2_print_type (struct type *type, const char *varstring,

     case TYPE_CODE_STRUCT:
       if (m2_long_set (type, stream, show, level, flags)
-	  || m2_unbounded_array (type, stream, show, level, flags))
+	  || m2_unbounded_array (val,
+				 type, stream, show, level, flags))
 	break;
       m2_record_fields (type, stream, show, level, flags);
       break;
@@ -229,11 +242,7 @@ static void m2_array (struct type *type, struct ui_file *stream,
       && type->bounds ()->high.kind () != PROP_UNDEFINED)
     {
       if (type->index_type () != 0)
-	{
-	  m2_print_bounds (type->index_type (), stream, show, -1, 0);
-	  gdb_printf (stream, "..");
-	  m2_print_bounds (type->index_type (), stream, show, -1, 1);
-	}
+	m2_print_array_bounds (type->index_type (), stream, show, -1);
       else
 	gdb_puts (pulongest ((TYPE_LENGTH (type)
 			      / TYPE_LENGTH (TYPE_TARGET_TYPE (type)))),
@@ -304,11 +313,119 @@ m2_procedure (struct type *type, struct ui_file *stream,
     }
 }

+
+static bool
+m2_get_array_bounds (struct type *type, LONGEST *lo, LONGEST *hi)
+{
+  switch (type->code ())
+    {
+    case TYPE_CODE_RANGE:
+      {
+	const dynamic_prop &low = type->bounds ()->low;
+	if (low.kind () == PROP_CONST)
+	  *lo = low.const_val ();
+	else
+	  *lo = 0;
+	const dynamic_prop &high = type->bounds ()->high;
+	if (high.kind () == PROP_CONST)
+	  *hi = high.const_val ();
+	else
+	  return false;
+      }
+      break;
+    case TYPE_CODE_ENUM:
+      {
+	*hi = type->field (type->num_fields () - 1).loc_enumval ();
+	*lo = 0;
+      }
+      break;
+    case TYPE_CODE_BOOL:
+      {
+	*hi = 1;
+	*lo = 0;
+      }
+      break;
+    case TYPE_CODE_CHAR:
+    case TYPE_CODE_INT:
+      {
+	*lo = min_of_type (type);
+	*hi = max_of_type (type);
+      }
+      break;
+    default:
+      return false;
+    }
+  return true;
+}
+
+
+/* Maximum value of a SIZE-byte signed integer type.  */
+static LONGEST
+max_of_size (int size)
+{
+  LONGEST top_bit = (LONGEST) 1 << (size * 8 - 2);
+
+  return top_bit | (top_bit - 1);
+}
+
+/* Minimum value of a SIZE-byte signed integer type.  */
+static LONGEST
+min_of_size (int size)
+{
+  return -max_of_size (size) - 1;
+}
+
+/* Maximum value of a SIZE-byte unsigned integer type.  */
+static ULONGEST
+umax_of_size (int size)
+{
+  ULONGEST top_bit = (ULONGEST) 1 << (size * 8 - 1);
+
+  return top_bit | (top_bit - 1);
+}
+
+/* Maximum value of integral type T, as a signed quantity.  */
+static LONGEST
+max_of_type (struct type *t)
+{
+  if (t->is_unsigned ())
+    return (LONGEST) umax_of_size (TYPE_LENGTH (t));
+  else
+    return max_of_size (TYPE_LENGTH (t));
+}
+
+/* Minimum value of integral type T, as a signed quantity.  */
+static LONGEST
+min_of_type (struct type *t)
+{
+  if (t->is_unsigned ())
+    return 0;
+  else
+    return min_of_size (TYPE_LENGTH (t));
+}
+
+
+static void
+m2_print_array_bounds (struct type *type,
+		       struct ui_file *stream, int show, int level)
+{
+  LONGEST lo, hi;
+
+  if (m2_get_array_bounds (type, &lo, &hi))
+    {
+      struct type *target = TYPE_TARGET_TYPE (type);
+      print_type_scalar (target, lo, stream);
+      gdb_printf (stream, "..");
+      print_type_scalar (target, hi, stream);
+    }
+}
+
 static void
 m2_print_bounds (struct type *type,
 		 struct ui_file *stream, int show, int level,
-		 int print_high)
+		 bool print_high)
 {
+
   struct type *target = TYPE_TARGET_TYPE (type);

   if (type->num_fields () == 0)
@@ -476,49 +593,135 @@ m2_long_set (struct type *type, struct ui_file *stream, int show, int level,
   return 0;
 }

+/* m2_unbounded_array_dimensions - returns the number of dimension
+   an unbounded array contains.  If type is not an unbounded array
+   then zero is returned.  */
+
+int
+m2_unbounded_array_dimensions (struct type *type)
+{
+  if (m2_is_unbounded_array (type))
+    return type->num_fields () - 1;
+  return 0;
+}
+
 /* m2_is_unbounded_array - returns TRUE if, type, should be regarded
 			   as a Modula-2 unbounded ARRAY type.  */

 int
 m2_is_unbounded_array (struct type *type)
+{
+  return (type != NULL)
+    && (m2_is_unbounded_array_single (type)
+	|| m2_is_unbounded_array_multiple (type));
+}
+
+/* m2_is_unbounded_array_single - returns TRUE if, type, should be
+                                  regarded as a single dimension
+                                  unbounded ARRAY type.  */
+
+int
+m2_is_unbounded_array_single (struct type *type)
 {
   if (type->code () == TYPE_CODE_STRUCT)
     {
-      /*
-       *  check if we have a structure with exactly two fields named
-       *  _m2_contents and _m2_high.  It also checks to see if the
-       *  type of _m2_contents is a pointer.  The TYPE_TARGET_TYPE
-       *  of the pointer determines the unbounded ARRAY OF type.
-       */
-      if (type->num_fields () != 2)
+      if (type->num_fields () <= 1)
 	return 0;
+      // if (type->field (0).type ()->code () != TYPE_CODE_PTR)
+      // return 0;
       if (strcmp (type->field (0).name (), "_m2_contents") != 0)
 	return 0;
-      if (strcmp (type->field (1).name (), "_m2_high") != 0)
-	return 0;
-      if (type->field (0).type ()->code () != TYPE_CODE_PTR)
-	return 0;
-      return 1;
+      /* Older versions of gm2 used _m2_high for single dimensional
+	 unbounded array types.  */
+      if ((strcmp (type->field (1).name (), "_m2_high") == 0)
+	  && (type->num_fields () == 2))
+	return 1;  /*  Found an old style unbounded array.  */
     }
   return 0;
 }

+
+/* m2_is_unbounded_array_single - returns TRUE if, type, should be
+                                  regarded as a multi dimensional
+                                  unbounded ARRAY type.  */
+
+int
+m2_is_unbounded_array_multiple (struct type *type)
+{
+  /*
+   * Check if we have a structure with two fields or more named
+   * _m2_contents and _m2_high_%d (where %d is 1..n) for
+   * multidimensional unbounded arrays.
+   * It also checks to see if the type of _m2_contents is a pointer.
+   * The TYPE_TARGET_TYPE of the pointer determines the unbounded
+   * ARRAY OF type.
+   */
+  if (type->num_fields () <= 1)
+    return 0;
+  if (strcmp (type->field (0).name (), "_m2_contents") != 0)
+    return 0;
+  /*  Check second field upwards for the high template _m2_high_%d.  */
+  for (int i = 1; i < type->num_fields (); i++)
+    {
+      char high_field_name[30];
+
+      snprintf (high_field_name, sizeof (high_field_name),
+		"_m2_high_%d", i);
+      if (strcmp (type->field (i).name (), high_field_name) != 0)
+	return 0;  /* Doesn't match template, exit.  */
+    }
+  return 1;  /* New style unbounded array seen.  */
+}
+
 /* m2_unbounded_array - if the struct type matches a Modula-2 unbounded
 			parameter type then display the type as an
 			ARRAY OF type.  Returns TRUE if an unbounded
 			array type was detected.  */

 static int
-m2_unbounded_array (struct type *type, struct ui_file *stream, int show,
+m2_unbounded_array (struct value *val,
+		    struct type *type, struct ui_file *stream, int show,
 		    int level, const struct type_print_options *flags)
 {
   if (m2_is_unbounded_array (type))
     {
       if (show > 0)
 	{
-	  gdb_puts ("ARRAY OF ", stream);
+	  int total = m2_unbounded_array_dimensions (type);
+	  int dim = 1;
+
+	  if (val != NULL)
+	    dim += value_subscript_level (val);
+
+	  while (dim <= total)
+	    {
+	      LONGEST hi = -1;  /* init for gcc -Wall.  */
+	      bool found_high = false;
+
+	      gdb_puts ("ARRAY ", stream);
+	      if (val)
+		{
+		  try
+		    {
+		      hi = m2_unbounded_type_high (val, type, dim);
+		      found_high = true;
+		    }
+		  catch (const gdb_exception_error &e)
+		    {
+		      /* It might be the case that we cannot access the high
+			 fields of the unbounded array type.  */
+		    }
+		}
+	      if (found_high)
+		{
+		  gdb_printf (stream, "<0..%ld", hi);
+		  gdb_printf (stream, "> ");
+		}
+	      gdb_puts ("OF ", stream);
+	      dim += 1;
+	    }
 	  m2_print_type (TYPE_TARGET_TYPE (type->field (0).type ()),
-			 "", stream, 0, level, flags);
+			 "", stream, show, level, flags);
 	}
       return 1;
     }
@@ -581,7 +784,7 @@ m2_record_fields (struct type *type, struct ui_file *stream, int show,
 	    }
 	  gdb_printf (stream, ";\n");
 	}
-
+
       gdb_printf (stream, "%*sEND ", level, "");
     }
 }
diff --git a/gdb/m2-valprint.c b/gdb/m2-valprint.c
index 9e3f16a896f..f9f56a75852 100644
--- a/gdb/m2-valprint.c
+++ b/gdb/m2-valprint.c
@@ -30,15 +30,19 @@
 #include "target.h"
 #include "cli/cli-style.h"

+
+LONGEST
+m2_unbounded_type_high (struct value *val, struct type *type, int dim);
+
+LONGEST
+m2_size_unbounded (struct value *value, struct type *unbounded_array_type,
+		   int dimension);
+
+
 static int print_unpacked_pointer (struct type *type,
 				   CORE_ADDR address, CORE_ADDR addr,
 				   const struct value_print_options *options,
 				   struct ui_file *stream);
-static void
-m2_print_array_contents (struct value *val,
-			 struct ui_file *stream, int recurse,
-			 const struct value_print_options *options,
-			 int len);


 /* get_long_set_bounds - assigns the bounds of the long set to low and
@@ -63,6 +67,34 @@ get_long_set_bounds (struct type *type, LONGEST *low, LONGEST *high)
   return 0;
 }

+/* Return the high field from dimension dim in unbounded array val.
+   It returns -1 if the high value is unavailable or type is not
+   an unbounded array or does not have the dimension requested.  */
+
+LONGEST
+m2_unbounded_type_high (struct value *val, struct type *type, int dim)
+{
+  LONGEST len = -1;
+
+  if (val
+      && ((m2_is_unbounded_array_single (type) && (dim == 1))
+	  || (m2_is_unbounded_array_multiple (type) && (dim >= 1))))
+    {
+      LONGEST high_offset = type->field (dim).loc_bitpos () / 8;
+      struct type *high_type = type->field (dim).type ();
+      const gdb_byte *valaddr = value_contents_for_printing (val).data ();
+
+      if (value_bytes_available (val, high_offset, TYPE_LENGTH (high_type)))
+	{
+	  len = unpack_long (type->field (dim).type (),
+			     (type->field (dim).loc_bitpos () / 8) +
+			     valaddr);
+	}
+    }
+  return len;
+}
+
+
 static void
 m2_print_long_set (struct type *type, const gdb_byte *valaddr,
 		   int embedded_offset, CORE_ADDR address,
@@ -155,29 +187,168 @@ m2_print_long_set (struct type *type, const gdb_byte *valaddr,
     }
 }

+/* m2_size_unbounded returns the no of bytes used to contain the dimension
+   of the unbounded_array_type.  */
+
+LONGEST
+m2_size_unbounded (struct value *value, struct type *unbounded_array_type,
+		   int dimension)
+{
+  struct type *field_type = unbounded_array_type->field (0).type ();
+  LONGEST element_size = TYPE_LENGTH (TYPE_TARGET_TYPE (field_type));
+  int total_dimensions = m2_unbounded_array_dimensions (unbounded_array_type);
+  while (dimension < total_dimensions)
+    {
+      dimension += 1;
+      LONGEST dimension_high = m2_unbounded_type_high (value,
+						       unbounded_array_type,
+						       dimension);
+      element_size *= (dimension_high + 1);
+    }
+  return element_size;
+}
+
+/* Print the unbounded array contents.  It checks for an array of characters
+   and user level options (for stopping at a nul character).  */
+
 static void
-m2_print_unbounded_array (struct value *value,
-			  struct ui_file *stream, int recurse,
-			  const struct value_print_options *options)
+m2_print_unbounded_array_contents (struct value *value,
+				   struct ui_file *stream,
+				   int recurse,
+				   const struct value_print_options *options,
+				   int dimension_high,
+				   struct value *array_data,
+				   LONGEST offset,
+				   struct type *elttype)
 {
-  CORE_ADDR addr;
-  LONGEST len;
-  struct value *val;
+  /* For an array of chars, print with string syntax.  */
+  if (TYPE_LENGTH (elttype) == 1 &&
+      ((elttype->code () == TYPE_CODE_INT)
+       || ((current_language->la_language == language_m2)
+	   && (elttype->code () == TYPE_CODE_CHAR)))
+      && (options->format == 0 || options->format == 's'))
+    {
+      const gdb_byte *valaddr = value_contents_for_printing (array_data).data ();

-  struct type *type = check_typedef (value_type (value));
-  const gdb_byte *valaddr = value_contents_for_printing (value).data ();
+      /* If requested, look for the first null char and only print
+	 elements up to it.  */
+      if (options->stop_print_at_null)
+	{
+	  unsigned int temp_len;
+
+	  /* Look for a NULL char.  */
+	  for (temp_len = 0;
+	       (valaddr[temp_len]
+		&& temp_len < dimension_high && temp_len < options->print_max);
+	       temp_len++);
+	  dimension_high = temp_len;
+	}

-  addr = unpack_pointer (type->field (0).type (),
-			 (type->field (0).loc_bitpos () / 8) +
-			 valaddr);
+      unsigned int eltlen = type_length_units (check_typedef (elttype));
+      gdb_puts ("\"", stream);
+      for (unsigned int i = 0; i <= dimension_high; i++)
+	{
+	  CORE_ADDR address = value_address (array_data);
+	  struct value *element = value_at_lazy (elttype, address + (offset + i) * eltlen);
+	  const gdb_byte *addr = value_contents_for_printing (element).data ();
+	  gdb_printf (stream, "%c", *addr);
+	}
+      gdb_puts ("\"", stream);
+    }
+  else
+    {
+      unsigned int eltlen = type_length_units (check_typedef (elttype));
+      gdb_printf (stream, "{");
+      for (unsigned int i = 0; i <= dimension_high; i++)
+	{
+	  CORE_ADDR address = value_address (array_data);
+	  struct value *element = value_at_lazy (elttype, address + (i + offset) * eltlen);
+	  common_val_print (element, stream, recurse + 1, options,
+			    current_language);
+	  gdb_printf (stream, ", ");
+	}
+      gdb_printf (stream, "}");
+    }
+}
+
+
+/* m2_print_unbounded_array_dim recursively print the array contents
+   dimension by dimension.  It uses common_val_print for the data
+   element.  */
+
+static void
+m2_print_unbounded_array_dim (struct value *value,
+			      struct ui_file *stream, int recurse,
+			      const struct value_print_options *options,
+			      struct type *unbounded_array_type,
+			      int dimension,
+			      struct value *array_data,
+			      LONGEST offset,
+			      struct type *elttype)
+{
+  if (dimension > m2_unbounded_array_dimensions (unbounded_array_type))
+    {
+      unsigned int eltlen = type_length_units (check_typedef (elttype));
+      CORE_ADDR address = value_address (array_data);
+      struct value *element = value_at_lazy (elttype, address + offset * eltlen);
+      common_val_print (element, stream, recurse + 1, options,
+			current_language);
+    }
+  else
+    {
+      LONGEST dimension_high = m2_unbounded_type_high (value,
+						       unbounded_array_type,
+						       dimension);
+      if (dimension == m2_unbounded_array_dimensions (unbounded_array_type))
+	m2_print_unbounded_array_contents (value, stream, recurse,
+					   options, dimension_high,
+					   array_data, offset, elttype);
+      else
+	{
+	  /* Otherwise we need to calculate the address of the next dimension.  */
+	  LONGEST size_of_unbounded = m2_size_unbounded (value,
+							 unbounded_array_type,
+							 dimension);
+	  LONGEST offset_dimension = size_of_unbounded / TYPE_LENGTH (elttype);
+
+	  for (LONGEST i = 0; i <= dimension_high; i++)
+	    {
+	      m2_print_unbounded_array_dim (value, stream, recurse, options,
+					    unbounded_array_type, dimension + 1,
+					    array_data, offset, elttype);
+	      if (i < dimension_high)
+		offset += offset_dimension;
+	    }
+	}
+    }
+}

-  val = value_at_lazy (TYPE_TARGET_TYPE (type->field (0).type ()),
-		       addr);
-  len = unpack_field_as_long (type, valaddr, 1);
+/* m2_print_unbounded_array describe the unbounded array including
+   its bounds using value.  */

-  gdb_printf (stream, "{");
-  m2_print_array_contents (val, stream, recurse, options, len);
-  gdb_printf (stream, ", HIGH = %d}", (int) len);
+static void
+m2_print_unbounded_array (struct value *value,
+			  struct ui_file *stream, int recurse,
+			  const struct value_print_options *options)
+{
+  struct type *unbounded_array_type = check_typedef (value_type (value));
+  const gdb_byte *valaddr = value_contents_for_printing (value).data ();
+  struct type *field0_type = unbounded_array_type->field (0).type ();
+  CORE_ADDR addr = unpack_pointer (field0_type,
+		   (unbounded_array_type->field (0).loc_bitpos () / 8) +
+				   valaddr);
+  struct value *array_data = value_at_lazy (TYPE_TARGET_TYPE (field0_type),
+					    addr);
+  struct type *type = check_typedef (field0_type);
+  struct type *unresolved_elttype = TYPE_TARGET_TYPE (type);
+  struct type *elttype = check_typedef (unresolved_elttype);
+
+  m2_print_unbounded_array_dim (value, stream, recurse, options,
+				unbounded_array_type,
+				1 + value_subscript_level (value),
+				array_data,
+				value_unbounded_array_offset (value),
+				elttype);
 }

 static int
@@ -217,10 +388,11 @@ print_unpacked_pointer (struct type *type,
       return val_print_string (TYPE_TARGET_TYPE (type), NULL, addr, -1,
 			       stream, options);
     }
-
+
   return 0;
 }

+
 static void
 print_variable_at_address (struct type *type,
 			   const gdb_byte *valaddr,
@@ -235,7 +407,7 @@ print_variable_at_address (struct type *type,
   gdb_printf (stream, "[");
   gdb_puts (paddress (gdbarch, addr), stream);
   gdb_printf (stream, "] : ");
-
+
   if (elttype->code () != TYPE_CODE_UNDEF)
     {
       struct value *deref_val =
@@ -248,39 +420,6 @@ print_variable_at_address (struct type *type,
 }


-/* m2_print_array_contents - prints out the contents of an
-			     array up to a max_print values.
-			     It prints arrays of char as a string
-			     and all other data types as comma
-			     separated values.  */
-
-static void
-m2_print_array_contents (struct value *val,
-			 struct ui_file *stream, int recurse,
-			 const struct value_print_options *options,
-			 int len)
-{
-  struct type *type = check_typedef (value_type (val));
-
-  if (TYPE_LENGTH (type) > 0)
-    {
-      /* For an array of chars, print with string syntax.  */
-      if (TYPE_LENGTH (type) == 1 &&
-	  ((type->code () == TYPE_CODE_INT)
-	   || ((current_language->la_language == language_m2)
-	       && (type->code () == TYPE_CODE_CHAR)))
-	  && (options->format == 0 || options->format == 's'))
-	val_print_string (type, NULL, value_address (val), len+1, stream,
-			  options);
-      else
-	{
-	  gdb_printf (stream, "{");
-	  value_print_array_elements (val, stream, recurse, options, 0);
-	  gdb_printf (stream, "}");
-	}
-    }
-}
-
 /* Decorations for Modula 2.  */

 static const struct generic_val_print_decorations m2_decorations =
@@ -316,6 +455,7 @@ m2_language::value_print_inner (struct value *val, struct ui_file *stream,
 	{
 	  elttype = check_typedef (TYPE_TARGET_TYPE (type));
 	  len = TYPE_LENGTH (type) / TYPE_LENGTH (elttype);
+
 	  /* For an array of chars, print with string syntax.  */
 	  if (TYPE_LENGTH (elttype) == 1 &&
 	      ((elttype->code () == TYPE_CODE_INT)
diff --git a/gdb/objc-lang.c b/gdb/objc-lang.c
index ed13097f7a8..08b6f998f70 100644
--- a/gdb/objc-lang.c
+++ b/gdb/objc-lang.c
@@ -268,7 +268,8 @@ class objc_language : public language_defn

   void print_type (struct type *type, const char *varstring,
 		   struct ui_file *stream, int show, int level,
-		   const struct type_print_options *flags) const override
+		   const struct type_print_options *flags,
+		   struct value *val) const override
   {
     c_print_type (type, varstring, stream, show, level, flags);
   }
diff --git a/gdb/opencl-lang.c b/gdb/opencl-lang.c
index 5145cd4062e..135338d630e 100644
--- a/gdb/opencl-lang.c
+++ b/gdb/opencl-lang.c
@@ -956,7 +956,8 @@ class opencl_language : public language_defn

   void print_type (struct type *type, const char *varstring,
 		   struct ui_file *stream, int show, int level,
-		   const struct type_print_options *flags) const override
+		   const struct type_print_options *flags,
+		   struct value *val) const override
   {
     /* We nearly always defer to C type printing, except that vector types
        are considered primitive in OpenCL, and should always be printed
diff --git a/gdb/p-lang.h b/gdb/p-lang.h
index eae73e19346..4b04bf6fde2 100644
--- a/gdb/p-lang.h
+++ b/gdb/p-lang.h
@@ -90,7 +90,8 @@ class pascal_language : public language_defn

   void print_type (struct type *type, const char *varstring,
 		   struct ui_file *stream, int show, int level,
-		   const struct type_print_options *flags) const override;
+		   const struct type_print_options *flags,
+		   struct value *val = NULL) const override;

   /* See language.h.  */

diff --git a/gdb/p-typeprint.c b/gdb/p-typeprint.c
index f222f01b429..d3775875908 100644
--- a/gdb/p-typeprint.c
+++ b/gdb/p-typeprint.c
@@ -39,7 +39,8 @@
 void
 pascal_language::print_type (struct type *type, const char *varstring,
 			     struct ui_file *stream, int show, int level,
-			     const struct type_print_options *flags) const
+			     const struct type_print_options *flags,
+			     struct value *val) const
 {
   enum type_code code;
   int demangled_args;
diff --git a/gdb/rust-lang.c b/gdb/rust-lang.c
index 836ea37f153..71ca592d4c5 100644
--- a/gdb/rust-lang.c
+++ b/gdb/rust-lang.c
@@ -1581,7 +1581,8 @@ rust_language::language_arch_info (struct gdbarch *gdbarch,
 void
 rust_language::print_type (struct type *type, const char *varstring,
 			   struct ui_file *stream, int show, int level,
-			   const struct type_print_options *flags) const
+			   const struct type_print_options *flags,
+			   struct value *val) const
 {
   print_offset_data podata (flags);
   rust_internal_print_type (type, varstring, stream, show, level,
diff --git a/gdb/rust-lang.h b/gdb/rust-lang.h
index 0e89b8822cb..7bc6c62b344 100644
--- a/gdb/rust-lang.h
+++ b/gdb/rust-lang.h
@@ -104,7 +104,8 @@ class rust_language : public language_defn

   void print_type (struct type *type, const char *varstring,
 		   struct ui_file *stream, int show, int level,
-		   const struct type_print_options *flags) const override;
+		   const struct type_print_options *flags,
+		   struct value *val = NULL) const override;

   /* See language.h.  */

diff --git a/gdb/std-operator.def b/gdb/std-operator.def
index 5e6cad06379..dd0e44a8089 100644
--- a/gdb/std-operator.def
+++ b/gdb/std-operator.def
@@ -237,7 +237,7 @@ OP (UNOP_ALIGNOF)		/* Unary alignof (followed by expression) */
 OP (UNOP_PLUS)			/* Unary plus */

 OP (UNOP_ABS)
-OP (UNOP_HIGH)
+OP (UNOP_HIGH)                  /* Modula-2 HIGH.  */

 OP (OP_BOOL)			/* Modula-2 builtin BOOLEAN type */

diff --git a/gdb/testsuite/gdb.modula2/unbounded-array.exp b/gdb/testsuite/gdb.modula2/unbounded-array.exp
index 596bd16aaa2..83043fc7789 100644
--- a/gdb/testsuite/gdb.modula2/unbounded-array.exp
+++ b/gdb/testsuite/gdb.modula2/unbounded-array.exp
@@ -30,11 +30,11 @@ if ![runto foo] then {

 gdb_test "set lang modula-2" ".*does not match.*" "switch to modula-2"

-# gdb test "ptype a" ".*ARRAY OF.*" "print out unbounded ARRAY type"
+gdb_test "ptype a" ".*ARRAY.*OF.*" "describe unbounded ARRAY type"

 gdb_test "print HIGH(a)" ".*= 4.*" "print the last legal element of array a"

-gdb_test "print a" ".*abcde.*HIGH.*4.*"    "print unbounded array contents"
+gdb_test "print a" ".*abcde.*"    "print unbounded array contents"

 gdb_test "print/c a\[0\]" ".*a.*" "print the 1st element of array a"
 gdb_test "print/c a\[1\]" ".*b.*" "print the 2nd element of array a"
diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index 0ef7e1215a4..7dc54d468df 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -2338,6 +2338,12 @@ proc skip_d_tests {} {
     return 0
 }

+# Return a 1 if I don't even want to try to test Modula2.
+
+proc skip_modula2_tests {} {
+    return 0
+}
+
 # Return 1 to skip Rust tests, 0 to try them.
 proc skip_rust_tests {} {
     if { ![isnative] } {
diff --git a/gdb/typeprint.c b/gdb/typeprint.c
index 3356bdde2e7..0ae070163a0 100644
--- a/gdb/typeprint.c
+++ b/gdb/typeprint.c
@@ -370,7 +370,7 @@ typedef_hash_table::find_typedef (const struct type_print_options *flags,

 \f

-/* Print a description of a type in the format of a
+/* Print a description of a type in the format of a
    typedef for the current language.
    NEW is the new name for a type TYPE.  */

@@ -575,10 +575,11 @@ whatis_exp (const char *exp, int show)
       type_print (real_type, "", gdb_stdout, -1);
       if (! full)
 	gdb_printf (" (incomplete object)");
-      gdb_printf (" */\n");
+      gdb_printf (" */\n");
     }

-  current_language->print_type (type, "", gdb_stdout, show, 0, &flags);
+  current_language->print_type (type, "", gdb_stdout, show, 0,
+				&flags, val);
   gdb_printf ("\n");
 }

diff --git a/gdb/valarith.c b/gdb/valarith.c
index 36d30f161f6..68266aa984b 100644
--- a/gdb/valarith.c
+++ b/gdb/valarith.c
@@ -147,6 +147,7 @@ value_subscript (struct value *array, LONGEST index)
 {
   bool c_style = current_language->c_style_arrays_p ();
   struct type *tarray;
+  struct value *result = NULL;

   array = coerce_ref (array);
   tarray = check_typedef (value_type (array));
@@ -160,7 +161,10 @@ value_subscript (struct value *array, LONGEST index)
 	lowerbound = 0;

       if (VALUE_LVAL (array) != lval_memory)
-	return value_subscripted_rvalue (array, index, *lowerbound);
+	{
+	  result = value_subscripted_rvalue (array, index, *lowerbound);
+	  return value_inc_subscript_level (result);
+	}

       gdb::optional<LONGEST> upperbound
 	= get_discrete_high_bound (range_type);
@@ -169,7 +173,10 @@ value_subscript (struct value *array, LONGEST index)
 	upperbound = -1;

       if (index >= *lowerbound && index <= *upperbound)
-	return value_subscripted_rvalue (array, index, *lowerbound);
+	{
+	  result = value_subscripted_rvalue (array, index, *lowerbound);
+	  return value_inc_subscript_level (result);
+	}

       if (!c_style)
 	{
@@ -186,7 +193,11 @@ value_subscript (struct value *array, LONGEST index)
     }

   if (c_style)
-    return value_ind (value_ptradd (array, index));
+    {
+      result = value_ind (value_ptradd (array, index));
+      set_value_subscript_level (result, value_subscript_level (array) + 1);
+      return result;
+    }
   else
     error (_("not an array or string"));
 }
diff --git a/gdb/value.c b/gdb/value.c
index 24f1151c03f..0da8935619d 100644
--- a/gdb/value.c
+++ b/gdb/value.c
@@ -291,6 +291,12 @@ struct value
      The caller must arrange for a call to value_free later.  */
   int reference_count = 1;

+  /* The number of subscript levels which have been accessed.  */
+  int subscript_level = 0;
+
+  /* The unbounded array offset.  */
+  LONGEST unbounded_array_offset = 0;
+
   /* Only used for bitfields; the containing value.  This allows a
      single read from the target when displaying multiple
      bitfields.  */
@@ -1148,6 +1154,47 @@ set_value_parent (struct value *value, struct value *parent)
   value->parent = value_ref_ptr::new_reference (parent);
 }

+/* Return the subscript_level the value has accessed.  */
+
+int
+value_subscript_level (const struct value *value)
+{
+  return value->subscript_level;
+}
+
+/* Set the subscript_level of a value.  */
+
+void
+set_value_subscript_level (struct value *value, int level)
+{
+  value->subscript_level = level;
+}
+
+/* Increment the subscript_level of a value.  */
+
+struct value *
+value_inc_subscript_level (struct value *value)
+{
+  set_value_subscript_level (value, value_subscript_level (value) + 1);
+  return value;
+}
+
+/* Set the unbounded_array_offset in value to offset.  */
+
+void
+set_value_unbounded_array_offset (struct value *value, LONGEST offset)
+{
+  value->unbounded_array_offset = offset;
+}
+
+/* Return the unbounded_array_offset field in value.  */
+
+LONGEST
+value_unbounded_array_offset (struct value *value)
+{
+  return value->unbounded_array_offset;
+}
+
 gdb::array_view<gdb_byte>
 value_contents_raw (struct value *value)
 {
diff --git a/gdb/value.h b/gdb/value.h
index 7e1eec22413..09a51ee4da4 100644
--- a/gdb/value.h
+++ b/gdb/value.h
@@ -241,6 +241,28 @@ extern void set_value_pointed_to_offset (struct value *value, LONGEST val);
 extern LONGEST value_embedded_offset (const struct value *value);
 extern void set_value_embedded_offset (struct value *value, LONGEST val);

+/* Return the subscript_level the value has accessed.  */
+
+extern int value_subscript_level (const struct value *value);
+
+/* Set the subscript_level of a value.  */
+
+extern void set_value_subscript_level (struct value *value, int level);
+
+/* Increment the subscript_level of a value and return the value.  */
+
+extern struct value *value_inc_subscript_level (struct value *value);
+
+/* Return the unbounded_array_offset field in value.  */
+
+extern LONGEST
+value_unbounded_array_offset (struct value *value);
+
+/* Set the unbounded_array_offset in value to offset.  */
+
+extern void
+set_value_unbounded_array_offset (struct value *value, LONGEST offset);
+
 /* For lval_computed values, this structure holds functions used to
    retrieve and set the value (or portions of the value).

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v2 05/18] Don't use wchar_printable in print_wchar
  2022-02-22 15:36   ` Andrew Burgess
@ 2022-10-10 16:39     ` Tom Tromey
  0 siblings, 0 replies; 30+ messages in thread
From: Tom Tromey @ 2022-10-10 16:39 UTC (permalink / raw)
  To: Andrew Burgess via Gdb-patches; +Cc: Tom Tromey, Andrew Burgess

>>>>> "Andrew" == Andrew Burgess via Gdb-patches <gdb-patches@sourceware.org> writes:

Andrew> I wonder if we should leave some comment here that just says something
Andrew> like:

Andrew>   /* If any additional cases are added to this switch block, then the
Andrew>      function wchar_printable will likely need updating too.  */

I made this change.

Tom

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v2 00/18] Refactor character printing
  2022-02-17 22:05 [PATCH v2 00/18] Refactor character printing Tom Tromey
                   ` (17 preceding siblings ...)
  2022-02-17 22:05 ` [PATCH v2 18/18] Simplify Fortran string printing Tom Tromey
@ 2022-10-10 17:37 ` Tom Tromey
  18 siblings, 0 replies; 30+ messages in thread
From: Tom Tromey @ 2022-10-10 17:37 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

>>>>> "Tom" == Tom Tromey <tom@tromey.com> writes:

Tom> Here's v2 of the series to refactor character printing.
Tom> v1 is here:

Tom> https://sourceware.org/pipermail/gdb-patches/2022-February/185918.html

Tom> I believe this version addresses all the review comments.  The big
Tom> changes are that the emission API is changed as Andrew suggested, and
Tom> the latent bug fix with escaping is now done only for hex escapes.

I'm checking in the first 6 patches from this series.  They were all
reviewed by Andrew, and I needed them for another series I'm working on.
I rebased and regression tested them again.

Tom

^ permalink raw reply	[flat|nested] 30+ messages in thread

end of thread, other threads:[~2022-10-10 17:38 UTC | newest]

Thread overview: 30+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-02-17 22:05 [PATCH v2 00/18] Refactor character printing Tom Tromey
2022-02-17 22:05 ` [PATCH v2 01/18] Fix latent quote char bug in generic_printstr Tom Tromey
2022-02-17 22:05 ` [PATCH v2 02/18] Boolify need_escape in generic_emit_char Tom Tromey
2022-02-17 22:05 ` [PATCH v2 03/18] Remove c_emit_char Tom Tromey
2022-02-17 22:05 ` [PATCH v2 04/18] Remove c_printstr Tom Tromey
2022-02-17 22:05 ` [PATCH v2 05/18] Don't use wchar_printable in print_wchar Tom Tromey
2022-02-22 15:36   ` Andrew Burgess
2022-10-10 16:39     ` Tom Tromey
2022-02-17 22:05 ` [PATCH v2 06/18] Fix a latent bug " Tom Tromey
2022-02-17 22:05 ` [PATCH v2 07/18] Remove language_defn::emitchar Tom Tromey
2022-02-17 22:05 ` [PATCH v2 08/18] Add gdb_iswcntrl Tom Tromey
2022-02-17 22:05 ` [PATCH v2 09/18] Include \0 in printable wide characters Tom Tromey
2022-02-23 13:49   ` Andrew Burgess
2022-02-23 22:28     ` Tom Tromey
2022-02-23 23:59       ` Tom Tromey
2022-02-17 22:05 ` [PATCH v2 10/18] Use a ui_file in print_wchar Tom Tromey
2022-02-17 22:05 ` [PATCH v2 11/18] Add an emitter callback to generic_printstr and generic_emit_char Tom Tromey
2022-02-17 22:05 ` [PATCH v2 12/18] Add a default encoding to generic_emit_char and generic_printstr Tom Tromey
2022-02-17 22:05 ` [PATCH v2 13/18] Change generic_emit_char to print the quotes Tom Tromey
2022-02-17 22:05 ` [PATCH v2 14/18] Use generic_emit_char in Rust Tom Tromey
2022-02-17 22:05 ` [PATCH v2 15/18] Use generic_emit_char in Ada Tom Tromey
2022-02-17 22:05 ` [PATCH v2 16/18] Use generic_emit_char in Modula-2 Tom Tromey
2022-02-23 20:17   ` Gaius Mulley
2022-03-16 12:29   ` [PATCH] Additional modula2 tests Gaius Mulley
2022-04-07 14:21     ` Tom Tromey
2022-04-09 23:16       ` Gaius Mulley
2022-04-11 19:45   ` [PATCH v1] Array access in Modula-2 Gaius Mulley
2022-02-17 22:05 ` [PATCH v2 17/18] Use generic_emit_char in Pascal Tom Tromey
2022-02-17 22:05 ` [PATCH v2 18/18] Simplify Fortran string printing Tom Tromey
2022-10-10 17:37 ` [PATCH v2 00/18] Refactor character printing Tom Tromey

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).