maint: update copyright
[gnulib.git] / lib / uniconv / u16-conv-to-enc.c
index 4e92570..1f8672b 100644 (file)
@@ -1,20 +1,18 @@
 /* Conversion from UTF-16 to legacy encodings.
-   Copyright (C) 2002, 2006-2007 Free Software Foundation, Inc.
+   Copyright (C) 2002, 2006-2014 Free Software Foundation, Inc.
 
-   This program is free software; you can redistribute it and/or modify it
-   under the terms of the GNU Library General Public License as published
-   by the Free Software Foundation; either version 2, or (at your option)
-   any later version.
+   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
-   Library General Public License for more details.
+   Lesser General Public License for more details.
 
-   You should have received a copy of the GNU Library General Public
-   License along with this program; if not, write to the Free Software
-   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
-   USA.  */
+   You should have received a copy of the GNU Lesser General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 /* Written by Bruno Haible <bruno@clisp.org>.  */
 
@@ -33,7 +31,7 @@
 #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)
+#if defined _LIBICONV_VERSION || (((__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 2)) && !defined __UCLIBC__)
 # ifdef WORDS_BIGENDIAN
 #  define UTF16_NAME "UTF-16BE"
 # else
 # 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
+#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