1 /* Output stream that converts the output to another encoding.
2 Copyright (C) 2006-2007 Free Software Foundation, Inc.
3 Written by Bruno Haible <bruno@clisp.org>, 2006.
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 3 of the License, or
8 (at your option) any later version.
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.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>. */
21 #include "iconv-ostream.h"
30 #include "c-strcase.h"
35 #define _(str) gettext (str)
37 #endif /* HAVE_ICONV */
39 struct iconv_ostream : struct ostream
43 /* The destination stream. */
44 ostream_t destination;
45 /* The from and to encodings. */
50 /* Last few bytes that could not yet be converted. */
54 #endif /* HAVE_ICONV */
59 /* Implementation of ostream_t methods. */
62 iconv_ostream::write_mem (iconv_ostream_t stream, const void *data, size_t len)
66 #define BUFFERSIZE 256
67 char inbuffer[BUFFERSIZE];
70 inbufcount = stream->buflen;
72 memcpy (inbuffer, stream->buf, inbufcount);
75 /* At this point, inbuffer[0..inbufcount-1] is filled. */
77 /* Combine the previous rest with a chunk of new input. */
79 (len <= BUFFERSIZE - inbufcount ? len : BUFFERSIZE - inbufcount);
83 memcpy (inbuffer + inbufcount, data, n);
84 data = (char *) data + n;
90 /* Attempt to convert the combined input. */
91 char outbuffer[8*BUFFERSIZE];
93 const char *inptr = inbuffer;
94 size_t insize = inbufcount;
95 char *outptr = outbuffer;
96 size_t outsize = sizeof (outbuffer);
98 size_t res = iconv (stream->cd,
99 (ICONV_CONST char **) &inptr, &insize,
101 #if !defined _LIBICONV_VERSION && !defined __GLIBC__
102 /* Irix iconv() inserts a NUL byte if it cannot convert.
103 NetBSD iconv() inserts a question mark if it cannot convert.
104 Only GNU libiconv and GNU libc are known to prefer to fail rather
105 than doing a lossy conversion. */
112 if (res == (size_t)(-1) && errno != EINVAL)
113 error (EXIT_FAILURE, 0, _("%s: cannot convert from %s to %s"),
115 stream->from_encoding, stream->to_encoding);
116 /* Output the converted part. */
117 if (sizeof (outbuffer) - outsize > 0)
118 ostream_write_mem (stream->destination,
119 outbuffer, sizeof (outbuffer) - outsize);
120 /* Put back the unconverted part. */
121 if (insize > BUFSIZE)
122 error (EXIT_FAILURE, 0, _("%s: shift sequence too long"),
127 memcpy (stream->buf, inptr, insize);
128 stream->buflen = insize;
132 memmove (inbuffer, inptr, insize);
141 iconv_ostream::flush (iconv_ostream_t stream)
143 /* There's nothing we can do here, since stream->buf[] contains only a few
144 bytes that don't correspond to a character. */
148 iconv_ostream::free (iconv_ostream_t stream)
150 /* Silently ignore the few bytes in stream->buf[] that don't correspond to a
153 /* Avoid glibc-2.1 bug and Solaris 2.7 bug. */
154 #if defined _LIBICONV_VERSION \
155 || !((__GLIBC__ - 0 == 2 && __GLIBC_MINOR__ - 0 <= 1) || defined __sun)
157 char outbuffer[2048];
158 char *outptr = outbuffer;
159 size_t outsize = sizeof (outbuffer);
160 size_t res = iconv (stream->cd, NULL, NULL, &outptr, &outsize);
161 if (res == (size_t)(-1))
162 error (EXIT_FAILURE, 0, _("%s: cannot convert from %s to %s"),
163 "iconv_ostream", stream->from_encoding, stream->to_encoding);
164 /* Output the converted part. */
165 if (sizeof (outbuffer) - outsize > 0)
166 ostream_write_mem (stream->destination,
167 outbuffer, sizeof (outbuffer) - outsize);
171 iconv_close (stream->cd);
172 free (stream->from_encoding);
173 free (stream->to_encoding);
180 iconv_ostream_create (const char *from_encoding, const char *to_encoding,
181 ostream_t destination)
183 iconv_ostream_t stream = XMALLOC (struct iconv_ostream_representation);
185 stream->base.vtable = &iconv_ostream_vtable;
186 stream->destination = destination;
187 stream->from_encoding = xstrdup (from_encoding);
188 stream->to_encoding = xstrdup (to_encoding);
190 /* Avoid glibc-2.1 bug with EUC-KR. */
191 #if (__GLIBC__ - 0 == 2 && __GLIBC_MINOR__ - 0 <= 1) && !defined _LIBICONV_VERSION
192 if (c_strcasecmp (from_encoding, "EUC-KR") == 0
193 || c_strcasecmp (to_encoding, "EUC-KR") == 0)
194 stream->cd = (iconv_t)(-1):
197 stream->cd = iconv_open (to_encoding, from_encoding);
198 if (stream->cd == (iconv_t)(-1))
200 if (iconv_open ("UTF-8", from_encoding) == (iconv_t)(-1))
201 error (EXIT_FAILURE, 0, _("%s does not support conversion from %s"),
202 "iconv", from_encoding);
203 else if (iconv_open (to_encoding, "UTF-8") == (iconv_t)(-1))
204 error (EXIT_FAILURE, 0, _("%s does not support conversion to %s"),
205 "iconv", to_encoding);
207 error (EXIT_FAILURE, 0,
208 _("%s does not support conversion from %s to %s"),
209 "iconv", from_encoding, to_encoding);
220 iconv_ostream::write_mem (iconv_ostream_t stream, const void *data, size_t len)
226 iconv_ostream::flush (iconv_ostream_t stream)
232 iconv_ostream::free (iconv_ostream_t stream)
237 #endif /* HAVE_ICONV */