1 /* -*- c-basic-offset:2; tab-width:2; indent-tabs-mode:nil -*- */
2 
3 #include "bl_locale.h"
4 
5 #include <stdio.h>  /* sprintf */
6 #include <locale.h> /* setlocale() */
7 
8 /* for bl_get_codeset_win32() */
9 #ifdef HAVE_WINDOWS_H
10 #include <windows.h>
11 #endif
12 
13 #include "bl_langinfo.h" /* bl_langinfo() */
14 #include "bl_debug.h"
15 #include "bl_mem.h" /* alloca */
16 #include "bl_str.h"
17 #include "bl_util.h" /* BL_MIN */
18 
19 #if 0
20 #define __DEBUG
21 #endif
22 
23 typedef struct lang_codeset_table {
24   char *lang;
25   char *codeset;
26 
27 } lang_codeset_table_t;
28 
29 typedef struct alias_codeset_table {
30   char *codeset;
31   char *locale;
32 
33   char *alias;
34 
35 } alias_codeset_table_t;
36 
37 /* --- static variables --- */
38 
39 static char *sys_locale = NULL;
40 static char *sys_lang = NULL;
41 static char *sys_country = NULL;
42 static char *sys_codeset = NULL;
43 
44 /* for sys_lang and sys_country memory */
45 static char *sys_lang_country = NULL;
46 
47 #ifndef USE_WIN32API
48 static lang_codeset_table_t lang_codeset_table[] = {
49   { "en", "ISO8859-1", },
50   { "da", "ISO8859-1", },
51   { "de", "ISO8859-1", },
52   { "fi", "ISO8859-1", },
53   { "fr", "ISO8859-1", },
54   { "is", "ISO8859-1", },
55   { "it", "ISO8859-1", },
56   { "nl", "ISO8859-1", },
57   { "no", "ISO8859-1", },
58   { "pt", "ISO8859-1", },
59   { "sv", "ISO8859-1", },
60   { "cs", "ISO8859-2", },
61   { "hr", "ISO8859-2", },
62   { "hu", "ISO8859-2", },
63   { "la", "ISO8859-2", },
64   { "lt", "ISO8859-2", },
65   { "pl", "ISO8859-2", },
66   { "sl", "ISO8859-2", },
67   { "el", "ISO8859-7", },
68   { "ru", "KOI8-R", },
69   { "uk", "KOI8-U", },
70   { "vi", "VISCII", },
71   { "th", "TIS-620", },
72   { "ja", "eucJP", },
73   { "ko", "eucKR", },
74   { "zh_CN", "eucCN", },
75   { "zh_TW", "Big5", },
76   { "zh_HK", "Big5HKSCS", },
77 };
78 #endif
79 
80 static alias_codeset_table_t alias_codeset_table[] = {
81   { "EUC", "ja_JP.EUC", "eucJP", },
82   { "EUC", "ko_KR.EUC", "eucKR", },
83 };
84 
85 /* --- global functions --- */
86 
bl_locale_init(const char * locale)87 int bl_locale_init(const char *locale) {
88   char *locale_p;
89   int result;
90 
91   if (sys_locale && locale && strcmp(locale, sys_locale) == 0) {
92     return 1;
93   }
94 
95   if ((locale = setlocale(LC_CTYPE, locale)) == NULL) {
96 #ifdef DEBUG
97     bl_warn_printf(BL_DEBUG_TAG " setlocale() failed.\n");
98 #endif
99 
100     if (sys_locale) {
101       /* restoring locale info. nothing is changed. */
102       setlocale(LC_CTYPE, sys_locale);
103 
104       return 0;
105     } else if ((locale = getenv("LC_ALL")) == NULL && (locale = getenv("LC_CTYPE")) == NULL &&
106                (locale = getenv("LANG")) == NULL) {
107       /* nothing is changed */
108 
109       return 0;
110     }
111 
112     result = 0;
113   } else {
114     result = 1;
115   }
116 
117   if (sys_locale) {
118     free(sys_locale);
119     sys_locale = NULL;
120   }
121 
122   if (sys_lang_country) {
123     free(sys_lang_country);
124     sys_lang_country = NULL;
125   }
126 
127   /*
128    * If external library calls setlocale(), this 'locale' variable
129    * can be free'ed, so strdup() shoule be called.
130    */
131   sys_locale = strdup(locale);
132 
133   if ((locale_p = sys_lang_country = strdup(locale)) == NULL) {
134     sys_locale = NULL;
135 
136     return 0;
137   }
138 
139   if ((sys_lang = bl_str_sep(&locale_p, "_")) == NULL) {
140     /* this never happends */
141 
142     return 0;
143   }
144 
145   sys_country = bl_str_sep(&locale_p, ".");
146 
147   sys_codeset = bl_langinfo(CODESET);
148 
149   if (strcmp(sys_codeset, "") == 0) {
150     if (locale_p && *locale_p) {
151       sys_codeset = locale_p;
152     } else {
153       sys_codeset = NULL;
154     }
155   }
156 
157   if (sys_codeset) {
158     /*
159      * normalizing codeset name.
160      */
161 
162     int count;
163 
164     for (count = 0; count < sizeof(alias_codeset_table) / sizeof(alias_codeset_table[0]); count++) {
165       if (strcmp(sys_codeset, alias_codeset_table[count].codeset) == 0 &&
166           strcmp(locale, alias_codeset_table[count].locale) == 0) {
167         sys_codeset = alias_codeset_table[count].alias;
168 
169         break;
170       }
171     }
172   }
173 
174 #ifdef __DEBUG
175   bl_debug_printf("locale setttings -> locale %s lang %s country %s codeset %s\n", sys_locale,
176                   sys_lang, sys_country, sys_codeset);
177 #endif
178 
179   return result;
180 }
181 
bl_locale_final(void)182 void bl_locale_final(void) {
183   if (sys_locale) {
184     free(sys_locale);
185     sys_locale = NULL;
186   }
187 
188   if (sys_lang_country) {
189     free(sys_lang_country);
190     sys_lang_country = NULL;
191   }
192 }
193 
bl_get_locale(void)194 char *bl_get_locale(void) {
195   if (sys_locale) {
196     return sys_locale;
197   } else {
198     return "C";
199   }
200 }
201 
bl_get_lang(void)202 char *bl_get_lang(void) {
203   if (sys_lang) {
204     return sys_lang;
205   } else {
206     return "en";
207   }
208 }
209 
bl_get_country(void)210 char *bl_get_country(void) {
211   if (sys_country) {
212     return sys_country;
213   } else {
214     return "US";
215   }
216 }
217 
218 #ifndef USE_WIN32API
219 
bl_get_codeset(void)220 char *bl_get_codeset(void) {
221   if (sys_codeset) {
222     return sys_codeset;
223   } else if (sys_lang) {
224     int count;
225     char *lang;
226     u_int lang_len;
227 
228     lang_len = strlen(sys_lang) + 1;
229     if (sys_country) {
230       /* "+ 1" is for '_' */
231       lang_len += strlen(sys_country) + 1;
232     }
233 
234     if ((lang = alloca(lang_len)) == NULL) {
235       return "ISO8859-1";
236     }
237 
238     if (sys_country) {
239       sprintf(lang, "%s_%s", sys_lang, sys_country);
240     } else {
241       sprintf(lang, "%s", sys_lang);
242     }
243 
244 #ifdef __DEBUG
245     bl_debug_printf("lang -> %s\n", lang);
246 #endif
247 
248     for (count = 0; count < sizeof(lang_codeset_table) / sizeof(lang_codeset_table[0]); count++) {
249       if (strncmp(lang, lang_codeset_table[count].lang,
250                   /* lang_len *- 1* is excluing NULL */
251                   BL_MIN(lang_len - 1, strlen(lang_codeset_table[count].lang))) == 0) {
252         return lang_codeset_table[count].codeset;
253       }
254     }
255   }
256 
257   return "ISO8859-1";
258 }
259 
260 #endif /* USE_WIN32API */
261 
262 #ifdef HAVE_WINDOWS_H
263 
264 typedef struct cp_cs_table {
265   int codepage;
266   char *codeset;
267 
268 } cp_cs_table_t;
269 
270 static cp_cs_table_t cp_cs_table[] = {
271     {
272      1250, "CP1250",
273     }, /* EastEurope_CHARSET */
274     {
275      1251, "CP1251",
276     }, /* RUSSIAN_CHARSET */
277     {
278      1252, "CP1252",
279     }, /* ANSI_CHARSET */
280     {
281      1253, "CP1253",
282     }, /* GREEK_CHARSET */
283     {
284      1254, "CP1254",
285     }, /* TURKISH_CHARSET */
286     {
287      1255, "CP1255",
288     }, /* HEBREW_CHARSET */
289     {
290      1256, "CP1256",
291     }, /* ARABIC_CHARSET */
292     {
293      1257, "CP1257",
294     }, /* BALTIC_CHARSET */
295     {
296      1258, "CP1258",
297     }, /* VIETNAMESE_CHARSET */
298     {
299      874, "ISO8859-11",
300     }, /* THAI_CHARSET XXX CP874 is extended from ISO8859-11 */
301     {
302      932, "SJIS",
303     }, /* SHIFTJIS_CHARSET */
304     {
305      936, "GBK",
306     }, /* GB2313_CHARSET */
307     {
308      949, "UHC",
309     }, /* HANGEUL_CHARSET */
310     {
311      950, "BIG5",
312     }, /* CHINESEBIG5_CHARSET */
313 };
314 
bl_get_codeset_win32(void)315 char *bl_get_codeset_win32(void) {
316   int count;
317   int codepage;
318 
319   codepage = GetACP();
320 
321   for (count = 0; count < sizeof(cp_cs_table) / sizeof(cp_cs_table_t); count++) {
322     if (cp_cs_table[count].codepage == codepage) {
323       return cp_cs_table[count].codeset;
324     }
325   }
326 
327   return "ISO8859-1";
328 }
329 
330 #endif /* HAVE_WINDOWS_H */
331