Add GCharsetConverter
[platform/upstream/glib.git] / gio / gcharsetconverter.c
1 /* GIO - GLib Input, Output and Streaming Library
2  *
3  * Copyright (C) 2009 Red Hat, Inc.
4  *
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.
9  *
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.
14  *
15  * You should have received a copy of the GNU Lesser General
16  * Public License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18  * Boston, MA 02111-1307, USA.
19  *
20  * Author: Alexander Larsson <alexl@redhat.com>
21  */
22
23 #include "config.h"
24
25 #include <errno.h>
26
27 #include "gcontenttypeprivate.h"
28 #include "gcharsetconverter.h"
29 #include "glib.h"
30 #include "ginitable.h"
31 #include "gioerror.h"
32 #include "glibintl.h"
33
34 #include "gioalias.h"
35
36 enum {
37   PROP_0,
38   PROP_FROM_CHARSET,
39   PROP_TO_CHARSET
40 };
41
42 /**
43  * SECTION:gcharsetconverter
44  * @short_description: Convert between charsets
45  * @include: gio/gio.h
46  *
47  * #GCharsetConverter is an implementation of #GConverter based on
48  * GIConv.
49  */
50
51 static void g_charset_converter_iface_init          (GConverterIface *iface);
52 static void g_charset_converter_initable_iface_init (GInitableIface  *iface);
53
54 /**
55  * GCharsetConverter:
56  *
57  * Conversions between character sets.
58  */
59 struct _GCharsetConverter
60 {
61   GObject parent_instance;
62
63   char *from;
64   char *to;
65   GIConv iconv;
66 };
67
68 G_DEFINE_TYPE_WITH_CODE (GCharsetConverter, g_charset_converter, G_TYPE_OBJECT,
69                          G_IMPLEMENT_INTERFACE (G_TYPE_CONVERTER,
70                                                 g_charset_converter_iface_init);
71                          G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
72                                                 g_charset_converter_initable_iface_init))
73
74 static void
75 g_charset_converter_finalize (GObject *object)
76 {
77   GCharsetConverter *conv;
78
79   conv = G_CHARSET_CONVERTER (object);
80
81   g_free (conv->from);
82   g_free (conv->to);
83   if (conv->iconv)
84     g_iconv_close (conv->iconv);
85
86   G_OBJECT_CLASS (g_charset_converter_parent_class)->finalize (object);
87 }
88
89 static void
90 g_charset_converter_set_property (GObject      *object,
91                                   guint         prop_id,
92                                   const GValue *value,
93                                   GParamSpec   *pspec)
94 {
95   GCharsetConverter *conv;
96
97   conv = G_CHARSET_CONVERTER (object);
98
99   switch (prop_id)
100     {
101     case PROP_TO_CHARSET:
102       g_free (conv->to);
103       conv->to = g_value_dup_string (value);
104       break;
105
106     case PROP_FROM_CHARSET:
107       g_free (conv->from);
108       conv->from = g_value_dup_string (value);
109       break;
110
111     default:
112       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
113       break;
114     }
115
116 }
117
118 static void
119 g_charset_converter_get_property (GObject    *object,
120                                   guint       prop_id,
121                                   GValue     *value,
122                                   GParamSpec *pspec)
123 {
124   GCharsetConverter *conv;
125
126   conv = G_CHARSET_CONVERTER (object);
127
128   switch (prop_id)
129     {
130     case PROP_TO_CHARSET:
131       g_value_set_string (value, conv->to);
132       break;
133
134     case PROP_FROM_CHARSET:
135       g_value_set_string (value, conv->from);
136       break;
137
138     default:
139       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
140       break;
141     }
142 }
143
144 static void
145 g_charset_converter_class_init (GCharsetConverterClass *klass)
146 {
147   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
148
149   gobject_class->finalize = g_charset_converter_finalize;
150   gobject_class->get_property = g_charset_converter_get_property;
151   gobject_class->set_property = g_charset_converter_set_property;
152
153   g_object_class_install_property (gobject_class,
154                                    PROP_TO_CHARSET,
155                                    g_param_spec_string ("to-charset",
156                                                         P_("To Charset"),
157                                                         P_("The character encoding to convert to"),
158                                                         NULL,
159                                                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
160                                                         G_PARAM_STATIC_STRINGS));
161   g_object_class_install_property (gobject_class,
162                                    PROP_FROM_CHARSET,
163                                    g_param_spec_string ("from-charset",
164                                                         P_("From Charset"),
165                                                         P_("The character encoding to convert from"),
166                                                         NULL,
167                                                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
168                                                         G_PARAM_STATIC_STRINGS));
169
170 }
171
172 static void
173 g_charset_converter_init (GCharsetConverter *local)
174 {
175 }
176
177
178 /**
179  * g_charset_converter_new:
180  * @to_charset: destination charset
181  * @from_charset: source charset
182  * @error: #GError for error reporting, or %NULL to ignore.
183  *
184  * Creates a new #GCharsetConverter.
185  *
186  * Returns: a new #GCharsetConverter or %NULL on error.
187  *
188  * Since: 2.24
189  **/
190 GCharsetConverter *
191 g_charset_converter_new (const gchar  *to_charset,
192                          const gchar  *from_charset,
193                          GError       **error)
194 {
195   GCharsetConverter *conv;
196
197   conv = g_initable_new (G_TYPE_CHARSET_CONVERTER,
198                          NULL, error,
199                          "to-charset", to_charset,
200                          "from-charset", from_charset,
201                          NULL);
202
203   return conv;
204 }
205
206 static void
207 g_charset_converter_reset (GConverter *converter)
208 {
209   GCharsetConverter *conv = G_CHARSET_CONVERTER (converter);
210
211   if (conv->iconv == NULL)
212     {
213       g_warning ("Invalid object, not initialized");
214       return;
215     }
216
217   g_iconv (conv->iconv, NULL, NULL, NULL, NULL);
218 }
219
220 static GConverterResult
221 g_charset_converter_convert (GConverter *converter,
222                              const void *inbuf,
223                              gsize       inbuf_size,
224                              void       *outbuf,
225                              gsize       outbuf_size,
226                              GConverterFlags flags,
227                              gsize      *bytes_read,
228                              gsize      *bytes_written,
229                              GError    **error)
230 {
231   GCharsetConverter  *conv;
232   gsize res;
233   GConverterResult ret;
234   gchar *inbufp, *outbufp;
235   gsize in_left, out_left;
236   int errsv;
237
238   conv = G_CHARSET_CONVERTER (converter);
239
240   if (conv->iconv == NULL)
241     {
242       g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_INITIALIZED,
243                            _("Invalid object, not initialized"));
244       return G_CONVERTER_ERROR;
245     }
246
247   /* Iconv never produces output with no input, so handle this
248      specially */
249   if (inbuf_size == 0)
250     {
251       if (flags & G_CONVERTER_INPUT_AT_END)
252         return G_CONVERTER_FINISHED;
253
254       if (flags & G_CONVERTER_FLUSH)
255         return G_CONVERTER_FLUSHED;
256
257       g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT,
258                            _("Incomplete multibyte sequence in input"));
259       return G_CONVERTER_ERROR;
260     }
261
262   inbufp = (char *)inbuf;
263   outbufp = (char *)outbuf;
264   in_left = inbuf_size;
265   out_left = outbuf_size;
266
267   res = g_iconv (conv->iconv,
268                  &inbufp, &in_left,
269                  &outbufp, &out_left);
270
271   *bytes_read = inbufp - (char *)inbuf;
272   *bytes_written = outbufp - (char *)outbuf;
273
274   /* Don't report error if we converted anything */
275   if (res == (gsize) -1 && *bytes_read == 0)
276     {
277       errsv = errno;
278
279       switch (errsv)
280         {
281         case EINVAL:
282           /* Incomplete input text */
283           g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT,
284                                _("Incomplete multibyte sequence in input"));
285           break;
286
287         case E2BIG:
288           /* Not enough destination space */
289           g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NO_SPACE,
290                                _("Not enough space in destination"));
291           break;
292
293         case EILSEQ:
294           /* Invalid code sequence */
295           g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
296                                _("Invalid byte sequence in conversion input"));
297           break;
298
299         default:
300           g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
301                        _("Error during conversion: %s"),
302                        g_strerror (errsv));
303           break;
304         }
305       ret = G_CONVERTER_ERROR;
306     }
307   else
308     {
309       ret = G_CONVERTER_CONVERTED;
310
311       if (in_left == 0 &&
312           (flags & G_CONVERTER_INPUT_AT_END))
313         ret = G_CONVERTER_FINISHED;
314       else if (in_left == 0 &&
315                (flags & G_CONVERTER_FLUSH))
316         ret = G_CONVERTER_FLUSHED;
317     }
318
319   return ret;
320 }
321
322 static void
323 g_charset_converter_iface_init (GConverterIface *iface)
324 {
325   iface->convert = g_charset_converter_convert;
326   iface->reset = g_charset_converter_reset;
327 }
328
329 static gboolean
330 g_charset_converter_initable_init (GInitable *initable,
331                                    GCancellable *cancellable,
332                                    GError  **error)
333 {
334   GCharsetConverter  *conv;
335
336   g_return_val_if_fail (G_IS_CHARSET_CONVERTER (initable), FALSE);
337
338   conv = G_CHARSET_CONVERTER (initable);
339
340   if (cancellable != NULL)
341     {
342       g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
343                            _("Cancellable initialization not supported"));
344       return FALSE;
345     }
346
347   conv->iconv =
348     g_iconv_open (conv->to, conv->from);
349
350   if (conv->iconv == NULL)
351     {
352       if (errno == EINVAL)
353         g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
354                      _("Conversion from character set '%s' to '%s' is not supported"),
355                      conv->from, conv->to);
356       else
357         g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
358                      _("Could not open converter from '%s' to '%s'"),
359                      conv->from, conv->to);
360       return FALSE;
361     }
362
363   return TRUE;
364 }
365
366 static void
367 g_charset_converter_initable_iface_init (GInitableIface *iface)
368 {
369   iface->init = g_charset_converter_initable_init;
370 }
371
372 #define __G_CHARSET_CONVERTER_C__
373 #include "gioaliasdef.c"