/*
*******************************************************************************
*
*   Copyright (C) 1998-1999, International Business Machines
*   Corporation and others.  All Rights Reserved.
*
*******************************************************************************
*
*  ucnv.c:
*  Implements APIs for the ICU's codeset conversion library
*  mostly calls through internal functions created and maintained 
*  by Bertrand A. Damiba
*
* Modification History:
*
*   Date        Name        Description
*   04/04/99    helena      Fixed internal header inclusion.
*/

#include <errno.h>

#include "umutex.h"
/* #include "unicode/ures.h" */
#include "uhash.h"
#include "unicode/utypes.h"
#include "ucmp16.h"
#include "ucmp8.h"
#include "unicode/ucnv_bld.h"
#include "ucnv_io.h"
#include "unicode/ucnv_err.h"
#include "ucnv_cnv.h"
#include "ucnv_imp.h"
#include "unicode/ucnv.h"
#include "cmemory.h"
#include "cstring.h"
#include "unicode/ustring.h"
/* #include "unicode/uloc.h" */
#ifdef DEBUG
#include <stdio.h>
#endif

#ifndef DEBUG
#define CHUNK_SIZE 1024*5
#else
#define CHUNK_SIZE 102
#endif
/* just for test; but invalid even though DEBUG not defined */

/* Internal function : begin */
static int32_t ucnv_getAmbiguousCCSID (const UConverter* cnv);
/* Internal function : end */

typedef void (*T_ToUnicodeFunction) (UConverter *,
				     UChar **,
				     const UChar *,
				     const char **,
				     const char *,
				     int32_t* offsets,
				     bool_t,
				     UErrorCode *);

typedef void (*T_FromUnicodeFunction) (UConverter *,
				       char **,
				       const char *,
				       const UChar **,
				       const UChar *,
				       int32_t* offsets,
				       bool_t,
				       UErrorCode *);

typedef UChar (*T_GetNextUCharFunction) (UConverter *,
					 const char **,
					 const char *,
					 UErrorCode *);

static T_ToUnicodeFunction TO_UNICODE_FUNCTIONS[UCNV_NUMBER_OF_SUPPORTED_CONVERTER_TYPES] =

{
  T_UConverter_toUnicode_SBCS,
  T_UConverter_toUnicode_DBCS,
  T_UConverter_toUnicode_MBCS,
  T_UConverter_toUnicode_LATIN_1,
  T_UConverter_toUnicode_UTF8,
  T_UConverter_toUnicode_UTF16_BE,
  T_UConverter_toUnicode_UTF16_LE,
  T_UConverter_toUnicode_EBCDIC_STATEFUL,
  T_UConverter_toUnicode_ISO_2022
};

static T_ToUnicodeFunction TO_UNICODE_FUNCTIONS_OFFSETS_LOGIC[UCNV_NUMBER_OF_SUPPORTED_CONVERTER_TYPES] =

{
  NULL, /*UCNV_SBCS*/
  NULL, /*UCNV_DBCS*/
  T_UConverter_toUnicode_MBCS_OFFSETS_LOGIC,
  NULL, /*UCNV_LATIN_1*/
  T_UConverter_toUnicode_UTF8_OFFSETS_LOGIC,
  NULL, /*UTF16_BE*/
  NULL, /*UTF16_LE*/
  T_UConverter_toUnicode_EBCDIC_STATEFUL_OFFSETS_LOGIC,
  T_UConverter_toUnicode_ISO_2022_OFFSETS_LOGIC
};

static T_FromUnicodeFunction FROM_UNICODE_FUNCTIONS_OFFSETS_LOGIC[UCNV_NUMBER_OF_SUPPORTED_CONVERTER_TYPES] =

{
  NULL, /*UCNV_SBCS*/
  NULL, /*UCNV_DBCS*/
  T_UConverter_fromUnicode_MBCS_OFFSETS_LOGIC,
  NULL, /*UCNV_LATIN_1*/
  T_UConverter_fromUnicode_UTF8_OFFSETS_LOGIC,
  NULL, /*UTF16_BE*/
  NULL, /*UTF16_LE*/
  T_UConverter_fromUnicode_EBCDIC_STATEFUL_OFFSETS_LOGIC,
  T_UConverter_fromUnicode_ISO_2022_OFFSETS_LOGIC
};

static T_FromUnicodeFunction FROM_UNICODE_FUNCTIONS[UCNV_NUMBER_OF_SUPPORTED_CONVERTER_TYPES] =
{
  T_UConverter_fromUnicode_SBCS,
  T_UConverter_fromUnicode_DBCS,
  T_UConverter_fromUnicode_MBCS,
  T_UConverter_fromUnicode_LATIN_1,
  T_UConverter_fromUnicode_UTF8,
  T_UConverter_fromUnicode_UTF16_BE,
  T_UConverter_fromUnicode_UTF16_LE,
  T_UConverter_fromUnicode_EBCDIC_STATEFUL,
  T_UConverter_fromUnicode_ISO_2022
};

