/* EWL
 * Copyright © 1995-2007 Freescale Corporation.  All rights reserved.
 *
 * $Date: 2012/06/01 15:49:55 $
 * $Revision: 1.1 $
 */

/*
 *	Routines
 *	--------
 *		mblen
 *		mbtowc
 *		wctomb
 *		wctomb_s
 *		wcrtomb
 *		wcrtomb_s
 *		mbsrtowcs
 *		mbsrtowcs_s
 *		wcsrtombs
 *		wcsrtombs_s
 *		mbstowcs
 *		mbstowcs_s
 *		wcstombs
 *		wcstombs_s
 *		mbsinit
 */

#include <ansi_parms.h>
_MISRA_EXCEPTION_RULE_19_6()
#ifdef __STDC_WANT_LIB_EXT1__
/* enforce __STDC_WANT_LIB_EXT1__ support */
#undef __STDC_WANT_LIB_EXT1__
#endif
_MISRA_RESTORE()
#define __STDC_WANT_LIB_EXT1__ 1

#if _EWL_WIDE_CHAR

#include <ewl_misra_types.h>
#include <errno.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>

#include <locale_api.h>
#include <mbstring.h>
#include <ewl_thread_local_data.h>

int_t __unicode_to_UTF8_s(char_t * _EWL_RESTRICT s, size_t smax, wchar_t wchar)
{
	int_t    number_of_bytes;
	uint32_t wide_char;
	uchar_t *target_ptr;
	uchar_t  first_byte_mark[6] = {0x00u, 0xc0u, 0xe0u, 0xf0u, 0xf8u, 0xfcu};

	wide_char = (uint32_t)wchar;

	if (wide_char < 0x00000080u) {
		number_of_bytes = 1;
	} else if (wide_char < 0x00000800u) {
		number_of_bytes = 2;
	} else if (wide_char < 0x00010000u) {
		number_of_bytes = 3;
	} else if (wide_char < 0x00200000u) {
		number_of_bytes = 4;
	} else if (wide_char < 0x04000000u) {
		number_of_bytes = 5;
	} else {
		number_of_bytes = 6;
	}

	target_ptr = (uchar_t *)s + number_of_bytes;

	switch (number_of_bytes)
	{				/* Note: the cases are meant to fall through! */
		case 6:
			if (smax > 6U) {
				*--target_ptr = (((uchar_t)wide_char & 0x003fu) | 0x80u);
			} else {
				--target_ptr;
			}
			wide_char >>= 6;
			MISRA_EXCEPTION_RULE_15_2()
		case 5:
			if (smax > 5U) {
				*--target_ptr = (((uchar_t)wide_char & 0x003fu) | 0x80u);
			} else {
				--target_ptr;
			}
			wide_char >>= 6;
			MISRA_EXCEPTION_RULE_15_2()
		case 4:
			if (smax > 4U) {
				*--target_ptr = (((uchar_t)wide_char & 0x003fu) | 0x80u);
			} else {
				--target_ptr;
			}
			wide_char >>= 6;
			MISRA_EXCEPTION_RULE_15_2()
		case 3:
			if (smax > 3U) {
				*--target_ptr = (((uchar_t)wide_char & 0x003fu) | 0x80u);
			} else {
				--target_ptr;
			}
			wide_char >>= 6;
			MISRA_EXCEPTION_RULE_15_2()
		case 2:
			if (smax > 2U) {
				*--target_ptr = (((uchar_t)wide_char & 0x003fu) | 0x80u);
			} else {
				--target_ptr;
			}
			wide_char >>= 6;
			MISRA_EXCEPTION_RULE_15_2()
		case 1:
			*--target_ptr = ((uchar_t)wide_char | first_byte_mark[number_of_bytes - 1]);
			break;
		default: break;
	}

	return (number_of_bytes);
}


MISRA_ALLOW_POINTER_CASTS()

int_t _EWL_CDECL wctomb_s(int_t * _EWL_RESTRICT status, char_t * _EWL_RESTRICT s, rsize_t smax, wchar_t wchar)
{
	int_t newstatus;
	int_t len;
	char_t wc[MB_CUR_MAX];

	if (((s == NULL) && (smax != 0U)) || (smax > (rsize_t)RSIZE_MAX) || (status == NULL)) {
		__ewl_runtime_constraint_violation_s(NULL, NULL, ERANGE);
		MISRA_EXCEPTION_RULE_14_7()
		return ERANGE;
	}

	if (s == NULL) {
#if defined(_EMBEDDED_WARRIOR_HAS_NO_LOCALE) && _EMBEDDED_WARRIOR_HAS_NO_LOCALE
		*status = 0;	/* no state-dependent encoding for noconv encoder */
#else
		if (_EWL_LOCALDATA(_current_locale).ctype_cmpt_ptr->encode_wc == __wctomb_noconv) {
			*status = 0;	/* no state-dependent encoding for noconv encoder */
		} else {
			*status = 1;	/* assume state-dependent encoding for all other encoders */
		}
#endif
		MISRA_EXCEPTION_RULE_14_7()
		return ENOERR;
	}

#if defined(_EMBEDDED_WARRIOR_HAS_NO_LOCALE) && _EMBEDDED_WARRIOR_HAS_NO_LOCALE
	newstatus = __wctomb_noconv(wc, wchar);
#else
	newstatus = _EWL_LOCALDATA(_current_locale).ctype_cmpt_ptr->encode_wc(wc, wchar);
#endif /* _EMBEDDED_WARRIOR_HAS_NO_LOCALE*/

	len = mblen(wc, sizeof(wc));

	if ((rsize_t)len > smax) {
		__ewl_runtime_constraint_violation_s(NULL, NULL, ERANGE);
		MISRA_EXCEPTION_RULE_14_7()
		return ERANGE;
	}

	*status = newstatus;
	memcpy(s, wc, (uint_t)len);

	return ENOERR;
}

