1/*
2 * Copyright (C) 1999-2001, 2005 Free Software Foundation, Inc.
3 * This file is part of the GNU LIBICONV Library.
4 *
5 * The GNU LIBICONV Library is free software; you can redistribute it
6 * and/or modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
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 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public
16 * License along with the GNU LIBICONV Library; see the file COPYING.LIB.
17 * If not, write to the Free Software Foundation, Inc., 51 Franklin Street,
18 * Fifth Floor, Boston, MA 02110-1301, USA.
19 */
20
21/*
22 * BIG5-2003
23 */
24
25/*
26 * BIG5-2003 is a slightly extended and slightly modified version of BIG5.
27 * It is actually nearer to Microsoft CP950 than to BIG5. The differences
28 * between EASTASIA/OTHER/BIG5.TXT found on ftp.unicode.org and BIG5-2003.TXT
29 * are as follows:
30 *
31 * 1. Some characters in the symbols area (0xA140..0xA2CE) are defined
32 *    differently:
33 *
34 *     code   BIG5.TXT                       BIG5-2003.TXT
35 *    0xA145  0x2022 # BULLET                0x2027 # HYPHENATION POINT
36 *    0xA14E  0xFF64 # HALFWIDTH IDEOGRAPHIC COMMA
37 *                                           0xFE51 # SMALL IDEOGRAPHIC COMMA
38 *    0xA156  0x2013 # EN DASH               0x2015 # HORIZONTAL BAR
39 *    0xA15A    ---                          0x2574 # BOX DRAWINGS LIGHT LEFT
40 *    0xA1C3    ---                          0xFFE3 # FULLWIDTH MACRON
41 *    0xA1C5    ---                          0x02CD # MODIFIER LETTER LOW MACRON
42 *    0xA1E3  0x223C # TILDE OPERATOR        0xFF5E # FULLWIDTH TILDE
43 *    0xA1F2  0x2641 # EARTH                 0x2295 # CIRCLED PLUS
44 *    0xA1F3  0x2609 # SUN                   0x2299 # CIRCLED DOT OPERATOR
45 *    0xA1FE    ---                          0xFF0F # FULLWIDTH SOLIDUS
46 *    0xA240    ---                          0xFF3C # FULLWIDTH REVERSE SOLIDUS
47 *    0xA241  0xFF0F # FULLWIDTH SOLIDUS     0x2215 # DIVISION SLASH
48 *    0xA242  0xFF3C # FULLWIDTH REVERSE SOLIDUS
49 *                                           0xFE68 # SMALL REVERSE SOLIDUS
50 *    0xA244  0x00A5 # YEN SIGN              0xFFE5 # FULLWIDTH YEN SIGN
51 *    0xA246  0x00A2 # CENT SIGN             0xFFE0 # FULLWIDTH CENT SIGN
52 *    0xA247  0x00A3 # POUND SIGN            0xFFE1 # FULLWIDTH POUND SIGN
53 *    0xA2A4  0x2550 # BOX DRAWINGS DOUBLE HORIZONTAL
54 *                                           0x2501 # BOX DRAWINGS HEAVY HORIZONTAL
55 *    0xA2A5  0x255E # BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE
56 *                                           0x251D # BOX DRAWINGS VERTICAL LIGHT AND RIGHT HEAVY
57 *    0xA2A6  0x256A # BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE
58 *                                           0x253F # BOX DRAWINGS VERTICAL LIGHT AND HORIZONTAL HEAVY
59 *    0xA2A7  0x2561 # BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE
60 *                                           0x2525 # BOX DRAWINGS VERTICAL LIGHT AND LEFT HEAVY
61 *    0xA2CC    ---                          0x3038 # HANGZHOU NUMERAL TEN
62 *    0xA2CD  0x5344                         0x3039 # HANGZHOU NUMERAL TWENTY
63 *    0xA2CE    ---                          0x303A # HANGZHOU NUMERAL THIRTY
64 *
65 * 2. A control symbols area is added:
66 *
67 *         code
68 *    0xA3C0..0xA3E0  U+2400..U+2421
69 *
70 * 3. The Euro sign is added:
71 *
72 *     code
73 *    0xA3E1  0x20AC # EURO SIGN
74 *
75 * 4. Some characters in the main area are defined differently:
76 *
77 *     code   BIG5.TXT                       BIG5-2003.TXT
78 *    0xC255  0x5F5D                         0x5F5E
79 *
80 * 5. The area 0xC6A1..0xC7FE is organized differently:
81 *
82 *         code
83 *    0xC6A1..0xC6BE  numerals (was in BIG5.TXT at 0xC7E9..0xC7FC)
84 *    0xC6BF..0xC6D7  radicals
85 *    0xC6D8..0xC6E6  rarely used symbols
86 *    0xC6E7..0xC77A  hiragana (U+3041..U+3093, was in BIG5.TXT at 0xC6A5..0xC6F7)
87 *    0xC77B..0xC7F2  katakana (U+30A1..U+30F6, was in BIG5.TXT at 0xC6F8..0xC7B0)
88 *
89 * 6. Some characters are added at 0xF9D6..0xF9DC.
90 *
91 * 7. Box drawing characters are added at 0xF9DD..0xF9FE.
92 *
93 *    Note: 4 of these characters are mapped in a non-inversible way, because
94 *    Unicode does not yet include the corresponding characters:
95 *
96 *     code                                           Unicode approximation
97 *    0xF9FA  BOX DRAWINGS DOUBLE ARC DOWN AND RIGHT  0x2554
98 *    0xF9FB  BOX DRAWINGS DOUBLE ARC DOWN AND LEFT   0x2557
99 *    0xF9FC  BOX DRAWINGS DOUBLE ARC UP AND RIGHT    0x255A
100 *    0xF9FD  BOX DRAWINGS DOUBLE ARC UP AND LEFT     0x255D
101 *
102 * 8. Private area mappings are added:
103 *
104 *              code                 Unicode
105 *    0x{81..8D}{40..7E,A1..FE}  U+EEB8..U+F6B0
106 *    0x{8E..A0}{40..7E,A1..FE}  U+E311..U+EEB7
107 *    0x{FA..FE}{40..7E,A1..FE}  U+E000..U+E310
108 *
109 *    These mappings are not contained in the BSMI Big5-2003 standard. However,
110 *    they were contained in a draft of it.
111 */
112
113static const unsigned short big5_2003_2uni_pagea1[314] = {
114  /* 0xa1 */
115  0x3000, 0xff0c, 0x3001, 0x3002, 0xff0e, 0x2027, 0xff1b, 0xff1a,
116  0xff1f, 0xff01, 0xfe30, 0x2026, 0x2025, 0xfe50, 0xfe51, 0xfe52,
117  0x00b7, 0xfe54, 0xfe55, 0xfe56, 0xfe57, 0xff5c, 0x2015, 0xfe31,
118  0x2014, 0xfe33, 0x2574, 0xfe34, 0xfe4f, 0xff08, 0xff09, 0xfe35,
119  0xfe36, 0xff5b, 0xff5d, 0xfe37, 0xfe38, 0x3014, 0x3015, 0xfe39,
120  0xfe3a, 0x3010, 0x3011, 0xfe3b, 0xfe3c, 0x300a, 0x300b, 0xfe3d,
121  0xfe3e, 0x3008, 0x3009, 0xfe3f, 0xfe40, 0x300c, 0x300d, 0xfe41,
122  0xfe42, 0x300e, 0x300f, 0xfe43, 0xfe44, 0xfe59, 0xfe5a, 0xfe5b,
123  0xfe5c, 0xfe5d, 0xfe5e, 0x2018, 0x2019, 0x201c, 0x201d, 0x301d,
124  0x301e, 0x2035, 0x2032, 0xff03, 0xff06, 0xff0a, 0x203b, 0x00a7,
125  0x3003, 0x25cb, 0x25cf, 0x25b3, 0x25b2, 0x25ce, 0x2606, 0x2605,
126  0x25c7, 0x25c6, 0x25a1, 0x25a0, 0x25bd, 0x25bc, 0x32a3, 0x2105,
127  0x203e, 0xffe3, 0xff3f, 0x02cd, 0xfe49, 0xfe4a, 0xfe4d, 0xfe4e,
128  0xfe4b, 0xfe4c, 0xfe5f, 0xfe60, 0xfe61, 0xff0b, 0xff0d, 0x00d7,
129  0x00f7, 0x00b1, 0x221a, 0xff1c, 0xff1e, 0xff1d, 0x2266, 0x2267,
130  0x2260, 0x221e, 0x2252, 0x2261, 0xfe62, 0xfe63, 0xfe64, 0xfe65,
131  0xfe66, 0xff5e, 0x2229, 0x222a, 0x22a5, 0x2220, 0x221f, 0x22bf,
132  0x33d2, 0x33d1, 0x222b, 0x222e, 0x2235, 0x2234, 0x2640, 0x2642,
133  0x2295, 0x2299, 0x2191, 0x2193, 0x2190, 0x2192, 0x2196, 0x2197,
134  0x2199, 0x2198, 0x2225, 0x2223, 0xff0f,
135  /* 0xa2 */
136  0xff3c, 0x2215, 0xfe68, 0xff04, 0xffe5, 0x3012, 0xffe0, 0xffe1,
137  0xff05, 0xff20, 0x2103, 0x2109, 0xfe69, 0xfe6a, 0xfe6b, 0x33d5,
138  0x339c, 0x339d, 0x339e, 0x33ce, 0x33a1, 0x338e, 0x338f, 0x33c4,
139  0x00b0, 0x5159, 0x515b, 0x515e, 0x515d, 0x5161, 0x5163, 0x55e7,
140  0x74e9, 0x7cce, 0x2581, 0x2582, 0x2583, 0x2584, 0x2585, 0x2586,
141  0x2587, 0x2588, 0x258f, 0x258e, 0x258d, 0x258c, 0x258b, 0x258a,
142  0x2589, 0x253c, 0x2534, 0x252c, 0x2524, 0x251c, 0x2594, 0x2500,
143  0x2502, 0x2595, 0x250c, 0x2510, 0x2514, 0x2518, 0x256d, 0x256e,
144  0x2570, 0x256f, 0x2501, 0x251d, 0x253f, 0x2525, 0x25e2, 0x25e3,
145  0x25e5, 0x25e4, 0x2571, 0x2572, 0x2573, 0xff10, 0xff11, 0xff12,
146  0xff13, 0xff14, 0xff15, 0xff16, 0xff17, 0xff18, 0xff19, 0x2160,
147  0x2161, 0x2162, 0x2163, 0x2164, 0x2165, 0x2166, 0x2167, 0x2168,
148  0x2169, 0x3021, 0x3022, 0x3023, 0x3024, 0x3025, 0x3026, 0x3027,
149  0x3028, 0x3029, 0x3038, 0x3039, 0x303a, 0xff21, 0xff22, 0xff23,
150  0xff24, 0xff25, 0xff26, 0xff27, 0xff28, 0xff29, 0xff2a, 0xff2b,
151  0xff2c, 0xff2d, 0xff2e, 0xff2f, 0xff30, 0xff31, 0xff32, 0xff33,
152  0xff34, 0xff35, 0xff36, 0xff37, 0xff38, 0xff39, 0xff3a, 0xff41,
153  0xff42, 0xff43, 0xff44, 0xff45, 0xff46, 0xff47, 0xff48, 0xff49,
154  0xff4a, 0xff4b, 0xff4c, 0xff4d, 0xff4e, 0xff4f, 0xff50, 0xff51,
155  0xff52, 0xff53, 0xff54, 0xff55, 0xff56,
156};
157
158static const unsigned short big5_2003_2uni_pagec6[70] = {
159  /* 0xc6a1 */
160  0x2460, 0x2461, 0x2462, 0x2463, 0x2464, 0x2465, 0x2466, 0x2467,
161  0x2468, 0x2469, 0x2474, 0x2475, 0x2476, 0x2477, 0x2478, 0x2479,
162  0x247a, 0x247b, 0x247c, 0x247d, 0x2170, 0x2171, 0x2172, 0x2173,
163  0x2174, 0x2175, 0x2176, 0x2177, 0x2178, 0x2179, 0x2f02, 0x2f03,
164  0x2f05, 0x2f07, 0x2f0c, 0x2f0d, 0x2f0e, 0x2f13, 0x2f16, 0x2f19,
165  0x2f1b, 0x2f22, 0x2f27, 0x2f2e, 0x2f33, 0x2f34, 0x2f35, 0x2f39,
166  0x2f3a, 0x2f41, 0x2f46, 0x2f67, 0x2f68, 0x2fa1, 0x2faa, 0x00a8,
167  0xff3e, 0x30fd, 0x30fe, 0x309d, 0x309e, 0xfffd, 0xfffd, 0x3005,
168  0x3006, 0x3007, 0x30fc, 0xff3b, 0xff3d, 0x273d,
169};
170
171static const unsigned short big5_2003_2uni_pagef9[41] = {
172  /* 0xf9d6 */
173  0x7881, 0x92b9, 0x88cf, 0x58bb, 0x6052, 0x7ca7, 0x5afa,
174  /* 0xf9dd */
175  0x2554, 0x2566, 0x2557, 0x2560, 0x256c, 0x2563, 0x255a, 0x2569,
176  0x255d, 0x2552, 0x2564, 0x2555, 0x255e, 0x256a, 0x2561, 0x2558,
177  0x2567, 0x255b, 0x2553, 0x2565, 0x2556, 0x255f, 0x256b, 0x2562,
178  0x2559, 0x2568, 0x255c, 0x2551, 0x2550,
179  0x2554, 0x2557, 0x255a, 0x255d, /* not invertible */
180  0x2593,
181};
182
183static int
184big5_2003_mbtowc (conv_t conv, ucs4_t *pwc, const unsigned char *s, int n)
185{
186  unsigned char c = *s;
187  /* Code set 0 (ASCII) */
188  if (c < 0x80)
189    return ascii_mbtowc(conv,pwc,s,n);
190  /* Code set 1 (BIG5 extended) */
191  if (c >= 0x81 && c < 0xff) {
192    if (n < 2)
193      return RET_TOOFEW(0);
194    {
195      unsigned char c2 = s[1];
196      if ((c2 >= 0x40 && c2 < 0x7f) || (c2 >= 0xa1 && c2 < 0xff)) {
197        if (c >= 0xa1) {
198          if (c < 0xa3) {
199            unsigned int i = 157 * (c - 0xa1) + (c2 - (c2 >= 0xa1 ? 0x62 : 0x40));
200            unsigned short wc = big5_2003_2uni_pagea1[i];
201            if (wc != 0xfffd) {
202              *pwc = (ucs4_t) wc;
203              return 2;
204            }
205          }
206          if (!((c == 0xc6 && c2 >= 0xa1) || c == 0xc7)) {
207            if (!(c == 0xc2 && c2 == 0x55)) {
208              int ret = big5_mbtowc(conv,pwc,s,2);
209              if (ret != RET_ILSEQ)
210                return ret;
211              if (c == 0xa3) {
212                if (c2 >= 0xc0 && c2 <= 0xe1) {
213                  *pwc = (c2 == 0xe1 ? 0x20ac : c2 == 0xe0 ? 0x2421 : 0x2340 + c2);
214                  return 2;
215                }
216              } else if (c == 0xf9) {
217                if (c2 >= 0xd6) {
218                  *pwc = big5_2003_2uni_pagef9[c2-0xd6];
219                  return 2;
220                }
221              } else if (c >= 0xfa) {
222                *pwc = 0xe000 + 157 * (c - 0xfa) + (c2 - (c2 >= 0xa1 ? 0x62 : 0x40));
223                return 2;
224              }
225            } else {
226              /* c == 0xc2 && c2 == 0x55. */
227              *pwc = 0x5f5e;
228              return 2;
229            }
230          } else {
231            /* (c == 0xc6 && c2 >= 0xa1) || c == 0xc7. */
232            unsigned int i = 157 * (c - 0xc6) + (c2 - (c2 >= 0xa1 ? 0x62 : 0x40));
233            if (i < 133) {
234              /* 63 <= i < 133. */
235              unsigned short wc = big5_2003_2uni_pagec6[i-63];
236              if (wc != 0xfffd) {
237                *pwc = (ucs4_t) wc;
238                return 2;
239              }
240            } else if (i < 216) {
241              /* 133 <= i < 216. Hiragana. */
242              *pwc = 0x3041 - 133 + i;
243              return 2;
244            } else if (i < 302) {
245              /* 216 <= i < 302. Katakana. */
246              *pwc = 0x30a1 - 216 + i;
247              return 2;
248            }
249          }
250        } else {
251          /* 0x81 <= c < 0xa1. */
252          *pwc = (c >= 0x8e ? 0xdb18 : 0xeeb8) + 157 * (c - 0x81)
253                 + (c2 - (c2 >= 0xa1 ? 0x62 : 0x40));
254          return 2;
255        }
256      }
257    }
258  }
259  return RET_ILSEQ;
260}
261
262static const unsigned char big5_2003_2charset_page25[29] = {
263  /* 0x2550 */
264  0xf9, 0xf8, 0xe6, 0xef, 0xdd, 0xe8, 0xf1, 0xdf,
265  0xec, 0xf5, 0xe3, 0xee, 0xf7, 0xe5, 0xe9, 0xf2,
266  0xe0, 0xeb, 0xf4, 0xe2, 0xe7, 0xf0, 0xde, 0xed,
267  0xf6, 0xe4, 0xea, 0xf3, 0xe1,
268};
269
270static int
271big5_2003_wctomb (conv_t conv, unsigned char *r, ucs4_t wc, int n)
272{
273  unsigned char buf[2];
274  int ret;
275
276  /* Code set 0 (ASCII) */
277  ret = ascii_wctomb(conv,r,wc,n);
278  if (ret != RET_ILUNI)
279    return ret;
280
281  /* Code set 1 (BIG5 extended) */
282  switch (wc >> 8) {
283    case 0x00:
284      if (wc == 0x00a8) { buf[0] = 0xc6; buf[1] = 0xd8; ret = 2; break; }
285      if (wc == 0x00a2 || wc == 0x00a3 || wc == 0x00a5)
286        return RET_ILUNI;
287      break;
288    case 0x02:
289      if (wc == 0x02cd) { buf[0] = 0xa1; buf[1] = 0xc5; ret = 2; break; }
290      break;
291    case 0x04:
292      return RET_ILUNI;
293    case 0x20:
294      if (wc == 0x2015) { buf[0] = 0xa1; buf[1] = 0x56; ret = 2; break; }
295      if (wc == 0x2027) { buf[0] = 0xa1; buf[1] = 0x45; ret = 2; break; }
296      if (wc == 0x20ac) { buf[0] = 0xa3; buf[1] = 0xe1; ret = 2; break; }
297      if (wc == 0x2013 || wc == 0x2022)
298        return RET_ILUNI;
299      break;
300    case 0x21:
301      if (wc >= 0x2170 && wc <= 0x2179) {
302        buf[0] = 0xc6; buf[1] = wc - 0x20bb; ret = 2;
303        break;
304      }
305      break;
306    case 0x22:
307      if (wc == 0x2215) { buf[0] = 0xa2; buf[1] = 0x41; ret = 2; break; }
308      if (wc == 0x2295) { buf[0] = 0xa1; buf[1] = 0xf2; ret = 2; break; }
309      if (wc == 0x2299) { buf[0] = 0xa1; buf[1] = 0xf3; ret = 2; break; }
310      if (wc == 0x223c)
311        return RET_ILUNI;
312      break;
313    case 0x24:
314      if (wc <= 0x241f) { buf[0] = 0xa3; buf[1] = wc - 0x2340; ret = 2; break; }
315      if (wc == 0x2421) { buf[0] = 0xa3; buf[1] = 0xe0; ret = 2; break; }
316      if (wc >= 0x2460 && wc <= 0x2469) {
317        buf[0] = 0xc6; buf[1] = wc - 0x23bf; ret = 2;
318        break;
319      }
320      if (wc >= 0x2474 && wc <= 0x247d) {
321        buf[0] = 0xc6; buf[1] = wc - 0x23c9; ret = 2;
322        break;
323      }
324      break;
325    case 0x25:
326      if (wc == 0x2501) { buf[0] = 0xa2; buf[1] = 0xa4; ret = 2; break; }
327      if (wc == 0x251d) { buf[0] = 0xa2; buf[1] = 0xa5; ret = 2; break; }
328      if (wc == 0x2525) { buf[0] = 0xa2; buf[1] = 0xa7; ret = 2; break; }
329      if (wc == 0x253f) { buf[0] = 0xa2; buf[1] = 0xa6; ret = 2; break; }
330      if (wc >= 0x2550 && wc <= 0x256c) {
331        buf[0] = 0xf9; buf[1] = big5_2003_2charset_page25[wc-0x2550]; ret = 2;
332        break;
333      }
334      if (wc == 0x2574) { buf[0] = 0xa1; buf[1] = 0x5a; ret = 2; break; }
335      if (wc == 0x2593) { buf[0] = 0xf9; buf[1] = 0xfe; ret = 2; break; }
336      break;
337    case 0x26:
338      if (wc == 0x2609 || wc == 0x2641)
339        return RET_ILUNI;
340      break;
341    case 0x27:
342      if (wc == 0x273d) { buf[0] = 0xc6; buf[1] = 0xe6; ret = 2; break; }
343      break;
344    case 0x2f:
345      if (wc == 0x2f02) { buf[0] = 0xc6; buf[1] = 0xbf; ret = 2; break; }
346      if (wc == 0x2f03) { buf[0] = 0xc6; buf[1] = 0xc0; ret = 2; break; }
347      if (wc == 0x2f05) { buf[0] = 0xc6; buf[1] = 0xc1; ret = 2; break; }
348      if (wc == 0x2f07) { buf[0] = 0xc6; buf[1] = 0xc2; ret = 2; break; }
349      if (wc == 0x2f0c) { buf[0] = 0xc6; buf[1] = 0xc3; ret = 2; break; }
350      if (wc == 0x2f0d) { buf[0] = 0xc6; buf[1] = 0xc4; ret = 2; break; }
351      if (wc == 0x2f0e) { buf[0] = 0xc6; buf[1] = 0xc5; ret = 2; break; }
352      if (wc == 0x2f13) { buf[0] = 0xc6; buf[1] = 0xc6; ret = 2; break; }
353      if (wc == 0x2f16) { buf[0] = 0xc6; buf[1] = 0xc7; ret = 2; break; }
354      if (wc == 0x2f19) { buf[0] = 0xc6; buf[1] = 0xc8; ret = 2; break; }
355      if (wc == 0x2f1b) { buf[0] = 0xc6; buf[1] = 0xc9; ret = 2; break; }
356      if (wc == 0x2f22) { buf[0] = 0xc6; buf[1] = 0xca; ret = 2; break; }
357      if (wc == 0x2f27) { buf[0] = 0xc6; buf[1] = 0xcb; ret = 2; break; }
358      if (wc == 0x2f2e) { buf[0] = 0xc6; buf[1] = 0xcc; ret = 2; break; }
359      if (wc == 0x2f33) { buf[0] = 0xc6; buf[1] = 0xcd; ret = 2; break; }
360      if (wc == 0x2f34) { buf[0] = 0xc6; buf[1] = 0xce; ret = 2; break; }
361      if (wc == 0x2f35) { buf[0] = 0xc6; buf[1] = 0xcf; ret = 2; break; }
362      if (wc == 0x2f39) { buf[0] = 0xc6; buf[1] = 0xd0; ret = 2; break; }
363      if (wc == 0x2f3a) { buf[0] = 0xc6; buf[1] = 0xd1; ret = 2; break; }
364      if (wc == 0x2f41) { buf[0] = 0xc6; buf[1] = 0xd2; ret = 2; break; }
365      if (wc == 0x2f46) { buf[0] = 0xc6; buf[1] = 0xd3; ret = 2; break; }
366      if (wc == 0x2f67) { buf[0] = 0xc6; buf[1] = 0xd4; ret = 2; break; }
367      if (wc == 0x2f68) { buf[0] = 0xc6; buf[1] = 0xd5; ret = 2; break; }
368      if (wc == 0x2fa1) { buf[0] = 0xc6; buf[1] = 0xd6; ret = 2; break; }
369      if (wc == 0x2faa) { buf[0] = 0xc6; buf[1] = 0xd7; ret = 2; break; }
370      break;
371    case 0x30:
372      if (wc >= 0x3005 && wc <= 0x3007) {
373        buf[0] = 0xc6; buf[1] = wc - 0x2f25; ret = 2;
374        break;
375      }
376      if (wc >= 0x3038 && wc <= 0x303a) {
377        buf[0] = 0xa2; buf[1] = wc - 0x2f6c; ret = 2;
378        break;
379      }
380      if (wc >= 0x3041 && wc <= 0x3093) {
381        if (wc < 0x3059) {
382          buf[0] = 0xc6; buf[1] = wc - 0x2f5a;
383        } else {
384          buf[0] = 0xc7; buf[1] = wc - 0x3019;
385        }
386        ret = 2;
387        break;
388      }
389      if (wc == 0x309d) { buf[0] = 0xc6; buf[1] = 0xdc; ret = 2; break; }
390      if (wc == 0x309e) { buf[0] = 0xc6; buf[1] = 0xdd; ret = 2; break; }
391      if (wc >= 0x30a1 && wc <= 0x30f6) {
392        buf[0] = 0xc7; buf[1] = wc - (wc < 0x30a5 ? 0x3026 : 0x3004); ret = 2;
393        break;
394      }
395      if (wc == 0x30fc) { buf[0] = 0xc6; buf[1] = 0xe3; ret = 2; break; }
396      if (wc == 0x30fd) { buf[0] = 0xc6; buf[1] = 0xda; ret = 2; break; }
397      if (wc == 0x30fe) { buf[0] = 0xc6; buf[1] = 0xdb; ret = 2; break; }
398      break;
399    case 0x53:
400      if (wc == 0x5344)
401        return RET_ILUNI;
402      break;
403    case 0x58:
404      if (wc == 0x58bb) { buf[0] = 0xf9; buf[1] = 0xd9; ret = 2; break; }
405      break;
406    case 0x5a:
407      if (wc == 0x5afa) { buf[0] = 0xf9; buf[1] = 0xdc; ret = 2; break; }
408      break;
409    case 0x5f:
410      if (wc == 0x5f5e) { buf[0] = 0xc2; buf[1] = 0x55; ret = 2; break; }
411      if (wc == 0x5f5d)
412        return RET_ILUNI;
413      break;
414    case 0x60:
415      if (wc == 0x6052) { buf[0] = 0xf9; buf[1] = 0xda; ret = 2; break; }
416      break;
417    case 0x78:
418      if (wc == 0x7881) { buf[0] = 0xf9; buf[1] = 0xd6; ret = 2; break; }
419      break;
420    case 0x7c:
421      if (wc == 0x7ca7) { buf[0] = 0xf9; buf[1] = 0xdb; ret = 2; break; }
422      break;
423    case 0x88:
424      if (wc == 0x88cf) { buf[0] = 0xf9; buf[1] = 0xd8; ret = 2; break; }
425      break;
426    case 0x92:
427      if (wc == 0x92b9) { buf[0] = 0xf9; buf[1] = 0xd7; ret = 2; break; }
428      break;
429    case 0xe0: case 0xe1: case 0xe2: case 0xe3: case 0xe4: case 0xe5:
430    case 0xe6: case 0xe7: case 0xe8: case 0xe9: case 0xea: case 0xeb:
431    case 0xec: case 0xed: case 0xee: case 0xef: case 0xf0: case 0xf1:
432    case 0xf2: case 0xf3: case 0xf4: case 0xf5: case 0xf6:
433      {
434        unsigned int i = wc - 0xe000;
435        if (i < 5809) {
436          unsigned int c1 = i / 157;
437          unsigned int c2 = i % 157;
438          buf[0] = c1 + (c1 < 5 ? 0xfa : c1 < 24 ? 0x89 : 0x69);
439          buf[1] = c2 + (c2 < 0x3f ? 0x40 : 0x62);
440          ret = 2;
441          break;
442        }
443      }
444      break;
445    case 0xfe:
446      if (wc == 0xfe51) { buf[0] = 0xa1; buf[1] = 0x4e; ret = 2; break; }
447      if (wc == 0xfe68) { buf[0] = 0xa2; buf[1] = 0x42; ret = 2; break; }
448      break;
449    case 0xff:
450      if (wc == 0xff0f) { buf[0] = 0xa1; buf[1] = 0xfe; ret = 2; break; }
451      if (wc == 0xff3b) { buf[0] = 0xc6; buf[1] = 0xe4; ret = 2; break; }
452      if (wc == 0xff3c) { buf[0] = 0xa2; buf[1] = 0x40; ret = 2; break; }
453      if (wc == 0xff3d) { buf[0] = 0xc6; buf[1] = 0xe5; ret = 2; break; }
454      if (wc == 0xff3e) { buf[0] = 0xc6; buf[1] = 0xd9; ret = 2; break; }
455      if (wc == 0xff5e) { buf[0] = 0xa1; buf[1] = 0xe3; ret = 2; break; }
456      if (wc == 0xffe0) { buf[0] = 0xa2; buf[1] = 0x46; ret = 2; break; }
457      if (wc == 0xffe1) { buf[0] = 0xa2; buf[1] = 0x47; ret = 2; break; }
458      if (wc == 0xffe3) { buf[0] = 0xa1; buf[1] = 0xc3; ret = 2; break; }
459      if (wc == 0xffe5) { buf[0] = 0xa2; buf[1] = 0x44; ret = 2; break; }
460      if (wc == 0xff64)
461        return RET_ILUNI;
462      break;
463  }
464  if (ret == RET_ILUNI)
465    ret = big5_wctomb(conv,buf,wc,2);
466  if (ret != RET_ILUNI) {
467    if (ret != 2) abort();
468    if (n < 2)
469      return RET_TOOSMALL;
470    r[0] = buf[0];
471    r[1] = buf[1];
472    return 2;
473  }
474
475  return RET_ILUNI;
476}
477