Intial commit
[profile/ivi/w3m.git] / libwc / charset.c
1
2 #include <stdlib.h>
3 #include <ctype.h>
4 #include <gc.h>
5 #define New_N(type,n) ((type*)GC_MALLOC((n)*sizeof(type)))
6
7 #include "wc.h"
8
9 #ifdef HAVE_LANGINFO_CODESET
10 #include <langinfo.h>
11 #endif
12
13 wc_locale WcLocale = 0;
14
15 static struct {
16   char *lang;
17   wc_ces ces;
18 } lang_ces_table[] = {
19   { "cs", WC_CES_ISO_8859_2 },  /* cs_CZ */
20   { "el", WC_CES_ISO_8859_7 },  /* el_GR */
21   { "iw", WC_CES_ISO_8859_8 },  /* iw_IL */
22   { "ja", WC_CES_EUC_JP },      /* ja_JP */
23   { "ko", WC_CES_EUC_KR },      /* ko_KR */
24   { "hu", WC_CES_ISO_8859_2 },  /* hu_HU */
25   { "pl", WC_CES_ISO_8859_2 },  /* pl_PL */
26   { "ro", WC_CES_ISO_8859_2 },  /* ro_RO */
27   { "ru", WC_CES_ISO_8859_5 },  /* ru_SU */
28   { "sk", WC_CES_ISO_8859_2 },  /* sk_SK */
29   { "sl", WC_CES_ISO_8859_2 },  /* sl_CS */
30   { "tr", WC_CES_ISO_8859_9 },  /* tr_TR */
31   { "zh", WC_CES_EUC_CN },      /* zh_CN */
32   { NULL, 0 }
33 };
34
35 wc_ces
36 wc_guess_charset(char *charset, wc_ces orig)
37 {
38     wc_ces guess;
39
40     if (charset == NULL || *charset == '\0')
41         return orig;
42     guess = wc_charset_to_ces(charset);
43     return guess ? guess : orig;
44 }
45
46 wc_ces
47 wc_guess_charset_short(char *charset, wc_ces orig)
48 {
49     wc_ces guess;
50
51     if (charset == NULL || *charset == '\0')
52         return orig;
53     guess = wc_charset_short_to_ces(charset);
54     return guess ? guess : orig;
55 }
56
57 wc_ces
58 wc_guess_locale_charset(char *locale, wc_ces orig)
59 {
60     wc_ces guess;
61
62     if (locale == NULL || *locale == '\0')
63         return orig;
64     guess = wc_locale_to_ces(locale);
65     return guess ? guess : orig;
66 }
67
68 wc_ces
69 wc_charset_to_ces(char *charset)
70 {
71     char *p = charset;
72     char buf[16];
73     int n;
74
75     if (tolower(*p) == 'x' && *(p+1) == '-')
76         p += 2;
77     for (n = 0; *p && n < 15; p++) {
78         if ((unsigned char)*p > 0x20 && *p != '_' && *p != '-')
79             buf[n++] = tolower(*p);
80     }
81     buf[n] = 0;
82     p = buf;
83     switch (*p) {
84     case 'e':
85         if (! strncmp(p, "euc", 3)) {
86             p += 3;
87             switch (*p) {
88             case 'j': return WC_CES_EUC_JP;
89             case 'c': return WC_CES_EUC_CN;
90             case 't': return WC_CES_EUC_TW;
91             case 'k': return WC_CES_EUC_KR;
92             }
93             switch (WcLocale) {
94             case WC_LOCALE_JA_JP: return WC_CES_EUC_JP;
95             case WC_LOCALE_ZH_CN: return WC_CES_EUC_CN;
96             case WC_LOCALE_ZH_TW: return WC_CES_EUC_TW;
97             case WC_LOCALE_ZH_HK: return WC_CES_EUC_CN;
98             case WC_LOCALE_KO_KR: return WC_CES_EUC_KR;
99             }
100             return WC_CES_EUC_JP;
101         }
102         break;
103     case 'i':
104         if (! strncmp(p, "iso2022", 7)) {
105             p += 7;
106             switch (*p) {
107             case 'j':
108                 if (! strncmp(p, "jp2", 3))
109                     return WC_CES_ISO_2022_JP_2;
110                 if (! strncmp(p, "jp3", 3))
111                     return WC_CES_ISO_2022_JP_3;
112                 return WC_CES_ISO_2022_JP;
113             case 'c': return WC_CES_ISO_2022_CN;
114             case 'k': return WC_CES_ISO_2022_KR;
115             }
116             return WC_CES_ISO_2022_JP;
117         } else if (! strncmp(p, "iso8859", 7)) {
118             n = atoi(p + 7);
119             if (n >= 1 && n <= 16 && n != 12)
120                 return (WC_CES_E_ISO_8859 | n);
121             return WC_CES_ISO_8859_1;
122         }
123         break;
124     case 'j':
125         if (! strncmp(p, "johab", 5))
126             return WC_CES_JOHAB;
127         if (! strncmp(p, "jis", 3))
128             return WC_CES_ISO_2022_JP;
129         break;
130     case 's':
131         if (! strncmp(p, "shiftjisx0213", 13) ||
132             ! strncmp(p, "sjisx0213", 9))
133             return WC_CES_SHIFT_JISX0213;
134         if (! strncmp(p, "shiftjis", 8) ||
135             ! strncmp(p, "sjis", 4))
136             return WC_CES_SHIFT_JIS;
137         break;
138     case 'g':
139         if (! strncmp(p, "gb18030", 7) ||
140             ! strncmp(p, "gbk2k", 5))
141             return WC_CES_GB18030;
142         if (! strncmp(p, "gbk", 3))
143             return WC_CES_GBK;
144         if (! strncmp(p, "gb2312", 6))
145             return WC_CES_EUC_CN;
146         break;
147     case 'b':
148         if (! strncmp(p, "big5hkscs", 9))
149             return WC_CES_HKSCS;
150         if (! strncmp(p, "big5", 4))
151             return WC_CES_BIG5;
152         break;
153     case 'h':
154         if (! strncmp(p, "hz", 2))
155             return WC_CES_HZ_GB_2312;
156         if (! strncmp(p, "hkscs", 5))
157             return WC_CES_HKSCS;
158         break;
159     case 'k':
160         if (! strncmp(p, "koi8r", 5))
161             return WC_CES_KOI8_R;
162         if (! strncmp(p, "koi8u", 5))
163             return WC_CES_KOI8_U;
164         if (! strncmp(p, "ksx1001", 7))
165             return WC_CES_EUC_KR;
166         if (! strncmp(p, "ksc5601", 7))
167             return WC_CES_EUC_KR;
168         break;
169     case 't':
170         if (! strncmp(p, "tis620", 6))
171             return WC_CES_TIS_620;
172         if (! strncmp(p, "tcvn", 4))
173             return WC_CES_TCVN_5712;
174         break;
175     case 'n':
176         if (! strncmp(p, "next", 4))
177             return WC_CES_NEXTSTEP;
178         break;
179     case 'v':
180         if (! strncmp(p, "viet", 4)) {
181             p += 4;
182             if (! strncmp(p, "tcvn", 4))
183                 return WC_CES_TCVN_5712;
184         }
185         if (! strncmp(p, "viscii", 6))
186             return WC_CES_VISCII_11;
187         if (! strncmp(p, "vps", 3))
188             return WC_CES_VPS;
189         break;
190     case 'u':
191 #ifdef USE_UNICODE
192         if (! strncmp(p, "utf8", 4))
193             return WC_CES_UTF_8;
194         if (! strncmp(p, "utf7", 4))
195             return WC_CES_UTF_7;
196 #endif
197         if (! strncmp(p, "uhc", 3))
198             return WC_CES_UHC;
199         if (! strncmp(p, "ujis", 4))
200             return WC_CES_EUC_JP;
201         if (! strncmp(p, "usascii", 7))
202             return WC_CES_US_ASCII;
203         break;
204     case 'a':
205         if (! strncmp(p, "ascii", 5))
206             return WC_CES_US_ASCII;
207         break;
208     case 'c':
209         if (! strncmp(p, "cngb", 4))
210             return WC_CES_EUC_CN;
211         if (*(p+1) != 'p')
212             break;
213         n = atoi(p + 2);
214         switch (n) {
215         case 437: return WC_CES_CP437;
216         case 737: return WC_CES_CP737;
217         case 775: return WC_CES_CP775;
218         case 850: return WC_CES_CP850;
219         case 852: return WC_CES_CP852;
220         case 855: return WC_CES_CP855;
221         case 856: return WC_CES_CP856;
222         case 857: return WC_CES_CP857;
223         case 860: return WC_CES_CP860;
224         case 861: return WC_CES_CP861;
225         case 862: return WC_CES_CP862;
226         case 863: return WC_CES_CP863;
227         case 864: return WC_CES_CP864;
228         case 865: return WC_CES_CP865;
229         case 866: return WC_CES_CP866;
230         case 869: return WC_CES_CP869;
231         case 874: return WC_CES_CP874;
232         case 932: return WC_CES_CP932;          /* CP932 = Shift_JIS */
233         case 936: return WC_CES_CP936;          /* CP936 = GBK > EUC_CN */
234         case 949: return WC_CES_CP949;          /* CP949 = UHC > EUC_KR */
235         case 950: return WC_CES_CP950;          /* CP950 = Big5 */
236         case 1006: return WC_CES_CP1006;
237         case 1250: return WC_CES_CP1250;
238         case 1251: return WC_CES_CP1251;
239         case 1252: return WC_CES_CP1252;
240         case 1253: return WC_CES_CP1253;
241         case 1254: return WC_CES_CP1254;
242         case 1255: return WC_CES_CP1255;
243         case 1256: return WC_CES_CP1256;
244         case 1257: return WC_CES_CP1257;
245         case 1258: return WC_CES_CP1258;
246         }
247         break;
248     case 'w':
249         if (strncmp(p, "windows", 7))
250             break;
251         if (! strncmp(p, "31j", 3))
252             return WC_CES_CP932;
253         n = atoi(p + 7);
254         switch (n) {
255         case 1250: return WC_CES_CP1250;
256         case 1251: return WC_CES_CP1251;
257         case 1252: return WC_CES_CP1252;
258         case 1253: return WC_CES_CP1253;
259         case 1254: return WC_CES_CP1254;
260         case 1255: return WC_CES_CP1255;
261         case 1256: return WC_CES_CP1256;
262         case 1257: return WC_CES_CP1257;
263         case 1258: return WC_CES_CP1258;
264         }
265         break;
266     }
267     return 0;
268 }
269
270 wc_ces
271 wc_charset_short_to_ces(char *charset)
272 {
273     char *p = charset;
274     char buf[16];
275     wc_ces ces;
276     int n;
277
278     ces = wc_charset_to_ces(charset);
279     if (ces)
280         return ces;
281
282     for (n = 0; *p && n < 15; p++) {
283         if ((unsigned char)*p > 0x20 && *p != '_' && *p != '-')
284             buf[n++] = tolower(*p);
285     }
286     buf[n] = 0;
287     p = buf;
288     switch (*p) {
289     case 'e':
290         switch (*(p+1)) {
291         case 'j': return WC_CES_EUC_JP;
292         case 'c': return WC_CES_EUC_CN;
293         case 't': return WC_CES_EUC_TW;
294         case 'k': return WC_CES_EUC_KR;
295         }
296         return WC_CES_EUC_JP;
297     case 'j':
298         p++;
299         if (*p == 'o')
300             return WC_CES_JOHAB;
301         if (*p == 'p')
302            p++;
303         if (*p == '2')
304            return WC_CES_ISO_2022_JP_2;
305         if (*p == '3')
306            return WC_CES_ISO_2022_JP_3;
307         return WC_CES_ISO_2022_JP;
308     case 's':
309         return WC_CES_SHIFT_JIS;
310     case 'g':
311         return WC_CES_EUC_CN;
312     case 'b':
313         return WC_CES_BIG5;
314     case 'h':
315         if (*(p+1) == 'k')
316             return WC_CES_HKSCS;
317         return WC_CES_HZ_GB_2312;
318     case 'k':
319         if (*(p+1) == 'o')
320             return WC_CES_KOI8_R;
321         return WC_CES_ISO_2022_KR;
322     case 'l':
323         n = atoi(p + 1);
324         if (n >= 1 && n <= 16 && n != 12)
325             return (WC_CES_E_ISO_8859 | n);
326         return WC_CES_ISO_8859_1;
327     case 't':
328         if (*(p+1) == 'c')
329             return WC_CES_TCVN_5712;
330         return WC_CES_TIS_620;
331     case 'n':
332         return WC_CES_NEXTSTEP;
333     case 'v':
334         if (*(p+1) == 'p')
335             return WC_CES_VPS;
336         return WC_CES_VISCII_11;
337 #ifdef USE_UNICODE
338     case 'u':
339         if (*(p+1) == '7')
340             return WC_CES_UTF_7;
341         return WC_CES_UTF_8;
342 #endif
343     case 'a':
344         return WC_CES_US_ASCII;
345     case 'c':
346         return WC_CES_ISO_2022_CN;
347     case 'w':
348         n = atoi(p + 1);
349         switch (n) {
350         case 1250: return WC_CES_CP1250;
351         case 1251: return WC_CES_CP1251;
352         case 1252: return WC_CES_CP1252;
353         case 1253: return WC_CES_CP1253;
354         case 1254: return WC_CES_CP1254;
355         case 1255: return WC_CES_CP1255;
356         case 1256: return WC_CES_CP1256;
357         case 1257: return WC_CES_CP1257;
358         case 1258: return WC_CES_CP1258;
359         }
360         break;
361     case 'r':
362         return WC_CES_RAW;
363     }
364     return 0;
365 }
366
367 wc_ces
368 wc_locale_to_ces(char *locale)
369 {
370     char *p = locale;
371     char buf[6];
372     int n;
373
374     if (*p == 'C' && *(p+1) == '\0')
375         return WC_CES_US_ASCII;
376 #ifdef HAVE_LANGINFO_CODESET
377     {
378         char *cs = nl_langinfo(CODESET);
379         if (cs && strcmp(cs, "US-ASCII"))
380             return wc_charset_to_ces(cs);
381     }
382 #endif
383     for (n = 0; *p && *p != '.' && n < 5; p++) {
384         if ((unsigned char)*p > 0x20)
385             buf[n++] = tolower(*p);
386     }
387     buf[n] = 0;
388     if (*p == '.') {
389         p++;
390         if (! strcasecmp(p, "euc")) {
391             switch (buf[0]) {
392             case 'j':
393                 WcLocale = WC_LOCALE_JA_JP;
394                 break;
395             case 'k':
396                 WcLocale = WC_LOCALE_KO_KR;
397                 break;
398             case 'z':
399                 if (!strcmp(buf, "zh_tw"))
400                     WcLocale = WC_LOCALE_ZH_TW;
401                 else if (!strcmp(buf, "zh_hk"))
402                     WcLocale = WC_LOCALE_ZH_HK;
403                 else
404                     WcLocale = WC_LOCALE_ZH_CN;
405                 break;
406             default:
407                 WcLocale = 0;
408                 break;
409             }
410         }
411         return wc_charset_to_ces(p);
412     }
413
414     if (!strcmp(buf, "japanese"))
415         return WC_CES_SHIFT_JIS;
416     if (!strcmp(buf, "zh_tw") ||
417         !strcmp(buf, "zh_hk"))
418         return WC_CES_BIG5;
419     for (n = 0; lang_ces_table[n].lang; n++) {
420         if (!strncmp(buf, lang_ces_table[n].lang, 2))
421             return lang_ces_table[n].ces;
422     }
423     return WC_CES_ISO_8859_1;
424 }
425
426 char *
427 wc_ces_to_charset(wc_ces ces)
428 {
429     if (ces == WC_CES_WTF)
430         return "WTF";
431     return WcCesInfo[WC_CES_INDEX(ces)].name;
432 }
433
434 char *
435 wc_ces_to_charset_desc(wc_ces ces)
436 {
437     if (ces == WC_CES_WTF)
438         return "W3M Transfer Format";
439     return WcCesInfo[WC_CES_INDEX(ces)].desc;
440 }
441
442 wc_ces
443 wc_guess_8bit_charset(wc_ces orig)
444 {
445     switch (orig) {
446     case WC_CES_ISO_2022_JP:
447     case WC_CES_ISO_2022_JP_2:
448     case WC_CES_ISO_2022_JP_3:
449         return WC_CES_EUC_JP;
450     case WC_CES_ISO_2022_KR:
451         return WC_CES_EUC_KR;
452     case WC_CES_ISO_2022_CN:
453     case WC_CES_HZ_GB_2312:
454         return WC_CES_EUC_CN;
455     case WC_CES_US_ASCII:
456         return WC_CES_ISO_8859_1;
457     }
458     return orig;
459 }
460
461 wc_bool
462 wc_check_ces(wc_ces ces)
463 {
464     size_t i = WC_CES_INDEX(ces);
465
466     return (i <= WC_CES_END && WcCesInfo[i].id == ces);
467 }
468
469 static int
470 wc_ces_list_cmp(const void *a, const void *b)
471 {
472     return strcasecmp(((wc_ces_list *)a)->desc, ((wc_ces_list *)b)->desc);
473 }
474
475 static wc_ces_list *list = NULL;
476
477 wc_ces_list *
478 wc_get_ces_list(void)
479 {
480     wc_ces_info *info;
481     size_t n;
482
483     if (list)
484         return list;
485     for (info = WcCesInfo, n = 0; info->id; info++) {
486         if (info->name != NULL)
487             n++;
488     }
489     list = New_N(wc_ces_list, n + 1);
490     for (info = WcCesInfo, n = 0; info->id; info++) {
491         if (info->name != NULL) {
492             list[n].id = info->id;
493             list[n].name = info->name;
494             list[n].desc = info->desc;
495             n++;
496         }
497     }
498     list[n].id = 0;
499     list[n].name = NULL;
500     list[n].desc = NULL;
501     qsort(list, n, sizeof(wc_ces_list), wc_ces_list_cmp);
502     return list;
503 }