1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * gsf-output.c: interface for storing 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-output-impl.h>
24 #include <gsf/gsf-impl-utils.h>
27 static gsf_off_t gsf_output_real_vprintf (GsfOutput *output,
28 char const* format, va_list args) G_GNUC_PRINTF (2, 0);
30 #define GET_CLASS(instance) G_TYPE_INSTANCE_GET_CLASS (instance, GSF_OUTPUT_TYPE, GsfOutputClass)
32 static GObjectClass *parent_class;
43 gsf_output_set_property (GObject *object,
45 G_GNUC_UNUSED GValue const *value,
48 switch (property_id) {
50 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
56 gsf_output_get_property (GObject *object,
61 /* gsf_off_t is typedef'd to gint64 */
62 switch (property_id) {
64 g_value_set_string (value, gsf_output_name (GSF_OUTPUT (object)));
67 g_value_set_int64 (value, gsf_output_size (GSF_OUTPUT (object)));
70 g_value_set_int64 (value, gsf_output_tell (GSF_OUTPUT (object)));
73 g_value_set_boolean (value, gsf_output_is_closed (GSF_OUTPUT (object)));
76 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
82 gsf_output_dispose (GObject *obj)
84 GsfOutput *output = GSF_OUTPUT (obj);
86 if (!output->is_closed) {
87 /* g_warning ("Disposing of an unclosed stream"); */
88 gsf_output_close (output);
91 g_free (output->name);
93 g_free (output->printf_buf);
94 output->printf_buf = NULL;
96 g_clear_error (&output->err);
98 if (output->container != NULL) {
99 g_object_unref (G_OBJECT (output->container));
100 output->container = NULL;
103 G_OBJECT_CLASS (parent_class)->dispose (obj);
107 gsf_output_init (GObject *obj)
109 GsfOutput *output = GSF_OUTPUT (obj);
111 output->cur_offset = 0;
112 output->cur_size = 0;
114 output->wrapped_by = NULL;
115 output->container = NULL;
117 output->is_closed = FALSE;
118 output->printf_buf = NULL;
119 output->printf_buf_size = 0;
123 gsf_output_class_init (GObjectClass *gobject_class)
125 GsfOutputClass *output_class = GSF_OUTPUT_CLASS (gobject_class);
127 gobject_class->dispose = gsf_output_dispose;
128 gobject_class->set_property = gsf_output_set_property;
129 gobject_class->get_property = gsf_output_get_property;
130 output_class->Vprintf = gsf_output_real_vprintf;
132 parent_class = g_type_class_peek_parent (gobject_class);
134 g_object_class_install_property (gobject_class,
136 g_param_spec_string ("name", "Name",
141 g_object_class_install_property (gobject_class,
143 g_param_spec_int64 ("size", "Size",
148 g_object_class_install_property (gobject_class,
150 g_param_spec_int64 ("position", "Position",
151 "The Output's Current Position",
155 g_object_class_install_property (gobject_class,
157 g_param_spec_boolean ("is-closed", "Is Closed",
158 "Whether the Output is Closed",
164 GSF_CLASS_ABSTRACT (GsfOutput, gsf_output,
165 gsf_output_class_init, gsf_output_init,
170 * @output: #GsfOutput
172 * Give the name of @output.
174 * Returns: @output's name in utf8 form, DO NOT FREE THIS STRING
177 gsf_output_name (GsfOutput const *output)
179 g_return_val_if_fail (GSF_IS_OUTPUT (output), NULL);
184 * gsf_output_container :
187 * Returns: but does not add a reference to @output's container.
191 gsf_output_container (GsfOutput const *output)
193 g_return_val_if_fail (GSF_IS_OUTPUT (output), NULL);
194 return output->container;
199 * @output: #GsfOutput
201 * Determine the size of the output stream @output.
203 * Returns: the size of the output, or -1 if it does not have a size.
206 gsf_output_size (GsfOutput *output)
208 g_return_val_if_fail (GSF_IS_OUTPUT (output), -1);
209 return output->cur_size;
214 * @output: #GsfOutput
218 * Returns: %FALSE on error
221 gsf_output_close (GsfOutput *output)
225 g_return_val_if_fail (GSF_IS_OUTPUT (output),
226 gsf_output_set_error (output, 0, "<internal>"));
227 g_return_val_if_fail (!output->is_closed,
228 gsf_output_set_error (output, 0, "<internal>"));
230 /* The implementation will log any errors, but we can never try to
231 * close multiple times even on failure.
233 res = GET_CLASS (output)->Close (output);
234 output->is_closed = TRUE;
239 * gsf_output_is_closed :
240 * @output: #GsfOutput
242 * Returns: %TRUE if @output has already been closed.
245 gsf_output_is_closed (GsfOutput const *output)
247 g_return_val_if_fail (GSF_IS_OUTPUT (output), TRUE);
248 return output->is_closed;
253 * @output : #GsfOutput
255 * Tell the current position in @output, similar to
256 * <citerefentry><refentrytitle>ftell</refentrytitle>
257 * <manvolnum>3</manvolnum></citerefentry>.
259 * Returns: the current position in the file
262 gsf_output_tell (GsfOutput *output)
264 g_return_val_if_fail (output != NULL, 0);
266 return output->cur_offset;
271 * @output : #GsfOutput
272 * @offset : Relative amount to reposition
273 * @whence : What the offset is relative to.
275 * Reposition in output stream @output. @whence specifies what the offset is
276 * relative to: the beginning of the stream (%G_SEEK_SET), current position in
277 * the stream (%G_SEEK_CUR) or the end of the stream (%G_SEEK_END).
278 * This function is similar to
279 * <citerefentry><refentrytitle>fseek</refentrytitle>
280 * <manvolnum>3</manvolnum></citerefentry>.
282 * Returns: %FALSE on error.
285 gsf_output_seek (GsfOutput *output, gsf_off_t offset, GSeekType whence)
287 gsf_off_t pos = offset;
289 g_return_val_if_fail (output != NULL, FALSE);
292 case G_SEEK_SET: break;
293 case G_SEEK_CUR: pos += output->cur_offset; break;
294 case G_SEEK_END: pos += output->cur_size; break;
296 g_warning ("Invalid seek type %d", (int)whence);
301 g_warning ("Invalid seek position %" GSF_OFF_T_FORMAT
302 ", which is before the start of the file", pos);
306 /* If we go nowhere, just return. This in particular handles null
307 * seeks for streams with no seek method.
309 if (pos == output->cur_offset)
312 if (GET_CLASS (output)->Seek (output, offset, whence)) {
313 /* NOTE : it is possible for the current pos to be beyond the
314 * end of the file. The intervening space is not filled with 0
315 * until something is written.
317 output->cur_offset = pos;
321 /* the implementation should have assigned whatever errors are necessary */
325 static inline gboolean
326 gsf_output_inc_cur_offset (GsfOutput *output, gsf_off_t num_bytes)
328 output->cur_offset += num_bytes;
329 if (output->cur_offset < num_bytes)
330 return gsf_output_set_error (output, 0, "Output size overflow.");
331 if (output->cur_size < output->cur_offset)
332 output->cur_size = output->cur_offset;
338 * @output : Output stream
339 * @num_bytes : Number of bytes to write
340 * @data : Data to write.
342 * Write @num_bytes of @data to @output.
344 * Returns: %FALSE on error.
347 gsf_output_write (GsfOutput *output,
348 size_t num_bytes, guint8 const *data)
350 g_return_val_if_fail (output != NULL, FALSE);
354 if (GET_CLASS (output)->Write (output, num_bytes, data))
355 return gsf_output_inc_cur_offset (output, num_bytes);
357 /* the implementation should have assigned whatever errors are necessary */
365 * Returns: the last error logged on the output, or %NULL.
368 gsf_output_error (GsfOutput const *output)
370 g_return_val_if_fail (GSF_IS_OUTPUT (output), NULL);
375 * gsf_output_set_name :
376 * @output: #GsfOutput
377 * @name: the new name
379 * <note>This is a utility routine that should only be used by derived
382 * Returns: %TRUE if the assignment was ok.
385 gsf_output_set_name (GsfOutput *output, char const *name)
389 g_return_val_if_fail (GSF_IS_OUTPUT (output), FALSE);
391 buf = g_strdup (name);
392 g_free (output->name);
398 * gsf_output_set_name_from_filename :
399 * @output : the output stream
400 * @filename : the (fs-sys encoded) filename
402 * <note>This is a utility routine that should only be used by derived
405 * Returns: %TRUE if the assignment was ok.
408 gsf_output_set_name_from_filename (GsfOutput *output, char const *filename)
410 g_return_val_if_fail (GSF_IS_OUTPUT (output), FALSE);
412 g_free (output->name);
413 output->name = filename
414 ? g_filename_to_utf8 (filename, -1, NULL, NULL, NULL)
420 * gsf_output_set_container :
421 * @output: #GsfOutput
422 * @container: #GsfOutfile
424 * <note>This is a utility routine that should only be used by derived
427 * Returns: %TRUE if the assignment was ok.
430 gsf_output_set_container (GsfOutput *output, GsfOutfile *container)
432 g_return_val_if_fail (GSF_IS_OUTPUT (output), FALSE);
434 if (container != NULL)
435 g_object_ref (G_OBJECT (container));
436 if (output->container != NULL)
437 g_object_unref (G_OBJECT (output->container));
438 output->container = container;
443 * gsf_output_set_error :
444 * @output: #GsfOutput
445 * @code: The error id
446 * @format: printf style format string
447 * @Varargs: arguments for @format
449 * <note>This is a utility routine that should only be used by derived
452 * Returns: Always returns %FALSE to facilitate its use.
455 gsf_output_set_error (GsfOutput *output,
460 g_return_val_if_fail (GSF_IS_OUTPUT (output), FALSE);
462 g_clear_error (&output->err);
464 if (format != NULL) {
468 va_start (args, format);
469 message = g_strdup_vprintf (format, args);
472 output->err = g_error_new_literal (gsf_output_error_id (),
482 cb_output_unwrap (GsfOutput *wrapee, G_GNUC_UNUSED GObject *wrapper)
484 wrapee->wrapped_by = NULL;
492 * Returns: %TRUE if the wrapping succeeded.
495 gsf_output_wrap (GObject *wrapper, GsfOutput *wrapee)
497 g_return_val_if_fail (wrapper != NULL, FALSE);
498 g_return_val_if_fail (wrapee != NULL, FALSE);
500 if (wrapee->wrapped_by != NULL) {
501 g_warning ("Attempt to wrap an output that is already wrapped.");
505 g_object_weak_ref (G_OBJECT (wrapper),
506 (GWeakNotify) cb_output_unwrap, wrapee);
507 wrapee->wrapped_by = wrapper;
512 * gsf_output_unwrap :
516 * Returns: %TRUE if the unwrapping succeeded.
519 gsf_output_unwrap (GObject *wrapper, GsfOutput *wrapee)
521 g_return_val_if_fail (wrapee != NULL, FALSE);
522 g_return_val_if_fail (wrapee->wrapped_by == wrapper, FALSE);
524 wrapee->wrapped_by = NULL;
525 g_object_weak_unref (G_OBJECT (wrapper),
526 (GWeakNotify) cb_output_unwrap, wrapee);
531 gsf_output_error_id (void)
535 quark = g_quark_from_static_string ("gsf_output_error");
540 * gsf_output_printf :
541 * @output : A #GsfOutput
542 * @format : The printf-style format string
543 * @Varargs : the arguments for @format
545 * Output @Varargs to @output using the format string @format, similar to
546 * <citerefentry><refentrytitle>printf</refentrytitle>
547 * <manvolnum>3</manvolnum></citerefentry>.
549 * Returns: %TRUE if successful, %FALSE if not
552 gsf_output_printf (GsfOutput *output, char const *format, ...)
557 va_start (args, format);
558 res = (gsf_output_vprintf (output, format, args) >= 0);
564 * gsf_output_vprintf :
565 * @output : A #GsfOutput
566 * @format : The printf-style format string
567 * @args : the arguments for @format
569 * Output @args to @output using the format string @format, similar to
570 * <citerefentry><refentrytitle>vprintf</refentrytitle>
571 * <manvolnum>3</manvolnum></citerefentry>.
573 * Returns: number of bytes printed, a negative value if not successful
576 gsf_output_vprintf (GsfOutput *output, char const *format, va_list args)
580 g_return_val_if_fail (output != NULL, -1);
581 g_return_val_if_fail (format != NULL, -1);
582 /* g_return_val_if_fail (strlen (format) > 0, -1); -- Why? */
584 num_bytes = GET_CLASS (output)->Vprintf (output, format, args);
587 if (!gsf_output_inc_cur_offset (output, num_bytes))
593 gsf_output_real_vprintf (GsfOutput *output, char const *fmt, va_list args)
599 * We need to make a copy as args will become unusable after
600 * the g_vsnprintf call.
602 G_VA_COPY (args2, args);
604 if (NULL == output->printf_buf) {
605 output->printf_buf_size = 128;
606 output->printf_buf = g_new (char, output->printf_buf_size);
608 reslen = g_vsnprintf (output->printf_buf, output->printf_buf_size, fmt, args);
610 /* handle C99 or older -1 case of vsnprintf */
611 if (reslen < 0 || reslen >= output->printf_buf_size) {
612 g_free (output->printf_buf);
613 output->printf_buf = g_strdup_vprintf (fmt, args2);
614 reslen = output->printf_buf_size = strlen (output->printf_buf);
619 GET_CLASS (output)->Write (output, reslen, output->printf_buf))
627 * @output: A #GsfOutput
628 * @line: %null terminated string to write
630 * Like fputs, this assumes that the line already ends with a newline
632 * Returns: %TRUE if successful, %FALSE if not
635 gsf_output_puts (GsfOutput *output, char const *line)
639 g_return_val_if_fail (line != NULL, FALSE);
641 nbytes = strlen (line);
642 return gsf_output_write (output, nbytes, line);