/*
 * Copyright (c) 1999 Sun Microsystems, Inc.
 * Copyright (c) 1999 Nihon Sun Microsystems K.K.
 * All rights reserved.
 */

/*
 * "$Id: ct_euc.c,v 1.1.1.1 2000/10/29 16:44:42 himi Exp $"
 */

#pragma ident	"@(#)ct_euc.c 1.4	99/05/20 SMI"


#include "config.h"


#if defined(CSC_CT_WCS)
#  define CSC_CT_EUC	1
#endif /* CSC_CT_WCS */

#if defined(CSC_CTEX_WCS)
#  define CSC_CT_WCS	1
#endif /* CSC_CTEX_WCS */

#if defined(CSC_CT_PCKW)
#  define CSC_CT_WCS	1
#  define CSC_CT_PCK	1
#endif /* CSC_CT_PCKW */

#if defined(CSC_CT_CNS)
#  define CSC_CT_EUC	1
#endif /* CSC_CT_CNS */

#if defined(CSC_CT_CNSW)
#  define CSC_CT_EUC	1
#  define CSC_CT_WCS	1
#endif /* CSC_CT_CNSW */

#if defined(CSC_CT_BIG5)
#  define CSC_CT_EUC	1
#endif /* CSC_CT_BIG5 */

#if defined(CSC_CT_BIG5W)
#  define CSC_CT_EUC	1
#  define CSC_CT_WCS	1
#endif /* CSC_CT_BIG5W */


#if defined(CSC_CT_EUC)
#if defined(CSC_CTEX_MBS) || defined(CSC_CT_PCK)
error {error} error
#endif /* CSC_CTEX_MBS || CSC_CT_PCK */
#else /* !CSC_CT_EUC */
#if defined(CSC_CTEX_MBS) && defined(CSC_CT_PCK)
error {error} error
#endif /* CSC_CTEX_MBS && CSC_CT_PCK */
#endif /* !CSC_CT_EUC */

#if defined(CSC_CT_EUCW)
#if defined(CSC_CTEX_WCS) || defined(CSC_CT_PCKW)
error {error} error
#endif /* CSC_CTEX_WCS || CSC_CT_PCKW */
#else /* !CSC_CT_EUCW */
#if defined(CSC_CTEX_WCS) && defined(CSC_CT_PCKW)
error {error} error
#endif /* CSC_CTEX_WCS && CSC_CT_PCKW */
#endif /* !CSC_CT_EUCW */


#if defined(CSC_CT_PCK) || defined(CSC_CT_PCKW)
#define CSC_USE_ICONV		1
#define CSC_CT_EUC		1
#endif /* CSC_CT_PCK || CSC_CT_PCKW */

#if defined(CSC_CT_PCKW)
#define CSC_CT_EUCW		1
#endif

#if defined(CSC_CT_BIG5) || defined(CSC_CT_BIG5W)
#define CSC_USE_ICONV		1
#define CSC_EUC_CT		1
#endif /* CSC_CT_BIG5 || CSC_CT_BIG5W */


#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#if defined(CSC_CT_WCS)
#include <locale.h>
#include <wchar.h>
#endif /* CSC_CT_WCS */
#include <errno.h>
#include <sys/types.h>
#if defined(CSC_USE_ICONV)
#include <sys/param.h>
#endif /* CSC_USE_ICONV */

#include "csconv.h"
#include "ct_map.h"
#include "euc_ct_set.h"

#if defined(CSC_USE_ICONV)
#include <iconv.h>
#include "csc_norm.h"
#endif /* CSC_USE_ICONV */


#define EUC_CS0		(0)
#define EUC_CS1		(1)
#define EUC_CS2		(2)
#define EUC_CS3		(3)
#define EUC_CS_NUM	(4)

#define SS2		(0x8E)
#define SS3		(0x8F)

#define	ERROR_BREAK(err)	errno = (err);		\
				ret_val = (size_t)(-1);	\
				break


