/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

/*
 * Kernel iconv code conversion module (kiconv_emea) for Europe, Middle East,
 * and South East Asia (PSARC/2007/173).
 */

#include <sys/types.h>
#include <sys/param.h>
#include <sys/sysmacros.h>
#include <sys/systm.h>
#include <sys/debug.h>
#include <sys/kmem.h>
#include <sys/cmn_err.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/ksynch.h>
#include <sys/modctl.h>
#include <sys/byteorder.h>
#include <sys/errno.h>
#include <sys/kiconv.h>
#include <sys/kiconv_emea1.h>
#include <sys/kiconv_emea2.h>


/*
 * The following macros indicate ids to the correct code conversion mapping
 * data tables to use. The actual tables are coming from <sys/kiconv_emea1.h>
 * and <sys/kiconv_emea2.h>. If you update the header files, then, you might
 * also need to update the table ids at below.
 *
 * The table for KICONV_TBLID_720 is a special case and should come from
 * a separate header file than others at <sys/kiconv_emea1.h> hence it has
 * an id that is rather unusual distinguishing itself from others. (And,
 * the ids much be of uint8_t.)
 */
#define	KICONV_TBLID_720		(0xFFU)
#define	KICONV_TBLID_RANGE1_START	KICONV_TBLID_720
#define	KICONV_TBLID_RANGE1_END		KICONV_TBLID_720

#define	KICONV_TBLID_737		(0)
#define	KICONV_TBLID_852		(1)
#define	KICONV_TBLID_857		(2)
#define	KICONV_TBLID_862		(3)
#define	KICONV_TBLID_866		(4)
#define	KICONV_TBLID_1250		(5)
#define	KICONV_TBLID_1251		(6)
#define	KICONV_TBLID_1253		(7)
#define	KICONV_TBLID_1254		(8)
#define	KICONV_TBLID_1255		(9)
#define	KICONV_TBLID_1256		(10)
#define	KICONV_TBLID_1257		(11)
#define	KICONV_TBLID_8859_2		(12)
#define	KICONV_TBLID_8859_3		(13)
#define	KICONV_TBLID_8859_4		(14)
#define	KICONV_TBLID_8859_5		(15)
#define	KICONV_TBLID_8859_6		(16)
#define	KICONV_TBLID_8859_7		(17)
#define	KICONV_TBLID_8859_8		(18)
#define	KICONV_TBLID_8859_9		(19)
#define	KICONV_TBLID_8859_10		(20)
#define	KICONV_TBLID_8859_11		(21)
#define	KICONV_TBLID_8859_13		(22)
#define	KICONV_TBLID_KOI8_R		(23)

#define	KICONV_MAX_MAPPING_TBLID	KICONV_TBLID_KOI8_R

/*
 * The following tables are coming from u8_textprep.c. We use them to
 * check on validity of UTF-8 characters and their bytes.
 */
extern const int8_t u8_number_of_bytes[];
extern const uint8_t u8_valid_min_2nd_byte[];
extern const uint8_t u8_valid_max_2nd_byte[];


/*
 * The following 25 open_to_xxxx() functions are kiconv_open functions for
 * the conversions from UTF-8 to xxxx single byte codesets.
 */
static void *
open_to_720()
{
	kiconv_state_t s;

	s = (kiconv_state_t)kmem_alloc(sizeof (kiconv_state_data_t), KM_SLEEP);
	s->id = KICONV_TBLID_720;
	s->bom_processed = 0;

	return ((void *)s);
}

static void *
open_to_737()
{
	kiconv_state_t s;

	s = (kiconv_state_t)kmem_alloc(sizeof (kiconv_state_data_t), KM_SLEEP);
	s->id = KICONV_TBLID_737;
	s->bom_processed = 0;

	return ((void *)s);
}

static void *
open_to_852()
{
	kiconv_state_t s;

	s = (kiconv_state_t)kmem_alloc(sizeof (kiconv_state_data_t), KM_SLEEP);
	s->id = KICONV_TBLID_852;
	s->bom_processed = 0;

	return ((void *)s);
}

static void *
open_to_857()
{
	kiconv_state_t s;

	s = (kiconv_state_t)kmem_alloc(sizeof (kiconv_state_data_t), KM_SLEEP);
	s->id = KICONV_TBLID_857;
	s->bom_processed = 0;

	return ((void *)s);
}

static void *
open_to_862()
{
	kiconv_state_t s;

	s = (kiconv_state_t)kmem_alloc(sizeof (kiconv_state_data_t), KM_SLEEP);
	s->id = KICONV_TBLID_862;
	s->bom_processed = 0;

	return ((void *)s);
}

static void *
open_to_866()
{
	kiconv_state_t s;

	s = (kiconv_state_t)kmem_alloc(sizeof (kiconv_state_data_t), KM_SLEEP);
	s->id = KICONV_TBLID_866;
	s->bom_processed = 0;

	return ((void *)s);
}

static void *
open_to_1250()
{
	kiconv_state_t s;

	s = (kiconv_state_t)kmem_alloc(sizeof (kiconv_state_data_t), KM_SLEEP);
	s->id = KICONV_TBLID_1250;
	s->bom_processed = 0;

	return ((void *)s);
}

static void *
open_to_1251()
{
	kiconv_state_t s;

	s = (kiconv_state_t)kmem_alloc(sizeof (kiconv_state_data_t), KM_SLEEP);
	s->id = KICONV_TBLID_1251;
	s->bom_processed = 0;

	return ((void *)s);
}

static void *
open_to_1253()
{
	kiconv_state_t s;

	s = (kiconv_state_t)kmem_alloc(sizeof (kiconv_state_data_t), KM_SLEEP);
	s->id = KICONV_TBLID_1253;
	s->bom_processed = 0;

	return ((void *)s);
}

static void *
open_to_1254()
{
	kiconv_state_t s;

	s = (kiconv_state_t)kmem_alloc(sizeof (kiconv_state_data_t), KM_SLEEP);
	s->id = KICONV_TBLID_1254;
	s->bom_processed = 0;

	return ((void *)s);
}

static void *
open_to_1255()
{
	kiconv_state_t s;

	s = (kiconv_state_t)kmem_alloc(sizeof (kiconv_state_data_t), KM_SLEEP);
	s->id = KICONV_TBLID_1255;
	s->bom_processed = 0;

	return ((void *)s);
}

static void *
open_to_1256()
{
	kiconv_state_t s;

	s = (kiconv_state_t)kmem_alloc(sizeof (kiconv_state_data_t), KM_SLEEP);
	s->id = KICONV_TBLID_1256;
	s->bom_processed = 0;

	return ((void *)s);
}

static void *
open_to_1257()
{
	kiconv_state_t s;

	s = (kiconv_state_t)kmem_alloc(sizeof (kiconv_state_data_t), KM_SLEEP);
	s->id = KICONV_TBLID_1257;
	s->bom_processed = 0;

	return ((void *)s);
}

