New file from Bruno.
[gnulib.git] / lib / unicodeio.c
1 /* Unicode character output to streams with locale dependent encoding.
2
3    Copyright (C) 2000 Free Software Foundation, Inc.
4
5    This program is free software; you can redistribute it and/or modify it
6    under the terms of the GNU Library General Public License as published
7    by 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 GNU
13    Library General Public License for more details.
14
15    You should have received a copy of the GNU Library General Public
16    License along with this program; if not, write to the Free Software
17    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
18    USA.  */
19
20 /* Written by Bruno Haible <haible@clisp.cons.org>.  */
21
22 #ifdef HAVE_CONFIG_H
23 # include <config.h>
24 #endif
25
26 #if HAVE_STDDEF_H
27 # include <stddef.h>
28 #endif
29
30 #include <stdio.h>
31
32 #if HAVE_ICONV
33 # include <iconv.h>
34 /* Name of UCS-4 encoding with machine dependent endianness and alignment.  */
35 # ifdef _LIBICONV_VERSION
36 #  define UCS4_NAME "UCS-4-INTERNAL"
37 # else
38 #  define UCS4_NAME "INTERNAL"
39 # endif
40 #endif
41
42 #include <error.h>
43
44 #if ENABLE_NLS
45 # include <libintl.h>
46 # define _(Text) gettext (Text)
47 #else
48 # define _(Text) Text
49 #endif
50
51 #include "unicodeio.h"
52
53 /* Use md5.h for its nice detection of unsigned 32-bit type.  */
54 #include "md5.h"
55 #undef uint32_t
56 #define uint32_t md5_uint32
57
58 /* Outputs the Unicode character CODE to the output stream STREAM.
59    Assumes that the locale doesn't change between two calls.  */
60 void
61 print_unicode_char (FILE *stream, unsigned int code)
62 {
63 #if HAVE_ICONV
64   static int initialized;
65   static iconv_t ucs4_to_local;
66
67   uint32_t in;
68   char outbuf[25];
69   const char *inptr;
70   size_t inbytesleft;
71   char *outptr;
72   size_t outbytesleft;
73   size_t res;
74
75   if (!initialized)
76     {
77       extern const char *locale_charset (void);
78       const char *charset = locale_charset ();
79
80       ucs4_to_local = (charset != NULL
81                        ? iconv_open (charset, UCS4_NAME)
82                        : (iconv_t)(-1));
83       if (ucs4_to_local == (iconv_t)(-1))
84         {
85           /* For an unknown encoding, assume ASCII.  */
86           ucs4_to_local = iconv_open ("ASCII", UCS4_NAME);
87           if (ucs4_to_local == (iconv_t)(-1))
88             error (1, 0, _("cannot output U+%04X: iconv function not usable"),
89                    code);
90         }
91       initialized = 1;
92     }
93
94   in = code;
95   inptr = (char *) &in;
96   inbytesleft = sizeof (in);
97   outptr = outbuf;
98   outbytesleft = sizeof (outbuf);
99
100   /* Convert the character.  */
101   res = iconv (ucs4_to_local, &inptr, &inbytesleft, &outptr, &outbytesleft);
102   if (inbytesleft > 0 || res == (size_t)(-1))
103     error (1, res == (size_t)(-1) ? errno : 0,
104            _("cannot convert U+%04X to local character set"), code);
105
106   /* Avoid glibc-2.1 bug.  */
107 # if defined _LIBICONV_VERSION || !(__GLIBC__ - 0 == 2 && __GLIBC_MINOR__ - 0 <= 1)
108
109   /* Get back to the initial shift state.  */
110   res = iconv (ucs4_to_local, NULL, NULL, &outptr, &outbytesleft);
111   if (res == (size_t)(-1))
112     error (1, errno, _("cannot convert U+%04X to local character set"), code);
113
114 # endif
115
116   fwrite (outbuf, 1, outptr - outbuf, stream);
117
118 #else
119   error (1, 0, _("cannot output U+%04X: iconv function not available"), code);
120 #endif
121 }