#if defined(CSC_CT_WCS)
#  define MB_BUF_LEN	(32)
#if defined(CSC_CTEX_WCS)
#  define ct_euc_open	ctex_wcs_open
#  define ct_euc_close	ctex_wcs_close
#  define ct_euc_conv	ctex_wcs_conv
#else /* !CSC_CTEX_WCS */
#if defined(CSC_CT_PCKW)
#  define ct_euc_open	ct_pckw_open
#  define ct_euc_close	ct_pckw_close
#  define ct_euc_conv	ct_pckw_conv
#else /* !CSC_CT_PCKW */
#  define ct_euc_open	ct_eucw_open
#  define ct_euc_close	ct_eucw_close
#  define ct_euc_conv	ct_eucw_conv
#endif /* !CSC_CT_PCKW */
#endif /* !CSC_CTEX_WCS */
#else /* !CSC_CT_WCS */
#if defined(CSC_CTEX_MBS)
#  define ct_euc_open	ctex_mbs_open
#  define ct_euc_close	ctex_mbs_close
#  define ct_euc_conv	ctex_mbs_conv
#else /* !CSC_CTEX_MBS */
#if defined(CSC_CT_PCK)
#  define ct_euc_open	ct_pck_open
#  define ct_euc_close	ct_pck_close
#  define ct_euc_conv	ct_pck_conv
#else /* !CSC_CT_PCK */
#endif /* !CSC_CT_PCK */
#endif /* !CSC_CTEX_MBS */
#endif /* !CSC_CT_WCS */

#if defined(CSC_CT_CNS)
#  undef ct_euc_open
#  undef ct_euc_close
#  undef ct_euc_conv
#  define ct_euc_open	ct_cns_open
#  define ct_euc_close	ct_cns_close
#  define ct_euc_conv	ct_cns_conv
#else /* !CSC_CT_CNS */
#if defined(CSC_CT_CNSW)
#  undef ct_euc_open
#  undef ct_euc_close
#  undef ct_euc_conv
#  define ct_euc_open	ct_cnsw_open
#  define ct_euc_close	ct_cnsw_close
#  define ct_euc_conv	ct_cnsw_conv
#else /* !CSC_CT_CNSW */
#endif /* !CSC_CT_CNSW */
#endif /* !CSC_CT_CNS */

#if defined(CSC_CT_BIG5)
#  undef ct_euc_open
#  undef ct_euc_close
#  undef ct_euc_conv
#  define ct_euc_open	ct_big5_open
#  define ct_euc_close	ct_big5_close
#  define ct_euc_conv	ct_big5_conv
#else /* !CSC_CT_BIG5 */
#if defined(CSC_CT_BIG5W)
#  undef ct_euc_open
#  undef ct_euc_close
#  undef ct_euc_conv
#  define ct_euc_open	ct_big5w_open
#  define ct_euc_close	ct_big5w_close
#  define ct_euc_conv	ct_big5w_conv
#else /* !CSC_CT_BIG5W */
#endif /* !CSC_CT_BIG5W */
#endif /* !CSC_CT_BIG5 */

#if !defined(INTERIM_ENCODING)
#define INTERIM_ENCODING	"UTF-8"
#endif /* !INTERIM_ENCODING */
#if !defined(INTERIM_ENCODING_DELIM)
#define INTERIM_ENCODING_DELIM '%'
#endif /* !INTERIM_ENCODING_DELIM */


typedef struct _csc_state {
	const csc_euc_ct_set_t *	euc_ct_set;
#if defined(CSC_USE_ICONV)
	iconv_t				iconv_cd;
#endif /* CSC_USE_ICONV */
#if defined(CSC_CT_WCS)
#  if defined(CSC_USE_ICONV)
	unsigned char *			mb_buf_pre;
#  endif /* CSC_USE_ICONV */
	char *				locale;
	unsigned char *			mb_buf;
	size_t				mb_buf_len;
#endif /* CSC_CT_WCS */
} csc_state_t;


extern const csc_euc_ct_set_t	euc_ct_set[];

static int
extended_segment_conv(
	const csc_euc_ct_t *		ec,
	const unsigned char **		inbuf,
	size_t *			inbytesleft,
	unsigned char **		outbuf,
	size_t *			outbytesleft
);


