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