hjl / source-git / glibc

Forked from source-git/glibc 3 years ago
Clone

Blame iconv/skeleton.c

Packit 6c4009
/* Skeleton for a conversion module.
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
   The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public
Packit 6c4009
   License as published by the Free Software Foundation; either
Packit 6c4009
   version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   The GNU C Library 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 GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library; if not, see
Packit 6c4009
   <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
/* This file can be included to provide definitions of several things
Packit 6c4009
   many modules have in common.  It can be customized using the following
Packit 6c4009
   macros:
Packit 6c4009
Packit 6c4009
     DEFINE_INIT	define the default initializer.  This requires the
Packit 6c4009
			following symbol to be defined.
Packit 6c4009
Packit 6c4009
     CHARSET_NAME	string with official name of the coded character
Packit 6c4009
			set (in all-caps)
Packit 6c4009
Packit 6c4009
     DEFINE_FINI	define the default destructor function.
Packit 6c4009
Packit 6c4009
     MIN_NEEDED_FROM	minimal number of bytes needed for the from-charset.
Packit 6c4009
     MIN_NEEDED_TO	likewise for the to-charset.
Packit 6c4009
Packit 6c4009
     MAX_NEEDED_FROM	maximal number of bytes needed for the from-charset.
Packit 6c4009
			This macro is optional, it defaults to MIN_NEEDED_FROM.
Packit 6c4009
     MAX_NEEDED_TO	likewise for the to-charset.
Packit 6c4009
Packit 6c4009
     FROM_LOOP_MIN_NEEDED_FROM
Packit 6c4009
     FROM_LOOP_MAX_NEEDED_FROM
Packit 6c4009
			minimal/maximal number of bytes needed on input
Packit 6c4009
			of one round through the FROM_LOOP.  Defaults
Packit 6c4009
			to MIN_NEEDED_FROM and MAX_NEEDED_FROM, respectively.
Packit 6c4009
     FROM_LOOP_MIN_NEEDED_TO
Packit 6c4009
     FROM_LOOP_MAX_NEEDED_TO
Packit 6c4009
			minimal/maximal number of bytes needed on output
Packit 6c4009
			of one round through the FROM_LOOP.  Defaults
Packit 6c4009
			to MIN_NEEDED_TO and MAX_NEEDED_TO, respectively.
Packit 6c4009
     TO_LOOP_MIN_NEEDED_FROM
Packit 6c4009
     TO_LOOP_MAX_NEEDED_FROM
Packit 6c4009
			minimal/maximal number of bytes needed on input
Packit 6c4009
			of one round through the TO_LOOP.  Defaults
Packit 6c4009
			to MIN_NEEDED_TO and MAX_NEEDED_TO, respectively.
Packit 6c4009
     TO_LOOP_MIN_NEEDED_TO
Packit 6c4009
     TO_LOOP_MAX_NEEDED_TO
Packit 6c4009
			minimal/maximal number of bytes needed on output
Packit 6c4009
			of one round through the TO_LOOP.  Defaults
Packit 6c4009
			to MIN_NEEDED_FROM and MAX_NEEDED_FROM, respectively.
Packit 6c4009
Packit 6c4009
     FROM_DIRECTION	this macro is supposed to return a value != 0
Packit 6c4009
			if we convert from the current character set,
Packit 6c4009
			otherwise it return 0.
Packit 6c4009
Packit 6c4009
     EMIT_SHIFT_TO_INIT	this symbol is optional.  If it is defined it
Packit 6c4009
			defines some code which writes out a sequence
Packit 6c4009
			of bytes which bring the current state into
Packit 6c4009
			the initial state.
Packit 6c4009
Packit 6c4009
     FROM_LOOP		name of the function implementing the conversion
Packit 6c4009
			from the current character set.
Packit 6c4009
     TO_LOOP		likewise for the other direction
Packit 6c4009
Packit 6c4009
     ONE_DIRECTION	optional.  If defined to 1, only one conversion
Packit 6c4009
			direction is defined instead of two.  In this
Packit 6c4009
			case, FROM_DIRECTION should be defined to 1, and
Packit 6c4009
			FROM_LOOP and TO_LOOP should have the same value.
Packit 6c4009
Packit 6c4009
     SAVE_RESET_STATE	in case of an error we must reset the state for
Packit 6c4009
			the rerun so this macro must be defined for
Packit 6c4009
			stateful encodings.  It takes an argument which
Packit 6c4009
			is nonzero when saving.
Packit 6c4009
Packit 6c4009
     RESET_INPUT_BUFFER	If the input character sets allow this the macro
Packit 6c4009
			can be defined to reset the input buffer pointers
Packit 6c4009
			to cover only those characters up to the error.
Packit 6c4009
Packit 6c4009
     FUNCTION_NAME	if not set the conversion function is named `gconv'.
Packit 6c4009
Packit 6c4009
     PREPARE_LOOP	optional code preparing the conversion loop.  Can
Packit 6c4009
			contain variable definitions.
Packit 6c4009
     END_LOOP		also optional, may be used to store information
Packit 6c4009
Packit 6c4009
     EXTRA_LOOP_ARGS	optional macro specifying extra arguments passed
Packit 6c4009
			to loop function.
Packit 6c4009
Packit 6c4009
     STORE_REST		optional, needed only when MAX_NEEDED_FROM > 4.
Packit 6c4009
			This macro stores the seen but unconverted input bytes
Packit 6c4009
			in the state.
Packit 6c4009
Packit 6c4009
     FROM_ONEBYTE	optional.  If defined, should be the name of a
Packit 6c4009
			specialized conversion function for a single byte
Packit 6c4009
			from the current character set to INTERNAL.  This
Packit 6c4009
			function has prototype
Packit 6c4009
			   wint_t
Packit 6c4009
			   FROM_ONEBYTE (struct __gconv_step *, unsigned char);
Packit 6c4009
			and does a special conversion:
Packit 6c4009
			- The input is a single byte.
Packit 6c4009
			- The output is a single uint32_t.
Packit 6c4009
			- The state before the conversion is the initial state;
Packit 6c4009
			  the state after the conversion is irrelevant.
Packit 6c4009
			- No transliteration.
Packit 6c4009
			- __invocation_counter = 0.
Packit 6c4009
			- __internal_use = 1.
Packit 6c4009
			- do_flush = 0.
Packit 6c4009
Packit 6c4009
   Modules can use mbstate_t to store conversion state as follows:
Packit 6c4009
Packit 6c4009
   * Bits 2..0 of '__count' contain the number of lookahead input bytes
Packit 6c4009
     stored in __value.__wchb.  Always zero if the converter never
Packit 6c4009
     returns __GCONV_INCOMPLETE_INPUT.
Packit 6c4009
Packit 6c4009
   * Bits 31..3 of '__count' are module dependent shift state.
Packit 6c4009
Packit 6c4009
   * __value: When STORE_REST/UNPACK_BYTES aren't defined and when the
Packit 6c4009
     converter has returned __GCONV_INCOMPLETE_INPUT, this contains
Packit 6c4009
     at most 4 lookahead bytes. Converters with an mb_cur_max > 4
Packit 6c4009
     (currently only UTF-8) must find a way to store their state
Packit 6c4009
     in __value.__wch and define STORE_REST/UNPACK_BYTES appropriately.
Packit 6c4009
Packit 6c4009
   When __value contains lookahead, __count must not be zero, because
Packit 6c4009
   the converter is not in the initial state then, and mbsinit() --
Packit 6c4009
   defined as a (__count == 0) test -- must reflect this.
Packit 6c4009
 */