csc_state_t *
ct_euc_open(
	const char *	locale,
	const char *	tocode,
	const char *	fromcode)
{
	csc_state_t *			csc_state;
	const csc_euc_ct_set_t *	ecs;
	int				i;
	int				ret_errno;
#if defined(CSC_USE_ICONV)
	iconv_t				iconv_cd;
	csc_norm_encoding_t *		norm_encoding;
#endif /* CSC_USE_ICONV */
#if defined(CSC_CT_WCS)
#  if defined(CSC_USE_ICONV)
	unsigned char *			mb_buf_pre;
#  endif /* CSC_USE_ICONV */
	unsigned char *			mb_buf;
	size_t				mb_buf_len;
	char *				locale_dup;
#endif /* CSC_CT_WCS */
#if defined(CSC_USE_ICONV)
	char				interim_encoding_buf[MAXPATHLEN];
	char *				interim_encoding;
	char *				p;
	int				n;
	int				len;
#endif /* 0 */

	csc_state = NULL;
	ecs = NULL;
#if defined(CSC_USE_ICONV)
	iconv_cd = (iconv_t)(-1);
#endif /* CSC_USE_ICONV */
#if defined(CSC_CT_WCS)
	locale_dup = NULL;
	mb_buf = NULL;
	mb_buf_len = 0;
#  if defined(CSC_USE_ICONV)
	mb_buf_pre = NULL;
#  endif /* CSC_USE_ICONV */
#endif /* CSC_CT_WCS */

#if defined(CSC_USE_ICONV)
	p = strchr(tocode, INTERIM_ENCODING_DELIM);
	if (NULL == p) {
		interim_encoding = INTERIM_ENCODING;
	} else {
		len = strlen(tocode);
		n = (p - tocode);

		if (((sizeof (interim_encoding_buf)) <= n) ||
		    (n <= 0) ||
		    (strlen(tocode) <= (n + 1))) {
			errno = EINVAL;
			return NULL;
		}

		interim_encoding = interim_encoding_buf;
		memcpy(interim_encoding_buf, tocode, n);
		interim_encoding_buf[n] = '\0';
		tocode = tocode + n + 1;
	}

	norm_encoding = NULL;

#endif /* CSC_USE_ICONV */

	do {
		for (i = 0; NULL != euc_ct_set[i].name; i++) {
			if (0 == strcmp(fromcode, euc_ct_set[i].name)) {
				ecs = (euc_ct_set + i);
				break;
			}
		}
		if (NULL == ecs) {
			ret_errno = EINVAL;
			continue;
		}

		csc_state = malloc(sizeof (*csc_state));
		if (NULL == csc_state) {
			ret_errno = ENOMEM;
			continue;
		}

#if defined(CSC_USE_ICONV)
		norm_encoding = csc_norm_encoding(CSC_SYSTEM_NAME,
						  locale,
						  interim_encoding,
						  tocode);
		if (NULL == norm_encoding) {
			iconv_cd = iconv_open(tocode, interim_encoding);
		} else {
			iconv_cd = iconv_open(norm_encoding->encoding2,
					      norm_encoding->encoding1);
		}

		if ((iconv_t)(-1) == iconv_cd) {
			ret_errno = EINVAL;
			continue;
		}

		csc_norm_free(norm_encoding);
		norm_encoding = NULL;
#endif /* CSC_USE_ICONV */

#if defined(CSC_CT_WCS)
		locale_dup = strdup(locale);
		if (NULL == locale_dup) {
			ret_errno = ENOMEM;
			continue;
		}

		if (MB_BUF_LEN < MB_CUR_MAX) {
			mb_buf = malloc(MB_CUR_MAX);
			if (NULL == mb_buf) {
				ret_errno = ENOMEM;
				continue;
			}
			mb_buf_len = MB_CUR_MAX;
#  if defined(CSC_USE_ICONV)
			mb_buf_pre = malloc(MB_CUR_MAX);
			if (NULL == mb_buf_pre) {
				free(mb_buf);
				ret_errno = ENOMEM;
				continue;
			}
#  endif /* CSC_USE_ICONV */
		} else {
			mb_buf = NULL;
			mb_buf_len = MB_BUF_LEN;
#  if defined(CSC_USE_ICONV)
			mb_buf_pre = NULL;
#  endif /* CSC_USE_ICONV */
		}
#endif /* CSC_CT_WCS */

		csc_state->euc_ct_set = ecs;
#if defined(CSC_USE_ICONV)
		csc_state->iconv_cd = iconv_cd;
#endif /* CSC_USE_ICONV */
#if defined(CSC_CT_WCS)
		csc_state->locale = locale_dup;
		csc_state->mb_buf = mb_buf;
		csc_state->mb_buf_len = mb_buf_len;
#  if defined(CSC_USE_ICONV)
		csc_state->mb_buf_pre = mb_buf_pre;
#  endif /* CSC_USE_ICONV */
#endif /* CSC_CT_WCS */

		return csc_state;

	} while(0);

	free(csc_state);
#if defined(CSC_USE_ICONV)
	csc_norm_free(norm_encoding);
	if ((iconv_t)(-1) != iconv_cd) {
		iconv_close(iconv_cd);
	}
#endif /* CSC_USE_ICONV */
#if defined(CSC_CT_WCS)
#  if defined(CSC_USE_ICONV)
	free(mb_buf_pre);
#  endif /* CSC_USE_ICONV */
	free(mb_buf);
	free(locale_dup);
#endif /* CSC_CT_WCS */
	errno = ret_errno;

	return NULL;
}