static T_GetNextUCharFunction GET_NEXT_UChar_FUNCTIONS[UCNV_NUMBER_OF_SUPPORTED_CONVERTER_TYPES] =
{
  T_UConverter_getNextUChar_SBCS,
  T_UConverter_getNextUChar_DBCS,
  T_UConverter_getNextUChar_MBCS,
  T_UConverter_getNextUChar_LATIN_1,
  T_UConverter_getNextUChar_UTF8,
  T_UConverter_getNextUChar_UTF16_BE,
  T_UConverter_getNextUChar_UTF16_LE,
  T_UConverter_getNextUChar_EBCDIC_STATEFUL,
  T_UConverter_getNextUChar_ISO_2022
};

void flushInternalUnicodeBuffer (UConverter * _this,
				 UChar * myTarget,
				 int32_t * myTargetIndex,
				 int32_t targetLength,
				 int32_t** offsets,
				 UErrorCode * err);

void flushInternalCharBuffer (UConverter * _this,
			      char *myTarget,
			      int32_t * myTargetIndex,
			      int32_t targetLength,
			      int32_t** offsets,
			      UErrorCode * err);




/*Calls through createConverter */
UConverter* ucnv_open (const char *name,
		       UErrorCode * err)
{
  if (err == NULL || U_FAILURE (*err)) {
    return NULL;
  }

  return createConverter (name, err);
}

/*Extracts the UChar* to a char* and calls through createConverter */
UConverter*  ucnv_openU (const UChar * name,
			 UErrorCode * err)
{
  char asciiName[UCNV_MAX_CONVERTER_NAME_LENGTH];
  
  if (U_FAILURE (*err))
    return NULL;
  if (name == NULL)
    return ucnv_open (NULL, err);
  if (u_strlen (name) > UCNV_MAX_CONVERTER_NAME_LENGTH)
    {
      *err = U_ILLEGAL_ARGUMENT_ERROR;
      return NULL;
    }
  return ucnv_open (u_austrcpy (asciiName, name), err);
}

/*Assumes a $platform-#codepage.$CONVERTER_FILE_EXTENSION scheme and calls
 *through createConverter*/
UConverter*  ucnv_openCCSID (int32_t codepage,
			     UConverterPlatform platform,
			     UErrorCode * err)
{
  char myName[UCNV_MAX_CONVERTER_NAME_LENGTH];

  if (U_FAILURE (*err))
    return NULL;

  copyPlatformString (myName, platform);
  uprv_strcat (myName, "-");
  T_CString_integerToString (myName + uprv_strlen (myName), codepage, 10);


  return createConverter (myName, err);
}

/*Decreases the reference counter in the shared immutable section of the object
 *and frees the mutable part*/

void ucnv_close (UConverter * converter)
{
  if (converter == NULL)
    return;
  if ((converter->sharedData->conversionType == UCNV_ISO_2022) &&
      (converter->mode == UCNV_SO))
    {
      ucnv_close (((UConverterDataISO2022 *) (converter->extraInfo))->currentConverter);
      uprv_free (converter->extraInfo);
    }

  umtx_lock (NULL);
  converter->sharedData->referenceCounter--;
  umtx_unlock (NULL);
  uprv_free (converter);

  return;
}


/*Frees all shared immutable objects that aren't referred to (reference count = 0)
 */
int32_t  ucnv_flushCache ()
{
  UConverterSharedData *mySharedData = NULL;
  int32_t pos = -1;
  int32_t tableDeletedNum = 0;

  /*if shared data hasn't even been lazy evaluated yet
   * return 0
   */
  if (SHARED_DATA_HASHTABLE == NULL)
    return 0;

  /*creates an enumeration to iterate through every element in the
   *table
   */
  umtx_lock (NULL);
  while (mySharedData = (UConverterSharedData *) uhash_nextElement (SHARED_DATA_HASHTABLE, &pos))
    {
      /*deletes only if reference counter == 0 */
      if (mySharedData->referenceCounter == 0)
	{
	  UErrorCode err = U_ZERO_ERROR;
	  tableDeletedNum++;

	  uhash_remove (SHARED_DATA_HASHTABLE, uhash_hashIString (mySharedData->name), &err);
	  deleteSharedConverterData (mySharedData);

	}
    }
  umtx_unlock (NULL);

  return tableDeletedNum;
}

/*returns a single Name from the list, will return NULL if out of bounds
 */
const char*  ucnv_getAvailableName (int32_t index)
{
  if (0 <= index && index <= 0xffff) {
    UErrorCode err = U_ZERO_ERROR;
    const char *name = ucnv_io_getAvailableConverter((uint16_t)index, &err);
    if (U_SUCCESS(err)) {
      return name;
    }
  }
  return NULL;
}

int32_t  ucnv_countAvailable ()
{
  UErrorCode err = U_ZERO_ERROR;
  return ucnv_io_countAvailableConverters(&err);
}

U_CAPI uint16_t
ucnv_countAliases(const char *alias, UErrorCode *pErrorCode) {
    const char *p;
    return ucnv_io_getAliases(alias, &p, pErrorCode);
}


U_CAPI const char *
ucnv_getAlias(const char *alias, uint16_t index, UErrorCode *pErrorCode) {
    return ucnv_io_getAlias(alias, index, pErrorCode);
}

U_CAPI void
ucnv_getAliases(const char *alias, const char **aliases, UErrorCode *pErrorCode) {
    const char *p;
    uint16_t count=ucnv_io_getAliases(alias, &p, pErrorCode);
    while(count>0) {
        *aliases++=p;
        /* skip a name, first the canonical converter name */
        p+=uprv_strlen(p)+1;
        --count;
    }
}