static void *
open_to_88592()
{
	kiconv_state_t s;

	s = (kiconv_state_t)kmem_alloc(sizeof (kiconv_state_data_t), KM_SLEEP);
	s->id = KICONV_TBLID_8859_2;
	s->bom_processed = 0;

	return ((void *)s);
}

static void *
open_to_88593()
{
	kiconv_state_t s;

	s = (kiconv_state_t)kmem_alloc(sizeof (kiconv_state_data_t), KM_SLEEP);
	s->id = KICONV_TBLID_8859_3;
	s->bom_processed = 0;

	return ((void *)s);
}

static void *
open_to_88594()
{
	kiconv_state_t s;

	s = (kiconv_state_t)kmem_alloc(sizeof (kiconv_state_data_t), KM_SLEEP);
	s->id = KICONV_TBLID_8859_4;
	s->bom_processed = 0;

	return ((void *)s);
}

static void *
open_to_88595()
{
	kiconv_state_t s;

	s = (kiconv_state_t)kmem_alloc(sizeof (kiconv_state_data_t), KM_SLEEP);
	s->id = KICONV_TBLID_8859_5;
	s->bom_processed = 0;

	return ((void *)s);
}

static void *
open_to_88596()
{
	kiconv_state_t s;

	s = (kiconv_state_t)kmem_alloc(sizeof (kiconv_state_data_t), KM_SLEEP);
	s->id = KICONV_TBLID_8859_6;
	s->bom_processed = 0;

	return ((void *)s);
}

static void *
open_to_88597()
{
	kiconv_state_t s;

	s = (kiconv_state_t)kmem_alloc(sizeof (kiconv_state_data_t), KM_SLEEP);
	s->id = KICONV_TBLID_8859_7;
	s->bom_processed = 0;

	return ((void *)s);
}

static void *
open_to_88598()
{
	kiconv_state_t s;

	s = (kiconv_state_t)kmem_alloc(sizeof (kiconv_state_data_t), KM_SLEEP);
	s->id = KICONV_TBLID_8859_8;
	s->bom_processed = 0;

	return ((void *)s);
}

static void *
open_to_88599()
{
	kiconv_state_t s;

	s = (kiconv_state_t)kmem_alloc(sizeof (kiconv_state_data_t), KM_SLEEP);
	s->id = KICONV_TBLID_8859_9;
	s->bom_processed = 0;

	return ((void *)s);
}

static void *
open_to_885910()
{
	kiconv_state_t s;

	s = (kiconv_state_t)kmem_alloc(sizeof (kiconv_state_data_t), KM_SLEEP);
	s->id = KICONV_TBLID_8859_10;
	s->bom_processed = 0;

	return ((void *)s);
}

static void *
open_to_885911()
{
	kiconv_state_t s;

	s = (kiconv_state_t)kmem_alloc(sizeof (kiconv_state_data_t), KM_SLEEP);
	s->id = KICONV_TBLID_8859_11;
	s->bom_processed = 0;

	return ((void *)s);
}

static void *
open_to_885913()
{
	kiconv_state_t s;

	s = (kiconv_state_t)kmem_alloc(sizeof (kiconv_state_data_t), KM_SLEEP);
	s->id = KICONV_TBLID_8859_13;
	s->bom_processed = 0;

	return ((void *)s);
}

static void *
open_to_koi8r()
{
	kiconv_state_t s;

	s = (kiconv_state_t)kmem_alloc(sizeof (kiconv_state_data_t), KM_SLEEP);
	s->id = KICONV_TBLID_KOI8_R;
	s->bom_processed = 0;

	return ((void *)s);
}

/*
 * The following 25 open_fr_xxxx() functions are kiconv_open functions for
 * the conversions from xxxx single byte codeset to UTF-8.
 */
static void *
open_fr_720()
{
	return ((void *)KICONV_TBLID_720);
}

static void *
open_fr_737()
{
	return ((void *)KICONV_TBLID_737);
}

static void *
open_fr_852()
{
	return ((void *)KICONV_TBLID_852);
}

static void *
open_fr_857()
{
	return ((void *)KICONV_TBLID_857);
}

static void *
open_fr_862()
{
	return ((void *)KICONV_TBLID_862);
}

static void *
open_fr_866()
{
	return ((void *)KICONV_TBLID_866);
}

static void *
open_fr_1250()
{
	return ((void *)KICONV_TBLID_1250);
}

static void *
open_fr_1251()
{
	return ((void *)KICONV_TBLID_1251);
}

static void *
open_fr_1253()
{
	return ((void *)KICONV_TBLID_1253);
}

static void *
open_fr_1254()
{
	return ((void *)KICONV_TBLID_1254);
}

static void *
open_fr_1255()
{
	return ((void *)KICONV_TBLID_1255);
}

static void *
open_fr_1256()
{
	return ((void *)KICONV_TBLID_1256);
}

static void *
open_fr_1257()
{
	return ((void *)KICONV_TBLID_1257);
}

static void *
open_fr_88592()
{
	return ((void *)KICONV_TBLID_8859_2);
}

static void *
open_fr_88593()
{
	return ((void *)KICONV_TBLID_8859_3);
}

static void *
open_fr_88594()
{
	return ((void *)KICONV_TBLID_8859_4);
}

static void *
open_fr_88595()
{
	return ((void *)KICONV_TBLID_8859_5);
}

static void *
open_fr_88596()
{
	return ((void *)KICONV_TBLID_8859_6);
}

static void *
open_fr_88597()
{
	return ((void *)KICONV_TBLID_8859_7);
}

static void *
open_fr_88598()
{
	return ((void *)KICONV_TBLID_8859_8);
}

static void *
open_fr_88599()
{
	return ((void *)KICONV_TBLID_8859_9);
}

static void *
open_fr_885910()
{
	return ((void *)KICONV_TBLID_8859_10);
}

static void *
open_fr_885911()
{
	return ((void *)KICONV_TBLID_8859_11);
}

static void *
open_fr_885913()
{
	return ((void *)KICONV_TBLID_8859_13);
}

static void *
open_fr_koi8r()
{
	return ((void *)KICONV_TBLID_KOI8_R);
}

/*
 * The following is the common kiconv_close function for the conversions from
 * UTF-8 to single byte codesets.
 */
static int
close_to_sb(void *s)
{
	if (! s || s == (void *)-1)
		return (EBADF);

	kmem_free(s, sizeof (kiconv_state_data_t));

	return (0);
}

/*
 * The following is the common kiconv_close function for the conversions from
 * single byte codesets to UTF-8.
 */
static int
close_fr_sb(void *s)
{
	if ((ulong_t)s > KICONV_MAX_MAPPING_TBLID &&
	    ((ulong_t)s < KICONV_TBLID_RANGE1_START ||
	    (ulong_t)s > KICONV_TBLID_RANGE1_END))
		return (EBADF);

	return (0);
}

/*
 * The following is the common kiconv function for the conversions from
 * UTF-8 to single byte codesets. (This may look a lot similar to
 * kiconvstr_to_sb() but they do have different features to cover and
 * it's not really worth to try to merge them into a single function since
 * you'll have to add performance penalty for both per each character
 * conversion as you will have to figure out if this is kiconv_to_sb() or
 * kiconvstr_to_sb().)
 */
