/*
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.

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

/*
 * "$Id: csconv.c,v 1.1.1.1.2.1 2001/03/15 07:52:17 himi Exp $"
 */

#pragma ident	"@(#)csconv.c 1.16	00/09/14 SMI"

#include "config.h"

#include <stddef.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <strings.h>
#include <locale.h>
#include <dlfcn.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/param.h>
#if defined(HAVE_SYSINFO) && defined(HAVE_SYS_SYSTEMINFO_H)
#  include <sys/systeminfo.h>
#else /* !HAVE_SYSINFO */
#  if defined(HAVE_UNAME)
#    include <sys/utsname.h>
#  endif /* HAVE_UNAME */
#endif /* !HAVE_SYSINFO */

#include "csconv.h"
#include "csc_entry.h"
#include "csc_conf_info.h"
#include "csc_conf_internal.h"
#include "csc_norm.h"

#include "trace_message.h"

#if !defined(MAXNAMELEN)
#  if defined(NAME_MAX)
#    define MAXNAMELEN NAME_MAX
#  else /* !NAME_MAX */
#    define MAXNAMELEN 256
#  endif /* !NAME_MAX */
#endif

#if !defined(SHLIB_EXT)
#define SHLIB_EXT		".so"
#endif /* !SHLIB_EXT */

#if !defined(CSC_DEFAULT_SYSNAME)
#define CSC_DEFAULT_SYSNAME	"Unknown"
#endif /* !CSC_DEFAULT_SYSNAME */
#if !defined(CSC_ANY)
#define CSC_ANY			"-"
#endif /* !CSC_ANY */

#if !defined(CSC_CONF_FILE)
#define CSC_CONF_FILE		"csconv.conf"
#endif /* !CSC_CONF_FILE */

#if !defined(CSC_SHLIB_HOME_DIR)
#define CSC_SHLIB_HOME_DIR	CSC_BASE_DIR_PERSONAL
#endif /* !CSC_SHLIB_HOME_DIR */
#if !defined(CSC_SHLIB_SYSTEM_DIR)
#define CSC_SHLIB_SYSTEM_DIR	CSC_BASE_DIR_SYSTEM
#endif /* !CSC_SHLIB_SYSTEM_DIR */
#if !defined(CSC_SHLIB_VENDOR_DIR)
#define CSC_SHLIB_VENDOR_DIR	CSC_BASE_DIR_DEFAULT
#endif /* !CSC_SHLIB_VENDOR_DIR */

#if defined(CSC_STRIP_UTF16_BOM)
#define CSC_ENC_NAME_UTF16	"UTF-16"
#define CSC_UTF16_BOM_0		(0xFE)
#define CSC_UTF16_BOM_1		(0xFF)
#endif /* CSC_STRIP_UTF16_BOM */

struct _csconv_info {
	void *		dl_handle;	/* dlopen(3X)'ed object */
	void *		info;
	csc_conv_t *	csc_conv;
	csc_close_t *	csc_close;
#if defined(CSC_STRIP_UTF16_BOM)
	int		utf16;
	int		strip_utf16_bom;
#endif /* CSC_STRIP_UTF16_BOM */
#if defined(ENABLE_TRACE)
	char *		locale;
	char *		tocode;
	char *		fromcode;
#endif /* ENABLE_TRACE */
};

#define 	CSC_CONF_PLATFORM	(0)
#define 	CSC_CONF_LOCALE		(1)
#define 	CSC_CONF_FROMCODE	(2)
#define 	CSC_CONF_TOCODE		(3)
#define 	CSC_CONF_OBJECT_NAME	(4)
#define 	CSC_CONF_ENTRY_NAME	(5)
#define 	CSC_CONF_NUM		(6)


#if defined(ENABLE_TRACE)
static void	dump(char * title, const char * ptr, size_t len);
static void	dump_in(char * title,
			const char ** inbuf, size_t * inbytesleft);
