1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * gsf-input.c: interface for used by the ole layer to read raw data
5 * Copyright (C) 2002-2006 Jody Goldberg (jody@gnome.org)
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.
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.
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
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>
29 #include <gsf/gsf-input-bzip.h>
32 #define GET_CLASS(instance) G_TYPE_INSTANCE_GET_CLASS (instance, GSF_INPUT_TYPE, GsfInputClass)
34 static GObjectClass *parent_class;
47 gsf_input_set_property (GObject *object,
55 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
62 gsf_input_get_property (GObject *object,
67 /* gsf_off_t is typedef'd to gint64 */
68 switch (property_id) {
70 g_value_set_string (value, gsf_input_name (GSF_INPUT (object)));
73 g_value_set_int64 (value, gsf_input_size (GSF_INPUT (object)));
76 g_value_set_boolean (value, gsf_input_eof (GSF_INPUT (object)));
79 g_value_set_int64 (value, gsf_input_remaining (GSF_INPUT (object)));
82 g_value_set_int64 (value, gsf_input_tell (GSF_INPUT (object)));
85 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
91 gsf_input_finalize (GObject *obj)
93 GsfInput *input = GSF_INPUT (obj);
97 if (input->container != NULL) {
98 g_object_unref (G_OBJECT (input->container));
99 input->container = NULL;
101 parent_class->finalize (obj);
105 gsf_input_init (GObject *obj)
107 GsfInput *input = GSF_INPUT (obj);
110 input->cur_offset = 0;
112 input->container = NULL;
116 gsf_input_class_init (GObjectClass *gobject_class)
118 parent_class = g_type_class_peek_parent (gobject_class);
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;
124 g_object_class_install_property (gobject_class,
126 g_param_spec_string ("name", "Name",
131 g_object_class_install_property (gobject_class,
133 g_param_spec_int64 ("size", "Size",
138 g_object_class_install_property (gobject_class,
140 g_param_spec_boolean ("eof", "OEF",
145 g_object_class_install_property (gobject_class,
147 g_param_spec_int64 ("remaining", "Remaining",
148 "Amount of Data Remaining",
152 g_object_class_install_property (gobject_class,
154 g_param_spec_int64 ("position", "Position",
155 "The Output's Current Position",
161 GSF_CLASS_ABSTRACT (GsfInput, gsf_input,
162 gsf_input_class_init, gsf_input_init,
167 * @input: the input stream
169 * The name of the input stream.
171 * Returns: @input's name in utf8 form, or %NULL if it has no name.
174 gsf_input_name (GsfInput *input)
176 g_return_val_if_fail (GSF_IS_INPUT (input), NULL);
181 * gsf_input_container :
182 * @input: the input stream
184 * Returns: but does not add a reference to @input's container.
188 gsf_input_container (GsfInput *input)
190 g_return_val_if_fail (GSF_IS_INPUT (input), NULL);
191 return input->container;
196 * @input: The input to duplicate
197 * @err: optionally %NULL
199 * Duplicates input @src leaving the new one at the same offset.
201 * Returns: the duplicate, or %NULL on error
204 gsf_input_dup (GsfInput *input, GError **err)
208 g_return_val_if_fail (input != NULL, NULL);
210 dst = GET_CLASS (input)->Dup (input, err);
212 if (dst->size != input->size) {
214 *err = g_error_new (gsf_input_error_id (), 0,
215 "Duplicate size mismatch");
216 g_object_unref (dst);
219 if (gsf_input_seek (dst, input->cur_offset, G_SEEK_SET)) {
221 *err = g_error_new (gsf_input_error_id (), 0,
223 g_object_unref (dst);
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));
237 * gsf_input_open_sibling :
240 * UNIMPLEMENTED BY ANY BACKEND
241 * and it is probably unnecessary. gsf_input_get_container provides
242 * enough power to do what is necessary.
244 * Attempts to open a 'sibling' of @input. The caller is responsible for
245 * managing the resulting object.
247 * Returns: A related #GsfInput or %NULL on failure.
250 gsf_input_sibling (GsfInput const *input, char const *name, GError **err)
252 g_return_val_if_fail (GET_CLASS (input)->OpenSibling, NULL);
254 return GET_CLASS (input)->OpenSibling (input, name, err);
261 * Looks up and caches the number of bytes in the input
263 * Returns: the size or -1 on error
266 gsf_input_size (GsfInput *input)
268 g_return_val_if_fail (input != NULL, -1);
276 * Are we at the end of the file ?
278 * Returns: TRUE if the input is at the eof.
281 gsf_input_eof (GsfInput *input)
283 g_return_val_if_fail (input != NULL, FALSE);
285 return input->cur_offset >= input->size;
290 * @input: the input stream
291 * @num_bytes: number of bytes to read
292 * @optional_buffer: %NULL, or pointer to destination memory area
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.
298 * Returns: pointer to the buffer or %NULL if there is an error or 0 bytes are
302 gsf_input_read (GsfInput *input, size_t num_bytes, guint8 *optional_buffer)
305 gsf_off_t newpos = input->cur_offset + num_bytes;
307 g_return_val_if_fail (input != NULL, NULL);
309 if (num_bytes == 0 || newpos > input->size)
311 res = GET_CLASS (input)->Read (input, num_bytes, optional_buffer);
315 input->cur_offset = newpos;
320 * gsf_input_remaining :
321 * @input: the input stream
323 * Returns: the number of bytes left in the file.
326 gsf_input_remaining (GsfInput *input)
328 g_return_val_if_fail (input != NULL, 0);
330 return input->size - input->cur_offset;
335 * @input: the input stream
337 * Returns: the current offset in the file.
340 gsf_input_tell (GsfInput *input)
342 g_return_val_if_fail (input != NULL, 0);
344 return input->cur_offset;
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.
354 * Move the current location in the input stream.
356 * Returns: TRUE on error.
359 gsf_input_seek (GsfInput *input, gsf_off_t offset, GSeekType whence)
361 gsf_off_t pos = offset;
363 g_return_val_if_fail (input != NULL, TRUE);
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;
372 if (pos < 0 || pos > input->size)
376 * If we go nowhere, just return. This in particular handles null
377 * seeks for streams with no seek method.
379 if (pos == input->cur_offset)
382 if (GET_CLASS (input)->Seek (input, offset, whence))
385 input->cur_offset = pos;
390 * gsf_input_set_name :
391 * @input: the input stream
392 * @name: the new name of the stream, or %NULL.
396 * Returns: TRUE if the assignment was ok.
399 gsf_input_set_name (GsfInput *input, char const *name)
403 g_return_val_if_fail (input != NULL, FALSE);
405 buf = g_strdup (name);
406 g_free (input->name);
412 * gsf_input_set_name_from_filename :
413 * @input: the input stream
414 * @filename: the (fs-sys encoded) filename
418 * Returns: TRUE if the assignment was ok.
421 gsf_input_set_name_from_filename (GsfInput *input, char const *filename)
423 g_return_val_if_fail (input != NULL, FALSE);
425 g_free (input->name);
426 input->name = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
432 * gsf_input_set_container :
433 * @input: the input stream
436 * Returns: TRUE if the assignment was ok.
439 gsf_input_set_container (GsfInput *input, GsfInfile *container)
441 g_return_val_if_fail (input != NULL, FALSE);
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;
452 * gsf_input_set_size :
453 * @input: the input stream
454 * @size: the size of the stream
456 * Returns: TRUE if the assignment was ok.
459 gsf_input_set_size (GsfInput *input, gsf_off_t size)
461 g_return_val_if_fail (input != NULL, FALSE);
462 g_return_val_if_fail (size >= 0, FALSE);
469 * gsf_input_seek_emulate :
470 * @input: stream to emulate seek for
471 * @pos: absolute position to seek to
473 * Emulate forward seeks by reading.
475 * Returns: TRUE if the emulation failed.
478 gsf_input_seek_emulate (GsfInput *input, gsf_off_t pos)
480 if (pos < input->cur_offset)
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))
491 /****************************************************************************/
494 * gsf_input_error_id :
496 * Returns: A utility quark to flag a GError as being an input problem.
499 gsf_input_error_id (void)
503 quark = g_quark_from_static_string ("gsf_input_error_id");
510 * Deprecated as of GSF 1.12.0; use gsf_input_error_id() instead.
512 * Returns: A utility quark to flag a GError as being an input problem.
515 gsf_input_error (void)
517 return gsf_input_error_id ();
520 /****************************************************************************/
522 #define GSF_READ_BUFSIZE (1024 * 4)
526 * @input: a non-null #GsfInput
527 * @output: a non-null #GsfOutput
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.
534 * Returns: TRUE on Success
537 gsf_input_copy (GsfInput *input, GsfOutput *output)
539 gsf_off_t remaining = 0;
540 gsf_off_t toread = 0;
541 const guint8 * buffer = NULL;
542 gboolean success = TRUE;
544 g_return_val_if_fail (input != NULL, FALSE);
545 g_return_val_if_fail (output != NULL, FALSE);
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)))
552 success = gsf_output_write (output, toread, buffer);
558 /****************************************************************************/
561 * gsf_input_uncompress :
562 * @src: stream to be uncompressed.
564 * This functions takes ownership of the incoming reference and yields a
565 * new one as its output.
567 * Returns: A stream equivalent to the source stream, but uncompressed if
568 * the source was compressed.
571 gsf_input_uncompress (GsfInput *src)
573 gsf_off_t cur_offset = src->cur_offset;
576 if (gsf_input_seek (src, 0, G_SEEK_SET))
579 /* Read header up front, so we avoid extra seeks in tests. */
580 data = gsf_input_read (src, 4, NULL);
584 /* Let's try gzip. */
586 const unsigned char gzip_sig[2] = { 0x1f, 0x8b };
588 if (memcmp (gzip_sig, data, sizeof (gzip_sig)) == 0) {
589 GsfInput *res = gsf_input_gzip_new (src, NULL);
591 g_object_unref (G_OBJECT (src));
592 return gsf_input_uncompress (res);
598 /* Let's try bzip. */
600 guint8 const *bzip_sig = "BZh";
602 if (memcmp (bzip_sig, data, strlen (bzip_sig)) == 0) {
603 GsfInput *res = gsf_input_memory_new_from_bzip (src, NULL);
605 g_object_unref (G_OBJECT (src));
606 return gsf_input_uncompress (res);
612 /* Other methods go here. */
615 (void)gsf_input_seek (src, cur_offset, G_SEEK_SET);
621 #include <gsf/gsf-input-stdio.h>
624 #include <gsf-gnome/gsf-input-gnomevfs.h>
628 gsf_input_new_for_uri (char const * uri, GError ** err)
630 GsfInput * input = NULL;
633 g_return_val_if_fail (uri, NULL);
636 g_return_val_if_fail (len, NULL);
638 if (len > 3 && !strstr (uri, ":/")) {
639 /* assume plain file */
640 input = gsf_input_stdio_new (uri, err);
643 /* have gnome, let GnomeVFS deal with this */
644 input = gsf_input_gnomevfs_new (uri, err);
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);
650 /* else: unknown or unhandled protocol - bail */