"Initial commit to Gerrit"
[profile/ivi/libgsf.git] / gsf / gsf-output.c
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-impl.h>
24 #include <gsf/gsf-impl-utils.h>
25 #include <string.h>
26
27 static gsf_off_t gsf_output_real_vprintf (GsfOutput *output,
28         char const* format, va_list args) G_GNUC_PRINTF (2, 0);
29
30 #define GET_CLASS(instance) G_TYPE_INSTANCE_GET_CLASS (instance, GSF_OUTPUT_TYPE, GsfOutputClass)
31
32 static GObjectClass *parent_class;
33
34 enum {
35         PROP_0,
36         PROP_NAME,
37         PROP_SIZE,
38         PROP_CLOSED,
39         PROP_POS
40 };
41
42 static void
43 gsf_output_set_property (GObject      *object,
44                          guint         property_id,
45          G_GNUC_UNUSED   GValue const *value,
46                          GParamSpec   *pspec)
47 {
48         switch (property_id) {
49         default:
50                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
51                 break;
52         }
53 }
54
55 static void
56 gsf_output_get_property (GObject     *object,
57                          guint        property_id,
58                          GValue      *value,
59                          GParamSpec  *pspec)
60 {
61         /* gsf_off_t is typedef'd to gint64 */
62         switch (property_id) {
63         case PROP_NAME:
64                 g_value_set_string (value, gsf_output_name (GSF_OUTPUT (object)));
65                 break;
66         case PROP_SIZE:
67                 g_value_set_int64 (value, gsf_output_size (GSF_OUTPUT (object)));
68                 break;
69         case PROP_POS:
70                 g_value_set_int64 (value, gsf_output_tell (GSF_OUTPUT (object)));
71                 break;
72         case PROP_CLOSED:
73                 g_value_set_boolean (value, gsf_output_is_closed (GSF_OUTPUT (object)));
74                 break;
75         default:
76                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
77                 break;
78         }
79 }
80
81 static void
82 gsf_output_dispose (GObject *obj)
83 {
84         GsfOutput *output = GSF_OUTPUT (obj);
85
86         if (!output->is_closed) {
87                 /* g_warning ("Disposing of an unclosed stream"); */
88                 gsf_output_close (output);
89         }
90
91         g_free (output->name);
92         output->name = NULL;
93         g_free (output->printf_buf);
94         output->printf_buf = NULL;
95
96         g_clear_error (&output->err);
97
98         if (output->container != NULL) {
99                 g_object_unref (G_OBJECT (output->container));
100                 output->container = NULL;
101         }
102
103         G_OBJECT_CLASS (parent_class)->dispose (obj);
104 }
105
106 static void
107 gsf_output_init (GObject *obj)
108 {
109         GsfOutput *output = GSF_OUTPUT (obj);
110
111         output->cur_offset      = 0;
112         output->cur_size        = 0;
113         output->name            = NULL;
114         output->wrapped_by      = NULL;
115         output->container       = NULL;
116         output->err             = NULL;
117         output->is_closed       = FALSE;
118         output->printf_buf      = NULL;
119         output->printf_buf_size = 0;
120 }
121
122 static void
123 gsf_output_class_init (GObjectClass *gobject_class)
124 {
125         GsfOutputClass  *output_class  = GSF_OUTPUT_CLASS (gobject_class);
126
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;
131
132         parent_class = g_type_class_peek_parent (gobject_class);
133
134         g_object_class_install_property (gobject_class,
135                                          PROP_NAME,
136                                          g_param_spec_string ("name", "Name",
137                                                               "The Output's Name",
138                                                               NULL,
139                                                               GSF_PARAM_STATIC |
140                                                               G_PARAM_READABLE));
141         g_object_class_install_property (gobject_class,
142                                          PROP_SIZE,
143                                          g_param_spec_int64 ("size", "Size",
144                                                              "The Output's Size",
145                                                              0, G_MAXINT64, 0,
146                                                              GSF_PARAM_STATIC |
147                                                              G_PARAM_READABLE));
148         g_object_class_install_property (gobject_class,
149                                          PROP_POS,
150                                          g_param_spec_int64 ("position", "Position",
151                                                              "The Output's Current Position",
152                                                              0, G_MAXINT64, 0,
153                                                              GSF_PARAM_STATIC |
154                                                              G_PARAM_READABLE));
155         g_object_class_install_property (gobject_class,
156                                          PROP_CLOSED,
157                                          g_param_spec_boolean ("is-closed", "Is Closed",
158                                                                "Whether the Output is Closed",
159                                                                FALSE,
160                                                                GSF_PARAM_STATIC |
161                                                                G_PARAM_READABLE));
162 }
163
164 GSF_CLASS_ABSTRACT (GsfOutput, gsf_output,
165                     gsf_output_class_init, gsf_output_init,
166                     G_TYPE_OBJECT)
167
168 /**
169  * gsf_output_name :
170  * @output: #GsfOutput
171  *
172  * Give the name of @output.
173  *
174  * Returns: @output's name in utf8 form, DO NOT FREE THIS STRING
175  **/
176 char const *
177 gsf_output_name (GsfOutput const *output)
178 {
179         g_return_val_if_fail (GSF_IS_OUTPUT (output), NULL);
180         return output->name;
181 }
182
183 /**
184  * gsf_output_container :
185  * @output :
186  *
187  * Returns: but does not add a reference to @output's container.
188  *      Potentially %NULL
189  **/
190 GsfOutfile *
191 gsf_output_container (GsfOutput const *output)
192 {
193         g_return_val_if_fail (GSF_IS_OUTPUT (output), NULL);
194         return output->container;
195 }
196
197 /**
198  * gsf_output_size :
199  * @output: #GsfOutput
200  *
201  * Determine the size of the output stream @output.
202  *
203  * Returns: the size of the output, or -1 if it does not have a size.
204  **/
205 gsf_off_t
206 gsf_output_size (GsfOutput *output)
207 {
208         g_return_val_if_fail (GSF_IS_OUTPUT (output), -1);
209         return output->cur_size;
210 }
211
212 /**
213  * gsf_output_close :
214  * @output: #GsfOutput
215  *
216  * Close a stream.
217  *
218  * Returns: %FALSE on error
219  **/
220 gboolean
221 gsf_output_close (GsfOutput *output)
222 {
223         gboolean res;
224
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>"));
229
230         /* The implementation will log any errors, but we can never try to
231          * close multiple times even on failure.
232          */
233         res = GET_CLASS (output)->Close (output);
234         output->is_closed = TRUE;
235         return res;
236 }
237
238 /**
239  * gsf_output_is_closed :
240  * @output: #GsfOutput
241  *
242  * Returns: %TRUE if @output has already been closed.
243  **/
244 gboolean
245 gsf_output_is_closed (GsfOutput const *output)
246 {
247         g_return_val_if_fail (GSF_IS_OUTPUT (output), TRUE);
248         return output->is_closed;
249 }
250
251 /**
252  * gsf_output_tell :
253  * @output : #GsfOutput
254  *
255  * Tell the current position in @output, similar to
256  * <citerefentry><refentrytitle>ftell</refentrytitle>
257  * <manvolnum>3</manvolnum></citerefentry>.
258  *
259  * Returns: the current position in the file
260  **/
261 gsf_off_t
262 gsf_output_tell (GsfOutput *output)
263 {
264         g_return_val_if_fail (output != NULL, 0);
265
266         return output->cur_offset;
267 }
268
269 /**
270  * gsf_output_seek :
271  * @output : #GsfOutput
272  * @offset : Relative amount to reposition
273  * @whence : What the offset is relative to.
274  *
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>.
281  *
282  * Returns: %FALSE on error.
283  **/
284 gboolean
285 gsf_output_seek (GsfOutput *output, gsf_off_t offset, GSeekType whence)
286 {
287         gsf_off_t pos = offset;
288
289         g_return_val_if_fail (output != NULL, FALSE);
290
291         switch (whence) {
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;
295         default :
296                 g_warning ("Invalid seek type %d", (int)whence);
297                 return FALSE;
298         }
299
300         if (pos < 0) {
301                 g_warning ("Invalid seek position %" GSF_OFF_T_FORMAT
302                            ", which is before the start of the file", pos);
303                 return FALSE;
304         }
305
306         /* If we go nowhere, just return.  This in particular handles null
307          * seeks for streams with no seek method.
308          */
309         if (pos == output->cur_offset)
310                 return TRUE;
311
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.
316                  */
317                 output->cur_offset = pos;
318                 return TRUE;
319         }
320
321         /* the implementation should have assigned whatever errors are necessary */
322         return FALSE;
323 }
324
325 static inline gboolean
326 gsf_output_inc_cur_offset (GsfOutput *output, gsf_off_t num_bytes)
327 {
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;
333         return TRUE;
334 }
335
336 /**
337  * gsf_output_write :
338  * @output : Output stream
339  * @num_bytes : Number of bytes to write
340  * @data : Data to write.
341  *
342  * Write @num_bytes of @data to @output.
343  *
344  * Returns: %FALSE on error.
345  **/
346 gboolean
347 gsf_output_write (GsfOutput *output,
348                   size_t num_bytes, guint8 const *data)
349 {
350         g_return_val_if_fail (output != NULL, FALSE);
351
352         if (num_bytes == 0)
353                 return TRUE;
354         if (GET_CLASS (output)->Write (output, num_bytes, data))
355                 return gsf_output_inc_cur_offset (output, num_bytes);
356
357         /* the implementation should have assigned whatever errors are necessary */
358         return FALSE;
359 }
360
361 /**
362  * gsf_output_error :
363  * @output:
364  *
365  * Returns: the last error logged on the output, or %NULL.
366  **/
367 GError const *
368 gsf_output_error (GsfOutput const *output)
369 {
370         g_return_val_if_fail (GSF_IS_OUTPUT (output), NULL);
371         return output->err;
372 }
373
374 /**
375  * gsf_output_set_name :
376  * @output: #GsfOutput
377  * @name: the new name
378  *
379  * <note>This is a utility routine that should only be used by derived
380  * outputs.</note>
381  *
382  * Returns: %TRUE if the assignment was ok.
383  **/
384 gboolean
385 gsf_output_set_name (GsfOutput *output, char const *name)
386 {
387         char *buf;
388
389         g_return_val_if_fail (GSF_IS_OUTPUT (output), FALSE);
390
391         buf = g_strdup (name);
392         g_free (output->name);
393         output->name = buf;
394         return TRUE;
395 }
396
397 /**
398  * gsf_output_set_name_from_filename :
399  * @output : the output stream
400  * @filename : the (fs-sys encoded) filename
401  *
402  * <note>This is a utility routine that should only be used by derived
403  * outputs.</note>
404  *
405  * Returns: %TRUE if the assignment was ok.
406  **/
407 gboolean
408 gsf_output_set_name_from_filename (GsfOutput *output, char const *filename)
409 {
410         g_return_val_if_fail (GSF_IS_OUTPUT (output), FALSE);
411
412         g_free (output->name);
413         output->name = filename
414                 ? g_filename_to_utf8 (filename, -1, NULL, NULL, NULL)
415                 : NULL;
416         return TRUE;
417 }
418
419 /**
420  * gsf_output_set_container :
421  * @output: #GsfOutput
422  * @container: #GsfOutfile
423  *
424  * <note>This is a utility routine that should only be used by derived
425  * outputs.</note>
426  *
427  * Returns: %TRUE if the assignment was ok.
428  **/
429 gboolean
430 gsf_output_set_container (GsfOutput *output, GsfOutfile *container)
431 {
432         g_return_val_if_fail (GSF_IS_OUTPUT (output), FALSE);
433
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;
439         return TRUE;
440 }
441
442 /**
443  * gsf_output_set_error :
444  * @output: #GsfOutput
445  * @code: The error id
446  * @format: printf style format string
447  * @Varargs: arguments for @format
448  *
449  * <note>This is a utility routine that should only be used by derived
450  * outputs.</note>
451  *
452  * Returns: Always returns %FALSE to facilitate its use.
453  **/
454 gboolean
455 gsf_output_set_error (GsfOutput  *output,
456                       gint        code,
457                       char const *format,
458                       ...)
459 {
460         g_return_val_if_fail (GSF_IS_OUTPUT (output), FALSE);
461
462         g_clear_error (&output->err);
463
464         if (format != NULL) {
465                 char *message;
466                 va_list args;
467
468                 va_start (args, format);
469                 message = g_strdup_vprintf (format, args);
470                 va_end (args);
471
472                 output->err = g_error_new_literal (gsf_output_error_id (),
473                                                    code,
474                                                    message);
475                 g_free (message);
476         }
477
478         return FALSE;
479 }
480
481 static void
482 cb_output_unwrap (GsfOutput *wrapee, G_GNUC_UNUSED GObject *wrapper)
483 {
484         wrapee->wrapped_by = NULL;
485 }
486
487 /**
488  * gsf_output_wrap :
489  * @wrapper:
490  * @wrapee:
491  *
492  * Returns: %TRUE if the wrapping succeeded.
493  **/
494 gboolean
495 gsf_output_wrap (GObject *wrapper, GsfOutput *wrapee)
496 {
497         g_return_val_if_fail (wrapper != NULL, FALSE);
498         g_return_val_if_fail (wrapee != NULL, FALSE);
499
500         if (wrapee->wrapped_by != NULL) {
501                 g_warning ("Attempt to wrap an output that is already wrapped.");
502                 return FALSE;
503         }
504
505         g_object_weak_ref (G_OBJECT (wrapper),
506                 (GWeakNotify) cb_output_unwrap, wrapee);
507         wrapee->wrapped_by = wrapper;
508         return TRUE;
509 }
510
511 /**
512  * gsf_output_unwrap :
513  * @wrapper:
514  * @wrapee:
515  *
516  * Returns: %TRUE if the unwrapping succeeded.
517  **/
518 gboolean
519 gsf_output_unwrap (GObject *wrapper, GsfOutput *wrapee)
520 {
521         g_return_val_if_fail (wrapee != NULL, FALSE);
522         g_return_val_if_fail (wrapee->wrapped_by == wrapper, FALSE);
523
524         wrapee->wrapped_by = NULL;
525         g_object_weak_unref (G_OBJECT (wrapper),
526                 (GWeakNotify) cb_output_unwrap, wrapee);
527         return TRUE;
528 }
529
530 GQuark
531 gsf_output_error_id (void)
532 {
533         static GQuark quark;
534         if (!quark)
535                 quark = g_quark_from_static_string ("gsf_output_error");
536         return quark;
537 }
538
539 /**
540  * gsf_output_printf :
541  * @output : A #GsfOutput
542  * @format : The printf-style format string
543  * @Varargs : the arguments for @format
544  *
545  * Output @Varargs to @output using the format string @format, similar to
546  * <citerefentry><refentrytitle>printf</refentrytitle>
547  * <manvolnum>3</manvolnum></citerefentry>.
548  *
549  * Returns: %TRUE if successful, %FALSE if not
550  **/
551 gboolean
552 gsf_output_printf (GsfOutput *output, char const *format, ...)
553 {
554         va_list args;
555         gboolean res;
556
557         va_start (args, format);
558         res = (gsf_output_vprintf (output, format, args) >= 0);
559         va_end (args);
560         return res;
561 }
562
563 /**
564  * gsf_output_vprintf :
565  * @output : A #GsfOutput
566  * @format : The printf-style format string
567  * @args : the arguments for @format
568  *
569  * Output @args to @output using the format string @format, similar to
570  * <citerefentry><refentrytitle>vprintf</refentrytitle>
571  * <manvolnum>3</manvolnum></citerefentry>.
572  *
573  * Returns: number of bytes printed, a negative value if not successful
574  **/
575 gsf_off_t
576 gsf_output_vprintf (GsfOutput *output, char const *format, va_list args)
577 {
578         gsf_off_t num_bytes;
579
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? */
583
584         num_bytes = GET_CLASS (output)->Vprintf (output, format, args);
585
586         if (num_bytes >= 0)
587                 if (!gsf_output_inc_cur_offset (output, num_bytes))
588                         return -1;
589         return num_bytes;
590 }
591
592 static gsf_off_t
593 gsf_output_real_vprintf (GsfOutput *output, char const *fmt, va_list args)
594 {
595         gsf_off_t reslen;
596         va_list args2;
597
598         /*
599          * We need to make a copy as args will become unusable after
600          * the g_vsnprintf call.
601          */
602         G_VA_COPY (args2, args);
603
604         if (NULL == output->printf_buf) {
605                 output->printf_buf_size = 128;
606                 output->printf_buf = g_new (char, output->printf_buf_size);
607         }
608         reslen = g_vsnprintf (output->printf_buf, output->printf_buf_size, fmt, args);
609
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);
615         }
616         va_end (args2);
617
618         if (reslen == 0 ||
619             GET_CLASS (output)->Write (output, reslen, output->printf_buf))
620                 return reslen;
621
622         return -1;
623 }
624
625 /**
626  * gsf_output_puts:
627  * @output: A #GsfOutput
628  * @line: %null terminated string to write
629  *
630  * Like fputs, this assumes that the line already ends with a newline 
631  *
632  * Returns: %TRUE if successful, %FALSE if not
633  **/
634 gboolean
635 gsf_output_puts (GsfOutput *output, char const *line)
636 {
637         size_t nbytes = 0;
638
639         g_return_val_if_fail (line != NULL, FALSE);
640
641         nbytes = strlen (line);
642         return gsf_output_write (output, nbytes, line);
643 }