static void	dump_out(char *	title,
			 const char * op, size_t oleft,
			 const char ** outbuf, size_t * outbytesleft);
#endif /* ENABLE_TRACE */

static void *	csc_dlopen_real(
	const char *		base_path,
	const char *		locale,
	csc_conf_str_t *	config_str,
	csc_norm_encoding_t *	norm_encoding,
	csconv_t		cd
);

static void *	csc_dlopen(
	const char *		locale,
	csc_conf_str_t *	config_str,
	csc_norm_encoding_t *	norm_encoding,
	csconv_t		cd
);

static void *	csc_internal_open(
	const char *		locale,
	csc_conf_str_t *	config_str,
	csc_norm_encoding_t *	norm_encoding,
	csconv_t		cd
);


csconv_t
csconv_open_locale(const char * locale,
		   const char * tocode, const char * fromcode)
{
	csconv_t		cd;
	csc_conf_file_t *	config_file;
	csc_conf_str_t		config_str[CSC_CONF_NUM];
	char			platform[257];
#if defined(HAVE_SYSINFO) && defined(HAVE_SYS_SYSTEMINFO_H)
#else
#if defined(HAVE_UNAME)
	struct utsname		name;
#endif /* HAVE_UNAME */
#endif /* HAVE_SYSINFO */
	csc_norm_encoding_t *	norm_encoding;

	TRACE_MESSAGE_INIT("CSC_TRACE");

	TRACE_MESSAGE('C', ("csconv_open_locale: locale=%s tocode=%s fromcode=%s\n",
			    locale, tocode, fromcode));

#if defined(HAVE_SYSINFO) && defined(HAVE_SYS_SYSTEMINFO_H)
	if (sysinfo(SI_SYSNAME, platform, sizeof(platform)) < 0) {
		strcpy(platform, CSC_DEFAULT_SYSNAME);
	}
#else /* !HAVE_SYSINFO */
#if defined(HAVE_UNAME)
	if (uname(&name) < 0) {
#if defined(CSC_SYSTEM_NAME)
		strcpy(platform, CSC_SYSTEM_NAME);
#else /* !CSC_SYSTEM_NAME */
		strcpy(platform, CSC_DEFAULT_SYSNAME);
#endif /* !CSC_SYSTEM_NAME */
	} else {
		strcpy(platform, name.sysname);
        }
#else /* !HAVE_UNAME */
#if defined(CSC_SYSTEM_NAME)
	strcpy(platform, CSC_SYSTEM_NAME);
#else /* !CSC_SYSTEM_NAME */
	strcpy(platform, CSC_DEFAULT_SYSNAME);
#endif /* !CSC_SYSTEM_NAME */
#endif /* !HAVE_UNAME */
#endif /* !HAVE_SYSINFO */

	if (NULL == (cd = (csconv_t)malloc(sizeof (*cd)))) {
		errno = ENOMEM;
		return (csconv_t)(-1);
	}
	cd->dl_handle = NULL;
	cd->info = NULL;
	cd->csc_conv = NULL;
	cd->csc_close = NULL;
#if defined(ENABLE_TRACE)
	cd->locale = strdup(locale);
	cd->tocode = strdup(tocode);
	cd->fromcode = strdup(fromcode);
#endif /* ENABLE_TRACE */
#if defined(CSC_STRIP_UTF16_BOM)
	if (0 == strcmp(tocode, CSC_ENC_NAME_UTF16)) {
		cd->utf16 = 1;
		cd->strip_utf16_bom = 1;
	} else {
		cd->utf16 = 0;
		cd->strip_utf16_bom = 0;
	}
#endif /* CSC_ENC_NAME_UTF16 */

#if defined(CSC_PRIVATE_CUSTOMIZE)
	config_file = csc_conf_open(CSC_CONF_FILE, CSC_CONF_CUSTOMIZE_ENABLED,
				    csc_conf_internal, csc_conf_internal_len);
#else /* !CSC_PRIVATE_CUSTOMIZE */
	config_file = csc_conf_open(CSC_CONF_FILE, CSC_CONF_CUSTOMIZE_DISABLED,
				    csc_conf_internal, csc_conf_internal_len);
#endif /* !CSC_PRIVATE_CUSTOMIZE */
	if (NULL == config_file) {
		errno = EINVAL;
		return (csconv_t)(-1);
	}

	norm_encoding = csc_norm_encoding(platform, locale, fromcode, tocode);

	while (0 != csc_conf_read(config_file, config_str, CSC_CONF_NUM)) {
		if (((0 != csc_strcmp(CSC_ANY,
				      &(config_str[CSC_CONF_PLATFORM]))) &&
		     (0 != csc_strcmp(platform,
				      &(config_str[CSC_CONF_PLATFORM]))))   ||
		    ((0 != csc_strcmp(CSC_ANY,
				      &(config_str[CSC_CONF_LOCALE])))   &&
		     (0 != csc_strcmp(locale,
				      &(config_str[CSC_CONF_LOCALE]))))     ||
		    ((0 != csc_strcmp(tocode,
				      &(config_str[CSC_CONF_TOCODE]))))     ||
		    ((0 != csc_strcmp(fromcode,
				      &(config_str[CSC_CONF_FROMCODE]))))) {
			continue;
		}
		TRACE_MESSAGE('o',
			      ("csconv_open_locale: "
			       "\"%.*s\" \"%.*s\" \"%.*s\" "
			       "\"%.*s\" \"%.*s\" \"%.*s\"\n",
			       config_str[CSC_CONF_PLATFORM].len,
			       config_str[CSC_CONF_PLATFORM].ptr,
			       config_str[CSC_CONF_LOCALE].len,
			       config_str[CSC_CONF_LOCALE].ptr,
			       config_str[CSC_CONF_FROMCODE].len,
			       config_str[CSC_CONF_FROMCODE].ptr,
			       config_str[CSC_CONF_TOCODE].len,
			       config_str[CSC_CONF_TOCODE].ptr,
			       config_str[CSC_CONF_OBJECT_NAME].len,
			       config_str[CSC_CONF_OBJECT_NAME].ptr,
			       config_str[CSC_CONF_ENTRY_NAME].len,
			       config_str[CSC_CONF_ENTRY_NAME].ptr));
		if ((1 == config_str[CSC_CONF_OBJECT_NAME].len) &&
		    ('-' == (*(config_str[CSC_CONF_OBJECT_NAME].ptr)))) {
			csc_internal_open(locale, config_str,
					  norm_encoding, cd);
		}

		if (NULL == cd->csc_conv) {
			csc_dlopen(locale, config_str, norm_encoding, cd);
		}

		if (NULL != cd->csc_conv) {
			break;
		}
	}
	csc_conf_close(config_file);
	csc_norm_free(norm_encoding);

	if (NULL == cd->info) {
		free(cd);
		cd = (csconv_t)(-1);
	}

	return cd;
}