Packit 6c4009
Packit 6c4009
#include <assert.h>
Packit 6c4009
#include <gconv.h>
Packit 6c4009
#include <string.h>
Packit 6c4009
#define __need_size_t
Packit 6c4009
#define __need_NULL
Packit 6c4009
#include <stddef.h>
Packit 6c4009
Packit 6c4009
#ifndef STATIC_GCONV
Packit 6c4009
# include <dlfcn.h>
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#include <sysdep.h>
Packit 6c4009
#include <stdint.h>
Packit 6c4009
Packit 6c4009
#ifndef DL_CALL_FCT
Packit 6c4009
# define DL_CALL_FCT(fct, args) fct args
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
/* The direction objects.  */
Packit 6c4009
#if DEFINE_INIT
Packit 6c4009
# ifndef FROM_DIRECTION
Packit 6c4009
#  define FROM_DIRECTION_VAL NULL
Packit 6c4009
#  define TO_DIRECTION_VAL ((void *) ~((uintptr_t) 0))
Packit 6c4009
#  define FROM_DIRECTION (step->__data == FROM_DIRECTION_VAL)
Packit 6c4009
# endif
Packit 6c4009
#else
Packit 6c4009
# ifndef FROM_DIRECTION
Packit 6c4009
#  error "FROM_DIRECTION must be provided if non-default init is used"
Packit 6c4009
# endif
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
/* How many bytes are needed at most for the from-charset.  */
Packit 6c4009
#ifndef MAX_NEEDED_FROM
Packit 6c4009
# define MAX_NEEDED_FROM	MIN_NEEDED_FROM
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
/* Same for the to-charset.  */
Packit 6c4009
#ifndef MAX_NEEDED_TO
Packit 6c4009
# define MAX_NEEDED_TO		MIN_NEEDED_TO
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
/* Defaults for the per-direction min/max constants.  */
Packit 6c4009
#ifndef FROM_LOOP_MIN_NEEDED_FROM
Packit 6c4009
# define FROM_LOOP_MIN_NEEDED_FROM	MIN_NEEDED_FROM
Packit 6c4009
#endif
Packit 6c4009
#ifndef FROM_LOOP_MAX_NEEDED_FROM
Packit 6c4009
# define FROM_LOOP_MAX_NEEDED_FROM	MAX_NEEDED_FROM
Packit 6c4009
#endif
Packit 6c4009
#ifndef FROM_LOOP_MIN_NEEDED_TO
Packit 6c4009
# define FROM_LOOP_MIN_NEEDED_TO	MIN_NEEDED_TO
Packit 6c4009
#endif
Packit 6c4009
#ifndef FROM_LOOP_MAX_NEEDED_TO
Packit 6c4009
# define FROM_LOOP_MAX_NEEDED_TO	MAX_NEEDED_TO
Packit 6c4009
#endif
Packit 6c4009
#ifndef TO_LOOP_MIN_NEEDED_FROM
Packit 6c4009
# define TO_LOOP_MIN_NEEDED_FROM	MIN_NEEDED_TO
Packit 6c4009
#endif
Packit 6c4009
#ifndef TO_LOOP_MAX_NEEDED_FROM
Packit 6c4009
# define TO_LOOP_MAX_NEEDED_FROM	MAX_NEEDED_TO
Packit 6c4009
#endif
Packit 6c4009
#ifndef TO_LOOP_MIN_NEEDED_TO
Packit 6c4009
# define TO_LOOP_MIN_NEEDED_TO		MIN_NEEDED_FROM
Packit 6c4009
#endif
Packit 6c4009
#ifndef TO_LOOP_MAX_NEEDED_TO
Packit 6c4009
# define TO_LOOP_MAX_NEEDED_TO		MAX_NEEDED_FROM
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Define macros which can access unaligned buffers.  These macros are
Packit 6c4009
   supposed to be used only in code outside the inner loops.  For the inner
Packit 6c4009
   loops we have other definitions which allow optimized access.  */
