home *** CD-ROM | disk | FTP | other *** search
/ ftp.uv.es / 2014.11.ftp.uv.es.tar / ftp.uv.es / pub / unix / pine4.10.tar.gz / pine4.10.tar / pine4.10 / imap / src / c-client / utf8.c < prev    next >
C/C++ Source or Header  |  1998-09-16  |  30KB  |  929 lines

  1. /*
  2.  * Program:    UTF-8 routines
  3.  *
  4.  * Author:    Mark Crispin
  5.  *        Networks and Distributed Computing
  6.  *        Computing & Communications
  7.  *        University of Washington
  8.  *        Administration Building, AG-44
  9.  *        Seattle, WA  98195
  10.  *        Internet: MRC@CAC.Washington.EDU
  11.  *
  12.  * Date:    11 June 1997
  13.  * Last Edited:    26 July 1998
  14.  *
  15.  * Copyright 1998 by the University of Washington
  16.  *
  17.  *  Permission to use, copy, modify, and distribute this software and its
  18.  * documentation for any purpose and without fee is hereby granted, provided
  19.  * that the above copyright notices appear in all copies and that both the
  20.  * above copyright notices and this permission notice appear in supporting
  21.  * documentation, and that the name of the University of Washington not be
  22.  * used in advertising or publicity pertaining to distribution of the software
  23.  * without specific, written prior permission.  This software is made
  24.  * available "as is", and
  25.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  26.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  27.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  28.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  29.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
  30.  * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  31.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN
  32.  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  33.  *
  34.  */
  35.  
  36.  
  37. #include <stdio.h>
  38. #include <ctype.h>
  39. #include "mail.h"
  40. #include "osdep.h"
  41. #include "misc.h"
  42. #include "rfc822.h"
  43. #include "utf8.h"
  44.  
  45. /*    *** IMPORTANT ***
  46.  *
  47.  *  There is a very important difference between "character set" and "charset",
  48.  * and the comments in this file reflect these differences.  A "character set"
  49.  * (also known as "coded character set") is a mapping between codepoints and
  50.  * characters.  A "charset" is as defined in MIME, and incorporates one or more
  51.  * coded character sets in a character encoding scheme.  See RFC 2130 for more
  52.  * details.
  53.  */
  54.  
  55.  
  56. /* Character set conversion tables */
  57.  
  58. #include "iso_8859.c"        /* 8-bit single-byte coded graphic */
  59. #include "koi8_r.c"        /* Cyrillic - Russia */
  60. #include "koi8_u.c"        /* Cyrillic - Ukraine */
  61. #include "tis_620.c"        /* Thai */
  62. #include "viscii.c"        /* Vietnamese */
  63. #include "gb_2312.c"        /* Chinese (PRC) - simplified */
  64. #include "gb_12345.c"        /* Chinese (PRC) - traditional */
  65. #include "jis_0208.c"        /* Japanese - basic */
  66. #include "jis_0212.c"        /* Japanese - supplementary */
  67. #include "ksc_5601.c"        /* Korean */
  68. #include "big5.c"        /* Taiwanese (ROC) - industrial standard */
  69. #include "cns11643.c"        /* Taiwanese (ROC) - national standard */
  70. #if 0        /* no charset accesses this table */
  71. #include "cns-14.c"        /* plane 14 of CNS 11643 */
  72. #endif
  73.  
  74. /* EUC parameters */
  75.  
  76. #ifdef GBTOUNICODE        /* PRC simplified Chinese */
  77. static const struct utf8_eucparam gb_param[] = {
  78.   {BASE_GB2312_KU,BASE_GB2312_TEN,MAX_GB2312_KU,MAX_GB2312_TEN,
  79.      (void *) gb2312tab},
  80.   {0,0,0,0,NIL},
  81.   {0,0,0,0,NIL},
  82. };
  83. #endif
  84.  
  85.  
  86. #ifdef GB12345TOUNICODE        /* PRC traditional Chinese */
  87. static const struct utf8_eucparam gbt_param[] = {
  88.   {BASE_GB12345_KU,BASE_GB12345_TEN,MAX_GB12345_KU,MAX_GB12345_TEN,
  89.      (void *) gb12345tab},
  90.   {0,0,0,0,NIL},
  91.   {0,0,0,0,NIL}
  92. };
  93. #endif
  94.  
  95.  
  96. #ifdef BIG5TOUNICODE        /* ROC traditional Chinese */
  97. static const struct utf8_eucparam big5_param[] = {
  98.   {BASE_BIG5_KU,BASE_BIG5_TEN_0,MAX_BIG5_KU,MAX_BIG5_TEN_0,(void *) big5tab},
  99.   {BASE_BIG5_KU,BASE_BIG5_TEN_1,MAX_BIG5_KU,MAX_BIG5_TEN_1,NIL}
  100. };
  101. #endif
  102.  
  103.  
  104. #ifdef JISTOUNICODE        /* Japanese */
  105. static const struct utf8_eucparam jis_param[] = {
  106.   {BASE_JIS0208_KU,BASE_JIS0208_TEN,MAX_JIS0208_KU,MAX_JIS0208_TEN,
  107.      (void *) jis0208tab},
  108.   {MIN_KANA_8,0,MAX_KANA_8,0,(void *) KANA_8},
  109. #ifdef JIS0212TOUNICODE        /* Japanese extended */
  110.   {BASE_JIS0212_KU,BASE_JIS0212_TEN,MAX_JIS0212_KU,MAX_JIS0212_TEN,
  111.      (void *) jis0212tab}
  112. #else
  113.   {0,0,0,0,NIL}
  114. #endif
  115. };
  116. #endif
  117.  
  118.  
  119. #ifdef KSCTOUNICODE        /* Korean */
  120. static const struct utf8_eucparam ksc_param = {
  121.   BASE_KSC5601_KU,BASE_KSC5601_TEN,MAX_KSC5601_KU,MAX_KSC5601_TEN,(void *) ksc5601tab};
  122. #endif
  123.  
  124. /* List of supported charsets (note: all names must be uppercase!) */
  125. static const struct utf8_csent utf8_csvalid[] = {
  126.   {"US-ASCII",NIL,NIL},{"UTF-8",NIL,NIL},
  127.   {"ISO-8859-1",utf8_text_8859_1,NIL},
  128.   {"ISO-8859-2",utf8_text_1byte,(void *) iso8859_2tab},
  129.   {"ISO-8859-3",utf8_text_1byte,(void *) iso8859_3tab},
  130.   {"ISO-8859-4",utf8_text_1byte,(void *) iso8859_4tab},
  131.   {"ISO-8859-5",utf8_text_1byte,(void *) iso8859_5tab},
  132.   {"ISO-8859-6",utf8_text_1byte,(void *) iso8859_6tab},
  133.   {"ISO-8859-7",utf8_text_1byte,(void *) iso8859_7tab},
  134.   {"ISO-8859-8",utf8_text_1byte,(void *) iso8859_8tab},
  135.   {"ISO-8859-9",utf8_text_1byte,(void *) iso8859_9tab},
  136.   {"ISO-8859-10",utf8_text_1byte,(void *) iso8859_10tab},
  137.   {"ISO-8859-13",utf8_text_1byte,(void *) iso8859_13tab},
  138.   {"ISO-8859-15",utf8_text_1byte,(void *) iso8859_15tab},
  139. #ifdef GBTOUNICODE
  140.   {"GB2312",utf8_text_euc,(void *) gb_param},
  141.   {"CN-GB",utf8_text_euc,(void *) gb_param},
  142. #ifdef CNS1TOUNICODE
  143.   {"ISO-2022-CN",utf8_text_2022,NIL},
  144. #endif
  145. #endif
  146. #ifdef GB12345TOUNICODE
  147.   {"CN-GB-12345",utf8_text_euc,(void *) gbt_param},
  148. #endif
  149. #ifdef BIG5TOUNICODE
  150.   {"BIG5",utf8_text_dbyte2,(void *) big5_param},
  151.   {"CN-BIG5",utf8_text_dbyte2,(void *) big5_param},
  152. #endif
  153. #ifdef JISTOUNICODE
  154.   {"ISO-2022-JP",utf8_text_2022,NIL},
  155.   {"EUC-JP",utf8_text_euc,(void *) jis_param},
  156.   {"SHIFT_JIS",utf8_text_sjis,NIL},{"SHIFT-JIS",utf8_text_sjis,NIL},
  157. #ifdef JIS0212TOUNICODE
  158.   {"ISO-2022-JP-1",utf8_text_2022,NIL},
  159. #ifdef GBTOUNICODE
  160. #ifdef KSCTOUNICODE
  161.   {"ISO-2022-JP-2",utf8_text_2022,NIL},
  162. #endif
  163. #endif
  164. #endif
  165. #endif
  166. #ifdef KSCTOUNICODE
  167.   {"ISO-2022-KR",utf8_text_2022,NIL},
  168.   {"EUC-KR",utf8_text_dbyte,(void *) &ksc_param},
  169. #endif
  170.   {"KOI8-R",utf8_text_1byte,(void *) koi8rtab},
  171.   {"KOI8-U",utf8_text_1byte,(void *) koi8utab},
  172.   {"KOI8-RU",utf8_text_1byte,(void *) koi8utab},
  173.   {"TIS-620",utf8_text_1byte,(void *) tis620tab},
  174.   {"VISCII",utf8_text_1byte8,(void *) visciitab},
  175.   NIL
  176. };
  177.  
  178. /* Convert charset labelled sized text to UTF-8
  179.  * Accepts: source sized text
  180.  *        charset
  181.  *        pointer to returned sized text if non-NIL
  182.  *        flags (currently non-zero if want error for unknown charset)
  183.  * Returns: T if successful, NIL if failure
  184.  */
  185.  
  186. long utf8_text (SIZEDTEXT *text,char *charset,SIZEDTEXT *ret,long flags)
  187. {
  188.   unsigned long i;
  189.   char *t,tmp[MAILTMPLEN];
  190.   if (ret) {            /* default is to just return identity */
  191.     ret->data = text->data;
  192.     ret->size = text->size;
  193.   }
  194.   if (!charset || !*charset) {    /* missing charset? */
  195.     if (ret && (text->size > 2)) for (i = 0; i < text->size - 1; i++) {
  196.                 /* special hack for untagged ISO-2022 */
  197.       if ((text->data[i] == '\033') && (text->data[i+1] == '$')) {
  198.     utf8_text_2022 (text,ret,NIL);
  199.     break;
  200.       }
  201.                 /* special hack for "just send 8" cretins */
  202.       else if (text->data[i] & BIT8) {
  203.     utf8_text_8859_1 (text,ret,NIL);
  204.     break;
  205.       }
  206.     }
  207.     return LONGT;
  208.   }
  209.                 /* otherwise look for charset */
  210.   for (i = 0, ucase (strcpy (tmp,charset)); utf8_csvalid[i].name; i++)
  211.     if (!strcmp (tmp,utf8_csvalid[i].name)) {
  212.       if (ret && utf8_csvalid[i].dsp)
  213.     (*utf8_csvalid[i].dsp) (text,ret,utf8_csvalid[i].tab);
  214.       return LONGT;        /* success */
  215.     }
  216.   if (flags) {            /* charset not found */
  217.     strcpy (tmp,"[BADCHARSET (");
  218.     for (i = 0, t = tmp + strlen (tmp); utf8_csvalid[i].name;
  219.      i++,t += strlen (t)) sprintf (t,"%s ",utf8_csvalid[i].name);
  220.     sprintf (t + strlen (t) - 1,")] Unknown charset: %.80s",charset);
  221.     mm_log (tmp,ERROR);
  222.   }
  223.   return NIL;            /* failed */
  224. }
  225.  
  226. /* Convert ISO-8859-1 sized text to UTF-8
  227.  * Accepts: source sized text
  228.  *        pointer to returned sized text
  229.  *        conversion table
  230.  */
  231.  
  232. void utf8_text_8859_1 (SIZEDTEXT *text,SIZEDTEXT *ret,void *tab)
  233. {
  234.   unsigned long i;
  235.   unsigned char *s;
  236.   unsigned int c;
  237.   for (ret->size = i = 0; i < text->size;
  238.        ret->size += (text->data[i++] & BIT8) ? 2 : 1);
  239.   s = ret->data = (unsigned char *) fs_get (ret->size + 1);
  240.   for (i = 0; i < text->size;) {
  241.     if ((c = text->data[i++]) & BIT8) {
  242.       *s++ = 0xc0 | ((c >> 6) & 0x3f);
  243.       *s++ = BIT8 | (c & 0x3f);
  244.     }
  245.     else *s++ = c;        /* ASCII character */
  246.   }
  247. }
  248.  
  249. /* Convert single byte ASCII+8bit character set sized text to UTF-8
  250.  * Accepts: source sized text
  251.  *        pointer to return sized text
  252.  *        conversion table
  253.  */
  254.  
  255. void utf8_text_1byte (SIZEDTEXT *text,SIZEDTEXT *ret,void *tab)
  256. {
  257.   unsigned long i;
  258.   unsigned char *s;
  259.   unsigned int c;
  260.   unsigned short *tbl = (unsigned short *) tab;
  261.   for (ret->size = i = 0; i < text->size; ret->size += UTF8_SIZE (c))
  262.     if ((c = text->data[i++]) & BIT8) c = tbl[c & BITS7];
  263.   s = ret->data = (unsigned char *) fs_get (ret->size + 1);
  264.   for (i = 0; i < text->size;) {
  265.     if ((c = text->data[i++]) & BIT8) c = tbl[c & BITS7];
  266.     UTF8_PUT (s,c)        /* convert Unicode to UTF-8 */
  267.   }
  268. }
  269.  
  270.  
  271. /* Convert single byte 8bit character set sized text to UTF-8
  272.  * Accepts: source sized text
  273.  *        pointer to return sized text
  274.  *        conversion table
  275.  */
  276.  
  277. void utf8_text_1byte8 (SIZEDTEXT *text,SIZEDTEXT *ret,void *tab)
  278. {
  279.   unsigned long i;
  280.   unsigned char *s;
  281.   unsigned int c;
  282.   unsigned short *tbl = (unsigned short *) tab;
  283.   for (ret->size = i = 0; i < text->size; ret->size += UTF8_SIZE (c))
  284.     c = tbl[text->data[i++]];
  285.   s = ret->data = (unsigned char *) fs_get (ret->size + 1);
  286.   for (i = 0; i < text->size;) {
  287.     c = tbl[text->data[i++]];
  288.     UTF8_PUT (s,c)        /* convert Unicode to UTF-8 */
  289.   }
  290. }
  291.  
  292. /* Convert EUC sized text to UTF-8
  293.  * Accepts: source sized text
  294.  *        pointer to return sized text
  295.  *        EUC parameter table
  296.  */
  297.  
  298. void utf8_text_euc (SIZEDTEXT *text,SIZEDTEXT *ret,void *tab)
  299. {
  300.   unsigned long i;
  301.   unsigned char *s;
  302.   unsigned int pass,c,c1,ku,ten;
  303.   struct utf8_eucparam *p1 = (struct utf8_eucparam *) tab;
  304.   struct utf8_eucparam *p2 = p1 + 1;
  305.   struct utf8_eucparam *p3 = p1 + 2;
  306.   unsigned short *t1 = (unsigned short *) p1->tab;
  307.   unsigned short *t2 = (unsigned short *) p2->tab;
  308.   unsigned short *t3 = (unsigned short *) p3->tab;
  309.   for (pass = 0,ret->size = 0; pass <= 1; pass++) {
  310.     for (i = 0; i < text->size;) {
  311.                 /* not CS0? */
  312.       if ((c = text->data[i++]) & BIT8) {
  313.                 /* yes, must have another high byte */
  314.     if ((i >= text->size) || !((c1 = text->data[i++]) & BIT8))
  315.       c = BOGON;        /* out of space or bogon */
  316.     else switch (c) {    /* check 8bit code set */
  317.     case EUC_CS2:        /* CS2 */
  318.       if (p2->base_ku) {    /* CS2 set up? */
  319.         if (p2->base_ten)    /* yes, multibyte? */
  320.           c = ((i < text->size) && ((c = text->data[i++]) & BIT8) &&
  321.            ((ku = (c1 & BITS7) - p2->base_ku) < p2->max_ku) &&
  322.            ((ten = (c & BITS7) - p2->base_ten) < p2->max_ten)) ?
  323.              t2[(ku*p2->max_ten) + ten] : BOGON;
  324.         else c = ((c1 >= p2->base_ku) && (c1 <= p2->max_ku)) ?
  325.           c1 + ((unsigned int) p2->tab) : BOGON;
  326.       }      
  327.       else {        /* CS2 not set up */
  328.         c = BOGON;        /* swallow byte, say bogon */
  329.         if (i < text->size) i++;
  330.       }
  331.       break;
  332.     case EUC_CS3:        /* CS3 */
  333.       if (p3->base_ku) {    /* CS3 set up? */
  334.         if (p3->base_ten)    /* yes, multibyte? */
  335.           c = ((i < text->size) && ((c = text->data[i++]) & BIT8) &&
  336.            ((ku = (c1 & BITS7) - p3->base_ku) < p3->max_ku) &&
  337.            ((ten = (c & BITS7) - p3->base_ten) < p3->max_ten)) ?
  338.              t3[(ku*p3->max_ten) + ten] : BOGON;
  339.         else c = ((c1 >= p3->base_ku) && (c1 <= p3->max_ku)) ?
  340.           c1 + ((unsigned int) p3->tab) : BOGON;
  341.       }      
  342.       else {        /* CS3 not set up */
  343.         c = BOGON;        /* swallow byte, say bogon */
  344.         if (i < text->size) i++;
  345.       }
  346.       break;
  347.  
  348.     default:
  349.       c = (((ku = (c & BITS7) - p1->base_ku) < p1->max_ku) &&
  350.            ((ten = (c1 & BITS7) - p1->base_ten) < p1->max_ten)) ?
  351.          t1[(ku*p1->max_ten) + ten] : BOGON;
  352.         /* special hack for JIS X 0212: merge rows less than 10 */
  353.       if (!c && ku && (ku < 10) && t3 && p3->base_ten)
  354.         c = t3[((ku - (p3->base_ku - p1->base_ku))*p3->max_ten) + ten];
  355.     }
  356.       }
  357.       if (pass) UTF8_PUT (s,c)
  358.       else ret->size += UTF8_SIZE (c);
  359.     }
  360.     if (!pass) s = ret->data = (unsigned char *) fs_get (ret->size + 1);
  361.   }
  362. }
  363.  
  364.  
  365. /* Convert ASCII + double-byte sized text to UTF-8
  366.  * Accepts: source sized text
  367.  *        pointer to return sized text
  368.  *        conversion table
  369.  */
  370.  
  371. void utf8_text_dbyte (SIZEDTEXT *text,SIZEDTEXT *ret,void *tab)
  372. {
  373.   unsigned long i;
  374.   unsigned char *s;
  375.   unsigned int c,c1,ku,ten;
  376.   struct utf8_eucparam *p1 = (struct utf8_eucparam *) tab;
  377.   unsigned short *t1 = (unsigned short *) p1->tab;
  378.   for (ret->size = i = 0; i < text->size; ret->size += UTF8_SIZE (c))
  379.     if ((c = text->data[i++]) & BIT8)
  380.       c = ((i < text->size) && (c1 = text->data[i++]) &&
  381.        ((ku = c - p1->base_ku) < p1->max_ku) &&
  382.        ((ten = c1 - p1->base_ten) < p1->max_ten)) ?
  383.          t1[(ku*p1->max_ten) + ten] : BOGON;
  384.   s = ret->data = (unsigned char *) fs_get (ret->size + 1);
  385.   for (i = 0; i < text->size;) {
  386.     if ((c = text->data[i++]) & BIT8)
  387.       c = ((i < text->size) && (c1 = text->data[i++]) &&
  388.        ((ku = c - p1->base_ku) < p1->max_ku) &&
  389.        ((ten = c1 - p1->base_ten) < p1->max_ten)) ?
  390.          t1[(ku*p1->max_ten) + ten] : BOGON;
  391.     UTF8_PUT (s,c)        /* convert Unicode to UTF-8 */
  392.   }
  393. }
  394.  
  395. /* Convert ASCII + double byte 2 plane sized text to UTF-8
  396.  * Accepts: source sized text
  397.  *        pointer to return sized text
  398.  *        conversion table
  399.  */
  400.  
  401. void utf8_text_dbyte2 (SIZEDTEXT *text,SIZEDTEXT *ret,void *tab)
  402. {
  403.   unsigned long i,j;
  404.   unsigned char *s;
  405.   unsigned int c,c1,ku,ten;
  406.   struct utf8_eucparam *p1 = (struct utf8_eucparam *) tab;
  407.   struct utf8_eucparam *p2 = p1 + 1;
  408.   unsigned short *t = (unsigned short *) p1->tab;
  409.   for (ret->size = i = 0; i < text->size; ret->size += UTF8_SIZE (c))
  410.     if ((c = text->data[i++]) & BIT8) {
  411.       if ((i >= text->size) || !(c1 = text->data[i++]))
  412.     c = BOGON;        /* out of space or bogon */
  413.       else if (c1 & BIT8)    /* high vs. low plane */
  414.     c = ((ku = c - p2->base_ku) < p2->max_ku &&
  415.          ((ten = c1 - p2->base_ten) < p2->max_ten)) ?
  416.            t[(ku*(p1->max_ten + p2->max_ten)) + p1->max_ten + ten] : BOGON;
  417.       else c = ((ku = c - p1->base_ku) < p1->max_ku &&
  418.         ((ten = c1 - p1->base_ten) < p1->max_ten)) ?
  419.           t[(ku*(p1->max_ten + p2->max_ten)) + ten] : BOGON;
  420.     }
  421.   s = ret->data = (unsigned char *) fs_get (ret->size + 1);
  422.   for (i = j = 0; i < text->size;) {
  423.     if ((c = text->data[i++]) & BIT8) {
  424.       if ((i >= text->size) || !(c1 = text->data[i++]))
  425.     c = BOGON;        /* out of space or bogon */
  426.       else if (c1 & BIT8)    /* high vs. low plane */
  427.     c = ((ku = c - p2->base_ku) < p2->max_ku &&
  428.          ((ten = c1 - p2->base_ten) < p2->max_ten)) ?
  429.            t[(ku*(p1->max_ten + p2->max_ten)) + p1->max_ten + ten] : BOGON;
  430.       else c = ((ku = c - p1->base_ku) < p1->max_ku &&
  431.         ((ten = c1 - p1->base_ten) < p1->max_ten)) ?
  432.           t[(ku*(p1->max_ten + p2->max_ten)) + ten] : BOGON;
  433.     }
  434.     UTF8_PUT (s,c)    /* convert Unicode to UTF-8 */
  435.   }
  436. }
  437.  
  438. #ifdef JISTOUNICODE        /* Japanese */
  439. /* Convert Shift JIS sized text to UTF-8
  440.  * Accepts: source sized text
  441.  *        pointer to return sized text
  442.  *        conversion table
  443.  */
  444.  
  445. void utf8_text_sjis (SIZEDTEXT *text,SIZEDTEXT *ret,void *tab)
  446. {
  447.   unsigned long i;
  448.   unsigned char *s;
  449.   unsigned int c,c1,ku,ten;
  450.   for (ret->size = i = 0; i < text->size; ret->size += UTF8_SIZE (c))
  451.     if ((c = text->data[i++]) & BIT8) {
  452.                 /* half-width katakana */
  453.       if ((c >= MIN_KANA_8) && (c <= MAX_KANA_8)) c += KANA_8;
  454.       else if (i >= text->size) c = BOGON;
  455.       else {        /* Shift-JIS */
  456.     c1 = text->data[i++];
  457.     SJISTOJIS (c,c1);
  458.     c = JISTOUNICODE (c,c1,ku,ten);
  459.       }
  460.     }
  461.   s = ret->data = (unsigned char *) fs_get (ret->size + 1);
  462.   for (i = 0; i < text->size;) {
  463.     if ((c = text->data[i++]) & BIT8) {
  464.                 /* half-width katakana */
  465.       if ((c >= MIN_KANA_8) && (c <= MAX_KANA_8)) c += KANA_8;
  466.       else {        /* Shift-JIS */
  467.     c1 = text->data[i++];
  468.     SJISTOJIS (c,c1);
  469.     c = JISTOUNICODE (c,c1,ku,ten);
  470.       }
  471.     }
  472.     UTF8_PUT (s,c)        /* convert Unicode to UTF-8 */
  473.   }
  474. }
  475. #endif
  476.  
  477. /* Convert ISO-2022 sized text to UTF-8
  478.  * Accepts: source sized text
  479.  *        pointer to returned sized text
  480.  *        conversion table
  481.  */
  482.  
  483. void utf8_text_2022 (SIZEDTEXT *text,SIZEDTEXT *ret,void *tab)
  484. {
  485.   unsigned long i;
  486.   unsigned char *s;
  487.   unsigned int pass,state,c,co,gi,gl,gr,g[4],ku,ten;
  488.   for (pass = 0,ret->size = 0; pass <= 1; pass++) {
  489.     gi = 0;            /* quell compiler warnings */
  490.     state = I2S_CHAR;        /* initialize engine */
  491.     g[0]= g[2] = I2CS_ASCII;    /* G0 and G2 are ASCII */
  492.     g[1]= g[3] = I2CS_ISO8859_1;/* G1 and G3 are ISO-8850-1 */
  493.     gl = I2C_G0; gr = I2C_G1;    /* left is G0, right is G1 */
  494.     for (i = 0; i < text->size;) {
  495.       c = text->data[i++];
  496.       switch (state) {        /* dispatch based upon engine state */
  497.       case I2S_ESC:        /* ESC seen */
  498.     switch (c) {        /* process intermediate character */
  499.     case I2C_MULTI:        /* multibyte character? */
  500.       state = I2S_MUL;    /* mark multibyte flag seen */
  501.       break;
  502.         case I2C_SS2:        /* single shift GL to G2 */
  503.     case I2C_SS2_ALT:    /* Taiwan SeedNet */
  504.       gl |= I2C_SG2;
  505.       break;
  506.         case I2C_SS3:        /* single shift GL to G3 */
  507.     case I2C_SS3_ALT:    /* Taiwan SeedNet */
  508.       gl |= I2C_SG3;
  509.       break;
  510.         case I2C_LS2:        /* shift GL to G2 */
  511.       gl = I2C_G2;
  512.       break;
  513.         case I2C_LS3:        /* shift GL to G3 */
  514.       gl = I2C_G3;
  515.       break;
  516.         case I2C_LS1R:        /* shift GR to G1 */
  517.       gr = I2C_G1;
  518.       break;
  519.         case I2C_LS2R:        /* shift GR to G2 */
  520.       gr = I2C_G2;
  521.       break;
  522.         case I2C_LS3R:        /* shift GR to G3 */
  523.       gr = I2C_G3;
  524.       break;
  525.     case I2C_G0_94: case I2C_G1_94: case I2C_G2_94:    case I2C_G3_94:
  526.       g[gi = c - I2C_G0_94] = (state == I2S_MUL) ? I2CS_94x94 : I2CS_94;
  527.       state = I2S_INT;    /* ready for character set */
  528.       break;
  529.     case I2C_G0_96:    case I2C_G1_96: case I2C_G2_96:    case I2C_G3_96:
  530.       g[gi = c - I2C_G0_96] = (state == I2S_MUL) ? I2CS_96x96 : I2CS_96;
  531.       state = I2S_INT;    /* ready for character set */
  532.       break;
  533.     default:        /* bogon */
  534.       if (pass) *s++ = I2C_ESC,*s++ = c;
  535.       else ret->size += 2;
  536.       state = I2S_CHAR;    /* return to previous state */
  537.     }
  538.     break;
  539.  
  540.       case I2S_MUL:        /* ESC $ */
  541.     switch (c) {        /* process multibyte intermediate character */
  542.     case I2C_G0_94: case I2C_G1_94: case I2C_G2_94:    case I2C_G3_94:
  543.       g[gi = c - I2C_G0_94] = I2CS_94x94;
  544.       state = I2S_INT;    /* ready for character set */
  545.       break;
  546.     case I2C_G0_96:    case I2C_G1_96: case I2C_G2_96:    case I2C_G3_96:
  547.       g[gi = c - I2C_G0_96] = I2CS_96x96;
  548.       state = I2S_INT;    /* ready for character set */
  549.       break;
  550.     default:        /* probably omitted I2CS_94x94 */
  551.       g[gi = I2C_G0] = I2CS_94x94 | c;
  552.       state = I2S_CHAR;    /* return to character state */
  553.     }
  554.     break;
  555.       case I2S_INT:
  556.     state = I2S_CHAR;    /* return to character state */
  557.     g[gi] |= c;        /* set character set */
  558.     break;
  559.  
  560.       case I2S_CHAR:        /* character data */
  561.     switch (c) {
  562.     case I2C_ESC:        /* ESC character */
  563.       state = I2S_ESC;    /* see if ISO-2022 prefix */
  564.       break;
  565.     case I2C_SI:        /* shift GL to G0 */
  566.       gl = I2C_G0;
  567.       break;
  568.     case I2C_SO:        /* shift GL to G1 */
  569.       gl = I2C_G1;
  570.       break;
  571.         case I2C_SS2_ALT:    /* single shift GL to G2 */
  572.     case I2C_SS2_ALT_7:
  573.       gl |= I2C_SG2;
  574.       break;
  575.         case I2C_SS3_ALT:    /* single shift GL to G3 */
  576.     case I2C_SS3_ALT_7:
  577.       gl |= I2C_SG3;
  578.       break;
  579.  
  580.     default:        /* ordinary character */
  581.       co = c;        /* note original character */
  582.       if (gl & (3 << 2)) {    /* single shifted? */
  583.         gi = g[gl >> 2];    /* get shifted character set */
  584.         gl &= 0x3;        /* cancel shift */
  585.       }
  586.                 /* select left or right half */
  587.       else gi = (c & BIT8) ? g[gr] : g[gl];
  588.       c &= BITS7;        /* make 7-bit */
  589.       switch (gi) {        /* interpret in character set */
  590.       case I2CS_ASCII:    /* ASCII */
  591.         break;        /* easy! */
  592.       case I2CS_BRITISH:    /* British ASCII */
  593.                 /* Pound sterling sign */
  594.         if (c == 0x23) c = UCS2_POUNDSTERLING;
  595.         break;
  596.       case I2CS_JIS_ROMAN:    /* JIS Roman */
  597.       case I2CS_JIS_BUGROM:    /* old bugs */
  598.         switch (c) {    /* two exceptions to ASCII */
  599.         case 0x5c:        /* Yen sign */
  600.           c = UCS2_YEN;
  601.           break;
  602.         case 0x7e:        /* overline */
  603.           c = UCS2_OVERLINE;
  604.           break;
  605.         }
  606.         break;
  607.       case I2CS_JIS_KANA:    /* JIS katakana */
  608.         if ((c >= MIN_KANA_7) && (c <= MAX_KANA_7)) c += KANA_7;
  609.         break;
  610.       case I2CS_ISO8859_1:    /* Latin-1 (West European) */
  611.         c |= BIT8;        /* just turn on high bit */
  612.         break;
  613.       case I2CS_ISO8859_2:    /* Latin-2 (Czech, Slovak) */
  614.         c = iso8859_2tab[c];
  615.         break;
  616.       case I2CS_ISO8859_3:    /* Latin-3 (Dutch, Turkish) */
  617.         c = iso8859_3tab[c];
  618.         break;
  619.       case I2CS_ISO8859_4:    /* Latin-4 (Scandinavian) */
  620.         c = iso8859_4tab[c];
  621.         break;
  622.       case I2CS_ISO8859_5:    /* Cyrillic */
  623.         c = iso8859_5tab[c];
  624.         break;
  625.       case I2CS_ISO8859_6:    /* Arabic */
  626.         c = iso8859_6tab[c];
  627.         break;
  628.       case I2CS_ISO8859_7:    /* Greek */
  629.         c = iso8859_7tab[c];
  630.         break;
  631.       case I2CS_ISO8859_8:    /* Hebrew */
  632.         c = iso8859_8tab[c];
  633.         break;
  634.       case I2CS_ISO8859_9:    /* Latin-5 (Finnish, Portuguese) */
  635.         c = iso8859_9tab[c];
  636.         break;
  637.       case I2CS_TIS620:    /* Thai */
  638.         c = tis620tab[c];
  639.         break;
  640.       case I2CS_ISO8859_10:    /* Latin-6 (Northern Europe) */
  641.         c = iso8859_10tab[c];
  642.         break;
  643.       case I2CS_ISO8859_13:    /* Baltic */
  644.         c = iso8859_13tab[c];
  645.         break;
  646.       case I2CS_VSCII:    /* Vietnamese */
  647.         c = visciitab[c];
  648.         break;
  649.       case I2CS_ISO8859_15:    /* Euro */
  650.         c = iso8859_15tab[c];
  651.         break;
  652.  
  653.       default:        /* all other character sets */
  654.                 /* multibyte character set */
  655.         if ((gi & I2CS_MUL) && !(c & BIT8) && isgraph (c)) {
  656.           c = (i < text->size) ? text->data[i++] : 0;
  657.           switch (gi) {
  658. #ifdef GBTOUNICODE
  659.           case I2CS_GB:    /* GB 2312 */
  660.         c = GBTOUNICODE (co,c,ku,ten);
  661.         break;
  662. #endif
  663. #ifdef JISTOUNICODE
  664.           case I2CS_JIS_OLD:/* JIS X 0208-1978 */
  665.           case I2CS_JIS_NEW:/* JIS X 0208-1983 */
  666.         c = JISTOUNICODE (co,c,ku,ten);
  667.         break;
  668. #endif
  669. #ifdef JIS0212TOUNICODE
  670.           case I2CS_JIS_EXT:/* JIS X 0212-1990 */
  671.         c = JIS0212TOUNICODE (co,c,ku,ten);
  672.         break;
  673. #endif
  674. #ifdef KSCTOUNICODE
  675.           case I2CS_KSC:    /* KSC 5601 */
  676.         co |= BIT8;    /* make into EUC */
  677.         c |= BIT8;
  678.         c = KSCTOUNICODE (co,c,ku,ten);
  679.         break;
  680. #endif
  681. #ifdef CNS1TOUNICODE
  682.           case I2CS_CNS1:    /* CNS 11643 plane 1 */
  683.         c = CNS1TOUNICODE (co,c,ku,ten);
  684.         break;
  685. #endif
  686. #ifdef CNS2TOUNICODE
  687.           case I2CS_CNS2:    /* CNS 11643 plane 2 */
  688.         c = CNS2TOUNICODE (co,c,ku,ten);
  689.         break;
  690. #endif
  691.           default:        /* unknown multibyte, treat as UCS-2 */
  692.         c |= (co << 8);    /* wrong, but nothing else to do */
  693.         break;
  694.           }
  695.         }
  696.         else c = co;    /* unknown single byte, treat as 8859-1 */
  697.       }
  698.       if (pass) UTF8_PUT (s,c)
  699.       else ret->size += UTF8_SIZE (c);
  700.     }
  701.       }
  702.     }
  703.     if (!pass) s = ret->data = (unsigned char *) fs_get (ret->size + 1);
  704.     else if (((unsigned long) (s - ret->data)) != ret->size)
  705.       fatal ("ISO-2022 to UTF-8 botch");
  706.   }
  707. }
  708.  
  709. /* Convert charset labelled searchpgm to UTF-8 in place
  710.  * Accepts: search program
  711.  *        charset
  712.  */
  713.  
  714. void utf8_searchpgm (SEARCHPGM *pgm,char *charset)
  715. {
  716.   SIZEDTEXT txt;
  717.   SEARCHHEADER *hl;
  718.   SEARCHOR *ol;
  719.   SEARCHPGMLIST *pl;
  720.   if (pgm) {            /* must have a search program */
  721.     utf8_stringlist (pgm->bcc,charset);
  722.     utf8_stringlist (pgm->cc,charset);
  723.     utf8_stringlist (pgm->from,charset);
  724.     utf8_stringlist (pgm->to,charset);
  725.     utf8_stringlist (pgm->subject,charset);
  726.     for (hl = pgm->header; hl; hl = hl->next) {
  727.       if (utf8_text (&hl->line,charset,&txt,NIL)) {
  728.     fs_give ((void **) &hl->line.data);
  729.     hl->line.data = txt.data;
  730.     hl->line.size = txt.size;
  731.       }
  732.       if (utf8_text (&hl->text,charset,&txt,NIL)) {
  733.     fs_give ((void **) &hl->text.data);
  734.     hl->text.data = txt.data;
  735.     hl->text.size = txt.size;
  736.       }
  737.     }
  738.     utf8_stringlist (pgm->body,charset);
  739.     utf8_stringlist (pgm->text,charset);
  740.     for (ol = pgm->or; ol; ol = ol->next) {
  741.       utf8_searchpgm (ol->first,charset);
  742.       utf8_searchpgm (ol->second,charset);
  743.     }
  744.     for (pl = pgm->not; pl; pl = pl->next) utf8_searchpgm (pl->pgm,charset);
  745.   }
  746. }
  747.  
  748.  
  749. /* Convert charset labelled stringlist to UTF-8 in place
  750.  * Accepts: string list
  751.  *        charset
  752.  */
  753.  
  754. void utf8_stringlist (STRINGLIST *st,char *charset)
  755. {
  756.   SIZEDTEXT txt;
  757.                 /* convert entire stringstruct */
  758.   if (st) do if (utf8_text (&st->text,charset,&txt,NIL)) {
  759.     fs_give ((void **) &st->text.data);
  760.     st->text.data = txt.data; /* transfer this text */
  761.     st->text.size = txt.size;
  762.   } while (st = st->next);
  763. }
  764.  
  765. /* Convert MIME-2 sized text to UTF-8
  766.  * Accepts: source sized text
  767.  *        charset
  768.  * Returns: T if successful, NIL if failure
  769.  */
  770.  
  771. #define MINENCWORD 9
  772.  
  773. long utf8_mime2text (SIZEDTEXT *src,SIZEDTEXT *dst)
  774. {
  775.   unsigned char *s,*se,*e,*ee,*t,*te;
  776.   char *cs,*ce,*ls;
  777.   SIZEDTEXT txt,rtxt;
  778.   unsigned long i;
  779.   dst->data = NIL;        /* default is no encoded words */
  780.                 /* look for encoded words */
  781.   for (s = src->data, se = src->data + src->size; s < se; s++) {
  782.     if (((se - s) > MINENCWORD) && (*s == '=') && (s[1] == '?') &&
  783.       (cs = (char *) mime2_token (s+2,se,(unsigned char **) &ce)) &&
  784.     (e = mime2_token ((unsigned char *) ce+1,se,&ee)) &&
  785.     (t = mime2_text (e+2,se,&te)) && (ee == e + 1)) {
  786.       if (mime2_decode (e,t,te,&txt)) {
  787.     *ce = '\0';        /* temporarily tie off charset */
  788.     if (ls = strchr (cs,'*')) *ls = '\0';
  789.     if (utf8_text (&txt,cs,&rtxt,NIL)) {
  790.       if (!dst->data) {    /* need to create buffer now? */
  791.                 /* allocate for worst case */
  792.         dst->data = (unsigned char *)
  793.           fs_get ((size_t) ((src->size / 8) + 1) * 9);
  794.         memcpy (dst->data,src->data,(size_t) (dst->size = s - src->data));
  795.       }
  796.       for (i=0; i < rtxt.size; i++) dst->data[dst->size++] = rtxt.data[i];
  797.                 /* all done with converted text */
  798.       if (rtxt.data != txt.data) fs_give ((void **) &rtxt.data);
  799.     }
  800.     if (ls) *ls = '*';    /* restore language tag delimiter */
  801.     *ce = '?';        /* restore charset delimiter */
  802.                 /* all done with decoded text */
  803.     fs_give ((void **) &txt.data);
  804.     s = te+1;        /* continue scan after encoded word */
  805.  
  806.                 /* skip leading whitespace */
  807.     for (t = s + 1; (t < se) && ((*t == ' ') || (*t == '\t')); t++);
  808.                 /* see if likely continuation encoded word */
  809.     if (t < (se - MINENCWORD)) switch (*t) {
  810.     case '=':        /* possible encoded word? */
  811.       if (t[1] == '?') s = t - 1;
  812.       break;
  813.     case '\015':        /* CR, eat a following LF */
  814.       if (t[1] == '\012') t++;
  815.     case '\012':        /* possible end of logical line */
  816.       if ((t[1] == ' ') || (t[1] == '\t')) {
  817.         do t++;
  818.         while ((t < (se - MINENCWORD)) && ((t[1] == ' ')||(t[1] == '\t')));
  819.         if ((t < (se - MINENCWORD)) && (t[1] == '=') && (t[2] == '?'))
  820.           s = t;        /* definitely looks like continuation */
  821.       }
  822.     }
  823.       }
  824.       else {            /* restore original text */
  825.     if (dst->data) fs_give ((void **) &dst->data);
  826.     dst->data = src->data;
  827.     dst->size = src->size;
  828.     return NIL;        /* syntax error: MIME-2 decoding failure */
  829.       }
  830.     }
  831.                 /* stash ordinary character */
  832.     else if (dst->data) dst->data[dst->size++] = *s;
  833.   }
  834.   if (dst->data) dst->data[dst->size] = '\0';
  835.   else {            /* nothing converted, return identity */
  836.     dst->data = src->data;
  837.     dst->size = src->size;
  838.   }
  839.   return T;            /* success */
  840. }
  841.  
  842. /* Decode MIME-2 text
  843.  * Accepts: Encoding
  844.  *        text
  845.  *        text end
  846.  *        destination sized text
  847.  * Returns: T if successful, else NIL
  848.  */
  849.  
  850. long mime2_decode (unsigned char *e,unsigned char *t,unsigned char *te,
  851.            SIZEDTEXT *txt)
  852. {
  853.   unsigned char *q;
  854.   txt->data = NIL;        /* initially no returned data */
  855.   switch (*e) {            /* dispatch based upon encoding */
  856.   case 'Q': case 'q':        /* sort-of QUOTED-PRINTABLE */
  857.     txt->data = (unsigned char *) fs_get ((size_t) (te - t) + 1);
  858.     for (q = t,txt->size = 0; q < te; q++) switch (*q) {
  859.     case '=':            /* quoted character */
  860.                 /* both must be hex */
  861.       if (!isxdigit (q[1]) || !isxdigit (q[2])) {
  862.     fs_give ((void **) &txt->data);
  863.     return NIL;        /* syntax error: bad quoted character */
  864.       }
  865.       txt->data[txt->size++] =    /* assemble character */
  866.     ((q[1] - (isdigit (q[1]) ? '0' :
  867.           ((isupper (q[1]) ? 'A' : 'a') - 10))) << 4) +
  868.             (q[2] - (isdigit (q[2]) ? '0' :
  869.                  ((isupper (q[2]) ? 'A' : 'a') - 10)));
  870.       q += 2;            /* advance past quoted character */
  871.       break;
  872.     case '_':            /* convert to space */
  873.       txt->data[txt->size++] = ' ';
  874.       break;
  875.     default:            /* ordinary character */
  876.       txt->data[txt->size++] = *q;
  877.       break;
  878.     }
  879.     txt->data[txt->size] = '\0';
  880.     break;
  881.   case 'B': case 'b':        /* BASE64 */
  882.     if (txt->data = (unsigned char *) rfc822_base64 (t,te - t,&txt->size))
  883.       break;
  884.   default:            /* any other encoding is unknown */
  885.     return NIL;            /* syntax error: unknown encoding */
  886.   }
  887.   return T;
  888. }
  889.  
  890. /* Get MIME-2 token from encoded word
  891.  * Accepts: current text pointer
  892.  *        text limit pointer
  893.  *        pointer to returned end pointer
  894.  * Returns: current text pointer & end pointer if success, else NIL
  895.  */
  896.  
  897. unsigned char *mime2_token (unsigned char *s,unsigned char *se,
  898.                 unsigned char **t)
  899. {
  900.   for (*t = s; **t != '?'; ++*t) {
  901.     if ((*t < se) && isgraph (**t)) switch (**t) {
  902.     case '(': case ')': case '<': case '>': case '@': case ',': case ';':
  903.     case ':': case '\\': case '"': case '/': case '[': case ']': case '.':
  904.     case '=':
  905.       return NIL;        /* none of these are valid in tokens */
  906.     }
  907.     else return NIL;        /* out of text or CTL or space */
  908.   }
  909.   return s;
  910. }
  911.  
  912.  
  913. /* Get MIME-2 text from encoded word
  914.  * Accepts: current text pointer
  915.  *        text limit pointer
  916.  *        pointer to returned end pointer
  917.  * Returns: current text pointer & end pointer if success, else NIL
  918.  */
  919.  
  920. unsigned char *mime2_text (unsigned char *s,unsigned char *se,
  921.                unsigned char **t)
  922. {
  923.                 /* make sure valid, search for closing ? */
  924.   for (*t = s; **t != '?'; ++*t) if ((*t >= se) || !isgraph (**t)) return NIL;
  925.                 /* make sure terminated properly */
  926.   if ((*t)[1] != '=') return NIL;
  927.   return s;
  928. }
  929.