csconv_t
csconv_open(const char * tocode, const char * fromcode)
{
	char *		locale;
	csconv_t	cd;

	TRACE_MESSAGE_INIT("CSC_TRACE");

	TRACE_MESSAGE('C', ("csconv_open: tocode=%s fromcode=%s\n", tocode, fromcode));

	if (NULL == (locale = setlocale(LC_CTYPE, NULL))) {
		errno = EINVAL;
		return (csconv_t)(-1);
	}

	if (NULL == (locale = strdup(locale))) {
		return (csconv_t)(-1);
	}

	cd = csconv_open_locale(locale, tocode, fromcode);

	free(locale);

	return cd;
}


size_t
csconv(csconv_t cd,
       const char ** inbuf, size_t * inbytesleft,
       char ** outbuf, size_t * outbytesleft)
{
	size_t	ret_val;
#if defined(CSC_STRIP_UTF16_BOM)
	int	strip_utf16_bom_next_time;
#endif /* CSC_STRIP_UTF16_BOM */
#if defined(ENABLE_TRACE) || defined(CSC_STRIP_UTF16_BOM)
	char *	op;
	size_t	oleft;
#endif /* ENABLE_TRACE || CSC_STRIP_UTF16_BOM */
#if defined(ENABLE_TRACE)
	char	title_buf[1024];
#endif /* ENABLE_TRACE */

	TRACE_MESSAGE('C', ("csconv\n"));

	if (((csconv_t)(-1) == cd) || (NULL == cd->csc_conv)) {
		return (size_t)(-1);
	}

#if defined(CSC_STRIP_UTF16_BOM)
	if (1 == cd->utf16) {
		if ((NULL == inbuf) || (NULL == *inbuf)) {
			strip_utf16_bom_next_time = 1;
		} else {
			strip_utf16_bom_next_time = 0;
		}
	} else {
		strip_utf16_bom_next_time = 0;
	}
#endif /* CSC_STRIP_UTF16_BOM */
#if defined(ENABLE_TRACE) || defined(CSC_STRIP_UTF16_BOM)
	if (NULL == outbuf) {
		op = NULL;
	} else {
		op = *outbuf;
	}
	if ((NULL != op) && (NULL != outbytesleft)) {
		oleft = *outbytesleft;
	} else {
		oleft = 0;
	}
#endif /* ENABLE_TRACE || defined(CSC_STRIP_UTF16_BOM) */

#if defined(ENABLE_TRACE)
	sprintf(title_buf, "in : %.*s : %.*s", 256, cd->locale, 256, cd->fromcode);
	dump_in(title_buf, inbuf, inbytesleft);
#endif /* ENABLE_TRACE */

	ret_val = (cd->csc_conv)(cd->info,
				 inbuf, inbytesleft, outbuf, outbytesleft);

#if defined(CSC_STRIP_UTF16_BOM)
	if ((1 == cd->strip_utf16_bom) &&
	    (NULL != op)) {
		int	length;
		length = (oleft - *outbytesleft);
		if ((2 <= length) &&
		    ((CSC_UTF16_BOM_0 == *((unsigned char *)op + 0)) &&
		     (CSC_UTF16_BOM_1 == *((unsigned char *)op + 1))) ||
		    ((CSC_UTF16_BOM_1 == *((unsigned char *)op + 0)) &&
		     (CSC_UTF16_BOM_0 == *((unsigned char *)op + 1)))) {
			memmove(op, op + 2, length - 2);
			*outbuf -= 2;
			*outbytesleft += 2;
		}
	}
	if (1 == strip_utf16_bom_next_time) {
		cd->strip_utf16_bom = 1;
	}
#endif /* CSC_STRIP_UTF16_BOM */

#if defined(ENABLE_TRACE)
	sprintf(title_buf, "out : %.*s : %.*s", 256, cd->locale, 256, cd->tocode);
	dump_out(title_buf,
		 (const char *)op, oleft, (const char **)outbuf, outbytesleft);
#endif /* ENABLE_TRACE */

	return ret_val;
}


