PR gdb/14288
authorKeith Seitz <keiths@redhat.com>
Sat, 10 Nov 2012 20:19:01 +0000 (20:19 +0000)
committerKeith Seitz <keiths@redhat.com>
Sat, 10 Nov 2012 20:19:01 +0000 (20:19 +0000)
        * c-valprint.c (c_val_print): For character arrays
        with "print null" option on, print ellipses if
        the output is truncated and the next character is not \000.
        * valprint.c (MAX_WCHARS): Define.
        (WCHAR_BUFLEN): Likewise.
        (WCHAR_BUFLEN_MAX): Likewise.
        (struct converted_character): New structure.
        (count_next_character): New function.
        (print_converted_chars_to_obstack): New function.
        (generic_printstr): Rewrite using count_next_character
        and print_converted_chars_to_obstack.

        * gdb.base/printcmds.c: Add invalid_XXX globals
        for repeated byte tests.
        * gdb.base/printcmds.exp (test_repeat_bytes): New procedure.
        * gdb.base/wchar.c (main): Add and construct a wchar_t
        array with repeated characters.
        * gdb.base/wchar.exp: Add repeated character tests.

gdb/ChangeLog
gdb/c-valprint.c
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.base/printcmds.c
gdb/testsuite/gdb.base/printcmds.exp
gdb/testsuite/gdb.base/wchar.c
gdb/testsuite/gdb.base/wchar.exp
gdb/valprint.c

index b8fc3969c9a64160d24a4f2589a20ed4335983cf..4175f2ff7cbad90d0270cb167f471fe12a58bc1f 100644 (file)
@@ -1,3 +1,18 @@
+2012-11-10  Keith Seitz  <keiths@redhat.com>
+
+       PR gdb/14288
+       * c-valprint.c (c_val_print): For character arrays
+       with "print null" option on, print ellipses if
+       the output is truncated and the next character is not \000.
+       * valprint.c (MAX_WCHARS): Define.
+       (WCHAR_BUFLEN): Likewise.
+       (WCHAR_BUFLEN_MAX): Likewise.
+       (struct converted_character): New structure.
+       (count_next_character): New function.
+       (print_converted_chars_to_obstack): New function.
+       (generic_printstr): Rewrite using count_next_character
+       and print_converted_chars_to_obstack.
+
 2012-11-10  Stephane Carrez  <Stephane.Carrez@gmail.com>
 
        * tui/tui.c (tui_rl_command_key): Switch to TUI_ONE_COMMAND_MODE
index 7a1bb02be630d141b8c178ef00a7d2bbc933413f..dada9e2ac2cc5a436decd60ae3b563d3f80ce2d0 100644 (file)
@@ -177,6 +177,8 @@ c_val_print (struct type *type, const gdb_byte *valaddr,
                                   TARGET_CHAR_BIT * embedded_offset,
                                   TARGET_CHAR_BIT * TYPE_LENGTH (type)))
            {
+             int force_ellipses = 0;
+
              /* If requested, look for the first null char and only
                 print elements up to it.  */
              if (options->stop_print_at_null)
@@ -191,12 +193,26 @@ c_val_print (struct type *type, const gdb_byte *valaddr,
                                                     eltlen, byte_order) != 0);
                       ++temp_len)
                    ;
+
+                 /* Force LA_PRINT_STRING to print ellipses if
+                    we've printed the maximum characters and
+                    the next character is not \000.  */
+                 if (temp_len == options->print_max && temp_len < len)
+                   {
+                     ULONGEST val
+                       = extract_unsigned_integer (valaddr + embedded_offset
+                                                   + temp_len * eltlen,
+                                                   eltlen, byte_order);
+                     if (val != 0)
+                       force_ellipses = 1;
+                   }
+
                  len = temp_len;
                }
 
              LA_PRINT_STRING (stream, unresolved_elttype,
                               valaddr + embedded_offset, len,
-                              NULL, 0, options);
+                              NULL, force_ellipses, options);
              i = len;
            }
          else