void
ct_euc_close(csc_state_t * csc_state)
{
	if (NULL == csc_state) {
		return;
	}

#if defined(CSC_USE_ICONV)
	if ((iconv_t)(-1) != csc_state->iconv_cd) {
		iconv_close(csc_state->iconv_cd);
	}
#endif /* CSC_USE_ICONV */
#if defined(CSC_CT_WCS)
#  if defined(CSC_USE_ICONV)
	free(csc_state->mb_buf_pre);
#  endif /* CSC_USE_ICONV */
	free(csc_state->mb_buf);
	free(csc_state->locale);
#endif /* !CSC_CT_WCS */
	free(csc_state);
	return;
}

size_t
ct_euc_conv(
	csc_state_t *	csc_state,
	const char **	inbuf,
	size_t *	inbytesleft,
	char **		outbuf,
	size_t *	outbytesleft)
{
	size_t				ret_val;
	const csc_euc_ct_set_t *	ecs;
	const csc_euc_ct_t *		ec;
	const unsigned char * 		ip;
	size_t				ileft;
	unsigned char *			op;
	size_t				oleft;
#if defined(CSC_CT_WCS)
	char *				p;
	unsigned char * 		owp;
	size_t				owleft;
#endif /* CSC_CT_WCS */
	int				i;
	int				cs_id;
	unsigned int			ss;
	int				ss_len;
	int				len;
	int				error_return;
#if defined(CSC_CT_WCS)
	unsigned char			mb_buf[MB_BUF_LEN];
	unsigned char *			mb;
	size_t				mb_buf_len;
	char *				locale;
#  if defined(CSC_USE_ICONV)
	unsigned char			mb_buf_pre[MB_BUF_LEN];
	unsigned char *			mb_pre;
	const unsigned char * 		ip2;
	size_t				ileft2;
	unsigned char *			op2;
	size_t				oleft2;
	size_t				ret_val2;
#  endif /* CSC_USE_ICONV */
#endif /* CSC_CT_WCS */
#if defined(CSC_USE_ICONV)
	unsigned char			interim_buf[1024];
	unsigned char *			interim;
	size_t				interim_len;
	int				interim_error;
	unsigned char *			ptr;
	int				n;
#endif /* CSC_USE_ICONV */
#if defined(CSC_CT_CNS) || defined(CSC_CT_CNSW)
	unsigned char			cns_plane;
#endif /* CSC_CT_CNS || CSC_CT_CNSW */

	ret_val = 0;
	error_return = 0;

	if ((NULL == inbuf) || (NULL == *inbuf)) {
		return ret_val;
	}

	ecs = csc_state->euc_ct_set;
	ec = NULL;
	cs_id = 0;
	ss = 0;
	ss_len = 0;

#if defined(CSC_CT_WCS)
	p = setlocale(LC_CTYPE, NULL);
	if (0 == strcmp(p, csc_state->locale)) {
		locale = NULL;
	} else {
		locale = strdup(p);
		if (NULL == locale) {
			errno = ENOMEM;
			return (size_t)(-1);
		}
		if (NULL == setlocale(LC_CTYPE, csc_state->locale)) {
			free(locale);
			errno = EBADF;
			return (size_t)(-1);
		}
	}
#endif /* CSC_CT_WCS */

#if defined(CSC_USE_ICONV)
	interim_len = *inbytesleft;

	if ((sizeof (interim_buf)) < interim_len) {
		interim = malloc(interim_len);
		if (NULL == interim) {
#if defined(CSC_CT_WCS)
			if (NULL != locale) {
				setlocale(LC_CTYPE, locale);
				free(locale);
			}
#endif /* CSC_CT_WCS */
			errno = ENOMEM;
			return (size_t)(-1);
		}
	} else {
		interim = interim_buf;
	}
#endif /* CSC_USE_ICONV */

	ip = (unsigned char *)(*inbuf);
	ileft = *inbytesleft;
#if defined(CSC_CT_WCS)
	owp = (unsigned char *)(*outbuf);
	owleft = *outbytesleft;
#else /* !CSC_CT_WCS */
#  if defined(CSC_USE_ICONV)
	op = interim;
	oleft = interim_len;
#  else /* !CSC_USE_ICONV */
	op = (unsigned char *)(*outbuf);
	oleft = *outbytesleft;
#  endif /* !CSC_USE_ICONV */
#endif /* !CSC_CT_WCS */

#if defined(CSC_CT_WCS)
	if (NULL == csc_state->mb_buf) {
		mb = mb_buf;
	} else {
		mb = csc_state->mb_buf;
	}
	mb_buf_len = csc_state->mb_buf_len;
#  if defined(CSC_USE_ICONV)
	if (NULL == csc_state->mb_buf_pre) {
		mb_pre = mb_buf_pre;
	} else {
		mb_pre = csc_state->mb_buf_pre;
	}
#  endif /* CSC_USE_ICONV */
#endif /* CSC_CT_WCS */

	while (0 < ileft) {
#if defined(CSC_CT_WCS)
#  if defined(CSC_USE_ICONV)
		op = mb_pre;
#  else /* !CSC_USE_ICONV */
		op = mb;
#  endif /* !CSC_USE_ICONV */
		oleft = mb_buf_len;
#endif /* !CSC_CT_WCS */

		if (0x1b != *(ip)) {
#if defined(CSC_CT_WCS)
			if (owleft < (sizeof (wchar_t))) {
				ERROR_BREAK(E2BIG);
			}
#endif /* CSC_CT_WCS */

			if (NULL == ec) {
				if (oleft <= 0) {
					ERROR_BREAK(E2BIG);
				}
				*(op++) = *(ip++);
				--ileft;
				--oleft;
#if defined(CSC_CT_WCS)
#  if defined(CSC_USE_ICONV)
				ip2 = mb_pre;
				ileft2 = 1;
				op2 = mb;
				oleft2 = mb_buf_len;
				ret_val2 == iconv(csc_state->iconv_cd,
						  (const char **)(&ip2),
						  &ileft2,
						  (char **)(&op2),
						  &oleft2);
				if ((size_t)(-1) == ret_val2) {
					continue;
				}
#  endif /* CSC_USE_ICONV */
				if ((-1) != mbtowc((wchar_t *)owp,
						   (const char *)mb,
						   mb_buf_len)) {
					owp += (sizeof (wchar_t));
					owleft -= (sizeof (wchar_t));
				}
#endif /* CSC_CT_WCS */
				continue;
			}

			if ((ileft < (ec->length)) ||
			    (oleft < (ec->length + ss_len))) {
				ERROR_BREAK(E2BIG);
			}

			if (0 != ss_len) {
				*(op++) = ss;
				--oleft;
#if defined(CSC_CT_CNS) || defined(CSC_CT_CNSW)
				*(op++) = cns_plane;
				--oleft;
#endif /* CSC_CT_CNS || CSC_CT_CNSW */
			}

			if (EUC_CS0 == cs_id) {
				for (i = 0; i < ec->length; i++) {
					*(op++) = *(ip++);
				}
			} else {
				for (i = 0; i < ec->length; i++) {
					*(op++) = (0x80 | *(ip++));
				}
			}

			ileft -= ec->length;
			oleft -= ec->length;

#if defined(CSC_CT_WCS)
#  if defined(CSC_USE_ICONV)
			ip2 = mb_pre;
			if (0 == ss) {
				ileft2 = ec->length;
			} else {
				ileft2 = (ec->length + 1);
			}
			op2 = mb;
			oleft2 = mb_buf_len;
			ret_val2 = iconv(csc_state->iconv_cd,
					 (const char **)(&ip2),
					 &ileft2,
					 (char **)(&op2),
					 &oleft2);
			if ((size_t)(-1) == ret_val2) {
				continue;
			}
#  endif /* CSC_USE_ICONV */
			if ((-1) != mbtowc((wchar_t *)owp,
					   (const char *)mb,
					   mb_buf_len)) {
				owp += (sizeof (wchar_t));
				owleft -= (sizeof (wchar_t));
			}
#endif /* CSC_CT_WCS */

			continue;
		}

		for (cs_id = 0; cs_id < EUC_CS_NUM; cs_id++) {
			if (NULL == ecs->cs[cs_id].name) {
				continue;
			}

			if (1 == ecs->cs[cs_id].extended_segment) {
				len = extended_segment_conv(ecs->cs + cs_id,
							    &ip, &ileft,
#if defined(CSC_CT_WCS)
							    &owp, &owleft
#else /* !CSC_CT_WCS */
							    &op, &oleft
#endif /* !CSC_CT_WCS */
					);
				if (len < 0) {
					error_return = errno;
					break;
				} else if (0 == len) {
					;
				} else {
					break;
				}
			}

			if (ileft < ecs->cs[cs_id].desig_len) {
				continue;
			}

			if (0 == memcmp(ip, ecs->cs[cs_id].desig,
					ecs->cs[cs_id].desig_len)) {
				ec = (ecs->cs + cs_id);
				ip += ecs->cs[cs_id].desig_len;
				ileft -= ecs->cs[cs_id].desig_len;
				break;
			} else {
				continue;
			}
		}

		if (0 != error_return) {
			ERROR_BREAK(error_return);
		}

		switch (cs_id) {
		case EUC_CS0:
		case EUC_CS1:
			ss = 0;
			ss_len = 0;
			break;
		case EUC_CS2:
			ss = SS2;
#if defined(CSC_CT_CNS) || defined(CSC_CT_CNSW)
			cns_plane = 0xA2;
			ss_len = 2;
#else /* !(CSC_CT_CNS || CSC_CT_CNSW) */
			ss_len = 1;
#endif /* !(CSC_CT_CNS || CSC_CT_CNSW) */
			break;
		case EUC_CS3:
#if defined(CSC_CT_CNS) || defined(CSC_CT_CNSW)
			cns_plane = 0xA3;
			ss = SS2;
			ss_len = 2;
#else /* !(CSC_CT_CNS || CSC_CT_CNSW) */
			ss = SS3;
			ss_len = 1;
#endif /* !(CSC_CT_CNS || CSC_CT_CNSW) */
			break;
		default:
			ec = (ecs->cs + EUC_CS0);
			cs_id = EUC_CS0;
			ss = 0;
			ss_len = 0;
			break;
		}
	}

#if defined(CSC_USE_ICONV) && !defined(CSC_CT_WCS)
	ip = interim;
	ileft = interim_len - oleft;
	op = (unsigned char *)(*outbuf);
	oleft = *outbytesleft;
	ret_val = iconv(csc_state->iconv_cd,
			(const char **)(&ip), &ileft,
			(char **)(&op), &oleft);
#endif /* CSC_USE_ICONV */

	*inbuf = (const char *)ip;
	*inbytesleft = ileft;
#if defined(CSC_CT_WCS)
	*outbuf = (char *)owp;
	*outbytesleft = owleft;
#else /* !CSC_CT_WCS */
	*outbuf = (char *)op;
	*outbytesleft = oleft;
#endif /* !CSC_CT_WCS */

#if defined(CSC_USE_ICONV)
	if (interim_buf != interim) {
		free(interim);
	}
#endif /* CSC_USE_ICONV */

#if defined(CSC_CT_WCS)
	if (NULL != locale) {
		setlocale(LC_CTYPE, locale);
		free(locale);
	}
#endif /* CSC_CT_WCS */

	return ret_val;
}


