Allow overriding the newline conversion for EBCDIC encodings.
[libiconv.git] / lib / tcvn.h
blobdd981c448cffe9c3073806ec48be6798f033a6f3
1 /*
2 * Copyright (C) 1999-2002, 2004, 2016, 2019 Free Software Foundation, Inc.
3 * This file is part of the GNU LIBICONV Library.
5 * The GNU LIBICONV Library is free software; you can redistribute it
6 * and/or modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either version 2.1
8 * of the License, or (at your option) any later version.
10 * The GNU LIBICONV Library is distributed in the hope that it will be
11 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with the GNU LIBICONV Library; see the file COPYING.LIB.
17 * If not, see <https://www.gnu.org/licenses/>.
21 * TCVN-5712
24 #include "flushwc.h"
25 #include "vietcomb.h"
27 static const unsigned char tcvn_comb_table[] = {
28 0xb0, 0xb3, 0xb2, 0xb1, 0xb4,
31 /* The possible bases in viet_comp_table_data:
32 0x0041..0x0045, 0x0047..0x0049, 0x004B..0x0050, 0x0052..0x0057,
33 0x0059..0x005A, 0x0061..0x0065, 0x0067..0x0069, 0x006B..0x0070,
34 0x0072..0x0077, 0x0079..0x007A, 0x00A5, 0x00C2, 0x00CA, 0x00D3..0x00D6,
35 0x00DA, 0x00E2, 0x00EA, 0x00F3..0x00F6, 0x00FA, 0x0102..0x0103,
36 0x0168..0x0169, 0x01A0..0x01A1, 0x01AF..0x01B0. */
37 static const unsigned int tcvn_comp_bases[] = {
38 0x06fdfbbe, 0x06fdfbbe, 0x00000000, 0x00000020, 0x04780404, 0x04780404,
39 0x0000000c, 0x00000000, 0x00000000, 0x00000300, 0x00000000, 0x00018003
42 static const unsigned short tcvn_2uni_1[24] = {
43 /* 0x00 */
44 0x0000, 0x00da, 0x1ee4, 0x0003, 0x1eea, 0x1eec, 0x1eee, 0x0007,
45 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,
46 /* 0x10 */
47 0x0010, 0x1ee8, 0x1ef0, 0x1ef2, 0x1ef6, 0x1ef8, 0x00dd, 0x1ef4,
49 static const unsigned short tcvn_2uni_2[128] = {
50 /* 0x80 */
51 0x00c0, 0x1ea2, 0x00c3, 0x00c1, 0x1ea0, 0x1eb6, 0x1eac, 0x00c8,
52 0x1eba, 0x1ebc, 0x00c9, 0x1eb8, 0x1ec6, 0x00cc, 0x1ec8, 0x0128,
53 /* 0x90 */
54 0x00cd, 0x1eca, 0x00d2, 0x1ece, 0x00d5, 0x00d3, 0x1ecc, 0x1ed8,
55 0x1edc, 0x1ede, 0x1ee0, 0x1eda, 0x1ee2, 0x00d9, 0x1ee6, 0x0168,
56 /* 0xa0 */
57 0x00a0, 0x0102, 0x00c2, 0x00ca, 0x00d4, 0x01a0, 0x01af, 0x0110,
58 0x0103, 0x00e2, 0x00ea, 0x00f4, 0x01a1, 0x01b0, 0x0111, 0x1eb0,
59 /* 0xb0 */
60 0x0300, 0x0309, 0x0303, 0x0301, 0x0323, 0x00e0, 0x1ea3, 0x00e3,
61 0x00e1, 0x1ea1, 0x1eb2, 0x1eb1, 0x1eb3, 0x1eb5, 0x1eaf, 0x1eb4,
62 /* 0xc0 */
63 0x1eae, 0x1ea6, 0x1ea8, 0x1eaa, 0x1ea4, 0x1ec0, 0x1eb7, 0x1ea7,
64 0x1ea9, 0x1eab, 0x1ea5, 0x1ead, 0x00e8, 0x1ec2, 0x1ebb, 0x1ebd,
65 /* 0xd0 */
66 0x00e9, 0x1eb9, 0x1ec1, 0x1ec3, 0x1ec5, 0x1ebf, 0x1ec7, 0x00ec,
67 0x1ec9, 0x1ec4, 0x1ebe, 0x1ed2, 0x0129, 0x00ed, 0x1ecb, 0x00f2,
68 /* 0xe0 */
69 0x1ed4, 0x1ecf, 0x00f5, 0x00f3, 0x1ecd, 0x1ed3, 0x1ed5, 0x1ed7,
70 0x1ed1, 0x1ed9, 0x1edd, 0x1edf, 0x1ee1, 0x1edb, 0x1ee3, 0x00f9,
71 /* 0xf0 */
72 0x1ed6, 0x1ee7, 0x0169, 0x00fa, 0x1ee5, 0x1eeb, 0x1eed, 0x1eef,
73 0x1ee9, 0x1ef1, 0x1ef3, 0x1ef7, 0x1ef9, 0x00fd, 0x1ef5, 0x1ed0,
76 /* In the TCVN to Unicode direction, the state contains a buffered
77 character, or 0 if none. */
79 static int
80 tcvn_mbtowc (conv_t conv, ucs4_t *pwc, const unsigned char *s, size_t n)
82 unsigned char c = *s;
83 unsigned short wc;
84 unsigned short last_wc;
85 if (c < 0x18)
86 wc = tcvn_2uni_1[c];
87 else if (c < 0x80)
88 wc = c;
89 else
90 wc = tcvn_2uni_2[c-0x80];
91 last_wc = conv->istate;
92 if (last_wc) {
93 if (wc >= 0x0300 && wc < 0x0340) {
94 /* See whether last_wc and wc can be combined. */
95 unsigned int k;
96 unsigned int i1, i2;
97 switch (wc) {
98 case 0x0300: k = 0; break;
99 case 0x0301: k = 1; break;
100 case 0x0303: k = 2; break;
101 case 0x0309: k = 3; break;
102 case 0x0323: k = 4; break;
103 default: abort();
105 i1 = viet_comp_table[k].idx;
106 i2 = i1 + viet_comp_table[k].len-1;
107 if (last_wc >= viet_comp_table_data[i1].base
108 && last_wc <= viet_comp_table_data[i2].base) {
109 unsigned int i;
110 for (;;) {
111 i = (i1+i2)>>1;
112 if (last_wc == viet_comp_table_data[i].base)
113 break;
114 if (last_wc < viet_comp_table_data[i].base) {
115 if (i1 == i)
116 goto not_combining;
117 i2 = i;
118 } else {
119 if (i1 != i)
120 i1 = i;
121 else {
122 i = i2;
123 if (last_wc == viet_comp_table_data[i].base)
124 break;
125 goto not_combining;
129 last_wc = viet_comp_table_data[i].composed;
130 /* Output the combined character. */
131 conv->istate = 0;
132 *pwc = (ucs4_t) last_wc;
133 return 1;
136 not_combining:
137 /* Output the buffered character. */
138 conv->istate = 0;
139 *pwc = (ucs4_t) last_wc;
140 return 0; /* Don't advance the input pointer. */
142 if (wc >= 0x0041 && wc <= 0x01b0
143 && ((tcvn_comp_bases[(wc - 0x0040) >> 5] >> (wc & 0x1f)) & 1)) {
144 /* wc is a possible match in viet_comp_table_data. Buffer it. */
145 conv->istate = wc;
146 return RET_TOOFEW(1);
147 } else {
148 /* Output wc immediately. */
149 *pwc = (ucs4_t) wc;
150 return 1;
154 #define tcvn_flushwc normal_flushwc
156 static const unsigned char tcvn_page00[96+184] = {
157 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
158 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
159 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
160 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
161 0x80, 0x83, 0xa2, 0x82, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
162 0x87, 0x8a, 0xa3, 0x00, 0x8d, 0x90, 0x00, 0x00, /* 0xc8-0xcf */
163 0x00, 0x00, 0x92, 0x95, 0xa4, 0x94, 0x00, 0x00, /* 0xd0-0xd7 */
164 0x00, 0x9d, 0x01, 0x00, 0x00, 0x16, 0x00, 0x00, /* 0xd8-0xdf */
165 0xb5, 0xb8, 0xa9, 0xb7, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
166 0xcc, 0xd0, 0xaa, 0x00, 0xd7, 0xdd, 0x00, 0x00, /* 0xe8-0xef */
167 0x00, 0x00, 0xdf, 0xe3, 0xab, 0xe2, 0x00, 0x00, /* 0xf0-0xf7 */
168 0x00, 0xef, 0xf3, 0x00, 0x00, 0xfd, 0x00, 0x00, /* 0xf8-0xff */
169 /* 0x0100 */
170 0x00, 0x00, 0xa1, 0xa8, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
171 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
172 0xa7, 0xae, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
173 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
174 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
175 0x8f, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
176 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
177 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
178 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
179 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
180 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
181 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
182 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
183 0x9f, 0xf2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
184 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
185 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
186 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
187 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
188 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
189 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
190 0xa5, 0xac, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
191 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa6, /* 0xa8-0xaf */
192 0xad, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
194 static const unsigned char tcvn_page03[40] = {
195 0xb0, 0xb3, 0x00, 0xb2, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
196 0x00, 0xb1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
197 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
198 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
199 0x00, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
201 static const unsigned char tcvn_page1e[96] = {
202 0x84, 0xb9, 0x81, 0xb6, 0xc4, 0xca, 0xc1, 0xc7, /* 0xa0-0xa7 */
203 0xc2, 0xc8, 0xc3, 0xc9, 0x86, 0xcb, 0xc0, 0xbe, /* 0xa8-0xaf */
204 0xaf, 0xbb, 0xba, 0xbc, 0xbf, 0xbd, 0x85, 0xc6, /* 0xb0-0xb7 */
205 0x8b, 0xd1, 0x88, 0xce, 0x89, 0xcf, 0xda, 0xd5, /* 0xb8-0xbf */
206 0xc5, 0xd2, 0xcd, 0xd3, 0xd9, 0xd4, 0x8c, 0xd6, /* 0xc0-0xc7 */
207 0x8e, 0xd8, 0x91, 0xde, 0x96, 0xe4, 0x93, 0xe1, /* 0xc8-0xcf */
208 0xff, 0xe8, 0xdb, 0xe5, 0xe0, 0xe6, 0xf0, 0xe7, /* 0xd0-0xd7 */
209 0x97, 0xe9, 0x9b, 0xed, 0x98, 0xea, 0x99, 0xeb, /* 0xd8-0xdf */
210 0x9a, 0xec, 0x9c, 0xee, 0x02, 0xf4, 0x9e, 0xf1, /* 0xe0-0xe7 */
211 0x11, 0xf8, 0x04, 0xf5, 0x05, 0xf6, 0x06, 0xf7, /* 0xe8-0xef */
212 0x12, 0xf9, 0x13, 0xfa, 0x17, 0xfe, 0x14, 0xfb, /* 0xf0-0xf7 */
213 0x15, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
216 static int
217 tcvn_wctomb (conv_t conv, unsigned char *r, ucs4_t wc, size_t n)
219 unsigned char c = 0;
220 if (wc < 0x0080 && (wc >= 0x0020 || (0x00fe0076U & (1U << wc)) == 0)) {
221 *r = wc;
222 return 1;
224 else if (wc >= 0x00a0 && wc < 0x01b8)
225 c = tcvn_page00[wc-0x00a0];
226 else if (wc >= 0x0300 && wc < 0x0328)
227 c = tcvn_page03[wc-0x0300];
228 else if (wc >= 0x0340 && wc < 0x0342) /* deprecated Vietnamese tone marks */
229 c = tcvn_page03[wc-0x0340];
230 else if (wc >= 0x1ea0 && wc < 0x1f00)
231 c = tcvn_page1e[wc-0x1ea0];
232 if (c != 0) {
233 *r = c;
234 return 1;
236 /* Try compatibility or canonical decomposition. */
238 /* Binary search through viet_decomp_table. */
239 unsigned int i1 = 0;
240 unsigned int i2 = sizeof(viet_decomp_table)/sizeof(viet_decomp_table[0])-1;
241 if (wc >= viet_decomp_table[i1].composed
242 && wc <= viet_decomp_table[i2].composed) {
243 unsigned int i;
244 for (;;) {
245 /* Here i2 - i1 > 0. */
246 i = (i1+i2)>>1;
247 if (wc == viet_decomp_table[i].composed)
248 break;
249 if (wc < viet_decomp_table[i].composed) {
250 if (i1 == i)
251 return RET_ILUNI;
252 /* Here i1 < i < i2. */
253 i2 = i;
254 } else {
255 /* Here i1 <= i < i2. */
256 if (i1 != i)
257 i1 = i;
258 else {
259 /* Here i2 - i1 = 1. */
260 i = i2;
261 if (wc == viet_decomp_table[i].composed)
262 break;
263 else
264 return RET_ILUNI;
268 /* Found a compatibility or canonical decomposition. */
269 wc = viet_decomp_table[i].base;
270 /* wc is one of 0x0020, 0x0041..0x005a, 0x0061..0x007a, 0x00a5, 0x00a8,
271 0x00c2, 0x00c5..0x00c7, 0x00ca, 0x00cf, 0x00d3, 0x00d4, 0x00d6,
272 0x00d8, 0x00da, 0x00dc, 0x00e2, 0x00e5..0x00e7, 0x00ea, 0x00ef,
273 0x00f3, 0x00f4, 0x00f6, 0x00f8, 0x00fc, 0x0102, 0x0103, 0x01a0,
274 0x01a1, 0x01af, 0x01b0. */
275 if (wc < 0x0080)
276 c = wc;
277 else {
278 c = tcvn_page00[wc-0x00a0];
279 if (c == 0)
280 return RET_ILUNI;
282 if (n < 2)
283 return RET_TOOSMALL;
284 r[0] = c;
285 r[1] = tcvn_comb_table[viet_decomp_table[i].comb1];
286 return 2;
289 return RET_ILUNI;