2 * Copyright (c) 2003-2007 Tim Kientzle
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 #include "archive_platform.h"
28 __FBSDID("$FreeBSD$");
45 #include "archive_private.h"
46 #include "archive_write_private.h"
48 #if !defined(HAVE_BZLIB_H) || !defined(BZ_CONFIG_ERROR)
50 archive_write_set_compression_bzip2(struct archive *a)
52 archive_set_error(a, ARCHIVE_ERRNO_MISC,
53 "bzip2 compression not supported on this platform");
54 return (ARCHIVE_FATAL);
57 /* Don't compile this if we don't have bzlib. */
63 size_t compressed_buffer_size;
66 struct private_config {
67 int compression_level;
71 * Yuck. bzlib.h is not const-correct, so I need this one bit
72 * of ugly hackery to convert a const * pointer to a non-const pointer.
74 #define SET_NEXT_IN(st,src) \
75 (st)->stream.next_in = (char *)(uintptr_t)(const void *)(src)
77 static int archive_compressor_bzip2_finish(struct archive_write *);
78 static int archive_compressor_bzip2_init(struct archive_write *);
79 static int archive_compressor_bzip2_options(struct archive_write *,
80 const char *, const char *);
81 static int archive_compressor_bzip2_write(struct archive_write *,
82 const void *, size_t);
83 static int drive_compressor(struct archive_write *, struct private_data *,
87 * Allocate, initialize and return an archive object.
90 archive_write_set_compression_bzip2(struct archive *_a)
92 struct archive_write *a = (struct archive_write *)_a;
93 struct private_config *config;
94 __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
95 ARCHIVE_STATE_NEW, "archive_write_set_compression_bzip2");
96 config = malloc(sizeof(*config));
98 archive_set_error(&a->archive, ENOMEM, "Out of memory");
99 return (ARCHIVE_FATAL);
101 a->compressor.config = config;
102 a->compressor.finish = archive_compressor_bzip2_finish;
103 config->compression_level = 9; /* default */
104 a->compressor.init = &archive_compressor_bzip2_init;
105 a->compressor.options = &archive_compressor_bzip2_options;
106 a->archive.compression_code = ARCHIVE_COMPRESSION_BZIP2;
107 a->archive.compression_name = "bzip2";
115 archive_compressor_bzip2_init(struct archive_write *a)
118 struct private_data *state;
119 struct private_config *config;
121 config = (struct private_config *)a->compressor.config;
122 if (a->client_opener != NULL) {
123 ret = (a->client_opener)(&a->archive, a->client_data);
128 state = (struct private_data *)malloc(sizeof(*state));
130 archive_set_error(&a->archive, ENOMEM,
131 "Can't allocate data for compression");
132 return (ARCHIVE_FATAL);
134 memset(state, 0, sizeof(*state));
136 state->compressed_buffer_size = a->bytes_per_block;
137 state->compressed = (char *)malloc(state->compressed_buffer_size);
139 if (state->compressed == NULL) {
140 archive_set_error(&a->archive, ENOMEM,
141 "Can't allocate data for compression buffer");
143 return (ARCHIVE_FATAL);
146 state->stream.next_out = state->compressed;
147 state->stream.avail_out = state->compressed_buffer_size;
148 a->compressor.write = archive_compressor_bzip2_write;
150 /* Initialize compression library */
151 ret = BZ2_bzCompressInit(&(state->stream),
152 config->compression_level, 0, 30);
154 a->compressor.data = state;
158 /* Library setup failed: clean up. */
159 archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
160 "Internal error initializing compression library");
161 free(state->compressed);
164 /* Override the error message if we know what really went wrong. */
167 archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
168 "Internal error initializing compression library: "
169 "invalid setup parameter");
172 archive_set_error(&a->archive, ENOMEM,
173 "Internal error initializing compression library: "
176 case BZ_CONFIG_ERROR:
177 archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
178 "Internal error initializing compression library: "
179 "mis-compiled library");
183 return (ARCHIVE_FATAL);
191 archive_compressor_bzip2_options(struct archive_write *a, const char *key,
194 struct private_config *config;
196 config = (struct private_config *)a->compressor.config;
197 if (strcmp(key, "compression-level") == 0) {
198 if (value == NULL || !(value[0] >= '0' && value[0] <= '9') ||
200 return (ARCHIVE_WARN);
201 config->compression_level = value[0] - '0';
202 /* Make '0' be a synonym for '1'. */
203 /* This way, bzip2 compressor supports the same 0..9
204 * range of levels as gzip. */
205 if (config->compression_level < 1)
206 config->compression_level = 1;
210 return (ARCHIVE_WARN);
214 * Write data to the compressed stream.
216 * Returns ARCHIVE_OK if all data written, error otherwise.
219 archive_compressor_bzip2_write(struct archive_write *a, const void *buff,
222 struct private_data *state;
224 state = (struct private_data *)a->compressor.data;
225 if (a->client_writer == NULL) {
226 archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
227 "No write callback is registered? "
228 "This is probably an internal programming error.");
229 return (ARCHIVE_FATAL);
232 /* Update statistics */
233 state->total_in += length;
235 /* Compress input data to output buffer */
236 SET_NEXT_IN(state, buff);
237 state->stream.avail_in = length;
238 if (drive_compressor(a, state, 0))
239 return (ARCHIVE_FATAL);
240 a->archive.file_position += length;
246 * Finish the compression.
249 archive_compressor_bzip2_finish(struct archive_write *a)
251 ssize_t block_length;
253 struct private_data *state;
254 ssize_t target_block_length;
255 ssize_t bytes_written;
259 state = (struct private_data *)a->compressor.data;
261 if (a->client_writer == NULL) {
262 archive_set_error(&a->archive,
263 ARCHIVE_ERRNO_PROGRAMMER,
264 "No write callback is registered?\n"
265 "This is probably an internal programming error.");
270 /* By default, always pad the uncompressed data. */
271 if (a->pad_uncompressed) {
272 tocopy = a->bytes_per_block -
273 (state->total_in % a->bytes_per_block);
274 while (tocopy > 0 && tocopy < (unsigned)a->bytes_per_block) {
275 SET_NEXT_IN(state, a->nulls);
276 state->stream.avail_in = tocopy < a->null_length ?
277 tocopy : a->null_length;
278 state->total_in += state->stream.avail_in;
279 tocopy -= state->stream.avail_in;
280 ret = drive_compressor(a, state, 0);
281 if (ret != ARCHIVE_OK)
286 /* Finish compression cycle. */
287 if ((ret = drive_compressor(a, state, 1)))
290 /* Optionally, pad the final compressed block. */
291 block_length = state->stream.next_out - state->compressed;
293 /* Tricky calculation to determine size of last block. */
294 if (a->bytes_in_last_block <= 0)
295 /* Default or Zero: pad to full block */
296 target_block_length = a->bytes_per_block;
298 /* Round length to next multiple of bytes_in_last_block. */
299 target_block_length = a->bytes_in_last_block *
300 ( (block_length + a->bytes_in_last_block - 1) /
301 a->bytes_in_last_block);
302 if (target_block_length > a->bytes_per_block)
303 target_block_length = a->bytes_per_block;
304 if (block_length < target_block_length) {
305 memset(state->stream.next_out, 0,
306 target_block_length - block_length);
307 block_length = target_block_length;
310 /* Write the last block */
311 bytes_written = (a->client_writer)(&a->archive, a->client_data,
312 state->compressed, block_length);
314 /* TODO: Handle short write of final block. */
315 if (bytes_written <= 0)
318 a->archive.raw_position += ret;
322 /* Cleanup: shut down compressor, release memory, etc. */
324 switch (BZ2_bzCompressEnd(&(state->stream))) {
328 archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
329 "Failed to clean up compressor");
333 free(state->compressed);
336 /* Free configuration data even if we were never fully initialized. */
337 free(a->compressor.config);
338 a->compressor.config = NULL;
343 * Utility function to push input data through compressor, writing
344 * full output blocks as necessary.
346 * Note that this handles both the regular write case (finishing ==
347 * false) and the end-of-archive case (finishing == true).
350 drive_compressor(struct archive_write *a, struct private_data *state, int finishing)
352 ssize_t bytes_written;
356 if (state->stream.avail_out == 0) {
357 bytes_written = (a->client_writer)(&a->archive,
358 a->client_data, state->compressed,
359 state->compressed_buffer_size);
360 if (bytes_written <= 0) {
361 /* TODO: Handle this write failure */
362 return (ARCHIVE_FATAL);
363 } else if ((size_t)bytes_written < state->compressed_buffer_size) {
364 /* Short write: Move remainder to
365 * front and keep filling */
366 memmove(state->compressed,
367 state->compressed + bytes_written,
368 state->compressed_buffer_size - bytes_written);
371 a->archive.raw_position += bytes_written;
372 state->stream.next_out = state->compressed +
373 state->compressed_buffer_size - bytes_written;
374 state->stream.avail_out = bytes_written;
377 /* If there's nothing to do, we're done. */
378 if (!finishing && state->stream.avail_in == 0)
381 ret = BZ2_bzCompress(&(state->stream),
382 finishing ? BZ_FINISH : BZ_RUN);
386 /* In non-finishing case, did compressor
387 * consume everything? */
388 if (!finishing && state->stream.avail_in == 0)
391 case BZ_FINISH_OK: /* Finishing: There's more work to do */
393 case BZ_STREAM_END: /* Finishing: all done */
394 /* Only occurs in finishing case */
397 /* Any other return value indicates an error */
398 archive_set_error(&a->archive,
399 ARCHIVE_ERRNO_PROGRAMMER,
400 "Bzip2 compression failed;"
401 " BZ2_bzCompress() returned %d",
403 return (ARCHIVE_FATAL);
408 #endif /* HAVE_BZLIB_H && BZ_CONFIG_ERROR */