void   ucnv_getSubstChars (const UConverter * converter,
			   char *mySubChar,
			   int8_t * len,
			   UErrorCode * err)
{
  if (U_FAILURE (*err))
    return;

  if (*len < converter->subCharLen)	/*not enough space in subChars */
    {
      *err = U_INDEX_OUTOFBOUNDS_ERROR;
      return;
    }

  uprv_memcpy (mySubChar, converter->subChar, converter->subCharLen);	/*fills in the subchars */
  *len = converter->subCharLen;	/*store # of bytes copied to buffer */

  return;
}

void   ucnv_setSubstChars (UConverter * converter,
			   const char *mySubChar,
			   int8_t len,
			   UErrorCode * err)
{
  uint8_t x = 0;

  if (U_FAILURE (*err))
    return;

  /*Makes sure that the subChar is within the codepages char length boundaries */
  if ((len > converter->sharedData->maxBytesPerChar)
      || (len < converter->sharedData->minBytesPerChar))
    {
      *err = U_ILLEGAL_ARGUMENT_ERROR;
      return;
    }

  uprv_memcpy (converter->subChar, mySubChar, len);	/*copies the subchars */
  converter->subCharLen = len;	/*sets the new len */

  return;
}



#ifdef COMMENTOUT
int32_t  ucnv_getDisplayName (const UConverter * converter,
			      const char *displayLocale,
			      UChar * displayName,
			      int32_t displayNameCapacity,
			      UErrorCode * err)
{
  UChar stringToWriteBuffer[UCNV_MAX_CONVERTER_NAME_LENGTH];
  UChar const *stringToWrite;
  int32_t stringToWriteLength;
  UResourceBundle *rb = NULL;

  if (U_FAILURE (*err))
    return 0;

  /*create an RB, init the fill-in string, gets it from the RB */
  rb = ures_open (NULL, displayLocale, err);

  stringToWrite = ures_get (rb,
			    converter->sharedData->name,
			    err);

  if (rb)
    ures_close (rb);

  if (U_SUCCESS (*err))
    stringToWriteLength = u_strlen (stringToWrite);
  else
    {
      /*Error While creating or getting resource from the resource bundle
       *use the internal name instead
       *
       *sets stringToWriteLength (which accounts for a NULL terminator)
       *and stringToWrite
       */
      stringToWriteLength = uprv_strlen (converter->sharedData->name) + 1;
      stringToWrite = u_uastrcpy (stringToWriteBuffer, converter->sharedData->name);

      /*Hides the fallback to the internal name from the user */
      if (*err == U_MISSING_RESOURCE_ERROR)
	*err = U_ZERO_ERROR;
    }

  /*At this point we have a displayName and its length
   *we want to see if it fits in the user provided params
   */

  if (stringToWriteLength <= displayNameCapacity)
    {
      /*it fits */
      u_strcpy (displayName, stringToWrite);
    }
  else
    {
      /*it doesn't fit */
      *err = U_BUFFER_OVERFLOW_ERROR;

      u_strncpy (displayName,
		 stringToWrite,
		 displayNameCapacity);
      /*Zero terminates the string */
      if (displayNameCapacity > 0)
	displayName[displayNameCapacity - 1] = 0x0000;
    }

  /*if the user provided us with a with an outputLength
   *buffer we'll store in it the theoretical size of the
   *displayString
   */
  return stringToWriteLength;
}
#endif

/*resets the internal states of a converter
 *goal : have the same behaviour than a freshly created converter
 */
void  ucnv_reset (UConverter * converter)
{
  converter->toUnicodeStatus = converter->sharedData->defaultConverterValues.toUnicodeStatus;
  converter->fromUnicodeStatus = 0;
  converter->UCharErrorBufferLength = 0;
  converter->charErrorBufferLength = 0;
  if ((converter->sharedData->conversionType == UCNV_ISO_2022) &&
      (converter->mode == UCNV_SO))
    {
      converter->charErrorBufferLength = 3;
      converter->charErrorBuffer[0] = 0x1b;
      converter->charErrorBuffer[1] = 0x25;
      converter->charErrorBuffer[2] = 0x42;
      ucnv_close (((UConverterDataISO2022 *) (converter->extraInfo))->currentConverter);
      ((UConverterDataISO2022 *) (converter->extraInfo))->currentConverter = NULL;
      ((UConverterDataISO2022 *) (converter->extraInfo))->escSeq2022Length = 0;
    }
  converter->mode = UCNV_SI;

  return;
}

int8_t  ucnv_getMaxCharSize (const UConverter * converter)
{
  return converter->sharedData->maxBytesPerChar;
}


int8_t  ucnv_getMinCharSize (const UConverter * converter)
{
  return converter->sharedData->minBytesPerChar;
}

const char*  ucnv_getName (const UConverter * converter, UErrorCode * err)
     
{
  if (U_FAILURE (*err))
    return NULL;

  return converter->sharedData->name;
}

int32_t  ucnv_getCCSID (const UConverter * converter,
			UErrorCode * err)
{
  if (U_FAILURE (*err))
    return -1;

  return converter->sharedData->codepage;
}


