Back to index

glibc  2.9
iconv_prog.c
Go to the documentation of this file.
00001 /* Convert text in given files from the specified from-set to the to-set.
00002    Copyright (C) 1998-2007, 2008 Free Software Foundation, Inc.
00003    This file is part of the GNU C Library.
00004    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License as published
00008    by the Free Software Foundation; version 2 of the License, or
00009    (at your option) any later version.
00010 
00011    This program is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014    GNU General Public License for more details.
00015 
00016    You should have received a copy of the GNU General Public License
00017    along with this program; if not, write to the Free Software Foundation,
00018    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
00019 
00020 #include <argp.h>
00021 #include <assert.h>
00022 #include <ctype.h>
00023 #include <errno.h>
00024 #include <error.h>
00025 #include <fcntl.h>
00026 #include <iconv.h>
00027 #include <langinfo.h>
00028 #include <locale.h>
00029 #include <search.h>
00030 #include <stdbool.h>
00031 #include <stdio.h>
00032 #include <stdlib.h>
00033 #include <string.h>
00034 #include <unistd.h>
00035 #include <libintl.h>
00036 #ifdef _POSIX_MAPPED_FILES
00037 # include <sys/mman.h>
00038 #endif
00039 #include <charmap.h>
00040 #include <gconv_int.h>
00041 #include "iconv_prog.h"
00042 #include "iconvconfig.h"
00043 
00044 /* Get libc version number.  */
00045 #include "../version.h"
00046 
00047 #define PACKAGE _libc_intl_domainname
00048 
00049 
00050 /* Name and version of program.  */
00051 static void print_version (FILE *stream, struct argp_state *state);
00052 void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
00053 
00054 #define OPT_VERBOSE  1000
00055 #define OPT_LIST     'l'
00056 
00057 /* Definitions of arguments for argp functions.  */
00058 static const struct argp_option options[] =
00059 {
00060   { NULL, 0, NULL, 0, N_("Input/Output format specification:") },
00061   { "from-code", 'f', "NAME", 0, N_("encoding of original text") },
00062   { "to-code", 't', "NAME", 0, N_("encoding for output") },
00063   { NULL, 0, NULL, 0, N_("Information:") },
00064   { "list", 'l', NULL, 0, N_("list all known coded character sets") },
00065   { NULL, 0, NULL, 0, N_("Output control:") },
00066   { NULL, 'c', NULL, 0, N_("omit invalid characters from output") },
00067   { "output", 'o', "FILE", 0, N_("output file") },
00068   { "silent", 's', NULL, 0, N_("suppress warnings") },
00069   { "verbose", OPT_VERBOSE, NULL, 0, N_("print progress information") },
00070   { NULL, 0, NULL, 0, NULL }
00071 };
00072 
00073 /* Short description of program.  */
00074 static const char doc[] = N_("\
00075 Convert encoding of given files from one encoding to another.");
00076 
00077 /* Strings for arguments in help texts.  */
00078 static const char args_doc[] = N_("[FILE...]");
00079 
00080 /* Prototype for option handler.  */
00081 static error_t parse_opt (int key, char *arg, struct argp_state *state);
00082 
00083 /* Function to print some extra text in the help message.  */
00084 static char *more_help (int key, const char *text, void *input);
00085 
00086 /* Data structure to communicate with argp functions.  */
00087 static struct argp argp =
00088 {
00089   options, parse_opt, args_doc, doc, NULL, more_help
00090 };
00091 
00092 /* Code sets to convert from and to respectively.  An empty string as the
00093    default causes the 'iconv_open' function to look up the charset of the
00094    currently selected locale and use it.  */
00095 static const char *from_code = "";
00096 static const char *to_code = "";
00097 
00098 /* File to write output to.  If NULL write to stdout.  */
00099 static const char *output_file;
00100 
00101 /* Nonzero if verbose ouput is wanted.  */
00102 int verbose;
00103 
00104 /* Nonzero if list of all coded character sets is wanted.  */
00105 static int list;
00106 
00107 /* If nonzero omit invalid character from output.  */
00108 int omit_invalid;
00109 
00110 /* Prototypes for the functions doing the actual work.  */
00111 static int process_block (iconv_t cd, char *addr, size_t len, FILE **output,
00112                        const char *output_file);
00113 static int process_fd (iconv_t cd, int fd, FILE **output,
00114                      const char *output_file);
00115 static int process_file (iconv_t cd, FILE *input, FILE **output,
00116                       const char *output_file);
00117 static void print_known_names (void) internal_function;
00118 
00119 
00120 int
00121 main (int argc, char *argv[])
00122 {
00123   int status = EXIT_SUCCESS;
00124   int remaining;
00125   iconv_t cd;
00126   const char *orig_to_code;
00127   struct charmap_t *from_charmap = NULL;
00128   struct charmap_t *to_charmap = NULL;
00129 
00130   /* Set locale via LC_ALL.  */
00131   setlocale (LC_ALL, "");
00132 
00133   /* Set the text message domain.  */
00134   textdomain (_libc_intl_domainname);
00135 
00136   /* Parse and process arguments.  */
00137   argp_parse (&argp, argc, argv, 0, &remaining, NULL);
00138 
00139   /* List all coded character sets if wanted.  */
00140   if (list)
00141     {
00142       print_known_names ();
00143       exit (EXIT_SUCCESS);
00144     }
00145 
00146   /* If we have to ignore errors make sure we use the appropriate name for
00147      the to-character-set.  */
00148   orig_to_code = to_code;
00149   if (omit_invalid)
00150     {
00151       const char *errhand = strchrnul (to_code, '/');
00152       int nslash = 2;
00153       char *newp;
00154       char *cp;
00155 
00156       if (*errhand == '/')
00157        {
00158          --nslash;
00159          errhand = strchrnul (errhand, '/');
00160 
00161          if (*errhand == '/')
00162            {
00163              --nslash;
00164              errhand = strchr (errhand, '\0');
00165            }
00166        }
00167 
00168       newp = (char *) alloca (errhand - to_code + nslash + 7 + 1);
00169       cp = mempcpy (newp, to_code, errhand - to_code);
00170       while (nslash-- > 0)
00171        *cp++ = '/';
00172       if (cp[-1] != '/')
00173        *cp++ = ',';
00174       memcpy (cp, "IGNORE", sizeof ("IGNORE"));
00175 
00176       to_code = newp;
00177     }
00178 
00179   /* POSIX 1003.2b introduces a silly thing: the arguments to -t anf -f
00180      can be file names of charmaps.  In this case iconv will have to read
00181      those charmaps and use them to do the conversion.  But there are
00182      holes in the specification.  There is nothing said that if -f is a
00183      charmap filename that -t must be, too.  And vice versa.  There is
00184      also no word about the symbolic names used.  What if they don't
00185      match?  */
00186   if (strchr (from_code, '/') != NULL)
00187     /* The from-name might be a charmap file name.  Try reading the
00188        file.  */
00189     from_charmap = charmap_read (from_code, /*0, 1*/1, 0, 0, 0);
00190 
00191   if (strchr (orig_to_code, '/') != NULL)
00192     /* The to-name might be a charmap file name.  Try reading the
00193        file.  */
00194     to_charmap = charmap_read (orig_to_code, /*0, 1,*/1, 0, 0, 0);
00195 
00196 
00197   /* At this point we have to handle two cases.  The first one is
00198      where a charmap is used for the from- or to-charset, or both.  We
00199      handle this special since it is very different from the sane way of
00200      doing things.  The other case allows converting using the iconv()
00201      function.  */
00202   if (from_charmap != NULL || to_charmap != NULL)
00203     /* Construct the conversion table and do the conversion.  */
00204     status = charmap_conversion (from_code, from_charmap, to_code, to_charmap,
00205                              argc, remaining, argv, output_file);
00206   else
00207     {
00208       /* Let's see whether we have these coded character sets.  */
00209       cd = iconv_open (to_code, from_code);
00210       if (cd == (iconv_t) -1)
00211        {
00212          if (errno == EINVAL)
00213            {
00214              /* Try to be nice with the user and tell her which of the
00215                two encoding names is wrong.  This is possible because
00216                all supported encodings can be converted from/to Unicode,
00217                in other words, because the graph of encodings is
00218                connected.  */
00219              bool from_wrong =
00220               (iconv_open ("UTF-8", from_code) == (iconv_t) -1
00221                && errno == EINVAL);
00222              bool to_wrong =
00223               (iconv_open (to_code, "UTF-8") == (iconv_t) -1
00224                && errno == EINVAL);
00225              const char *from_pretty =
00226               (from_code[0] ? from_code : nl_langinfo (CODESET));
00227              const char *to_pretty =
00228               (orig_to_code[0] ? orig_to_code : nl_langinfo (CODESET));
00229 
00230              if (from_wrong)
00231               {
00232                 if (to_wrong)
00233                   error (0, 0,
00234                         _("\
00235 conversions from `%s' and to `%s' are not supported"),
00236                         from_pretty, to_pretty);
00237                 else
00238                   error (0, 0,
00239                         _("conversion from `%s' is not supported"),
00240                         from_pretty);
00241               }
00242              else
00243               {
00244                 if (to_wrong)
00245                   error (0, 0,
00246                         _("conversion to `%s' is not supported"),
00247                         to_pretty);
00248                 else
00249                   error (0, 0,
00250                         _("conversion from `%s' to `%s' is not supported"),
00251                         from_pretty, to_pretty);
00252               }
00253 
00254              argp_help (&argp, stderr, ARGP_HELP_SEE,
00255                       program_invocation_short_name);
00256              exit (1);
00257            }
00258          else
00259            error (EXIT_FAILURE, errno,
00260                  _("failed to start conversion processing"));
00261        }
00262 
00263       /* The output file.  Will be opened when we are ready to produce
00264         output.  */
00265       FILE *output = NULL;
00266 
00267       /* Now process the remaining files.  Write them to stdout or the file
00268         specified with the `-o' parameter.  If we have no file given as
00269         the parameter process all from stdin.  */
00270       if (remaining == argc)
00271        {
00272          if (process_file (cd, stdin, &output, output_file) != 0)
00273            status = EXIT_FAILURE;
00274        }
00275       else
00276        do
00277          {
00278 #ifdef _POSIX_MAPPED_FILES
00279            struct stat st;
00280            char *addr;
00281 #endif
00282            int fd, ret;
00283 
00284            if (verbose)
00285              fprintf (stderr, "%s:\n", argv[remaining]);
00286            if (strcmp (argv[remaining], "-") == 0)
00287              fd = 0;
00288            else
00289              {
00290               fd = open (argv[remaining], O_RDONLY);
00291 
00292               if (fd == -1)
00293                 {
00294                   error (0, errno, _("cannot open input file `%s'"),
00295                         argv[remaining]);
00296                   status = EXIT_FAILURE;
00297                   continue;
00298                 }
00299              }
00300 
00301 #ifdef _POSIX_MAPPED_FILES
00302            /* We have possibilities for reading the input file.  First try
00303               to mmap() it since this will provide the fastest solution.  */
00304            if (fstat (fd, &st) == 0
00305               && ((addr = mmap (NULL, st.st_size, PROT_READ, MAP_PRIVATE,
00306                               fd, 0)) != MAP_FAILED))
00307              {
00308               /* Yes, we can use mmap().  The descriptor is not needed
00309                  anymore.  */
00310               if (close (fd) != 0)
00311                 error (EXIT_FAILURE, errno,
00312                       _("error while closing input `%s'"),
00313                       argv[remaining]);
00314 
00315               ret = process_block (cd, addr, st.st_size, &output,
00316                                  output_file);
00317 
00318               /* We don't need the input data anymore.  */
00319               munmap ((void *) addr, st.st_size);
00320 
00321               if (ret != 0)
00322                 {
00323                   status = EXIT_FAILURE;
00324 
00325                   if (ret < 0)
00326                     /* We cannot go on with producing output since it might
00327                       lead to problem because the last output might leave
00328                       the output stream in an undefined state.  */
00329                     break;
00330                 }
00331              }
00332            else
00333 #endif /* _POSIX_MAPPED_FILES */
00334              {
00335               /* Read the file in pieces.  */
00336               ret = process_fd (cd, fd, &output, output_file);
00337 
00338               /* Now close the file.  */
00339               close (fd);
00340 
00341               if (ret != 0)
00342                 {
00343                   /* Something went wrong.  */
00344                   status = EXIT_FAILURE;
00345 
00346                   if (ret < 0)
00347                     /* We cannot go on with producing output since it might
00348                       lead to problem because the last output might leave
00349                       the output stream in an undefined state.  */
00350                     break;
00351                 }
00352              }
00353          }
00354        while (++remaining < argc);
00355 
00356       /* Close the output file now.  */
00357       if (output != NULL && fclose (output))
00358        error (EXIT_FAILURE, errno, _("error while closing output file"));
00359     }
00360 
00361   return status;
00362 }
00363 
00364 
00365 /* Handle program arguments.  */
00366 static error_t
00367 parse_opt (int key, char *arg, struct argp_state *state)
00368 {
00369   switch (key)
00370     {
00371     case 'f':
00372       from_code = arg;
00373       break;
00374     case 't':
00375       to_code = arg;
00376       break;
00377     case 'o':
00378       output_file = arg;
00379       break;
00380     case 's':
00381       /* Nothing, for now at least.  We are not giving out any information
00382         about missing character or so.  */
00383       break;
00384     case 'c':
00385       /* Omit invalid characters from output.  */
00386       omit_invalid = 1;
00387       break;
00388     case OPT_VERBOSE:
00389       verbose = 1;
00390       break;
00391     case OPT_LIST:
00392       list = 1;
00393       break;
00394     default:
00395       return ARGP_ERR_UNKNOWN;
00396     }
00397   return 0;
00398 }
00399 
00400 
00401 static char *
00402 more_help (int key, const char *text, void *input)
00403 {
00404   switch (key)
00405     {
00406     case ARGP_KEY_HELP_EXTRA:
00407       /* We print some extra information.  */
00408       return strdup (gettext ("\
00409 For bug reporting instructions, please see:\n\
00410 <http://www.gnu.org/software/libc/bugs.html>.\n"));
00411     default:
00412       break;
00413     }
00414   return (char *) text;
00415 }
00416 
00417 
00418 /* Print the version information.  */
00419 static void
00420 print_version (FILE *stream, struct argp_state *state)
00421 {
00422   fprintf (stream, "iconv (GNU %s) %s\n", PACKAGE, VERSION);
00423   fprintf (stream, gettext ("\
00424 Copyright (C) %s Free Software Foundation, Inc.\n\
00425 This is free software; see the source for copying conditions.  There is NO\n\
00426 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
00427 "), "2008");
00428   fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
00429 }
00430 
00431 
00432 static int
00433 write_output (const char *outbuf, const char *outptr, FILE **output,
00434              const char *output_file)
00435 {
00436   /* We have something to write out.  */
00437   int errno_save = errno;
00438 
00439   if (*output == NULL)
00440     {
00441       /* Determine output file.  */
00442       if (output_file != NULL && strcmp (output_file, "-") != 0)
00443        {
00444          *output = fopen (output_file, "w");
00445          if (output == NULL)
00446            error (EXIT_FAILURE, errno, _("cannot open output file"));
00447        }
00448       else
00449        *output = stdout;
00450     }
00451 
00452   if (fwrite (outbuf, 1, outptr - outbuf, *output) < (size_t) (outptr - outbuf)
00453       || ferror (*output))
00454     {
00455       /* Error occurred while printing the result.  */
00456       error (0, 0, _("\
00457 conversion stopped due to problem in writing the output"));
00458       return -1;
00459     }
00460 
00461   errno = errno_save;
00462 
00463   return 0;
00464 }
00465 
00466 
00467 static int
00468 process_block (iconv_t cd, char *addr, size_t len, FILE **output,
00469               const char *output_file)
00470 {
00471 #define OUTBUF_SIZE  32768
00472   const char *start = addr;
00473   char outbuf[OUTBUF_SIZE];
00474   char *outptr;
00475   size_t outlen;
00476   size_t n;
00477   int ret = 0;
00478 
00479   while (len > 0)
00480     {
00481       outptr = outbuf;
00482       outlen = OUTBUF_SIZE;
00483       n = iconv (cd, &addr, &len, &outptr, &outlen);
00484 
00485       if (n == (size_t) -1 && omit_invalid && errno == EILSEQ)
00486        {
00487          ret = 1;
00488          if (len == 0)
00489            n = 0;
00490          else
00491            errno = E2BIG;
00492        }
00493 
00494       if (outptr != outbuf)
00495        {
00496          ret = write_output (outbuf, outptr, output, output_file);
00497          if (ret != 0)
00498            break;
00499        }
00500 
00501       if (n != (size_t) -1)
00502        {
00503          /* All the input test is processed.  For state-dependent
00504              character sets we have to flush the state now.  */
00505          outptr = outbuf;
00506          outlen = OUTBUF_SIZE;
00507          n = iconv (cd, NULL, NULL, &outptr, &outlen);
00508 
00509          if (outptr != outbuf)
00510            {
00511              ret = write_output (outbuf, outptr, output, output_file);
00512              if (ret != 0)
00513               break;
00514            }
00515 
00516          if (n != (size_t) -1)
00517            break;
00518 
00519          if (omit_invalid && errno == EILSEQ)
00520            {
00521              ret = 1;
00522              break;
00523            }
00524        }
00525 
00526       if (errno != E2BIG)
00527        {
00528          /* iconv() ran into a problem.  */
00529          switch (errno)
00530            {
00531            case EILSEQ:
00532              if (! omit_invalid)
00533               error (0, 0, _("illegal input sequence at position %ld"),
00534                      (long int) (addr - start));
00535              break;
00536            case EINVAL:
00537              error (0, 0, _("\
00538 incomplete character or shift sequence at end of buffer"));
00539              break;
00540            case EBADF:
00541              error (0, 0, _("internal error (illegal descriptor)"));
00542              break;
00543            default:
00544              error (0, 0, _("unknown iconv() error %d"), errno);
00545              break;
00546            }
00547 
00548          return -1;
00549        }
00550     }
00551 
00552   return ret;
00553 }
00554 
00555 
00556 static int
00557 process_fd (iconv_t cd, int fd, FILE **output, const char *output_file)
00558 {
00559   /* we have a problem with reading from a desriptor since we must not
00560      provide the iconv() function an incomplete character or shift
00561      sequence at the end of the buffer.  Since we have to deal with
00562      arbitrary encodings we must read the whole text in a buffer and
00563      process it in one step.  */
00564   static char *inbuf = NULL;
00565   static size_t maxlen = 0;
00566   char *inptr = NULL;
00567   size_t actlen = 0;
00568 
00569   while (actlen < maxlen)
00570     {
00571       ssize_t n = read (fd, inptr, maxlen - actlen);
00572 
00573       if (n == 0)
00574        /* No more text to read.  */
00575        break;
00576 
00577       if (n == -1)
00578        {
00579          /* Error while reading.  */
00580          error (0, errno, _("error while reading the input"));
00581          return -1;
00582        }
00583 
00584       inptr += n;
00585       actlen += n;
00586     }
00587 
00588   if (actlen == maxlen)
00589     while (1)
00590       {
00591        ssize_t n;
00592        char *new_inbuf;
00593 
00594        /* Increase the buffer.  */
00595        new_inbuf = (char *) realloc (inbuf, maxlen + 32768);
00596        if (new_inbuf == NULL)
00597          {
00598            error (0, errno, _("unable to allocate buffer for input"));
00599            return -1;
00600          }
00601        inbuf = new_inbuf;
00602        maxlen += 32768;
00603        inptr = inbuf + actlen;
00604 
00605        do
00606          {
00607            n = read (fd, inptr, maxlen - actlen);
00608 
00609            if (n == 0)
00610              /* No more text to read.  */
00611              break;
00612 
00613            if (n == -1)
00614              {
00615               /* Error while reading.  */
00616               error (0, errno, _("error while reading the input"));
00617               return -1;
00618              }
00619 
00620            inptr += n;
00621            actlen += n;
00622          }
00623        while (actlen < maxlen);
00624 
00625        if (n == 0)
00626          /* Break again so we leave both loops.  */
00627          break;
00628       }
00629 
00630   /* Now we have all the input in the buffer.  Process it in one run.  */
00631   return process_block (cd, inbuf, actlen, output, output_file);
00632 }
00633 
00634 
00635 static int
00636 process_file (iconv_t cd, FILE *input, FILE **output, const char *output_file)
00637 {
00638   /* This should be safe since we use this function only for `stdin' and
00639      we haven't read anything so far.  */
00640   return process_fd (cd, fileno (input), output, output_file);
00641 }
00642 
00643 
00644 /* Print all known character sets/encodings.  */
00645 static void *printlist;
00646 static size_t column;
00647 static int not_first;
00648 
00649 static void
00650 insert_print_list (const void *nodep, VISIT value, int level)
00651 {
00652   if (value == leaf || value == postorder)
00653     {
00654       const struct gconv_alias *s = *(const struct gconv_alias **) nodep;
00655       tsearch (s->fromname, &printlist, (__compar_fn_t) strverscmp);
00656     }
00657 }
00658 
00659 static void
00660 do_print_human  (const void *nodep, VISIT value, int level)
00661 {
00662   if (value == leaf || value == postorder)
00663     {
00664       const char *s = *(const char **) nodep;
00665       size_t len = strlen (s);
00666       size_t cnt;
00667 
00668       while (len > 0 && s[len - 1] == '/')
00669        --len;
00670 
00671       for (cnt = 0; cnt < len; ++cnt)
00672        if (isalnum (s[cnt]))
00673          break;
00674       if (cnt == len)
00675        return;
00676 
00677       if (not_first)
00678        {
00679          putchar (',');
00680          ++column;
00681 
00682          if (column > 2 && column + len > 77)
00683            {
00684              fputs ("\n  ", stdout);
00685              column = 2;
00686            }
00687          else
00688            {
00689              putchar (' ');
00690              ++column;
00691            }
00692        }
00693       else
00694        not_first = 1;
00695 
00696       fwrite (s, len, 1, stdout);
00697       column += len;
00698     }
00699 }
00700 
00701 static void
00702 do_print  (const void *nodep, VISIT value, int level)
00703 {
00704   if (value == leaf || value == postorder)
00705     {
00706       const char *s = *(const char **) nodep;
00707 
00708       puts (s);
00709     }
00710 }
00711 
00712 static void
00713 internal_function
00714 add_known_names (struct gconv_module *node)
00715 {
00716   if (node->left != NULL)
00717     add_known_names (node->left);
00718   if (node->right != NULL)
00719     add_known_names (node->right);
00720   do
00721     {
00722       if (strcmp (node->from_string, "INTERNAL"))
00723        tsearch (node->from_string, &printlist,
00724                (__compar_fn_t) strverscmp);
00725       if (strcmp (node->to_string, "INTERNAL") != 0)
00726        tsearch (node->to_string, &printlist, (__compar_fn_t) strverscmp);
00727 
00728       node = node->same;
00729     }
00730   while (node != NULL);
00731 }
00732 
00733 
00734 static void
00735 insert_cache (void)
00736 {
00737   const struct gconvcache_header *header;
00738   const char *strtab;
00739   const struct hash_entry *hashtab;
00740   size_t cnt;
00741 
00742   header = (const struct gconvcache_header *) __gconv_get_cache ();
00743   strtab = (char *) header + header->string_offset;
00744   hashtab = (struct hash_entry *) ((char *) header + header->hash_offset);
00745 
00746   for (cnt = 0; cnt < header->hash_size; ++cnt)
00747     if (hashtab[cnt].string_offset != 0)
00748       {
00749        const char *str = strtab + hashtab[cnt].string_offset;
00750 
00751        if (strcmp (str, "INTERNAL") != 0)
00752          tsearch (str, &printlist, (__compar_fn_t) strverscmp);
00753       }
00754 }
00755 
00756 
00757 static void
00758 internal_function
00759 print_known_names (void)
00760 {
00761   iconv_t h;
00762   void *cache;
00763 
00764   /* We must initialize the internal databases first.  */
00765   h = iconv_open ("L1", "L1");
00766   iconv_close (h);
00767 
00768   /* See whether we have a cache.  */
00769   cache = __gconv_get_cache ();
00770   if (cache != NULL)
00771     /* Yep, use only this information.  */
00772     insert_cache ();
00773   else
00774     {
00775       struct gconv_module *modules;
00776 
00777       /* No, then use the information read from the gconv-modules file.
00778         First add the aliases.  */
00779       twalk (__gconv_get_alias_db (), insert_print_list);
00780 
00781       /* Add the from- and to-names from the known modules.  */
00782       modules = __gconv_get_modules_db ();
00783       if (modules != NULL)
00784        add_known_names (modules);
00785     }
00786 
00787   bool human_readable = isatty (fileno (stdout));
00788 
00789   if (human_readable)
00790     fputs (_("\
00791 The following list contain all the coded character sets known.  This does\n\
00792 not necessarily mean that all combinations of these names can be used for\n\
00793 the FROM and TO command line parameters.  One coded character set can be\n\
00794 listed with several different names (aliases).\n\n  "), stdout);
00795 
00796   /* Now print the collected names.  */
00797   column = 2;
00798   twalk (printlist, human_readable ? do_print_human : do_print);
00799 
00800   if (human_readable && column != 0)
00801     puts ("");
00802 }