This commit sets new users to see the DragonFly-tips fortunes instead
[dragonfly.git] / contrib / libarchive / archive_write_set_compression_gzip.c
bloba08fffca943688e458f7807d0e46ffa0616a5f76
1 /*-
2 * Copyright (c) 2003-2004 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 * 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.
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.
27 #include "archive_platform.h"
29 /* Don't compile this if we don't have zlib. */
30 #if HAVE_ZLIB_H
32 __FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_compression_gzip.c,v 1.9 2004/11/06 05:25:53 kientzle Exp $");
34 #include <errno.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <time.h>
38 #include <zlib.h>
40 #include "archive.h"
41 #include "archive_private.h"
43 struct private_data {
44 z_stream stream;
45 int64_t total_in;
46 unsigned char *compressed;
47 size_t compressed_buffer_size;
48 unsigned long crc;
53 * Yuck. zlib.h is not const-correct, so I need this one bit
54 * of ugly hackery to convert a const * pointer to a non-const pointer.
56 #define SET_NEXT_IN(st,src) \
57 (st)->stream.next_in = (void *)(uintptr_t)(const void *)(src)
59 static int archive_compressor_gzip_finish(struct archive *);
60 static int archive_compressor_gzip_init(struct archive *);
61 static int archive_compressor_gzip_write(struct archive *, const void *,
62 size_t);
63 static int drive_compressor(struct archive *, struct private_data *,
64 int finishing);
68 * Allocate, initialize and return a archive object.
70 int
71 archive_write_set_compression_gzip(struct archive *a)
73 archive_check_magic(a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW);
74 a->compression_init = &archive_compressor_gzip_init;
75 a->compression_code = ARCHIVE_COMPRESSION_GZIP;
76 a->compression_name = "gzip";
77 return (ARCHIVE_OK);
81 * Setup callback.
83 static int
84 archive_compressor_gzip_init(struct archive *a)
86 int ret;
87 struct private_data *state;
88 time_t t;
90 a->compression_code = ARCHIVE_COMPRESSION_GZIP;
91 a->compression_name = "gzip";
93 if (a->client_opener != NULL) {
94 ret = (a->client_opener)(a, a->client_data);
95 if (ret != ARCHIVE_OK)
96 return (ret);
99 state = (struct private_data *)malloc(sizeof(*state));
100 if (state == NULL) {
101 archive_set_error(a, ENOMEM,
102 "Can't allocate data for compression");
103 return (ARCHIVE_FATAL);
105 memset(state, 0, sizeof(*state));
107 state->compressed_buffer_size = a->bytes_per_block;
108 state->compressed = malloc(state->compressed_buffer_size);
109 state->crc = crc32(0L, NULL, 0);
111 if (state->compressed == NULL) {
112 archive_set_error(a, ENOMEM,
113 "Can't allocate data for compression buffer");
114 free(state);
115 return (ARCHIVE_FATAL);
118 state->stream.next_out = state->compressed;
119 state->stream.avail_out = state->compressed_buffer_size;
121 /* Prime output buffer with a gzip header. */
122 t = time(NULL);
123 state->compressed[0] = 0x1f; /* GZip signature bytes */
124 state->compressed[1] = 0x8b;
125 state->compressed[2] = 0x08; /* "Deflate" compression */
126 state->compressed[3] = 0; /* No options */
127 state->compressed[4] = (t)&0xff; /* Timestamp */
128 state->compressed[5] = (t>>8)&0xff;
129 state->compressed[6] = (t>>16)&0xff;
130 state->compressed[7] = (t>>24)&0xff;
131 state->compressed[8] = 0; /* No deflate options */
132 state->compressed[9] = 3; /* OS=Unix */
133 state->stream.next_out += 10;
134 state->stream.avail_out -= 10;
136 a->compression_write = archive_compressor_gzip_write;
137 a->compression_finish = archive_compressor_gzip_finish;
139 /* Initialize compression library. */
140 ret = deflateInit2(&(state->stream),
141 Z_DEFAULT_COMPRESSION,
142 Z_DEFLATED,
143 -15 /* < 0 to suppress zlib header */,
145 Z_DEFAULT_STRATEGY);
147 if (ret == Z_OK) {
148 a->compression_data = state;
149 return (0);
152 /* Library setup failed: clean up. */
153 archive_set_error(a, ARCHIVE_ERRNO_MISC, "Internal error "
154 "initializing compression library");
155 free(state->compressed);
156 free(state);
158 /* Override the error message if we know what really went wrong. */
159 switch (ret) {
160 case Z_STREAM_ERROR:
161 archive_set_error(a, ARCHIVE_ERRNO_MISC,
162 "Internal error initializing "
163 "compression library: invalid setup parameter");
164 break;
165 case Z_MEM_ERROR:
166 archive_set_error(a, ENOMEM, "Internal error initializing "
167 "compression library");
168 break;
169 case Z_VERSION_ERROR:
170 archive_set_error(a, ARCHIVE_ERRNO_MISC,
171 "Internal error initializing "
172 "compression library: invalid library version");
173 break;
176 return (ARCHIVE_FATAL);
180 * Write data to the compressed stream.
182 static int
183 archive_compressor_gzip_write(struct archive *a, const void *buff,
184 size_t length)
186 struct private_data *state;
187 int ret;
189 state = a->compression_data;
190 if (a->client_writer == NULL) {
191 archive_set_error(a, ARCHIVE_ERRNO_PROGRAMMER,
192 "No write callback is registered? "
193 "This is probably an internal programming error.");
194 return (ARCHIVE_FATAL);
197 /* Update statistics */
198 state->crc = crc32(state->crc, buff, length);
199 state->total_in += length;
201 /* Compress input data to output buffer */
202 SET_NEXT_IN(state, buff);
203 state->stream.avail_in = length;
204 if ((ret = drive_compressor(a, state, 0)) != ARCHIVE_OK)
205 return (ret);
207 a->file_position += length;
208 return (ARCHIVE_OK);
213 * Finish the compression...
215 static int
216 archive_compressor_gzip_finish(struct archive *a)
218 ssize_t block_length, target_block_length, bytes_written;
219 int ret;
220 struct private_data *state;
221 unsigned tocopy;
222 unsigned char trailer[8];
224 state = a->compression_data;
225 ret = 0;
226 if (a->client_writer == NULL) {
227 archive_set_error(a, ARCHIVE_ERRNO_PROGRAMMER,
228 "No write callback is registered? "
229 "This is probably an internal programming error.");
230 ret = ARCHIVE_FATAL;
231 goto cleanup;
234 /* By default, always pad the uncompressed data. */
235 if (a->pad_uncompressed) {
236 tocopy = a->bytes_per_block -
237 (state->total_in % a->bytes_per_block);
238 while (tocopy > 0 && tocopy < (unsigned)a->bytes_per_block) {
239 SET_NEXT_IN(state, a->nulls);
240 state->stream.avail_in = tocopy < a->null_length ?
241 tocopy : a->null_length;
242 state->crc = crc32(state->crc, a->nulls,
243 state->stream.avail_in);
244 state->total_in += state->stream.avail_in;
245 tocopy -= state->stream.avail_in;
246 ret = drive_compressor(a, state, 0);
247 if (ret != ARCHIVE_OK)
248 goto cleanup;
252 /* Finish compression cycle */
253 if (((ret = drive_compressor(a, state, 1))) != ARCHIVE_OK)
254 goto cleanup;
256 /* Build trailer: 4-byte CRC and 4-byte length. */
257 trailer[0] = (state->crc)&0xff;
258 trailer[1] = (state->crc >> 8)&0xff;
259 trailer[2] = (state->crc >> 16)&0xff;
260 trailer[3] = (state->crc >> 24)&0xff;
261 trailer[4] = (state->total_in)&0xff;
262 trailer[5] = (state->total_in >> 8)&0xff;
263 trailer[6] = (state->total_in >> 16)&0xff;
264 trailer[7] = (state->total_in >> 24)&0xff;
266 /* Add trailer to current block. */
267 tocopy = 8;
268 if (tocopy > state->stream.avail_out)
269 tocopy = state->stream.avail_out;
270 memcpy(state->stream.next_out, trailer, tocopy);
271 state->stream.next_out += tocopy;
272 state->stream.avail_out -= tocopy;
274 /* If it overflowed, flush and start a new block. */
275 if (tocopy < 8) {
276 bytes_written = (a->client_writer)(a, a->client_data,
277 state->compressed, state->compressed_buffer_size);
278 if (bytes_written <= 0) {
279 ret = ARCHIVE_FATAL;
280 goto cleanup;
282 a->raw_position += bytes_written;
283 state->stream.next_out = state->compressed;
284 state->stream.avail_out = state->compressed_buffer_size;
285 memcpy(state->stream.next_out, trailer + tocopy, 8-tocopy);
286 state->stream.next_out += 8-tocopy;
287 state->stream.avail_out -= 8-tocopy;
290 /* Optionally, pad the final compressed block. */
291 block_length = state->stream.next_out - state->compressed;
294 /* Tricky calculation to determine size of last block. */
295 target_block_length = block_length;
296 if (a->bytes_in_last_block <= 0)
297 /* Default or Zero: pad to full block */
298 target_block_length = a->bytes_per_block;
299 else
300 /* Round length to next multiple of bytes_in_last_block. */
301 target_block_length = a->bytes_in_last_block *
302 ( (block_length + a->bytes_in_last_block - 1) /
303 a->bytes_in_last_block);
304 if (target_block_length > a->bytes_per_block)
305 target_block_length = a->bytes_per_block;
306 if (block_length < target_block_length) {
307 memset(state->stream.next_out, 0,
308 target_block_length - block_length);
309 block_length = target_block_length;
312 /* Write the last block */
313 bytes_written = (a->client_writer)(a, a->client_data,
314 state->compressed, block_length);
315 if (bytes_written <= 0) {
316 ret = ARCHIVE_FATAL;
317 goto cleanup;
319 a->raw_position += bytes_written;
321 /* Cleanup: shut down compressor, release memory, etc. */
322 cleanup:
323 switch (deflateEnd(&(state->stream))) {
324 case Z_OK:
325 break;
326 default:
327 archive_set_error(a, ARCHIVE_ERRNO_MISC,
328 "Failed to clean up compressor");
329 ret = ARCHIVE_FATAL;
331 free(state->compressed);
332 free(state);
334 /* Close the output */
335 if (a->client_closer != NULL)
336 (a->client_closer)(a, a->client_data);
338 return (ret);
342 * Utility function to push input data through compressor,
343 * writing full output blocks as necessary.
345 * Note that this handles both the regular write case (finishing ==
346 * false) and the end-of-archive case (finishing == true).
348 static int
349 drive_compressor(struct archive *a, struct private_data *state, int finishing)
351 ssize_t bytes_written;
352 int ret;
354 for (;;) {
355 if (state->stream.avail_out == 0) {
356 bytes_written = (a->client_writer)(a, a->client_data,
357 state->compressed, state->compressed_buffer_size);
358 if (bytes_written <= 0) {
359 /* TODO: Handle this write failure */
360 return (ARCHIVE_FATAL);
361 } else if ((size_t)bytes_written < state->compressed_buffer_size) {
362 /* Short write: Move remaining to
363 * front of block and keep filling */
364 memmove(state->compressed,
365 state->compressed + bytes_written,
366 state->compressed_buffer_size - bytes_written);
368 a->raw_position += bytes_written;
369 state->stream.next_out
370 = state->compressed +
371 state->compressed_buffer_size - bytes_written;
372 state->stream.avail_out = bytes_written;
375 ret = deflate(&(state->stream),
376 finishing ? Z_FINISH : Z_NO_FLUSH );
378 switch (ret) {
379 case Z_OK:
380 /* In non-finishing case, check if compressor
381 * consumed everything */
382 if (!finishing && state->stream.avail_in == 0)
383 return (ARCHIVE_OK);
384 /* In finishing case, this return always means
385 * there's more work */
386 break;
387 case Z_STREAM_END:
388 /* This return can only occur in finishing case. */
389 return (ARCHIVE_OK);
390 default:
391 /* Any other return value indicates an error. */
392 archive_set_error(a, ARCHIVE_ERRNO_MISC,
393 "GZip compression failed");
394 return (ARCHIVE_FATAL);
399 #endif /* HAVE_ZLIB_H */