"Initial commit to Gerrit"
[profile/ivi/libgsf.git] / gsf / gsf-input.c
1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * gsf-input.c: interface for used by the ole layer to read raw data
4  *
5  * Copyright (C) 2002-2006 Jody Goldberg (jody@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-input-impl.h>
24 #include <gsf/gsf-input-gzip.h>
25 #include <gsf/gsf-impl-utils.h>
26 #include <string.h>
27
28 #ifdef HAVE_BZ2
29 #include <gsf/gsf-input-bzip.h>
30 #endif
31
32 #define GET_CLASS(instance) G_TYPE_INSTANCE_GET_CLASS (instance, GSF_INPUT_TYPE, GsfInputClass)
33
34 static GObjectClass *parent_class;
35
36 enum {
37         PROP_0,
38         PROP_NAME,
39         PROP_SIZE,
40         PROP_EOF,
41         PROP_REMAINING,
42         PROP_POS
43 };
44
45 #if 0
46 static void
47 gsf_input_set_property (GObject      *object,
48                         guint         property_id,
49                         GValue const *value,
50                         GParamSpec   *pspec)
51 {
52         switch (property_id)
53                 {
54                 default:
55                         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
56                         break;
57                 }
58 }
59 #endif
60
61 static void
62 gsf_input_get_property (GObject     *object,
63                         guint        property_id,
64                         GValue      *value,
65                         GParamSpec  *pspec)
66 {
67         /* gsf_off_t is typedef'd to gint64 */
68         switch (property_id) {
69         case PROP_NAME:
70                 g_value_set_string (value, gsf_input_name (GSF_INPUT (object)));
71                 break;
72         case PROP_SIZE:
73                 g_value_set_int64 (value, gsf_input_size (GSF_INPUT (object)));
74                 break;
75         case PROP_EOF:
76                 g_value_set_boolean (value, gsf_input_eof (GSF_INPUT (object)));
77                 break;
78         case PROP_REMAINING:
79                 g_value_set_int64 (value, gsf_input_remaining (GSF_INPUT (object)));
80                 break;
81         case PROP_POS:
82                 g_value_set_int64 (value, gsf_input_tell (GSF_INPUT (object)));
83                 break;
84         default:
85                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
86                 break;
87         }
88 }
89
90 static void
91 gsf_input_finalize (GObject *obj)
92 {
93         GsfInput *input = GSF_INPUT (obj);
94
95         g_free (input->name);
96         input->name = NULL;
97         if (input->container != NULL) {
98                 g_object_unref (G_OBJECT (input->container));
99                 input->container = NULL;
100         }
101         parent_class->finalize (obj);
102 }
103
104 static void
105 gsf_input_init (GObject *obj)
106 {
107         GsfInput *input = GSF_INPUT (obj);
108
109         input->size = 0;
110         input->cur_offset = 0;
111         input->name = NULL;
112         input->container = NULL;
113 }
114
115 static void
116 gsf_input_class_init (GObjectClass *gobject_class)
117 {
118         parent_class = g_type_class_peek_parent (gobject_class);
119
120         gobject_class->finalize     = gsf_input_finalize;
121         /* gobject_class->set_property = gsf_input_set_property; */
122         gobject_class->get_property = gsf_input_get_property;
123
124         g_object_class_install_property (gobject_class,
125                                          PROP_NAME,
126                                          g_param_spec_string ("name", "Name",
127                                                               "The Input's Name",
128                                                               NULL,
129                                                               GSF_PARAM_STATIC |
130                                                               G_PARAM_READABLE));
131         g_object_class_install_property (gobject_class,
132                                          PROP_SIZE,
133                                          g_param_spec_int64 ("size", "Size",
134                                                              "The Input's Size",
135                                                              0, G_MAXINT64, 0,
136                                                              GSF_PARAM_STATIC |
137                                                              G_PARAM_READABLE));
138         g_object_class_install_property (gobject_class,
139                                          PROP_EOF,
140                                          g_param_spec_boolean ("eof", "OEF",
141                                                                "End Of File",
142                                                                FALSE,
143                                                                GSF_PARAM_STATIC |
144                                                                G_PARAM_READABLE));
145         g_object_class_install_property (gobject_class,
146                                          PROP_REMAINING,
147                                          g_param_spec_int64 ("remaining", "Remaining",
148                                                              "Amount of Data Remaining",
149                                                              0, G_MAXINT64, 0,
150                                                              GSF_PARAM_STATIC |
151                                                              G_PARAM_READABLE));
152         g_object_class_install_property (gobject_class,
153                                          PROP_POS,
154                                          g_param_spec_int64 ("position", "Position",
155                                                              "The Output's Current Position",
156                                                              0, G_MAXINT64, 0,
157                                                              GSF_PARAM_STATIC |
158                                                              G_PARAM_READABLE));
159 }
160
161 GSF_CLASS_ABSTRACT (GsfInput, gsf_input,
162                     gsf_input_class_init, gsf_input_init,
163                     G_TYPE_OBJECT)
164
165 /**
166  * gsf_input_name :
167  * @input: the input stream
168  *
169  * The name of the input stream.
170  *
171  * Returns: @input's name in utf8 form, or %NULL if it has no name.
172  **/
173 char const *
174 gsf_input_name (GsfInput *input)
175 {
176         g_return_val_if_fail (GSF_IS_INPUT (input), NULL);
177         return input->name;
178 }
179
180 /**
181  * gsf_input_container :
182  * @input: the input stream
183  *
184  * Returns: but does not add a reference to @input's container.
185  *      Potentially %NULL
186  **/
187 GsfInfile *
188 gsf_input_container (GsfInput *input)
189 {
190         g_return_val_if_fail (GSF_IS_INPUT (input), NULL);
191         return input->container;
192 }
193
194 /**
195  * gsf_input_dup :
196  * @input: The input to duplicate
197  * @err: optionally %NULL
198  *
199  * Duplicates input @src leaving the new one at the same offset.
200  *
201  * Returns: the duplicate, or %NULL on error
202  **/
203 GsfInput *
204 gsf_input_dup (GsfInput *input, GError **err)
205 {
206         GsfInput *dst;
207
208         g_return_val_if_fail (input != NULL, NULL);
209
210         dst = GET_CLASS (input)->Dup (input, err);
211         if (dst != NULL) {
212                 if (dst->size != input->size) {
213                         if (err != NULL)
214                                 *err = g_error_new (gsf_input_error_id (), 0,
215                                                     "Duplicate size mismatch");
216                         g_object_unref (dst);
217                         return NULL;
218                 }
219                 if (gsf_input_seek (dst, input->cur_offset, G_SEEK_SET)) {
220                         if (err != NULL)
221                                 *err = g_error_new (gsf_input_error_id (), 0,
222                                                     "Seek failed");
223                         g_object_unref (dst);
224                         return NULL;
225                 }
226
227                 if (input->name != NULL)
228                         gsf_input_set_name (dst, input->name);
229                 dst->container = input->container;
230                 if (dst->container != NULL)
231                         g_object_ref (G_OBJECT (dst->container));
232         }
233         return dst;
234 }
235
236 /**
237  * gsf_input_open_sibling :
238  * @input: The input
239  *
240  * UNIMPLEMENTED BY ANY BACKEND
241  *      and it is probably unnecessary.   gsf_input_get_container provides
242  *      enough power to do what is necessary.
243  *
244  * Attempts to open a 'sibling' of @input.  The caller is responsible for
245  * managing the resulting object.
246  *
247  * Returns:  A related #GsfInput or %NULL on failure.
248  **/
249 GsfInput *
250 gsf_input_sibling (GsfInput const *input, char const *name, GError **err)
251 {
252         g_return_val_if_fail (GET_CLASS (input)->OpenSibling, NULL);
253
254         return GET_CLASS (input)->OpenSibling (input, name, err);
255 }
256
257 /**
258  * gsf_input_size :
259  * @input: The input
260  *
261  * Looks up and caches the number of bytes in the input
262  *
263  * Returns:  the size or -1 on error
264  **/
265 gsf_off_t
266 gsf_input_size (GsfInput *input)
267 {
268         g_return_val_if_fail (input != NULL, -1);
269         return input->size;
270 }
271
272 /**
273  * gsf_input_eof :
274  * @input: the input
275  *
276  * Are we at the end of the file ?
277  *
278  * Returns: TRUE if the input is at the eof.
279  **/
280 gboolean
281 gsf_input_eof (GsfInput *input)
282 {
283         g_return_val_if_fail (input != NULL, FALSE);
284
285         return input->cur_offset >= input->size;
286 }
287
288 /**
289  * gsf_input_read :
290  * @input: the input stream
291  * @num_bytes: number of bytes to read
292  * @optional_buffer: %NULL, or pointer to destination memory area
293  *
294  * Read at least @num_bytes.  Does not change the current position if there
295  * is an error.  Will only read if the entire amount can be read.  Invalidates
296  * the buffer associated with previous calls to gsf_input_read.
297  *
298  * Returns: pointer to the buffer or %NULL if there is an error or 0 bytes are
299  *      requested.
300  **/
301 guint8 const *
302 gsf_input_read (GsfInput *input, size_t num_bytes, guint8 *optional_buffer)
303 {
304         guint8 const *res;
305         gsf_off_t newpos = input->cur_offset + num_bytes;
306
307         g_return_val_if_fail (input != NULL, NULL);
308
309         if (num_bytes == 0 || newpos > input->size)
310                 return NULL;
311         res = GET_CLASS (input)->Read (input, num_bytes, optional_buffer);
312         if (res == NULL)
313                 return NULL;
314
315         input->cur_offset = newpos;
316         return res;
317 }
318
319 /**
320  * gsf_input_remaining :
321  * @input: the input stream
322  *
323  * Returns: the number of bytes left in the file.
324  **/
325 gsf_off_t
326 gsf_input_remaining (GsfInput *input)
327 {
328         g_return_val_if_fail (input != NULL, 0);
329
330         return input->size - input->cur_offset;
331 }
332
333 /**
334  * gsf_input_tell :
335  * @input: the input stream
336  *
337  * Returns: the current offset in the file.
338  **/
339 gsf_off_t
340 gsf_input_tell (GsfInput *input)
341 {
342         g_return_val_if_fail (input != NULL, 0);
343
344         return input->cur_offset;
345 }
346
347 /**
348  * gsf_input_seek :
349  * @input: the input stream
350  * @offset: target offset
351  * @whence: determines whether the offset is relative to the beginning or
352  *          the end of the stream, or to the current location.
353  *
354  * Move the current location in the input stream.
355  *
356  * Returns: TRUE on error.
357  **/
358 gboolean
359 gsf_input_seek (GsfInput *input, gsf_off_t offset, GSeekType whence)
360 {
361         gsf_off_t pos = offset;
362
363         g_return_val_if_fail (input != NULL, TRUE);
364
365         switch (whence) {
366         case G_SEEK_SET : break;
367         case G_SEEK_CUR : pos += input->cur_offset;     break;
368         case G_SEEK_END : pos += input->size;           break;
369         default : return TRUE;
370         }
371
372         if (pos < 0 || pos > input->size)
373                 return TRUE;
374
375         /*
376          * If we go nowhere, just return.  This in particular handles null
377          * seeks for streams with no seek method.
378          */
379         if (pos == input->cur_offset)
380                 return FALSE;
381
382         if (GET_CLASS (input)->Seek (input, offset, whence))
383                 return TRUE;
384
385         input->cur_offset = pos;
386         return FALSE;
387 }
388
389 /**
390  * gsf_input_set_name :
391  * @input: the input stream
392  * @name: the new name of the stream, or %NULL.
393  *
394  * protected.
395  *
396  * Returns: TRUE if the assignment was ok.
397  **/
398 gboolean
399 gsf_input_set_name (GsfInput *input, char const *name)
400 {
401         char *buf;
402
403         g_return_val_if_fail (input != NULL, FALSE);
404
405         buf = g_strdup (name);
406         g_free (input->name);
407         input->name = buf;
408         return TRUE;
409 }
410
411 /**
412  * gsf_input_set_name_from_filename :
413  * @input: the input stream
414  * @filename: the (fs-sys encoded) filename
415  *
416  * protected.
417  *
418  * Returns: TRUE if the assignment was ok.
419  **/
420 gboolean
421 gsf_input_set_name_from_filename (GsfInput *input, char const *filename)
422 {
423         g_return_val_if_fail (input != NULL, FALSE);
424
425         g_free (input->name);
426         input->name = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
427         return TRUE;
428 }
429
430
431 /**
432  * gsf_input_set_container :
433  * @input: the input stream
434  * @container:
435  *
436  * Returns: TRUE if the assignment was ok.
437  */
438 gboolean
439 gsf_input_set_container (GsfInput *input, GsfInfile *container)
440 {
441         g_return_val_if_fail (input != NULL, FALSE);
442
443         if (container != NULL)
444                 g_object_ref (G_OBJECT (container));
445         if (input->container != NULL)
446                 g_object_unref (G_OBJECT (input->container));
447         input->container = container;
448         return TRUE;
449 }
450
451 /**
452  * gsf_input_set_size :
453  * @input: the input stream
454  * @size: the size of the stream
455  *
456  * Returns: TRUE if the assignment was ok.
457  */
458 gboolean
459 gsf_input_set_size (GsfInput *input, gsf_off_t size)
460 {
461         g_return_val_if_fail (input != NULL, FALSE);
462         g_return_val_if_fail (size >= 0, FALSE);
463
464         input->size = size;
465         return TRUE;
466 }
467
468 /**
469  * gsf_input_seek_emulate :
470  * @input: stream to emulate seek for
471  * @pos: absolute position to seek to
472  *
473  * Emulate forward seeks by reading.
474  *
475  * Returns: TRUE if the emulation failed.
476  */
477 gboolean
478 gsf_input_seek_emulate (GsfInput *input, gsf_off_t pos)
479 {
480         if (pos < input->cur_offset)
481                 return TRUE;
482
483         while (pos > input->cur_offset) {
484                 gsf_off_t readcount = MIN (pos - input->cur_offset, 8192);
485                 if (!gsf_input_read (input, readcount, NULL))
486                         return TRUE;
487         }
488         return FALSE;
489 }
490
491 /****************************************************************************/
492
493 /**
494  * gsf_input_error_id :
495  *
496  * Returns: A utility quark to flag a GError as being an input problem.
497  */
498 GQuark 
499 gsf_input_error_id (void)
500 {
501         static GQuark quark;
502         if (!quark)
503                 quark = g_quark_from_static_string ("gsf_input_error_id");
504         return quark;
505 }
506
507 /**
508  * gsf_input_error :
509  *
510  * Deprecated as of GSF 1.12.0; use gsf_input_error_id() instead.
511  *
512  * Returns: A utility quark to flag a GError as being an input problem.
513  */
514 GQuark 
515 gsf_input_error (void)
516 {
517         return gsf_input_error_id ();
518 }
519
520 /****************************************************************************/
521
522 #define GSF_READ_BUFSIZE (1024 * 4)
523
524 /**
525  * gsf_input_copy :
526  * @input: a non-null #GsfInput
527  * @output: a non-null #GsfOutput
528  *
529  * Copy the contents from @input to @output from their respective
530  * current positions. So if you want to be sure to copy *everything*,
531  * make sure to call gsf_input_seek (input, 0, G_SEEK_SET) and
532  * gsf_output_seek (output, 0, G_SEEK_SET) first, if applicable.
533  *
534  * Returns: TRUE on Success
535  **/
536 gboolean
537 gsf_input_copy (GsfInput *input, GsfOutput *output)
538 {
539         gsf_off_t    remaining = 0;
540         gsf_off_t    toread    = 0;
541         const guint8 * buffer  = NULL;
542         gboolean     success   = TRUE;
543
544         g_return_val_if_fail (input != NULL, FALSE);
545         g_return_val_if_fail (output != NULL, FALSE);
546
547         while ((remaining = gsf_input_remaining (input)) > 0 && (success)) {
548                 toread = MIN (remaining, GSF_READ_BUFSIZE);
549                 if (NULL == (buffer = gsf_input_read (input, toread, NULL)))
550                         success = FALSE;
551                 else
552                         success = gsf_output_write (output, toread, buffer);
553         }
554
555         return success;
556 }
557
558 /****************************************************************************/
559
560 /**
561  * gsf_input_uncompress :
562  * @src: stream to be uncompressed.
563  *
564  * This functions takes ownership of the incoming reference and yields a
565  * new one as its output.
566  *
567  * Returns: A stream equivalent to the source stream, but uncompressed if
568  * the source was compressed.
569  **/
570 GsfInput *
571 gsf_input_uncompress (GsfInput *src)
572 {
573         gsf_off_t cur_offset = src->cur_offset;
574         const guint8 *data;
575
576         if (gsf_input_seek (src, 0, G_SEEK_SET))
577                 goto error;
578
579         /* Read header up front, so we avoid extra seeks in tests.  */
580         data = gsf_input_read (src, 4, NULL);
581         if (!data)
582                 goto error;
583
584         /* Let's try gzip.  */
585         {
586                 const unsigned char gzip_sig[2] = { 0x1f, 0x8b };
587
588                 if (memcmp (gzip_sig, data, sizeof (gzip_sig)) == 0) {
589                         GsfInput *res = gsf_input_gzip_new (src, NULL);
590                         if (res) {
591                                 g_object_unref (G_OBJECT (src));
592                                 return gsf_input_uncompress (res);
593                         } 
594                 }
595         }
596
597 #ifdef HAVE_BZ2
598         /* Let's try bzip.  */
599         {
600                 guint8 const *bzip_sig = "BZh";
601
602                 if (memcmp (bzip_sig, data, strlen (bzip_sig)) == 0) {
603                         GsfInput *res = gsf_input_memory_new_from_bzip (src, NULL);
604                         if (res) {
605                                 g_object_unref (G_OBJECT (src));
606                                 return gsf_input_uncompress (res);
607                         }
608                 }
609         }
610 #endif
611
612         /* Other methods go here.  */
613
614  error:
615         (void)gsf_input_seek (src, cur_offset, G_SEEK_SET);
616         return src;
617 }
618
619 #if 0
620
621 #include <gsf/gsf-input-stdio.h>
622
623 #ifdef HAVE_GNOME
624 #include <gsf-gnome/gsf-input-gnomevfs.h>
625 #endif
626
627 GsfInput *
628 gsf_input_new_for_uri (char const * uri, GError ** err)
629 {
630         GsfInput * input = NULL;
631         size_t len;
632
633         g_return_val_if_fail (uri, NULL);
634
635         len = strlen (uri);
636         g_return_val_if_fail (len, NULL);
637
638         if (len > 3 && !strstr (uri, ":/")) {
639                 /* assume plain file */
640                 input = gsf_input_stdio_new (uri, err);
641         } else {
642 #if HAVE_GNOME
643                 /* have gnome, let GnomeVFS deal with this */
644                 input = gsf_input_gnomevfs_new (uri, err);
645 #else           
646                 if (len > 7 && !strncmp (uri, "file:/", 6)) {
647                         /* dumb attempt to translate this into a local path */
648                         input = gsf_input_stdio_new (uri+7, err);
649                 } 
650                 /* else: unknown or unhandled protocol - bail */
651 #endif
652         }
653
654         return input;
655 }
656
657 #endif