Add some more cases to the app-id unit tests
[glib.git] / gio / gcharsetconverter.c
blob77e5a231e71474bafce245f632bac22861885741
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>
21 #include "config.h"
23 #include "gcharsetconverter.h"
25 #include <errno.h>
27 #include "ginitable.h"
28 #include "gioerror.h"
29 #include "glibintl.h"
32 enum {
33 PROP_0,
34 PROP_FROM_CHARSET,
35 PROP_TO_CHARSET,
36 PROP_USE_FALLBACK
39 /**
40 * SECTION:gcharsetconverter
41 * @short_description: Convert between charsets
42 * @include: gio/gio.h
44 * #GCharsetConverter is an implementation of #GConverter based on
45 * GIConv.
48 static void g_charset_converter_iface_init (GConverterIface *iface);
49 static void g_charset_converter_initable_iface_init (GInitableIface *iface);
51 /**
52 * GCharsetConverter:
54 * Conversions between character sets.
56 struct _GCharsetConverter
58 GObject parent_instance;
60 char *from;
61 char *to;
62 GIConv iconv;
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))
73 static void
74 g_charset_converter_finalize (GObject *object)
76 GCharsetConverter *conv;
78 conv = G_CHARSET_CONVERTER (object);
80 g_free (conv->from);
81 g_free (conv->to);
82 if (conv->iconv)
83 g_iconv_close (conv->iconv);
85 G_OBJECT_CLASS (g_charset_converter_parent_class)->finalize (object);
88 static void
89 g_charset_converter_set_property (GObject *object,
90 guint prop_id,
91 const GValue *value,
92 GParamSpec *pspec)
94 GCharsetConverter *conv;
96 conv = G_CHARSET_CONVERTER (object);
98 switch (prop_id)
100 case PROP_TO_CHARSET:
101 g_free (conv->to);
102 conv->to = g_value_dup_string (value);
103 break;
105 case PROP_FROM_CHARSET:
106 g_free (conv->from);
107 conv->from = g_value_dup_string (value);
108 break;
110 case PROP_USE_FALLBACK:
111 conv->use_fallback = g_value_get_boolean (value);
112 break;
114 default:
115 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
116 break;
121 static void
122 g_charset_converter_get_property (GObject *object,
123 guint prop_id,
124 GValue *value,
125 GParamSpec *pspec)
127 GCharsetConverter *conv;
129 conv = G_CHARSET_CONVERTER (object);
131 switch (prop_id)
133 case PROP_TO_CHARSET:
134 g_value_set_string (value, conv->to);
135 break;
137 case PROP_FROM_CHARSET:
138 g_value_set_string (value, conv->from);
139 break;
141 case PROP_USE_FALLBACK:
142 g_value_set_boolean (value, conv->use_fallback);
143 break;
145 default:
146 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
147 break;
151 static void
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,
161 PROP_TO_CHARSET,
162 g_param_spec_string ("to-charset",
163 P_("To Charset"),
164 P_("The character encoding to convert to"),
165 NULL,
166 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
167 G_PARAM_STATIC_STRINGS));
168 g_object_class_install_property (gobject_class,
169 PROP_FROM_CHARSET,
170 g_param_spec_string ("from-charset",
171 P_("From Charset"),
172 P_("The character encoding to convert from"),
173 NULL,
174 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
175 G_PARAM_STATIC_STRINGS));
176 g_object_class_install_property (gobject_class,
177 PROP_USE_FALLBACK,
178 g_param_spec_boolean ("use-fallback",
179 P_("Fallback enabled"),
180 P_("Use fallback (of form \\<hexval>) for invalid bytes"),
181 FALSE,
182 G_PARAM_READWRITE |
183 G_PARAM_CONSTRUCT |
184 G_PARAM_STATIC_STRINGS));
187 static void
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.
203 * Since: 2.24
205 GCharsetConverter *
206 g_charset_converter_new (const gchar *to_charset,
207 const gchar *from_charset,
208 GError **error)
210 GCharsetConverter *conv;
212 conv = g_initable_new (G_TYPE_CHARSET_CONVERTER,
213 NULL, error,
214 "to-charset", to_charset,
215 "from-charset", from_charset,
216 NULL);
218 return conv;
221 static void
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");
229 return;
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,
238 const void *inbuf,
239 gsize inbuf_size,
240 void *outbuf,
241 gsize outbuf_size,
242 GConverterFlags flags,
243 gsize *bytes_read,
244 gsize *bytes_written,
245 GError **error)
247 GCharsetConverter *conv;
248 gsize res;
249 GConverterResult ret;
250 gchar *inbufp, *outbufp;
251 gsize in_left, out_left;
252 int errsv;
253 gboolean reset;
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;
268 reset = FALSE;
270 /* if there is not input try to flush the data */
271 if (inbuf_size == 0)
273 if (flags & G_CONVERTER_INPUT_AT_END ||
274 flags & G_CONVERTER_FLUSH)
276 reset = TRUE;
278 else
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;
286 if (reset)
287 /* call g_iconv with NULL inbuf to cleanup shift state */
288 res = g_iconv (conv->iconv,
289 NULL, &in_left,
290 &outbufp, &out_left);
291 else
292 res = g_iconv (conv->iconv,
293 &inbufp, &in_left,
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)
302 errsv = errno;
304 switch (errsv)
306 case EINVAL:
307 /* Incomplete input text */
308 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT,
309 _("Incomplete multibyte sequence in input"));
310 break;
312 case E2BIG:
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"));
316 break;
318 case EILSEQ:
319 /* Invalid code sequence */
320 if (conv->use_fallback)
322 if (outbuf_size < 3)
323 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NO_SPACE,
324 _("Not enough space in destination"));
325 else
327 const char hex[] = "0123456789ABCDEF";
328 guint8 v = *(guint8 *)inbuf;
329 guint8 *out = (guint8 *)outbuf;
330 out[0] = '\\';
331 out[1] = hex[(v & 0xf0) >> 4];
332 out[2] = hex[(v & 0x0f) >> 0];
333 *bytes_read = 1;
334 *bytes_written = 3;
335 in_left--;
336 conv->n_fallback_errors++;
337 goto ok;
340 else
341 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
342 _("Invalid byte sequence in conversion input"));
343 break;
345 default:
346 g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
347 _("Error during conversion: %s"),
348 g_strerror (errsv));
349 break;
351 ret = G_CONVERTER_ERROR;
353 else
356 ret = G_CONVERTER_CONVERTED;
358 if (reset &&
359 (flags & G_CONVERTER_INPUT_AT_END))
360 ret = G_CONVERTER_FINISHED;
361 else if (reset &&
362 (flags & G_CONVERTER_FLUSH))
363 ret = G_CONVERTER_FLUSHED;
366 return ret;
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.
376 * Since: 2.24
378 void
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
399 * Since: 2.24
401 gboolean
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
415 * Since: 2.24
417 guint
418 g_charset_converter_get_num_fallbacks (GCharsetConverter *converter)
420 return converter->n_fallback_errors;
423 static void
424 g_charset_converter_iface_init (GConverterIface *iface)
426 iface->convert = g_charset_converter_convert;
427 iface->reset = g_charset_converter_reset;
430 static gboolean
431 g_charset_converter_initable_init (GInitable *initable,
432 GCancellable *cancellable,
433 GError **error)
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"));
445 return FALSE;
448 conv->iconv = g_iconv_open (conv->to, conv->from);
450 if (conv->iconv == (GIConv)-1)
452 if (errno == EINVAL)
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);
456 else
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);
460 return FALSE;
463 return TRUE;
466 static void
467 g_charset_converter_initable_iface_init (GInitableIface *iface)
469 iface->init = g_charset_converter_initable_init;