iconv.c
Upload User: yingmei828
Upload Date: 2007-01-01
Package Size: 1646k
Code Size: 13k
Development Platform:

Unix_Linux

  1. /* Copyright (C) 1999-2000 Free Software Foundation, Inc.
  2.    This file is part of the GNU ICONV Library.
  3.    The GNU ICONV Library is free software; you can redistribute it and/or
  4.    modify it under the terms of the GNU Library General Public License as
  5.    published by the Free Software Foundation; either version 2 of the
  6.    License, or (at your option) any later version.
  7.    The GNU ICONV Library is distributed in the hope that it will be useful,
  8.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  9.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  10.    Library General Public License for more details.
  11.    You should have received a copy of the GNU Library General Public
  12.    License along with the GNU ICONV Library; see the file COPYING.LIB.  If not,
  13.    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  14.    Boston, MA 02111-1307, USA.  */
  15. #include <iconv.h>
  16. #include <stdlib.h>
  17. #include <string.h>
  18. #include "config.h"
  19. /*
  20.  * Converters.
  21.  */
  22. #include "converters.h"
  23. /*
  24.  * Transliteration tables.
  25.  */
  26. #include "cjk_variants.h"
  27. #include "translit.h"
  28. /*
  29.  * Table of all supported encodings.
  30.  */
  31. struct encoding {
  32.   struct mbtowc_funcs ifuncs; /* conversion multibyte -> unicode */
  33.   struct wctomb_funcs ofuncs; /* conversion unicode -> multibyte */
  34.   int oflags;                 /* flags for unicode -> multibyte conversion */
  35. };
  36. enum {
  37. #define DEFENCODING(xxx_names,xxx,xxx_ifuncs,xxx_ofuncs1,xxx_ofuncs2) 
  38.   ei_##xxx ,
  39. #include "encodings.def"
  40. #undef DEFENCODING
  41. ei_for_broken_compilers_that_dont_like_trailing_commas
  42. };
  43. #include "flags.h"
  44. static struct encoding const all_encodings[] = {
  45. #define DEFENCODING(xxx_names,xxx,xxx_ifuncs,xxx_ofuncs1,xxx_ofuncs2) 
  46.   { xxx_ifuncs, xxx_ofuncs1,xxx_ofuncs2, ei_##xxx##_oflags },
  47. #include "encodings.def"
  48. #undef DEFENCODING
  49. };
  50. /*
  51.  * Alias lookup function.
  52.  * Defines
  53.  *   struct alias { const char* name; unsigned int encoding_index; };
  54.  *   const struct alias * aliases_lookup (const char *str, unsigned int len);
  55.  *   #define MAX_WORD_LENGTH ...
  56.  */
  57. #include "aliases.h"
  58. #if 0
  59. /* Like !strcasecmp, except that the both strings can be assumed to be ASCII
  60.    and the first string can be assumed to be in uppercase. */
  61. static int strequal (const char* str1, const char* str2)
  62. {
  63.   unsigned char c1;
  64.   unsigned char c2;
  65.   for (;;) {
  66.     c1 = * (unsigned char *) str1++;
  67.     c2 = * (unsigned char *) str2++;
  68.     if (c1 == 0)
  69.       break;
  70.     if (c2 >= 'a' && c2 <= 'z')
  71.       c2 -= 'a'-'A';
  72.     if (c1 != c2)
  73.       break;
  74.   }
  75.   return (c1 == c2);
  76. }
  77. #endif
  78. iconv_t iconv_open (const char* tocode, const char* fromcode)
  79. {
  80.   struct conv_struct * cd = (struct conv_struct *) malloc(sizeof(struct conv_struct));
  81.   char buf[MAX_WORD_LENGTH+1];
  82.   const char* cp;
  83.   char* bp;
  84.   const struct alias * ap;
  85.   unsigned int count;
  86.   if (cd == NULL) {
  87.     errno = ENOMEM;
  88.     return (iconv_t)(-1);
  89.   }
  90.   /* Before calling aliases_lookup, convert the input string to upper case,
  91.    * and check whether it's entirely ASCII (we call gperf with option "-7"
  92.    * to achieve a smaller table) and non-empty. If it's not entirely ASCII,
  93.    * or if it's too long, it is not a valid encoding name.
  94.    */
  95.   /* Search tocode in the table. */
  96.   for (cp = tocode, bp = buf, count = MAX_WORD_LENGTH+1; ; cp++, bp++) {
  97.     unsigned char c = * (unsigned char *) cp;
  98.     if (c >= 0x80)
  99.       goto invalid;
  100.     if (c >= 'a' && c <= 'z')
  101.       c -= 'a'-'A';
  102.     *bp = c;
  103.     if (c == '')
  104.       break;
  105.     if (--count == 0)
  106.       goto invalid;
  107.   }
  108.   ap = aliases_lookup(buf,bp-buf);
  109.   if (ap == NULL)
  110.     goto invalid;
  111.   cd->oindex = ap->encoding_index;
  112.   cd->ofuncs = all_encodings[ap->encoding_index].ofuncs;
  113.   cd->oflags = all_encodings[ap->encoding_index].oflags;
  114.   /* Search fromcode in the table. */
  115.   for (cp = fromcode, bp = buf, count = MAX_WORD_LENGTH+1; ; cp++, bp++) {
  116.     unsigned char c = * (unsigned char *) cp;
  117.     if (c >= 0x80)
  118.       goto invalid;
  119.     if (c >= 'a' && c <= 'z')
  120.       c -= 'a'-'A';
  121.     *bp = c;
  122.     if (c == '')
  123.       break;
  124.     if (--count == 0)
  125.       goto invalid;
  126.   }
  127.   ap = aliases_lookup(buf,bp-buf);
  128.   if (ap == NULL)
  129.     goto invalid;
  130.   cd->iindex = ap->encoding_index;
  131.   cd->ifuncs = all_encodings[ap->encoding_index].ifuncs;
  132.   /* Initialize the states. */
  133.   memset(&cd->istate,'',sizeof(state_t));
  134.   memset(&cd->ostate,'',sizeof(state_t));
  135.   /* Initialize the operation flags. */
  136.   cd->transliterate = 1;
  137.   /* Done. */
  138.   return (iconv_t)cd;
  139. invalid:
  140.   errno = EINVAL;
  141.   return (iconv_t)(-1);
  142. }
  143. size_t iconv (iconv_t icd,
  144.               ICONV_CONST char* * inbuf, size_t *inbytesleft,
  145.               char* * outbuf, size_t *outbytesleft)
  146. {
  147.   conv_t cd = (conv_t) icd;
  148.   if (inbuf == NULL || *inbuf == NULL) {
  149.     if (outbuf == NULL || *outbuf == NULL) {
  150.       /* Reset the states. */
  151.       memset(&cd->istate,'',sizeof(state_t));
  152.       memset(&cd->ostate,'',sizeof(state_t));
  153.       return 0;
  154.     } else {
  155.       if (cd->ofuncs.xxx_reset) {
  156.         int outcount = cd->ofuncs.xxx_reset(cd,*outbuf,*outbytesleft);
  157.         if (outcount < 0) {
  158.           errno = E2BIG;
  159.           return -1;
  160.         }
  161.         *outbuf += outcount; *outbytesleft -= outcount;
  162.       }
  163.       memset(&cd->istate,'',sizeof(state_t));
  164.       memset(&cd->ostate,'',sizeof(state_t));
  165.       return 0;
  166.     }
  167.   } else {
  168.     size_t result = 0;
  169.     const unsigned char* inptr = (const unsigned char*) *inbuf;
  170.     size_t inleft = *inbytesleft;
  171.     unsigned char* outptr = (unsigned char*) *outbuf;
  172.     size_t outleft = *outbytesleft;
  173.     while (inleft > 0) {
  174.       wchar_t wc;
  175.       int incount;
  176.       int outcount;
  177.       incount = cd->ifuncs.xxx_mbtowc(cd,&wc,inptr,inleft);
  178.       if (incount <= 0) {
  179.         if (incount == 0) {
  180.           /* Case 1: invalid input */
  181.           errno = EILSEQ;
  182.           result = -1;
  183.           break;
  184.         }
  185.         if (incount == -1) {
  186.           /* Case 2: not enough bytes available to detect anything */
  187.           errno = EINVAL;
  188.           result = -1;
  189.           break;
  190.         }
  191.         /* Case 3: k bytes read, but only a shift sequence */
  192.         incount = -1-incount;
  193.       } else {
  194.         /* Case 4: k bytes read, making up a wide character */
  195.         outcount = cd->ofuncs.xxx_wctomb(cd,outptr,wc,outleft);
  196.         if (outcount != 0)
  197.           goto outcount_ok;
  198.         /* Try transliteration. */
  199.         result++;
  200.         if (cd->transliterate) {
  201.           if (cd->oflags & HAVE_HANGUL_JAMO) {
  202.             /* Decompose Hangul into Jamo. Use double-width Jamo (contained
  203.                in all Korean encodings and ISO-2022-JP-2), not half-width Jamo
  204.                (contained in Unicode only). */
  205.             wchar_t buf[3];
  206.             int ret = johab_hangul_decompose(cd,buf,wc);
  207.             if (ret != RET_ILSEQ) {
  208.               /* we know 1 <= ret <= 3 */
  209.               state_t backup_state = cd->ostate;
  210.               unsigned char* backup_outptr = outptr;
  211.               size_t backup_outleft = outleft;
  212.               int i, sub_outcount;
  213.               for (i = 0; i < ret; i++) {
  214.                 if (outleft == 0) {
  215.                   sub_outcount = RET_TOOSMALL;
  216.                   goto johab_hangul_failed;
  217.                 }
  218.                 sub_outcount = cd->ofuncs.xxx_wctomb(cd,outptr,buf[i],outleft);
  219.                 if (sub_outcount <= 0)
  220.                   goto johab_hangul_failed;
  221.                 if (!(sub_outcount <= outleft)) abort();
  222.                 outptr += sub_outcount; outleft -= sub_outcount;
  223.               }
  224.               goto char_done;
  225.             johab_hangul_failed:
  226.               cd->ostate = backup_state;
  227.               outptr = backup_outptr;
  228.               outleft = backup_outleft;
  229.               if (sub_outcount < 0) {
  230.                 errno = E2BIG;
  231.                 result = -1;
  232.                 break;
  233.               }
  234.             }
  235.           }
  236.           {
  237.             /* Try to use a variant, but postfix it with
  238.                U+303E IDEOGRAPHIC VARIATION INDICATOR
  239.                (cf. Ken Lunde's "CJKV information processing", p. 188). */
  240.             int indx = -1;
  241.             if (wc == 0x3006)
  242.               indx = 0;
  243.             else if (wc == 0x30f6)
  244.               indx = 1;
  245.             else if (wc >= 0x4e00 && wc < 0xa000)
  246.               indx = cjk_variants_indx[wc-0x4e00];
  247.             if (indx >= 0) {
  248.               for (;; indx++) {
  249.                 wchar_t buf[2];
  250.                 unsigned short variant = cjk_variants[indx];
  251.                 unsigned short last = variant & 0x8000;
  252.                 variant &= 0x7fff;
  253.                 variant += 0x3000;
  254.                 buf[0] = variant; buf[1] = 0x303e;
  255.                 {
  256.                   state_t backup_state = cd->ostate;
  257.                   unsigned char* backup_outptr = outptr;
  258.                   size_t backup_outleft = outleft;
  259.                   int i, sub_outcount;
  260.                   for (i = 0; i < 2; i++) {
  261.                     if (outleft == 0) {
  262.                       sub_outcount = RET_TOOSMALL;
  263.                       goto variant_failed;
  264.                     }
  265.                     sub_outcount = cd->ofuncs.xxx_wctomb(cd,outptr,buf[i],outleft);
  266.                     if (sub_outcount <= 0)
  267.                       goto variant_failed;
  268.                     if (!(sub_outcount <= outleft)) abort();
  269.                     outptr += sub_outcount; outleft -= sub_outcount;
  270.                   }
  271.                   goto char_done;
  272.                 variant_failed:
  273.                   cd->ostate = backup_state;
  274.                   outptr = backup_outptr;
  275.                   outleft = backup_outleft;
  276.                   if (sub_outcount < 0) {
  277.                     errno = E2BIG;
  278.                     result = -1;
  279.                     break;
  280.                   }
  281.                 }
  282.                 if (last)
  283.                   break;
  284.               }
  285.             }
  286.           }
  287.           if (wc >= 0x2018 && wc <= 0x201a) {
  288.             /* Special case for quotation marks 0x2018, 0x2019, 0x201a */
  289.             wchar_t substitute =
  290.               (cd->oflags & HAVE_QUOTATION_MARKS
  291.                ? (wc == 0x201a ? 0x2018 : wc)
  292.                : (cd->oflags & HAVE_ACCENTS
  293.                   ? (wc==0x2019 ? 0x00b4 : 0x0060) /* use accents */
  294.                   : 0x0027 /* use apostrophe */
  295.               )  );
  296.             outcount = cd->ofuncs.xxx_wctomb(cd,outptr,substitute,outleft);
  297.             if (outcount != 0)
  298.               goto outcount_ok;
  299.           }
  300.           {
  301.             /* Use the transliteration table. */
  302.             int indx = translit_index(wc);
  303.             if (indx >= 0) {
  304.               const unsigned char * cp = &translit_data[indx];
  305.               unsigned int num = *cp++;
  306.               state_t backup_state = cd->ostate;
  307.               unsigned char* backup_outptr = outptr;
  308.               size_t backup_outleft = outleft;
  309.               unsigned int i;
  310.               int sub_outcount;
  311.               for (i = 0; i < num; i++) {
  312.                 if (outleft == 0) {
  313.                   sub_outcount = RET_TOOSMALL;
  314.                   goto translit_failed;
  315.                 }
  316.                 sub_outcount = cd->ofuncs.xxx_wctomb(cd,outptr,cp[i],outleft);
  317.                 if (sub_outcount <= 0)
  318.                   goto translit_failed;
  319.                 if (!(sub_outcount <= outleft)) abort();
  320.                 outptr += sub_outcount; outleft -= sub_outcount;
  321.               }
  322.               goto char_done;
  323.             translit_failed:
  324.               cd->ostate = backup_state;
  325.               outptr = backup_outptr;
  326.               outleft = backup_outleft;
  327.               if (sub_outcount < 0) {
  328.                 errno = E2BIG;
  329.                 result = -1;
  330.                 break;
  331.               }
  332.             }
  333.           }
  334.         }
  335.         outcount = cd->ofuncs.xxx_wctomb(cd,outptr,0xFFFD,outleft);
  336.         if (outcount != 0)
  337.           goto outcount_ok;
  338.         errno = EILSEQ;
  339.         result = -1;
  340.         break;
  341.       outcount_ok:
  342.         if (outcount < 0) {
  343.           errno = E2BIG;
  344.           result = -1;
  345.           break;
  346.         }
  347.         if (!(outcount <= outleft)) abort();
  348.         outptr += outcount; outleft -= outcount;
  349.       char_done:
  350.         ;
  351.       }
  352.       if (!(incount <= inleft)) abort();
  353.       inptr += incount; inleft -= incount;
  354.     }
  355.     *inbuf = (ICONV_CONST char*) inptr;
  356.     *inbytesleft = inleft;
  357.     *outbuf = (char*) outptr;
  358.     *outbytesleft = outleft;
  359.     return result;
  360.   }
  361. }
  362. int iconv_close (iconv_t icd)
  363. {
  364.   conv_t cd = (conv_t) icd;
  365.   free(cd);
  366.   return 0;
  367. }
  368. #ifndef LIBICONV_PLUG
  369. int iconvctl (iconv_t icd, int request, void* argument)
  370. {
  371.   conv_t cd = (conv_t) icd;
  372.   switch (request) {
  373.     case ICONV_TRIVIALP:
  374.       *(int *)argument = (cd->iindex == cd->oindex ? 1 : 0);
  375.       return 0;
  376.     case ICONV_GET_TRANSLITERATE:
  377.       *(int *)argument = cd->transliterate;
  378.       return 0;
  379.     case ICONV_SET_TRANSLITERATE:
  380.       cd->transliterate = (*(const int *)argument ? 1 : 0);
  381.       return 0;
  382.     default:
  383.       errno = EINVAL;
  384.       return -1;
  385.   }
  386. }
  387. #endif