Enable Gold linker for cross-*-binutils build
[platform/upstream/binutils.git] / gdb / charset.c
index 4631af8..bc7bd65 100644 (file)
@@ -1,6 +1,6 @@
 /* Character set conversion support for GDB.
 
 /* Character set conversion support for GDB.
 
-   Copyright (C) 2001, 2003, 2007, 2008, 2009 Free Software Foundation, Inc.
+   Copyright (C) 2001-2014 Free Software Foundation, Inc.
 
    This file is part of GDB.
 
 
    This file is part of GDB.
 
 #include "defs.h"
 #include "charset.h"
 #include "gdbcmd.h"
 #include "defs.h"
 #include "charset.h"
 #include "gdbcmd.h"
-#include "gdb_assert.h"
 #include "gdb_obstack.h"
 #include "gdb_obstack.h"
+#include "gdb_wait.h"
 #include "charset-list.h"
 #include "vec.h"
 #include "charset-list.h"
 #include "vec.h"
-
-#include <stddef.h>
-#include "gdb_string.h"
+#include "environ.h"
+#include "arch-utils.h"
+#include "gdb_vecs.h"
 #include <ctype.h>
 
 #include <ctype.h>
 
+#ifdef USE_WIN32API
+#include <windows.h>
+#endif
 \f
 /* How GDB's character set support works
 
 \f
 /* How GDB's character set support works
 
 #undef iconv_t
 #define iconv_t int
 #undef iconv_open
 #undef iconv_t
 #define iconv_t int
 #undef iconv_open
+#define iconv_open phony_iconv_open
 #undef iconv
 #undef iconv
+#define iconv phony_iconv
 #undef iconv_close
 #undef iconv_close
+#define iconv_close phony_iconv_close
 
 #undef ICONV_CONST
 #define ICONV_CONST const
 
 
 #undef ICONV_CONST
 #define ICONV_CONST const
 
-/* Some systems, don't have EILSEQ, so we define it here, but not as
+/* 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
    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
 #define EILSEQ ENOENT
 #endif
 
 #define EILSEQ ENOENT
 #endif
 
-iconv_t
-iconv_open (const char *to, const char *from)
+static iconv_t
+phony_iconv_open (const char *to, const char *from)
 {
 {
-  /* We allow conversions from UCS-4BE, wchar_t, and the host charset.
+  /* We allow conversions from UTF-32BE, wchar_t, and the host charset.
      We allow conversions to wchar_t and the host charset.  */
      We allow conversions to wchar_t and the host charset.  */
-  if (strcmp (from, "UCS-4BE") && strcmp (from, "wchar_t")
+  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;
 
       && 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 UCS-4BE, 0 otherwise.  This is
+  /* Return 1 if we are converting from UTF-32BE, 0 otherwise.  This is
      used as a flag in calls to iconv.  */
      used as a flag in calls to iconv.  */
-  return !strcmp (from, "UCS-4BE");
+  return !strcmp (from, "UTF-32BE");
 }
 
 }
 
-int
-iconv_close (iconv_t arg)
+static int
+phony_iconv_close (iconv_t arg)
 {
   return 0;
 }
 
 {
   return 0;
 }
 