static size_t
kiconv_to_sb(void *kcd, char **inbuf, size_t *inbytesleft, char **outbuf,
	size_t *outbytesleft, int *errno)
{
	kiconv_to_sb_tbl_comp_t *tbl;
	size_t id;
	size_t ret_val;
	uchar_t *ib;
	uchar_t *oldib;
	uchar_t *ob;
	uchar_t *ibtail;
	uchar_t *obtail;
	uint32_t u8;
	size_t i;
	size_t l;
	size_t h;
	size_t init_h;
	int8_t sz;
	boolean_t second;

	/* Check on the kiconv code conversion descriptor. */
	if (! kcd || kcd == (void *)-1) {
		*errno = EBADF;
		return ((size_t)-1);
	}

	/* Get the table id and check on it. */
	id = ((kiconv_state_t)kcd)->id;
	if (id > KICONV_MAX_MAPPING_TBLID &&
	    (id < KICONV_TBLID_RANGE1_START || id > KICONV_TBLID_RANGE1_END)) {
		*errno = EBADF;
		return ((size_t)-1);
	}

	/* If this is a state reset request, process and return. */
	if (! inbuf || ! (*inbuf)) {
		((kiconv_state_t)kcd)->bom_processed = 0;
		return ((size_t)0);
	}

	ret_val = 0;
	ib = (uchar_t *)*inbuf;
	ob = (uchar_t *)*outbuf;
	ibtail = ib + *inbytesleft;
	obtail = ob + *outbytesleft;

	/*
	 * Get the table we want to use and also calculate the "init_h"
	 * which is the initial high index for the binary search that we will
	 * use. While the table sizes are all the same at the moment, to be
	 * ready for future cases where tables could be in different sizes,
	 * we separately calculate the init_h at here.
	 */
	if (id == KICONV_TBLID_720) {
		tbl = (kiconv_to_sb_tbl_comp_t *)u8_to_cp720_tbl;
		init_h = sizeof (u8_to_cp720_tbl);
	} else {
		tbl = (kiconv_to_sb_tbl_comp_t *)to_sb_tbl[id];
		init_h = sizeof (to_sb_tbl[id]);
	}
	init_h = init_h / sizeof (kiconv_to_sb_tbl_comp_t) - 1;

	/*
	 * If we haven't checked on the UTF-8 signature BOM character in
	 * the beginning of the conversion data stream, we check it and if
	 * find one, we skip it since we have no use for it.
	 */
	if (((kiconv_state_t)kcd)->bom_processed == 0 && (ibtail - ib) >= 3 &&
	    *ib == 0xef && *(ib + 1) == 0xbb && *(ib + 2) == 0xbf)
			ib += 3;
	((kiconv_state_t)kcd)->bom_processed = 1;

	while (ib < ibtail) {
		sz = u8_number_of_bytes[*ib];
		if (sz <= 0) {
			*errno = EILSEQ;
			ret_val = (size_t)-1;
			break;
		}

		/*
		 * If there is no room to write at the output buffer,
		 * we issue E2BIG and let the caller knows about it.
		 */
		if (ob >= obtail) {
			*errno = E2BIG;
			ret_val = (size_t)-1;
			break;
		}

		/*
		 * If it is a 7-bit ASCII character, we don't need to
		 * process further and we just copy the character over.
		 *
		 * If not, we collect the character bytes up to four bytes,
		 * validate the bytes, and binary search for the corresponding
		 * single byte codeset character byte. If we find it from
		 * the mapping table, we put that into the output buffer;
		 * otherwise, we put a replacement character instead as
		 * a non-identical conversion.
		 */
		if (sz == 1) {
			*ob++ = *ib++;
			continue;
		}

		/*
		 * Issue EINVAL if the last character at the input buffer
		 * is an incomplete character missing a byte or more.
		 */
		if ((ibtail - ib) < sz) {
			*errno = EINVAL;
			ret_val = (size_t)-1;
			break;
		}

		/*
		 * We collect UTF-8 character bytes and at the same time,
		 * check on if the bytes are valid bytes or not. This follows
		 * the latest UTF-8 byte representation.
		 */
		oldib = ib;
		u8 = *ib++;
		second = B_TRUE;
		for (i = 1; i < sz; i++) {
			if (second) {
				if (*ib < u8_valid_min_2nd_byte[u8] ||
				    *ib > u8_valid_max_2nd_byte[u8]) {
					*errno = EILSEQ;
					ret_val = (size_t)-1;
					ib = oldib;
					goto TO_SB_ILLEGAL_CHAR_ERR;
				}
				second = B_FALSE;
			} else if (*ib < 0x80 || *ib > 0xbf) {
				*errno = EILSEQ;
				ret_val = (size_t)-1;
				ib = oldib;
				goto TO_SB_ILLEGAL_CHAR_ERR;
			}
			u8 = (u8 << 8) | ((uint32_t)*ib);
			ib++;
		}

		i = l = 0;
		h = init_h;
		while (l <= h) {
			i = (l + h) / 2;
			if (tbl[i].u8 == u8)
				break;
			else if (tbl[i].u8 < u8)
				l = i + 1;
			else
				h = i - 1;
		}

		if (tbl[i].u8 == u8) {
			*ob++ = tbl[i].sb;
		} else {
			/*
			 * What this means is that we encountered
			 * a non-identical conversion. In other words,
			 * input buffer contains a valid character in
			 * the fromcode but the tocode doesn't have
			 * any character that can be mapped to.
			 *
			 * In this case, we insert an ASCII replacement
			 * character instead at the output buffer and
			 * count such non-identical conversions by
			 * increasing the ret_val.
			 *
			 * If the return value of the function is bigger
			 * than zero, that means we had such non-identical
			 * conversion(s).
			 */
			*ob++ = KICONV_ASCII_REPLACEMENT_CHAR;
			ret_val++;
		}
	}

TO_SB_ILLEGAL_CHAR_ERR:
	*inbuf = (char *)ib;
	*inbytesleft = ibtail - ib;
	*outbuf = (char *)ob;
	*outbytesleft = obtail - ob;

	return (ret_val);
}

/*
 * The following is the common kiconv function for the conversions from
 * single byte codesets to UTf-8.
 */