index 26f2b3f08f4e31470e9c5f5669266f62c4c14170..f42ca37d43cda62236999a2e6034f80ecdae32f1 100644 (file)
@@ -1,3 +1,13 @@
+2012-11-10  Keith Seitz  <keiths@redhat.com>
+
+       PR gdb/14288
+       * gdb.base/printcmds.c: Add invalid_XXX globals
+       for repeated byte tests.
+       * gdb.base/printcmds.exp (test_repeat_bytes): New procedure.
+       * gdb.base/wchar.c (main): Add and construct a wchar_t
+       array with repeated characters.
+       * gdb.base/wchar.exp: Add repeated character tests.
+
 2012-11-09  Andrew Burgess  <aburgess@broadcom.com>
 
        * gdb.mi/mi-disassemble.exp: Expect fullname field in mi
index 743734b4a5c25cc79017743b92c738b0dea7059f..d80c13d95af7c0979b4496071f3c2c4273854bc7 100644 (file)
@@ -122,6 +122,98 @@ struct some_struct
   }
 };
 
+/* The following variables are used for testing byte repeat sequences.
+   The variable names are encoded: invalid_XYZ where:
+   X = start
+   Y = invalid
+   Z = end
+
+   Each of X and Z can be "E" (empty), "S" (single), "L" (long single),
+   or "R" (repeat).
+
+   Y can be either any of the above except "E" (otherwise there is nothing
+   to test).  */
+char invalid_ESE[] = "\240";
+char invalid_SSE[] = "a\240";
+char invalid_LSE[] = "abaabbaaabbb\240";
+char invalid_RSE[] = "aaaaaaaaaaaaaaaaaaaa\240";
+char invalid_ESS[] = "\240c";
+char invalid_SSS[] = "a\240c";
+char invalid_LSS[] = "abaabbaaabbb\240c";
+char invalid_RSS[] = "aaaaaaaaaaaaaaaaaaaa\240c";
+char invalid_ESL[] = "\240cdccddcccddd";
+char invalid_SSL[] = "a\240cdccddcccddd";
+char invalid_LSL[] = "abaabbaaabbb\240cdccddcccddd";
+char invalid_RSL[] = "aaaaaaaaaaaaaaaaaaaa\240cdccddcccddd";
+char invalid_ESR[] = "\240cccccccccccccccccccc";
+char invalid_SSR[] = "a\240cccccccccccccccccccc";
+char invalid_LSR[] = "abaabbaaabbb\240cccccccccccccccccccc";
+char invalid_RSR[] = "aaaaaaaaaaaaaaaaaaaa\240cccccccccccccccccccc";
+char invalid_ELE[] = "\240\240\240\240";
+char invalid_SLE[] = "a\240\240\240\240";
+char invalid_LLE[] = "abaabbaaabbb\240\240\240\240";
+char invalid_RLE[] = "aaaaaaaaaaaaaaaaaaaa\240\240\240\240";
+char invalid_ELS[] = "\240\240\240\240c";
+char invalid_SLS[] = "a\240\240\240\240c";
+char invalid_LLS[] = "abaabbaaabbb\240\240\240\240c";
+char invalid_RLS[] = "aaaaaaaaaaaaaaaaaaaa\240\240\240\240c";
+char invalid_ELL[] = "\240\240\240\240cdccddcccddd";
+char invalid_SLL[] = "a\240\240\240\240cdccddcccddd";
+char invalid_LLL[] = "abaabbaaabbb\240\240\240\240cdccddcccddd";
+char invalid_RLL[] = "aaaaaaaaaaaaaaaaaaaa\240\240\240\240cdccddcccddd";
+char invalid_ELR[] = "\240\240\240\240cccccccccccccccccccc";
+char invalid_SLR[] = "a\240\240\240\240cccccccccccccccccccc";
+char invalid_LLR[] = "abaabbaaabbb\240\240\240\240cccccccccccccccccccc";
+char invalid_RLR[] = "aaaaaaaaaaaaaaaaaaaa\240\240\240\240cccccccccccccccccccc";
+char invalid_ERE[] = ""
+  "\240\240\240\240\240\240\240\240\240\240"
+  "\240\240\240\240\240\240\240\240\240\240";
+char invalid_LRE[] = "abaabbaaabbb"
+  "\240\240\240\240\240\240\240\240\240\240"
+  "\240\240\240\240\240\240\240\240\240\240";
+char invalid_RRE[] = "aaaaaaaaaaaaaaaaaaaa"
+  "\240\240\240\240\240\240\240\240\240\240"
+  "\240\240\240\240\240\240\240\240\240\240";
+char invalid_ERS[] = ""
+  "\240\240\240\240\240\240\240\240\240\240"
+  "\240\240\240\240\240\240\240\240\240\240c";
+char invalid_ERL[] = ""
+  "\240\240\240\240\240\240\240\240\240\240"
+  "\240\240\240\240\240\240\240\240\240\240cdccddcccddd";
+char invalid_ERR[] = ""
+  "\240\240\240\240\240\240\240\240\240\240"
+  "\240\240\240\240\240\240\240\240\240\240cccccccccccccccccccc";
+char invalid_SRE[] = "a"
+  "\240\240\240\240\240\240\240\240\240\240"
+  "\240\240\240\240\240\240\240\240\240\240";
+char invalid_SRS[] = "a"
+  "\240\240\240\240\240\240\240\240\240\240"
+  "\240\240\240\240\240\240\240\240\240\240c";
+char invalid_SRL[] = "a"
+  "\240\240\240\240\240\240\240\240\240\240"
+  "\240\240\240\240\240\240\240\240\240\240cdccddcccddd";
+char invalid_SRR[] = "a"
+  "\240\240\240\240\240\240\240\240\240\240"
+  "\240\240\240\240\240\240\240\240\240\240cccccccccccccccccccc";
+char invalid_LRS[] = "abaabbaaabbb"
+  "\240\240\240\240\240\240\240\240\240\240"
+  "\240\240\240\240\240\240\240\240\240\240c";
+char invalid_LRL[] = "abaabbaaabbb"
+  "\240\240\240\240\240\240\240\240\240\240"
+  "\240\240\240\240\240\240\240\240\240\240cdccddcccddd";
+char invalid_LRR[] = "abaabbaaabbb"
+  "\240\240\240\240\240\240\240\240\240\240"
+  "\240\240\240\240\240\240\240\240\240\240cccccccccccccccccccc";
+char invalid_RRS[] = "aaaaaaaaaaaaaaaaaaaa"
+  "\240\240\240\240\240\240\240\240\240\240"
+  "\240\240\240\240\240\240\240\240\240\240c";
+char invalid_RRL[] = "aaaaaaaaaaaaaaaaaaaa"
+  "\240\240\240\240\240\240\240\240\240\240"
+  "\240\240\240\240\240\240\240\240\240\240cdccddcccddd";
+char invalid_RRR[] = "aaaaaaaaaaaaaaaaaaaa"
+  "\240\240\240\240\240\240\240\240\240\240"
+  "\240\240\240\240\240\240\240\240\240\240cccccccccccccccccccc";
+
 /* -- */
 
 int main ()