int
csconv_close(csconv_t cd)
{
	TRACE_MESSAGE('C', ("csconv_close: 0x%08x\n", cd));

	if ((csconv_t)(-1) == cd) {
		return -1;
	}

	(cd->csc_close)(cd->info);

	if (NULL != cd->dl_handle) {
		dlclose(cd->dl_handle);
	}

#if defined(ENABLE_TRACE)
	free(cd->locale);
	free(cd->tocode);
	free(cd->fromcode);
#endif /* ENABLE_TRACE */

	free(cd);
	return 0;
}


void *
csc_dlopen_real(
	const char *		base_path,
	const char *		locale,
	csc_conf_str_t *	config_str,
	csc_norm_encoding_t *	norm_encoding,
	csconv_t		cd)
{
	char		sh_object[MAXPATHLEN];
	char		sh_entry[MAXPATHLEN];
	char		tocode[MAXNAMELEN];
	char		fromcode[MAXNAMELEN];
	int		len;
	csc_open_t *	csc_open;
	const char *	sh_object_name_ptr;
	int		sh_object_name_len;

	TRACE_MESSAGE('d', ("csc_dlopen_real: %s %.*s\n",
			    (NULL == base_path) ? "(nil)" : base_path,
			    config_str[CSC_CONF_OBJECT_NAME].len,
			    config_str[CSC_CONF_OBJECT_NAME].ptr));

	if (NULL == base_path) {
		len = 0;
	} else {
		len = strlen(base_path);
	}

	if ((1 == config_str[CSC_CONF_OBJECT_NAME].len) &&
	    ('-' == (*(config_str[CSC_CONF_OBJECT_NAME].ptr)))) {
		sh_object_name_ptr = config_str[CSC_CONF_ENTRY_NAME].ptr;
		sh_object_name_len = config_str[CSC_CONF_ENTRY_NAME].len;
	} else {
		sh_object_name_ptr = config_str[CSC_CONF_OBJECT_NAME].ptr;
		sh_object_name_len = config_str[CSC_CONF_OBJECT_NAME].len;
	}

	if ((sizeof (sh_object)) <=
	    (len + sh_object_name_len + strlen(SHLIB_EXT))) {
		return NULL;
	}

	if (NULL != base_path) {
		strcpy(sh_object, base_path);
	}

	strncpy(sh_object + len, sh_object_name_ptr, sh_object_name_len);
	strcpy(sh_object + len + sh_object_name_len, SHLIB_EXT);

	if ((sizeof (sh_entry)) <=
	    (config_str[CSC_CONF_ENTRY_NAME].len + 7)) {
				       /* 7 == (strlen("_close") + 1) */
				       /* 6 == (strlen("_open") + 1) */
				       /* 6 == (strlen("_conv") + 1) */
		return NULL;
	} else {
		len = config_str[CSC_CONF_ENTRY_NAME].len;
	}
	memmove(sh_entry, config_str[CSC_CONF_ENTRY_NAME].ptr, len);
	sh_entry[len] = '\0';

	cd->dl_handle = dlopen(sh_object, RTLD_LAZY);
	if (NULL == cd->dl_handle) {
		return NULL;
	}

	memmove(sh_entry + len, "_open", 6);
	csc_open = (csc_open_t *)dlsym(cd->dl_handle, sh_entry);

	memmove(sh_entry + len, "_conv", 6);
	cd->csc_conv = (csc_conv_t *)dlsym(cd->dl_handle, sh_entry);

	memmove(sh_entry + len, "_close", 7);
	cd->csc_close = (csc_close_t *)dlsym(cd->dl_handle, sh_entry);

	if ((NULL == csc_open) ||
	    (NULL == cd->csc_conv) ||
	    (NULL == cd->csc_close)) {
		dlclose(cd->dl_handle);
		cd->dl_handle = NULL;
		cd->info = NULL;
		cd->csc_conv = NULL;
		cd->csc_close = NULL;
		return NULL;
	}

	if (((sizeof (tocode)) <= config_str[CSC_CONF_TOCODE].len) ||
	    ((sizeof (fromcode)) <= config_str[CSC_CONF_FROMCODE].len)) {
		dlclose(cd->dl_handle);
		cd->dl_handle = NULL;
		cd->info = NULL;
		cd->csc_conv = NULL;
		cd->csc_close = NULL;
		return NULL;
	}

	memmove(tocode,
		config_str[CSC_CONF_TOCODE].ptr,
		config_str[CSC_CONF_TOCODE].len);
	tocode[config_str[CSC_CONF_TOCODE].len] = '\0';
	memmove(fromcode,
		config_str[CSC_CONF_FROMCODE].ptr,
		config_str[CSC_CONF_FROMCODE].len);
	fromcode[config_str[CSC_CONF_FROMCODE].len] = '\0';

	if (NULL == norm_encoding) {
		cd->info = csc_open(locale, tocode, fromcode);
	} else {
		cd->info = csc_open(norm_encoding->locale,
				    norm_encoding->encoding2,
				    norm_encoding->encoding1);
	}
	if (NULL == cd->info) {
		dlclose(cd->dl_handle);
		cd->dl_handle = NULL;
		cd->info = NULL;
		cd->csc_conv = NULL;
		cd->csc_close = NULL;
		return NULL;
	}

#if defined(ENABLE_TRACE)
	if ((TRACE_P('O')) && (NULL != cd->info)) {
		sh_entry[len] = '\0';
		if (NULL == norm_encoding) {
			TRACE_MESSAGE('O',
				      ("using %s(%s, %s, %s) in %s\n",
				       sh_entry,
				       locale, tocode, fromcode,
				       sh_object));
		} else {
			TRACE_MESSAGE('O',
				      ("using %s(%s, %s, %s) in %s\n",
				       sh_entry,
				       norm_encoding->locale,
				       norm_encoding->encoding2,
				       norm_encoding->encoding1,
				       sh_object));
		}
	}
#endif /* ENABLE_TRACE */

	return cd->dl_handle;
}