static size_t
kiconv_fr_sb(void *kcd, char **inbuf, size_t *inbytesleft, char **outbuf,
	size_t *outbytesleft, int *errno)
{
	kiconv_to_utf8_tbl_comp_t *tbl;
	size_t ret_val;
	uchar_t *ib;
	uchar_t *ob;
	uchar_t *ibtail;
	uchar_t *obtail;
	size_t i;
	size_t k;
	int8_t sz;

	/* Validate the kiconv code conversion descriptor. */
	if ((ulong_t)kcd > KICONV_MAX_MAPPING_TBLID &&
	    ((ulong_t)kcd < KICONV_TBLID_RANGE1_START ||
	    (ulong_t)kcd > KICONV_TBLID_RANGE1_END)) {
		*errno = EBADF;
		return ((size_t)-1);
	}

	/*
	 * If this is a state reset request, there is nothing to do and so
	 * we just return.
	 */
	if (! inbuf || ! (*inbuf))
		return ((size_t)0);

	ret_val = 0;
	ib = (uchar_t *)*inbuf;
	ob = (uchar_t *)*outbuf;
	ibtail = ib + *inbytesleft;
	obtail = ob + *outbytesleft;

	tbl = ((ulong_t)kcd == KICONV_TBLID_720) ?
	    (kiconv_to_utf8_tbl_comp_t *)cp720_to_u8_tbl :
	    (kiconv_to_utf8_tbl_comp_t *)to_u8_tbl[(ulong_t)kcd];

	while (ib < ibtail) {
		/*
		 * If this is a 7-bit ASCII character, we just copy over and
		 * that's all we need to do for this character.
		 */
		if (*ib < 0x80) {
			if (ob >= obtail) {
				*errno = E2BIG;
				ret_val = (size_t)-1;
				break;
			}

			*ob++ = *ib++;
			continue;
		}

		/*
		 * Otherwise, we get the corresponding UTF-8 character bytes
		 * from the mapping table and copy them over.
		 *
		 * We don't need to worry about if the UTF-8 character bytes
		 * at the mapping tables are valid or not since they are good.
		 */
		k = *ib - 0x80;
		sz = u8_number_of_bytes[tbl[k].u8[0]];

		/*
		 * If (sz <= 0), that means the character in the input buffer
		 * is an illegal character possibly unassigned or non-character
		 * at the fromcode single byte codeset.
		 */
		if (sz <= 0) {
			*errno = EILSEQ;
			ret_val = (size_t)-1;
			break;
		}

		if ((obtail - ob) < sz) {
			*errno = E2BIG;
			ret_val = (size_t)-1;
			break;
		}

		for (i = 0; i < sz; i++)
			*ob++ = tbl[k].u8[i];

		ib++;
	}

	*inbuf = (char *)ib;
	*inbytesleft = ibtail - ib;
	*outbuf = (char *)ob;
	*outbytesleft = obtail - ob;

	return (ret_val);
}

/*
 * The following is the common kiconvstr function for the conversions from
 * UTF-8 to single byte codeset.
 */
static size_t
kiconvstr_to_sb(size_t id, uchar_t *ib, size_t *inlen, uchar_t *ob,
	size_t *outlen, int flag, int *errno)
{
	kiconv_to_sb_tbl_comp_t *tbl;
	size_t ret_val;
	uchar_t *oldib;
	uchar_t *ibtail;
	uchar_t *obtail;
	uint32_t u8;
	size_t i;
	size_t l;
	size_t h;
	size_t init_h;
	int8_t sz;
	boolean_t second;
	boolean_t do_not_ignore_null;

	/* Let's double check on the table id. */
	if (id > KICONV_MAX_MAPPING_TBLID &&
	    (id < KICONV_TBLID_RANGE1_START || id > KICONV_TBLID_RANGE1_END)) {
		*errno = EBADF;
		return ((size_t)-1);
	}

	ret_val = 0;
	ibtail = ib + *inlen;
	obtail = ob + *outlen;
	do_not_ignore_null = ((flag & KICONV_IGNORE_NULL) == 0);

	if (id == KICONV_TBLID_720) {
		tbl = (kiconv_to_sb_tbl_comp_t *)u8_to_cp720_tbl;
		init_h = sizeof (u8_to_cp720_tbl);
	} else {
		tbl = (kiconv_to_sb_tbl_comp_t *)to_sb_tbl[id];
		init_h = sizeof (to_sb_tbl[id]);
	}
	init_h = init_h / sizeof (kiconv_to_sb_tbl_comp_t) - 1;

	/* Skip any UTF-8 signature BOM character in the beginning. */
	if ((ibtail - ib) >= 3 && *ib == 0xef && *(ib + 1) == 0xbb &&
	    *(ib + 2) == 0xbf)
			ib += 3;

	/*
	 * Basically this is pretty much the same as kiconv_to_sb() except
	 * that we are now accepting two flag values and doing the processing
	 * accordingly.
	 */
	while (ib < ibtail) {
		sz = u8_number_of_bytes[*ib];
		if (sz <= 0) {
			if (flag & KICONV_REPLACE_INVALID) {
				if (ob >= obtail) {
					*errno = E2BIG;
					ret_val = (size_t)-1;
					break;
				}

				ib++;
				goto STR_TO_SB_REPLACE_INVALID;
			}

			*errno = EILSEQ;
			ret_val = (size_t)-1;
			break;
		}

		if (*ib == '\0' && do_not_ignore_null)
			break;

		if (ob >= obtail) {
			*errno = E2BIG;
			ret_val = (size_t)-1;
			break;
		}

		if (sz == 1) {
			*ob++ = *ib++;
			continue;
		}

		if ((ibtail - ib) < sz) {
			if (flag & KICONV_REPLACE_INVALID) {
				ib = ibtail;
				goto STR_TO_SB_REPLACE_INVALID;
			}

			*errno = EINVAL;
			ret_val = (size_t)-1;
			break;
		}

		oldib = ib;
		u8 = *ib++;
		second = B_TRUE;
		for (i = 1; i < sz; i++) {
			if (second) {
				if (*ib < u8_valid_min_2nd_byte[u8] ||
				    *ib > u8_valid_max_2nd_byte[u8]) {
					if (flag & KICONV_REPLACE_INVALID) {
						ib = oldib + sz;
						goto STR_TO_SB_REPLACE_INVALID;
					}

					*errno = EILSEQ;
					ret_val = (size_t)-1;
					ib = oldib;
					goto STR_TO_SB_ILLEGAL_CHAR_ERR;
				}
				second = B_FALSE;
			} else if (*ib < 0x80 || *ib > 0xbf) {
				if (flag & KICONV_REPLACE_INVALID) {
					ib = oldib + sz;
					goto STR_TO_SB_REPLACE_INVALID;
				}

				*errno = EILSEQ;
				ret_val = (size_t)-1;
				ib = oldib;
				goto STR_TO_SB_ILLEGAL_CHAR_ERR;
			}
			u8 = (u8 << 8) | ((uint32_t)*ib);
			ib++;
		}

		i = l = 0;
		h = init_h;
		while (l <= h) {
			i = (l + h) / 2;
			if (tbl[i].u8 == u8)
				break;
			else if (tbl[i].u8 < u8)
				l = i + 1;
			else
				h = i - 1;
		}

		if (tbl[i].u8 == u8) {
			*ob++ = tbl[i].sb;
		} else {
STR_TO_SB_REPLACE_INVALID:
			*ob++ = KICONV_ASCII_REPLACEMENT_CHAR;
			ret_val++;
		}
	}

STR_TO_SB_ILLEGAL_CHAR_ERR:
	*inlen = ibtail - ib;
	*outlen = obtail - ob;

	return (ret_val);
}

/*
 * The following 25 functions are the real entry points that will be
 * given to the kiconv framework at the genunix.
 */
