"Initial commit to Gerrit"
[profile/ivi/libgsf.git] / gsf / gsf-output-bzip.c
1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * gsf-output-bzip.c: wrapper to compress to bzipped output
4  *
5  * Copyright (C) 2003-2006 Dom Lachowicz (cinamod@hotmail.com)
6  *               2002-2006 Jon K Hellan (hellan@acm.org)
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of version 2.1 of the GNU Lesser General Public
10  * License as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
20  * USA
21  */
22
23 #include <gsf-config.h>
24
25 #include <gsf/gsf-output-bzip.h>
26 #include <gsf/gsf-output-impl.h>
27 #include <gsf/gsf-impl-utils.h>
28 #include <gsf/gsf-utils.h>
29
30 #ifdef HAVE_BZ2
31 /* For getting FILE.  Don't ask.  */
32 #include <stdio.h>
33 #include <bzlib.h>
34 #define BZ_BUFSIZE 1024
35 #endif
36
37 static GObjectClass *parent_class;
38
39 struct _GsfOutputBzip {
40         GsfOutput  output;
41
42 #ifdef HAVE_BZ2
43         GsfOutput *sink; /* compressed data */
44         bz_stream  stream;
45         guint8    *buf;
46         size_t     buf_size;
47 #endif
48 };
49
50 typedef struct {
51         GsfOutputClass output_class;
52 } GsfOutputBzipClass;
53
54 static void
55 gsf_output_bzip_finalize (GObject *obj)
56 {
57 #ifdef HAVE_BZ2
58         GsfOutputBzip *bzip = (GsfOutputBzip *)obj;
59
60         if (bzip->sink != NULL) {
61                 g_object_unref (G_OBJECT (bzip->sink));
62                 bzip->sink = NULL;
63         }
64         g_free (bzip->buf);
65 #endif
66         parent_class->finalize (obj);
67 }
68
69 #ifdef HAVE_BZ2
70 static gboolean
71 init_bzip (GsfOutputBzip *bzip, GError **err)
72 {
73         int ret;
74         
75         ret = BZ2_bzCompressInit (&bzip->stream, 6, 0, 0);
76
77         if (ret != BZ_OK) {
78                 if (err != NULL)
79                         *err = g_error_new (gsf_output_error_id (), 0,
80                                             "Unable to initialize BZ2 library");
81                 return FALSE;
82         }
83         if (!bzip->buf) {
84                 bzip->buf_size = BZ_BUFSIZE; 
85                 bzip->buf = g_new (guint8, bzip->buf_size);
86         }
87         bzip->stream.next_out  = bzip->buf;
88         bzip->stream.avail_out = bzip->buf_size;
89
90         return TRUE;
91 }
92
93 static gboolean
94 bzip_output_block (GsfOutputBzip *bzip)
95 {
96         size_t num_bytes = bzip->buf_size - bzip->stream.avail_out;
97         
98         if (!gsf_output_write (bzip->sink, num_bytes, bzip->buf))
99                 return FALSE;
100
101         bzip->stream.next_out  = bzip->buf;
102         bzip->stream.avail_out = bzip->buf_size;
103
104         return TRUE;
105 }
106
107 static gboolean
108 bzip_flush (GsfOutputBzip *bzip)
109 {
110         int zret;
111
112         do {
113                 zret = BZ2_bzCompress (&bzip->stream, BZ_FINISH);
114                 if (zret == BZ_FINISH_OK) {
115                         /*  In this case BZ_FINISH_OK means more buffer space
116                             needed  */
117                         if (!bzip_output_block (bzip))
118                                 return FALSE;
119                 }
120         } while (zret == BZ_FINISH_OK);
121         if (zret != BZ_STREAM_END) {
122                 g_warning ("Unexpected error code %d from bzlib during compression.",
123                            zret);
124                 return FALSE;
125         }
126         if (!bzip_output_block (bzip))
127                 return FALSE;
128
129         return TRUE;
130 }
131 #endif
132
133 static gboolean
134 gsf_output_bzip_write (GsfOutput *output,
135                        size_t num_bytes, guint8 const *data)
136 {
137 #ifdef HAVE_BZ2
138         GsfOutputBzip *bzip = GSF_OUTPUT_BZIP (output);
139
140         g_return_val_if_fail (data, FALSE);
141
142         bzip->stream.next_in  = (unsigned char *) data;
143         bzip->stream.avail_in = num_bytes;
144         
145         while (bzip->stream.avail_in > 0) {
146                 int zret;
147
148                 if (bzip->stream.avail_out == 0) {
149                         if (!bzip_output_block (bzip))
150                                 return FALSE;
151                 }
152
153                 zret = BZ2_bzCompress (&bzip->stream, BZ_RUN);
154                 if (zret != BZ_RUN_OK) {
155                         g_warning ("Unexpected error code %d from bzlib during compression.",
156                                    zret);
157                         return FALSE;
158                 }
159         }
160
161         if (bzip->stream.avail_out == 0) {
162                 if (!bzip_output_block (bzip))
163                         return FALSE;
164         }
165
166         return TRUE;
167 #else
168         (void)output;
169         (void)num_bytes;
170         (void)data;
171         return FALSE;
172 #endif
173 }
174
175 static gboolean
176 gsf_output_bzip_seek (G_GNUC_UNUSED GsfOutput *output,
177                       G_GNUC_UNUSED gsf_off_t offset,
178                       G_GNUC_UNUSED GSeekType whence)
179 {       
180         return FALSE;
181 }
182
183 static gboolean
184 gsf_output_bzip_close (GsfOutput *output)
185 {
186 #ifdef HAVE_BZ2
187         GsfOutputBzip *bzip = GSF_OUTPUT_BZIP (output);
188         gboolean rt;
189
190         rt = bzip_flush (bzip);
191         BZ2_bzCompressEnd (&bzip->stream);
192
193         return rt;
194 #else
195         (void)output;
196         return FALSE;
197 #endif
198 }
199
200 static void
201 gsf_output_bzip_init (GObject *obj)
202 {
203 #ifdef HAVE_BZ2
204         GsfOutputBzip *bzip = GSF_OUTPUT_BZIP (obj);
205
206         bzip->sink = NULL;
207         bzip->stream.bzalloc    = NULL;
208         bzip->stream.bzfree     = NULL;
209         bzip->stream.opaque     = NULL;
210         bzip->stream.next_in    = NULL;
211         bzip->stream.next_out   = NULL;
212         bzip->stream.avail_in   = bzip->stream.avail_out = 0;
213         bzip->buf               = NULL;
214         bzip->buf_size          = 0;
215 #else
216         (void)obj;
217 #endif
218 }
219
220 static void
221 gsf_output_bzip_class_init (GObjectClass *gobject_class)
222 {
223         GsfOutputClass *output_class = GSF_OUTPUT_CLASS (gobject_class);
224
225         gobject_class->finalize = gsf_output_bzip_finalize;
226         output_class->Write     = gsf_output_bzip_write;
227         output_class->Seek      = gsf_output_bzip_seek;
228         output_class->Close     = gsf_output_bzip_close;
229
230         parent_class = g_type_class_peek_parent (gobject_class);
231 }
232
233 GSF_CLASS (GsfOutputBzip, gsf_output_bzip,
234            gsf_output_bzip_class_init, gsf_output_bzip_init,
235            GSF_OUTPUT_TYPE)
236
237 /**
238  * gsf_output_bzip_new :
239  * @sink : The underlying data source.
240  * @err    : optionally %NULL.
241  *
242  * Adds a reference to @sink.
243  *
244  * Returns: a new file or %NULL.
245  **/
246 GsfOutput *
247 gsf_output_bzip_new (GsfOutput *sink, GError **err)
248 {
249 #ifdef HAVE_BZ2
250         GsfOutputBzip *bzip;
251
252         g_return_val_if_fail (GSF_IS_OUTPUT (sink), NULL);
253
254         bzip = g_object_new (GSF_OUTPUT_BZIP_TYPE, NULL);
255         if (G_UNLIKELY (NULL == bzip)) return NULL;
256
257         g_object_ref (G_OBJECT (sink));
258         bzip->sink = sink;
259
260         if (!init_bzip (bzip, err)) {
261                 g_object_unref (G_OBJECT (bzip));
262                 return NULL;
263         }
264
265         return GSF_OUTPUT (bzip);
266 #else
267         (void)sink;
268         if (err)
269                 *err = g_error_new (gsf_output_error_id (), 0,
270                                     "BZ2 support not enabled");
271         return NULL;
272 #endif
273 }