"Initial commit to Gerrit"
[profile/ivi/libgsf.git] / gsf / gsf-input-gio.c
1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * gsf-input-gio.c:
4  *
5  * Copyright (C) 2007 Dom Lachowicz <cinamod@hotmail.com>
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-input-gio.h>
24 #include <gsf/gsf-input-memory.h>
25 #include <gsf/gsf-output-memory.h>
26 #include <gsf/gsf-input-impl.h>
27 #include <gsf/gsf-impl-utils.h>
28 #include <string.h>
29
30 struct _GsfInputGio {
31         GsfInput     input;
32         GFile        *file;
33         GInputStream *stream;
34         guint8       *buf;
35         size_t       buf_size;
36 };
37
38 typedef struct {
39         GsfInputClass input_class;
40 } GsfInputGioClass;
41
42 static gboolean
43 can_seek (GInputStream *stream)
44 {
45         if (!G_IS_SEEKABLE (stream))
46                 return FALSE;
47
48         return g_seekable_can_seek (G_SEEKABLE (stream));
49 }
50
51 static GsfInput *
52 make_local_copy (GFile *file, GInputStream *stream)
53 {
54         GsfOutput *out;
55         GsfInput  *copy;
56         GFileInfo *info;
57
58         out = gsf_output_memory_new ();
59
60         while (1) {
61                 guint8 buf[4096];
62                 gssize nread;
63
64                 nread = g_input_stream_read (stream, buf, sizeof(buf), NULL, NULL);
65
66                 if (nread > 0) {
67                         if (!gsf_output_write (out, nread, buf)) {
68                                 copy = NULL;
69                                 goto cleanup_and_exit;
70                         }
71                 }
72                 else if (nread == 0)
73                         break;
74                 else {
75                         copy = NULL;
76                         goto cleanup_and_exit;
77                 }
78         }
79
80         copy = gsf_input_memory_new_clone
81                 (gsf_output_memory_get_bytes (GSF_OUTPUT_MEMORY (out)),
82                  gsf_output_size (out));
83
84         if (copy != NULL) {
85                 info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_NAME, 0, NULL, NULL);
86                 if (info) {
87                         gsf_input_set_name (GSF_INPUT (copy), g_file_info_get_name (info));
88                         g_object_unref (info);
89                 }
90         }
91
92  cleanup_and_exit:
93
94         gsf_output_close (out);
95         g_object_unref (out);
96
97         g_input_stream_close (stream, NULL, NULL);
98         g_object_unref (stream);
99         return copy;
100 }
101
102 /**
103  * gsf_input_gio_new:
104  * @file:
105  * @err: optionally NULL.
106  *
107  * Returns: A new #GsfInputGio or NULL
108  */
109 GsfInput *
110 gsf_input_gio_new (GFile *file, GError **err)
111 {
112         GsfInputGio *input;
113         GInputStream *stream;
114         GFileInfo    *info;
115
116         g_return_val_if_fail (file != NULL, NULL);
117
118         stream = (GInputStream *)g_file_read (file, NULL, err);
119         if (stream == NULL)
120                 return NULL;
121
122         if (!can_seek (stream))
123                 return make_local_copy (file, stream);
124
125         info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_SIZE, 0, NULL, NULL);
126         if (!info)
127                 return make_local_copy (file, stream);
128
129         input = g_object_new (GSF_INPUT_GIO_TYPE, NULL);
130         if (G_UNLIKELY (NULL == input)) {
131                 g_input_stream_close (stream, NULL, NULL);
132                 g_object_unref (stream);
133                 return NULL;
134         }
135
136         gsf_input_set_size (GSF_INPUT (input), g_file_info_get_size (info));
137         g_object_unref (info);
138
139         g_object_ref (G_OBJECT (file));
140
141         input->stream = stream;
142         input->file = file;
143         input->buf  = NULL;
144         input->buf_size = 0;
145
146         info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_NAME, 0, NULL, NULL);
147         if (info) {
148                 gsf_input_set_name (GSF_INPUT (input), g_file_info_get_name (info));
149                 g_object_unref (info);
150         }
151
152         return GSF_INPUT (input);
153 }
154
155 /**
156  * gsf_input_gio_new_for_path:
157  * @path:
158  * @err: optionally NULL.
159  *
160  * Returns: A new #GsfInputGio or NULL
161  */
162 GsfInput *
163 gsf_input_gio_new_for_path (char const *path, GError **err)
164 {
165         GFile *file;
166         GsfInput *input;
167
168         g_return_val_if_fail (path != NULL, NULL);
169
170         file = g_file_new_for_path (path);
171         input = gsf_input_gio_new (file, err);
172         g_object_unref (file);
173
174         return input;
175 }
176
177 /**
178  * gsf_input_gio_new_for_uri:
179  * @uri:
180  * @err: optionally NULL.
181  *
182  * Returns: A new #GsfInputGio or NULL
183  */
184 GsfInput *
185 gsf_input_gio_new_for_uri (char const *uri, GError **err)
186 {
187         GFile *file;
188         GsfInput *input;
189
190         g_return_val_if_fail (uri != NULL, NULL);
191
192         file = g_file_new_for_uri (uri);
193         input = gsf_input_gio_new (file, err);
194         g_object_unref (file);
195
196         return input;
197 }
198
199 static void
200 gsf_input_gio_finalize (GObject *obj)
201 {
202         GObjectClass *parent_class;
203         GsfInputGio *input = (GsfInputGio *)obj;
204
205         g_input_stream_close (input->stream, NULL, NULL);
206         g_object_unref (input->stream);
207         input->stream = NULL;
208
209         g_object_unref (input->file);
210         input->file = NULL;
211
212         if (input->buf != NULL) {
213                 g_free (input->buf);
214                 input->buf  = NULL;
215                 input->buf_size = 0;
216         }
217
218         parent_class = g_type_class_peek (GSF_INPUT_TYPE);
219         if (parent_class && parent_class->finalize)
220                 parent_class->finalize (obj);
221 }
222
223 static GsfInput *
224 gsf_input_gio_dup (GsfInput *src_input, GError **err)
225 {
226         GsfInputGio *src = (GsfInputGio *)src_input;
227         GFile *clone;
228
229         g_return_val_if_fail (src_input != NULL, NULL);
230         g_return_val_if_fail (src->file != NULL, NULL);
231
232         clone = g_file_dup (src->file);
233         if (clone != NULL) {
234                 GsfInput *dst = gsf_input_gio_new (clone, err);
235
236                 /*
237                  * gsf_input_gio_new() adds a ref, or fails to create a new
238                  * file.  in any case, we need to unref the clone
239                  */
240                 g_object_unref (clone);
241
242                 return dst;
243         }
244
245         return NULL;
246 }
247
248 static guint8 const *
249 gsf_input_gio_read (GsfInput *input, size_t num_bytes, guint8 *buffer)
250 {
251         GsfInputGio *gio = GSF_INPUT_GIO (input);
252         size_t total_read = 0;
253
254         g_return_val_if_fail (gio != NULL, NULL);
255         g_return_val_if_fail (gio->stream != NULL, NULL);
256
257         if (buffer == NULL) {
258                 if (gio->buf_size < num_bytes) {
259                         gio->buf_size = num_bytes;
260                         g_free (gio->buf);
261                         gio->buf = g_new (guint8, gio->buf_size);
262                 }
263                 buffer = gio->buf;
264         }
265
266         while (1) {
267                 gssize nread;
268
269                 nread = g_input_stream_read (gio->stream, (buffer + total_read), (num_bytes - total_read), NULL, NULL);
270
271                 if (nread >= 0) {
272                         total_read += nread;
273                         if ((size_t) total_read == num_bytes) {
274                                 return buffer;
275                         }
276                 } else
277                         break;
278         }
279
280         return NULL;
281 }
282
283 static gboolean
284 gsf_input_gio_seek (GsfInput *input, gsf_off_t offset, GSeekType whence)
285 {
286         GsfInputGio *gio = GSF_INPUT_GIO (input);
287
288         g_return_val_if_fail (gio != NULL, TRUE);
289         g_return_val_if_fail (gio->stream != NULL, TRUE);
290         g_return_val_if_fail (can_seek (gio->stream), TRUE);
291
292         return (g_seekable_seek (G_SEEKABLE (gio->stream), offset, whence, NULL, NULL) ? FALSE : TRUE);
293 }
294
295 static void
296 gsf_input_gio_init (GObject *obj)
297 {
298         GsfInputGio *gio = GSF_INPUT_GIO (obj);
299
300         gio->file = NULL;
301         gio->stream = NULL;
302         gio->buf  = NULL;
303         gio->buf_size = 0;
304 }
305
306 static void
307 gsf_input_gio_class_init (GObjectClass *gobject_class)
308 {
309         GsfInputClass *input_class = GSF_INPUT_CLASS (gobject_class);
310
311         gobject_class->finalize = gsf_input_gio_finalize;
312         input_class->Dup        = gsf_input_gio_dup;
313         input_class->Read       = gsf_input_gio_read;
314         input_class->Seek       = gsf_input_gio_seek;
315 }
316
317 GSF_CLASS (GsfInputGio, gsf_input_gio,
318            gsf_input_gio_class_init, gsf_input_gio_init, GSF_INPUT_TYPE)
319
320 /***************************************************************************/
321 /***************************************************************************/