UConverterPlatform  ucnv_getPlatform (const UConverter * converter,
				 UErrorCode * err)
{
  if (U_FAILURE (*err))
    return UCNV_UNKNOWN;
  
  return converter->sharedData->platform;
}

UConverterToUCallback  ucnv_getToUCallBack (const UConverter * converter)
{
  return converter->fromCharErrorBehaviour;
}

UConverterFromUCallback  ucnv_getFromUCallBack (const UConverter * converter)
{
  return converter->fromUCharErrorBehaviour;
}

UConverterToUCallback   ucnv_setToUCallBack (UConverter * converter,
					UConverterToUCallback action,
					UErrorCode * err)
{
  UConverterToUCallback myReturn = NULL;

  if (U_FAILURE (*err))
    return NULL;
  myReturn = converter->fromCharErrorBehaviour;
  converter->fromCharErrorBehaviour = action;

  return myReturn;
}

UConverterFromUCallback   ucnv_setFromUCallBack (UConverter * converter,
					    UConverterFromUCallback action,
					    UErrorCode * err)
{
  UConverterFromUCallback myReturn = NULL;
  
  if (U_FAILURE (*err))
    return NULL;
  myReturn = converter->fromUCharErrorBehaviour;
  converter->fromUCharErrorBehaviour = action;

  return myReturn;
}
void   ucnv_fromUnicode (UConverter * _this,
			 char **target,
			 const char *targetLimit,
			 const UChar ** source,
			 const UChar * sourceLimit,
			 int32_t* offsets,
			 bool_t flush,
			 UErrorCode * err)
{
  UConverterType myConvType;
  /*
   * Check parameters in for all conversions
   */
  if (U_FAILURE (*err))   return;
  if ((_this == NULL) || ((char *) targetLimit < *target) || (sourceLimit < *source))
    {
      *err = U_ILLEGAL_ARGUMENT_ERROR;
      return;
    }
  

  /*
   * Deal with stored carry over data.  This is done in the common location
   * to avoid doing it for each conversion.
   */
  if (_this->charErrorBufferLength > 0)
    {
      int32_t myTargetIndex = 0;

      flushInternalCharBuffer (_this, 
			       (char *) *target,
			       &myTargetIndex,
			       targetLimit - *target,
			       offsets?&offsets:NULL,
			       err);
      *target += myTargetIndex;
      if (U_FAILURE (*err)) return;
    }
  
  myConvType = _this->sharedData->conversionType;  
  if (offsets) 
    {
       int32_t targetSize = targetLimit - *target;
       int32_t i;
       switch (myConvType)
	 {
	 case UCNV_LATIN_1: case UCNV_SBCS : 
	   {
	     for (i=0; i<targetSize; i++) offsets[i] = i;
	     break;
	   }
	 case UCNV_UTF16_LittleEndian: case UCNV_UTF16_BigEndian: case UCNV_DBCS: 
	   {
	     --targetSize;
	     for (i=0; i<targetSize; i+=2) 
	       {
		 offsets[i] = i;
		 offsets[i+1] = i;
	       }
	     break;
	   }
	 default:
	   {
	     
	     FROM_UNICODE_FUNCTIONS_OFFSETS_LOGIC[(int) myConvType] (_this,
								     target,
								     targetLimit,
								     source,
								     sourceLimit,
								     offsets,
								     flush,
								     err);
	     return;
	   }
	 };    
    }
  /*calls the specific conversion routines */
  FROM_UNICODE_FUNCTIONS[(int)myConvType] (_this,
					   target,
					   targetLimit,
					   source,
					   sourceLimit,
					   offsets,
					   flush,
					   err);
  
  return;
}



void   ucnv_toUnicode (UConverter * _this,
		       UChar ** target,
		       const UChar * targetLimit,
		       const char **source,
		       const char *sourceLimit,
		       int32_t* offsets,
		       bool_t flush,
		       UErrorCode * err)
{
  /*
   * Check parameters in for all conversions
   */
  UConverterType myConvType;
  if (U_FAILURE (*err))   return;
  if ((_this == NULL) || ((UChar *) targetLimit < *target) || (sourceLimit < *source))
    {
      *err = U_ILLEGAL_ARGUMENT_ERROR;
      return;
    }

  myConvType = _this->sharedData->conversionType;
  /*
   * Deal with stored carry over data.  This is done in the common location
   * to avoid doing it for each conversion.
   */
  if (_this->UCharErrorBufferLength > 0)
    {
      int32_t myTargetIndex = 0;

      flushInternalUnicodeBuffer (_this, 
				  *target,
				  &myTargetIndex,
				  targetLimit - *target,
				  offsets?&offsets:NULL,
				  err);
      *target += myTargetIndex;
      if (U_FAILURE (*err))
	return;
    }

  if (offsets) 
    {
      int32_t targetSize = targetLimit - *target;
      int32_t i;

      switch (myConvType)
	{
	case UCNV_LATIN_1: case UCNV_SBCS : 
	  {
	    for (i=0; i<targetSize; i++) offsets[i] = i;
	    break;
	  }
	case UCNV_UTF16_LittleEndian: case UCNV_UTF16_BigEndian: case UCNV_DBCS: 
	  {
	    for (i=0; i<targetSize; i++) 
	      {
		offsets[i] = i*2;
	      }
	    break;
	  }
	default:
	  {
	    
	    TO_UNICODE_FUNCTIONS_OFFSETS_LOGIC[(int) myConvType] (_this,
								  target,
								  targetLimit,
								  source,
								  sourceLimit,
								  offsets,
								  flush,
								  err);
	    return;
	  }
	};
    }
  /*calls the specific conversion routines */
  TO_UNICODE_FUNCTIONS[(int) myConvType] (_this,
					  target,
					  targetLimit,
					  source,
					  sourceLimit,
					  offsets,
					  flush,
					  err);
  
  
  return;
}

