/*
Copyright 1990-2001 Sun Microsystems, Inc. All Rights Reserved.

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions: The above copyright notice and this
permission notice shall be included in all copies or substantial
portions of the Software.


THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE OPEN GROUP OR SUN MICROSYSTEMS, INC. BE LIABLE
FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE EVEN IF
ADVISED IN ADVANCE OF THE POSSIBILITY OF SUCH DAMAGES.


Except as contained in this notice, the names of The Open Group and/or
Sun Microsystems, Inc. shall not be used in advertising or otherwise to
promote the sale, use or other dealings in this Software without prior
written authorization from The Open Group and/or Sun Microsystems,
Inc., as applicable.


X Window System is a trademark of The Open Group

OSF/1, OSF/Motif and Motif are registered trademarks, and OSF, the OSF
logo, LBX, X Window System, and Xinerama are trademarks of the Open
Group. All other trademarks and registered trademarks mentioned herein
are the property of their respective owners. No right, title or
interest in or to any trademark, service mark, logo or trade name of
Sun Microsystems, Inc. or its licensors is granted.

*/

#if !defined(USE_ICONV) && !defined(USE_CSCONV)
#if defined(sun)
#define USE_ICONV
#else
#define USE_ICONV
#endif
#endif

#ifdef USE_ICONV
#include <iconv.h> 
#endif

#include <locale.h>
#include <langinfo.h>
#include <strings.h>
#if !defined(USE_CSCONV)
#include <errno.h>
#endif /* !USE_CSCONV */
#include "iiimpIM.h"
#include "xiiimp.h"
#include "iiimpReq.h"
#include "iiimpColor.h"
#include "iiimpConv.h"

#define UTF16_STRLEN	4096

#ifdef USE_CSCONV
#include <dlfcn.h>
#include "csconv.h"
#define CSC_PATH	IIIMLIBDIR "/csconv/csconv.so"
#define CSC_OPEN_LOCALE	"csconv_open_locale"
#define CSC_OPEN	"csconv_open"
#define CSC_CONV	"csconv"
#define CSC_CLOSE	"csconv_close"

typedef csconv_t	(* csc_open_locale_t)(const char *,
					      const char *, const char *);
typedef csconv_t	(* csc_open_t)(const char *, const char *);
typedef size_t		(* csc_conv_t)(csconv_t, const char **, size_t *,
				       char **, size_t *);
typedef int		(* csc_close_t)(csconv_t);

static void *			csc_handle;	
static csc_open_locale_t	csc_open_locale;
static csc_open_t		csc_open;
static csc_conv_t		csc_conv;
static csc_close_t		csc_close;

static char *current_locale = 0;
#endif

#ifdef USE_ICONV
static iconv_t i_conv1 = NULL;
static iconv_t i_conv2 = NULL;
static Bool skip_native_to_utf8_conv = False;
#endif

#ifdef USE_CSCONV
static void
dlopen_csconv()
{
    csc_handle = dlopen(CSC_PATH, RTLD_LAZY);
    if (NULL == csc_handle) {
	csc_handle = (void *)(-1);
	return;
    }

    csc_open_locale = (csc_open_locale_t)dlsym(csc_handle, CSC_OPEN_LOCALE);
    csc_open = (csc_open_t)dlsym(csc_handle, CSC_OPEN);
    csc_conv = (csc_conv_t)dlsym(csc_handle, CSC_CONV);
    csc_close = (csc_close_t)dlsym(csc_handle, CSC_CLOSE);

    if ((NULL == csc_open_locale) || (NULL == csc_open) ||
	(NULL == csc_conv) || (NULL == csc_close)) {
	dlclose(csc_handle);
	csc_handle = (void *)(-1);
	return;
    }
}
#endif

