1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * gsf-output.c: interface for storing 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-output.h>
24 #include <gsf/gsf.h>
25 #include <glib/gi18n-lib.h>
26 
27 #include <string.h>
28 
29 /*
30  * FIXME!
31  *
32  * We cannot extend GsfInput, so for now we hang this on the object as
33  * an attribute.
34  */
35 #define MODTIME_ATTR "GsfOutput::modtime"
36 
37 
38 static gsf_off_t gsf_output_real_vprintf (GsfOutput *output,
39 	char const* format, va_list args) G_GNUC_PRINTF (2, 0);
40 
41 #define GET_CLASS(instance) G_TYPE_INSTANCE_GET_CLASS (instance, GSF_OUTPUT_TYPE, GsfOutputClass)
42 
43 static GObjectClass *parent_class;
44 
45 enum {
46 	PROP_0,
47 	PROP_NAME,
48 	PROP_SIZE,
49 	PROP_CLOSED,
50 	PROP_POS,
51 	PROP_MODTIME,
52 	PROP_CONTAINER
53 };
54 
55 static void
gsf_output_set_property(GObject * object,guint property_id,GValue const * value,GParamSpec * pspec)56 gsf_output_set_property (GObject      *object,
57 			 guint         property_id,
58 			 GValue const *value,
59 			 GParamSpec   *pspec)
60 {
61 	GsfOutput *output = GSF_OUTPUT (object);
62 
63 	switch (property_id) {
64 	case PROP_NAME:
65 		gsf_output_set_name (output, g_value_get_string (value));
66 		break;
67 	case PROP_MODTIME:
68 		gsf_output_set_modtime (output, g_value_get_boxed (value));
69 		break;
70 	case PROP_CONTAINER:
71 		gsf_output_set_container (output, g_value_get_object (value));
72 		break;
73 	default:
74 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
75 		break;
76 	}
77 }
78 
79 static void
gsf_output_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)80 gsf_output_get_property (GObject     *object,
81 			 guint        property_id,
82 			 GValue      *value,
83 			 GParamSpec  *pspec)
84 {
85 	GsfOutput *output = GSF_OUTPUT (object);
86 
87 	/* gsf_off_t is typedef'd to gint64 */
88 
89 	switch (property_id) {
90 	case PROP_NAME:
91 		g_value_set_string (value, gsf_output_name (output));
92 		break;
93 	case PROP_SIZE:
94 		g_value_set_int64 (value, gsf_output_size (output));
95 		break;
96 	case PROP_CLOSED:
97 		g_value_set_boolean (value, gsf_output_is_closed (output));
98 		break;
99 	case PROP_POS:
100 		g_value_set_int64 (value, gsf_output_tell (output));
101 		break;
102 	case PROP_MODTIME:
103 		g_value_set_boxed (value, gsf_output_get_modtime (output));
104 		break;
105 	case PROP_CONTAINER:
106 		g_value_set_object (value, output->container);
107 		break;
108 	default:
109 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
110 		break;
111 	}
112 }
113 
114 static void
gsf_output_dispose(GObject * obj)115 gsf_output_dispose (GObject *obj)
116 {
117 	GsfOutput *output = GSF_OUTPUT (obj);
118 
119 	if (!output->is_closed) {
120 		/* g_warning ("Disposing of an unclosed stream"); */
121 		gsf_output_close (output);
122 	}
123 
124 	gsf_output_set_container (output, NULL);
125 	gsf_output_set_name (output, NULL);
126 	gsf_output_set_modtime (output, NULL);
127 
128 	g_free (output->printf_buf);
129 	output->printf_buf = NULL;
130 
131 	g_clear_error (&output->err);
132 
133 	parent_class->dispose (obj);
134 }
135 
136 static void
gsf_output_init(GObject * obj)137 gsf_output_init (GObject *obj)
138 {
139 	GsfOutput *output = GSF_OUTPUT (obj);
140 
141 	output->cur_offset	= 0;
142 	output->cur_size	= 0;
143 	output->name		= NULL;
144 	output->wrapped_by	= NULL;
145 	output->container	= NULL;
146 	output->err		= NULL;
147 	output->is_closed	= FALSE;
148 	output->printf_buf	= NULL;
149 	output->printf_buf_size = 0;
150 }
151 
152 static void
gsf_output_class_init(GObjectClass * gobject_class)153 gsf_output_class_init (GObjectClass *gobject_class)
154 {
155 	GsfOutputClass  *output_class  = GSF_OUTPUT_CLASS (gobject_class);
156 
157 	gobject_class->dispose      = gsf_output_dispose;
158 	gobject_class->set_property = gsf_output_set_property;
159 	gobject_class->get_property = gsf_output_get_property;
160 	output_class->Vprintf       = gsf_output_real_vprintf;
161 
162 	parent_class = g_type_class_peek_parent (gobject_class);
163 
164 	g_object_class_install_property
165 		(gobject_class,
166 		 PROP_NAME,
167 		 g_param_spec_string ("name",
168 				      _("Name"),
169 				      _("The output's name"),
170 				      NULL,
171 				      G_PARAM_STATIC_STRINGS |
172 				      G_PARAM_READWRITE));
173 
174 	/**
175 	 * GsfOutput:size:
176 	 *
177 	 * The current file size.
178 	 */
179 	g_object_class_install_property
180 		(gobject_class,
181 		 PROP_SIZE,
182 		 g_param_spec_int64 ("size",
183 				     _("Size"),
184 				     _("The output's size"),
185 				     0, G_MAXINT64, 0,
186 				     G_PARAM_STATIC_STRINGS |
187 				     G_PARAM_READABLE));
188 
189 	/**
190 	 * GsfOutput:is-closed:
191 	 *
192 	 * %TRUE if the output has been closed.
193 	 */
194 	g_object_class_install_property
195 		(gobject_class,
196 		 PROP_CLOSED,
197 		 g_param_spec_boolean ("is-closed",
198 				       _("Is Closed"),
199 				       _("Whether the output is closed"),
200 				       FALSE,
201 				       G_PARAM_STATIC_STRINGS |
202 				       G_PARAM_READABLE));
203 
204 	/**
205 	 * GsfOutput:position:
206 	 *
207 	 * The current position in the output.
208 	 */
209 	g_object_class_install_property
210 		(gobject_class,
211 		 PROP_POS,
212 		 g_param_spec_int64 ("position",
213 				     _("Position"),
214 				     _("The output's current position"),
215 				     0, G_MAXINT64, 0,
216 				     G_PARAM_STATIC_STRINGS |
217 				     G_PARAM_READABLE));
218 
219 	/**
220 	 * GsfOutput:modtime:
221 	 *
222 	 * The time the output was last updated.  This must be set on object
223 	 * construction and represents the timestamp to put on the resulting
224 	 * file or #GsfOutfile member.  Not all derived classes will actually
225 	 * do anything with this property.
226 	 */
227 	g_object_class_install_property
228 		(gobject_class,
229 		 PROP_MODTIME,
230 		 g_param_spec_boxed
231 		 ("modtime",
232 		  _("Modification time"),
233 		  _("An optional GDateTime representing the time the output was last changed"),
234 		  G_TYPE_DATE_TIME,
235 		  G_PARAM_STATIC_STRINGS |
236 		  G_PARAM_CONSTRUCT_ONLY |
237 		  G_PARAM_READWRITE));
238 
239 	/**
240 	 * GsfOutput:container:
241 	 *
242 	 * The container, optionally %NULL, in which this output lives.
243 	 */
244 	g_object_class_install_property
245 		(gobject_class, PROP_CONTAINER,
246 		 g_param_spec_object ("container",
247 				      _("Container"),
248 				      _("The parent GsfOutfile"),
249 				      GSF_OUTFILE_TYPE,
250 				      G_PARAM_STATIC_STRINGS |
251 				      G_PARAM_READWRITE));
252 }
253 
GSF_CLASS_ABSTRACT(GsfOutput,gsf_output,gsf_output_class_init,gsf_output_init,G_TYPE_OBJECT)254 GSF_CLASS_ABSTRACT (GsfOutput, gsf_output,
255 		    gsf_output_class_init, gsf_output_init,
256 		    G_TYPE_OBJECT)
257 
258 /**
259  * gsf_output_name:
260  * @output: #GsfOutput
261  *
262  * Give the name of @output.
263  *
264  * Returns: (transfer none) (nullable): @output's name in utf8 form.
265  **/
266 char const *
267 gsf_output_name (GsfOutput const *output)
268 {
269 	g_return_val_if_fail (GSF_IS_OUTPUT (output), NULL);
270 	return output->name;
271 }
272 
273 /**
274  * gsf_output_container:
275  * @output:
276  *
277  * Returns: (transfer none) (nullable): @output's container
278  **/
279 GsfOutfile *
gsf_output_container(GsfOutput const * output)280 gsf_output_container (GsfOutput const *output)
281 {
282 	g_return_val_if_fail (GSF_IS_OUTPUT (output), NULL);
283 	return output->container;
284 }
285 
286 /**
287  * gsf_output_size:
288  * @output: #GsfOutput
289  *
290  * Determine the size of the output stream @output.
291  *
292  * Returns: the size of the output, or -1 if it does not have a size.
293  **/
294 gsf_off_t
gsf_output_size(GsfOutput * output)295 gsf_output_size (GsfOutput *output)
296 {
297 	g_return_val_if_fail (GSF_IS_OUTPUT (output), -1);
298 	return output->cur_size;
299 }
300 
301 /**
302  * gsf_output_close: (virtual Close)
303  * @output: #GsfOutput
304  *
305  * Close a stream.
306  *
307  * Returns: %FALSE on error
308  **/
309 gboolean
gsf_output_close(GsfOutput * output)310 gsf_output_close (GsfOutput *output)
311 {
312 	gboolean res;
313 
314 	g_return_val_if_fail (GSF_IS_OUTPUT (output),
315 		gsf_output_set_error (output, 0, "<internal>"));
316 	g_return_val_if_fail (!output->is_closed,
317 		gsf_output_set_error (output, 0, "<internal>"));
318 
319 	/* The implementation will log any errors, but we can never try to
320 	 * close multiple times even on failure.
321 	 */
322 	res = GET_CLASS (output)->Close (output);
323 	output->is_closed = TRUE;
324 	return res;
325 }
326 
327 /**
328  * gsf_output_is_closed:
329  * @output: #GsfOutput
330  *
331  * Returns: %TRUE if @output has already been closed.
332  **/
333 gboolean
gsf_output_is_closed(GsfOutput const * output)334 gsf_output_is_closed (GsfOutput const *output)
335 {
336 	g_return_val_if_fail (GSF_IS_OUTPUT (output), TRUE);
337 	return output->is_closed;
338 }
339 
340 /**
341  * gsf_output_tell:
342  * @output: #GsfOutput
343  *
344  * Tell the current position in @output, similar to
345  * <citerefentry><refentrytitle>ftell</refentrytitle>
346  * <manvolnum>3</manvolnum></citerefentry>.
347  *
348  * Returns: the current position in the file
349  **/
350 gsf_off_t
gsf_output_tell(GsfOutput * output)351 gsf_output_tell	(GsfOutput *output)
352 {
353 	g_return_val_if_fail (output != NULL, 0);
354 
355 	return output->cur_offset;
356 }
357 
358 /**
359  * gsf_output_seek: (virtual Seek)
360  * @output: #GsfOutput
361  * @offset: Relative amount to reposition
362  * @whence: What the offset is relative to.
363  *
364  * Reposition in output stream @output. @whence specifies what the offset is
365  * relative to: the beginning of the stream (%G_SEEK_SET), current position in
366  * the stream (%G_SEEK_CUR) or the end of the stream (%G_SEEK_END).
367  * This function is similar to
368  * <citerefentry><refentrytitle>fseek</refentrytitle>
369  * <manvolnum>3</manvolnum></citerefentry>.
370  *
371  * Returns: %FALSE on error.
372  **/
373 gboolean
gsf_output_seek(GsfOutput * output,gsf_off_t offset,GSeekType whence)374 gsf_output_seek	(GsfOutput *output, gsf_off_t offset, GSeekType whence)
375 {
376 	gsf_off_t pos = offset;
377 
378 	g_return_val_if_fail (output != NULL, FALSE);
379 
380 	switch (whence) {
381 	case G_SEEK_SET: break;
382 	case G_SEEK_CUR: pos += output->cur_offset;	break;
383 	case G_SEEK_END: pos += output->cur_size;	break;
384 	default :
385 		g_warning ("Invalid seek type %d", (int)whence);
386 		return FALSE;
387 	}
388 
389 	if (pos < 0) {
390 		g_warning ("Invalid seek position %" GSF_OFF_T_FORMAT
391 			   ", which is before the start of the file", pos);
392 		return FALSE;
393 	}
394 
395 	/* If we go nowhere, just return.  This in particular handles null
396 	 * seeks for streams with no seek method.
397 	 */
398 	if (pos == output->cur_offset)
399 		return TRUE;
400 
401 	if (GET_CLASS (output)->Seek (output, offset, whence)) {
402 		/* NOTE : it is possible for the current pos to be beyond the
403 		 * end of the file.  The intervening space is not filled with 0
404 		 * until something is written.
405 		 */
406 		output->cur_offset = pos;
407 		return TRUE;
408 	}
409 
410 	/* the implementation should have assigned whatever errors are necessary */
411 	return FALSE;
412 }
413 
414 static inline gboolean
gsf_output_inc_cur_offset(GsfOutput * output,gsf_off_t num_bytes)415 gsf_output_inc_cur_offset (GsfOutput *output, gsf_off_t num_bytes)
416 {
417 	output->cur_offset += num_bytes;
418 	if (output->cur_offset < num_bytes)
419 		return gsf_output_set_error (output, 0, "Output size overflow.");
420 	if (output->cur_size < output->cur_offset)
421 		output->cur_size = output->cur_offset;
422 	return TRUE;
423 }
424 
425 /**
426  * gsf_output_write: (virtual Write)
427  * @output: Output stream
428  * @num_bytes: Number of bytes to write
429  * @data: (in) (array length=num_bytes): Data to write.
430  *
431  * Write @num_bytes of @data to @output.
432  *
433  * Returns: %FALSE on error.
434  **/
435 gboolean
gsf_output_write(GsfOutput * output,size_t num_bytes,guint8 const * data)436 gsf_output_write (GsfOutput *output,
437 		  size_t num_bytes, guint8 const *data)
438 {
439 	g_return_val_if_fail (output != NULL, FALSE);
440 
441 	if (num_bytes == 0)
442 		return TRUE;
443 	if (GET_CLASS (output)->Write (output, num_bytes, data))
444 		return gsf_output_inc_cur_offset (output, num_bytes);
445 
446 	/* the implementation should have assigned whatever errors are necessary */
447 	return FALSE;
448 }
449 
450 /**
451  * gsf_output_error:
452  * @output:
453  *
454  * Returns: (transfer none) (nullable): the last error logged on the output
455  **/
456 GError const *
gsf_output_error(GsfOutput const * output)457 gsf_output_error (GsfOutput const *output)
458 {
459 	g_return_val_if_fail (GSF_IS_OUTPUT (output), NULL);
460 	return output->err;
461 }
462 
463 /**
464  * gsf_output_set_name:
465  * @output: #GsfOutput
466  * @name: (nullable): the new name
467  *
468  * <note>This is a utility routine that should only be used by derived
469  * outputs.</note>
470  *
471  * Returns: %TRUE if the assignment was ok.
472  **/
473 gboolean
gsf_output_set_name(GsfOutput * output,char const * name)474 gsf_output_set_name (GsfOutput *output, char const *name)
475 {
476 	g_return_val_if_fail (GSF_IS_OUTPUT (output), FALSE);
477 
478 	if (g_strcmp0 (name, output->name)) {
479 		g_free (output->name);
480 		output->name = g_strdup (name);
481 		g_object_notify (G_OBJECT (output), "name");
482 	}
483 
484 	return TRUE;
485 }
486 
487 /**
488  * gsf_output_set_name_from_filename:
489  * @output: the output stream
490  * @filename: (nullable): the (fs-sys encoded) filename
491  *
492  * <note>This is a utility routine that should only be used by derived
493  * outputs.</note>
494  *
495  * Returns: %TRUE if the assignment was ok.
496  **/
497 gboolean
gsf_output_set_name_from_filename(GsfOutput * output,char const * filename)498 gsf_output_set_name_from_filename (GsfOutput *output, char const *filename)
499 {
500 	char *name;
501 	gboolean res;
502 
503 	g_return_val_if_fail (GSF_IS_OUTPUT (output), FALSE);
504 
505 	name = filename
506 		? g_filename_to_utf8 (filename, -1, NULL, NULL, NULL)
507 		: NULL;
508 	res = gsf_output_set_name (output, name);
509 	g_free (name);
510 	return res;
511 }
512 
513 /**
514  * gsf_output_set_container:
515  * @output: #GsfOutput
516  * @container: (nullable): #GsfOutfile
517  *
518  * <note>This is a utility routine that should only be used by derived
519  * outputs.</note>
520  *
521  * Returns: %TRUE if the assignment was ok.
522  **/
523 gboolean
gsf_output_set_container(GsfOutput * output,GsfOutfile * container)524 gsf_output_set_container (GsfOutput *output, GsfOutfile *container)
525 {
526 	g_return_val_if_fail (GSF_IS_OUTPUT (output), FALSE);
527 
528 	if (container != NULL)
529 		g_object_ref (container);
530 	if (output->container != NULL)
531 		g_object_unref (output->container);
532 	output->container = container;
533 	return TRUE;
534 }
535 
536 /**
537  * gsf_output_set_error:
538  * @output: #GsfOutput
539  * @code: The error id
540  * @format: printf style format string
541  * @...: arguments for @format
542  *
543  * <note>This is a utility routine that should only be used by derived
544  * outputs.</note>
545  *
546  * Returns: Always returns %FALSE to facilitate its use.
547  **/
548 gboolean
gsf_output_set_error(GsfOutput * output,gint code,char const * format,...)549 gsf_output_set_error (GsfOutput  *output,
550 		      gint        code,
551 		      char const *format,
552 		      ...)
553 {
554 	g_return_val_if_fail (GSF_IS_OUTPUT (output), FALSE);
555 
556 	g_clear_error (&output->err);
557 
558 	if (format != NULL) {
559 		char *message;
560 		va_list args;
561 
562 		va_start (args, format);
563 		message = g_strdup_vprintf (format, args);
564 		va_end (args);
565 
566 		output->err = g_error_new_literal (gsf_output_error_id (),
567 		                                   code,
568 		                                   message);
569 		g_free (message);
570 	}
571 
572 	return FALSE;
573 }
574 
575 static void
cb_output_unwrap(GsfOutput * wrapee,G_GNUC_UNUSED GObject * wrapper)576 cb_output_unwrap (GsfOutput *wrapee, G_GNUC_UNUSED GObject *wrapper)
577 {
578 	wrapee->wrapped_by = NULL;
579 }
580 
581 /**
582  * gsf_output_wrap:
583  * @wrapper:
584  * @wrapee:
585  *
586  * Returns: %TRUE if the wrapping succeeded.
587  **/
588 gboolean
gsf_output_wrap(GObject * wrapper,GsfOutput * wrapee)589 gsf_output_wrap (GObject *wrapper, GsfOutput *wrapee)
590 {
591 	g_return_val_if_fail (wrapper != NULL, FALSE);
592 	g_return_val_if_fail (wrapee != NULL, FALSE);
593 
594 	if (wrapee->wrapped_by != NULL) {
595 		g_warning ("Attempt to wrap an output that is already wrapped.");
596 		return FALSE;
597 	}
598 
599 	g_object_weak_ref (wrapper,
600 			   (GWeakNotify) cb_output_unwrap, wrapee);
601 	wrapee->wrapped_by = wrapper;
602 	return TRUE;
603 }
604 
605 /**
606  * gsf_output_unwrap:
607  * @wrapper:
608  * @wrapee:
609  *
610  * Returns: %TRUE if the unwrapping succeeded.
611  **/
612 gboolean
gsf_output_unwrap(GObject * wrapper,GsfOutput * wrapee)613 gsf_output_unwrap (GObject *wrapper, GsfOutput *wrapee)
614 {
615 	g_return_val_if_fail (wrapee != NULL, FALSE);
616 	g_return_val_if_fail (wrapee->wrapped_by == wrapper, FALSE);
617 
618 	wrapee->wrapped_by = NULL;
619 	g_object_weak_unref (wrapper,
620 			     (GWeakNotify) cb_output_unwrap, wrapee);
621 	return TRUE;
622 }
623 
624 /**
625  * gsf_output_get_modtime:
626  * @output: the output stream
627  *
628  * Returns: (transfer none) (nullable): A #GDateTime representing when
629  * the output was last modified
630  */
631 GDateTime *
gsf_output_get_modtime(GsfOutput * output)632 gsf_output_get_modtime (GsfOutput *output)
633 {
634 	g_return_val_if_fail (GSF_IS_OUTPUT (output), NULL);
635 
636 	return g_object_get_data (G_OBJECT (output), MODTIME_ATTR);
637 }
638 
639 /**
640  * gsf_output_set_modtime:
641  * @output: the output stream
642  * @modtime: (transfer none) (allow-none): the new modification time.
643  *
644  * Returns: %TRUE if the assignment was ok.
645  */
646 gboolean
gsf_output_set_modtime(GsfOutput * output,GDateTime * modtime)647 gsf_output_set_modtime (GsfOutput *output, GDateTime *modtime)
648 {
649 	g_return_val_if_fail (GSF_IS_OUTPUT (output), FALSE);
650 
651 	if (modtime)
652 		modtime = g_date_time_add (modtime, 0); /* Copy */
653 
654 	/* This actually also works for null modtime.  */
655 	g_object_set_data_full (G_OBJECT (output),
656 				MODTIME_ATTR, modtime,
657 				(GDestroyNotify)g_date_time_unref);
658 
659 	return TRUE;
660 }
661 
662 
663 GQuark
gsf_output_error_id(void)664 gsf_output_error_id (void)
665 {
666 	static GQuark quark;
667 	if (!quark)
668 		quark = g_quark_from_static_string ("gsf_output_error");
669 	return quark;
670 }
671 
672 /**
673  * gsf_output_printf:
674  * @output: A #GsfOutput
675  * @format: The printf-style format string
676  * @...: the arguments for @format
677  *
678  * Output @Varargs to @output using the format string @format, similar to
679  * <citerefentry><refentrytitle>printf</refentrytitle>
680  * <manvolnum>3</manvolnum></citerefentry>.
681  *
682  * Returns: %TRUE if successful, %FALSE if not
683  **/
684 gboolean
gsf_output_printf(GsfOutput * output,char const * format,...)685 gsf_output_printf (GsfOutput *output, char const *format, ...)
686 {
687 	va_list args;
688 	gboolean res;
689 
690 	va_start (args, format);
691 	res = (gsf_output_vprintf (output, format, args) >= 0);
692 	va_end (args);
693 	return res;
694 }
695 
696 /**
697  * gsf_output_vprintf: (virtual Vprintf)
698  * @output: A #GsfOutput
699  * @format: The printf-style format string
700  * @args: the arguments for @format
701  *
702  * Output @args to @output using the format string @format, similar to
703  * <citerefentry><refentrytitle>vprintf</refentrytitle>
704  * <manvolnum>3</manvolnum></citerefentry>.
705  *
706  * Returns: number of bytes printed, a negative value if not successful
707  **/
708 gsf_off_t
gsf_output_vprintf(GsfOutput * output,char const * format,va_list args)709 gsf_output_vprintf (GsfOutput *output, char const *format, va_list args)
710 {
711 	gsf_off_t num_bytes;
712 
713 	g_return_val_if_fail (output != NULL, -1);
714 	g_return_val_if_fail (format != NULL, -1);
715 	/* g_return_val_if_fail (strlen (format) > 0, -1); -- Why? */
716 
717 	num_bytes = GET_CLASS (output)->Vprintf (output, format, args);
718 
719 	if (num_bytes >= 0)
720 		if (!gsf_output_inc_cur_offset (output, num_bytes))
721 			return -1;
722 	return num_bytes;
723 }
724 
725 static gsf_off_t
gsf_output_real_vprintf(GsfOutput * output,char const * fmt,va_list args)726 gsf_output_real_vprintf (GsfOutput *output, char const *fmt, va_list args)
727 {
728 	gsf_off_t reslen;
729 	va_list args2;
730 
731 	/*
732 	 * We need to make a copy as args will become unusable after
733 	 * the g_vsnprintf call.
734 	 */
735 	G_VA_COPY (args2, args);
736 
737 	if (NULL == output->printf_buf) {
738 		output->printf_buf_size = 128;
739 		output->printf_buf = g_new (char, output->printf_buf_size);
740 	}
741 	reslen = g_vsnprintf (output->printf_buf, output->printf_buf_size, fmt, args);
742 
743 	/* handle C99 or older -1 case of vsnprintf */
744 	if (reslen < 0 || reslen >= output->printf_buf_size) {
745 		g_free (output->printf_buf);
746 		output->printf_buf = g_strdup_vprintf (fmt, args2);
747 		reslen = output->printf_buf_size = strlen (output->printf_buf);
748 	}
749 	va_end (args2);
750 
751 	if (reslen == 0 ||
752 	    GET_CLASS (output)->Write (output, reslen, output->printf_buf))
753 		return reslen;
754 
755 	return -1;
756 }
757 
758 /**
759  * gsf_output_puts:
760  * @output: A #GsfOutput
761  * @line: Nul terminated string to write
762  *
763  * Like fputs, this assumes that the line already ends with a newline
764  *
765  * Returns: %TRUE if successful, %FALSE if not
766  **/
767 gboolean
gsf_output_puts(GsfOutput * output,char const * line)768 gsf_output_puts (GsfOutput *output, char const *line)
769 {
770 	size_t nbytes = 0;
771 
772 	g_return_val_if_fail (line != NULL, FALSE);
773 
774 	nbytes = strlen (line);
775 	return gsf_output_write (output, nbytes, line);
776 }
777