errno_t _EWL_CDECL wcrtomb_s(size_t * _EWL_RESTRICT retval, char_t * _EWL_RESTRICT s, rsize_t smax, wchar_t wc, mbstate_t * _EWL_RESTRICT ps)
{
	char_t buf[MB_CUR_MAX + 1];
	char_t *use_s;
	rsize_t use_smax;

	if ((retval == NULL) || (ps == NULL) || ((s != NULL) && (smax > (rsize_t)RSIZE_MAX)))
	{
		__ewl_runtime_constraint_violation_s(NULL, NULL, ERANGE);
		if (retval != NULL) {
			*retval = (size_t)-1;
		}
		if ((s != NULL) && (smax <= (rsize_t)RSIZE_MAX)) {
			s[0] = 0;
		}
		MISRA_EXCEPTION_RULE_14_7()
		return ERANGE;
	}

	if (s == NULL) {
		use_s = buf;
		use_smax = sizeof(buf);
	} else {
		use_s = s;
		use_smax = smax;
	}

	*retval = (size_t)__unicode_to_UTF8_s(use_s, use_smax, wc);

	return ENOERR;

	MISRA_EXCEPTION_RULE_16_7()
}

errno_t _EWL_CDECL wcstombs_s(size_t * _EWL_RESTRICT retval, char_t * _EWL_RESTRICT s, rsize_t s_max, const wchar_t * _EWL_RESTRICT pwcs, rsize_t n)
{
	size_t    chars_written = 0u;
	size_t    result = 0u;
	char_t    temp[MB_LEN_MAX];
	wchar_t * source;

	if ((retval == NULL) || (pwcs == NULL) || ((s != NULL) && ((s_max > (rsize_t)RSIZE_MAX) || (n > (rsize_t)RSIZE_MAX) || (s_max < n))))
	{
		__ewl_runtime_constraint_violation_s(NULL, NULL, ERANGE);
		if (retval != NULL) {
			*retval = (size_t)-1;
		}
		MISRA_EXCEPTION_RULE_14_7()
		return ERANGE;
	}

	if (!s) {
		*retval = 0u;
		MISRA_EXCEPTION_RULE_14_7()
		return 0;
	}

	source = (wchar_t*)pwcs;
	while(chars_written <= n) {
		if (!*source) {
			*(s + chars_written) = '\0';
			break;
		} else {
       		if (chars_written < s_max) {
				result = (size_t)wctomb(temp, *source++);
			} else {
				MISRA_EXCEPTION_RULE_14_6()
				break;
			}

			if ((chars_written + result) <= n) {
				strncpy(s + chars_written, temp, result);
				chars_written += result;
			} else {
				MISRA_EXCEPTION_RULE_14_6()
				break;
			}
		}
	}

	*retval = chars_written;

	return 0;
}

errno_t _EWL_CDECL wcsrtombs_s(size_t * _EWL_RESTRICT retval, char_t * _EWL_RESTRICT dst, rsize_t dstmax, const wchar_t ** _EWL_RESTRICT src, rsize_t len, mbstate_t * _EWL_RESTRICT ps)
{

	size_t    chars_written = 0u;
	size_t    result;
	char_t    temp[MB_LEN_MAX];
	wchar_t * source;

	if ((retval == NULL) || (src == NULL) || (*src == NULL) || (ps == NULL) ||
	    ((dst != NULL) && ((len > (rsize_t)RSIZE_MAX) || (dstmax > (rsize_t)RSIZE_MAX) || (dstmax < len))))
	{
		__ewl_runtime_constraint_violation_s(NULL, NULL, ERANGE);
		if (retval != NULL) {
			*retval = (size_t)-1;
		}
		MISRA_EXCEPTION_RULE_14_7()
		return ERANGE;
	}

	if (!*src) {
		*retval = 0u;
		MISRA_EXCEPTION_RULE_14_7()
		return 0;
	}

	source = (wchar_t*)*src;
	while(chars_written <= len) {
		if (!*source) {
			if (dst) {
				*(dst + chars_written) = '\0';
				*src = NULL;
			}
			break;
		} else {
			result = wcrtomb(temp, *source++, ps);
			if ((chars_written + result) <= len) {
				if (dst) {
					strncpy(dst + chars_written, temp, result);
				}
				chars_written += result;
				*src = source;
			} else {
				MISRA_EXCEPTION_RULE_14_6()
				break;
			}
		}
	}

	*retval = chars_written;

	return 0;
}
#endif  /*  _EWL_WIDE_CHAR  */
