summaryrefslogtreecommitdiff
path: root/libsylph/codeconv.c
diff options
context:
space:
mode:
authorSimeon Simeonov2018-02-26 11:23:00 +0100
committerSimeon Simeonov2018-02-26 11:23:00 +0100
commit0b3cbf57875fd692e4ba0b336fefa4bee1ed00dc (patch)
treeaf916a30553c78ce9d4f2d8658656a175c5b6921 /libsylph/codeconv.c
Initial commit for sylpheed 3.7.0
Diffstat (limited to 'libsylph/codeconv.c')
-rw-r--r--libsylph/codeconv.c2856
1 files changed, 2856 insertions, 0 deletions
diff --git a/libsylph/codeconv.c b/libsylph/codeconv.c
new file mode 100644
index 0000000..49d1b3d
--- /dev/null
+++ b/libsylph/codeconv.c
@@ -0,0 +1,2856 @@
1/*
2 * LibSylph -- E-Mail client library
3 * Copyright (C) 1999-2017 Hiroyuki Yamamoto
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library 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 GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#ifdef HAVE_CONFIG_H
21# include "config.h"
22#endif
23
24#include "defs.h"
25
26#include <glib.h>
27#include <glib/gi18n.h>
28#include <string.h>
29#include <ctype.h>
30#include <stdlib.h>
31#include <errno.h>
32
33#if HAVE_LOCALE_H
34# include <locale.h>
35#endif
36
37#include <iconv.h>
38
39#include "codeconv.h"
40#include "prefs_common.h"
41#include "unmime.h"
42#include "base64.h"
43#include "quoted-printable.h"
44#include "utils.h"
45
46typedef enum
47{
48 JIS_ASCII,
49 JIS_KANJI,
50 JIS_HWKANA,
51 JIS_AUXKANJI,
52 JIS_UDC
53} JISState;
54
55#define SUBST_CHAR '_'
56#define ESC '\033'
57#define SO 0x0e
58#define SI 0x0f
59#define SS2 0x8e
60#define SS3 0x8f
61
62#define iseuckanji(c) \
63 (((c) & 0xff) >= 0xa1 && ((c) & 0xff) <= 0xfe)
64#define iseuchwkana1(c) \
65 (((c) & 0xff) == SS2)
66#define iseuchwkana2(c) \
67 (((c) & 0xff) >= 0xa1 && ((c) & 0xff) <= 0xdf)
68#define iseucaux(c) \
69 (((c) & 0xff) == SS3)
70
71#define issjiskanji1(c) \
72 ((((c) & 0xff) >= 0x81 && ((c) & 0xff) <= 0x9f) || \
73 (((c) & 0xff) >= 0xe0 && ((c) & 0xff) <= 0xef))
74#define issjiskanji2(c) \
75 ((((c) & 0xff) >= 0x40 && ((c) & 0xff) <= 0x7e) || \
76 (((c) & 0xff) >= 0x80 && ((c) & 0xff) <= 0xfc))
77#define issjishwkana(c) \
78 (((c) & 0xff) >= 0xa1 && ((c) & 0xff) <= 0xdf)
79#define issjisext(c) \
80 (((c) & 0xff) >= 0xf0 && ((c) & 0xff) <= 0xfc)
81#define issjisudc(c) \
82 (((c) & 0xff) >= 0xf0 && ((c) & 0xff) <= 0xf9)
83#define issjisibmext(c1, c2) \
84 ((((c1) & 0xff) >= 0xfa && ((c1) & 0xff) <= 0xfb && \
85 issjiskanji2(c2)) || \
86 (((c1) & 0xff) == 0xfc && \
87 ((c2) & 0xff) >= 0x40 && ((c2) & 0xff) <= 0x4b))
88
89#define isjiskanji(c) \
90 (((c) & 0xff) >= 0x21 && ((c) & 0xff) <= 0x7e)
91#define isjishwkana(c) \
92 (((c) & 0xff) >= 0x21 && ((c) & 0xff) <= 0x5f)
93#define isjisudc(c) \
94 (((c) & 0xff) >= 0x21 && ((c) & 0xff) <= 0x34)
95#define isjisudclow(c) \
96 (((c) & 0xff) >= 0x21 && ((c) & 0xff) <= 0x2a)
97#define isjisudchigh(c) \
98 (((c) & 0xff) >= 0x2b && ((c) & 0xff) <= 0x34)
99
100/* U+0080 - U+07FF */
101#define isutf8_2_1(c) \
102 (((c) & 0xe0) == 0xc0)
103#define isutf8_2_2(c) \
104 (((c) & 0xc0) == 0x80)
105/* U+0800 - U+FFFF */
106#define isutf8_3_1(c) \
107 (((c) & 0xf0) == 0xe0)
108#define isutf8_3_2(c) \
109 (((c) & 0xc0) == 0x80)
110
111#define isutf8bom(s) \
112 (((*(s)) & 0xff) == 0xef && ((*(s + 1)) & 0xff) == 0xbb && \
113 ((*(s + 2)) & 0xff) == 0xbf)
114
115#define K_IN() \
116 if (state != JIS_KANJI) { \
117 *out++ = ESC; \
118 *out++ = '$'; \
119 *out++ = 'B'; \
120 state = JIS_KANJI; \
121 }
122
123#define K_OUT() \
124 if (state != JIS_ASCII) { \
125 *out++ = ESC; \
126 *out++ = '('; \
127 *out++ = 'B'; \
128 state = JIS_ASCII; \
129 }
130
131#define HW_IN() \
132 if (state != JIS_HWKANA) { \
133 *out++ = ESC; \
134 *out++ = '('; \
135 *out++ = 'I'; \
136 state = JIS_HWKANA; \
137 }
138
139#define AUX_IN() \
140 if (state != JIS_AUXKANJI) { \
141 *out++ = ESC; \
142 *out++ = '$'; \
143 *out++ = '('; \
144 *out++ = 'D'; \
145 state = JIS_AUXKANJI; \
146 }
147
148#define UDC_IN() \
149 if (state != JIS_UDC) { \
150 *out++ = ESC; \
151 *out++ = '$'; \
152 *out++ = '('; \
153 *out++ = '?'; \
154 state = JIS_UDC; \
155 }
156
157static ConvADType conv_ad_type = C_AD_BY_LOCALE;
158
159static gchar *conv_jistoeuc(const gchar *inbuf, gint *error);
160static gchar *conv_jistosjis(const gchar *inbuf, gint *error);
161static gchar *conv_euctojis(const gchar *inbuf, gint *error);
162static gchar *conv_sjistojis(const gchar *inbuf, gint *error);
163static gchar *conv_sjistoeuc(const gchar *inbuf, gint *error);
164
165static gchar *conv_jistoutf8(const gchar *inbuf, gint *error);
166static gchar *conv_sjistoutf8(const gchar *inbuf, gint *error);
167static gchar *conv_euctoutf8(const gchar *inbuf, gint *error);
168static gchar *conv_anytoutf8(const gchar *inbuf, gint *error);
169
170static gchar *conv_utf8toeuc(const gchar *inbuf, gint *error);
171static gchar *conv_utf8tojis(const gchar *inbuf, gint *error);
172static gchar *conv_utf8tosjis(const gchar *inbuf, gint *error);
173
174/* static void conv_unreadable_eucjp(gchar *str); */
175static void conv_unreadable_8bit(gchar *str);
176/* static void conv_unreadable_latin(gchar *str); */
177
178static gchar *conv_jistodisp(const gchar *inbuf, gint *error);
179static gchar *conv_sjistodisp(const gchar *inbuf, gint *error);
180static gchar *conv_euctodisp(const gchar *inbuf, gint *error);
181
182static gchar *conv_anytodisp(const gchar *inbuf, gint *error);
183static gchar *conv_ustodisp(const gchar *inbuf, gint *error);
184static gchar *conv_noconv(const gchar *inbuf, gint *error);
185
186static gchar *conv_jistoeuc(const gchar *inbuf, gint *error)
187{
188 gchar *outbuf;
189 const guchar *in = (guchar *)inbuf;
190 guchar *out;
191 JISState state = JIS_ASCII;
192 gint error_ = 0;
193
194 outbuf = g_malloc(strlen(inbuf) * 2 + 1);
195 out = (guchar *)outbuf;
196
197 while (*in != '\0') {
198 if (*in == ESC) {
199 in++;
200 if (*in == '$') {
201 if (*(in + 1) == '@' || *(in + 1) == 'B') {
202 state = JIS_KANJI;
203 in += 2;
204 } else if (*(in + 1) == '(' &&
205 *(in + 2) == 'D') {
206 state = JIS_AUXKANJI;
207 in += 3;
208 } else {
209 /* unknown escape sequence */
210 error_ = -1;
211 state = JIS_ASCII;
212 }
213 } else if (*in == '(') {
214 if (*(in + 1) == 'B' || *(in + 1) == 'J') {
215 state = JIS_ASCII;
216 in += 2;
217 } else if (*(in + 1) == 'I') {
218 state = JIS_HWKANA;
219 in += 2;
220 } else {
221 /* unknown escape sequence */
222 error_ = -1;
223 state = JIS_ASCII;
224 }
225 } else {
226 /* unknown escape sequence */
227 error_ = -1;
228 state = JIS_ASCII;
229 }
230 } else if (*in == 0x0e) {
231 state = JIS_HWKANA;
232 in++;
233 } else if (*in == 0x0f) {
234 state = JIS_ASCII;
235 in++;
236 } else {
237 switch (state) {
238 case JIS_ASCII:
239 *out++ = *in++;
240 break;
241 case JIS_KANJI:
242 *out++ = *in++ | 0x80;
243 if (*in == '\0') break;
244 *out++ = *in++ | 0x80;
245 break;
246 case JIS_HWKANA:
247 *out++ = 0x8e;
248 *out++ = *in++ | 0x80;
249 break;
250 case JIS_AUXKANJI:
251 *out++ = 0x8f;
252 *out++ = *in++ | 0x80;
253 if (*in == '\0') break;
254 *out++ = *in++ | 0x80;
255 break;
256 default:
257 *out++ = *in++;
258 break;
259 }
260 }
261 }
262
263 *out = '\0';
264
265 if (error)
266 *error = error_;
267
268 return outbuf;
269}
270
271static gchar *conv_jistosjis(const gchar *inbuf, gint *error)
272{
273 gchar *outbuf;
274 const guchar *in = (guchar *)inbuf;
275 guchar *out;
276 JISState state = JIS_ASCII;
277 gint error_ = 0;
278
279 outbuf = g_malloc(strlen(inbuf) * 2 + 1);
280 out = (guchar *)outbuf;
281
282 while (*in != '\0') {
283 if (*in == ESC) {
284 in++;
285 if (*in == '$') {
286 if (*(in + 1) == '@' || *(in + 1) == 'B') {
287 state = JIS_KANJI;
288 in += 2;
289 } else if (*(in + 1) == '(' &&
290 *(in + 2) == '?') {
291 /* ISO-2022-JP-MS extention */
292 state = JIS_UDC;
293 in += 3;
294 } else {
295 /* unknown escape sequence */
296 error_ = -1;
297 state = JIS_ASCII;
298 }
299 } else if (*in == '(') {
300 if (*(in + 1) == 'B' || *(in + 1) == 'J') {
301 state = JIS_ASCII;
302 in += 2;
303 } else if (*(in + 1) == 'I') {
304 state = JIS_HWKANA;
305 in += 2;
306 } else {
307 /* unknown escape sequence */
308 error_ = -1;
309 state = JIS_ASCII;
310 }
311 } else {
312 /* unknown escape sequence */
313 error_ = -1;
314 state = JIS_ASCII;
315 }
316 } else if (*in == SO) {
317 state = JIS_HWKANA;
318 in++;
319 } else if (*in == SI) {
320 state = JIS_ASCII;
321 in++;
322 } else {
323 switch (state) {
324 case JIS_ASCII:
325 *out++ = *in++;
326 break;
327 case JIS_HWKANA:
328 *out++ = *in++ | 0x80;
329 break;
330 case JIS_KANJI:
331 if ((isjiskanji(*in) ||
332 (*in >= 0x7f && *in <= 0x97)) &&
333 isjiskanji(*(in + 1))) {
334 *out++ = ((*in < 0x5f)
335 ? (((*in - 0x21) / 2) + 0x81)
336 : (((*in - 0x21) / 2) + 0xc1));
337 *out++ = ((*in % 2)
338 ? ((*(in + 1) + ((*(in + 1) < 0x60)
339 ? 0x1f : 0x20)))
340 : *(in + 1) + 0x7e);
341 in += 2;
342 } else {
343 error_ = -1;
344 *out++ = SUBST_CHAR;
345 in++;
346 if (*in != '\0') {
347 *out++ = SUBST_CHAR;
348 in++;
349 }
350 }
351 break;
352 case JIS_UDC:
353 if (isjisudc(*in) && isjiskanji(*(in + 1))) {
354 *out++ = (((*in - 0x21) / 2) + 0xf0);
355 *out++ = ((*in % 2)
356 ? ((*(in + 1) + ((*(in + 1) < 0x60)
357 ? 0x1f : 0x20)))
358 : *(in + 1) + 0x7e);
359 in += 2;
360 } else {
361 error_ = -1;
362 *out++ = SUBST_CHAR;
363 in++;
364 if (*in != '\0') {
365 *out++ = SUBST_CHAR;
366 in++;
367 }
368 }
369 break;
370 default:
371 *out++ = *in++;
372 break;
373 }
374 }
375 }
376
377 *out = '\0';
378
379 if (error)
380 *error = error_;
381
382 return outbuf;
383}
384
385#define JIS_HWDAKUTEN 0x5e
386#define JIS_HWHANDAKUTEN 0x5f
387
388static gint conv_jis_hantozen(guchar *outbuf, guchar jis_code, guchar sound_sym)
389{
390 static guint16 h2z_tbl[] = {
391 /* 0x20 - 0x2f */
392 0x0000, 0x2123, 0x2156, 0x2157, 0x2122, 0x2126, 0x2572, 0x2521,
393 0x2523, 0x2525, 0x2527, 0x2529, 0x2563, 0x2565, 0x2567, 0x2543,
394 /* 0x30 - 0x3f */
395 0x213c, 0x2522, 0x2524, 0x2526, 0x2528, 0x252a, 0x252b, 0x252d,
396 0x252f, 0x2531, 0x2533, 0x2535, 0x2537, 0x2539, 0x253b, 0x253d,
397 /* 0x40 - 0x4f */
398 0x253f, 0x2541, 0x2544, 0x2546, 0x2548, 0x254a, 0x254b, 0x254c,
399 0x254d, 0x254e, 0x254f, 0x2552, 0x2555, 0x2558, 0x255b, 0x255e,
400 /* 0x50 - 0x5f */
401 0x255f, 0x2560, 0x2561, 0x2562, 0x2564, 0x2566, 0x2568, 0x2569,
402 0x256a, 0x256b, 0x256c, 0x256d, 0x256f, 0x2573, 0x212b, 0x212c
403 };
404
405 static guint16 dakuten_tbl[] = {
406 /* 0x30 - 0x3f */
407 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x252c, 0x252e,
408 0x2530, 0x2532, 0x2534, 0x2536, 0x2538, 0x253a, 0x253c, 0x253e,
409 /* 0x40 - 0x4f */
410 0x2540, 0x2542, 0x2545, 0x2547, 0x2549, 0x0000, 0x0000, 0x0000,
411 0x0000, 0x0000, 0x2550, 0x2553, 0x2556, 0x2559, 0x255c, 0x0000
412 };
413
414 static guint16 handakuten_tbl[] = {
415 /* 0x4a - 0x4e */
416 0x2551, 0x2554, 0x2557, 0x255a, 0x255d
417 };
418
419 guint16 out_code;
420
421 jis_code &= 0x7f;
422 sound_sym &= 0x7f;
423
424 if (jis_code < 0x21 || jis_code > 0x5f)
425 return 0;
426
427 if (sound_sym == JIS_HWDAKUTEN &&
428 jis_code >= 0x36 && jis_code <= 0x4e) {
429 out_code = dakuten_tbl[jis_code - 0x30];
430 if (out_code != 0) {
431 *outbuf = out_code >> 8;
432 *(outbuf + 1) = out_code & 0xff;
433 return 2;
434 }
435 }
436
437 if (sound_sym == JIS_HWHANDAKUTEN &&
438 jis_code >= 0x4a && jis_code <= 0x4e) {
439 out_code = handakuten_tbl[jis_code - 0x4a];
440 *outbuf = out_code >> 8;
441 *(outbuf + 1) = out_code & 0xff;
442 return 2;
443 }
444
445 out_code = h2z_tbl[jis_code - 0x20];
446 *outbuf = out_code >> 8;
447 *(outbuf + 1) = out_code & 0xff;
448 return 1;
449}
450
451static gchar *conv_euctojis(const gchar *inbuf, gint *error)
452{
453 gchar *outbuf;
454 const guchar *in = (guchar *)inbuf;
455 guchar *out;
456 JISState state = JIS_ASCII;
457 gint error_ = 0;
458
459 outbuf = g_malloc(strlen(inbuf) * 3 + 4);
460 out = (guchar *)outbuf;
461
462 while (*in != '\0') {
463 if (isascii(*in)) {
464 K_OUT();
465 *out++ = *in++;
466 } else if (iseuckanji(*in)) {
467 if (iseuckanji(*(in + 1))) {
468 K_IN();
469 *out++ = *in++ & 0x7f;
470 *out++ = *in++ & 0x7f;
471 } else {
472 error_ = -1;
473 K_OUT();
474 *out++ = SUBST_CHAR;
475 in++;
476 if (*in != '\0' && !isascii(*in)) {
477 *out++ = SUBST_CHAR;
478 in++;
479 }
480 }
481 } else if (iseuchwkana1(*in)) {
482 if (iseuchwkana2(*(in + 1))) {
483 if (prefs_common.allow_jisx0201_kana) {
484 HW_IN();
485 in++;
486 *out++ = *in++ & 0x7f;
487 } else {
488 guchar jis_ch[2];
489 gint len;
490
491 if (iseuchwkana1(*(in + 2)) &&
492 iseuchwkana2(*(in + 3)))
493 len = conv_jis_hantozen
494 (jis_ch,
495 *(in + 1), *(in + 3));
496 else
497 len = conv_jis_hantozen
498 (jis_ch,
499 *(in + 1), '\0');
500 if (len == 0)
501 in += 2;
502 else {
503 K_IN();
504 in += len * 2;
505 *out++ = jis_ch[0];
506 *out++ = jis_ch[1];
507 }
508 }
509 } else {
510 error_ = -1;
511 K_OUT();
512 in++;
513 if (*in != '\0' && !isascii(*in)) {
514 *out++ = SUBST_CHAR;
515 in++;
516 }
517 }
518 } else if (iseucaux(*in)) {
519 in++;
520 if (iseuckanji(*in) && iseuckanji(*(in + 1))) {
521 AUX_IN();
522 *out++ = *in++ & 0x7f;
523 *out++ = *in++ & 0x7f;
524 } else {
525 error_ = -1;
526 K_OUT();
527 if (*in != '\0' && !isascii(*in)) {
528 *out++ = SUBST_CHAR;
529 in++;
530 if (*in != '\0' && !isascii(*in)) {
531 *out++ = SUBST_CHAR;
532 in++;
533 }
534 }
535 }
536 } else {
537 error_ = -1;
538 K_OUT();
539 *out++ = SUBST_CHAR;
540 in++;
541 }
542 }
543
544 K_OUT();
545 *out = '\0';
546
547 if (error)
548 *error = error_;
549
550 return outbuf;
551}
552
553#define sjistoidx(c1, c2) \
554 (((c1) > 0x9f) \
555 ? (((c1) - 0xc1) * 188 + (c2) - (((c2) > 0x7e) ? 0x41 : 0x40)) \
556 : (((c1) - 0x81) * 188 + (c2) - (((c2) > 0x7e) ? 0x41 : 0x40)))
557#define idxtojis1(c) (((c) / 94) + 0x21)
558#define idxtojis2(c) (((c) % 94) + 0x21)
559
560static guint conv_idx_ibmtonec(guint idx)
561{
562 if (idx >= sjistoidx(0xfa, 0x5c))
563 idx -= sjistoidx(0xfa, 0x5c)
564 - sjistoidx(0xed, 0x40);
565/* else if (idx == sjistoidx(0xfa, 0x5b)) */
566/* idx = sjistoidx(0x81, 0xe6); */
567/* else if (idx == sjistoidx(0xfa, 0x5a)) */
568/* idx = sjistoidx(0x87, 0x84); */
569/* else if (idx == sjistoidx(0xfa, 0x59)) */
570/* idx = sjistoidx(0x87, 0x82); */
571/* else if (idx == sjistoidx(0xfa, 0x58)) */
572/* idx = sjistoidx(0x87, 0x8a); */
573 else if (idx >= sjistoidx(0xfa, 0x55))
574 idx -= sjistoidx(0xfa, 0x55)
575 - sjistoidx(0xee, 0xfa);
576/* else if (idx == sjistoidx(0xfa, 0x54)) */
577/* idx = sjistoidx(0x81, 0xca); */
578/* else if (idx >= sjistoidx(0xfa, 0x4a)) */
579/* idx -= sjistoidx(0xfa, 0x4a) */
580/* - sjistoidx(0x87, 0x54); */
581 else if (idx >= sjistoidx(0xfa, 0x40))
582 idx -= sjistoidx(0xfa, 0x40)
583 - sjistoidx(0xee, 0xef);
584 return idx;
585}
586
587static gchar *conv_sjistojis(const gchar *inbuf, gint *error)
588{
589 gchar *outbuf;
590 const guchar *in = (guchar *)inbuf;
591 guchar *out;
592 JISState state = JIS_ASCII;
593 gint error_ = 0;
594 guint idx;
595
596 outbuf = g_malloc(strlen(inbuf) * 5 + 4);
597 out = (guchar *)outbuf;
598
599 while (*in != '\0') {
600 if (isascii(*in)) {
601 K_OUT();
602 *out++ = *in++;
603 } else if (issjiskanji1(*in)) {
604 if (issjiskanji2(*(in + 1))) {
605 K_IN();
606 idx = sjistoidx(*in, *(in + 1));
607 *out++ = idxtojis1(idx);
608 *out++ = idxtojis2(idx);
609 in += 2;
610 } else {
611 error_ = -1;
612 K_OUT();
613 *out++ = SUBST_CHAR;
614 in++;
615 if (*in != '\0' && !isascii(*in)) {
616 *out++ = SUBST_CHAR;
617 in++;
618 }
619 }
620 } else if (issjishwkana(*in)) {
621 if (prefs_common.allow_jisx0201_kana) {
622 HW_IN();
623 *out++ = *in++ & 0x7f;
624 } else {
625 guchar jis_ch[2];
626 gint len;
627
628 if (issjishwkana(*(in + 1)))
629 len = conv_jis_hantozen
630 (jis_ch,
631 *in, *(in + 1));
632 else
633 len = conv_jis_hantozen
634 (jis_ch,
635 *in, '\0');
636 if (len == 0)
637 in++;
638 else {
639 K_IN();
640 in += len;
641 *out++ = jis_ch[0];
642 *out++ = jis_ch[1];
643 }
644 }
645 } else if (issjisibmext(*in, *(in + 1))) {
646 K_IN();
647 idx = sjistoidx(*in, *(in + 1));
648 idx = conv_idx_ibmtonec(idx);
649 *out++ = idxtojis1(idx);
650 *out++ = idxtojis2(idx);
651 in += 2;
652#if 0
653 } else if (issjisudc(*in)) {
654 UDC_IN();
655 idx = sjistoidx(*in, *(in + 1))
656 - sjistoidx(0xf0, 0x40);
657 *out++ = idxtojis1(idx);
658 *out++ = idxtojis2(idx);
659 in += 2;
660#endif
661 } else if (issjisext(*in)) {
662 error_ = -1;
663 K_OUT();
664 *out++ = SUBST_CHAR;
665 in++;
666 if (*in != '\0' && !isascii(*in)) {
667 *out++ = SUBST_CHAR;
668 in++;
669 }
670 } else {
671 error_ = -1;
672 K_OUT();
673 *out++ = SUBST_CHAR;
674 in++;
675 }
676 }
677
678 K_OUT();
679 *out = '\0';
680
681 if (error)
682 *error = error_;
683
684 return outbuf;
685}
686
687static gchar *conv_sjistoeuc(const gchar *inbuf, gint *error)
688{
689 gchar *outbuf;
690 const guchar *in = (guchar *)inbuf;
691 guchar *out;
692 gint error_ = 0;
693
694 outbuf = g_malloc(strlen(inbuf) * 2 + 1);
695 out = (guchar *)outbuf;
696
697 while (*in != '\0') {
698 if (isascii(*in)) {
699 *out++ = *in++;
700 } else if (issjiskanji1(*in)) {
701 if (issjiskanji2(*(in + 1))) {
702 guchar out1 = *in;
703 guchar out2 = *(in + 1);
704 guchar row;
705
706 row = out1 < 0xa0 ? 0x70 : 0xb0;
707 if (out2 < 0x9f) {
708 out1 = (out1 - row) * 2 - 1;
709 out2 -= out2 > 0x7f ? 0x20 : 0x1f;
710 } else {
711 out1 = (out1 - row) * 2;
712 out2 -= 0x7e;
713 }
714
715 *out++ = out1 | 0x80;
716 *out++ = out2 | 0x80;
717 in += 2;
718 } else {
719 error_ = -1;
720 *out++ = SUBST_CHAR;
721 in++;
722 if (*in != '\0' && !isascii(*in)) {
723 *out++ = SUBST_CHAR;
724 in++;
725 }
726 }
727 } else if (issjishwkana(*in)) {
728 *out++ = SS2;
729 *out++ = *in++;
730 } else if (issjisext(*in)) {
731 error_ = -1;
732 *out++ = SUBST_CHAR;
733 in++;
734 if (*in != '\0' && !isascii(*in)) {
735 *out++ = SUBST_CHAR;
736 in++;
737 }
738 } else {
739 error_ = -1;
740 *out++ = SUBST_CHAR;
741 in++;
742 }
743 }
744
745 *out = '\0';
746
747 if (error)
748 *error = error_;
749
750 return outbuf;
751}
752
753static gchar *conv_jistoutf8(const gchar *inbuf, gint *error)
754{
755 gchar *tmpstr, *utf8str;
756 gint t_error = 0, u_error = 0;
757
758 if (strstr(inbuf, "\033$(D")) {
759 tmpstr = conv_jistoeuc(inbuf, &t_error);
760 utf8str = conv_euctoutf8(tmpstr, &u_error);
761 } else {
762 tmpstr = conv_jistosjis(inbuf, &t_error);
763 utf8str = conv_sjistoutf8(tmpstr, &u_error);
764 }
765 g_free(tmpstr);
766
767 if (error)
768 *error = (t_error | u_error);
769
770 return utf8str;
771}
772
773#if USE_THREADS
774#define S_LOCK_DEFINE_STATIC(name) G_LOCK_DEFINE_STATIC(name)
775#define S_LOCK(name) G_LOCK(name)
776#define S_UNLOCK(name) G_UNLOCK(name)
777#else
778#define S_LOCK_DEFINE_STATIC(name)
779#define S_LOCK(name)
780#define S_UNLOCK(name)
781#endif
782
783static gchar *conv_sjistoutf8(const gchar *inbuf, gint *error)
784{
785 static iconv_t cd = (iconv_t)-1;
786 static gboolean iconv_ok = TRUE;
787 S_LOCK_DEFINE_STATIC(cd);
788 gchar *ret;
789
790 S_LOCK(cd);
791
792 if (cd == (iconv_t)-1) {
793 if (!iconv_ok) {
794 S_UNLOCK(cd);
795 if (error)
796 *error = -1;
797 return g_strdup(inbuf);
798 }
799
800 cd = iconv_open(CS_UTF_8, CS_CP932);
801 if (cd == (iconv_t)-1) {
802 cd = iconv_open(CS_UTF_8, CS_SHIFT_JIS);
803 if (cd == (iconv_t)-1) {
804 g_warning("conv_sjistoutf8(): %s\n",
805 g_strerror(errno));
806 iconv_ok = FALSE;
807 S_UNLOCK(cd);
808 if (error)
809 *error = -1;
810 return g_strdup(inbuf);
811 }
812 }
813 }
814
815 ret = conv_iconv_strdup_with_cd(inbuf, cd, error);
816 S_UNLOCK(cd);
817 return ret;
818}
819
820static gchar *conv_euctoutf8(const gchar *inbuf, gint *error)
821{
822 static iconv_t cd = (iconv_t)-1;
823 static gboolean iconv_ok = TRUE;
824 S_LOCK_DEFINE_STATIC(cd);
825 gchar *ret;
826
827 S_LOCK(cd);
828
829 if (cd == (iconv_t)-1) {
830 if (!iconv_ok) {
831 S_UNLOCK(cd);
832 if (error)
833 *error = -1;
834 return g_strdup(inbuf);
835 }
836
837 cd = iconv_open(CS_UTF_8, CS_EUC_JP_MS);
838 if (cd == (iconv_t)-1) {
839 cd = iconv_open(CS_UTF_8, CS_EUC_JP);
840 if (cd == (iconv_t)-1) {
841 g_warning("conv_euctoutf8(): %s\n",
842 g_strerror(errno));
843 iconv_ok = FALSE;
844 S_UNLOCK(cd);
845 if (error)
846 *error = -1;
847 return g_strdup(inbuf);
848 }
849 }
850 }
851
852 ret = conv_iconv_strdup_with_cd(inbuf, cd, error);
853 S_UNLOCK(cd);
854 return ret;
855}
856
857static gchar *conv_anytoutf8(const gchar *inbuf, gint *error)
858{
859 switch (conv_guess_ja_encoding(inbuf)) {
860 case C_ISO_2022_JP:
861 return conv_jistoutf8(inbuf, error);
862 case C_SHIFT_JIS:
863 return conv_sjistoutf8(inbuf, error);
864 case C_EUC_JP:
865 return conv_euctoutf8(inbuf, error);
866 case C_UTF_8:
867 if (error)
868 *error = 0;
869 if (isutf8bom(inbuf))
870 inbuf += 3;
871 return g_strdup(inbuf);
872 default:
873 if (error)
874 *error = 0;
875 return g_strdup(inbuf);
876 }
877}
878
879static gchar *conv_utf8tosjis(const gchar *inbuf, gint *error)
880{
881 static iconv_t cd = (iconv_t)-1;
882 static gboolean iconv_ok = TRUE;
883 S_LOCK_DEFINE_STATIC(cd);
884 gchar *ret;
885
886 S_LOCK(cd);
887
888 if (cd == (iconv_t)-1) {
889 if (!iconv_ok) {
890 S_UNLOCK(cd);
891 if (error)
892 *error = -1;
893 return g_strdup(inbuf);
894 }
895
896 cd = iconv_open(CS_CP932, CS_UTF_8);
897 if (cd == (iconv_t)-1) {
898 cd = iconv_open(CS_SHIFT_JIS, CS_UTF_8);
899 if (cd == (iconv_t)-1) {
900 g_warning("conv_utf8tosjis(): %s\n",
901 g_strerror(errno));
902 iconv_ok = FALSE;
903 S_UNLOCK(cd);
904 if (error)
905 *error = -1;
906 return g_strdup(inbuf);
907 }
908 }
909 }
910
911 if (isutf8bom(inbuf))
912 inbuf += 3;
913 ret = conv_iconv_strdup_with_cd(inbuf, cd, error);
914 S_UNLOCK(cd);
915 return ret;
916}
917
918static gchar *conv_utf8toeuc(const gchar *inbuf, gint *error)
919{
920 static iconv_t cd = (iconv_t)-1;
921 static gboolean iconv_ok = TRUE;
922 S_LOCK_DEFINE_STATIC(cd);
923 gchar *ret;
924
925 S_LOCK(cd);
926
927 if (cd == (iconv_t)-1) {
928 if (!iconv_ok) {
929 S_UNLOCK(cd);
930 if (error)
931 *error = -1;
932 return g_strdup(inbuf);
933 }
934
935 cd = iconv_open(CS_EUC_JP_MS, CS_UTF_8);
936 if (cd == (iconv_t)-1) {
937 cd = iconv_open(CS_EUC_JP, CS_UTF_8);
938 if (cd == (iconv_t)-1) {
939 g_warning("conv_utf8toeuc(): %s\n",
940 g_strerror(errno));
941 iconv_ok = FALSE;
942 S_UNLOCK(cd);
943 if (error)
944 *error = -1;
945 return g_strdup(inbuf);
946 }
947 }
948 }
949
950 if (isutf8bom(inbuf))
951 inbuf += 3;
952 ret = conv_iconv_strdup_with_cd(inbuf, cd, error);
953 S_UNLOCK(cd);
954 return ret;
955}
956
957static gchar *conv_utf8tojis(const gchar *inbuf, gint *error)
958{
959 gchar *tmpstr, *jisstr;
960 gint t_error = 0, j_error = 0;
961
962#if 1
963 tmpstr = conv_utf8tosjis(inbuf, &t_error);
964 jisstr = conv_sjistojis(tmpstr, &j_error);
965#else
966 tmpstr = conv_utf8toeuc(inbuf, &t_error);
967 jisstr = conv_euctojis(tmpstr, &j_error);
968#endif
969 g_free(tmpstr);
970
971 if (error)
972 *error = (t_error | j_error);
973
974 return jisstr;
975}
976
977#if 0
978static gchar valid_eucjp_tbl[][96] = {
979 /* 0xa2a0 - 0xa2ff */
980 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
981 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,
982 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,
983 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
984 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
985 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0 },
986
987 /* 0xa3a0 - 0xa3ff */
988 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
989 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
990 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
991 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
992 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
993 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 },
994
995 /* 0xa4a0 - 0xa4ff */
996 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
997 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
998 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
999 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1000 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1001 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
1002
1003 /* 0xa5a0 - 0xa5ff */
1004 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1005 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1006 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1007 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1008 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1009 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
1010
1011 /* 0xa6a0 - 0xa6ff */
1012 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1013 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
1014 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1015 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
1016 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1017 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
1018
1019 /* 0xa7a0 - 0xa7ff */
1020 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1021 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1022 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1023 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1024 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1025 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
1026
1027 /* 0xa8a0 - 0xa8ff */
1028 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1029 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1030 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1031 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1032 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1033 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
1034};
1035
1036static gboolean isprintableeuckanji(guchar c1, guchar c2)
1037{
1038 if (c1 <= 0xa0 || c1 >= 0xf5)
1039 return FALSE;
1040 if (c2 <= 0xa0 || c2 == 0xff)
1041 return FALSE;
1042
1043 if (c1 >= 0xa9 && c1 <= 0xaf)
1044 return FALSE;
1045
1046 if (c1 >= 0xa2 && c1 <= 0xa8)
1047 return (gboolean)valid_eucjp_tbl[c1 - 0xa2][c2 - 0xa0];
1048
1049 if (c1 == 0xcf) {
1050 if (c2 >= 0xd4 && c2 <= 0xfe)
1051 return FALSE;
1052 } else if (c1 == 0xf4) {
1053 if (c2 >= 0xa7 && c2 <= 0xfe)
1054 return FALSE;
1055 }
1056
1057 return TRUE;
1058}
1059
1060static void conv_unreadable_eucjp(gchar *str)
1061{
1062 register guchar *p = str;
1063
1064 while (*p != '\0') {
1065 if (isascii(*p)) {
1066 /* convert CR+LF -> LF */
1067 if (*p == '\r' && *(p + 1) == '\n')
1068 memmove(p, p + 1, strlen(p));
1069 /* printable 7 bit code */
1070 p++;
1071 } else if (iseuckanji(*p)) {
1072 if (isprintableeuckanji(*p, *(p + 1))) {
1073 /* printable euc-jp code */
1074 p += 2;
1075 } else {
1076 /* substitute unprintable code */
1077 *p++ = SUBST_CHAR;
1078 if (*p != '\0') {
1079 if (isascii(*p))
1080 p++;
1081 else
1082 *p++ = SUBST_CHAR;
1083 }
1084 }
1085 } else if (iseuchwkana1(*p)) {
1086 if (iseuchwkana2(*(p + 1)))
1087 /* euc-jp hankaku kana */
1088 p += 2;
1089 else
1090 *p++ = SUBST_CHAR;
1091 } else if (iseucaux(*p)) {
1092 if (iseuckanji(*(p + 1)) && iseuckanji(*(p + 2))) {
1093 /* auxiliary kanji */
1094 p += 3;
1095 } else
1096 *p++ = SUBST_CHAR;
1097 } else
1098 /* substitute unprintable 1 byte code */
1099 *p++ = SUBST_CHAR;
1100 }
1101}
1102#endif
1103
1104static void conv_unreadable_8bit(gchar *str)
1105{
1106 register gchar *p = str;
1107
1108 while (*p != '\0') {
1109 /* convert CR+LF -> LF */
1110 if (*p == '\r' && *(p + 1) == '\n')
1111 memmove(p, p + 1, strlen(p));
1112 else if (!isascii(*(guchar *)p)) *p = SUBST_CHAR;
1113 p++;
1114 }
1115}
1116
1117#if 0
1118static void conv_unreadable_latin(gchar *str)
1119{
1120 register guchar *p = str;
1121
1122 while (*p != '\0') {
1123 /* convert CR+LF -> LF */
1124 if (*p == '\r' && *(p + 1) == '\n')
1125 memmove(p, p + 1, strlen(p));
1126 else if ((*p & 0xff) >= 0x7f && (*p & 0xff) <= 0x9f)
1127 *p = SUBST_CHAR;
1128 p++;
1129 }
1130}
1131#endif
1132
1133#define NCV '\0'
1134
1135void conv_mb_alnum(gchar *str)
1136{
1137 static guchar char_tbl[] = {
1138 /* 0xa0 - 0xaf */
1139 NCV, ' ', NCV, NCV, ',', '.', NCV, ':',
1140 ';', '?', '!', NCV, NCV, NCV, NCV, NCV,
1141 /* 0xb0 - 0xbf */
1142 NCV, NCV, NCV, NCV, NCV, NCV, NCV, NCV,
1143 NCV, NCV, NCV, NCV, NCV, NCV, NCV, NCV,
1144 /* 0xc0 - 0xcf */
1145 NCV, NCV, NCV, NCV, NCV, NCV, NCV, NCV,
1146 NCV, NCV, '(', ')', NCV, NCV, '[', ']',
1147 /* 0xd0 - 0xdf */
1148 '{', '}', NCV, NCV, NCV, NCV, NCV, NCV,
1149 NCV, NCV, NCV, NCV, '+', '-', NCV, NCV,
1150 /* 0xe0 - 0xef */
1151 NCV, '=', NCV, '<', '>', NCV, NCV, NCV,
1152 NCV, NCV, NCV, NCV, NCV, NCV, NCV, NCV
1153 };
1154
1155 register guchar *p = (guchar *)str;
1156 register gint len;
1157
1158 len = strlen(str);
1159
1160 while (len > 1) {
1161 if (*p == 0xa3) {
1162 register guchar ch = *(p + 1);
1163
1164 if (ch >= 0xb0 && ch <= 0xfa) {
1165 /* [a-zA-Z] */
1166 *p = ch & 0x7f;
1167 p++;
1168 len--;
1169 memmove(p, p + 1, len);
1170 len--;
1171 } else {
1172 p += 2;
1173 len -= 2;
1174 }
1175 } else if (*p == 0xa1) {
1176 register guchar ch = *(p + 1);
1177
1178 if (ch >= 0xa0 && ch <= 0xef &&
1179 NCV != char_tbl[ch - 0xa0]) {
1180 *p = char_tbl[ch - 0xa0];
1181 p++;
1182 len--;
1183 memmove(p, p + 1, len);
1184 len--;
1185 } else {
1186 p += 2;
1187 len -= 2;
1188 }
1189 } else if (iseuckanji(*p)) {
1190 p += 2;
1191 len -= 2;
1192 } else {
1193 p++;
1194 len--;
1195 }
1196 }
1197}
1198
1199CharSet conv_guess_ja_encoding(const gchar *str)
1200{
1201 const guchar *p = (const guchar *)str;
1202 CharSet guessed = C_US_ASCII;
1203
1204 while (*p != '\0') {
1205 if (*p == ESC && (*(p + 1) == '$' || *(p + 1) == '(')) {
1206 if (guessed == C_US_ASCII)
1207 return C_ISO_2022_JP;
1208 p += 2;
1209 } else if (isascii(*p)) {
1210 p++;
1211 } else if (iseuckanji(*p) && iseuckanji(*(p + 1))) {
1212 if (*p >= 0xfd && *p <= 0xfe)
1213 return C_EUC_JP;
1214 else if (guessed == C_SHIFT_JIS) {
1215 if ((issjiskanji1(*p) &&
1216 issjiskanji2(*(p + 1))) ||
1217 issjishwkana(*p))
1218 guessed = C_SHIFT_JIS;
1219 else
1220 guessed = C_EUC_JP;
1221 } else
1222 guessed = C_EUC_JP;
1223 p += 2;
1224 } else if (issjiskanji1(*p) && issjiskanji2(*(p + 1))) {
1225 guessed = C_SHIFT_JIS;
1226 p += 2;
1227 } else if (issjishwkana(*p)) {
1228 guessed = C_SHIFT_JIS;
1229 p++;
1230 } else {
1231 if (guessed == C_US_ASCII)
1232 guessed = C_AUTO;
1233 p++;
1234 }
1235 }
1236
1237 if (guessed != C_US_ASCII) {
1238 p = (const guchar *)str;
1239
1240 while (*p != '\0') {
1241 if (isascii(*p)) {
1242 p++;
1243 } else if (isutf8_3_1(*p) &&
1244 isutf8_3_2(*(p + 1)) &&
1245 isutf8_3_2(*(p + 2))) {
1246 p += 3;
1247 } else {
1248 return guessed;
1249 }
1250 }
1251
1252 return C_UTF_8;
1253 }
1254
1255 return guessed;
1256}
1257
1258static gchar *conv_jistodisp(const gchar *inbuf, gint *error)
1259{
1260 return conv_jistoutf8(inbuf, error);
1261}
1262
1263static gchar *conv_sjistodisp(const gchar *inbuf, gint *error)
1264{
1265 return conv_sjistoutf8(inbuf, error);
1266}
1267
1268static gchar *conv_euctodisp(const gchar *inbuf, gint *error)
1269{
1270 return conv_euctoutf8(inbuf, error);
1271}
1272
1273gchar *conv_utf8todisp(const gchar *inbuf, gint *error)
1274{
1275 if (g_utf8_validate(inbuf, -1, NULL) == TRUE) {
1276 if (error)
1277 *error = 0;
1278 if (isutf8bom(inbuf))
1279 inbuf += 3;
1280 return g_strdup(inbuf);
1281 } else
1282 return conv_ustodisp(inbuf, error);
1283}
1284
1285static gchar *conv_anytodisp(const gchar *inbuf, gint *error)
1286{
1287 gchar *outbuf;
1288
1289 outbuf = conv_anytoutf8(inbuf, error);
1290 if (g_utf8_validate(outbuf, -1, NULL) != TRUE) {
1291 if (error)
1292 *error = -1;
1293 conv_unreadable_8bit(outbuf);
1294 }
1295
1296 return outbuf;
1297}
1298
1299static gchar *conv_ustodisp(const gchar *inbuf, gint *error)
1300{
1301 gchar *outbuf;
1302
1303 outbuf = g_strdup(inbuf);
1304 conv_unreadable_8bit(outbuf);
1305 if (error)
1306 *error = 0;
1307
1308 return outbuf;
1309}
1310
1311gchar *conv_localetodisp(const gchar *inbuf, gint *error)
1312{
1313 gchar *str;
1314
1315 str = conv_iconv_strdup(inbuf, conv_get_locale_charset_str(),
1316 CS_INTERNAL, error);
1317 if (!str)
1318 str = conv_utf8todisp(inbuf, NULL);
1319
1320 return str;
1321}
1322
1323static gchar *conv_noconv(const gchar *inbuf, gint *error)
1324{
1325 if (error)
1326 *error = 0;
1327 return g_strdup(inbuf);
1328}
1329
1330static const gchar *
1331conv_get_fallback_for_private_encoding(const gchar *encoding)
1332{
1333 if (encoding) {
1334 if ((encoding[0] == 'X' || encoding[0] == 'x') &&
1335 encoding[1] == '-') {
1336 if (!g_ascii_strcasecmp(encoding, CS_X_GBK))
1337 return CS_GBK;
1338 else if (!g_ascii_strcasecmp(encoding, CS_X_SJIS))
1339 return CS_SHIFT_JIS;
1340 } else if ((encoding[0] == 'K' || encoding[0] == 'k') &&
1341 (encoding[1] == 'S' || encoding[1] == 's')) {
1342 if (!g_ascii_strcasecmp(encoding, CS_KS_C_5601_1987))
1343 return CS_EUC_KR;
1344 }
1345 }
1346
1347 return encoding;
1348}
1349
1350CodeConverter *conv_code_converter_new(const gchar *src_encoding,
1351 const gchar *dest_encoding)
1352{
1353 CodeConverter *conv;
1354
1355 src_encoding = conv_get_fallback_for_private_encoding(src_encoding);
1356
1357 conv = g_new0(CodeConverter, 1);
1358 conv->code_conv_func =
1359 conv_get_code_conv_func(src_encoding, dest_encoding);
1360 conv->src_encoding = g_strdup(src_encoding);
1361 conv->dest_encoding = g_strdup(dest_encoding);
1362
1363 return conv;
1364}
1365
1366void conv_code_converter_destroy(CodeConverter *conv)
1367{
1368 g_free(conv->src_encoding);
1369 g_free(conv->dest_encoding);
1370 g_free(conv);
1371}
1372
1373gchar *conv_convert(CodeConverter *conv, const gchar *inbuf)
1374{
1375 if (!inbuf)
1376 return NULL;
1377 else if (conv->code_conv_func != conv_noconv)
1378 return conv->code_conv_func(inbuf, NULL);
1379 else
1380 return conv_iconv_strdup
1381 (inbuf, conv->src_encoding, conv->dest_encoding, NULL);
1382}
1383
1384gchar *conv_codeset_strdup_full(const gchar *inbuf,
1385 const gchar *src_encoding,
1386 const gchar *dest_encoding,
1387 gint *error)
1388{
1389 CodeConvFunc conv_func;
1390
1391 if (!inbuf) {
1392 if (error)
1393 *error = 0;
1394 return NULL;
1395 }
1396
1397 src_encoding = conv_get_fallback_for_private_encoding(src_encoding);
1398
1399 conv_func = conv_get_code_conv_func(src_encoding, dest_encoding);
1400 if (conv_func != conv_noconv)
1401 return conv_func(inbuf, error);
1402
1403 return conv_iconv_strdup(inbuf, src_encoding, dest_encoding, error);
1404}
1405
1406CodeConvFunc conv_get_code_conv_func(const gchar *src_encoding,
1407 const gchar *dest_encoding)
1408{
1409 CodeConvFunc code_conv = conv_noconv;
1410 CharSet src_charset;
1411 CharSet dest_charset;
1412
1413 if (!src_encoding)
1414 src_charset = conv_get_locale_charset();
1415 else
1416 src_charset = conv_get_charset_from_str(src_encoding);
1417
1418 /* auto detection mode */
1419 if (!src_encoding && !dest_encoding) {
1420 if (conv_ad_type == C_AD_JAPANESE ||
1421 (conv_ad_type == C_AD_BY_LOCALE && conv_is_ja_locale()))
1422 return conv_anytodisp;
1423 else
1424 return conv_noconv;
1425 }
1426
1427 dest_charset = conv_get_charset_from_str(dest_encoding);
1428
1429 if (dest_charset == C_US_ASCII)
1430 return conv_ustodisp;
1431
1432 switch (src_charset) {
1433 case C_US_ASCII:
1434 case C_ISO_8859_1:
1435 case C_ISO_8859_2:
1436 case C_ISO_8859_3:
1437 case C_ISO_8859_4:
1438 case C_ISO_8859_5:
1439 case C_ISO_8859_6:
1440 case C_ISO_8859_7:
1441 case C_ISO_8859_8:
1442 case C_ISO_8859_9:
1443 case C_ISO_8859_10:
1444 case C_ISO_8859_11:
1445 case C_ISO_8859_13:
1446 case C_ISO_8859_14:
1447 case C_ISO_8859_15:
1448 case C_ISO_8859_16:
1449 break;
1450 case C_ISO_2022_JP:
1451 case C_ISO_2022_JP_2:
1452 case C_ISO_2022_JP_3:
1453 if (dest_charset == C_AUTO)
1454 code_conv = conv_jistodisp;
1455 else if (dest_charset == C_EUC_JP)
1456 code_conv = conv_jistoeuc;
1457 else if (dest_charset == C_SHIFT_JIS ||
1458 dest_charset == C_CP932)
1459 code_conv = conv_jistosjis;
1460 else if (dest_charset == C_UTF_8)
1461 code_conv = conv_jistoutf8;
1462 break;
1463 case C_SHIFT_JIS:
1464 case C_CP932:
1465 if (dest_charset == C_AUTO)
1466 code_conv = conv_sjistodisp;
1467 else if (dest_charset == C_ISO_2022_JP ||
1468 dest_charset == C_ISO_2022_JP_2 ||
1469 dest_charset == C_ISO_2022_JP_3)
1470 code_conv = conv_sjistojis;
1471 else if (dest_charset == C_EUC_JP)
1472 code_conv = conv_sjistoeuc;
1473 else if (dest_charset == C_UTF_8)
1474 code_conv = conv_sjistoutf8;
1475 break;
1476 case C_EUC_JP:
1477 if (dest_charset == C_AUTO)
1478 code_conv = conv_euctodisp;
1479 else if (dest_charset == C_ISO_2022_JP ||
1480 dest_charset == C_ISO_2022_JP_2 ||
1481 dest_charset == C_ISO_2022_JP_3)
1482 code_conv = conv_euctojis;
1483 else if (dest_charset == C_UTF_8)
1484 code_conv = conv_euctoutf8;
1485 break;
1486 case C_UTF_8:
1487 if (dest_charset == C_EUC_JP)
1488 code_conv = conv_utf8toeuc;
1489 else if (dest_charset == C_ISO_2022_JP ||
1490 dest_charset == C_ISO_2022_JP_2 ||
1491 dest_charset == C_ISO_2022_JP_3)
1492 code_conv = conv_utf8tojis;
1493 else if (dest_charset == C_SHIFT_JIS ||
1494 dest_charset == C_CP932)
1495 code_conv = conv_utf8tosjis;
1496 break;
1497 default:
1498 break;
1499 }
1500
1501 return code_conv;
1502}
1503
1504gchar *conv_iconv_strdup(const gchar *inbuf,
1505 const gchar *src_code, const gchar *dest_code,
1506 gint *error)
1507{
1508 iconv_t cd;
1509 gchar *outbuf;
1510
1511 if (!src_code)
1512 src_code = conv_get_locale_charset_str();
1513 if (!dest_code)
1514 dest_code = CS_INTERNAL;
1515
1516 cd = iconv_open(dest_code, src_code);
1517 if (cd == (iconv_t)-1) {
1518 if (error)
1519 *error = -1;
1520 return NULL;
1521 }
1522
1523 outbuf = conv_iconv_strdup_with_cd(inbuf, cd, error);
1524
1525 iconv_close(cd);
1526
1527 return outbuf;
1528}
1529
1530gchar *conv_iconv_strdup_with_cd(const gchar *inbuf, iconv_t cd, gint *error)
1531{
1532 const gchar *inbuf_p;
1533 gchar *outbuf;
1534 gchar *outbuf_p;
1535 size_t in_size;
1536 size_t in_left;
1537 size_t out_size;
1538 size_t out_left;
1539 size_t n_conv;
1540 size_t len;
1541 gint error_ = 0;
1542
1543 if (!inbuf) {
1544 if (error)
1545 *error = 0;
1546 return NULL;
1547 }
1548
1549 inbuf_p = inbuf;
1550 in_size = strlen(inbuf);
1551 in_left = in_size;
1552 out_size = (in_size + 1) * 2;
1553 outbuf = g_malloc(out_size);
1554 outbuf_p = outbuf;
1555 out_left = out_size;
1556
1557#define EXPAND_BUF() \
1558{ \
1559 len = outbuf_p - outbuf; \
1560 out_size *= 2; \
1561 outbuf = g_realloc(outbuf, out_size); \
1562 outbuf_p = outbuf + len; \
1563 out_left = out_size - len; \
1564}
1565
1566 while ((n_conv = iconv(cd, (ICONV_CONST gchar **)&inbuf_p, &in_left,
1567 &outbuf_p, &out_left)) == (size_t)-1) {
1568 if (EILSEQ == errno) {
1569 /* g_print("iconv(): at %d: %s\n", in_size - in_left, g_strerror(errno)); */
1570 error_ = -1;
1571 inbuf_p++;
1572 in_left--;
1573 if (out_left == 0) {
1574 EXPAND_BUF();
1575 }
1576 *outbuf_p++ = SUBST_CHAR;
1577 out_left--;
1578 } else if (EINVAL == errno) {
1579 error_ = -1;
1580 break;
1581 } else if (E2BIG == errno) {
1582 EXPAND_BUF();
1583 } else {
1584 g_warning("conv_iconv_strdup(): %s\n",
1585 g_strerror(errno));
1586 error_ = -1;
1587 break;
1588 }
1589 }
1590
1591 while ((n_conv = iconv(cd, NULL, NULL, &outbuf_p, &out_left)) ==
1592 (size_t)-1) {
1593 if (E2BIG == errno) {
1594 EXPAND_BUF();
1595 } else {
1596 g_warning("conv_iconv_strdup(): %s\n",
1597 g_strerror(errno));
1598 error_ = -1;
1599 break;
1600 }
1601 }
1602
1603#undef EXPAND_BUF
1604
1605 len = outbuf_p - outbuf;
1606 outbuf = g_realloc(outbuf, len + 1);
1607 outbuf[len] = '\0';
1608
1609 if (error)
1610 *error = error_;
1611
1612 return outbuf;
1613}
1614
1615static const struct {
1616 CharSet charset;
1617 gchar *const name;
1618} charsets[] = {
1619 {C_US_ASCII, CS_US_ASCII},
1620 {C_US_ASCII, CS_ANSI_X3_4_1968},
1621 {C_UTF_8, CS_UTF_8},
1622 {C_UTF_7, CS_UTF_7},
1623 {C_ISO_8859_1, CS_ISO_8859_1},
1624 {C_ISO_8859_2, CS_ISO_8859_2},
1625 {C_ISO_8859_3, CS_ISO_8859_3},
1626 {C_ISO_8859_4, CS_ISO_8859_4},
1627 {C_ISO_8859_5, CS_ISO_8859_5},
1628 {C_ISO_8859_6, CS_ISO_8859_6},
1629 {C_ISO_8859_7, CS_ISO_8859_7},
1630 {C_ISO_8859_8, CS_ISO_8859_8},
1631 {C_ISO_8859_9, CS_ISO_8859_9},
1632 {C_ISO_8859_10, CS_ISO_8859_10},
1633 {C_ISO_8859_11, CS_ISO_8859_11},
1634 {C_ISO_8859_13, CS_ISO_8859_13},
1635 {C_ISO_8859_14, CS_ISO_8859_14},
1636 {C_ISO_8859_15, CS_ISO_8859_15},
1637 {C_BALTIC, CS_BALTIC},
1638 {C_CP932, CS_CP932},
1639 {C_CP1250, CS_CP1250},
1640 {C_CP1251, CS_CP1251},
1641 {C_CP1252, CS_CP1252},
1642 {C_CP1253, CS_CP1253},
1643 {C_CP1254, CS_CP1254},
1644 {C_CP1255, CS_CP1255},
1645 {C_CP1256, CS_CP1256},
1646 {C_CP1257, CS_CP1257},
1647 {C_CP1258, CS_CP1258},
1648 {C_WINDOWS_932, CS_WINDOWS_932},
1649 {C_WINDOWS_1250, CS_WINDOWS_1250},
1650 {C_WINDOWS_1251, CS_WINDOWS_1251},
1651 {C_WINDOWS_1252, CS_WINDOWS_1252},
1652 {C_WINDOWS_1253, CS_WINDOWS_1253},
1653 {C_WINDOWS_1254, CS_WINDOWS_1254},
1654 {C_WINDOWS_1255, CS_WINDOWS_1255},
1655 {C_WINDOWS_1256, CS_WINDOWS_1256},
1656 {C_WINDOWS_1257, CS_WINDOWS_1257},
1657 {C_WINDOWS_1258, CS_WINDOWS_1258},
1658 {C_KOI8_R, CS_KOI8_R},
1659 {C_KOI8_T, CS_KOI8_T},
1660 {C_KOI8_U, CS_KOI8_U},
1661 {C_ISO_2022_JP, CS_ISO_2022_JP},
1662 {C_ISO_2022_JP_2, CS_ISO_2022_JP_2},
1663 {C_ISO_2022_JP_3, CS_ISO_2022_JP_3},
1664 {C_EUC_JP, CS_EUC_JP},
1665 {C_EUC_JP, CS_EUCJP},
1666 {C_EUC_JP_MS, CS_EUC_JP_MS},
1667 {C_SHIFT_JIS, CS_SHIFT_JIS},
1668 {C_SHIFT_JIS, CS_SHIFT__JIS},
1669 {C_SHIFT_JIS, CS_SJIS},
1670 {C_ISO_2022_KR, CS_ISO_2022_KR},
1671 {C_EUC_KR, CS_EUC_KR},
1672 {C_ISO_2022_CN, CS_ISO_2022_CN},
1673 {C_EUC_CN, CS_EUC_CN},
1674 {C_GB2312, CS_GB2312},
1675 {C_GBK, CS_GBK},
1676 {C_EUC_TW, CS_EUC_TW},
1677 {C_BIG5, CS_BIG5},
1678 {C_BIG5_HKSCS, CS_BIG5_HKSCS},
1679 {C_TIS_620, CS_TIS_620},
1680 {C_WINDOWS_874, CS_WINDOWS_874},
1681 {C_GEORGIAN_PS, CS_GEORGIAN_PS},
1682 {C_TCVN5712_1, CS_TCVN5712_1},
1683 {C_ISO_8859_16, CS_ISO_8859_16},
1684 {C_UTF_16, CS_UTF_16},
1685 {C_UTF_16BE, CS_UTF_16BE},
1686 {C_UTF_16LE, CS_UTF_16LE},
1687};
1688
1689static const struct {
1690 gchar *const locale;
1691 CharSet charset;
1692 CharSet out_charset;
1693} locale_table[] = {
1694 {"ja_JP.eucJP" , C_EUC_JP , C_ISO_2022_JP},
1695 {"ja_JP.EUC-JP" , C_EUC_JP , C_ISO_2022_JP},
1696 {"ja_JP.EUC" , C_EUC_JP , C_ISO_2022_JP},
1697 {"ja_JP.ujis" , C_EUC_JP , C_ISO_2022_JP},
1698 {"ja_JP.SJIS" , C_SHIFT_JIS , C_ISO_2022_JP},
1699 {"ja_JP.JIS" , C_ISO_2022_JP , C_ISO_2022_JP},
1700#ifdef G_OS_WIN32
1701 {"ja_JP" , C_CP932 , C_ISO_2022_JP},
1702#elif defined(__APPLE__)
1703 {"ja_JP" , C_UTF_8 , C_ISO_2022_JP},
1704#else
1705 {"ja_JP" , C_EUC_JP , C_ISO_2022_JP},
1706#endif
1707 {"ko_KR.EUC-KR" , C_EUC_KR , C_EUC_KR},
1708 {"ko_KR" , C_EUC_KR , C_EUC_KR},
1709 {"zh_CN.GB2312" , C_GB2312 , C_GB2312},
1710 {"zh_CN.GBK" , C_GBK , C_GBK},
1711 {"zh_CN" , C_GB2312 , C_GB2312},
1712 {"zh_HK" , C_BIG5_HKSCS , C_BIG5_HKSCS},
1713 {"zh_TW.eucTW" , C_EUC_TW , C_BIG5},
1714 {"zh_TW.EUC-TW" , C_EUC_TW , C_BIG5},
1715 {"zh_TW.Big5" , C_BIG5 , C_BIG5},
1716 {"zh_TW" , C_BIG5 , C_BIG5},
1717
1718 {"ru_RU.KOI8-R" , C_KOI8_R , C_KOI8_R},
1719 {"ru_RU.KOI8R" , C_KOI8_R , C_KOI8_R},
1720 {"ru_RU.CP1251" , C_WINDOWS_1251, C_KOI8_R},
1721 {"ru_RU" , C_ISO_8859_5 , C_KOI8_R},
1722 {"tg_TJ" , C_KOI8_T , C_KOI8_T},
1723 {"ru_UA" , C_KOI8_U , C_KOI8_U},
1724 {"uk_UA.CP1251" , C_WINDOWS_1251, C_KOI8_U},
1725 {"uk_UA" , C_KOI8_U , C_KOI8_U},
1726
1727 {"be_BY" , C_WINDOWS_1251, C_WINDOWS_1251},
1728 {"bg_BG" , C_WINDOWS_1251, C_WINDOWS_1251},
1729
1730 {"yi_US" , C_WINDOWS_1255, C_WINDOWS_1255},
1731
1732 {"af_ZA" , C_ISO_8859_1 , C_ISO_8859_1},
1733 {"br_FR" , C_ISO_8859_1 , C_ISO_8859_1},
1734 {"ca_ES" , C_ISO_8859_1 , C_ISO_8859_1},
1735 {"da_DK" , C_ISO_8859_1 , C_ISO_8859_1},
1736 {"de_AT" , C_ISO_8859_1 , C_ISO_8859_1},
1737 {"de_BE" , C_ISO_8859_1 , C_ISO_8859_1},
1738 {"de_CH" , C_ISO_8859_1 , C_ISO_8859_1},
1739 {"de_DE" , C_ISO_8859_1 , C_ISO_8859_1},
1740 {"de_LU" , C_ISO_8859_1 , C_ISO_8859_1},
1741 {"en_AU" , C_ISO_8859_1 , C_ISO_8859_1},
1742 {"en_BW" , C_ISO_8859_1 , C_ISO_8859_1},
1743 {"en_CA" , C_ISO_8859_1 , C_ISO_8859_1},
1744 {"en_DK" , C_ISO_8859_1 , C_ISO_8859_1},
1745 {"en_GB" , C_ISO_8859_1 , C_ISO_8859_1},
1746 {"en_HK" , C_ISO_8859_1 , C_ISO_8859_1},
1747 {"en_IE" , C_ISO_8859_1 , C_ISO_8859_1},
1748 {"en_NZ" , C_ISO_8859_1 , C_ISO_8859_1},
1749 {"en_PH" , C_ISO_8859_1 , C_ISO_8859_1},
1750 {"en_SG" , C_ISO_8859_1 , C_ISO_8859_1},
1751 {"en_US" , C_ISO_8859_1 , C_ISO_8859_1},
1752 {"en_ZA" , C_ISO_8859_1 , C_ISO_8859_1},
1753 {"en_ZW" , C_ISO_8859_1 , C_ISO_8859_1},
1754 {"es_AR" , C_ISO_8859_1 , C_ISO_8859_1},
1755 {"es_BO" , C_ISO_8859_1 , C_ISO_8859_1},
1756 {"es_CL" , C_ISO_8859_1 , C_ISO_8859_1},
1757 {"es_CO" , C_ISO_8859_1 , C_ISO_8859_1},
1758 {"es_CR" , C_ISO_8859_1 , C_ISO_8859_1},
1759 {"es_DO" , C_ISO_8859_1 , C_ISO_8859_1},
1760 {"es_EC" , C_ISO_8859_1 , C_ISO_8859_1},
1761 {"es_ES" , C_ISO_8859_1 , C_ISO_8859_1},
1762 {"es_GT" , C_ISO_8859_1 , C_ISO_8859_1},
1763 {"es_HN" , C_ISO_8859_1 , C_ISO_8859_1},
1764 {"es_MX" , C_ISO_8859_1 , C_ISO_8859_1},
1765 {"es_NI" , C_ISO_8859_1 , C_ISO_8859_1},
1766 {"es_PA" , C_ISO_8859_1 , C_ISO_8859_1},
1767 {"es_PE" , C_ISO_8859_1 , C_ISO_8859_1},
1768 {"es_PR" , C_ISO_8859_1 , C_ISO_8859_1},
1769 {"es_PY" , C_ISO_8859_1 , C_ISO_8859_1},
1770 {"es_SV" , C_ISO_8859_1 , C_ISO_8859_1},
1771 {"es_US" , C_ISO_8859_1 , C_ISO_8859_1},
1772 {"es_UY" , C_ISO_8859_1 , C_ISO_8859_1},
1773 {"es_VE" , C_ISO_8859_1 , C_ISO_8859_1},
1774 {"et_EE" , C_ISO_8859_1 , C_ISO_8859_1},
1775 {"eu_ES" , C_ISO_8859_1 , C_ISO_8859_1},
1776 {"fi_FI" , C_ISO_8859_1 , C_ISO_8859_1},
1777 {"fo_FO" , C_ISO_8859_1 , C_ISO_8859_1},
1778 {"fr_BE" , C_ISO_8859_1 , C_ISO_8859_1},
1779 {"fr_CA" , C_ISO_8859_1 , C_ISO_8859_1},
1780 {"fr_CH" , C_ISO_8859_1 , C_ISO_8859_1},
1781 {"fr_FR" , C_ISO_8859_1 , C_ISO_8859_1},
1782 {"fr_LU" , C_ISO_8859_1 , C_ISO_8859_1},
1783 {"ga_IE" , C_ISO_8859_1 , C_ISO_8859_1},
1784 {"gl_ES" , C_ISO_8859_1 , C_ISO_8859_1},
1785 {"gv_GB" , C_ISO_8859_1 , C_ISO_8859_1},
1786 {"id_ID" , C_ISO_8859_1 , C_ISO_8859_1},
1787 {"is_IS" , C_ISO_8859_1 , C_ISO_8859_1},
1788 {"it_CH" , C_ISO_8859_1 , C_ISO_8859_1},
1789 {"it_IT" , C_ISO_8859_1 , C_ISO_8859_1},
1790 {"kl_GL" , C_ISO_8859_1 , C_ISO_8859_1},
1791 {"kw_GB" , C_ISO_8859_1 , C_ISO_8859_1},
1792 {"ms_MY" , C_ISO_8859_1 , C_ISO_8859_1},
1793 {"nl_BE" , C_ISO_8859_1 , C_ISO_8859_1},
1794 {"nl_NL" , C_ISO_8859_1 , C_ISO_8859_1},
1795 {"nn_NO" , C_ISO_8859_1 , C_ISO_8859_1},
1796 {"no_NO" , C_ISO_8859_1 , C_ISO_8859_1},
1797 {"oc_FR" , C_ISO_8859_1 , C_ISO_8859_1},
1798 {"pt_BR" , C_ISO_8859_1 , C_ISO_8859_1},
1799 {"pt_PT" , C_ISO_8859_1 , C_ISO_8859_1},
1800 {"sq_AL" , C_ISO_8859_1 , C_ISO_8859_1},
1801 {"sv_FI" , C_ISO_8859_1 , C_ISO_8859_1},
1802 {"sv_SE" , C_ISO_8859_1 , C_ISO_8859_1},
1803 {"tl_PH" , C_ISO_8859_1 , C_ISO_8859_1},
1804 {"uz_UZ" , C_ISO_8859_1 , C_ISO_8859_1},
1805 {"wa_BE" , C_ISO_8859_1 , C_ISO_8859_1},
1806
1807 {"bs_BA" , C_ISO_8859_2 , C_ISO_8859_2},
1808 {"cs_CZ" , C_ISO_8859_2 , C_ISO_8859_2},
1809 {"hr_HR" , C_ISO_8859_2 , C_ISO_8859_2},
1810 {"hu_HU" , C_ISO_8859_2 , C_ISO_8859_2},
1811 {"pl_PL" , C_ISO_8859_2 , C_ISO_8859_2},
1812 {"ro_RO" , C_ISO_8859_2 , C_ISO_8859_2},
1813 {"sk_SK" , C_ISO_8859_2 , C_ISO_8859_2},
1814 {"sl_SI" , C_ISO_8859_2 , C_ISO_8859_2},
1815
1816 {"sr_YU@cyrillic" , C_ISO_8859_5 , C_ISO_8859_5},
1817 {"sr_YU" , C_ISO_8859_2 , C_ISO_8859_2},
1818
1819 {"mt_MT" , C_ISO_8859_3 , C_ISO_8859_3},
1820
1821 {"lt_LT.iso88594" , C_ISO_8859_4 , C_ISO_8859_4},
1822 {"lt_LT.ISO8859-4" , C_ISO_8859_4 , C_ISO_8859_4},
1823 {"lt_LT.ISO_8859-4" , C_ISO_8859_4 , C_ISO_8859_4},
1824 {"lt_LT" , C_ISO_8859_13 , C_ISO_8859_13},
1825
1826 {"mk_MK" , C_ISO_8859_5 , C_ISO_8859_5},
1827
1828 {"ar_AE" , C_ISO_8859_6 , C_ISO_8859_6},
1829 {"ar_BH" , C_ISO_8859_6 , C_ISO_8859_6},
1830 {"ar_DZ" , C_ISO_8859_6 , C_ISO_8859_6},
1831 {"ar_EG" , C_ISO_8859_6 , C_ISO_8859_6},
1832 {"ar_IQ" , C_ISO_8859_6 , C_ISO_8859_6},
1833 {"ar_JO" , C_ISO_8859_6 , C_ISO_8859_6},
1834 {"ar_KW" , C_ISO_8859_6 , C_ISO_8859_6},
1835 {"ar_LB" , C_ISO_8859_6 , C_ISO_8859_6},
1836 {"ar_LY" , C_ISO_8859_6 , C_ISO_8859_6},
1837 {"ar_MA" , C_ISO_8859_6 , C_ISO_8859_6},
1838 {"ar_OM" , C_ISO_8859_6 , C_ISO_8859_6},
1839 {"ar_QA" , C_ISO_8859_6 , C_ISO_8859_6},
1840 {"ar_SA" , C_ISO_8859_6 , C_ISO_8859_6},
1841 {"ar_SD" , C_ISO_8859_6 , C_ISO_8859_6},
1842 {"ar_SY" , C_ISO_8859_6 , C_ISO_8859_6},
1843 {"ar_TN" , C_ISO_8859_6 , C_ISO_8859_6},
1844 {"ar_YE" , C_ISO_8859_6 , C_ISO_8859_6},
1845
1846 {"el_GR" , C_ISO_8859_7 , C_ISO_8859_7},
1847 {"he_IL" , C_ISO_8859_8 , C_ISO_8859_8},
1848 {"iw_IL" , C_ISO_8859_8 , C_ISO_8859_8},
1849 {"tr_TR" , C_ISO_8859_9 , C_ISO_8859_9},
1850
1851 {"lv_LV" , C_ISO_8859_13 , C_ISO_8859_13},
1852 {"mi_NZ" , C_ISO_8859_13 , C_ISO_8859_13},
1853
1854 {"cy_GB" , C_ISO_8859_14 , C_ISO_8859_14},
1855
1856 {"ar_IN" , C_UTF_8 , C_UTF_8},
1857 {"en_IN" , C_UTF_8 , C_UTF_8},
1858 {"se_NO" , C_UTF_8 , C_UTF_8},
1859 {"ta_IN" , C_UTF_8 , C_UTF_8},
1860 {"te_IN" , C_UTF_8 , C_UTF_8},
1861 {"ur_PK" , C_UTF_8 , C_UTF_8},
1862
1863 {"th_TH" , C_TIS_620 , C_TIS_620},
1864 /* {"th_TH" , C_WINDOWS_874}, */
1865 /* {"th_TH" , C_ISO_8859_11}, */
1866
1867 {"ka_GE" , C_GEORGIAN_PS , C_GEORGIAN_PS},
1868 {"vi_VN.TCVN" , C_TCVN5712_1 , C_TCVN5712_1},
1869
1870 {"C" , C_US_ASCII , C_US_ASCII},
1871 {"POSIX" , C_US_ASCII , C_US_ASCII},
1872 {"ANSI_X3.4-1968" , C_US_ASCII , C_US_ASCII},
1873};
1874
1875static GHashTable *conv_get_charset_to_str_table(void)
1876{
1877 static GHashTable *table;
1878 gint i;
1879 S_LOCK_DEFINE_STATIC(table);
1880
1881 S_LOCK(table);
1882
1883 if (table) {
1884 S_UNLOCK(table);
1885 return table;
1886 }
1887
1888 table = g_hash_table_new(NULL, g_direct_equal);
1889
1890 for (i = 0; i < sizeof(charsets) / sizeof(charsets[0]); i++) {
1891 if (g_hash_table_lookup(table, GUINT_TO_POINTER(charsets[i].charset))
1892 == NULL) {
1893 g_hash_table_insert
1894 (table, GUINT_TO_POINTER(charsets[i].charset),
1895 charsets[i].name);
1896 }
1897 }
1898
1899 S_UNLOCK(table);
1900 return table;
1901}
1902
1903static GHashTable *conv_get_charset_from_str_table(void)
1904{
1905 static GHashTable *table;
1906 S_LOCK_DEFINE_STATIC(table);
1907
1908 gint i;
1909
1910 S_LOCK(table);
1911
1912 if (table) {
1913 S_UNLOCK(table);
1914 return table;
1915 }
1916
1917 table = g_hash_table_new(str_case_hash, str_case_equal);
1918
1919 for (i = 0; i < sizeof(charsets) / sizeof(charsets[0]); i++) {
1920 g_hash_table_insert(table, charsets[i].name,
1921 GUINT_TO_POINTER(charsets[i].charset));
1922 }
1923
1924 S_UNLOCK(table);
1925 return table;
1926}
1927
1928const gchar *conv_get_charset_str(CharSet charset)
1929{
1930 GHashTable *table;
1931
1932 table = conv_get_charset_to_str_table();
1933 return g_hash_table_lookup(table, GUINT_TO_POINTER(charset));
1934}
1935
1936CharSet conv_get_charset_from_str(const gchar *charset)
1937{
1938 GHashTable *table;
1939
1940 if (!charset) return C_AUTO;
1941
1942 table = conv_get_charset_from_str_table();
1943 return GPOINTER_TO_UINT(g_hash_table_lookup(table, charset));
1944}
1945
1946CharSet conv_get_locale_charset(void)
1947{
1948 static CharSet cur_charset = -1;
1949 const gchar *cur_locale;
1950 const gchar *p;
1951#if !defined(G_OS_WIN32) && !defined(__APPLE__)
1952 gint i;
1953#endif
1954 S_LOCK_DEFINE_STATIC(cur_charset);
1955
1956 S_LOCK(cur_charset);
1957
1958 if (cur_charset != -1) {
1959 S_UNLOCK(cur_charset);
1960 return cur_charset;
1961 }
1962
1963 cur_locale = conv_get_current_locale();
1964 if (!cur_locale) {
1965 cur_charset = C_US_ASCII;
1966 S_UNLOCK(cur_charset);
1967 return cur_charset;
1968 }
1969
1970 if (strcasestr(cur_locale, "UTF-8") || strcasestr(cur_locale, "utf8")) {
1971 cur_charset = C_UTF_8;
1972 S_UNLOCK(cur_charset);
1973 return cur_charset;
1974 }
1975
1976 if ((p = strcasestr(cur_locale, "@euro")) && p[5] == '\0') {
1977 cur_charset = C_ISO_8859_15;
1978 S_UNLOCK(cur_charset);
1979 return cur_charset;
1980 }
1981
1982#if defined(G_OS_WIN32) || defined(__APPLE__)
1983 cur_charset = conv_get_charset_from_str(conv_get_locale_charset_str());
1984
1985 S_UNLOCK(cur_charset);
1986 return cur_charset;
1987#else
1988 for (i = 0; i < sizeof(locale_table) / sizeof(locale_table[0]); i++) {
1989 const gchar *p;
1990
1991 /* "ja_JP.EUC" matches with "ja_JP.eucJP", "ja_JP.EUC" and
1992 "ja_JP". "ja_JP" matches with "ja_JP.xxxx" and "ja" */
1993 if (!g_ascii_strncasecmp(cur_locale, locale_table[i].locale,
1994 strlen(locale_table[i].locale))) {
1995 cur_charset = locale_table[i].charset;
1996 S_UNLOCK(cur_charset);
1997 return cur_charset;
1998 } else if ((p = strchr(locale_table[i].locale, '_')) &&
1999 !strchr(p + 1, '.')) {
2000 if (strlen(cur_locale) == 2 &&
2001 !g_ascii_strncasecmp(cur_locale,
2002 locale_table[i].locale, 2)) {
2003 cur_charset = locale_table[i].charset;
2004 S_UNLOCK(cur_charset);
2005 return cur_charset;
2006 }
2007 }
2008 }
2009
2010 cur_charset = C_AUTO;
2011 S_UNLOCK(cur_charset);
2012 return cur_charset;
2013#endif
2014}
2015
2016const gchar *conv_get_locale_charset_str(void)
2017{
2018 static const gchar *codeset = NULL;
2019 S_LOCK_DEFINE_STATIC(codeset);
2020
2021 S_LOCK(codeset);
2022
2023 if (!codeset) {
2024#if defined(G_OS_WIN32) || defined(__APPLE__)
2025 g_get_charset(&codeset);
2026 if (!strcmp(codeset, CS_US_ASCII) ||
2027 !strcmp(codeset, CS_ANSI_X3_4_1968))
2028 codeset = CS_INTERNAL;
2029#else
2030 codeset = conv_get_charset_str(conv_get_locale_charset());
2031#endif
2032 }
2033
2034 if (codeset) {
2035 S_UNLOCK(codeset);
2036 return codeset;
2037 }
2038
2039 S_UNLOCK(codeset);
2040 return CS_INTERNAL;
2041}
2042
2043CharSet conv_get_internal_charset(void)
2044{
2045 return C_INTERNAL;
2046}
2047
2048const gchar *conv_get_internal_charset_str(void)
2049{
2050 return CS_INTERNAL;
2051}
2052
2053CharSet conv_get_outgoing_charset(void)
2054{
2055 static CharSet out_charset = -1;
2056 const gchar *cur_locale;
2057 const gchar *p;
2058 gint i;
2059 S_LOCK_DEFINE_STATIC(out_charset);
2060
2061 S_LOCK(out_charset);
2062
2063 if (out_charset != -1) {
2064 S_UNLOCK(out_charset);
2065 return out_charset;
2066 }
2067
2068 cur_locale = conv_get_current_locale();
2069 if (!cur_locale) {
2070 out_charset = C_AUTO;
2071 S_UNLOCK(out_charset);
2072 return out_charset;
2073 }
2074
2075 if ((p = strcasestr(cur_locale, "@euro")) && p[5] == '\0') {
2076 out_charset = C_ISO_8859_15;
2077 S_UNLOCK(out_charset);
2078 return out_charset;
2079 }
2080
2081 for (i = 0; i < sizeof(locale_table) / sizeof(locale_table[0]); i++) {
2082 const gchar *p;
2083
2084 if (!g_ascii_strncasecmp(cur_locale, locale_table[i].locale,
2085 strlen(locale_table[i].locale))) {
2086 out_charset = locale_table[i].out_charset;
2087 break;
2088 } else if ((p = strchr(locale_table[i].locale, '_')) &&
2089 !strchr(p + 1, '.')) {
2090 if (strlen(cur_locale) == 2 &&
2091 !g_ascii_strncasecmp(cur_locale,
2092 locale_table[i].locale, 2)) {
2093 out_charset = locale_table[i].out_charset;
2094 break;
2095 }
2096 }
2097 }
2098
2099 S_UNLOCK(out_charset);
2100 return out_charset;
2101}
2102
2103const gchar *conv_get_outgoing_charset_str(void)
2104{
2105 CharSet out_charset;
2106 const gchar *str;
2107
2108 out_charset = conv_get_outgoing_charset();
2109 str = conv_get_charset_str(out_charset);
2110
2111 return str ? str : CS_UTF_8;
2112}
2113
2114gboolean conv_is_multibyte_encoding(CharSet encoding)
2115{
2116 switch (encoding) {
2117 case C_EUC_JP:
2118 case C_EUC_JP_MS:
2119 case C_EUC_KR:
2120 case C_EUC_TW:
2121 case C_EUC_CN:
2122 case C_ISO_2022_JP:
2123 case C_ISO_2022_JP_2:
2124 case C_ISO_2022_JP_3:
2125 case C_ISO_2022_KR:
2126 case C_ISO_2022_CN:
2127 case C_SHIFT_JIS:
2128 case C_CP932:
2129 case C_GB2312:
2130 case C_GBK:
2131 case C_BIG5:
2132 case C_UTF_8:
2133 case C_UTF_7:
2134 return TRUE;
2135 default:
2136 return FALSE;
2137 }
2138}
2139
2140const gchar *conv_get_current_locale(void)
2141{
2142 static const gchar *cur_locale;
2143 S_LOCK_DEFINE_STATIC(cur_locale);
2144
2145 S_LOCK(cur_locale);
2146
2147 if (!cur_locale) {
2148#ifdef G_OS_WIN32
2149 cur_locale = g_win32_getlocale();
2150#else
2151 cur_locale = g_getenv("LC_ALL");
2152 if (!cur_locale || *cur_locale == '\0')
2153 cur_locale = g_getenv("LC_CTYPE");
2154 if (!cur_locale || *cur_locale == '\0')
2155 cur_locale = g_getenv("LANG");
2156#ifdef HAVE_LOCALE_H
2157 if (!cur_locale || *cur_locale == '\0')
2158 cur_locale = setlocale(LC_CTYPE, NULL);
2159#endif /* HAVE_LOCALE_H */
2160#endif /* G_OS_WIN32 */
2161
2162 debug_print("current locale: %s\n",
2163 cur_locale ? cur_locale : "(none)");
2164 }
2165
2166 S_UNLOCK(cur_locale);
2167 return cur_locale;
2168}
2169
2170gboolean conv_is_ja_locale(void)
2171{
2172 static gint is_ja_locale = -1;
2173 const gchar *cur_locale;
2174 S_LOCK_DEFINE_STATIC(is_ja_locale);
2175
2176 S_LOCK(is_ja_locale);
2177
2178 if (is_ja_locale != -1) {
2179 S_UNLOCK(is_ja_locale);
2180 return is_ja_locale != 0;
2181 }
2182
2183 is_ja_locale = 0;
2184 cur_locale = conv_get_current_locale();
2185 if (cur_locale) {
2186 if (g_ascii_strncasecmp(cur_locale, "ja", 2) == 0)
2187 is_ja_locale = 1;
2188 }
2189
2190 S_UNLOCK(is_ja_locale);
2191 return is_ja_locale != 0;
2192}
2193
2194void conv_set_autodetect_type(ConvADType type)
2195{
2196 conv_ad_type = type;
2197}
2198
2199ConvADType conv_get_autodetect_type(void)
2200{
2201 return conv_ad_type;
2202}
2203
2204gchar *conv_unmime_header(const gchar *str, const gchar *default_encoding)
2205{
2206 gchar *buf;
2207 gchar *decoded_str;
2208
2209 if (is_ascii_str(str))
2210 return unmime_header(str);
2211
2212 if (default_encoding) {
2213 buf = conv_codeset_strdup
2214 (str, default_encoding, CS_INTERNAL);
2215 if (buf) {
2216 decoded_str = unmime_header(buf);
2217 g_free(buf);
2218 return decoded_str;
2219 }
2220 }
2221
2222 if (conv_ad_type == C_AD_JAPANESE ||
2223 (conv_ad_type == C_AD_BY_LOCALE && conv_is_ja_locale()))
2224 buf = conv_anytodisp(str, NULL);
2225 else
2226 buf = conv_localetodisp(str, NULL);
2227
2228 decoded_str = unmime_header(buf);
2229 g_free(buf);
2230
2231 return decoded_str;
2232}
2233
2234#define MAX_LINELEN 76
2235#define MAX_HARD_LINELEN 996
2236#define MIMESEP_BEGIN "=?"
2237#define MIMESEP_END "?="
2238
2239#define B64LEN(len) ((len) / 3 * 4 + ((len) % 3 ? 4 : 0))
2240
2241#define LBREAK_IF_REQUIRED(cond, is_plain_text) \
2242{ \
2243 if (len - (destp - dest) < MAX_LINELEN + 2) { \
2244 *destp = '\0'; \
2245 return; \
2246 } \
2247 \
2248 if ((cond) && *srcp) { \
2249 if (destp > dest && left < MAX_LINELEN - 1) { \
2250 if (g_ascii_isspace(*(destp - 1))) \
2251 destp--; \
2252 else if (is_plain_text && \
2253 g_ascii_isspace(*srcp)) \
2254 srcp++; \
2255 if (*srcp) { \
2256 *destp++ = '\n'; \
2257 *destp++ = ' '; \
2258 left = MAX_LINELEN - 1; \
2259 } \
2260 } \
2261 } \
2262}
2263
2264void conv_encode_header(gchar *dest, gint len, const gchar *src,
2265 gint header_len, gboolean addr_field,
2266 const gchar *out_encoding)
2267{
2268 const gchar *src_encoding;
2269 gint mimestr_len;
2270 gchar *mimesep_enc;
2271 gint left;
2272 const gchar *srcp = src;
2273 gchar *destp = dest;
2274 gboolean use_base64;
2275
2276 g_return_if_fail(g_utf8_validate(src, -1, NULL) == TRUE);
2277
2278 src_encoding = CS_INTERNAL;
2279 if (!out_encoding)
2280 out_encoding = conv_get_outgoing_charset_str();
2281 if (!strcmp(out_encoding, CS_US_ASCII))
2282 out_encoding = CS_ISO_8859_1;
2283
2284 if (!g_ascii_strncasecmp(out_encoding, "ISO-8859-", 9) ||
2285 !g_ascii_strncasecmp(out_encoding, "KOI8-", 5) ||
2286 !g_ascii_strncasecmp(out_encoding, "Windows-", 8)) {
2287 use_base64 = FALSE;
2288 mimesep_enc = "?Q?";
2289 } else {
2290 use_base64 = TRUE;
2291 mimesep_enc = "?B?";
2292 }
2293
2294 mimestr_len = strlen(MIMESEP_BEGIN) + strlen(mimesep_enc) +
2295 strlen(MIMESEP_END);
2296
2297 left = MAX_LINELEN - header_len;
2298
2299 while (*srcp) {
2300 gboolean in_quote = FALSE;
2301
2302 LBREAK_IF_REQUIRED(left <= 0, TRUE);
2303
2304 while (g_ascii_isspace(*srcp)) {
2305 *destp++ = *srcp++;
2306 left--;
2307 LBREAK_IF_REQUIRED(left <= 0, TRUE);
2308 }
2309
2310 /* output as it is if the next word is ASCII string */
2311 if (!is_next_nonascii(srcp)) {
2312 gint word_len;
2313
2314 word_len = get_next_word_len(srcp);
2315 LBREAK_IF_REQUIRED(left < word_len, TRUE);
2316 while (word_len > 0) {
2317 LBREAK_IF_REQUIRED(left + (MAX_HARD_LINELEN - MAX_LINELEN) <= 0, TRUE)
2318 *destp++ = *srcp++;
2319 left--;
2320 word_len--;
2321 }
2322
2323 continue;
2324 }
2325
2326 /* don't include parentheses in encoded strings */
2327 if (addr_field && (*srcp == '(' || *srcp == ')')) {
2328 LBREAK_IF_REQUIRED(left < 2, FALSE);
2329 *destp++ = *srcp++;
2330 left--;
2331 }
2332
2333 while (1) {
2334 gint mb_len = 0;
2335 gint cur_len = 0;
2336 gchar *part_str;
2337 gchar *out_str;
2338 gchar *enc_str;
2339 const gchar *p = srcp;
2340 const gchar *block_encoding = out_encoding;
2341 gint out_str_len;
2342 gint out_enc_str_len;
2343 gint mime_block_len;
2344 gint error = 0;
2345 gboolean cont = FALSE;
2346
2347 while (*p != '\0') {
2348 if (*p == '"')
2349 in_quote ^= TRUE;
2350 else if (!in_quote) {
2351 if (g_ascii_isspace(*p) &&
2352 !is_next_nonascii(p + 1))
2353 break;
2354 /* don't include parentheses in encoded
2355 strings */
2356 if (addr_field &&
2357 (*p == '(' || *p == ')'))
2358 break;
2359 }
2360
2361 mb_len = g_utf8_skip[*(guchar *)p];
2362
2363 part_str = g_strndup(srcp, cur_len + mb_len);
2364 out_str = conv_codeset_strdup_full
2365 (part_str, src_encoding, block_encoding,
2366 &error);
2367 if (!out_str || error != 0) {
2368 g_warning("conv_encode_header(): code conversion failed. Keeping UTF-8.\n");
2369 out_str = g_strdup(part_str);
2370 block_encoding = CS_UTF_8;
2371 }
2372 out_str_len = strlen(out_str);
2373
2374 if (use_base64)
2375 out_enc_str_len = B64LEN(out_str_len);
2376 else
2377 out_enc_str_len =
2378 qp_get_q_encoding_len
2379 ((guchar *)out_str);
2380
2381 g_free(out_str);
2382 g_free(part_str);
2383
2384 if (mimestr_len + strlen(block_encoding) + out_enc_str_len <= left) {
2385 cur_len += mb_len;
2386 p += mb_len;
2387 } else if (cur_len == 0) {
2388 LBREAK_IF_REQUIRED(1, FALSE);
2389 if (*p == '"')
2390 in_quote ^= TRUE;
2391 continue;
2392 } else {
2393 cont = TRUE;
2394 if (*p == '"')
2395 in_quote ^= TRUE;
2396 break;
2397 }
2398 }
2399
2400 if (cur_len > 0) {
2401 error = 0;
2402 part_str = g_strndup(srcp, cur_len);
2403 out_str = conv_codeset_strdup_full
2404 (part_str, src_encoding, block_encoding,
2405 &error);
2406 if (!out_str || error != 0) {
2407 g_warning("conv_encode_header(): code conversion failed\n");
2408 out_str = g_strdup(part_str);
2409 block_encoding = CS_UTF_8;
2410 }
2411 out_str_len = strlen(out_str);
2412
2413 if (use_base64)
2414 out_enc_str_len = B64LEN(out_str_len);
2415 else
2416 out_enc_str_len =
2417 qp_get_q_encoding_len
2418 ((guchar *)out_str);
2419
2420 enc_str = g_malloc(out_enc_str_len + 1);
2421 if (use_base64)
2422 base64_encode(enc_str,
2423 (guchar *)out_str,
2424 out_str_len);
2425 else
2426 qp_q_encode(enc_str, (guchar *)out_str);
2427
2428 /* output MIME-encoded string block */
2429 mime_block_len = mimestr_len +
2430 strlen(block_encoding) +
2431 strlen(enc_str);
2432 g_snprintf(destp, mime_block_len + 1,
2433 MIMESEP_BEGIN "%s%s%s" MIMESEP_END,
2434 block_encoding, mimesep_enc,
2435 enc_str);
2436 destp += mime_block_len;
2437 srcp += cur_len;
2438
2439 left -= mime_block_len;
2440
2441 g_free(enc_str);
2442 g_free(out_str);
2443 g_free(part_str);
2444 }
2445
2446 LBREAK_IF_REQUIRED(cont, FALSE);
2447
2448 if (cur_len == 0)
2449 break;
2450 }
2451 }
2452
2453 *destp = '\0';
2454}
2455
2456#undef LBREAK_IF_REQUIRED
2457
2458#define INT_TO_HEX_UPPER(outp, val) \
2459{ \
2460 if ((val) < 10) \
2461 *outp = '0' + (val); \
2462 else \
2463 *outp = 'A' + (val) - 10; \
2464}
2465
2466#define IS_ESCAPE_CHAR(c) \
2467 (c < 0x20 || c > 0x7f || \
2468 strchr("\t \r\n*'%!#$&~`,{}|()<>@,;:\\\"/[]?=", c))
2469
2470static gchar *encode_rfc2231_filename(const gchar *str)
2471{
2472 const gchar *p;
2473 gchar *out;
2474 gchar *outp;
2475
2476 outp = out = g_malloc(strlen(str) * 3 + 1);
2477
2478 for (p = str; *p != '\0'; ++p) {
2479 guchar ch = *(guchar *)p;
2480
2481 if (IS_ESCAPE_CHAR(ch)) {
2482 *outp++ = '%';
2483 INT_TO_HEX_UPPER(outp, ch >> 4);
2484 ++outp;
2485 INT_TO_HEX_UPPER(outp, ch & 0x0f);
2486 ++outp;
2487 } else
2488 *outp++ = ch;
2489 }
2490
2491 *outp = '\0';
2492 return out;
2493}
2494
2495gchar *conv_encode_filename(const gchar *src, const gchar *param_name,
2496 const gchar *out_encoding)
2497{
2498 gint name_len, max_linelen;
2499 gchar *out_str, *enc_str;
2500 gchar cur_param[80];
2501 GString *string;
2502 gint count = 0;
2503 gint cur_left_len;
2504 gchar *p;
2505
2506 g_return_val_if_fail(src != NULL, NULL);
2507 g_return_val_if_fail(param_name != NULL, NULL);
2508
2509 if (is_ascii_str(src))
2510 return g_strdup_printf(" %s=\"%s\"", param_name, src);
2511
2512 name_len = strlen(param_name);
2513 max_linelen = MAX_LINELEN - name_len - 3;
2514
2515 if (!out_encoding)
2516 out_encoding = conv_get_outgoing_charset_str();
2517 if (!strcmp(out_encoding, CS_US_ASCII))
2518 out_encoding = CS_ISO_8859_1;
2519
2520 out_str = conv_codeset_strdup(src, CS_INTERNAL, out_encoding);
2521 if (!out_str)
2522 return NULL;
2523 enc_str = encode_rfc2231_filename(out_str);
2524 g_free(out_str);
2525
2526 if (strlen(enc_str) <= max_linelen) {
2527 gchar *ret;
2528 ret = g_strdup_printf(" %s*=%s''%s",
2529 param_name, out_encoding, enc_str);
2530 g_free(enc_str);
2531 return ret;
2532 }
2533
2534 string = g_string_new(NULL);
2535 g_string_printf(string, " %s*0*=%s''", param_name, out_encoding);
2536 cur_left_len = MAX_LINELEN - string->len;
2537
2538 p = enc_str;
2539
2540 while (*p != '\0') {
2541 if ((*p == '%' && cur_left_len < 4) ||
2542 (*p != '%' && cur_left_len < 2)) {
2543 gint len;
2544
2545 g_string_append(string, ";\n");
2546 ++count;
2547 len = g_snprintf(cur_param, sizeof(cur_param),
2548 " %s*%d*=", param_name, count);
2549 g_string_append(string, cur_param);
2550 cur_left_len = MAX_LINELEN - len;
2551 }
2552
2553 if (*p == '%') {
2554 g_string_append_len(string, p, 3);
2555 p += 3;
2556 cur_left_len -= 3;
2557 } else {
2558 g_string_append_c(string, *p);
2559 ++p;
2560 --cur_left_len;
2561 }
2562 }
2563
2564 g_free(enc_str);
2565
2566 return g_string_free(string, FALSE);
2567}
2568
2569static gint conv_copy_file_with_gconvert(const gchar *src, const gchar *dest,
2570 const gchar *encoding)
2571{
2572 gchar *src_s = NULL;
2573 gsize len = 0, dlen = 0;
2574 gchar *dest_s;
2575 GError *error = NULL;
2576
2577 g_return_val_if_fail(src != NULL, -1);
2578 g_return_val_if_fail(dest != NULL, -1);
2579 g_return_val_if_fail(encoding != NULL, -1);
2580
2581 if (g_file_get_contents(src, &src_s, &len, &error) == FALSE) {
2582 g_warning("conv_copy_utf16_file(): %s: %s", src, error->message);
2583 g_error_free(error);
2584 return -1;
2585 }
2586
2587 dest_s = g_convert(src_s, len, CS_UTF_8, encoding, NULL, &dlen, &error);
2588 if (!dest_s) {
2589 g_warning("conv_copy_utf16_file(): %s: %s", src, error->message);
2590 g_error_free(error);
2591 g_free(src_s);
2592 return -1;
2593 }
2594
2595 if (g_file_set_contents(dest, dest_s, dlen, &error) == FALSE) {
2596 g_warning("conv_copy_utf16_file(): %s: %s", dest, error->message);
2597 g_error_free(error);
2598 g_free(dest_s);
2599 g_free(src_s);
2600 return -1;
2601 }
2602
2603 g_free(dest_s);
2604 g_free(src_s);
2605 return 0;
2606}
2607
2608gint conv_copy_file(const gchar *src, const gchar *dest, const gchar *encoding)
2609{
2610 FILE *src_fp, *dest_fp;
2611 gchar buf[BUFFSIZE];
2612 CodeConverter *conv;
2613 gboolean err = FALSE;
2614 CharSet charset;
2615
2616 charset = conv_get_charset_from_str(encoding);
2617 if (charset == C_UTF_16 || charset == C_UTF_16BE || charset == C_UTF_16LE) {
2618 return conv_copy_file_with_gconvert(src, dest, encoding);
2619 }
2620
2621 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2622 FILE_OP_ERROR(src, "fopen");
2623 return -1;
2624 }
2625 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2626 FILE_OP_ERROR(dest, "fopen");
2627 fclose(src_fp);
2628 return -1;
2629 }
2630
2631 if (change_file_mode_rw(dest_fp, dest) < 0) {
2632 FILE_OP_ERROR(dest, "chmod");
2633 g_warning("can't change file mode\n");
2634 }
2635
2636 conv = conv_code_converter_new(encoding, NULL);
2637
2638 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2639 gchar *outbuf;
2640
2641 outbuf = conv_convert(conv, buf);
2642 if (outbuf) {
2643 fputs(outbuf, dest_fp);
2644 g_free(outbuf);
2645 } else
2646 fputs(buf, dest_fp);
2647 }
2648
2649 conv_code_converter_destroy(conv);
2650
2651 if (ferror(src_fp)) {
2652 FILE_OP_ERROR(src, "fgets");
2653 err = TRUE;
2654 }
2655 fclose(src_fp);
2656 if (fclose(dest_fp) == EOF) {
2657 FILE_OP_ERROR(dest, "fclose");
2658 err = TRUE;
2659 }
2660 if (err) {
2661 g_unlink(dest);
2662 return -1;
2663 }
2664
2665 return 0;
2666}
2667
2668gint conv_copy_dir(const gchar *src, const gchar *dest, const gchar *encoding)
2669{
2670 GDir *dir;
2671 const gchar *dir_name;
2672 gchar *src_file;
2673 gchar *dest_file;
2674
2675 if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
2676 g_warning("failed to open directory: %s\n", src);
2677 return -1;
2678 }
2679
2680 if (make_dir_hier(dest) < 0) {
2681 g_dir_close(dir);
2682 return -1;
2683 }
2684
2685 while ((dir_name = g_dir_read_name(dir)) != NULL) {
2686 src_file = g_strconcat(src, G_DIR_SEPARATOR_S, dir_name, NULL);
2687 dest_file = g_strconcat(dest, G_DIR_SEPARATOR_S, dir_name,
2688 NULL);
2689 if (is_file_exist(src_file))
2690 conv_copy_file(src_file, dest_file, encoding);
2691 g_free(dest_file);
2692 g_free(src_file);
2693 }
2694
2695 g_dir_close(dir);
2696
2697 return 0;
2698}
2699
2700CharSet conv_check_file_encoding(const gchar *file)
2701{
2702 FILE *fp;
2703 gchar buf[BUFFSIZE];
2704 CharSet enc;
2705 const gchar *enc_str;
2706 gboolean is_locale = TRUE, is_utf8 = TRUE;
2707 size_t size;
2708
2709 g_return_val_if_fail(file != NULL, C_AUTO);
2710
2711 enc = conv_get_locale_charset();
2712 enc_str = conv_get_locale_charset_str();
2713 if (enc == C_UTF_8)
2714 is_locale = FALSE;
2715
2716 if ((fp = g_fopen(file, "rb")) == NULL) {
2717 FILE_OP_ERROR(file, "fopen");
2718 return C_AUTO;
2719 }
2720
2721 /* UTF-16 check */
2722 if ((size = fread(buf, 2, BUFFSIZE / 2, fp)) > 0) {
2723 CharSet guess_enc = C_AUTO;
2724
2725 debug_print("conv_check_file_encoding: check first %d bytes of file %s\n", size * 2, file);
2726
2727 /* BOM check */
2728 if ((buf[0] & 0xff) == 0xfe && (buf[1] & 0xff) == 0xff) {
2729 debug_print("conv_check_file_encoding: UTF-16 BOM (BE) found\n");
2730 guess_enc = C_UTF_16; /* UTF-16BE */
2731 } else if ((buf[0] & 0xff) == 0xff && (buf[1] & 0xff) == 0xfe) {
2732 debug_print("conv_check_file_encoding: UTF-16 BOM (LE) found\n");
2733 guess_enc = C_UTF_16; /* UTF-16LE */
2734 }
2735 if (guess_enc != C_AUTO) {
2736 fclose(fp);
2737 return guess_enc;
2738 }
2739
2740 /* search UTF-16 CR/LF */
2741 if (memchr(buf, 0x00, size * 2) != NULL) {
2742 gint i;
2743 guchar c1, c2;
2744
2745 for (i = 0; i < size; i++) {
2746 c1 = buf[i * 2] & 0xff;
2747 c2 = buf[i * 2 + 1] & 0xff;
2748 if (c1 == 0x00 && c2 == 0x0d) { /* UTF-16BE CR */
2749 i++;
2750 if (i >= size) {
2751 break;
2752 }
2753 c1 = buf[i * 2] & 0xff;
2754 c2 = buf[i * 2 + 1] & 0xff;
2755 if (c1 == 0x00 && c2 == 0x0a) { /* UTF-16BE LF */
2756 guess_enc = C_UTF_16BE;
2757 break;
2758 }
2759 } else if (c1 == 0x0d && c2 == 0x00) { /* UTF-16LE CR */
2760 i++;
2761 if (i >= size) {
2762 break;
2763 }
2764 c1 = buf[i * 2] & 0xff;
2765 c2 = buf[i * 2 + 1] & 0xff;
2766 if (c1 == 0x0a && c2 == 0x00) { /* UTF-16LE LF */
2767 guess_enc = C_UTF_16LE;
2768 break;
2769 }
2770 } else if (c1 == 0x00 && c2 == 0x0a) { /* UTF-16BE LF */
2771 guess_enc = C_UTF_16BE;
2772 break;
2773 } else if (c1 == 0x0a && c2 == 0x00) { /* UTF-16LE LF */
2774 guess_enc = C_UTF_16LE;
2775 break;
2776 }
2777 }
2778
2779 if (guess_enc != C_AUTO) {
2780 debug_print("conv_check_file_encoding: %s detected\n",
2781 conv_get_charset_str(guess_enc));
2782 fclose(fp);
2783 return guess_enc;
2784 }
2785 }
2786 }
2787
2788 rewind(fp);
2789
2790 while (fgets(buf, sizeof(buf), fp) != NULL) {
2791 gchar *str;
2792 gint error = 0;
2793
2794 if (is_locale) {
2795 str = conv_codeset_strdup_full(buf, enc_str,
2796 CS_INTERNAL, &error);
2797 if (!str || error != 0)
2798 is_locale = FALSE;
2799 g_free(str);
2800 }
2801
2802 if (is_utf8 && g_utf8_validate(buf, -1, NULL) == FALSE) {
2803 is_utf8 = FALSE;
2804 }
2805
2806 if (!is_locale && !is_utf8)
2807 break;
2808 }
2809
2810 fclose(fp);
2811
2812 if (is_locale)
2813 return enc;
2814 else if (is_utf8)
2815 return C_UTF_8;
2816 else
2817 return C_AUTO;
2818}
2819
2820gchar *conv_filename_from_utf8(const gchar *utf8_file)
2821{
2822 gchar *fs_file;
2823 GError *error = NULL;
2824
2825 g_return_val_if_fail(utf8_file != NULL, NULL);
2826
2827 fs_file = g_filename_from_utf8(utf8_file, -1, NULL, NULL, &error);
2828 if (error) {
2829 g_warning("failed to convert encoding of file name: %s\n",
2830 error->message);
2831 g_error_free(error);
2832 }
2833 if (!fs_file)
2834 fs_file = g_strdup(utf8_file);
2835
2836 return fs_file;
2837}
2838
2839gchar *conv_filename_to_utf8(const gchar *fs_file)
2840{
2841 gchar *utf8_file;
2842 GError *error = NULL;
2843
2844 g_return_val_if_fail(fs_file != NULL, NULL);
2845
2846 utf8_file = g_filename_to_utf8(fs_file, -1, NULL, NULL, &error);
2847 if (error) {
2848 g_warning("failed to convert encoding of file name: %s\n",
2849 error->message);
2850 g_error_free(error);
2851 }
2852 if (!utf8_file)
2853 utf8_file = g_strdup(fs_file);
2854
2855 return utf8_file;
2856}