Merge from libarchive.googlecode.com: Numerous fixes to the
write options handling, including documentation.
This commit is contained in:
parent
625683944d
commit
8952729381
@ -43,6 +43,9 @@
|
||||
.Nm archive_write_set_compression_gzip ,
|
||||
.Nm archive_write_set_compression_none ,
|
||||
.Nm archive_write_set_compression_program ,
|
||||
.Nm archive_write_set_compressor_options ,
|
||||
.Nm archive_write_set_format_options ,
|
||||
.Nm archive_write_set_options ,
|
||||
.Nm archive_write_open ,
|
||||
.Nm archive_write_open_fd ,
|
||||
.Nm archive_write_open_FILE ,
|
||||
@ -73,10 +76,7 @@
|
||||
.Ft int
|
||||
.Fn archive_write_set_compression_none "struct archive *"
|
||||
.Ft int
|
||||
.Fo archive_write_set_compression_program
|
||||
.Fa "struct archive *"
|
||||
.Fa "const char * cmd"
|
||||
.Fc
|
||||
.Fn archive_write_set_compression_program "struct archive *" "const char * cmd"
|
||||
.Ft int
|
||||
.Fn archive_write_set_format_cpio "struct archive *"
|
||||
.Ft int
|
||||
@ -90,6 +90,12 @@
|
||||
.Ft int
|
||||
.Fn archive_write_set_format_ustar "struct archive *"
|
||||
.Ft int
|
||||
.Fn archive_write_set_format_options "struct archive *" "const char *"
|
||||
.Ft int
|
||||
.Fn archive_write_set_compressor_options "struct archive *" "const char *"
|
||||
.Ft int
|
||||
.Fn archive_write_set_options "struct archive *" "const char *"
|
||||
.Ft int
|
||||
.Fo archive_write_open
|
||||
.Fa "struct archive *"
|
||||
.Fa "void *client_data"
|
||||
@ -210,6 +216,68 @@ Note that the compressed output is always properly blocked.
|
||||
The archive will be fed into the specified compression program.
|
||||
The output of that program is blocked and written to the client
|
||||
write callbacks.
|
||||
.It Xo
|
||||
.Fn archive_write_set_compressor_options ,
|
||||
.Fn archive_write_set_format_options ,
|
||||
.Fn archive_write_set_options
|
||||
.Xc
|
||||
Specifies options that will be passed to the currently-enabled
|
||||
compressor and/or format writer.
|
||||
The argument is a comma-separated list of individual options.
|
||||
Individual options have one of the following forms:
|
||||
.Bl -tag -compact -width indent
|
||||
.It Ar option=value
|
||||
The option/value pair will be provided to every module.
|
||||
Modules that do not accept an option with this name will ignore it.
|
||||
.It Ar option
|
||||
The option will be provided to every module with a value of
|
||||
.Dq 1 .
|
||||
.It Ar !option
|
||||
The option will be provided to every module with a NULL value.
|
||||
.It Ar module:option=value , Ar module:option , Ar module:!option
|
||||
As above, but the corresponding option and value will be provided
|
||||
only to modules whose name matches
|
||||
.Ar module .
|
||||
.El
|
||||
The return value will be
|
||||
.Cm ARCHIVE_OK
|
||||
if any module accepts the option, or
|
||||
.Cm ARCHIVE_WARN
|
||||
if no module accepted the option, or
|
||||
.Cm ARCHIVE_FATAL
|
||||
if there was a fatal error while attempting to process the option.
|
||||
.Pp
|
||||
The currently supported options are:
|
||||
.Bl -tag -compact -width indent
|
||||
.It Compressor gzip
|
||||
.Bl -tag -compact -width indent
|
||||
.It Cm compression-level
|
||||
The value is interpreted as a decimal integer specifying the
|
||||
gzip compression level.
|
||||
.El
|
||||
.It Compressor xz
|
||||
.Bl -tag -compact -width indent
|
||||
.It Cm compression-level
|
||||
The value is interpreted as a decimal integer specifying the
|
||||
compression level.
|
||||
.El
|
||||
.It Format mtree
|
||||
.Bl -tag -compact -width indent
|
||||
.It Cm cksum , Cm device , Cm flags , Cm gid , Cm gname , Cm indent , Cm link , Cm md5 , Cm mode , Cm nlink , Cm rmd160 , Cm sha1 , Cm sha256 , Cm sha384 , Cm sha512 , Cm size , Cm time , Cm uid , Cm uname
|
||||
Enable a particular keyword in the mtree output.
|
||||
Prefix with an exclamation mark to disable the corresponding keyword.
|
||||
The default is equivalent to
|
||||
.Dq device, flags, gid, gname, link, mode, nlink, size, time, type, uid, uname .
|
||||
.It Cm all
|
||||
Enables all of the above keywords.
|
||||
.It Cm use-set
|
||||
Enables generation of
|
||||
.Cm /set
|
||||
lines that specify default values for the following files and/or directories.
|
||||
.It Cm indent
|
||||
XXX needs explanation XXX
|
||||
.El
|
||||
.El
|
||||
.It Fn archive_write_open
|
||||
Freeze the settings, open the archive, and prepare for writing entries.
|
||||
This is the most generic form of this function, which accepts
|
||||
|
@ -132,8 +132,14 @@ archive_write_set_format_options(struct archive *_a, const char *s)
|
||||
{
|
||||
struct archive_write *a = (struct archive_write *)_a;
|
||||
char key[64], val[64];
|
||||
int len, r;
|
||||
int len, r, ret = ARCHIVE_OK;
|
||||
|
||||
__archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
|
||||
ARCHIVE_STATE_NEW, "archive_write_set_format_options");
|
||||
archive_clear_error(&a->archive);
|
||||
|
||||
if (s == NULL || *s == '\0')
|
||||
return (ARCHIVE_OK);
|
||||
if (a->format_options == NULL)
|
||||
/* This format does not support option. */
|
||||
return (ARCHIVE_OK);
|
||||
@ -146,14 +152,19 @@ archive_write_set_format_options(struct archive *_a, const char *s)
|
||||
r = a->format_options(a, key, val);
|
||||
if (r == ARCHIVE_FATAL)
|
||||
return (r);
|
||||
if (r < ARCHIVE_OK) { /* This key was not handled. */
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
|
||||
"Unsupported option ``%s''", key);
|
||||
ret = ARCHIVE_WARN;
|
||||
}
|
||||
s += len;
|
||||
}
|
||||
if (len < 0) {
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
|
||||
"Illegal format options.");
|
||||
"Malformed options string.");
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
return (ARCHIVE_OK);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -165,10 +176,20 @@ archive_write_set_compressor_options(struct archive *_a, const char *s)
|
||||
struct archive_write *a = (struct archive_write *)_a;
|
||||
char key[64], val[64];
|
||||
int len, r;
|
||||
int ret = ARCHIVE_OK;
|
||||
|
||||
if (a->compressor.options == NULL)
|
||||
/* This compressor does not support option. */
|
||||
__archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
|
||||
ARCHIVE_STATE_NEW, "archive_write_set_compressor_options");
|
||||
archive_clear_error(&a->archive);
|
||||
|
||||
if (s == NULL || *s == '\0')
|
||||
return (ARCHIVE_OK);
|
||||
if (a->compressor.options == NULL) {
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
|
||||
"Unsupported option ``%s''", s);
|
||||
/* This compressor does not support option. */
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
|
||||
while ((len = __archive_parse_options(s, a->archive.compression_name,
|
||||
sizeof(key), key, sizeof(val), val)) > 0) {
|
||||
@ -178,6 +199,11 @@ archive_write_set_compressor_options(struct archive *_a, const char *s)
|
||||
r = a->compressor.options(a, key, val);
|
||||
if (r == ARCHIVE_FATAL)
|
||||
return (r);
|
||||
if (r < ARCHIVE_OK) {
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
|
||||
"Unsupported option ``%s''", key);
|
||||
ret = ARCHIVE_WARN;
|
||||
}
|
||||
s += len;
|
||||
}
|
||||
if (len < 0) {
|
||||
@ -185,7 +211,7 @@ archive_write_set_compressor_options(struct archive *_a, const char *s)
|
||||
"Illegal format options.");
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
return (ARCHIVE_OK);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -194,14 +220,16 @@ archive_write_set_compressor_options(struct archive *_a, const char *s)
|
||||
int
|
||||
archive_write_set_options(struct archive *_a, const char *s)
|
||||
{
|
||||
int r;
|
||||
int r1, r2;
|
||||
|
||||
r = archive_write_set_format_options(_a, s);
|
||||
if (r != ARCHIVE_OK)
|
||||
return (r);
|
||||
r = archive_write_set_compressor_options(_a, s);
|
||||
if (r != ARCHIVE_OK)
|
||||
return (r);
|
||||
r1 = archive_write_set_format_options(_a, s);
|
||||
if (r1 < ARCHIVE_WARN)
|
||||
return (r1);
|
||||
r2 = archive_write_set_compressor_options(_a, s);
|
||||
if (r2 < ARCHIVE_WARN)
|
||||
return (r2);
|
||||
if (r1 == ARCHIVE_WARN && r2 == ARCHIVE_WARN)
|
||||
return (ARCHIVE_WARN);
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
|
@ -47,9 +47,10 @@ __FBSDID("$FreeBSD$");
|
||||
|
||||
#ifndef HAVE_BZLIB_H
|
||||
int
|
||||
archive_write_set_compression_bzip2(struct archive *_a)
|
||||
archive_write_set_compression_bzip2(struct archive *a)
|
||||
{
|
||||
/* Unsupported bzip2 compression, we don't have bzlib */
|
||||
archive_set_error(a, ARCHIVE_ERRNO_MISC,
|
||||
"bzip2 compression not supported on this platform");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
#else
|
||||
@ -62,6 +63,9 @@ struct private_data {
|
||||
size_t compressed_buffer_size;
|
||||
};
|
||||
|
||||
struct private_config {
|
||||
int compression_level;
|
||||
};
|
||||
|
||||
/*
|
||||
* Yuck. bzlib.h is not const-correct, so I need this one bit
|
||||
@ -72,6 +76,8 @@ struct private_data {
|
||||
|
||||
static int archive_compressor_bzip2_finish(struct archive_write *);
|
||||
static int archive_compressor_bzip2_init(struct archive_write *);
|
||||
static int archive_compressor_bzip2_options(struct archive_write *,
|
||||
const char *, const char *);
|
||||
static int archive_compressor_bzip2_write(struct archive_write *,
|
||||
const void *, size_t);
|
||||
static int drive_compressor(struct archive_write *, struct private_data *,
|
||||
@ -84,9 +90,21 @@ int
|
||||
archive_write_set_compression_bzip2(struct archive *_a)
|
||||
{
|
||||
struct archive_write *a = (struct archive_write *)_a;
|
||||
struct private_config *config;
|
||||
__archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
|
||||
ARCHIVE_STATE_NEW, "archive_write_set_compression_bzip2");
|
||||
config = malloc(sizeof(*config));
|
||||
if (config == NULL) {
|
||||
archive_set_error(&a->archive, ENOMEM, "Out of memory");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
a->compressor.config = config;
|
||||
a->compressor.finish = archive_compressor_bzip2_finish;
|
||||
config->compression_level = 9; /* default */
|
||||
a->compressor.init = &archive_compressor_bzip2_init;
|
||||
a->compressor.options = &archive_compressor_bzip2_options;
|
||||
a->archive.compression_code = ARCHIVE_COMPRESSION_BZIP2;
|
||||
a->archive.compression_name = "bzip2";
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
@ -98,10 +116,9 @@ archive_compressor_bzip2_init(struct archive_write *a)
|
||||
{
|
||||
int ret;
|
||||
struct private_data *state;
|
||||
struct private_config *config;
|
||||
|
||||
a->archive.compression_code = ARCHIVE_COMPRESSION_BZIP2;
|
||||
a->archive.compression_name = "bzip2";
|
||||
|
||||
config = (struct private_config *)a->compressor.config;
|
||||
if (a->client_opener != NULL) {
|
||||
ret = (a->client_opener)(&a->archive, a->client_data);
|
||||
if (ret != 0)
|
||||
@ -129,10 +146,10 @@ archive_compressor_bzip2_init(struct archive_write *a)
|
||||
state->stream.next_out = state->compressed;
|
||||
state->stream.avail_out = state->compressed_buffer_size;
|
||||
a->compressor.write = archive_compressor_bzip2_write;
|
||||
a->compressor.finish = archive_compressor_bzip2_finish;
|
||||
|
||||
/* Initialize compression library */
|
||||
ret = BZ2_bzCompressInit(&(state->stream), 9, 0, 30);
|
||||
ret = BZ2_bzCompressInit(&(state->stream),
|
||||
config->compression_level, 0, 30);
|
||||
if (ret == BZ_OK) {
|
||||
a->compressor.data = state;
|
||||
return (ARCHIVE_OK);
|
||||
@ -167,6 +184,32 @@ archive_compressor_bzip2_init(struct archive_write *a)
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Set write options.
|
||||
*/
|
||||
static int
|
||||
archive_compressor_bzip2_options(struct archive_write *a, const char *key,
|
||||
const char *value)
|
||||
{
|
||||
struct private_config *config;
|
||||
|
||||
config = (struct private_config *)a->compressor.config;
|
||||
if (strcmp(key, "compression-level") == 0) {
|
||||
if (value == NULL || !(value[0] >= '0' && value[0] <= '9') ||
|
||||
value[1] != '\0')
|
||||
return (ARCHIVE_WARN);
|
||||
config->compression_level = value[0] - '0';
|
||||
/* Make '0' be a synonym for '1'. */
|
||||
/* This way, bzip2 compressor supports the same 0..9
|
||||
* range of levels as gzip. */
|
||||
if (config->compression_level < 1)
|
||||
config->compression_level = 1;
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write data to the compressed stream.
|
||||
*
|
||||
@ -212,83 +255,88 @@ archive_compressor_bzip2_finish(struct archive_write *a)
|
||||
ssize_t bytes_written;
|
||||
unsigned tocopy;
|
||||
|
||||
state = (struct private_data *)a->compressor.data;
|
||||
ret = ARCHIVE_OK;
|
||||
if (a->client_writer == NULL) {
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
|
||||
"No write callback is registered?\n"
|
||||
"This is probably an internal programming error.");
|
||||
ret = ARCHIVE_FATAL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* By default, always pad the uncompressed data. */
|
||||
if (a->pad_uncompressed) {
|
||||
tocopy = a->bytes_per_block -
|
||||
(state->total_in % a->bytes_per_block);
|
||||
while (tocopy > 0 && tocopy < (unsigned)a->bytes_per_block) {
|
||||
SET_NEXT_IN(state, a->nulls);
|
||||
state->stream.avail_in = tocopy < a->null_length ?
|
||||
tocopy : a->null_length;
|
||||
state->total_in += state->stream.avail_in;
|
||||
tocopy -= state->stream.avail_in;
|
||||
ret = drive_compressor(a, state, 0);
|
||||
if (ret != ARCHIVE_OK)
|
||||
goto cleanup;
|
||||
state = (struct private_data *)a->compressor.data;
|
||||
if (state != NULL) {
|
||||
if (a->client_writer == NULL) {
|
||||
archive_set_error(&a->archive,
|
||||
ARCHIVE_ERRNO_PROGRAMMER,
|
||||
"No write callback is registered?\n"
|
||||
"This is probably an internal programming error.");
|
||||
ret = ARCHIVE_FATAL;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
/* Finish compression cycle. */
|
||||
if ((ret = drive_compressor(a, state, 1)))
|
||||
goto cleanup;
|
||||
/* By default, always pad the uncompressed data. */
|
||||
if (a->pad_uncompressed) {
|
||||
tocopy = a->bytes_per_block -
|
||||
(state->total_in % a->bytes_per_block);
|
||||
while (tocopy > 0 && tocopy < (unsigned)a->bytes_per_block) {
|
||||
SET_NEXT_IN(state, a->nulls);
|
||||
state->stream.avail_in = tocopy < a->null_length ?
|
||||
tocopy : a->null_length;
|
||||
state->total_in += state->stream.avail_in;
|
||||
tocopy -= state->stream.avail_in;
|
||||
ret = drive_compressor(a, state, 0);
|
||||
if (ret != ARCHIVE_OK)
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
/* Optionally, pad the final compressed block. */
|
||||
block_length = state->stream.next_out - state->compressed;
|
||||
/* Finish compression cycle. */
|
||||
if ((ret = drive_compressor(a, state, 1)))
|
||||
goto cleanup;
|
||||
|
||||
/* Optionally, pad the final compressed block. */
|
||||
block_length = state->stream.next_out - state->compressed;
|
||||
|
||||
/* Tricky calculation to determine size of last block. */
|
||||
target_block_length = block_length;
|
||||
if (a->bytes_in_last_block <= 0)
|
||||
/* Default or Zero: pad to full block */
|
||||
target_block_length = a->bytes_per_block;
|
||||
else
|
||||
/* Round length to next multiple of bytes_in_last_block. */
|
||||
target_block_length = a->bytes_in_last_block *
|
||||
( (block_length + a->bytes_in_last_block - 1) /
|
||||
a->bytes_in_last_block);
|
||||
if (target_block_length > a->bytes_per_block)
|
||||
target_block_length = a->bytes_per_block;
|
||||
if (block_length < target_block_length) {
|
||||
memset(state->stream.next_out, 0,
|
||||
target_block_length - block_length);
|
||||
block_length = target_block_length;
|
||||
}
|
||||
/* Tricky calculation to determine size of last block. */
|
||||
target_block_length = block_length;
|
||||
if (a->bytes_in_last_block <= 0)
|
||||
/* Default or Zero: pad to full block */
|
||||
target_block_length = a->bytes_per_block;
|
||||
else
|
||||
/* Round length to next multiple of bytes_in_last_block. */
|
||||
target_block_length = a->bytes_in_last_block *
|
||||
( (block_length + a->bytes_in_last_block - 1) /
|
||||
a->bytes_in_last_block);
|
||||
if (target_block_length > a->bytes_per_block)
|
||||
target_block_length = a->bytes_per_block;
|
||||
if (block_length < target_block_length) {
|
||||
memset(state->stream.next_out, 0,
|
||||
target_block_length - block_length);
|
||||
block_length = target_block_length;
|
||||
}
|
||||
|
||||
/* Write the last block */
|
||||
bytes_written = (a->client_writer)(&a->archive, a->client_data,
|
||||
state->compressed, block_length);
|
||||
/* Write the last block */
|
||||
bytes_written = (a->client_writer)(&a->archive, a->client_data,
|
||||
state->compressed, block_length);
|
||||
|
||||
/* TODO: Handle short write of final block. */
|
||||
if (bytes_written <= 0)
|
||||
ret = ARCHIVE_FATAL;
|
||||
else {
|
||||
a->archive.raw_position += ret;
|
||||
ret = ARCHIVE_OK;
|
||||
}
|
||||
/* TODO: Handle short write of final block. */
|
||||
if (bytes_written <= 0)
|
||||
ret = ARCHIVE_FATAL;
|
||||
else {
|
||||
a->archive.raw_position += ret;
|
||||
ret = ARCHIVE_OK;
|
||||
}
|
||||
|
||||
/* Cleanup: shut down compressor, release memory, etc. */
|
||||
/* Cleanup: shut down compressor, release memory, etc. */
|
||||
cleanup:
|
||||
switch (BZ2_bzCompressEnd(&(state->stream))) {
|
||||
case BZ_OK:
|
||||
break;
|
||||
default:
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
|
||||
"Failed to clean up compressor");
|
||||
ret = ARCHIVE_FATAL;
|
||||
}
|
||||
switch (BZ2_bzCompressEnd(&(state->stream))) {
|
||||
case BZ_OK:
|
||||
break;
|
||||
default:
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
|
||||
"Failed to clean up compressor");
|
||||
ret = ARCHIVE_FATAL;
|
||||
}
|
||||
|
||||
free(state->compressed);
|
||||
free(state);
|
||||
free(state->compressed);
|
||||
free(state);
|
||||
}
|
||||
/* Free configuration data even if we were never fully initialized. */
|
||||
free(a->compressor.config);
|
||||
a->compressor.config = NULL;
|
||||
return (ret);
|
||||
}
|
||||
|
||||
|
@ -47,9 +47,10 @@ __FBSDID("$FreeBSD$");
|
||||
|
||||
#ifndef HAVE_ZLIB_H
|
||||
int
|
||||
archive_write_set_compression_gzip(struct archive *_a)
|
||||
archive_write_set_compression_gzip(struct archive *a)
|
||||
{
|
||||
/* Unsupported gzip compression, we don't have zlib */
|
||||
archive_set_error(a, ARCHIVE_ERRNO_MISC,
|
||||
"gzip compression not supported on this platform");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
#else
|
||||
@ -61,7 +62,9 @@ struct private_data {
|
||||
unsigned char *compressed;
|
||||
size_t compressed_buffer_size;
|
||||
unsigned long crc;
|
||||
/* Options */
|
||||
};
|
||||
|
||||
struct private_config {
|
||||
int compression_level;
|
||||
};
|
||||
|
||||
@ -90,9 +93,19 @@ int
|
||||
archive_write_set_compression_gzip(struct archive *_a)
|
||||
{
|
||||
struct archive_write *a = (struct archive_write *)_a;
|
||||
struct private_config *config;
|
||||
__archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
|
||||
ARCHIVE_STATE_NEW, "archive_write_set_compression_gzip");
|
||||
config = malloc(sizeof(*config));
|
||||
if (config == NULL) {
|
||||
archive_set_error(&a->archive, ENOMEM, "Out of memory");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
a->compressor.config = config;
|
||||
a->compressor.finish = &archive_compressor_gzip_finish;
|
||||
config->compression_level = Z_DEFAULT_COMPRESSION;
|
||||
a->compressor.init = &archive_compressor_gzip_init;
|
||||
a->compressor.options = &archive_compressor_gzip_options;
|
||||
a->archive.compression_code = ARCHIVE_COMPRESSION_GZIP;
|
||||
a->archive.compression_name = "gzip";
|
||||
return (ARCHIVE_OK);
|
||||
@ -106,10 +119,10 @@ archive_compressor_gzip_init(struct archive_write *a)
|
||||
{
|
||||
int ret;
|
||||
struct private_data *state;
|
||||
struct private_config *config;
|
||||
time_t t;
|
||||
|
||||
a->archive.compression_code = ARCHIVE_COMPRESSION_GZIP;
|
||||
a->archive.compression_name = "gzip";
|
||||
config = (struct private_config *)a->compressor.config;
|
||||
|
||||
if (a->client_opener != NULL) {
|
||||
ret = (a->client_opener)(&a->archive, a->client_data);
|
||||
@ -147,7 +160,6 @@ archive_compressor_gzip_init(struct archive_write *a)
|
||||
state->compressed_buffer_size = a->bytes_per_block;
|
||||
state->compressed = (unsigned char *)malloc(state->compressed_buffer_size);
|
||||
state->crc = crc32(0L, NULL, 0);
|
||||
state->compression_level = Z_DEFAULT_COMPRESSION;
|
||||
|
||||
if (state->compressed == NULL) {
|
||||
archive_set_error(&a->archive, ENOMEM,
|
||||
@ -174,13 +186,11 @@ archive_compressor_gzip_init(struct archive_write *a)
|
||||
state->stream.next_out += 10;
|
||||
state->stream.avail_out -= 10;
|
||||
|
||||
a->compressor.options = archive_compressor_gzip_options;
|
||||
a->compressor.write = archive_compressor_gzip_write;
|
||||
a->compressor.finish = archive_compressor_gzip_finish;
|
||||
|
||||
/* Initialize compression library. */
|
||||
ret = deflateInit2(&(state->stream),
|
||||
state->compression_level,
|
||||
config->compression_level,
|
||||
Z_DEFLATED,
|
||||
-15 /* < 0 to suppress zlib header */,
|
||||
8,
|
||||
@ -225,45 +235,15 @@ static int
|
||||
archive_compressor_gzip_options(struct archive_write *a, const char *key,
|
||||
const char *value)
|
||||
{
|
||||
struct private_data *state;
|
||||
int ret;
|
||||
struct private_config *config;
|
||||
|
||||
state = (struct private_data *)a->compressor.data;
|
||||
config = (struct private_config *)a->compressor.config;
|
||||
if (strcmp(key, "compression-level") == 0) {
|
||||
int level;
|
||||
|
||||
if (value == NULL || !(value[0] >= '0' && value[0] <= '9') ||
|
||||
value[1] != '\0')
|
||||
return (ARCHIVE_WARN);
|
||||
level = value[0] - '0';
|
||||
if (level == state->compression_level)
|
||||
return (ARCHIVE_OK);
|
||||
|
||||
ret = deflateParams(&(state->stream), level,
|
||||
Z_DEFAULT_STRATEGY);
|
||||
if (ret == Z_OK) {
|
||||
state->compression_level = level;
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
switch (ret) {
|
||||
case Z_STREAM_ERROR:
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
|
||||
"Internal error updating params "
|
||||
"compression library: state was inconsistent "
|
||||
"or parameter was invalid");
|
||||
break;
|
||||
case Z_BUF_ERROR:
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
|
||||
"Internal error updating params "
|
||||
"compression library: out buffer was zero");
|
||||
break;
|
||||
default:
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
|
||||
"Internal error updatng params "
|
||||
"compression library");
|
||||
break;
|
||||
}
|
||||
return (ARCHIVE_FATAL);
|
||||
config->compression_level = value[0] - '0';
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
return (ARCHIVE_WARN);
|
||||
@ -301,7 +281,6 @@ archive_compressor_gzip_write(struct archive_write *a, const void *buff,
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Finish the compression...
|
||||
*/
|
||||
@ -316,113 +295,118 @@ archive_compressor_gzip_finish(struct archive_write *a)
|
||||
|
||||
state = (struct private_data *)a->compressor.data;
|
||||
ret = 0;
|
||||
if (a->client_writer == NULL) {
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
|
||||
"No write callback is registered? "
|
||||
"This is probably an internal programming error.");
|
||||
ret = ARCHIVE_FATAL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* By default, always pad the uncompressed data. */
|
||||
if (a->pad_uncompressed) {
|
||||
tocopy = a->bytes_per_block -
|
||||
(state->total_in % a->bytes_per_block);
|
||||
while (tocopy > 0 && tocopy < (unsigned)a->bytes_per_block) {
|
||||
SET_NEXT_IN(state, a->nulls);
|
||||
state->stream.avail_in = tocopy < a->null_length ?
|
||||
tocopy : a->null_length;
|
||||
state->crc = crc32(state->crc, a->nulls,
|
||||
state->stream.avail_in);
|
||||
state->total_in += state->stream.avail_in;
|
||||
tocopy -= state->stream.avail_in;
|
||||
ret = drive_compressor(a, state, 0);
|
||||
if (ret != ARCHIVE_OK)
|
||||
goto cleanup;
|
||||
if (state != NULL) {
|
||||
if (a->client_writer == NULL) {
|
||||
archive_set_error(&a->archive,
|
||||
ARCHIVE_ERRNO_PROGRAMMER,
|
||||
"No write callback is registered? "
|
||||
"This is probably an internal programming error.");
|
||||
ret = ARCHIVE_FATAL;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
/* Finish compression cycle */
|
||||
if (((ret = drive_compressor(a, state, 1))) != ARCHIVE_OK)
|
||||
goto cleanup;
|
||||
/* By default, always pad the uncompressed data. */
|
||||
if (a->pad_uncompressed) {
|
||||
tocopy = a->bytes_per_block -
|
||||
(state->total_in % a->bytes_per_block);
|
||||
while (tocopy > 0 && tocopy < (unsigned)a->bytes_per_block) {
|
||||
SET_NEXT_IN(state, a->nulls);
|
||||
state->stream.avail_in = tocopy < a->null_length ?
|
||||
tocopy : a->null_length;
|
||||
state->crc = crc32(state->crc, a->nulls,
|
||||
state->stream.avail_in);
|
||||
state->total_in += state->stream.avail_in;
|
||||
tocopy -= state->stream.avail_in;
|
||||
ret = drive_compressor(a, state, 0);
|
||||
if (ret != ARCHIVE_OK)
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
/* Build trailer: 4-byte CRC and 4-byte length. */
|
||||
trailer[0] = (state->crc)&0xff;
|
||||
trailer[1] = (state->crc >> 8)&0xff;
|
||||
trailer[2] = (state->crc >> 16)&0xff;
|
||||
trailer[3] = (state->crc >> 24)&0xff;
|
||||
trailer[4] = (state->total_in)&0xff;
|
||||
trailer[5] = (state->total_in >> 8)&0xff;
|
||||
trailer[6] = (state->total_in >> 16)&0xff;
|
||||
trailer[7] = (state->total_in >> 24)&0xff;
|
||||
/* Finish compression cycle */
|
||||
if (((ret = drive_compressor(a, state, 1))) != ARCHIVE_OK)
|
||||
goto cleanup;
|
||||
|
||||
/* Add trailer to current block. */
|
||||
tocopy = 8;
|
||||
if (tocopy > state->stream.avail_out)
|
||||
tocopy = state->stream.avail_out;
|
||||
memcpy(state->stream.next_out, trailer, tocopy);
|
||||
state->stream.next_out += tocopy;
|
||||
state->stream.avail_out -= tocopy;
|
||||
/* Build trailer: 4-byte CRC and 4-byte length. */
|
||||
trailer[0] = (state->crc)&0xff;
|
||||
trailer[1] = (state->crc >> 8)&0xff;
|
||||
trailer[2] = (state->crc >> 16)&0xff;
|
||||
trailer[3] = (state->crc >> 24)&0xff;
|
||||
trailer[4] = (state->total_in)&0xff;
|
||||
trailer[5] = (state->total_in >> 8)&0xff;
|
||||
trailer[6] = (state->total_in >> 16)&0xff;
|
||||
trailer[7] = (state->total_in >> 24)&0xff;
|
||||
|
||||
/* If it overflowed, flush and start a new block. */
|
||||
if (tocopy < 8) {
|
||||
/* Add trailer to current block. */
|
||||
tocopy = 8;
|
||||
if (tocopy > state->stream.avail_out)
|
||||
tocopy = state->stream.avail_out;
|
||||
memcpy(state->stream.next_out, trailer, tocopy);
|
||||
state->stream.next_out += tocopy;
|
||||
state->stream.avail_out -= tocopy;
|
||||
|
||||
/* If it overflowed, flush and start a new block. */
|
||||
if (tocopy < 8) {
|
||||
bytes_written = (a->client_writer)(&a->archive, a->client_data,
|
||||
state->compressed, state->compressed_buffer_size);
|
||||
if (bytes_written <= 0) {
|
||||
ret = ARCHIVE_FATAL;
|
||||
goto cleanup;
|
||||
}
|
||||
a->archive.raw_position += bytes_written;
|
||||
state->stream.next_out = state->compressed;
|
||||
state->stream.avail_out = state->compressed_buffer_size;
|
||||
memcpy(state->stream.next_out, trailer + tocopy, 8-tocopy);
|
||||
state->stream.next_out += 8-tocopy;
|
||||
state->stream.avail_out -= 8-tocopy;
|
||||
}
|
||||
|
||||
/* Optionally, pad the final compressed block. */
|
||||
block_length = state->stream.next_out - state->compressed;
|
||||
|
||||
/* Tricky calculation to determine size of last block. */
|
||||
target_block_length = block_length;
|
||||
if (a->bytes_in_last_block <= 0)
|
||||
/* Default or Zero: pad to full block */
|
||||
target_block_length = a->bytes_per_block;
|
||||
else
|
||||
/* Round length to next multiple of bytes_in_last_block. */
|
||||
target_block_length = a->bytes_in_last_block *
|
||||
( (block_length + a->bytes_in_last_block - 1) /
|
||||
a->bytes_in_last_block);
|
||||
if (target_block_length > a->bytes_per_block)
|
||||
target_block_length = a->bytes_per_block;
|
||||
if (block_length < target_block_length) {
|
||||
memset(state->stream.next_out, 0,
|
||||
target_block_length - block_length);
|
||||
block_length = target_block_length;
|
||||
}
|
||||
|
||||
/* Write the last block */
|
||||
bytes_written = (a->client_writer)(&a->archive, a->client_data,
|
||||
state->compressed, state->compressed_buffer_size);
|
||||
state->compressed, block_length);
|
||||
if (bytes_written <= 0) {
|
||||
ret = ARCHIVE_FATAL;
|
||||
goto cleanup;
|
||||
}
|
||||
a->archive.raw_position += bytes_written;
|
||||
state->stream.next_out = state->compressed;
|
||||
state->stream.avail_out = state->compressed_buffer_size;
|
||||
memcpy(state->stream.next_out, trailer + tocopy, 8-tocopy);
|
||||
state->stream.next_out += 8-tocopy;
|
||||
state->stream.avail_out -= 8-tocopy;
|
||||
|
||||
/* Cleanup: shut down compressor, release memory, etc. */
|
||||
cleanup:
|
||||
switch (deflateEnd(&(state->stream))) {
|
||||
case Z_OK:
|
||||
break;
|
||||
default:
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
|
||||
"Failed to clean up compressor");
|
||||
ret = ARCHIVE_FATAL;
|
||||
}
|
||||
free(state->compressed);
|
||||
free(state);
|
||||
}
|
||||
|
||||
/* Optionally, pad the final compressed block. */
|
||||
block_length = state->stream.next_out - state->compressed;
|
||||
|
||||
|
||||
/* Tricky calculation to determine size of last block. */
|
||||
target_block_length = block_length;
|
||||
if (a->bytes_in_last_block <= 0)
|
||||
/* Default or Zero: pad to full block */
|
||||
target_block_length = a->bytes_per_block;
|
||||
else
|
||||
/* Round length to next multiple of bytes_in_last_block. */
|
||||
target_block_length = a->bytes_in_last_block *
|
||||
( (block_length + a->bytes_in_last_block - 1) /
|
||||
a->bytes_in_last_block);
|
||||
if (target_block_length > a->bytes_per_block)
|
||||
target_block_length = a->bytes_per_block;
|
||||
if (block_length < target_block_length) {
|
||||
memset(state->stream.next_out, 0,
|
||||
target_block_length - block_length);
|
||||
block_length = target_block_length;
|
||||
}
|
||||
|
||||
/* Write the last block */
|
||||
bytes_written = (a->client_writer)(&a->archive, a->client_data,
|
||||
state->compressed, block_length);
|
||||
if (bytes_written <= 0) {
|
||||
ret = ARCHIVE_FATAL;
|
||||
goto cleanup;
|
||||
}
|
||||
a->archive.raw_position += bytes_written;
|
||||
|
||||
/* Cleanup: shut down compressor, release memory, etc. */
|
||||
cleanup:
|
||||
switch (deflateEnd(&(state->stream))) {
|
||||
case Z_OK:
|
||||
break;
|
||||
default:
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
|
||||
"Failed to clean up compressor");
|
||||
ret = ARCHIVE_FATAL;
|
||||
}
|
||||
free(state->compressed);
|
||||
free(state);
|
||||
/* Clean up config area even if we never initialized. */
|
||||
free(a->compressor.config);
|
||||
a->compressor.config = NULL;
|
||||
return (ret);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user