iconvert.c
上传用户:wstnjxml
上传日期:2014-04-03
资源大小:7248k
文件大小:6k
源码类别:

Windows CE

开发平台:

C/C++

  1. /*
  2.  * Copyright (C) 2001 Edmund Grimley Evans <edmundo@rano.org>
  3.  * 
  4.  * This program is free software; you can redistribute it and/or modify
  5.  * it under the terms of the GNU General Public License as published by
  6.  * the Free Software Foundation; either version 2 of the License, or
  7.  * (at your option) any later version.
  8.  * 
  9.  * This program is distributed in the hope that it will be useful,
  10.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.  * GNU General Public License for more details.
  13.  * 
  14.  * You should have received a copy of the GNU General Public License
  15.  * along with this program; if not, write to the Free Software
  16.  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  17.  */
  18. #ifdef HAVE_CONFIG_H
  19. #include <config.h>
  20. #endif
  21. #ifdef HAVE_ICONV
  22. #include <assert.h>
  23. #include <errno.h>
  24. #include <iconv.h>
  25. #include <stdlib.h>
  26. #include <string.h>
  27. /*
  28.  * Convert data from one encoding to another. Return:
  29.  *
  30.  *  -2 : memory allocation failed
  31.  *  -1 : unknown encoding
  32.  *   0 : data was converted exactly
  33.  *   1 : data was converted inexactly
  34.  *   2 : data was invalid (but still converted)
  35.  *
  36.  * We convert in two steps, via UTF-8, as this is the only
  37.  * reliable way of distinguishing between invalid input
  38.  * and valid input which iconv refuses to transliterate.
  39.  * We convert from UTF-8 twice, because we have no way of
  40.  * knowing whether the conversion was exact if iconv returns
  41.  * E2BIG (due to a bug in the specification of iconv).
  42.  * An alternative approach is to assume that the output of
  43.  * iconv is never more than 4 times as long as the input,
  44.  * but I prefer to avoid that assumption if possible.
  45.  */
  46. int iconvert(const char *fromcode, const char *tocode,
  47.      const char *from, size_t fromlen,
  48.      char **to, size_t *tolen)
  49. {
  50.   int ret = 0;
  51.   iconv_t cd1, cd2;
  52.   char *ib;
  53.   char *ob;
  54.   char *utfbuf = 0, *outbuf, *newbuf;
  55.   size_t utflen, outlen, ibl, obl, k;
  56.   char tbuf[2048];
  57.   cd1 = iconv_open("UTF-8", fromcode);
  58.   if (cd1 == (iconv_t)(-1))
  59.     return -1;
  60.   cd2 = (iconv_t)(-1);
  61.   /* Don't use strcasecmp() as it's locale-dependent. */
  62.   if (!strchr("Uu", tocode[0]) ||
  63.       !strchr("Tt", tocode[1]) ||
  64.       !strchr("Ff", tocode[2]) ||
  65.       tocode[3] != '-' ||
  66.       tocode[4] != '8' ||
  67.       tocode[5] != '') {
  68.     char *tocode1;
  69.     /*
  70.      * Try using this non-standard feature of glibc and libiconv.
  71.      * This is deliberately not a config option as people often
  72.      * change their iconv library without rebuilding applications.
  73.      */
  74.     tocode1 = (char *)malloc(strlen(tocode) + 11);
  75.     if (!tocode1)
  76.       goto fail;
  77.     strcpy(tocode1, tocode);
  78.     strcat(tocode1, "//TRANSLIT");
  79.     cd2 = iconv_open(tocode1, "UTF-8");
  80.     free(tocode1);
  81.     if (cd2 == (iconv_t)(-1))
  82.       cd2 = iconv_open(tocode, fromcode);
  83.     if (cd2 == (iconv_t)(-1)) {
  84.       iconv_close(cd1);
  85.       return -1;
  86.     }
  87.   }
  88.   utflen = 1; /*fromlen * 2 + 1; XXX */
  89.   utfbuf = (char *)malloc(utflen);
  90.   if (!utfbuf)
  91.     goto fail;
  92.   /* Convert to UTF-8 */
  93.   ib = (char *)from;
  94.   ibl = fromlen;
  95.   ob = utfbuf;
  96.   obl = utflen;
  97.   for (;;) {
  98.     k = iconv(cd1, &ib, &ibl, &ob, &obl);
  99.     assert((!k && !ibl) ||
  100.    (k == (size_t)(-1) && errno == E2BIG && ibl && obl < 6) ||
  101.    (k == (size_t)(-1) &&
  102.     (errno == EILSEQ || errno == EINVAL) && ibl));
  103.     if (!ibl)
  104.       break;
  105.     if (obl < 6) {
  106.       /* Enlarge the buffer */
  107.       utflen *= 2;
  108.       newbuf = (char *)realloc(utfbuf, utflen);
  109.       if (!newbuf)
  110. goto fail;
  111.       ob = (ob - utfbuf) + newbuf;
  112.       obl = utflen - (ob - newbuf);
  113.       utfbuf = newbuf;
  114.     }
  115.     else {
  116.       /* Invalid input */
  117.       ib++, ibl--;
  118.       *ob++ = '#', obl--;
  119.       ret = 2;
  120.       iconv(cd1, 0, 0, 0, 0);
  121.     }
  122.   }
  123.   if (cd2 == (iconv_t)(-1)) {
  124.     /* The target encoding was UTF-8 */
  125.     if (tolen)
  126.       *tolen = ob - utfbuf;
  127.     if (!to) {
  128.       free(utfbuf);
  129.       iconv_close(cd1);
  130.       return ret;
  131.     }
  132.     newbuf = (char *)realloc(utfbuf, (ob - utfbuf) + 1);
  133.     if (!newbuf)
  134.       goto fail;
  135.     ob = (ob - utfbuf) + newbuf;
  136.     *ob = '';
  137.     *to = newbuf;
  138.     iconv_close(cd1);
  139.     return ret;
  140.   }
  141.   /* Truncate the buffer to be tidy */
  142.   utflen = ob - utfbuf;
  143.   newbuf = (char *)realloc(utfbuf, utflen);
  144.   if (!newbuf)
  145.     goto fail;
  146.   utfbuf = newbuf;
  147.   /* Convert from UTF-8 to discover how long the output is */
  148.   outlen = 0;
  149.   ib = utfbuf;
  150.   ibl = utflen;
  151.   while (ibl) {
  152.     ob = tbuf;
  153.     obl = sizeof(tbuf);
  154.     k = iconv(cd2, &ib, &ibl, &ob, &obl);
  155.     assert((k != (size_t)(-1) && !ibl) ||
  156.    (k == (size_t)(-1) && errno == E2BIG && ibl) ||
  157.    (k == (size_t)(-1) && errno == EILSEQ && ibl));
  158.     if (ibl && !(k == (size_t)(-1) && errno == E2BIG)) {
  159.       /* Replace one character */
  160.       char *tb = "?";
  161.       size_t tbl = 1;
  162.       outlen += ob - tbuf;
  163.       ob = tbuf;
  164.       obl = sizeof(tbuf);
  165.       k = iconv(cd2, &tb, &tbl, &ob, &obl);
  166.       assert((!k && !tbl) ||
  167.      (k == (size_t)(-1) && errno == EILSEQ && tbl));
  168.       for (++ib, --ibl; ibl && (*ib & 0x80); ib++, ibl--)
  169. ;
  170.     }
  171.     outlen += ob - tbuf;
  172.   }
  173.   ob = tbuf;
  174.   obl = sizeof(tbuf);
  175.   k = iconv(cd2, 0, 0, &ob, &obl);
  176.   assert(!k);
  177.   outlen += ob - tbuf;
  178.   /* Convert from UTF-8 for real */
  179.   outbuf = (char *)malloc(outlen + 1);
  180.   if (!outbuf)
  181.     goto fail;
  182.   ib = utfbuf;
  183.   ibl = utflen;
  184.   ob = outbuf;
  185.   obl = outlen;
  186.   while (ibl) {
  187.     k = iconv(cd2, &ib, &ibl, &ob, &obl);
  188.     assert((k != (size_t)(-1) && !ibl) ||
  189.    (k == (size_t)(-1) && errno == EILSEQ && ibl));
  190.     if (k && !ret)
  191.       ret = 1;
  192.     if (ibl && !(k == (size_t)(-1) && errno == E2BIG)) {
  193.       /* Replace one character */
  194.       char *tb = "?";
  195.       size_t tbl = 1;
  196.       k = iconv(cd2, &tb, &tbl, &ob, &obl);
  197.       assert((!k && !tbl) ||
  198.      (k == (size_t)(-1) && errno == EILSEQ && tbl));
  199.       for (++ib, --ibl; ibl && (*ib & 0x80); ib++, ibl--)
  200. ;
  201.     }
  202.   }
  203.   k = iconv(cd2, 0, 0, &ob, &obl);
  204.   assert(!k);
  205.   assert(!obl);
  206.   *ob = '';
  207.   free(utfbuf);
  208.   iconv_close(cd1);
  209.   iconv_close(cd2);
  210.   if (tolen)
  211.     *tolen = outlen;
  212.   if (!to) {
  213.     free(outbuf);
  214.     return ret;
  215.   }
  216.   *to = outbuf;
  217.   return ret;
  218.  fail:
  219.   if(0 != utfbuf)
  220.     free(utfbuf);
  221.   iconv_close(cd1);
  222.   if (cd2 != (iconv_t)(-1))
  223.     iconv_close(cd2);
  224.   return -2;
  225. }
  226. #endif /* HAVE_ICONV */