static int
ConvertToUTF16(
    char *current_encoding,
    char *from_buf,
    size_t from_left,
    char **to_buf,
    size_t *to_left
)
{
    size_t src_len, dst_len;
    char *src, *dst;
#ifndef USE_CSCONV
    const char *ip;
    size_t ileft;
    char *op;
    size_t oleft;
    char buffer[UTF16_STRLEN];		/* Fix me! */
#endif /* !USE_CSCONV */
    const size_t buf_len = UTF16_STRLEN;
    size_t ret = 0;
#ifdef USE_CSCONV
    static csconv_t csconv_cd = NULL;

#endif

    if (current_encoding == NULL) return 0;

#ifdef USE_CSCONV

    do {
	if (((void *)(-1) == csc_handle) ||
	    ((csconv_t)(-1) == csconv_cd)) {
	    continue;
	}
	if (NULL == csc_handle) {
	    dlopen_csconv();
	    if ((void *)(-1) == csc_handle) {
		continue;
	    }
	}
	if (NULL == csconv_cd) {

	    csconv_cd = (csc_open_locale)(current_encoding,
					  "UTF-16", "MultiByte");
      
	    if ((csconv_t)(-1) == csconv_cd) {
		continue;
	    }
	}

	dst = *((char **)to_buf);

	ret = csc_conv(csconv_cd,
		       (const char **)&from_buf, &from_left,
		       &dst, to_left);
	return ret;
    } while (0);
#endif

#ifdef USE_ICONV
    if (i_conv1 == (iconv_t)-1 || i_conv2 == (iconv_t)-1){
	goto done;
    }

    if (i_conv1 == NULL && !skip_native_to_utf8_conv) {
	if (!strcmp(current_encoding, "UTF-8")) {
	    skip_native_to_utf8_conv = True;
	} else {
	    if ((i_conv1 = iconv_open("UTF-8", current_encoding))
		== (iconv_t)-1) {
		/* fallback */
		current_encoding = "646";
		if ((i_conv1 = iconv_open("UTF-8", current_encoding))
		    == (iconv_t)-1)
		    goto done;
	    }
	}
    }

    if (i_conv2 == NULL){
	if ((i_conv2 = iconv_open("UTF-16",
				  "UTF-8")) == (iconv_t)-1)
	    goto done;
    }

    if (!skip_native_to_utf8_conv) {
	ip = (const char *)from_buf;
	ileft = from_left;

	op = *((char **)to_buf);
	oleft = *to_left;

	while ((0 < ileft) && (0 < oleft)) {
	    dst = buffer;
	    dst_len = buf_len;
	    ret = iconv(i_conv1, &ip, &ileft, (char**)&dst, &dst_len);
	    if ((ret != 0) && (E2BIG != errno)) {
		goto done;
	    }

	    src = buffer;
	    src_len = buf_len - dst_len;

	    ret = iconv(i_conv2, (const char**)&src, &src_len, &op, &oleft);
	    if (ret != 0) goto done;
	}

	dst_len = oleft;

    } else {
	src = from_buf;
	src_len = from_left;
	dst = *to_buf;
	dst_len = *to_left;

	ret = iconv(i_conv2, (const char**)&src, &src_len, (char**)&dst, &dst_len);
    }

    if (0xFEFF == **((CARD16 **)to_buf)) {
	memmove(*to_buf, *to_buf + 2, *to_left - dst_len - 2);
	*to_left = (dst_len + 2);
    } else {
	*to_left = dst_len;
    }
done:
#endif /* USE_ICONV */
    return ret;
}

/* this function should be only used from composeIM. */
int
IMConvertFromUTF8ToUTF16(
    char *from_buf,
    size_t from_left,
    char **to_buf,
    size_t *to_left)
{
    char *encoding;
    int ret;
#ifdef USE_CSCONV
    encoding = "en_US.UTF-8";
#else
    int restore = False;
    encoding = "UTF-8";
    if (!skip_native_to_utf8_conv) {
	restore = True;
	skip_native_to_utf8_conv = True;
    }
#endif
    ret = ConvertToUTF16(encoding,
			 from_buf, from_left,
			 to_buf, to_left);
#ifndef USE_CSCONV
    if (restore)
	skip_native_to_utf8_conv = False;
#endif

    return ret;
}

#ifdef USE_ICONV
static iconv_t i_conv1_FromUTF16 = NULL;
static iconv_t i_conv2_FromUTF16 = NULL;
#endif

int
IMConvertFromUTF8(
    char *from_buf,
    size_t from_left,
    char **to_buf,
    size_t *to_left
)
{
    size_t ret = 0;
#ifdef USE_CSCONV
    static csconv_t csconv_cd = NULL;
#endif

#ifdef USE_ICONV
    if (skip_native_to_utf8_conv)
	return -1;

    if (i_conv2_FromUTF16 == (iconv_t)-1)
	/* already failed */
	return -1;

    if (i_conv2_FromUTF16 == NULL) {
	char *encoding = nl_langinfo(CODESET);

	if (!strcmp(encoding, "UTF-8")) {
	    skip_native_to_utf8_conv = True;
	    return -1;
	} else {
	    if ((i_conv2_FromUTF16 = iconv_open(encoding, "UTF-8"))
		== (iconv_t) - 1) {
		/* fallback */
		encoding = "646";
		if ((i_conv2_FromUTF16 = iconv_open(encoding, "UTF-8"))
		    == (iconv_t) - 1)
		    goto done;
	    }
	}
    }

    ret = iconv(i_conv2_FromUTF16, (const char**)&from_buf, &from_left,
		to_buf, to_left);
done:
#endif /* USE_ICONV */

    return ret;
}

