/* Character set conversion support for GDB.
- Copyright (C) 2001, 2003, 2007, 2008, 2009, 2010, 2011
- Free Software Foundation, Inc.
+ Copyright (C) 2001-2018 Free Software Foundation, Inc.
This file is part of GDB.
#include "defs.h"
#include "charset.h"
#include "gdbcmd.h"
-#include "gdb_assert.h"
#include "gdb_obstack.h"
#include "gdb_wait.h"
#include "charset-list.h"
#include "vec.h"
#include "environ.h"
#include "arch-utils.h"
-
-#include <stddef.h>
-#include "gdb_string.h"
+#include "gdb_vecs.h"
#include <ctype.h>
#ifdef USE_WIN32API
arrange for there to be a single available character set. */
#undef GDB_DEFAULT_HOST_CHARSET
-#define GDB_DEFAULT_HOST_CHARSET "ISO-8859-1"
-#define GDB_DEFAULT_TARGET_CHARSET "ISO-8859-1"
-#define GDB_DEFAULT_TARGET_WIDE_CHARSET "ISO-8859-1"
+#ifdef USE_WIN32API
+# define GDB_DEFAULT_HOST_CHARSET "CP1252"
+#else
+# define GDB_DEFAULT_HOST_CHARSET "ISO-8859-1"
+#endif
+#define GDB_DEFAULT_TARGET_CHARSET GDB_DEFAULT_HOST_CHARSET
+#define GDB_DEFAULT_TARGET_WIDE_CHARSET "UTF-32"
#undef DEFAULT_CHARSET_NAMES
#define DEFAULT_CHARSET_NAMES GDB_DEFAULT_HOST_CHARSET ,
#undef ICONV_CONST
#define ICONV_CONST const
-/* Some systems don't have EILSEQ, so we define it here, but not as
- EINVAL, because callers of `iconv' want to distinguish EINVAL and
- EILSEQ. This is what iconv.h from libiconv does as well. Note
- that wchar.h may also define EILSEQ, so this needs to be after we
- include wchar.h, which happens in defs.h through gdb_wchar.h. */
-#ifndef EILSEQ
-#define EILSEQ ENOENT
-#endif
+/* We allow conversions from UTF-32, wchar_t, and the host charset.
+ We allow conversions to wchar_t and the host charset.
+ Return 1 if we are converting from UTF-32BE, 2 if from UTF32-LE,
+ 0 otherwise. This is used as a flag in calls to iconv. */
-iconv_t
+static iconv_t
phony_iconv_open (const char *to, const char *from)
{
- /* We allow conversions from UTF-32BE, wchar_t, and the host charset.
- We allow conversions to wchar_t and the host charset. */
- if (strcmp (from, "UTF-32BE") && strcmp (from, "wchar_t")
- && strcmp (from, GDB_DEFAULT_HOST_CHARSET))
- return -1;
if (strcmp (to, "wchar_t") && strcmp (to, GDB_DEFAULT_HOST_CHARSET))
return -1;
- /* Return 1 if we are converting from UTF-32BE, 0 otherwise. This is
- used as a flag in calls to iconv. */
- return !strcmp (from, "UTF-32BE");
+ if (!strcmp (from, "UTF-32BE") || !strcmp (from, "UTF-32"))
+ return 1;
+
+ if (!strcmp (from, "UTF-32LE"))
+ return 2;
+
+ if (strcmp (from, "wchar_t") && strcmp (from, GDB_DEFAULT_HOST_CHARSET))
+ return -1;
+
+ return 0;
}
-int
+static int
phony_iconv_close (iconv_t arg)
{
return 0;
}
-size_t
+static size_t
phony_iconv (iconv_t utf_flag, const char **inbuf, size_t *inbytesleft,
char **outbuf, size_t *outbytesleft)
{
if (utf_flag)
{
+ enum bfd_endian endian
+ = utf_flag == 1 ? BFD_ENDIAN_BIG : BFD_ENDIAN_LITTLE;
while (*inbytesleft >= 4)
{
- size_t j;
- unsigned long c = 0;
-
- for (j = 0; j < 4; ++j)
- {
- c <<= 8;
- c += (*inbuf)[j] & 0xff;
- }
+ unsigned long c
+ = extract_unsigned_integer ((const gdb_byte *)*inbuf, 4, endian);
if (c >= 256)
{
errno = EILSEQ;
return -1;
}
+ if (*outbytesleft < 1)
+ {
+ errno = E2BIG;
+ return -1;
+ }
**outbuf = c & 0xff;
++*outbuf;
--*outbytesleft;
- ++*inbuf;
+ *inbuf += 4;
*inbytesleft -= 4;
}
- if (*inbytesleft < 4)
+ if (*inbytesleft)
{
+ /* Partial sequence on input. */
errno = EINVAL;
return -1;
}
*outbuf += amt;
*inbytesleft -= amt;
*outbytesleft -= amt;
- }
-
- if (*inbytesleft)
- {
- errno = E2BIG;
- return -1;
+ if (*inbytesleft)
+ {
+ errno = E2BIG;
+ return -1;
+ }
}
/* The number of non-reversible conversions -- but they were all
return 0;
}
-#endif
+#else /* PHONY_ICONV */
+
+/* On systems that don't have EILSEQ, GNU iconv's iconv.h defines it
+ to ENOENT, while gnulib defines it to a different value. Always
+ map ENOENT to gnulib's EILSEQ, leaving callers agnostic. */
+
+static size_t
+gdb_iconv (iconv_t utf_flag, ICONV_CONST char **inbuf, size_t *inbytesleft,
+ char **outbuf, size_t *outbytesleft)
+{
+ size_t ret;
+
+ ret = iconv (utf_flag, inbuf, inbytesleft, outbuf, outbytesleft);
+ if (errno == ENOENT)
+ errno = EILSEQ;
+ return ret;
+}
+#undef iconv
+#define iconv gdb_iconv
+
+#endif /* PHONY_ICONV */
\f
/* The global lists of character sets and translations. */
return;
be_le_arch = gdbarch;
+#ifdef PHONY_ICONV
+ /* Match the wide charset names recognized by phony_iconv_open. */
+ target_wide_charset_le_name = "UTF-32LE";
+ target_wide_charset_be_name = "UTF-32BE";
+#else
target_wide_charset_le_name = NULL;
target_wide_charset_be_name = NULL;
target_wide_charset_le_name = charset_enum[i];
}
}
+# endif /* PHONY_ICONV */
}
/* 'Set charset', 'set host-charset', 'set target-charset', 'set
desc = iconv_open (target_wide_cset, host_cset);
if (desc == (iconv_t) -1)
- error ("Cannot convert between character sets `%s' and `%s'",
+ error (_("Cannot convert between character sets `%s' and `%s'"),
target_wide_cset, host_cset);
iconv_close (desc);
desc = iconv_open (target_cset, host_cset);
if (desc == (iconv_t) -1)
- error ("Cannot convert between character sets `%s' and `%s'",
+ error (_("Cannot convert between character sets `%s' and `%s'"),
target_cset, host_cset);
iconv_close (desc);
/* This is the sfunc for the 'set charset' command. */
static void
-set_charset_sfunc (char *charset, int from_tty,
+set_charset_sfunc (const char *charset, int from_tty,
struct cmd_list_element *c)
{
/* CAREFUL: set the target charset here as well. */
/* 'set host-charset' command sfunc. We need a wrapper here because
the function needs to have a specific signature. */
static void
-set_host_charset_sfunc (char *charset, int from_tty,
+set_host_charset_sfunc (const char *charset, int from_tty,
struct cmd_list_element *c)
{
validate (get_current_arch ());
/* Wrapper for the 'set target-charset' command. */
static void
-set_target_charset_sfunc (char *charset, int from_tty,
+set_target_charset_sfunc (const char *charset, int from_tty,
struct cmd_list_element *c)
{
validate (get_current_arch ());
/* Wrapper for the 'set target-wide-charset' command. */
static void
-set_target_wide_charset_sfunc (char *charset, int from_tty,
+set_target_wide_charset_sfunc (const char *charset, int from_tty,
struct cmd_list_element *c)
{
validate (get_current_arch ());
\f
/* Public character management functions. */
-/* A cleanup function which is run to close an iconv descriptor. */
-
-static void
-cleanup_iconv (void *p)
+class iconv_wrapper
{
- iconv_t *descp = p;
- iconv_close (*descp);
-}
+public:
+
+ iconv_wrapper (const char *to, const char *from)
+ {
+ m_desc = iconv_open (to, from);
+ if (m_desc == (iconv_t) -1)
+ perror_with_name (_("Converting character sets"));
+ }
+
+ ~iconv_wrapper ()
+ {
+ iconv_close (m_desc);
+ }
+
+ size_t convert (ICONV_CONST char **inp, size_t *inleft, char **outp,
+ size_t *outleft)
+ {
+ return iconv (m_desc, inp, inleft, outp, outleft);
+ }
+
+private:
+
+ iconv_t m_desc;
+};
void
convert_between_encodings (const char *from, const char *to,
int width, struct obstack *output,
enum transliterations translit)
{
- iconv_t desc;
- struct cleanup *cleanups;
size_t inleft;
- char *inp;
+ ICONV_CONST char *inp;
unsigned int space_request;
/* Often, the host and target charsets will be the same. */
return;
}
- desc = iconv_open (to, from);
- if (desc == (iconv_t) -1)
- perror_with_name ("Converting character sets");
- cleanups = make_cleanup (cleanup_iconv, &desc);
+ iconv_wrapper desc (to, from);
inleft = num_bytes;
- inp = (char *) bytes;
+ inp = (ICONV_CONST char *) bytes;
space_request = num_bytes;
old_size = obstack_object_size (output);
obstack_blank (output, space_request);
- outp = obstack_base (output) + old_size;
+ outp = (char *) obstack_base (output) + old_size;
outleft = space_request;
- r = iconv (desc, (ICONV_CONST char **) &inp, &inleft, &outp, &outleft);
+ r = desc.convert (&inp, &inleft, &outp, &outleft);
/* Now make sure that the object on the obstack only includes
bytes we have converted. */
- obstack_blank (output, - (int) outleft);
+ obstack_blank_fast (output, -outleft);
if (r == (size_t) -1)
{
{
char octal[5];
- sprintf (octal, "\\%.3o", *inp & 0xff);
+ xsnprintf (octal, sizeof (octal), "\\%.3o", *inp & 0xff);
obstack_grow_str (output, octal);
++inp;
break;
default:
- perror_with_name ("Internal error while "
- "converting character sets");
+ perror_with_name (_("Internal error while "
+ "converting character sets"));
}
}
}
-
- do_cleanups (cleanups);
}
\f
-/* An iterator that returns host wchar_t's from a target string. */
-struct wchar_iterator
-{
- /* The underlying iconv descriptor. */
- iconv_t desc;
-
- /* The input string. This is updated as convert characters. */
- char *input;
- /* The number of bytes remaining in the input. */
- size_t bytes;
-
- /* The width of an input character. */
- size_t width;
-
- /* The output buffer and its size. */
- gdb_wchar_t *out;
- size_t out_size;
-};
-
/* Create a new iterator. */
-struct wchar_iterator *
-make_wchar_iterator (const gdb_byte *input, size_t bytes,
- const char *charset, size_t width)
+wchar_iterator::wchar_iterator (const gdb_byte *input, size_t bytes,
+ const char *charset, size_t width)
+: m_input (input),
+ m_bytes (bytes),
+ m_width (width),
+ m_out (1)
{
- struct wchar_iterator *result;
- iconv_t desc;
-
- desc = iconv_open (INTERMEDIATE_ENCODING, charset);
- if (desc == (iconv_t) -1)
- perror_with_name ("Converting character sets");
-
- result = XNEW (struct wchar_iterator);
- result->desc = desc;
- result->input = (char *) input;
- result->bytes = bytes;
- result->width = width;
-
- result->out = XNEW (gdb_wchar_t);
- result->out_size = 1;
-
- return result;
-}
-
-static void
-do_cleanup_iterator (void *p)
-{
- struct wchar_iterator *iter = p;
-
- iconv_close (iter->desc);
- xfree (iter->out);
- xfree (iter);
+ m_desc = iconv_open (INTERMEDIATE_ENCODING, charset);
+ if (m_desc == (iconv_t) -1)
+ perror_with_name (_("Converting character sets"));
}
-struct cleanup *
-make_cleanup_wchar_iterator (struct wchar_iterator *iter)
+wchar_iterator::~wchar_iterator ()
{
- return make_cleanup (do_cleanup_iterator, iter);
+ if (m_desc != (iconv_t) -1)
+ iconv_close (m_desc);
}
int
-wchar_iterate (struct wchar_iterator *iter,
- enum wchar_iterate_result *out_result,
- gdb_wchar_t **out_chars,
- const gdb_byte **ptr,
- size_t *len)
+wchar_iterator::iterate (enum wchar_iterate_result *out_result,
+ gdb_wchar_t **out_chars,
+ const gdb_byte **ptr,
+ size_t *len)
{
size_t out_request;
invalid input sequence -- but we want to reliably report this to
our caller so it can emit an escape sequence. */
out_request = 1;
- while (iter->bytes > 0)
+ while (m_bytes > 0)
{
- char *outptr = (char *) &iter->out[0];
- char *orig_inptr = iter->input;
- size_t orig_in = iter->bytes;
+ ICONV_CONST char *inptr = (ICONV_CONST char *) m_input;
+ char *outptr = (char *) m_out.data ();
+ const gdb_byte *orig_inptr = m_input;
+ size_t orig_in = m_bytes;
size_t out_avail = out_request * sizeof (gdb_wchar_t);
size_t num;
- size_t r = iconv (iter->desc,
- (ICONV_CONST char **) &iter->input,
- &iter->bytes, &outptr, &out_avail);
+ size_t r = iconv (m_desc, &inptr, &m_bytes, &outptr, &out_avail);
+
+ m_input = (gdb_byte *) inptr;
if (r == (size_t) -1)
{
/* Otherwise skip the first invalid character, and let
the caller know about it. */
*out_result = wchar_iterate_invalid;
- *ptr = iter->input;
- *len = iter->width;
- iter->input += iter->width;
- iter->bytes -= iter->width;
+ *ptr = m_input;
+ *len = m_width;
+ m_input += m_width;
+ m_bytes -= m_width;
return 0;
case E2BIG:
break;
++out_request;
- if (out_request > iter->out_size)
- {
- iter->out_size = out_request;
- iter->out = xrealloc (iter->out,
- out_request * sizeof (gdb_wchar_t));
- }
+ if (out_request > m_out.size ())
+ m_out.resize (out_request);
continue;
case EINVAL:
/* Incomplete input sequence. Let the caller know, and
arrange for future calls to see EOF. */
*out_result = wchar_iterate_incomplete;
- *ptr = iter->input;
- *len = iter->bytes;
- iter->bytes = 0;
+ *ptr = m_input;
+ *len = m_bytes;
+ m_bytes = 0;
return 0;
default:
- perror_with_name ("Internal error while "
- "converting character sets");
+ perror_with_name (_("Internal error while "
+ "converting character sets"));
}
}
/* We converted something. */
num = out_request - out_avail / sizeof (gdb_wchar_t);
*out_result = wchar_iterate_ok;
- *out_chars = iter->out;
+ *out_chars = m_out.data ();
*ptr = orig_inptr;
- *len = orig_in - iter->bytes;
+ *len = orig_in - m_bytes;
return num;
}
return -1;
}
-\f
-/* The charset.c module initialization function. */
+struct charset_vector
+{
+ ~charset_vector ()
+ {
+ clear ();
+ }
+
+ void clear ()
+ {
+ for (char *c : charsets)
+ xfree (c);
-extern initialize_file_ftype _initialize_charset; /* -Wmissing-prototype */
+ charsets.clear ();
+ }
-DEF_VEC_P (char_ptr);
+ std::vector<char *> charsets;
+};
-static VEC (char_ptr) *charsets;
+static charset_vector charsets;
#ifdef PHONY_ICONV
static void
find_charset_names (void)
{
- VEC_safe_push (char_ptr, charsets, GDB_DEFAULT_HOST_CHARSET);
- VEC_safe_push (char_ptr, charsets, NULL);
+ charsets.charsets.push_back (xstrdup (GDB_DEFAULT_HOST_CHARSET));
+ charsets.charsets.push_back (NULL);
}
#else /* PHONY_ICONV */
unsigned int i;
for (i = 0; i < count; ++i)
- VEC_safe_push (char_ptr, charsets, xstrdup (names[i]));
+ charsets.charsets.push_back (xstrdup (names[i]));
return 0;
}
find_charset_names (void)
{
iconvlist (add_one, NULL);
- VEC_safe_push (char_ptr, charsets, NULL);
+
+ charsets.charsets.push_back (NULL);
}
#else
find_charset_names (void)
{
struct pex_obj *child;
- char *args[3];
+ const char *args[3];
int err, status;
int fail = 1;
- struct gdb_environ *iconv_env;
+ int flags;
+ gdb_environ iconv_env = gdb_environ::from_host_environ ();
+ char *iconv_program;
/* Older iconvs, e.g. 2.2.2, don't omit the intro text if stdout is
not a tty. We need to recognize it and ignore it. This text is
subject to translation, so force LANGUAGE=C. */
- iconv_env = make_environ ();
- init_environ (iconv_env);
- set_in_environ (iconv_env, "LANGUAGE", "C");
- set_in_environ (iconv_env, "LC_ALL", "C");
+ iconv_env.set ("LANGUAGE", "C");
+ iconv_env.set ("LC_ALL", "C");
child = pex_init (PEX_USE_PIPES, "iconv", NULL);
- args[0] = "iconv";
+#ifdef ICONV_BIN
+ {
+ char *iconv_dir = relocate_gdb_directory (ICONV_BIN,
+ ICONV_BIN_RELOCATABLE);
+ iconv_program = concat (iconv_dir, SLASH_STRING, "iconv", NULL);
+ xfree (iconv_dir);
+ }
+#else
+ iconv_program = xstrdup ("iconv");
+#endif
+ args[0] = iconv_program;
args[1] = "-l";
args[2] = NULL;
+ flags = PEX_STDERR_TO_STDOUT;
+#ifndef ICONV_BIN
+ flags |= PEX_SEARCH;
+#endif
/* Note that we simply ignore errors here. */
- if (!pex_run_in_environment (child, PEX_SEARCH | PEX_STDERR_TO_STDOUT,
- "iconv", args, environ_vector (iconv_env),
+ if (!pex_run_in_environment (child, flags,
+ args[0], const_cast<char **> (args),
+ iconv_env.envp (),
NULL, NULL, &err))
{
FILE *in = pex_read_output (child, 0);
parse the glibc and libiconv formats; feel free to add others
as needed. */
- while (!feof (in))
+ while (in != NULL && !feof (in))
{
/* The size of buf is chosen arbitrarily. */
char buf[1024];
break;
keep_going = *p;
*p = '\0';
- VEC_safe_push (char_ptr, charsets, xstrdup (start));
+ charsets.charsets.push_back (xstrdup (start));
if (!keep_going)
break;
/* Skip any extra spaces. */
}
+ xfree (iconv_program);
pex_free (child);
- free_environ (iconv_env);
if (fail)
{
/* Some error occurred, so drop the vector. */
- int ix;
- char *elt;
- for (ix = 0; VEC_iterate (char_ptr, charsets, ix, elt); ++ix)
- xfree (elt);
- VEC_truncate (char_ptr, charsets, 0);
+ charsets.clear ();
}
else
- VEC_safe_push (char_ptr, charsets, NULL);
+ charsets.charsets.push_back (NULL);
}
#endif /* HAVE_ICONVLIST || HAVE_LIBICONVLIST */
return GDB_DEFAULT_TARGET_WIDE_CHARSET;
}
+
+#ifdef USE_INTERMEDIATE_ENCODING_FUNCTION
+/* Macro used for UTF or UCS endianness suffix. */
+#if WORDS_BIGENDIAN
+#define ENDIAN_SUFFIX "BE"
+#else
+#define ENDIAN_SUFFIX "LE"
+#endif
+
+/* The code below serves to generate a compile time error if
+ gdb_wchar_t type is not of size 2 nor 4, despite the fact that
+ macro __STDC_ISO_10646__ is defined.
+ This is better than a gdb_assert call, because GDB cannot handle
+ strings correctly if this size is different. */
+
+extern char your_gdb_wchar_t_is_bogus[(sizeof (gdb_wchar_t) == 2
+ || sizeof (gdb_wchar_t) == 4)
+ ? 1 : -1];
+
+/* intermediate_encoding returns the charset used internally by
+ GDB to convert between target and host encodings. As the test above
+ compiled, sizeof (gdb_wchar_t) is either 2 or 4 bytes.
+ UTF-16/32 is tested first, UCS-2/4 is tested as a second option,
+ otherwise an error is generated. */
+
+const char *
+intermediate_encoding (void)
+{
+ iconv_t desc;
+ static const char *stored_result = NULL;
+ char *result;
+
+ if (stored_result)
+ return stored_result;
+ result = xstrprintf ("UTF-%d%s", (int) (sizeof (gdb_wchar_t) * 8),
+ ENDIAN_SUFFIX);
+ /* Check that the name is supported by iconv_open. */
+ desc = iconv_open (result, host_charset ());
+ if (desc != (iconv_t) -1)
+ {
+ iconv_close (desc);
+ stored_result = result;
+ return result;
+ }
+ /* Not valid, free the allocated memory. */
+ xfree (result);
+ /* Second try, with UCS-2 type. */
+ result = xstrprintf ("UCS-%d%s", (int) sizeof (gdb_wchar_t),
+ ENDIAN_SUFFIX);
+ /* Check that the name is supported by iconv_open. */
+ desc = iconv_open (result, host_charset ());
+ if (desc != (iconv_t) -1)
+ {
+ iconv_close (desc);
+ stored_result = result;
+ return result;
+ }
+ /* Not valid, free the allocated memory. */
+ xfree (result);
+ /* No valid charset found, generate error here. */
+ error (_("Unable to find a vaild charset for string conversions"));
+}
+
+#endif /* USE_INTERMEDIATE_ENCODING_FUNCTION */
+
void
_initialize_charset (void)
{
/* The first element is always "auto". */
- VEC_safe_push (char_ptr, charsets, xstrdup ("auto"));
+ charsets.charsets.push_back (xstrdup ("auto"));
find_charset_names ();
- if (VEC_length (char_ptr, charsets) > 1)
- charset_enum = (const char **) VEC_address (char_ptr, charsets);
+ if (charsets.charsets.size () > 1)
+ charset_enum = (const char **) charsets.charsets.data ();
else
charset_enum = default_charset_names;
Show the target character set."), _("\
The `target character set' is the one used by the program being debugged.\n\
GDB translates characters and strings between the host and target\n\
-character sets as needed.\n
+character sets as needed.\n\
To see a list of the character sets GDB supports, type `set target-charset'<TAB>"),
set_target_charset_sfunc,
show_target_charset_name,