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