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.h>
24 #include <gsf/gsf.h>
25 #include <glib/gi18n-lib.h>
26 
27 #include <string.h>
28 
29 
30 /*
31  * FIXME!
32  *
33  * We cannot extend GsfInput, so for now we hang this on the object as
34  * an attribute.
35  */
36 #define MODTIME_ATTR "GsfInput::modtime"
37 
38 
39 
40 #define GET_CLASS(instance) G_TYPE_INSTANCE_GET_CLASS (instance, GSF_INPUT_TYPE, GsfInputClass)
41 
42 static GObjectClass *parent_class;
43 
44 enum {
45 	PROP_0,
46 	PROP_NAME,
47 	PROP_SIZE,
48 	PROP_EOF,
49 	PROP_REMAINING,
50 	PROP_POS,
51 	PROP_MODTIME,
52 	PROP_CONTAINER
53 };
54 
55 static void
gsf_input_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)56 gsf_input_get_property (GObject     *object,
57 			guint        property_id,
58 			GValue      *value,
59 			GParamSpec  *pspec)
60 {
61 	GsfInput *input = GSF_INPUT (object);
62 
63 	/* gsf_off_t is typedef'd to gint64 */
64 
65 	switch (property_id) {
66 	case PROP_NAME:
67 		g_value_set_string (value, gsf_input_name (input));
68 		break;
69 	case PROP_SIZE:
70 		g_value_set_int64 (value, gsf_input_size (input));
71 		break;
72 	case PROP_EOF:
73 		g_value_set_boolean (value, gsf_input_eof (input));
74 		break;
75 	case PROP_REMAINING:
76 		g_value_set_int64 (value, gsf_input_remaining (input));
77 		break;
78 	case PROP_POS:
79 		g_value_set_int64 (value, gsf_input_tell (input));
80 		break;
81 	case PROP_MODTIME:
82 		g_value_set_boxed (value, gsf_input_get_modtime (input));
83 		break;
84 	case PROP_CONTAINER:
85 		g_value_set_object (value, input->container);
86 		break;
87 	default:
88 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
89 		break;
90 	}
91 }
92 
93 static void
gsf_input_dispose(GObject * obj)94 gsf_input_dispose (GObject *obj)
95 {
96 	GsfInput *input = GSF_INPUT (obj);
97 
98 	gsf_input_set_container (input, NULL);
99 	gsf_input_set_name (input, NULL);
100 	gsf_input_set_modtime (input, NULL);
101 
102 	parent_class->dispose (obj);
103 }
104 
105 static void
gsf_input_init(GObject * obj)106 gsf_input_init (GObject *obj)
107 {
108 	GsfInput *input = GSF_INPUT (obj);
109 
110 	input->size = 0;
111 	input->cur_offset = 0;
112 	input->name = NULL;
113 	input->container = NULL;
114 }
115 
116 static void
gsf_input_class_init(GObjectClass * gobject_class)117 gsf_input_class_init (GObjectClass *gobject_class)
118 {
119 	parent_class = g_type_class_peek_parent (gobject_class);
120 
121 	gobject_class->dispose = gsf_input_dispose;
122 	gobject_class->get_property = gsf_input_get_property;
123 
124 	g_object_class_install_property
125 		(gobject_class,
126 		 PROP_NAME,
127 		 g_param_spec_string ("name",
128 				      _("Name"),
129 				      _("The input's name"),
130 				      NULL,
131 				      G_PARAM_STATIC_STRINGS |
132 				      G_PARAM_READABLE));
133 
134 	/**
135 	 * GsfInput:size:
136 	 *
137 	 * The total number of bytes in the file.
138 	 */
139 	g_object_class_install_property
140 		(gobject_class,
141 		 PROP_SIZE,
142 		 g_param_spec_int64 ("size",
143 				     _("Size"),
144 				     _("The input's size"),
145 				     0, G_MAXINT64, 0,
146 				     G_PARAM_STATIC_STRINGS |
147 				     G_PARAM_READABLE));
148 
149 	/**
150 	 * GsfInput:eof:
151 	 *
152 	 * %TRUE if the end of the file has been reached.
153 	 */
154 	g_object_class_install_property
155 		(gobject_class,
156 		 PROP_EOF,
157 		 g_param_spec_boolean ("eof",
158 				       _("EOF"),
159 				       _("End of file"),
160 				       FALSE,
161 				       G_PARAM_STATIC_STRINGS |
162 				       G_PARAM_READABLE));
163 
164 	/**
165 	 * GsfInput:remaining:
166 	 *
167 	 * The number of bytes remaining in the file.
168 	 */
169 	g_object_class_install_property
170 		(gobject_class,
171 		 PROP_REMAINING,
172 		 g_param_spec_int64 ("remaining",
173 				     _("Remaining"),
174 				     _("Amount of data remaining"),
175 				     0, G_MAXINT64, 0,
176 				     G_PARAM_STATIC_STRINGS |
177 				     G_PARAM_READABLE));
178 
179 	/**
180 	 * GsfInput:position:
181 	 *
182 	 * The current position in the input.
183 	 */
184 	g_object_class_install_property
185 		(gobject_class,
186 		 PROP_POS,
187 		 g_param_spec_int64 ("position",
188 				     _("Position"),
189 				     _("The input's current position"),
190 				     0, G_MAXINT64, 0,
191 				     G_PARAM_STATIC_STRINGS |
192 				     G_PARAM_READABLE));
193 
194 	/**
195 	 * GsfInput:modtime:
196 	 *
197 	 * The time the input was last updated.  This represents the
198 	 * timestamp from the originating file or @GsfInfile member.
199 	 * It is not supported by all derived classes.
200 	 */
201 	g_object_class_install_property
202 		(gobject_class,
203 		 PROP_MODTIME,
204 		 g_param_spec_boxed
205 		 ("modtime",
206 		  _("Modification time"),
207 		  _("An optional GDateTime representing the time the input was last changed"),
208 		  G_TYPE_DATE_TIME,
209 		  G_PARAM_STATIC_STRINGS |
210 		  G_PARAM_READABLE));
211 
212 	/**
213 	 * GsfInput:container:
214 	 *
215 	 * The container, optionally %NULL, in which this input lives.
216 	 */
217 	g_object_class_install_property
218 		(gobject_class, PROP_CONTAINER,
219 		 g_param_spec_object ("container",
220 				      _("Container"),
221 				      _("The parent GsfInfile"),
222 				      GSF_INFILE_TYPE,
223 				      G_PARAM_STATIC_STRINGS |
224 				      G_PARAM_READABLE));
225 }
226 
GSF_CLASS_ABSTRACT(GsfInput,gsf_input,gsf_input_class_init,gsf_input_init,G_TYPE_OBJECT)227 GSF_CLASS_ABSTRACT (GsfInput, gsf_input,
228 		    gsf_input_class_init, gsf_input_init,
229 		    G_TYPE_OBJECT)
230 
231 /**
232  * gsf_input_name:
233  * @input: the input stream
234  *
235  * The name of the input stream.
236  *
237  * Returns: (transfer none): @input's name in utf8 form, or %NULL if it
238  * has no name.
239  **/
240 char const *
241 gsf_input_name (GsfInput *input)
242 {
243 	g_return_val_if_fail (GSF_IS_INPUT (input), NULL);
244 	return input->name;
245 }
246 
247 /**
248  * gsf_input_container:
249  * @input: the input stream
250  *
251  * Returns: (transfer none) (nullable): @input's container
252  **/
253 GsfInfile *
gsf_input_container(GsfInput * input)254 gsf_input_container (GsfInput *input)
255 {
256 	g_return_val_if_fail (GSF_IS_INPUT (input), NULL);
257 	return input->container;
258 }
259 
260 /**
261  * gsf_input_dup: (virtual Dup)
262  * @input: The input to duplicate
263  * @err: (allow-none): place to store a #GError if anything goes wrong
264  *
265  * Duplicates @input leaving the new one at the same offset.
266  *
267  * Returns: (transfer full) (nullable): the duplicate
268  **/
269 GsfInput *
gsf_input_dup(GsfInput * input,GError ** err)270 gsf_input_dup (GsfInput *input, GError **err)
271 {
272 	GsfInput *dst;
273 
274 	g_return_val_if_fail (input != NULL, NULL);
275 
276 	dst = GET_CLASS (input)->Dup (input, err);
277 	if (dst != NULL) {
278 		if (dst->size != input->size) {
279 			if (err != NULL)
280 				*err = g_error_new (gsf_input_error_id (), 0,
281 						    _("Duplicate size mismatch"));
282 			g_object_unref (dst);
283 			return NULL;
284 		}
285 		if (gsf_input_seek (dst, input->cur_offset, G_SEEK_SET)) {
286 			if (err != NULL)
287 				*err = g_error_new (gsf_input_error_id (), 0,
288 						    _("Seek failed"));
289 			g_object_unref (dst);
290 			return NULL;
291 		}
292 
293 		gsf_input_set_name (dst, input->name);
294 		gsf_input_set_container (dst, input->container);
295 	}
296 	return dst;
297 }
298 
299 /**
300  * gsf_input_sibling: (virtual OpenSibling)
301  * @input: The input
302  * @name: name.
303  * @err: (allow-none): place to store a #GError if anything goes wrong
304  *
305  * UNIMPLEMENTED BY ANY BACKEND
306  * 	and it is probably unnecessary.   gsf_input_get_container provides
307  * 	enough power to do what is necessary.
308  *
309  * Attempts to open a 'sibling' of @input.  The caller is responsible for
310  * managing the resulting object.
311  *
312  * Returns: (transfer full): A related #GsfInput
313  **/
314 GsfInput *
gsf_input_sibling(GsfInput const * input,char const * name,GError ** err)315 gsf_input_sibling (GsfInput const *input, char const *name, GError **err)
316 {
317 	g_return_val_if_fail (GET_CLASS (input)->OpenSibling, NULL);
318 
319 	return GET_CLASS (input)->OpenSibling (input, name, err);
320 }
321 
322 /**
323  * gsf_input_size:
324  * @input: The input
325  *
326  * Returns: the total number of bytes in the input or -1 on error
327  **/
328 gsf_off_t
gsf_input_size(GsfInput * input)329 gsf_input_size (GsfInput *input)
330 {
331 	g_return_val_if_fail (input != NULL, -1);
332 	return input->size;
333 }
334 
335 /**
336  * gsf_input_eof:
337  * @input: the input
338  *
339  * Are we at the end of the file?
340  *
341  * Returns: %TRUE if the input is at the eof.
342  **/
343 gboolean
gsf_input_eof(GsfInput * input)344 gsf_input_eof (GsfInput *input)
345 {
346 	g_return_val_if_fail (input != NULL, FALSE);
347 
348 	return input->cur_offset >= input->size;
349 }
350 
351 /**
352  * gsf_input_read: (virtual Read) (skip)
353  * @input: the input stream
354  * @num_bytes: number of bytes to read
355  * @optional_buffer: (array) (allow-none): Pointer to destination memory area
356  *
357  * Read at least @num_bytes.  Does not change the current position if there
358  * is an error.  Will only read if the entire amount can be read.  Invalidates
359  * the buffer associated with previous calls to gsf_input_read.
360  *
361  * Returns: (array) (nullable): pointer to the buffer or %NULL if there is
362  * an error or 0 bytes are requested.
363  **/
364 
365 guint8 const *
gsf_input_read(GsfInput * input,size_t num_bytes,guint8 * optional_buffer)366 gsf_input_read (GsfInput *input, size_t num_bytes, guint8 *optional_buffer)
367 {
368 	guint8 const *res;
369 	gsf_off_t newpos = input->cur_offset + num_bytes;
370 
371 	g_return_val_if_fail (input != NULL, NULL);
372 
373 	if (newpos <= input->cur_offset || newpos > input->size)
374 		return NULL;
375 	res = GET_CLASS (input)->Read (input, num_bytes, optional_buffer);
376 	if (res == NULL)
377 		return NULL;
378 
379 	input->cur_offset = newpos;
380 	return res;
381 }
382 
383 /**
384  * gsf_input_read0: (rename-to gsf_input_read)
385  * @input: the input stream
386  * @num_bytes: (in): number of bytes to read
387  * @bytes_read: (out): copy of @num_bytes
388  *
389  * Read @num_bytes.  Does not change the current position if there
390  * is an error.  Will only read if the entire amount can be read.
391  *
392  * Returns: (array length=bytes_read) (element-type guint8) (transfer full):
393  * the data read.
394  **/
395 
396 guint8 *
gsf_input_read0(GsfInput * input,size_t num_bytes,size_t * bytes_read)397 gsf_input_read0 (GsfInput *input, size_t num_bytes, size_t *bytes_read)
398 {
399 	guint8 *res;
400 
401 	g_return_val_if_fail (input != NULL, NULL);
402 	g_return_val_if_fail (bytes_read != NULL, NULL);
403 
404 	*bytes_read = num_bytes;
405 
406 	if (num_bytes < 0 || (gsf_off_t)num_bytes > gsf_input_remaining (input))
407 		return NULL;
408 
409 	res = g_new (guint8, num_bytes);
410 	if (gsf_input_read (input, num_bytes, res))
411 		return res;
412 
413 	g_free (res);
414 	return NULL;
415 }
416 
417 /**
418  * gsf_input_remaining:
419  * @input: the input stream
420  *
421  * Returns: the number of bytes left in the file.
422  **/
423 gsf_off_t
gsf_input_remaining(GsfInput * input)424 gsf_input_remaining (GsfInput *input)
425 {
426 	g_return_val_if_fail (input != NULL, 0);
427 
428 	return input->size - input->cur_offset;
429 }
430 
431 /**
432  * gsf_input_tell:
433  * @input: the input stream
434  *
435  * Returns: the current offset in the file.
436  **/
437 gsf_off_t
gsf_input_tell(GsfInput * input)438 gsf_input_tell (GsfInput *input)
439 {
440 	g_return_val_if_fail (input != NULL, 0);
441 
442 	return input->cur_offset;
443 }
444 
445 /**
446  * gsf_input_seek: (virtual Seek)
447  * @input: the input stream
448  * @offset: target offset
449  * @whence: determines whether the offset is relative to the beginning or
450  *          the end of the stream, or to the current location.
451  *
452  * Move the current location in the input stream.
453  *
454  * Returns: %TRUE on error.
455  **/
456 gboolean
gsf_input_seek(GsfInput * input,gsf_off_t offset,GSeekType whence)457 gsf_input_seek (GsfInput *input, gsf_off_t offset, GSeekType whence)
458 {
459 	gsf_off_t pos = offset;
460 
461 	g_return_val_if_fail (input != NULL, TRUE);
462 
463 	switch (whence) {
464 	case G_SEEK_SET : break;
465 	case G_SEEK_CUR : pos += input->cur_offset;	break;
466 	case G_SEEK_END : pos += input->size;		break;
467 	default : return TRUE;
468 	}
469 
470 	if (pos < 0 || pos > input->size)
471 		return TRUE;
472 
473 	/*
474 	 * If we go nowhere, just return.  This in particular handles null
475 	 * seeks for streams with no seek method.
476 	 */
477 	if (pos == input->cur_offset)
478 		return FALSE;
479 
480 	if (GET_CLASS (input)->Seek (input, offset, whence))
481 		return TRUE;
482 
483 	input->cur_offset = pos;
484 	return FALSE;
485 }
486 
487 /**
488  * gsf_input_set_name:
489  * @input: the input stream
490  * @name: (allow-none): the new name of the stream
491  *
492  * protected.
493  *
494  * Returns: %TRUE if the assignment was ok.
495  **/
496 gboolean
gsf_input_set_name(GsfInput * input,char const * name)497 gsf_input_set_name (GsfInput *input, char const *name)
498 {
499 	g_return_val_if_fail (input != NULL, FALSE);
500 
501 	if (g_strcmp0 (name, input->name)) {
502 		g_free (input->name);
503 		input->name = g_strdup (name);
504 		g_object_notify (G_OBJECT (input), "name");
505 	}
506 
507 	return TRUE;
508 }
509 
510 /**
511  * gsf_input_set_name_from_filename:
512  * @input: the input stream
513  * @filename: the (fs-sys encoded) filename
514  *
515  * protected.
516  *
517  * Returns: %TRUE if the assignment was ok.
518  **/
519 gboolean
gsf_input_set_name_from_filename(GsfInput * input,char const * filename)520 gsf_input_set_name_from_filename (GsfInput *input, char const *filename)
521 {
522 	g_return_val_if_fail (input != NULL, FALSE);
523 
524 	g_free (input->name);
525 	input->name = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
526 	return TRUE;
527 }
528 
529 
530 /**
531  * gsf_input_set_container:
532  * @input: the input stream
533  * @container: (allow-none)
534  *
535  * Returns: %TRUE if the assignment was ok.
536  */
537 gboolean
gsf_input_set_container(GsfInput * input,GsfInfile * container)538 gsf_input_set_container (GsfInput *input, GsfInfile *container)
539 {
540 	g_return_val_if_fail (input != NULL, FALSE);
541 
542 	if (container != NULL)
543 		g_object_ref (container);
544 	if (input->container != NULL)
545 		g_object_unref (input->container);
546 	input->container = container;
547 	return TRUE;
548 }
549 
550 /**
551  * gsf_input_set_size:
552  * @input: the input stream
553  * @size: the size of the stream
554  *
555  * Returns: %TRUE if the assignment was ok.
556  */
557 gboolean
gsf_input_set_size(GsfInput * input,gsf_off_t size)558 gsf_input_set_size (GsfInput *input, gsf_off_t size)
559 {
560 	g_return_val_if_fail (input != NULL, FALSE);
561 	g_return_val_if_fail (size >= 0, FALSE);
562 
563 	input->size = size;
564 	return TRUE;
565 }
566 
567 /**
568  * gsf_input_seek_emulate:
569  * @input: stream to emulate seek for
570  * @pos: absolute position to seek to
571  *
572  * Emulate forward seeks by reading.
573  *
574  * Returns: %TRUE if the emulation failed.
575  */
576 gboolean
gsf_input_seek_emulate(GsfInput * input,gsf_off_t pos)577 gsf_input_seek_emulate (GsfInput *input, gsf_off_t pos)
578 {
579 	if (pos < input->cur_offset)
580 		return TRUE;
581 
582 	while (pos > input->cur_offset) {
583 		gsf_off_t readcount = MIN (pos - input->cur_offset, 8192);
584 		if (!gsf_input_read (input, readcount, NULL))
585 			return TRUE;
586 	}
587 	return FALSE;
588 }
589 
590 /**
591  * gsf_input_get_modtime:
592  * @input: the input stream
593  *
594  * Returns: (transfer none): A #GDateTime representing when the input
595  * was last modified, or %NULL if not known.
596  */
597 GDateTime *
gsf_input_get_modtime(GsfInput * input)598 gsf_input_get_modtime (GsfInput *input)
599 {
600 	g_return_val_if_fail (GSF_IS_INPUT (input), NULL);
601 
602 	return g_object_get_data (G_OBJECT (input), MODTIME_ATTR);
603 }
604 
605 /**
606  * gsf_input_set_modtime:
607  * @input: the input stream
608  * @modtime: (transfer none) (allow-none): the new modification time.
609  *
610  * protected.
611  *
612  * Returns: %TRUE if the assignment was ok.
613  */
614 gboolean
gsf_input_set_modtime(GsfInput * input,GDateTime * modtime)615 gsf_input_set_modtime (GsfInput *input, GDateTime *modtime)
616 {
617 	g_return_val_if_fail (GSF_IS_INPUT (input), FALSE);
618 
619 	if (modtime)
620 		modtime = g_date_time_add (modtime, 0); /* Copy */
621 
622 	/* This actually also works for null modtime.  */
623 	g_object_set_data_full (G_OBJECT (input),
624 				MODTIME_ATTR, modtime,
625 				(GDestroyNotify)g_date_time_unref);
626 
627 	return TRUE;
628 }
629 
630 gboolean
gsf_input_set_modtime_from_stat(GsfInput * input,const struct stat * st)631 gsf_input_set_modtime_from_stat (GsfInput *input,
632 				 const struct stat *st)
633 {
634 	GDateTime *modtime = NULL, *ut = NULL;
635 	gboolean res;
636 	gint64 sec, usec;
637 
638 	if (st->st_mtime == (time_t)-1)
639 		return FALSE;
640 
641 	sec = st->st_mtime;
642 #if defined (HAVE_STRUCT_STAT_ST_MTIMENSEC)
643 	usec = st->st_mtimensec / 1000;
644 #elif defined (HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC)
645 	usec =  st->st_mtim.tv_nsec / 1000;
646 #else
647 	usec = 0;
648 #endif
649 
650 	ut = g_date_time_new_from_unix_utc (sec);
651 	modtime = g_date_time_add (ut, usec);
652 	res = gsf_input_set_modtime (GSF_INPUT (input), modtime);
653 	g_date_time_unref (ut);
654 	g_date_time_unref (modtime);
655 
656 	return res;
657 }
658 
659 
660 /****************************************************************************/
661 
662 /**
663  * gsf_input_error_id:
664  *
665  * Returns: A utility quark to flag a #GError as being an input problem.
666  */
667 GQuark
gsf_input_error_id(void)668 gsf_input_error_id (void)
669 {
670 	static GQuark quark;
671 	if (!quark)
672 		quark = g_quark_from_static_string ("gsf_input_error_id");
673 	return quark;
674 }
675 
676 /**
677  * gsf_input_error:
678  *
679  * Deprecated as of GSF 1.12.0; use gsf_input_error_id() instead.
680  *
681  * Returns: A utility quark to flag a #GError as being an input problem.
682  */
683 GQuark
gsf_input_error(void)684 gsf_input_error (void)
685 {
686 	return gsf_input_error_id ();
687 }
688 
689 /****************************************************************************/
690 
691 #define GSF_READ_BUFSIZE (1024 * 4)
692 
693 /**
694  * gsf_input_copy:
695  * @input: a non-null #GsfInput
696  * @output: a non-null #GsfOutput
697  *
698  * Copy the contents from @input to @output from their respective
699  * current positions. So if you want to be sure to copy *everything*,
700  * make sure to call gsf_input_seek (input, 0, G_SEEK_SET) and
701  * gsf_output_seek (output, 0, G_SEEK_SET) first, if applicable.
702  *
703  * Returns: %TRUE on success
704  **/
705 gboolean
gsf_input_copy(GsfInput * input,GsfOutput * output)706 gsf_input_copy (GsfInput *input, GsfOutput *output)
707 {
708 	gboolean success = TRUE;
709 	gsf_off_t remaining;
710 
711 	g_return_val_if_fail (input != NULL, FALSE);
712 	g_return_val_if_fail (output != NULL, FALSE);
713 
714 	while (success && (remaining = gsf_input_remaining (input)) > 0) {
715 		gsf_off_t toread = MIN (remaining, GSF_READ_BUFSIZE);
716 		const guint8 * buffer = gsf_input_read (input, toread, NULL);
717 		if (buffer)
718 			success = gsf_output_write (output, toread, buffer);
719 		else
720 			success = FALSE;
721 	}
722 
723 	return success;
724 }
725 
726 /****************************************************************************/
727 
728 /**
729  * gsf_input_uncompress:
730  * @src: (transfer full): stream to be uncompressed.
731  *
732  * This functions takes ownership of the incoming reference and yields a
733  * new one as its output.
734  *
735  * Returns: (transfer full): A stream equivalent to the source stream,
736  * but uncompressed if the source was compressed.
737  **/
738 GsfInput *
gsf_input_uncompress(GsfInput * src)739 gsf_input_uncompress (GsfInput *src)
740 {
741 	gsf_off_t cur_offset = src->cur_offset;
742 	guint8 header[4];
743 
744 	if (gsf_input_seek (src, 0, G_SEEK_SET))
745 		goto error;
746 
747 	/* Read header up front, so we avoid extra seeks in tests.  */
748 	if (!gsf_input_read (src, 4, header))
749 		goto error;
750 
751 	/* Let's try gzip.  */
752 	{
753 		const unsigned char gzip_sig[2] = { 0x1f, 0x8b };
754 
755 		if (memcmp (gzip_sig, header, sizeof (gzip_sig)) == 0) {
756 			GsfInput *res = gsf_input_gzip_new (src, NULL);
757 			if (res) {
758 				g_object_unref (src);
759 				return gsf_input_uncompress (res);
760 			}
761 		}
762 	}
763 
764 	/* Let's try bzip.  */
765 	{
766 		guint8 const *bzip_sig = "BZh";
767 
768 		if (memcmp (bzip_sig, header, strlen (bzip_sig)) == 0) {
769 			GsfInput *res = gsf_input_memory_new_from_bzip (src, NULL);
770 			if (res) {
771 				g_object_unref (src);
772 				return gsf_input_uncompress (res);
773 			}
774 		}
775 	}
776 
777 	/* Other methods go here.  */
778 
779  error:
780 	(void)gsf_input_seek (src, cur_offset, G_SEEK_SET);
781 	return src;
782 }
783