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 *
FUNC(const SRC_UNIT * s,size_t n,DST_UNIT * resultbuf,size_t * lengthp)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"
178