static int
extended_segment_conv(
	const csc_euc_ct_t *		ec,
	const unsigned char **		inbuf,
	size_t *			inbytesleft,
	unsigned char **		outbuf,
	size_t *			outbytesleft)
{
	size_t			ret_val;
	int			len;
	int			ret;
	const unsigned char *	ip;
	size_t			ileft;
	unsigned char *		op;
	size_t			oleft;
	const unsigned char *	desig;
	unsigned int		desig_len;
	unsigned int		high;
	unsigned int		low;
	const unsigned char	desig_unknown[] = {0x1b, 0x25, 0x2f};

	ip = *inbuf;
	ileft = *inbytesleft;

	if (NULL == ec) {
		if (ileft < 6) {
			return 0;
		}
		if ((desig_unknown[1] != *(ip + 1)) ||
		    (desig_unknown[2] != *(ip + 2)) ||
		    (*(ip + 3) < 0x30) || 
		    (0x34 < *(ip + 3))) {
			return 0;
		}

		len = (((*(ip + 4) & 0x007f) << 7) + (*(ip + 5) & 0x007f) + 6);
		ret = len;

		op = *outbuf;
		oleft = *outbytesleft;
		
		ip += 6;
		ileft -= 6;

		while ((0 < ileft) && (0 < len)) {
			if (0x02 == *ip) {
				break;
			}
			ip += 1;
			ileft -= 1;
			len -= 1;
		}

		while ((0 < ileft) && (0 < len)) {
			if (oleft <= 0) {
				errno = E2BIG;
				ret = (-1);
				break;
			}
			*(op++) = *(ip++);
			ileft -= 1;
			oleft -= 1;
			len -= 1;
		}

		*inbuf = ip;
		*inbytesleft = ileft;
		*outbuf = op;
		*outbytesleft = oleft;

		return ret;
	}

	if (ileft < (ec->desig_len - 1)) {
		return 0;
	}

	desig = ec->desig;
	desig_len = ec->desig_len;

	if ((*(desig + 1) != *(ip + 1)) ||
	    (*(desig + 2) != *(ip + 2)) ||
	    (*(desig + 3) != *(ip + 3))) {
		return 0;
	}

	if (0 != memcmp(ip + 6, desig + 6, desig_len - 6 - 1)) {
		return 0;
	}

	len = (((*(ip + 4) & 0x007f) << 7) + (*(ip + 5) & 0x007f) + 6);

	if (ileft < desig_len) {
		if ((desig_len - 1) != len) {
			errno = EINVAL;
			return -1;
		} else {
			ip += ileft;
			ileft = 0;
			len = 0;
		}
	} else if (ileft == desig_len) {
		if (0x02 == *(ip + ileft - 1)) {
			ip += ileft;
			ileft = 0;
			len = 0;
		} else {
			ip += (ileft - 1);
			ileft = 1;
			len = 1;
		}
	} else {
		if (0x02 == *(ip + desig_len - 1)) {
			ip += desig_len;
			ileft -= desig_len;
			len -= desig_len;
		} else {
			ip += (desig_len - 1);
			ileft -= (desig_len - 1);
			len -= (desig_len - 1);
		}
	}

	op = *outbuf;
	oleft = *outbytesleft;

	ret = len;

	while ((0 < ileft) && (0 < len)) {
		if (oleft <= 0) {
			errno = E2BIG;
			ret = (-1);
			break;
		}
		*(op++) = *(ip++);
		ileft -= 1;
		oleft -= 1;
		len -= 1;
	}

	*inbuf = ip;
	*inbytesleft = ileft;
	*outbuf = op;
	*outbytesleft = oleft;

	if (0 < ret) {
		return (ret - len);
	} else {
		return ret;
	}
}