#ifdef USE_ICONV
static iconv_t i_conv_ToUTF8 = NULL;
#endif

int
IMConvertToUTF8(
    char *from_buf,
    size_t from_left,
    char **to_buf,
    size_t *to_left
)
{
  size_t ret = 0;
#ifdef USE_CSCONV
  static csconv_t csconv_cd = NULL;
#endif

#ifdef USE_ICONV
  if (skip_native_to_utf8_conv) {
    strcpy (to_buf, from_buf);
    *to_left -= from_left;
    return ret;
  }

  if (i_conv_ToUTF8 == (iconv_t)-1)
    /* already failed */
    return -1;

  if (i_conv_ToUTF8 == NULL) {
    char *encoding = nl_langinfo(CODESET);

    if (!strcmp(encoding, "UTF-8")) {
      skip_native_to_utf8_conv = True;
      strcpy (to_buf, from_buf);
      *to_left -= from_left;
      return ret;
    } else {
      if ((i_conv_ToUTF8 = iconv_open("UTF-8", encoding))
	  == (iconv_t) - 1) {
	/* fallback */
	encoding = "646";
	if ((i_conv_ToUTF8 = iconv_open("UTF-8", encoding))
	    == (iconv_t) - 1)
	  goto done;
      }
    }
  }

  ret = iconv(i_conv_ToUTF8, (const char**)&from_buf, &from_left,
	      to_buf, to_left);
 done:
#endif /* USE_ICONV */

  return ret;
}

int
IIimpConvertToUTF16(
    char *from_buf,
    size_t from_left,
    char **to_buf,
    size_t *to_left
)
{
#ifdef USE_CSCONV
    if (current_locale == NULL){
	current_locale = (char*)strdup(setlocale(LC_CTYPE, NULL));
    }
    return ConvertToUTF16(current_locale,
			  from_buf, from_left,
			  to_buf, to_left);
#else
    char *encoding = nl_langinfo(CODESET);
    return ConvertToUTF16(encoding,
			  from_buf, from_left,
			  to_buf, to_left);
#endif
}

/* Used when transfering data from AUX to IM,
   do data conversion from UTF16 to EUC */
int
IIimpConvertFromUTF16(
    const char *from_buf,
    size_t from_left,
    char **to_buf,
    size_t * to_left
)
{
    size_t src_len, dst_len;
    char *src, *dst;
#ifndef USE_CSCONV
    const char *ip;
    size_t ileft;
    char *op;
    size_t oleft;
    char buffer[UTF16_STRLEN];	/* Fix me! */
#endif /* !USE_CSCONV */
    const size_t buf_len = UTF16_STRLEN;
    size_t ret = 0;

#ifdef USE_CSCONV
    static csconv_t csconv_cd = NULL;
#endif

#ifdef USE_CSCONV
    if (current_locale == NULL){
	current_locale = (char*)strdup(setlocale(LC_CTYPE, NULL));
    }
    do {
	if (((void *)(-1) == csc_handle) ||
	    ((csconv_t)(-1) == csconv_cd)) {
	    continue;
	}
	if (NULL == csc_handle) {
	    dlopen_csconv();
	    if ((void *)(-1) == csc_handle) {
		continue;
	    }
	}
	if (NULL == csconv_cd) {

	    csconv_cd = (csc_open_locale)(current_locale, "MultiByte", "UTF-16");
      
	    if ((csconv_t)(-1) == csconv_cd) {
		continue;
	    }
	}

	dst = *((char **)to_buf);

	ret = csc_conv(csconv_cd,
		       &from_buf, &from_left, &dst, to_left);
	return ret;
    } while (0);
#endif

#ifdef USE_ICONV
    if (i_conv1_FromUTF16 == (iconv_t)-1 ||
	i_conv2_FromUTF16 == (iconv_t)-1) {
	goto done;
    }

    if (i_conv2_FromUTF16 == NULL && !skip_native_to_utf8_conv) {
	char *encoding = nl_langinfo(CODESET);
	if (!strcmp(encoding, "UTF-8")) {
	    skip_native_to_utf8_conv = True;
	} else {
	    if ((i_conv2_FromUTF16 = iconv_open(encoding, "UTF-8"))
		== (iconv_t) - 1) {
		/* fallback */
		encoding = "646";
		if ((i_conv2_FromUTF16 = iconv_open(encoding, "UTF-8"))
		    == (iconv_t) - 1)
		    goto done;
	    }
	}
    }
    if (i_conv1_FromUTF16 == NULL) {
	if ((i_conv1_FromUTF16 = iconv_open("UTF-8", "UTF-16"))
	    == (iconv_t) - 1) {
	    goto done;
	}
    }

    if (skip_native_to_utf8_conv) {
	src = from_buf;
	src_len = from_left;	/* don't stop at '\0' in case of ascii */
	dst = *((char **) to_buf);

	ret = iconv(i_conv1_FromUTF16, (const char**)&src, &src_len,
		    (char**)&dst, to_left);
    } else {
	ip = (const char *)from_buf;
	ileft = from_left;

	op = *((char **)to_buf);
	oleft = *to_left;

	while ((0 < ileft) && (0 < oleft)) {
	    dst = buffer;
	    dst_len = buf_len;
	    ret = iconv(i_conv1_FromUTF16, &ip, &ileft, (char**)&dst, &dst_len);
	    if ((ret != 0) && (E2BIG != errno)) {
		goto done;
	    }

	    src = buffer;
	    src_len = buf_len - dst_len;

	    ret = iconv(i_conv2_FromUTF16, (const char**)&src, &src_len,
			&op, &oleft);
	    if (ret != 0) goto done;
	}

	*to_left = oleft;
    }
done:
#endif /* USE_ICONV */
    return ret;
}

