Add discussion point for XIM/UTF-8 locales.
[fvwm.git] / libs / Ficonv.c
blob46199fd7d46eff96df5608b60f135e26e731eacb
1 /* -*-c-*- */
2 /* Copyright (C) 2002 Olivier Chapuis */
3 /* This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 /* Some code (convert_charsets) inspired by the glib-2 (gutf8.c) copyrighted
19 * by Tom Tromey & Red Hat, Inc. */
21 /* ---------------------------- included header files ---------------------- */
23 #include "config.h"
24 #include <stdio.h>
25 #include <errno.h>
27 #include <X11/Xlib.h>
29 #include "FlocaleCharset.h"
30 #include "Ficonv.h"
31 #include "Strings.h"
33 #if FiconvSupport
34 #include <iconv.h>
36 #if defined(USE_LIBICONV) && !defined (_LIBICONV_H)
37 #error libiconv in use but included iconv.h not from libiconv
38 #endif
39 #if !defined(USE_LIBICONV) && defined (_LIBICONV_H)
40 #error libiconv not in use but included iconv.h is from libiconv
41 #endif
42 #endif /* FiconvSupport */
44 /* ---------------------------- local definitions -------------------------- */
46 /* ---------------------------- local macros ------------------------------- */
48 /* ---------------------------- imports ------------------------------------ */
50 /* ---------------------------- included code files ------------------------ */
52 /* ---------------------------- local types -------------------------------- */
54 /* ---------------------------- forward declarations ----------------------- */
56 /* ---------------------------- local variables ---------------------------- */
58 static Bool FiconvInitialized = False;
59 static FlocaleCharset *FLCIconvUtf8Charset = NULL; /* UTF-8 charset */
60 static FlocaleCharset *FLCIconvDefaultCharset = NULL;
61 static int do_transliterate_utf8 = 0;
63 /* ---------------------------- exported variables (globals) --------------- */
65 /* ---------------------------- local functions ---------------------------- */
67 static
68 Bool is_iconv_supported(char *c1, char *c2)
70 Ficonv_t cd1,cd2;
71 Bool r = False;
72 int dummy;
74 if (!FiconvSupport || !c1 || !c2)
75 return False;
77 cd1 = Ficonv_open(c1, c2);
78 cd2 = Ficonv_open(c2, c1);
79 if (cd1 != (Ficonv_t) -1 && cd2 != (Ficonv_t) -1)
80 r = True;
81 if (cd1 != (Ficonv_t) -1)
82 dummy = Ficonv_close(cd1);
83 if (cd2 != (Ficonv_t) -1)
84 dummy = Ficonv_close(cd2);
85 return r;
88 #define TRANSLIT_SUFFIX "//TRANSLIT"
89 static
90 char *translit_csname(char *cs)
92 return CatString2(cs, TRANSLIT_SUFFIX);
95 static
96 int is_translit_supported(char *c1, char *c2)
98 Ficonv_t cd;
100 if (!FiconvSupport || !c1 || !c2)
101 return 0;
103 cd = Ficonv_open(translit_csname(c1),c2);
104 if (cd == (Ficonv_t) -1)
106 return 0;
108 Ficonv_close(cd);
109 cd = Ficonv_open(translit_csname(c2),c1);
110 if (cd == (Ficonv_t) -1)
112 return 0;
114 Ficonv_close(cd);
116 return 1;
119 static
120 int set_default_iconv_charsets(FlocaleCharset *fc)
122 int i=0,j=0;
124 if (!FiconvSupport || FLCIconvUtf8Charset == NULL || fc == NULL)
125 return False;
127 while(FLC_GET_LOCALE_CHARSET(FLCIconvUtf8Charset,i) != NULL)
129 j = 0;
130 while(FLC_GET_LOCALE_CHARSET(fc,j) != NULL)
132 if (is_iconv_supported(
133 FLC_GET_LOCALE_CHARSET(
134 FLCIconvUtf8Charset,i),
135 FLC_GET_LOCALE_CHARSET(fc,j)))
137 FLC_SET_ICONV_INDEX(FLCIconvUtf8Charset,i);
138 FLC_SET_ICONV_INDEX(fc,j);
140 if (is_translit_supported(
141 FLC_GET_LOCALE_CHARSET(
142 FLCIconvUtf8Charset,i),
143 FLC_GET_LOCALE_CHARSET(fc,j)))
145 FLC_SET_ICONV_TRANSLIT_CHARSET(
146 fc, safestrdup(translit_csname(
147 FLC_GET_LOCALE_CHARSET(
148 fc,j))));
149 } else {
150 FLC_SET_ICONV_TRANSLIT_CHARSET(
151 fc, FLC_TRANSLIT_NOT_SUPPORTED);
154 return 1;
156 j++;
158 i++;
160 FLC_SET_ICONV_INDEX(FLCIconvUtf8Charset,
161 FLC_INDEX_ICONV_CHARSET_NOT_FOUND);
162 FLC_SET_ICONV_INDEX(fc, FLC_INDEX_ICONV_CHARSET_NOT_FOUND);
163 return 0;
166 static
167 void set_iconv_charset_index(FlocaleCharset *fc)
169 int i = 0;
171 if (!FiconvSupport || fc == NULL)
172 return;
174 if (FLC_DO_ICONV_CHARSET_INITIALIZED(fc))
175 return; /* already set */
176 if (FLCIconvUtf8Charset == NULL ||
177 !FLC_DO_ICONV_CHARSET_INITIALIZED(FLCIconvUtf8Charset))
179 FLC_SET_ICONV_INDEX(fc, FLC_INDEX_ICONV_CHARSET_NOT_FOUND);
180 return;
182 while(FLC_GET_LOCALE_CHARSET(fc,i) != NULL)
184 if (is_iconv_supported(
185 FLC_GET_ICONV_CHARSET(FLCIconvUtf8Charset),
186 FLC_GET_LOCALE_CHARSET(fc,i)))
188 FLC_SET_ICONV_INDEX(fc,i);
189 if (is_translit_supported(
190 FLC_GET_ICONV_CHARSET(FLCIconvUtf8Charset),
191 FLC_GET_LOCALE_CHARSET(fc,i)))
193 FLC_SET_ICONV_TRANSLIT_CHARSET(
194 fc, safestrdup(
195 translit_csname(
196 FLC_GET_LOCALE_CHARSET(
197 fc,i))));
198 } else {
199 FLC_SET_ICONV_TRANSLIT_CHARSET(
200 fc, FLC_TRANSLIT_NOT_SUPPORTED);
202 return;
204 i++;
206 FLC_SET_ICONV_INDEX(fc, FLC_INDEX_ICONV_CHARSET_NOT_FOUND);
209 static
210 char *convert_charsets(const char *in_charset, const char *out_charset,
211 const char *in, unsigned int in_size)
213 static int error_count = 0;
214 Ficonv_t cd;
215 int have_error = 0;
216 int is_finished = 0;
217 size_t nconv;
218 size_t insize,outbuf_size,outbytes_remaining,len;
219 const char *inptr;
220 char *dest;
221 char *outp;
223 if (in == NULL || !FiconvSupport)
224 return NULL;
226 cd = Ficonv_open(out_charset, in_charset);
227 if (cd == (Ficonv_t) -1)
229 /* Something went wrong. */
230 if (error_count > FICONV_CONVERSION_MAX_NUMBER_OF_WARNING)
231 return NULL;
232 error_count++;
233 if (errno == EINVAL)
235 fprintf(
236 stderr,
237 "[fvwm][convert_charsets]: WARNING -\n\t");
238 fprintf(
239 stderr,
240 "conversion from `%s' to `%s' not available\n",
241 in_charset,out_charset);
243 else
245 fprintf(
246 stderr,
247 "[fvwm][convert_charsets]: WARNING -\n\t");
248 fprintf(
249 stderr,
250 "conversion from `%s' to `%s' fail (init)\n",
251 in_charset,out_charset);
253 /* Terminate the output string. */
254 return NULL;
257 /* in maybe a none terminate string */
258 len = in_size;
260 outbuf_size = len + 1;
261 outbytes_remaining = outbuf_size - 1;
262 insize = len;
263 outp = dest = safemalloc(outbuf_size);
265 inptr = in;
267 for (is_finished = 0; is_finished == 0; )
269 nconv = Ficonv(
270 cd, (ICONV_ARG_CONST char **)&inptr, &insize, &outp,
271 &outbytes_remaining);
272 is_finished = 1;
273 if (nconv == (size_t) - 1)
275 switch (errno)
277 case EINVAL:
278 /* Incomplete text, do not report an error */
279 break;
280 case E2BIG:
282 size_t used = outp - dest;
284 outbuf_size *= 2;
285 dest = realloc (dest, outbuf_size);
287 outp = dest + used;
288 /* -1 for nul */
289 outbytes_remaining = outbuf_size - used - 1;
290 is_finished = 0;
291 break;
293 #ifdef EILSEQ
294 case EILSEQ:
295 /* Something went wrong. */
296 if (error_count <=
297 FICONV_CONVERSION_MAX_NUMBER_OF_WARNING)
299 fprintf(
300 stderr,
301 "[fvwm][convert_charsets]:"
302 " WARNING -\n\t");
303 fprintf(
304 stderr,
305 "Invalid byte sequence during"
306 " conversion from %s to %s\n",
307 in_charset,out_charset);
309 have_error = 1;
310 break;
311 #endif
312 default:
313 if (error_count <=
314 FICONV_CONVERSION_MAX_NUMBER_OF_WARNING)
316 fprintf(
317 stderr,
318 "[fvwm][convert_charsets]:"
319 " WARNING -\n\t");
320 fprintf(
321 stderr,
322 "Error during conversion from"
323 " %s to %s\n", in_charset,
324 out_charset);
326 have_error = 1;
327 break;
332 /* Terminate the output string */
333 *outp = '\0';
335 if (Ficonv_close (cd) != 0)
337 fprintf(
338 stderr,
339 "[fvwm][convert_charsets]: WARNING - iconv_close"
340 " fail\n");
343 if (have_error)
345 error_count++;
346 free (dest);
347 return NULL;
349 else
351 return dest;
355 static
356 void FiconvInit(Display *dpy, const char *module)
358 int suc = False;
360 if (!FiconvSupport || FiconvInitialized)
361 return;
363 FiconvInitialized = True;
364 FlocaleCharsetInit(dpy, module);
365 FLCIconvUtf8Charset = FlocaleCharsetGetUtf8Charset();
366 FLCIconvDefaultCharset = FlocaleCharsetGetFLCXOMCharset();
368 suc = set_default_iconv_charsets(FLCIconvDefaultCharset);
369 if (!suc)
371 FLCIconvDefaultCharset = FlocaleCharsetGetLocaleCharset();
372 suc = set_default_iconv_charsets(FLCIconvDefaultCharset);
374 if (!suc)
376 fprintf(stderr,
377 "[%s][FiconvInit]: WARN -- Cannot get default "
378 "iconv charset for default charsets '%s' and '%s'\n",
379 module,
380 FLC_DEBUG_GET_X_CHARSET(
381 FlocaleCharsetGetFLCXOMCharset()),
382 FLC_DEBUG_GET_X_CHARSET(FLCIconvDefaultCharset));
383 FLCIconvUtf8Charset = NULL;
384 FLCIconvDefaultCharset = NULL;
387 #if FLOCALE_DEBUG_CHARSET
388 fprintf(stderr,"[FiconvInit] iconv charset: x:%s, iconv:%s\n",
389 FLC_DEBUG_GET_X_CHARSET(FLCIconvDefaultCharset),
390 FLC_DEBUG_GET_ICONV_CHARSET(FLCIconvDefaultCharset));
391 fprintf(stderr,"[FiconvInit] UTF-8 charset: x:%s, iconv:%s\n",
392 FLC_DEBUG_GET_X_CHARSET(FLCIconvUtf8Charset),
393 FLC_DEBUG_GET_ICONV_CHARSET(FLCIconvUtf8Charset));
394 #endif
398 static
399 FlocaleCharset *FiconvSetupConversion(Display *dpy, FlocaleCharset *fc)
401 FlocaleCharset *my_fc = NULL;
403 if (!FiconvSupport)
405 return NULL;
408 if (!FiconvInitialized)
410 FiconvInit(dpy, "fvwm");
413 if (FLCIconvUtf8Charset == NULL)
415 return NULL;
418 if (fc == NULL)
420 my_fc = FLCIconvDefaultCharset;
421 if (my_fc == NULL)
423 return NULL;
426 else
428 my_fc = fc;
431 if (!FLC_DO_ICONV_CHARSET_INITIALIZED(my_fc))
433 set_iconv_charset_index(my_fc);
434 #if FLOCALE_DEBUG_CHARSET
435 fprintf(stderr, "[Flocale] set up iconv charset: "
436 "x: %s, iconv: %s\n",
437 FLC_DEBUG_GET_X_CHARSET(my_fc),
438 FLC_DEBUG_GET_ICONV_CHARSET(my_fc));
439 #endif
440 if (!FLC_HAVE_ICONV_CHARSET(my_fc))
442 fprintf(
443 stderr,
444 "[fvwmlibs] cannot get iconv converter "
445 "for charset %s\n",
446 FLC_DEBUG_GET_X_CHARSET(my_fc));
447 return NULL;
450 if (!FLC_HAVE_ICONV_CHARSET(my_fc))
452 return NULL;
455 return my_fc;
458 /* ---------------------------- interface functions ------------------------ */
460 /* set transliteration state */
461 void FiconvSetTransliterateUtf8(int toggle)
463 switch (toggle)
465 case -1:
466 do_transliterate_utf8 ^= 1;
467 break;
468 case 0:
469 case 1:
470 do_transliterate_utf8 = toggle;
471 break;
472 default:
473 do_transliterate_utf8 = 0;
479 /* conversion from UTF8 to the "current" charset */
480 char *FiconvUtf8ToCharset(Display *dpy, FlocaleCharset *fc,
481 const char *in, unsigned int in_size)
483 char *out = NULL;
484 FlocaleCharset *my_fc = NULL;
486 if (!FiconvSupport)
488 return NULL;
491 my_fc = FiconvSetupConversion(dpy, fc);
492 if (my_fc == NULL)
494 return NULL;
497 #if FLOCALE_DEBUG_ICONV
498 fprintf(stderr, "[FiconvUtf8ToCharset] conversion from %s to %s\n",
499 FLC_DEBUG_GET_ICONV_CHARSET(FLCIconvUtf8Charset),
500 FLC_DEBUG_GET_ICONV_CHARSET(my_fc));
501 #endif
503 if (FLC_ENCODING_TYPE_IS_UTF_8(my_fc))
505 /* in can be a none terminate string so do not use CopyString
507 out = safemalloc(in_size+1);
508 strncpy(out, in, in_size);
509 out[in_size]=0;
511 else
513 char *to_cs;
514 if (do_transliterate_utf8 && FLC_IS_TRANSLIT_SUPPORTED(my_fc))
516 to_cs = FLC_GET_ICONV_TRANSLIT_CHARSET(my_fc);
518 else
520 to_cs = FLC_GET_ICONV_CHARSET(my_fc);
522 out = convert_charsets(
523 FLC_GET_ICONV_CHARSET(FLCIconvUtf8Charset),
524 to_cs,
525 in, in_size);
528 return out;
531 /* conversion from the current charset to UTF8 */
532 char *FiconvCharsetToUtf8(Display *dpy, FlocaleCharset *fc,
533 const char *in, unsigned int in_size)
535 char *out = NULL;
536 FlocaleCharset *my_fc = NULL;
538 if (!FiconvSupport)
540 return NULL;
543 my_fc = FiconvSetupConversion(dpy, fc);
544 if (my_fc == NULL)
546 return NULL;
549 #if FLOCALE_DEBUG_ICONV
550 fprintf(stderr, "[FiconvCharsetToUtf8] conversion from %s to %s\n",
551 FLC_DEBUG_GET_ICONV_CHARSET(my_fc),
552 FLC_DEBUG_GET_ICONV_CHARSET(FLCIconvUtf8Charset));
553 #endif
555 if (FLC_ENCODING_TYPE_IS_UTF_8(my_fc))
557 /* in can be a non terminate string so do not use CopyString */
558 out = safemalloc(in_size+1);
559 strncpy(out, in, in_size);
560 out[in_size]=0;
562 else
564 out = convert_charsets(
565 FLC_GET_ICONV_CHARSET(my_fc),
566 FLC_GET_ICONV_CHARSET(FLCIconvUtf8Charset),
567 in, in_size);
569 return out;
572 /* conversion from charset to charset */
573 char *FiconvCharsetToCharset(
574 Display *dpy, FlocaleCharset *in_fc, FlocaleCharset *out_fc,
575 const char *in, unsigned int in_size)
577 char *out = NULL;
578 char *tmp = NULL;
579 int tmp_len;
580 Bool free_tmp = False;
581 FlocaleCharset *my_in_fc;
582 FlocaleCharset *my_out_fc;
584 if (!FiconvSupport ||
585 (my_in_fc = FiconvSetupConversion(dpy, in_fc)) == NULL)
587 return NULL;
589 if (!FiconvSupport ||
590 (my_out_fc = FiconvSetupConversion(dpy, out_fc)) == NULL)
592 return NULL;
595 tmp = (char *)in;
596 tmp_len = in_size;
597 if (!FLC_ENCODING_TYPE_IS_UTF_8(my_in_fc))
599 tmp = FiconvCharsetToUtf8(
600 dpy, my_in_fc, (const char *)in, in_size);
601 if (tmp != NULL)
603 free_tmp = True;
604 tmp_len = strlen(tmp);
606 else
608 /* fail to convert */
609 return NULL;
613 out = tmp;
614 if (!FLC_ENCODING_TYPE_IS_UTF_8(my_out_fc))
616 out = FiconvUtf8ToCharset(
617 dpy, my_out_fc, (const char *)tmp, tmp_len);
618 if (free_tmp)
620 free(tmp);
624 return out;