static void *
csc_dlopen(
	const char *		locale,
	csc_conf_str_t *	config_str,
	csc_norm_encoding_t *	norm_encoding,
	csconv_t		cd)
{
	void *		rv;
	char		path[MAXPATHLEN];
	char *		env_home;

	TRACE_MESSAGE('d', ("csc_dlopen\n"));

	if (CSC_IS_PATH_ABS(config_str[CSC_CONF_OBJECT_NAME].ptr)) {
		rv = csc_dlopen_real(NULL, locale, config_str,
				     norm_encoding, cd);
		return rv;

	} else {
#if defined(CSC_PRIVATE_CUSTOMIZE)
		if ((0 != getpid()) &&
		    (NULL != (env_home = getenv("HOME")))) {
			int	len_base;
			int	len_home;

			len_base = strlen(CSC_SHLIB_HOME_DIR);
			len_home = strlen(env_home);
			if ((len_base + len_home) < (sizeof (path))) {
				strcpy(path, env_home);
				strcpy(path + len_home, CSC_SHLIB_HOME_DIR);
				rv = csc_dlopen_real(path,
						     locale, config_str,
						     norm_encoding, cd);
				if (NULL != rv) {
					return rv;
				}
			}
		}
#endif /* !CSC_PRIVATE_CUSTOMIZE */

		rv = csc_dlopen_real(CSC_SHLIB_SYSTEM_DIR,
				     locale, config_str, norm_encoding, cd);
		if (NULL != rv) {
			return rv;
		}

		rv = csc_dlopen_real(CSC_SHLIB_VENDOR_DIR,
				     locale, config_str, norm_encoding, cd);
		if (NULL != rv) {
			return rv;
		}

		return NULL;
	}
}


