chiark / gitweb /
Imported Upstream version 1.0.0
[e16] / src / lang.c
1 /*
2  * Copyright (C) 2000-2007 Carsten Haitzler, Geoff Harrison and various contributors
3  * Copyright (C) 2004-2008 Kim Woelders
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy
6  * of this software and associated documentation files (the "Software"), to
7  * deal in the Software without restriction, including without limitation the
8  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9  * sell copies of the Software, and to permit persons to whom the Software is
10  * furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies of the Software, its documentation and marketing & publicity
14  * materials, and acknowledgment shall be given in the documentation, materials
15  * and software packages that this Software was used.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20  * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  */
24 #include "E.h"
25 #include "emodule.h"
26 #include "lang.h"
27 #include <X11/Xlib.h>
28
29 #ifdef HAVE_LOCALE_H
30 #include <locale.h>
31 #endif
32
33 #if HAVE_LANGINFO_CODESET
34 #include <langinfo.h>
35 #endif
36
37 #ifndef ENABLE_NLS
38 #define bindtextdomain(pkg,locale)
39 #define textdomain(pkg)
40 #define bind_textdomain_codeset(pkg,enc)
41 #endif
42
43 #if HAVE_ICONV
44
45 #include <iconv.h>
46 #define BAD_CD ((iconv_t)-1)
47
48 #ifndef ICONV_CONST             /* Not sure this is necessary */
49 #define ICONV_CONST
50 #endif
51
52 static iconv_t      iconv_cd_int2utf8 = BAD_CD;
53 static iconv_t      iconv_cd_utf82int = BAD_CD;
54 static iconv_t      iconv_cd_int2loc = BAD_CD;
55 static iconv_t      iconv_cd_loc2int = BAD_CD;
56
57 static char        *
58 Eiconv(iconv_t icd, const char *txt, size_t len)
59 {
60    char                buf[4096];
61    ICONV_CONST char   *pi;
62    char               *po;
63    size_t              err, ni, no;
64
65    pi = (ICONV_CONST char *)txt;
66    po = buf;
67    ni = (len > 0) ? len : strlen(txt);
68    if (icd == BAD_CD)
69       return Estrndup(txt, ni);
70    no = sizeof(buf);
71    err = iconv(icd, &pi, &ni, &po, &no);
72
73    po = Estrndup(buf, sizeof(buf) - no);
74
75    return po;
76 }
77
78 static              iconv_t
79 EiconvOpen(const char *to, const char *from)
80 {
81    iconv_t             icd;
82
83    icd = iconv_open(to, from);
84    if (icd == BAD_CD)
85       Eprintf("*** WARNING - Missing conversion %s->%s\n", from, to);
86
87    return icd;
88 }
89
90 #endif
91
92 #if 0                           /* Unused */
93 /* Convert locale to internal format (alloc always) */
94 char               *
95 EstrLoc2Int(const char *str, int len)
96 {
97    if (str == NULL)
98       return NULL;
99
100 #if HAVE_ICONV
101    if (iconv_cd_loc2int != BAD_CD)
102       return Eiconv(iconv_cd_loc2int, str, len);
103 #endif
104
105    if (len <= 0)
106       len = strlen(str);
107    return Estrndup(str, len);
108 }
109 #endif
110
111 /* Convert UTF-8 to internal format (alloc always) */
112 char               *
113 EstrUtf82Int(const char *str, int len)
114 {
115    if (str == NULL)
116       return NULL;
117
118 #if HAVE_ICONV
119    if (iconv_cd_utf82int != BAD_CD)
120       return Eiconv(iconv_cd_utf82int, str, len);
121 #endif
122
123    if (len <= 0)
124       len = strlen(str);
125    return Estrndup(str, len);
126 }
127
128 /* Convert internal to required (alloc only if necessary) */
129 const char         *
130 EstrInt2Enc(const char *str, int want_utf8)
131 {
132 #if HAVE_ICONV
133    if (Mode.locale.utf8_int == want_utf8)
134       return str;
135
136    if (str == NULL)
137       return NULL;
138
139    if (want_utf8)
140       return Eiconv(iconv_cd_int2utf8, str, strlen(str));
141
142    return Eiconv(iconv_cd_int2loc, str, strlen(str));
143 #else
144    want_utf8 = 0;
145    return str;
146 #endif
147 }
148
149 /* Free string returned by EstrInt2Enc() */
150 void
151 EstrInt2EncFree(const char *str, int want_utf8)
152 {
153 #if HAVE_ICONV
154    if (Mode.locale.utf8_int == want_utf8)
155       return;
156
157    Efree((char *)str);
158 #else
159    str = NULL;
160    want_utf8 = 0;
161 #endif
162 }
163
164 /*
165  * Stuff to do mb/utf8 <-> wc conversions.
166  */
167 #if HAVE_ICONV
168 static iconv_t      iconv_cd_str2wcs = BAD_CD;
169 static iconv_t      iconv_cd_wcs2str = BAD_CD;
170 #endif
171
172 int
173 EwcOpen(int utf8)
174 {
175 #if HAVE_ICONV
176    const char         *enc;
177
178    if (utf8)
179       enc = "UTF-8";
180    else
181       enc = nl_langinfo(CODESET);
182
183 #if SIZEOF_WCHAR_T == 4
184    iconv_cd_str2wcs = EiconvOpen("UCS-4", enc);
185    iconv_cd_wcs2str = EiconvOpen(enc, "UCS-4");
186 #else
187    iconv_cd_str2wcs = EiconvOpen("WCHAR_T", enc);
188    iconv_cd_wcs2str = EiconvOpen(enc, "WCHAR_T");
189 #endif
190
191    if (iconv_cd_str2wcs != BAD_CD && iconv_cd_wcs2str != BAD_CD)
192       return 0;
193
194    EwcClose();
195    return -1;
196 #else
197    /* NB! This case will not work properly if needed MB encoding is utf8
198     * but locale isn't */
199    utf8 = 0;
200    return 0;
201 #endif
202 }
203
204 void
205 EwcClose(void)
206 {
207 #if HAVE_ICONV
208    if (iconv_cd_str2wcs != BAD_CD)
209       iconv_close(iconv_cd_str2wcs);
210    iconv_cd_str2wcs = BAD_CD;
211    if (iconv_cd_wcs2str != BAD_CD)
212       iconv_close(iconv_cd_wcs2str);
213    iconv_cd_wcs2str = BAD_CD;
214 #endif
215 }
216
217 int
218 EwcStrToWcs(const char *str, int len, wchar_t * wcs, int wcl)
219 {
220 #if HAVE_ICONV
221    ICONV_CONST char   *pi;
222    char               *po;
223    size_t              ni, no, rc;
224    char                buf[4096];
225
226    pi = (ICONV_CONST char *)str;
227    ni = len;
228
229    if (!wcs)
230      {
231         no = 4096;
232         po = buf;
233         rc = iconv(iconv_cd_str2wcs, &pi, &ni, &po, &no);
234         if (rc == (size_t) (-1) || no == 0)
235            return -1;
236         wcl = (4096 - no) / sizeof(wchar_t);
237         return wcl;
238      }
239
240    po = (char *)wcs;
241    no = wcl * sizeof(wchar_t);
242    rc = iconv(iconv_cd_str2wcs, &pi, &ni, &po, &no);
243    if (rc == (size_t) (-1))
244       return 0;
245    return wcl - no / sizeof(wchar_t);
246 #else
247    if (!wcs)
248       return mbstowcs(NULL, str, 0);
249
250    mbstowcs(wcs, str, wcl);
251    wcs[wcl] = (wchar_t) '\0';
252
253    len = 0;
254    return wcl;
255 #endif
256 }
257
258 int
259 EwcWcsToStr(const wchar_t * wcs, int wcl, char *str, int len)
260 {
261 #if HAVE_ICONV
262    ICONV_CONST char   *pi;
263    size_t              ni, no, rc;
264
265    pi = (ICONV_CONST char *)wcs;
266    ni = wcl * sizeof(wchar_t);
267    no = len;
268    rc = iconv(iconv_cd_wcs2str, &pi, &ni, &str, &no);
269    if (rc == (size_t) (-1))
270       return 0;
271    return len - no;
272 #else
273    int                 i, j, n;
274
275    j = 0;
276    for (i = 0; i < wcl; i++)
277      {
278         if (j + (int)MB_CUR_MAX > len)
279            break;
280         n = wctomb(str + j, wcs[i]);
281         if (n > 0)
282            j += n;
283      }
284    str[j] = '\0';
285    return j;
286 #endif
287 }
288
289 /*
290  * Setup
291  */
292
293 static struct {
294    char               *internal;
295    char               *exported;
296 } Conf_locale =
297 {
298 NULL, NULL};
299
300 static struct {
301    char                init;
302    char               *env_language;
303    char               *env_lc_all;
304    char               *env_lc_messages;
305    char               *env_lang;
306 } locale_data;
307
308 static void
309 LangEnvironmentSetup(const char *locale)
310 {
311    /* Precedence:  LANGUAGE, LC_ALL, LC_MESSAGES, LANG */
312    if (locale)
313      {
314         /* Set requested */
315         Esetenv("LANGUAGE", locale);
316         Esetenv("LC_ALL", locale);
317         Esetenv("LANG", locale);
318      }
319    else
320      {
321         /* Restore saved */
322         Esetenv("LANGUAGE", locale_data.env_language);
323         Esetenv("LC_ALL", locale_data.env_lc_all);
324         Esetenv("LC_MESSAGES", locale_data.env_lc_messages);
325         Esetenv("LANG", locale_data.env_lang);
326      }
327 }
328
329 static void
330 LangEnvironmentSave(void)
331 {
332    if (locale_data.init)
333       return;
334    locale_data.init = 1;
335
336    locale_data.env_language = Estrdup(getenv("LANGUAGE"));
337    locale_data.env_lc_all = Estrdup(getenv("LC_ALL"));
338    locale_data.env_lc_messages = Estrdup(getenv("LC_MESSAGES"));
339    locale_data.env_lang = Estrdup(getenv("LANG"));
340 }
341
342 void
343 LangExport(void)
344 {
345    if (Conf_locale.exported)
346       LangEnvironmentSetup(Conf_locale.exported);
347    else if (Conf_locale.internal)
348       LangEnvironmentSetup(NULL);
349 }
350
351 void
352 LangInit(void)
353 {
354    const char         *enc_loc, *enc_int;
355
356    if (!locale_data.init)
357       LangEnvironmentSave();
358
359    LangEnvironmentSetup(Conf_locale.internal);
360
361    setlocale(LC_ALL, "");       /* Set up things according to env vars */
362
363    bindtextdomain(PACKAGE, LOCALEDIR);
364    textdomain(PACKAGE);
365
366    if (!XSupportsLocale())
367       setlocale(LC_ALL, "C");
368    XSetLocaleModifiers("");
369
370    /* I dont want any internationalisation of my numeric input & output */
371    setlocale(LC_NUMERIC, "C");
372
373    /* Get the environment character encoding */
374 #if HAVE_LANGINFO_CODESET
375    enc_loc = nl_langinfo(CODESET);
376 #else
377    enc_loc = "ISO-8859-1";
378 #endif
379
380    /* Debug - possibility to set desired internal representation */
381    enc_int = getenv("E_CHARSET");
382    if (enc_int)
383       bind_textdomain_codeset(PACKAGE, enc_int);
384    else
385       enc_int = enc_loc;
386
387    Mode.locale.lang = setlocale(LC_MESSAGES, NULL);
388    if (EDebug(EDBUG_TYPE_VERBOSE))
389      {
390         Eprintf("Locale: %s\n", setlocale(LC_ALL, NULL));
391         Eprintf("Character encoding: locale=%s internal=%s MB_CUR_MAX=%zu\n",
392                 enc_loc, enc_int, MB_CUR_MAX);
393      }
394
395    if (!Estrcasecmp(enc_loc, "utf8") || !Estrcasecmp(enc_loc, "utf-8"))
396       Mode.locale.utf8_loc = 1;
397    if (!Estrcasecmp(enc_int, "utf8") || !Estrcasecmp(enc_int, "utf-8"))
398       Mode.locale.utf8_int = 1;
399
400 #if HAVE_ICONV
401    if (Mode.locale.utf8_int && Mode.locale.utf8_loc)
402       return;
403    if (Mode.locale.utf8_int)
404      {
405         iconv_cd_loc2int = EiconvOpen("UTF-8", enc_loc);
406         iconv_cd_int2loc = EiconvOpen(enc_loc, "UTF-8");
407      }
408    else
409      {
410         iconv_cd_utf82int = EiconvOpen(enc_loc, "UTF-8");
411         iconv_cd_int2utf8 = EiconvOpen("UTF-8", enc_loc);
412      }
413 #endif
414 }
415
416 void
417 LangExit(void)
418 {
419 #if HAVE_ICONV
420    if (iconv_cd_int2utf8 != BAD_CD)
421       iconv_close(iconv_cd_int2utf8);
422    if (iconv_cd_utf82int != BAD_CD)
423       iconv_close(iconv_cd_utf82int);
424    if (iconv_cd_int2loc != BAD_CD)
425       iconv_close(iconv_cd_int2loc);
426    if (iconv_cd_loc2int != BAD_CD)
427       iconv_close(iconv_cd_loc2int);
428    iconv_cd_int2utf8 = iconv_cd_utf82int = BAD_CD;
429    iconv_cd_int2loc = iconv_cd_loc2int = BAD_CD;
430 #endif
431
432    LangEnvironmentSetup(NULL);
433 }
434
435 static void
436 LangCfgChange(void *item __UNUSED__, const char *locale)
437 {
438    if (*locale == '\0')
439       locale = NULL;
440    LangExit();
441    _EFDUP(Conf_locale.internal, locale);
442    LangInit();
443 }
444
445 static const CfgItem LocaleCfgItems[] = {
446    CFG_FUNC_STR(Conf_locale, internal, LangCfgChange),
447    CFG_ITEM_STR(Conf_locale, exported),
448 };
449 #define N_CFG_ITEMS (sizeof(LocaleCfgItems)/sizeof(CfgItem))
450
451 extern const EModule ModLocale;
452 const EModule       ModLocale = {
453    "locale", NULL,
454    NULL,
455    {0, NULL},
456    {N_CFG_ITEMS, LocaleCfgItems}
457 };