static size_t
kiconvstr_to_720(char *inarray, size_t *inlen, char *outarray,
	size_t *outlen, int flag, int *errno)
{
	return (kiconvstr_to_sb(KICONV_TBLID_720, (uchar_t *)inarray,
	    inlen, (uchar_t *)outarray, outlen, flag, errno));
}

static size_t
kiconvstr_to_737(char *inarray, size_t *inlen, char *outarray,
	size_t *outlen, int flag, int *errno)
{
	return (kiconvstr_to_sb(KICONV_TBLID_737, (uchar_t *)inarray,
	    inlen, (uchar_t *)outarray, outlen, flag, errno));
}

static size_t
kiconvstr_to_852(char *inarray, size_t *inlen, char *outarray,
	size_t *outlen, int flag, int *errno)
{
	return (kiconvstr_to_sb(KICONV_TBLID_852, (uchar_t *)inarray,
	    inlen, (uchar_t *)outarray, outlen, flag, errno));
}

static size_t
kiconvstr_to_857(char *inarray, size_t *inlen, char *outarray,
	size_t *outlen, int flag, int *errno)
{
	return (kiconvstr_to_sb(KICONV_TBLID_857, (uchar_t *)inarray,
	    inlen, (uchar_t *)outarray, outlen, flag, errno));
}

static size_t
kiconvstr_to_862(char *inarray, size_t *inlen, char *outarray,
	size_t *outlen, int flag, int *errno)
{
	return (kiconvstr_to_sb(KICONV_TBLID_862, (uchar_t *)inarray,
	    inlen, (uchar_t *)outarray, outlen, flag, errno));
}

static size_t
kiconvstr_to_866(char *inarray, size_t *inlen, char *outarray,
	size_t *outlen, int flag, int *errno)
{
	return (kiconvstr_to_sb(KICONV_TBLID_866, (uchar_t *)inarray,
	    inlen, (uchar_t *)outarray, outlen, flag, errno));
}

static size_t
kiconvstr_to_1250(char *inarray, size_t *inlen, char *outarray,
	size_t *outlen, int flag, int *errno)
{
	return (kiconvstr_to_sb(KICONV_TBLID_1250, (uchar_t *)inarray,
	    inlen, (uchar_t *)outarray, outlen, flag, errno));
}

static size_t
kiconvstr_to_1251(char *inarray, size_t *inlen, char *outarray,
	size_t *outlen, int flag, int *errno)
{
	return (kiconvstr_to_sb(KICONV_TBLID_1251, (uchar_t *)inarray,
	    inlen, (uchar_t *)outarray, outlen, flag, errno));
}

static size_t
kiconvstr_to_1253(char *inarray, size_t *inlen, char *outarray,
	size_t *outlen, int flag, int *errno)
{
	return (kiconvstr_to_sb(KICONV_TBLID_1253, (uchar_t *)inarray,
	    inlen, (uchar_t *)outarray, outlen, flag, errno));
}

static size_t
kiconvstr_to_1254(char *inarray, size_t *inlen, char *outarray,
	size_t *outlen, int flag, int *errno)
{
	return (kiconvstr_to_sb(KICONV_TBLID_1254, (uchar_t *)inarray,
	    inlen, (uchar_t *)outarray, outlen, flag, errno));
}

static size_t
kiconvstr_to_1255(char *inarray, size_t *inlen, char *outarray,
	size_t *outlen, int flag, int *errno)
{
	return (kiconvstr_to_sb(KICONV_TBLID_1255, (uchar_t *)inarray,
	    inlen, (uchar_t *)outarray, outlen, flag, errno));
}

static size_t
kiconvstr_to_1256(char *inarray, size_t *inlen, char *outarray,
	size_t *outlen, int flag, int *errno)
{
	return (kiconvstr_to_sb(KICONV_TBLID_1256, (uchar_t *)inarray,
	    inlen, (uchar_t *)outarray, outlen, flag, errno));
}

static size_t
kiconvstr_to_1257(char *inarray, size_t *inlen, char *outarray,
	size_t *outlen, int flag, int *errno)
{
	return (kiconvstr_to_sb(KICONV_TBLID_1257, (uchar_t *)inarray,
	    inlen, (uchar_t *)outarray, outlen, flag, errno));
}

static size_t
kiconvstr_to_88592(char *inarray, size_t *inlen, char *outarray,
	size_t *outlen, int flag, int *errno)
{
	return (kiconvstr_to_sb(KICONV_TBLID_8859_2, (uchar_t *)inarray,
	    inlen, (uchar_t *)outarray, outlen, flag, errno));
}

static size_t
kiconvstr_to_88593(char *inarray, size_t *inlen, char *outarray,
	size_t *outlen, int flag, int *errno)
{
	return (kiconvstr_to_sb(KICONV_TBLID_8859_3, (uchar_t *)inarray,
	    inlen, (uchar_t *)outarray, outlen, flag, errno));
}

static size_t
kiconvstr_to_88594(char *inarray, size_t *inlen, char *outarray,
	size_t *outlen, int flag, int *errno)
{
	return (kiconvstr_to_sb(KICONV_TBLID_8859_4, (uchar_t *)inarray,
	    inlen, (uchar_t *)outarray, outlen, flag, errno));
}

static size_t
kiconvstr_to_88595(char *inarray, size_t *inlen, char *outarray,
	size_t *outlen, int flag, int *errno)
{
	return (kiconvstr_to_sb(KICONV_TBLID_8859_5, (uchar_t *)inarray,
	    inlen, (uchar_t *)outarray, outlen, flag, errno));
}

static size_t
kiconvstr_to_88596(char *inarray, size_t *inlen, char *outarray,
	size_t *outlen, int flag, int *errno)
{
	return (kiconvstr_to_sb(KICONV_TBLID_8859_6, (uchar_t *)inarray,
	    inlen, (uchar_t *)outarray, outlen, flag, errno));
}

static size_t
kiconvstr_to_88597(char *inarray, size_t *inlen, char *outarray,
	size_t *outlen, int flag, int *errno)
{
	return (kiconvstr_to_sb(KICONV_TBLID_8859_7, (uchar_t *)inarray,
	    inlen, (uchar_t *)outarray, outlen, flag, errno));
}

static size_t
kiconvstr_to_88598(char *inarray, size_t *inlen, char *outarray,
	size_t *outlen, int flag, int *errno)
{
	return (kiconvstr_to_sb(KICONV_TBLID_8859_8, (uchar_t *)inarray,
	    inlen, (uchar_t *)outarray, outlen, flag, errno));
}

static size_t
kiconvstr_to_88599(char *inarray, size_t *inlen, char *outarray,
	size_t *outlen, int flag, int *errno)
{
	return (kiconvstr_to_sb(KICONV_TBLID_8859_9, (uchar_t *)inarray,
	    inlen, (uchar_t *)outarray, outlen, flag, errno));
}