int32_t   ucnv_fromUChars (const UConverter * converter,
			   char *target,
			   int32_t targetSize,
			   const UChar * source,
			   UErrorCode * err)
{
  const UChar *mySource = source;
  const UChar *mySource_limit;
  int32_t mySourceLength = 0;
  UConverter myConverter;
  char *myTarget = target;
  char *myTarget_limit;
  int32_t targetCapacity = 0;

  if (U_FAILURE (*err))
    return 0;

  if ((converter == NULL) || (targetSize < 0))
    {
      *err = U_ILLEGAL_ARGUMENT_ERROR;
      return 0;
    }

  /*makes a local copy of the UConverter */
  myConverter = *converter;


  /*Removes all state info on the UConverter */
  ucnv_reset (&myConverter);

  /*if the source is empty we return immediately */
  mySourceLength = u_strlen (source);
  if (mySourceLength == 0)
    {
      /*for consistency we still need to
       *store 0 in the targetCapacity
       *if the user requires it
       */
      return 0;
    }

  mySource_limit = mySource + mySourceLength;
  myTarget_limit = target + targetSize;

  /* Pin the limit to U_MAX_PTR.  NULL check is for AS/400. */
  if((myTarget_limit < target) || (myTarget_limit == NULL))
    myTarget_limit = (char *)U_MAX_PTR;

  if (targetSize > 0)
    {
      ucnv_fromUnicode (&myConverter,
			&myTarget,
			myTarget_limit,
			&mySource,
			mySource_limit,
			NULL,
			TRUE,
			err);
      targetCapacity = myTarget - target;
    }

  /*Updates targetCapacity to contain the number of bytes written to target */

  if (targetSize == 0)
    {
      *err = U_INDEX_OUTOFBOUNDS_ERROR;
    }

  /* If the output buffer is exhausted, we need to stop writing
   * to it but continue the conversion in order to store in targetSize
   * the number of bytes that was required*/
  if (*err == U_INDEX_OUTOFBOUNDS_ERROR)
    {
      char target2[CHUNK_SIZE];
      char *target2_alias = target2;
      const char *target2_limit = target2 + CHUNK_SIZE;

      /*We use a stack allocated buffer around which we loop
       *(in case the output is greater than CHUNK_SIZE)
       */

      while (*err == U_INDEX_OUTOFBOUNDS_ERROR)
	{
	  *err = U_ZERO_ERROR;
	  target2_alias = target2;
	  ucnv_fromUnicode (&myConverter,
			    &target2_alias,
			    target2_limit,
			    &mySource,
			    mySource_limit,
			    NULL,
			    TRUE,
			    err);

	  /*updates the output parameter to contain the number of char required */
	  targetCapacity += (target2_alias - target2) + 1;
	}
      /*We will set the erro code to U_BUFFER_OVERFLOW_ERROR only if
       *nothing graver happened in the previous loop*/
      (targetCapacity)--;
      if (U_SUCCESS (*err))
	*err = U_BUFFER_OVERFLOW_ERROR;
    }

  return targetCapacity;
}

