talons

Fork of Claws Mail https://www.claws-mail
Log | Files | Refs | README | LICENSE

codeconv.c (47282B)


      1 /*
      2  * Claws Mail -- a GTK based, lightweight, and fast e-mail client
      3  * Copyright (C) 1999-2012 Hiroyuki Yamamoto and the Claws Mail team
      4  *
      5  * This program is free software; you can redistribute it and/or modify
      6  * it under the terms of the GNU General Public License as published by
      7  * the Free Software Foundation; either version 3 of the License, or
      8  * (at your option) any later version.
      9  *
     10  * This program is distributed in the hope that it will be useful,
     11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13  * GNU General Public License for more details.
     14  *
     15  * You should have received a copy of the GNU General Public License
     16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
     17  *
     18  */
     19 
     20 #include "defs.h"
     21 
     22 #include <glib.h>
     23 #include <glib/gi18n.h>
     24 #include <string.h>
     25 #include <ctype.h>
     26 #include <stdlib.h>
     27 #include <errno.h>
     28 #include <locale.h>
     29 
     30 #include "codeconv.h"
     31 #include "unmime.h"
     32 #include "quoted-printable.h"
     33 #include "utils.h"
     34 
     35 /* For unknown reasons the inconv.m4 macro undefs that macro if no
     36    const is needed.  This would break the code below so we define it. */
     37 #ifndef ICONV_CONST
     38 #define ICONV_CONST
     39 #endif
     40 
     41 typedef enum
     42 {
     43 	JIS_ASCII,
     44 	JIS_KANJI,
     45 	JIS_HWKANA,
     46 	JIS_AUXKANJI
     47 } JISState;
     48 
     49 #define SUBST_CHAR	0x5f;
     50 #define ESC		'\033'
     51 
     52 #define iseuckanji(c) \
     53 	(((c) & 0xff) >= 0xa1 && ((c) & 0xff) <= 0xfe)
     54 #define iseuchwkana1(c) \
     55 	(((c) & 0xff) == 0x8e)
     56 #define iseuchwkana2(c) \
     57 	(((c) & 0xff) >= 0xa1 && ((c) & 0xff) <= 0xdf)
     58 #define iseucaux(c) \
     59 	(((c) & 0xff) == 0x8f)
     60 #define issjiskanji1(c) \
     61 	((((c) & 0xff) >= 0x81 && ((c) & 0xff) <= 0x9f) || \
     62 	 (((c) & 0xff) >= 0xe0 && ((c) & 0xff) <= 0xfc))
     63 #define issjiskanji2(c) \
     64 	((((c) & 0xff) >= 0x40 && ((c) & 0xff) <= 0x7e) || \
     65 	 (((c) & 0xff) >= 0x80 && ((c) & 0xff) <= 0xfc))
     66 #define issjishwkana(c) \
     67 	(((c) & 0xff) >= 0xa1 && ((c) & 0xff) <= 0xdf)
     68 
     69 #define K_IN()				\
     70 	if (state != JIS_KANJI) {	\
     71 		*out++ = ESC;		\
     72 		*out++ = '$';		\
     73 		*out++ = 'B';		\
     74 		state = JIS_KANJI;	\
     75 	}
     76 
     77 #define K_OUT()				\
     78 	if (state != JIS_ASCII) {	\
     79 		*out++ = ESC;		\
     80 		*out++ = '(';		\
     81 		*out++ = 'B';		\
     82 		state = JIS_ASCII;	\
     83 	}
     84 
     85 #define HW_IN()				\
     86 	if (state != JIS_HWKANA) {	\
     87 		*out++ = ESC;		\
     88 		*out++ = '(';		\
     89 		*out++ = 'I';		\
     90 		state = JIS_HWKANA;	\
     91 	}
     92 
     93 #define AUX_IN()			\
     94 	if (state != JIS_AUXKANJI) {	\
     95 		*out++ = ESC;		\
     96 		*out++ = '$';		\
     97 		*out++ = '(';		\
     98 		*out++ = 'D';		\
     99 		state = JIS_AUXKANJI;	\
    100 	}
    101 
    102 static CodeConvFunc conv_get_code_conv_func	(const gchar	*src_charset_str,
    103 					 const gchar	*dest_charset_str);
    104 
    105 static gchar *conv_iconv_strdup_with_cd	(const gchar	*inbuf,
    106 					 iconv_t	 cd);
    107 
    108 static gchar *conv_iconv_strdup		(const gchar	*inbuf,
    109 					 const gchar	*src_code,
    110 					 const gchar	*dest_code);
    111 
    112 static CharSet conv_get_locale_charset			(void);
    113 static CharSet conv_get_outgoing_charset		(void);
    114 static CharSet conv_guess_ja_encoding(const gchar *str);
    115 static gboolean conv_is_ja_locale			(void);
    116 
    117 static gint conv_jistoeuc(gchar *outbuf, gint outlen, const gchar *inbuf);
    118 static gint conv_euctojis(gchar *outbuf, gint outlen, const gchar *inbuf);
    119 static gint conv_sjistoeuc(gchar *outbuf, gint outlen, const gchar *inbuf);
    120 
    121 static gint conv_jistoutf8(gchar *outbuf, gint outlen, const gchar *inbuf);
    122 static gint conv_sjistoutf8(gchar *outbuf, gint outlen, const gchar *inbuf);
    123 static gint conv_euctoutf8(gchar *outbuf, gint outlen, const gchar *inbuf);
    124 static gint conv_anytoutf8(gchar *outbuf, gint outlen, const gchar *inbuf);
    125 
    126 static gint conv_utf8toeuc(gchar *outbuf, gint outlen, const gchar *inbuf);
    127 static gint conv_utf8tojis(gchar *outbuf, gint outlen, const gchar *inbuf);
    128 
    129 static void conv_unreadable_8bit(gchar *str);
    130 
    131 static gint conv_jistodisp(gchar *outbuf, gint outlen, const gchar *inbuf);
    132 static gint conv_sjistodisp(gchar *outbuf, gint outlen, const gchar *inbuf);
    133 static gint conv_euctodisp(gchar *outbuf, gint outlen, const gchar *inbuf);
    134 
    135 static gint conv_anytodisp(gchar *outbuf, gint outlen, const gchar *inbuf);
    136 static gint conv_ustodisp(gchar *outbuf, gint outlen, const gchar *inbuf);
    137 static gint conv_noconv(gchar *outbuf, gint outlen, const gchar *inbuf);
    138 
    139 static gboolean codeconv_strict_mode = FALSE;
    140 static gboolean codeconv_allow_jisx0201_kana = FALSE;
    141 static gboolean codeconv_broken_are_utf8 = FALSE;
    142 
    143 void codeconv_set_strict(gboolean mode)
    144 {
    145 	codeconv_strict_mode = mode;
    146 }
    147 
    148 void codeconv_set_allow_jisx0201_kana(gboolean allow)
    149 {
    150 	codeconv_allow_jisx0201_kana = allow;
    151 }
    152 
    153 void codeconv_set_broken_are_utf8(gboolean are)
    154 {
    155 	codeconv_broken_are_utf8 = are;
    156 }
    157 
    158 static gint conv_jistoeuc(gchar *outbuf, gint outlen, const gchar *inbuf)
    159 {
    160 	const guchar *in = inbuf;
    161 	gchar *out = outbuf;
    162 	JISState state = JIS_ASCII;
    163 
    164 	cm_return_val_if_fail(outbuf != NULL, 0);
    165 
    166 	/*
    167 	 * Loop outputs up to 3 bytes in each pass (aux kanji) and we
    168 	 * need 1 byte to terminate the output
    169 	 */
    170 	while (*in != '\0' && (out - outbuf) < outlen - 4) {
    171 		if (*in == ESC) {
    172 			in++;
    173 			if (*in == '$') {
    174 				if (*(in + 1) == '@' || *(in + 1) == 'B') {
    175 					state = JIS_KANJI;
    176 					in += 2;
    177 				} else if (*(in + 1) == '(' &&
    178 					   *(in + 2) == 'D') {
    179 					state = JIS_AUXKANJI;
    180 					in += 3;
    181 				} else {
    182 					/* unknown escape sequence */
    183 					state = JIS_ASCII;
    184 				}
    185 			} else if (*in == '(') {
    186 				if (*(in + 1) == 'B' || *(in + 1) == 'J') {
    187 					state = JIS_ASCII;
    188 					in += 2;
    189 				} else if (*(in + 1) == 'I') {
    190 					state = JIS_HWKANA;
    191 					in += 2;
    192 				} else {
    193 					/* unknown escape sequence */
    194 					state = JIS_ASCII;
    195 				}
    196 			} else {
    197 				/* unknown escape sequence */
    198 				state = JIS_ASCII;
    199 			}
    200 		} else if (*in == 0x0e) {
    201 			state = JIS_HWKANA;
    202 			in++;
    203 		} else if (*in == 0x0f) {
    204 			state = JIS_ASCII;
    205 			in++;
    206 		} else {
    207 			switch (state) {
    208 			case JIS_ASCII:
    209 				*out++ = *in++;
    210 				break;
    211 			case JIS_KANJI:
    212 				*out++ = *in++ | 0x80;
    213 				if (*in == '\0') break;
    214 				*out++ = *in++ | 0x80;
    215 				break;
    216 			case JIS_HWKANA:
    217 				*out++ = 0x8e;
    218 				*out++ = *in++ | 0x80;
    219 				break;
    220 			case JIS_AUXKANJI:
    221 				*out++ = 0x8f;
    222 				*out++ = *in++ | 0x80;
    223 				if (*in == '\0') break;
    224 				*out++ = *in++ | 0x80;
    225 				break;
    226 			}
    227 		}
    228 	}
    229 
    230 	*out = '\0';
    231 	return 0;
    232 }
    233 
    234 #define JIS_HWDAKUTEN		0x5e
    235 #define JIS_HWHANDAKUTEN	0x5f
    236 
    237 static gint conv_jis_hantozen(guchar *outbuf, guchar jis_code, guchar sound_sym)
    238 {
    239 	static guint16 h2z_tbl[] = {
    240 		/* 0x20 - 0x2f */
    241 		0x0000, 0x2123, 0x2156, 0x2157, 0x2122, 0x2126, 0x2572, 0x2521,
    242 		0x2523, 0x2525, 0x2527, 0x2529, 0x2563, 0x2565, 0x2567, 0x2543,
    243 		/* 0x30 - 0x3f */
    244 		0x213c, 0x2522, 0x2524, 0x2526, 0x2528, 0x252a, 0x252b, 0x252d,
    245 		0x252f, 0x2531, 0x2533, 0x2535, 0x2537, 0x2539, 0x253b, 0x253d,
    246 		/* 0x40 - 0x4f */
    247 		0x253f, 0x2541, 0x2544, 0x2546, 0x2548, 0x254a, 0x254b, 0x254c,
    248 		0x254d, 0x254e, 0x254f, 0x2552, 0x2555, 0x2558, 0x255b, 0x255e,
    249 		/* 0x50 - 0x5f */
    250 		0x255f, 0x2560, 0x2561, 0x2562, 0x2564, 0x2566, 0x2568, 0x2569,
    251 		0x256a, 0x256b, 0x256c, 0x256d, 0x256f, 0x2573, 0x212b, 0x212c
    252 	};
    253 
    254 	static guint16 dakuten_tbl[] = {
    255 		/* 0x30 - 0x3f */
    256 		0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x252c, 0x252e,
    257 		0x2530, 0x2532, 0x2534, 0x2536, 0x2538, 0x253a, 0x253c, 0x253e,
    258 		/* 0x40 - 0x4f */
    259 		0x2540, 0x2542, 0x2545, 0x2547, 0x2549, 0x0000, 0x0000, 0x0000,
    260 		0x0000, 0x0000, 0x2550, 0x2553, 0x2556, 0x2559, 0x255c, 0x0000
    261 	};
    262 
    263 	static guint16 handakuten_tbl[] = {
    264 		/* 0x4a - 0x4e */
    265 		0x2551, 0x2554, 0x2557, 0x255a, 0x255d
    266 	};
    267 
    268 	guint16 out_code;
    269 
    270 	cm_return_val_if_fail(outbuf != NULL, 0);
    271 
    272 	jis_code &= 0x7f;
    273 	sound_sym &= 0x7f;
    274 
    275 	if (jis_code < 0x21 || jis_code > 0x5f)
    276 		return 0;
    277 
    278 	if (sound_sym == JIS_HWDAKUTEN &&
    279 	    jis_code >= 0x36 && jis_code <= 0x4e) {
    280 		out_code = dakuten_tbl[jis_code - 0x30];
    281 		if (out_code != 0) {
    282 			*outbuf = out_code >> 8;
    283 			*(outbuf + 1) = out_code & 0xff;
    284 			return 2;
    285 		}
    286 	}
    287 
    288 	if (sound_sym == JIS_HWHANDAKUTEN &&
    289 	    jis_code >= 0x4a && jis_code <= 0x4e) {
    290 		out_code = handakuten_tbl[jis_code - 0x4a];
    291 		*outbuf = out_code >> 8;
    292 		*(outbuf + 1) = out_code & 0xff;
    293 		return 2;
    294 	}
    295 
    296 	out_code = h2z_tbl[jis_code - 0x20];
    297 	*outbuf = out_code >> 8;
    298 	*(outbuf + 1) = out_code & 0xff;
    299 	return 1;
    300 }
    301 
    302 static gint conv_euctojis(gchar *outbuf, gint outlen, const gchar *inbuf)
    303 {
    304 	const guchar *in = inbuf;
    305 	gchar *out = outbuf;
    306 	JISState state = JIS_ASCII;
    307 
    308 	cm_return_val_if_fail(outbuf != NULL, 0);
    309 
    310 	/*
    311 	 * Loop outputs up to 6 bytes in each pass (aux shift + aux
    312 	 * kanji) and we need up to 4 bytes to terminate the output
    313 	 * (ASCII shift + null)
    314 	 */
    315 	while (*in != '\0' && (out - outbuf) < outlen - 10) {
    316 		if (IS_ASCII(*in)) {
    317 			K_OUT();
    318 			*out++ = *in++;
    319 		} else if (iseuckanji(*in)) {
    320 			if (iseuckanji(*(in + 1))) {
    321 				K_IN();
    322 				*out++ = *in++ & 0x7f;
    323 				*out++ = *in++ & 0x7f;
    324 			} else {
    325 				K_OUT();
    326 				*out++ = SUBST_CHAR;
    327 				in++;
    328 				if (*in != '\0' && !IS_ASCII(*in)) {
    329 					*out++ = SUBST_CHAR;
    330 					in++;
    331 				}
    332 			}
    333 		} else if (iseuchwkana1(*in)) {
    334 			if (iseuchwkana2(*(in + 1))) {
    335 				if (codeconv_allow_jisx0201_kana) {
    336 					HW_IN();
    337 					in++;
    338 					*out++ = *in++ & 0x7f;
    339 				} else {
    340 					guchar jis_ch[2];
    341 					gint len;
    342 
    343 					if (iseuchwkana1(*(in + 2)) &&
    344 					    iseuchwkana2(*(in + 3)))
    345 						len = conv_jis_hantozen
    346 							(jis_ch,
    347 							 *(in + 1), *(in + 3));
    348 					else
    349 						len = conv_jis_hantozen
    350 							(jis_ch,
    351 							 *(in + 1), '\0');
    352 					if (len == 0)
    353 						in += 2;
    354 					else {
    355 						K_IN();
    356 						in += len * 2;
    357 						*out++ = jis_ch[0];
    358 						*out++ = jis_ch[1];
    359 					}
    360 				}
    361 			} else {
    362 				K_OUT();
    363 				in++;
    364 				if (*in != '\0' && !IS_ASCII(*in)) {
    365 					*out++ = SUBST_CHAR;
    366 					in++;
    367 				}
    368 			}
    369 		} else if (iseucaux(*in)) {
    370 			in++;
    371 			if (iseuckanji(*in) && iseuckanji(*(in + 1))) {
    372 				AUX_IN();
    373 				*out++ = *in++ & 0x7f;
    374 				*out++ = *in++ & 0x7f;
    375 			} else {
    376 				K_OUT();
    377 				if (*in != '\0' && !IS_ASCII(*in)) {
    378 					*out++ = SUBST_CHAR;
    379 					in++;
    380 					if (*in != '\0' && !IS_ASCII(*in)) {
    381 						*out++ = SUBST_CHAR;
    382 						in++;
    383 					}
    384 				}
    385 			}
    386 		} else {
    387 			K_OUT();
    388 			*out++ = SUBST_CHAR;
    389 			in++;
    390 		}
    391 	}
    392 
    393 	K_OUT();
    394 	*out = '\0';
    395 	return 0;
    396 }
    397 
    398 static gint conv_sjistoeuc(gchar *outbuf, gint outlen, const gchar *inbuf)
    399 {
    400 	const guchar *in = inbuf;
    401 	gchar *out = outbuf;
    402 
    403 	cm_return_val_if_fail(outbuf != NULL, 0);
    404 
    405 	/*
    406 	 * Loop outputs up to 2 bytes in each pass and we need 1 byte
    407 	 * to terminate the output
    408 	 */
    409 	while (*in != '\0' && (out - outbuf) < outlen - 3) {
    410 		if (IS_ASCII(*in)) {
    411 			*out++ = *in++;
    412 		} else if (issjiskanji1(*in)) {
    413 			if (issjiskanji2(*(in + 1))) {
    414 				guchar out1 = *in;
    415 				guchar out2 = *(in + 1);
    416 				guchar row;
    417 
    418 				row = out1 < 0xa0 ? 0x70 : 0xb0;
    419 				if (out2 < 0x9f) {
    420 					out1 = (out1 - row) * 2 - 1;
    421 					out2 -= out2 > 0x7f ? 0x20 : 0x1f;
    422 				} else {
    423 					out1 = (out1 - row) * 2;
    424 					out2 -= 0x7e;
    425 				}
    426 
    427 				*out++ = out1 | 0x80;
    428 				*out++ = out2 | 0x80;
    429 				in += 2;
    430 			} else {
    431 				*out++ = SUBST_CHAR;
    432 				in++;
    433 				if (*in != '\0' && !IS_ASCII(*in)) {
    434 					*out++ = SUBST_CHAR;
    435 					in++;
    436 				}
    437 			}
    438 		} else if (issjishwkana(*in)) {
    439 			*out++ = 0x8e;
    440 			*out++ = *in++;
    441 		} else {
    442 			*out++ = SUBST_CHAR;
    443 			in++;
    444 		}
    445 	}
    446 
    447 	*out = '\0';
    448 	return 0;
    449 }
    450 
    451 static gint conv_jistoutf8(gchar *outbuf, gint outlen, const gchar *inbuf)
    452 {
    453 	gchar *eucstr;
    454 
    455 	cm_return_val_if_fail(inbuf != NULL, 0);
    456 	cm_return_val_if_fail(outbuf != NULL, 0);
    457 
    458 	Xalloca(eucstr, outlen, return -1);
    459 
    460 	if (conv_jistoeuc(eucstr, outlen, inbuf) <0)
    461 		return -1;
    462 	if (conv_euctoutf8(outbuf, outlen, eucstr) < 0)
    463 		return -1;
    464 	return 0;
    465 }
    466 
    467 static gint conv_sjistoutf8(gchar *outbuf, gint outlen, const gchar *inbuf)
    468 {
    469 	gchar *tmpstr;
    470 
    471 	cm_return_val_if_fail(inbuf != NULL, 0);
    472 	cm_return_val_if_fail(outbuf != NULL, 0);
    473 
    474 	tmpstr = conv_iconv_strdup(inbuf, CS_SHIFT_JIS, CS_UTF_8);
    475 	if (tmpstr) {
    476 		strncpy2(outbuf, tmpstr, outlen);
    477 		g_free(tmpstr);
    478 		return 0;
    479 	} else {
    480 		strncpy2(outbuf, inbuf, outlen);
    481 		return -1;
    482 	}
    483 }
    484 
    485 static gint conv_euctoutf8(gchar *outbuf, gint outlen, const gchar *inbuf)
    486 {
    487 	static iconv_t cd = (iconv_t)-1;
    488 	static gboolean iconv_ok = TRUE;
    489 	gchar *tmpstr;
    490 
    491 	cm_return_val_if_fail(inbuf != NULL, 0);
    492 	cm_return_val_if_fail(outbuf != NULL, 0);
    493 
    494 	if (cd == (iconv_t)-1) {
    495 		if (!iconv_ok) {
    496 			strncpy2(outbuf, inbuf, outlen);
    497 			return -1;
    498 		}
    499 		cd = iconv_open(CS_UTF_8, CS_EUC_JP_MS);
    500 		if (cd == (iconv_t)-1) {
    501 			cd = iconv_open(CS_UTF_8, CS_EUC_JP);
    502 			if (cd == (iconv_t)-1) {
    503 				g_warning("conv_euctoutf8(): %s",
    504 					  g_strerror(errno));
    505 				iconv_ok = FALSE;
    506 				strncpy2(outbuf, inbuf, outlen);
    507 				return -1;
    508 			}
    509 		}
    510 	}
    511 
    512 	tmpstr = conv_iconv_strdup_with_cd(inbuf, cd);
    513 	if (tmpstr) {
    514 		strncpy2(outbuf, tmpstr, outlen);
    515 		g_free(tmpstr);
    516 		return 0;
    517 	} else {
    518 		strncpy2(outbuf, inbuf, outlen);
    519 		return -1;
    520 	}
    521 }
    522 
    523 static gint conv_anytoutf8(gchar *outbuf, gint outlen, const gchar *inbuf)
    524 {
    525 	gint r = -1;
    526 
    527 	cm_return_val_if_fail(inbuf != NULL, 0);
    528 	cm_return_val_if_fail(outbuf != NULL, 0);
    529 
    530 	switch (conv_guess_ja_encoding(inbuf)) {
    531 	case C_ISO_2022_JP:
    532 		r = conv_jistoutf8(outbuf, outlen, inbuf);
    533 		break;
    534 	case C_SHIFT_JIS:
    535 		r = conv_sjistoutf8(outbuf, outlen, inbuf);
    536 		break;
    537 	case C_EUC_JP:
    538 		r = conv_euctoutf8(outbuf, outlen, inbuf);
    539 		break;
    540 	default:
    541 		r = 0;
    542 		strncpy2(outbuf, inbuf, outlen);
    543 		break;
    544 	}
    545 
    546 	return r;
    547 }
    548 
    549 static gint conv_utf8toeuc(gchar *outbuf, gint outlen, const gchar *inbuf)
    550 {
    551 	static iconv_t cd = (iconv_t)-1;
    552 	static gboolean iconv_ok = TRUE;
    553 	gchar *tmpstr;
    554 
    555 	cm_return_val_if_fail(inbuf != NULL, 0);
    556 	cm_return_val_if_fail(outbuf != NULL, 0);
    557 
    558 	if (cd == (iconv_t)-1) {
    559 		if (!iconv_ok) {
    560 			strncpy2(outbuf, inbuf, outlen);
    561 			return -1;
    562 		}
    563 		cd = iconv_open(CS_EUC_JP_MS, CS_UTF_8);
    564 		if (cd == (iconv_t)-1) {
    565 			cd = iconv_open(CS_EUC_JP, CS_UTF_8);
    566 			if (cd == (iconv_t)-1) {
    567 				g_warning("conv_utf8toeuc(): %s",
    568 					  g_strerror(errno));
    569 				iconv_ok = FALSE;
    570 				strncpy2(outbuf, inbuf, outlen);
    571 				return -1;
    572 			}
    573 		}
    574 	}
    575 
    576 	tmpstr = conv_iconv_strdup_with_cd(inbuf, cd);
    577 	if (tmpstr) {
    578 		strncpy2(outbuf, tmpstr, outlen);
    579 		g_free(tmpstr);
    580 		return 0;
    581 	} else {
    582 		strncpy2(outbuf, inbuf, outlen);
    583 		return -1;
    584 	}
    585 }
    586 
    587 static gint conv_utf8tojis(gchar *outbuf, gint outlen, const gchar *inbuf)
    588 {
    589 	gchar *eucstr;
    590 
    591 	cm_return_val_if_fail(inbuf != NULL, 0);
    592 	cm_return_val_if_fail(outbuf != NULL, 0);
    593 
    594 	Xalloca(eucstr, outlen, return -1);
    595 
    596 	if (conv_utf8toeuc(eucstr, outlen, inbuf) < 0)
    597 		return -1;
    598 	if (conv_euctojis(outbuf, outlen, eucstr) < 0)
    599 		return -1;
    600 
    601 	return 0;
    602 }
    603 
    604 static void conv_unreadable_8bit(gchar *str)
    605 {
    606 	register guchar *p = str;
    607 
    608 	while (*p != '\0') {
    609 		/* convert CR+LF -> LF */
    610 		if (*p == '\r' && *(p + 1) == '\n')
    611 			memmove(p, p + 1, strlen(p));
    612 		else if (!IS_ASCII(*p)) *p = SUBST_CHAR;
    613 		p++;
    614 	}
    615 }
    616 
    617 static CharSet conv_guess_ja_encoding(const gchar *str)
    618 {
    619 	const guchar *p = str;
    620 	CharSet guessed = C_US_ASCII;
    621 
    622 	while (*p != '\0') {
    623 		if (*p == ESC && (*(p + 1) == '$' || *(p + 1) == '(')) {
    624 			if (guessed == C_US_ASCII)
    625 				return C_ISO_2022_JP;
    626 			p += 2;
    627 		} else if (IS_ASCII(*p)) {
    628 			p++;
    629 		} else if (iseuckanji(*p) && iseuckanji(*(p + 1))) {
    630 			if (*p >= 0xfd && *p <= 0xfe)
    631 				return C_EUC_JP;
    632 			else if (guessed == C_SHIFT_JIS) {
    633 				if ((issjiskanji1(*p) &&
    634 				     issjiskanji2(*(p + 1))) ||
    635 				    issjishwkana(*p))
    636 					guessed = C_SHIFT_JIS;
    637 				else
    638 					guessed = C_EUC_JP;
    639 			} else
    640 				guessed = C_EUC_JP;
    641 			p += 2;
    642 		} else if (issjiskanji1(*p) && issjiskanji2(*(p + 1))) {
    643 			if (iseuchwkana1(*p) && iseuchwkana2(*(p + 1)))
    644 				guessed = C_SHIFT_JIS;
    645 			else
    646 				return C_SHIFT_JIS;
    647 			p += 2;
    648 		} else if (issjishwkana(*p)) {
    649 			guessed = C_SHIFT_JIS;
    650 			p++;
    651 		} else {
    652 			p++;
    653 		}
    654 	}
    655 
    656 	return guessed;
    657 }
    658 
    659 static gint conv_jistodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
    660 {
    661 	cm_return_val_if_fail(inbuf != NULL, 0);
    662 	cm_return_val_if_fail(outbuf != NULL, 0);
    663 
    664 	return conv_jistoutf8(outbuf, outlen, inbuf);
    665 }
    666 
    667 static gint conv_sjistodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
    668 {
    669 	cm_return_val_if_fail(inbuf != NULL, 0);
    670 	cm_return_val_if_fail(outbuf != NULL, 0);
    671 
    672 	return conv_sjistoutf8(outbuf, outlen, inbuf);
    673 }
    674 
    675 static gint conv_euctodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
    676 {
    677 	cm_return_val_if_fail(inbuf != NULL, 0);
    678 	cm_return_val_if_fail(outbuf != NULL, 0);
    679 
    680 	return conv_euctoutf8(outbuf, outlen, inbuf);
    681 }
    682 
    683 void conv_utf8todisp(gchar *outbuf, gint outlen, const gchar *inbuf)
    684 {
    685 	cm_return_if_fail(inbuf != NULL);
    686 	cm_return_if_fail(outbuf != NULL);
    687 
    688 	if (g_utf8_validate(inbuf, -1, NULL) == TRUE)
    689 		strncpy2(outbuf, inbuf, outlen);
    690 	else
    691 		conv_ustodisp(outbuf, outlen, inbuf);
    692 }
    693 
    694 static gint conv_anytodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
    695 {
    696 	gint r = 0;
    697 
    698 	cm_return_val_if_fail(inbuf != NULL, 0);
    699 	cm_return_val_if_fail(outbuf != NULL, 0);
    700 
    701 	if (conv_anytoutf8(outbuf, outlen, inbuf) < 0)
    702 		r = -1;
    703 	if (g_utf8_validate(outbuf, -1, NULL) != TRUE)
    704 		conv_unreadable_8bit(outbuf);
    705 	return r;
    706 }
    707 
    708 static gint conv_ustodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
    709 {
    710 	cm_return_val_if_fail(inbuf != NULL, 0);
    711 	cm_return_val_if_fail(outbuf != NULL, 0);
    712 
    713 	strncpy2(outbuf, inbuf, outlen);
    714 	conv_unreadable_8bit(outbuf);
    715 
    716 	return 0;
    717 }
    718 
    719 void conv_localetodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
    720 {
    721 	gchar *tmpstr;
    722 
    723 	cm_return_if_fail(inbuf != NULL);
    724 	cm_return_if_fail(outbuf != NULL);
    725 
    726 	codeconv_set_strict(TRUE);
    727 	tmpstr = conv_iconv_strdup(inbuf, conv_get_locale_charset_str(),
    728 				   CS_INTERNAL);
    729 	codeconv_set_strict(FALSE);
    730 	if (tmpstr && g_utf8_validate(tmpstr, -1, NULL)) {
    731 		strncpy2(outbuf, tmpstr, outlen);
    732 		g_free(tmpstr);
    733 		return;
    734 	} else if (tmpstr && !g_utf8_validate(tmpstr, -1, NULL)) {
    735 		g_free(tmpstr);
    736 		codeconv_set_strict(TRUE);
    737 		tmpstr = conv_iconv_strdup(inbuf,
    738 				conv_get_locale_charset_str_no_utf8(),
    739 				CS_INTERNAL);
    740 		codeconv_set_strict(FALSE);
    741 	}
    742 	if (tmpstr && g_utf8_validate(tmpstr, -1, NULL)) {
    743 		strncpy2(outbuf, tmpstr, outlen);
    744 		g_free(tmpstr);
    745 		return;
    746 	} else {
    747 		g_free(tmpstr);
    748 		conv_utf8todisp(outbuf, outlen, inbuf);
    749 	}
    750 }
    751 
    752 static gint conv_noconv(gchar *outbuf, gint outlen, const gchar *inbuf)
    753 {
    754 	cm_return_val_if_fail(inbuf != NULL, 0);
    755 	cm_return_val_if_fail(outbuf != NULL, 0);
    756 
    757 	strncpy2(outbuf, inbuf, outlen);
    758 	return 0;
    759 }
    760 
    761 static const gchar *
    762 conv_get_fallback_for_private_encoding(const gchar *encoding)
    763 {
    764 	if (encoding) {
    765 		if ((encoding[0] == 'X' || encoding[0] == 'x') &&
    766 		    encoding[1] == '-') {
    767 			if (!g_ascii_strcasecmp(encoding, CS_X_MACCYR))
    768 				return CS_MACCYR;
    769 			if (!g_ascii_strcasecmp(encoding, CS_X_GBK))
    770 				return CS_GBK;
    771 		}
    772 		else if(!g_ascii_strcasecmp(encoding, CS_ISO_8859_8_I)) {
    773 			/*
    774 			 * ISO-8859-8-I is a variant which fully
    775 			 * agrees with ISO-8859-8 on character
    776 			 * codings, and differs only in directionality
    777 			 * implications, which are ignored here
    778 			 * anyway; and is not recognized by iconv
    779 			 */
    780 			return CS_ISO_8859_8;
    781 		}
    782 	}
    783 
    784 	return encoding;
    785 }
    786 
    787 CodeConverter *conv_code_converter_new(const gchar *src_charset)
    788 {
    789 	CodeConverter *conv;
    790 
    791 	src_charset = conv_get_fallback_for_private_encoding(src_charset);
    792 
    793 	conv = g_new0(CodeConverter, 1);
    794 	conv->code_conv_func = conv_get_code_conv_func(src_charset, NULL);
    795 	conv->charset_str = g_strdup(src_charset);
    796 	conv->charset = conv_get_charset_from_str(src_charset);
    797 
    798 	return conv;
    799 }
    800 
    801 void conv_code_converter_destroy(CodeConverter *conv)
    802 {
    803 	g_free(conv->charset_str);
    804 	g_free(conv);
    805 }
    806 
    807 gint conv_convert(CodeConverter *conv, gchar *outbuf, gint outlen,
    808 		  const gchar *inbuf)
    809 {
    810 	cm_return_val_if_fail(inbuf != NULL, -1);
    811 	cm_return_val_if_fail(outbuf != NULL, -1);
    812 
    813 	if (conv->code_conv_func != conv_noconv)
    814 		return conv->code_conv_func(outbuf, outlen, inbuf);
    815 	else {
    816 		gchar *str;
    817 
    818 		str = conv_iconv_strdup(inbuf, conv->charset_str, NULL);
    819 		if (!str)
    820 			return -1;
    821 		else {
    822 			strncpy2(outbuf, str, outlen);
    823 			g_free(str);
    824 		}
    825 	}
    826 
    827 	return 0;
    828 }
    829 
    830 gchar *conv_codeset_strdup(const gchar *inbuf,
    831 			   const gchar *src_code, const gchar *dest_code)
    832 {
    833 	gchar *buf;
    834 	size_t len;
    835 	CodeConvFunc conv_func;
    836 
    837 	cm_return_val_if_fail(inbuf != NULL, NULL);
    838 
    839 	if (!g_strcmp0(src_code, dest_code)) {
    840 		CharSet dest_charset = conv_get_charset_from_str(dest_code);
    841 		if (codeconv_strict_mode && dest_charset == C_UTF_8) {
    842 			/* ensure valid UTF-8 if target is UTF-8 */
    843 			if (!g_utf8_validate(inbuf, -1, NULL)) {
    844 				return NULL;
    845 			}
    846 		}
    847 		/* otherwise, try for a lucky day */
    848 		return g_strdup(inbuf);
    849 	}
    850 
    851 	src_code = conv_get_fallback_for_private_encoding(src_code);
    852 	conv_func = conv_get_code_conv_func(src_code, dest_code);
    853 	if (conv_func == conv_ustodisp
    854 			&& codeconv_strict_mode
    855 			&& !is_ascii_str(inbuf))
    856 		return NULL;
    857 
    858 	if (conv_func != conv_noconv) {
    859 		len = (strlen(inbuf) + 1) * 3;
    860 		buf = g_malloc(len);
    861 
    862 		if (conv_func(buf, len, inbuf) == 0 || !codeconv_strict_mode)
    863 			return g_realloc(buf, strlen(buf) + 1);
    864 		else {
    865 			g_free(buf);
    866 			return NULL;
    867 		}
    868 	}
    869 
    870 	return conv_iconv_strdup(inbuf, src_code, dest_code);
    871 }
    872 
    873 static CodeConvFunc conv_get_code_conv_func(const gchar *src_charset_str,
    874 				     const gchar *dest_charset_str)
    875 {
    876 	CodeConvFunc code_conv = conv_noconv;
    877 	CharSet src_charset;
    878 	CharSet dest_charset;
    879 
    880 	if (!src_charset_str)
    881 		src_charset = conv_get_locale_charset();
    882 	else
    883 		src_charset = conv_get_charset_from_str(src_charset_str);
    884 
    885 	/* auto detection mode */
    886 	if (!src_charset_str && !dest_charset_str) {
    887 		if (conv_is_ja_locale())
    888 			return conv_anytodisp;
    889 		else
    890 			return conv_noconv;
    891 	}
    892 
    893 	dest_charset = conv_get_charset_from_str(dest_charset_str);
    894 
    895 	if (dest_charset == C_US_ASCII)
    896 		return conv_ustodisp;
    897 
    898 	switch (src_charset) {
    899 	case C_US_ASCII:
    900 	case C_ISO_8859_1:
    901 	case C_ISO_8859_2:
    902 	case C_ISO_8859_3:
    903 	case C_ISO_8859_4:
    904 	case C_ISO_8859_5:
    905 	case C_ISO_8859_6:
    906 	case C_ISO_8859_7:
    907 	case C_ISO_8859_8:
    908 	case C_ISO_8859_9:
    909 	case C_ISO_8859_10:
    910 	case C_ISO_8859_11:
    911 	case C_ISO_8859_13:
    912 	case C_ISO_8859_14:
    913 	case C_ISO_8859_15:
    914 		break;
    915 	case C_ISO_2022_JP:
    916 	case C_ISO_2022_JP_2:
    917 	case C_ISO_2022_JP_3:
    918 		if (dest_charset == C_AUTO)
    919 			code_conv = conv_jistodisp;
    920 		else if (dest_charset == C_EUC_JP)
    921 			code_conv = conv_jistoeuc;
    922 		else if (dest_charset == C_UTF_8)
    923 			code_conv = conv_jistoutf8;
    924 		break;
    925 	case C_SHIFT_JIS:
    926 		if (dest_charset == C_AUTO)
    927 			code_conv = conv_sjistodisp;
    928 		else if (dest_charset == C_EUC_JP)
    929 			code_conv = conv_sjistoeuc;
    930 		else if (dest_charset == C_UTF_8)
    931 			code_conv = conv_sjistoutf8;
    932 		break;
    933 	case C_EUC_JP:
    934 		if (dest_charset == C_AUTO)
    935 			code_conv = conv_euctodisp;
    936 		else if (dest_charset == C_ISO_2022_JP   ||
    937 			 dest_charset == C_ISO_2022_JP_2 ||
    938 			 dest_charset == C_ISO_2022_JP_3)
    939 			code_conv = conv_euctojis;
    940 		else if (dest_charset == C_UTF_8)
    941 			code_conv = conv_euctoutf8;
    942 		break;
    943 	case C_UTF_8:
    944 		if (dest_charset == C_EUC_JP)
    945 			code_conv = conv_utf8toeuc;
    946 		else if (dest_charset == C_ISO_2022_JP   ||
    947 			 dest_charset == C_ISO_2022_JP_2 ||
    948 			 dest_charset == C_ISO_2022_JP_3)
    949 			code_conv = conv_utf8tojis;
    950 		break;
    951 	default:
    952 		break;
    953 	}
    954 
    955 	return code_conv;
    956 }
    957 
    958 static gchar *conv_iconv_strdup(const gchar *inbuf,
    959 			 const gchar *src_code, const gchar *dest_code)
    960 {
    961 	iconv_t cd;
    962 	gchar *outbuf;
    963 
    964 	cm_return_val_if_fail(inbuf != NULL, NULL);
    965 
    966 	if (!src_code && !dest_code &&
    967 	    g_utf8_validate(inbuf, -1, NULL))
    968 	    	return g_strdup(inbuf);
    969 
    970 	if (!src_code)
    971 		src_code = conv_get_outgoing_charset_str();
    972 	if (!dest_code)
    973 		dest_code = CS_INTERNAL;
    974 
    975 	/* don't convert if src and dest codeset are identical */
    976 	if (!strcasecmp(src_code, dest_code))
    977 		return g_strdup(inbuf);
    978 
    979 	/* don't convert if dest codeset is US-ASCII */
    980 	if (!strcasecmp(src_code, CS_US_ASCII))
    981 		return g_strdup(inbuf);
    982 
    983 	/* don't convert if dest codeset is US-ASCII */
    984 	if (!strcasecmp(dest_code, CS_US_ASCII))
    985 		return g_strdup(inbuf);
    986 
    987 	cd = iconv_open(dest_code, src_code);
    988 	if (cd == (iconv_t)-1)
    989 		return NULL;
    990 
    991 	outbuf = conv_iconv_strdup_with_cd(inbuf, cd);
    992 
    993 	iconv_close(cd);
    994 
    995 	return outbuf;
    996 }
    997 
    998 gchar *conv_iconv_strdup_with_cd(const gchar *inbuf, iconv_t cd)
    999 {
   1000 	const gchar *inbuf_p;
   1001 	gchar *outbuf;
   1002 	gchar *outbuf_p;
   1003 	size_t in_size;
   1004 	size_t in_left;
   1005 	size_t out_size;
   1006 	size_t out_left;
   1007 	size_t n_conv;
   1008 	size_t len;
   1009 
   1010 	cm_return_val_if_fail(inbuf != NULL, NULL);
   1011 
   1012 	inbuf_p = inbuf;
   1013 	in_size = strlen(inbuf);
   1014 	in_left = in_size;
   1015 	out_size = (in_size + 1) * 2;
   1016 	outbuf = g_malloc(out_size);
   1017 	outbuf_p = outbuf;
   1018 	out_left = out_size;
   1019 
   1020 #define EXPAND_BUF()				\
   1021 {						\
   1022 	len = outbuf_p - outbuf;		\
   1023 	out_size *= 2;				\
   1024 	outbuf = g_realloc(outbuf, out_size);	\
   1025 	outbuf_p = outbuf + len;		\
   1026 	out_left = out_size - len;		\
   1027 }
   1028 
   1029 	while ((n_conv = iconv(cd, (ICONV_CONST gchar **)&inbuf_p, &in_left,
   1030 			       &outbuf_p, &out_left)) == (size_t)-1) {
   1031 		if (EILSEQ == errno) {
   1032 			if (codeconv_strict_mode) {
   1033 				g_free(outbuf);
   1034 				return NULL;
   1035 			}
   1036 			//g_print("iconv(): at %d: %s\n", in_size - in_left, g_strerror(errno));
   1037 			inbuf_p++;
   1038 			in_left--;
   1039 			if (out_left == 0) {
   1040 				EXPAND_BUF();
   1041 			}
   1042 			*outbuf_p++ = SUBST_CHAR;
   1043 			out_left--;
   1044 		} else if (EINVAL == errno) {
   1045 			break;
   1046 		} else if (E2BIG == errno) {
   1047 			EXPAND_BUF();
   1048 		} else {
   1049 			g_warning("conv_iconv_strdup(): %s",
   1050 				  g_strerror(errno));
   1051 			break;
   1052 		}
   1053 	}
   1054 
   1055 	while ((n_conv = iconv(cd, NULL, NULL, &outbuf_p, &out_left)) ==
   1056 	       (size_t)-1) {
   1057 		if (E2BIG == errno) {
   1058 			EXPAND_BUF();
   1059 		} else {
   1060 			g_warning("conv_iconv_strdup(): %s",
   1061 				  g_strerror(errno));
   1062 			break;
   1063 		}
   1064 	}
   1065 
   1066 #undef EXPAND_BUF
   1067 
   1068 	len = outbuf_p - outbuf;
   1069 	outbuf = g_realloc(outbuf, len + 1);
   1070 	outbuf[len] = '\0';
   1071 
   1072 	return outbuf;
   1073 }
   1074 
   1075 static const struct {
   1076 	CharSet charset;
   1077 	gchar *const name;
   1078 } charsets[] = {
   1079 	{C_US_ASCII,		CS_US_ASCII},
   1080 	{C_US_ASCII,		CS_ANSI_X3_4_1968},
   1081 	{C_UTF_8,		CS_UTF_8},
   1082 	{C_UTF_7,		CS_UTF_7},
   1083 	{C_ISO_8859_1,		CS_ISO_8859_1},
   1084 	{C_ISO_8859_2,		CS_ISO_8859_2},
   1085 	{C_ISO_8859_3,		CS_ISO_8859_3},
   1086 	{C_ISO_8859_4,		CS_ISO_8859_4},
   1087 	{C_ISO_8859_5,		CS_ISO_8859_5},
   1088 	{C_ISO_8859_6,		CS_ISO_8859_6},
   1089 	{C_ISO_8859_7,		CS_ISO_8859_7},
   1090 	{C_ISO_8859_8,		CS_ISO_8859_8},
   1091 	{C_ISO_8859_9,		CS_ISO_8859_9},
   1092 	{C_ISO_8859_10,		CS_ISO_8859_10},
   1093 	{C_ISO_8859_11,		CS_ISO_8859_11},
   1094 	{C_ISO_8859_13,		CS_ISO_8859_13},
   1095 	{C_ISO_8859_14,		CS_ISO_8859_14},
   1096 	{C_ISO_8859_15,		CS_ISO_8859_15},
   1097 	{C_BALTIC,		CS_BALTIC},
   1098 	{C_CP1250,		CS_CP1250},
   1099 	{C_CP1251,		CS_CP1251},
   1100 	{C_CP1252,		CS_CP1252},
   1101 	{C_CP1253,		CS_CP1253},
   1102 	{C_CP1254,		CS_CP1254},
   1103 	{C_CP1255,		CS_CP1255},
   1104 	{C_CP1256,		CS_CP1256},
   1105 	{C_CP1257,		CS_CP1257},
   1106 	{C_CP1258,		CS_CP1258},
   1107 	{C_WINDOWS_1250,	CS_WINDOWS_1250},
   1108 	{C_WINDOWS_1251,	CS_WINDOWS_1251},
   1109 	{C_WINDOWS_1252,	CS_WINDOWS_1252},
   1110 	{C_WINDOWS_1253,	CS_WINDOWS_1253},
   1111 	{C_WINDOWS_1254,	CS_WINDOWS_1254},
   1112 	{C_WINDOWS_1255,	CS_WINDOWS_1255},
   1113 	{C_WINDOWS_1256,	CS_WINDOWS_1256},
   1114 	{C_WINDOWS_1257,	CS_WINDOWS_1257},
   1115 	{C_WINDOWS_1258,	CS_WINDOWS_1258},
   1116 	{C_KOI8_R,		CS_KOI8_R},
   1117 	{C_MACCYR,		CS_MACCYR},
   1118 	{C_KOI8_T,		CS_KOI8_T},
   1119 	{C_KOI8_U,		CS_KOI8_U},
   1120 	{C_ISO_2022_JP,		CS_ISO_2022_JP},
   1121 	{C_ISO_2022_JP_2,	CS_ISO_2022_JP_2},
   1122 	{C_ISO_2022_JP_3,	CS_ISO_2022_JP_3},
   1123 	{C_EUC_JP,		CS_EUC_JP},
   1124 	{C_EUC_JP,		CS_EUCJP},
   1125 	{C_EUC_JP_MS,		CS_EUC_JP_MS},
   1126 	{C_SHIFT_JIS,		CS_SHIFT_JIS},
   1127 	{C_SHIFT_JIS,		CS_SHIFT__JIS},
   1128 	{C_SHIFT_JIS,		CS_SJIS},
   1129 	{C_ISO_2022_KR,		CS_ISO_2022_KR},
   1130 	{C_EUC_KR,		CS_EUC_KR},
   1131 	{C_ISO_2022_CN,		CS_ISO_2022_CN},
   1132 	{C_EUC_CN,		CS_EUC_CN},
   1133 	{C_GB18030,		CS_GB18030},
   1134 	{C_GB2312,		CS_GB2312},
   1135 	{C_GBK,			CS_GBK},
   1136 	{C_EUC_TW,		CS_EUC_TW},
   1137 	{C_BIG5,		CS_BIG5},
   1138 	{C_BIG5_HKSCS,		CS_BIG5_HKSCS},
   1139 	{C_TIS_620,		CS_TIS_620},
   1140 	{C_WINDOWS_874,		CS_WINDOWS_874},
   1141 	{C_GEORGIAN_PS,		CS_GEORGIAN_PS},
   1142 	{C_TCVN5712_1,		CS_TCVN5712_1},
   1143 };
   1144 
   1145 static const struct {
   1146 	gchar *const locale;
   1147 	CharSet charset;
   1148 	CharSet out_charset;
   1149 } locale_table[] = {
   1150 	{"ja_JP.eucJP"		, C_EUC_JP	, C_ISO_2022_JP},
   1151 	{"ja_JP.EUC-JP"		, C_EUC_JP	, C_ISO_2022_JP},
   1152 	{"ja_JP.EUC"		, C_EUC_JP	, C_ISO_2022_JP},
   1153 	{"ja_JP.ujis"		, C_EUC_JP	, C_ISO_2022_JP},
   1154 	{"ja_JP.SJIS"		, C_SHIFT_JIS	, C_ISO_2022_JP},
   1155 	{"ja_JP.JIS"		, C_ISO_2022_JP	, C_ISO_2022_JP},
   1156 	{"ja_JP"		, C_EUC_JP	, C_ISO_2022_JP},
   1157 	{"ko_KR.EUC-KR"		, C_EUC_KR	, C_EUC_KR},
   1158 	{"ko_KR"		, C_EUC_KR	, C_EUC_KR},
   1159 	{"zh_CN.GB18030"	, C_GB18030	, C_GB18030},
   1160 	{"zh_CN.GB2312"		, C_GB2312	, C_GB2312},
   1161 	{"zh_CN.GBK"		, C_GBK		, C_GBK},
   1162 	{"zh_CN"		, C_GB18030	, C_GB18030},
   1163 	{"zh_HK"		, C_BIG5_HKSCS	, C_BIG5_HKSCS},
   1164 	{"zh_TW.eucTW"		, C_EUC_TW	, C_BIG5},
   1165 	{"zh_TW.EUC-TW"		, C_EUC_TW	, C_BIG5},
   1166 	{"zh_TW.Big5"		, C_BIG5	, C_BIG5},
   1167 	{"zh_TW"		, C_BIG5	, C_BIG5},
   1168 
   1169 	{"ru_RU.KOI8-R"		, C_KOI8_R	, C_KOI8_R},
   1170 	{"ru_RU.KOI8R"		, C_KOI8_R	, C_KOI8_R},
   1171 	{"ru_RU.CP1251"		, C_WINDOWS_1251, C_KOI8_R},
   1172 	{"ru_RU"		, C_ISO_8859_5	, C_KOI8_R},
   1173 
   1174 	{"tg_TJ"		, C_KOI8_T	, C_KOI8_T},
   1175 	{"ru_UA"		, C_KOI8_U	, C_KOI8_U},
   1176 	{"uk_UA.CP1251"		, C_WINDOWS_1251, C_KOI8_U},
   1177 	{"uk_UA"		, C_KOI8_U	, C_KOI8_U},
   1178 
   1179 	{"be_BY"		, C_WINDOWS_1251, C_WINDOWS_1251},
   1180 	{"bg_BG"		, C_WINDOWS_1251, C_WINDOWS_1251},
   1181 
   1182 	{"yi_US"		, C_WINDOWS_1255, C_WINDOWS_1255},
   1183 
   1184 	{"af_ZA"		, C_ISO_8859_1  , C_ISO_8859_1},
   1185 	{"br_FR"		, C_ISO_8859_1	, C_ISO_8859_1},
   1186 	{"ca_ES"		, C_ISO_8859_1	, C_ISO_8859_1},
   1187 	{"da_DK"		, C_ISO_8859_1	, C_ISO_8859_1},
   1188 	{"de_AT"		, C_ISO_8859_1	, C_ISO_8859_1},
   1189 	{"de_BE"		, C_ISO_8859_1	, C_ISO_8859_1},
   1190 	{"de_CH"		, C_ISO_8859_1	, C_ISO_8859_1},
   1191 	{"de_DE"		, C_ISO_8859_1	, C_ISO_8859_1},
   1192 	{"de_LU"		, C_ISO_8859_1	, C_ISO_8859_1},
   1193 	{"en_AU"		, C_ISO_8859_1	, C_ISO_8859_1},
   1194 	{"en_BW"		, C_ISO_8859_1	, C_ISO_8859_1},
   1195 	{"en_CA"		, C_ISO_8859_1	, C_ISO_8859_1},
   1196 	{"en_DK"		, C_ISO_8859_1	, C_ISO_8859_1},
   1197 	{"en_GB"		, C_ISO_8859_1	, C_ISO_8859_1},
   1198 	{"en_HK"		, C_ISO_8859_1	, C_ISO_8859_1},
   1199 	{"en_IE"		, C_ISO_8859_1	, C_ISO_8859_1},
   1200 	{"en_NZ"		, C_ISO_8859_1	, C_ISO_8859_1},
   1201 	{"en_PH"		, C_ISO_8859_1	, C_ISO_8859_1},
   1202 	{"en_SG"		, C_ISO_8859_1	, C_ISO_8859_1},
   1203 	{"en_US"		, C_ISO_8859_1	, C_ISO_8859_1},
   1204 	{"en_ZA"		, C_ISO_8859_1	, C_ISO_8859_1},
   1205 	{"en_ZW"		, C_ISO_8859_1	, C_ISO_8859_1},
   1206 	{"es_AR"		, C_ISO_8859_1	, C_ISO_8859_1},
   1207 	{"es_BO"		, C_ISO_8859_1	, C_ISO_8859_1},
   1208 	{"es_CL"		, C_ISO_8859_1	, C_ISO_8859_1},
   1209 	{"es_CO"		, C_ISO_8859_1	, C_ISO_8859_1},
   1210 	{"es_CR"		, C_ISO_8859_1	, C_ISO_8859_1},
   1211 	{"es_DO"		, C_ISO_8859_1	, C_ISO_8859_1},
   1212 	{"es_EC"		, C_ISO_8859_1	, C_ISO_8859_1},
   1213 	{"es_ES"		, C_ISO_8859_1	, C_ISO_8859_1},
   1214 	{"es_GT"		, C_ISO_8859_1	, C_ISO_8859_1},
   1215 	{"es_HN"		, C_ISO_8859_1	, C_ISO_8859_1},
   1216 	{"es_MX"		, C_ISO_8859_1	, C_ISO_8859_1},
   1217 	{"es_NI"		, C_ISO_8859_1	, C_ISO_8859_1},
   1218 	{"es_PA"		, C_ISO_8859_1	, C_ISO_8859_1},
   1219 	{"es_PE"		, C_ISO_8859_1	, C_ISO_8859_1},
   1220 	{"es_PR"		, C_ISO_8859_1	, C_ISO_8859_1},
   1221 	{"es_PY"		, C_ISO_8859_1	, C_ISO_8859_1},
   1222 	{"es_SV"		, C_ISO_8859_1	, C_ISO_8859_1},
   1223 	{"es_US"		, C_ISO_8859_1	, C_ISO_8859_1},
   1224 	{"es_UY"		, C_ISO_8859_1	, C_ISO_8859_1},
   1225 	{"es_VE"		, C_ISO_8859_1	, C_ISO_8859_1},
   1226 	{"et_EE"		, C_ISO_8859_1	, C_ISO_8859_1},
   1227 	{"eu_ES"		, C_ISO_8859_1	, C_ISO_8859_1},
   1228 	{"fi_FI"		, C_ISO_8859_1	, C_ISO_8859_1},
   1229 	{"fo_FO"		, C_ISO_8859_1	, C_ISO_8859_1},
   1230 	{"fr_BE"		, C_ISO_8859_1	, C_ISO_8859_1},
   1231 	{"fr_CA"		, C_ISO_8859_1	, C_ISO_8859_1},
   1232 	{"fr_CH"		, C_ISO_8859_1	, C_ISO_8859_1},
   1233 	{"fr_FR"		, C_ISO_8859_1	, C_ISO_8859_1},
   1234 	{"fr_LU"		, C_ISO_8859_1	, C_ISO_8859_1},
   1235 	{"ga_IE"		, C_ISO_8859_1	, C_ISO_8859_1},
   1236 	{"gl_ES"		, C_ISO_8859_1	, C_ISO_8859_1},
   1237 	{"gv_GB"		, C_ISO_8859_1	, C_ISO_8859_1},
   1238 	{"id_ID"		, C_ISO_8859_1	, C_ISO_8859_1},
   1239 	{"is_IS"		, C_ISO_8859_1	, C_ISO_8859_1},
   1240 	{"it_CH"		, C_ISO_8859_1	, C_ISO_8859_1},
   1241 	{"it_IT"		, C_ISO_8859_1	, C_ISO_8859_1},
   1242 	{"kl_GL"		, C_ISO_8859_1	, C_ISO_8859_1},
   1243 	{"kw_GB"		, C_ISO_8859_1	, C_ISO_8859_1},
   1244 	{"ms_MY"		, C_ISO_8859_1	, C_ISO_8859_1},
   1245 	{"nl_BE"		, C_ISO_8859_1	, C_ISO_8859_1},
   1246 	{"nl_NL"		, C_ISO_8859_1	, C_ISO_8859_1},
   1247 	{"nb_NO"		, C_ISO_8859_1  , C_ISO_8859_1},
   1248 	{"nn_NO"		, C_ISO_8859_1	, C_ISO_8859_1},
   1249 	{"no_NO"		, C_ISO_8859_1	, C_ISO_8859_1},
   1250 	{"oc_FR"		, C_ISO_8859_1	, C_ISO_8859_1},
   1251 	{"pt_BR"		, C_ISO_8859_1	, C_ISO_8859_1},
   1252 	{"pt_PT"		, C_ISO_8859_1	, C_ISO_8859_1},
   1253 	{"sq_AL"		, C_ISO_8859_1	, C_ISO_8859_1},
   1254 	{"sv_FI"		, C_ISO_8859_1	, C_ISO_8859_1},
   1255 	{"sv_SE"		, C_ISO_8859_1	, C_ISO_8859_1},
   1256 	{"tl_PH"		, C_ISO_8859_1	, C_ISO_8859_1},
   1257 	{"uz_UZ"		, C_ISO_8859_1	, C_ISO_8859_1},
   1258 	{"wa_BE"		, C_ISO_8859_1	, C_ISO_8859_1},
   1259 
   1260 	{"bs_BA"		, C_ISO_8859_2	, C_ISO_8859_2},
   1261 	{"cs_CZ"		, C_ISO_8859_2	, C_ISO_8859_2},
   1262 	{"hr_HR"		, C_ISO_8859_2	, C_ISO_8859_2},
   1263 	{"hu_HU"		, C_ISO_8859_2	, C_ISO_8859_2},
   1264 	{"pl_PL"		, C_ISO_8859_2	, C_ISO_8859_2},
   1265 	{"ro_RO"		, C_ISO_8859_2	, C_ISO_8859_2},
   1266 	{"sk_SK"		, C_ISO_8859_2	, C_ISO_8859_2},
   1267 	{"sl_SI"		, C_ISO_8859_2	, C_ISO_8859_2},
   1268 
   1269 	{"sr_YU@cyrillic"	, C_ISO_8859_5	, C_ISO_8859_5},
   1270 	{"sr_YU"		, C_ISO_8859_2	, C_ISO_8859_2},
   1271 
   1272 	{"mt_MT"		, C_ISO_8859_3	, C_ISO_8859_3},
   1273 
   1274 	{"lt_LT.iso88594"	, C_ISO_8859_4	, C_ISO_8859_4},
   1275 	{"lt_LT.ISO8859-4"	, C_ISO_8859_4	, C_ISO_8859_4},
   1276 	{"lt_LT.ISO_8859-4"	, C_ISO_8859_4	, C_ISO_8859_4},
   1277 	{"lt_LT"		, C_ISO_8859_13	, C_ISO_8859_13},
   1278 
   1279 	{"mk_MK"		, C_ISO_8859_5	, C_ISO_8859_5},
   1280 
   1281 	{"ar_AE"		, C_ISO_8859_6	, C_ISO_8859_6},
   1282 	{"ar_BH"		, C_ISO_8859_6	, C_ISO_8859_6},
   1283 	{"ar_DZ"		, C_ISO_8859_6	, C_ISO_8859_6},
   1284 	{"ar_EG"		, C_ISO_8859_6	, C_ISO_8859_6},
   1285 	{"ar_IQ"		, C_ISO_8859_6	, C_ISO_8859_6},
   1286 	{"ar_JO"		, C_ISO_8859_6	, C_ISO_8859_6},
   1287 	{"ar_KW"		, C_ISO_8859_6	, C_ISO_8859_6},
   1288 	{"ar_LB"		, C_ISO_8859_6	, C_ISO_8859_6},
   1289 	{"ar_LY"		, C_ISO_8859_6	, C_ISO_8859_6},
   1290 	{"ar_MA"		, C_ISO_8859_6	, C_ISO_8859_6},
   1291 	{"ar_OM"		, C_ISO_8859_6	, C_ISO_8859_6},
   1292 	{"ar_QA"		, C_ISO_8859_6	, C_ISO_8859_6},
   1293 	{"ar_SA"		, C_ISO_8859_6	, C_ISO_8859_6},
   1294 	{"ar_SD"		, C_ISO_8859_6	, C_ISO_8859_6},
   1295 	{"ar_SY"		, C_ISO_8859_6	, C_ISO_8859_6},
   1296 	{"ar_TN"		, C_ISO_8859_6	, C_ISO_8859_6},
   1297 	{"ar_YE"		, C_ISO_8859_6	, C_ISO_8859_6},
   1298 
   1299 	{"el_GR"		, C_ISO_8859_7	, C_ISO_8859_7},
   1300 	{"he_IL"		, C_ISO_8859_8	, C_ISO_8859_8},
   1301 	{"iw_IL"		, C_ISO_8859_8	, C_ISO_8859_8},
   1302 	{"tr_TR"		, C_ISO_8859_9	, C_ISO_8859_9},
   1303 
   1304 	{"lv_LV"		, C_ISO_8859_13	, C_ISO_8859_13},
   1305 	{"mi_NZ"		, C_ISO_8859_13	, C_ISO_8859_13},
   1306 
   1307 	{"cy_GB"		, C_ISO_8859_14	, C_ISO_8859_14},
   1308 
   1309 	{"ar_IN"		, C_UTF_8	, C_UTF_8},
   1310 	{"en_IN"		, C_UTF_8	, C_UTF_8},
   1311 	{"se_NO"		, C_UTF_8	, C_UTF_8},
   1312 	{"ta_IN"		, C_UTF_8	, C_UTF_8},
   1313 	{"te_IN"		, C_UTF_8	, C_UTF_8},
   1314 	{"ur_PK"		, C_UTF_8	, C_UTF_8},
   1315 
   1316 	{"th_TH"		, C_TIS_620	, C_TIS_620},
   1317 	/* {"th_TH"		, C_WINDOWS_874}, */
   1318 	/* {"th_TH"		, C_ISO_8859_11}, */
   1319 
   1320 	{"ka_GE"		, C_GEORGIAN_PS	, C_GEORGIAN_PS},
   1321 	{"vi_VN.TCVN"		, C_TCVN5712_1	, C_TCVN5712_1},
   1322 
   1323 	{"C"			, C_US_ASCII	, C_US_ASCII},
   1324 	{"POSIX"		, C_US_ASCII	, C_US_ASCII},
   1325 	{"ANSI_X3.4-1968"	, C_US_ASCII	, C_US_ASCII},
   1326 };
   1327 
   1328 static GHashTable *conv_get_charset_to_str_table(void)
   1329 {
   1330 	static GHashTable *table;
   1331 	gint i;
   1332 
   1333 	if (table)
   1334 		return table;
   1335 
   1336 	table = g_hash_table_new(NULL, g_direct_equal);
   1337 
   1338 	for (i = 0; i < sizeof(charsets) / sizeof(charsets[0]); i++) {
   1339 		if (g_hash_table_lookup(table, GUINT_TO_POINTER(charsets[i].charset))
   1340 		    == NULL) {
   1341 			g_hash_table_insert
   1342 				(table, GUINT_TO_POINTER(charsets[i].charset),
   1343 				 charsets[i].name);
   1344 		}
   1345 	}
   1346 
   1347 	return table;
   1348 }
   1349 
   1350 static GHashTable *conv_get_charset_from_str_table(void)
   1351 {
   1352 	static GHashTable *table;
   1353 	gint i;
   1354 
   1355 	if (table)
   1356 		return table;
   1357 
   1358 	table = g_hash_table_new(str_case_hash, str_case_equal);
   1359 
   1360 	for (i = 0; i < sizeof(charsets) / sizeof(charsets[0]); i++) {
   1361 		g_hash_table_insert(table, charsets[i].name,
   1362 				    GUINT_TO_POINTER(charsets[i].charset));
   1363 	}
   1364 
   1365 	return table;
   1366 }
   1367 
   1368 const gchar *conv_get_charset_str(CharSet charset)
   1369 {
   1370 	GHashTable *table;
   1371 
   1372 	table = conv_get_charset_to_str_table();
   1373 	return g_hash_table_lookup(table, GUINT_TO_POINTER(charset));
   1374 }
   1375 
   1376 CharSet conv_get_charset_from_str(const gchar *charset)
   1377 {
   1378 	GHashTable *table;
   1379 
   1380 	if (!charset) return C_AUTO;
   1381 
   1382 	table = conv_get_charset_from_str_table();
   1383 	return GPOINTER_TO_UINT(g_hash_table_lookup(table, charset));
   1384 }
   1385 
   1386 static CharSet conv_get_locale_charset(void)
   1387 {
   1388 	static CharSet cur_charset = C_UNINITIALIZED;
   1389 	const gchar *cur_locale;
   1390 	const gchar *p;
   1391 	gint i;
   1392 
   1393 	if (cur_charset != C_UNINITIALIZED)
   1394 		return cur_charset;
   1395 
   1396 	cur_locale = conv_get_current_locale();
   1397 	if (!cur_locale) {
   1398 		cur_charset = C_US_ASCII;
   1399 		return cur_charset;
   1400 	}
   1401 
   1402 	if (strcasestr(cur_locale, "UTF-8") ||
   1403 	    strcasestr(cur_locale, "utf8")) {
   1404 		cur_charset = C_UTF_8;
   1405 		return cur_charset;
   1406 	}
   1407 
   1408 	if ((p = strcasestr(cur_locale, "@euro")) && p[5] == '\0') {
   1409 		cur_charset = C_ISO_8859_15;
   1410 		return cur_charset;
   1411 	}
   1412 
   1413 	for (i = 0; i < sizeof(locale_table) / sizeof(locale_table[0]); i++) {
   1414 		const gchar *p;
   1415 
   1416 		/* "ja_JP.EUC" matches with "ja_JP.eucJP", "ja_JP.EUC" and
   1417 		   "ja_JP". "ja_JP" matches with "ja_JP.xxxx" and "ja" */
   1418 		if (!g_ascii_strncasecmp(cur_locale, locale_table[i].locale,
   1419 				 strlen(locale_table[i].locale))) {
   1420 			cur_charset = locale_table[i].charset;
   1421 			return cur_charset;
   1422 		} else if ((p = strchr(locale_table[i].locale, '_')) &&
   1423 			 !strchr(p + 1, '.')) {
   1424 			if (strlen(cur_locale) == 2 &&
   1425 			    !g_ascii_strncasecmp(cur_locale, locale_table[i].locale, 2)) {
   1426 				cur_charset = locale_table[i].charset;
   1427 				return cur_charset;
   1428 			}
   1429 		}
   1430 	}
   1431 
   1432 	cur_charset = C_AUTO;
   1433 	return cur_charset;
   1434 }
   1435 
   1436 static CharSet conv_get_locale_charset_no_utf8(void)
   1437 {
   1438 	static CharSet cur_charset = C_UNINITIALIZED;
   1439 	const gchar *cur_locale;
   1440 	const gchar *p;
   1441 	gint i;
   1442 
   1443 	if (codeconv_broken_are_utf8) {
   1444 		cur_charset = C_UTF_8;
   1445 		return cur_charset;
   1446 	}
   1447 
   1448 	cur_locale = conv_get_current_locale();
   1449 	if (!cur_locale) {
   1450 		cur_charset = C_US_ASCII;
   1451 		return cur_charset;
   1452 	}
   1453 
   1454 	if (strcasestr(cur_locale, "UTF-8") ||
   1455 	    strcasestr(cur_locale, "utf8")) {
   1456 		cur_charset = C_UTF_8;
   1457 		return cur_charset;
   1458 	}
   1459 
   1460 	if ((p = strcasestr(cur_locale, "@euro")) && p[5] == '\0') {
   1461 		cur_charset = C_ISO_8859_15;
   1462 		return cur_charset;
   1463 	}
   1464 
   1465 	for (i = 0; i < sizeof(locale_table) / sizeof(locale_table[0]); i++) {
   1466 		const gchar *p;
   1467 
   1468 		/* "ja_JP.EUC" matches with "ja_JP.eucJP", "ja_JP.EUC" and
   1469 		   "ja_JP". "ja_JP" matches with "ja_JP.xxxx" and "ja" */
   1470 		if (!g_ascii_strncasecmp(cur_locale, locale_table[i].locale,
   1471 				 strlen(locale_table[i].locale))) {
   1472 			cur_charset = locale_table[i].charset;
   1473 			return cur_charset;
   1474 		} else if ((p = strchr(locale_table[i].locale, '_')) &&
   1475 			 !strchr(p + 1, '.')) {
   1476 			if (strlen(cur_locale) == 2 &&
   1477 			    !g_ascii_strncasecmp(cur_locale, locale_table[i].locale, 2)) {
   1478 				cur_charset = locale_table[i].charset;
   1479 				return cur_charset;
   1480 			}
   1481 		}
   1482 	}
   1483 
   1484 	cur_charset = C_AUTO;
   1485 	return cur_charset;
   1486 }
   1487 
   1488 const gchar *conv_get_locale_charset_str(void)
   1489 {
   1490 	static const gchar *codeset = NULL;
   1491 
   1492 	if (!codeset)
   1493 		codeset = conv_get_charset_str(conv_get_locale_charset());
   1494 
   1495 	return codeset ? codeset : CS_INTERNAL;
   1496 }
   1497 
   1498 const gchar *conv_get_locale_charset_str_no_utf8(void)
   1499 {
   1500 	static const gchar *codeset = NULL;
   1501 
   1502 	if (!codeset)
   1503 		codeset = conv_get_charset_str(conv_get_locale_charset_no_utf8());
   1504 
   1505 	return codeset ? codeset : CS_INTERNAL;
   1506 }
   1507 
   1508 static CharSet conv_get_outgoing_charset(void)
   1509 {
   1510 	static CharSet out_charset = C_UNINITIALIZED;
   1511 	const gchar *cur_locale;
   1512 	const gchar *p;
   1513 	gint i;
   1514 
   1515 	if (out_charset != C_UNINITIALIZED)
   1516 		return out_charset;
   1517 
   1518 	cur_locale = conv_get_current_locale();
   1519 	if (!cur_locale) {
   1520 		out_charset = C_AUTO;
   1521 		return out_charset;
   1522 	}
   1523 
   1524 	if (strcasestr(cur_locale, "UTF-8") ||
   1525 	    strcasestr(cur_locale, "utf8")) {
   1526 		out_charset = C_UTF_8;
   1527 		return out_charset;
   1528 	}
   1529 
   1530 	if ((p = strcasestr(cur_locale, "@euro")) && p[5] == '\0') {
   1531 		out_charset = C_ISO_8859_15;
   1532 		return out_charset;
   1533 	}
   1534 
   1535 	for (i = 0; i < sizeof(locale_table) / sizeof(locale_table[0]); i++) {
   1536 		const gchar *p;
   1537 
   1538 		if (!g_ascii_strncasecmp(cur_locale, locale_table[i].locale,
   1539 				 strlen(locale_table[i].locale))) {
   1540 			out_charset = locale_table[i].out_charset;
   1541 			break;
   1542 		} else if ((p = strchr(locale_table[i].locale, '_')) &&
   1543 			 !strchr(p + 1, '.')) {
   1544 			if (strlen(cur_locale) == 2 &&
   1545 			    !g_ascii_strncasecmp(cur_locale, locale_table[i].locale, 2)) {
   1546 				out_charset = locale_table[i].out_charset;
   1547 				break;
   1548 			}
   1549 		}
   1550 	}
   1551 
   1552 	return out_charset;
   1553 }
   1554 
   1555 const gchar *conv_get_outgoing_charset_str(void)
   1556 {
   1557 	CharSet out_charset;
   1558 	const gchar *str;
   1559 
   1560 	out_charset = conv_get_outgoing_charset();
   1561 	str = conv_get_charset_str(out_charset);
   1562 
   1563 	return str ? str : CS_UTF_8;
   1564 }
   1565 
   1566 const gchar *conv_get_current_locale(void)
   1567 {
   1568 	const gchar *cur_locale;
   1569 
   1570 	cur_locale = g_getenv("LC_ALL");
   1571 	if (!cur_locale) cur_locale = g_getenv("LC_CTYPE");
   1572 	if (!cur_locale) cur_locale = g_getenv("LANG");
   1573 	if (!cur_locale) cur_locale = setlocale(LC_CTYPE, NULL);
   1574 
   1575 	debug_print("current locale: %s\n",
   1576 		    cur_locale ? cur_locale : "(none)");
   1577 
   1578 	return cur_locale;
   1579 }
   1580 
   1581 static gboolean conv_is_ja_locale(void)
   1582 {
   1583 	static gint is_ja_locale = -1;
   1584 	const gchar *cur_locale;
   1585 
   1586 	if (is_ja_locale != -1)
   1587 		return is_ja_locale != 0;
   1588 
   1589 	is_ja_locale = 0;
   1590 	cur_locale = conv_get_current_locale();
   1591 	if (cur_locale) {
   1592 		if (g_ascii_strncasecmp(cur_locale, "ja", 2) == 0)
   1593 			is_ja_locale = 1;
   1594 	}
   1595 
   1596 	return is_ja_locale != 0;
   1597 }
   1598 
   1599 gchar *conv_unmime_header(const gchar *str, const gchar *default_encoding,
   1600 			   gboolean addr_field)
   1601 {
   1602 	gchar buf[BUFFSIZE];
   1603 
   1604 	cm_return_val_if_fail(str != NULL, NULL);
   1605 
   1606 	if (is_ascii_str(str))
   1607 		return unmime_header(str, addr_field);
   1608 
   1609 	if (default_encoding) {
   1610 		gchar *utf8_buf;
   1611 
   1612 		utf8_buf = conv_codeset_strdup
   1613 			(str, default_encoding, CS_INTERNAL);
   1614 		if (utf8_buf) {
   1615 			gchar *decoded_str;
   1616 
   1617 			decoded_str = unmime_header(utf8_buf, addr_field);
   1618 			g_free(utf8_buf);
   1619 			return decoded_str;
   1620 		}
   1621 	}
   1622 
   1623 	if (conv_is_ja_locale())
   1624 		conv_anytodisp(buf, sizeof(buf), str);
   1625 	else
   1626 		conv_localetodisp(buf, sizeof(buf), str);
   1627 
   1628 	return unmime_header(buf, addr_field);
   1629 }
   1630 
   1631 #define MAX_LINELEN		76
   1632 #define MAX_HARD_LINELEN	996
   1633 #define MIMESEP_BEGIN		"=?"
   1634 #define MIMESEP_END		"?="
   1635 
   1636 #define LBREAK_IF_REQUIRED(cond, is_plain_text)				\
   1637 {									\
   1638 	if (len - (destp - (guchar *)dest) < MAX_LINELEN + 2) {		\
   1639 		*destp = '\0';						\
   1640 		return;							\
   1641 	}								\
   1642 									\
   1643 	if ((cond) && *srcp) {						\
   1644 		if (destp > (guchar *)dest && left < MAX_LINELEN - 1) {	\
   1645 			if (isspace(*(destp - 1)))			\
   1646 				destp--;				\
   1647 			else if (is_plain_text && isspace(*srcp))	\
   1648 				srcp++;					\
   1649 			if (*srcp) {					\
   1650 				*destp++ = '\n';			\
   1651 				*destp++ = ' ';				\
   1652 				left = MAX_LINELEN - 1;			\
   1653 			}						\
   1654 		} else if (destp == (guchar *)dest && left < 7) {	\
   1655 			if (is_plain_text && isspace(*srcp))		\
   1656 				srcp++;					\
   1657 			if (*srcp) {					\
   1658 				*destp++ = '\n';			\
   1659 				*destp++ = ' ';				\
   1660 				left = MAX_LINELEN - 1;			\
   1661 			}						\
   1662 		}							\
   1663 	}								\
   1664 }
   1665 
   1666 #define B64LEN(len) ((len) / 3 * 4 + ((len) % 3 ? 4 : 0))
   1667 
   1668 void conv_encode_header_full(gchar *dest, gint len, const gchar *src,
   1669 			gint header_len, gboolean addr_field,
   1670 			const gchar *out_encoding_)
   1671 {
   1672 	const gchar *cur_encoding;
   1673 	const gchar *out_encoding;
   1674 	gint mimestr_len;
   1675 	gchar *mimesep_enc;
   1676 	gint left;
   1677 	const guchar *srcp = src;
   1678 	guchar *destp = dest;
   1679 	gboolean use_base64;
   1680 
   1681 	cm_return_if_fail(g_utf8_validate(src, -1, NULL) == TRUE);
   1682 	cm_return_if_fail(destp != NULL);
   1683 
   1684 	if (MB_CUR_MAX > 1) {
   1685 		use_base64 = TRUE;
   1686 		mimesep_enc = "?B?";
   1687 	} else {
   1688 		use_base64 = FALSE;
   1689 		mimesep_enc = "?Q?";
   1690 	}
   1691 
   1692 	cur_encoding = CS_INTERNAL;
   1693 
   1694 	if (out_encoding_)
   1695 		out_encoding = out_encoding_;
   1696 	else
   1697 		out_encoding = conv_get_outgoing_charset_str();
   1698 
   1699 	if (!strcmp(out_encoding, CS_US_ASCII))
   1700 		out_encoding = CS_ISO_8859_1;
   1701 
   1702 	mimestr_len = strlen(MIMESEP_BEGIN) + strlen(out_encoding) +
   1703 		strlen(mimesep_enc) + strlen(MIMESEP_END);
   1704 
   1705 	left = MAX_LINELEN - header_len;
   1706 
   1707 	while (*srcp) {
   1708 		LBREAK_IF_REQUIRED(left <= 0, TRUE);
   1709 
   1710 		while (isspace(*srcp)) {
   1711 			*destp++ = *srcp++;
   1712 			left--;
   1713 			LBREAK_IF_REQUIRED(left <= 0, TRUE);
   1714 		}
   1715 
   1716 		/* output as it is if the next word is ASCII string */
   1717 		if (!is_next_nonascii(srcp)) {
   1718 			gint word_len;
   1719 
   1720 			word_len = get_next_word_len(srcp);
   1721 			LBREAK_IF_REQUIRED(left < word_len, TRUE);
   1722 			while (word_len > 0) {
   1723 				LBREAK_IF_REQUIRED(left + (MAX_HARD_LINELEN - MAX_LINELEN) <= 0, TRUE)
   1724 				*destp++ = *srcp++;
   1725 				left--;
   1726 				word_len--;
   1727 			}
   1728 
   1729 			continue;
   1730 		}
   1731 
   1732 		/* don't include parentheses and quotes in encoded strings */
   1733 		if (addr_field && (*srcp == '(' || *srcp == ')' || *srcp == '"')) {
   1734 			LBREAK_IF_REQUIRED(left < 2, FALSE);
   1735 			*destp++ = *srcp++;
   1736 			left--;
   1737 		}
   1738 
   1739 		while (1) {
   1740 			gint mb_len = 0;
   1741 			gint cur_len = 0;
   1742 			gchar *part_str;
   1743 			gchar *out_str;
   1744 			gchar *enc_str;
   1745 			const guchar *p = srcp;
   1746 			gint out_str_len;
   1747 			gint out_enc_str_len;
   1748 			gint mime_block_len;
   1749 			gboolean cont = FALSE;
   1750 
   1751 			while (*p != '\0') {
   1752 				if (isspace(*p) && !is_next_nonascii(p + 1))
   1753 					break;
   1754 				/* don't include parentheses in encoded
   1755 				   strings */
   1756 				if (addr_field && (*p == '(' || *p == ')' || *p == '"'))
   1757 					break;
   1758 
   1759 				mb_len = g_utf8_skip[*p];
   1760 
   1761 				Xstrndup_a(part_str, srcp, cur_len + mb_len, );
   1762 				out_str = conv_codeset_strdup
   1763 					(part_str, cur_encoding, out_encoding);
   1764 				if (!out_str) {
   1765 					if (codeconv_strict_mode) {
   1766 						*dest = '\0';
   1767 						return;
   1768 					} else {
   1769 						g_warning("conv_encode_header_full(): code conversion failed");
   1770 						conv_unreadable_8bit(part_str);
   1771 						out_str = g_strdup(part_str);
   1772 					}
   1773 				}
   1774 				out_str_len = strlen(out_str);
   1775 
   1776 				if (use_base64)
   1777 					out_enc_str_len = B64LEN(out_str_len);
   1778 				else
   1779 					out_enc_str_len =
   1780 						qp_get_q_encoding_len(out_str);
   1781 
   1782 				g_free(out_str);
   1783 
   1784 				if (mimestr_len + out_enc_str_len <= left) {
   1785 					cur_len += mb_len;
   1786 					p += mb_len;
   1787 				} else if (cur_len == 0) {
   1788 					left = 0;
   1789 					LBREAK_IF_REQUIRED(1, FALSE);
   1790 					continue;
   1791 				} else {
   1792 					cont = TRUE;
   1793 					break;
   1794 				}
   1795 			}
   1796 
   1797 			if (cur_len > 0) {
   1798 				Xstrndup_a(part_str, srcp, cur_len, );
   1799 				out_str = conv_codeset_strdup
   1800 					(part_str, cur_encoding, out_encoding);
   1801 				if (!out_str) {
   1802 					g_warning("conv_encode_header_full(): code conversion failed");
   1803 					conv_unreadable_8bit(part_str);
   1804 					out_str = g_strdup(part_str);
   1805 				}
   1806 				out_str_len = strlen(out_str);
   1807 
   1808 				if (use_base64)
   1809 					out_enc_str_len = B64LEN(out_str_len);
   1810 				else
   1811 					out_enc_str_len =
   1812 						qp_get_q_encoding_len(out_str);
   1813 
   1814 				if (use_base64)
   1815 					enc_str = g_base64_encode(out_str, out_str_len);
   1816 				else {
   1817 					Xalloca(enc_str, out_enc_str_len + 1, );
   1818 					qp_q_encode(enc_str, out_str);
   1819 				}
   1820 
   1821 				g_free(out_str);
   1822 
   1823 				/* output MIME-encoded string block */
   1824 				mime_block_len = mimestr_len + strlen(enc_str);
   1825 				g_snprintf(destp, mime_block_len + 1,
   1826 					   MIMESEP_BEGIN "%s%s%s" MIMESEP_END,
   1827 					   out_encoding, mimesep_enc, enc_str);
   1828 
   1829 				if (use_base64)
   1830 					g_free(enc_str);
   1831 
   1832 				destp += mime_block_len;
   1833 				srcp += cur_len;
   1834 
   1835 				left -= mime_block_len;
   1836 			}
   1837 
   1838 			LBREAK_IF_REQUIRED(cont, FALSE);
   1839 
   1840 			if (cur_len == 0)
   1841 				break;
   1842 		}
   1843 	}
   1844 
   1845 	*destp = '\0';
   1846 }
   1847 
   1848 void conv_encode_header(gchar *dest, gint len, const gchar *src,
   1849 			gint header_len, gboolean addr_field)
   1850 {
   1851 	conv_encode_header_full(dest,len,src,header_len,addr_field,NULL);
   1852 }
   1853 
   1854 #undef LBREAK_IF_REQUIRED
   1855 #undef B64LEN
   1856 
   1857 gchar *conv_filename_from_utf8(const gchar *utf8_file)
   1858 {
   1859 	gchar *fs_file;
   1860 	GError *error = NULL;
   1861 
   1862 	cm_return_val_if_fail(utf8_file != NULL, NULL);
   1863 
   1864 	fs_file = g_filename_from_utf8(utf8_file, -1, NULL, NULL, &error);
   1865 	if (error) {
   1866 		debug_print("failed to convert encoding of file name: %s\n",
   1867 			  error->message);
   1868 		g_error_free(error);
   1869 	}
   1870 	if (!fs_file)
   1871 		fs_file = g_strdup(utf8_file);
   1872 
   1873 	return fs_file;
   1874 }
   1875 
   1876 gchar *conv_filename_to_utf8(const gchar *fs_file)
   1877 {
   1878 	gchar *utf8_file = NULL;
   1879 	GError *error = NULL;
   1880 
   1881 	cm_return_val_if_fail(fs_file != NULL, NULL);
   1882 
   1883 	utf8_file = g_filename_to_utf8(fs_file, -1, NULL, NULL, &error);
   1884 	if (error) {
   1885 		g_warning("failed to convert encoding of file name: %s",
   1886 			  error->message);
   1887 		g_error_free(error);
   1888 	}
   1889 
   1890 	if (!utf8_file || !g_utf8_validate(utf8_file, -1, NULL)) {
   1891 		g_free(utf8_file);
   1892 		utf8_file = g_strdup(fs_file);
   1893 		conv_unreadable_8bit(utf8_file);
   1894 	}
   1895 
   1896 	return utf8_file;
   1897 }