1 /* Output stream that converts the output to another encoding.
2 Copyright (C) 2006-2007, 2010, 2015 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 \
102 && !(defined __GLIBC__ && !defined __UCLIBC__)
103 /* Irix iconv() inserts a NUL byte if it cannot convert.
104 NetBSD iconv() inserts a question mark if it cannot convert.
105 Only GNU libiconv and GNU libc are known to prefer to fail rather
106 than doing a lossy conversion. */
113 if (res == (size_t)(-1) && errno != EINVAL)
114 error (EXIT_FAILURE, 0, _("%s: cannot convert from %s to %s"),
116 stream->from_encoding, stream->to_encoding);
117 /* Output the converted part. */
118 if (sizeof (outbuffer) - outsize > 0)
119 ostream_write_mem (stream->destination,
120 outbuffer, sizeof (outbuffer) - outsize);
121 /* Put back the unconverted part. */
122 if (insize > BUFSIZE)
123 error (EXIT_FAILURE, 0, _("%s: shift sequence too long"),
128 memcpy (stream->buf, inptr, insize);
129 stream->buflen = insize;
133 memmove (inbuffer, inptr, insize);
142 iconv_ostream::flush (iconv_ostream_t stream)
144 /* There's nothing we can do here, since stream->buf[] contains only a few
145 bytes that don't correspond to a character. */
149 iconv_ostream::free (iconv_ostream_t stream)
151 /* Silently ignore the few bytes in stream->buf[] that don't correspond to a
154 /* Avoid glibc-2.1 bug and Solaris 2.7 bug. */
155 #if defined _LIBICONV_VERSION \
156 || !(((__GLIBC__ - 0 == 2 && __GLIBC_MINOR__ - 0 <= 1) \
157 && !defined __UCLIBC__) \
160 char outbuffer[2048];
161 char *outptr = outbuffer;
162 size_t outsize = sizeof (outbuffer);
163 size_t res = iconv (stream->cd, NULL, NULL, &outptr, &outsize);
164 if (res == (size_t)(-1))
165 error (EXIT_FAILURE, 0, _("%s: cannot convert from %s to %s"),
166 "iconv_ostream", stream->from_encoding, stream->to_encoding);
167 /* Output the converted part. */
168 if (sizeof (outbuffer) - outsize > 0)
169 ostream_write_mem (stream->destination,
170 outbuffer, sizeof (outbuffer) - outsize);
174 iconv_close (stream->cd);
175 free (stream->from_encoding);
176 free (stream->to_encoding);
183 iconv_ostream_create (const char *from_encoding, const char *to_encoding,
184 ostream_t destination)
186 iconv_ostream_t stream = XMALLOC (struct iconv_ostream_representation);
188 stream->base.vtable = &iconv_ostream_vtable;
189 stream->destination = destination;
190 stream->from_encoding = xstrdup (from_encoding);
191 stream->to_encoding = xstrdup (to_encoding);
193 /* Avoid glibc-2.1 bug with EUC-KR. */
194 #if ((__GLIBC__ - 0 == 2 && __GLIBC_MINOR__ - 0 <= 1) \
195 && !defined __UCLIBC__) \
196 && !defined _LIBICONV_VERSION
197 if (c_strcasecmp (from_encoding, "EUC-KR") == 0
198 || c_strcasecmp (to_encoding, "EUC-KR") == 0)
199 stream->cd = (iconv_t)(-1):
202 stream->cd = iconv_open (to_encoding, from_encoding);
203 if (stream->cd == (iconv_t)(-1))
205 if (iconv_open ("UTF-8", from_encoding) == (iconv_t)(-1))
206 error (EXIT_FAILURE, 0, _("%s does not support conversion from %s"),
207 "iconv", from_encoding);
208 else if (iconv_open (to_encoding, "UTF-8") == (iconv_t)(-1))
209 error (EXIT_FAILURE, 0, _("%s does not support conversion to %s"),
210 "iconv", to_encoding);
212 error (EXIT_FAILURE, 0,
213 _("%s does not support conversion from %s to %s"),
214 "iconv", from_encoding, to_encoding);
225 iconv_ostream::write_mem (iconv_ostream_t stream, const void *data, size_t len)
231 iconv_ostream::flush (iconv_ostream_t stream)
237 iconv_ostream::free (iconv_ostream_t stream)
242 #endif /* HAVE_ICONV */