/* Copyright (c) 2009 Corinna Vinschen <corinna@vinschen.de> */
/*
FUNCTION
<<mbsrtowcs>>, <<mbsnrtowcs>>---convert a character string to a wide-character string

INDEX
	mbsrtowcs
INDEX
	_mbsrtowcs_r
INDEX
	mbsnrtowcs
INDEX
	_mbsnrtowcs_r

SYNOPSIS
	#include <wchar.h>
	size_t mbsrtowcs(wchar_t *__restrict <[dst]>,
			 const char **__restrict <[src]>,
			 size_t <[len]>,
			 mbstate_t *__restrict <[ps]>);

	#include <wchar.h>
	size_t mbsnrtowcs(wchar_t *__ restrict <[dst]>, 
			  const char **__restrict <[src]>, size_t <[nms]>,
			  size_t <[len]>, mbstate_t *__restrict <[ps]>);


DESCRIPTION
The <<mbsrtowcs>> function converts a sequence of multibyte characters
pointed to indirectly by <[src]> into a sequence of corresponding wide
characters and stores at most <[len]> of them in the wchar_t array pointed
to by <[dst]>, until it encounters a terminating null character ('\0').

If <[dst]> is NULL, no characters are stored.

If <[dst]> is not NULL, the pointer pointed to by <[src]> is updated to point
to the character after the one that conversion stopped at.  If conversion
stops because a null character is encountered, *<[src]> is set to NULL.

The mbstate_t argument, <[ps]>, is used to keep track of the shift state.  If
it is NULL, <<mbsrtowcs>> uses an internal, static mbstate_t object, which
is initialized to the initial conversion state at program startup.

The <<mbsnrtowcs>> function behaves identically to <<mbsrtowcs>>, except that
conversion stops after reading at most <[nms]> bytes from the buffer pointed
to by <[src]>.

RETURNS
The <<mbsrtowcs>> and <<mbsnrtowcs>> functions return the number of wide
characters stored in the array pointed to by <[dst]> if successful, otherwise
it returns (size_t)-1.

PORTABILITY
<<mbsrtowcs>> is defined by the C99 standard.
<<mbsnrtowcs>> is defined by the POSIX.1-2008 standard.
*/

#include <newlib.h>
#include <wchar.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>

size_t
mbsnrtowcs (
	wchar_t *dst,
	const char **src,
	size_t nms,
	size_t len,
	mbstate_t *ps)
{
  wchar_t *ptr = dst;
  const char *tmp_src;
  size_t max;
  size_t count = 0;
  int bytes;

#ifdef _MB_CAPABLE
  if (ps == NULL)
    {
      static NEWLIB_THREAD_LOCAL mbstate_t _mbsrtowcs_state;
      ps = &_mbsrtowcs_state;
    }
#endif

  if (dst == NULL)
    {
      /* Ignore original len value and do not alter src pointer if the
         dst pointer is NULL.  */
      len = (size_t)-1;
      tmp_src = *src;
      src = &tmp_src;
    }      
  
  max = len;
  while (len > 0)
    {
      bytes = mbrtowc (ptr, *src, nms, ps);
      if (bytes > 0)
	{
	  *src += bytes;
	  nms -= bytes;
	  ++count;
	  ptr = (dst == NULL) ? NULL : ptr + 1;
	  --len;
	}
      else if (bytes == -2)
	{
	  *src += nms;
	  return count;
	}
      else if (bytes == 0)
	{
	  *src = NULL;
	  return count;
	}
      else
	{
	  ps->__count = 0;
	  _REENT_ERRNO(r) = EILSEQ;
	  return (size_t)-1;
	}
    }

  return (size_t)max;
}
