/* Conversion from UTF-16 to legacy encodings.
Copyright (C) 2002, 2006-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see . */
/* Written by Bruno Haible . */
#include
/* Specification. */
#include "uniconv.h"
#include
#include
#include
#include "striconveha.h"
#include "unistr.h"
#define SIZEOF(array) (sizeof (array) / sizeof (array[0]))
/* Name of UTF-16 encoding with machine dependent endianness and alignment. */
#if defined _LIBICONV_VERSION || (((__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 2)) && !defined __UCLIBC__)
# ifdef WORDS_BIGENDIAN
# define UTF16_NAME "UTF-16BE"
# else
# define UTF16_NAME "UTF-16LE"
# endif
#endif
#if !defined UTF16_NAME
/* A variant of u16_to_u8 that treats an incomplete sequence of units at the
end as a harmless no-op, rather than reporting it as an EILSEQ error. */
#define FUNC u16_to_u8_lenient
#define SRC_UNIT uint16_t
#define DST_UNIT uint8_t
static DST_UNIT *
FUNC (const SRC_UNIT *s, size_t n, DST_UNIT *resultbuf, size_t *lengthp)
{
const SRC_UNIT *s_end = s + n;
/* Output string accumulator. */
DST_UNIT *result;
size_t allocated;
size_t length;
if (resultbuf != NULL)
{
result = resultbuf;
allocated = *lengthp;
}
else
{
result = NULL;
allocated = 0;
}
length = 0;
/* Invariants:
result is either == resultbuf or == NULL or malloc-allocated.
If length > 0, then result != NULL. */
while (s < s_end)
{
ucs4_t uc;
int count;
/* Fetch a Unicode character from the input string. */
count = u16_mbtoucr (&uc, s, s_end - s);
if (count < 0)
{
if (count == -2)
/* Incomplete sequence of units. */
break;
if (!(result == resultbuf || result == NULL))
free (result);
errno = EILSEQ;
return NULL;
}
s += count;
/* Store it in the output string. */
count = u8_uctomb (result + length, uc, allocated - length);
if (count == -1)
{
if (!(result == resultbuf || result == NULL))
free (result);
errno = EILSEQ;
return NULL;
}
if (count == -2)
{
DST_UNIT *memory;
allocated = (allocated > 0 ? 2 * allocated : 12);
if (length + 6 > allocated)
allocated = length + 6;
if (result == resultbuf || result == NULL)
memory = (DST_UNIT *) malloc (allocated * sizeof (DST_UNIT));
else
memory =
(DST_UNIT *) realloc (result, allocated * sizeof (DST_UNIT));
if (memory == NULL)
{
if (!(result == resultbuf || result == NULL))
free (result);
errno = ENOMEM;
return NULL;
}
if (result == resultbuf && length > 0)
memcpy ((char *) memory, (char *) result,
length * sizeof (DST_UNIT));
result = memory;
count = u8_uctomb (result + length, uc, allocated - length);
if (count < 0)
abort ();
}
length += count;
}
if (length == 0)
{
if (result == NULL)
{
/* Return a non-NULL value. NULL means error. */
result = (DST_UNIT *) malloc (1);
if (result == NULL)
{
errno = ENOMEM;
return NULL;
}
}
}
else if (result != resultbuf && length < allocated)
{
/* Shrink the allocated memory if possible. */
DST_UNIT *memory;
memory = (DST_UNIT *) realloc (result, length * sizeof (DST_UNIT));
if (memory != NULL)
result = memory;
}
*lengthp = length;
return result;
}
#undef DST_UNIT
#undef SRC_UNIT
#undef FUNC
#endif
#define FUNC u16_conv_to_encoding
#define UNIT uint16_t
#define U_TO_U8 u16_to_u8_lenient
#define U_MBLEN u16_mblen
#if defined UTF16_NAME
# define UTF_NAME UTF16_NAME
# define HAVE_UTF_NAME 1
#endif
#include "u-conv-to-enc.h"