"Initial commit to Gerrit"
[profile/ivi/libgsf.git] / gsf / gsf-output-iconv.c
1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * gsf-output-iconv.c: wrapper to convert character sets.
4  *
5  * Copyright (C) 2005-2006 Morten Welinder (terra@gnome.org)
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of version 2.1 of the GNU Lesser General Public
9  * License as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
19  * USA
20  */
21
22 #include <gsf-config.h>
23 #include <gsf/gsf-output-iconv.h>
24 #include <gsf/gsf-output-impl.h>
25 #include <gsf/gsf-impl-utils.h>
26 #include <gsf/gsf-utils.h>
27 #include <glib.h>
28 #include <string.h>
29
30 #define BUF_SIZE 0x400
31
32 static GObjectClass *parent_class;
33
34 struct _GsfOutputIconv {
35         GsfOutput output;
36
37         GsfOutput *sink;
38         char *input_charset;
39         char *output_charset;
40         char *fallback;
41
42         guint8 *buf;
43         size_t buf_len;
44 };
45
46 enum {
47         PROP_0,
48         PROP_SINK,
49         PROP_INPUT_CHARSET,
50         PROP_OUTPUT_CHARSET,
51         PROP_FALLBACK
52 };
53
54 /**
55  * gsf_output_iconv_new :
56  * @sink : The underlying data source.
57  * @dst : The target character set.
58  * @src : The source character set.
59  *
60  * Adds a reference to @sink.
61  *
62  * Returns: a new GsfOutput object or %NULL.
63  **/
64 GsfOutput *
65 gsf_output_iconv_new (GsfOutput *sink, char const *dst, char const *src)
66 {
67         GError *error = NULL;
68         g_return_val_if_fail (GSF_IS_OUTPUT (sink), NULL);
69
70         if (!dst) dst = "UTF-8";
71         if (!src) src = "UTF-8";
72         g_free (g_convert ("", 0, dst, src, NULL, NULL, &error));
73
74         if (error) {
75                 g_error_free (error);
76                 return NULL;
77         }
78
79         return g_object_new (GSF_OUTPUT_ICONV_TYPE,
80                              "sink", sink,
81                              "input-charset", src,
82                              "output-charset", dst,
83                              NULL);
84 }
85
86 static void
87 gsf_output_iconv_finalize (GObject *obj)
88 {
89         GsfOutputIconv *ic = (GsfOutputIconv *)obj;
90
91         if (ic->sink != NULL)
92                 g_object_unref (G_OBJECT (ic->sink));
93         g_free (ic->input_charset);
94         g_free (ic->output_charset);
95         g_free (ic->buf);
96
97         parent_class->finalize (obj);
98 }
99
100 static gboolean
101 iconv_flush (GsfOutputIconv *ic, gboolean must_empty)
102 {
103         if (gsf_output_error (GSF_OUTPUT (ic)))
104                 return FALSE;
105
106         if (ic->buf_len > 0) {
107                 gsize bytes_read, bytes_written;
108                 gboolean ok;
109                 char *data = g_convert_with_fallback (ic->buf, ic->buf_len,
110                                                       ic->output_charset,
111                                                       ic->input_charset,
112                                                       ic->fallback,
113                                                       &bytes_read,
114                                                       &bytes_written,
115                                                       NULL);
116                 if (data == NULL || bytes_read <= 0) {
117                         gsf_output_set_error (GSF_OUTPUT (ic),
118                                               0,
119                                               "Failed to convert string");
120                         ok = FALSE;
121                 } else {
122                         ic->buf_len -= bytes_read;
123                         g_memmove (ic->buf, ic->buf + bytes_read, ic->buf_len);
124
125                         ok = gsf_output_write (ic->sink, bytes_written, data);
126                         if (!ok) {
127                                 gsf_output_set_error (GSF_OUTPUT (ic),
128                                                       0,
129                                                       "Failed to write");
130                         }
131                 }
132
133                 g_free (data);
134                 return ok && (!must_empty || ic->buf_len == 0);
135         } else
136                 return TRUE;
137 }
138
139 static gboolean
140 gsf_output_iconv_write (GsfOutput *output,
141                         size_t num_bytes, guint8 const *data)
142 {
143         GsfOutputIconv *ic = GSF_OUTPUT_ICONV (output);
144
145         g_return_val_if_fail (data, FALSE);
146
147         while (num_bytes > 0) {
148                 if (gsf_output_error (output))
149                         return FALSE;
150                 if (ic->buf_len == BUF_SIZE)
151                         iconv_flush (ic, FALSE);
152                 else {
153                         size_t count = MIN (BUF_SIZE - ic->buf_len, num_bytes);
154                         memcpy (ic->buf + ic->buf_len, data, count);
155                         ic->buf_len += count;
156                         num_bytes -= count;
157                         data += count;
158                 }
159         }
160
161         return TRUE;
162 }
163
164 static gboolean
165 gsf_output_iconv_seek (G_GNUC_UNUSED GsfOutput *output,
166                       G_GNUC_UNUSED gsf_off_t offset,
167                       G_GNUC_UNUSED GSeekType whence)
168 {
169         return FALSE;
170 }
171
172 static gboolean
173 gsf_output_iconv_close (GsfOutput *output)
174 {
175         if (!gsf_output_error (output)) {
176                 GsfOutputIconv *ic = GSF_OUTPUT_ICONV (output);
177
178                 if (!iconv_flush (ic, TRUE))
179                         return FALSE;
180         }
181
182         return TRUE;
183 }
184
185 static void
186 gsf_output_iconv_init (GObject *obj)
187 {
188         GsfOutputIconv *ic = GSF_OUTPUT_ICONV (obj);
189
190         ic->buf = g_malloc (BUF_SIZE);
191         ic->buf_len = 0;
192 }
193
194 static void
195 gsf_output_iconv_get_property (GObject     *object,
196                               guint        property_id,
197                               GValue      *value,
198                               GParamSpec  *pspec)
199 {
200         GsfOutputIconv *ic = (GsfOutputIconv *)object;
201
202         switch (property_id) {
203         case PROP_SINK:
204                 g_value_set_object (value, ic->sink);
205                 break;
206         case PROP_INPUT_CHARSET:
207                 g_value_set_string (value, ic->input_charset);
208                 break;
209         case PROP_OUTPUT_CHARSET:
210                 g_value_set_string (value, ic->output_charset);
211                 break;
212         case PROP_FALLBACK:
213                 g_value_set_string (value, ic->fallback);
214                 break;
215         default:
216                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
217                 break;
218         }
219 }
220
221 static void
222 gsf_output_iconv_set_sink (GsfOutputIconv *ic, GsfOutput *sink)
223 {
224         g_return_if_fail (GSF_IS_OUTPUT (sink));
225         g_object_ref (sink);
226         if (ic->sink)
227                 g_object_unref (ic->sink);
228         ic->sink = sink;
229 }
230
231 static void
232 gsf_output_iconv_set_property (GObject      *object,
233                                guint         property_id,
234                                GValue const *value,
235                                GParamSpec   *pspec)
236 {
237         GsfOutputIconv *ic = (GsfOutputIconv *)object;
238         char *scopy;
239
240         switch (property_id) {
241         case PROP_SINK:
242                 gsf_output_iconv_set_sink (ic, g_value_get_object (value));
243                 break;
244         case PROP_INPUT_CHARSET:
245                 ic->input_charset = g_strdup (g_value_get_string (value));
246                 break;
247         case PROP_OUTPUT_CHARSET:
248                 ic->output_charset = g_strdup (g_value_get_string (value));
249                 break;
250         case PROP_FALLBACK:
251                 scopy = g_strdup (g_value_get_string (value));
252                 g_free (ic->fallback);
253                 ic->fallback = scopy;
254                 break;
255         default:
256                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
257                 break;
258         }
259 }
260
261 static void
262 gsf_output_iconv_class_init (GObjectClass *gobject_class)
263 {
264         GsfOutputClass *output_class = GSF_OUTPUT_CLASS (gobject_class);
265
266         gobject_class->finalize     = gsf_output_iconv_finalize;
267         gobject_class->set_property = gsf_output_iconv_set_property;
268         gobject_class->get_property = gsf_output_iconv_get_property;
269         output_class->Write         = gsf_output_iconv_write;
270         output_class->Seek          = gsf_output_iconv_seek;
271         output_class->Close         = gsf_output_iconv_close;
272
273         g_object_class_install_property
274                 (gobject_class,
275                  PROP_SINK,
276                  g_param_spec_object ("sink", "Sink",
277                                       "Where the converted data is written.",
278                                       GSF_OUTPUT_TYPE,
279                                       GSF_PARAM_STATIC |
280                                       G_PARAM_READWRITE |
281                                       G_PARAM_CONSTRUCT_ONLY));
282         g_object_class_install_property
283                 (gobject_class,
284                  PROP_INPUT_CHARSET,
285                  g_param_spec_string ("input-charset", "Input Charset",
286                                       "The character set to convert from.",
287                                       "UTF-8",
288                                       GSF_PARAM_STATIC |
289                                       G_PARAM_READWRITE |
290                                       G_PARAM_CONSTRUCT_ONLY));
291         g_object_class_install_property
292                 (gobject_class,
293                  PROP_OUTPUT_CHARSET,
294                  g_param_spec_string ("output-charset", "Output Charset",
295                                       "The character set to convert to.",
296                                       "UTF-8",
297                                       GSF_PARAM_STATIC |
298                                       G_PARAM_READWRITE |
299                                       G_PARAM_CONSTRUCT_ONLY));
300         /**
301          * GsfOutputIconv:fallback:
302          *
303          * Either NULL or a UTF-8 string (representable in the target encoding)
304          * to convert and output in place of characters that cannot be represented
305          * in the target encoding.  NULL means use \u1234 or \U12345678 format.
306          */  
307         g_object_class_install_property
308                 (gobject_class,
309                  PROP_FALLBACK,
310                  g_param_spec_string ("fallback", "Fallback",
311                                       "The string to use for invalid characters.",
312                                       NULL,
313                                       GSF_PARAM_STATIC |
314                                       G_PARAM_READWRITE));
315
316         parent_class = g_type_class_peek_parent (gobject_class);
317 }
318
319 GSF_CLASS (GsfOutputIconv, gsf_output_iconv,
320            gsf_output_iconv_class_init, gsf_output_iconv_init,
321            GSF_OUTPUT_TYPE)