svn commit: r191166 - head/lib/libarchive

Tim Kientzle kientzle at FreeBSD.org
Fri Apr 17 00:42:46 UTC 2009


Author: kientzle
Date: Fri Apr 17 00:42:45 2009
New Revision: 191166
URL: http://svn.freebsd.org/changeset/base/191166

Log:
  Merge from libarchive.googlecode.com:  Numerous fixes to the
  write options handling, including documentation.

Modified:
  head/lib/libarchive/archive_write.3
  head/lib/libarchive/archive_write.c
  head/lib/libarchive/archive_write_set_compression_bzip2.c
  head/lib/libarchive/archive_write_set_compression_gzip.c

Modified: head/lib/libarchive/archive_write.3
==============================================================================
--- head/lib/libarchive/archive_write.3	Fri Apr 17 00:39:35 2009	(r191165)
+++ head/lib/libarchive/archive_write.3	Fri Apr 17 00:42:45 2009	(r191166)
@@ -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 alway
 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

Modified: head/lib/libarchive/archive_write.c
==============================================================================
--- head/lib/libarchive/archive_write.c	Fri Apr 17 00:39:35 2009	(r191165)
+++ head/lib/libarchive/archive_write.c	Fri Apr 17 00:42:45 2009	(r191166)
@@ -132,8 +132,14 @@ archive_write_set_format_options(struct 
 {
 	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 
 			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(str
 	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(str
 			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(str
 		    "Illegal format options.");
 		return (ARCHIVE_WARN);
 	}
-	return (ARCHIVE_OK);
+	return (ret);
 }
 
 /*
@@ -194,14 +220,16 @@ archive_write_set_compressor_options(str
 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);
 }
 

Modified: head/lib/libarchive/archive_write_set_compression_bzip2.c
==============================================================================
--- head/lib/libarchive/archive_write_set_compression_bzip2.c	Fri Apr 17 00:39:35 2009	(r191165)
+++ head/lib/libarchive/archive_write_set_compression_bzip2.c	Fri Apr 17 00:42:45 2009	(r191166)
@@ -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 arc
 {
 	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 arc
 	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);
@@ -168,6 +185,32 @@ archive_compressor_bzip2_init(struct arc
 }
 
 /*
+ * 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.
  *
  * Returns ARCHIVE_OK if all data written, error otherwise.
@@ -212,83 +255,88 @@ archive_compressor_bzip2_finish(struct 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;
-	}
+	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;
+		}
 
-	/* 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;
+		/* 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;
+			}
 		}
-	}
 
-	/* 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;
-	}
+		/* 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;
+		}
 
-	/* 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;
-	}
+		/* 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;
+		}
 
-	/* 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);
 }
 

Modified: head/lib/libarchive/archive_write_set_compression_gzip.c
==============================================================================
--- head/lib/libarchive/archive_write_set_compression_gzip.c	Fri Apr 17 00:39:35 2009	(r191165)
+++ head/lib/libarchive/archive_write_set_compression_gzip.c	Fri Apr 17 00:42:45 2009	(r191166)
@@ -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 arch
 {
 	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 arch
 	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 arch
 	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 arc
 	return (ARCHIVE_OK);
 }
 
-
 /*
  * Finish the compression...
  */
@@ -316,113 +295,118 @@ archive_compressor_gzip_finish(struct ar
 
 	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;
-	}
+	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;
+		}
+
+		/* 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;
+			}
+		}
+
+		/* 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)
+		/* 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;
+
+		/* 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;
 		}
-	}
 
-	/* Finish compression cycle */
-	if (((ret = drive_compressor(a, state, 1))) != 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;
-
-	/* 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;
+		/* Optionally, pad the final compressed block. */
+		block_length = state->stream.next_out - state->compressed;
 
-	/* If it overflowed, flush and start a new block. */
-	if (tocopy < 8) {
+		/* 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;
-	}
-
-	/* 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;
+
+		/* 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);
 	}
-	free(state->compressed);
-	free(state);
+	/* Clean up config area even if we never initialized. */
+	free(a->compressor.config);
+	a->compressor.config = NULL;
 	return (ret);
 }
 


More information about the svn-src-head mailing list