Blame iconv/iconv_prog.c

Packit 6c4009
/* Convert text in given files from the specified from-set to the to-set.
Packit 6c4009
   Copyright (C) 1998-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
Packit 6c4009
Packit 6c4009
   This program is free software; you can redistribute it and/or modify
Packit 6c4009
   it under the terms of the GNU General Public License as published
Packit 6c4009
   by the Free Software Foundation; version 2 of the License, or
Packit 6c4009
   (at your option) any later version.
Packit 6c4009
Packit 6c4009
   This program is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 6c4009
   GNU General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU General Public License
Packit 6c4009
   along with this program; if not, see <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#include <argp.h>
Packit 6c4009
#include <assert.h>
Packit 6c4009
#include <ctype.h>
Packit 6c4009
#include <errno.h>
Packit 6c4009
#include <error.h>
Packit 6c4009
#include <fcntl.h>
Packit 6c4009
#include <iconv.h>
Packit 6c4009
#include <langinfo.h>
Packit 6c4009
#include <locale.h>
Packit 6c4009
#include <search.h>
Packit 6c4009
#include <stdbool.h>
Packit 6c4009
#include <stdio.h>
Packit 6c4009
#include <stdlib.h>
Packit 6c4009
#include <string.h>
Packit 6c4009
#include <unistd.h>
Packit 6c4009
#include <libintl.h>
Packit 6c4009
#ifdef _POSIX_MAPPED_FILES
Packit 6c4009
# include <sys/mman.h>
Packit 6c4009
#endif
Packit 6c4009
#include <charmap.h>
Packit 6c4009
#include <gconv_int.h>
Packit 6c4009
#include "iconv_prog.h"
Packit 6c4009
#include "iconvconfig.h"
Packit Service da2e36
#include "gconv_charset.h"
Packit 6c4009
Packit 6c4009
/* Get libc version number.  */
Packit 6c4009
#include "../version.h"
Packit 6c4009
Packit 6c4009
#define PACKAGE _libc_intl_domainname
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Name and version of program.  */
Packit 6c4009
static void print_version (FILE *stream, struct argp_state *state);
Packit 6c4009
void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
Packit 6c4009
Packit 6c4009
#define OPT_VERBOSE	1000
Packit 6c4009
#define OPT_LIST	'l'
Packit 6c4009
Packit 6c4009
/* Definitions of arguments for argp functions.  */
Packit 6c4009
static const struct argp_option options[] =
Packit 6c4009
{
Packit 6c4009
  { NULL, 0, NULL, 0, N_("Input/Output format specification:") },
Packit 6c4009
  { "from-code", 'f', N_("NAME"), 0, N_("encoding of original text") },
Packit 6c4009
  { "to-code", 't', N_("NAME"), 0, N_("encoding for output") },
Packit 6c4009
  { NULL, 0, NULL, 0, N_("Information:") },
Packit 6c4009
  { "list", 'l', NULL, 0, N_("list all known coded character sets") },
Packit 6c4009
  { NULL, 0, NULL, 0, N_("Output control:") },
Packit 6c4009
  { NULL, 'c', NULL, 0, N_("omit invalid characters from output") },
Packit 6c4009
  { "output", 'o', N_("FILE"), 0, N_("output file") },
Packit 6c4009
  { "silent", 's', NULL, 0, N_("suppress warnings") },
Packit 6c4009
  { "verbose", OPT_VERBOSE, NULL, 0, N_("print progress information") },
Packit 6c4009
  { NULL, 0, NULL, 0, NULL }
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
/* Short description of program.  */
Packit 6c4009
static const char doc[] = N_("\
Packit 6c4009
Convert encoding of given files from one encoding to another.");
Packit 6c4009
Packit 6c4009
/* Strings for arguments in help texts.  */
Packit 6c4009
static const char args_doc[] = N_("[FILE...]");
Packit 6c4009
Packit 6c4009
/* Prototype for option handler.  */
Packit 6c4009
static error_t parse_opt (int key, char *arg, struct argp_state *state);
Packit 6c4009
Packit 6c4009
/* Function to print some extra text in the help message.  */
Packit 6c4009
static char *more_help (int key, const char *text, void *input);
Packit 6c4009
Packit 6c4009
/* Data structure to communicate with argp functions.  */
Packit 6c4009
static struct argp argp =
Packit 6c4009
{
Packit 6c4009
  options, parse_opt, args_doc, doc, NULL, more_help
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
/* Code sets to convert from and to respectively.  An empty string as the
Packit 6c4009
   default causes the 'iconv_open' function to look up the charset of the
Packit 6c4009
   currently selected locale and use it.  */
Packit 6c4009
static const char *from_code = "";
Packit 6c4009
static const char *to_code = "";
Packit 6c4009
Packit 6c4009
/* File to write output to.  If NULL write to stdout.  */
Packit 6c4009
static const char *output_file;
Packit 6c4009
Packit 6c4009
/* Nonzero if list of all coded character sets is wanted.  */
Packit 6c4009
static int list;
Packit 6c4009
Packit 6c4009
/* If nonzero omit invalid character from output.  */
Packit 6c4009
int omit_invalid;
Packit 6c4009
Packit 6c4009
/* Prototypes for the functions doing the actual work.  */
Packit 6c4009
static int process_block (iconv_t cd, char *addr, size_t len, FILE **output,
Packit 6c4009
			  const char *output_file);
Packit 6c4009
static int process_fd (iconv_t cd, int fd, FILE **output,
Packit 6c4009
		       const char *output_file);
Packit 6c4009
static int process_file (iconv_t cd, FILE *input, FILE **output,
Packit 6c4009
			 const char *output_file);
Packit 6c4009
static void print_known_names (void);
Packit 6c4009
Packit 6c4009
Packit 6c4009
int
Packit 6c4009
main (int argc, char *argv[])
Packit 6c4009
{
Packit 6c4009
  int status = EXIT_SUCCESS;
Packit 6c4009
  int remaining;
Packit Service da2e36
  __gconv_t cd;
Packit 6c4009
  struct charmap_t *from_charmap = NULL;
Packit 6c4009
  struct charmap_t *to_charmap = NULL;
Packit 6c4009
Packit 6c4009
  /* Set locale via LC_ALL.  */
Packit 6c4009
  setlocale (LC_ALL, "");
Packit 6c4009
Packit 6c4009
  /* Set the text message domain.  */
Packit 6c4009
  textdomain (_libc_intl_domainname);
Packit 6c4009
Packit 6c4009
  /* Parse and process arguments.  */
Packit 6c4009
  argp_parse (&argp, argc, argv, 0, &remaining, NULL);
Packit 6c4009
Packit 6c4009
  /* List all coded character sets if wanted.  */
Packit 6c4009
  if (list)
Packit 6c4009
    {
Packit 6c4009
      print_known_names ();
Packit 6c4009
      exit (EXIT_SUCCESS);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* POSIX 1003.2b introduces a silly thing: the arguments to -t anf -f
Packit 6c4009
     can be file names of charmaps.  In this case iconv will have to read
Packit 6c4009
     those charmaps and use them to do the conversion.  But there are
Packit 6c4009
     holes in the specification.  There is nothing said that if -f is a
Packit 6c4009
     charmap filename that -t must be, too.  And vice versa.  There is
Packit 6c4009
     also no word about the symbolic names used.  What if they don't
Packit 6c4009
     match?  */
Packit 6c4009
  if (strchr (from_code, '/') != NULL)
Packit 6c4009
    /* The from-name might be a charmap file name.  Try reading the
Packit 6c4009
       file.  */
Packit 6c4009
    from_charmap = charmap_read (from_code, /*0, 1*/1, 0, 0, 0);
Packit 6c4009
Packit Service da2e36
  if (strchr (to_code, '/') != NULL)
Packit 6c4009
    /* The to-name might be a charmap file name.  Try reading the
Packit 6c4009
       file.  */
Packit Service da2e36
    to_charmap = charmap_read (to_code, /*0, 1,*/1, 0, 0, 0);
Packit 6c4009
Packit 6c4009
Packit 6c4009
  /* At this point we have to handle two cases.  The first one is
Packit 6c4009
     where a charmap is used for the from- or to-charset, or both.  We
Packit 6c4009
     handle this special since it is very different from the sane way of
Packit 6c4009
     doing things.  The other case allows converting using the iconv()
Packit 6c4009
     function.  */
Packit 6c4009
  if (from_charmap != NULL || to_charmap != NULL)
Packit 6c4009
    /* Construct the conversion table and do the conversion.  */
Packit 6c4009
    status = charmap_conversion (from_code, from_charmap, to_code, to_charmap,
Packit 6c4009
				 argc, remaining, argv, output_file);
Packit 6c4009
  else
Packit 6c4009
    {
Packit Service da2e36
      struct gconv_spec conv_spec;
Packit Service da2e36
      int res;
Packit Service da2e36
Packit Service da2e36
      if (__gconv_create_spec (&conv_spec, from_code, to_code) == NULL)
Packit Service da2e36
        {
Packit Service da2e36
          error (EXIT_FAILURE, errno,
Packit Service da2e36
                 _("failed to start conversion processing"));
Packit Service da2e36
          exit (1);
Packit Service da2e36
        }
Packit Service da2e36
Packit Service da2e36
      if (omit_invalid)
Packit Service da2e36
        conv_spec.ignore = true;
Packit Service da2e36
Packit 6c4009
      /* Let's see whether we have these coded character sets.  */
Packit Service da2e36
      res = __gconv_open (&conv_spec, &cd, 0);
Packit Service da2e36
Packit Service a0e2fb
      __gconv_destroy_spec (&conv_spec);
Packit Service da2e36
Packit Service da2e36
      if (res != __GCONV_OK)
Packit 6c4009
	{
Packit 6c4009
	  if (errno == EINVAL)
Packit 6c4009
	    {
Packit 6c4009
	      /* Try to be nice with the user and tell her which of the
Packit 6c4009
		 two encoding names is wrong.  This is possible because
Packit 6c4009
		 all supported encodings can be converted from/to Unicode,
Packit 6c4009
		 in other words, because the graph of encodings is
Packit 6c4009
		 connected.  */
Packit 6c4009
	      bool from_wrong =
Packit 6c4009
		(iconv_open ("UTF-8", from_code) == (iconv_t) -1
Packit 6c4009
		 && errno == EINVAL);
Packit 6c4009
	      bool to_wrong =
Packit 6c4009
		(iconv_open (to_code, "UTF-8") == (iconv_t) -1
Packit 6c4009
		 && errno == EINVAL);
Packit 6c4009
	      const char *from_pretty =
Packit 6c4009
		(from_code[0] ? from_code : nl_langinfo (CODESET));
Packit 6c4009
	      const char *to_pretty =
Packit Service da2e36
		(to_code[0] ? to_code : nl_langinfo (CODESET));
Packit 6c4009
Packit 6c4009
	      if (from_wrong)
Packit 6c4009
		{
Packit 6c4009
		  if (to_wrong)
Packit 6c4009
		    error (0, 0,
Packit 6c4009
			   _("\
Packit 6c4009
conversions from `%s' and to `%s' are not supported"),
Packit 6c4009
			   from_pretty, to_pretty);
Packit 6c4009
		  else
Packit 6c4009
		    error (0, 0,
Packit 6c4009
			   _("conversion from `%s' is not supported"),
Packit 6c4009
			   from_pretty);
Packit 6c4009
		}
Packit 6c4009
	      else
Packit 6c4009
		{
Packit 6c4009
		  if (to_wrong)
Packit 6c4009
		    error (0, 0,
Packit 6c4009
			   _("conversion to `%s' is not supported"),
Packit 6c4009
			   to_pretty);
Packit 6c4009
		  else
Packit 6c4009
		    error (0, 0,
Packit 6c4009
			   _("conversion from `%s' to `%s' is not supported"),
Packit 6c4009
			   from_pretty, to_pretty);
Packit 6c4009
		}
Packit 6c4009
Packit 6c4009
	      argp_help (&argp, stderr, ARGP_HELP_SEE,
Packit 6c4009
			 program_invocation_short_name);
Packit 6c4009
	      exit (1);
Packit 6c4009
	    }
Packit 6c4009
	  else
Packit 6c4009
	    error (EXIT_FAILURE, errno,
Packit 6c4009
		   _("failed to start conversion processing"));
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      /* The output file.  Will be opened when we are ready to produce
Packit 6c4009
	 output.  */
Packit 6c4009
      FILE *output = NULL;
Packit 6c4009
Packit 6c4009
      /* Now process the remaining files.  Write them to stdout or the file
Packit 6c4009
	 specified with the `-o' parameter.  If we have no file given as
Packit 6c4009
	 the parameter process all from stdin.  */
Packit 6c4009
      if (remaining == argc)
Packit 6c4009
	{
Packit 6c4009
	  if (process_file (cd, stdin, &output, output_file) != 0)
Packit 6c4009
	    status = EXIT_FAILURE;
Packit 6c4009
	}
Packit 6c4009
      else
Packit 6c4009
	do
Packit 6c4009
	  {
Packit 6c4009
#ifdef _POSIX_MAPPED_FILES
Packit 6c4009
	    struct stat64 st;
Packit 6c4009
	    char *addr;
Packit 6c4009
#endif
Packit 6c4009
	    int fd, ret;
Packit 6c4009
Packit 6c4009
	    if (verbose)
Packit 6c4009
	      fprintf (stderr, "%s:\n", argv[remaining]);
Packit 6c4009
	    if (strcmp (argv[remaining], "-") == 0)
Packit 6c4009
	      fd = 0;
Packit 6c4009
	    else
Packit 6c4009
	      {
Packit 6c4009
		fd = open (argv[remaining], O_RDONLY);
Packit 6c4009
Packit 6c4009
		if (fd == -1)
Packit 6c4009
		  {
Packit 6c4009
		    error (0, errno, _("cannot open input file `%s'"),
Packit 6c4009
			   argv[remaining]);
Packit 6c4009
		    status = EXIT_FAILURE;
Packit 6c4009
		    continue;
Packit 6c4009
		  }
Packit 6c4009
	      }
Packit 6c4009
Packit 6c4009
#ifdef _POSIX_MAPPED_FILES
Packit 6c4009
	    /* We have possibilities for reading the input file.  First try
Packit 6c4009
	       to mmap() it since this will provide the fastest solution.  */
Packit 6c4009
	    if (fstat64 (fd, &st) == 0
Packit 6c4009
		&& ((addr = mmap (NULL, st.st_size, PROT_READ, MAP_PRIVATE,
Packit 6c4009
				  fd, 0)) != MAP_FAILED))
Packit 6c4009
	      {
Packit 6c4009
		/* Yes, we can use mmap().  The descriptor is not needed
Packit 6c4009
		   anymore.  */
Packit 6c4009
		if (close (fd) != 0)
Packit 6c4009
		  error (EXIT_FAILURE, errno,
Packit 6c4009
			 _("error while closing input `%s'"),
Packit 6c4009
			 argv[remaining]);
Packit 6c4009
Packit 6c4009
		ret = process_block (cd, addr, st.st_size, &output,
Packit 6c4009
				     output_file);
Packit 6c4009
Packit 6c4009
		/* We don't need the input data anymore.  */
Packit 6c4009
		munmap ((void *) addr, st.st_size);
Packit 6c4009
Packit 6c4009
		if (ret != 0)
Packit 6c4009
		  {
Packit 6c4009
		    status = EXIT_FAILURE;
Packit 6c4009
Packit 6c4009
		    if (ret < 0)
Packit 6c4009
		      /* We cannot go on with producing output since it might
Packit 6c4009
			 lead to problem because the last output might leave
Packit 6c4009
			 the output stream in an undefined state.  */
Packit 6c4009
		      break;
Packit 6c4009
		  }
Packit 6c4009
	      }
Packit 6c4009
	    else
Packit 6c4009
#endif	/* _POSIX_MAPPED_FILES */
Packit 6c4009
	      {
Packit 6c4009
		/* Read the file in pieces.  */
Packit 6c4009
		ret = process_fd (cd, fd, &output, output_file);
Packit 6c4009
Packit 6c4009
		/* Now close the file.  */
Packit 6c4009
		close (fd);
Packit 6c4009
Packit 6c4009
		if (ret != 0)
Packit 6c4009
		  {
Packit 6c4009
		    /* Something went wrong.  */
Packit 6c4009
		    status = EXIT_FAILURE;
Packit 6c4009
Packit 6c4009
		    if (ret < 0)
Packit 6c4009
		      /* We cannot go on with producing output since it might
Packit 6c4009
			 lead to problem because the last output might leave
Packit 6c4009
			 the output stream in an undefined state.  */
Packit 6c4009
		      break;
Packit 6c4009
		  }
Packit 6c4009
	      }
Packit 6c4009
	  }
Packit 6c4009
	while (++remaining < argc);
Packit 6c4009
Packit 6c4009
      /* Close the output file now.  */
Packit 6c4009
      if (output != NULL && fclose (output))
Packit 6c4009
	error (EXIT_FAILURE, errno, _("error while closing output file"));
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  return status;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Handle program arguments.  */
Packit 6c4009
static error_t
Packit 6c4009
parse_opt (int key, char *arg, struct argp_state *state)
Packit 6c4009
{
Packit 6c4009
  switch (key)
Packit 6c4009
    {
Packit 6c4009
    case 'f':
Packit 6c4009
      from_code = arg;
Packit 6c4009
      break;
Packit 6c4009
    case 't':
Packit 6c4009
      to_code = arg;
Packit 6c4009
      break;
Packit 6c4009
    case 'o':
Packit 6c4009
      output_file = arg;
Packit 6c4009
      break;
Packit 6c4009
    case 's':
Packit 6c4009
      /* Nothing, for now at least.  We are not giving out any information
Packit 6c4009
	 about missing character or so.  */
Packit 6c4009
      break;
Packit 6c4009
    case 'c':
Packit 6c4009
      /* Omit invalid characters from output.  */
Packit 6c4009
      omit_invalid = 1;
Packit 6c4009
      break;
Packit 6c4009
    case OPT_VERBOSE:
Packit 6c4009
      verbose = 1;
Packit 6c4009
      break;
Packit 6c4009
    case OPT_LIST:
Packit 6c4009
      list = 1;
Packit 6c4009
      break;
Packit 6c4009
    default:
Packit 6c4009
      return ARGP_ERR_UNKNOWN;
Packit 6c4009
    }
Packit 6c4009
  return 0;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
static char *
Packit 6c4009
more_help (int key, const char *text, void *input)
Packit 6c4009
{
Packit 6c4009
  char *tp = NULL;
Packit 6c4009
  switch (key)
Packit 6c4009
    {
Packit 6c4009
    case ARGP_KEY_HELP_EXTRA:
Packit 6c4009
      /* We print some extra information.  */
Packit 6c4009
      if (asprintf (&tp, gettext ("\
Packit 6c4009
For bug reporting instructions, please see:\n\
Packit 6c4009
%s.\n"), REPORT_BUGS_TO) < 0)
Packit 6c4009
	return NULL;
Packit 6c4009
      return tp;
Packit 6c4009
    default:
Packit 6c4009
      break;
Packit 6c4009
    }
Packit 6c4009
  return (char *) text;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Print the version information.  */
Packit 6c4009
static void
Packit 6c4009
print_version (FILE *stream, struct argp_state *state)
Packit 6c4009
{
Packit 6c4009
  fprintf (stream, "iconv %s%s\n", PKGVERSION, VERSION);
Packit 6c4009
  fprintf (stream, gettext ("\
Packit 6c4009
Copyright (C) %s Free Software Foundation, Inc.\n\
Packit 6c4009
This is free software; see the source for copying conditions.  There is NO\n\
Packit 6c4009
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
Packit 6c4009
"), "2018");
Packit 6c4009
  fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
static int
Packit 6c4009
write_output (const char *outbuf, const char *outptr, FILE **output,
Packit 6c4009
	      const char *output_file)
Packit 6c4009
{
Packit 6c4009
  /* We have something to write out.  */
Packit 6c4009
  int errno_save = errno;
Packit 6c4009
Packit 6c4009
  if (*output == NULL)
Packit 6c4009
    {
Packit 6c4009
      /* Determine output file.  */
Packit 6c4009
      if (output_file != NULL && strcmp (output_file, "-") != 0)
Packit 6c4009
	{
Packit 6c4009
	  *output = fopen (output_file, "w");
Packit 6c4009
	  if (*output == NULL)
Packit 6c4009
	    error (EXIT_FAILURE, errno, _("cannot open output file"));
Packit 6c4009
	}
Packit 6c4009
      else
Packit 6c4009
	*output = stdout;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (fwrite (outbuf, 1, outptr - outbuf, *output) < (size_t) (outptr - outbuf)
Packit 6c4009
      || ferror (*output))
Packit 6c4009
    {
Packit 6c4009
      /* Error occurred while printing the result.  */
Packit 6c4009
      error (0, 0, _("\
Packit 6c4009
conversion stopped due to problem in writing the output"));
Packit 6c4009
      return -1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  errno = errno_save;
Packit 6c4009
Packit 6c4009
  return 0;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
static int
Packit 6c4009
process_block (iconv_t cd, char *addr, size_t len, FILE **output,
Packit 6c4009
	       const char *output_file)
Packit 6c4009
{
Packit 6c4009
#define OUTBUF_SIZE	32768
Packit 6c4009
  const char *start = addr;
Packit 6c4009
  char outbuf[OUTBUF_SIZE];
Packit 6c4009
  char *outptr;
Packit 6c4009
  size_t outlen;
Packit 6c4009
  size_t n;
Packit 6c4009
  int ret = 0;
Packit 6c4009
Packit 6c4009
  while (len > 0)
Packit 6c4009
    {
Packit 6c4009
      outptr = outbuf;
Packit 6c4009
      outlen = OUTBUF_SIZE;
Packit 6c4009
      n = iconv (cd, &addr, &len, &outptr, &outlen);
Packit 6c4009
Packit 6c4009
      if (n == (size_t) -1 && omit_invalid && errno == EILSEQ)
Packit 6c4009
	{
Packit 6c4009
	  ret = 1;
Packit 6c4009
	  if (len == 0)
Packit 6c4009
	    n = 0;
Packit 6c4009
	  else
Packit 6c4009
	    errno = E2BIG;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      if (outptr != outbuf)
Packit 6c4009
	{
Packit 6c4009
	  ret = write_output (outbuf, outptr, output, output_file);
Packit 6c4009
	  if (ret != 0)
Packit 6c4009
	    break;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      if (n != (size_t) -1)
Packit 6c4009
	{
Packit 6c4009
	  /* All the input test is processed.  For state-dependent
Packit 6c4009
	     character sets we have to flush the state now.  */
Packit 6c4009
	  outptr = outbuf;
Packit 6c4009
	  outlen = OUTBUF_SIZE;
Packit 6c4009
	  n = iconv (cd, NULL, NULL, &outptr, &outlen);
Packit 6c4009
Packit 6c4009
	  if (outptr != outbuf)
Packit 6c4009
	    {
Packit 6c4009
	      ret = write_output (outbuf, outptr, output, output_file);
Packit 6c4009
	      if (ret != 0)
Packit 6c4009
		break;
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  if (n != (size_t) -1)
Packit 6c4009
	    break;
Packit 6c4009
Packit 6c4009
	  if (omit_invalid && errno == EILSEQ)
Packit 6c4009
	    {
Packit 6c4009
	      ret = 1;
Packit 6c4009
	      break;
Packit 6c4009
	    }
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      if (errno != E2BIG)
Packit 6c4009
	{
Packit 6c4009
	  /* iconv() ran into a problem.  */
Packit 6c4009
	  switch (errno)
Packit 6c4009
	    {
Packit 6c4009
	    case EILSEQ:
Packit 6c4009
	      if (! omit_invalid)
Packit 6c4009
		error (0, 0, _("illegal input sequence at position %ld"),
Packit 6c4009
		       (long int) (addr - start));
Packit 6c4009
	      break;
Packit 6c4009
	    case EINVAL:
Packit 6c4009
	      error (0, 0, _("\
Packit 6c4009
incomplete character or shift sequence at end of buffer"));
Packit 6c4009
	      break;
Packit 6c4009
	    case EBADF:
Packit 6c4009
	      error (0, 0, _("internal error (illegal descriptor)"));
Packit 6c4009
	      break;
Packit 6c4009
	    default:
Packit 6c4009
	      error (0, 0, _("unknown iconv() error %d"), errno);
Packit 6c4009
	      break;
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  return -1;
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  return ret;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
static int
Packit 6c4009
process_fd (iconv_t cd, int fd, FILE **output, const char *output_file)
Packit 6c4009
{
Packit 6c4009
  /* we have a problem with reading from a desriptor since we must not
Packit 6c4009
     provide the iconv() function an incomplete character or shift
Packit 6c4009
     sequence at the end of the buffer.  Since we have to deal with
Packit 6c4009
     arbitrary encodings we must read the whole text in a buffer and
Packit 6c4009
     process it in one step.  */
Packit 6c4009
  static char *inbuf = NULL;
Packit 6c4009
  static size_t maxlen = 0;
Packit 6c4009
  char *inptr = NULL;
Packit 6c4009
  size_t actlen = 0;
Packit 6c4009
Packit 6c4009
  while (actlen < maxlen)
Packit 6c4009
    {
Packit 6c4009
      ssize_t n = read (fd, inptr, maxlen - actlen);
Packit 6c4009
Packit 6c4009
      if (n == 0)
Packit 6c4009
	/* No more text to read.  */
Packit 6c4009
	break;
Packit 6c4009
Packit 6c4009
      if (n == -1)
Packit 6c4009
	{
Packit 6c4009
	  /* Error while reading.  */
Packit 6c4009
	  error (0, errno, _("error while reading the input"));
Packit 6c4009
	  return -1;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      inptr += n;
Packit 6c4009
      actlen += n;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (actlen == maxlen)
Packit 6c4009
    while (1)
Packit 6c4009
      {
Packit 6c4009
	ssize_t n;
Packit 6c4009
	char *new_inbuf;
Packit 6c4009
Packit 6c4009
	/* Increase the buffer.  */
Packit 6c4009
	new_inbuf = (char *) realloc (inbuf, maxlen + 32768);
Packit 6c4009
	if (new_inbuf == NULL)
Packit 6c4009
	  {
Packit 6c4009
	    error (0, errno, _("unable to allocate buffer for input"));
Packit 6c4009
	    return -1;
Packit 6c4009
	  }
Packit 6c4009
	inbuf = new_inbuf;
Packit 6c4009
	maxlen += 32768;
Packit 6c4009
	inptr = inbuf + actlen;
Packit 6c4009
Packit 6c4009
	do
Packit 6c4009
	  {
Packit 6c4009
	    n = read (fd, inptr, maxlen - actlen);
Packit 6c4009
Packit 6c4009
	    if (n == 0)
Packit 6c4009
	      /* No more text to read.  */
Packit 6c4009
	      break;
Packit 6c4009
Packit 6c4009
	    if (n == -1)
Packit 6c4009
	      {
Packit 6c4009
		/* Error while reading.  */
Packit 6c4009
		error (0, errno, _("error while reading the input"));
Packit 6c4009
		return -1;
Packit 6c4009
	      }
Packit 6c4009
Packit 6c4009
	    inptr += n;
Packit 6c4009
	    actlen += n;
Packit 6c4009
	  }
Packit 6c4009
	while (actlen < maxlen);
Packit 6c4009
Packit 6c4009
	if (n == 0)
Packit 6c4009
	  /* Break again so we leave both loops.  */
Packit 6c4009
	  break;
Packit 6c4009
      }
Packit 6c4009
Packit 6c4009
  /* Now we have all the input in the buffer.  Process it in one run.  */
Packit 6c4009
  return process_block (cd, inbuf, actlen, output, output_file);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
static int
Packit 6c4009
process_file (iconv_t cd, FILE *input, FILE **output, const char *output_file)
Packit 6c4009
{
Packit 6c4009
  /* This should be safe since we use this function only for `stdin' and
Packit 6c4009
     we haven't read anything so far.  */
Packit 6c4009
  return process_fd (cd, fileno (input), output, output_file);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Print all known character sets/encodings.  */
Packit 6c4009
static void *printlist;
Packit 6c4009
static size_t column;
Packit 6c4009
static int not_first;
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
insert_print_list (const void *nodep, VISIT value, int level)
Packit 6c4009
{
Packit 6c4009
  if (value == leaf || value == postorder)
Packit 6c4009
    {
Packit 6c4009
      const struct gconv_alias *s = *(const struct gconv_alias **) nodep;
Packit 6c4009
      tsearch (s->fromname, &printlist, (__compar_fn_t) strverscmp);
Packit 6c4009
    }
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
do_print_human  (const void *nodep, VISIT value, int level)
Packit 6c4009
{
Packit 6c4009
  if (value == leaf || value == postorder)
Packit 6c4009
    {
Packit 6c4009
      const char *s = *(const char **) nodep;
Packit 6c4009
      size_t len = strlen (s);
Packit 6c4009
      size_t cnt;
Packit 6c4009
Packit 6c4009
      while (len > 0 && s[len - 1] == '/')
Packit 6c4009
	--len;
Packit 6c4009
Packit 6c4009
      for (cnt = 0; cnt < len; ++cnt)
Packit 6c4009
	if (isalnum (s[cnt]))
Packit 6c4009
	  break;
Packit 6c4009
      if (cnt == len)
Packit 6c4009
	return;
Packit 6c4009
Packit 6c4009
      if (not_first)
Packit 6c4009
	{
Packit 6c4009
	  putchar (',');
Packit 6c4009
	  ++column;
Packit 6c4009
Packit 6c4009
	  if (column > 2 && column + len > 77)
Packit 6c4009
	    {
Packit 6c4009
	      fputs ("\n  ", stdout);
Packit 6c4009
	      column = 2;
Packit 6c4009
	    }
Packit 6c4009
	  else
Packit 6c4009
	    {
Packit 6c4009
	      putchar (' ');
Packit 6c4009
	      ++column;
Packit 6c4009
	    }
Packit 6c4009
	}
Packit 6c4009
      else
Packit 6c4009
	not_first = 1;
Packit 6c4009
Packit 6c4009
      fwrite (s, len, 1, stdout);
Packit 6c4009
      column += len;
Packit 6c4009
    }
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
do_print  (const void *nodep, VISIT value, int level)
Packit 6c4009
{
Packit 6c4009
  if (value == leaf || value == postorder)
Packit 6c4009
    {
Packit 6c4009
      const char *s = *(const char **) nodep;
Packit 6c4009
Packit 6c4009
      puts (s);
Packit 6c4009
    }
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
add_known_names (struct gconv_module *node)
Packit 6c4009
{
Packit 6c4009
  if (node->left != NULL)
Packit 6c4009
    add_known_names (node->left);
Packit 6c4009
  if (node->right != NULL)
Packit 6c4009
    add_known_names (node->right);
Packit 6c4009
  do
Packit 6c4009
    {
Packit 6c4009
      if (strcmp (node->from_string, "INTERNAL") != 0)
Packit 6c4009
	tsearch (node->from_string, &printlist, (__compar_fn_t) strverscmp);
Packit 6c4009
      if (strcmp (node->to_string, "INTERNAL") != 0)
Packit 6c4009
	tsearch (node->to_string, &printlist, (__compar_fn_t) strverscmp);
Packit 6c4009
Packit 6c4009
      node = node->same;
Packit 6c4009
    }
Packit 6c4009
  while (node != NULL);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
insert_cache (void)
Packit 6c4009
{
Packit 6c4009
  const struct gconvcache_header *header;
Packit 6c4009
  const char *strtab;
Packit 6c4009
  const struct hash_entry *hashtab;
Packit 6c4009
  size_t cnt;
Packit 6c4009
Packit 6c4009
  header = (const struct gconvcache_header *) __gconv_get_cache ();
Packit 6c4009
  strtab = (char *) header + header->string_offset;
Packit 6c4009
  hashtab = (struct hash_entry *) ((char *) header + header->hash_offset);
Packit 6c4009
Packit 6c4009
  for (cnt = 0; cnt < header->hash_size; ++cnt)
Packit 6c4009
    if (hashtab[cnt].string_offset != 0)
Packit 6c4009
      {
Packit 6c4009
	const char *str = strtab + hashtab[cnt].string_offset;
Packit 6c4009
Packit 6c4009
	if (strcmp (str, "INTERNAL") != 0)
Packit 6c4009
	  tsearch (str, &printlist, (__compar_fn_t) strverscmp);
Packit 6c4009
      }
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
print_known_names (void)
Packit 6c4009
{
Packit 6c4009
  iconv_t h;
Packit 6c4009
  void *cache;
Packit 6c4009
Packit 6c4009
  /* We must initialize the internal databases first.  */
Packit 6c4009
  h = iconv_open ("L1", "L1");
Packit 6c4009
  iconv_close (h);
Packit 6c4009
Packit 6c4009
  /* See whether we have a cache.  */
Packit 6c4009
  cache = __gconv_get_cache ();
Packit 6c4009
  if (cache != NULL)
Packit 6c4009
    /* Yep, use only this information.  */
Packit 6c4009
    insert_cache ();
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
      struct gconv_module *modules;
Packit 6c4009
Packit 6c4009
      /* No, then use the information read from the gconv-modules file.
Packit 6c4009
	 First add the aliases.  */
Packit 6c4009
      twalk (__gconv_get_alias_db (), insert_print_list);
Packit 6c4009
Packit 6c4009
      /* Add the from- and to-names from the known modules.  */
Packit 6c4009
      modules = __gconv_get_modules_db ();
Packit 6c4009
      if (modules != NULL)
Packit 6c4009
	add_known_names (modules);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  bool human_readable = isatty (fileno (stdout));
Packit 6c4009
Packit 6c4009
  if (human_readable)
Packit 6c4009
    fputs (_("\
Packit 6c4009
The following list contains all the coded character sets known.  This does\n\
Packit 6c4009
not necessarily mean that all combinations of these names can be used for\n\
Packit 6c4009
the FROM and TO command line parameters.  One coded character set can be\n\
Packit 6c4009
listed with several different names (aliases).\n\n  "), stdout);
Packit 6c4009
Packit 6c4009
  /* Now print the collected names.  */
Packit 6c4009
  column = 2;
Packit 6c4009
  twalk (printlist, human_readable ? do_print_human : do_print);
Packit 6c4009
Packit 6c4009
  if (human_readable && column != 0)
Packit 6c4009
    puts ("");
Packit 6c4009
}