"Initial commit to Gerrit"
[profile/ivi/libgsf.git] / gsf / gsf-output-gzip.c
1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * gsf-output-gzip.c: wrapper to compress to gzipped output. See rfc1952.
4  *
5  * Copyright (C) 2002-2006 Jon K Hellan (hellan@acm.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-gzip.h>
24 #include <gsf/gsf-output-impl.h>
25 #include <gsf/gsf-impl-utils.h>
26 #include <gsf/gsf-utils.h>
27
28 #include <zlib.h>
29 #include <stdio.h>
30 #include <time.h>
31 #include <string.h>
32
33 static GObjectClass *parent_class;
34
35 struct _GsfOutputGZip {
36         GsfOutput output;
37
38         GsfOutput *sink; /* compressed data */
39         gboolean raw; /* No header and no trailer.  */
40
41         z_stream  stream;
42         uLong     crc;     /* crc32 of uncompressed data */
43         size_t    isize;
44
45         guint8   *buf;
46         size_t    buf_size;
47 };
48
49 typedef struct {
50         GsfOutputClass output_class;
51 } GsfOutputGZipClass;
52
53 enum {
54         PROP_0,
55         PROP_RAW,
56         PROP_SINK
57 };
58
59
60 /* gzip flag byte */
61 #define GZIP_ORIGINAL_NAME      0x08 /* the original is stored */
62
63 static gboolean
64 init_gzip (GsfOutputGZip *gzip)
65 {
66         int ret;
67
68         ret = deflateInit2 (&gzip->stream, Z_DEFAULT_COMPRESSION,
69                             Z_DEFLATED, -MAX_WBITS, MAX_MEM_LEVEL,
70                             Z_DEFAULT_STRATEGY);
71         if (ret != Z_OK)
72                 return FALSE;
73
74         if (!gzip->buf) {
75                 gzip->buf_size = 0x100;
76                 gzip->buf = g_new (guint8, gzip->buf_size);
77         }
78         gzip->stream.next_out  = gzip->buf;
79         gzip->stream.avail_out = gzip->buf_size;
80
81         return TRUE;
82 }
83
84 static gboolean
85 gzip_output_header (GsfOutputGZip *gzip)
86 {
87         guint8 buf[3 + 1 + 4 + 2];
88         static guint8 const gzip_signature[] = { 0x1f, 0x8b, 0x08 } ;
89         time_t mtime = time (NULL);
90         char const *name = gsf_output_name (gzip->sink);
91         /* FIXME: What to do about gz extension ... ? */
92         int nlen = 0;  /* name ? strlen (name) : 0; */
93         gboolean ret;
94
95         memset (buf, 0, sizeof buf);
96         memcpy (buf, gzip_signature, 3);
97         if (nlen > 0)
98                 buf[3] = GZIP_ORIGINAL_NAME;
99         GSF_LE_SET_GUINT32 (buf + 4, (guint32) mtime);
100         buf[9] = 3;     /* UNIX */
101         ret = gsf_output_write (gzip->sink, sizeof buf, buf);
102         if (ret && name && nlen > 0)
103                 ret = gsf_output_write (gzip->sink, nlen, name);
104
105         return ret;
106 }
107
108 /**
109  * gsf_output_gzip_new :
110  * @sink : The underlying data source.
111  * @err    : optionally %NULL.
112  *
113  * Adds a reference to @sink.
114  *
115  * Returns: a new file or %NULL.
116  **/
117 GsfOutput *
118 gsf_output_gzip_new (GsfOutput *sink, GError **err)
119 {
120         GsfOutput *output;
121         GError const *con_err;
122
123         g_return_val_if_fail (GSF_IS_OUTPUT (sink), NULL);
124
125         output = g_object_new (GSF_OUTPUT_GZIP_TYPE, "sink", sink, NULL);
126         if (G_UNLIKELY (NULL == output)) return NULL;
127
128         con_err = gsf_output_error (output);
129
130         if (con_err) {
131                 if (err)
132                         *err = g_error_copy (con_err);
133                 g_object_unref (output);
134                 return NULL;
135         }
136
137         return output;
138 }
139
140 static void
141 gsf_output_gzip_finalize (GObject *obj)
142 {
143         GsfOutputGZip *gzip = (GsfOutputGZip *)obj;
144
145         if (gzip->sink != NULL) {
146                 g_object_unref (G_OBJECT (gzip->sink));
147                 gzip->sink = NULL;
148         }
149
150         g_free (gzip->buf);
151
152         /* FIXME: check for error?  */
153         deflateEnd (&gzip->stream);
154
155         parent_class->finalize (obj);
156 }
157
158 static gboolean
159 gzip_output_block (GsfOutputGZip *gzip)
160 {
161         size_t num_bytes = gzip->buf_size - gzip->stream.avail_out;
162
163         if (!gsf_output_write (gzip->sink, num_bytes, gzip->buf)) {
164                 gsf_output_set_error (GSF_OUTPUT (gzip), 0,
165                                       "Failed to write");
166                 return FALSE;
167         }
168         gzip->stream.next_out  = gzip->buf;
169         gzip->stream.avail_out = gzip->buf_size;
170
171         return TRUE;
172 }
173
174 static gboolean
175 gzip_flush (GsfOutputGZip *gzip)
176 {
177         int zret;
178
179         do {
180                 zret = deflate (&gzip->stream, Z_FINISH);
181                 if (zret == Z_OK) {
182                         /*  In this case Z_OK means more buffer space
183                             needed  */
184                         if (!gzip_output_block (gzip))
185                                 return FALSE;
186                 }
187         } while (zret == Z_OK);
188         if (zret != Z_STREAM_END) {
189                 gsf_output_set_error (GSF_OUTPUT (gzip), 0,
190                                       "Unexpected compression failure");
191                 g_warning ("Unexpected error code %d from zlib during compression.",
192                            zret);
193                 return FALSE;
194         }
195         if (!gzip_output_block (gzip))
196                 return FALSE;
197
198         return TRUE;
199 }
200
201 static gboolean
202 gsf_output_gzip_write (GsfOutput *output,
203                        size_t num_bytes, guint8 const *data)
204 {
205         GsfOutputGZip *gzip = GSF_OUTPUT_GZIP (output);
206
207         g_return_val_if_fail (data, FALSE);
208
209         gzip->stream.next_in  = (unsigned char *) data;
210         gzip->stream.avail_in = num_bytes;
211
212         while (gzip->stream.avail_in > 0) {
213                 int zret;
214                 if (gzip->stream.avail_out == 0) {
215                         if (!gzip_output_block (gzip))
216                                 return FALSE;
217                 }
218
219                 zret = deflate (&gzip->stream, Z_NO_FLUSH);
220                 if (zret != Z_OK) {
221                         gsf_output_set_error (output, 0,
222                                               "Unexpected compression failure");
223                         g_warning ("Unexpected error code %d from zlib during compression.",
224                                    zret);
225                         return FALSE;
226                 }
227         }
228
229         gzip->crc = crc32 (gzip->crc, data, num_bytes);
230         gzip->isize += num_bytes;
231
232         if (gzip->stream.avail_out == 0) {
233                 if (!gzip_output_block (gzip))
234                         return FALSE;
235         }
236
237         return TRUE;
238 }
239
240 static gboolean
241 gsf_output_gzip_seek (G_GNUC_UNUSED GsfOutput *output,
242                       G_GNUC_UNUSED gsf_off_t offset,
243                       G_GNUC_UNUSED GSeekType whence)
244 {
245         return FALSE;
246 }
247
248 static gboolean
249 gsf_output_gzip_close (GsfOutput *output)
250 {
251         if (!gsf_output_error (output)) {
252                 GsfOutputGZip *gzip = GSF_OUTPUT_GZIP (output);
253
254                 if (!gzip_flush (gzip))
255                         return FALSE;
256
257                 if (!gzip->raw) {
258                         guint8 buf[8];
259
260                         GSF_LE_SET_GUINT32 (buf,     gzip->crc);
261                         GSF_LE_SET_GUINT32 (buf + 4, gzip->isize);
262                         if (!gsf_output_write (gzip->sink, 8, buf))
263                                 return FALSE;
264                 }
265         }
266
267         return TRUE;
268 }
269
270 static void
271 gsf_output_gzip_init (GObject *obj)
272 {
273         GsfOutputGZip *gzip = GSF_OUTPUT_GZIP (obj);
274
275         gzip->sink = NULL;
276         gzip->stream.zalloc     = (alloc_func)0;
277         gzip->stream.zfree      = (free_func)0;
278         gzip->stream.opaque     = (voidpf)0;
279         gzip->stream.next_in    = Z_NULL;
280         gzip->stream.next_out   = Z_NULL;
281         gzip->stream.avail_in   = gzip->stream.avail_out = 0;
282         gzip->crc               = crc32 (0L, Z_NULL, 0);
283         gzip->isize             = 0;
284         gzip->buf               = NULL;
285         gzip->buf_size          = 0;
286 }
287
288 static void
289 gsf_output_gzip_get_property (GObject     *object,
290                               guint        property_id,
291                               GValue      *value,
292                               GParamSpec  *pspec)
293 {
294         GsfOutputGZip *gzip = (GsfOutputGZip *)object;
295
296         switch (property_id) {
297         case PROP_RAW:
298                 g_value_set_boolean (value, gzip->raw);
299                 break;
300         case PROP_SINK:
301                 g_value_set_object (value, gzip->sink);
302                 break;
303         default:
304                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
305                 break;
306         }
307 }
308
309 static void
310 gsf_output_gzip_set_sink (GsfOutputGZip *gzip, GsfOutput *sink)
311 {
312         if (sink)
313                 g_object_ref (GSF_OUTPUT (sink));
314         if (gzip->sink)
315                 g_object_unref (gzip->sink);
316         gzip->sink = sink;
317 }
318
319 static void
320 gsf_output_gzip_set_property (GObject      *object,
321                               guint         property_id,
322                               GValue const *value,
323                               GParamSpec   *pspec)
324 {
325         GsfOutputGZip *gzip = (GsfOutputGZip *)object;
326
327         switch (property_id) {
328         case PROP_RAW:
329                 gzip->raw = g_value_get_boolean (value);
330                 break;
331         case PROP_SINK:
332                 gsf_output_gzip_set_sink (gzip, g_value_get_object (value));
333                 break;
334         default:
335                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
336                 break;
337         }
338 }
339
340 static GObject*
341 gsf_output_gzip_constructor (GType                  type,
342                              guint                  n_construct_properties,
343                              GObjectConstructParam *construct_params)
344 {
345         GsfOutputGZip *gzip;
346
347         gzip = (GsfOutputGZip *)(parent_class->constructor (type,
348                                                             n_construct_properties,
349                                                             construct_params));
350
351         if (!gzip->sink)
352                 gsf_output_set_error (GSF_OUTPUT (gzip),
353                                       0,
354                                       "NULL sink");
355         else if (!init_gzip (gzip))
356                 gsf_output_set_error (GSF_OUTPUT (gzip),
357                                       0,
358                                       "Failed to initialize zlib structure");
359         else if (!gzip->raw && !gzip_output_header (gzip))
360                 gsf_output_set_error (GSF_OUTPUT (gzip),
361                                       0,
362                                       "Failed to write gzip header");
363
364         return (GObject *)gzip;
365 }
366
367 static void
368 gsf_output_gzip_class_init (GObjectClass *gobject_class)
369 {
370         GsfOutputClass *output_class = GSF_OUTPUT_CLASS (gobject_class);
371
372         gobject_class->constructor  = gsf_output_gzip_constructor;
373         gobject_class->finalize     = gsf_output_gzip_finalize;
374         gobject_class->set_property = gsf_output_gzip_set_property;
375         gobject_class->get_property = gsf_output_gzip_get_property;
376         output_class->Write         = gsf_output_gzip_write;
377         output_class->Seek          = gsf_output_gzip_seek;
378         output_class->Close         = gsf_output_gzip_close;
379
380         g_object_class_install_property
381                 (gobject_class,
382                  PROP_RAW,
383                  g_param_spec_boolean ("raw", "Raw",
384                                        "Whether to write compressed data with no header/tailer.",
385                                        FALSE,
386                                        GSF_PARAM_STATIC |
387                                        G_PARAM_READWRITE |
388                                        G_PARAM_CONSTRUCT_ONLY));
389         g_object_class_install_property
390                 (gobject_class,
391                  PROP_SINK,
392                  g_param_spec_object ("sink", "Sink",
393                                       "Where the compressed data is written.",
394                                       GSF_OUTPUT_TYPE,
395                                       GSF_PARAM_STATIC |
396                                       G_PARAM_READWRITE |
397                                       G_PARAM_CONSTRUCT_ONLY));
398
399         parent_class = g_type_class_peek_parent (gobject_class);
400 }
401
402 GSF_CLASS (GsfOutputGZip, gsf_output_gzip,
403            gsf_output_gzip_class_init, gsf_output_gzip_init,
404            GSF_OUTPUT_TYPE)