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.1 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
;
438 g_return_val_if_fail (G_IS_CHARSET_CONVERTER (initable
), FALSE
);
440 conv
= G_CHARSET_CONVERTER (initable
);
442 if (cancellable
!= NULL
)
444 g_set_error_literal (error
, G_IO_ERROR
, G_IO_ERROR_NOT_SUPPORTED
,
445 _("Cancellable initialization not supported"));
449 conv
->iconv
= g_iconv_open (conv
->to
, conv
->from
);
452 if (conv
->iconv
== (GIConv
)-1)
455 g_set_error (error
, G_IO_ERROR
, G_IO_ERROR_NOT_SUPPORTED
,
456 _("Conversion from character set “%s” to “%s” is not supported"),
457 conv
->from
, conv
->to
);
459 g_set_error (error
, G_IO_ERROR
, G_IO_ERROR_FAILED
,
460 _("Could not open converter from “%s” to “%s”"),
461 conv
->from
, conv
->to
);
469 g_charset_converter_initable_iface_init (GInitableIface
*iface
)
471 iface
->init
= g_charset_converter_initable_init
;