index 4f76cec98f7c599c400fa76e2ad184371fcdcf22..4dfe90f3f119ee618f4711689b8e9f37750b2ad4 100644 (file)
@@ -792,6 +792,76 @@ proc gdb_test_escape_braces { args } {
     gdb_test [lindex $args 0] $esc_pattern [lindex $args 2]
 }
 
+proc test_repeat_bytes {} {
+    set start(E) {}
+    set start(S) {a}
+    set start(L) {abaabbaaabbb}
+    set start(R) {'a' <repeats 20 times>}
+    set end(E) {}
+    set end(S) {c}
+    set end(L) {cdccddcccddd}
+    set end(R) {'c' <repeats 20 times>}
+    set invalid(S) {\\240}
+    set invalid(L) {\\240\\240\\240\\240}
+    set invalid(R) {'\\240' <repeats 20 times>}
+
+    set fmt(SSS) "\"%s%s%s\""
+    set fmt(SSR) "\"%s%s\", %s"
+    set fmt(SRS) "\"%s\", %s, \"%s\""
+    set fmt(RSS) "%s, \"%s%s\""
+    set fmt(RSR) "%s, \"%s\", %s"
+    set fmt(SRR) "\"%s\", %s, %s"
+    set fmt(RRS) "%s, %s, \"%s\""
+    set fmt(RRR) "%s, %s, %s"
+
+    set fmt(RS) "%s, \"%s\""
+    set fmt(RR) "%s, %s"
+    set fmt(SR) "\"%s\", %s"
+    set fmt(SS) "\"%s%s\""
+
+    # Test the various permutations of invalid characters
+    foreach i [array names invalid] {
+       set I $i
+
+       if {$i == "L"} {
+           set i "S"
+       }
+
+       foreach s [array names start] {
+           set S $s
+
+           if {$s == "L"} {
+               set s "S"
+           }
+
+
+           foreach e [array names end] {
+               set E $e
+
+               if {$e == "L"} {
+                   set e "S"
+               }
+
+               # Skip E*E.
+               if {$s == "E" && $e == "E"} { continue }
+
+               # Special cases...
+               if {$s == "E"} {
+                   set result [format $fmt($i$e) $invalid($I) $end($E)]
+               } elseif {$e == "E"} {
+                   set result [format $fmt($s$i) $start($S) $invalid($I)]
+               } else {
+                   set result [format $fmt($s$i$e) \
+                                   $start($S) $invalid($I) $end($E)]
+               }
+
+               send_log "expecting: = $result\n"
+               gdb_test "print invalid_$S$I$E" "= $result"
+           }
+       }
+    }
+}
+
 # Start with a fresh gdb.
 
 gdb_exit
