Import libarchive-2.4.8:
[dragonfly.git] / contrib / libarchive-2 / libarchive / archive_write_set_compression_bzip2.c
blobb32167c364f6f236342f719d3868a7161c1055af
1 /*-
2 * Copyright (c) 2003-2007 Tim Kientzle
3 * All rights reserved.
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 * 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 /* Don't compile this if we don't have bzlib. */
29 #if HAVE_BZLIB_H
31 __FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_compression_bzip2.c,v 1.12 2007/05/29 01:00:19 kientzle Exp $");
33 #ifdef HAVE_ERRNO_H
34 #include <errno.h>
35 #endif
36 #include <stdio.h>
37 #ifdef HAVE_STDLIB_H
38 #include <stdlib.h>
39 #endif
40 #ifdef HAVE_STRING_H
41 #include <string.h>
42 #endif
43 #ifdef HAVE_BZLIB_H
44 #include <bzlib.h>
45 #endif
47 #include "archive.h"
48 #include "archive_private.h"
49 #include "archive_write_private.h"
51 struct private_data {
52 bz_stream stream;
53 int64_t total_in;
54 char *compressed;
55 size_t compressed_buffer_size;
60 * Yuck. bzlib.h is not const-correct, so I need this one bit
61 * of ugly hackery to convert a const * pointer to a non-const pointer.
63 #define SET_NEXT_IN(st,src) \
64 (st)->stream.next_in = (char *)(uintptr_t)(const void *)(src)
66 static int archive_compressor_bzip2_finish(struct archive_write *);
67 static int archive_compressor_bzip2_init(struct archive_write *);
68 static int archive_compressor_bzip2_write(struct archive_write *,
69 const void *, size_t);
70 static int drive_compressor(struct archive_write *, struct private_data *,
71 int finishing);
74 * Allocate, initialize and return an archive object.
76 int
77 archive_write_set_compression_bzip2(struct archive *_a)
79 struct archive_write *a = (struct archive_write *)_a;
80 __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
81 ARCHIVE_STATE_NEW, "archive_write_set_compression_bzip2");
82 a->compressor.init = &archive_compressor_bzip2_init;
83 return (ARCHIVE_OK);
87 * Setup callback.
89 static int
90 archive_compressor_bzip2_init(struct archive_write *a)
92 int ret;
93 struct private_data *state;
95 a->archive.compression_code = ARCHIVE_COMPRESSION_BZIP2;
96 a->archive.compression_name = "bzip2";
98 if (a->client_opener != NULL) {
99 ret = (a->client_opener)(&a->archive, a->client_data);
100 if (ret != 0)
101 return (ret);
104 state = (struct private_data *)malloc(sizeof(*state));
105 if (state == NULL) {
106 archive_set_error(&a->archive, ENOMEM,
107 "Can't allocate data for compression");
108 return (ARCHIVE_FATAL);
110 memset(state, 0, sizeof(*state));
112 state->compressed_buffer_size = a->bytes_per_block;
113 state->compressed = (char *)malloc(state->compressed_buffer_size);
115 if (state->compressed == NULL) {
116 archive_set_error(&a->archive, ENOMEM,
117 "Can't allocate data for compression buffer");
118 free(state);
119 return (ARCHIVE_FATAL);
122 state->stream.next_out = state->compressed;
123 state->stream.avail_out = state->compressed_buffer_size;
124 a->compressor.write = archive_compressor_bzip2_write;
125 a->compressor.finish = archive_compressor_bzip2_finish;
127 /* Initialize compression library */
128 ret = BZ2_bzCompressInit(&(state->stream), 9, 0, 30);
129 if (ret == BZ_OK) {
130 a->compressor.data = state;
131 return (ARCHIVE_OK);
134 /* Library setup failed: clean up. */
135 archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
136 "Internal error initializing compression library");
137 free(state->compressed);
138 free(state);
140 /* Override the error message if we know what really went wrong. */
141 switch (ret) {
142 case BZ_PARAM_ERROR:
143 archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
144 "Internal error initializing compression library: "
145 "invalid setup parameter");
146 break;
147 case BZ_MEM_ERROR:
148 archive_set_error(&a->archive, ENOMEM,
149 "Internal error initializing compression library: "
150 "out of memory");
151 break;
152 case BZ_CONFIG_ERROR:
153 archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
154 "Internal error initializing compression library: "
155 "mis-compiled library");
156 break;
159 return (ARCHIVE_FATAL);
164 * Write data to the compressed stream.
166 * Returns ARCHIVE_OK if all data written, error otherwise.
168 static int
169 archive_compressor_bzip2_write(struct archive_write *a, const void *buff,
170 size_t length)
172 struct private_data *state;
174 state = (struct private_data *)a->compressor.data;
175 if (a->client_writer == NULL) {
176 archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
177 "No write callback is registered? "
178 "This is probably an internal programming error.");
179 return (ARCHIVE_FATAL);
182 /* Update statistics */
183 state->total_in += length;
185 /* Compress input data to output buffer */
186 SET_NEXT_IN(state, buff);
187 state->stream.avail_in = length;
188 if (drive_compressor(a, state, 0))
189 return (ARCHIVE_FATAL);
190 a->archive.file_position += length;
191 return (ARCHIVE_OK);
196 * Finish the compression.
198 static int
199 archive_compressor_bzip2_finish(struct archive_write *a)
201 ssize_t block_length;
202 int ret;
203 struct private_data *state;
204 ssize_t target_block_length;
205 ssize_t bytes_written;
206 unsigned tocopy;
208 state = (struct private_data *)a->compressor.data;
209 ret = ARCHIVE_OK;
210 if (a->client_writer == NULL) {
211 archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
212 "No write callback is registered?\n"
213 "This is probably an internal programming error.");
214 ret = ARCHIVE_FATAL;
215 goto cleanup;
218 /* By default, always pad the uncompressed data. */
219 if (a->pad_uncompressed) {
220 tocopy = a->bytes_per_block -
221 (state->total_in % a->bytes_per_block);
222 while (tocopy > 0 && tocopy < (unsigned)a->bytes_per_block) {
223 SET_NEXT_IN(state, a->nulls);
224 state->stream.avail_in = tocopy < a->null_length ?
225 tocopy : a->null_length;
226 state->total_in += state->stream.avail_in;
227 tocopy -= state->stream.avail_in;
228 ret = drive_compressor(a, state, 0);
229 if (ret != ARCHIVE_OK)
230 goto cleanup;
234 /* Finish compression cycle. */
235 if ((ret = drive_compressor(a, state, 1)))
236 goto cleanup;
238 /* Optionally, pad the final compressed block. */
239 block_length = state->stream.next_out - state->compressed;
242 /* Tricky calculation to determine size of last block. */
243 target_block_length = block_length;
244 if (a->bytes_in_last_block <= 0)
245 /* Default or Zero: pad to full block */
246 target_block_length = a->bytes_per_block;
247 else
248 /* Round length to next multiple of bytes_in_last_block. */
249 target_block_length = a->bytes_in_last_block *
250 ( (block_length + a->bytes_in_last_block - 1) /
251 a->bytes_in_last_block);
252 if (target_block_length > a->bytes_per_block)
253 target_block_length = a->bytes_per_block;
254 if (block_length < target_block_length) {
255 memset(state->stream.next_out, 0,
256 target_block_length - block_length);
257 block_length = target_block_length;
260 /* Write the last block */
261 bytes_written = (a->client_writer)(&a->archive, a->client_data,
262 state->compressed, block_length);
264 /* TODO: Handle short write of final block. */
265 if (bytes_written <= 0)
266 ret = ARCHIVE_FATAL;
267 else {
268 a->archive.raw_position += ret;
269 ret = ARCHIVE_OK;
272 /* Cleanup: shut down compressor, release memory, etc. */
273 cleanup:
274 switch (BZ2_bzCompressEnd(&(state->stream))) {
275 case BZ_OK:
276 break;
277 default:
278 archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
279 "Failed to clean up compressor");
280 ret = ARCHIVE_FATAL;
283 free(state->compressed);
284 free(state);
285 return (ret);
289 * Utility function to push input data through compressor, writing
290 * full output blocks as necessary.
292 * Note that this handles both the regular write case (finishing ==
293 * false) and the end-of-archive case (finishing == true).
295 static int
296 drive_compressor(struct archive_write *a, struct private_data *state, int finishing)
298 ssize_t bytes_written;
299 int ret;
301 for (;;) {
302 if (state->stream.avail_out == 0) {
303 bytes_written = (a->client_writer)(&a->archive,
304 a->client_data, state->compressed,
305 state->compressed_buffer_size);
306 if (bytes_written <= 0) {
307 /* TODO: Handle this write failure */
308 return (ARCHIVE_FATAL);
309 } else if ((size_t)bytes_written < state->compressed_buffer_size) {
310 /* Short write: Move remainder to
311 * front and keep filling */
312 memmove(state->compressed,
313 state->compressed + bytes_written,
314 state->compressed_buffer_size - bytes_written);
317 a->archive.raw_position += bytes_written;
318 state->stream.next_out = state->compressed +
319 state->compressed_buffer_size - bytes_written;
320 state->stream.avail_out = bytes_written;
323 /* If there's nothing to do, we're done. */
324 if (!finishing && state->stream.avail_in == 0)
325 return (ARCHIVE_OK);
327 ret = BZ2_bzCompress(&(state->stream),
328 finishing ? BZ_FINISH : BZ_RUN);
330 switch (ret) {
331 case BZ_RUN_OK:
332 /* In non-finishing case, did compressor
333 * consume everything? */
334 if (!finishing && state->stream.avail_in == 0)
335 return (ARCHIVE_OK);
336 break;
337 case BZ_FINISH_OK: /* Finishing: There's more work to do */
338 break;
339 case BZ_STREAM_END: /* Finishing: all done */
340 /* Only occurs in finishing case */
341 return (ARCHIVE_OK);
342 default:
343 /* Any other return value indicates an error */
344 archive_set_error(&a->archive,
345 ARCHIVE_ERRNO_PROGRAMMER,
346 "Bzip2 compression failed;"
347 " BZ2_bzCompress() returned %d",
348 ret);
349 return (ARCHIVE_FATAL);
354 #endif /* HAVE_BZLIB_H */