maint: update copyright
[gnulib.git] / lib / uniconv / u16-conv-to-enc.c
1 /* Conversion from UTF-16 to legacy encodings.
2    Copyright (C) 2002, 2006-2014 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify it
5    under the terms of the GNU Lesser General Public License as published
6    by the Free Software Foundation; either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Lesser General Public License for more details.
13
14    You should have received a copy of the GNU Lesser General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
16
17 /* Written by Bruno Haible <bruno@clisp.org>.  */
18
19 #include <config.h>
20
21 /* Specification.  */
22 #include "uniconv.h"
23
24 #include <errno.h>
25 #include <stdlib.h>
26 #include <string.h>
27
28 #include "striconveha.h"
29 #include "unistr.h"
30
31 #define SIZEOF(array) (sizeof (array) / sizeof (array[0]))
32
33 /* Name of UTF-16 encoding with machine dependent endianness and alignment.  */
34 #if defined _LIBICONV_VERSION || (((__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 2)) && !defined __UCLIBC__)
35 # ifdef WORDS_BIGENDIAN
36 #  define UTF16_NAME "UTF-16BE"
37 # else
38 #  define UTF16_NAME "UTF-16LE"
39 # endif
40 #endif
41
42
43 #if !defined UTF16_NAME
44
45 /* A variant of u16_to_u8 that treats an incomplete sequence of units at the
46    end as a harmless no-op, rather than reporting it as an EILSEQ error.  */
47
48 #define FUNC u16_to_u8_lenient
49 #define SRC_UNIT uint16_t
50 #define DST_UNIT uint8_t
51
52 static DST_UNIT *
53 FUNC (const SRC_UNIT *s, size_t n, DST_UNIT *resultbuf, size_t *lengthp)
54 {
55   const SRC_UNIT *s_end = s + n;
56   /* Output string accumulator.  */
57   DST_UNIT *result;
58   size_t allocated;
59   size_t length;
60
61   if (resultbuf != NULL)
62     {
63       result = resultbuf;
64       allocated = *lengthp;
65     }
66   else
67     {
68       result = NULL;
69       allocated = 0;
70     }
71   length = 0;
72   /* Invariants:
73      result is either == resultbuf or == NULL or malloc-allocated.
74      If length > 0, then result != NULL.  */
75
76   while (s < s_end)
77     {
78       ucs4_t uc;
79       int count;
80
81       /* Fetch a Unicode character from the input string.  */
82       count = u16_mbtoucr (&uc, s, s_end - s);
83       if (count < 0)
84         {
85           if (count == -2)
86             /* Incomplete sequence of units.  */
87             break;
88           if (!(result == resultbuf || result == NULL))
89             free (result);
90           errno = EILSEQ;
91           return NULL;
92         }
93       s += count;
94
95       /* Store it in the output string.  */
96       count = u8_uctomb (result + length, uc, allocated - length);
97       if (count == -1)
98         {
99           if (!(result == resultbuf || result == NULL))
100             free (result);
101           errno = EILSEQ;
102           return NULL;
103         }
104       if (count == -2)
105         {
106           DST_UNIT *memory;
107
108           allocated = (allocated > 0 ? 2 * allocated : 12);
109           if (length + 6 > allocated)
110             allocated = length + 6;
111           if (result == resultbuf || result == NULL)
112             memory = (DST_UNIT *) malloc (allocated * sizeof (DST_UNIT));
113           else
114             memory =
115               (DST_UNIT *) realloc (result, allocated * sizeof (DST_UNIT));
116
117           if (memory == NULL)
118             {
119               if (!(result == resultbuf || result == NULL))
120                 free (result);
121               errno = ENOMEM;
122               return NULL;
123             }
124           if (result == resultbuf && length > 0)
125             memcpy ((char *) memory, (char *) result,
126                     length * sizeof (DST_UNIT));
127           result = memory;
128           count = u8_uctomb (result + length, uc, allocated - length);
129           if (count < 0)
130             abort ();
131         }
132       length += count;
133     }
134
135   if (length == 0)
136     {
137       if (result == NULL)
138         {
139           /* Return a non-NULL value.  NULL means error.  */
140           result = (DST_UNIT *) malloc (1);
141           if (result == NULL)
142             {
143               errno = ENOMEM;
144               return NULL;
145             }
146         }
147     }
148   else if (result != resultbuf && length < allocated)
149     {
150       /* Shrink the allocated memory if possible.  */
151       DST_UNIT *memory;
152
153       memory = (DST_UNIT *) realloc (result, length * sizeof (DST_UNIT));
154       if (memory != NULL)
155         result = memory;
156     }
157
158   *lengthp = length;
159   return result;
160 }
161
162 #undef DST_UNIT
163 #undef SRC_UNIT
164 #undef FUNC
165
166 #endif
167
168
169 #define FUNC u16_conv_to_encoding
170 #define UNIT uint16_t
171 #define U_TO_U8 u16_to_u8_lenient
172 #define U_MBLEN u16_mblen
173 #if defined UTF16_NAME
174 # define UTF_NAME UTF16_NAME
175 # define HAVE_UTF_NAME 1
176 #endif
177 #include "u-conv-to-enc.h"