extern int errno considered harmful.
[dragonfly.git] / contrib / libarchive / archive_write_set_compression_bzip2.c
1 /*-
2  * Copyright (c) 2003-2004 Tim Kientzle
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer
10  *    in this position and unchanged.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include "archive_platform.h"
28
29 /* Don't compile this if we don't have bzlib. */
30 #if HAVE_BZLIB_H
31
32 __FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_compression_bzip2.c,v 1.7 2004/11/06 05:25:53 kientzle Exp $");
33
34 #include <errno.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <bzlib.h>
39
40 #include "archive.h"
41 #include "archive_private.h"
42
43 struct private_data {
44         bz_stream        stream;
45         int64_t          total_in;
46         char            *compressed;
47         size_t           compressed_buffer_size;
48 };
49
50
51 /*
52  * Yuck.  bzlib.h is not const-correct, so I need this one bit
53  * of ugly hackery to convert a const * pointer to a non-const pointer.
54  */
55 #define SET_NEXT_IN(st,src)                                     \
56         (st)->stream.next_in = (void *)(uintptr_t)(const void *)(src)
57
58 static int      archive_compressor_bzip2_finish(struct archive *);
59 static int      archive_compressor_bzip2_init(struct archive *);
60 static int      archive_compressor_bzip2_write(struct archive *, const void *,
61                     size_t);
62 static int      drive_compressor(struct archive *, struct private_data *,
63                     int finishing);
64
65 /*
66  * Allocate, initialize and return an archive object.
67  */
68 int
69 archive_write_set_compression_bzip2(struct archive *a)
70 {
71         archive_check_magic(a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW);
72         a->compression_init = &archive_compressor_bzip2_init;
73         a->compression_code = ARCHIVE_COMPRESSION_BZIP2;
74         a->compression_name = "bzip2";
75         return (ARCHIVE_OK);
76 }
77
78 /*
79  * Setup callback.
80  */
81 static int
82 archive_compressor_bzip2_init(struct archive *a)
83 {
84         int ret;
85         struct private_data *state;
86
87         a->compression_code = ARCHIVE_COMPRESSION_BZIP2;
88         a->compression_name = "bzip2";
89
90         if (a->client_opener != NULL) {
91                 ret = (a->client_opener)(a, a->client_data);
92                 if (ret != 0)
93                         return (ret);
94         }
95
96         state = malloc(sizeof(*state));
97         if (state == NULL) {
98                 archive_set_error(a, ENOMEM,
99                     "Can't allocate data for compression");
100                 return (ARCHIVE_FATAL);
101         }
102         memset(state, 0, sizeof(*state));
103
104         state->compressed_buffer_size = a->bytes_per_block;
105         state->compressed = malloc(state->compressed_buffer_size);
106
107         if (state->compressed == NULL) {
108                 archive_set_error(a, ENOMEM,
109                     "Can't allocate data for compression buffer");
110                 free(state);
111                 return (ARCHIVE_FATAL);
112         }
113
114         state->stream.next_out = state->compressed;
115         state->stream.avail_out = state->compressed_buffer_size;
116         a->compression_write = archive_compressor_bzip2_write;
117         a->compression_finish = archive_compressor_bzip2_finish;
118
119         /* Initialize compression library */
120         ret = BZ2_bzCompressInit(&(state->stream), 9, 0, 30);
121         if (ret == BZ_OK) {
122                 a->compression_data = state;
123                 return (ARCHIVE_OK);
124         }
125
126         /* Library setup failed: clean up. */
127         archive_set_error(a, ARCHIVE_ERRNO_MISC,
128             "Internal error initializing compression library");
129         free(state->compressed);
130         free(state);
131
132         /* Override the error message if we know what really went wrong. */
133         switch (ret) {
134         case BZ_PARAM_ERROR:
135                 archive_set_error(a, ARCHIVE_ERRNO_MISC,
136                     "Internal error initializing compression library: "
137                     "invalid setup parameter");
138                 break;
139         case BZ_MEM_ERROR:
140                 archive_set_error(a, ENOMEM,
141                     "Internal error initializing compression library: "
142                     "out of memory");
143                 break;
144         case BZ_CONFIG_ERROR:
145                 archive_set_error(a, ARCHIVE_ERRNO_MISC,
146                     "Internal error initializing compression library: "
147                     "mis-compiled library");
148                 break;
149         }
150
151         return (ARCHIVE_FATAL);
152
153 }
154
155 /*
156  * Write data to the compressed stream.
157  *
158  * Returns ARCHIVE_OK if all data written, error otherwise.
159  */
160 static int
161 archive_compressor_bzip2_write(struct archive *a, const void *buff,
162     size_t length)
163 {
164         struct private_data *state;
165
166         state = a->compression_data;
167         if (a->client_writer == NULL) {
168                 archive_set_error(a, ARCHIVE_ERRNO_PROGRAMMER,
169                     "No write callback is registered?  "
170                     "This is probably an internal programming error.");
171                 return (ARCHIVE_FATAL);
172         }
173
174         /* Update statistics */
175         state->total_in += length;
176
177         /* Compress input data to output buffer */
178         SET_NEXT_IN(state, buff);
179         state->stream.avail_in = length;
180         if (drive_compressor(a, state, 0))
181                 return (ARCHIVE_FATAL);
182         a->file_position += length;
183         return (ARCHIVE_OK);
184 }
185
186
187 /*
188  * Finish the compression.
189  */
190 static int
191 archive_compressor_bzip2_finish(struct archive *a)
192 {
193         ssize_t block_length;
194         int ret;
195         struct private_data *state;
196         ssize_t target_block_length;
197         ssize_t bytes_written;
198         unsigned tocopy;
199
200         state = a->compression_data;
201         ret = ARCHIVE_OK;
202         if (a->client_writer == NULL) {
203                 archive_set_error(a, ARCHIVE_ERRNO_PROGRAMMER,
204                     "No write callback is registered?\n"
205                     "This is probably an internal programming error.");
206                 ret = ARCHIVE_FATAL;
207                 goto cleanup;
208         }
209
210         /* By default, always pad the uncompressed data. */
211         if (a->pad_uncompressed) {
212                 tocopy = a->bytes_per_block -
213                     (state->total_in % a->bytes_per_block);
214                 while (tocopy > 0 && tocopy < (unsigned)a->bytes_per_block) {
215                         SET_NEXT_IN(state, a->nulls);
216                         state->stream.avail_in = tocopy < a->null_length ?
217                             tocopy : a->null_length;
218                         state->total_in += state->stream.avail_in;
219                         tocopy -= state->stream.avail_in;
220                         ret = drive_compressor(a, state, 0);
221                         if (ret != ARCHIVE_OK)
222                                 goto cleanup;
223                 }
224         }
225
226         /* Finish compression cycle. */
227         if ((ret = drive_compressor(a, state, 1)))
228                 goto cleanup;
229
230         /* Optionally, pad the final compressed block. */
231         block_length = state->stream.next_out - state->compressed;
232
233
234         /* Tricky calculation to determine size of last block. */
235         target_block_length = block_length;
236         if (a->bytes_in_last_block <= 0)
237                 /* Default or Zero: pad to full block */
238                 target_block_length = a->bytes_per_block;
239         else
240                 /* Round length to next multiple of bytes_in_last_block. */
241                 target_block_length = a->bytes_in_last_block *
242                     ( (block_length + a->bytes_in_last_block - 1) /
243                         a->bytes_in_last_block);
244         if (target_block_length > a->bytes_per_block)
245                 target_block_length = a->bytes_per_block;
246         if (block_length < target_block_length) {
247                 memset(state->stream.next_out, 0,
248                     target_block_length - block_length);
249                 block_length = target_block_length;
250         }
251
252         /* Write the last block */
253         bytes_written = (a->client_writer)(a, a->client_data,
254             state->compressed, block_length);
255
256         /* TODO: Handle short write of final block. */
257         if (bytes_written <= 0)
258                 ret = ARCHIVE_FATAL;
259         else {
260                 a->raw_position += ret;
261                 ret = ARCHIVE_OK;
262         }
263
264         /* Cleanup: shut down compressor, release memory, etc. */
265 cleanup:
266         switch (BZ2_bzCompressEnd(&(state->stream))) {
267         case BZ_OK:
268                 break;
269         default:
270                 archive_set_error(a, ARCHIVE_ERRNO_PROGRAMMER,
271                     "Failed to clean up compressor");
272                 ret = ARCHIVE_FATAL;
273         }
274
275         free(state->compressed);
276         free(state);
277
278         /* Close the output */
279         if (a->client_closer != NULL)
280                 (a->client_closer)(a, a->client_data);
281
282         return (ret);
283 }
284
285 /*
286  * Utility function to push input data through compressor, writing
287  * full output blocks as necessary.
288  *
289  * Note that this handles both the regular write case (finishing ==
290  * false) and the end-of-archive case (finishing == true).
291  */
292 static int
293 drive_compressor(struct archive *a, struct private_data *state, int finishing)
294 {
295         ssize_t bytes_written;
296         int ret;
297
298         for (;;) {
299                 if (state->stream.avail_out == 0) {
300                         bytes_written = (a->client_writer)(a, a->client_data,
301                             state->compressed, state->compressed_buffer_size);
302                         if (bytes_written <= 0) {
303                                 /* TODO: Handle this write failure */
304                                 return (ARCHIVE_FATAL);
305                         } else if ((size_t)bytes_written < state->compressed_buffer_size) {
306                                 /* Short write: Move remainder to
307                                  * front and keep filling */
308                                 memmove(state->compressed,
309                                     state->compressed + bytes_written,
310                                     state->compressed_buffer_size - bytes_written);
311                         }
312
313                         a->raw_position += bytes_written;
314                         state->stream.next_out = state->compressed +
315                             state->compressed_buffer_size - bytes_written;
316                         state->stream.avail_out = bytes_written;
317                 }
318
319                 ret = BZ2_bzCompress(&(state->stream),
320                     finishing ? BZ_FINISH : BZ_RUN);
321
322                 switch (ret) {
323                 case BZ_RUN_OK:
324                         /* In non-finishing case, did compressor
325                          * consume everything? */
326                         if (!finishing && state->stream.avail_in == 0)
327                                 return (ARCHIVE_OK);
328                         break;
329                 case BZ_FINISH_OK:  /* Finishing: There's more work to do */
330                         break;
331                 case BZ_STREAM_END: /* Finishing: all done */
332                         /* Only occurs in finishing case */
333                         return (ARCHIVE_OK);
334                 default:
335                         /* Any other return value indicates an error */
336                         archive_set_error(a, ARCHIVE_ERRNO_PROGRAMMER,
337                             "Bzip2 compression failed");
338                         return (ARCHIVE_FATAL);
339                 }
340         }
341 }
342
343 #endif /* HAVE_BZLIB_H */