Packit 6c4009
#if _STRING_ARCH_unaligned
Packit 6c4009
/* We can handle unaligned memory access.  */
Packit 6c4009
# define get16u(addr) *((const uint16_t *) (addr))
Packit 6c4009
# define get32u(addr) *((const uint32_t *) (addr))
Packit 6c4009
Packit 6c4009
/* We need no special support for writing values either.  */
Packit 6c4009
# define put16u(addr, val) *((uint16_t *) (addr)) = (val)
Packit 6c4009
# define put32u(addr, val) *((uint32_t *) (addr)) = (val)
Packit 6c4009
#else
Packit 6c4009
/* Distinguish between big endian and little endian.  */
Packit 6c4009
# if __BYTE_ORDER == __LITTLE_ENDIAN
Packit 6c4009
#  define get16u(addr) \
Packit 6c4009
     (((const unsigned char *) (addr))[1] << 8				      \
Packit 6c4009
      | ((const unsigned char *) (addr))[0])
Packit 6c4009
#  define get32u(addr) \
Packit 6c4009
     (((((const unsigned char *) (addr))[3] << 8			      \
Packit 6c4009
	| ((const unsigned char *) (addr))[2]) << 8			      \
Packit 6c4009
       | ((const unsigned char *) (addr))[1]) << 8			      \
Packit 6c4009
      | ((const unsigned char *) (addr))[0])
Packit 6c4009
Packit 6c4009
#  define put16u(addr, val) \
Packit 6c4009
     ({ uint16_t __val = (val);						      \
Packit 6c4009
	((unsigned char *) (addr))[0] = __val;				      \
Packit 6c4009
	((unsigned char *) (addr))[1] = __val >> 8;			      \
Packit 6c4009
	(void) 0; })
Packit 6c4009
#  define put32u(addr, val) \
Packit 6c4009
     ({ uint32_t __val = (val);						      \
Packit 6c4009
	((unsigned char *) (addr))[0] = __val;				      \
Packit 6c4009
	__val >>= 8;							      \
Packit 6c4009
	((unsigned char *) (addr))[1] = __val;				      \
Packit 6c4009
	__val >>= 8;							      \
Packit 6c4009
	((unsigned char *) (addr))[2] = __val;				      \
Packit 6c4009
	__val >>= 8;							      \
Packit 6c4009
	((unsigned char *) (addr))[3] = __val;				      \
Packit 6c4009
	(void) 0; })
Packit 6c4009
# else
Packit 6c4009
#  define get16u(addr) \
Packit 6c4009
     (((const unsigned char *) (addr))[0] << 8				      \
Packit 6c4009
      | ((const unsigned char *) (addr))[1])
Packit 6c4009
#  define get32u(addr) \
Packit 6c4009
     (((((const unsigned char *) (addr))[0] << 8			      \
Packit 6c4009
	| ((const unsigned char *) (addr))[1]) << 8			      \
Packit 6c4009
       | ((const unsigned char *) (addr))[2]) << 8			      \
Packit 6c4009
      | ((const unsigned char *) (addr))[3])
Packit 6c4009
Packit 6c4009
#  define put16u(addr, val) \
Packit 6c4009
     ({ uint16_t __val = (val);						      \
Packit 6c4009
	((unsigned char *) (addr))[1] = __val;				      \
Packit 6c4009
	((unsigned char *) (addr))[0] = __val >> 8;			      \
Packit 6c4009
	(void) 0; })
Packit 6c4009
#  define put32u(addr, val) \
Packit 6c4009
     ({ uint32_t __val = (val);						      \
Packit 6c4009
	((unsigned char *) (addr))[3] = __val;				      \
Packit 6c4009
	__val >>= 8;							      \
Packit 6c4009
	((unsigned char *) (addr))[2] = __val;				      \
Packit 6c4009
	__val >>= 8;							      \
Packit 6c4009
	((unsigned char *) (addr))[1] = __val;				      \
Packit 6c4009
	__val >>= 8;							      \
Packit 6c4009
	((unsigned char *) (addr))[0] = __val;				      \
Packit 6c4009
	(void) 0; })
Packit 6c4009
# endif
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* For conversions from a fixed width character set to another fixed width
Packit 6c4009
   character set we can define RESET_INPUT_BUFFER in a very fast way.  */
Packit 6c4009
#if !defined RESET_INPUT_BUFFER && !defined SAVE_RESET_STATE
Packit 6c4009
# if FROM_LOOP_MIN_NEEDED_FROM == FROM_LOOP_MAX_NEEDED_FROM \
Packit 6c4009
     && FROM_LOOP_MIN_NEEDED_TO == FROM_LOOP_MAX_NEEDED_TO \
Packit 6c4009
     && TO_LOOP_MIN_NEEDED_FROM == TO_LOOP_MAX_NEEDED_FROM \
Packit 6c4009
     && TO_LOOP_MIN_NEEDED_TO == TO_LOOP_MAX_NEEDED_TO
Packit 6c4009
/* We have to use these `if's here since the compiler cannot know that
Packit 6c4009
   (outbuf - outerr) is always divisible by FROM/TO_LOOP_MIN_NEEDED_TO.
Packit 6c4009
   The ?:1 avoids division by zero warnings that gcc 3.2 emits even for
Packit 6c4009
   obviously unreachable code.  */
Packit 6c4009
#  define RESET_INPUT_BUFFER \
Packit 6c4009
  if (FROM_DIRECTION)							      \
Packit 6c4009
    {									      \
Packit 6c4009
      if (FROM_LOOP_MIN_NEEDED_FROM % FROM_LOOP_MIN_NEEDED_TO == 0)	      \
Packit 6c4009
	*inptrp -= (outbuf - outerr)					      \
Packit 6c4009
		   * (FROM_LOOP_MIN_NEEDED_FROM / FROM_LOOP_MIN_NEEDED_TO);   \
Packit 6c4009
      else if (FROM_LOOP_MIN_NEEDED_TO % FROM_LOOP_MIN_NEEDED_FROM == 0)      \
Packit 6c4009
	*inptrp -= (outbuf - outerr)					      \
Packit 6c4009
		   / (FROM_LOOP_MIN_NEEDED_TO / FROM_LOOP_MIN_NEEDED_FROM     \
Packit 6c4009
		      ? : 1);						      \
Packit 6c4009
      else								      \
Packit 6c4009
	*inptrp -= ((outbuf - outerr) / FROM_LOOP_MIN_NEEDED_TO)	      \
Packit 6c4009
		   * FROM_LOOP_MIN_NEEDED_FROM;				      \
Packit 6c4009
    }									      \