static IIIMF_status
UTF16ToString(
    int len,
    const IIIMP_card16 *ucstr,
    Bool useUnicode,
    char **pstr
)
{
    if (len == 0) {
	*pstr = NULL;
	return IIIMF_STATUS_SUCCESS;
    }

    if (useUnicode) {
	IIIMP_card16 *ustr;

	ustr = (IIIMP_card16*) Xmalloc(sizeof(*ustr) * (len + 1));
	if (!ustr) return IIIMF_STATUS_MALLOC;
	memcpy(ustr, ucstr, sizeof(*ucstr) * len);
	ustr[len] = 0;
	*pstr = (char*) ustr;

	return IIIMF_STATUS_SUCCESS;
    } else {
	int dst_len, dst_len_orig, src_len;
	char *str;
	const char *src;

	str = (char*) Xmalloc(len * MB_CUR_MAX + 1);
	if (!str) return IIIMF_STATUS_MALLOC;
	dst_len = dst_len_orig = len * MB_CUR_MAX;
	src_len = len * sizeof(IIIMP_card16);

	src = (const char*) ucstr;
	IIimpConvertFromUTF16(src, src_len, &str, &dst_len);
	str[dst_len_orig - dst_len] = '\0';
	*pstr = str;
    }

    return IIIMF_STATUS_SUCCESS;
}

IIIMF_status
IIimpUTF16ToString(
    const IIIMP_card16 *ucstr,
    Bool useUnicode,
    char **pstr
)
{
    int len;

    for (len = 0; ucstr[len]; len++);
    return UTF16ToString(len, ucstr, useUnicode, pstr);
}

IIIMF_status
IIimpConvertToString(
    IIIMCF_text text,
    Bool useUnicode,
    char **pstr
)
{
    IIIMF_status st;
    int len;
    const IIIMP_card16 *ucstr;

    st = iiimcf_get_text_length(text, &len); 
    if (st != IIIMF_STATUS_SUCCESS) return st;
    if (len == 0) {
	*pstr = NULL;
	return IIIMF_STATUS_SUCCESS;
    }
    st = iiimcf_get_text_utf16string(text, &ucstr);
    if (st != IIIMF_STATUS_SUCCESS) return st;

    return UTF16ToString(len, ucstr, useUnicode, pstr);
}

