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

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. /*
  19.  * See the corresponding header file for a description of the functions
  20.  * that this file provides.
  21.  *
  22.  * This was first written for Ogg Vorbis but could be of general use.
  23.  *
  24.  * The only deliberate assumption about data sizes is that a short has
  25.  * at least 16 bits, but this code has only been tested on systems with
  26.  * 8-bit char, 16-bit short and 32-bit int.
  27.  */
  28. #ifdef HAVE_CONFIG_H
  29. #include <config.h>
  30. #endif
  31. #ifndef HAVE_ICONV /* should be ifdef USE_CHARSET_CONVERT */
  32. #include <stdlib.h>
  33. #include "charset.h"
  34. #include "charmaps.h"
  35. /*
  36.  * This is like the standard strcasecmp, but it does not depend
  37.  * on the locale. Locale-dependent functions can be dangerous:
  38.  * we once had a bug involving strcasecmp("iso", "ISO") in a
  39.  * Turkish locale!
  40.  *
  41.  * (I'm not really sure what the official standard says
  42.  * about the sign of strcasecmp("Z", "["), but usually
  43.  * we're only interested in whether it's zero.)
  44.  */
  45. static int ascii_strcasecmp(const char *s1, const char *s2)
  46. {
  47.   char c1, c2;
  48.   for (;; s1++, s2++) {
  49.     if (!*s1 || !*s1)
  50.       break;
  51.     if (*s1 == *s2)
  52.       continue;
  53.     c1 = *s1;
  54.     if ('a' <= c1 && c1 <= 'z')
  55.       c1 += 'A' - 'a';
  56.     c2 = *s2;
  57.     if ('a' <= c2 && c2 <= 'z')
  58.       c2 += 'A' - 'a';
  59.     if (c1 != c2)
  60.       break;
  61.   }
  62.   return (unsigned char)*s1 - (unsigned char)*s2;
  63. }
  64. /*
  65.  * UTF-8 equivalents of the C library's wctomb() and mbtowc().
  66.  */
  67. int utf8_mbtowc(int *pwc, const char *s, size_t n)
  68. {
  69.   unsigned char c;
  70.   int wc, i, k;
  71.   if (!n || !s)
  72.     return 0;
  73.   c = *s;
  74.   if (c < 0x80) {
  75.     if (pwc)
  76.       *pwc = c;
  77.     return c ? 1 : 0;
  78.   }
  79.   else if (c < 0xc2)
  80.     return -1;
  81.   else if (c < 0xe0) {
  82.     if (n >= 2 && (s[1] & 0xc0) == 0x80) {
  83.       if (pwc)
  84. *pwc = ((c & 0x1f) << 6) | (s[1] & 0x3f);
  85.       return 2;
  86.     }
  87.     else
  88.       return -1;
  89.   }
  90.   else if (c < 0xf0)
  91.     k = 3;
  92.   else if (c < 0xf8)
  93.     k = 4;
  94.   else if (c < 0xfc)
  95.     k = 5;
  96.   else if (c < 0xfe)
  97.     k = 6;
  98.   else
  99.     return -1;
  100.   if (n < (size_t)k)
  101.     return -1;
  102.   wc = *s++ & ((1 << (7 - k)) - 1);
  103.   for (i = 1; i < k; i++) {
  104.     if ((*s & 0xc0) != 0x80)
  105.       return -1;
  106.     wc = (wc << 6) | (*s++ & 0x3f);
  107.   }
  108.   if (wc < (1 << (5 * k - 4)))
  109.     return -1;
  110.   if (pwc)
  111.     *pwc = wc;
  112.   return k;
  113. }
  114. int utf8_wctomb(char *s, int wc1)
  115. {
  116.   unsigned int wc = wc1;
  117.   if (!s)
  118.     return 0;
  119.   if (wc < (1u << 7)) {
  120.     *s++ = wc;
  121.     return 1;
  122.   }
  123.   else if (wc < (1u << 11)) {
  124.     *s++ = 0xc0 | (wc >> 6);
  125.     *s++ = 0x80 | (wc & 0x3f);
  126.     return 2;
  127.   }
  128.   else if (wc < (1u << 16)) {
  129.     *s++ = 0xe0 | (wc >> 12);
  130.     *s++ = 0x80 | ((wc >> 6) & 0x3f);
  131.     *s++ = 0x80 | (wc & 0x3f);
  132.     return 3;
  133.   }
  134.   else if (wc < (1u << 21)) {
  135.     *s++ = 0xf0 | (wc >> 18);
  136.     *s++ = 0x80 | ((wc >> 12) & 0x3f);
  137.     *s++ = 0x80 | ((wc >> 6) & 0x3f);
  138.     *s++ = 0x80 | (wc & 0x3f);
  139.     return 4;
  140.   }
  141.   else if (wc < (1u << 26)) {
  142.     *s++ = 0xf8 | (wc >> 24);
  143.     *s++ = 0x80 | ((wc >> 18) & 0x3f);
  144.     *s++ = 0x80 | ((wc >> 12) & 0x3f);
  145.     *s++ = 0x80 | ((wc >> 6) & 0x3f);
  146.     *s++ = 0x80 | (wc & 0x3f);
  147.     return 5;
  148.   }
  149.   else if (wc < (1u << 31)) {
  150.     *s++ = 0xfc | (wc >> 30);
  151.     *s++ = 0x80 | ((wc >> 24) & 0x3f);
  152.     *s++ = 0x80 | ((wc >> 18) & 0x3f);
  153.     *s++ = 0x80 | ((wc >> 12) & 0x3f);
  154.     *s++ = 0x80 | ((wc >> 6) & 0x3f);
  155.     *s++ = 0x80 | (wc & 0x3f);
  156.     return 6;
  157.   }
  158.   else
  159.     return -1;
  160. }
  161. /*
  162.  * The charset "object" and methods.
  163.  */
  164. struct charset {
  165.   int max;
  166.   int (*mbtowc)(void *table, int *pwc, const char *s, size_t n);
  167.   int (*wctomb)(void *table, char *s, int wc);
  168.   void *map;
  169. };
  170. int charset_mbtowc(struct charset *charset, int *pwc, const char *s, size_t n)
  171. {
  172.   return (*charset->mbtowc)(charset->map, pwc, s, n);
  173. }
  174. int charset_wctomb(struct charset *charset, char *s, int wc)
  175. {
  176.   return (*charset->wctomb)(charset->map, s, wc);
  177. }
  178. int charset_max(struct charset *charset)
  179. {
  180.   return charset->max;
  181. }
  182. /*
  183.  * Implementation of UTF-8.
  184.  */
  185. static int mbtowc_utf8(void *map, int *pwc, const char *s, size_t n)
  186. {
  187.   (void)map;
  188.   return utf8_mbtowc(pwc, s, n);
  189. }
  190. static int wctomb_utf8(void *map, char *s, int wc)
  191. {
  192.   (void)map;
  193.   return utf8_wctomb(s, wc);
  194. }
  195. /*
  196.  * Implementation of US-ASCII.
  197.  * Probably on most architectures this compiles to less than 256 bytes
  198.  * of code, so we can save space by not having a table for this one.
  199.  */
  200. static int mbtowc_ascii(void *map, int *pwc, const char *s, size_t n)
  201. {
  202.   int wc;
  203.   (void)map;
  204.   if (!n || !s)
  205.     return 0;
  206.   wc = (unsigned char)*s;
  207.   if (wc & ~0x7f)
  208.     return -1;
  209.   if (pwc)
  210.     *pwc = wc;
  211.   return wc ? 1 : 0;
  212. }
  213. static int wctomb_ascii(void *map, char *s, int wc)
  214. {
  215.   (void)map;
  216.   if (!s)
  217.     return 0;
  218.   if (wc & ~0x7f)
  219.     return -1;
  220.   *s = wc;
  221.   return 1;
  222. }
  223. /*
  224.  * Implementation of ISO-8859-1.
  225.  * Probably on most architectures this compiles to less than 256 bytes
  226.  * of code, so we can save space by not having a table for this one.
  227.  */
  228. static int mbtowc_iso1(void *map, int *pwc, const char *s, size_t n)
  229. {
  230.   int wc;
  231.   (void)map;
  232.   if (!n || !s)
  233.     return 0;
  234.   wc = (unsigned char)*s;
  235.   if (wc & ~0xff)
  236.     return -1;
  237.   if (pwc)
  238.     *pwc = wc;
  239.   return wc ? 1 : 0;
  240. }
  241. static int wctomb_iso1(void *map, char *s, int wc)
  242. {
  243.   (void)map;
  244.   if (!s)
  245.     return 0;
  246.   if (wc & ~0xff)
  247.     return -1;
  248.   *s = wc;
  249.   return 1;
  250. }
  251. /*
  252.  * Implementation of any 8-bit charset.
  253.  */
  254. struct map {
  255.   const unsigned short *from;
  256.   struct inverse_map *to;
  257. };
  258. static int mbtowc_8bit(void *map1, int *pwc, const char *s, size_t n)
  259. {
  260.   struct map *map = map1;
  261.   unsigned short wc;
  262.   if (!n || !s)
  263.     return 0;
  264.   wc = map->from[(unsigned char)*s];
  265.   if (wc == 0xffff)
  266.     return -1;
  267.   if (pwc)
  268.     *pwc = (int)wc;
  269.   return wc ? 1 : 0;
  270. }
  271. /*
  272.  * For the inverse map we use a hash table, which has the advantages
  273.  * of small constant memory requirement and simple memory allocation,
  274.  * but the disadvantage of slow conversion in the worst case.
  275.  * If you need real-time performance while letting a potentially
  276.  * malicious user define their own map, then the method used in
  277.  * linux/drivers/char/consolemap.c would be more appropriate.
  278.  */
  279. struct inverse_map {
  280.   unsigned char first[256];
  281.   unsigned char next[256];
  282. };
  283. /*
  284.  * The simple hash is good enough for this application.
  285.  * Use the alternative trivial hashes for testing.
  286.  */
  287. #define HASH(i) ((i) & 0xff)
  288. /* #define HASH(i) 0 */
  289. /* #define HASH(i) 99 */
  290. static struct inverse_map *make_inverse_map(const unsigned short *from)
  291. {
  292.   struct inverse_map *to;
  293.   char used[256];
  294.   int i, j, k;
  295.   to = (struct inverse_map *)malloc(sizeof(struct inverse_map));
  296.   if (!to)
  297.     return 0;
  298.   for (i = 0; i < 256; i++)
  299.     to->first[i] = to->next[i] = used[i] = 0;
  300.   for (i = 255; i >= 0; i--)
  301.     if (from[i] != 0xffff) {
  302.       k = HASH(from[i]);
  303.       to->next[i] = to->first[k];
  304.       to->first[k] = i;
  305.       used[k] = 1;
  306.     }
  307.   /* Point the empty buckets at an empty list. */
  308.   for (i = 0; i < 256; i++)
  309.     if (!to->next[i])
  310.       break;
  311.   if (i < 256)
  312.     for (j = 0; j < 256; j++)
  313.       if (!used[j])
  314. to->first[j] = i;
  315.   return to;
  316. }
  317. int wctomb_8bit(void *map1, char *s, int wc1)
  318. {
  319.   struct map *map = map1;
  320.   unsigned short wc = wc1;
  321.   int i;
  322.   if (!s)
  323.     return 0;
  324.   if (wc1 & ~0xffff)
  325.     return -1;
  326.   if (1) /* Change 1 to 0 to test the case where malloc fails. */
  327.     if (!map->to)
  328.       map->to = make_inverse_map(map->from);
  329.   if (map->to) {
  330.     /* Use the inverse map. */
  331.     i = map->to->first[HASH(wc)];
  332.     for (;;) {
  333.       if (map->from[i] == wc) {
  334. *s = i;
  335. return 1;
  336.       }
  337.       if (!(i = map->to->next[i]))
  338. break;
  339.     }
  340.   }
  341.   else {
  342.     /* We don't have an inverse map, so do a linear search. */
  343.     for (i = 0; i < 256; i++)
  344.       if (map->from[i] == wc) {
  345. *s = i;
  346. return 1;
  347.       }
  348.   }
  349.   return -1;
  350. }
  351. /*
  352.  * The "constructor" charset_find().
  353.  */
  354. struct charset charset_utf8 = {
  355.   6,
  356.   &mbtowc_utf8,
  357.   &wctomb_utf8,
  358.   0
  359. };
  360. struct charset charset_iso1 = {
  361.   1,
  362.   &mbtowc_iso1,
  363.   &wctomb_iso1,
  364.   0
  365. };
  366. struct charset charset_ascii = {
  367.   1,
  368.   &mbtowc_ascii,
  369.   &wctomb_ascii,
  370.   0
  371. };
  372. struct charset *charset_find(const char *code)
  373. {
  374.   int i;
  375.   /* Find good (MIME) name. */
  376.   for (i = 0; names[i].bad; i++)
  377.     if (!ascii_strcasecmp(code, names[i].bad)) {
  378.       code = names[i].good;
  379.       break;
  380.     }
  381.   /* Recognise some charsets for which we avoid using a table. */
  382.   if (!ascii_strcasecmp(code, "UTF-8"))
  383.     return &charset_utf8;
  384.   if (!ascii_strcasecmp(code, "US-ASCII"))
  385.     return &charset_ascii;
  386.   if (!ascii_strcasecmp(code, "ISO-8859-1"))
  387.     return &charset_iso1;
  388.   /* Look for a mapping for a simple 8-bit encoding. */
  389.   for (i = 0; maps[i].name; i++)
  390.     if (!ascii_strcasecmp(code, maps[i].name)) {
  391.       if (!maps[i].charset) {
  392. maps[i].charset = (struct charset *)malloc(sizeof(struct charset));
  393. if (maps[i].charset) {
  394.   struct map *map = (struct map *)malloc(sizeof(struct map));
  395.   if (!map) {
  396.     free(maps[i].charset);
  397.     maps[i].charset = 0;
  398.   }
  399.   else {
  400.     maps[i].charset->max = 1;
  401.     maps[i].charset->mbtowc = &mbtowc_8bit;
  402.     maps[i].charset->wctomb = &wctomb_8bit;
  403.     maps[i].charset->map = map;
  404.     map->from = maps[i].map;
  405.     map->to = 0; /* inverse mapping is created when required */
  406.   }
  407. }
  408.       }
  409.       return maps[i].charset;
  410.     }
  411.   return 0;
  412. }
  413. /*
  414.  * Function to convert a buffer from one encoding to another.
  415.  * Invalid bytes are replaced by '#', and characters that are
  416.  * not available in the target encoding are replaced by '?'.
  417.  * Each of TO and TOLEN may be zero, if the result is not needed.
  418.  * The output buffer is null-terminated, so it is all right to
  419.  * use charset_convert(fromcode, tocode, s, strlen(s), &t, 0).
  420.  */
  421. int charset_convert(const char *fromcode, const char *tocode,
  422.     const char *from, size_t fromlen,
  423.     char **to, size_t *tolen)
  424. {
  425.   int ret = 0;
  426.   struct charset *charset1, *charset2;
  427.   char *tobuf, *p, *newbuf;
  428.   int i, j, wc;
  429.   charset1 = charset_find(fromcode);
  430.   charset2 = charset_find(tocode);
  431.   if (!charset1 || !charset2 )
  432.     return -1;
  433.   tobuf = (char *)malloc(fromlen * charset2->max + 1);
  434.   if (!tobuf)
  435.     return -2;
  436.   for (p = tobuf; fromlen; from += i, fromlen -= i, p += j) {
  437.     i = charset_mbtowc(charset1, &wc, from, fromlen);
  438.     if (!i)
  439.       i = 1;
  440.     else if (i == -1) {
  441.       i  = 1;
  442.       wc = '#';
  443.       ret = 2;
  444.     }
  445.     j = charset_wctomb(charset2, p, wc);
  446.     if (j == -1) {
  447.       if (!ret)
  448. ret = 1;
  449.       j = charset_wctomb(charset2, p, '?');
  450.       if (j == -1)
  451. j = 0;
  452.     }
  453.   }
  454.   if (tolen)
  455.     *tolen = p - tobuf;
  456.   *p++ = '';
  457.   if (to) {
  458.     newbuf = realloc(tobuf, p - tobuf);
  459.     *to = newbuf ? newbuf : tobuf;
  460.   }
  461.   else
  462.     free(tobuf);
  463.   return ret;
  464. }
  465. #endif /* USE_CHARSET_ICONV */