Packit 6c4009
  else									      \
Packit 6c4009
    {									      \
Packit 6c4009
      if (TO_LOOP_MIN_NEEDED_FROM % TO_LOOP_MIN_NEEDED_TO == 0)		      \
Packit 6c4009
	*inptrp -= (outbuf - outerr)					      \
Packit 6c4009
		   * (TO_LOOP_MIN_NEEDED_FROM / TO_LOOP_MIN_NEEDED_TO);	      \
Packit 6c4009
      else if (TO_LOOP_MIN_NEEDED_TO % TO_LOOP_MIN_NEEDED_FROM == 0)	      \
Packit 6c4009
	*inptrp -= (outbuf - outerr)					      \
Packit 6c4009
		   / (TO_LOOP_MIN_NEEDED_TO / TO_LOOP_MIN_NEEDED_FROM ? : 1); \
Packit 6c4009
      else								      \
Packit 6c4009
	*inptrp -= ((outbuf - outerr) / TO_LOOP_MIN_NEEDED_TO)		      \
Packit 6c4009
		   * TO_LOOP_MIN_NEEDED_FROM;				      \
Packit 6c4009
    }
Packit 6c4009
# endif
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* The default init function.  It simply matches the name and initializes
Packit 6c4009
   the step data to point to one of the objects above.  */