IIIMF_status
IIimpConvertToXIMText(
    IIIMCF_text text,
    XIMText *pxt,
    IMFeedbackList **ppfeedbacks,
    Bool useUnicode
)
{
    IIIMF_status st;
    IIIMP_card16 ch;
    const IIIMP_card32 *pids, *pfbs;
    XIMFeedback *ximfb = NULL, *ximfb_top = NULL;
    IMFeedbackList *pfl = NULL, *pfl2 = NULL;
    IMFeedback *pfb, *pfb2;
    int i, len;
    int j, nfb;
    Bool has_feedback = False;

    memset(pxt, 0, sizeof(*pxt));
    st = iiimcf_get_text_length(text, &len); 
    if (st != IIIMF_STATUS_SUCCESS) goto error;
    if (len == 0) {
	if (ppfeedbacks) *ppfeedbacks = NULL;
	return IIIMF_STATUS_SUCCESS;
    }

    pfl = pfl2 = NULL;
    ximfb_top = ximfb = (XIMFeedback*) Xmalloc(sizeof(*ximfb) * len);
    memset(ximfb, 0, sizeof(*ximfb) * len);
    if (!ximfb) goto memory_error;
    if (ppfeedbacks) {
	pfl = pfl2 = (IMFeedbackList*) Xmalloc(sizeof(*pfl) * len);
	if (!pfl) goto memory_error;
	memset(pfl, 0, sizeof(*pfl) * len);
    }
    pxt->feedback = ximfb;
    pxt->length = len;

    for (i = 0; i < len; i++, ximfb++) {
	st = iiimcf_get_char_with_feedback(text, i, &ch, &nfb, &pids, &pfbs);
	if (st != IIIMF_STATUS_SUCCESS) goto error;
	if (pfl) {
	    int count_nfb = 0;
	    pfb = pfb2 = (IMFeedback*) Xmalloc(sizeof(IMFeedback) * nfb);
	    if (!pfb) goto memory_error;
	    for (j = 0; j < nfb; j++, pids++, pfbs++) {
		if (*pids == 0)
		  *ximfb = *pfbs;
		else {
		  has_feedback = True;
		  pfb2->type = *pids;
		  pfb2->value = *pfbs;
		  pfb2++;
		  count_nfb++;
		}
	    }
	    pfl2->count_feedbacks = count_nfb;
	    pfl2->feedbacks = pfb;
	    pfl2++;
	} else {
	    for (j = 0; j < nfb; j++, pids++) {
		if (*pids == 0) {
		    *ximfb = pfbs[j];
		    break;
		}
	    }
	}
    }

    st = IIimpConvertToString(text, useUnicode, &pxt->string.multi_byte);
    if (st != IIIMF_STATUS_SUCCESS) goto error;

    if (ppfeedbacks) {
      if (!has_feedback) {
	FreeFeedbackList(pfl, len);
        pfl = NULL;
      }
      *ppfeedbacks = pfl;
    }

    return IIIMF_STATUS_SUCCESS;

memory_error:
    st = IIIMF_STATUS_MALLOC;
error:
    if (ximfb_top) free(ximfb_top);
    if (pfl) FreeFeedbackList(pfl, len);
    if (pxt->string.multi_byte) free(pxt->string.multi_byte);

    return st;
}

#ifndef MAXINT
#define MAXINT          (~((unsigned int)1 << (8 * sizeof(int)) - 1))
#endif /* !MAXINT */

int
IIimpWcstoMbs(XimCommon im, wchar_t *from, int from_len,
	      char *to, int to_len, Status *state) {
  XlcConv conv = im->wtom_conv;
  int from_left;
  int to_left;
  int from_savelen;
  int to_savelen;
  int from_cnvlen;
  int to_cnvlen;
  wchar_t *from_buf;
  char	*to_buf, *tmp_buf;
  Status tmp_state;

  if (!state)
    state = &tmp_state;

  if (!conv || !from || !from_len) {
    *state = XLookupNone;
    return 0;
  }

  if (*from == (wchar_t)0) {
    to[0] = (char)0;
    return 0;
  }
  if (to && to_len) {
    from_left = from_len;
    to_left = to_len;
    from_cnvlen = 0;
    to_cnvlen = 0;
    for (;;) {
      from_savelen = from_left;
      to_savelen = to_left;
      from_buf = &from[from_cnvlen];
      to_buf = &to[to_cnvlen];
      if (_XlcConvert(conv, (XPointer *)&from_buf, &from_left,
		      (XPointer *)&to_buf, &to_left, NULL, 0) < 0) {
	*state = XLookupNone;
	return 0;
      }
      from_cnvlen += (from_savelen - from_left);
      to_cnvlen += (to_savelen - to_left);
      if (from_left == 0) {
	if (to_cnvlen > 0) {
	  *state = XLookupChars;
	} else {
	  *state = XLookupNone;
	}
	return to_cnvlen;
      }
      if (to_left == 0)
	break;
      /* Overflow : the to_left length is so small that it cannot
         accomodate the first mb character in the next conversion block */
      if (to_left < MB_CUR_MAX)
         break;
    }
  }

  from_left = from_len;
  from_cnvlen = 0;
  to_cnvlen = 0;
  /* BugId : 4253988. In case the from_buf is bigger than the to_buffer,
       we need to return the required buffer size and status as XBufferOverflow.
       We create a tmp buffer equals 8 times the from_len and do conversion.
  */
  to_left = (sizeof(wchar_t) * from_len) ;
  tmp_buf = (char *)malloc(to_left);
  for (;;) {
    from_savelen = from_left;
    from_buf = &from[from_cnvlen];
    to_savelen = to_left;
    to_buf = &tmp_buf[to_cnvlen];
    if (_XlcConvert(conv, (XPointer *)&from_buf, &from_left,
		    (XPointer *)&to_buf, &to_left, NULL, 0) < 0) {
      *state = XLookupNone;
      return 0;
    }
    from_cnvlen += (from_savelen - from_left);
    to_cnvlen += (to_savelen - to_left);
    if (from_left == 0) {
      if (to_cnvlen > 0)
	*state = XBufferOverflow;
      else
	*state = XLookupNone;
      break;
    }
  }
  free(tmp_buf);
  return to_cnvlen;
}