static void *
csc_internal_open(
	const char *		locale,
	csc_conf_str_t *	config_str,
	csc_norm_encoding_t *	norm_encoding,
	csconv_t		cd)
{
	void *			info;
	csc_entry_internal_t *	csc_ent_int;
	char			tocode[MAXNAMELEN];
	char			fromcode[MAXNAMELEN];

	TRACE_MESSAGE('i', ("csc_internal_open\n"));

	if (NULL == config_str) {
		return NULL;
	}

	cd->dl_handle = NULL;
	cd->info = NULL;
	cd->csc_conv = NULL;
	cd->csc_close = NULL;

	memmove(tocode,
		config_str[CSC_CONF_TOCODE].ptr,
		config_str[CSC_CONF_TOCODE].len);
	tocode[config_str[CSC_CONF_TOCODE].len] = '\0';
	memmove(fromcode,
		config_str[CSC_CONF_FROMCODE].ptr,
		config_str[CSC_CONF_FROMCODE].len);
	fromcode[config_str[CSC_CONF_FROMCODE].len] = '\0';

	csc_ent_int = NULL;

	do {
		csc_ent_int =
			csc_lookup_entry_internal(csc_ent_int,
						  config_str[CSC_CONF_ENTRY_NAME].ptr,
						  config_str[CSC_CONF_ENTRY_NAME].len);

		if (NULL == csc_ent_int) {
			return NULL;
		}

		if (NULL == norm_encoding) {
			info = (csc_ent_int->csc_open)(locale, tocode, fromcode);
		} else {
			info = (csc_ent_int->csc_open)(norm_encoding->locale,
						       norm_encoding->encoding2,
						       norm_encoding->encoding1);
		}

		if (NULL != info) {
			cd->info = info;
			cd->csc_conv = csc_ent_int->csc_conv;
			cd->csc_close = csc_ent_int->csc_close;
			break;
		}
	} while(1);

#if defined(ENABLE_TRACE)
	if ((TRACE_P('O')) && (NULL != info)) {
		if (NULL == norm_encoding) {
			TRACE_MESSAGE('O',
				      ("using %.*s(%s, %s, %s) - internal\n",
				       config_str[CSC_CONF_ENTRY_NAME].len,
				       config_str[CSC_CONF_ENTRY_NAME].ptr,
				       locale, tocode, fromcode));
		} else {
			TRACE_MESSAGE('O',
				      ("using %.*s(%s, %s, %s) - internal\n",
				       config_str[CSC_CONF_ENTRY_NAME].len,
				       config_str[CSC_CONF_ENTRY_NAME].ptr,
				       norm_encoding->locale,
				       norm_encoding->encoding2,
				       norm_encoding->encoding1));
		}
	}
#endif /* ENABLE_TRACE */

	return NULL;
}