static size_t
kiconvstr_to_885910(char *inarray, size_t *inlen, char *outarray,
	size_t *outlen, int flag, int *errno)
{
	return (kiconvstr_to_sb(KICONV_TBLID_8859_10, (uchar_t *)inarray,
	    inlen, (uchar_t *)outarray, outlen, flag, errno));
}

static size_t
kiconvstr_to_885911(char *inarray, size_t *inlen, char *outarray,
	size_t *outlen, int flag, int *errno)
{
	return (kiconvstr_to_sb(KICONV_TBLID_8859_11, (uchar_t *)inarray,
	    inlen, (uchar_t *)outarray, outlen, flag, errno));
}

static size_t
kiconvstr_to_885913(char *inarray, size_t *inlen, char *outarray,
	size_t *outlen, int flag, int *errno)
{
	return (kiconvstr_to_sb(KICONV_TBLID_8859_13, (uchar_t *)inarray,
	    inlen, (uchar_t *)outarray, outlen, flag, errno));
}

static size_t
kiconvstr_to_koi8r(char *inarray, size_t *inlen, char *outarray,
	size_t *outlen, int flag, int *errno)
{
	return (kiconvstr_to_sb(KICONV_TBLID_KOI8_R, (uchar_t *)inarray,
	    inlen, (uchar_t *)outarray, outlen, flag, errno));
}

/*
 * The following is the common kiconvstr function for the conversions from
 * single byte codeset to UTF-8.
 */
static size_t
kiconvstr_fr_sb(size_t id, uchar_t *ib, size_t *inlen, uchar_t *ob,
	size_t *outlen, int flag, int *errno)
{
	kiconv_to_utf8_tbl_comp_t *tbl;
	size_t ret_val;
	uchar_t *ibtail;
	uchar_t *obtail;
	size_t i;
	size_t k;
	int8_t sz;
	boolean_t do_not_ignore_null;

	if (id > KICONV_MAX_MAPPING_TBLID &&
	    (id < KICONV_TBLID_RANGE1_START || id > KICONV_TBLID_RANGE1_END)) {
		*errno = EBADF;
		return ((size_t)-1);
	}

	ret_val = 0;
	ibtail = ib + *inlen;
	obtail = ob + *outlen;
	do_not_ignore_null = ((flag & KICONV_IGNORE_NULL) == 0);

	tbl = (id == KICONV_TBLID_720) ?
	    (kiconv_to_utf8_tbl_comp_t *)cp720_to_u8_tbl :
	    (kiconv_to_utf8_tbl_comp_t *)to_u8_tbl[id];

	while (ib < ibtail) {
		if (*ib == '\0' && do_not_ignore_null)
			break;

		if (*ib < 0x80) {
			if (ob >= obtail) {
				*errno = E2BIG;
				ret_val = (size_t)-1;
				break;
			}
			*ob++ = *ib++;
			continue;
		}

		k = *ib - 0x80;
		sz = u8_number_of_bytes[tbl[k].u8[0]];

		if (sz <= 0) {
			if (flag & KICONV_REPLACE_INVALID) {
				if ((obtail - ob) < 3) {
					*errno = E2BIG;
					ret_val = (size_t)-1;
					break;
				}

				/* Save KICONV_UTF8_REPLACEMENT_CHAR. */
				*ob++ = 0xef;
				*ob++ = 0xbf;
				*ob++ = 0xbd;
				ret_val++;
				ib++;

				continue;
			}

			*errno = EILSEQ;
			ret_val = (size_t)-1;
			break;
		}

		if ((obtail - ob) < sz) {
			*errno = E2BIG;
			ret_val = (size_t)-1;
			break;
		}

		for (i = 0; i < sz; i++)
			*ob++ = tbl[k].u8[i];

		ib++;
	}

	*inlen = ibtail - ib;
	*outlen = obtail - ob;

	return (ret_val);
}

/*
 * The following 25 functions are the real entry points that will be
 * given to kiconv framework at the genunix.
 */
static size_t
kiconvstr_fr_720(char *inarray, size_t *inlen, char *outarray,
	size_t *outlen, int flag, int *errno)
{
	return (kiconvstr_fr_sb(KICONV_TBLID_720, (uchar_t *)inarray,
	    inlen, (uchar_t *)outarray, outlen, flag, errno));
}

static size_t
kiconvstr_fr_737(char *inarray, size_t *inlen, char *outarray,
	size_t *outlen, int flag, int *errno)
{
	return (kiconvstr_fr_sb(KICONV_TBLID_737, (uchar_t *)inarray,
	    inlen, (uchar_t *)outarray, outlen, flag, errno));
}

static size_t
kiconvstr_fr_852(char *inarray, size_t *inlen, char *outarray,
	size_t *outlen, int flag, int *errno)
{
	return (kiconvstr_fr_sb(KICONV_TBLID_852, (uchar_t *)inarray,
	    inlen, (uchar_t *)outarray, outlen, flag, errno));
}

static size_t
kiconvstr_fr_857(char *inarray, size_t *inlen, char *outarray,
	size_t *outlen, int flag, int *errno)
{
	return (kiconvstr_fr_sb(KICONV_TBLID_857, (uchar_t *)inarray,
	    inlen, (uchar_t *)outarray, outlen, flag, errno));
}

static size_t
kiconvstr_fr_862(char *inarray, size_t *inlen, char *outarray,
	size_t *outlen, int flag, int *errno)
{
	return (kiconvstr_fr_sb(KICONV_TBLID_862, (uchar_t *)inarray,
	    inlen, (uchar_t *)outarray, outlen, flag, errno));
}

static size_t
kiconvstr_fr_866(char *inarray, size_t *inlen, char *outarray,
	size_t *outlen, int flag, int *errno)
{
	return (kiconvstr_fr_sb(KICONV_TBLID_866, (uchar_t *)inarray,
	    inlen, (uchar_t *)outarray, outlen, flag, errno));
}

static size_t
kiconvstr_fr_1250(char *inarray, size_t *inlen, char *outarray,
	size_t *outlen, int flag, int *errno)
{
	return (kiconvstr_fr_sb(KICONV_TBLID_1250, (uchar_t *)inarray,
	    inlen, (uchar_t *)outarray, outlen, flag, errno));
}

static size_t
kiconvstr_fr_1251(char *inarray, size_t *inlen, char *outarray,
	size_t *outlen, int flag, int *errno)
{
	return (kiconvstr_fr_sb(KICONV_TBLID_1251, (uchar_t *)inarray,
	    inlen, (uchar_t *)outarray, outlen, flag, errno));
}

static size_t
kiconvstr_fr_1253(char *inarray, size_t *inlen, char *outarray,
	size_t *outlen, int flag, int *errno)
{
	return (kiconvstr_fr_sb(KICONV_TBLID_1253, (uchar_t *)inarray,
	    inlen, (uchar_t *)outarray, outlen, flag, errno));
}

static size_t
kiconvstr_fr_1254(char *inarray, size_t *inlen, char *outarray,
	size_t *outlen, int flag, int *errno)
{
	return (kiconvstr_fr_sb(KICONV_TBLID_1254, (uchar_t *)inarray,
	    inlen, (uchar_t *)outarray, outlen, flag, errno));
}