int
IIimpMbstoWcs(XimCommon im, char *from, int from_len,
	      wchar_t *to, int to_len, Status *state) {
  XlcConv conv = im->mtow_conv;
  int from_left;
  int to_left;
  int from_savelen;
  int to_savelen;
  int from_cnvlen;
  int to_cnvlen;
  char *from_buf;
  wchar_t *to_buf, *tmp_buf;
  Status tmp_state;

  if (!state)
    state = &tmp_state;

  /* Change to_len as to_len * sizeof(wchar_t)  - byte length */
  to_len = to_len * sizeof(wchar_t); 
  if (!conv || !from || !from_len) {
    *state = XLookupNone;
    return 0;
  }

  if (to && to_len) {
    from_left = from_len;
    to_left = to_len;
    from_cnvlen = 0;
    to_cnvlen = 0;
    for (;;) {
      from_savelen = from_left;
      to_savelen = to_left;
      from_buf = &from[from_cnvlen];
      to_buf = &to[to_cnvlen];
      if (_XlcConvert(conv, (XPointer *)&from_buf, &from_left,
		      (XPointer *)&to_buf, &to_left, NULL, 0) < 0) {
	*state = XLookupNone;
	return 0;
      }
      from_cnvlen += (from_savelen - from_left);
      to_cnvlen += (to_savelen - to_left);
      if (from_left == 0) {
	if (to_cnvlen > 0) {
	  *state = XLookupChars;
	} else {
	  *state = XLookupNone;
	}
	return to_cnvlen;
      }
      if (to_left == 0)
	break;
      /* Overflow : the to_left length is so small that it cannot
         accomodate the first mb character in the next conversion block */
      if (to_left < sizeof(wchar_t)) {
         break;
      }
    }
  }
  from_left = from_len;
  from_cnvlen = 0;
  to_cnvlen = 0;
  /* BugId : 4253988. In case the from_buf is bigger than the to_buffer,
       we need to return the required buffer size and status as XBufferOverflow.
       We create a tmp buffer equals 8 times the from_len and do conversion.
  */
  to_left = from_len ;
  tmp_buf = (wchar_t *)malloc(to_left * sizeof(wchar_t));
  for (;;) {
    from_savelen = from_left;
    from_buf = &from[from_cnvlen];
    to_savelen = to_left;
    to_buf = &tmp_buf[to_cnvlen];
    from_buf = &from[from_cnvlen];
    if (_XlcConvert(conv, (XPointer *)&from_buf, &from_left,
		    (XPointer *)&to_buf, &to_left, NULL, 0) < 0) {
      *state = XLookupNone;
      return 0;
    }
    from_cnvlen += (from_savelen - from_left);
    to_cnvlen += (to_savelen - to_left);
    if (from_left == 0) {
      if (to_cnvlen > 0)
	*state = XBufferOverflow;
      else
	*state = XLookupNone;
      break;
    }
  }
  free(tmp_buf);
  return to_cnvlen;
}

/* Local Variables: */
/* c-file-style: "iiim-project" */
/* End: */
