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

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

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


#include <stdlib.h>
#include <string.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 *
wcs_mbs_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
wcs_mbs_close(csc_state_t * csc_state)
{
	if (NULL == csc_state) {
		return;
	}

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

	return;
}

size_t
wcs_mbs_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;
	char *		bufp;
	char		buf[64];
	char *		op;
	wchar_t *	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)) {
			errno_save = errno;
			free(locale_current);
			errno = errno_save;
			return (size_t)(-1);
		}
	} else {
		free(locale_current);
		locale_current = NULL;
	}


	/*
	 * allocate intermediate storage
	 */
	if ((sizeof (buf)) < MB_CUR_MAX) {
		bufp = malloc(MB_CUR_MAX);
		if (NULL == bufp) {
			errno_save = errno;
			free(locale_current);
			errno = errno_save;
			return (size_t)(-1);
		}
	} else {
		bufp = buf;
	}


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

	while (0 < ileft) {
		length = wctomb(bufp, *ip);

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

		} else if (oleft < length) {
			ret_val = (size_t)(-1);
			errno_save = E2BIG;
			break;
		}

		memcpy(op, buf, length);

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


	/*
	 * free intermediate storage
	 */
	if (buf != bufp) {
		free(bufp);
	}


	/*
	 * 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 = (const char *)ip;
	*inbytesleft = (ileft * (sizeof (wchar_t)));
	*outbuf = op;
	*outbytesleft = oleft;

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


	return ret_val;
}