static size_t
kiconvstr_fr_1255(char *inarray, size_t *inlen, char *outarray,
	size_t *outlen, int flag, int *errno)
{
	return (kiconvstr_fr_sb(KICONV_TBLID_1255, (uchar_t *)inarray,
	    inlen, (uchar_t *)outarray, outlen, flag, errno));
}

static size_t
kiconvstr_fr_1256(char *inarray, size_t *inlen, char *outarray,
	size_t *outlen, int flag, int *errno)
{
	return (kiconvstr_fr_sb(KICONV_TBLID_1256, (uchar_t *)inarray,
	    inlen, (uchar_t *)outarray, outlen, flag, errno));
}

static size_t
kiconvstr_fr_1257(char *inarray, size_t *inlen, char *outarray,
	size_t *outlen, int flag, int *errno)
{
	return (kiconvstr_fr_sb(KICONV_TBLID_1257, (uchar_t *)inarray,
	    inlen, (uchar_t *)outarray, outlen, flag, errno));
}

static size_t
kiconvstr_fr_88592(char *inarray, size_t *inlen, char *outarray,
	size_t *outlen, int flag, int *errno)
{
	return (kiconvstr_fr_sb(KICONV_TBLID_8859_2, (uchar_t *)inarray,
	    inlen, (uchar_t *)outarray, outlen, flag, errno));
}

static size_t
kiconvstr_fr_88593(char *inarray, size_t *inlen, char *outarray,
	size_t *outlen, int flag, int *errno)
{
	return (kiconvstr_fr_sb(KICONV_TBLID_8859_3, (uchar_t *)inarray,
	    inlen, (uchar_t *)outarray, outlen, flag, errno));
}

static size_t
kiconvstr_fr_88594(char *inarray, size_t *inlen, char *outarray,
	size_t *outlen, int flag, int *errno)
{
	return (kiconvstr_fr_sb(KICONV_TBLID_8859_4, (uchar_t *)inarray,
	    inlen, (uchar_t *)outarray, outlen, flag, errno));
}

static size_t
kiconvstr_fr_88595(char *inarray, size_t *inlen, char *outarray,
	size_t *outlen, int flag, int *errno)
{
	return (kiconvstr_fr_sb(KICONV_TBLID_8859_5, (uchar_t *)inarray,
	    inlen, (uchar_t *)outarray, outlen, flag, errno));
}

static size_t
kiconvstr_fr_88596(char *inarray, size_t *inlen, char *outarray,
	size_t *outlen, int flag, int *errno)
{
	return (kiconvstr_fr_sb(KICONV_TBLID_8859_6, (uchar_t *)inarray,
	    inlen, (uchar_t *)outarray, outlen, flag, errno));
}

static size_t
kiconvstr_fr_88597(char *inarray, size_t *inlen, char *outarray,
	size_t *outlen, int flag, int *errno)
{
	return (kiconvstr_fr_sb(KICONV_TBLID_8859_7, (uchar_t *)inarray,
	    inlen, (uchar_t *)outarray, outlen, flag, errno));
}

static size_t
kiconvstr_fr_88598(char *inarray, size_t *inlen, char *outarray,
	size_t *outlen, int flag, int *errno)
{
	return (kiconvstr_fr_sb(KICONV_TBLID_8859_8, (uchar_t *)inarray,
	    inlen, (uchar_t *)outarray, outlen, flag, errno));
}

static size_t
kiconvstr_fr_88599(char *inarray, size_t *inlen, char *outarray,
	size_t *outlen, int flag, int *errno)
{
	return (kiconvstr_fr_sb(KICONV_TBLID_8859_9, (uchar_t *)inarray,
	    inlen, (uchar_t *)outarray, outlen, flag, errno));
}

static size_t
kiconvstr_fr_885910(char *inarray, size_t *inlen, char *outarray,
	size_t *outlen, int flag, int *errno)
{
	return (kiconvstr_fr_sb(KICONV_TBLID_8859_10, (uchar_t *)inarray,
	    inlen, (uchar_t *)outarray, outlen, flag, errno));
}

static size_t
kiconvstr_fr_885911(char *inarray, size_t *inlen, char *outarray,
	size_t *outlen, int flag, int *errno)
{
	return (kiconvstr_fr_sb(KICONV_TBLID_8859_11, (uchar_t *)inarray,
	    inlen, (uchar_t *)outarray, outlen, flag, errno));
}

static size_t
kiconvstr_fr_885913(char *inarray, size_t *inlen, char *outarray,
	size_t *outlen, int flag, int *errno)
{
	return (kiconvstr_fr_sb(KICONV_TBLID_8859_13, (uchar_t *)inarray,
	    inlen, (uchar_t *)outarray, outlen, flag, errno));
}

static size_t
kiconvstr_fr_koi8r(char *inarray, size_t *inlen, char *outarray,
	size_t *outlen, int flag, int *errno)
{
	return (kiconvstr_fr_sb(KICONV_TBLID_KOI8_R, (uchar_t *)inarray,
	    inlen, (uchar_t *)outarray, outlen, flag, errno));
}


/*
 * The following are the supported code conversions that will be passed to
 * and registered from this module. The tocode and fromcode names are
 * normalized.
 */
#define	KICONV_MAX_EMEA_OPS		50

