Blame iconv/iconv_prog.c

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