#if defined(ENABLE_TRACE)
static void
dump(char * title, const char * ptr, size_t len)
{
	size_t	i;
	int	sp;
	int	br;

	TRACE_MESSAGE('D', ("---- %s ----\n", title));
	for (i = 0, sp = 0, br = 0; i < len; i++) {
		TRACE_MESSAGE('D', ("%02x", *(unsigned char *)(ptr + i)));
		if (3 <= sp) {
			if (3 <= br) {
				TRACE_MESSAGE('D', ("\n"));
				br = 0;
			} else {
				TRACE_MESSAGE('D', (" "));
				br += 1;
			}
			sp = 0;
		} else {
			sp += 1;
		}
	}
	if ((0 != br) || ((0 == br) && (0 != sp))) {
		TRACE_MESSAGE('D', ("\n"));
	}
}


static void
dump_in(char * title, const char ** inbuf, size_t * inbytesleft)
{
	if (!(TRACE_P('D'))) {
		return;
	}

	if (NULL == inbuf) {
		TRACE_MESSAGE('D', ("NULL == inbuf\n"));
		return;
	}

	if (NULL == *inbuf) {
		TRACE_MESSAGE('D', ("NULL == *inbuf\n"));
		return;
	}

	dump(title, *inbuf, *inbytesleft);

	return;
}


static void
dump_out(char *		title,
	 const char *	op,
	 size_t		oleft,
	 const char **	outbuf,
	 size_t *	outbytesleft)
{
	if (!(TRACE_P('D'))) {
		return;
	}

	if (NULL == outbuf) {
		TRACE_MESSAGE('D', ("NULL == outbuf\n"));
		return;
	}

	if (NULL == *outbuf) {
		TRACE_MESSAGE('D', ("NULL == *outbuf\n"));
		return;
	}

	dump(title, op, oleft - *outbytesleft);

	return;
}
#endif /* ENABLE_TRACE */