int32_t ucnv_toUChars (const UConverter * converter,
		       UChar * target,
		       int32_t targetSize,
		       const char *source,
		       int32_t sourceSize,
		       UErrorCode * err)
{
  const char *mySource = source;
  const char *mySource_limit = source + sourceSize;
  UConverter myConverter;
  UChar *myTarget = target;
  UChar *myTarget_limit;
  int32_t targetCapacity;

  if (U_FAILURE (*err))
    return 0;

  if ((converter == NULL) || (targetSize < 0) || (sourceSize < 0))
    {
      *err = U_ILLEGAL_ARGUMENT_ERROR;
      return 0;
    }
  /*Means there is no work to be done */
  if (sourceSize == 0)
    {
      /*for consistency we still need to
       *store 0 in the targetCapacity
       *if the user requires it
       */
      if (targetSize >= 1)
	{
	  target[0] = 0x0000;
	  return 1;
	}
      else
	return 0;
    }

  /*makes a local copy of the UConverter */
  myConverter = *converter;

  /*Removes all state info on the UConverter */
  ucnv_reset (&myConverter);

  if (targetSize > 0)
  {
      myTarget_limit = target + targetSize - 1;

      /* Pin the limit to U_MAX_PTR.  NULL check is for AS/400. */
      if ((myTarget_limit == NULL) || (myTarget_limit < target)) {
          myTarget_limit = ((UChar*)U_MAX_PTR) - 1; 
      }

      /*Not in pure pre-flight mode */

      ucnv_toUnicode (&myConverter,
		      &myTarget,
		      myTarget_limit,	  /*Save a spot for the Null terminator */
		      &mySource,
		      mySource_limit,
		      NULL,
		      TRUE,
		      err);

      /*Null terminates the string */
      *(myTarget) = 0x0000;
    }


  /*Rigs targetCapacity to have at least one cell for zero termination */
  /*Updates targetCapacity to contain the number of bytes written to target */
  targetCapacity = 1;
  targetCapacity += myTarget - target;
  if (targetSize == 0)
    {
      *err = U_INDEX_OUTOFBOUNDS_ERROR;
    }
  /* If the output buffer is exhausted, we need to stop writing
   * to it but if the input buffer is not exhausted,
   * we need to continue the conversion in order to store in targetSize
   * the number of bytes that was required
   */
  if (*err == U_INDEX_OUTOFBOUNDS_ERROR)
    {
      UChar target2[CHUNK_SIZE];
      UChar *target2_alias = target2;
      const UChar *target2_limit = target2 + CHUNK_SIZE;

      /*We use a stack allocated buffer around which we loop
         (in case the output is greater than CHUNK_SIZE) */

      while (*err == U_INDEX_OUTOFBOUNDS_ERROR)
	{
	  *err = U_ZERO_ERROR;
	  target2_alias = target2;
	  ucnv_toUnicode (&myConverter,
			  &target2_alias,
			  target2_limit,
			  &mySource,
			  mySource_limit,
			  NULL,
			  TRUE,
			  err);

	  /*updates the output parameter to contain the number of char required */
	  targetCapacity += target2_alias - target2 + 1;
	}
      (targetCapacity)--;	/*adjust for last one */
      if (U_SUCCESS (*err))
	*err = U_BUFFER_OVERFLOW_ERROR;
    }

  return targetCapacity;
}

UChar ucnv_getNextUChar (UConverter * converter,
			 const char **source,
			 const char *sourceLimit,
			 UErrorCode * err)
{
  /* In case internal data had been stored
   * we return the first UChar in the internal buffer,
   * and update the internal state accordingly
   */
  if (converter->UCharErrorBufferLength > 0)
    {
      UChar myUChar = converter->UCharErrorBuffer[0];
      /*In this memmove we update the internal buffer by
       *popping the first character.
         *Note that in the call itself we decrement
         *UCharErrorBufferLength
       */
      uprv_memmove (converter->UCharErrorBuffer,
		   converter->UCharErrorBuffer + 1,
		   --(converter->UCharErrorBufferLength) * sizeof (UChar));
      return myUChar;
    }
  /*calls the specific conversion routines */
  /*as dictated in a code review, avoids a switch statement */
  return GET_NEXT_UChar_FUNCTIONS[(int) (converter->sharedData->conversionType)] (converter,
										  source,
										  sourceLimit,
										  err);
}



