+/* Return true if print_wchar can display W without resorting to a
+ numeric escape, false otherwise. */
+
+static int
+wchar_printable (gdb_wchar_t w)
+{
+ return (gdb_iswprint (w)
+ || w == LCST ('\a') || w == LCST ('\b')
+ || w == LCST ('\f') || w == LCST ('\n')
+ || w == LCST ('\r') || w == LCST ('\t')
+ || w == LCST ('\v'));
+}
+
+/* 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));
+ }
+}
+
+/* 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. */
+
+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, int *need_escapep)
+{
+ int need_escape = *need_escapep;
+
+ *need_escapep = 0;
+ if (gdb_iswprint (w) && (!need_escape || (!gdb_iswdigit (w)
+ && w != LCST ('8')
+ && w != LCST ('9'))))
+ {
+ 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));
+ }
+ else
+ {
+ switch (w)
+ {
+ case LCST ('\a'):
+ obstack_grow_wstr (output, LCST ("\\a"));
+ break;
+ case LCST ('\b'):
+ obstack_grow_wstr (output, LCST ("\\b"));
+ break;
+ case LCST ('\f'):
+ obstack_grow_wstr (output, LCST ("\\f"));
+ break;
+ case LCST ('\n'):
+ obstack_grow_wstr (output, LCST ("\\n"));
+ break;
+ case LCST ('\r'):
+ obstack_grow_wstr (output, LCST ("\\r"));
+ break;
+ case LCST ('\t'):
+ obstack_grow_wstr (output, LCST ("\\t"));
+ break;
+ case LCST ('\v'):
+ obstack_grow_wstr (output, LCST ("\\v"));
+ break;
+ default:
+ {
+ int i;
+
+ for (i = 0; i + width <= orig_len; i += width)
+ {
+ char octal[30];
+ ULONGEST value;
+
+ value = extract_unsigned_integer (&orig[i], width,
+ byte_order);
+ /* 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));
+ else
+ xsnprintf (octal, sizeof (octal), "\\x%lx", (long) value);
+ 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);
+ append_string_as_wide (octal, output);
+ ++i;
+ }
+
+ *need_escapep = 1;
+ }
+ break;
+ }
+ }
+}
+
+/* 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. */
+
+void
+generic_emit_char (int c, struct type *type, struct ui_file *stream,
+ int quoter, const char *encoding)
+{
+ enum bfd_endian byte_order
+ = gdbarch_byte_order (get_type_arch (type));
+ struct obstack wchar_buf, output;
+ struct cleanup *cleanups;
+ gdb_byte *buf;
+ struct wchar_iterator *iter;
+ int need_escape = 0;
+
+ buf = alloca (TYPE_LENGTH (type));
+ pack_long (buf, type, c);
+
+ iter = make_wchar_iterator (buf, TYPE_LENGTH (type),
+ encoding, TYPE_LENGTH (type));
+ cleanups = make_cleanup_wchar_iterator (iter);
+
+ /* This holds the printable form of the wchar_t data. */
+ obstack_init (&wchar_buf);
+ make_cleanup_obstack_free (&wchar_buf);
+
+ while (1)
+ {
+ int num_chars;
+ gdb_wchar_t *chars;
+ const gdb_byte *buf;
+ size_t buflen;
+ int print_escape = 1;
+ enum wchar_iterate_result result;
+
+ num_chars = wchar_iterate (iter, &result, &chars, &buf, &buflen);
+ if (num_chars < 0)
+ break;
+ if (num_chars > 0)
+ {
+ /* 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. */
+ int i;
+
+ print_escape = 0;
+ for (i = 0; i < num_chars; ++i)
+ if (!wchar_printable (chars[i]))
+ {
+ print_escape = 1;
+ break;
+ }
+
+ 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);
+ }
+ }
+
+ /* 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);
+ }
+
+ /* The output in the host encoding. */
+ obstack_init (&output);
+ make_cleanup_obstack_free (&output);
+
+ convert_between_encodings (INTERMEDIATE_ENCODING, host_charset (),
+ (gdb_byte *) obstack_base (&wchar_buf),
+ obstack_object_size (&wchar_buf),
+ sizeof (gdb_wchar_t), &output, translit_char);
+ obstack_1grow (&output, '\0');
+
+ fputs_filtered (obstack_base (&output), 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 (", "));
+ 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. */
+ 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. */
+ 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)
+ 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;
+ 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.
+ QUOTE_CHAR is the character to print at each end of the string. If
+ C_STYLE_TERMINATOR is true, and the last character is 0, then it is
+ omitted. */
+
+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)
+{
+ enum bfd_endian byte_order = gdbarch_byte_order (get_type_arch (type));
+ unsigned int i;
+ int width = TYPE_LENGTH (type);
+ struct obstack wchar_buf, output;
+ struct cleanup *cleanup;
+ struct wchar_iterator *iter;
+ int finished = 0;
+ struct converted_character *last;
+ VEC (converted_character_d) *converted_chars;
+
+ if (length == -1)
+ {
+ unsigned long current_char = 1;
+
+ for (i = 0; current_char; ++i)
+ {
+ QUIT;
+ current_char = extract_unsigned_integer (string + i * width,
+ width, byte_order);
+ }
+ length = i;
+ }
+
+ /* 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 (c_style_terminator
+ && !force_ellipses
+ && length > 0
+ && (extract_unsigned_integer (string + (length - 1) * width,
+ width, byte_order) == 0))
+ length--;
+
+ if (length == 0)
+ {
+ fputs_filtered ("\"\"", stream);
+ return;
+ }
+
+ /* 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);
+
+ /* 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 r;
+
+ QUIT;
+
+ /* Grab the next character and repeat count. */
+ r = count_next_character (iter, &converted_chars);
+
+ /* If less than zero, the end of the input string was reached. */
+ if (r < 0)
+ break;
+
+ /* Otherwise, add the count to the total print count and get
+ the next character. */
+ i += r;
+ }
+
+ /* 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);
+
+ /* Ensure that CONVERTED_CHARS is terminated. */
+ last->result = wchar_iterate_eof;
+
+ /* 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);
+
+ /* 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 ("..."));
+
+ /* OUTPUT is where we collect `char's for printing. */
+ obstack_init (&output);
+ make_cleanup_obstack_free (&output);
+
+ convert_between_encodings (INTERMEDIATE_ENCODING, host_charset (),
+ (gdb_byte *) obstack_base (&wchar_buf),
+ obstack_object_size (&wchar_buf),
+ sizeof (gdb_wchar_t), &output, translit_char);
+ obstack_1grow (&output, '\0');
+
+ fputs_filtered (obstack_base (&output), stream);
+
+ do_cleanups (cleanup);
+}
+