@@ -851,3 +921,4 @@ test_print_enums
 test_printf
 test_printf_with_dfp
 test_print_symbol
+test_repeat_bytes
index 22f5beb534152840cc4a0ff6a0c558050000170a..7bdfd68677eeeacfd48aa22ef209278acb3570af 100644 (file)
@@ -25,11 +25,21 @@ do_nothing (wchar_t *c)
 int
 main (void)
 {
+  int i;
   wchar_t narrow = 97;
   wchar_t single = 0xbeef;
   wchar_t simple[] = L"facile";
   wchar_t difficile[] = { 0xdead, 0xbeef, 0xfeed, 0xface};
   wchar_t mixed[] = {L'f', 0xdead, L'a', L'c', 0xfeed, 0xface};
+  wchar_t *cent = L"\242";
+  wchar_t repeat[128];
+  wchar_t *repeat_p = repeat;
+
+  repeat[0] = 0;
+  wcscat (repeat, L"A");
+  for (i = 0; i < 21; ++i)
+    wcscat (repeat, cent);
+  wcscat (repeat, L"B");
 
   do_nothing (&narrow); /* START */
   do_nothing (&single);
index 2451d92edcda1d67d1ff33b3e50b70759b3e8ad2..4c7ebf1fc401392da196618bcec2998ca234a193 100644 (file)
@@ -36,3 +36,22 @@ gdb_test "print simple\[2\]" "= 99 L'c'"
 
 gdb_test "print difficile\[2\]" "= 65261 L'\\\\xfeed'"
 
+set cent "\\\\242"
+gdb_test "print repeat" "= L\"A\", '$cent' <repeats 21 times>, \"B.*"
+
+global hex
+gdb_test "print repeat_p" \
+    "= $hex L\"A\", '$cent' <repeats 21 times>, \"B\""
+
+gdb_test_no_output "set print null on"
+
+gdb_test "print repeat" "= L\"A\", '$cent' <repeats 21 times>, \"B\"" \
+    "print repeat (print null on)"
+
+gdb_test_no_output "set print elements 3"
+
+gdb_test "print repeat" "= L\"A$cent$cent\"\.\.\." \
+    "print repeat (print elements 3)"
+
+gdb_test "print repeat_p" "= $hex L\"A$cent$cent\"\.\.\." \
+    "print repeat_p (print elements 3)"
index 583329d3baf4d2d180dac7d9c803750ba5f3fb30..8600be03dce2fdb0d68fa8aede7bca546df0845c 100644 (file)
 
 #include <errno.h>
 
+/* Maximum number of wchars returned from wchar_iterate.  */
+#define MAX_WCHARS 4
+
+/* A convenience macro to compute the size of a wchar_t buffer containing X
+   characters.  */
+#define WCHAR_BUFLEN(X) ((X) * sizeof (gdb_wchar_t))
+
+/* Character buffer size saved while iterating over wchars.  */
+#define WCHAR_BUFLEN_MAX WCHAR_BUFLEN (MAX_WCHARS)
+
+/* A structure to encapsulate state information from iterated
+   character conversions.  */
+struct converted_character
+{
+  /* The number of characters converted.  */
+  int num_chars;
+
+  /* The result of the conversion.  See charset.h for more.  */
+  enum wchar_iterate_result result;
+
+  /* The (saved) converted character(s).  */
+  gdb_wchar_t chars[WCHAR_BUFLEN_MAX];
+
+  /* The first converted target byte.  */
+  const gdb_byte *buf;
+
+  /* The number of bytes converted.  */
+  size_t buflen;
+
+  /* How many times this character(s) is repeated.  */
+  int repeat_count;
+};
+
+typedef struct converted_character converted_character_d;
+DEF_VEC_O (converted_character_d);
+
+
 /* Prototypes for local functions */
 
 static int partial_memory_read (CORE_ADDR memaddr, gdb_byte *myaddr,
@@ -2045,6 +2082,253 @@ generic_emit_char (int c, struct type *type, struct ui_file *stream,
   do_cleanups (cleanups);
 }
 
+/* Return the repeat count of the next character/byte in ITER,
+   storing the result in VEC.  */
+
+static int
+count_next_character (struct wchar_iterator *iter,
+                     VEC (converted_character_d) **vec)
+{
+  struct converted_character *current;
+
+  if (VEC_empty (converted_character_d, *vec))
+    {
+      struct converted_character tmp;
+      gdb_wchar_t *chars;
+
+      tmp.num_chars
+       = wchar_iterate (iter, &tmp.result, &chars, &tmp.buf, &tmp.buflen);
+      if (tmp.num_chars > 0)
+       {
+         gdb_assert (tmp.num_chars < MAX_WCHARS);
+         memcpy (tmp.chars, chars, tmp.num_chars * sizeof (gdb_wchar_t));
+       }
+      VEC_safe_push (converted_character_d, *vec, &tmp);
+    }
+
+  current = VEC_last (converted_character_d, *vec);
+
+  /* Count repeated characters or bytes.  */
+  current->repeat_count = 1;
+  if (current->num_chars == -1)
+    {
+      /* EOF  */
+      return -1;
+    }
+  else
+    {
+      gdb_wchar_t *chars;
+      struct converted_character d;
+      int repeat;
+
+      d.repeat_count = 0;
+
+      while (1)
+       {
+         /* Get the next character.  */
+         d.num_chars
+           = wchar_iterate (iter, &d.result, &chars, &d.buf, &d.buflen);
+
+         /* If a character was successfully converted, save the character
+            into the converted character.  */
+         if (d.num_chars > 0)
+           {
+             gdb_assert (d.num_chars < MAX_WCHARS);
+             memcpy (d.chars, chars, WCHAR_BUFLEN (d.num_chars));
+           }
+
+         /* Determine if the current character is the same as this
+            new character.  */
+         if (d.num_chars == current->num_chars && d.result == current->result)
+           {
+             /* There are two cases to consider:
+
+                1) Equality of converted character (num_chars > 0)
+                2) Equality of non-converted character (num_chars == 0)  */
+             if ((current->num_chars > 0
+                  && memcmp (current->chars, d.chars,
+                             WCHAR_BUFLEN (current->num_chars)) == 0)
+                 || (current->num_chars == 0
+                     && current->buflen == d.buflen
+                     && memcmp (current->buf, d.buf, current->buflen) == 0))
+               ++current->repeat_count;
+             else
+               break;
+           }
+         else
+           break;
+       }
+
+      /* Push this next converted character onto the result vector.  */
+      repeat = current->repeat_count;
+      VEC_safe_push (converted_character_d, *vec, &d);
+      return repeat;
+    }
+}
+
+/* Print the characters in CHARS to the OBSTACK.  QUOTE_CHAR is the quote
+   character to use with string output.  WIDTH is the size of the output
+   character type.  BYTE_ORDER is the the target byte order.  OPTIONS
+   is the user's print options.  */
+
+static void
+print_converted_chars_to_obstack (struct obstack *obstack,
+                                 VEC (converted_character_d) *chars,
+                                 int quote_char, int width,
+                                 enum bfd_endian byte_order,
+                                 const struct value_print_options *options)
+{
+  unsigned int idx;
+  struct 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;
+
+  /* Set the start state.  */
+  idx = 0;
+  last = state = START;
+  elem = NULL;
+
+  while (1)
+    {
+      switch (state)
+       {
+       case START:
+         /* Nothing to do.  */
+         break;
+
+       case SINGLE:
+         {
+           int j;
+
+           /* We are outputting a single character
+              (< options->repeat_count_threshold).  */
+
+           if (last != SINGLE)
+             {
+               /* We were outputting some other type of content, so we
+                  must output and a comma and a quote.  */
+               if (last != START)
+                 obstack_grow_wstr (obstack, LCST (", "));
+               if (options->inspect_it)
+                 obstack_grow_wstr (obstack, LCST ("\\"));
+               obstack_grow (obstack, &wide_quote_char, sizeof (gdb_wchar_t));
+             }
+           /* 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);
+               else
+                 print_wchar (gdb_WEOF, elem->buf, elem->buflen, width,
+                              byte_order, obstack, quote_char, &need_escape);
+             }
+         }
+         break;
+
+       case REPEAT:
+         {
+           int j;
+           char *s;
+
+           /* We are outputting a character with a repeat count
+              greater than options->repeat_count_threshold.  */
+
+           if (last == SINGLE)
+             {
+               /* We were outputting a single string.  Terminate the
+                  string.  */
+               if (options->inspect_it)
+                 obstack_grow_wstr (obstack, LCST ("\\"));
+               obstack_grow (obstack, &wide_quote_char, sizeof (gdb_wchar_t));
+             }
+           if (last != START)
+             obstack_grow_wstr (obstack, LCST (", "));
+
+           /* Output the character and repeat string.  */
+           obstack_grow_wstr (obstack, LCST ("'"));
+           if (elem->result == wchar_iterate_ok)
+             print_wchar (elem->chars[0], elem->buf, elem->buflen, width,
+                          byte_order, obstack, quote_char, &need_escape);
+           else
+             print_wchar (gdb_WEOF, elem->buf, elem->buflen, width,
+                          byte_order, obstack, quote_char, &need_escape);
+           obstack_grow_wstr (obstack, LCST ("'"));
+           s = xstrprintf (_(" <repeats %u times>"), elem->repeat_count);
+           for (j = 0; s[j]; ++j)
+             {
+               gdb_wchar_t w = gdb_btowc (s[j]);
+               obstack_grow (obstack, &w, sizeof (gdb_wchar_t));
+             }
+           xfree (s);
+         }
+         break;
+
+       case INCOMPLETE:
+         /* We are outputting an incomplete sequence.  */
+         if (last == SINGLE)
+           {
+             /* If we were outputting a string of SINGLE characters,
+                terminate the quote.  */
+             if (options->inspect_it)
+               obstack_grow_wstr (obstack, LCST ("\\"));
+             obstack_grow (obstack, &wide_quote_char, sizeof (gdb_wchar_t));
+           }
+         if (last != START)
+           obstack_grow_wstr (obstack, LCST (", "));
+
+         /* 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_grow_wstr (obstack, LCST (">"));
+
+         /* We do not attempt to outupt anything after this.  */
+         state = FINISH;
+         break;
+
+       case FINISH:
+         /* All done.  If we were outputting a string of SINGLE
+            characters, the string must be terminated.  Otherwise,
+            REPEAT and INCOMPLETE are always left properly terminated.  */
+         if (last == SINGLE)
+           {
+             if (options->inspect_it)
+               obstack_grow_wstr (obstack, LCST ("\\"));
+             obstack_grow (obstack, &wide_quote_char, sizeof (gdb_wchar_t));
+           }
+
+         return;
+       }
+
+      /* Get the next element and state.  */
+      last = state;
+      if (state != FINISH)
+       {
+         elem = VEC_index (converted_character_d, chars, idx++);
+         switch (elem->result)
+           {
+           case wchar_iterate_ok:
+           case wchar_iterate_invalid:
+             if (elem->repeat_count > options->repeat_count_threshold)
+               state = REPEAT;
+             else
+               state = SINGLE;
+             break;
+
+           case wchar_iterate_incomplete:
+             state = INCOMPLETE;
+             break;
+
+           case wchar_iterate_eof:
+             state = FINISH;
+             break;
+           }
+       }
+    }
+}
+
 /* Print the character string STRING, printing at most LENGTH
    characters.  LENGTH is -1 if the string is nul terminated.  TYPE is
    the type of each character.  OPTIONS holds the printing options;
@@ -2064,16 +2348,13 @@ generic_printstr (struct ui_file *stream, struct type *type,
 {
   enum bfd_endian byte_order = gdbarch_byte_order (get_type_arch (type));
   unsigned int i;
-  unsigned int things_printed = 0;
-  int in_quotes = 0;
-  int need_comma = 0;
   int width = TYPE_LENGTH (type);
   struct obstack wchar_buf, output;
   struct cleanup *cleanup;
   struct wchar_iterator *iter;
   int finished = 0;
-  int need_escape = 0;
-  gdb_wchar_t wide_quote_char = gdb_btowc (quote_char);
+  struct converted_character *last;
+  VEC (converted_character_d) *converted_chars;
 
   if (length == -1)
     {
@@ -2107,166 +2388,46 @@ generic_printstr (struct ui_file *stream, struct type *type,
   /* Arrange to iterate over the characters, in wchar_t form.  */
   iter = make_wchar_iterator (string, length * width, encoding, width);
   cleanup = make_cleanup_wchar_iterator (iter);
+  converted_chars = NULL;
+  make_cleanup (VEC_cleanup (converted_character_d), &converted_chars);
 
-  /* WCHAR_BUF is the obstack we use to represent the string in
-     wchar_t form.  */
-  obstack_init (&wchar_buf);
-  make_cleanup_obstack_free (&wchar_buf);
-
-  while (!finished && things_printed < options->print_max)
+  /* Convert characters until the string is over or the maximum
+     number of printed characters has been reached.  */
+  i = 0;
+  while (i < options->print_max)
     {
-      int num_chars;
-      enum wchar_iterate_result result;
-      gdb_wchar_t *chars;
-      const gdb_byte *buf;
-      size_t buflen;
+      int r;
 
       QUIT;
 
-      if (need_comma)
-       {
-         obstack_grow_wstr (&wchar_buf, LCST (", "));
-         need_comma = 0;
-       }
-
-      num_chars = wchar_iterate (iter, &result, &chars, &buf, &buflen);
-      /* We only look at repetitions when we were able to convert a
-        single character in isolation.  This makes the code simpler
-        and probably does the sensible thing in the majority of
-        cases.  */
-      while (num_chars == 1 && things_printed < options->print_max)
-       {
-         /* Count the number of repetitions.  */
-         unsigned int reps = 0;
-         gdb_wchar_t current_char = chars[0];
-         const gdb_byte *orig_buf = buf;
-         int orig_len = buflen;
-
-         if (need_comma)
-           {
-             obstack_grow_wstr (&wchar_buf, LCST (", "));
-             need_comma = 0;
-           }
-
-         while (num_chars == 1 && current_char == chars[0])
-           {
-             num_chars = wchar_iterate (iter, &result, &chars,
-                                        &buf, &buflen);
-             ++reps;
-           }
+      /* Grab the next character and repeat count.  */
+      r = count_next_character (iter, &converted_chars);
 
-         /* Emit CURRENT_CHAR according to the repetition count and
-            options.  */
-         if (reps > options->repeat_count_threshold)
-           {
-             if (in_quotes)
-               {
-                 if (options->inspect_it)
-                   obstack_grow_wstr (&wchar_buf, LCST ("\\"));
-                 obstack_grow (&wchar_buf, &wide_quote_char,
-                               sizeof (gdb_wchar_t));
-                 obstack_grow_wstr (&wchar_buf, LCST (", "));
-                 in_quotes = 0;
-               }
-             obstack_grow_wstr (&wchar_buf, LCST ("'"));
-             need_escape = 0;
-             print_wchar (current_char, orig_buf, orig_len, width,
-                          byte_order, &wchar_buf, '\'', &need_escape);
-             obstack_grow_wstr (&wchar_buf, LCST ("'"));
-             {
-               /* Painful gyrations.  */
-               int j;
-               char *s = xstrprintf (_(" <repeats %u times>"), reps);
-
-               for (j = 0; s[j]; ++j)
-                 {
-                   gdb_wchar_t w = gdb_btowc (s[j]);
-                   obstack_grow (&wchar_buf, &w, sizeof (gdb_wchar_t));
-                 }
-               xfree (s);
-             }
-             things_printed += options->repeat_count_threshold;
-             need_comma = 1;
-           }
-         else
-           {
-             /* Saw the character one or more times, but fewer than
-                the repetition threshold.  */
-             if (!in_quotes)
-               {
-                 if (options->inspect_it)
-                   obstack_grow_wstr (&wchar_buf, LCST ("\\"));
-                 obstack_grow (&wchar_buf, &wide_quote_char,
-                               sizeof (gdb_wchar_t));
-                 in_quotes = 1;
-                 need_escape = 0;
-               }
+      /* If less than zero, the end of the input string was reached.  */
+      if (r < 0)
+       break;
 
-             while (reps-- > 0)
-               {
-                 print_wchar (current_char, orig_buf,
-                              orig_len, width,
-                              byte_order, &wchar_buf,
-                              quote_char, &need_escape);
-                 ++things_printed;
-               }
-           }
-       }
+      /* Otherwise, add the count to the total print count and get
+        the next character.  */
+      i += r;
+    }
 
-      /* NUM_CHARS and the other outputs from wchar_iterate are valid
-        here regardless of which branch was taken above.  */
-      if (num_chars < 0)
-       {
-         /* Hit EOF.  */
-         finished = 1;
-         break;
-       }
+  /* Get the last element and determine if the entire string was
+     processed.  */
+  last = VEC_last (converted_character_d, converted_chars);
+  finished = (last->result == wchar_iterate_eof);
 
-      switch (result)
-       {
-       case wchar_iterate_invalid:
-         if (!in_quotes)
-           {
-             if (options->inspect_it)
-               obstack_grow_wstr (&wchar_buf, LCST ("\\"));
-             obstack_grow (&wchar_buf, &wide_quote_char,
-                           sizeof (gdb_wchar_t));
-             in_quotes = 1;
-           }
-         need_escape = 0;
-         print_wchar (gdb_WEOF, buf, buflen, width, byte_order,
-                      &wchar_buf, quote_char, &need_escape);
-         break;
+  /* Ensure that CONVERTED_CHARS is terminated.  */
+  last->result = wchar_iterate_eof;
 
-       case wchar_iterate_incomplete:
-         if (in_quotes)
-           {
-             if (options->inspect_it)
-               obstack_grow_wstr (&wchar_buf, LCST ("\\"));
-             obstack_grow (&wchar_buf, &wide_quote_char,
-                           sizeof (gdb_wchar_t));
-             obstack_grow_wstr (&wchar_buf, LCST (","));
-             in_quotes = 0;
-           }
-         obstack_grow_wstr (&wchar_buf,
-                            LCST (" <incomplete sequence "));
-         print_wchar (gdb_WEOF, buf, buflen, width,
-                      byte_order, &wchar_buf,
-                      0, &need_escape);
-         obstack_grow_wstr (&wchar_buf, LCST (">"));
-         finished = 1;
-         break;
-       }
-    }
+  /* WCHAR_BUF is the obstack we use to represent the string in
+     wchar_t form.  */
+  obstack_init (&wchar_buf);
+  make_cleanup_obstack_free (&wchar_buf);
 
-  /* Terminate the quotes if necessary.  */
-  if (in_quotes)
-    {
-      if (options->inspect_it)
-       obstack_grow_wstr (&wchar_buf, LCST ("\\"));
-      obstack_grow (&wchar_buf, &wide_quote_char,
-                   sizeof (gdb_wchar_t));
-    }
+  /* Print the output string to the obstack.  */
+  print_converted_chars_to_obstack (&wchar_buf, converted_chars, quote_char,
+                                   width, byte_order, options);
 
   if (force_ellipses || !finished)
     obstack_grow_wstr (&wchar_buf, LCST ("..."));