Include <config.h> unconditionally.
[gnulib.git] / lib / unicodeio.c
1 /* Unicode character output to streams with locale dependent encoding.
2
3    Copyright (C) 2000-2003, 2006 Free Software Foundation, Inc.
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2, or (at your option)
8    any later version.
9
10    This program 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
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License along
16    with this program; if not, write to the Free Software Foundation,
17    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
18
19 /* Written by Bruno Haible <haible@clisp.cons.org>.  */
20
21 /* Note: This file requires the locale_charset() function.  See in
22    libiconv-1.8/libcharset/INTEGRATE for how to obtain it.  */
23
24 #include <config.h>
25
26 /* Specification.  */
27 #include "unicodeio.h"
28
29 #include <stdio.h>
30 #include <string.h>
31 #include <errno.h>
32
33 #if HAVE_ICONV
34 # include <iconv.h>
35 #endif
36
37 #include <error.h>
38
39 #include "gettext.h"
40 #define _(msgid) gettext (msgid)
41 #define N_(msgid) msgid
42
43 #include "localcharset.h"
44
45 /* When we pass a Unicode character to iconv(), we must pass it in a
46    suitable encoding. The standardized Unicode encodings are
47    UTF-8, UCS-2, UCS-4, UTF-16, UTF-16BE, UTF-16LE, UTF-7.
48    UCS-2 supports only characters up to \U0000FFFF.
49    UTF-16 and variants support only characters up to \U0010FFFF.
50    UTF-7 is way too complex and not supported by glibc-2.1.
51    UCS-4 specification leaves doubts about endianness and byte order
52    mark. glibc currently interprets it as big endian without byte order
53    mark, but this is not backed by an RFC.
54    So we use UTF-8. It supports characters up to \U7FFFFFFF and is
55    unambiguously defined.  */
56
57 /* Stores the UTF-8 representation of the Unicode character wc in r[0..5].
58    Returns the number of bytes stored, or -1 if wc is out of range.  */
59 static int
60 utf8_wctomb (unsigned char *r, unsigned int wc)
61 {
62   int count;
63
64   if (wc < 0x80)
65     count = 1;
66   else if (wc < 0x800)
67     count = 2;
68   else if (wc < 0x10000)
69     count = 3;
70   else if (wc < 0x200000)
71     count = 4;
72   else if (wc < 0x4000000)
73     count = 5;
74   else if (wc <= 0x7fffffff)
75     count = 6;
76   else
77     return -1;
78
79   switch (count)
80     {
81       /* Note: code falls through cases! */
82       case 6: r[5] = 0x80 | (wc & 0x3f); wc = wc >> 6; wc |= 0x4000000;
83       case 5: r[4] = 0x80 | (wc & 0x3f); wc = wc >> 6; wc |= 0x200000;
84       case 4: r[3] = 0x80 | (wc & 0x3f); wc = wc >> 6; wc |= 0x10000;
85       case 3: r[2] = 0x80 | (wc & 0x3f); wc = wc >> 6; wc |= 0x800;
86       case 2: r[1] = 0x80 | (wc & 0x3f); wc = wc >> 6; wc |= 0xc0;
87       case 1: r[0] = wc;
88     }
89
90   return count;
91 }
92
93 /* Luckily, the encoding's name is platform independent.  */
94 #define UTF8_NAME "UTF-8"
95
96 /* Converts the Unicode character CODE to its multibyte representation
97    in the current locale and calls the SUCCESS callback on the resulting
98    byte sequence.  If an error occurs, invokes the FAILURE callback instead,
99    passing it CODE and an English error string.
100    Returns whatever the callback returned.
101    Assumes that the locale doesn't change between two calls.  */
102 long
103 unicode_to_mb (unsigned int code,
104                long (*success) (const char *buf, size_t buflen,
105                                 void *callback_arg),
106                long (*failure) (unsigned int code, const char *msg,
107                                 void *callback_arg),
108                void *callback_arg)
109 {
110   static int initialized;
111   static int is_utf8;
112 #if HAVE_ICONV
113   static iconv_t utf8_to_local;
114 #endif
115
116   char inbuf[6];
117   int count;
118
119   if (!initialized)
120     {
121       const char *charset = locale_charset ();
122
123       is_utf8 = !strcmp (charset, UTF8_NAME);
124 #if HAVE_ICONV
125       if (!is_utf8)
126         {
127           utf8_to_local = iconv_open (charset, UTF8_NAME);
128           if (utf8_to_local == (iconv_t)(-1))
129             /* For an unknown encoding, assume ASCII.  */
130             utf8_to_local = iconv_open ("ASCII", UTF8_NAME);
131         }
132 #endif
133       initialized = 1;
134     }
135
136   /* Test whether the utf8_to_local converter is available at all.  */
137   if (!is_utf8)
138     {
139 #if HAVE_ICONV
140       if (utf8_to_local == (iconv_t)(-1))
141         return failure (code, N_("iconv function not usable"), callback_arg);
142 #else
143       return failure (code, N_("iconv function not available"), callback_arg);
144 #endif
145     }
146
147   /* Convert the character to UTF-8.  */
148   count = utf8_wctomb ((unsigned char *) inbuf, code);
149   if (count < 0)
150     return failure (code, N_("character out of range"), callback_arg);
151
152 #if HAVE_ICONV
153   if (!is_utf8)
154     {
155       char outbuf[25];
156       const char *inptr;
157       size_t inbytesleft;
158       char *outptr;
159       size_t outbytesleft;
160       size_t res;
161
162       inptr = inbuf;
163       inbytesleft = count;
164       outptr = outbuf;
165       outbytesleft = sizeof (outbuf);
166
167       /* Convert the character from UTF-8 to the locale's charset.  */
168       res = iconv (utf8_to_local,
169                    (ICONV_CONST char **)&inptr, &inbytesleft,
170                    &outptr, &outbytesleft);
171       if (inbytesleft > 0 || res == (size_t)(-1)
172           /* Irix iconv() inserts a NUL byte if it cannot convert. */
173 # if !defined _LIBICONV_VERSION && (defined sgi || defined __sgi)
174           || (res > 0 && code != 0 && outptr - outbuf == 1 && *outbuf == '\0')
175 # endif
176          )
177         return failure (code, NULL, callback_arg);
178
179       /* Avoid glibc-2.1 bug and Solaris 7 bug.  */
180 # if defined _LIBICONV_VERSION \
181     || !((__GLIBC__ - 0 == 2 && __GLIBC_MINOR__ - 0 <= 1) || defined __sun)
182
183       /* Get back to the initial shift state.  */
184       res = iconv (utf8_to_local, NULL, NULL, &outptr, &outbytesleft);
185       if (res == (size_t)(-1))
186         return failure (code, NULL, callback_arg);
187 # endif
188
189       return success (outbuf, outptr - outbuf, callback_arg);
190     }
191 #endif
192
193   /* At this point, is_utf8 is true, so no conversion is needed.  */
194   return success (inbuf, count, callback_arg);
195 }
196
197 /* Simple success callback that outputs the converted string.
198    The STREAM is passed as callback_arg.  */
199 long
200 fwrite_success_callback (const char *buf, size_t buflen, void *callback_arg)
201 {
202   FILE *stream = (FILE *) callback_arg;
203
204   fwrite (buf, 1, buflen, stream);
205   return 0;
206 }
207
208 /* Simple failure callback that displays an error and exits.  */
209 static long
210 exit_failure_callback (unsigned int code, const char *msg, void *callback_arg)
211 {
212   if (msg == NULL)
213     error (1, 0, _("cannot convert U+%04X to local character set"), code);
214   else
215     error (1, 0, _("cannot convert U+%04X to local character set: %s"), code,
216            gettext (msg));
217   return -1;
218 }
219
220 /* Simple failure callback that displays a fallback representation in plain
221    ASCII, using the same notation as ISO C99 strings.  */
222 static long
223 fallback_failure_callback (unsigned int code, const char *msg, void *callback_arg)
224 {
225   FILE *stream = (FILE *) callback_arg;
226
227   if (code < 0x10000)
228     fprintf (stream, "\\u%04X", code);
229   else
230     fprintf (stream, "\\U%08X", code);
231   return -1;
232 }
233
234 /* Outputs the Unicode character CODE to the output stream STREAM.
235    Upon failure, exit if exit_on_error is true, otherwise output a fallback
236    notation.  */
237 void
238 print_unicode_char (FILE *stream, unsigned int code, int exit_on_error)
239 {
240   unicode_to_mb (code, fwrite_success_callback,
241                  exit_on_error
242                  ? exit_failure_callback
243                  : fallback_failure_callback,
244                  stream);
245 }