utf8.c
上传用户:ycwykj01
上传日期:2007-01-04
资源大小:1819k
文件大小:33k
源码类别:

网络编程

开发平台:

Unix_Linux

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