Simplify step data handling.
[kopensolaris-gnu/glibc.git] / iconvdata / johab.c
1 /* Mapping tables for JOHAB handling.
2    Copyright (C) 1998 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4    Contributed by Jungshik Shin <jshin@pantheon.yale.edu>, 1998.
5
6    The GNU C Library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Library General Public License as
8    published by the Free Software Foundation; either version 2 of the
9    License, or (at your option) any later version.
10
11    The GNU C Library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Library General Public License for more details.
15
16    You should have received a copy of the GNU Library General Public
17    License along with the GNU C Library; see the file COPYING.LIB.  If not,
18    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19    Boston, MA 02111-1307, USA.  */
20
21 #include <gconv.h>
22 #include <stdint.h>
23 #include <string.h>
24 #include <wchar.h>
25 #include <ksc5601.h>
26
27 /* Direction of the transformation.  */
28 static int to_johab_object;
29 static int from_johab_object;
30
31 /* The table for Bit pattern to Hangul Jamo
32    5 bits each are used to encode
33    leading consonants(19 + 1 filler), medial vowels(21 + 1 filler)
34    and trailing consonants(27 + 1 filler).
35
36    KS C 5601-1992 Annex 3 Table 2
37    0 : Filler, -1: invalid, >= 1 : valid
38
39  */
40 const int init[32] =
41 {
42   -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
43   19, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
44 };
45 const int mid[32] =
46 {
47   -1, -1, 0, 1, 2, 3, 4, 5,
48   -1, -1, 6, 7, 8, 9, 10, 11,
49   -1, -1, 12, 13, 14, 15, 16, 17,
50   -1, -1, 18, 19, 20, 21, -1, -1
51 };
52 const int final[32] =
53 {
54   -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
55   -1, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, -1, -1
56 };
57
58 /*
59    Hangul Jamo in Johab to Unicode 2.0 : Unicode 2.0
60    defines 51 Hangul Compatibility Jamos in the block [0x3131,0x314e]
61
62    It's to be considered later which Jamo block to use, Compatibility
63    block [0x3131,0x314e] or Hangul Conjoining Jamo block, [0x1100,0x11ff]
64
65  */
66 const wchar_t init_to_ucs[19] =
67 {
68   0x3131, 0x3132, 0x3134, 0x3137, 0x3138, 0x3139, 0x3141, 0x3142,
69   0x3143, 0x3145, 0x3146, 0x3147, 0x3148, 0x3149, 0x314a, 0x314b,
70   0x314c, 0x314d, 0x314e
71 };
72
73 const wchar_t final_to_ucs[27] =
74 {
75   L'\0', L'\0', 0x3133, L'\0', 0x3135, 0x3136, L'\0', L'\0',
76   0x313a, 0x313b, 0x314c, 0x313d, 0x313e, 0x313f,
77   0x3140, L'\0', L'\0', 0x3144, L'\0', L'\0', L'\0',
78   L'\0', L'\0', L'\0', L'\0', L'\0', L'\0'
79 };
80
81 /* The following three arrays are used to convert
82    precomposed Hangul syllables in [0xac00,0xd???]
83    to Jamo bit patterns for Johab encoding
84
85    cf. : KS C 5601-1992, Annex3 Table 2
86
87    Arrays are used to speed up things although it's possible
88    to get the same result arithmetically.
89
90  */
91 const int init_to_bit[19] =
92 {
93   0x8800, 0x8c00, 0x9000, 0x9400, 0x9800, 0x9c00,
94   0xa000, 0xa400, 0xa800, 0xac00, 0xb000, 0xb400,
95   0xb800, 0xbc00, 0xc000, 0xc400, 0xc800, 0xcc00,
96   0xd000
97 };
98
99 const int mid_to_bit[21] =
100 {
101           0x0060, 0x0080, 0x00a0, 0x00c0, 0x00e0,
102   0x0140, 0x0160, 0x0180, 0x01a0, 0x01c0, 0x1e0,
103   0x0240, 0x0260, 0x0280, 0x02a0, 0x02c0, 0x02e0,
104   0x0340, 0x0360, 0x0380, 0x03a0
105 };
106
107 const int final_to_bit[28] =
108 {
109   1, 2, 3, 4, 5, 6, 7, 8, 9, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11,
110   0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d
111 };
112
113 /* The conversion table from
114    UCS4 Hangul Compatibility Jamo in [0x3131,0x3163]
115    to Johab
116
117    cf. 1. KS C 5601-1992 Annex 3 Table 2
118    2. Unicode 2.0 manual
119
120  */
121 const uint16_t jamo_from_ucs_table[51] =
122 {
123   0x8841, 0x8c41,
124   0x8444,
125   0x9041,
126   0x8446, 0x8447,
127   0x9441, 0x9841, 0x9c41,
128   0x844a, 0x844b, 0x844c, 0x844d, 0x884e, 0x884f, 0x8450,
129   0xa041, 0xa441, 0xa841,
130   0x8454,
131   0xac41, 0xb041, 0xb441, 0xb841, 0xbc41,
132   0xc041, 0xc441, 0xc841, 0xca41, 0xd041,
133   0x8461, 0x8481, 0x84a1, 0x84c1, 0x84e1,
134   0x8541, 0x8561, 0x8581, 0x85a1, 0x85c1, 0x85e1,
135   0x8641, 0x8661, 0x8681, 0x86a1, 0x86c1, 0x86e1,
136   0x8741, 0x8761, 0x8781, 0x87a1
137 };
138
139
140 static inline wchar_t
141 johab_sym_hanja_to_ucs (int idx, int c1, int c2)
142 {
143   if (idx <= 0xdefe)
144     return (wchar_t) ksc5601_sym_to_ucs[(c1 - 0xd9) * 188 + c2
145                                         - (c2 > 0x90 ? 0x43 : 0x31)];
146   else
147     return (wchar_t) ksc5601_hanja_to_ucs[(c1 - 0xe0) * 188 + c2
148                                           - (c2 > 0x90 ? 0x43 : 0x31)];
149 }
150
151 static uint16_t
152 johab_hanja_from_ucs (wchar_t ch)
153 {
154
155   uint16_t idx;
156   if (ucs4_to_ksc5601_hanja (ch, &idx))
157     {
158       int idx1, idx2;
159       /* Hanja begins at the 42th row. 42=0x2a : 0x2a + 0x20 = 0x4a.  */
160       idx1 = idx / 256 - 0x4a;
161       idx2 = idx % 256 + 0x80;
162
163       return ((idx1 / 2) * 256 + 0xe000 + idx2
164               + (idx1 % 2 ? 0 :  (idx2 > 0xee ? 0x43 : 0x31) - 0xa1));
165     }
166   else
167     return 0;
168 }
169
170 static uint16_t
171 johab_sym_from_ucs (wchar_t ch)
172 {
173   uint16_t idx;
174   if (ucs4_to_ksc5601_sym (ch, &idx))
175     {
176       int idx1, idx2;
177
178       idx1 = idx / 256 - 0x21;
179       idx2 = idx % 256 + 0x80;
180
181       return ((idx1 / 2) * 256 + 0xd900 + idx2
182               + (idx1 % 2 ? 0 : (idx2 > 0xee ? 0x43 : 0x31) - 0xa1));
183     }
184   else
185     return 0;
186 }
187
188
189
190 static inline void
191 johab_from_ucs4 (wchar_t ch, unsigned char *cp)
192 {
193   if (ch >= 0x7f)
194     {
195       int idx;
196
197       if (ch >= 0xac00 && ch <= 0xd7a3)
198         {
199           ch -= 0xac00;
200           idx = init_to_bit[ch / 588];  /* 21*28 = 588 */
201           idx += mid_to_bit[(ch / 28) % 21];  /* (ch % (21 * 28)) / 28 */
202           idx += final_to_bit[ch %  28]; /* (ch % (21 * 28)) % 28 */
203         }
204       /* KS C 5601-1992 Annex 3 regards  0xA4DA(Hangul Filler : U3164)
205          as symbol */
206       else if (ch >= 0x3131 && ch <= 0x3163)
207         idx = jamo_from_ucs_table[ch - 0x3131];
208       else if (ch >= 0x4e00 && ch <= 0x9fa5
209                || ch >= 0xf900 && ch <= 0xfa0b)
210         idx = johab_hanja_from_ucs (ch);
211       /*       Half-width Korean Currency Won Sign
212                else if ( ch == 0x20a9 )
213                idx = 0x5c00;
214       */
215       else
216         idx = johab_sym_from_ucs (ch);
217
218       *cp = (char) (idx / 256);
219       *(cp + 1) = (char) (idx & 0xff);
220
221     }
222   else
223     {
224       *cp = (char) (0x7f & ch);
225       *(cp + 1) = (char) 0;
226     }
227
228 }
229
230
231 int
232 gconv_init (struct gconv_step *step)
233 {
234   /* Determine which direction.  */
235   if (strcasestr (step->from_name, "JOHAB") != NULL)
236     step->data = &from_johab_object;
237   else if (strcasestr (step->to_name, "JOHAB") != NULL)
238     step->data = &to_johab_object;
239   else
240     return GCONV_NOCONV;
241
242   return GCONV_OK;
243 }
244
245
246 void
247 gconv_end (struct gconv_step *data)
248 {
249   /* Nothing to do.  */
250 }
251
252
253 int
254 gconv (struct gconv_step *step, struct gconv_step_data *data,
255        const char *inbuf, size_t *inbufsize, size_t * written, int do_flush)
256 {
257   struct gconv_step *next_step = step + 1;
258   struct gconv_step_data *next_data = data + 1;
259   gconv_fct fct = next_step->fct;
260   size_t do_write;
261   int result;
262
263   /* If the function is called with no input this means we have to reset
264      to the initial state.  The possibly partly converted input is
265      dropped.  */
266   if (do_flush)
267     {
268       do_write = 0;
269
270       /* Call the steps down the chain if there are any.  */
271       if (data->is_last)
272         result = GCONV_OK;
273       else
274         {
275           struct gconv_step *next_step = step + 1;
276           struct gconv_step_data *next_data = data + 1;
277
278           result = (*fct) (next_step, next_data, NULL, 0, written, 1);
279
280           /* Clear output buffer.  */
281           data->outbufavail = 0;
282         }
283     }
284   else
285     {
286       do_write = 0;
287
288       do
289         {
290           result = GCONV_OK;
291
292           if (step->data == &from_johab_object)
293             {
294               size_t inchars = *inbufsize;
295               size_t outwchars = data->outbufavail;
296               char *outbuf = data->outbuf;
297               size_t cnt = 0;
298
299               while (cnt < inchars
300                      && (outwchars + sizeof (wchar_t) <= data->outbufsize))
301                 {
302                   int inchar = (unsigned char) inbuf[cnt];
303                   wchar_t ch;
304                   /* half-width Korean Currency WON sign
305                      if (inchar == 0x5c)
306                      ch =  0x20a9;
307                      else if (inchar < 0x7f)
308                      ch = (wchar_t) inchar;
309                   */
310                   if (inchar < 0x7f)
311                     ch = (wchar_t) inchar;
312
313                   /* Johab : 1. Hangul
314                      1st byte : 0x84-0xd3
315                      2nd byte : 0x41-0x7e, 0x81-0xfe
316                      2. Hanja & Symbol  :
317                      1st byte : 0xd8-0xde, 0xe0-0xf9
318                      2nd byte : 0x31-0x7e, 0x91-0xfe
319                      0xd831-0xd87e and 0xd891-0xd8fe are user-defined area */
320
321                   else if (inchar > 0xf9 || inchar == 0xdf
322                            || (inchar > 0x7e && inchar < 0x84)
323                            || (inchar > 0xd3 && inchar < 0xd9))
324                     /* These are illegal.  */
325                     ch = L'\0';
326                   else
327                     {
328                       /* Two-byte character.  First test whether the next
329                          character is also available.  */
330                       int inchar2;
331                       int idx;
332
333                       if (cnt + 1 >= inchars)
334                         {
335                           /* The second character is not available.  Store
336                              the intermediate result.  */
337                           result = GCONV_INCOMPLETE_INPUT;
338                           break;
339                         }
340
341                       inchar2 = (unsigned char) inbuf[++cnt];
342                       idx = inchar * 256 + inchar2;
343                       if (inchar <= 0xd3)
344                         {       /* Hangul */
345                           int i, m, f;
346                           i = init[(idx & 0x7c00) >> 10];
347                           m = mid[(idx & 0x03e0) >> 5];
348                           f = final[idx & 0x001f];
349                           if (i == -1 || m == -1 || f == -1)
350                             /* This is illegal.  */
351                             ch = L'\0';
352                           else if (i > 0 && m > 0)
353                             ch = ((i - 1) * 21 + (m - 1)) * 28 + f + 0xac00;
354                           else if (i > 0 && m == 0 & f == 0)
355                             ch = init_to_ucs[i - 1];
356                           else if (i == 0 && m > 0 & f == 0)
357                             ch = 0x314e + m;    /* 0x314f + m - 1 */
358                           else if (i == 0 && m == 0 & f > 0)
359                             ch = final_to_ucs[f - 1];   /* round trip?? */
360                           else
361                             /* This is illegal.  */
362                             ch = L'\0';
363                         }
364                       else
365                         {
366                           if (inchar2 < 0x31
367                               || (inchar2 > 0x7e && inchar2 < 0x91)
368                               || inchar2 == 0xff)
369                             /* This is illegal.  */
370                             ch = L'\0';
371                           else if (inchar == 0xda
372                                    && inchar2 > 0xa0 && inchar2 < 0xd4)
373                             /* This is illegal.  */
374                             /* Modern Hangul Jaso is defined elsewhere
375                                in Johab */
376                             ch = L'\0';
377                           else
378                             {
379                               ch = johab_sym_hanja_to_ucs (idx, inchar,
380                                                            inchar2);
381                               /*                if (idx <= 0xdefe)
382                                  ch = ksc5601_sym_to_ucs[(inchar - 0xd9) * 192
383                                  + inchar2
384                                  -  (inchar2>0x90 ? 0x43 : 0x31)];
385
386                                  else
387                                  ch = ksc5601_hanja_to_ucs[(inchar - 0xe0) *192
388                                  + inchar2
389                                  -  (inchar2>0x90 ? 0x43 : 0x31)];
390                                */
391                             }
392                         }
393
394                       if (ch == L'\0')
395                         --cnt;
396                     }
397
398                   if (ch == L'\0' && inbuf[cnt] != '\0')
399                     {
400                       /* This is an illegal character.  */
401                       result = GCONV_ILLEGAL_INPUT;
402                       break;
403                     }
404
405                   *((wchar_t *) (outbuf + outwchars)) = ch;
406                   ++do_write;
407                   outwchars += sizeof (wchar_t);
408                   ++cnt;
409                 }
410               *inbufsize -= cnt;
411               data->outbufavail = outwchars;
412             }
413           else
414             {
415               size_t inwchars = *inbufsize;
416               size_t outchars = data->outbufavail;
417               char *outbuf = data->outbuf;
418               size_t cnt = 0;
419               int extra = 0;
420
421               while (inwchars >= cnt + sizeof (wchar_t)
422                      && outchars < data->outbufsize)
423                 {
424                   wchar_t ch = *((wchar_t *) (inbuf + cnt));
425                   unsigned char cp[2];
426                   /*
427                     if (ch >= (sizeof (from_ucs4_lat1)
428                         / sizeof (from_ucs4_lat1[0])))
429                       {
430                         if (ch >= 0x0391 && ch <= 0x0451)
431                           cp = from_ucs4_greek[ch - 0x391];
432                         else if (ch >= 0x2010 && ch <= 0x9fa0)
433                           cp = from_ucs4_cjk[ch - 0x02010];
434                         else
435                           break;
436                       }
437                     else
438                       cp = from_ucs4_lat1[ch];
439                   */
440                   johab_from_ucs4 (ch, cp);
441
442                   if (cp[0] == '\0' && ch != 0)
443                     /* Illegal character.  */
444                     break;
445
446                   outbuf[outchars] = cp[0];
447                   /* Now test for a possible second byte and write this
448                      if possible.  */
449                   if (cp[1] != '\0')
450                     {
451                       if (outchars + 1 >= data->outbufsize)
452                         {
453                           /* The result does not fit into the buffer.  */
454                           extra = 1;
455                           break;
456                         }
457                       outbuf[++outchars] = cp[1];
458                     }
459
460                   ++do_write;
461                   ++outchars;
462                   cnt += sizeof (wchar_t);
463                 }
464               *inbufsize -= cnt;
465               data->outbufavail = outchars;
466
467               if (outchars + extra < data->outbufsize)
468                 {
469                   /* If there is still room in the output buffer something
470                      is wrong with the input.  */
471                   if (inwchars >= cnt + sizeof (wchar_t))
472                     {
473                       /* An error occurred.  */
474                       result = GCONV_ILLEGAL_INPUT;
475                       break;
476                     }
477                   if (inwchars != cnt)
478                     {
479                       /* There are some unprocessed bytes at the end of the
480                          input buffer.  */
481                       result = GCONV_INCOMPLETE_INPUT;
482                       break;
483                     }
484                 }
485             }
486
487           if (result != GCONV_OK)
488             break;
489
490           if (data->is_last)
491             {
492               /* This is the last step.  */
493               result = (*inbufsize > (step->data == &from_johab_object
494                                       ? 0 : sizeof (wchar_t) - 1)
495                         ? GCONV_FULL_OUTPUT : GCONV_EMPTY_INPUT);
496               break;
497             }
498
499           /* Status so far.  */
500           result = GCONV_EMPTY_INPUT;
501
502           if (data->outbufavail > 0)
503             {
504               /* Call the functions below in the chain.  */
505               size_t newavail = data->outbufavail;
506
507               result = (*fct) (next_step, next_data, data->outbuf, &newavail,
508                                written, 0);
509
510               /* Correct the output buffer.  */
511               if (newavail != data->outbufavail && newavail > 0)
512                 {
513                   memmove (data->outbuf,
514                            &data->outbuf[data->outbufavail - newavail],
515                            newavail);
516                   data->outbufavail = newavail;
517                 }
518             }
519         }
520       while (*inbufsize > 0 && result == GCONV_EMPTY_INPUT);
521     }
522
523   if (written != NULL && data->is_last)
524     *written = do_write;
525
526   return result;
527 }