1 /* GIO - GLib Input, Output and Streaming Library
3 * Copyright (C) 2009 Red Hat, Inc.
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library 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 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General
16 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
18 * Author: Alexander Larsson <alexl@redhat.com>
23 #include "gcharsetconverter.h"
27 #include "ginitable.h"
40 * SECTION:gcharsetconverter
41 * @short_description: Convert between charsets
44 * #GCharsetConverter is an implementation of #GConverter based on
48 static void g_charset_converter_iface_init (GConverterIface *iface);
49 static void g_charset_converter_initable_iface_init (GInitableIface *iface);
54 * Conversions between character sets.
56 struct _GCharsetConverter
58 GObject parent_instance;
63 gboolean use_fallback;
64 guint n_fallback_errors;
67 G_DEFINE_TYPE_WITH_CODE (GCharsetConverter, g_charset_converter, G_TYPE_OBJECT,
68 G_IMPLEMENT_INTERFACE (G_TYPE_CONVERTER,
69 g_charset_converter_iface_init);
70 G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
71 g_charset_converter_initable_iface_init))
74 g_charset_converter_finalize (GObject *object)
76 GCharsetConverter *conv;
78 conv = G_CHARSET_CONVERTER (object);
83 g_iconv_close (conv->iconv);
85 G_OBJECT_CLASS (g_charset_converter_parent_class)->finalize (object);
89 g_charset_converter_set_property (GObject *object,
94 GCharsetConverter *conv;
96 conv = G_CHARSET_CONVERTER (object);
100 case PROP_TO_CHARSET:
102 conv->to = g_value_dup_string (value);
105 case PROP_FROM_CHARSET:
107 conv->from = g_value_dup_string (value);
110 case PROP_USE_FALLBACK:
111 conv->use_fallback = g_value_get_boolean (value);
115 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
122 g_charset_converter_get_property (GObject *object,
127 GCharsetConverter *conv;
129 conv = G_CHARSET_CONVERTER (object);
133 case PROP_TO_CHARSET:
134 g_value_set_string (value, conv->to);
137 case PROP_FROM_CHARSET:
138 g_value_set_string (value, conv->from);
141 case PROP_USE_FALLBACK:
142 g_value_set_boolean (value, conv->use_fallback);
146 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
152 g_charset_converter_class_init (GCharsetConverterClass *klass)
154 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
156 gobject_class->finalize = g_charset_converter_finalize;
157 gobject_class->get_property = g_charset_converter_get_property;
158 gobject_class->set_property = g_charset_converter_set_property;
160 g_object_class_install_property (gobject_class,
162 g_param_spec_string ("to-charset",
164 P_("The character encoding to convert to"),
166 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
167 G_PARAM_STATIC_STRINGS));
168 g_object_class_install_property (gobject_class,
170 g_param_spec_string ("from-charset",
172 P_("The character encoding to convert from"),
174 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
175 G_PARAM_STATIC_STRINGS));
176 g_object_class_install_property (gobject_class,
178 g_param_spec_boolean ("use-fallback",
179 P_("Fallback enabled"),
180 P_("Use fallback (of form \\<hexval>) for invalid bytes"),
184 G_PARAM_STATIC_STRINGS));
188 g_charset_converter_init (GCharsetConverter *local)
194 * g_charset_converter_new:
195 * @to_charset: destination charset
196 * @from_charset: source charset
197 * @error: #GError for error reporting, or %NULL to ignore.
199 * Creates a new #GCharsetConverter.
201 * Returns: a new #GCharsetConverter or %NULL on error.
206 g_charset_converter_new (const gchar *to_charset,
207 const gchar *from_charset,
210 GCharsetConverter *conv;
212 conv = g_initable_new (G_TYPE_CHARSET_CONVERTER,
214 "to-charset", to_charset,
215 "from-charset", from_charset,
222 g_charset_converter_reset (GConverter *converter)
224 GCharsetConverter *conv = G_CHARSET_CONVERTER (converter);
226 if (conv->iconv == NULL)
228 g_warning ("Invalid object, not initialized");
232 g_iconv (conv->iconv, NULL, NULL, NULL, NULL);
233 conv->n_fallback_errors = 0;
236 static GConverterResult
237 g_charset_converter_convert (GConverter *converter,
242 GConverterFlags flags,
244 gsize *bytes_written,
247 GCharsetConverter *conv;
249 GConverterResult ret;
250 gchar *inbufp, *outbufp;
251 gsize in_left, out_left;
255 conv = G_CHARSET_CONVERTER (converter);
257 if (conv->iconv == NULL)
259 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_INITIALIZED,
260 _("Invalid object, not initialized"));
261 return G_CONVERTER_ERROR;
264 inbufp = (char *)inbuf;
265 outbufp = (char *)outbuf;
266 in_left = inbuf_size;
267 out_left = outbuf_size;
270 /* if there is not input try to flush the data */
273 if (flags & G_CONVERTER_INPUT_AT_END ||
274 flags & G_CONVERTER_FLUSH)
280 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT,
281 _("Incomplete multibyte sequence in input"));
282 return G_CONVERTER_ERROR;
287 /* call g_iconv with NULL inbuf to cleanup shift state */
288 res = g_iconv (conv->iconv,
290 &outbufp, &out_left);
292 res = g_iconv (conv->iconv,
294 &outbufp, &out_left);
296 *bytes_read = inbufp - (char *)inbuf;
297 *bytes_written = outbufp - (char *)outbuf;
299 /* Don't report error if we converted anything */
300 if (res == (gsize) -1 && *bytes_read == 0)
307 /* Incomplete input text */
308 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT,
309 _("Incomplete multibyte sequence in input"));
313 /* Not enough destination space */
314 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NO_SPACE,
315 _("Not enough space in destination"));
319 /* Invalid code sequence */
320 if (conv->use_fallback)
323 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NO_SPACE,
324 _("Not enough space in destination"));
327 const char hex[] = "0123456789ABCDEF";
328 guint8 v = *(guint8 *)inbuf;
329 guint8 *out = (guint8 *)outbuf;
331 out[1] = hex[(v & 0xf0) >> 4];
332 out[2] = hex[(v & 0x0f) >> 0];
336 conv->n_fallback_errors++;
341 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
342 _("Invalid byte sequence in conversion input"));
346 g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
347 _("Error during conversion: %s"),
351 ret = G_CONVERTER_ERROR;
356 ret = G_CONVERTER_CONVERTED;
359 (flags & G_CONVERTER_INPUT_AT_END))
360 ret = G_CONVERTER_FINISHED;
362 (flags & G_CONVERTER_FLUSH))
363 ret = G_CONVERTER_FLUSHED;
370 * g_charset_converter_set_use_fallback:
371 * @converter: a #GCharsetConverter
372 * @use_fallback: %TRUE to use fallbacks
374 * Sets the #GCharsetConverter:use-fallback property.
379 g_charset_converter_set_use_fallback (GCharsetConverter *converter,
380 gboolean use_fallback)
382 use_fallback = !!use_fallback;
384 if (converter->use_fallback != use_fallback)
386 converter->use_fallback = use_fallback;
387 g_object_notify (G_OBJECT (converter), "use-fallback");
392 * g_charset_converter_get_use_fallback:
393 * @converter: a #GCharsetConverter
395 * Gets the #GCharsetConverter:use-fallback property.
397 * Returns: %TRUE if fallbacks are used by @converter
402 g_charset_converter_get_use_fallback (GCharsetConverter *converter)
404 return converter->use_fallback;
408 * g_charset_converter_get_num_fallbacks:
409 * @converter: a #GCharsetConverter
411 * Gets the number of fallbacks that @converter has applied so far.
413 * Returns: the number of fallbacks that @converter has applied
418 g_charset_converter_get_num_fallbacks (GCharsetConverter *converter)
420 return converter->n_fallback_errors;
424 g_charset_converter_iface_init (GConverterIface *iface)
426 iface->convert = g_charset_converter_convert;
427 iface->reset = g_charset_converter_reset;
431 g_charset_converter_initable_init (GInitable *initable,
432 GCancellable *cancellable,
435 GCharsetConverter *conv;
437 g_return_val_if_fail (G_IS_CHARSET_CONVERTER (initable), FALSE);
439 conv = G_CHARSET_CONVERTER (initable);
441 if (cancellable != NULL)
443 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
444 _("Cancellable initialization not supported"));
448 conv->iconv = g_iconv_open (conv->to, conv->from);
450 if (conv->iconv == (GIConv)-1)
453 g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
454 _("Conversion from character set '%s' to '%s' is not supported"),
455 conv->from, conv->to);
457 g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
458 _("Could not open converter from '%s' to '%s'"),
459 conv->from, conv->to);
467 g_charset_converter_initable_iface_init (GInitableIface *iface)
469 iface->init = g_charset_converter_initable_init;