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