-size_t
-iconv (iconv_t ucs_flag, const char **inbuf, size_t *inbytesleft,
-       char **outbuf, size_t *outbytesleft)
+static size_t
+phony_iconv (iconv_t utf_flag, const char **inbuf, size_t *inbytesleft,
+            char **outbuf, size_t *outbytesleft)
 {
 {
-  if (ucs_flag)
+  if (utf_flag)
     {
       while (*inbytesleft >= 4)
        {
     {
       while (*inbytesleft >= 4)
        {
@@ -160,6 +166,7 @@ iconv (iconv_t ucs_flag, const char **inbuf, size_t *inbytesleft,
       /* In all other cases we simply copy input bytes to the
         output.  */
       size_t amt = *inbytesleft;
       /* In all other cases we simply copy input bytes to the
         output.  */
       size_t amt = *inbytesleft;
+
       if (amt > *outbytesleft)
        amt = *outbytesleft;
       memcpy (*outbuf, *inbuf, amt);
       if (amt > *outbytesleft)
        amt = *outbytesleft;
       memcpy (*outbuf, *inbuf, amt);
@@ -192,7 +199,7 @@ iconv (iconv_t ucs_flag, const char **inbuf, size_t *inbytesleft,
 #endif
 
 #ifndef GDB_DEFAULT_TARGET_WIDE_CHARSET
 #endif
 
 #ifndef GDB_DEFAULT_TARGET_WIDE_CHARSET
-#define GDB_DEFAULT_TARGET_WIDE_CHARSET "UCS-4"
+#define GDB_DEFAULT_TARGET_WIDE_CHARSET "UTF-32"
 #endif
 
 static const char *auto_host_charset_name = GDB_DEFAULT_HOST_CHARSET;
 #endif
 
 static const char *auto_host_charset_name = GDB_DEFAULT_HOST_CHARSET;
@@ -210,22 +217,36 @@ show_host_charset_name (struct ui_file *file, int from_tty,
     fprintf_filtered (file, _("The host character set is \"%s\".\n"), value);
 }
 
     fprintf_filtered (file, _("The host character set is \"%s\".\n"), value);
 }
 
-static const char *target_charset_name = GDB_DEFAULT_TARGET_CHARSET;
+static const char *target_charset_name = "auto";
 static void
 show_target_charset_name (struct ui_file *file, int from_tty,
                          struct cmd_list_element *c, const char *value)
 {
 static void
 show_target_charset_name (struct ui_file *file, int from_tty,
                          struct cmd_list_element *c, const char *value)
 {
-  fprintf_filtered (file, _("The target character set is \"%s\".\n"),
-                   value);
+  if (!strcmp (value, "auto"))
+    fprintf_filtered (file,
+                     _("The target character set is \"auto; "
+                       "currently %s\".\n"),
+                     gdbarch_auto_charset (get_current_arch ()));
+  else
+    fprintf_filtered (file, _("The target character set is \"%s\".\n"),
+                     value);
 }
 
 }
 
-static const char *target_wide_charset_name = GDB_DEFAULT_TARGET_WIDE_CHARSET;
+static const char *target_wide_charset_name = "auto";
 static void
 static void
-show_target_wide_charset_name (struct ui_file *file, int from_tty,
-                              struct cmd_list_element *c, const char *value)
+show_target_wide_charset_name (struct ui_file *file, 
+                              int from_tty,
+                              struct cmd_list_element *c, 
+                              const char *value)
 {
 {
-  fprintf_filtered (file, _("The target wide character set is \"%s\".\n"),
-                   value);
+  if (!strcmp (value, "auto"))
+    fprintf_filtered (file,
+                     _("The target wide character set is \"auto; "
+                       "currently %s\".\n"),
+                     gdbarch_auto_wide_charset (get_current_arch ()));
+  else
+    fprintf_filtered (file, _("The target wide character set is \"%s\".\n"),
+                     value);
 }
 
 static const char *default_charset_names[] =
 }
 
 static const char *default_charset_names[] =
@@ -242,21 +263,33 @@ static const char **charset_enum;
 static const char *target_wide_charset_be_name;
 static const char *target_wide_charset_le_name;
 
 static const char *target_wide_charset_be_name;
 static const char *target_wide_charset_le_name;
 
-/* A helper function for validate which sets the target wide big- and
-   little-endian character set names, if possible.  */
+/* The architecture for which the BE- and LE-names are valid.  */
+static struct gdbarch *be_le_arch;
+
+/* A helper function which sets the target wide big- and little-endian
+   character set names, if possible.  */
 
 static void
 
 static void
-set_be_le_names (void)
+set_be_le_names (struct gdbarch *gdbarch)
 {
   int i, len;
 {
   int i, len;
+  const char *target_wide;
+
+  if (be_le_arch == gdbarch)
+    return;
+  be_le_arch = gdbarch;
 
   target_wide_charset_le_name = NULL;
   target_wide_charset_be_name = NULL;
 
 
   target_wide_charset_le_name = NULL;
   target_wide_charset_be_name = NULL;
 
-  len = strlen (target_wide_charset_name);
+  target_wide = target_wide_charset_name;
+  if (!strcmp (target_wide, "auto"))
+    target_wide = gdbarch_auto_wide_charset (gdbarch);
+
+  len = strlen (target_wide);
   for (i = 0; charset_enum[i]; ++i)
     {
   for (i = 0; charset_enum[i]; ++i)
     {
-      if (strncmp (target_wide_charset_name, charset_enum[i], len))
+      if (strncmp (target_wide, charset_enum[i], len))
        continue;
       if ((charset_enum[i][len] == 'B'
           || charset_enum[i][len] == 'L')
        continue;
       if ((charset_enum[i][len] == 'B'
           || charset_enum[i][len] == 'L')
@@ -275,33 +308,40 @@ set_be_le_names (void)
    target-wide-charset', 'set charset' sfunc's.  */
 
 static void
    target-wide-charset', 'set charset' sfunc's.  */
 
 static void
-validate (void)
+validate (struct gdbarch *gdbarch)
 {
   iconv_t desc;
   const char *host_cset = host_charset ();
 {
   iconv_t desc;
   const char *host_cset = host_charset ();
+  const char *target_cset = target_charset (gdbarch);
+  const char *target_wide_cset = target_wide_charset_name;
 
 
-  desc = iconv_open (target_wide_charset_name, host_cset);
+  if (!strcmp (target_wide_cset, "auto"))
+    target_wide_cset = gdbarch_auto_wide_charset (gdbarch);
+
+  desc = iconv_open (target_wide_cset, host_cset);
   if (desc == (iconv_t) -1)
   if (desc == (iconv_t) -1)
-    error ("Cannot convert between character sets `%s' and `%s'",
-          target_wide_charset_name, host_cset);
+    error (_("Cannot convert between character sets `%s' and `%s'"),
+          target_wide_cset, host_cset);
   iconv_close (desc);
 
   iconv_close (desc);
 
-  desc = iconv_open (target_charset_name, host_cset);
+  desc = iconv_open (target_cset, host_cset);
   if (desc == (iconv_t) -1)
   if (desc == (iconv_t) -1)
-    error ("Cannot convert between character sets `%s' and `%s'",
-          target_charset_name, host_cset);
+    error (_("Cannot convert between character sets `%s' and `%s'"),
+          target_cset, host_cset);
   iconv_close (desc);
 
   iconv_close (desc);
 
-  set_be_le_names ();
+  /* Clear the cache.  */
+  be_le_arch = NULL;
 }
 
 /* This is the sfunc for the 'set charset' command.  */
 static void
 }
 
 /* This is the sfunc for the 'set charset' command.  */
 static void
-set_charset_sfunc (char *charset, int from_tty, struct cmd_list_element *c)
+set_charset_sfunc (char *charset, int from_tty, 
+                  struct cmd_list_element *c)
 {
 {
-  /* CAREFUL: set the target charset here as well. */
+  /* CAREFUL: set the target charset here as well.  */
   target_charset_name = host_charset_name;
   target_charset_name = host_charset_name;
-  validate ();
+  validate (get_current_arch ());
 }
 
 /* 'set host-charset' command sfunc.  We need a wrapper here because
 }
 
 /* 'set host-charset' command sfunc.  We need a wrapper here because
@@ -310,7 +350,7 @@ static void
 set_host_charset_sfunc (char *charset, int from_tty,
                        struct cmd_list_element *c)
 {
 set_host_charset_sfunc (char *charset, int from_tty,
                        struct cmd_list_element *c)
 {
-  validate ();
+  validate (get_current_arch ());
 }
 
 /* Wrapper for the 'set target-charset' command.  */
 }
 
 /* Wrapper for the 'set target-charset' command.  */
@@ -318,7 +358,7 @@ static void
 set_target_charset_sfunc (char *charset, int from_tty,
                          struct cmd_list_element *c)
 {
 set_target_charset_sfunc (char *charset, int from_tty,
                          struct cmd_list_element *c)
 {
-  validate ();
+  validate (get_current_arch ());
 }
 
 /* Wrapper for the 'set target-wide-charset' command.  */
 }
 
 /* Wrapper for the 'set target-wide-charset' command.  */
@@ -326,17 +366,19 @@ static void
 set_target_wide_charset_sfunc (char *charset, int from_tty,
                               struct cmd_list_element *c)
 {
 set_target_wide_charset_sfunc (char *charset, int from_tty,
                               struct cmd_list_element *c)
 {
-  validate ();
+  validate (get_current_arch ());
 }
 
 /* sfunc for the 'show charset' command.  */
 static void
 }
 
 /* sfunc for the 'show charset' command.  */
 static void
-show_charset (struct ui_file *file, int from_tty, struct cmd_list_element *c,
+show_charset (struct ui_file *file, int from_tty, 
+             struct cmd_list_element *c,
              const char *name)
 {
   show_host_charset_name (file, from_tty, c, host_charset_name);
   show_target_charset_name (file, from_tty, c, target_charset_name);
              const char *name)
 {
   show_host_charset_name (file, from_tty, c, host_charset_name);
   show_target_charset_name (file, from_tty, c, target_charset_name);
-  show_target_wide_charset_name (file, from_tty, c, target_wide_charset_name);
+  show_target_wide_charset_name (file, from_tty, c, 
+                                target_wide_charset_name);
 }
 
 \f
 }
 
 \f
@@ -351,15 +393,20 @@ host_charset (void)
 }
 
 const char *
 }
 
 const char *
-target_charset (void)
+target_charset (struct gdbarch *gdbarch)
 {
 {
+  if (!strcmp (target_charset_name, "auto"))
+    return gdbarch_auto_charset (gdbarch);
   return target_charset_name;
 }
 
 const char *
   return target_charset_name;
 }
 
 const char *
-target_wide_charset (void)
+target_wide_charset (struct gdbarch *gdbarch)
 {
 {
-  if (gdbarch_byte_order (current_gdbarch) == BFD_ENDIAN_BIG)
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+
+  set_be_le_names (gdbarch);
+  if (byte_order == BFD_ENDIAN_BIG)
     {
       if (target_wide_charset_be_name)
        return target_wide_charset_be_name;
     {
       if (target_wide_charset_be_name)
        return target_wide_charset_be_name;
@@ -370,6 +417,9 @@ target_wide_charset (void)
        return target_wide_charset_le_name;
     }
 
        return target_wide_charset_le_name;
     }
 
+  if (!strcmp (target_wide_charset_name, "auto"))
+    return gdbarch_auto_wide_charset (gdbarch);
+
   return target_wide_charset_name;
 }
 
   return target_wide_charset_name;
 }
 
@@ -420,7 +470,7 @@ convert_between_encodings (const char *from, const char *to,
   iconv_t desc;
   struct cleanup *cleanups;
   size_t inleft;
   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.  */
   unsigned int space_request;
 
   /* Often, the host and target charsets will be the same.  */
@@ -432,11 +482,11 @@ convert_between_encodings (const char *from, const char *to,
 
   desc = iconv_open (to, from);
   if (desc == (iconv_t) -1)
 
   desc = iconv_open (to, from);
   if (desc == (iconv_t) -1)
-    perror_with_name ("Converting character sets");
+    perror_with_name (_("Converting character sets"));
   cleanups = make_cleanup (cleanup_iconv, &desc);
 
   inleft = num_bytes;
   cleanups = make_cleanup (cleanup_iconv, &desc);
 
   inleft = num_bytes;
-  inp = (char *) bytes;
+  inp = (ICONV_CONST char *) bytes;
 
   space_request = num_bytes;
 
 
   space_request = num_bytes;
 
@@ -449,10 +499,10 @@ convert_between_encodings (const char *from, const char *to,
       old_size = obstack_object_size (output);
       obstack_blank (output, space_request);
 
       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;
 
       outleft = space_request;
 
-      r = iconv (desc, (ICONV_CONST char **) &inp, &inleft, &outp, &outleft);
+      r = iconv (desc, &inp, &inleft, &outp, &outleft);
 
       /* Now make sure that the object on the obstack only includes
         bytes we have converted.  */
 
       /* Now make sure that the object on the obstack only includes
         bytes we have converted.  */
@@ -468,8 +518,8 @@ convert_between_encodings (const char *from, const char *to,
 
                /* Invalid input sequence.  */
                if (translit == translit_none)
 
                /* Invalid input sequence.  */
                if (translit == translit_none)
-                 error (_("Could not convert character to `%s' character set"),
-                        to);
+                 error (_("Could not convert character "
+                          "to `%s' character set"), to);
 
                /* We emit escape sequence for the bytes, skip them,
                   and try again.  */
 
                /* We emit escape sequence for the bytes, skip them,
                   and try again.  */
@@ -477,7 +527,7 @@ convert_between_encodings (const char *from, const char *to,
                  {
                    char octal[5];
 
                  {
                    char octal[5];
 
-                   sprintf (octal, "\\%.3o", *inp & 0xff);
+                   xsnprintf (octal, sizeof (octal), "\\%.3o", *inp & 0xff);
                    obstack_grow_str (output, octal);
 
                    ++inp;
                    obstack_grow_str (output, octal);
 
                    ++inp;
@@ -499,7 +549,8 @@ convert_between_encodings (const char *from, const char *to,
              break;
 
            default:
              break;
 
            default:
-             perror_with_name ("Internal error while converting character sets");
+             perror_with_name (_("Internal error while "
+                                 "converting character sets"));
            }
        }
     }
            }
        }
     }
@@ -516,7 +567,7 @@ struct wchar_iterator
   iconv_t desc;
 
   /* The input string.  This is updated as convert characters.  */
   iconv_t desc;
 
   /* The input string.  This is updated as convert characters.  */
-  char *input;
+  const gdb_byte *input;
   /* The number of bytes remaining in the input.  */
   size_t bytes;
 
   /* The number of bytes remaining in the input.  */
   size_t bytes;
 
@@ -530,19 +581,19 @@ struct wchar_iterator
 
 /* Create a new iterator.  */
 struct wchar_iterator *
 
 /* Create a new iterator.  */
 struct wchar_iterator *
-make_wchar_iterator (const gdb_byte *input, size_t bytes, const char *charset,
-                    size_t width)
+make_wchar_iterator (const gdb_byte *input, size_t bytes, 
+                    const char *charset, size_t width)
 {
   struct wchar_iterator *result;
   iconv_t desc;
 
 {
   struct wchar_iterator *result;
   iconv_t desc;
 
-  desc = iconv_open ("wchar_t", charset);
+  desc = iconv_open (INTERMEDIATE_ENCODING, charset);
   if (desc == (iconv_t) -1)
   if (desc == (iconv_t) -1)
-    perror_with_name ("Converting character sets");
+    perror_with_name (_("Converting character sets"));
 
   result = XNEW (struct wchar_iterator);
   result->desc = desc;
 
   result = XNEW (struct wchar_iterator);
   result->desc = desc;
-  result->input = (char *) input;
+  result->input = input;
   result->bytes = bytes;
   result->width = width;
 
   result->bytes = bytes;
   result->width = width;
 
@@ -585,23 +636,28 @@ wchar_iterate (struct wchar_iterator *iter,
   out_request = 1;
   while (iter->bytes > 0)
     {
   out_request = 1;
   while (iter->bytes > 0)
     {
+      ICONV_CONST char *inptr = (ICONV_CONST char *) iter->input;
       char *outptr = (char *) &iter->out[0];
       char *outptr = (char *) &iter->out[0];
-      char *orig_inptr = iter->input;
+      const gdb_byte *orig_inptr = iter->input;
       size_t orig_in = iter->bytes;
       size_t out_avail = out_request * sizeof (gdb_wchar_t);
       size_t num;
       size_t orig_in = iter->bytes;
       size_t out_avail = out_request * sizeof (gdb_wchar_t);
       size_t num;
-      gdb_wchar_t result;
+      size_t r = iconv (iter->desc, &inptr, &iter->bytes, &outptr, &out_avail);
+
+      iter->input = (gdb_byte *) inptr;
 
 
-      size_t r = iconv (iter->desc,
-                       (ICONV_CONST char **) &iter->input, &iter->bytes,
-                       &outptr, &out_avail);
       if (r == (size_t) -1)
        {
          switch (errno)
            {
            case EILSEQ:
       if (r == (size_t) -1)
        {
          switch (errno)
            {
            case EILSEQ:
-             /* Invalid input sequence.  Skip it, and let the caller
-                know about it.  */
+             /* Invalid input sequence.  We still might have
+                converted a character; if so, return it.  */
+             if (out_avail < out_request * sizeof (gdb_wchar_t))
+               break;
+             
+             /* Otherwise skip the first invalid character, and let
+                the caller know about it.  */
              *out_result = wchar_iterate_invalid;
              *ptr = iter->input;
              *len = iter->width;
              *out_result = wchar_iterate_invalid;
              *ptr = iter->input;
              *len = iter->width;
@@ -635,7 +691,8 @@ wchar_iterate (struct wchar_iterator *iter,
              return 0;
 
            default:
              return 0;
 
            default:
-             perror_with_name ("Internal error while converting character sets");
+             perror_with_name (_("Internal error while "
+                                 "converting character sets"));
            }
        }
 
            }
        }
 
@@ -658,9 +715,6 @@ wchar_iterate (struct wchar_iterator *iter,
 
 extern initialize_file_ftype _initialize_charset; /* -Wmissing-prototype */
 
 
 extern initialize_file_ftype _initialize_charset; /* -Wmissing-prototype */
 
-typedef char *char_ptr;
-DEF_VEC_P (char_ptr);
-
 static VEC (char_ptr) *charsets;
 
 #ifdef PHONY_ICONV
 static VEC (char_ptr) *charsets;
 
 #ifdef PHONY_ICONV
@@ -704,51 +758,249 @@ find_charset_names (void)
 
 #else
 
 
 #else
 
+/* Return non-zero if LINE (output from iconv) should be ignored.
+   Older iconv programs (e.g. 2.2.2) include the human readable
+   introduction even when stdout is not a tty.  Newer versions omit
+   the intro if stdout is not a tty.  */
+
+static int
+ignore_line_p (const char *line)
+{
+  /* This table is used to filter the output.  If this text appears
+     anywhere in the line, it is ignored (strstr is used).  */
+  static const char * const ignore_lines[] =
+    {
+      "The following",
+      "not necessarily",
+      "the FROM and TO",
+      "listed with several",
+      NULL
+    };
+  int i;
+
+  for (i = 0; ignore_lines[i] != NULL; ++i)
+    {
+      if (strstr (line, ignore_lines[i]) != NULL)
+       return 1;
+    }
+
+  return 0;
+}
+
 static void
 find_charset_names (void)
 {
 static void
 find_charset_names (void)
 {
-  FILE *in;
+  struct pex_obj *child;
+  char *args[3];
+  int err, status;
+  int fail = 1;
+  int flags;
+  struct gdb_environ *iconv_env;
+  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");
+
+  child = pex_init (PEX_USE_PIPES, "iconv", NULL);
+
+#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, flags,
+                              args[0], args, environ_vector (iconv_env),
+                              NULL, NULL, &err))
+    {
+      FILE *in = pex_read_output (child, 0);
 
 
-  in = popen ("iconv -l", "r");
-  /* It is ok to ignore errors; we'll fall back on a default.  */
-  if (!in)
-    return;
+      /* POSIX says that iconv -l uses an unspecified format.  We
+        parse the glibc and libiconv formats; feel free to add others
+        as needed.  */
+
+      while (in != NULL && !feof (in))
+       {
+         /* The size of buf is chosen arbitrarily.  */
+         char buf[1024];
+         char *start, *r;
+         int len;
+
+         r = fgets (buf, sizeof (buf), in);
+         if (!r)
+           break;
+         len = strlen (r);
+         if (len <= 3)
+           continue;
+         if (ignore_line_p (r))
+           continue;
+
+         /* Strip off the newline.  */
+         --len;
+         /* Strip off one or two '/'s.  glibc will print lines like
+            "8859_7//", but also "10646-1:1993/UCS4/".  */
+         if (buf[len - 1] == '/')
+           --len;
+         if (buf[len - 1] == '/')
+           --len;
+         buf[len] = '\0';
+
+         /* libiconv will print multiple entries per line, separated
+            by spaces.  Older iconvs will print multiple entries per
+            line, indented by two spaces, and separated by ", "
+            (i.e. the human readable form).  */
+         start = buf;
+         while (1)
+           {
+             int keep_going;
+             char *p;
+
+             /* Skip leading blanks.  */
+             for (p = start; *p && *p == ' '; ++p)
+               ;
+             start = p;
+             /* Find the next space, comma, or end-of-line.  */
+             for ( ; *p && *p != ' ' && *p != ','; ++p)
+               ;
+             /* Ignore an empty result.  */
+             if (p == start)
+               break;
+             keep_going = *p;
+             *p = '\0';
+             VEC_safe_push (char_ptr, charsets, xstrdup (start));
+             if (!keep_going)
+               break;
+             /* Skip any extra spaces.  */
+             for (start = p + 1; *start && *start == ' '; ++start)
+               ;
+           }
+       }
+
+      if (pex_get_status (child, 1, &status)
+         && WIFEXITED (status) && !WEXITSTATUS (status))
+       fail = 0;
 
 
-  /* POSIX says that iconv -l uses an unspecified format.  We parse
-     the glibc format; feel free to add others as needed.  */
-  while (!feof (in))
-    {
-      /* The size of buf is chosen arbitrarily.  A character set name
-        longer than this would not be very nice.  */
-      char buf[80];
-      int len;
-      char *r = fgets (buf, sizeof (buf), in);
-      if (!r)
-       break;
-      len = strlen (r);
-      if (len <= 3)
-       continue;
-      if (buf[len - 2] == '/' && buf[len - 3] == '/')
-       buf[len - 3] = '\0';
-      VEC_safe_push (char_ptr, charsets, xstrdup (buf));
     }
 
     }
 
-  pclose (in);
+  xfree (iconv_program);
+  pex_free (child);
+  free_environ (iconv_env);
 
 
-  VEC_safe_push (char_ptr, charsets, NULL);
+  if (fail)
+    {
+      /* Some error occurred, so drop the vector.  */
+      free_char_ptr_vec (charsets);
+      charsets = NULL;
+    }
+  else
+    VEC_safe_push (char_ptr, charsets, NULL);
 }
 
 #endif /* HAVE_ICONVLIST || HAVE_LIBICONVLIST */
 #endif /* PHONY_ICONV */
 
 }
 
 #endif /* HAVE_ICONVLIST || HAVE_LIBICONVLIST */
 #endif /* PHONY_ICONV */
 
+/* The "auto" target charset used by default_auto_charset.  */
+static const char *auto_target_charset_name = GDB_DEFAULT_TARGET_CHARSET;
+
+const char *
+default_auto_charset (void)
+{
+  return auto_target_charset_name;
+}
+
+const char *
+default_auto_wide_charset (void)
+{
+  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)
 {
 void
 _initialize_charset (void)
 {
-  struct cmd_list_element *new_cmd;
-
-  /* The first element is always "auto"; then we skip it for the
-     commands where it is not allowed.  */
-  VEC_safe_push (char_ptr, charsets, "auto");
+  /* The first element is always "auto".  */
+  VEC_safe_push (char_ptr, charsets, xstrdup ("auto"));
   find_charset_names ();
 
   if (VEC_length (char_ptr, charsets) > 1)
   find_charset_names ();
 
   if (VEC_length (char_ptr, charsets) > 1)
@@ -758,15 +1010,31 @@ _initialize_charset (void)
 
 #ifndef PHONY_ICONV
 #ifdef HAVE_LANGINFO_CODESET
 
 #ifndef PHONY_ICONV
 #ifdef HAVE_LANGINFO_CODESET
-  auto_host_charset_name = nl_langinfo (CODESET);
-  target_charset_name = auto_host_charset_name;
-
-  set_be_le_names ();
+  /* The result of nl_langinfo may be overwritten later.  This may
+     leak a little memory, if the user later changes the host charset,
+     but that doesn't matter much.  */
+  auto_host_charset_name = xstrdup (nl_langinfo (CODESET));
+  /* Solaris will return `646' here -- but the Solaris iconv then does
+     not accept this.  Darwin (and maybe FreeBSD) may return "" here,
+     which GNU libiconv doesn't like (infinite loop).  */
+  if (!strcmp (auto_host_charset_name, "646") || !*auto_host_charset_name)
+    auto_host_charset_name = "ASCII";
+  auto_target_charset_name = auto_host_charset_name;
+#elif defined (USE_WIN32API)
+  {
+    /* "CP" + x<=5 digits + paranoia.  */
+    static char w32_host_default_charset[16];
+
+    snprintf (w32_host_default_charset, sizeof w32_host_default_charset,
+             "CP%d", GetACP());
+    auto_host_charset_name = w32_host_default_charset;
+    auto_target_charset_name = auto_host_charset_name;
+  }
 #endif
 #endif
 
   add_setshow_enum_cmd ("charset", class_support,
 #endif
 #endif
 
   add_setshow_enum_cmd ("charset", class_support,
-                       &charset_enum[1], &host_charset_name, _("\
+                       charset_enum, &host_charset_name, _("\
 Set the host and target character sets."), _("\
 Show the host and target character sets."), _("\
 The `host character set' is the one used by the system GDB is running on.\n\
 Set the host and target character sets."), _("\
 Show the host and target character sets."), _("\
 The `host character set' is the one used by the system GDB is running on.\n\
@@ -794,7 +1062,7 @@ To see a list of the character sets GDB supports, type `set host-charset <TAB>'.
                        &setlist, &showlist);
 
   add_setshow_enum_cmd ("target-charset", class_support,
                        &setlist, &showlist);
 
   add_setshow_enum_cmd ("target-charset", class_support,
-                       &charset_enum[1], &target_charset_name, _("\
+                       charset_enum, &target_charset_name, _("\
 Set the target character set."), _("\
 Show the target character set."), _("\
 The `target character set' is the one used by the program being debugged.\n\
 Set the target character set."), _("\
 Show the target character set."), _("\
 The `target character set' is the one used by the program being debugged.\n\
@@ -806,12 +1074,12 @@ To see a list of the character sets GDB supports, type `set target-charset'<TAB>
                        &setlist, &showlist);
 
   add_setshow_enum_cmd ("target-wide-charset", class_support,
                        &setlist, &showlist);
 
   add_setshow_enum_cmd ("target-wide-charset", class_support,
-                       &charset_enum[1], &target_wide_charset_name,
+                       charset_enum, &target_wide_charset_name,
                        _("\
 Set the target wide character set."), _("\
 Show the target wide character set."), _("\
                        _("\
 Set the target wide character set."), _("\
 Show the target wide character set."), _("\
-The `target wide character set' is the one used by the program being debugged.\n\
-In particular it is the encoding used by `wchar_t'.\n\
+The `target wide character set' is the one used by the program being debugged.\
+\nIn particular it is the encoding used by `wchar_t'.\n\
 GDB translates characters and strings between the host and target\n\
 character sets as needed.\n\
 To see a list of the character sets GDB supports, type\n\
 GDB translates characters and strings between the host and target\n\
 character sets as needed.\n\
 To see a list of the character sets GDB supports, type\n\