static kiconv_ops_t kiconv_emea_ops[KICONV_MAX_EMEA_OPS] = {
	{
		"utf8", "cp1250",
		open_fr_1250, kiconv_fr_sb, close_fr_sb, kiconvstr_fr_1250
	},
	{
		"cp1250", "utf8",
		open_to_1250, kiconv_to_sb, close_to_sb, kiconvstr_to_1250
	},
	{
		"utf8", "iso88592",
		open_fr_88592, kiconv_fr_sb, close_fr_sb, kiconvstr_fr_88592
	},
	{
		"iso88592", "utf8",
		open_to_88592, kiconv_to_sb, close_to_sb, kiconvstr_to_88592
	},
	{
		"utf8", "cp852",
		open_fr_852, kiconv_fr_sb, close_fr_sb, kiconvstr_fr_852
	},
	{
		"cp852", "utf8",
		open_to_852, kiconv_to_sb, close_to_sb, kiconvstr_to_852
	},
	{
		"utf8", "cp1251",
		open_fr_1251, kiconv_fr_sb, close_fr_sb, kiconvstr_fr_1251
	},
	{
		"cp1251", "utf8",
		open_to_1251, kiconv_to_sb, close_to_sb, kiconvstr_to_1251
	},
	{
		"utf8", "iso88595",
		open_fr_88595, kiconv_fr_sb, close_fr_sb, kiconvstr_fr_88595
	},
	{
		"iso88595", "utf8",
		open_to_88595, kiconv_to_sb, close_to_sb, kiconvstr_to_88595
	},
	{
		"utf8", "koi8r",
		open_fr_koi8r, kiconv_fr_sb, close_fr_sb, kiconvstr_fr_koi8r
	},
	{
		"koi8r", "utf8",
		open_to_koi8r, kiconv_to_sb, close_to_sb, kiconvstr_to_koi8r
	},
	{
		"utf8", "cp866",
		open_fr_866, kiconv_fr_sb, close_fr_sb, kiconvstr_fr_866
	},
	{
		"cp866", "utf8",
		open_to_866, kiconv_to_sb, close_to_sb, kiconvstr_to_866
	},
	{
		"utf8", "cp1253",
		open_fr_1253, kiconv_fr_sb, close_fr_sb, kiconvstr_fr_1253
	},
	{
		"cp1253", "utf8",
		open_to_1253, kiconv_to_sb, close_to_sb, kiconvstr_to_1253
	},
	{
		"utf8", "iso88597",
		open_fr_88597, kiconv_fr_sb, close_fr_sb, kiconvstr_fr_88597
	},
	{
		"iso88597", "utf8",
		open_to_88597, kiconv_to_sb, close_to_sb, kiconvstr_to_88597
	},
	{
		"utf8", "cp737",
		open_fr_737, kiconv_fr_sb, close_fr_sb, kiconvstr_fr_737
	},
	{
		"cp737", "utf8",
		open_to_737, kiconv_to_sb, close_to_sb, kiconvstr_to_737
	},
	{
		"utf8", "cp1254",
		open_fr_1254, kiconv_fr_sb, close_fr_sb, kiconvstr_fr_1254
	},
	{
		"cp1254", "utf8",
		open_to_1254, kiconv_to_sb, close_to_sb, kiconvstr_to_1254
	},
	{
		"utf8", "iso88599",
		open_fr_88599, kiconv_fr_sb, close_fr_sb, kiconvstr_fr_88599
	},
	{
		"iso88599", "utf8",
		open_to_88599, kiconv_to_sb, close_to_sb, kiconvstr_to_88599
	},
	{
		"utf8", "cp857",
		open_fr_857, kiconv_fr_sb, close_fr_sb, kiconvstr_fr_857
	},
	{
		"cp857", "utf8",
		open_to_857, kiconv_to_sb, close_to_sb, kiconvstr_to_857
	},
	{
		"utf8", "cp1256",
		open_fr_1256, kiconv_fr_sb, close_fr_sb, kiconvstr_fr_1256
	},
	{
		"cp1256", "utf8",
		open_to_1256, kiconv_to_sb, close_to_sb, kiconvstr_to_1256
	},
	{
		"utf8", "iso88596",
		open_fr_88596, kiconv_fr_sb, close_fr_sb, kiconvstr_fr_88596
	},
	{
		"iso88596", "utf8",
		open_to_88596, kiconv_to_sb, close_to_sb, kiconvstr_to_88596
	},
	{
		"utf8", "cp720",
		open_fr_720, kiconv_fr_sb, close_fr_sb, kiconvstr_fr_720
	},
	{
		"cp720", "utf8",
		open_to_720, kiconv_to_sb, close_to_sb, kiconvstr_to_720
	},
	{
		"utf8", "cp1255",
		open_fr_1255, kiconv_fr_sb, close_fr_sb, kiconvstr_fr_1255
	},
	{
		"cp1255", "utf8",
		open_to_1255, kiconv_to_sb, close_to_sb, kiconvstr_to_1255
	},
	{
		"utf8", "iso88598",
		open_fr_88598, kiconv_fr_sb, close_fr_sb, kiconvstr_fr_88598
	},
	{
		"iso88598", "utf8",
		open_to_88598, kiconv_to_sb, close_to_sb, kiconvstr_to_88598
	},
	{
		"utf8", "cp862",
		open_fr_862, kiconv_fr_sb, close_fr_sb, kiconvstr_fr_862
	},
	{
		"cp862", "utf8",
		open_to_862, kiconv_to_sb, close_to_sb, kiconvstr_to_862
	},
	{
		"utf8", "cp1257",
		open_fr_1257, kiconv_fr_sb, close_fr_sb, kiconvstr_fr_1257
	},
	{
		"cp1257", "utf8",
		open_to_1257, kiconv_to_sb, close_to_sb, kiconvstr_to_1257
	},
	{
		"utf8", "iso885913",
		open_fr_885913, kiconv_fr_sb, close_fr_sb, kiconvstr_fr_885913
	},
	{
		"iso885913", "utf8",
		open_to_885913, kiconv_to_sb, close_to_sb, kiconvstr_to_885913
	},
	{
		"utf8", "iso885910",
		open_fr_885910, kiconv_fr_sb, close_fr_sb, kiconvstr_fr_885910
	},
	{
		"iso885910", "utf8",
		open_to_885910, kiconv_to_sb, close_to_sb, kiconvstr_to_885910
	},
	{
		"utf8", "iso885911",
		open_fr_885911, kiconv_fr_sb, close_fr_sb, kiconvstr_fr_885911
	},
	{
		"iso885911", "utf8",
		open_to_885911, kiconv_to_sb, close_to_sb, kiconvstr_to_885911
	},
	{
		"utf8", "iso88593",
		open_fr_88593, kiconv_fr_sb, close_fr_sb, kiconvstr_fr_88593
	},
	{
		"iso88593", "utf8",
		open_to_88593, kiconv_to_sb, close_to_sb, kiconvstr_to_88593
	},
	{
		"utf8", "iso88594",
		open_fr_88594, kiconv_fr_sb, close_fr_sb, kiconvstr_fr_88594
	},
	{
		"iso88594", "utf8",
		open_to_88594, kiconv_to_sb, close_to_sb, kiconvstr_to_88594
	},
};

static kiconv_module_info_t kiconv_emea_modinfo = {
	"kiconv_emea",		/* Must be the same as in kiconv framework. */
	KICONV_MAX_EMEA_OPS,	/* size_t kiconv_num_convs */
	kiconv_emea_ops,	/* kiconv_ops_t *kiconv_ops_tbl */
	0,			/* size_t kiconv_num_aliases */
	NULL,			/* char **aliases */
	NULL,			/* char **canonicals */
	0			/* int nowait */
};

static struct modlkiconv kiconv_emea = {
	&mod_kiconvops,
	"kiconv module for EMEA",
	&kiconv_emea_modinfo
};

static struct modlinkage linkage = {
	MODREV_1,
	(void *)&kiconv_emea,
	NULL
};

int
_init()
{
	int err;

	err = mod_install(&linkage);
	if (err)
		cmn_err(CE_WARN, "kiconv_emea: failed to load kernel module");

	return (err);
}

int
_info(struct modinfo *modinfop)
{
	return (mod_info(&linkage, modinfop));
}

int
_fini()
{
	int err;

	/*
	 * If this module is being used, then, we cannot remove the module.
	 * The following checking will catch pretty much all usual cases.
	 *
	 * Any remaining will be catached by the kiconv_unregister_module()
	 * during mod_remove() at below.
	 */
	if (kiconv_module_ref_count(KICONV_MODULE_ID_EMEA))
		return (EBUSY);

	err = mod_remove(&linkage);
	if (err)
		cmn_err(CE_WARN, "kiconv_emea: failed to remove kernel module");

	return (err);
}