/**************************
* Will convert a sequence of bytes from one codepage to another.
* @param toConverterName: The name of the converter that will be used to encode the output buffer
* @param fromConverterName: The name of the converter that will be used to decode the input buffer
* @param target: Pointer to the output buffer* written
* @param targetLength: on input contains the capacity of target, on output the number of bytes copied to target
* @param source: Pointer to the input buffer
* @param sourceLength: on input contains the capacity of source, on output the number of bytes processed in "source"
* @param internal: used internally to store store state data across calls
* @param err: fills in an error status
*/
void 
T_UConverter_fromCodepageToCodepage (UConverter * outConverter,
				     UConverter * inConverter,
				     char **target,
				     const char *targetLimit,
				     const char **source,
				     const char *sourceLimit,
				     int32_t* offsets,
				     bool_t flush,
				     UErrorCode * err)
{
  const char *source_base = *source;
  int32_t source_index = 0;
  int32_t offsets_index = 0;
  UChar out_chunk[CHUNK_SIZE];
  const UChar *out_chunk_limit = out_chunk + CHUNK_SIZE;
  UChar *out_chunk_alias;
  UChar const *out_chunk_alias2;

  int32_t offsets_toUnicode_size = ((CHUNK_SIZE+10)*sizeof(int32_t));
  int32_t* offsets_toUnicode = uprv_malloc(offsets_toUnicode_size);
  int32_t offsets_fromUnicode_size = (targetLimit-*target+10)*sizeof(int32_t);
  int32_t* offsets_fromUnicode =  uprv_malloc(offsets_fromUnicode_size);

  if (U_FAILURE (*err))    return;
  if (offsets_toUnicode == NULL || offsets_fromUnicode == NULL ) {
    *err = U_MISSING_RESOURCE_ERROR;
    return;
  }

#ifdef DEBUG
  if (offsets == NULL) {
    offsets = uprv_malloc((offsets_fromUnicode_size+10)*sizeof(int32_t));
  }
  if (offsets == NULL){
    *err = U_MISSING_RESOURCE_ERROR;
    return;
  }
#endif


  /*loops until the input buffer is completely consumed
   *or if an error has be encountered
   *first we convert from inConverter codepage to Unicode
   *then from Unicode to outConverter codepage
   */
  while ((*source != sourceLimit) && U_SUCCESS (*err))
    {
      out_chunk_alias = out_chunk;
      source_index = *source - source_base;
      ucnv_toUnicode (inConverter,
		      &out_chunk_alias,
		      out_chunk_limit,
		      source,
		      sourceLimit,
		      offsets_toUnicode,
		      flush,
		      err);

      /*U_INDEX_OUTOFBOUNDS_ERROR means that the output "CHUNK" is full
       *we will require at least another loop (it's a recoverable error)
       */

      if (U_SUCCESS (*err) || (*err == U_INDEX_OUTOFBOUNDS_ERROR))
	{
	  char *target_base = *target;
	  *err = U_ZERO_ERROR;
	  out_chunk_alias2 = out_chunk;

#ifdef DEBUG
		fprintf(stderr, "Source: \'%x\' in[%d]->\t Unicode:\'%x\' in[%d]\n",
			(uchar_t)*(source_base + source_index + offsets_toUnicode[0]), 
			offsets_toUnicode[0],
			out_chunk[0], 0);
		fprintf(stderr, "Source: \'%x\' in[%d]->\t Unicode:\'%x\' in[%d]\n",
			(uchar_t)*(source_base + source_index + 
			offsets_toUnicode[out_chunk_alias-out_chunk-1]),
			offsets_toUnicode[out_chunk_alias-out_chunk-1],
			out_chunk[out_chunk_alias-out_chunk-1], 
			out_chunk_alias-out_chunk-1);
#endif

	  ucnv_fromUnicode (outConverter,
			    target,
			    targetLimit,
			    &out_chunk_alias2,
			    out_chunk_alias,
			    offsets_fromUnicode,
			    TRUE,
			    err);

	  if (U_SUCCESS (*err) || (*err == U_INDEX_OUTOFBOUNDS_ERROR)){
	    int32_t i;
#ifdef DEBUG
	    int32_t offsets_index2 = offsets_index;
#endif

#ifdef DEBUG
		fprintf(stderr, "Unicode:\'%x\' in[%d]->\t Target:\'%x\' in[%d]\n",
			out_chunk[0], offsets_toUnicode[0], 
			(uchar_t)target_base[0], 0);
		fprintf(stderr, "Unicode:\'%x\' in[%d]->\t Target:\'%x\' in[%d]\n",
			out_chunk[offsets_fromUnicode[*target-target_base-1]],
			offsets_fromUnicode[*target-target_base-1],
			target_base[*target-target_base-1],
			(uchar_t)(*target-target_base-1));
#endif



#ifdef DEBUG_sumi
		fprintf(stderr, "offsets_fromUnicode[%d] = %d(0x%x)\n",
			0, offsets_fromUnicode[0],offsets_fromUnicode[0]);
		fprintf(stderr, "offsets_fromUnicode[%d] = %d(0x%x)\n",
			(*target-target_base-1),
			offsets_fromUnicode[(*target-target_base-1)],
			offsets_fromUnicode[(*target-target_base-1)]);
#endif

	    if (offsets != NULL) {
	      for (i = 0; i < (*target - target_base); i++){ 
		offsets[offsets_index++] = 
		  offsets_toUnicode[offsets_fromUnicode[i]] + source_index;
#ifdef DEBUG
		fprintf(stderr, "offsets[%d] (%d) = o_to[o_fr[%d](%d)](%d) + %d;\n",
			offsets_index-1, offsets[offsets_index-1], i,
			offsets_fromUnicode[i],
			offsets_toUnicode[offsets_fromUnicode[i]],
			source_index);
#endif
	      }
#ifdef DEBUG
	      fprintf(stderr, "Source:\t\'%x\' in[%d]->\t Target:\'%x\' in[%d]\n",
		      (uchar_t)source_base[offsets[offsets_index2]], offsets[offsets_index2],
		      (uchar_t)target_base[0], 0);

	      fprintf(stderr, "Source:\t\'%x\' in[%d]->\t Target:\'%x\' in[%d]\n",
		      (uchar_t)source_base[offsets[offsets_index-1]], offsets[offsets_index-1],
		      (uchar_t)*(*target-1), *target-target_base-1);
#endif
#ifdef DEBUG_old
	      fprintf(stderr, "offsets[%d(0x%x)] = %d(0x%x)\n", offsets_index2,
		      offsets_index2, offsets[offsets_index2], offsets[offsets_index2]);
	      fprintf(stderr, "offsets[%d(0x%x)] = %d(0x%x)\n",offsets_index -1,
		      offsets_index-1, offsets[offsets_index-1], offsets[offsets_index-1]);
#endif
	      }
	    if (*err == U_INDEX_OUTOFBOUNDS_ERROR){
	      *source = source_base + source_index + offsets_toUnicode
		[offsets_fromUnicode[*target-target_base-1]+1];
	      ucnv_reset(inConverter);
	      ucnv_reset(outConverter);
#ifdef DEBUG
	    fprintf(stderr, " adjusted source=%x: source_index = %d(0x%x)\n", 
		    *source,
		    *source - source_base, *source - source_base);
#endif
	      break;
	    }
#ifdef DEBUG
	    fprintf(stderr, " (completed) source_index = %d(0x%x)\n", 
		    *source - source_base, *source - source_base);
#endif
	  }
	}
      else
	{
	  break;
	}
    }

  uprv_free(offsets_toUnicode);
  uprv_free(offsets_fromUnicode);

  return;
}