Packit 6c4009
#if DEFINE_INIT
Packit 6c4009
# ifndef CHARSET_NAME
Packit 6c4009
#  error "CHARSET_NAME not defined"
Packit 6c4009
# endif
Packit 6c4009
Packit 6c4009
extern int gconv_init (struct __gconv_step *step);
Packit 6c4009
int
Packit 6c4009
gconv_init (struct __gconv_step *step)
Packit 6c4009
{
Packit 6c4009
  /* Determine which direction.  */
Packit 6c4009
  if (strcmp (step->__from_name, CHARSET_NAME) == 0)
Packit 6c4009
    {
Packit 6c4009
      step->__data = FROM_DIRECTION_VAL;
Packit 6c4009
Packit 6c4009
      step->__min_needed_from = FROM_LOOP_MIN_NEEDED_FROM;
Packit 6c4009
      step->__max_needed_from = FROM_LOOP_MAX_NEEDED_FROM;
Packit 6c4009
      step->__min_needed_to = FROM_LOOP_MIN_NEEDED_TO;
Packit 6c4009
      step->__max_needed_to = FROM_LOOP_MAX_NEEDED_TO;
Packit 6c4009
Packit 6c4009
#ifdef FROM_ONEBYTE
Packit 6c4009
      step->__btowc_fct = FROM_ONEBYTE;
Packit 6c4009
#endif
Packit 6c4009
    }
Packit 6c4009
  else if (__builtin_expect (strcmp (step->__to_name, CHARSET_NAME), 0) == 0)
Packit 6c4009
    {
Packit 6c4009
      step->__data = TO_DIRECTION_VAL;
Packit 6c4009
Packit 6c4009
      step->__min_needed_from = TO_LOOP_MIN_NEEDED_FROM;
Packit 6c4009
      step->__max_needed_from = TO_LOOP_MAX_NEEDED_FROM;
Packit 6c4009
      step->__min_needed_to = TO_LOOP_MIN_NEEDED_TO;
Packit 6c4009
      step->__max_needed_to = TO_LOOP_MAX_NEEDED_TO;
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    return __GCONV_NOCONV;
Packit 6c4009
Packit 6c4009
#ifdef SAVE_RESET_STATE
Packit 6c4009
  step->__stateful = 1;
Packit 6c4009
#else
Packit 6c4009
  step->__stateful = 0;
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
  return __GCONV_OK;
Packit 6c4009
}
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* The default destructor function does nothing in the moment and so
Packit 6c4009
   we don't define it at all.  But we still provide the macro just in
Packit 6c4009
   case we need it some day.  */
Packit 6c4009
#if DEFINE_FINI
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* If no arguments have to passed to the loop function define the macro
Packit 6c4009
   as empty.  */
Packit 6c4009
#ifndef EXTRA_LOOP_ARGS
Packit 6c4009
# define EXTRA_LOOP_ARGS
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* This is the actual conversion function.  */
Packit 6c4009
#ifndef FUNCTION_NAME
Packit 6c4009
# define FUNCTION_NAME	gconv
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
/* The macros are used to access the function to convert single characters.  */
Packit 6c4009
#define SINGLE(fct) SINGLE2 (fct)
Packit 6c4009
#define SINGLE2(fct) fct##_single
Packit 6c4009
Packit 6c4009
Packit 6c4009
extern int FUNCTION_NAME (struct __gconv_step *step,
Packit 6c4009
			  struct __gconv_step_data *data,
Packit 6c4009
			  const unsigned char **inptrp,
Packit 6c4009
			  const unsigned char *inend,
Packit 6c4009
			  unsigned char **outbufstart, size_t *irreversible,
Packit 6c4009
			  int do_flush, int consume_incomplete);
Packit 6c4009
int
Packit 6c4009
FUNCTION_NAME (struct __gconv_step *step, struct __gconv_step_data *data,
Packit 6c4009
	       const unsigned char **inptrp, const unsigned char *inend,
Packit 6c4009
	       unsigned char **outbufstart, size_t *irreversible, int do_flush,
Packit 6c4009
	       int consume_incomplete)
Packit 6c4009
{
Packit 6c4009
  struct __gconv_step *next_step = step + 1;
Packit 6c4009
  struct __gconv_step_data *next_data = data + 1;
Packit 6c4009
  __gconv_fct fct = NULL;
Packit 6c4009
  int status;
Packit 6c4009
Packit 6c4009
  if ((data->__flags & __GCONV_IS_LAST) == 0)
Packit 6c4009
    {
Packit 6c4009
      fct = next_step->__fct;
Packit 6c4009
#ifdef PTR_DEMANGLE
Packit 6c4009
      if (next_step->__shlib_handle != NULL)
Packit 6c4009
	PTR_DEMANGLE (fct);
Packit 6c4009
#endif
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* If the function is called with no input this means we have to reset
Packit 6c4009
     to the initial state.  The possibly partly converted input is
Packit 6c4009
     dropped.  */
Packit 6c4009
  if (__glibc_unlikely (do_flush))
Packit 6c4009
    {
Packit 6c4009
      /* This should never happen during error handling.  */
Packit 6c4009
      assert (outbufstart == NULL);
Packit 6c4009
Packit 6c4009
      status = __GCONV_OK;
Packit 6c4009
Packit 6c4009
#ifdef EMIT_SHIFT_TO_INIT
Packit 6c4009
      if (do_flush == 1)
Packit 6c4009
	{
Packit 6c4009
	  /* We preserve the initial values of the pointer variables.  */
Packit 6c4009
	  unsigned char *outbuf = data->__outbuf;
Packit 6c4009
	  unsigned char *outstart = outbuf;
Packit 6c4009
	  unsigned char *outend = data->__outbufend;
Packit 6c4009
Packit 6c4009
# ifdef PREPARE_LOOP
Packit 6c4009
	  PREPARE_LOOP
Packit 6c4009
# endif
Packit 6c4009
Packit 6c4009
# ifdef SAVE_RESET_STATE
Packit 6c4009
	  SAVE_RESET_STATE (1);
Packit 6c4009
# endif
Packit 6c4009
Packit 6c4009
	  /* Emit the escape sequence to reset the state.  */
Packit 6c4009
	  EMIT_SHIFT_TO_INIT;
Packit 6c4009
Packit 6c4009
	  /* Call the steps down the chain if there are any but only if we
Packit 6c4009
	     successfully emitted the escape sequence.  This should only
Packit 6c4009
	     fail if the output buffer is full.  If the input is invalid
Packit 6c4009
	     it should be discarded since the user wants to start from a
Packit 6c4009
	     clean state.  */
Packit 6c4009
	  if (status == __GCONV_OK)
Packit 6c4009
	    {
Packit 6c4009
	      if (data->__flags & __GCONV_IS_LAST)
Packit 6c4009
		/* Store information about how many bytes are available.  */
Packit 6c4009
		data->__outbuf = outbuf;
Packit 6c4009
	      else
Packit 6c4009
		{
Packit 6c4009
		  /* Write out all output which was produced.  */
Packit 6c4009
		  if (outbuf > outstart)
Packit 6c4009
		    {
Packit 6c4009
		      const unsigned char *outerr = outstart;
Packit 6c4009
		      int result;
Packit 6c4009
Packit 6c4009
		      result = DL_CALL_FCT (fct, (next_step, next_data,
Packit 6c4009
						  &outerr, outbuf, NULL,
Packit 6c4009
						  irreversible, 0,
Packit 6c4009
						  consume_incomplete));
Packit 6c4009
Packit 6c4009
		      if (result != __GCONV_EMPTY_INPUT)
Packit 6c4009
			{
Packit 6c4009
			  if (__glibc_unlikely (outerr != outbuf))
Packit 6c4009
			    {
Packit 6c4009
			      /* We have a problem.  Undo the conversion.  */
Packit 6c4009
			      outbuf = outstart;
Packit 6c4009
Packit 6c4009
			      /* Restore the state.  */
Packit 6c4009
# ifdef SAVE_RESET_STATE
Packit 6c4009
			      SAVE_RESET_STATE (0);
Packit 6c4009
# endif
Packit 6c4009
			    }
Packit 6c4009
Packit 6c4009
			  /* Change the status.  */
Packit 6c4009
			  status = result;
Packit 6c4009
			}
Packit 6c4009
		    }
Packit 6c4009
Packit 6c4009
		  if (status == __GCONV_OK)
Packit 6c4009
		    /* Now flush the remaining steps.  */
Packit 6c4009
		    status = DL_CALL_FCT (fct, (next_step, next_data, NULL,
Packit 6c4009
						NULL, NULL, irreversible, 1,
Packit 6c4009
						consume_incomplete));
Packit 6c4009
		}
Packit 6c4009
	    }
Packit 6c4009
	}
Packit 6c4009
      else
Packit 6c4009
#endif
Packit 6c4009
	{
Packit 6c4009
	  /* Clear the state object.  There might be bytes in there from
Packit 6c4009
	     previous calls with CONSUME_INCOMPLETE == 1.  But don't emit
Packit 6c4009
	     escape sequences.  */
Packit 6c4009
	  memset (data->__statep, '\0', sizeof (*data->__statep));
Packit 6c4009
Packit 6c4009
	  if (! (data->__flags & __GCONV_IS_LAST))
Packit 6c4009
	    /* Now flush the remaining steps.  */
Packit 6c4009
	    status = DL_CALL_FCT (fct, (next_step, next_data, NULL, NULL,
Packit 6c4009
					NULL, irreversible, do_flush,
Packit 6c4009
					consume_incomplete));
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
      /* We preserve the initial values of the pointer variables,
Packit 6c4009
	 but only some conversion modules need it.  */
Packit 6c4009
      const unsigned char *inptr __attribute__ ((__unused__)) = *inptrp;
Packit 6c4009
      unsigned char *outbuf = (__builtin_expect (outbufstart == NULL, 1)
Packit 6c4009
			       ? data->__outbuf : *outbufstart);
Packit 6c4009
      unsigned char *outend = data->__outbufend;
Packit 6c4009
      unsigned char *outstart;
Packit 6c4009
      /* This variable is used to count the number of characters we
Packit 6c4009
	 actually converted.  */
Packit 6c4009
      size_t lirreversible = 0;
Packit 6c4009
      size_t *lirreversiblep = irreversible ? &lirreversible : NULL;
Packit 6c4009
Packit 6c4009
      /* The following assumes that encodings, which have a variable length
Packit 6c4009
	 what might unalign a buffer even though it is an aligned in the
Packit 6c4009
	 beginning, either don't have the minimal number of bytes as a divisor
Packit 6c4009
	 of the maximum length or have a minimum length of 1.  This is true
Packit 6c4009
	 for all known and supported encodings.
Packit 6c4009
	 We use && instead of || to combine the subexpression for the FROM
Packit 6c4009
	 encoding and for the TO encoding, because usually one of them is
Packit 6c4009
	 INTERNAL, for which the subexpression evaluates to 1, but INTERNAL
Packit 6c4009
	 buffers are always aligned correctly.  */
Packit 6c4009
#define POSSIBLY_UNALIGNED \
Packit 6c4009
  (!_STRING_ARCH_unaligned					              \
Packit 6c4009
   && (((FROM_LOOP_MIN_NEEDED_FROM != 1					      \
Packit 6c4009
	 && FROM_LOOP_MAX_NEEDED_FROM % FROM_LOOP_MIN_NEEDED_FROM == 0)	      \
Packit 6c4009
	&& (FROM_LOOP_MIN_NEEDED_TO != 1				      \
Packit 6c4009
	    && FROM_LOOP_MAX_NEEDED_TO % FROM_LOOP_MIN_NEEDED_TO == 0))	      \
Packit 6c4009
       || ((TO_LOOP_MIN_NEEDED_FROM != 1				      \
Packit 6c4009
	    && TO_LOOP_MAX_NEEDED_FROM % TO_LOOP_MIN_NEEDED_FROM == 0)	      \
Packit 6c4009
	   && (TO_LOOP_MIN_NEEDED_TO != 1				      \
Packit 6c4009
	       && TO_LOOP_MAX_NEEDED_TO % TO_LOOP_MIN_NEEDED_TO == 0))))
Packit 6c4009
#if POSSIBLY_UNALIGNED
Packit 6c4009
      int unaligned;
Packit 6c4009
# define GEN_unaligned(name) GEN_unaligned2 (name)
Packit 6c4009
# define GEN_unaligned2(name) name##_unaligned
Packit 6c4009
#else
Packit 6c4009
# define unaligned 0
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#ifdef PREPARE_LOOP
Packit 6c4009
      PREPARE_LOOP
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#if FROM_LOOP_MAX_NEEDED_FROM > 1 || TO_LOOP_MAX_NEEDED_FROM > 1
Packit 6c4009
      /* If the function is used to implement the mb*towc*() or wc*tomb*()
Packit 6c4009
	 functions we must test whether any bytes from the last call are
Packit 6c4009
	 stored in the `state' object.  */
Packit 6c4009
      if (((FROM_LOOP_MAX_NEEDED_FROM > 1 && TO_LOOP_MAX_NEEDED_FROM > 1)
Packit 6c4009
	   || (FROM_LOOP_MAX_NEEDED_FROM > 1 && FROM_DIRECTION)
Packit 6c4009
	   || (TO_LOOP_MAX_NEEDED_FROM > 1 && !FROM_DIRECTION))
Packit 6c4009
	  && consume_incomplete && (data->__statep->__count & 7) != 0)
Packit 6c4009
	{
Packit 6c4009
	  /* Yep, we have some bytes left over.  Process them now.
Packit 6c4009
	     But this must not happen while we are called from an
Packit 6c4009
	     error handler.  */
Packit 6c4009
	  assert (outbufstart == NULL);
Packit 6c4009
Packit 6c4009
# if FROM_LOOP_MAX_NEEDED_FROM > 1
Packit 6c4009
	  if (TO_LOOP_MAX_NEEDED_FROM == 1 || FROM_DIRECTION)
Packit 6c4009
	    status = SINGLE(FROM_LOOP) (step, data, inptrp, inend, &outbuf,
Packit 6c4009
					outend, lirreversiblep
Packit 6c4009
					EXTRA_LOOP_ARGS);
Packit 6c4009
# endif
Packit 6c4009
# if !ONE_DIRECTION
Packit 6c4009
#  if FROM_LOOP_MAX_NEEDED_FROM > 1 && TO_LOOP_MAX_NEEDED_FROM > 1
Packit 6c4009
	  else
Packit 6c4009
#  endif
Packit 6c4009
#  if TO_LOOP_MAX_NEEDED_FROM > 1
Packit 6c4009
	    status = SINGLE(TO_LOOP) (step, data, inptrp, inend, &outbuf,
Packit 6c4009
				      outend, lirreversiblep EXTRA_LOOP_ARGS);
Packit 6c4009
#  endif
Packit 6c4009
# endif
Packit 6c4009
Packit 6c4009
	  if (__builtin_expect (status, __GCONV_OK) != __GCONV_OK)
Packit 6c4009
	    return status;
Packit 6c4009
	}
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#if POSSIBLY_UNALIGNED
Packit 6c4009
      unaligned =
Packit 6c4009
	((FROM_DIRECTION
Packit 6c4009
	  && ((uintptr_t) inptr % FROM_LOOP_MIN_NEEDED_FROM != 0
Packit 6c4009
	      || ((data->__flags & __GCONV_IS_LAST)
Packit 6c4009
		  && (uintptr_t) outbuf % FROM_LOOP_MIN_NEEDED_TO != 0)))
Packit 6c4009
	 || (!FROM_DIRECTION
Packit 6c4009
	     && (((data->__flags & __GCONV_IS_LAST)
Packit 6c4009
		  && (uintptr_t) outbuf % TO_LOOP_MIN_NEEDED_TO != 0)
Packit 6c4009
		 || (uintptr_t) inptr % TO_LOOP_MIN_NEEDED_FROM != 0)));
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
      while (1)
Packit 6c4009
	{
Packit 6c4009
	  /* Remember the start value for this round.  */
Packit 6c4009
	  inptr = *inptrp;
Packit 6c4009
	  /* The outbuf buffer is empty.  */
Packit 6c4009
	  outstart = outbuf;
Packit 6c4009
Packit 6c4009
#ifdef SAVE_RESET_STATE
Packit 6c4009
	  SAVE_RESET_STATE (1);
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
	  if (__glibc_likely (!unaligned))
Packit 6c4009
	    {
Packit 6c4009
	      if (FROM_DIRECTION)
Packit 6c4009
		/* Run the conversion loop.  */
Packit 6c4009
		status = FROM_LOOP (step, data, inptrp, inend, &outbuf, outend,
Packit 6c4009
				    lirreversiblep EXTRA_LOOP_ARGS);
Packit 6c4009
	      else
Packit 6c4009
		/* Run the conversion loop.  */
Packit 6c4009
		status = TO_LOOP (step, data, inptrp, inend, &outbuf, outend,
Packit 6c4009
				  lirreversiblep EXTRA_LOOP_ARGS);
Packit 6c4009
	    }
Packit 6c4009
#if POSSIBLY_UNALIGNED
Packit 6c4009
	  else
Packit 6c4009
	    {
Packit 6c4009
	      if (FROM_DIRECTION)
Packit 6c4009
		/* Run the conversion loop.  */
Packit 6c4009
		status = GEN_unaligned (FROM_LOOP) (step, data, inptrp, inend,
Packit 6c4009
						    &outbuf, outend,
Packit 6c4009
						    lirreversiblep
Packit 6c4009
						    EXTRA_LOOP_ARGS);
Packit 6c4009
	      else
Packit 6c4009
		/* Run the conversion loop.  */
Packit 6c4009
		status = GEN_unaligned (TO_LOOP) (step, data, inptrp, inend,
Packit 6c4009
						  &outbuf, outend,
Packit 6c4009
						  lirreversiblep
Packit 6c4009
						  EXTRA_LOOP_ARGS);
Packit 6c4009
	    }
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
	  /* If we were called as part of an error handling module we
Packit 6c4009
	     don't do anything else here.  */
Packit 6c4009
	  if (__glibc_unlikely (outbufstart != NULL))
Packit 6c4009
	    {
Packit 6c4009
	      *outbufstart = outbuf;
Packit 6c4009
	      return status;
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  /* We finished one use of the loops.  */
Packit 6c4009
	  ++data->__invocation_counter;
Packit 6c4009
Packit 6c4009
	  /* If this is the last step leave the loop, there is nothing
Packit 6c4009
	     we can do.  */
Packit 6c4009
	  if (__glibc_unlikely (data->__flags & __GCONV_IS_LAST))
Packit 6c4009
	    {
Packit 6c4009
	      /* Store information about how many bytes are available.  */
Packit 6c4009
	      data->__outbuf = outbuf;
Packit 6c4009
Packit 6c4009
	      /* Remember how many non-identical characters we
Packit 6c4009
		 converted in an irreversible way.  */
Packit 6c4009
	      *irreversible += lirreversible;
Packit 6c4009
Packit 6c4009
	      break;
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  /* Write out all output which was produced.  */
Packit 6c4009
	  if (__glibc_likely (outbuf > outstart))
Packit 6c4009
	    {
Packit 6c4009
	      const unsigned char *outerr = data->__outbuf;
Packit 6c4009
	      int result;
Packit 6c4009
Packit 6c4009
	      result = DL_CALL_FCT (fct, (next_step, next_data, &outerr,
Packit 6c4009
					  outbuf, NULL, irreversible, 0,
Packit 6c4009
					  consume_incomplete));
Packit 6c4009
Packit 6c4009
	      if (result != __GCONV_EMPTY_INPUT)
Packit 6c4009
		{
Packit 6c4009
		  if (__glibc_unlikely (outerr != outbuf))
Packit 6c4009
		    {
Packit 6c4009
#ifdef RESET_INPUT_BUFFER
Packit 6c4009
		      RESET_INPUT_BUFFER;
Packit 6c4009
#else
Packit 6c4009
		      /* We have a problem in one of the functions below.
Packit 6c4009
			 Undo the conversion upto the error point.  */
Packit 6c4009
		      size_t nstatus __attribute__ ((unused));
Packit 6c4009
Packit 6c4009
		      /* Reload the pointers.  */
Packit 6c4009
		      *inptrp = inptr;
Packit 6c4009
		      outbuf = outstart;
Packit 6c4009
Packit 6c4009
		      /* Restore the state.  */
Packit 6c4009
# ifdef SAVE_RESET_STATE
Packit 6c4009
		      SAVE_RESET_STATE (0);
Packit 6c4009
# endif
Packit 6c4009
Packit 6c4009
		      if (__glibc_likely (!unaligned))
Packit 6c4009
			{
Packit 6c4009
			  if (FROM_DIRECTION)
Packit 6c4009
			    /* Run the conversion loop.  */
Packit 6c4009
			    nstatus = FROM_LOOP (step, data, inptrp, inend,
Packit 6c4009
						 &outbuf, outerr,
Packit 6c4009
						 lirreversiblep
Packit 6c4009
						 EXTRA_LOOP_ARGS);
Packit 6c4009
			  else
Packit 6c4009
			    /* Run the conversion loop.  */
Packit 6c4009
			    nstatus = TO_LOOP (step, data, inptrp, inend,
Packit 6c4009
					       &outbuf, outerr,
Packit 6c4009
					       lirreversiblep
Packit 6c4009
					       EXTRA_LOOP_ARGS);
Packit 6c4009
			}
Packit 6c4009
# if POSSIBLY_UNALIGNED
Packit 6c4009
		      else
Packit 6c4009
			{
Packit 6c4009
			  if (FROM_DIRECTION)
Packit 6c4009
			    /* Run the conversion loop.  */
Packit 6c4009
			    nstatus = GEN_unaligned (FROM_LOOP) (step, data,
Packit 6c4009
								 inptrp, inend,
Packit 6c4009
								 &outbuf,
Packit 6c4009
								 outerr,
Packit 6c4009
								 lirreversiblep
Packit 6c4009
								 EXTRA_LOOP_ARGS);
Packit 6c4009
			  else
Packit 6c4009
			    /* Run the conversion loop.  */
Packit 6c4009
			    nstatus = GEN_unaligned (TO_LOOP) (step, data,
Packit 6c4009
							       inptrp, inend,
Packit 6c4009
							       &outbuf, outerr,
Packit 6c4009
							       lirreversiblep
Packit 6c4009
							       EXTRA_LOOP_ARGS);
Packit 6c4009
			}
Packit 6c4009
# endif
Packit 6c4009
Packit 6c4009
		      /* We must run out of output buffer space in this
Packit 6c4009
			 rerun.  */
Packit 6c4009
		      assert (outbuf == outerr);
Packit 6c4009
		      assert (nstatus == __GCONV_FULL_OUTPUT);
Packit 6c4009
Packit 6c4009
		      /* If we haven't consumed a single byte decrement
Packit 6c4009
			 the invocation counter.  */
Packit 6c4009
		      if (__glibc_unlikely (outbuf == outstart))
Packit 6c4009
			--data->__invocation_counter;
Packit 6c4009
#endif	/* reset input buffer */
Packit 6c4009
		    }
Packit 6c4009
Packit 6c4009
		  /* Change the status.  */
Packit 6c4009
		  status = result;
Packit 6c4009
		}
Packit 6c4009
	      else
Packit 6c4009
		/* All the output is consumed, we can make another run
Packit 6c4009
		   if everything was ok.  */
Packit 6c4009
		if (status == __GCONV_FULL_OUTPUT)
Packit 6c4009
		  {
Packit 6c4009
		    status = __GCONV_OK;
Packit 6c4009
		    outbuf = data->__outbuf;
Packit 6c4009
		  }
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  if (status != __GCONV_OK)
Packit 6c4009
	    break;
Packit 6c4009
Packit 6c4009
	  /* Reset the output buffer pointer for the next round.  */
Packit 6c4009
	  outbuf = data->__outbuf;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
#ifdef END_LOOP
Packit 6c4009
      END_LOOP
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
      /* If we are supposed to consume all character store now all of the
Packit 6c4009
	 remaining characters in the `state' object.  */
Packit 6c4009
#if FROM_LOOP_MAX_NEEDED_FROM > 1 || TO_LOOP_MAX_NEEDED_FROM > 1
Packit 6c4009
      if (((FROM_LOOP_MAX_NEEDED_FROM > 1 && TO_LOOP_MAX_NEEDED_FROM > 1)
Packit 6c4009
	   || (FROM_LOOP_MAX_NEEDED_FROM > 1 && FROM_DIRECTION)
Packit 6c4009
	   || (TO_LOOP_MAX_NEEDED_FROM > 1 && !FROM_DIRECTION))
Packit 6c4009
	  && __builtin_expect (consume_incomplete, 0)
Packit 6c4009
	  && status == __GCONV_INCOMPLETE_INPUT)
Packit 6c4009
	{
Packit 6c4009
# ifdef STORE_REST
Packit 6c4009
	  mbstate_t *state = data->__statep;
Packit 6c4009
Packit 6c4009
	  STORE_REST
Packit 6c4009
# else
Packit 6c4009
	  /* Make sure the remaining bytes fit into the state objects
Packit 6c4009
	     buffer.  */
Packit 6c4009
	  assert (inend - *inptrp < 4);
Packit 6c4009
Packit 6c4009
	  size_t cnt;
Packit 6c4009
	  for (cnt = 0; *inptrp < inend; ++cnt)
Packit 6c4009
	    data->__statep->__value.__wchb[cnt] = *(*inptrp)++;
Packit 6c4009
	  data->__statep->__count &= ~7;
Packit 6c4009
	  data->__statep->__count |= cnt;
Packit 6c4009
# endif
Packit 6c4009
	}
Packit 6c4009
#endif
Packit 6c4009
#undef unaligned
Packit 6c4009
#undef POSSIBLY_UNALIGNED
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  return status;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
#undef DEFINE_INIT
Packit 6c4009
#undef CHARSET_NAME
Packit 6c4009
#undef DEFINE_FINI
Packit 6c4009
#undef MIN_NEEDED_FROM
Packit 6c4009
#undef MIN_NEEDED_TO
Packit 6c4009
#undef MAX_NEEDED_FROM
Packit 6c4009
#undef MAX_NEEDED_TO
Packit 6c4009
#undef FROM_LOOP_MIN_NEEDED_FROM
Packit 6c4009
#undef FROM_LOOP_MAX_NEEDED_FROM
Packit 6c4009
#undef FROM_LOOP_MIN_NEEDED_TO
Packit 6c4009
#undef FROM_LOOP_MAX_NEEDED_TO
Packit 6c4009
#undef TO_LOOP_MIN_NEEDED_FROM
Packit 6c4009
#undef TO_LOOP_MAX_NEEDED_FROM
Packit 6c4009
#undef TO_LOOP_MIN_NEEDED_TO
Packit 6c4009
#undef TO_LOOP_MAX_NEEDED_TO
Packit 6c4009
#undef FROM_DIRECTION
Packit 6c4009
#undef EMIT_SHIFT_TO_INIT
Packit 6c4009
#undef FROM_LOOP
Packit 6c4009
#undef TO_LOOP
Packit 6c4009
#undef ONE_DIRECTION
Packit 6c4009
#undef SAVE_RESET_STATE
Packit 6c4009
#undef RESET_INPUT_BUFFER
Packit 6c4009
#undef FUNCTION_NAME
Packit 6c4009
#undef PREPARE_LOOP
Packit 6c4009
#undef END_LOOP
Packit 6c4009
#undef EXTRA_LOOP_ARGS
Packit 6c4009
#undef STORE_REST
Packit 6c4009
#undef FROM_ONEBYTE