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

/*
 * "$Id: mbs_wcs.c,v 1.1.1.1 2000/10/29 16:45:23 himi Exp $"
 */

#pragma ident	"@(#)mbs_wcs.c 1.2	99/04/30 SMI"


#include <stdlib.h>
#include <locale.h>
#include <errno.h>
#include <sys/types.h>

#include "csconv.h"


typedef struct _csc_state {
	char *		locale;
} csc_state_t;


csc_state_t *
mbs_wcs_open(
	const char *	locale,
	const char *	tocode,
	const char *	fromcode)
{
	csc_state_t *	csc_state;
	char *		locale_current;
	char *		locale_dup;

	csc_state = NULL;
	locale_current = NULL;
	locale_dup = NULL;

	do {
		if (NULL == (locale_current = setlocale(LC_CTYPE, NULL))) {
			continue;
		}

		if (NULL == (locale_current = strdup(locale_current))) {
			continue;
		}

		if (NULL == locale) {
			locale = locale_current;
			locale_dup = locale_current;
			locale_current = NULL;

		} else if (0 == strcmp(locale, locale_current)) {
			locale_dup = locale_current;
			locale_current = NULL;

		} else {
			if (NULL == setlocale(LC_CTYPE, locale)) {
				continue;
			}

			if (NULL == setlocale(LC_CTYPE, locale_current)) {
				continue;
			}

			free(locale_current);
			locale_current = NULL;

			if (NULL == (locale_dup = strdup(locale))) {
				continue;
			}
		}

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

		csc_state->locale = locale_dup;

		return csc_state;
	} while (0);

	free(locale_current);
	free(locale_dup);
	free(csc_state);

	return NULL;
}

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

	free(csc_state->locale);
	free(csc_state);

	return;
}

size_t
mbs_wcs_conv(
	csc_state_t *	csc_state,
	const char **	inbuf,
	size_t *	inbytesleft,
	char **		outbuf,
	size_t *	outbytesleft)
{
	size_t		ret_val;
	char *		locale_current;
	int		errno_save;
	wchar_t *	op;
	const char *	ip;
	size_t		ileft;
	size_t		oleft;
	int		length;


	ret_val = 0;

	/*
	 * initialize only
	 */
	if ((NULL == inbuf) || (NULL == *inbuf)) {
		return 0;
	}


	/*
	 * enter the locale
	 */
	if (NULL == (locale_current = setlocale(LC_CTYPE, NULL))) {
		return (size_t)(-1);
	}

	if (NULL == (locale_current = strdup(locale_current))) {
		return (size_t)(-1);
	}

	if (0 != strcmp(csc_state->locale, locale_current)) {
		if (NULL == setlocale(LC_CTYPE, csc_state->locale)) {
			free(locale_current);
			return (size_t)(-1);
		}
	} else {
		free(locale_current);
		locale_current = NULL;
	}


	/*
	 * MultiByte to WideChar
	 */
	length = 0;
	ip = (*inbuf);
	ileft = (*inbytesleft);
	op = (wchar_t *)(*outbuf);
	oleft = ((*outbytesleft) / (sizeof (wchar_t)));

	while (0 < ileft) {
		if (0 == oleft) {
			ret_val = (size_t)(-1);
			errno_save = E2BIG;
			break;
		}

		length = mbtowc(op, ip, ileft);
		if ((-1) == length) {
			ret_val = (size_t)(-1);
			errno_save = errno;
			break;
		}

		ip += length;
		ileft -= length;
		op += 1;
		oleft -= 1;
	}


	/*
	 * exit the locale, revert to the previous locale
	 */
	if (NULL != locale_current) {
		if (NULL == setlocale(LC_CTYPE, locale_current)) {
			if ((size_t)(-1) != ret_val) {
				ret_val = (size_t)(-1);
				errno_save = errno;
			}
		}
		free(locale_current);
	}


	/*
	 * put back return values
	 */
	*inbuf = ip;
	*inbytesleft = ileft;
	*outbuf = (char *)op;
	*outbytesleft = (oleft * (sizeof (wchar_t)));

	if ((size_t)(-1) == ret_val) {
		errno = errno_save;
	}


	return ret_val;
}