int32_t  ucnv_convert(const char *toConverterName,
		      const char *fromConverterName,
		      char *target,
		      int32_t targetSize,
		      const char *source,
		      int32_t sourceSize,
		      UErrorCode * err)
{
  const char *mySource = source;
  const char *mySource_limit = source + sourceSize;
  int32_t mySourceLength = 0;
  UConverter *inConverter;
  UConverter *outConverter;
  char *myTarget = target;
  int32_t targetCapacity = 0;

  if (U_FAILURE (*err))
    return 0;

  if ((targetSize < 0) || (sourceSize < 0))
    {
      *err = U_ILLEGAL_ARGUMENT_ERROR;
      return 0;
    }

  /*if there is no input data, we're done */
  if (sourceSize == 0)
    {
      /*in case the caller passed an output ptr
       *we update it
       */
      return 0;
    }

  /*create the converters */
  inConverter = ucnv_open (fromConverterName, err);
  if (U_FAILURE (*err)) return 0;
  outConverter = ucnv_open (toConverterName, err);
  if (U_FAILURE (*err))
    {
      ucnv_close (inConverter);
      return 0;
    }


  if (targetSize > 0)
    {
      T_UConverter_fromCodepageToCodepage (outConverter,
					   inConverter,
					   &myTarget,
					   target + targetSize,
					   &mySource,
					   mySource_limit,
					   NULL,
					   TRUE,
					   err);
    }


  /*Updates targetCapacity to contain the number of bytes written to target */
  targetCapacity = myTarget - target;
  if (targetSize == 0)
    {
      *err = U_INDEX_OUTOFBOUNDS_ERROR;
    }

  /* If the output buffer is exhausted, we need to stop writing
   * to it but continue the conversion in order to store in targetSize
   * the number of bytes that was required*/
  if (*err == U_INDEX_OUTOFBOUNDS_ERROR)
    {
      char target2[CHUNK_SIZE];
      char *target2_alias = target2;
      const char *target2_limit = target2 + CHUNK_SIZE;

      /*We use a stack allocated buffer around which we loop
       *(in case the output is greater than CHUNK_SIZE)
       */

      while (*err == U_INDEX_OUTOFBOUNDS_ERROR)
	{
	  *err = U_ZERO_ERROR;
	  target2_alias = target2;
	  T_UConverter_fromCodepageToCodepage (outConverter,
					       inConverter,
					       &target2_alias,
					       target2_limit,
					       &mySource,
					       mySource_limit,
					       NULL,
					       TRUE,
					       err);

	  /*updates the output parameter to contain the number of char required */
	  targetCapacity += (target2_alias - target2) + 1;
	}
      /*We will set the erro code to U_BUFFER_OVERFLOW_ERROR only if
       *nothing graver happened in the previous loop*/
      (targetCapacity)--;
      if (U_SUCCESS (*err))
	*err = U_BUFFER_OVERFLOW_ERROR;
    }

  ucnv_close (inConverter);
  ucnv_close (outConverter);

  return targetCapacity;
}

UConverterType ucnv_getType(const UConverter* converter)
{
  return converter->sharedData->conversionType;
}

void ucnv_getStarters(const UConverter* converter, 
		      bool_t starters[256],
		      UErrorCode* err)
{
  if (U_FAILURE(*err)) return;
  /*Fire off an error if converter is not UCNV_MBCS*/
  if (converter->sharedData->conversionType != UCNV_MBCS) 
    {
      *err = U_ILLEGAL_ARGUMENT_ERROR;
      return;
    }
  
  /*fill's in the starters boolean array*/
  uprv_memcpy(starters, converter->sharedData->table->mbcs.starters, 256*sizeof(bool_t));
  return;
}

int32_t ucnv_getAmbiguousCCSID(const UConverter *cnv)
{
    UErrorCode status = U_ZERO_ERROR;
    int32_t i = 0;
    int32_t ccsid = 0;
    if (cnv == NULL) 
    {
        return -1;
    }
    ccsid = ucnv_getCCSID(cnv, &status);
    if (U_FAILURE(status)) 
    {
        return -1;
    }
    for (i = 0; i < UCNV_MAX_AMBIGUOUSCCSIDS; i++) {
        if (ccsid == UCNV_AMBIGUOUSCONVERTERS[i].ccsid) 
        {
            return i;
        }
    }
    return -1;
}

void ucnv_fixFileSeparator(const UConverter *cnv, 
                           UChar* source, 
                           int32_t sourceLength)
{
    int32_t i = 0;
    int32_t index = 0;
    if ((source == NULL) || (cnv == NULL))
    {
        return;
    }
    if ((index = ucnv_getAmbiguousCCSID(cnv)) != -1)
    {
        for (i = 0; i < sourceLength; i++) 
        {
            if (source[i] == UCNV_AMBIGUOUSCONVERTERS[index].mismapped)
            {
                source[i] = UCNV_AMBIGUOUSCONVERTERS[index].replacement;
            }
        }
    }
}

bool_t ucnv_isAmbiguous(const UConverter *cnv)
{
    return (ucnv_getAmbiguousCCSID(cnv) == -1 ? FALSE : TRUE);
}
