diff --git a/c/archive.h b/c/archive.h
--- a/c/archive.h
+++ b/c/archive.h
@@ -34,7 +34,7 @@
  * assert that ARCHIVE_VERSION_NUMBER >= 2012108.
  */
 /* Note: Compiler will complain if this does not match archive_entry.h! */
-#define	ARCHIVE_VERSION_NUMBER 3008008
+#define	ARCHIVE_VERSION_NUMBER 3008009
 
 #include <sys/stat.h>
 #include <stddef.h>  /* for wchar_t */
@@ -177,7 +177,7 @@
 /*
  * Textual name/version of the library, useful for version displays.
  */
-#define	ARCHIVE_VERSION_ONLY_STRING "3.8.8"
+#define	ARCHIVE_VERSION_ONLY_STRING "3.8.9"
 #define	ARCHIVE_VERSION_STRING "libarchive " ARCHIVE_VERSION_ONLY_STRING
 __LA_DECL const char *	archive_version_string(void);
 
diff --git a/c/archive_acl.c b/c/archive_acl.c
--- a/c/archive_acl.c
+++ b/c/archive_acl.c
@@ -1516,7 +1516,7 @@
 		const char *end;
 	} field[6], name;
 
-	const char *s, *st;
+	const char *s, *st, *text_end;
 	int numfields, fields, n, r, sol, ret;
 	int type, types, tag, permset, id;
 	size_t len;
@@ -1539,6 +1539,7 @@
 
 	ret = ARCHIVE_OK;
 	types = 0;
+	text_end = (text == NULL) ? text : text + length;
 
 	while (text != NULL && length > 0 && *text != '\0') {
 		/*
@@ -1563,6 +1564,18 @@
 		if (field[0].start == NULL || field[0].end == NULL) {
 			/* This should never happen */
 			return (ARCHIVE_FATAL);
+		}
+
+		if (field[0].start == text_end) {
+			/*
+			 * Empty entry: next_field() consumed the rest of the
+			 * buffer as separators or whitespace, leaving
+			 * field[0].start one past the end.  'text' is not
+			 * guaranteed to be NUL terminated, so the byte after it
+			 * must not be dereferenced.
+			 */
+			ret = ARCHIVE_WARN;
+			continue;
 		}
 
 		if (*(field[0].start) == '#') {
diff --git a/c/archive_acl_private.h b/c/archive_acl_private.h
--- a/c/archive_acl_private.h
+++ b/c/archive_acl_private.h
@@ -27,7 +27,9 @@
 #define ARCHIVE_ACL_PRIVATE_H_INCLUDED
 
 #ifndef __LIBARCHIVE_BUILD
+#ifndef __LIBARCHIVE_TEST
 #error This header is only to be used internally to libarchive.
+#endif
 #endif
 
 #include "archive_string.h"
diff --git a/c/archive_check_magic.c b/c/archive_check_magic.c
--- a/c/archive_check_magic.c
+++ b/c/archive_check_magic.c
@@ -85,6 +85,7 @@
 	case ARCHIVE_STATE_NEW:		return ("new");
 	case ARCHIVE_STATE_HEADER:	return ("header");
 	case ARCHIVE_STATE_DATA:	return ("data");
+	case ARCHIVE_STATE_DATA_RECOVERY: return ("data_recovery");
 	case ARCHIVE_STATE_EOF:		return ("eof");
 	case ARCHIVE_STATE_CLOSED:	return ("closed");
 	case ARCHIVE_STATE_FATAL:	return ("fatal");
diff --git a/c/archive_entry.c b/c/archive_entry.c
--- a/c/archive_entry.c
+++ b/c/archive_entry.c
@@ -1562,17 +1562,21 @@
 archive_entry_copy_mac_metadata(struct archive_entry *entry,
     const void *p, size_t s)
 {
-  free(entry->mac_metadata);
-  if (p == NULL || s == 0) {
-    entry->mac_metadata = NULL;
-    entry->mac_metadata_size = 0;
-  } else {
-    entry->mac_metadata_size = s;
-    entry->mac_metadata = malloc(s);
-    if (entry->mac_metadata == NULL)
-      abort();
-    memcpy(entry->mac_metadata, p, s);
-  }
+	void *metadata;
+
+	if (p == NULL || s == 0) {
+		free(entry->mac_metadata);
+		entry->mac_metadata = NULL;
+		entry->mac_metadata_size = 0;
+	} else {
+		metadata = malloc(s);
+		if (metadata == NULL)
+			abort();
+		memcpy(metadata, p, s);
+		free(entry->mac_metadata);
+		entry->mac_metadata = metadata;
+		entry->mac_metadata_size = s;
+	}
 }
 
 /* Digest handling */
diff --git a/c/archive_entry.h b/c/archive_entry.h
--- a/c/archive_entry.h
+++ b/c/archive_entry.h
@@ -28,7 +28,7 @@
 #define	ARCHIVE_ENTRY_H_INCLUDED
 
 /* Note: Compiler will complain if this does not match archive.h! */
-#define	ARCHIVE_VERSION_NUMBER 3008008
+#define	ARCHIVE_VERSION_NUMBER 3008009
 
 /*
  * Note: archive_entry.h is for use outside of libarchive; the
@@ -666,7 +666,7 @@
 	    la_int64_t /* offset */, la_int64_t /* length */);
 
 /*
- * To retrieve the xattr list, first "reset", then repeatedly ask for the
+ * To retrieve the sparse list, first "reset", then repeatedly ask for the
  * "next" entry.
  */
 
diff --git a/c/archive_entry_link_resolver.c b/c/archive_entry_link_resolver.c
--- a/c/archive_entry_link_resolver.c
+++ b/c/archive_entry_link_resolver.c
@@ -41,6 +41,7 @@
 
 #include "archive.h"
 #include "archive_entry.h"
+#include "archive_integer.h"
 
 /*
  * This is mostly a pretty straightforward hash table implementation.
@@ -387,7 +388,7 @@
 	}
 
 	/* If the links cache is getting too full, enlarge the hash table. */
-	if (res->number_entries > res->number_buckets * 2)
+	if (res->number_entries / 2 > res->number_buckets)
 		grow_hash(res);
 
 	hash = (size_t)(archive_entry_dev(entry) ^ archive_entry_ino64(entry));
@@ -413,8 +414,7 @@
 	size_t i, bucket;
 
 	/* Try to enlarge the bucket list. */
-	new_size = res->number_buckets * 2;
-	if (new_size < res->number_buckets)
+	if (archive_ckd_mul_size(&new_size, res->number_buckets, 2))
 		return;
 	new_buckets = calloc(new_size, sizeof(struct links_entry *));
 
diff --git a/c/archive_entry_xattr.c b/c/archive_entry_xattr.c
--- a/c/archive_entry_xattr.c
+++ b/c/archive_entry_xattr.c
@@ -96,11 +96,11 @@
 	if ((xp->name = strdup(name)) == NULL)
 		__archive_errx(1, "Out of memory");
 
-	if ((xp->value = malloc(size)) != NULL) {
-		memcpy(xp->value, value, size);
-		xp->size = size;
-	} else
-		xp->size = 0;
+	if ((xp->value = malloc(size)) == NULL)
+		__archive_errx(1, "Out of memory");
+
+	memcpy(xp->value, value, size);
+	xp->size = size;
 
 	xp->next = entry->xattr_head;
 	entry->xattr_head = xp;
diff --git a/c/archive_integer.h b/c/archive_integer.h
--- a/c/archive_integer.h
+++ b/c/archive_integer.h
@@ -49,6 +49,9 @@
 #ifdef HAVE_STDINT_H
 #include <stdint.h>
 #endif
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #endif
@@ -243,5 +246,22 @@
 	return 0;
 #endif
 }
+
+#if !defined(TIME_MAX)
+#define TIME_MAX (((time_t)0 < (time_t)-1) ? (time_t)~0 :		\
+	    sizeof(time_t) == sizeof(long long) ? (time_t)LLONG_MAX :	\
+	    sizeof(time_t) == sizeof(long) ? (time_t)LONG_MAX :		\
+	    sizeof(time_t) == sizeof(int) ? (time_t)INT_MAX :		\
+	    sizeof(time_t) == sizeof(short) ? (time_t)SHRT_MAX :	\
+	    1 /* I give up */)
+#endif
+#if !defined(TIME_MIN)
+#define TIME_MIN (((time_t)0 < (time_t)-1) ? (time_t)0 :		\
+	    sizeof(time_t) == sizeof(long long) ? (time_t)LLONG_MIN :	\
+	    sizeof(time_t) == sizeof(long) ? (time_t)LONG_MIN :		\
+	    sizeof(time_t) == sizeof(int) ? (time_t)INT_MIN :		\
+	    sizeof(time_t) == sizeof(short) ? (time_t)SHRT_MIN :	\
+	    -1 /* I give up */)
+#endif
 
 #endif
diff --git a/c/archive_options.c b/c/archive_options.c
--- a/c/archive_options.c
+++ b/c/archive_options.c
@@ -33,7 +33,7 @@
 
 static char *
 parse_option(char **str,
-    char **mod, char **opt, char **val);
+    const char **mod, const char **opt, const char **val);
 
 int
 _archive_set_option(struct archive *a,
@@ -102,8 +102,8 @@
     unsigned int magic, const char *fn, option_handler use_option)
 {
 	int allok = 1, anyok = 0, ignore_mod_err = 0, r;
-	char *data;
-	char *s, *mod, *opt, *val;
+	char *data, *s;
+	const char *mod, *opt, *val;
 
 	archive_check_magic(a, magic, ARCHIVE_STATE_NEW, fn);
 
@@ -168,10 +168,10 @@
 }
 
 static char *
-parse_option(char **s, char **m, char **o, char **v)
+parse_option(char **s, const char **m, const char **o, const char **v)
 {
-	char *end, *mod, *opt, *val;
-	char *p;
+	const char *mod, *val;
+	char *end, *opt, *p;
 
 	end = NULL;
 	mod = NULL;
diff --git a/c/archive_pathmatch.c b/c/archive_pathmatch.c
--- a/c/archive_pathmatch.c
+++ b/c/archive_pathmatch.c
@@ -337,7 +337,7 @@
 			}
 			if (*end == L']') {
 				/* We found [...], try to match it. */
-				if (!pm_list_w(p + 1, end, *s, flags))
+				if (*s == L'\0' || !pm_list_w(p + 1, end, *s, flags))
 					return (0);
 				p = end; /* Jump to trailing ']' char. */
 				break;
diff --git a/c/archive_private.h b/c/archive_private.h
--- a/c/archive_private.h
+++ b/c/archive_private.h
@@ -61,12 +61,38 @@
 #define	ARCHIVE_READ_DISK_MAGIC (0xbadb0c5U)
 #define	ARCHIVE_MATCH_MAGIC	(0xcad11c9U)
 
+/*
+ * Having the state be a bitmask makes it easy to check
+ * for combinations of allowed states.  These should
+ * generally only be used in public API entry points,
+ * primarily in archive_read.c, archive_write.c, etc.
+ * Internal callbacks can rely on the core machinery
+ * to only call them when appropriate.
+ *
+ * Generally checked via `__archive_check_magic()`.
+ */
+
+/* Newly created archive object, not yet opened */
 #define	ARCHIVE_STATE_NEW	1U
+/* Archive is ready to read a header. */
 #define	ARCHIVE_STATE_HEADER	2U
+/* A header has been read: client can ask for data
+ * or they can ask for the next header and
+ * we'll automatically skip the remaining data. */
 #define	ARCHIVE_STATE_DATA	4U
+/* Similar to STATE_DATA but after a FAILED header:
+ * the client may not read data, but they may ask
+ * for the next header and we'll recover. */
+#define	ARCHIVE_STATE_DATA_RECOVERY	8U
+/* End-of-archive has been reached.  Client can only
+ * close or free the archive. */
 #define	ARCHIVE_STATE_EOF	0x10U
+/* Archive is closed; client is only allowed to free the archive. */
 #define	ARCHIVE_STATE_CLOSED	0x20U
+/* Archive is in a FATAL error state: a close request
+ * is permitted but ignored. */
 #define	ARCHIVE_STATE_FATAL	0x8000U
+/* Any valid (non-fatal) state. */
 #define	ARCHIVE_STATE_ANY	(0xFFFFU & ~ARCHIVE_STATE_FATAL)
 
 struct archive_vtable {
diff --git a/c/archive_read.c b/c/archive_read.c
--- a/c/archive_read.c
+++ b/c/archive_read.c
@@ -103,7 +103,12 @@
 
 	a->archive.state = ARCHIVE_STATE_NEW;
 	a->entry = archive_entry_new2(&a->archive);
+	if (a->entry == NULL) {
+		free(a);
+		return (NULL);
+	}
 	a->archive.vtable = &archive_read_vtable;
+	a->entry_bytes_declared = -1;
 
 	a->passphrases.last = &a->passphrases.first;
 
@@ -135,12 +140,16 @@
     archive_open_callback *client_opener, archive_read_callback *client_reader,
     archive_close_callback *client_closer)
 {
+	int r;
+
 	/* Old archive_read_open() is just a thin shell around
 	 * archive_read_open1. */
 	archive_read_set_open_callback(a, client_opener);
 	archive_read_set_read_callback(a, client_reader);
 	archive_read_set_close_callback(a, client_closer);
-	archive_read_set_callback_data(a, client_data);
+	r = archive_read_set_callback_data(a, client_data);
+	if (r < 0)
+		return (r);
 	return archive_read_open1(a);
 }
 
@@ -152,9 +161,13 @@
     archive_skip_callback *client_skipper,
     archive_close_callback *client_closer)
 {
+	int r;
+
 	/* Old archive_read_open2() is just a thin shell around
 	 * archive_read_open1. */
-	archive_read_set_callback_data(a, client_data);
+	r = archive_read_set_callback_data(a, client_data);
+	if (r < 0)
+		return (r);
 	archive_read_set_open_callback(a, client_opener);
 	archive_read_set_read_callback(a, client_reader);
 	archive_read_set_skip_callback(a, client_skipper);
@@ -163,28 +176,28 @@
 }
 
 static ssize_t
-client_read_proxy(struct archive_read_filter *self, const void **buff)
+client_read_proxy(struct archive_read_filter *f, const void **buff)
 {
 	ssize_t r;
-	r = (self->archive->client.reader)(&self->archive->archive,
-	    self->data, buff);
+	r = (f->archive->client.reader)(&f->archive->archive,
+	    f->data, buff);
 	return (r);
 }
 
 static int64_t
-client_skip_proxy(struct archive_read_filter *self, int64_t request)
+client_skip_proxy(struct archive_read_filter *f, int64_t request)
 {
 	if (request < 0)
 		__archive_errx(1, "Negative skip requested");
 	if (request == 0)
 		return 0;
 
-	if (self->archive->client.skipper != NULL) {
+	if (f->archive->client.skipper != NULL) {
 		int64_t total = 0;
 		for (;;) {
 			int64_t get, ask = request;
-			get = (self->archive->client.skipper)
-				(&self->archive->archive, self->data, ask);
+			get = (f->archive->client.skipper)
+				(&f->archive->archive, f->data, ask);
 			total += get;
 			if (get == 0 || get == request)
 				return (total);
@@ -192,7 +205,7 @@
 				return ARCHIVE_FATAL;
 			request -= get;
 		}
-	} else if (self->archive->client.seeker != NULL
+	} else if (f->archive->client.seeker != NULL
 		&& request > 64 * 1024) {
 		/* If the client provided a seeker but not a skipper,
 		 * we can use the seeker to skip forward.
@@ -205,9 +218,9 @@
 		 * to just reading and discarding.  That's why we
 		 * only do this for skips of over 64k.
 		 */
-		int64_t before = self->position;
-		int64_t after = (self->archive->client.seeker)
-		    (&self->archive->archive, self->data, request, SEEK_CUR);
+		int64_t before = f->position;
+		int64_t after = (f->archive->client.seeker)
+		    (&f->archive->archive, f->data, request, SEEK_CUR);
 		if (after != before + request)
 			return ARCHIVE_FATAL;
 		return after - before;
@@ -216,20 +229,20 @@
 }
 
 static int64_t
-client_seek_proxy(struct archive_read_filter *self, int64_t offset, int whence)
+client_seek_proxy(struct archive_read_filter *f, int64_t offset, int whence)
 {
 	/* DO NOT use the skipper here!  If we transparently handled
 	 * forward seek here by using the skipper, that will break
 	 * other libarchive code that assumes a successful forward
 	 * seek means it can also seek backwards.
 	 */
-	if (self->archive->client.seeker == NULL) {
-		archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+	if (f->archive->client.seeker == NULL) {
+		archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC,
 		    "Current client reader does not support seeking a device");
 		return (ARCHIVE_FAILED);
 	}
-	return (self->archive->client.seeker)(&self->archive->archive,
-	    self->data, offset, whence);
+	return (f->archive->client.seeker)(&f->archive->archive,
+	    f->data, offset, whence);
 }
 
 static int
@@ -251,21 +264,21 @@
 }
 
 static int
-client_close_proxy(struct archive_read_filter *self)
+client_close_proxy(struct archive_read_filter *f)
 {
-	return read_client_close_proxy(self->archive);
+	return read_client_close_proxy(f->archive);
 }
 
 static int
-client_switch_proxy(struct archive_read_filter *self, unsigned int iindex)
+client_switch_proxy(struct archive_read_filter *f, unsigned int iindex)
 {
 	struct archive_read *a;
 	int r1 = ARCHIVE_OK, r2 = ARCHIVE_OK;
 	void *data2;
 
-	while (self->upstream != NULL)
-		self = self->upstream;
-	a = self->archive;
+	while (f->upstream != NULL)
+		f = f->upstream;
+	a = f->archive;
 
 	/* Don't do anything if already in the specified data node */
 	if (a->client.cursor == iindex)
@@ -276,19 +289,19 @@
 	if (a->client.switcher != NULL)
 	{
 		r1 = r2 = (a->client.switcher)
-			((struct archive *)a, self->data, data2);
-		self->data = data2;
+			((struct archive *)a, f->data, data2);
+		f->data = data2;
 	}
 	else
 	{
 		/* Attempt to call close and open instead */
 		if (a->client.closer != NULL)
 			r1 = (a->client.closer)
-				((struct archive *)a, self->data);
-		self->data = data2;
+				((struct archive *)a, f->data);
+		f->data = data2;
 		if (a->client.opener != NULL)
 			r2 = (a->client.opener)
-				((struct archive *)a, self->data);
+				((struct archive *)a, f->data);
 	}
 	return (r1 < r2) ? r1 : r2;
 }
@@ -469,7 +482,7 @@
 archive_read_open1(struct archive *_a)
 {
 	struct archive_read *a = (struct archive_read *)_a;
-	struct archive_read_filter *filter, *tmp;
+	struct archive_read_filter *f, *tmp;
 	int slot, e = ARCHIVE_OK;
 
 	archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
@@ -493,23 +506,23 @@
 		}
 	}
 
-	filter = calloc(1, sizeof(*filter));
-	if (filter == NULL)
+	f = calloc(1, sizeof(*f));
+	if (f == NULL)
 		return (ARCHIVE_FATAL);
-	filter->bidder = NULL;
-	filter->upstream = NULL;
-	filter->archive = a;
-	filter->data = a->client.dataset[0].data;
-	filter->vtable = &none_reader_vtable;
-	filter->name = "none";
-	filter->code = ARCHIVE_FILTER_NONE;
-	filter->can_skip = 1;
-	filter->can_seek = 1;
+	f->bidder = NULL;
+	f->upstream = NULL;
+	f->archive = a;
+	f->data = a->client.dataset[0].data;
+	f->vtable = &none_reader_vtable;
+	f->name = "none";
+	f->code = ARCHIVE_FILTER_NONE;
+	f->can_skip = 1;
+	f->can_seek = 1;
 
 	a->client.dataset[0].begin_position = 0;
 	if (!a->filter || !a->bypass_filter_bidding)
 	{
-		a->filter = filter;
+		a->filter = f;
 		/* Build out the input pipeline. */
 		e = choose_filters(a);
 		if (e < ARCHIVE_WARN) {
@@ -523,7 +536,7 @@
 		tmp = a->filter;
 		while (tmp->upstream)
 			tmp = tmp->upstream;
-		tmp->upstream = filter;
+		tmp->upstream = f;
 	}
 
 	if (!a->format)
@@ -558,7 +571,7 @@
 {
 	int number_bidders, i, bid, best_bid, number_filters;
 	struct archive_read_filter_bidder *bidder, *best_bidder;
-	struct archive_read_filter *filter;
+	struct archive_read_filter *f;
 	ssize_t avail;
 	int r;
 
@@ -590,13 +603,13 @@
 			return (ARCHIVE_OK);
 		}
 
-		filter = calloc(1, sizeof(*filter));
-		if (filter == NULL)
+		f = calloc(1, sizeof(*f));
+		if (f == NULL)
 			return (ARCHIVE_FATAL);
-		filter->bidder = best_bidder;
-		filter->archive = a;
-		filter->upstream = a->filter;
-		a->filter = filter;
+		f->bidder = best_bidder;
+		f->archive = a;
+		f->upstream = a->filter;
+		a->filter = f;
 		r = (best_bidder->vtable->init)(a->filter);
 		if (r != ARCHIVE_OK) {
 			__archive_read_free_filters(a);
@@ -626,7 +639,8 @@
 	int r1 = ARCHIVE_OK, r2;
 
 	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
-	    ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
+	    ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA |
+	    ARCHIVE_STATE_DATA_RECOVERY,
 	    "archive_read_next_header");
 
 	archive_entry_clear(entry);
@@ -635,8 +649,12 @@
 	/*
 	 * If client didn't consume entire data, skip any remainder
 	 * (This is especially important for GNU incremental directories.)
+	 * A header that failed to parse (DATA_RECOVERY) still needs the
+	 * same treatment: whatever of its body the format reader left
+	 * unconsumed must be skipped before we can read the next header.
 	 */
-	if (a->archive.state == ARCHIVE_STATE_DATA) {
+	if (a->archive.state == ARCHIVE_STATE_DATA ||
+	    a->archive.state == ARCHIVE_STATE_DATA_RECOVERY) {
 		r1 = archive_read_data_skip(&a->archive);
 		if (r1 == ARCHIVE_EOF)
 			archive_set_error(&a->archive, EIO,
@@ -674,8 +692,26 @@
 	case ARCHIVE_FATAL:
 		a->archive.state = ARCHIVE_STATE_FATAL;
 		break;
+	case ARCHIVE_FAILED:
+		/*
+		 * This entry's header could not be parsed, so its metadata
+		 * cannot be trusted.  ARCHIVE_STATE_DATA_RECOVERY still
+		 * permits skipping past it (the format reader is
+		 * responsible for ensuring that's actually possible), but
+		 * blocks archive_read_data() and friends, which all check
+		 * for ARCHIVE_STATE_DATA specifically and would otherwise
+		 * return content for an entry we don't actually understand.
+		 */
+		a->archive.state = ARCHIVE_STATE_DATA_RECOVERY;
+		break;
 	}
 
+	if (r2 == ARCHIVE_OK || r2 == ARCHIVE_WARN)
+		a->entry_bytes_declared = archive_entry_size_is_set(entry)
+		    ? archive_entry_size(entry) : -1;
+	else
+		a->entry_bytes_declared = -1;
+
 	__archive_reset_read_data(&a->archive);
 
 	a->data_start_node = a->client.cursor;
@@ -764,7 +800,7 @@
 
 /*
  * Returns 1 if the archive contains at least one encrypted entry.
- * If the archive format not support encryption at all
+ * If the archive format does not support encryption at all,
  * ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED is returned.
  * If for any other reason (e.g. not enough data read so far)
  * we cannot say whether there are encrypted entries, then
@@ -934,7 +970,8 @@
 	size_t size;
 	int64_t offset;
 
-	archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA,
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+	    ARCHIVE_STATE_DATA | ARCHIVE_STATE_DATA_RECOVERY,
 	    "archive_read_data_skip");
 
 	if (a->format->read_data_skip != NULL)
@@ -966,11 +1003,9 @@
 	    "archive_seek_data_block");
 
 	if (a->format->seek_data == NULL) {
-		archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
-		    "Internal error: "
-		    "No format_seek_data_block function registered");
-		a->archive.state = ARCHIVE_STATE_FATAL;
-		return (ARCHIVE_FATAL);
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Cannot seek data with this format");
+		return (ARCHIVE_FAILED);
 	}
 
 	r = (a->format->seek_data)(a, offset, whence);
@@ -1347,13 +1382,13 @@
 }
 
 const void *
-__archive_read_filter_ahead(struct archive_read_filter *filter,
+__archive_read_filter_ahead(struct archive_read_filter *f,
     size_t min, ssize_t *avail)
 {
 	ssize_t bytes_read;
 	size_t tocopy;
 
-	if (filter->fatal) {
+	if (f->fatal) {
 		if (avail)
 			*avail = ARCHIVE_FATAL;
 		return (NULL);
@@ -1370,79 +1405,79 @@
 		 * note that min == 0 is a perfectly well-defined
 		 * request.
 		 */
-		if (filter->avail >= min && filter->avail > 0) {
+		if (f->avail >= min && f->avail > 0) {
 			if (avail != NULL)
-				*avail = filter->avail;
-			return (filter->next);
+				*avail = f->avail;
+			return (f->next);
 		}
 
 		/*
 		 * We can satisfy directly from client buffer if everything
 		 * currently in the copy buffer is still in the client buffer.
 		 */
-		if (filter->client_total >= filter->client_avail + filter->avail
-		    && filter->client_avail + filter->avail >= min) {
+		if (f->client_total >= f->client_avail + f->avail
+		    && f->client_avail + f->avail >= min) {
 			/* "Roll back" to client buffer. */
-			filter->client_avail += filter->avail;
-			filter->client_next -= filter->avail;
+			f->client_avail += f->avail;
+			f->client_next -= f->avail;
 			/* Copy buffer is now empty. */
-			filter->avail = 0;
-			filter->next = filter->buffer;
+			f->avail = 0;
+			f->next = f->buffer;
 			/* Return data from client buffer. */
 			if (avail != NULL)
-				*avail = filter->client_avail;
-			return (filter->client_next);
+				*avail = f->client_avail;
+			return (f->client_next);
 		}
 
 		/* Move data forward in copy buffer if necessary. */
-		if (filter->next > filter->buffer &&
-		    min > filter->buffer_size - (filter->next - filter->buffer)) {
-			if (filter->avail > 0)
-				memmove(filter->buffer, filter->next,
-				    filter->avail);
-			filter->next = filter->buffer;
+		if (f->next > f->buffer &&
+		    min > f->buffer_size - (f->next - f->buffer)) {
+			if (f->avail > 0)
+				memmove(f->buffer, f->next,
+				    f->avail);
+			f->next = f->buffer;
 		}
 
 		/* If we've used up the client data, get more. */
-		if (filter->client_avail <= 0) {
-			if (filter->end_of_file) {
+		if (f->client_avail <= 0) {
+			if (f->end_of_file) {
 				if (avail != NULL)
-					*avail = filter->avail;
+					*avail = f->avail;
 				return (NULL);
 			}
-			bytes_read = (filter->vtable->read)(filter,
-			    &filter->client_buff);
+			bytes_read = (f->vtable->read)(f,
+			    &f->client_buff);
 			if (bytes_read < 0) {		/* Read error. */
-				filter->client_total = filter->client_avail = 0;
-				filter->client_next =
-				    filter->client_buff = NULL;
-				filter->fatal = 1;
+				f->client_total = f->client_avail = 0;
+				f->client_next =
+				    f->client_buff = NULL;
+				f->fatal = 1;
 				if (avail != NULL)
 					*avail = ARCHIVE_FATAL;
 				return (NULL);
 			}
 			if (bytes_read == 0) {
 				/* Check for another client object first */
-				if (filter->archive->client.cursor !=
-				      filter->archive->client.nodes - 1) {
-					if (client_switch_proxy(filter,
-					    filter->archive->client.cursor + 1)
+				if (f->archive->client.cursor !=
+				      f->archive->client.nodes - 1) {
+					if (client_switch_proxy(f,
+					    f->archive->client.cursor + 1)
 					    == ARCHIVE_OK)
 						continue;
 				}
 				/* Premature end-of-file. */
-				filter->client_total = filter->client_avail = 0;
-				filter->client_next =
-				    filter->client_buff = NULL;
-				filter->end_of_file = 1;
+				f->client_total = f->client_avail = 0;
+				f->client_next =
+				    f->client_buff = NULL;
+				f->end_of_file = 1;
 				/* Return whatever we do have. */
 				if (avail != NULL)
-					*avail = filter->avail;
+					*avail = f->avail;
 				return (NULL);
 			}
-			filter->client_total = bytes_read;
-			filter->client_avail = filter->client_total;
-			filter->client_next = filter->client_buff;
+			f->client_total = bytes_read;
+			f->client_avail = f->client_total;
+			f->client_next = f->client_buff;
 		} else {
 			/*
 			 * We can't satisfy the request from the copy
@@ -1452,67 +1487,66 @@
 			 */
 
 			/* Ensure the buffer is big enough. */
-			if (min > filter->buffer_size) {
-				size_t s, t;
+			if (min > f->buffer_size) {
+				size_t s;
 				char *p;
 
 				/* Double the buffer; watch for overflow. */
-				s = t = filter->buffer_size;
+				s = f->buffer_size;
 				if (s == 0)
 					s = min;
 				while (s < min) {
-					t *= 2;
-					if (t <= s) { /* Integer overflow! */
+					if (archive_ckd_mul_size(&s, s, 2)) {
+						/* Integer overflow! */
 						archive_set_error(
-						    &filter->archive->archive,
+						    &f->archive->archive,
 						    ENOMEM,
 						    "Unable to allocate copy"
 						    " buffer");
-						filter->fatal = 1;
+						f->fatal = 1;
 						if (avail != NULL)
 							*avail = ARCHIVE_FATAL;
 						return (NULL);
 					}
-					s = t;
 				}
 				/* Now s >= min, so allocate a new buffer. */
 				p = malloc(s);
 				if (p == NULL) {
 					archive_set_error(
-						&filter->archive->archive,
+						&f->archive->archive,
 						ENOMEM,
 					    "Unable to allocate copy buffer");
-					filter->fatal = 1;
+					f->fatal = 1;
 					if (avail != NULL)
 						*avail = ARCHIVE_FATAL;
 					return (NULL);
 				}
 				/* Move data into newly-enlarged buffer. */
-				if (filter->avail > 0)
-					memmove(p, filter->next, filter->avail);
-				free(filter->buffer);
-				filter->next = filter->buffer = p;
-				filter->buffer_size = s;
+				if (f->avail > 0)
+					memmove(p, f->next, f->avail);
+				free(f->buffer);
+				f->next = f->buffer = p;
+				f->buffer_size = s;
 			}
 
 			/* We can add client data to copy buffer. */
 			/* First estimate: copy to fill rest of buffer. */
-			tocopy = (filter->buffer + filter->buffer_size)
-			    - (filter->next + filter->avail);
+			tocopy = (f->buffer + f->buffer_size)
+			    - (f->next + f->avail);
 			/* Don't waste time buffering more than we need to. */
-			if (tocopy + filter->avail > min)
-				tocopy = min - filter->avail;
+			if (tocopy + f->avail > min)
+				tocopy = min - f->avail;
 			/* Don't copy more than is available. */
-			if (tocopy > filter->client_avail)
-				tocopy = filter->client_avail;
+			if (tocopy > f->client_avail)
+				tocopy = f->client_avail;
 
-			memcpy(filter->next + filter->avail,
-			    filter->client_next, tocopy);
+			memcpy(f->next + f->avail,
+			    f->client_next, tocopy);
 			/* Remove this data from client buffer. */
-			filter->client_next += tocopy;
-			filter->client_avail -= tocopy;
+			f->client_next += tocopy;
+			f->client_avail -= tocopy;
 			/* add it to copy buffer. */
-			filter->avail += tocopy;
+			f->avail += tocopy;
 		}
 	}
 }
@@ -1527,7 +1561,7 @@
 }
 
 int64_t
-__archive_read_filter_consume(struct archive_read_filter * filter,
+__archive_read_filter_consume(struct archive_read_filter *f,
     int64_t request)
 {
 	int64_t skipped;
@@ -1537,13 +1571,13 @@
 	if (request == 0)
 		return 0;
 
-	skipped = advance_file_pointer(filter, request);
+	skipped = advance_file_pointer(f, request);
 	if (skipped == request)
 		return (skipped);
 	/* We hit EOF before we satisfied the skip request. */
 	if (skipped < 0)  /* Map error code to 0 for error message below. */
 		skipped = 0;
-	archive_set_error(&filter->archive->archive,
+	archive_set_error(&f->archive->archive,
 	    ARCHIVE_ERRNO_MISC,
 	    "Truncated input file (needed %jd bytes, only %jd available)",
 	    (intmax_t)request, (intmax_t)skipped);
@@ -1557,45 +1591,45 @@
  * Returns a negative value if there's an I/O error.
  */
 static int64_t
-advance_file_pointer(struct archive_read_filter *filter, int64_t request)
+advance_file_pointer(struct archive_read_filter *f, int64_t request)
 {
 	int64_t bytes_skipped, total_bytes_skipped = 0;
 	ssize_t bytes_read;
 	size_t min;
 
-	if (filter->fatal)
+	if (f->fatal)
 		return (-1);
 
 	/* Use up the copy buffer first. */
-	if (filter->avail > 0) {
-		min = (size_t)minimum(request, (int64_t)filter->avail);
-		filter->next += min;
-		filter->avail -= min;
+	if (f->avail > 0) {
+		min = (size_t)minimum(request, (int64_t)f->avail);
+		f->next += min;
+		f->avail -= min;
 		request -= min;
-		filter->position += min;
+		f->position += min;
 		total_bytes_skipped += min;
 	}
 
 	/* Then use up the client buffer. */
-	if (filter->client_avail > 0) {
-		min = (size_t)minimum(request, (int64_t)filter->client_avail);
-		filter->client_next += min;
-		filter->client_avail -= min;
+	if (f->client_avail > 0) {
+		min = (size_t)minimum(request, (int64_t)f->client_avail);
+		f->client_next += min;
+		f->client_avail -= min;
 		request -= min;
-		filter->position += min;
+		f->position += min;
 		total_bytes_skipped += min;
 	}
 	if (request == 0)
 		return (total_bytes_skipped);
 
 	/* If there's an optimized skip function, use it. */
-	if (filter->can_skip != 0) {
-		bytes_skipped = client_skip_proxy(filter, request);
+	if (f->can_skip != 0) {
+		bytes_skipped = client_skip_proxy(f, request);
 		if (bytes_skipped < 0) {	/* error */
-			filter->fatal = 1;
+			f->fatal = 1;
 			return (bytes_skipped);
 		}
-		filter->position += bytes_skipped;
+		f->position += bytes_skipped;
 		total_bytes_skipped += bytes_skipped;
 		request -= bytes_skipped;
 		if (request == 0)
@@ -1604,37 +1638,37 @@
 
 	/* Use ordinary reads as necessary to complete the request. */
 	for (;;) {
-		bytes_read = (filter->vtable->read)(filter, &filter->client_buff);
+		bytes_read = (f->vtable->read)(f, &f->client_buff);
 		if (bytes_read < 0) {
-			filter->client_buff = NULL;
-			filter->fatal = 1;
+			f->client_buff = NULL;
+			f->fatal = 1;
 			return (bytes_read);
 		}
 
 		if (bytes_read == 0) {
-			if (filter->archive->client.cursor !=
-			      filter->archive->client.nodes - 1) {
-				if (client_switch_proxy(filter,
-				    filter->archive->client.cursor + 1)
+			if (f->archive->client.cursor !=
+			      f->archive->client.nodes - 1) {
+				if (client_switch_proxy(f,
+				    f->archive->client.cursor + 1)
 				    == ARCHIVE_OK)
 					continue;
 			}
-			filter->client_buff = NULL;
-			filter->end_of_file = 1;
+			f->client_buff = NULL;
+			f->end_of_file = 1;
 			return (total_bytes_skipped);
 		}
 
 		if (bytes_read >= request) {
-			filter->client_next =
-			    ((const char *)filter->client_buff) + request;
-			filter->client_avail = (size_t)(bytes_read - request);
-			filter->client_total = bytes_read;
+			f->client_next =
+			    ((const char *)f->client_buff) + request;
+			f->client_avail = (size_t)(bytes_read - request);
+			f->client_total = bytes_read;
 			total_bytes_skipped += request;
-			filter->position += request;
+			f->position += request;
 			return (total_bytes_skipped);
 		}
 
-		filter->position += bytes_read;
+		f->position += bytes_read;
 		total_bytes_skipped += bytes_read;
 		request -= bytes_read;
 	}
@@ -1650,23 +1684,23 @@
 }
 
 int64_t
-__archive_read_filter_seek(struct archive_read_filter *filter, int64_t offset,
+__archive_read_filter_seek(struct archive_read_filter *f, int64_t offset,
     int whence)
 {
 	struct archive_read_client *client;
 	int64_t r;
 	unsigned int cursor;
 
-	if (filter->closed || filter->fatal)
+	if (f->closed || f->fatal)
 		return (ARCHIVE_FATAL);
-	if (filter->can_seek == 0)
+	if (f->can_seek == 0)
 		return (ARCHIVE_FAILED);
 
-	client = &(filter->archive->client);
+	client = &(f->archive->client);
 	switch (whence) {
 	case SEEK_CUR:
 		/* Adjust the offset and use SEEK_SET instead */
-		offset += filter->position;
+		offset += f->position;
 		__LA_FALLTHROUGH;
 	case SEEK_SET:
 		cursor = 0;
@@ -1683,10 +1717,10 @@
 			client->dataset[++cursor].begin_position = r;
 		}
 		while (1) {
-			r = client_switch_proxy(filter, cursor);
+			r = client_switch_proxy(f, cursor);
 			if (r != ARCHIVE_OK)
 				return r;
-			if ((r = client_seek_proxy(filter, 0, SEEK_END)) < 0)
+			if ((r = client_seek_proxy(f, 0, SEEK_END)) < 0)
 				return r;
 			client->dataset[cursor].total_size = r;
 			if (client->dataset[cursor].begin_position +
@@ -1701,7 +1735,7 @@
 		if (offset < 0
 		    || offset > client->dataset[cursor].total_size)
 			return ARCHIVE_FATAL;
-		if ((r = client_seek_proxy(filter, offset, SEEK_SET)) < 0)
+		if ((r = client_seek_proxy(f, offset, SEEK_SET)) < 0)
 			return r;
 		break;
 
@@ -1717,10 +1751,10 @@
 			client->dataset[++cursor].begin_position = r;
 		}
 		while (1) {
-			r = client_switch_proxy(filter, cursor);
+			r = client_switch_proxy(f, cursor);
 			if (r != ARCHIVE_OK)
 				return r;
-			if ((r = client_seek_proxy(filter, 0, SEEK_END)) < 0)
+			if ((r = client_seek_proxy(f, 0, SEEK_END)) < 0)
 				return r;
 			client->dataset[cursor].total_size = r;
 			r = client->dataset[cursor].begin_position +
@@ -1741,9 +1775,9 @@
 				client->dataset[cursor].total_size;
 		}
 		offset = (r + offset) - client->dataset[cursor].begin_position;
-		if ((r = client_switch_proxy(filter, cursor)) != ARCHIVE_OK)
+		if ((r = client_switch_proxy(f, cursor)) != ARCHIVE_OK)
 			return r;
-		r = client_seek_proxy(filter, offset, SEEK_SET);
+		r = client_seek_proxy(f, offset, SEEK_SET);
 		if (r < ARCHIVE_OK)
 			return r;
 		break;
@@ -1771,10 +1805,10 @@
 		 * size is (r - offset).  Can we use that to simplify
 		 * the TODO items above?
 		 */
-		filter->avail = filter->client_avail = 0;
-		filter->next = filter->buffer;
-		filter->position = r;
-		filter->end_of_file = 0;
+		f->avail = f->client_avail = 0;
+		f->next = f->buffer;
+		f->position = r;
+		f->end_of_file = 0;
 	}
 	return r;
 }
diff --git a/c/archive_read_append_filter.c b/c/archive_read_append_filter.c
--- a/c/archive_read_append_filter.c
+++ b/c/archive_read_append_filter.c
@@ -38,8 +38,8 @@
 {
   int r1, r2, number_bidders, i;
   const char *str;
-  struct archive_read_filter_bidder *bidder;
-  struct archive_read_filter *filter;
+  struct archive_read_filter_bidder *b;
+  struct archive_read_filter *f;
   struct archive_read *a = (struct archive_read *)_a;
 
   r2 = (ARCHIVE_OK);
@@ -118,30 +118,30 @@
   {
     number_bidders = sizeof(a->bidders) / sizeof(a->bidders[0]);
 
-    bidder = a->bidders;
-    for (i = 1; i < number_bidders; i++, bidder++)
+    b = a->bidders;
+    for (i = 1; i < number_bidders; i++, b++)
     {
-      if (!bidder->name || !strcmp(bidder->name, str))
+      if (!b->name || !strcmp(b->name, str))
         break;
     }
-    if (!bidder->name || strcmp(bidder->name, str))
+    if (!b->name || strcmp(b->name, str))
     {
       archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
           "Internal error: Unable to append filter");
       return (ARCHIVE_FATAL);
     }
 
-    filter = calloc(1, sizeof(*filter));
-    if (filter == NULL)
+    f = calloc(1, sizeof(*f));
+    if (f == NULL)
     {
       archive_set_error(&a->archive, ENOMEM, "Out of memory");
       return (ARCHIVE_FATAL);
     }
-    filter->bidder = bidder;
-    filter->archive = a;
-    filter->upstream = a->filter;
-    a->filter = filter;
-    r2 = (bidder->vtable->init)(a->filter);
+    f->bidder = b;
+    f->archive = a;
+    f->upstream = a->filter;
+    a->filter = f;
+    r2 = (b->vtable->init)(a->filter);
     if (r2 != ARCHIVE_OK) {
       __archive_read_free_filters(a);
       return (ARCHIVE_FATAL);
@@ -163,8 +163,8 @@
   const char *cmd, const void *signature, size_t signature_len)
 {
   int r, number_bidders, i;
-  struct archive_read_filter_bidder *bidder;
-  struct archive_read_filter *filter;
+  struct archive_read_filter_bidder *b;
+  struct archive_read_filter *f;
   struct archive_read *a = (struct archive_read *)_a;
 
   if (archive_read_support_filter_program_signature(_a, cmd, signature,
@@ -173,36 +173,36 @@
 
   number_bidders = sizeof(a->bidders) / sizeof(a->bidders[0]);
 
-  bidder = a->bidders;
-  for (i = 0; i < number_bidders; i++, bidder++)
+  b = a->bidders;
+  for (i = 0; i < number_bidders; i++, b++)
   {
     /* Program bidder name set to filter name after initialization */
-    if (bidder->data && !bidder->name)
+    if (b->data && !b->name)
       break;
   }
-  if (!bidder->data)
+  if (!b->data)
   {
     archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
         "Internal error: Unable to append program filter");
     return (ARCHIVE_FATAL);
   }
 
-  filter = calloc(1, sizeof(*filter));
-  if (filter == NULL)
+  f = calloc(1, sizeof(*f));
+  if (f == NULL)
   {
     archive_set_error(&a->archive, ENOMEM, "Out of memory");
     return (ARCHIVE_FATAL);
   }
-  filter->bidder = bidder;
-  filter->archive = a;
-  filter->upstream = a->filter;
-  a->filter = filter;
-  r = (bidder->vtable->init)(a->filter);
+  f->bidder = b;
+  f->archive = a;
+  f->upstream = a->filter;
+  a->filter = f;
+  r = (b->vtable->init)(a->filter);
   if (r != ARCHIVE_OK) {
     __archive_read_free_filters(a);
     return (ARCHIVE_FATAL);
   }
-  bidder->name = a->filter->name;
+  b->name = a->filter->name;
 
   a->bypass_filter_bidding = 1;
   return r;
diff --git a/c/archive_read_data_into_fd.c b/c/archive_read_data_into_fd.c
--- a/c/archive_read_data_into_fd.c
+++ b/c/archive_read_data_into_fd.c
@@ -37,6 +37,7 @@
 
 #include "archive.h"
 #include "archive_private.h"
+#include "archive_read_private.h"
 
 /* Maximum amount of data to write at one time. */
 #define	MAX_WRITE	(1024 * 1024)
@@ -97,12 +98,15 @@
 	int64_t fd_offset;
 	int64_t target_offset;
 	int64_t actual_offset = 0;
+	int64_t declared_size;
 	int can_lseek;
 	char *nulls = NULL;
 
 	archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA,
 	    "archive_read_data_into_fd");
 
+	declared_size = ((struct archive_read *)a)->entry_bytes_declared;
+
 	can_lseek = (fstat(fd, &st) == 0) && S_ISREG(st.st_mode);
 	if (can_lseek) {
 		fd_offset = lseek(fd, 0, SEEK_CUR);
@@ -113,6 +117,24 @@
 	while ((r = archive_read_data_block(a, &buff, &size, &target_offset)) ==
 	    ARCHIVE_OK) {
 		const char *p = buff;
+		int overran = 0;
+
+		if (declared_size >= 0) {
+			/* We know the expected size, let's enforce that we don't overrun. */
+			if (target_offset > declared_size) {
+				/* We're already beyond the end? Don't write any more. */
+				target_offset = declared_size;
+				size = 0;
+				overran = 1;
+			} else if (size > (uint64_t)(declared_size - target_offset)) {
+				/* Above is safe because target_offset <= declared_size */
+				/* This block is bigger than the expected remainder */
+				size = (size_t)(declared_size - target_offset);
+				overran = 1;
+			}
+			/* Else size <= expected remainder of data and we're OK. */
+		}
+
 		if (target_offset > actual_offset) {
 			r = pad_to(a, fd, can_lseek, &nulls,
 			    target_offset, actual_offset);
@@ -134,13 +156,37 @@
 			p += bytes_written;
 			size -= bytes_written;
 		}
+		if (overran) {
+			archive_set_error(a, ARCHIVE_ERRNO_MISC,
+			    "Actual entry size exceeds the declared size "
+			    "(%jd); truncated output at the declared size",
+			    (intmax_t)declared_size);
+			r = ARCHIVE_FAILED;
+			goto cleanup;
+		}
 	}
 
 	if (r == ARCHIVE_EOF && target_offset > actual_offset) {
-		r2 = pad_to(a, fd, can_lseek, &nulls,
-		    target_offset, actual_offset);
-		if (r2 != ARCHIVE_OK)
-			r = r2;
+		int trailing_hole_overran =
+		    declared_size >= 0 && target_offset > declared_size;
+		if (trailing_hole_overran)
+			target_offset = declared_size;
+		if (target_offset > actual_offset) {
+			r2 = pad_to(a, fd, can_lseek, &nulls,
+			    target_offset, actual_offset);
+			if (r2 != ARCHIVE_OK)
+				r = r2;
+			else
+				actual_offset = target_offset;
+		}
+		if (trailing_hole_overran) {
+			archive_set_error(a, ARCHIVE_ERRNO_MISC,
+			    "Actual entry size exceeds the declared size "
+			    "(%jd); truncated output at the declared size",
+			    (intmax_t)declared_size);
+			r = ARCHIVE_FAILED;
+			goto cleanup;
+		}
 	}
 
 cleanup:
diff --git a/c/archive_read_disk_posix.c b/c/archive_read_disk_posix.c
--- a/c/archive_read_disk_posix.c
+++ b/c/archive_read_disk_posix.c
@@ -453,6 +453,10 @@
 	a->archive.state = ARCHIVE_STATE_NEW;
 	a->archive.vtable = &archive_read_disk_vtable;
 	a->entry = archive_entry_new2(&a->archive);
+	if (a->entry == NULL) {
+		free(a);
+		return (NULL);
+	}
 	a->lookup_uname = trivial_lookup_uname;
 	a->lookup_gname = trivial_lookup_gname;
 	a->flags = ARCHIVE_READDISK_MAC_COPYFILE;
diff --git a/c/archive_read_disk_set_standard_lookup.c b/c/archive_read_disk_set_standard_lookup.c
--- a/c/archive_read_disk_set_standard_lookup.c
+++ b/c/archive_read_disk_set_standard_lookup.c
@@ -45,6 +45,7 @@
 #endif
 
 #include "archive.h"
+#include "archive_integer.h"
 
 #if defined(_WIN32) && !defined(__CYGWIN__)
 int
@@ -209,7 +210,8 @@
 		 * we just double it and try again.  Because the buffer
 		 * is kept around in the cache object, we shouldn't
 		 * have to do this very often. */
-		nbuff_size = cache->buff_size * 2;
+		if (archive_ckd_mul_size(&nbuff_size, cache->buff_size, 2))
+			break;
 		nbuff = realloc(cache->buff, nbuff_size);
 		if (nbuff == NULL)
 			break;
@@ -276,7 +278,8 @@
 		/* ERANGE means our buffer was too small, but POSIX
 		 * doesn't tell us how big the buffer should be, so
 		 * we just double it and try again. */
-		nbuff_size = cache->buff_size * 2;
+		if (archive_ckd_mul_size(&nbuff_size, cache->buff_size, 2))
+			break;
 		nbuff = realloc(cache->buff, nbuff_size);
 		if (nbuff == NULL)
 			break;
diff --git a/c/archive_read_disk_windows.c b/c/archive_read_disk_windows.c
--- a/c/archive_read_disk_windows.c
+++ b/c/archive_read_disk_windows.c
@@ -37,10 +37,11 @@
 #include <winioctl.h>
 
 #include "archive.h"
-#include "archive_string.h"
 #include "archive_entry.h"
+#include "archive_integer.h"
 #include "archive_private.h"
 #include "archive_read_disk_private.h"
+#include "archive_string.h"
 #include "archive_time_private.h"
 
 #ifndef O_BINARY
@@ -151,6 +152,8 @@
 	size_t			 dirname_length;
 
 	int	 depth;
+	/* Whether this tree began as a wildcard search */
+	int	 rootwild;
 
 	BY_HANDLE_FILE_INFORMATION	lst;
 	BY_HANDLE_FILE_INFORMATION	st;
@@ -555,6 +558,10 @@
 	a->archive.state = ARCHIVE_STATE_NEW;
 	a->archive.vtable = &archive_read_disk_vtable;
 	a->entry = archive_entry_new2(&a->archive);
+	if (a->entry == NULL) {
+		free(a);
+		return (NULL);
+	}
 	a->lookup_uname = trivial_lookup_uname;
 	a->lookup_gname = trivial_lookup_gname;
 	a->flags = ARCHIVE_READDISK_MAC_COPYFILE;
@@ -926,7 +933,27 @@
 		case 0:
 			return (ARCHIVE_EOF);
 		case TREE_POSTDESCENT:
+			if (t->depth == 1 && t->rootwild && t->symlink_mode == 'H') {
+				/*
+				 * We have descended from a directory at depth 0 specified by a
+				 * wildcard search in 'H'ybrid link mode. All items under this
+				 * directory should be 'P'hysical.
+				 */
+				t->symlink_mode = 'P';
+				a->follow_symlinks = 0;
+			}
+			break;
 		case TREE_POSTASCENT:
+			if (t->depth == 0 && t->rootwild && t->initial_symlink_mode == 'H') {
+				/*
+				 * We have returned to the root of a wildcard search, in 'H'ybrid
+				 * mode, so restore the symlink mode we overwrote in
+				 * TREE_POSTDESCENT.
+				 */
+				t->symlink_mode = t->initial_symlink_mode;
+				/* Mimics setup done in setup_symlink_mode */
+				a->follow_symlinks = 1;
+			}
 			break;
 		case TREE_REGULAR:
 			lst = tree_current_lstat(t);
@@ -966,7 +993,14 @@
 	switch(t->symlink_mode) {
 	case 'H':
 		/* 'H': After the first item, rest like 'P'. */
-		t->symlink_mode = 'P';
+		if (!t->rootwild) {
+			/*
+			 * 'H': When we started with a wildcard, entries at the starting
+			 * depth should be kept in `H` mode; switching into 'P' mode
+			 * is handled in TREE_POSTDESCENT when we enter depth = 1.
+			 */
+			t->symlink_mode = 'P';
+		}
 		/* 'H': First item (from command line) like 'L'. */
 		/* FALLTHROUGH */
 	case 'L':
@@ -1733,6 +1767,7 @@
 	t->full_path_dir_length = 0;
 	t->dirname_length = 0;
 	t->depth = 0;
+	t->rootwild = 0;
 	t->descend = 0;
 	t->current = NULL;
 	t->d = INVALID_HANDLE_VALUE;
@@ -1786,6 +1821,7 @@
 			t->full_path.length = wcslen(t->full_path.s);
 			t->full_path_dir_length = archive_strlen(&t->full_path);
 		}
+		t->rootwild = 1;
 	}
 	tree_push(t, base, t->full_path.s, 0, 0, 0, NULL);
 	archive_wstring_free(&ws);
@@ -2505,7 +2541,12 @@
 			    (DWORD)outranges_size, &retbytes, NULL);
 			if (ret == 0 && GetLastError() == ERROR_MORE_DATA) {
 				free(outranges);
-				outranges_size *= 2;
+				if (archive_ckd_mul_size(&outranges_size, outranges_size, 2)) {
+					archive_set_error(&a->archive, ENOMEM,
+					    "Couldn't allocate memory");
+					exit_sts = ARCHIVE_FATAL;
+					goto exit_setup_sparse;
+				}
 				outranges = (FILE_ALLOCATED_RANGE_BUFFER *)
 				    malloc(outranges_size);
 				if (outranges == NULL) {
diff --git a/c/archive_read_open_fd.c b/c/archive_read_open_fd.c
--- a/c/archive_read_open_fd.c
+++ b/c/archive_read_open_fd.c
@@ -69,6 +69,7 @@
 	la_seek_stat_t st;
 	struct read_fd_data *mine;
 	void *b;
+	int r;
 
 	archive_clear_error(a);
 	if (la_seek_fstat(fd, &st) != 0) {
@@ -107,7 +108,9 @@
 	archive_read_set_skip_callback(a, file_skip);
 	archive_read_set_seek_callback(a, file_seek);
 	archive_read_set_close_callback(a, file_close);
-	archive_read_set_callback_data(a, mine);
+	r = archive_read_set_callback_data(a, mine);
+	if (r < 0)
+		return (r);
 	return (archive_read_open1(a));
 }
 
diff --git a/c/archive_read_open_file.c b/c/archive_read_open_file.c
--- a/c/archive_read_open_file.c
+++ b/c/archive_read_open_file.c
@@ -70,6 +70,7 @@
 	struct read_FILE_data *mine;
 	size_t block_size = 128 * 1024;
 	void *b;
+	int r;
 
 	archive_clear_error(a);
 	mine = calloc(1, sizeof(*mine));
@@ -104,7 +105,9 @@
 	archive_read_set_skip_callback(a, FILE_skip);
 	archive_read_set_seek_callback(a, FILE_seek);
 	archive_read_set_close_callback(a, FILE_close);
-	archive_read_set_callback_data(a, mine);
+	r = archive_read_set_callback_data(a, mine);
+	if (r < 0)
+		return (r);
 	return (archive_read_open1(a));
 }
 
diff --git a/c/archive_read_open_memory.c b/c/archive_read_open_memory.c
--- a/c/archive_read_open_memory.c
+++ b/c/archive_read_open_memory.c
@@ -68,6 +68,7 @@
     size_t size, size_t read_size)
 {
 	struct read_memory_data *mine;
+	int r;
 
 	mine = calloc(1, sizeof(*mine));
 	if (mine == NULL) {
@@ -82,7 +83,9 @@
 	archive_read_set_seek_callback(a, memory_read_seek);
 	archive_read_set_skip_callback(a, memory_read_skip);
 	archive_read_set_close_callback(a, memory_read_close);
-	archive_read_set_callback_data(a, mine);
+	r = archive_read_set_callback_data(a, mine);
+	if (r < 0)
+		return (r);
 	return (archive_read_open1(a));
 }
 
diff --git a/c/archive_read_private.h b/c/archive_read_private.h
--- a/c/archive_read_private.h
+++ b/c/archive_read_private.h
@@ -76,10 +76,10 @@
 struct archive_read_filter_vtable {
 	/* Return next block. */
 	ssize_t (*read)(struct archive_read_filter *, const void **);
-	/* Close (just this filter) and free(self). */
-	int (*close)(struct archive_read_filter *self);
+	/* Close (just this filter) and free(filter). */
+	int (*close)(struct archive_read_filter *);
 	/* Read any header metadata if available. */
-	int (*read_header)(struct archive_read_filter *self, struct archive_entry *entry);
+	int (*read_header)(struct archive_read_filter *, struct archive_entry *);
 };
 
 /*
@@ -179,6 +179,15 @@
 
 	/* File offset of beginning of most recently-read header. */
 	int64_t		  header_position;
+
+	/*
+	 * Declared uncompressed size of the entry whose header was most
+	 * recently read, or -1 if no size was declared.  Lets format-
+	 * agnostic consumers like archive_read_data_into_fd() cap their
+	 * output at the size an entry actually promised, regardless of
+	 * whether the format reader enforces that itself.
+	 */
+	int64_t		  entry_bytes_declared;
 
 	/* Nodes and offsets of compressed data block */
 	unsigned int data_start_node;
diff --git a/c/archive_read_support_filter_bzip2.c b/c/archive_read_support_filter_bzip2.c
--- a/c/archive_read_support_filter_bzip2.c
+++ b/c/archive_read_support_filter_bzip2.c
@@ -50,7 +50,7 @@
 #include "archive_read_private.h"
 
 #if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
-struct private_data {
+struct bzip2 {
 	bz_stream	 stream;
 	char		*out_block;
 	size_t		 out_block_size;
@@ -112,15 +112,15 @@
  * from verifying as much as we would like.
  */
 static int
-bzip2_reader_bid(struct archive_read_filter_bidder *self, struct archive_read_filter *filter)
+bzip2_reader_bid(struct archive_read_filter_bidder *b, struct archive_read_filter *f)
 {
 	const unsigned char *buffer;
 	int bits_checked;
 
-	(void)self; /* UNUSED */
+	(void)b; /* UNUSED */
 
 	/* Minimal bzip2 archive is 14 bytes. */
-	buffer = __archive_read_filter_ahead(filter, 14, NULL);
+	buffer = __archive_read_filter_ahead(f, 14, NULL);
 	if (buffer == NULL)
 		return (0);
 
@@ -156,16 +156,16 @@
  * in case that's available.
  */
 static int
-bzip2_reader_init(struct archive_read_filter *self)
+bzip2_reader_init(struct archive_read_filter *f)
 {
 	int r;
 
-	r = __archive_read_program(self, "bzip2 -d");
+	r = __archive_read_program(f, "bzip2 -d");
 	/* Note: We set the format here even if __archive_read_program()
 	 * above fails.  We do, after all, know what the format is
 	 * even if we weren't able to read it. */
-	self->code = ARCHIVE_FILTER_BZIP2;
-	self->name = "bzip2";
+	f->code = ARCHIVE_FILTER_BZIP2;
+	f->name = "bzip2";
 	return (r);
 }
 
@@ -182,29 +182,29 @@
  * Setup the callbacks.
  */
 static int
-bzip2_reader_init(struct archive_read_filter *self)
+bzip2_reader_init(struct archive_read_filter *f)
 {
 	static const size_t out_block_size = 64 * 1024;
 	void *out_block;
-	struct private_data *state;
+	struct bzip2 *bzip2;
 
-	self->code = ARCHIVE_FILTER_BZIP2;
-	self->name = "bzip2";
+	f->code = ARCHIVE_FILTER_BZIP2;
+	f->name = "bzip2";
 
-	state = calloc(1, sizeof(*state));
+	bzip2 = calloc(1, sizeof(*bzip2));
 	out_block = malloc(out_block_size);
-	if (state == NULL || out_block == NULL) {
-		archive_set_error(&self->archive->archive, ENOMEM,
+	if (bzip2 == NULL || out_block == NULL) {
+		archive_set_error(&f->archive->archive, ENOMEM,
 		    "Can't allocate data for bzip2 decompression");
 		free(out_block);
-		free(state);
+		free(bzip2);
 		return (ARCHIVE_FATAL);
 	}
 
-	self->data = state;
-	state->out_block_size = out_block_size;
-	state->out_block = out_block;
-	self->vtable = &bzip2_reader_vtable;
+	f->data = bzip2;
+	bzip2->out_block_size = out_block_size;
+	bzip2->out_block = out_block;
+	f->vtable = &bzip2_reader_vtable;
 
 	return (ARCHIVE_OK);
 }
@@ -213,44 +213,42 @@
  * Return the next block of decompressed data.
  */
 static ssize_t
-bzip2_filter_read(struct archive_read_filter *self, const void **p)
+bzip2_filter_read(struct archive_read_filter *f, const void **p)
 {
-	struct private_data *state;
+	struct bzip2 *bzip2 = f->data;
 	size_t decompressed;
 	const char *read_buf;
 	ssize_t ret;
 
-	state = (struct private_data *)self->data;
-
-	if (state->eof) {
+	if (bzip2->eof) {
 		*p = NULL;
 		return (0);
 	}
 
 	/* Empty our output buffer. */
-	state->stream.next_out = state->out_block;
-	state->stream.avail_out = (uint32_t)state->out_block_size;
+	bzip2->stream.next_out = bzip2->out_block;
+	bzip2->stream.avail_out = (uint32_t)bzip2->out_block_size;
 
 	/* Try to fill the output buffer. */
 	for (;;) {
 		ssize_t max_in;
 
-		if (!state->valid) {
-			if (bzip2_reader_bid(self->bidder, self->upstream) == 0) {
-				state->eof = 1;
-				*p = state->out_block;
-				decompressed = state->stream.next_out
-				    - state->out_block;
+		if (!bzip2->valid) {
+			if (bzip2_reader_bid(f->bidder, f->upstream) == 0) {
+				bzip2->eof = 1;
+				*p = bzip2->out_block;
+				decompressed = bzip2->stream.next_out
+				    - bzip2->out_block;
 				return (decompressed);
 			}
 			/* Initialize compression library. */
-			ret = BZ2_bzDecompressInit(&(state->stream),
+			ret = BZ2_bzDecompressInit(&(bzip2->stream),
 					   0 /* library verbosity */,
 					   0 /* don't use low-mem algorithm */);
 
 			/* If init fails, try low-memory algorithm instead. */
 			if (ret == BZ_MEM_ERROR)
-				ret = BZ2_bzDecompressInit(&(state->stream),
+				ret = BZ2_bzDecompressInit(&(bzip2->stream),
 					   0 /* library verbosity */,
 					   1 /* do use low-mem algo */);
 
@@ -269,63 +267,63 @@
 					detail = "mis-compiled library";
 					break;
 				}
-				archive_set_error(&self->archive->archive, err,
+				archive_set_error(&f->archive->archive, err,
 				    "Internal error initializing decompressor%s%s",
 				    detail == NULL ? "" : ": ",
 				    detail);
 				return (ARCHIVE_FATAL);
 			}
-			state->valid = 1;
+			bzip2->valid = 1;
 		}
 
 		/* stream.next_in is really const, but bzlib
 		 * doesn't declare it so. <sigh> */
 		read_buf =
-		    __archive_read_filter_ahead(self->upstream, 1, &ret);
+		    __archive_read_filter_ahead(f->upstream, 1, &ret);
 		if (read_buf == NULL) {
-			archive_set_error(&self->archive->archive,
+			archive_set_error(&f->archive->archive,
 			    ARCHIVE_ERRNO_MISC,
 			    "truncated bzip2 input");
 			return (ARCHIVE_FATAL);
 		}
-		state->stream.next_in = (char *)(uintptr_t)read_buf;
+		bzip2->stream.next_in = (char *)(uintptr_t)read_buf;
 		if (UINT_MAX >= SSIZE_MAX)
 			max_in = SSIZE_MAX;
 		else
 			max_in = UINT_MAX;
 		if (ret > max_in)
 			ret = max_in;
-		state->stream.avail_in = (uint32_t)ret;
+		bzip2->stream.avail_in = (uint32_t)ret;
 
 		/* Decompress as much as we can in one pass. */
-		ret = BZ2_bzDecompress(&(state->stream));
-		__archive_read_filter_consume(self->upstream,
-		    state->stream.next_in - read_buf);
+		ret = BZ2_bzDecompress(&(bzip2->stream));
+		__archive_read_filter_consume(f->upstream,
+		    bzip2->stream.next_in - read_buf);
 
 		switch (ret) {
 		case BZ_STREAM_END: /* Found end of stream. */
-			switch (BZ2_bzDecompressEnd(&(state->stream))) {
+			switch (BZ2_bzDecompressEnd(&(bzip2->stream))) {
 			case BZ_OK:
 				break;
 			default:
-				archive_set_error(&(self->archive->archive),
+				archive_set_error(&(f->archive->archive),
 					  ARCHIVE_ERRNO_MISC,
 					  "Failed to clean up decompressor");
 				return (ARCHIVE_FATAL);
 			}
-			state->valid = 0;
+			bzip2->valid = 0;
 			/* FALLTHROUGH */
 		case BZ_OK: /* Decompressor made some progress. */
 			/* If we filled our buffer, update stats and return. */
-			if (state->stream.avail_out == 0) {
-				*p = state->out_block;
-				decompressed = state->stream.next_out
-				    - state->out_block;
+			if (bzip2->stream.avail_out == 0) {
+				*p = bzip2->out_block;
+				decompressed = bzip2->stream.next_out
+				    - bzip2->out_block;
 				return (decompressed);
 			}
 			break;
 		default: /* Return an error. */
-			archive_set_error(&self->archive->archive,
+			archive_set_error(&f->archive->archive,
 			    ARCHIVE_ERRNO_MISC, "bzip decompression failed");
 			return (ARCHIVE_FATAL);
 		}
@@ -336,28 +334,26 @@
  * Clean up the decompressor.
  */
 static int
-bzip2_filter_close(struct archive_read_filter *self)
+bzip2_filter_close(struct archive_read_filter *f)
 {
-	struct private_data *state;
+	struct bzip2 *bzip2 = f->data;
 	int ret = ARCHIVE_OK;
 
-	state = (struct private_data *)self->data;
-
-	if (state->valid) {
-		switch (BZ2_bzDecompressEnd(&state->stream)) {
+	if (bzip2->valid) {
+		switch (BZ2_bzDecompressEnd(&bzip2->stream)) {
 		case BZ_OK:
 			break;
 		default:
-			archive_set_error(&self->archive->archive,
+			archive_set_error(&f->archive->archive,
 					  ARCHIVE_ERRNO_MISC,
 					  "Failed to clean up decompressor");
 			ret = ARCHIVE_FATAL;
 		}
-		state->valid = 0;
+		bzip2->valid = 0;
 	}
 
-	free(state->out_block);
-	free(state);
+	free(bzip2->out_block);
+	free(bzip2);
 	return (ret);
 }
 
diff --git a/c/archive_read_support_filter_compress.c b/c/archive_read_support_filter_compress.c
--- a/c/archive_read_support_filter_compress.c
+++ b/c/archive_read_support_filter_compress.c
@@ -90,7 +90,7 @@
  * names and structure here don't exactly match those used by compress.
  */
 
-struct private_data {
+struct compress {
 	/* Input variables. */
 	const unsigned char	*next_in;
 	size_t			 avail_in;
@@ -169,16 +169,16 @@
  * This logic returns zero if any part of the signature fails.
  */
 static int
-compress_bidder_bid(struct archive_read_filter_bidder *self,
-    struct archive_read_filter *filter)
+compress_bidder_bid(struct archive_read_filter_bidder *b,
+    struct archive_read_filter *f)
 {
 	const unsigned char *buffer;
 	int bits_checked;
 
-	(void)self; /* UNUSED */
+	(void)b; /* UNUSED */
 
 	/* Shortest valid compress file is 3 bytes. */
-	buffer = __archive_read_filter_ahead(filter, 3, NULL);
+	buffer = __archive_read_filter_ahead(f, 3, NULL);
 
 	if (buffer == NULL)
 		return (0);
@@ -207,69 +207,69 @@
  * Setup the callbacks.
  */
 static int
-compress_bidder_init(struct archive_read_filter *self)
+compress_bidder_init(struct archive_read_filter *f)
 {
-	struct private_data *state;
+	struct compress *compress;
 	static const size_t out_block_size = 64 * 1024;
 	void *out_block;
 
-	self->code = ARCHIVE_FILTER_COMPRESS;
-	self->name = "compress (.Z)";
+	f->code = ARCHIVE_FILTER_COMPRESS;
+	f->name = "compress (.Z)";
 
-	state = calloc(1, sizeof(*state));
+	compress = calloc(1, sizeof(*compress));
 	out_block = malloc(out_block_size);
-	if (state == NULL || out_block == NULL) {
+	if (compress == NULL || out_block == NULL) {
 		free(out_block);
-		free(state);
-		archive_set_error(&self->archive->archive, ENOMEM,
+		free(compress);
+		archive_set_error(&f->archive->archive, ENOMEM,
 		    "Can't allocate data for %s decompression",
-		    self->name);
+		    f->name);
 		return (ARCHIVE_FATAL);
 	}
 
-	self->data = state;
-	state->out_block_size = out_block_size;
-	state->out_block = out_block;
-	self->vtable = &compress_reader_vtable;
+	f->data = compress;
+	compress->out_block_size = out_block_size;
+	compress->out_block = out_block;
+	f->vtable = &compress_reader_vtable;
 
 	return (ARCHIVE_OK);
 }
 
 static int
-compress_filter_init(struct archive_read_filter *self)
+compress_filter_init(struct archive_read_filter *f)
 {
-	struct private_data *state = (struct private_data *)self->data;
+	struct compress *compress = f->data;
 	int code;
 
-	state->initialized = 1;
+	compress->initialized = 1;
 
-	(void)getbits(self, 8); /* Skip first signature byte. */
-	(void)getbits(self, 8); /* Skip second signature byte. */
+	(void)getbits(f, 8); /* Skip first signature byte. */
+	(void)getbits(f, 8); /* Skip second signature byte. */
 
 	/* Get compression parameters. */
-	code = getbits(self, 8);
+	code = getbits(f, 8);
 	if (code < 0 || (code & 0x1f) > 16) {
-		archive_set_error(&self->archive->archive, -1,
+		archive_set_error(&f->archive->archive, -1,
 		    "Invalid compressed data");
 		return (ARCHIVE_FATAL);
 	}
-	state->maxcode_bits = code & 0x1f;
-	state->maxcode = (1 << state->maxcode_bits);
-	state->use_reset_code = code & 0x80;
+	compress->maxcode_bits = code & 0x1f;
+	compress->maxcode = (1 << compress->maxcode_bits);
+	compress->use_reset_code = code & 0x80;
 
 	/* Initialize decompressor. */
-	state->free_ent = 256;
-	state->stackp = state->stack;
-	if (state->use_reset_code)
-		state->free_ent++;
-	state->bits = 9;
-	state->section_end_code = (1<<state->bits) - 1;
-	state->oldcode = -1;
+	compress->free_ent = 256;
+	compress->stackp = compress->stack;
+	if (compress->use_reset_code)
+		compress->free_ent++;
+	compress->bits = 9;
+	compress->section_end_code = (1<<compress->bits) - 1;
+	compress->oldcode = -1;
 	for (code = 255; code >= 0; code--) {
-		state->prefix[code] = 0;
-		state->suffix[code] = code;
+		compress->prefix[code] = 0;
+		compress->suffix[code] = code;
 	}
-	next_code(self);
+	next_code(f);
 
 	return (ARCHIVE_OK);
 }
@@ -279,32 +279,31 @@
  * as necessary.
  */
 static ssize_t
-compress_filter_read(struct archive_read_filter *self, const void **pblock)
+compress_filter_read(struct archive_read_filter *f, const void **pblock)
 {
-	struct private_data *state;
+	struct compress *compress = f->data;
 	unsigned char *p, *start, *end;
 	int ret;
 
-	state = (struct private_data *)self->data;
-	if (!state->initialized) {
-		ret = compress_filter_init(self);
+	if (!compress->initialized) {
+		ret = compress_filter_init(f);
 		if (ret != ARCHIVE_OK)
 			return (ret);
 	}
-	if (state->end_of_stream) {
+	if (compress->end_of_stream) {
 		*pblock = NULL;
 		return (0);
 	}
-	p = start = (unsigned char *)state->out_block;
-	end = start + state->out_block_size;
+	p = start = (unsigned char *)compress->out_block;
+	end = start + compress->out_block_size;
 
-	while (p < end && !state->end_of_stream) {
-		if (state->stackp > state->stack) {
-			*p++ = *--state->stackp;
+	while (p < end && !compress->end_of_stream) {
+		if (compress->stackp > compress->stack) {
+			*p++ = *--compress->stackp;
 		} else {
-			ret = next_code(self);
+			ret = next_code(f);
 			if (ret == -1)
-				state->end_of_stream = ret;
+				compress->end_of_stream = ret;
 			else if (ret != ARCHIVE_OK)
 				return (ret);
 		}
@@ -318,12 +317,12 @@
  * Close and release the filter.
  */
 static int
-compress_filter_close(struct archive_read_filter *self)
+compress_filter_close(struct archive_read_filter *f)
 {
-	struct private_data *state = (struct private_data *)self->data;
+	struct compress *compress = f->data;
 
-	free(state->out_block);
-	free(state);
+	free(compress->out_block);
+	free(compress);
 	return (ARCHIVE_OK);
 }
 
@@ -333,18 +332,18 @@
  * format error, ARCHIVE_EOF if we hit end of data, ARCHIVE_OK otherwise.
  */
 static int
-next_code(struct archive_read_filter *self)
+next_code(struct archive_read_filter *f)
 {
-	struct private_data *state = (struct private_data *)self->data;
+	struct compress *compress = f->data;
 	int code, newcode;
 
 again:
-	code = newcode = getbits(self, state->bits);
+	code = newcode = getbits(f, compress->bits);
 	if (code < 0)
 		return (code);
 
 	/* If it's a reset code, reset the dictionary. */
-	if ((code == 256) && state->use_reset_code) {
+	if ((code == 256) && compress->use_reset_code) {
 		/*
 		 * The original 'compress' implementation blocked its
 		 * I/O in a manner that resulted in junk bytes being
@@ -352,63 +351,63 @@
 		 * this junk.  (Yes, the number of *bytes* to skip is
 		 * a function of the current *bit* length.)
 		 */
-		int skip_bytes =  state->bits -
-		    (state->bytes_in_section % state->bits);
-		skip_bytes %= state->bits;
-		state->bits_avail = 0; /* Discard rest of this byte. */
+		int skip_bytes =  compress->bits -
+		    (compress->bytes_in_section % compress->bits);
+		skip_bytes %= compress->bits;
+		compress->bits_avail = 0; /* Discard rest of this byte. */
 		while (skip_bytes-- > 0) {
-			code = getbits(self, 8);
+			code = getbits(f, 8);
 			if (code < 0)
 				return (code);
 		}
 		/* Now, actually do the reset. */
-		state->bytes_in_section = 0;
-		state->bits = 9;
-		state->section_end_code = (1 << state->bits) - 1;
-		state->free_ent = 257;
-		state->oldcode = -1;
+		compress->bytes_in_section = 0;
+		compress->bits = 9;
+		compress->section_end_code = (1 << compress->bits) - 1;
+		compress->free_ent = 257;
+		compress->oldcode = -1;
 		goto again;
 	}
 
-	if (code > state->free_ent
-	    || (code == state->free_ent && state->oldcode < 0)) {
+	if (code > compress->free_ent
+	    || (code == compress->free_ent && compress->oldcode < 0)) {
 		/* An invalid code is a fatal error. */
-		archive_set_error(&(self->archive->archive), -1,
+		archive_set_error(&(f->archive->archive), -1,
 		    "Invalid compressed data");
 		return (ARCHIVE_FATAL);
 	}
 
 	/* Special case for KwKwK string. */
-	if (code >= state->free_ent) {
-		*state->stackp++ = state->finbyte;
-		code = state->oldcode;
+	if (code >= compress->free_ent) {
+		*compress->stackp++ = compress->finbyte;
+		code = compress->oldcode;
 	}
 
 	/* Generate output characters in reverse order. */
 	while (code >= 256) {
-		*state->stackp++ = state->suffix[code];
-		code = state->prefix[code];
+		*compress->stackp++ = compress->suffix[code];
+		code = compress->prefix[code];
 	}
-	*state->stackp++ = state->finbyte = code;
+	*compress->stackp++ = compress->finbyte = code;
 
 	/* Generate the new entry. */
-	code = state->free_ent;
-	if (code < state->maxcode && state->oldcode >= 0) {
-		state->prefix[code] = state->oldcode;
-		state->suffix[code] = state->finbyte;
-		++state->free_ent;
+	code = compress->free_ent;
+	if (code < compress->maxcode && compress->oldcode >= 0) {
+		compress->prefix[code] = compress->oldcode;
+		compress->suffix[code] = compress->finbyte;
+		++compress->free_ent;
 	}
-	if (state->free_ent > state->section_end_code) {
-		state->bits++;
-		state->bytes_in_section = 0;
-		if (state->bits == state->maxcode_bits)
-			state->section_end_code = state->maxcode;
+	if (compress->free_ent > compress->section_end_code) {
+		compress->bits++;
+		compress->bytes_in_section = 0;
+		if (compress->bits == compress->maxcode_bits)
+			compress->section_end_code = compress->maxcode;
 		else
-			state->section_end_code = (1 << state->bits) - 1;
+			compress->section_end_code = (1 << compress->bits) - 1;
 	}
 
 	/* Remember previous code. */
-	state->oldcode = newcode;
+	compress->oldcode = newcode;
 	return (ARCHIVE_OK);
 }
 
@@ -418,9 +417,9 @@
  * -1 indicates end of available data.
  */
 static int
-getbits(struct archive_read_filter *self, int n)
+getbits(struct archive_read_filter *f, int n)
 {
-	struct private_data *state = (struct private_data *)self->data;
+	struct compress *compress = f->data;
 	int code;
 	ssize_t ret;
 	static const int mask[] = {
@@ -428,31 +427,31 @@
 		0x1ff, 0x3ff, 0x7ff, 0xfff, 0x1fff, 0x3fff, 0x7fff, 0xffff
 	};
 
-	while (state->bits_avail < n) {
-		if (state->avail_in <= 0) {
-			if (state->consume_unnotified) {
-				__archive_read_filter_consume(self->upstream,
-					state->consume_unnotified);
-				state->consume_unnotified = 0;
+	while (compress->bits_avail < n) {
+		if (compress->avail_in <= 0) {
+			if (compress->consume_unnotified) {
+				__archive_read_filter_consume(f->upstream,
+					compress->consume_unnotified);
+				compress->consume_unnotified = 0;
 			}
-			state->next_in
-			    = __archive_read_filter_ahead(self->upstream,
+			compress->next_in
+			    = __archive_read_filter_ahead(f->upstream,
 				1, &ret);
 			if (ret == 0)
 				return (-1);
-			if (state->next_in == NULL)
+			if (compress->next_in == NULL)
 				return (ARCHIVE_FATAL);
-			state->consume_unnotified = state->avail_in = ret;
+			compress->consume_unnotified = compress->avail_in = ret;
 		}
-		state->bit_buffer |= *state->next_in++ << state->bits_avail;
-		state->avail_in--;
-		state->bits_avail += 8;
-		state->bytes_in_section++;
+		compress->bit_buffer |= *compress->next_in++ << compress->bits_avail;
+		compress->avail_in--;
+		compress->bits_avail += 8;
+		compress->bytes_in_section++;
 	}
 
-	code = state->bit_buffer;
-	state->bit_buffer >>= n;
-	state->bits_avail -= n;
+	code = compress->bit_buffer;
+	compress->bit_buffer >>= n;
+	compress->bits_avail -= n;
 
 	return (code & mask[n]);
 }
diff --git a/c/archive_read_support_filter_grzip.c b/c/archive_read_support_filter_grzip.c
--- a/c/archive_read_support_filter_grzip.c
+++ b/c/archive_read_support_filter_grzip.c
@@ -76,14 +76,14 @@
  * Bidder just verifies the header and returns the number of verified bits.
  */
 static int
-grzip_bidder_bid(struct archive_read_filter_bidder *self,
-    struct archive_read_filter *filter)
+grzip_bidder_bid(struct archive_read_filter_bidder *b,
+    struct archive_read_filter *f)
 {
 	const unsigned char *p;
 
-	(void)self; /* UNUSED */
+	(void)b; /* UNUSED */
 
-	p = __archive_read_filter_ahead(filter, sizeof(grzip_magic), NULL);
+	p = __archive_read_filter_ahead(f, sizeof(grzip_magic), NULL);
 	if (p == NULL)
 		return (0);
 
@@ -94,15 +94,15 @@
 }
 
 static int
-grzip_bidder_init(struct archive_read_filter *self)
+grzip_bidder_init(struct archive_read_filter *f)
 {
 	int r;
 
-	r = __archive_read_program(self, "grzip -d");
+	r = __archive_read_program(f, "grzip -d");
 	/* Note: We set the format here even if __archive_read_program()
 	 * above fails.  We do, after all, know what the format is
 	 * even if we weren't able to read it. */
-	self->code = ARCHIVE_FILTER_GRZIP;
-	self->name = "grzip";
+	f->code = ARCHIVE_FILTER_GRZIP;
+	f->name = "grzip";
 	return (r);
 }
diff --git a/c/archive_read_support_filter_gzip.c b/c/archive_read_support_filter_gzip.c
--- a/c/archive_read_support_filter_gzip.c
+++ b/c/archive_read_support_filter_gzip.c
@@ -51,7 +51,7 @@
 #include "archive_read_private.h"
 
 #ifdef HAVE_ZLIB_H
-struct private_data {
+struct gzip {
 	z_stream	 stream;
 	char		 in_stream;
 	unsigned char	*out_block;
@@ -120,9 +120,9 @@
 #define MAX_FILENAME_LENGTH (1024 * 1024L)
 #define MAX_COMMENT_LENGTH (1024 * 1024L)
 static ssize_t
-peek_at_header(struct archive_read_filter *filter, int *pbits,
+peek_at_header(struct archive_read_filter *f, int *pbits,
 #ifdef HAVE_ZLIB_H
-	       struct private_data *state
+	       struct gzip *gzip
 #else
 	       void *state
 #endif
@@ -139,7 +139,7 @@
 	/* Start by looking at the first ten bytes of the header, which
 	 * is all fixed layout. */
 	len = 10;
-	p = __archive_read_filter_ahead(filter, len, &avail);
+	p = __archive_read_filter_ahead(f, len, &avail);
 	if (p == NULL)
 		return (0);
 	/* We only support deflation- third byte must be 0x08. */
@@ -152,8 +152,8 @@
 	header_flags = p[3];
 	/* Bytes 4-7 are mod time in little endian. */
 #ifdef HAVE_ZLIB_H
-	if (state)
-		state->mtime = archive_le32dec(p + 4);
+	if (gzip)
+		gzip->mtime = archive_le32dec(p + 4);
 #endif
 	/* Byte 8 is deflate flags. */
 	/* XXXX TODO: return deflate flags back to consume_header for use
@@ -162,7 +162,7 @@
 
 	/* Optional extra data:  2 byte length plus variable body. */
 	if (header_flags & 4) {
-		p = __archive_read_filter_ahead(filter, len + 2, &avail);
+		p = __archive_read_filter_ahead(f, len + 2, &avail);
 		if (p == NULL)
 			return (0);
 		len += archive_le16dec(p + len);
@@ -180,7 +180,7 @@
 				if (avail > MAX_FILENAME_LENGTH) {
 					return (0);
 				}
-				p = __archive_read_filter_ahead(filter,
+				p = __archive_read_filter_ahead(f,
 				    len, &avail);
 			}
 			if (p == NULL)
@@ -188,10 +188,10 @@
 		} while (p[len - 1] != 0);
 
 #ifdef HAVE_ZLIB_H
-		if (state) {
+		if (gzip) {
 			/* Reset the name in case of repeat header reads. */
-			free(state->name);
-			state->name = strdup((const char *)&p[file_start]);
+			free(gzip->name);
+			gzip->name = strdup((const char *)&p[file_start]);
 		}
 #endif
 	}
@@ -204,7 +204,7 @@
 				if (avail > MAX_COMMENT_LENGTH) {
 					return (0);
 				}
-				p = __archive_read_filter_ahead(filter,
+				p = __archive_read_filter_ahead(f,
 				    len, &avail);
 			}
 			if (p == NULL)
@@ -214,7 +214,7 @@
 
 	/* Optional header CRC */
 	if ((header_flags & 2)) {
-		p = __archive_read_filter_ahead(filter, len + 2, &avail);
+		p = __archive_read_filter_ahead(f, len + 2, &avail);
 		if (p == NULL)
 			return (0);
 #if 0
@@ -236,14 +236,14 @@
  * Bidder just verifies the header and returns the number of verified bits.
  */
 static int
-gzip_bidder_bid(struct archive_read_filter_bidder *self,
-    struct archive_read_filter *filter)
+gzip_bidder_bid(struct archive_read_filter_bidder *b,
+    struct archive_read_filter *f)
 {
 	int bits_checked;
 
-	(void)self; /* UNUSED */
+	(void)b; /* UNUSED */
 
-	if (peek_at_header(filter, &bits_checked, NULL))
+	if (peek_at_header(f, &bits_checked, NULL))
 		return (bits_checked);
 	return (0);
 }
@@ -256,35 +256,33 @@
  * in case that's available.
  */
 static int
-gzip_bidder_init(struct archive_read_filter *self)
+gzip_bidder_init(struct archive_read_filter *f)
 {
 	int r;
 
-	r = __archive_read_program(self, "gzip -d");
+	r = __archive_read_program(f, "gzip -d");
 	/* Note: We set the format here even if __archive_read_program()
 	 * above fails.  We do, after all, know what the format is
 	 * even if we weren't able to read it. */
-	self->code = ARCHIVE_FILTER_GZIP;
-	self->name = "gzip";
+	f->code = ARCHIVE_FILTER_GZIP;
+	f->name = "gzip";
 	return (r);
 }
 
 #else
 
 static int
-gzip_read_header(struct archive_read_filter *self, struct archive_entry *entry)
+gzip_read_header(struct archive_read_filter *f, struct archive_entry *entry)
 {
-	struct private_data *state;
-
-	state = (struct private_data *)self->data;
+	struct gzip *gzip = f->data;
 
 	/* An mtime of 0 is considered invalid/missing. */
-	if (state->mtime != 0)
-		archive_entry_set_mtime(entry, state->mtime, 0);
+	if (gzip->mtime != 0)
+		archive_entry_set_mtime(entry, gzip->mtime, 0);
 
 	/* If the name is available, extract it. */
-	if (state->name)
-		archive_entry_set_pathname(entry, state->name);
+	if (gzip->name)
+		archive_entry_set_pathname(entry, gzip->name);
 
 	return (ARCHIVE_OK);
 }
@@ -302,59 +300,57 @@
  * Initialize the filter object.
  */
 static int
-gzip_bidder_init(struct archive_read_filter *self)
+gzip_bidder_init(struct archive_read_filter *f)
 {
-	struct private_data *state;
+	struct gzip *gzip;
 	static const size_t out_block_size = 64 * 1024;
 	void *out_block;
 
-	self->code = ARCHIVE_FILTER_GZIP;
-	self->name = "gzip";
+	f->code = ARCHIVE_FILTER_GZIP;
+	f->name = "gzip";
 
-	state = calloc(1, sizeof(*state));
+	gzip = calloc(1, sizeof(*gzip));
 	out_block = malloc(out_block_size);
-	if (state == NULL || out_block == NULL) {
+	if (gzip == NULL || out_block == NULL) {
 		free(out_block);
-		free(state);
-		archive_set_error(&self->archive->archive, ENOMEM,
+		free(gzip);
+		archive_set_error(&f->archive->archive, ENOMEM,
 		    "Can't allocate data for gzip decompression");
 		return (ARCHIVE_FATAL);
 	}
 
-	self->data = state;
-	state->out_block_size = out_block_size;
-	state->out_block = out_block;
-	self->vtable = &gzip_reader_vtable;
+	f->data = gzip;
+	gzip->out_block_size = out_block_size;
+	gzip->out_block = out_block;
+	f->vtable = &gzip_reader_vtable;
 
-	state->in_stream = 0; /* We're not actually within a stream yet. */
+	gzip->in_stream = 0; /* We're not actually within a stream yet. */
 
 	return (ARCHIVE_OK);
 }
 
 static int
-consume_header(struct archive_read_filter *self)
+consume_header(struct archive_read_filter *f)
 {
-	struct private_data *state;
+	struct gzip *gzip = f->data;
 	ssize_t avail, max_in;
 	size_t len;
 	int ret;
 
-	state = (struct private_data *)self->data;
-
 	/* If this is a real header, consume it. */
-	len = peek_at_header(self->upstream, NULL, state);
+	len = peek_at_header(f->upstream, NULL, gzip);
 	if (len == 0)
 		return (ARCHIVE_EOF);
-	__archive_read_filter_consume(self->upstream, len);
+	__archive_read_filter_consume(f->upstream, len);
 
 	/* Initialize CRC accumulator. */
-	state->crc = crc32(0L, NULL, 0);
+	gzip->crc = crc32(0L, NULL, 0);
 
 	/* Initialize compression library. */
-	state->stream.next_in = (unsigned char *)(uintptr_t)
-	    __archive_read_filter_ahead(self->upstream, 1, &avail);
+	gzip->stream.next_in = (unsigned char *)(uintptr_t)
+	    __archive_read_filter_ahead(f->upstream, 1, &avail);
 	if (avail < 0) {
-		archive_set_error(&self->archive->archive,
+		archive_set_error(&f->archive->archive,
 		    ARCHIVE_ERRNO_MISC,
 		    "Failed to read gzip input");
 		return (ARCHIVE_FATAL);
@@ -365,34 +361,34 @@
 		max_in = UINT_MAX;
 	if (avail > max_in)
 		avail = max_in;
-	state->stream.avail_in = (uInt)avail;
-	ret = inflateInit2(&(state->stream),
+	gzip->stream.avail_in = (uInt)avail;
+	ret = inflateInit2(&(gzip->stream),
 	    -15 /* Don't check for zlib header */);
 
 	/* Decipher the error code. */
 	switch (ret) {
 	case Z_OK:
-		state->in_stream = 1;
+		gzip->in_stream = 1;
 		return (ARCHIVE_OK);
 	case Z_STREAM_ERROR:
-		archive_set_error(&self->archive->archive,
+		archive_set_error(&f->archive->archive,
 		    ARCHIVE_ERRNO_MISC,
 		    "Internal error initializing compression library: "
 		    "invalid setup parameter");
 		break;
 	case Z_MEM_ERROR:
-		archive_set_error(&self->archive->archive, ENOMEM,
+		archive_set_error(&f->archive->archive, ENOMEM,
 		    "Internal error initializing compression library: "
 		    "out of memory");
 		break;
 	case Z_VERSION_ERROR:
-		archive_set_error(&self->archive->archive,
+		archive_set_error(&f->archive->archive,
 		    ARCHIVE_ERRNO_MISC,
 		    "Internal error initializing compression library: "
 		    "invalid library version");
 		break;
 	default:
-		archive_set_error(&self->archive->archive,
+		archive_set_error(&f->archive->archive,
 		    ARCHIVE_ERRNO_MISC,
 		    "Internal error initializing compression library: "
 		    " Zlib error %d", ret);
@@ -402,59 +398,55 @@
 }
 
 static int
-consume_trailer(struct archive_read_filter *self)
+consume_trailer(struct archive_read_filter *f)
 {
-	struct private_data *state;
+	struct gzip *gzip = f->data;
 	const unsigned char *p;
 
-	state = (struct private_data *)self->data;
-
-	state->in_stream = 0;
-	switch (inflateEnd(&(state->stream))) {
+	gzip->in_stream = 0;
+	switch (inflateEnd(&(gzip->stream))) {
 	case Z_OK:
 		break;
 	default:
-		archive_set_error(&self->archive->archive,
+		archive_set_error(&f->archive->archive,
 		    ARCHIVE_ERRNO_MISC,
 		    "Failed to clean up gzip decompressor");
 		return (ARCHIVE_FATAL);
 	}
 
 	/* GZip trailer is a fixed 8 byte structure. */
-	p = __archive_read_filter_ahead(self->upstream, 8, NULL);
+	p = __archive_read_filter_ahead(f->upstream, 8, NULL);
 	if (p == NULL)
 		return (ARCHIVE_FATAL);
 
 	/* XXX TODO: Verify the length and CRC. */
 
 	/* We've verified the trailer, so consume it now. */
-	__archive_read_filter_consume(self->upstream, 8);
+	__archive_read_filter_consume(f->upstream, 8);
 
 	return (ARCHIVE_OK);
 }
 
 static ssize_t
-gzip_filter_read(struct archive_read_filter *self, const void **p)
+gzip_filter_read(struct archive_read_filter *f, const void **p)
 {
-	struct private_data *state;
+	struct gzip *gzip = f->data;
 	size_t decompressed;
 	ssize_t avail_in, max_in;
 	int ret;
 
-	state = (struct private_data *)self->data;
-
 	/* Empty our output buffer. */
-	state->stream.next_out = state->out_block;
-	state->stream.avail_out = (uInt)state->out_block_size;
+	gzip->stream.next_out = gzip->out_block;
+	gzip->stream.avail_out = (uInt)gzip->out_block_size;
 
 	/* Try to fill the output buffer. */
-	while (state->stream.avail_out > 0 && !state->eof) {
+	while (gzip->stream.avail_out > 0 && !gzip->eof) {
 		/* If we're not in a stream, read a header
 		 * and initialize the decompression library. */
-		if (!state->in_stream) {
-			ret = consume_header(self);
+		if (!gzip->in_stream) {
+			ret = consume_header(f);
 			if (ret == ARCHIVE_EOF) {
-				state->eof = 1;
+				gzip->eof = 1;
 				break;
 			}
 			if (ret < ARCHIVE_OK)
@@ -464,10 +456,10 @@
 		/* Peek at the next available data. */
 		/* ZLib treats stream.next_in as const but doesn't declare
 		 * it so, hence this ugly cast. */
-		state->stream.next_in = (unsigned char *)(uintptr_t)
-		    __archive_read_filter_ahead(self->upstream, 1, &avail_in);
-		if (state->stream.next_in == NULL) {
-			archive_set_error(&self->archive->archive,
+		gzip->stream.next_in = (unsigned char *)(uintptr_t)
+		    __archive_read_filter_ahead(f->upstream, 1, &avail_in);
+		if (gzip->stream.next_in == NULL) {
+			archive_set_error(&f->archive->archive,
 			    ARCHIVE_ERRNO_MISC,
 			    "truncated gzip input");
 			return (ARCHIVE_FATAL);
@@ -478,27 +470,27 @@
 			max_in = UINT_MAX;
 		if (avail_in > max_in)
 			avail_in = max_in;
-		state->stream.avail_in = (uInt)avail_in;
+		gzip->stream.avail_in = (uInt)avail_in;
 
 		/* Decompress and consume some of that data. */
-		ret = inflate(&(state->stream), 0);
+		ret = inflate(&(gzip->stream), 0);
 		switch (ret) {
 		case Z_OK: /* Decompressor made some progress. */
-			__archive_read_filter_consume(self->upstream,
-			    avail_in - state->stream.avail_in);
+			__archive_read_filter_consume(f->upstream,
+			    avail_in - gzip->stream.avail_in);
 			break;
 		case Z_STREAM_END: /* Found end of stream. */
-			__archive_read_filter_consume(self->upstream,
-			    avail_in - state->stream.avail_in);
+			__archive_read_filter_consume(f->upstream,
+			    avail_in - gzip->stream.avail_in);
 			/* Consume the stream trailer; release the
 			 * decompression library. */
-			ret = consume_trailer(self);
+			ret = consume_trailer(f);
 			if (ret < ARCHIVE_OK)
 				return (ret);
 			break;
 		default:
 			/* Return an error. */
-			archive_set_error(&self->archive->archive,
+			archive_set_error(&f->archive->archive,
 			    ARCHIVE_ERRNO_MISC,
 			    "gzip decompression failed");
 			return (ARCHIVE_FATAL);
@@ -506,11 +498,11 @@
 	}
 
 	/* We've read as much as we can. */
-	decompressed = state->stream.next_out - state->out_block;
+	decompressed = gzip->stream.next_out - gzip->out_block;
 	if (decompressed == 0)
 		*p = NULL;
 	else
-		*p = state->out_block;
+		*p = gzip->out_block;
 	return (decompressed);
 }
 
@@ -518,29 +510,28 @@
  * Clean up the decompressor.
  */
 static int
-gzip_filter_close(struct archive_read_filter *self)
+gzip_filter_close(struct archive_read_filter *f)
 {
-	struct private_data *state;
+	struct gzip *gzip = f->data;
 	int ret;
 
-	state = (struct private_data *)self->data;
 	ret = ARCHIVE_OK;
 
-	if (state->in_stream) {
-		switch (inflateEnd(&(state->stream))) {
+	if (gzip->in_stream) {
+		switch (inflateEnd(&(gzip->stream))) {
 		case Z_OK:
 			break;
 		default:
-			archive_set_error(&(self->archive->archive),
+			archive_set_error(&(f->archive->archive),
 			    ARCHIVE_ERRNO_MISC,
 			    "Failed to clean up gzip compressor");
 			ret = ARCHIVE_FATAL;
 		}
 	}
 
-	free(state->name);
-	free(state->out_block);
-	free(state);
+	free(gzip->name);
+	free(gzip->out_block);
+	free(gzip);
 	return (ret);
 }
 
diff --git a/c/archive_read_support_filter_lrzip.c b/c/archive_read_support_filter_lrzip.c
--- a/c/archive_read_support_filter_lrzip.c
+++ b/c/archive_read_support_filter_lrzip.c
@@ -75,18 +75,18 @@
  * Bidder just verifies the header and returns the number of verified bits.
  */
 static int
-lrzip_bidder_bid(struct archive_read_filter_bidder *self,
-    struct archive_read_filter *filter)
+lrzip_bidder_bid(struct archive_read_filter_bidder *b,
+    struct archive_read_filter *f)
 {
 	const unsigned char *p;
 	ssize_t len;
 	int i;
 
-	(void)self; /* UNUSED */
+	(void)b; /* UNUSED */
 	/* Start by looking at the first six bytes of the header, which
 	 * is all fixed layout. */
 	len = 6;
-	p = __archive_read_filter_ahead(filter, len, NULL);
+	p = __archive_read_filter_ahead(f, len, NULL);
 	if (p == NULL)
 		return (0);
 
@@ -105,15 +105,15 @@
 }
 
 static int
-lrzip_bidder_init(struct archive_read_filter *self)
+lrzip_bidder_init(struct archive_read_filter *f)
 {
 	int r;
 
-	r = __archive_read_program(self, "lrzip -d -q");
+	r = __archive_read_program(f, "lrzip -d -q");
 	/* Note: We set the format here even if __archive_read_program()
 	 * above fails.  We do, after all, know what the format is
 	 * even if we weren't able to read it. */
-	self->code = ARCHIVE_FILTER_LRZIP;
-	self->name = "lrzip";
+	f->code = ARCHIVE_FILTER_LRZIP;
+	f->name = "lrzip";
 	return (r);
 }
diff --git a/c/archive_read_support_filter_lz4.c b/c/archive_read_support_filter_lz4.c
--- a/c/archive_read_support_filter_lz4.c
+++ b/c/archive_read_support_filter_lz4.c
@@ -57,7 +57,7 @@
 #define LZ4_SKIPPABLE_MASK 0xFFFFFFF0
 
 #if defined(HAVE_LIBLZ4)
-struct private_data {
+struct lz4 {
 	enum {  SELECT_STREAM,
 		READ_DEFAULT_STREAM,
 		READ_DEFAULT_BLOCK,
@@ -139,8 +139,8 @@
  * from verifying as much as we would like.
  */
 static int
-lz4_reader_bid(struct archive_read_filter_bidder *self,
-    struct archive_read_filter *filter)
+lz4_reader_bid(struct archive_read_filter_bidder *b,
+    struct archive_read_filter *f)
 {
 	const unsigned char *buffer;
 	ssize_t avail;
@@ -159,7 +159,7 @@
 	const size_t max_lookahead = 64 * 1024;
 	uint32_t magic_number;
 
-	(void)self; /* UNUSED */
+	(void)b; /* UNUSED */
 
 	/*
 	 * Zstd and LZ4 skippable frame magic numbers are identical. To
@@ -168,7 +168,7 @@
 	 */
 
 	/* Minimal lz4 archive is 11 bytes. */
-	buffer = __archive_read_filter_ahead(filter, min_lz4_archive_size,
+	buffer = __archive_read_filter_ahead(f, min_lz4_archive_size,
 	    &avail);
 	if (buffer == NULL)
 		return (0);
@@ -184,7 +184,7 @@
 
 		/* Ensure that we can read another 4 bytes. */
 		if (offset_in_buffer + 4 > (size_t)avail) {
-			buffer = __archive_read_filter_ahead(filter,
+			buffer = __archive_read_filter_ahead(f,
 			    offset_in_buffer + 4, &avail);
 			if (buffer == NULL)
 				return (0);
@@ -212,7 +212,7 @@
 			if (min > max_lookahead)
 				return (0); 
 
-			buffer = __archive_read_filter_ahead(filter,
+			buffer = __archive_read_filter_ahead(f,
 			    min, &avail);
 			if (buffer == NULL)
 				return (0); 
@@ -271,16 +271,16 @@
  * in case that's available.
  */
 static int
-lz4_reader_init(struct archive_read_filter *self)
+lz4_reader_init(struct archive_read_filter *f)
 {
 	int r;
 
-	r = __archive_read_program(self, "lz4 -d -q");
+	r = __archive_read_program(f, "lz4 -d -q");
 	/* Note: We set the format here even if __archive_read_program()
 	 * above fails.  We do, after all, know what the format is
 	 * even if we weren't able to read it. */
-	self->code = ARCHIVE_FILTER_LZ4;
-	self->name = "lz4";
+	f->code = ARCHIVE_FILTER_LZ4;
+	f->name = "lz4";
 	return (r);
 }
 
@@ -297,73 +297,73 @@
  * Setup the callbacks.
  */
 static int
-lz4_reader_init(struct archive_read_filter *self)
+lz4_reader_init(struct archive_read_filter *f)
 {
-	struct private_data *state;
+	struct lz4 *lz4;
 
-	self->code = ARCHIVE_FILTER_LZ4;
-	self->name = "lz4";
+	f->code = ARCHIVE_FILTER_LZ4;
+	f->name = "lz4";
 
-	state = calloc(1, sizeof(*state));
-	if (state == NULL) {
-		archive_set_error(&self->archive->archive, ENOMEM,
+	lz4 = calloc(1, sizeof(*lz4));
+	if (lz4 == NULL) {
+		archive_set_error(&f->archive->archive, ENOMEM,
 		    "Can't allocate data for lz4 decompression");
 		return (ARCHIVE_FATAL);
 	}
 
-	self->data = state;
-	state->stage = SELECT_STREAM;
-	self->vtable = &lz4_reader_vtable;
+	f->data = lz4;
+	lz4->stage = SELECT_STREAM;
+	f->vtable = &lz4_reader_vtable;
 
 	return (ARCHIVE_OK);
 }
 
 static int
-lz4_allocate_out_block(struct archive_read_filter *self)
+lz4_allocate_out_block(struct archive_read_filter *f)
 {
-	struct private_data *state = (struct private_data *)self->data;
-	size_t out_block_size = state->flags.block_maximum_size;
+	struct lz4 *lz4 = f->data;
+	size_t out_block_size = lz4->flags.block_maximum_size;
 	void *out_block;
 
-	if (!state->flags.block_independence)
+	if (!lz4->flags.block_independence)
 		out_block_size += 64 * 1024;
-	if (state->out_block_size < out_block_size) {
-		free(state->out_block);
-		state->out_block = NULL;
+	if (lz4->out_block_size < out_block_size) {
+		free(lz4->out_block);
+		lz4->out_block = NULL;
 		out_block = malloc(out_block_size);
 		if (out_block == NULL) {
-			state->out_block_size = 0;
-			archive_set_error(&self->archive->archive, ENOMEM,
+			lz4->out_block_size = 0;
+			archive_set_error(&f->archive->archive, ENOMEM,
 			    "Can't allocate data for lz4 decompression");
 			return (ARCHIVE_FATAL);
 		}
-		state->out_block_size = out_block_size;
-		state->out_block = out_block;
+		lz4->out_block_size = out_block_size;
+		lz4->out_block = out_block;
 	}
-	if (!state->flags.block_independence)
-		memset(state->out_block, 0, 64 * 1024);
+	if (!lz4->flags.block_independence)
+		memset(lz4->out_block, 0, 64 * 1024);
 	return (ARCHIVE_OK);
 }
 
 static int
-lz4_allocate_out_block_for_legacy(struct archive_read_filter *self)
+lz4_allocate_out_block_for_legacy(struct archive_read_filter *f)
 {
-	struct private_data *state = (struct private_data *)self->data;
+	struct lz4 *lz4 = f->data;
 	size_t out_block_size = LEGACY_BLOCK_SIZE;
 	void *out_block;
 
-	if (state->out_block_size < out_block_size) {
-		free(state->out_block);
-		state->out_block = NULL;
+	if (lz4->out_block_size < out_block_size) {
+		free(lz4->out_block);
+		lz4->out_block = NULL;
 		out_block = malloc(out_block_size);
 		if (out_block == NULL) {
-			state->out_block_size = 0;
-			archive_set_error(&self->archive->archive, ENOMEM,
+			lz4->out_block_size = 0;
+			archive_set_error(&f->archive->archive, ENOMEM,
 			    "Can't allocate data for lz4 decompression");
 			return (ARCHIVE_FATAL);
 		}
-		state->out_block_size = out_block_size;
-		state->out_block = out_block;
+		lz4->out_block_size = out_block_size;
+		lz4->out_block = out_block;
 	}
 	return (ARCHIVE_OK);
 }
@@ -372,96 +372,96 @@
  * Return the next block of decompressed data.
  */
 static ssize_t
-lz4_filter_read(struct archive_read_filter *self, const void **p)
+lz4_filter_read(struct archive_read_filter *f, const void **p)
 {
-	struct private_data *state = (struct private_data *)self->data;
+	struct lz4 *lz4 = f->data;
 	ssize_t ret;
 
-	if (state->eof) {
+	if (lz4->eof) {
 		*p = NULL;
 		return (0);
 	}
 
-	__archive_read_filter_consume(self->upstream, state->unconsumed);
-	state->unconsumed = 0;
+	__archive_read_filter_consume(f->upstream, lz4->unconsumed);
+	lz4->unconsumed = 0;
 
-	switch (state->stage) {
+	switch (lz4->stage) {
 	case SELECT_STREAM:
 		break;
 	case READ_DEFAULT_STREAM:
 	case READ_LEGACY_STREAM:
 		/* Reading an lz4 stream already failed. */
-		archive_set_error(&self->archive->archive,
+		archive_set_error(&f->archive->archive,
 		    ARCHIVE_ERRNO_MISC, "Invalid sequence");
 		return (ARCHIVE_FATAL);
 	case READ_DEFAULT_BLOCK:
-		ret = lz4_filter_read_default_stream(self, p);
-		if (ret != 0 || state->stage != SELECT_STREAM)
+		ret = lz4_filter_read_default_stream(f, p);
+		if (ret != 0 || lz4->stage != SELECT_STREAM)
 			return ret;
 		break;
 	case READ_LEGACY_BLOCK:
-		ret = lz4_filter_read_legacy_stream(self, p);
-		if (ret != 0 || state->stage != SELECT_STREAM)
+		ret = lz4_filter_read_legacy_stream(f, p);
+		if (ret != 0 || lz4->stage != SELECT_STREAM)
 			return ret;
 		break;
 	default:
-		archive_set_error(&self->archive->archive,
+		archive_set_error(&f->archive->archive,
 		    ARCHIVE_ERRNO_MISC, "Program error");
 		return (ARCHIVE_FATAL);
 	}
 
-	while (state->stage == SELECT_STREAM) {
+	while (lz4->stage == SELECT_STREAM) {
 		const char *read_buf;
 
 		/* Read a magic number. */
-		read_buf = __archive_read_filter_ahead(self->upstream, 4,
+		read_buf = __archive_read_filter_ahead(f->upstream, 4,
 				NULL);
 		if (read_buf == NULL) {
-			state->eof = 1;
+			lz4->eof = 1;
 			*p = NULL;
 			return (0);
 		}
 		uint32_t number = archive_le32dec(read_buf);
-		__archive_read_filter_consume(self->upstream, 4);
+		__archive_read_filter_consume(f->upstream, 4);
 		if (number == LZ4_MAGICNUMBER)
-			return lz4_filter_read_default_stream(self, p);
+			return lz4_filter_read_default_stream(f, p);
 		else if (number == LZ4_LEGACY)
-			return lz4_filter_read_legacy_stream(self, p);
+			return lz4_filter_read_legacy_stream(f, p);
 		else if ((number & LZ4_SKIPPABLE_MASK) == LZ4_SKIPPABLE_START) {
 			read_buf = __archive_read_filter_ahead(
-				self->upstream, 4, NULL);
+				f->upstream, 4, NULL);
 			if (read_buf == NULL) {
 				archive_set_error(
-				    &self->archive->archive,
+				    &f->archive->archive,
 		    		    ARCHIVE_ERRNO_MISC,
 				    "Malformed lz4 data");
 				return (ARCHIVE_FATAL);
 			}
 			int64_t skip_bytes = archive_le32dec(read_buf);
-			if (__archive_read_filter_consume(self->upstream,
+			if (__archive_read_filter_consume(f->upstream,
 			    4 + skip_bytes) < 0) {
 				archive_set_error(
-				    &self->archive->archive,
+				    &f->archive->archive,
 				    ARCHIVE_ERRNO_MISC,
 				    "Malformed lz4 data");
 				return (ARCHIVE_FATAL);
 			}
 		} else {
 			/* Ignore following unrecognized data. */
-			state->eof = 1;
+			lz4->eof = 1;
 			*p = NULL;
 			return (0);
 		}
 	}
-	state->eof = 1;
+	lz4->eof = 1;
 	*p = NULL;
 	return (0);
 }
 
 static int
-lz4_filter_read_descriptor(struct archive_read_filter *self)
+lz4_filter_read_descriptor(struct archive_read_filter *f)
 {
-	struct private_data *state = (struct private_data *)self->data;
+	struct lz4 *lz4 = f->data;
 	const char *read_buf;
 	ssize_t bytes_remaining;
 	ssize_t descriptor_bytes;
@@ -469,10 +469,10 @@
 	unsigned int chsum, chsum_verifier;
 
 	/* Make sure we have 2 bytes for flags. */
-	read_buf = __archive_read_filter_ahead(self->upstream, 2,
+	read_buf = __archive_read_filter_ahead(f->upstream, 2,
 	    &bytes_remaining);
 	if (read_buf == NULL) {
-		archive_set_error(&self->archive->archive,
+		archive_set_error(&f->archive->archive,
 		    ARCHIVE_ERRNO_MISC,
 		    "truncated lz4 input");
 		return (ARCHIVE_FATAL);
@@ -488,11 +488,11 @@
 	/* A reserved bit must be zero. */
 	if (flag & 0x02)
 		goto malformed_error;
-	state->flags.block_independence = (flag & 0x20) != 0;
-	state->flags.block_checksum = (flag & 0x10)?4:0;
-	state->flags.stream_size = (flag & 0x08) != 0;
-	state->flags.stream_checksum = (flag & 0x04) != 0;
-	state->flags.preset_dictionary = (flag & 0x01) != 0;
+	lz4->flags.block_independence = (flag & 0x20) != 0;
+	lz4->flags.block_checksum = (flag & 0x10)?4:0;
+	lz4->flags.stream_size = (flag & 0x08) != 0;
+	lz4->flags.stream_checksum = (flag & 0x04) != 0;
+	lz4->flags.preset_dictionary = (flag & 0x01) != 0;
 
 	/* BD */
 	bd = (unsigned char)read_buf[1];
@@ -502,16 +502,16 @@
 	/* Get a maximum block size. */
 	switch (read_buf[1] >> 4) {
 	case 4: /* 64 KB */
-		state->flags.block_maximum_size = 64 * 1024;
+		lz4->flags.block_maximum_size = 64 * 1024;
 		break;
 	case 5: /* 256 KB */
-		state->flags.block_maximum_size = 256 * 1024;
+		lz4->flags.block_maximum_size = 256 * 1024;
 		break;
 	case 6: /* 1 MB */
-		state->flags.block_maximum_size = 1024 * 1024;
+		lz4->flags.block_maximum_size = 1024 * 1024;
 		break;
 	case 7: /* 4 MB */
-		state->flags.block_maximum_size = 4 * 1024 * 1024;
+		lz4->flags.block_maximum_size = 4 * 1024 * 1024;
 		break;
 	default:
 		goto malformed_error;
@@ -519,15 +519,15 @@
 
 	/* Read the whole descriptor in a stream block. */
 	descriptor_bytes = 3;
-	if (state->flags.stream_size)
+	if (lz4->flags.stream_size)
 		descriptor_bytes += 8;
-	if (state->flags.preset_dictionary)
+	if (lz4->flags.preset_dictionary)
 		descriptor_bytes += 4;
 	if (bytes_remaining < descriptor_bytes) {
-		read_buf = __archive_read_filter_ahead(self->upstream,
+		read_buf = __archive_read_filter_ahead(f->upstream,
 		    descriptor_bytes, &bytes_remaining);
 		if (read_buf == NULL) {
-			archive_set_error(&self->archive->archive,
+			archive_set_error(&f->archive->archive,
 			    ARCHIVE_ERRNO_MISC,
 			    "truncated lz4 input");
 			return (ARCHIVE_FATAL);
@@ -542,30 +542,30 @@
 		goto malformed_error;
 #endif
 
-	__archive_read_filter_consume(self->upstream, descriptor_bytes);
+	__archive_read_filter_consume(f->upstream, descriptor_bytes);
 
 	/* Make sure we have a large enough buffer for uncompressed data. */
-	if (lz4_allocate_out_block(self) != ARCHIVE_OK)
+	if (lz4_allocate_out_block(f) != ARCHIVE_OK)
 		return (ARCHIVE_FATAL);
-	if (state->flags.stream_checksum) {
-		state->xxh32_state = __archive_xxhash.XXH32_init(0);
-		if (state->xxh32_state == NULL)
+	if (lz4->flags.stream_checksum) {
+		lz4->xxh32_state = __archive_xxhash.XXH32_init(0);
+		if (lz4->xxh32_state == NULL)
 			return (ARCHIVE_FATAL);
 	}
 
-	state->decoded_size = 0;
+	lz4->decoded_size = 0;
 	/* Success */
 	return (ARCHIVE_OK);
 malformed_error:
-	archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+	archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC,
 	    "malformed lz4 data");
 	return (ARCHIVE_FATAL);
 }
 
 static ssize_t
-lz4_filter_read_data_block(struct archive_read_filter *self, const void **p)
+lz4_filter_read_data_block(struct archive_read_filter *f, const void **p)
 {
-	struct private_data *state = (struct private_data *)self->data;
+	struct lz4 *lz4 = f->data;
 	ssize_t compressed_size;
 	const char *read_buf;
 	int checksum_size;
@@ -575,19 +575,19 @@
 	*p = NULL;
 
 	/* Make sure we have 4 bytes for a block size. */
-	read_buf = __archive_read_filter_ahead(self->upstream, 4, NULL);
+	read_buf = __archive_read_filter_ahead(f->upstream, 4, NULL);
 	if (read_buf == NULL)
 		goto truncated_error;
 	compressed_size = archive_le32dec(read_buf);
-	if ((compressed_size & 0x7fffffff) > state->flags.block_maximum_size)
+	if ((compressed_size & 0x7fffffff) > lz4->flags.block_maximum_size)
 		goto malformed_error;
 	/* A compressed size == 0 means the end of stream blocks. */
 	if (compressed_size == 0) {
-		__archive_read_filter_consume(self->upstream, 4);
+		__archive_read_filter_consume(f->upstream, 4);
 		return 0;
 	}
 
-	checksum_size = state->flags.block_checksum;
+	checksum_size = lz4->flags.block_checksum;
 	/* Check if the block is uncompressed. */
 	if (compressed_size & 0x80000000U) {
 		compressed_size &= 0x7fffffff;
@@ -600,7 +600,7 @@
 	  for its decompression speed, so we read a whole block and allocate
 	  a huge buffer used for decoded data.
 	*/
-	read_buf = __archive_read_filter_ahead(self->upstream,
+	read_buf = __archive_read_filter_ahead(f->upstream,
 	    4 + compressed_size + checksum_size, NULL);
 	if (read_buf == NULL)
 		goto truncated_error;
@@ -621,24 +621,24 @@
 	/* If the block is uncompressed, there is nothing to do. */
 	if (uncompressed_size) {
 		/* Prepare a prefix 64k block for next block. */
-		if (!state->flags.block_independence) {
+		if (!lz4->flags.block_independence) {
 			prefix64k = 64 * 1024;
 			if (uncompressed_size < (ssize_t)prefix64k) {
-				memcpy(state->out_block
+				memcpy(lz4->out_block
 					+ prefix64k - uncompressed_size,
 				    read_buf + 4,
 				    uncompressed_size);
-				memset(state->out_block, 0,
+				memset(lz4->out_block, 0,
 				    prefix64k - uncompressed_size);
 			} else {
-				memcpy(state->out_block,
+				memcpy(lz4->out_block,
 				    read_buf + 4
 					+ uncompressed_size - prefix64k,
 				    prefix64k);
 			}
-			state->decoded_size = 0;
+			lz4->decoded_size = 0;
 		}
-		state->unconsumed = 4 + uncompressed_size + checksum_size;
+		lz4->unconsumed = 4 + uncompressed_size + checksum_size;
 		*p = read_buf + 4;
 		return uncompressed_size;
 	}
@@ -646,168 +646,168 @@
 	/*
 	   Decompress a block data.
 	 */
-	if (state->flags.block_independence) {
+	if (lz4->flags.block_independence) {
 		prefix64k = 0;
 		uncompressed_size = LZ4_decompress_safe(read_buf + 4,
-		    state->out_block, (int)compressed_size,
-		    state->flags.block_maximum_size);
+		    lz4->out_block, (int)compressed_size,
+		    lz4->flags.block_maximum_size);
 	} else {
 		prefix64k = 64 * 1024;
-		if (state->decoded_size) {
-			if (state->decoded_size < prefix64k) {
-				memmove(state->out_block
-					+ prefix64k - state->decoded_size,
-				    state->out_block + prefix64k,
-				    state->decoded_size);
-				memset(state->out_block, 0,
-				    prefix64k - state->decoded_size);
+		if (lz4->decoded_size) {
+			if (lz4->decoded_size < prefix64k) {
+				memmove(lz4->out_block
+					+ prefix64k - lz4->decoded_size,
+				    lz4->out_block + prefix64k,
+				    lz4->decoded_size);
+				memset(lz4->out_block, 0,
+				    prefix64k - lz4->decoded_size);
 			} else {
-				memmove(state->out_block,
-				    state->out_block + state->decoded_size,
+				memmove(lz4->out_block,
+				    lz4->out_block + lz4->decoded_size,
 				    prefix64k);
 			}
 		}
 #if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
 		uncompressed_size = LZ4_decompress_safe_usingDict(
 		    read_buf + 4,
-		    state->out_block + prefix64k, (int)compressed_size,
-		    state->flags.block_maximum_size,
-		    state->out_block,
+		    lz4->out_block + prefix64k, (int)compressed_size,
+		    lz4->flags.block_maximum_size,
+		    lz4->out_block,
 		    (int)prefix64k);
 #else
 		uncompressed_size = LZ4_decompress_safe_withPrefix64k(
 		    read_buf + 4,
-		    state->out_block + prefix64k, (int)compressed_size,
-		    state->flags.block_maximum_size);
+		    lz4->out_block + prefix64k, (int)compressed_size,
+		    lz4->flags.block_maximum_size);
 #endif
 	}
 
 	/* Check if an error occurred in the decompression process. */
 	if (uncompressed_size < 0) {
-		archive_set_error(&(self->archive->archive),
+		archive_set_error(&(f->archive->archive),
 		    ARCHIVE_ERRNO_MISC, "lz4 decompression failed");
 		return (ARCHIVE_FATAL);
 	}
 
-	state->unconsumed = 4 + compressed_size + checksum_size;
-	*p = state->out_block + prefix64k;
-	state->decoded_size = uncompressed_size;
+	lz4->unconsumed = 4 + compressed_size + checksum_size;
+	*p = lz4->out_block + prefix64k;
+	lz4->decoded_size = uncompressed_size;
 	return uncompressed_size;
 
 malformed_error:
-	archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+	archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC,
 	    "malformed lz4 data");
 	return (ARCHIVE_FATAL);
 truncated_error:
-	archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+	archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC,
 	    "truncated lz4 input");
 	return (ARCHIVE_FATAL);
 }
 
 static ssize_t
-lz4_filter_read_default_stream(struct archive_read_filter *self, const void **p)
+lz4_filter_read_default_stream(struct archive_read_filter *f, const void **p)
 {
-	struct private_data *state = (struct private_data *)self->data;
+	struct lz4 *lz4 = f->data;
 	const char *read_buf;
 	ssize_t ret;
 
-	if (state->stage == SELECT_STREAM) {
-		state->stage = READ_DEFAULT_STREAM;
+	if (lz4->stage == SELECT_STREAM) {
+		lz4->stage = READ_DEFAULT_STREAM;
 		/* First, read a descriptor. */
-		if((ret = lz4_filter_read_descriptor(self)) != ARCHIVE_OK)
+		if((ret = lz4_filter_read_descriptor(f)) != ARCHIVE_OK)
 			return (ret);
-		state->stage = READ_DEFAULT_BLOCK;
+		lz4->stage = READ_DEFAULT_BLOCK;
 	}
 	/* Decompress a block. */
-	ret = lz4_filter_read_data_block(self, p);
+	ret = lz4_filter_read_data_block(f, p);
 
 	/* If the end of block is detected, change the filter status
 	   to read next stream. */
 	if (ret == 0 && *p == NULL)
-		state->stage = SELECT_STREAM;
+		lz4->stage = SELECT_STREAM;
 
 	/* Optional processing, checking a stream sum. */
-	if (state->flags.stream_checksum) {
-		if (state->stage == SELECT_STREAM) {
+	if (lz4->flags.stream_checksum) {
+		if (lz4->stage == SELECT_STREAM) {
 			unsigned int checksum;
 			unsigned int checksum_stream;
-			read_buf = __archive_read_filter_ahead(self->upstream,
+			read_buf = __archive_read_filter_ahead(f->upstream,
 			    4, NULL);
 			if (read_buf == NULL) {
-				archive_set_error(&self->archive->archive,
+				archive_set_error(&f->archive->archive,
 				    ARCHIVE_ERRNO_MISC, "truncated lz4 input");
 				return (ARCHIVE_FATAL);
 			}
 			checksum = archive_le32dec(read_buf);
-			__archive_read_filter_consume(self->upstream, 4);
+			__archive_read_filter_consume(f->upstream, 4);
 			checksum_stream = __archive_xxhash.XXH32_digest(
-			    state->xxh32_state);
-			state->xxh32_state = NULL;
+			    lz4->xxh32_state);
+			lz4->xxh32_state = NULL;
 			if (checksum != checksum_stream) {
 #ifndef DONT_FAIL_ON_CRC_ERROR
-				archive_set_error(&self->archive->archive,
+				archive_set_error(&f->archive->archive,
 				    ARCHIVE_ERRNO_MISC,
 				    "lz4 stream checksum error");
 				return (ARCHIVE_FATAL);
 #endif
 			}
 		} else if (ret > 0)
-			__archive_xxhash.XXH32_update(state->xxh32_state,
+			__archive_xxhash.XXH32_update(lz4->xxh32_state,
 			    *p, (int)ret);
 	}
 	return (ret);
 }
 
 static ssize_t
-lz4_filter_read_legacy_stream(struct archive_read_filter *self, const void **p)
+lz4_filter_read_legacy_stream(struct archive_read_filter *f, const void **p)
 {
-	struct private_data *state = (struct private_data *)self->data;
+	struct lz4 *lz4 = f->data;
 	uint32_t compressed;
 	const char *read_buf;
 	ssize_t ret;
 
 	*p = NULL;
-	ret = lz4_allocate_out_block_for_legacy(self);
+	ret = lz4_allocate_out_block_for_legacy(f);
 	if (ret != ARCHIVE_OK)
 		return ret;
 
 	/* Make sure we have 4 bytes for a block size. */
-	read_buf = __archive_read_filter_ahead(self->upstream, 4, NULL);
+	read_buf = __archive_read_filter_ahead(f->upstream, 4, NULL);
 	if (read_buf == NULL) {
-		if (state->stage == SELECT_STREAM) {
-			state->stage = READ_LEGACY_STREAM;
-			archive_set_error(&self->archive->archive,
+		if (lz4->stage == SELECT_STREAM) {
+			lz4->stage = READ_LEGACY_STREAM;
+			archive_set_error(&f->archive->archive,
 			    ARCHIVE_ERRNO_MISC,
 			    "truncated lz4 input");
 			return (ARCHIVE_FATAL);
 		}
-		state->stage = SELECT_STREAM;
+		lz4->stage = SELECT_STREAM;
 		return 0;
 	}
-	state->stage = READ_LEGACY_BLOCK;
+	lz4->stage = READ_LEGACY_BLOCK;
 	compressed = archive_le32dec(read_buf);
 	if (compressed > LZ4_COMPRESSBOUND(LEGACY_BLOCK_SIZE)) {
-		state->stage = SELECT_STREAM;
+		lz4->stage = SELECT_STREAM;
 		return 0;
 	}
 
 	/* Make sure we have a whole block. */
-	read_buf = __archive_read_filter_ahead(self->upstream,
+	read_buf = __archive_read_filter_ahead(f->upstream,
 	    4 + compressed, NULL);
 	if (read_buf == NULL) {
-		archive_set_error(&(self->archive->archive),
+		archive_set_error(&(f->archive->archive),
 		    ARCHIVE_ERRNO_MISC, "truncated lz4 input");
 		return (ARCHIVE_FATAL);
 	}
-	ret = LZ4_decompress_safe(read_buf + 4, state->out_block,
-	    compressed, (int)state->out_block_size);
+	ret = LZ4_decompress_safe(read_buf + 4, lz4->out_block,
+	    compressed, (int)lz4->out_block_size);
 	if (ret < 0) {
-		archive_set_error(&(self->archive->archive),
+		archive_set_error(&(f->archive->archive),
 		    ARCHIVE_ERRNO_MISC, "lz4 decompression failed");
 		return (ARCHIVE_FATAL);
 	}
-	*p = state->out_block;
-	state->unconsumed = 4 + compressed;
+	*p = lz4->out_block;
+	lz4->unconsumed = 4 + compressed;
 	return ret;
 }
 
@@ -815,15 +815,14 @@
  * Clean up the decompressor.
  */
 static int
-lz4_filter_close(struct archive_read_filter *self)
+lz4_filter_close(struct archive_read_filter *f)
 {
-	struct private_data *state;
+	struct lz4 *lz4 = f->data;
 	int ret = ARCHIVE_OK;
 
-	state = (struct private_data *)self->data;
-	free(state->xxh32_state);
-	free(state->out_block);
-	free(state);
+	free(lz4->xxh32_state);
+	free(lz4->out_block);
+	free(lz4);
 	return (ret);
 }
 
diff --git a/c/archive_read_support_filter_lzop.c b/c/archive_read_support_filter_lzop.c
--- a/c/archive_read_support_filter_lzop.c
+++ b/c/archive_read_support_filter_lzop.c
@@ -68,7 +68,7 @@
 #define LZOP_HEADER_MAGIC_LEN 9
 
 #if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H)
-struct read_lzop {
+struct lzop {
 	unsigned char	*out_block;
 	size_t		 out_block_size;
 	int		 flags;
@@ -127,14 +127,14 @@
  * Bidder just verifies the header and returns the number of verified bits.
  */
 static int
-lzop_bidder_bid(struct archive_read_filter_bidder *self,
-    struct archive_read_filter *filter)
+lzop_bidder_bid(struct archive_read_filter_bidder *b,
+    struct archive_read_filter *f)
 {
 	const unsigned char *p;
 
-	(void)self; /* UNUSED */
+	(void)b; /* UNUSED */
 
-	p = __archive_read_filter_ahead(filter, LZOP_HEADER_MAGIC_LEN, NULL);
+	p = __archive_read_filter_ahead(f, LZOP_HEADER_MAGIC_LEN, NULL);
 	if (p == NULL)
 		return (0);
 
@@ -151,16 +151,16 @@
  * in case that's available.
  */
 static int
-lzop_bidder_init(struct archive_read_filter *self)
+lzop_bidder_init(struct archive_read_filter *f)
 {
 	int r;
 
-	r = __archive_read_program(self, "lzop -d");
+	r = __archive_read_program(f, "lzop -d");
 	/* Note: We set the format here even if __archive_read_program()
 	 * above fails.  We do, after all, know what the format is
 	 * even if we weren't able to read it. */
-	self->code = ARCHIVE_FILTER_LZOP;
-	self->name = "lzop";
+	f->code = ARCHIVE_FILTER_LZOP;
+	f->name = "lzop";
 	return (r);
 }
 #else
@@ -175,47 +175,47 @@
  * Initialize the filter object.
  */
 static int
-lzop_bidder_init(struct archive_read_filter *self)
+lzop_bidder_init(struct archive_read_filter *f)
 {
-	struct read_lzop *state;
+	struct lzop *lzop;
 
-	self->code = ARCHIVE_FILTER_LZOP;
-	self->name = "lzop";
+	f->code = ARCHIVE_FILTER_LZOP;
+	f->name = "lzop";
 
-	state = calloc(1, sizeof(*state));
-	if (state == NULL) {
-		archive_set_error(&self->archive->archive, ENOMEM,
+	lzop = calloc(1, sizeof(*lzop));
+	if (lzop == NULL) {
+		archive_set_error(&f->archive->archive, ENOMEM,
 		    "Can't allocate data for lzop decompression");
 		return (ARCHIVE_FATAL);
 	}
 
-	self->data = state;
-	self->vtable = &lzop_reader_vtable;
+	f->data = lzop;
+	f->vtable = &lzop_reader_vtable;
 
 	return (ARCHIVE_OK);
 }
 
 static int
-consume_header(struct archive_read_filter *self)
+consume_header(struct archive_read_filter *f)
 {
-	struct read_lzop *state = (struct read_lzop *)self->data;
+	struct lzop *lzop = f->data;
 	const unsigned char *p, *_p;
 	unsigned checksum, flags, len, method, version;
 
 	/*
 	 * Check LZOP magic code.
 	 */
-	p = __archive_read_filter_ahead(self->upstream,
+	p = __archive_read_filter_ahead(f->upstream,
 		LZOP_HEADER_MAGIC_LEN, NULL);
 	if (p == NULL)
 		return (ARCHIVE_EOF);
 
 	if (memcmp(p, LZOP_HEADER_MAGIC, LZOP_HEADER_MAGIC_LEN))
 		return (ARCHIVE_EOF);
-	__archive_read_filter_consume(self->upstream,
+	__archive_read_filter_consume(f->upstream,
 	    LZOP_HEADER_MAGIC_LEN);
 
-	p = __archive_read_filter_ahead(self->upstream, 29, NULL);
+	p = __archive_read_filter_ahead(f->upstream, 29, NULL);
 	if (p == NULL)
 		goto truncated;
 	_p = p;
@@ -225,7 +225,7 @@
 	if (version >= 0x940) {
 		unsigned reqversion = archive_be16dec(p); p += 2;
 		if (reqversion < 0x900) {
-			archive_set_error(&self->archive->archive,
+			archive_set_error(&f->archive->archive,
 			    ARCHIVE_ERRNO_MISC, "Invalid required version");
 			return (ARCHIVE_FAILED);
 		}
@@ -233,7 +233,7 @@
 
 	method = *p++;
 	if (method < 1 || method > 3) {
-		archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+		archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC,
 		    "Unsupported method");
 		return (ARCHIVE_FAILED);
 	}
@@ -252,7 +252,7 @@
 			;/* NOP */
 #endif
 		else if (level > 9) {
-			archive_set_error(&self->archive->archive,
+			archive_set_error(&f->archive->archive,
 			    ARCHIVE_ERRNO_MISC, "Invalid level");
 			return (ARCHIVE_FAILED);
 		}
@@ -270,7 +270,7 @@
 	len = *p++; /* Read filename length */
 	len += p - _p;
 	/* Make sure we have all bytes we need to calculate checksum. */
-	p = __archive_read_filter_ahead(self->upstream, len + 4, NULL);
+	p = __archive_read_filter_ahead(f->upstream, len + 4, NULL);
 	if (p == NULL)
 		goto truncated;
 	if (flags & CRC32_HEADER)
@@ -281,146 +281,146 @@
 	if (archive_be32dec(p + len) != checksum)
 		goto corrupted;
 #endif
-	__archive_read_filter_consume(self->upstream, len + 4);
+	__archive_read_filter_consume(f->upstream, len + 4);
 	if (flags & EXTRA_FIELD) {
 		/* Skip extra field */
-		p = __archive_read_filter_ahead(self->upstream, 4, NULL);
+		p = __archive_read_filter_ahead(f->upstream, 4, NULL);
 		if (p == NULL)
 			goto truncated;
 		len = archive_be32dec(p);
-		__archive_read_filter_consume(self->upstream,
+		__archive_read_filter_consume(f->upstream,
 		    (int64_t)len + 4 + 4);
 	}
-	state->flags = flags;
-	state->in_stream = 1;
+	lzop->flags = flags;
+	lzop->in_stream = 1;
 	return (ARCHIVE_OK);
 truncated:
-	archive_set_error(&self->archive->archive,
+	archive_set_error(&f->archive->archive,
 	    ARCHIVE_ERRNO_FILE_FORMAT, "Truncated lzop data");
 	return (ARCHIVE_FAILED);
 corrupted:
-	archive_set_error(&self->archive->archive,
+	archive_set_error(&f->archive->archive,
 	    ARCHIVE_ERRNO_FILE_FORMAT, "Corrupted lzop header");
 	return (ARCHIVE_FAILED);
 }
 
 static int
-consume_block_info(struct archive_read_filter *self)
+consume_block_info(struct archive_read_filter *f)
 {
-	struct read_lzop *state = (struct read_lzop *)self->data;
+	struct lzop *lzop = f->data;
 	const unsigned char *p;
-	unsigned flags = state->flags;
+	unsigned flags = lzop->flags;
 
-	p = __archive_read_filter_ahead(self->upstream, 4, NULL);
+	p = __archive_read_filter_ahead(f->upstream, 4, NULL);
 	if (p == NULL)
 		goto truncated;
-	state->uncompressed_size = archive_be32dec(p);
-	__archive_read_filter_consume(self->upstream, 4);
-	if (state->uncompressed_size == 0)
+	lzop->uncompressed_size = archive_be32dec(p);
+	__archive_read_filter_consume(f->upstream, 4);
+	if (lzop->uncompressed_size == 0)
 		return (ARCHIVE_EOF);
-	if (state->uncompressed_size > MAX_BLOCK_SIZE)
+	if (lzop->uncompressed_size > MAX_BLOCK_SIZE)
 		goto corrupted;
 
-	p = __archive_read_filter_ahead(self->upstream, 4, NULL);
+	p = __archive_read_filter_ahead(f->upstream, 4, NULL);
 	if (p == NULL)
 		goto truncated;
-	state->compressed_size = archive_be32dec(p);
-	__archive_read_filter_consume(self->upstream, 4);
-	if (state->compressed_size > state->uncompressed_size)
+	lzop->compressed_size = archive_be32dec(p);
+	__archive_read_filter_consume(f->upstream, 4);
+	if (lzop->compressed_size > lzop->uncompressed_size)
 		goto corrupted;
 
 	if (flags & (CRC32_UNCOMPRESSED | ADLER32_UNCOMPRESSED)) {
-		p = __archive_read_filter_ahead(self->upstream, 4, NULL);
+		p = __archive_read_filter_ahead(f->upstream, 4, NULL);
 		if (p == NULL)
 			goto truncated;
-		state->compressed_cksum = state->uncompressed_cksum =
+		lzop->compressed_cksum = lzop->uncompressed_cksum =
 		    archive_be32dec(p);
-		__archive_read_filter_consume(self->upstream, 4);
+		__archive_read_filter_consume(f->upstream, 4);
 	}
 	if ((flags & (CRC32_COMPRESSED | ADLER32_COMPRESSED)) &&
-	    state->compressed_size < state->uncompressed_size) {
-		p = __archive_read_filter_ahead(self->upstream, 4, NULL);
+	    lzop->compressed_size < lzop->uncompressed_size) {
+		p = __archive_read_filter_ahead(f->upstream, 4, NULL);
 		if (p == NULL)
 			goto truncated;
-		state->compressed_cksum = archive_be32dec(p);
-		__archive_read_filter_consume(self->upstream, 4);
+		lzop->compressed_cksum = archive_be32dec(p);
+		__archive_read_filter_consume(f->upstream, 4);
 	}
 	return (ARCHIVE_OK);
 truncated:
-	archive_set_error(&self->archive->archive,
+	archive_set_error(&f->archive->archive,
 	    ARCHIVE_ERRNO_FILE_FORMAT, "Truncated lzop data");
 	return (ARCHIVE_FAILED);
 corrupted:
-	archive_set_error(&self->archive->archive,
+	archive_set_error(&f->archive->archive,
 	    ARCHIVE_ERRNO_FILE_FORMAT, "Corrupted lzop header");
 	return (ARCHIVE_FAILED);
 }
 
 static ssize_t
-lzop_filter_read(struct archive_read_filter *self, const void **p)
+lzop_filter_read(struct archive_read_filter *f, const void **p)
 {
-	struct read_lzop *state = (struct read_lzop *)self->data;
+	struct lzop *lzop = f->data;
 	const void *b;
 	lzo_uint out_size;
 	uint32_t cksum;
 	int ret, r;
 
-	if (state->unconsumed_bytes) {
-		__archive_read_filter_consume(self->upstream,
-		    state->unconsumed_bytes);
-		state->unconsumed_bytes = 0;
+	if (lzop->unconsumed_bytes) {
+		__archive_read_filter_consume(f->upstream,
+		    lzop->unconsumed_bytes);
+		lzop->unconsumed_bytes = 0;
 	}
-	if (state->eof)
+	if (lzop->eof)
 		return (0);
 
 	for (;;) {
-		if (!state->in_stream) {
-			ret = consume_header(self);
+		if (!lzop->in_stream) {
+			ret = consume_header(f);
 			if (ret < ARCHIVE_OK)
 				return (ret);
 			if (ret == ARCHIVE_EOF) {
-				state->eof = 1;
+				lzop->eof = 1;
 				return (0);
 			}
 		}
-		ret = consume_block_info(self);
+		ret = consume_block_info(f);
 		if (ret < ARCHIVE_OK)
 			return (ret);
 		if (ret == ARCHIVE_EOF)
-			state->in_stream = 0;
+			lzop->in_stream = 0;
 		else
 			break;
 	}
 
-	if (state->out_block == NULL ||
-	    state->out_block_size < state->uncompressed_size) {
+	if (lzop->out_block == NULL ||
+	    lzop->out_block_size < lzop->uncompressed_size) {
 		void *new_block;
 
-		new_block = realloc(state->out_block, state->uncompressed_size);
+		new_block = realloc(lzop->out_block, lzop->uncompressed_size);
 		if (new_block == NULL) {
-			archive_set_error(&self->archive->archive, ENOMEM,
+			archive_set_error(&f->archive->archive, ENOMEM,
 			    "Can't allocate data for lzop decompression");
 			return (ARCHIVE_FATAL);
 		}
-		state->out_block = new_block;
-		state->out_block_size = state->uncompressed_size;
+		lzop->out_block = new_block;
+		lzop->out_block_size = lzop->uncompressed_size;
 	}
 
-	b = __archive_read_filter_ahead(self->upstream,
-		state->compressed_size, NULL);
+	b = __archive_read_filter_ahead(f->upstream,
+		lzop->compressed_size, NULL);
 	if (b == NULL) {
-		archive_set_error(&self->archive->archive,
+		archive_set_error(&f->archive->archive,
 		    ARCHIVE_ERRNO_FILE_FORMAT, "Truncated lzop data");
 		return (ARCHIVE_FATAL);
 	}
-	if (state->flags & CRC32_COMPRESSED)
-		cksum = crc32(crc32(0, NULL, 0), b, state->compressed_size);
-	else if (state->flags & ADLER32_COMPRESSED)
-		cksum = adler32(adler32(0, NULL, 0), b, state->compressed_size);
+	if (lzop->flags & CRC32_COMPRESSED)
+		cksum = crc32(crc32(0, NULL, 0), b, lzop->compressed_size);
+	else if (lzop->flags & ADLER32_COMPRESSED)
+		cksum = adler32(adler32(0, NULL, 0), b, lzop->compressed_size);
 	else
-		cksum = state->compressed_cksum;
-	if (cksum != state->compressed_cksum) {
-		archive_set_error(&self->archive->archive,
+		cksum = lzop->compressed_cksum;
+	if (cksum != lzop->compressed_cksum) {
+		archive_set_error(&f->archive->archive,
 		    ARCHIVE_ERRNO_MISC, "Corrupted data");
 		return (ARCHIVE_FATAL);
 	}
@@ -429,51 +429,51 @@
 	 * If both uncompressed size and compressed size are the same,
 	 * we do not decompress this block.
 	 */
-	if (state->uncompressed_size == state->compressed_size) {
+	if (lzop->uncompressed_size == lzop->compressed_size) {
 		*p = b;
-		state->unconsumed_bytes = state->compressed_size;
-		return ((ssize_t)state->uncompressed_size);
+		lzop->unconsumed_bytes = lzop->compressed_size;
+		return ((ssize_t)lzop->uncompressed_size);
 	}
 
 	/*
 	 * Drive lzo uncompression.
 	 */
-	out_size = (lzo_uint)state->uncompressed_size;
-	r = lzo1x_decompress_safe(b, (lzo_uint)state->compressed_size,
-		state->out_block, &out_size, NULL);
+	out_size = (lzo_uint)lzop->uncompressed_size;
+	r = lzo1x_decompress_safe(b, (lzo_uint)lzop->compressed_size,
+		lzop->out_block, &out_size, NULL);
 	switch (r) {
 	case LZO_E_OK:
-		if (out_size == state->uncompressed_size)
+		if (out_size == lzop->uncompressed_size)
 			break;
-		archive_set_error(&self->archive->archive,
+		archive_set_error(&f->archive->archive,
 		    ARCHIVE_ERRNO_MISC, "Corrupted data");
 		return (ARCHIVE_FATAL);
 	case LZO_E_OUT_OF_MEMORY:
-		archive_set_error(&self->archive->archive, ENOMEM,
+		archive_set_error(&f->archive->archive, ENOMEM,
 		    "lzop decompression failed: out of memory");
 		return (ARCHIVE_FATAL);
 	default:
-		archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+		archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC,
 		    "lzop decompression failed: %d", r);
 		return (ARCHIVE_FATAL);
 	}
 
-	if (state->flags & CRC32_UNCOMPRESSED)
-		cksum = crc32(crc32(0, NULL, 0), state->out_block,
-		    state->uncompressed_size);
-	else if (state->flags & ADLER32_UNCOMPRESSED)
-		cksum = adler32(adler32(0, NULL, 0), state->out_block,
-		    state->uncompressed_size);
+	if (lzop->flags & CRC32_UNCOMPRESSED)
+		cksum = crc32(crc32(0, NULL, 0), lzop->out_block,
+		    lzop->uncompressed_size);
+	else if (lzop->flags & ADLER32_UNCOMPRESSED)
+		cksum = adler32(adler32(0, NULL, 0), lzop->out_block,
+		    lzop->uncompressed_size);
 	else
-		cksum = state->uncompressed_cksum;
-	if (cksum != state->uncompressed_cksum) {
-		archive_set_error(&self->archive->archive,
+		cksum = lzop->uncompressed_cksum;
+	if (cksum != lzop->uncompressed_cksum) {
+		archive_set_error(&f->archive->archive,
 		    ARCHIVE_ERRNO_MISC, "Corrupted data");
 		return (ARCHIVE_FATAL);
 	}
 
-	__archive_read_filter_consume(self->upstream, state->compressed_size);
-	*p = state->out_block;
+	__archive_read_filter_consume(f->upstream, lzop->compressed_size);
+	*p = lzop->out_block;
 	return ((ssize_t)out_size);
 }
 
@@ -481,12 +481,12 @@
  * Clean up the decompressor.
  */
 static int
-lzop_filter_close(struct archive_read_filter *self)
+lzop_filter_close(struct archive_read_filter *f)
 {
-	struct read_lzop *state = (struct read_lzop *)self->data;
+	struct lzop *lzop = f->data;
 
-	free(state->out_block);
-	free(state);
+	free(lzop->out_block);
+	free(lzop);
 	return (ARCHIVE_OK);
 }
 
diff --git a/c/archive_read_support_filter_program.c b/c/archive_read_support_filter_program.c
--- a/c/archive_read_support_filter_program.c
+++ b/c/archive_read_support_filter_program.c
@@ -87,7 +87,7 @@
  * bid twice in the same pipeline.
  */
 struct program_bidder {
-	char *description;
+	struct archive_string description;
 	char *cmd;
 	void *signature;
 	size_t signature_len;
@@ -102,7 +102,7 @@
 /*
  * The actual filter needs to track input and output data.
  */
-struct program_filter {
+struct program {
 	struct archive_string description;
 #if defined(_WIN32) && !defined(__CYGWIN__)
 	HANDLE		 child;
@@ -145,6 +145,8 @@
 	state->cmd = strdup(cmd);
 	if (state->cmd == NULL)
 		goto memerr;
+	archive_strcpy(&state->description, "Program: ");
+	archive_strcat(&state->description, cmd);
 
 	if (signature != NULL && signature_len > 0) {
 		state->signature_len = signature_len;
@@ -168,9 +170,9 @@
 }
 
 static void
-program_bidder_free(struct archive_read_filter_bidder *self)
+program_bidder_free(struct archive_read_filter_bidder *b)
 {
-	struct program_bidder *state = (struct program_bidder *)self->data;
+	struct program_bidder *state = (struct program_bidder *)b->data;
 
 	free_state(state);
 }
@@ -180,6 +182,7 @@
 {
 
 	if (state) {
+		archive_string_free(&state->description);
 		free(state->cmd);
 		free(state->signature);
 		free(state);
@@ -193,15 +196,15 @@
  * we're called, then never bid again.
  */
 static int
-program_bidder_bid(struct archive_read_filter_bidder *self,
-    struct archive_read_filter *upstream)
+program_bidder_bid(struct archive_read_filter_bidder *b,
+    struct archive_read_filter *f)
 {
-	struct program_bidder *state = self->data;
+	struct program_bidder *state = b->data;
 	const char *p;
 
 	/* If we have a signature, use that to match. */
 	if (state->signature_len > 0) {
-		p = __archive_read_filter_ahead(upstream,
+		p = __archive_read_filter_ahead(f,
 		    state->signature_len, NULL);
 		if (p == NULL)
 			return (0);
@@ -226,36 +229,36 @@
  * (including error message if the child came to a bad end).
  */
 static int
-child_stop(struct archive_read_filter *self, struct program_filter *state)
+child_stop(struct archive_read_filter *f, struct program *program)
 {
 	/* Close our side of the I/O with the child. */
-	if (state->child_stdin != -1) {
-		close(state->child_stdin);
-		state->child_stdin = -1;
+	if (program->child_stdin != -1) {
+		close(program->child_stdin);
+		program->child_stdin = -1;
 	}
-	if (state->child_stdout != -1) {
-		close(state->child_stdout);
-		state->child_stdout = -1;
+	if (program->child_stdout != -1) {
+		close(program->child_stdout);
+		program->child_stdout = -1;
 	}
 
-	if (state->child != 0) {
+	if (program->child != 0) {
 		/* Reap the child. */
 		do {
-			state->waitpid_return
-			    = waitpid(state->child, &state->exit_status, 0);
-		} while (state->waitpid_return == -1 && errno == EINTR);
-		state->child = 0;
+			program->waitpid_return
+			    = waitpid(program->child, &program->exit_status, 0);
+		} while (program->waitpid_return == -1 && errno == EINTR);
+		program->child = 0;
 	}
 
-	if (state->waitpid_return < 0) {
+	if (program->waitpid_return < 0) {
 		/* waitpid() failed?  This is ugly. */
-		archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+		archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC,
 		    "Error closing child process");
 		return (ARCHIVE_WARN);
 	}
 
 #if !defined(_WIN32) || defined(__CYGWIN__)
-	if (WIFSIGNALED(state->exit_status)) {
+	if (WIFSIGNALED(program->exit_status)) {
 #ifdef SIGPIPE
 		/* If the child died because we stopped reading before
 		 * it was done, that's okay.  Some archive formats
@@ -263,24 +266,24 @@
 		/* The alternative to this would be to add a step
 		 * before close(child_stdout) above to read from the
 		 * child until the child has no more to write. */
-		if (WTERMSIG(state->exit_status) == SIGPIPE)
+		if (WTERMSIG(program->exit_status) == SIGPIPE)
 			return (ARCHIVE_OK);
 #endif
-		archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+		archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC,
 		    "Child process exited with signal %d",
-		    WTERMSIG(state->exit_status));
+		    WTERMSIG(program->exit_status));
 		return (ARCHIVE_WARN);
 	}
 #endif /* !_WIN32 || __CYGWIN__ */
 
-	if (WIFEXITED(state->exit_status)) {
-		if (WEXITSTATUS(state->exit_status) == 0)
+	if (WIFEXITED(program->exit_status)) {
+		if (WEXITSTATUS(program->exit_status) == 0)
 			return (ARCHIVE_OK);
 
-		archive_set_error(&self->archive->archive,
+		archive_set_error(&f->archive->archive,
 		    ARCHIVE_ERRNO_MISC,
 		    "Child process exited with status %d",
-		    WEXITSTATUS(state->exit_status));
+		    WEXITSTATUS(program->exit_status));
 		return (ARCHIVE_WARN);
 	}
 
@@ -291,13 +294,13 @@
  * Use select() to decide whether the child is ready for read or write.
  */
 static ssize_t
-child_read(struct archive_read_filter *self, char *buf, size_t buf_len)
+child_read(struct archive_read_filter *f, char *buf, size_t buf_len)
 {
-	struct program_filter *state = self->data;
+	struct program *program = f->data;
 	ssize_t ret, requested, avail;
 	const char *p;
 #if defined(_WIN32) && !defined(__CYGWIN__)
-	HANDLE handle = (HANDLE)_get_osfhandle(state->child_stdout);
+	HANDLE handle = (HANDLE)_get_osfhandle(program->child_stdout);
 #endif
 
 	requested = buf_len > SSIZE_MAX ? SSIZE_MAX : buf_len;
@@ -325,7 +328,7 @@
 				break;
 			}
 #endif
-			ret = read(state->child_stdout, buf, requested);
+			ret = read(program->child_stdout, buf, requested);
 		} while (ret == -1 && errno == EINTR);
 
 		if (ret > 0)
@@ -333,44 +336,44 @@
 		if (ret == 0 || (ret == -1 && errno == EPIPE))
 			/* Child has closed its output; reap the child
 			 * and return the status. */
-			return (child_stop(self, state));
+			return (child_stop(f, program));
 		if (ret == -1 && errno != EAGAIN)
 			return (-1);
 
-		if (state->child_stdin == -1) {
+		if (program->child_stdin == -1) {
 			/* Block until child has some I/O ready. */
-			__archive_check_child(state->child_stdin,
-			    state->child_stdout);
+			__archive_check_child(program->child_stdin,
+			    program->child_stdout);
 			continue;
 		}
 
 		/* Get some more data from upstream. */
-		p = __archive_read_filter_ahead(self->upstream, 1, &avail);
+		p = __archive_read_filter_ahead(f->upstream, 1, &avail);
 		if (p == NULL) {
-			close(state->child_stdin);
-			state->child_stdin = -1;
-			fcntl(state->child_stdout, F_SETFL, 0);
+			close(program->child_stdin);
+			program->child_stdin = -1;
+			fcntl(program->child_stdout, F_SETFL, 0);
 			if (avail < 0)
 				return (avail);
 			continue;
 		}
 
 		do {
-			ret = write(state->child_stdin, p, avail);
+			ret = write(program->child_stdin, p, avail);
 		} while (ret == -1 && errno == EINTR);
 
 		if (ret > 0) {
 			/* Consume whatever we managed to write. */
-			__archive_read_filter_consume(self->upstream, ret);
+			__archive_read_filter_consume(f->upstream, ret);
 		} else if (ret == -1 && errno == EAGAIN) {
 			/* Block until child has some I/O ready. */
-			__archive_check_child(state->child_stdin,
-			    state->child_stdout);
+			__archive_check_child(program->child_stdin,
+			    program->child_stdout);
 		} else {
 			/* Write failed. */
-			close(state->child_stdin);
-			state->child_stdin = -1;
-			fcntl(state->child_stdout, F_SETFL, 0);
+			close(program->child_stdin);
+			program->child_stdin = -1;
+			fcntl(program->child_stdout, F_SETFL, 0);
 			/* If it was a bad error, we're done; otherwise
 			 * it was EPIPE or EOF, and we can still read
 			 * from the child. */
@@ -387,9 +390,9 @@
 };
 
 int
-__archive_read_program(struct archive_read_filter *self, const char *cmd)
+__archive_read_program(struct archive_read_filter *f, const char *cmd)
 {
-	struct program_filter	*state;
+	struct program *program;
 	static const size_t out_buf_len = 65536;
 	char *out_buf;
 	const char *prefix = "Program: ";
@@ -397,70 +400,71 @@
 	size_t l;
 
 	l = strlen(prefix) + strlen(cmd) + 1;
-	state = calloc(1, sizeof(*state));
+	program = calloc(1, sizeof(*program));
 	out_buf = malloc(out_buf_len);
-	if (state == NULL || out_buf == NULL ||
-	    archive_string_ensure(&state->description, l) == NULL) {
-		archive_set_error(&self->archive->archive, ENOMEM,
+	if (program == NULL || out_buf == NULL ||
+	    archive_string_ensure(&program->description, l) == NULL) {
+		archive_set_error(&f->archive->archive, ENOMEM,
 		    "Can't allocate input data");
-		if (state != NULL) {
-			archive_string_free(&state->description);
-			free(state);
+		if (program != NULL) {
+			archive_string_free(&program->description);
+			free(program);
 		}
 		free(out_buf);
 		return (ARCHIVE_FATAL);
 	}
-	archive_strcpy(&state->description, prefix);
-	archive_strcat(&state->description, cmd);
+	archive_strcpy(&program->description, prefix);
+	archive_strcat(&program->description, cmd);
 
-	self->code = ARCHIVE_FILTER_PROGRAM;
-	self->name = state->description.s;
+	f->code = ARCHIVE_FILTER_PROGRAM;
+	f->name = program->description.s;
 
-	state->out_buf = out_buf;
-	state->out_buf_len = out_buf_len;
+	program->out_buf = out_buf;
+	program->out_buf_len = out_buf_len;
 
-	ret = __archive_create_child(cmd, &state->child_stdin,
-	    &state->child_stdout, &state->child);
+	ret = __archive_create_child(cmd, &program->child_stdin,
+	    &program->child_stdout, &program->child);
 	if (ret != ARCHIVE_OK) {
-		free(state->out_buf);
-		archive_string_free(&state->description);
-		free(state);
-		archive_set_error(&self->archive->archive, EINVAL,
+		free(program->out_buf);
+		archive_string_free(&program->description);
+		free(program);
+		archive_set_error(&f->archive->archive, EINVAL,
 		    "Can't initialize filter; unable to run program \"%s\"",
 		    cmd);
 		return (ARCHIVE_FATAL);
 	}
 
-	self->data = state;
-	self->vtable = &program_reader_vtable;
+	f->data = program;
+	f->vtable = &program_reader_vtable;
 
 	/* XXX Check that we can read at least one byte? */
 	return (ARCHIVE_OK);
 }
 
 static int
-program_bidder_init(struct archive_read_filter *self)
+program_bidder_init(struct archive_read_filter *f)
 {
 	struct program_bidder   *bidder_state;
+	int r;
 
-	bidder_state = (struct program_bidder *)self->bidder->data;
-	return (__archive_read_program(self, bidder_state->cmd));
+	bidder_state = (struct program_bidder *)f->bidder->data;
+	r = __archive_read_program(f, bidder_state->cmd);
+	f->name = bidder_state->description.s;
+	return (r);
 }
 
 static ssize_t
-program_filter_read(struct archive_read_filter *self, const void **buff)
+program_filter_read(struct archive_read_filter *f, const void **buff)
 {
-	struct program_filter *state;
+	struct program *program = f->data;
 	ssize_t bytes;
 	size_t total;
 	char *p;
 
-	state = (struct program_filter *)self->data;
-
 	total = 0;
-	p = state->out_buf;
-	while (state->child_stdout != -1 && total < state->out_buf_len) {
-		bytes = child_read(self, p, state->out_buf_len - total);
+	p = program->out_buf;
+	while (program->child_stdout != -1 && total < program->out_buf_len) {
+		bytes = child_read(f, p, program->out_buf_len - total);
 		if (bytes < 0)
 			/* No recovery is possible if we can no longer
 			 * read from the child. */
@@ -472,23 +476,22 @@
 		p += bytes;
 	}
 
-	*buff = state->out_buf;
+	*buff = program->out_buf;
 	return (total);
 }
 
 static int
-program_filter_close(struct archive_read_filter *self)
+program_filter_close(struct archive_read_filter *f)
 {
-	struct program_filter	*state;
+	struct program *program = f->data;
 	int e;
 
-	state = (struct program_filter *)self->data;
-	e = child_stop(self, state);
+	e = child_stop(f, program);
 
 	/* Release our private data. */
-	free(state->out_buf);
-	archive_string_free(&state->description);
-	free(state);
+	free(program->out_buf);
+	archive_string_free(&program->description);
+	free(program);
 
 	return (e);
 }
diff --git a/c/archive_read_support_filter_rpm.c b/c/archive_read_support_filter_rpm.c
--- a/c/archive_read_support_filter_rpm.c
+++ b/c/archive_read_support_filter_rpm.c
@@ -76,38 +76,38 @@
 }
 
 static int
-rpm_bidder_bid(struct archive_read_filter_bidder *self,
-    struct archive_read_filter *filter)
+rpm_bidder_bid(struct archive_read_filter_bidder *b,
+    struct archive_read_filter *f)
 {
-	const unsigned char *b;
+	const unsigned char *p;
 	int bits_checked;
 
-	(void)self; /* UNUSED */
+	(void)b; /* UNUSED */
 
-	b = __archive_read_filter_ahead(filter, 8, NULL);
-	if (b == NULL)
+	p = __archive_read_filter_ahead(f, 8, NULL);
+	if (p == NULL)
 		return (0);
 
 	bits_checked = 0;
 	/*
 	 * Verify Header Magic Bytes : 0XED 0XAB 0XEE 0XDB
 	 */
-	if (memcmp(b, "\xED\xAB\xEE\xDB", 4) != 0)
+	if (memcmp(p, "\xED\xAB\xEE\xDB", 4) != 0)
 		return (0);
 	bits_checked += 32;
 	/*
 	 * Check major version.
 	 */
-	if (b[4] != 3 && b[4] != 4)
+	if (p[4] != 3 && p[4] != 4)
 		return (0);
 	bits_checked += 8;
 	/*
 	 * Check package type; binary or source.
 	 */
-	if (b[6] != 0)
+	if (p[6] != 0)
 		return (0);
 	bits_checked += 8;
-	if (b[7] != 0 && b[7] != 1)
+	if (p[7] != 0 && p[7] != 1)
 		return (0);
 	bits_checked += 8;
 
@@ -121,40 +121,40 @@
 };
 
 static int
-rpm_bidder_init(struct archive_read_filter *self)
+rpm_bidder_init(struct archive_read_filter *f)
 {
 	struct rpm   *rpm;
 
-	self->code = ARCHIVE_FILTER_RPM;
-	self->name = "rpm";
+	f->code = ARCHIVE_FILTER_RPM;
+	f->name = "rpm";
 
 	rpm = calloc(1, sizeof(*rpm));
 	if (rpm == NULL) {
-		archive_set_error(&self->archive->archive, ENOMEM,
+		archive_set_error(&f->archive->archive, ENOMEM,
 		    "Can't allocate data for rpm");
 		return (ARCHIVE_FATAL);
 	}
 
-	self->data = rpm;
+	f->data = rpm;
 	rpm->data_reached = 0;
-	self->vtable = &rpm_reader_vtable;
+	f->vtable = &rpm_reader_vtable;
 
 	return (ARCHIVE_OK);
 }
 
 static ssize_t
-skip_padding(struct archive_read_filter *self)
+skip_padding(struct archive_read_filter *f)
 {
 	const unsigned char *h;
 	ssize_t avail, count, r;
 
 	do {
-		h = __archive_read_filter_ahead(self->upstream, 1, &avail);
+		h = __archive_read_filter_ahead(f->upstream, 1, &avail);
 		if (h == NULL)
 			return (ARCHIVE_FATAL);
 		for (count = 0; count < avail && *h++ == '\0'; count++)
 			;
-		r = __archive_read_filter_consume(self->upstream, count);
+		r = __archive_read_filter_consume(f->upstream, count);
 		if (r < 0)
 			return (r);
 	} while (count == avail);
@@ -163,20 +163,20 @@
 }
 
 static ssize_t
-skip_prologue(struct archive_read_filter *self)
+skip_prologue(struct archive_read_filter *f)
 {
 	const unsigned char *h;
 	ssize_t r;
 	int header, seen_header = 0;
 
 	/* Skip lead size. */
-	r = __archive_read_filter_consume(self->upstream, RPM_LEAD_SIZE);
+	r = __archive_read_filter_consume(f->upstream, RPM_LEAD_SIZE);
 	if (r < 0)
 		return (r);
 
 	do {
 		/* Read header intro. */
-		h = __archive_read_filter_ahead(self->upstream,
+		h = __archive_read_filter_ahead(f->upstream,
 		    RPM_MIN_HEAD_SIZE, NULL);
 		if (h == NULL)
 			return (ARCHIVE_FATAL);
@@ -193,13 +193,13 @@
 			length = RPM_MIN_HEAD_SIZE + section * 16 + bytes;
 
 			/* Skip header. */
-			r = __archive_read_filter_consume(self->upstream,
+			r = __archive_read_filter_consume(f->upstream,
 			     length);
 			if (r < 0)
 				return (r);
 
 			/* Skip padding. */
-			r = skip_padding(self);
+			r = skip_padding(f);
 			if (r != ARCHIVE_OK)
 				return (r);
 		}
@@ -208,7 +208,7 @@
 	/* At least one header must have been encountered. */
 	if (!seen_header) {
 		archive_set_error(
-		    &self->archive->archive,
+		    &f->archive->archive,
 		    ARCHIVE_ERRNO_FILE_FORMAT,
 		    "Unrecognized rpm header");
 		return (ARCHIVE_FATAL);
@@ -218,33 +218,30 @@
 }
 
 static ssize_t
-rpm_filter_read(struct archive_read_filter *self, const void **buff)
+rpm_filter_read(struct archive_read_filter *f, const void **buff)
 {
-	struct rpm *rpm;
+	struct rpm *rpm = f->data;
 	ssize_t r;
 
-	rpm = (struct rpm *)self->data;
-
 	if (!rpm->data_reached) {
-		r = skip_prologue(self);
+		r = skip_prologue(f);
 		if (r != ARCHIVE_OK)
 			return (r);
 		rpm->data_reached = 1;
 	}
 
-	*buff = __archive_read_filter_ahead(self->upstream, 1, &r);
+	*buff = __archive_read_filter_ahead(f->upstream, 1, &r);
 	if (r > 0)
-		__archive_read_filter_consume(self->upstream, r);
+		__archive_read_filter_consume(f->upstream, r);
 
 	return r;
 }
 
 static int
-rpm_filter_close(struct archive_read_filter *self)
+rpm_filter_close(struct archive_read_filter *f)
 {
-	struct rpm *rpm;
+	struct rpm *rpm = f->data;
 
-	rpm = (struct rpm *)self->data;
 	free(rpm);
 
 	return (ARCHIVE_OK);
diff --git a/c/archive_read_support_filter_uu.c b/c/archive_read_support_filter_uu.c
--- a/c/archive_read_support_filter_uu.c
+++ b/c/archive_read_support_filter_uu.c
@@ -43,7 +43,7 @@
 /* Maximum lookahead during bid phase */
 #define UUENCODE_BID_MAX_READ 128*1024 /* in bytes */
 
-struct uudecode {
+struct uu {
 	int64_t		 total;
 	unsigned char	*in_buff;
 #define IN_BUFF_SIZE	(1024)
@@ -63,7 +63,7 @@
 };
 
 static int	uudecode_bidder_bid(struct archive_read_filter_bidder *,
-		    struct archive_read_filter *filter);
+		    struct archive_read_filter *f);
 static int	uudecode_bidder_init(struct archive_read_filter *);
 
 static int	uudecode_read_header(struct archive_read_filter *,
@@ -207,7 +207,7 @@
 }
 
 static ssize_t
-bid_get_line(struct archive_read_filter *filter,
+bid_get_line(struct archive_read_filter *f,
     const unsigned char **b, ssize_t *avail, ssize_t *ravail,
     ssize_t *nl, size_t* nbytes_read)
 {
@@ -235,12 +235,12 @@
 		if (nbytes_req < (size_t)*ravail + 160)
 			nbytes_req <<= 1;
 
-		*b = __archive_read_filter_ahead(filter, nbytes_req, avail);
+		*b = __archive_read_filter_ahead(f, nbytes_req, avail);
 		if (*b == NULL) {
 			if (*ravail >= *avail)
 				return (0);
 			/* Reading bytes reaches the end of a stream. */
-			*b = __archive_read_filter_ahead(filter, *avail, avail);
+			*b = __archive_read_filter_ahead(f, *avail, avail);
 			quit = 1;
 		}
 		*nbytes_read = *avail;
@@ -258,42 +258,42 @@
 #define UUDECODE(c) (((c) - 0x20) & 0x3f)
 
 static int
-uudecode_bidder_bid(struct archive_read_filter_bidder *self,
-    struct archive_read_filter *filter)
+uudecode_bidder_bid(struct archive_read_filter_bidder *b,
+    struct archive_read_filter *f)
 {
-	const unsigned char *b;
+	const unsigned char *p;
 	ssize_t avail, ravail;
 	ssize_t len, nl;
 	int l;
 	int firstline;
 	size_t nbytes_read;
 
-	(void)self; /* UNUSED */
+	(void)b; /* UNUSED */
 
-	b = __archive_read_filter_ahead(filter, 1, &avail);
-	if (b == NULL)
+	p = __archive_read_filter_ahead(f, 1, &avail);
+	if (p == NULL)
 		return (0);
 
 	firstline = 20;
 	ravail = avail;
 	nbytes_read = avail;
 	for (;;) {
-		len = bid_get_line(filter, &b, &avail, &ravail, &nl, &nbytes_read);
+		len = bid_get_line(f, &p, &avail, &ravail, &nl, &nbytes_read);
 		if (len < 0 || nl == 0)
 			return (0); /* No match found. */
-		if (len - nl >= 11 && memcmp(b, "begin ", 6) == 0)
+		if (len - nl >= 11 && memcmp(p, "begin ", 6) == 0)
 			l = 6;
-		else if (len -nl >= 18 && memcmp(b, "begin-base64 ", 13) == 0)
+		else if (len -nl >= 18 && memcmp(p, "begin-base64 ", 13) == 0)
 			l = 13;
 		else
 			l = 0;
 
-		if (l > 0 && (b[l] < '0' || b[l] > '7' ||
-		    b[l+1] < '0' || b[l+1] > '7' ||
-		    b[l+2] < '0' || b[l+2] > '7' || b[l+3] != ' '))
+		if (l > 0 && (p[l] < '0' || p[l] > '7' ||
+		    p[l+1] < '0' || p[l+1] > '7' ||
+		    p[l+2] < '0' || p[l+2] > '7' || p[l+3] != ' '))
 			l = 0;
 
-		b += len;
+		p += len;
 		avail -= len;
 		if (l)
 			break;
@@ -305,51 +305,51 @@
 	}
 	if (!avail)
 		return (0);
-	len = bid_get_line(filter, &b, &avail, &ravail, &nl, &nbytes_read);
+	len = bid_get_line(f, &p, &avail, &ravail, &nl, &nbytes_read);
 	if (len < 0 || nl == 0)
 		return (0);/* There are non-ascii characters. */
 	avail -= len;
 
 	if (l == 6) {
 		/* "begin " */
-		if (!uuchar[*b])
+		if (!uuchar[*p])
 			return (0);
 		/* Get a length of decoded bytes. */
-		l = UUDECODE(*b++); len--;
+		l = UUDECODE(*p++); len--;
 		if (l > 45)
 			/* Normally, maximum length is 45(character 'M'). */
 			return (0);
 		if (l > len - nl)
 			return (0); /* Line too short. */
 		while (l) {
-			if (!uuchar[*b++])
+			if (!uuchar[*p++])
 				return (0);
 			--len;
 			--l;
 		}
 		if (len-nl == 1 &&
-		    (uuchar[*b] ||		 /* Check sum. */
-		     (*b >= 'a' && *b <= 'z'))) {/* Padding data(MINIX). */
-			++b;
+		    (uuchar[*p] ||		 /* Check sum. */
+		     (*p >= 'a' && *p <= 'z'))) {/* Padding data(MINIX). */
+			++p;
 			--len;
 		}
-		b += nl;
-		if (avail && uuchar[*b])
+		p += nl;
+		if (avail && uuchar[*p])
 			return (firstline+30);
 	} else if (l == 13) {
 		/* "begin-base64 " */
 		while (len-nl > 0) {
-			if (!base64[*b++])
+			if (!base64[*p++])
 				return (0);
 			--len;
 		}
-		b += nl;
+		p += nl;
 
-		if (avail >= 5 && memcmp(b, "====\n", 5) == 0)
+		if (avail >= 5 && memcmp(p, "====\n", 5) == 0)
 			return (firstline+40);
-		if (avail >= 6 && memcmp(b, "====\r\n", 6) == 0)
+		if (avail >= 6 && memcmp(p, "====\r\n", 6) == 0)
 			return (firstline+40);
-		if (avail > 0 && base64[*b])
+		if (avail > 0 && base64[*p])
 			return (firstline+30);
 	}
 
@@ -364,46 +364,46 @@
 };
 
 static int
-uudecode_bidder_init(struct archive_read_filter *self)
+uudecode_bidder_init(struct archive_read_filter *f)
 {
-	struct uudecode   *uudecode;
+	struct uu   *uu;
 	void *out_buff;
 	void *in_buff;
 
-	self->code = ARCHIVE_FILTER_UU;
-	self->name = "uu";
+	f->code = ARCHIVE_FILTER_UU;
+	f->name = "uu";
 
-	uudecode = calloc(1, sizeof(*uudecode));
+	uu = calloc(1, sizeof(*uu));
 	out_buff = malloc(OUT_BUFF_SIZE);
 	in_buff = malloc(IN_BUFF_SIZE);
-	if (uudecode == NULL || out_buff == NULL || in_buff == NULL) {
-		archive_set_error(&self->archive->archive, ENOMEM,
+	if (uu == NULL || out_buff == NULL || in_buff == NULL) {
+		archive_set_error(&f->archive->archive, ENOMEM,
 		    "Can't allocate data for uudecode");
-		free(uudecode);
+		free(uu);
 		free(out_buff);
 		free(in_buff);
 		return (ARCHIVE_FATAL);
 	}
 
-	self->data = uudecode;
-	uudecode->in_buff = in_buff;
-	uudecode->in_cnt = 0;
-	uudecode->in_allocated = IN_BUFF_SIZE;
-	uudecode->out_buff = out_buff;
-	uudecode->state = ST_FIND_HEAD;
-	uudecode->mode_set = 0;
-	uudecode->name = NULL;
-	self->vtable = &uudecode_reader_vtable;
+	f->data = uu;
+	uu->in_buff = in_buff;
+	uu->in_cnt = 0;
+	uu->in_allocated = IN_BUFF_SIZE;
+	uu->out_buff = out_buff;
+	uu->state = ST_FIND_HEAD;
+	uu->mode_set = 0;
+	uu->name = NULL;
+	f->vtable = &uudecode_reader_vtable;
 
 	return (ARCHIVE_OK);
 }
 
 static int
-ensure_in_buff_size(struct archive_read_filter *self,
-    struct uudecode *uudecode, size_t size)
+ensure_in_buff_size(struct archive_read_filter *f,
+    struct uu *uu, size_t size)
 {
 
-	if (size > uudecode->in_allocated) {
+	if (size > uu->in_allocated) {
 		unsigned char *ptr;
 		size_t newsize;
 
@@ -411,7 +411,7 @@
 		 * Calculate a new buffer size for in_buff.
 		 * Increase its value until it is enough for our needs.
 		 */
-		newsize = uudecode->in_allocated;
+		newsize = uu->in_allocated;
 		do {
 			if (newsize < IN_BUFF_SIZE*32)
 				newsize <<= 1;
@@ -421,42 +421,40 @@
 		/* Allocate the new buffer. */
 		ptr = malloc(newsize);
 		if (ptr == NULL) {
-			archive_set_error(&self->archive->archive,
+			archive_set_error(&f->archive->archive,
 			    ENOMEM,
     			    "Can't allocate data for uudecode");
 			return (ARCHIVE_FATAL);
 		}
 		/* Move the remaining data in in_buff into the new buffer. */
-		if (uudecode->in_cnt)
-			memmove(ptr, uudecode->in_buff, uudecode->in_cnt);
+		if (uu->in_cnt)
+			memmove(ptr, uu->in_buff, uu->in_cnt);
 		/* Replace in_buff with the new buffer. */
-		free(uudecode->in_buff);
-		uudecode->in_buff = ptr;
-		uudecode->in_allocated = newsize;
+		free(uu->in_buff);
+		uu->in_buff = ptr;
+		uu->in_allocated = newsize;
 	}
 	return (ARCHIVE_OK);
 }
 
 static int
-uudecode_read_header(struct archive_read_filter *self, struct archive_entry *entry)
+uudecode_read_header(struct archive_read_filter *f, struct archive_entry *entry)
 {
-
-	struct uudecode *uudecode;
-	uudecode = (struct uudecode *)self->data;
+	struct uu *uu = f->data;
 
-	if (uudecode->mode_set != 0)
-		archive_entry_set_mode(entry, S_IFREG | uudecode->mode);
+	if (uu->mode_set != 0)
+		archive_entry_set_mode(entry, S_IFREG | uu->mode);
 
-	if (uudecode->name != NULL)
-		archive_entry_set_pathname(entry, uudecode->name);
+	if (uu->name != NULL)
+		archive_entry_set_pathname(entry, uu->name);
 
 	return (ARCHIVE_OK);
 }
 
 static ssize_t
-uudecode_filter_read(struct archive_read_filter *self, const void **buff)
+uudecode_filter_read(struct archive_read_filter *f, const void **buff)
 {
-	struct uudecode *uudecode;
+	struct uu *uu = f->data;
 	const unsigned char *b, *d;
 	unsigned char *out;
 	ssize_t avail_in, ravail;
@@ -464,10 +462,8 @@
 	ssize_t total;
 	ssize_t len, llen, nl, namelen;
 
-	uudecode = (struct uudecode *)self->data;
-
 read_more:
-	d = __archive_read_filter_ahead(self->upstream, 1, &avail_in);
+	d = __archive_read_filter_ahead(f->upstream, 1, &avail_in);
 	if (d == NULL && avail_in < 0)
 		return (ARCHIVE_FATAL);
 	/* Quiet a code analyzer; make sure avail_in must be zero
@@ -476,17 +472,17 @@
 		avail_in = 0;
 	used = 0;
 	total = 0;
-	out = uudecode->out_buff;
+	out = uu->out_buff;
 	if (avail_in > 2 * UUENCODE_BID_MAX_READ)
 		avail_in = 2 * UUENCODE_BID_MAX_READ;
 	ravail = avail_in;
-	if (uudecode->state == ST_IGNORE) {
+	if (uu->state == ST_IGNORE) {
 		used = avail_in;
 		goto finish;
 	}
-	if (uudecode->in_cnt) {
-		if (uudecode->in_cnt > UUENCODE_BID_MAX_READ) {
-			archive_set_error(&self->archive->archive,
+	if (uu->in_cnt) {
+		if (uu->in_cnt > UUENCODE_BID_MAX_READ) {
+			archive_set_error(&f->archive->archive,
 			    ARCHIVE_ERRNO_FILE_FORMAT,
 			    "Invalid format data");
 			return (ARCHIVE_FATAL);
@@ -495,14 +491,15 @@
 		 * If there is remaining data which is saved by
 		 * a previous call, use it first.
 		 */
-		if (ensure_in_buff_size(self, uudecode,
-		    avail_in + uudecode->in_cnt) != ARCHIVE_OK)
+		if (ensure_in_buff_size(f, uu,
+		    avail_in + uu->in_cnt) != ARCHIVE_OK)
 			return (ARCHIVE_FATAL);
-		memcpy(uudecode->in_buff + uudecode->in_cnt,
-		    d, avail_in);
-		d = uudecode->in_buff;
-		avail_in += uudecode->in_cnt;
-		uudecode->in_cnt = 0;
+		if (avail_in > 0)
+			memcpy(uu->in_buff + uu->in_cnt,
+			    d, avail_in);
+		d = uu->in_buff;
+		avail_in += uu->in_cnt;
+		uu->in_cnt = 0;
 	}
 	for (;used < avail_in; d += llen, used += llen) {
 		ssize_t l, body;
@@ -511,28 +508,28 @@
 		len = get_line(b, avail_in - used, &nl);
 		if (len < 0) {
 			/* Non-ascii character is found. */
-			if (uudecode->state == ST_FIND_HEAD &&
-			    (uudecode->total > 0 || total > 0)) {
-				uudecode->state = ST_IGNORE;
+			if (uu->state == ST_FIND_HEAD &&
+			    (uu->total > 0 || total > 0)) {
+				uu->state = ST_IGNORE;
 				used = avail_in;
 				goto finish;
 			}
-			archive_set_error(&self->archive->archive,
+			archive_set_error(&f->archive->archive,
 			    ARCHIVE_ERRNO_MISC,
 			    "Insufficient compressed data");
 			return (ARCHIVE_FATAL);
 		}
 		if (len > UUENCODE_BID_MAX_READ) {
-			archive_set_error(&self->archive->archive,
+			archive_set_error(&f->archive->archive,
 			    ARCHIVE_ERRNO_FILE_FORMAT,
 			    "Invalid format data");
 			return (ARCHIVE_FATAL);
 		}
 		llen = len;
-		if ((nl == 0) && (uudecode->state != ST_UUEND)) {
+		if ((nl == 0) && (uu->state != ST_UUEND)) {
 			if (total == 0 && ravail <= 0) {
 				/* There is nothing more to read, fail */
-				archive_set_error(&self->archive->archive,
+				archive_set_error(&f->archive->archive,
 				    ARCHIVE_ERRNO_FILE_FORMAT,
 				    "Missing format data");
 				return (ARCHIVE_FATAL);
@@ -541,28 +538,28 @@
 			 * Save remaining data which does not contain
 			 * NL('\n','\r').
 			 */
-			if (ensure_in_buff_size(self, uudecode, len)
+			if (ensure_in_buff_size(f, uu, len)
 			    != ARCHIVE_OK)
 				return (ARCHIVE_FATAL);
-			if (uudecode->in_buff != b)
-				memmove(uudecode->in_buff, b, len);
-			uudecode->in_cnt = len;
+			if (uu->in_buff != b)
+				memmove(uu->in_buff, b, len);
+			uu->in_cnt = len;
 			if (total == 0) {
 				/* Do not return 0; it means end-of-file.
 				 * We should try to read more bytes. */
 				__archive_read_filter_consume(
-				    self->upstream, ravail);
+				    f->upstream, ravail);
 				goto read_more;
 			}
 			used += len;
 			break;
 		}
-		switch (uudecode->state) {
+		switch (uu->state) {
 		default:
 		case ST_FIND_HEAD:
 			/* Do not read more than UUENCODE_BID_MAX_READ bytes */
 			if (total + len >= UUENCODE_BID_MAX_READ) {
-				archive_set_error(&self->archive->archive,
+				archive_set_error(&f->archive->archive,
 				    ARCHIVE_ERRNO_FILE_FORMAT,
 				    "Invalid format data");
 				return (ARCHIVE_FATAL);
@@ -578,30 +575,30 @@
 			    b[l+1] >= '0' && b[l+1] <= '7' &&
 			    b[l+2] >= '0' && b[l+2] <= '7' && b[l+3] == ' ') {
 				if (l == 6)
-					uudecode->state = ST_READ_UU;
+					uu->state = ST_READ_UU;
 				else
-					uudecode->state = ST_READ_BASE64;
-				uudecode->mode = (mode_t)(
+					uu->state = ST_READ_BASE64;
+				uu->mode = (mode_t)(
 				    ((int)(b[l] - '0') * 64) +
 				    ((int)(b[l+1] - '0') * 8) +
 				     (int)(b[l+2] - '0'));
-				uudecode->mode_set = 1;
+				uu->mode_set = 1;
 				namelen = len - nl - 4 - l;
 				if (namelen > 1) {
-					if (uudecode->name != NULL)
-						free(uudecode->name);
-					uudecode->name = malloc(namelen + 1);
-					if (uudecode->name == NULL) {
+					if (uu->name != NULL)
+						free(uu->name);
+					uu->name = malloc(namelen + 1);
+					if (uu->name == NULL) {
 						archive_set_error(
-						    &self->archive->archive,
+						    &f->archive->archive,
 						    ENOMEM,
 						    "Can't allocate data for uudecode");
 						return (ARCHIVE_FATAL);
 					}
-					strncpy(uudecode->name,
+					strncpy(uu->name,
 					    (const char *)(b + l + 4),
 					    namelen);
-					uudecode->name[namelen] = '\0';
+					uu->name[namelen] = '\0';
 				}
 			}
 			break;
@@ -610,7 +607,7 @@
 				goto finish;
 			body = len - nl;
 			if (!uuchar[*b] || body <= 0) {
-				archive_set_error(&self->archive->archive,
+				archive_set_error(&f->archive->archive,
 				    ARCHIVE_ERRNO_MISC,
 				    "Insufficient compressed data");
 				return (ARCHIVE_FATAL);
@@ -619,13 +616,13 @@
 			l = UUDECODE(*b++);
 			body--;
 			if (l > body) {
-				archive_set_error(&self->archive->archive,
+				archive_set_error(&f->archive->archive,
 				    ARCHIVE_ERRNO_MISC,
 				    "Insufficient compressed data");
 				return (ARCHIVE_FATAL);
 			}
 			if (l == 0) {
-				uudecode->state = ST_UUEND;
+				uu->state = ST_UUEND;
 				break;
 			}
 			while (l > 0) {
@@ -654,7 +651,7 @@
 				}
 			}
 			if (l) {
-				archive_set_error(&self->archive->archive,
+				archive_set_error(&f->archive->archive,
 				    ARCHIVE_ERRNO_MISC,
 				    "Insufficient compressed data");
 				return (ARCHIVE_FATAL);
@@ -662,9 +659,9 @@
 			break;
 		case ST_UUEND:
 			if (len - nl == 3 && memcmp(b, "end ", 3) == 0)
-				uudecode->state = ST_FIND_HEAD;
+				uu->state = ST_FIND_HEAD;
 			else {
-				archive_set_error(&self->archive->archive,
+				archive_set_error(&f->archive->archive,
 				    ARCHIVE_ERRNO_MISC,
 				    "Insufficient compressed data");
 				return (ARCHIVE_FATAL);
@@ -676,7 +673,7 @@
 			l = len - nl;
 			if (l >= 3 && b[0] == '=' && b[1] == '=' &&
 			    b[2] == '=') {
-				uudecode->state = ST_FIND_HEAD;
+				uu->state = ST_FIND_HEAD;
 				break;
 			}
 			while (l > 0) {
@@ -709,7 +706,7 @@
 				}
 			}
 			if (l && *b != '=') {
-				archive_set_error(&self->archive->archive,
+				archive_set_error(&f->archive->archive,
 				    ARCHIVE_ERRNO_MISC,
 				    "Insufficient compressed data");
 				return (ARCHIVE_FATAL);
@@ -720,23 +717,22 @@
 finish:
 	if (ravail < avail_in)
 		used -= avail_in - ravail;
-	__archive_read_filter_consume(self->upstream, used);
+	__archive_read_filter_consume(f->upstream, used);
 
-	*buff = uudecode->out_buff;
-	uudecode->total += total;
+	*buff = uu->out_buff;
+	uu->total += total;
 	return (total);
 }
 
 static int
-uudecode_filter_close(struct archive_read_filter *self)
+uudecode_filter_close(struct archive_read_filter *f)
 {
-	struct uudecode *uudecode;
+	struct uu *uu = f->data;
 
-	uudecode = (struct uudecode *)self->data;
-	free(uudecode->in_buff);
-	free(uudecode->out_buff);
-	free(uudecode->name);
-	free(uudecode);
+	free(uu->in_buff);
+	free(uu->out_buff);
+	free(uu->name);
+	free(uu);
 
 	return (ARCHIVE_OK);
 }
diff --git a/c/archive_read_support_filter_xz.c b/c/archive_read_support_filter_xz.c
--- a/c/archive_read_support_filter_xz.c
+++ b/c/archive_read_support_filter_xz.c
@@ -50,7 +50,7 @@
 
 #if HAVE_LZMA_H && HAVE_LIBLZMA
 
-struct private_data {
+struct xz {
 	lzma_stream	 stream;
 	unsigned char	*out_block;
 	size_t		 out_block_size;
@@ -197,14 +197,14 @@
  * Test whether we can handle this data.
  */
 static int
-xz_bidder_bid(struct archive_read_filter_bidder *self,
-    struct archive_read_filter *filter)
+xz_bidder_bid(struct archive_read_filter_bidder *b,
+    struct archive_read_filter *f)
 {
 	const unsigned char *buffer;
 
-	(void)self; /* UNUSED */
+	(void)b; /* UNUSED */
 
-	buffer = __archive_read_filter_ahead(filter, 6, NULL);
+	buffer = __archive_read_filter_ahead(f, 6, NULL);
 	if (buffer == NULL)
 		return (0);
 
@@ -230,17 +230,17 @@
  * they have other evidence (file name, command-line option) to go on.
  */
 static int
-lzma_bidder_bid(struct archive_read_filter_bidder *self,
-    struct archive_read_filter *filter)
+lzma_bidder_bid(struct archive_read_filter_bidder *b,
+    struct archive_read_filter *f)
 {
 	const unsigned char *buffer;
 	uint32_t dicsize;
 	uint64_t uncompressed_size;
 	int bits_checked;
 
-	(void)self; /* UNUSED */
+	(void)b; /* UNUSED */
 
-	buffer = __archive_read_filter_ahead(filter, 14, NULL);
+	buffer = __archive_read_filter_ahead(f, 14, NULL);
 	if (buffer == NULL)
 		return (0);
 
@@ -336,13 +336,13 @@
 }
 
 static int
-lzip_has_member(struct archive_read_filter *filter)
+lzip_has_member(struct archive_read_filter *f)
 {
 	const unsigned char *buffer;
 	int bits_checked;
 	int log2dic;
 
-	buffer = __archive_read_filter_ahead(filter, 6, NULL);
+	buffer = __archive_read_filter_ahead(f, 6, NULL);
 	if (buffer == NULL)
 		return (0);
 
@@ -369,12 +369,12 @@
 }
 
 static int
-lzip_bidder_bid(struct archive_read_filter_bidder *self,
-    struct archive_read_filter *filter)
+lzip_bidder_bid(struct archive_read_filter_bidder *b,
+    struct archive_read_filter *f)
 {
 
-	(void)self; /* UNUSED */
-	return (lzip_has_member(filter));
+	(void)b; /* UNUSED */
+	return (lzip_has_member(f));
 }
 
 #if HAVE_LZMA_H && HAVE_LIBLZMA
@@ -383,34 +383,34 @@
  * liblzma 4.999.7 and later support both lzma and xz streams.
  */
 static int
-xz_bidder_init(struct archive_read_filter *self)
+xz_bidder_init(struct archive_read_filter *f)
 {
-	self->code = ARCHIVE_FILTER_XZ;
-	self->name = "xz";
-	return (xz_lzma_bidder_init(self));
+	f->code = ARCHIVE_FILTER_XZ;
+	f->name = "xz";
+	return (xz_lzma_bidder_init(f));
 }
 
 static int
-lzma_bidder_init(struct archive_read_filter *self)
+lzma_bidder_init(struct archive_read_filter *f)
 {
-	self->code = ARCHIVE_FILTER_LZMA;
-	self->name = "lzma";
-	return (xz_lzma_bidder_init(self));
+	f->code = ARCHIVE_FILTER_LZMA;
+	f->name = "lzma";
+	return (xz_lzma_bidder_init(f));
 }
 
 static int
-lzip_bidder_init(struct archive_read_filter *self)
+lzip_bidder_init(struct archive_read_filter *f)
 {
-	self->code = ARCHIVE_FILTER_LZIP;
-	self->name = "lzip";
-	return (xz_lzma_bidder_init(self));
+	f->code = ARCHIVE_FILTER_LZIP;
+	f->name = "lzip";
+	return (xz_lzma_bidder_init(f));
 }
 
 /*
  * Set an error code and choose an error message
  */
 static void
-set_error(struct archive_read_filter *self, int ret)
+set_error(struct archive_read_filter *f, int ret)
 {
 
 	switch (ret) {
@@ -418,36 +418,36 @@
 	case LZMA_OK: /* Decompressor made some progress. */
 		break;
 	case LZMA_MEM_ERROR:
-		archive_set_error(&self->archive->archive, ENOMEM,
+		archive_set_error(&f->archive->archive, ENOMEM,
 		    "Lzma library error: Cannot allocate memory");
 		break;
 	case LZMA_MEMLIMIT_ERROR:
-		archive_set_error(&self->archive->archive, ENOMEM,
+		archive_set_error(&f->archive->archive, ENOMEM,
 		    "Lzma library error: Out of memory");
 		break;
 	case LZMA_FORMAT_ERROR:
-		archive_set_error(&self->archive->archive,
+		archive_set_error(&f->archive->archive,
 		    ARCHIVE_ERRNO_MISC,
 		    "Lzma library error: format not recognized");
 		break;
 	case LZMA_OPTIONS_ERROR:
-		archive_set_error(&self->archive->archive,
+		archive_set_error(&f->archive->archive,
 		    ARCHIVE_ERRNO_MISC,
 		    "Lzma library error: Invalid options");
 		break;
 	case LZMA_DATA_ERROR:
-		archive_set_error(&self->archive->archive,
+		archive_set_error(&f->archive->archive,
 		    ARCHIVE_ERRNO_MISC,
 		    "Lzma library error: Corrupted input data");
 		break;
 	case LZMA_BUF_ERROR:
-		archive_set_error(&self->archive->archive,
+		archive_set_error(&f->archive->archive,
 		    ARCHIVE_ERRNO_MISC,
 		    "Lzma library error:  No progress is possible");
 		break;
 	default:
 		/* Return an error. */
-		archive_set_error(&self->archive->archive,
+		archive_set_error(&f->archive->archive,
 		    ARCHIVE_ERRNO_MISC,
 		    "Lzma decompression failed:  Unknown error");
 		break;
@@ -464,84 +464,83 @@
  * Setup the callbacks.
  */
 static int
-xz_lzma_bidder_init(struct archive_read_filter *self)
+xz_lzma_bidder_init(struct archive_read_filter *f)
 {
 	static const size_t out_block_size = 64 * 1024;
 	void *out_block;
-	struct private_data *state;
+	struct xz *xz;
 	int ret;
 
-	state = calloc(1, sizeof(*state));
+	xz = calloc(1, sizeof(*xz));
 	out_block = malloc(out_block_size);
-	if (state == NULL || out_block == NULL) {
-		archive_set_error(&self->archive->archive, ENOMEM,
+	if (xz == NULL || out_block == NULL) {
+		archive_set_error(&f->archive->archive, ENOMEM,
 		    "Can't allocate data for xz decompression");
 		free(out_block);
-		free(state);
+		free(xz);
 		return (ARCHIVE_FATAL);
 	}
 
-	self->data = state;
-	state->out_block_size = out_block_size;
-	state->out_block = out_block;
-	self->vtable = &xz_lzma_reader_vtable;
+	f->data = xz;
+	xz->out_block_size = out_block_size;
+	xz->out_block = out_block;
+	f->vtable = &xz_lzma_reader_vtable;
 
-	state->stream.avail_in = 0;
+	xz->stream.avail_in = 0;
 
-	state->stream.next_out = state->out_block;
-	state->stream.avail_out = state->out_block_size;
+	xz->stream.next_out = xz->out_block;
+	xz->stream.avail_out = xz->out_block_size;
 
-	state->crc32 = 0;
-	if (self->code == ARCHIVE_FILTER_LZIP) {
+	xz->crc32 = 0;
+	if (f->code == ARCHIVE_FILTER_LZIP) {
 		/*
 		 * We have to read a lzip header and use it to initialize
 		 * compression library, thus we cannot initialize the
 		 * library for lzip here.
 		 */
-		state->in_stream = 0;
+		xz->in_stream = 0;
 		return (ARCHIVE_OK);
 	} else
-		state->in_stream = 1;
+		xz->in_stream = 1;
 
 	/* Initialize compression library. */
-	if (self->code == ARCHIVE_FILTER_XZ)
-		ret = lzma_stream_decoder(&(state->stream),
+	if (f->code == ARCHIVE_FILTER_XZ)
+		ret = lzma_stream_decoder(&(xz->stream),
 		    LZMA_MEMLIMIT,/* memlimit */
 		    LZMA_CONCATENATED);
 	else
-		ret = lzma_alone_decoder(&(state->stream),
+		ret = lzma_alone_decoder(&(xz->stream),
 		    LZMA_MEMLIMIT);/* memlimit */
 
 	if (ret == LZMA_OK)
 		return (ARCHIVE_OK);
 
 	/* Library setup failed: Choose an error message and clean up. */
-	set_error(self, ret);
+	set_error(f, ret);
 
-	free(state->out_block);
-	free(state);
-	self->data = NULL;
-	self->vtable = NULL;
+	free(xz->out_block);
+	free(xz);
+	f->data = NULL;
+	f->vtable = NULL;
 	return (ARCHIVE_FATAL);
 }
 
 static int
-lzip_init(struct archive_read_filter *self)
+lzip_init(struct archive_read_filter *f)
 {
-	struct private_data *state;
+	struct xz *xz = f->data;
 	const unsigned char *h;
 	lzma_filter filters[2];
 	unsigned char props[5];
 	uint32_t dicsize;
 	int log2dic, ret;
 
-	state = (struct private_data *)self->data;
-	h = __archive_read_filter_ahead(self->upstream, 6, NULL);
+	h = __archive_read_filter_ahead(f->upstream, 6, NULL);
 	if (h == NULL)
 		return (ARCHIVE_FATAL);
 
 	/* Get a version number. */
-	state->lzip_ver = h[4];
+	xz->lzip_ver = h[4];
 
 	/*
 	 * Setup lzma property.
@@ -558,8 +557,8 @@
 	archive_le32enc(props+1, dicsize);
 
 	/* Consume lzip header. */
-	__archive_read_filter_consume(self->upstream, 6);
-	state->member_in = 6;
+	__archive_read_filter_consume(f->upstream, 6);
+	xz->member_in = 6;
 
 	filters[0].id = LZMA_FILTER_LZMA1;
 	filters[0].options = NULL;
@@ -568,74 +567,73 @@
 
 	ret = lzma_properties_decode(&filters[0], NULL, props, sizeof(props));
 	if (ret != LZMA_OK) {
-		set_error(self, ret);
+		set_error(f, ret);
 		return (ARCHIVE_FATAL);
 	}
-	ret = lzma_raw_decoder(&(state->stream), filters);
+	ret = lzma_raw_decoder(&(xz->stream), filters);
 	free(filters[0].options);
 	if (ret != LZMA_OK) {
-		set_error(self, ret);
+		set_error(f, ret);
 		return (ARCHIVE_FATAL);
 	}
 	return (ARCHIVE_OK);
 }
 
 static int
-lzip_tail(struct archive_read_filter *self)
+lzip_tail(struct archive_read_filter *f)
 {
-	struct private_data *state;
-	const unsigned char *f;
+	struct xz *xz = f->data;
+	const unsigned char *p;
 	ssize_t avail_in;
 	int tail;
 
-	state = (struct private_data *)self->data;
-	if (state->lzip_ver == 0)
+	if (xz->lzip_ver == 0)
 		tail = 12;
 	else
 		tail = 20;
-	f = __archive_read_filter_ahead(self->upstream, tail, &avail_in);
-	if (f == NULL && avail_in < 0)
+	p = __archive_read_filter_ahead(f->upstream, tail, &avail_in);
+	if (p == NULL && avail_in < 0)
 		return (ARCHIVE_FATAL);
-	if (f == NULL || avail_in < tail) {
-		archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+	if (p == NULL || avail_in < tail) {
+		archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC,
 		    "Lzip: Remaining data is less bytes");
 		return (ARCHIVE_FAILED);
 	}
 
 	/* Check the crc32 value of the uncompressed data of the current
 	 * member */
-	if (state->crc32 != archive_le32dec(f)) {
+	if (xz->crc32 != archive_le32dec(p)) {
 #ifndef DONT_FAIL_ON_CRC_ERROR
-		archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+		archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC,
 		    "Lzip: CRC32 error");
 		return (ARCHIVE_FAILED);
 #endif
 	}
 
 	/* Check the uncompressed size of the current member */
-	if ((uint64_t)state->member_out != archive_le64dec(f + 4)) {
-		archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+	if ((uint64_t)xz->member_out != archive_le64dec(p + 4)) {
+		archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC,
 		    "Lzip: Uncompressed size error");
 		return (ARCHIVE_FAILED);
 	}
 
 	/* Check the total size of the current member */
-	if (state->lzip_ver == 1 &&
-	    (uint64_t)state->member_in + tail != archive_le64dec(f + 12)) {
-		archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+	if (xz->lzip_ver == 1 &&
+	    (uint64_t)xz->member_in + tail != archive_le64dec(p + 12)) {
+		archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC,
 		    "Lzip: Member size error");
 		return (ARCHIVE_FAILED);
 	}
-	__archive_read_filter_consume(self->upstream, tail);
+	__archive_read_filter_consume(f->upstream, tail);
 
 	/* If current lzip data consists of multi member, try decompressing
 	 * a next member. */
-	if (lzip_has_member(self->upstream) != 0) {
-		state->in_stream = 0;
-		state->crc32 = 0;
-		state->member_out = 0;
-		state->member_in = 0;
-		state->eof = 0;
+	if (lzip_has_member(f->upstream) != 0) {
+		xz->in_stream = 0;
+		xz->crc32 = 0;
+		xz->member_out = 0;
+		xz->member_in = 0;
+		xz->eof = 0;
 	}
 	return (ARCHIVE_OK);
 }
@@ -644,82 +642,80 @@
  * Return the next block of decompressed data.
  */
 static ssize_t
-xz_filter_read(struct archive_read_filter *self, const void **p)
+xz_filter_read(struct archive_read_filter *f, const void **p)
 {
-	struct private_data *state;
+	struct xz *xz = f->data;
 	size_t decompressed;
 	ssize_t avail_in;
 	int64_t member_in;
 	int ret;
 
-	state = (struct private_data *)self->data;
-
 	redo:
 	/* Empty our output buffer. */
-	state->stream.next_out = state->out_block;
-	state->stream.avail_out = state->out_block_size;
-	member_in = state->member_in;
+	xz->stream.next_out = xz->out_block;
+	xz->stream.avail_out = xz->out_block_size;
+	member_in = xz->member_in;
 
 	/* Try to fill the output buffer. */
-	while (state->stream.avail_out > 0 && !state->eof) {
-		if (!state->in_stream) {
+	while (xz->stream.avail_out > 0 && !xz->eof) {
+		if (!xz->in_stream) {
 			/*
 			 * Initialize liblzma for lzip
 			 */
-			ret = lzip_init(self);
+			ret = lzip_init(f);
 			if (ret != ARCHIVE_OK)
 				return (ret);
-			state->in_stream = 1;
+			xz->in_stream = 1;
 		}
-		state->stream.next_in =
-		    __archive_read_filter_ahead(self->upstream, 1, &avail_in);
-		if (state->stream.next_in == NULL && avail_in < 0) {
-			archive_set_error(&self->archive->archive,
+		xz->stream.next_in =
+		    __archive_read_filter_ahead(f->upstream, 1, &avail_in);
+		if (xz->stream.next_in == NULL && avail_in < 0) {
+			archive_set_error(&f->archive->archive,
 			    ARCHIVE_ERRNO_MISC,
 			    "truncated input");
 			return (ARCHIVE_FATAL);
 		}
-		state->stream.avail_in = avail_in;
+		xz->stream.avail_in = avail_in;
 
 		/* Decompress as much as we can in one pass. */
-		ret = lzma_code(&(state->stream),
-		    (state->stream.avail_in == 0)? LZMA_FINISH: LZMA_RUN);
+		ret = lzma_code(&(xz->stream),
+		    (xz->stream.avail_in == 0)? LZMA_FINISH: LZMA_RUN);
 		switch (ret) {
 		case LZMA_STREAM_END: /* Found end of stream. */
-			state->eof = 1;
+			xz->eof = 1;
 			/* FALL THROUGH */
 		case LZMA_OK: /* Decompressor made some progress. */
-			__archive_read_filter_consume(self->upstream,
-			    avail_in - state->stream.avail_in);
-			state->member_in +=
-			    avail_in - state->stream.avail_in;
+			__archive_read_filter_consume(f->upstream,
+			    avail_in - xz->stream.avail_in);
+			xz->member_in +=
+			    avail_in - xz->stream.avail_in;
 			break;
 		default:
-			set_error(self, ret);
+			set_error(f, ret);
 			return (ARCHIVE_FATAL);
 		}
 	}
 
-	decompressed = state->stream.next_out - state->out_block;
-	state->member_out += decompressed;
+	decompressed = xz->stream.next_out - xz->out_block;
+	xz->member_out += decompressed;
 	if (decompressed == 0) {
-		if (member_in != state->member_in &&
-		    self->code == ARCHIVE_FILTER_LZIP &&
-		    state->eof) {
-			ret = lzip_tail(self);
+		if (member_in != xz->member_in &&
+		    f->code == ARCHIVE_FILTER_LZIP &&
+		    xz->eof) {
+			ret = lzip_tail(f);
 			if (ret != ARCHIVE_OK)
 				return (ret);
-			if (!state->eof)
+			if (!xz->eof)
 				goto redo;
 		}
 		*p = NULL;
 	} else {
-		*p = state->out_block;
-		if (self->code == ARCHIVE_FILTER_LZIP) {
-			state->crc32 = lzma_crc32(state->out_block,
-			    decompressed, state->crc32);
-			if (state->eof) {
-				ret = lzip_tail(self);
+		*p = xz->out_block;
+		if (f->code == ARCHIVE_FILTER_LZIP) {
+			xz->crc32 = lzma_crc32(xz->out_block,
+			    decompressed, xz->crc32);
+			if (xz->eof) {
+				ret = lzip_tail(f);
 				if (ret != ARCHIVE_OK)
 					return (ret);
 			}
@@ -732,14 +728,13 @@
  * Clean up the decompressor.
  */
 static int
-xz_filter_close(struct archive_read_filter *self)
+xz_filter_close(struct archive_read_filter *f)
 {
-	struct private_data *state;
+	struct xz *xz = f->data;
 
-	state = (struct private_data *)self->data;
-	lzma_end(&(state->stream));
-	free(state->out_block);
-	free(state);
+	lzma_end(&(xz->stream));
+	free(xz->out_block);
+	free(xz);
 	return (ARCHIVE_OK);
 }
 
@@ -753,44 +748,44 @@
  *
  */
 static int
-lzma_bidder_init(struct archive_read_filter *self)
+lzma_bidder_init(struct archive_read_filter *f)
 {
 	int r;
 
-	r = __archive_read_program(self, "lzma -d -qq");
+	r = __archive_read_program(f, "lzma -d -qq");
 	/* Note: We set the format here even if __archive_read_program()
 	 * above fails.  We do, after all, know what the format is
 	 * even if we weren't able to read it. */
-	self->code = ARCHIVE_FILTER_LZMA;
-	self->name = "lzma";
+	f->code = ARCHIVE_FILTER_LZMA;
+	f->name = "lzma";
 	return (r);
 }
 
 static int
-xz_bidder_init(struct archive_read_filter *self)
+xz_bidder_init(struct archive_read_filter *f)
 {
 	int r;
 
-	r = __archive_read_program(self, "xz -d -qq");
+	r = __archive_read_program(f, "xz -d -qq");
 	/* Note: We set the format here even if __archive_read_program()
 	 * above fails.  We do, after all, know what the format is
 	 * even if we weren't able to read it. */
-	self->code = ARCHIVE_FILTER_XZ;
-	self->name = "xz";
+	f->code = ARCHIVE_FILTER_XZ;
+	f->name = "xz";
 	return (r);
 }
 
 static int
-lzip_bidder_init(struct archive_read_filter *self)
+lzip_bidder_init(struct archive_read_filter *f)
 {
 	int r;
 
-	r = __archive_read_program(self, "lzip -d -q");
+	r = __archive_read_program(f, "lzip -d -q");
 	/* Note: We set the format here even if __archive_read_program()
 	 * above fails.  We do, after all, know what the format is
 	 * even if we weren't able to read it. */
-	self->code = ARCHIVE_FILTER_LZIP;
-	self->name = "lzip";
+	f->code = ARCHIVE_FILTER_LZIP;
+	f->name = "lzip";
 	return (r);
 }
 
diff --git a/c/archive_read_support_filter_zstd.c b/c/archive_read_support_filter_zstd.c
--- a/c/archive_read_support_filter_zstd.c
+++ b/c/archive_read_support_filter_zstd.c
@@ -54,7 +54,7 @@
 
 #if HAVE_ZSTD_H && HAVE_LIBZSTD
 
-struct private_data {
+struct zstd {
 	ZSTD_DStream	*dstream;
 	unsigned char	*out_block;
 	size_t		 out_block_size;
@@ -104,8 +104,8 @@
  * Test whether we can handle this data.
  */
 static int
-zstd_bidder_bid(struct archive_read_filter_bidder *self,
-    struct archive_read_filter *filter)
+zstd_bidder_bid(struct archive_read_filter_bidder *b,
+    struct archive_read_filter *f)
 {
 	const unsigned char *buffer;
 	ssize_t avail;
@@ -132,9 +132,9 @@
 	const uint32_t zstd_magic_skippable_start = 0x184D2A50;
 	const uint32_t zstd_magic_skippable_mask  = 0xFFFFFFF0;
 
-	(void) self; /* UNUSED */
+	(void) b; /* UNUSED */
 
-	buffer = __archive_read_filter_ahead(filter, min_zstd_frame_size,
+	buffer = __archive_read_filter_ahead(f, min_zstd_frame_size,
 	    &avail);
 	if (buffer == NULL)
 		return (0);
@@ -151,7 +151,7 @@
 
 		/* Ensure that we can read another 4 bytes. */
 		if (offset_in_buffer + 4 > (size_t)avail) {
-			buffer = __archive_read_filter_ahead(filter,
+			buffer = __archive_read_filter_ahead(f,
 			    offset_in_buffer + 4, &avail);
 			if (buffer == NULL)
 				return (0);
@@ -178,7 +178,7 @@
 			if (min > max_lookahead)
 				return (0);
 
-			buffer = __archive_read_filter_ahead(filter,
+			buffer = __archive_read_filter_ahead(f,
 			    min, &avail);
 			if (buffer == NULL)
 				return (0);
@@ -206,16 +206,16 @@
  * in case that's available.
  */
 static int
-zstd_bidder_init(struct archive_read_filter *self)
+zstd_bidder_init(struct archive_read_filter *f)
 {
 	int r;
 
-	r = __archive_read_program(self, "zstd -d -qq");
+	r = __archive_read_program(f, "zstd -d -qq");
 	/* Note: We set the format here even if __archive_read_program()
 	 * above fails.  We do, after all, know what the format is
 	 * even if we weren't able to read it. */
-	self->code = ARCHIVE_FILTER_ZSTD;
-	self->name = "zstd";
+	f->code = ARCHIVE_FILTER_ZSTD;
+	f->name = "zstd";
 	return (r);
 }
 
@@ -231,80 +231,78 @@
  * Initialize the filter object
  */
 static int
-zstd_bidder_init(struct archive_read_filter *self)
+zstd_bidder_init(struct archive_read_filter *f)
 {
-	struct private_data *state;
+	struct zstd *zstd;
 	size_t out_block_size = ZSTD_DStreamOutSize();
 	void *out_block;
 	ZSTD_DStream *dstream;
 
-	self->code = ARCHIVE_FILTER_ZSTD;
-	self->name = "zstd";
+	f->code = ARCHIVE_FILTER_ZSTD;
+	f->name = "zstd";
 
-	state = calloc(1, sizeof(*state));
+	zstd = calloc(1, sizeof(*zstd));
 	out_block = malloc(out_block_size);
 	dstream = ZSTD_createDStream();
 
-	if (state == NULL || out_block == NULL || dstream == NULL) {
+	if (zstd == NULL || out_block == NULL || dstream == NULL) {
 		free(out_block);
-		free(state);
+		free(zstd);
 		ZSTD_freeDStream(dstream); /* supports free on NULL */
-		archive_set_error(&self->archive->archive, ENOMEM,
+		archive_set_error(&f->archive->archive, ENOMEM,
 		    "Can't allocate data for zstd decompression");
 		return (ARCHIVE_FATAL);
 	}
 
-	self->data = state;
+	f->data = zstd;
 
-	state->out_block_size = out_block_size;
-	state->out_block = out_block;
-	state->dstream = dstream;
-	self->vtable = &zstd_reader_vtable;
+	zstd->out_block_size = out_block_size;
+	zstd->out_block = out_block;
+	zstd->dstream = dstream;
+	f->vtable = &zstd_reader_vtable;
 
-	state->eof = 0;
-	state->in_frame = 0;
+	zstd->eof = 0;
+	zstd->in_frame = 0;
 
 	return (ARCHIVE_OK);
 }
 
 static ssize_t
-zstd_filter_read(struct archive_read_filter *self, const void **p)
+zstd_filter_read(struct archive_read_filter *f, const void **p)
 {
-	struct private_data *state;
+	struct zstd *zstd = f->data;
 	size_t decompressed;
 	ssize_t avail_in;
 	ZSTD_outBuffer out;
 	ZSTD_inBuffer in;
 	size_t ret;
 
-	state = (struct private_data *)self->data;
-
-	out = (ZSTD_outBuffer) { state->out_block, state->out_block_size, 0 };
+	out = (ZSTD_outBuffer) { zstd->out_block, zstd->out_block_size, 0 };
 
 	/* Try to fill the output buffer. */
-	while (out.pos < out.size && !state->eof) {
-		if (!state->in_frame) {
-			ret = ZSTD_initDStream(state->dstream);
+	while (out.pos < out.size && !zstd->eof) {
+		if (!zstd->in_frame) {
+			ret = ZSTD_initDStream(zstd->dstream);
 			if (ZSTD_isError(ret)) {
-				archive_set_error(&self->archive->archive,
+				archive_set_error(&f->archive->archive,
 				    ARCHIVE_ERRNO_MISC,
 				    "Error initializing zstd decompressor: %s",
 				    ZSTD_getErrorName(ret));
 				return (ARCHIVE_FATAL);
 			}
 		}
-		in.src = __archive_read_filter_ahead(self->upstream, 1,
+		in.src = __archive_read_filter_ahead(f->upstream, 1,
 		    &avail_in);
 		if (avail_in < 0) {
 			return avail_in;
 		}
 		if (in.src == NULL && avail_in == 0) {
-			if (!state->in_frame) {
+			if (!zstd->in_frame) {
 				/* end of stream */
-				state->eof = 1;
+				zstd->eof = 1;
 				break;
 			} else {
-				archive_set_error(&self->archive->archive,
+				archive_set_error(&f->archive->archive,
 				    ARCHIVE_ERRNO_MISC,
 				    "Truncated zstd input");
 				return (ARCHIVE_FATAL);
@@ -314,10 +312,10 @@
 		in.pos = 0;
 
 		{
-			ret = ZSTD_decompressStream(state->dstream, &out, &in);
+			ret = ZSTD_decompressStream(zstd->dstream, &out, &in);
 
 			if (ZSTD_isError(ret)) {
-				archive_set_error(&self->archive->archive,
+				archive_set_error(&f->archive->archive,
 				    ARCHIVE_ERRNO_MISC,
 				    "Zstd decompression failed: %s",
 				    ZSTD_getErrorName(ret));
@@ -325,10 +323,10 @@
 			}
 
 			/* Decompressor made some progress */
-			__archive_read_filter_consume(self->upstream, in.pos);
+			__archive_read_filter_consume(f->upstream, in.pos);
 
 			/* ret guaranteed to be > 0 if frame isn't done yet */
-			state->in_frame = (ret != 0);
+			zstd->in_frame = (ret != 0);
 		}
 	}
 
@@ -336,7 +334,7 @@
 	if (decompressed == 0)
 		*p = NULL;
 	else
-		*p = state->out_block;
+		*p = zstd->out_block;
 	return (decompressed);
 }
 
@@ -344,15 +342,13 @@
  * Clean up the decompressor.
  */
 static int
-zstd_filter_close(struct archive_read_filter *self)
+zstd_filter_close(struct archive_read_filter *f)
 {
-	struct private_data *state;
-
-	state = (struct private_data *)self->data;
+	struct zstd *zstd = f->data;
 
-	ZSTD_freeDStream(state->dstream);
-	free(state->out_block);
-	free(state);
+	ZSTD_freeDStream(zstd->dstream);
+	free(zstd->out_block);
+	free(zstd);
 
 	return (ARCHIVE_OK);
 }
diff --git a/c/archive_read_support_format_7zip.c b/c/archive_read_support_format_7zip.c
--- a/c/archive_read_support_format_7zip.c
+++ b/c/archive_read_support_format_7zip.c
@@ -53,6 +53,7 @@
 #include "archive.h"
 #include "archive_entry.h"
 #include "archive_entry_locale.h"
+#include "archive_integer.h"
 #include "archive_ppmd7_private.h"
 #include "archive_private.h"
 #include "archive_read_private.h"
@@ -72,12 +73,12 @@
 /*
  * PE format
  */
-#define PE_DOS_HDR_LEN				0x40
+#define PE_DOS_HDR_LEN			0x40
 #define PE_DOS_HDR_ELFANEW_OFFSET	0x3c
-#define PE_COFF_HDR_LEN				0x18
+#define PE_COFF_HDR_LEN			0x18
 #define PE_COFF_HDR_SEC_CNT_OFFSET	0x6
 #define PE_COFF_HDR_OPT_SZ_OFFSET	0x14
-#define PE_SEC_HDR_LEN 				0x28
+#define PE_SEC_HDR_LEN 			0x28
 #define PE_SEC_HDR_RAW_ADDR_OFFSET	0x14
 #define PE_SEC_HDR_RAW_SZ_OFFSET	0x10
 
@@ -98,8 +99,8 @@
 #define _7Z_BZ2		0x040202
 #define _7Z_PPMD	0x030401
 #define _7Z_DELTA	0x03
-#define _7Z_CRYPTO_MAIN_ZIP			0x06F10101 /* Main Zip crypto algo */
-#define _7Z_CRYPTO_RAR_29			0x06F10303 /* Rar29 AES-128 + (modified SHA-1) */
+#define _7Z_CRYPTO_MAIN_ZIP		0x06F10101 /* Main Zip crypto algo */
+#define _7Z_CRYPTO_RAR_29		0x06F10303 /* Rar29 AES-128 + (modified SHA-1) */
 #define _7Z_CRYPTO_AES_256_SHA_256	0x06F10701 /* AES-256 + SHA-256 */
 
 
@@ -173,50 +174,48 @@
 };
 
 struct _7z_folder {
-	uint64_t		 numCoders;
+	size_t			 numCoders;
 	struct _7z_coder {
-		uint64_t	 codec;
-		uint64_t	 numInStreams;
-		uint64_t	 numOutStreams;
-		uint64_t	 propertiesSize;
+		int64_t		 codec;
+		size_t		 numInStreams;
+		size_t		 numOutStreams;
+		size_t		 propertiesSize;
 		unsigned char	*properties;
 	} *coders;
-	uint64_t		 numBindPairs;
+	size_t			 numBindPairs;
 	struct {
-		uint64_t	 inIndex;
-		uint64_t	 outIndex;
+		size_t		 inIndex;
+		size_t		 outIndex;
 	} *bindPairs;
-	uint64_t		 numPackedStreams;
-	uint64_t		*packedStreams;
-	uint64_t		 numInStreams;
-	uint64_t		 numOutStreams;
-	uint64_t		*unPackSize;
+	size_t			 numPackedStreams;
+	size_t			 numInStreams;
+	size_t			 numOutStreams;
+	int64_t			*unPackSize;
 	unsigned char		 digest_defined;
 	uint32_t		 digest;
-	uint64_t		 numUnpackStreams;
-	uint32_t		 packIndex;
+	size_t			 numUnpackStreams;
+	size_t			 packIndex;
 	/* Unoperated bytes. */
-	uint64_t		 skipped_bytes;
+	int64_t			 skipped_bytes;
 };
 
 struct _7z_coders_info {
-	uint64_t		 numFolders;
+	size_t			 numFolders;
 	struct _7z_folder	*folders;
-	uint64_t		 dataStreamIndex;
 };
 
 struct _7z_pack_info {
-	uint64_t		 pos;
-	uint64_t		 numPackStreams;
-	uint64_t		*sizes;
+	int64_t			 pos;
+	size_t			 numPackStreams;
+	int64_t			*sizes;
 	struct _7z_digests	 digest;
 	/* Calculated from pos and numPackStreams. */
-	uint64_t		*positions;
+	int64_t			*positions;
 };
 
 struct _7z_substream_info {
 	size_t			 unpack_streams;
-	uint64_t		*unpackSizes;
+	int64_t			*unpackSizes;
 	unsigned char		*digestsDefined;
 	uint32_t		*digests;
 };
@@ -228,8 +227,6 @@
 };
 
 struct _7z_header_info {
-	uint64_t		 dataIndex;
-
 	unsigned char		*emptyStreamBools;
 	unsigned char		*emptyFileBools;
 	unsigned char		*antiBools;
@@ -242,8 +239,8 @@
 #if defined(_WIN32) && !defined(__CYGWIN__) && defined(_DEBUG)
 	const wchar_t		*wname;
 #endif
-	uint32_t		 folderIndex;
-	uint32_t		 ssIndex;
+	size_t			 folderIndex;
+	size_t			 ssIndex;
 	unsigned		 flg;
 #define MTIME_IS_SET	(1<<0)
 #define ATIME_IS_SET	(1<<1)
@@ -267,24 +264,24 @@
 
 	int			 header_is_being_read;
 	int			 header_is_encoded;
-	uint64_t		 header_bytes_remaining;
+	int64_t			 header_bytes_remaining;
 	unsigned long		 header_crc32;
 	/* Header offset to check that reading points of the file contents
 	 * will not exceed the header. */
-	uint64_t		 header_offset;
+	int64_t			 header_offset;
 	/* Base offset of the archive file for a seek in case reading SFX. */
-	uint64_t		 seek_base;
+	int64_t			 seek_base;
 
 	/* List of entries */
 	size_t			 entries_remaining;
-	uint64_t		 numFiles;
+	size_t			 numFiles;
 	struct _7zip_entry	*entries;
 	struct _7zip_entry	*entry;
 	unsigned char		*entry_names;
 
 	/* entry_bytes_remaining is the number of bytes we expect. */
 	int64_t			 entry_offset;
-	uint64_t		 entry_bytes_remaining;
+	int64_t			 entry_bytes_remaining;
 
 	/* Running CRC32 of the decompressed data */
 	unsigned long		 entry_crc32;
@@ -305,16 +302,16 @@
 	/*
 	 * Decompressing control data.
 	 */
-	unsigned		 folder_index;
-	uint64_t		 folder_outbytes_remaining;
-	unsigned		 pack_stream_index;
-	unsigned		 pack_stream_remaining;
-	uint64_t		 pack_stream_inbytes_remaining;
-	size_t			 pack_stream_bytes_unconsumed;
+	size_t			 folder_index;
+	int64_t			 folder_outbytes_remaining;
+	size_t			 pack_stream_index;
+	size_t			 pack_stream_remaining;
+	int64_t			 pack_stream_inbytes_remaining;
+	int64_t			 pack_stream_bytes_unconsumed;
 
 	/* The codec information of a folder. */
-	unsigned long		 codec;
-	unsigned long		 codec2;
+	int64_t			 codec;
+	int64_t			 codec2;
 
 	/*
 	 * Decompressor controllers.
@@ -364,7 +361,7 @@
 	uint32_t		 bcj_ip;
 
 	/* Decoding BCJ2 data. */
-	size_t			 main_stream_bytes_remaining;
+	int64_t			 main_stream_bytes_remaining;
 	unsigned char		*sub_stream_buff[3];
 	size_t			 sub_stream_size[3];
 	size_t			 sub_stream_bytes_remaining[3];
@@ -381,13 +378,11 @@
 	uint8_t			 bcj2_prevByte;
 	uint32_t		 bcj2_range;
 	uint32_t		 bcj2_code;
-	uint64_t		 bcj2_outPos;
+	size_t			 bcj2_outPos;
 
 	/* Filename character-set conversion data. */
 	struct archive_string_conv *sconv;
 
-	char			 format_name[64];
-
 	/* Custom value that is non-zero if this archive contains encrypted entries. */
 	int			 has_encrypted_entries;
 };
@@ -395,31 +390,14 @@
 /* Maximum entry size. This limitation prevents reading intentional
  * corrupted 7-zip files on assuming there are not so many entries in
  * the files. */
-#define UMAX_ENTRY	ARCHIVE_LITERAL_ULL(100000000)
-
-/*
- * Files without unpack streams must be described by the EmptyStream bitmap,
- * which consumes one bit for every file entry in FilesInfo.
- */
-static int
-files_info_numfiles_is_sane(const struct _7zip *zip)
-{
-	uint64_t empty_stream_map_bytes;
-
-	if (zip->numFiles > UMAX_ENTRY)
-		return (0);
-	if (zip->numFiles > SIZE_MAX / sizeof(*zip->entries))
-		return (0);
-
-	if (zip->numFiles <= zip->si.ss.unpack_streams)
-		return (1);
+#define UMAX_ENTRY	ARCHIVE_LITERAL_LL(100000000)
 
-	empty_stream_map_bytes = (zip->numFiles + 7) / 8;
-	return (empty_stream_map_bytes <= zip->header_bytes_remaining);
-}
+/* Don't try to read more than 16 MB at a time */
+#define MAX_READ	16 * 1024 * 1024
 
+static size_t	align_size(size_t);
 static int	archive_read_format_7zip_has_encrypted_entries(struct archive_read *);
-static int	archive_read_support_format_7zip_capabilities(struct archive_read *a);
+static int	archive_read_support_format_7zip_capabilities(struct archive_read *);
 static int	archive_read_format_7zip_bid(struct archive_read *, int);
 static int	archive_read_format_7zip_cleanup(struct archive_read *);
 static int	archive_read_format_7zip_read_data(struct archive_read *,
@@ -427,14 +405,15 @@
 static int	archive_read_format_7zip_read_data_skip(struct archive_read *);
 static int	archive_read_format_7zip_read_header(struct archive_read *,
 		    struct archive_entry *);
-static int	check_7zip_header_in_sfx(const unsigned char *);
-static int	decode_codec_id(const unsigned char *, size_t, uint64_t *);
+static size_t	check_7zip_header_in_sfx(const unsigned char *);
+static int	decode_codec_id(const unsigned char *, size_t, int64_t *);
 static int	decode_encoded_header_info(struct archive_read *,
 		    struct _7z_stream_info *);
 static int	decompress(struct archive_read *, struct _7zip *,
 		    void *, size_t *, const void *, size_t *);
 static ssize_t	extract_pack_stream(struct archive_read *, size_t);
-static uint64_t folder_uncompressed_size(struct _7z_folder *);
+static int	files_info_numfiles_is_sane(const struct _7zip *);
+static int64_t	folder_uncompressed_size(struct _7z_folder *);
 static void	free_CodersInfo(struct _7z_coders_info *);
 static void	free_Digest(struct _7z_digests *);
 static void	free_Folder(struct _7z_folder *);
@@ -445,10 +424,11 @@
 static int	free_decompression(struct archive_read *, struct _7zip *);
 static ssize_t	get_uncompressed_data(struct archive_read *, const void **,
 		    size_t, size_t);
-static const unsigned char * header_bytes(struct archive_read *, size_t);
+static const unsigned char *header_bytes(struct archive_read *, size_t);
 static int	init_decompression(struct archive_read *, struct _7zip *,
 		    const struct _7z_coder *, const struct _7z_coder *);
-static int	parse_7zip_uint64(struct archive_read *, uint64_t *);
+static int	parse_7zip_size(struct archive_read *, size_t *);
+static int	parse_7zip_int64(struct archive_read *, int64_t *);
 static int	read_Bools(struct archive_read *, unsigned char *, size_t);
 static int	read_CodersInfo(struct archive_read *,
 		    struct _7z_coders_info *);
@@ -462,13 +442,12 @@
 		    struct _7z_stream_info *);
 static int	read_SubStreamsInfo(struct archive_read *,
 		    struct _7z_substream_info *, struct _7z_folder *, size_t);
-static int	read_Times(struct archive_read *, struct _7z_header_info *,
-		    int);
-static void	read_consume(struct archive_read *);
+static int	read_Times(struct archive_read *, int);
+static int	read_consume(struct archive_read *);
 static ssize_t	read_stream(struct archive_read *, const void **, size_t,
 		    size_t);
 static int	seek_pack(struct archive_read *);
-static int64_t	skip_stream(struct archive_read *, size_t);
+static int	skip_stream(struct archive_read *, int64_t);
 static int	get_data_offset(struct archive_read *, int64_t *, int);
 static int	get_pe_sfx_offset(struct archive_read *, int64_t *);
 static int	get_elf_sfx_offset(struct archive_read *, int64_t *, int);
@@ -481,7 +460,7 @@
 static void	arm_Init(struct _7zip *);
 static size_t	arm_Convert(struct _7zip *, uint8_t *, size_t);
 static size_t	arm64_Convert(struct _7zip *, uint8_t *, size_t);
-static ssize_t		Bcj2_Decode(struct _7zip *, uint8_t *, size_t);
+static ssize_t	Bcj2_Decode(struct _7zip *, uint8_t *, size_t);
 static size_t	sparc_Convert(struct _7zip *, uint8_t *, size_t);
 static size_t	powerpc_Convert(struct _7zip *, uint8_t *, size_t);
 static int64_t	seek_compat(struct archive_read *, int64_t, int, int);
@@ -530,19 +509,18 @@
 }
 
 static int
-archive_read_support_format_7zip_capabilities(struct archive_read * a)
+archive_read_support_format_7zip_capabilities(struct archive_read *a)
 {
 	(void)a; /* UNUSED */
 	return (ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA |
 			ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA);
 }
 
-
 static int
-archive_read_format_7zip_has_encrypted_entries(struct archive_read *_a)
+archive_read_format_7zip_has_encrypted_entries(struct archive_read *a)
 {
-	if (_a && _a->format) {
-		struct _7zip * zip = (struct _7zip *)_a->format->data;
+	if (a && a->format) {
+		struct _7zip *zip = a->format->data;
 		if (zip) {
 			return zip->has_encrypted_entries;
 		}
@@ -601,8 +579,8 @@
 			continue;
 		}
 		p = buff + offset;
-		while (p + 32 < buff + bytes_avail) {
-			int step = check_7zip_header_in_sfx(p);
+		while (buff + bytes_avail - p >= 32) {
+			size_t step = check_7zip_header_in_sfx(p);
 			if (step == 0) {
 				*data_offset = p - buff;
 				return (ARCHIVE_OK);
@@ -633,7 +611,7 @@
 	return (48);
 }
 
-static int
+static size_t
 check_7zip_header_in_sfx(const unsigned char *p)
 {
 	switch (p[5]) {
@@ -798,7 +776,7 @@
 			e_shentsize = (*dec16)(h + 0x3A);
 			e_shnum = (*dec16)(h + 0x3C);
 			e_shstrndx = (*dec16)(h + 0x3E);
-			if (e_shnum < e_shstrndx || e_shentsize < 0x28)
+			if (e_shnum <= e_shstrndx || e_shentsize < 0x28)
 				break;
 
 		} else {
@@ -806,7 +784,7 @@
 			e_shentsize = (*dec16)(h + 0x2E);
 			e_shnum = (*dec16)(h + 0x30);
 			e_shstrndx = (*dec16)(h + 0x32);
-			if (e_shnum < e_shstrndx || e_shentsize < 0x18)
+			if (e_shnum <= e_shstrndx || e_shentsize < 0x18)
 				break;
 		}
 
@@ -860,7 +838,7 @@
 			return (ARCHIVE_FATAL);
 		}
 		size_t data_sym_offset = strtab_size;
-		for (size_t offset = 0; offset < strtab_size - 6; offset++) {
+		for (size_t offset = 0; offset + 6 <= strtab_size; offset++) {
 			if (memcmp(h + offset, ".data\00", 6) == 0) {
 				data_sym_offset = offset;
 				break;
@@ -916,11 +894,10 @@
 archive_read_format_7zip_read_header(struct archive_read *a,
 	struct archive_entry *entry)
 {
-	struct _7zip *zip = (struct _7zip *)a->format->data;
+	struct _7zip *zip = a->format->data;
 	struct _7zip_entry *zip_entry;
 	int r, ret = ARCHIVE_OK;
 	struct _7z_folder *folder = 0;
-	uint64_t fidx = 0;
 
 	/*
 	 * It should be sufficient to call archive_read_next_header() for
@@ -934,8 +911,7 @@
 	}
 
 	a->archive.archive_format = ARCHIVE_FORMAT_7ZIP;
-	if (a->archive.archive_format_name == NULL)
-		a->archive.archive_format_name = "7-Zip";
+	a->archive.archive_format_name = "7-Zip";
 
 	if (zip->entries == NULL) {
 		struct _7z_header_info header;
@@ -945,20 +921,20 @@
 		free_Header(&header);
 		if (r != ARCHIVE_OK)
 			return (r);
-		zip->entries_remaining = (size_t)zip->numFiles;
+		zip->entries_remaining = zip->numFiles;
 		zip->entry = zip->entries;
 	} else {
 		++zip->entry;
 	}
 	zip_entry = zip->entry;
 
-	if (zip->entries_remaining <= 0 || zip_entry == NULL)
+	if (zip->entries_remaining == 0 || zip_entry == NULL)
 		return ARCHIVE_EOF;
 	--zip->entries_remaining;
 
 	zip->entry_offset = 0;
 	zip->end_of_entry = 0;
-	zip->entry_crc32 = crc32(0, NULL, 0);
+	zip->entry_crc32 = 0;
 
 	/* Setup a string conversion for a filename. */
 	if (zip->sconv == NULL) {
@@ -972,9 +948,11 @@
 	   that is associated to the current 7zip entry. If the folder
 	   has a coder with a _7Z_CRYPTO codec then the folder is encrypted.
 	   Hence the entry must also be encrypted. */
-	if (zip_entry && zip_entry->folderIndex < zip->si.ci.numFolders) {
+	if (zip_entry->folderIndex < zip->si.ci.numFolders) {
+		size_t fidx = 0;
+
 		folder = &(zip->si.ci.folders[zip_entry->folderIndex]);
-		for (fidx=0; folder && fidx<folder->numCoders; fidx++) {
+		for (fidx = 0; fidx < folder->numCoders; fidx++) {
 			switch(folder->coders[fidx].codec) {
 				case _7Z_CRYPTO_MAIN_ZIP:
 				case _7Z_CRYPTO_RAR_29:
@@ -987,13 +965,6 @@
 		}
 	}
 
-	/* Now that we've checked for encryption, if there were still no
-	 * encrypted entries found we can say for sure that there are none.
-	 */
-	if (zip->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) {
-		zip->has_encrypted_entries = 0;
-	}
-
 	if (archive_entry_copy_pathname_l(entry,
 	    (const char *)zip_entry->utf16name,
 	    zip_entry->name_len, zip->sconv) != 0) {
@@ -1021,7 +992,7 @@
 	if (zip_entry->flg & ATIME_IS_SET)
 		archive_entry_set_atime(entry, zip_entry->atime,
 		    zip_entry->atime_ns);
-	if (zip_entry->ssIndex != (uint32_t)-1) {
+	if (zip_entry->ssIndex != (size_t)-1) {
 		zip->entry_bytes_remaining =
 		    zip->si.ss.unpackSizes[zip_entry->ssIndex];
 		archive_entry_set_size(entry, zip->entry_bytes_remaining);
@@ -1035,8 +1006,8 @@
 
 	if (zip_entry->attr & supported_attrs) {
 		char buf[sizeof(",rdonly,hidden,system")];
-		char *fflags[3] = { "", "", "" };
-		char **flag = fflags;
+		const char *fflags[3] = { "", "", "" };
+		const char **flag = fflags;
 
 		if (zip_entry->attr & FILE_ATTRIBUTE_READONLY)
 			*flag++ = ",rdonly";
@@ -1117,10 +1088,6 @@
 		archive_entry_set_size(entry, 0);
 	}
 
-	/* Set up a more descriptive format name. */
-	snprintf(zip->format_name, sizeof(zip->format_name), "7-Zip");
-	a->archive.archive_format_name = zip->format_name;
-
 	return (ret);
 }
 
@@ -1128,12 +1095,10 @@
 archive_read_format_7zip_read_data(struct archive_read *a,
     const void **buff, size_t *size, int64_t *offset)
 {
-	struct _7zip *zip;
+	struct _7zip *zip = a->format->data;
 	ssize_t bytes;
 	int ret = ARCHIVE_OK;
 
-	zip = (struct _7zip *)(a->format->data);
-
 	if (zip->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) {
 		zip->has_encrypted_entries = 0;
 	}
@@ -1151,8 +1116,8 @@
 	if (zip->end_of_entry)
 		return (ARCHIVE_EOF);
 
-	size_t bytes_to_read = 16 * 1024 * 1024;  // Don't try to read more than 16 MB at a time
-	if ((uint64_t)bytes_to_read > zip->entry_bytes_remaining) {
+	size_t bytes_to_read = MAX_READ;
+	if ((int64_t)bytes_to_read > zip->entry_bytes_remaining) {
 		bytes_to_read = (size_t)zip->entry_bytes_remaining;
 	}
 	bytes = read_stream(a, buff, bytes_to_read, 0);
@@ -1181,7 +1146,7 @@
 		    zip->entry_crc32) {
 			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 			    "7-Zip bad CRC: 0x%lx should be 0x%lx",
-			    (unsigned long)zip->entry_crc32,
+			    zip->entry_crc32,
 			    (unsigned long)zip->si.ss.digests[
 			    		zip->entry->ssIndex]);
 			ret = ARCHIVE_WARN;
@@ -1198,10 +1163,8 @@
 static int
 archive_read_format_7zip_read_data_skip(struct archive_read *a)
 {
-	struct _7zip *zip;
-	int64_t bytes_skipped;
-
-	zip = (struct _7zip *)(a->format->data);
+	struct _7zip *zip = a->format->data;
+	int r;
 
 	if (zip->pack_stream_bytes_unconsumed)
 		read_consume(a);
@@ -1214,9 +1177,9 @@
 	 * If the length is at the beginning, we can skip the
 	 * compressed data much more quickly.
 	 */
-	bytes_skipped = skip_stream(a, (size_t)zip->entry_bytes_remaining);
-	if (bytes_skipped < 0)
-		return ((int)bytes_skipped);
+	r = skip_stream(a, zip->entry_bytes_remaining);
+	if (r < 0)
+		return (r);
 	zip->entry_bytes_remaining = 0;
 
 	/* This entry is finished and done. */
@@ -1227,9 +1190,8 @@
 static int
 archive_read_format_7zip_cleanup(struct archive_read *a)
 {
-	struct _7zip *zip;
+	struct _7zip *zip = a->format->data;
 
-	zip = (struct _7zip *)(a->format->data);
 	free_StreamsInfo(&(zip->si));
 	free(zip->entries);
 	free(zip->entry_names);
@@ -1240,24 +1202,30 @@
 	free(zip->sub_stream_buff[2]);
 	free(zip->tmp_stream_buff);
 	free(zip);
-	(a->format->data) = NULL;
+	a->format->data = NULL;
 	return (ARCHIVE_OK);
 }
 
-static void
+static int
 read_consume(struct archive_read *a)
 {
-	struct _7zip *zip = (struct _7zip *)a->format->data;
+	struct _7zip *zip = a->format->data;
 
 	if (zip->pack_stream_bytes_unconsumed) {
-		__archive_read_consume(a, zip->pack_stream_bytes_unconsumed);
+		int64_t r;
+
+		if ((r = __archive_read_consume(a,
+		    zip->pack_stream_bytes_unconsumed)) < 0)
+			return ((int)r);
+
 		zip->stream_offset += zip->pack_stream_bytes_unconsumed;
 		zip->pack_stream_bytes_unconsumed = 0;
 	}
+
+	return (ARCHIVE_OK);
 }
 
 #ifdef HAVE_LZMA_H
-
 /*
  * Set an error code and choose an error message for liblzma.
  */
@@ -1305,22 +1273,19 @@
 		break;
 	}
 }
-
 #endif
 
 static int
-decode_codec_id(const unsigned char *codecId, size_t id_size, uint64_t *id)
+decode_codec_id(const unsigned char *codecId, size_t id_size, int64_t *id)
 {
-	unsigned i;
-	uint64_t v = 0;
+	size_t i;
 
+	*id = 0;
 	for (i = 0; i < id_size; i++) {
-		if (v > (uint64_t)INT64_MAX / 256)
+		if (archive_ckd_mul_i64(id, *id, 256) ||
+		    archive_ckd_add_i64(id, *id, codecId[i]))
 			return (-1);
-		v <<= 8;
-		v += codecId[i];
 	}
-	*id = v;
 	return (0);
 }
 
@@ -1328,7 +1293,7 @@
 ppmd_read(void *p)
 {
 	struct archive_read *a = ((IByteIn*)p)->a;
-	struct _7zip *zip = (struct _7zip *)(a->format->data);
+	struct _7zip *zip = a->format->data;
 	Byte b;
 
 	if (zip->ppstream.avail_in == 0) {
@@ -1337,7 +1302,7 @@
 		 * and we are on boundary;
 		 * last resort to read using __archive_read_ahead.
 		 */
-		const uint8_t* data = __archive_read_ahead(a,
+		const uint8_t *data = __archive_read_ahead(a,
 		    zip->ppstream.stream_in + 1, NULL);
 		if (data == NULL) {
 			archive_set_error(&a->archive,
@@ -1466,7 +1431,7 @@
 				memset(&delta_opt, 0, sizeof(delta_opt));
 				delta_opt.type = LZMA_DELTA_TYPE_BYTE;
 				delta_opt.dist =
-				    (uint32_t)coder2->properties[0] + 1;
+				    coder2->properties[0] + 1;
 				filters[fi].options = &delta_opt;
 				fi++;
 				break;
@@ -1506,7 +1471,8 @@
 			default:
 				archive_set_error(&a->archive,
 				    ARCHIVE_ERRNO_MISC,
-				    "Unexpected codec ID: %lX", zip->codec2);
+				    "Unexpected codec ID: %jX",
+				    (uintmax_t)zip->codec2);
 				return (ARCHIVE_FAILED);
 			}
 		}
@@ -1518,7 +1484,7 @@
 		filters[fi].options = NULL;
 		ff = &filters[fi];
 		r = lzma_properties_decode(&filters[fi], NULL,
-		    coder1->properties, (size_t)coder1->propertiesSize);
+		    coder1->properties, coder1->propertiesSize);
 		if (r != LZMA_OK) {
 			set_error(a, r);
 			return (ARCHIVE_FAILED);
@@ -1673,7 +1639,7 @@
 	case _7Z_SPARC:
 	case _7Z_DELTA:
 		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
-		    "Unexpected codec ID: %lX", zip->codec);
+		    "Unexpected codec ID: %jX", (uintmax_t)zip->codec);
 		return (ARCHIVE_FAILED);
 	case _7Z_CRYPTO_MAIN_ZIP:
 	case _7Z_CRYPTO_RAR_29:
@@ -1684,11 +1650,12 @@
 			zip->has_encrypted_entries = 1;
 		}
 		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
-		    "Crypto codec not supported yet (ID: 0x%lX)", zip->codec);
+		    "Crypto codec not supported yet (ID: 0x%jX)",
+		    (uintmax_t)zip->codec);
 		return (ARCHIVE_FAILED);
 	default:
 		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
-		    "Unknown codec ID: %lX", zip->codec);
+		    "Unknown codec ID: %jX", (uintmax_t)zip->codec);
 		return (ARCHIVE_FAILED);
 	}
 
@@ -1740,7 +1707,7 @@
 	bcj2_avail_out = t_avail_out;
 	if (zip->codec2 == _7Z_X86_BCJ2) {
 		/*
-		 * Decord a remaining decompressed main stream for BCJ2.
+		 * Decode a remaining decompressed main stream for BCJ2.
 		 */
 		if (zip->tmp_stream_bytes_remaining) {
 			ssize_t bytes;
@@ -1774,8 +1741,8 @@
 	switch (zip->codec) {
 	case _7Z_COPY:
 	{
-		size_t bytes =
-		    (t_avail_in > t_avail_out)?t_avail_out:t_avail_in;
+		size_t bytes = (t_avail_in > t_avail_out) ?
+		    t_avail_out : t_avail_in;
 
 		memcpy(t_next_out, t_next_in, bytes);
 		t_avail_in -= bytes;
@@ -1813,10 +1780,14 @@
 #endif
 #if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
 	case _7Z_BZ2:
+		if (t_avail_in > UINT_MAX)
+			t_avail_in = UINT_MAX;
+		if (t_avail_out > UINT_MAX)
+			t_avail_out = UINT_MAX;
 		zip->bzstream.next_in = (char *)(uintptr_t)t_next_in;
-		zip->bzstream.avail_in = (uint32_t)t_avail_in;
+		zip->bzstream.avail_in = (unsigned int)t_avail_in;
 		zip->bzstream.next_out = (char *)(uintptr_t)t_next_out;
-		zip->bzstream.avail_out = (uint32_t)t_avail_out;
+		zip->bzstream.avail_out = (unsigned int)t_avail_out;
 		r = BZ2_bzDecompress(&(zip->bzstream));
 		switch (r) {
 		case BZ_STREAM_END: /* Found end of stream. */
@@ -1846,6 +1817,10 @@
 #endif
 #ifdef HAVE_ZLIB_H
 	case _7Z_DEFLATE:
+		if (t_avail_in > UINT_MAX)
+			t_avail_in = UINT_MAX;
+		if (t_avail_out > UINT_MAX)
+			t_avail_out = UINT_MAX;
 		zip->stream.next_in = (Bytef *)(uintptr_t)t_next_in;
 		zip->stream.avail_in = (uInt)t_avail_in;
 		zip->stream.next_out = t_next_out;
@@ -1869,12 +1844,17 @@
 #if HAVE_ZSTD_H && HAVE_LIBZSTD
 	case _7Z_ZSTD:
 	{
-		ZSTD_inBuffer input = { t_next_in, t_avail_in, 0 }; // src, size, pos
-		ZSTD_outBuffer output = { t_next_out, t_avail_out, 0 }; // dst, size, pos
+		/* src, size, pos */
+		ZSTD_inBuffer input = { t_next_in, t_avail_in, 0 };
+		/* dst, size, pos */
+		ZSTD_outBuffer output = { t_next_out, t_avail_out, 0 };
 
-		size_t const zret = ZSTD_decompressStream(zip->zstd_dstream, &output, &input);
+		size_t const zret = ZSTD_decompressStream(zip->zstd_dstream,
+		    &output, &input);
 		if (ZSTD_isError(zret)) {
-			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Zstd decompression failed: %s", ZSTD_getErrorName(zret));
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Zstd decompression failed: %s",
+			    ZSTD_getErrorName(zret));
 			return ARCHIVE_FAILED;
 		}
 		t_avail_in -= input.pos;
@@ -1884,7 +1864,7 @@
 #endif
 	case _7Z_PPMD:
 	{
-		uint64_t flush_bytes;
+		int64_t flush_bytes;
 
 		if (!zip->ppmd7_valid || zip->ppmd7_stat < 0 ||
 		    t_avail_out <= 0) {
@@ -1942,7 +1922,7 @@
 			}
 			*zip->ppstream.next_out++ = (unsigned char)sym;
 			zip->ppstream.avail_out--;
-			if (flush_bytes)
+			if (flush_bytes > 0)
 				flush_bytes--;
 		} while (zip->ppstream.avail_out &&
 			(zip->ppstream.avail_in || flush_bytes));
@@ -1963,7 +1943,7 @@
 	*outbytes = o_avail_out - t_avail_out;
 
 	/*
-	 * Decord BCJ.
+	 * Decode BCJ.
 	 */
 	if (zip->codec != _7Z_LZMA2) {
 		if (zip->codec2 == _7Z_X86) {
@@ -1971,9 +1951,10 @@
 
 			zip->odd_bcj_size = *outbytes - l;
 			if (zip->odd_bcj_size > 0 && zip->odd_bcj_size <= 4 &&
-		    	o_avail_in && ret != ARCHIVE_EOF) {
-				memcpy(zip->odd_bcj, ((unsigned char *)buff) + l,
-			    	zip->odd_bcj_size);
+			    o_avail_in && ret != ARCHIVE_EOF) {
+				memcpy(zip->odd_bcj,
+				    ((unsigned char *)buff) + l,
+				    zip->odd_bcj_size);
 				*outbytes = l;
 			} else
 				zip->odd_bcj_size = 0;
@@ -1989,7 +1970,7 @@
 	}
 
 	/*
-	 * Decord BCJ2 with a decompressed main stream.
+	 * Decode BCJ2 with a decompressed main stream.
 	 */
 	if (zip->codec2 == _7Z_X86_BCJ2) {
 		ssize_t bytes;
@@ -1997,7 +1978,7 @@
 		zip->tmp_stream_bytes_avail =
 		    zip->tmp_stream_buff_size - t_avail_out;
 		if (zip->tmp_stream_bytes_avail >
-		      zip->main_stream_bytes_remaining)
+		      (uint64_t)zip->main_stream_bytes_remaining)
 			zip->tmp_stream_bytes_avail =
 			    zip->main_stream_bytes_remaining;
 		zip->tmp_stream_bytes_remaining = zip->tmp_stream_bytes_avail;
@@ -2065,8 +2046,21 @@
 }
 
 static int
-parse_7zip_uint64(struct archive_read *a, uint64_t *val)
+parse_7zip_size(struct archive_read *a, size_t *val)
 {
+	int64_t v;
+
+	if (parse_7zip_int64(a, &v) < 0 || v > UMAX_ENTRY)
+		return (-1);
+
+	*val = (size_t)v;
+	return (0);
+}
+
+static int
+parse_7zip_int64(struct archive_read *a, int64_t *val)
+{
+	uint64_t v;
 	const unsigned char *p;
 	unsigned char avail, mask;
 	int i;
@@ -2075,18 +2069,21 @@
 		return (-1);
 	avail = *p;
 	mask = 0x80;
-	*val = 0;
+	v = 0;
 	for (i = 0; i < 8; i++) {
 		if (avail & mask) {
 			if ((p = header_bytes(a, 1)) == NULL)
 				return (-1);
-			*val |= ((uint64_t)*p) << (8 * i);
+			v |= ((uint64_t)*p) << (8 * i);
 			mask >>= 1;
 			continue;
 		}
-		*val += ((uint64_t)(avail & (mask -1))) << (8 * i);
+		v += ((uint64_t)(avail & (mask - 1))) << (8 * i);
 		break;
 	}
+	if (v > (uint64_t)INT64_MAX)
+		return (-1);
+	*val = (int64_t)v;
 	return (0);
 }
 
@@ -2094,7 +2091,8 @@
 read_Bools(struct archive_read *a, unsigned char *data, size_t num)
 {
 	const unsigned char *p;
-	unsigned i, mask = 0, avail = 0;
+	size_t i;
+	unsigned mask = 0, avail = 0;
 
 	for (i = 0; i < num; i++) {
 		if (mask == 0) {
@@ -2103,7 +2101,7 @@
 			avail = *p;
 			mask = 0x80;
 		}
-		data[i] = (avail & mask)?1:0;
+		data[i] = (avail & mask) ? 1 : 0;
 		mask >>= 1;
 	}
 	return (0);
@@ -2120,7 +2118,7 @@
 read_Digests(struct archive_read *a, struct _7z_digests *d, size_t num)
 {
 	const unsigned char *p;
-	unsigned i;
+	size_t i;
 
 	if (num == 0)
 		return (-1);
@@ -2167,25 +2165,23 @@
 read_PackInfo(struct archive_read *a, struct _7z_pack_info *pi)
 {
 	const unsigned char *p;
-	unsigned i;
+	size_t i;
 
 	memset(pi, 0, sizeof(*pi));
 
 	/*
 	 * Read PackPos.
 	 */
-	if (parse_7zip_uint64(a, &(pi->pos)) < 0)
+	if (parse_7zip_int64(a, &(pi->pos)) < 0)
 		return (-1);
 
 	/*
 	 * Read NumPackStreams.
 	 */
-	if (parse_7zip_uint64(a, &(pi->numPackStreams)) < 0)
+	if (parse_7zip_size(a, &(pi->numPackStreams)) < 0)
 		return (-1);
 	if (pi->numPackStreams == 0)
 		return (-1);
-	if (UMAX_ENTRY < pi->numPackStreams)
-		return (-1);
 
 	/*
 	 * Read PackSizes[num]
@@ -2197,13 +2193,13 @@
 		return (0);
 	if (*p != kSize)
 		return (-1);
-	pi->sizes = calloc((size_t)pi->numPackStreams, sizeof(uint64_t));
-	pi->positions = calloc((size_t)pi->numPackStreams, sizeof(uint64_t));
+	pi->sizes = calloc(pi->numPackStreams, sizeof(int64_t));
+	pi->positions = calloc(pi->numPackStreams, sizeof(int64_t));
 	if (pi->sizes == NULL || pi->positions == NULL)
 		return (-1);
 
 	for (i = 0; i < pi->numPackStreams; i++) {
-		if (parse_7zip_uint64(a, &(pi->sizes[i])) < 0)
+		if (parse_7zip_int64(a, &(pi->sizes[i])) < 0)
 			return (-1);
 	}
 
@@ -2215,9 +2211,9 @@
 	if (*p == kEnd) {
 		/* PackStreamDigests[num] are not present. */
 		pi->digest.defineds =
-		    calloc((size_t)pi->numPackStreams, sizeof(*pi->digest.defineds));
+		    calloc(pi->numPackStreams, sizeof(*pi->digest.defineds));
 		pi->digest.digests =
-		    calloc((size_t)pi->numPackStreams, sizeof(*pi->digest.digests));
+		    calloc(pi->numPackStreams, sizeof(*pi->digest.digests));
 		if (pi->digest.defineds == NULL || pi->digest.digests == NULL)
 			return (-1);
 		return (0);
@@ -2226,7 +2222,7 @@
 	if (*p != kCRC)
 		return (-1);
 
-	if (read_Digests(a, &(pi->digest), (size_t)pi->numPackStreams) < 0)
+	if (read_Digests(a, &(pi->digest), pi->numPackStreams) < 0)
 		return (-1);
 
 	/*
@@ -2242,40 +2238,39 @@
 static void
 free_Folder(struct _7z_folder *f)
 {
-	unsigned i;
-
 	if (f->coders) {
+		size_t i;
+
 		for (i = 0; i< f->numCoders; i++) {
 			free(f->coders[i].properties);
 		}
 		free(f->coders);
 	}
 	free(f->bindPairs);
-	free(f->packedStreams);
 	free(f->unPackSize);
 }
 
 static int
 read_Folder(struct archive_read *a, struct _7z_folder *f)
 {
-	struct _7zip *zip = (struct _7zip *)a->format->data;
+	struct _7zip *zip = a->format->data;
 	const unsigned char *p;
-	uint64_t numInStreamsTotal = 0;
-	uint64_t numOutStreamsTotal = 0;
-	unsigned i;
+	size_t numInStreamsTotal = 0;
+	size_t numOutStreamsTotal = 0;
+	size_t i;
 
 	memset(f, 0, sizeof(*f));
 
 	/*
 	 * Read NumCoders.
 	 */
-	if (parse_7zip_uint64(a, &(f->numCoders)) < 0)
+	if (parse_7zip_size(a, &(f->numCoders)) < 0)
 		return (-1);
 	if (f->numCoders > 4)
 		/* Too many coders. */
 		return (-1);
 
-	f->coders = calloc((size_t)f->numCoders, sizeof(*f->coders));
+	f->coders = calloc(f->numCoders, sizeof(*f->coders));
 	if (f->coders == NULL)
 		return (-1);
 	for (i = 0; i< f->numCoders; i++) {
@@ -2293,7 +2288,7 @@
 		 * 7:  Must be zero.
 		 */
 		codec_size = *p & 0xf;
-		simple = (*p & 0x10)?0:1;
+		simple = (*p & 0x10) ? 0 : 1;
 		attr = *p & 0x20;
 		if (*p & 0x80)
 			return (-1);/* Not supported. */
@@ -2311,37 +2306,34 @@
 			f->coders[i].numInStreams = 1;
 			f->coders[i].numOutStreams = 1;
 		} else {
-			if (parse_7zip_uint64(
+			if (parse_7zip_size(
 			    a, &(f->coders[i].numInStreams)) < 0)
 				return (-1);
-			if (UMAX_ENTRY < f->coders[i].numInStreams)
-				return (-1);
-			if (parse_7zip_uint64(
+			if (parse_7zip_size(
 			    a, &(f->coders[i].numOutStreams)) < 0)
 				return (-1);
-			if (UMAX_ENTRY < f->coders[i].numOutStreams)
-				return (-1);
 		}
 
 		if (attr) {
-			if (parse_7zip_uint64(
+			if (parse_7zip_size(
 			    a, &(f->coders[i].propertiesSize)) < 0)
 				return (-1);
-			if (UMAX_ENTRY < f->coders[i].propertiesSize)
-				return (-1);
 			if ((p = header_bytes(
-			    a, (size_t)f->coders[i].propertiesSize)) == NULL)
+			    a, f->coders[i].propertiesSize)) == NULL)
 				return (-1);
 			f->coders[i].properties =
-			    malloc((size_t)f->coders[i].propertiesSize);
+			    malloc(f->coders[i].propertiesSize);
 			if (f->coders[i].properties == NULL)
 				return (-1);
 			memcpy(f->coders[i].properties, p,
-			    (size_t)f->coders[i].propertiesSize);
+			    f->coders[i].propertiesSize);
 		}
 
-		numInStreamsTotal += f->coders[i].numInStreams;
-		numOutStreamsTotal += f->coders[i].numOutStreams;
+		if (archive_ckd_add_size(&numInStreamsTotal,
+		    numInStreamsTotal, f->coders[i].numInStreams) ||
+		    archive_ckd_add_size(&numOutStreamsTotal,
+		    numOutStreamsTotal, f->coders[i].numOutStreams))
+			return (-1);
 	}
 
 	if (numOutStreamsTotal == 0 ||
@@ -2349,34 +2341,27 @@
 		return (-1);
 
 	f->numBindPairs = numOutStreamsTotal - 1;
-	if (zip->header_bytes_remaining < f->numBindPairs)
+	if ((uint64_t)zip->header_bytes_remaining < f->numBindPairs)
 			return (-1);
 	if (f->numBindPairs > 0) {
 		f->bindPairs =
-			calloc((size_t)f->numBindPairs, sizeof(*f->bindPairs));
+			calloc(f->numBindPairs, sizeof(*f->bindPairs));
 		if (f->bindPairs == NULL)
 			return (-1);
 	} else
 		f->bindPairs = NULL;
 	for (i = 0; i < f->numBindPairs; i++) {
-		if (parse_7zip_uint64(a, &(f->bindPairs[i].inIndex)) < 0)
-			return (-1);
-		if (UMAX_ENTRY < f->bindPairs[i].inIndex)
-			return (-1);
-		if (parse_7zip_uint64(a, &(f->bindPairs[i].outIndex)) < 0)
+		if (parse_7zip_size(a, &(f->bindPairs[i].inIndex)) < 0)
 			return (-1);
-		if (UMAX_ENTRY < f->bindPairs[i].outIndex)
+		if (parse_7zip_size(a, &(f->bindPairs[i].outIndex)) < 0)
 			return (-1);
 	}
 
 	f->numPackedStreams = numInStreamsTotal - f->numBindPairs;
-	f->packedStreams =
-	    calloc((size_t)f->numPackedStreams, sizeof(*f->packedStreams));
-	if (f->packedStreams == NULL)
-		return (-1);
+	/* packedStreams are not needed; parse/verify nonetheless */
 	if (f->numPackedStreams == 1) {
 		for (i = 0; i < numInStreamsTotal; i++) {
-			unsigned j;
+			size_t j;
 			for (j = 0; j < f->numBindPairs; j++) {
 				if (f->bindPairs[j].inIndex == i)
 					break;
@@ -2386,12 +2371,10 @@
 		}
 		if (i == numInStreamsTotal)
 			return (-1);
-		f->packedStreams[0] = i;
 	} else {
 		for (i = 0; i < f->numPackedStreams; i++) {
-			if (parse_7zip_uint64(a, &(f->packedStreams[i])) < 0)
-				return (-1);
-			if (UMAX_ENTRY < f->packedStreams[i])
+			size_t packedStream;
+			if (parse_7zip_size(a, &packedStream) < 0)
 				return (-1);
 		}
 	}
@@ -2404,7 +2387,7 @@
 static void
 free_CodersInfo(struct _7z_coders_info *ci)
 {
-	unsigned i;
+	size_t i;
 
 	if (ci->folders) {
 		for (i = 0; i < ci->numFolders; i++)
@@ -2416,9 +2399,10 @@
 static int
 read_CodersInfo(struct archive_read *a, struct _7z_coders_info *ci)
 {
+	struct _7zip *zip = a->format->data;
 	const unsigned char *p;
 	struct _7z_digests digest;
-	unsigned i;
+	size_t dataStreamIndex, i;
 
 	memset(ci, 0, sizeof(*ci));
 	memset(&digest, 0, sizeof(digest));
@@ -2431,10 +2415,16 @@
 	/*
 	 * Read NumFolders.
 	 */
-	if (parse_7zip_uint64(a, &(ci->numFolders)) < 0)
+	if (parse_7zip_size(a, &(ci->numFolders)) < 0)
 		goto failed;
-	if (UMAX_ENTRY < ci->numFolders)
-		return (-1);
+	/*
+	 * Each folder is encoded by at least one byte in the coders
+	 * list that follows, so a folder count larger than the bytes
+	 * left in the header cannot be honored and is rejected here
+	 * before it is used to size the folders allocation.
+	 */
+	if (ci->numFolders > (uint64_t)zip->header_bytes_remaining)
+		goto failed;
 
 	/*
 	 * Read External.
@@ -2444,7 +2434,7 @@
 	switch (*p) {
 	case 0:
 		ci->folders =
-			calloc((size_t)ci->numFolders, sizeof(*ci->folders));
+			calloc(ci->numFolders, sizeof(*ci->folders));
 		if (ci->folders == NULL)
 			return (-1);
 		for (i = 0; i < ci->numFolders; i++) {
@@ -2453,9 +2443,7 @@
 		}
 		break;
 	case 1:
-		if (parse_7zip_uint64(a, &(ci->dataStreamIndex)) < 0)
-			return (-1);
-		if (UMAX_ENTRY < ci->dataStreamIndex)
+		if (parse_7zip_size(a, &dataStreamIndex) < 0)
 			return (-1);
 		if (ci->numFolders > 0) {
 			archive_set_error(&a->archive, -1,
@@ -2476,14 +2464,14 @@
 
 	for (i = 0; i < ci->numFolders; i++) {
 		struct _7z_folder *folder = &(ci->folders[i]);
-		unsigned j;
+		size_t j;
 
 		folder->unPackSize =
-		    calloc((size_t)folder->numOutStreams, sizeof(*folder->unPackSize));
+		    calloc(folder->numOutStreams, sizeof(*folder->unPackSize));
 		if (folder->unPackSize == NULL)
 			goto failed;
 		for (j = 0; j < folder->numOutStreams; j++) {
-			if (parse_7zip_uint64(a, &(folder->unPackSize[j])) < 0)
+			if (parse_7zip_int64(a, &(folder->unPackSize[j])) < 0)
 				goto failed;
 		}
 	}
@@ -2497,7 +2485,7 @@
 		return (0);
 	if (*p != kCRC)
 		goto failed;
-	if (read_Digests(a, &digest, (size_t)ci->numFolders) < 0)
+	if (read_Digests(a, &digest, ci->numFolders) < 0)
 		goto failed;
 	for (i = 0; i < ci->numFolders; i++) {
 		ci->folders[i].digest_defined = digest.defineds[i];
@@ -2518,16 +2506,16 @@
 	return (-1);
 }
 
-static uint64_t
+static int64_t
 folder_uncompressed_size(struct _7z_folder *f)
 {
-	int n = (int)f->numOutStreams;
-	unsigned pairs = (unsigned)f->numBindPairs;
+	size_t n = f->numOutStreams;
+	size_t pairs = f->numBindPairs;
 
-	while (--n >= 0) {
-		unsigned i;
+	while (n-- > 0) {
+		size_t i;
 		for (i = 0; i < pairs; i++) {
-			if (f->bindPairs[i].outIndex == (uint64_t)n)
+			if (f->bindPairs[i].outIndex == n)
 				break;
 		}
 		if (i >= pairs)
@@ -2549,11 +2537,11 @@
     struct _7z_folder *f, size_t numFolders)
 {
 	const unsigned char *p;
-	uint64_t *usizes;
+	int64_t *usizes;
+	size_t numDigests;
 	size_t unpack_streams;
+	size_t i;
 	int type;
-	unsigned i;
-	uint32_t numDigests;
 
 	memset(ss, 0, sizeof(*ss));
 
@@ -2567,13 +2555,11 @@
 	if (type == kNumUnPackStream) {
 		unpack_streams = 0;
 		for (i = 0; i < numFolders; i++) {
-			if (parse_7zip_uint64(a, &(f[i].numUnpackStreams)) < 0)
+			if (parse_7zip_size(a, &(f[i].numUnpackStreams)) < 0)
 				return (-1);
-			if (f[i].numUnpackStreams >
-			    UMAX_ENTRY - unpack_streams) {
+			if (archive_ckd_add_size(&unpack_streams,
+			    unpack_streams, f[i].numUnpackStreams))
 				return (-1);
-			}
-			unpack_streams += (size_t)f[i].numUnpackStreams;
 		}
 		if ((p = header_bytes(a, 1)) == NULL)
 			return (-1);
@@ -2581,6 +2567,9 @@
 	} else
 		unpack_streams = numFolders;
 
+	if (unpack_streams > UMAX_ENTRY)
+		return (-1);
+
 	if (type != kSize) {
 		for (i = 0; i < numFolders; i++) {
 			if (f[i].numUnpackStreams > 1)
@@ -2603,8 +2592,8 @@
 
 	usizes = ss->unpackSizes;
 	for (i = 0; i < numFolders; i++) {
-		unsigned pack;
-		uint64_t size, sum;
+		int64_t size, sum;
+		size_t pack;
 
 		if (f[i].numUnpackStreams == 0)
 			continue;
@@ -2612,11 +2601,10 @@
 		sum = 0;
 		if (type == kSize) {
 			for (pack = 1; pack < f[i].numUnpackStreams; pack++) {
-				if (parse_7zip_uint64(a, usizes) < 0)
+				if (parse_7zip_int64(a, usizes) < 0)
 					return (-1);
-				if (*usizes > UINT64_MAX - sum)
+				if (archive_ckd_add_i64(&sum, sum, *usizes++))
 					return (-1);
-				sum += *usizes++;
 			}
 		}
 		size = folder_uncompressed_size(&f[i]);
@@ -2634,14 +2622,18 @@
 	numDigests = 0;
 	for (i = 0; i < numFolders; i++) {
 		if (f[i].numUnpackStreams != 1 || !f[i].digest_defined)
-			numDigests += (uint32_t)f[i].numUnpackStreams;
+			if (archive_ckd_add_size(&numDigests,
+			    numDigests, f[i].numUnpackStreams)) {
+				errno = ENOMEM;
+				return (-1);
+			}
 	}
 
 	if (type == kCRC) {
 		struct _7z_digests tmpDigests;
 		unsigned char *digestsDefined = ss->digestsDefined;
-		uint32_t * digests = ss->digests;
-		int di = 0;
+		uint32_t *digests = ss->digests;
+		size_t di = 0;
 
 		memset(&tmpDigests, 0, sizeof(tmpDigests));
 		if (read_Digests(a, &(tmpDigests), numDigests) < 0) {
@@ -2653,7 +2645,7 @@
 				*digestsDefined++ = 1;
 				*digests++ = f[i].digest;
 			} else {
-				unsigned j;
+				size_t j;
 
 				for (j = 0; j < f[i].numUnpackStreams;
 				    j++, di++) {
@@ -2689,16 +2681,16 @@
 static int
 read_StreamsInfo(struct archive_read *a, struct _7z_stream_info *si)
 {
-	struct _7zip *zip = (struct _7zip *)a->format->data;
+	struct _7zip *zip = a->format->data;
 	const unsigned char *p;
-	unsigned i;
+	size_t i;
 
 	memset(si, 0, sizeof(*si));
 
 	if ((p = header_bytes(a, 1)) == NULL)
 		return (-1);
 	if (*p == kPackInfo) {
-		uint64_t packPos;
+		int64_t packPos;
 
 		if (read_PackInfo(a, &(si->pi)) < 0)
 			return (-1);
@@ -2711,9 +2703,9 @@
 		packPos = si->pi.pos;
 		for (i = 0; i < si->pi.numPackStreams; i++) {
 			si->pi.positions[i] = packPos;
-			if (packPos > UINT64_MAX - si->pi.sizes[i])
+			if (archive_ckd_add_i64(&packPos,
+			    packPos, si->pi.sizes[i]))
 				return (-1);
-			packPos += si->pi.sizes[i];
 			if (packPos > zip->header_offset)
 				return (-1);
 		}
@@ -2721,7 +2713,7 @@
 			return (-1);
 	}
 	if (*p == kUnPackInfo) {
-		uint32_t packIndex;
+		size_t packIndex;
 		struct _7z_folder *f;
 
 		if (read_CodersInfo(a, &(si->ci)) < 0)
@@ -2734,12 +2726,9 @@
 		f = si->ci.folders;
 		for (i = 0; i < si->ci.numFolders; i++) {
 			f[i].packIndex = packIndex;
-			if (f[i].numPackedStreams > UINT32_MAX)
-				return (-1);
-			if (packIndex > UINT32_MAX - (uint32_t)f[i].numPackedStreams)
-				return (-1);
-			packIndex += (uint32_t)f[i].numPackedStreams;
-			if (packIndex > si->pi.numPackStreams)
+			if (archive_ckd_add_size(&packIndex,
+			    packIndex, f[i].numPackedStreams) ||
+			    packIndex > si->pi.numPackStreams)
 				return (-1);
 		}
 		if ((p = header_bytes(a, 1)) == NULL)
@@ -2748,7 +2737,7 @@
 
 	if (*p == kSubStreamsInfo) {
 		if (read_SubStreamsInfo(a, &(si->ss),
-		    si->ci.folders, (size_t)si->ci.numFolders) < 0)
+		    si->ci.folders, si->ci.numFolders) < 0)
 			return (-1);
 		if ((p = header_bytes(a, 1)) == NULL)
 			return (-1);
@@ -2771,18 +2760,38 @@
 	free(h->attrBools);
 }
 
+/*
+ * Files without unpack streams must be described by the EmptyStream bitmap,
+ * which consumes one bit for every file entry in FilesInfo.
+ */
 static int
+files_info_numfiles_is_sane(const struct _7zip *zip)
+{
+	int64_t empty_stream_map_bytes;
+
+	if (zip->numFiles > SIZE_MAX / sizeof(*zip->entries))
+		return (0);
+
+	if (zip->numFiles <= zip->si.ss.unpack_streams)
+		return (1);
+
+	empty_stream_map_bytes = (zip->numFiles + 7) / 8;
+	return (empty_stream_map_bytes <= zip->header_bytes_remaining);
+}
+
+static int
 read_Header(struct archive_read *a, struct _7z_header_info *h,
     int check_header_id)
 {
-	struct _7zip *zip = (struct _7zip *)a->format->data;
+	struct _7zip *zip = a->format->data;
 	const unsigned char *p;
 	struct _7z_folder *folders;
 	struct _7z_stream_info *si = &(zip->si);
 	struct _7zip_entry *entries;
-	uint32_t folderIndex, indexInFolder;
-	unsigned i;
-	int eindex, empty_streams, sindex;
+	size_t folderIndex, indexInFolder;
+	size_t i;
+	size_t eindex, empty_streams, sindex;
+	int attr_seen = 0;
 
 	if (check_header_id) {
 		/*
@@ -2801,13 +2810,26 @@
 		return (-1);
 	if (*p == kArchiveProperties) {
 		for (;;) {
-			uint64_t size;
+			int64_t size;
 			if ((p = header_bytes(a, 1)) == NULL)
 				return (-1);
-			if (*p == 0)
+			if (*p == kEnd)
 				break;
-			if (parse_7zip_uint64(a, &size) < 0)
+			if (parse_7zip_int64(a, &size) < 0)
 				return (-1);
+			if (size < 0 || zip->header_bytes_remaining < size)
+				return (-1);
+
+			/* Skip the property data to keep header parsing aligned. */
+			while (size > 0) {
+				int64_t skip = size;
+
+				if (skip > UBUFF_SIZE)
+					skip = UBUFF_SIZE;
+				if (header_bytes(a, (size_t)skip) == NULL)
+					return (-1);
+				size -= skip;
+			}
 		}
 		if ((p = header_bytes(a, 1)) == NULL)
 			return (-1);
@@ -2831,12 +2853,12 @@
 	if (*p != kFilesInfo)
 		return (-1);
 
-	if (parse_7zip_uint64(a, &(zip->numFiles)) < 0)
+	if (parse_7zip_size(a, &(zip->numFiles)) < 0)
 		return (-1);
 	if (!files_info_numfiles_is_sane(zip))
 		return (-1);
 
-	zip->entries = calloc((size_t)zip->numFiles, sizeof(*zip->entries));
+	zip->entries = calloc(zip->numFiles, sizeof(*zip->entries));
 	if (zip->entries == NULL)
 		return (-1);
 	entries = zip->entries;
@@ -2844,7 +2866,7 @@
 	empty_streams = 0;
 	for (;;) {
 		int type;
-		uint64_t size;
+		int64_t size;
 		size_t ll;
 
 		if ((p = header_bytes(a, 1)) == NULL)
@@ -2853,9 +2875,10 @@
 		if (type == kEnd)
 			break;
 
-		if (parse_7zip_uint64(a, &size) < 0)
+		if (parse_7zip_int64(a, &size) < 0)
 			return (-1);
-		if (zip->header_bytes_remaining < size || size > SIZE_MAX / 4)
+		if (zip->header_bytes_remaining < size ||
+		    size > (int64_t)(SIZE_MAX / 4))
 			return (-1);
 		ll = (size_t)size;
 
@@ -2863,12 +2886,12 @@
 		case kEmptyStream:
 			if (h->emptyStreamBools != NULL)
 				return (-1);
-			h->emptyStreamBools = calloc((size_t)zip->numFiles,
+			h->emptyStreamBools = calloc(zip->numFiles,
 			    sizeof(*h->emptyStreamBools));
 			if (h->emptyStreamBools == NULL)
 				return (-1);
 			if (read_Bools(
-			    a, h->emptyStreamBools, (size_t)zip->numFiles) < 0)
+			    a, h->emptyStreamBools, zip->numFiles) < 0)
 				return (-1);
 			empty_streams = 0;
 			for (i = 0; i < zip->numFiles; i++) {
@@ -2911,7 +2934,7 @@
 		case kCTime:
 		case kATime:
 		case kMTime:
-			if (read_Times(a, h, type) < 0)
+			if (read_Times(a, type) < 0)
 				return (-1);
 			break;
 		case kName:
@@ -2981,21 +3004,20 @@
 			if ((p = header_bytes(a, 2)) == NULL)
 				return (-1);
 			allAreDefined = *p;
-			if (h->attrBools != NULL)
-				return (-1);
-			h->attrBools = calloc((size_t)zip->numFiles,
-			    sizeof(*h->attrBools));
-			if (h->attrBools == NULL)
+			if (attr_seen)
 				return (-1);
-			if (allAreDefined)
-				memset(h->attrBools, 1, (size_t)zip->numFiles);
-			else {
+			attr_seen = 1;
+			if (!allAreDefined) {
+				h->attrBools = calloc(zip->numFiles,
+				    sizeof(*h->attrBools));
+				if (h->attrBools == NULL)
+					return (-1);
 				if (read_Bools(a, h->attrBools,
-				      (size_t)zip->numFiles) < 0)
+				    zip->numFiles) < 0)
 					return (-1);
 			}
 			for (i = 0; i < zip->numFiles; i++) {
-				if (h->attrBools[i]) {
+				if (allAreDefined || h->attrBools[i]) {
 					if ((p = header_bytes(a, 4)) == NULL)
 						return (-1);
 					entries[i].attr = archive_le32dec(p);
@@ -3048,7 +3070,7 @@
 		}
 
 		if (entries[i].flg & HAS_STREAM) {
-			if ((size_t)sindex >= si->ss.unpack_streams)
+			if (sindex >= si->ss.unpack_streams)
 				return (-1);
 			if (entries[i].mode == 0)
 				entries[i].mode = AE_IFREG | 0666;
@@ -3057,14 +3079,10 @@
 			entries[i].ssIndex = sindex;
 			sindex++;
 		} else {
-			int dir;
-			if (h->emptyFileBools == NULL)
-				dir = 1;
-			else {
-				if (h->emptyFileBools[eindex])
-					dir = 0;
-				else
-					dir = 1;
+			int dir = 1;
+
+			if (h->emptyFileBools != NULL) {
+				dir = !h->emptyFileBools[eindex];
 				eindex++;
 			}
 			if (entries[i].mode == 0) {
@@ -3121,42 +3139,38 @@
 }
 
 static int
-read_Times(struct archive_read *a, struct _7z_header_info *h, int type)
+read_Times(struct archive_read *a, int type)
 {
-	struct _7zip *zip = (struct _7zip *)a->format->data;
+	struct _7zip *zip = a->format->data;
 	const unsigned char *p;
 	struct _7zip_entry *entries = zip->entries;
-	unsigned char *timeBools;
+	unsigned char *timeBools = NULL;
 	int allAreDefined;
-	unsigned i;
-
-	timeBools = calloc((size_t)zip->numFiles, sizeof(*timeBools));
-	if (timeBools == NULL)
-		return (-1);
+	size_t dataIndex, i;
 
 	/* Read allAreDefined. */
 	if ((p = header_bytes(a, 1)) == NULL)
 		goto failed;
 	allAreDefined = *p;
-	if (allAreDefined)
-		memset(timeBools, 1, (size_t)zip->numFiles);
-	else {
-		if (read_Bools(a, timeBools, (size_t)zip->numFiles) < 0)
+
+	if (!allAreDefined) {
+		timeBools = calloc(zip->numFiles, sizeof(*timeBools));
+		if (timeBools == NULL)
 			goto failed;
+		if (read_Bools(a, timeBools, zip->numFiles) < 0)
+			goto failed;
 	}
 
 	/* Read external. */
 	if ((p = header_bytes(a, 1)) == NULL)
 		goto failed;
 	if (*p) {
-		if (parse_7zip_uint64(a, &(h->dataIndex)) < 0)
-			goto failed;
-		if (UMAX_ENTRY < h->dataIndex)
+		if (parse_7zip_size(a, &dataIndex) < 0)
 			goto failed;
 	}
 
 	for (i = 0; i < zip->numFiles; i++) {
-		if (!timeBools[i])
+		if (!allAreDefined && !timeBools[i])
 			continue;
 		if ((p = header_bytes(a, 8)) == NULL)
 			goto failed;
@@ -3192,7 +3206,8 @@
 static int
 decode_encoded_header_info(struct archive_read *a, struct _7z_stream_info *si)
 {
-	struct _7zip *zip = (struct _7zip *)a->format->data;
+	struct _7zip *zip = a->format->data;
+	int64_t pi_end;
 
 	errno = 0;
 	if (read_StreamsInfo(a, si) < 0) {
@@ -3210,9 +3225,12 @@
 		return (ARCHIVE_FATAL);
 	}
 
-	if (zip->header_offset < si->pi.pos + si->pi.sizes[0] ||
-	    (int64_t)(si->pi.pos + si->pi.sizes[0]) < 0 ||
-	    si->pi.sizes[0] == 0 || (int64_t)si->pi.pos < 0) {
+	if (archive_ckd_add_i64(&pi_end, si->pi.pos, si->pi.sizes[0])) {
+		archive_set_error(&a->archive, -1, "Malformed 7-Zip archive");
+		return (ARCHIVE_FATAL);
+	}
+
+	if (zip->header_offset < pi_end) {
 		archive_set_error(&a->archive, -1, "Malformed Header offset");
 		return (ARCHIVE_FATAL);
 	}
@@ -3223,10 +3241,10 @@
 static const unsigned char *
 header_bytes(struct archive_read *a, size_t rbytes)
 {
-	struct _7zip *zip = (struct _7zip *)a->format->data;
+	struct _7zip *zip = a->format->data;
 	const unsigned char *p;
 
-	if (zip->header_bytes_remaining < rbytes)
+	if ((uint64_t)zip->header_bytes_remaining < rbytes)
 		return (NULL);
 	if (zip->pack_stream_bytes_unconsumed)
 		read_consume(a);
@@ -3258,8 +3276,8 @@
     struct _7z_header_info *header)
 {
 	const unsigned char *p;
-	uint64_t next_header_offset;
-	uint64_t next_header_size;
+	int64_t next_header_offset;
+	int64_t next_header_size;
 	uint32_t next_header_crc;
 	ssize_t bytes_avail;
 	int64_t data_offset;
@@ -3277,7 +3295,10 @@
 		return (ARCHIVE_FATAL);
 	}
 
-	zip->seek_base = (uint64_t)data_offset + 32;
+	if (archive_ckd_add_i64(&zip->seek_base, data_offset, 32)) {
+		archive_set_error(&a->archive, -1, "Malformed 7-Zip archive");
+		return (ARCHIVE_FATAL);
+	}
 
 	if (memcmp(p, _7ZIP_SIGNATURE, 6) != 0) {
 		archive_set_error(&a->archive, -1, "Not 7-Zip archive file");
@@ -3294,25 +3315,35 @@
 	}
 
 	next_header_offset = archive_le64dec(p + 12);
+	if (next_header_offset < 0) {
+		archive_set_error(&a->archive, -1, "Malformed 7-Zip archive");
+		return (ARCHIVE_FATAL);
+	}
 	next_header_size = archive_le64dec(p + 20);
+	if (next_header_size < 0) {
+		archive_set_error(&a->archive, -1, "Malformed 7-Zip archive");
+		return (ARCHIVE_FATAL);
+	}
 	next_header_crc = archive_le32dec(p + 28);
 
 	if (next_header_size == 0)
-		/* There is no entry in an archive file. */
+		/* There is no entry in the archive file. */
 		return (ARCHIVE_EOF);
 
-	if (((int64_t)next_header_offset) < 0) {
-		archive_set_error(&a->archive, -1, "Malformed 7-Zip archive");
-		return (ARCHIVE_FATAL);
-	}
 	__archive_read_consume(a, 32);
 	if (next_header_offset != 0) {
-		if (bytes_avail >= (ssize_t)next_header_offset)
+		if (bytes_avail >= next_header_offset)
 			__archive_read_consume(a, next_header_offset);
-		else if (seek_compat(a,
-		    next_header_offset + zip->seek_base, SEEK_SET, 1) < 0) {
-			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Seek error");
-			return (ARCHIVE_FATAL);
+		else {
+			int64_t target;
+
+			if (archive_ckd_add_i64(&target,
+			    zip->seek_base, next_header_offset) ||
+			    seek_compat(a, target, SEEK_SET, 1) < 0) {
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_MISC, "Seek error");
+				return (ARCHIVE_FATAL);
+			}
 		}
 	}
 	zip->stream_offset = next_header_offset;
@@ -3424,10 +3455,10 @@
 get_uncompressed_data(struct archive_read *a, const void **buff, size_t size,
     size_t minimum)
 {
-	struct _7zip *zip = (struct _7zip *)a->format->data;
+	struct _7zip *zip = a->format->data;
 	ssize_t bytes_avail;
 
-	if (zip->codec == _7Z_COPY && zip->codec2 == (unsigned long)-1) {
+	if (zip->codec == _7Z_COPY && zip->codec2 == -1) {
 		/* Copy mode. */
 
 		*buff = __archive_read_ahead(a, minimum, &bytes_avail);
@@ -3473,14 +3504,25 @@
 	return (bytes_avail);
 }
 
+static size_t
+align_size(size_t s)
+{
+	size_t r;
+
+	if (archive_ckd_add_size(&r, s, 1023))
+		return (s);
+	r &= ~0x3ff;
+	return (r);
+}
+
 static ssize_t
 extract_pack_stream(struct archive_read *a, size_t minimum)
 {
-	struct _7zip *zip = (struct _7zip *)a->format->data;
+	struct _7zip *zip = a->format->data;
 	ssize_t bytes_avail;
 	int r;
 
-	if (zip->codec == _7Z_COPY && zip->codec2 == (unsigned long)-1) {
+	if (zip->codec == _7Z_COPY && zip->codec2 == -1) {
 		if (minimum == 0)
 			minimum = 1;
 		if (__archive_read_ahead(a, minimum, &bytes_avail) == NULL
@@ -3490,10 +3532,10 @@
 			    "Truncated 7-Zip file body");
 			return (ARCHIVE_FATAL);
 		}
-		if ((uint64_t)bytes_avail > zip->pack_stream_inbytes_remaining)
+		if (bytes_avail > zip->pack_stream_inbytes_remaining)
 			bytes_avail = (ssize_t)zip->pack_stream_inbytes_remaining;
 		zip->pack_stream_inbytes_remaining -= bytes_avail;
-		if ((uint64_t)bytes_avail > zip->folder_outbytes_remaining)
+		if (bytes_avail > zip->folder_outbytes_remaining)
 			bytes_avail = (ssize_t)zip->folder_outbytes_remaining;
 		zip->folder_outbytes_remaining -= bytes_avail;
 		zip->uncompressed_buffer_bytes_remaining = bytes_avail;
@@ -3502,18 +3544,17 @@
 
 	/* If the buffer hasn't been allocated, allocate it now. */
 	if (zip->uncompressed_buffer == NULL) {
-		zip->uncompressed_buffer_size = UBUFF_SIZE;
-		if (zip->uncompressed_buffer_size < minimum) {
-			zip->uncompressed_buffer_size = minimum + 1023;
-			zip->uncompressed_buffer_size &= ~0x3ff;
-		}
-		zip->uncompressed_buffer =
-		    malloc(zip->uncompressed_buffer_size);
+		size_t new_size = UBUFF_SIZE;
+
+		if (new_size < minimum)
+		    new_size = align_size(minimum);
+		zip->uncompressed_buffer = malloc(new_size);
 		if (zip->uncompressed_buffer == NULL) {
 			archive_set_error(&a->archive, ENOMEM,
 			    "No memory for 7-Zip decompression");
 			return (ARCHIVE_FATAL);
 		}
+		zip->uncompressed_buffer_size = new_size;
 		zip->uncompressed_buffer_bytes_remaining = 0;
 	} else if (zip->uncompressed_buffer_size < minimum ||
 	    zip->uncompressed_buffer_bytes_remaining < minimum) {
@@ -3534,10 +3575,8 @@
 			 * the minimum size.
 			 */
 			void *p;
-			size_t new_size;
+			size_t new_size = align_size(minimum);
 
-			new_size = minimum + 1023;
-			new_size &= ~0x3ff;
 			p = realloc(zip->uncompressed_buffer, new_size);
 			if (p == NULL) {
 				archive_set_error(&a->archive, ENOMEM,
@@ -3583,7 +3622,7 @@
 		bytes_out = zip->uncompressed_buffer_size
 			- zip->uncompressed_buffer_bytes_remaining;
 		bytes_in = bytes_avail;
-		if (bytes_in > zip->pack_stream_inbytes_remaining)
+		if (bytes_in > (uint64_t)zip->pack_stream_inbytes_remaining)
 			bytes_in = (size_t)zip->pack_stream_inbytes_remaining;
 		/* Drive decompression. */
 		r = decompress(a, zip, buff_out, &bytes_out,
@@ -3599,7 +3638,7 @@
 			return (ARCHIVE_FATAL);
 		}
 		zip->pack_stream_inbytes_remaining -= bytes_in;
-		if (bytes_out > zip->folder_outbytes_remaining)
+		if (bytes_out > (uint64_t)zip->folder_outbytes_remaining)
 			bytes_out = (size_t)zip->folder_outbytes_remaining;
 		zip->folder_outbytes_remaining -= bytes_out;
 		zip->uncompressed_buffer_bytes_remaining += bytes_out;
@@ -3637,10 +3676,10 @@
 static int
 seek_pack(struct archive_read *a)
 {
-	struct _7zip *zip = (struct _7zip *)a->format->data;
+	struct _7zip *zip = a->format->data;
 	int64_t pack_offset;
 
-	if (zip->pack_stream_remaining <= 0) {
+	if (zip->pack_stream_remaining == 0) {
 		archive_set_error(&(a->archive),
 		    ARCHIVE_ERRNO_MISC, "Damaged 7-Zip archive");
 		return (ARCHIVE_FATAL);
@@ -3649,8 +3688,11 @@
 	    zip->si.pi.sizes[zip->pack_stream_index];
 	pack_offset = zip->si.pi.positions[zip->pack_stream_index];
 	if (zip->stream_offset != pack_offset) {
-		if (0 > seek_compat(a, pack_offset + zip->seek_base,
-		    SEEK_SET, 1)) {
+		int64_t target;
+
+		if (archive_ckd_add_i64(&target,
+		    zip->seek_base, pack_offset) ||
+		    0 > seek_compat(a, target, SEEK_SET, 1)) {
 			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Seek error");
 			return (ARCHIVE_FATAL);
 		}
@@ -3665,8 +3707,8 @@
 read_stream(struct archive_read *a, const void **buff, size_t size,
     size_t minimum)
 {
-	struct _7zip *zip = (struct _7zip *)a->format->data;
-	uint64_t skip_bytes = 0;
+	struct _7zip *zip = a->format->data;
+	int64_t skip_bytes = 0;
 	ssize_t r;
 
 	if (zip->uncompressed_buffer_bytes_remaining == 0) {
@@ -3737,9 +3779,10 @@
 		return (r);
 
 	/*
-	 * Skip the bytes we already has skipped in skip_stream().
+	 * Skip the bytes we already have skipped in skip_stream().
 	 */
 	while (1) {
+		size_t request;
 		ssize_t skipped;
 
 		if (zip->uncompressed_buffer_bytes_remaining == 0) {
@@ -3763,8 +3806,12 @@
 		if (!skip_bytes)
 			break;
 
-		skipped = get_uncompressed_data(
-			a, buff, (size_t)skip_bytes, 0);
+		if (skip_bytes > MAX_READ)
+			request = MAX_READ;
+		else
+			request = (size_t)skip_bytes;
+
+		skipped = get_uncompressed_data(a, buff, request, 0);
 		if (skipped < 0)
 			return (skipped);
 		skip_bytes -= skipped;
@@ -3779,10 +3826,10 @@
 setup_decode_folder(struct archive_read *a, struct _7z_folder *folder,
     int header)
 {
-	struct _7zip *zip = (struct _7zip *)a->format->data;
+	struct _7zip *zip = a->format->data;
 	const struct _7z_coder *coder1, *coder2;
 	const char *cname = (header)?"archive header":"file content";
-	unsigned i;
+	size_t i;
 	int r, found_bcj2 = 0;
 
 	/*
@@ -3840,8 +3887,8 @@
 	/*
 	 * Initialize a stream reader.
 	 */
-	zip->pack_stream_remaining = (unsigned)folder->numPackedStreams;
-	zip->pack_stream_index = (unsigned)folder->packIndex;
+	zip->pack_stream_remaining = folder->numPackedStreams;
+	zip->pack_stream_index = folder->packIndex;
 	zip->folder_outbytes_remaining = folder_uncompressed_size(folder);
 	zip->uncompressed_buffer_bytes_remaining = 0;
 	coder1 = &(folder->coders[0]);
@@ -3863,8 +3910,8 @@
 		const void *buff;
 		ssize_t bytes;
 		unsigned char *b[3] = {NULL, NULL, NULL};
-		uint64_t sunpack[3] ={-1, -1, -1};
-		uint64_t remaining;
+		int64_t sunpack[3] ={-1, -1, -1};
+		int64_t remaining;
 		size_t s[3] = {0, 0, 0};
 		int idx[3] = {0, 1, 2};
 
@@ -3917,15 +3964,11 @@
 				}
 			}
 			coder2 = &(fc[3]);
-			zip->main_stream_bytes_remaining =
-				(size_t)folder->unPackSize[2];
 			remaining = folder->unPackSize[2];
 		} else if (coder2 != NULL && coder2->codec == _7Z_X86_BCJ2 &&
 		    zip->pack_stream_remaining == 4 &&
 		    folder->numInStreams == 5 && folder->numOutStreams == 2) {
 			/* Source type 0 made by 7z */
-			zip->main_stream_bytes_remaining =
-				(size_t)folder->unPackSize[0];
 			remaining = folder->unPackSize[0];
 		} else {
 			/* We got an unexpected form. */
@@ -3934,13 +3977,6 @@
 			    "Unsupported form of BCJ2 streams");
 			return (ARCHIVE_FATAL);
 		}
-		if (remaining > SIZE_MAX) {
-			archive_set_error(&(a->archive),
-			    ARCHIVE_ERRNO_MISC,
-			    "7-Zip sub-stream size exceeds "
-			    "platform maximum");
-			return (ARCHIVE_FATAL);
-		}
 		zip->main_stream_bytes_remaining = remaining;
 
 
@@ -3948,8 +3984,9 @@
 		if ((r = seek_pack(a)) < 0)
 			return (r);
 		zip->pack_stream_bytes_unconsumed =
-		    (size_t)zip->pack_stream_inbytes_remaining;
-		read_consume(a);
+		    zip->pack_stream_inbytes_remaining;
+		if ((r = read_consume(a)) < 0)
+			return (r);
 
 		/* Read following three sub streams. */
 		for (i = 0; i < 3; i++) {
@@ -3960,7 +3997,7 @@
 				return (r);
 			}
 
-			if (sunpack[i] == (uint64_t)-1)
+			if (sunpack[i] == -1)
 				zip->folder_outbytes_remaining =
 				    zip->pack_stream_inbytes_remaining;
 			else
@@ -3974,7 +4011,7 @@
 
 			/* Allocate memory for the decoded data of a sub
 			 * stream. */
-			if (zip->folder_outbytes_remaining > SIZE_MAX) {
+			if ((uint64_t)zip->folder_outbytes_remaining > SIZE_MAX) {
 				free(b[0]); free(b[1]); free(b[2]);
 				archive_set_error(&a->archive,
 				    ARCHIVE_ERRNO_MISC,
@@ -4039,7 +4076,7 @@
 		 * of BCJ2.
 		 */
 		zip->pack_stream_remaining = 1;
-		zip->pack_stream_index = (unsigned)folder->packIndex;
+		zip->pack_stream_index = folder->packIndex;
 		zip->folder_outbytes_remaining =
 		    folder_uncompressed_size(folder);
 		zip->uncompressed_buffer_bytes_remaining = 0;
@@ -4054,26 +4091,40 @@
 	return (ARCHIVE_OK);
 }
 
-static int64_t
-skip_stream(struct archive_read *a, size_t skip_bytes)
+static int
+skip_stream(struct archive_read *a, int64_t skip_bytes)
 {
-	struct _7zip *zip = (struct _7zip *)a->format->data;
+	struct _7zip *zip = a->format->data;
 	const void *p;
 	int64_t skipped_bytes;
-	size_t bytes = skip_bytes;
+	int64_t bytes = skip_bytes;
 
 	if (zip->folder_index == 0) {
+		int64_t *v;
+
 		/*
 		 * Optimization for a list mode.
 		 * Avoid unnecessary decoding operations.
 		 */
-		zip->si.ci.folders[zip->entry->folderIndex].skipped_bytes
-		    += skip_bytes;
-		return (skip_bytes);
+		v = &zip->si.ci.folders[zip->entry->folderIndex].skipped_bytes;
+		if (archive_ckd_add_i64(v, *v, skip_bytes)) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Too many bytes to skip");
+			return (ARCHIVE_FATAL);
+		}
+		return (ARCHIVE_OK);
 	}
 
 	while (bytes) {
-		skipped_bytes = read_stream(a, &p, bytes, 0);
+		size_t request;
+
+		if (bytes > MAX_READ)
+			request = MAX_READ;
+		else
+			request = (size_t)bytes;
+
+		skipped_bytes = read_stream(a, &p, request, 0);
 		if (skipped_bytes < 0)
 			return (skipped_bytes);
 		if (skipped_bytes == 0) {
@@ -4082,11 +4133,11 @@
 			    "Truncated 7-Zip file body");
 			return (ARCHIVE_FATAL);
 		}
-		bytes -= (size_t)skipped_bytes;
+		bytes -= skipped_bytes;
 		if (zip->pack_stream_bytes_unconsumed)
 			read_consume(a);
 	}
-	return (skip_bytes);
+	return (ARCHIVE_OK);
 }
 
 /*
@@ -4135,7 +4186,7 @@
 		if (p >= limit)
 			break;
 		prevPosT = bufferPos - prevPosT;
-		if (prevPosT > 3)
+		if (prevPosT == 0 || prevPosT > 3)
 			prevMask = 0;
 		else {
 			prevMask = (prevMask << ((int)prevPosT - 1)) & 0x7;
@@ -4439,7 +4490,7 @@
 	const uint8_t *buf0, *buf1, *buf2, *buf3;
 	size_t size0, size1, size2, size3;
 	const uint8_t *buffer, *bufferLim;
-	unsigned int i, j;
+	size_t i, j;
 
 	size0 = zip->tmp_stream_bytes_remaining;
 	buf0 = zip->tmp_stream_buff + zip->tmp_stream_bytes_avail - size0;
@@ -4538,7 +4589,7 @@
 				size2 -= 4;
 			}
 			dest = archive_be32dec(v) -
-			    ((uint32_t)zip->bcj2_outPos + (uint32_t)outPos + 4);
+			    (zip->bcj2_outPos + outPos + 4);
 			archive_le32enc(out, dest);
 			zip->bcj2_prevByte = out[3];
 
@@ -4549,7 +4600,7 @@
 				 * Save odd bytes which we could not add into
 				 * the output buffer because of out of space.
 				 */
-				zip->odd_bcj_size = 4 -i;
+				zip->odd_bcj_size = 4 - i;
 				for (; i < 4; i++) {
 					j = i - 4 + (unsigned)zip->odd_bcj_size;
 					zip->odd_bcj[j] = out[i];
diff --git a/c/archive_read_support_format_all.c b/c/archive_read_support_format_all.c
--- a/c/archive_read_support_format_all.c
+++ b/c/archive_read_support_format_all.c
@@ -80,7 +80,7 @@
 	/* Note: We always return ARCHIVE_OK here, even if some of the
 	 * above return ARCHIVE_WARN.  The intent here is to enable
 	 * "as much as possible."  Clients who need specific
-	 * compression should enable those individually so they can
+	 * formats should enable those individually so they can
 	 * verify the level of support. */
 	/* Clear any warning messages set by the above functions. */
 	archive_clear_error(a);
diff --git a/c/archive_read_support_format_ar.c b/c/archive_read_support_format_ar.c
--- a/c/archive_read_support_format_ar.c
+++ b/c/archive_read_support_format_ar.c
@@ -55,7 +55,7 @@
 	 * but haven't yet marked as consumed.  Must be paired with
 	 * entry_bytes_remaining usage/modification.
 	 */
-	size_t   entry_bytes_unconsumed;
+	int64_t  entry_bytes_unconsumed;
 	int64_t	 entry_offset;
 	int64_t	 entry_padding;
 	char	*strtab;
@@ -81,18 +81,18 @@
 #define AR_fmag_offset 58
 #define AR_fmag_size 2
 
-static int	archive_read_format_ar_bid(struct archive_read *a, int);
-static int	archive_read_format_ar_cleanup(struct archive_read *a);
-static int	archive_read_format_ar_read_data(struct archive_read *a,
-		    const void **buff, size_t *size, int64_t *offset);
-static int	archive_read_format_ar_skip(struct archive_read *a);
-static int	archive_read_format_ar_read_header(struct archive_read *a,
-		    struct archive_entry *e);
-static uint64_t	ar_atol8(const char *p, unsigned char_cnt);
-static uint64_t	ar_atol10(const char *p, unsigned char_cnt);
-static int	ar_parse_gnu_filename_table(struct archive_read *a);
-static int	ar_parse_common_header(struct ar *ar, struct archive_entry *,
-		    const char *h);
+static int	archive_read_format_ar_bid(struct archive_read *, int);
+static int	archive_read_format_ar_cleanup(struct archive_read *);
+static int	archive_read_format_ar_read_data(struct archive_read *,
+		    const void **, size_t *, int64_t *);
+static int	archive_read_format_ar_skip(struct archive_read *);
+static int	archive_read_format_ar_read_header(struct archive_read *,
+		    struct archive_entry *);
+static uint64_t	ar_atol8(const char *, size_t);
+static uint64_t	ar_atol10(const char *, size_t);
+static int	ar_parse_gnu_filename_table(struct archive_read *);
+static void	ar_parse_common_header(struct ar *, struct archive_entry *,
+		    const char *);
 
 int
 archive_read_support_format_ar(struct archive *_a)
@@ -135,12 +135,12 @@
 static int
 archive_read_format_ar_cleanup(struct archive_read *a)
 {
-	struct ar *ar;
+	struct ar *ar = a->format->data;
 
-	ar = (struct ar *)(a->format->data);
 	free(ar->strtab);
 	free(ar);
-	(a->format->data) = NULL;
+	a->format->data = NULL;
+
 	return (ARCHIVE_OK);
 }
 
@@ -165,14 +165,12 @@
 
 static int
 _ar_read_header(struct archive_read *a, struct archive_entry *entry,
-	struct ar *ar, const char *h, size_t *unconsumed)
+	struct ar *ar, const char *h, int64_t *unconsumed)
 {
 	char filename[AR_name_size + 1];
 	uint64_t number; /* Used to hold parsed numbers before validation. */
-	size_t bsd_name_length, entry_size;
-	char *p, *st;
+	char *p;
 	const void *b;
-	int r;
 
 	/* Verify the magic signature on the file header. */
 	if (strncmp(h + AR_fmag_offset, "`\n", 2) != 0) {
@@ -248,14 +246,20 @@
 	}
 
 	/*
+	 * Parse the time, owner, mode, size fields.
+	 * This must come before any call to _read_ahead.
+	 */
+	ar_parse_common_header(ar, entry, h);
+
+	/*
 	 * '//' is the GNU filename table.
 	 * Later entries can refer to names in this table.
 	 */
 	if (strcmp(filename, "//") == 0) {
-		/* This must come before any call to _read_ahead. */
-		ar_parse_common_header(ar, entry, h);
+		char *st;
+		size_t entry_size;
+
 		archive_entry_copy_pathname(entry, filename);
-		archive_entry_set_filetype(entry, AE_IFREG);
 		/* Get the size of the filename table. */
 		number = ar_atol10(h + AR_size_offset, AR_size_size);
 		if (number > SIZE_MAX || number > 1024 * 1024 * 1024) {
@@ -310,22 +314,15 @@
 	 */
 	if (filename[0] == '/' && filename[1] >= '0' && filename[1] <= '9') {
 		number = ar_atol10(h + AR_name_offset + 1, AR_name_size - 1);
-		/*
-		 * If we can't look up the real name, warn and return
-		 * the entry with the wrong name.
-		 */
+		/* Fail if we can't look up the real name. */
 		if (ar->strtab == NULL || number >= ar->strtab_size) {
 			archive_set_error(&a->archive, EINVAL,
 			    "Can't find long filename for GNU/SVR4 archive entry");
-			archive_entry_copy_pathname(entry, filename);
-			/* Parse the time, owner, mode, size fields. */
-			ar_parse_common_header(ar, entry, h);
 			return (ARCHIVE_FATAL);
 		}
 
 		archive_entry_copy_pathname(entry, &ar->strtab[(size_t)number]);
-		/* Parse the time, owner, mode, size fields. */
-		return (ar_parse_common_header(ar, entry, h));
+		return (ARCHIVE_OK);
 	}
 
 	/*
@@ -334,9 +331,7 @@
 	 * the filename to the file contents.
 	 */
 	if (strncmp(filename, "#1/", 3) == 0) {
-		/* Parse the time, owner, mode, size fields. */
-		/* This must occur before _read_ahead is called again. */
-		ar_parse_common_header(ar, entry, h);
+		size_t bsd_name_length;
 
 		/* Parse the size of the name, adjust the file size. */
 		number = ar_atol10(h + AR_name_offset + 3, AR_name_size - 3);
@@ -347,7 +342,7 @@
 		 */
 		if (number > SIZE_MAX - 1
 		    || number > 1024 * 1024
-		    || (int64_t)number > ar->entry_bytes_remaining) {
+		    || number > (uint64_t)ar->entry_bytes_remaining) {
 			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 			    "Bad input file size");
 			return (ARCHIVE_FATAL);
@@ -386,49 +381,33 @@
 	}
 
 	/*
-	 * "/" is the SVR4/GNU archive symbol table.
-	 * "/SYM64/" is the SVR4/GNU 64-bit variant archive symbol table.
-	 */
-	if (strcmp(filename, "/") == 0 || strcmp(filename, "/SYM64/") == 0) {
-		archive_entry_copy_pathname(entry, filename);
-		/* Parse the time, owner, mode, size fields. */
-		r = ar_parse_common_header(ar, entry, h);
-		/* Force the file type to a regular file. */
-		archive_entry_set_filetype(entry, AE_IFREG);
-		return (r);
-	}
-
-	/*
-	 * "__.SYMDEF" is a BSD archive symbol table.
-	 */
-	if (strcmp(filename, "__.SYMDEF") == 0) {
-		archive_entry_copy_pathname(entry, filename);
-		/* Parse the time, owner, mode, size fields. */
-		return (ar_parse_common_header(ar, entry, h));
-	}
-
-	/*
 	 * Otherwise, this is a standard entry.  The filename
 	 * has already been trimmed as much as possible, based
 	 * on our current knowledge of the format.
+	 *
+	 * Note for future implementations:
+	 *
+	 * - "/" is the SVR4/GNU archive symbol table.
+	 * - "/SYM64/" is the SVR4/GNU 64-bit variant archive symbol table.
+	 * - "__.SYMDEF" is a BSD archive symbol table.
 	 */
 	archive_entry_copy_pathname(entry, filename);
-	return (ar_parse_common_header(ar, entry, h));
+	return (ARCHIVE_OK);
 }
 
 static int
 archive_read_format_ar_read_header(struct archive_read *a,
     struct archive_entry *entry)
 {
-	struct ar *ar = (struct ar*)(a->format->data);
-	size_t unconsumed;
+	struct ar *ar = a->format->data;
+	int64_t unconsumed;
 	const void *header_data;
 	int ret;
 
 	if (!ar->read_global_header) {
 		/*
-		 * We are now at the beginning of the archive,
-		 * so we need first consume the ar global header.
+		 * We are at the beginning of the archive now,
+		 * so we have to consume the ar global header first.
 		 */
 		__archive_read_consume(a, 8);
 		ar->read_global_header = 1;
@@ -452,11 +431,11 @@
 }
 
 
-static int
+static void
 ar_parse_common_header(struct ar *ar, struct archive_entry *entry,
     const char *h)
 {
-	uint64_t n;
+	int64_t n;
 
 	/* Copy remaining header */
 	archive_entry_set_mtime(entry,
@@ -468,24 +447,21 @@
 	archive_entry_set_mode(entry,
 	    (mode_t)ar_atol8(h + AR_mode_offset, AR_mode_size));
 	archive_entry_set_filetype(entry, AE_IFREG);
-	n = ar_atol10(h + AR_size_offset, AR_size_size);
+	n = (int64_t)ar_atol10(h + AR_size_offset, AR_size_size);
 
 	ar->entry_offset = 0;
 	ar->entry_padding = n % 2;
 	archive_entry_set_size(entry, n);
 	ar->entry_bytes_remaining = n;
-	return (ARCHIVE_OK);
 }
 
 static int
 archive_read_format_ar_read_data(struct archive_read *a,
     const void **buff, size_t *size, int64_t *offset)
 {
+	struct ar *ar = a->format->data;
 	ssize_t bytes_read;
-	struct ar *ar;
 
-	ar = (struct ar *)(a->format->data);
-
 	if (ar->entry_bytes_unconsumed) {
 		__archive_read_consume(a, ar->entry_bytes_unconsumed);
 		ar->entry_bytes_unconsumed = 0;
@@ -509,17 +485,9 @@
 		ar->entry_bytes_remaining -= bytes_read;
 		return (ARCHIVE_OK);
 	} else {
-		int64_t skipped = __archive_read_consume(a, ar->entry_padding);
-		if (skipped >= 0) {
-			ar->entry_padding -= skipped;
-		}
-		if (ar->entry_padding) {
-			if (skipped >= 0) {
-				archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
-					"Truncated ar archive - failed consuming padding");
-			}
+		if (__archive_read_consume(a, ar->entry_padding) < 0)
 			return (ARCHIVE_FATAL);
-		}
+		ar->entry_padding = 0;
 		*buff = NULL;
 		*size = 0;
 		*offset = ar->entry_offset;
@@ -530,15 +498,11 @@
 static int
 archive_read_format_ar_skip(struct archive_read *a)
 {
-	int64_t bytes_skipped;
-	struct ar* ar;
-
-	ar = (struct ar *)(a->format->data);
+	struct ar *ar = a->format->data;
 
-	bytes_skipped = __archive_read_consume(a,
-	    ar->entry_bytes_remaining + ar->entry_padding
-	    + ar->entry_bytes_unconsumed);
-	if (bytes_skipped < 0)
+	if (__archive_read_consume(a,
+	    ar->entry_bytes_remaining + ar->entry_padding +
+	    ar->entry_bytes_unconsumed) < 0)
 		return (ARCHIVE_FATAL);
 
 	ar->entry_bytes_remaining = 0;
@@ -551,11 +515,10 @@
 static int
 ar_parse_gnu_filename_table(struct archive_read *a)
 {
-	struct ar *ar;
+	struct ar *ar = a->format->data;
 	char *p;
 	size_t size;
 
-	ar = (struct ar*)(a->format->data);
 	size = ar->strtab_size;
 
 	for (p = ar->strtab; p < ar->strtab + size - 1; ++p) {
@@ -587,7 +550,7 @@
 }
 
 static uint64_t
-ar_atol8(const char *p, unsigned char_cnt)
+ar_atol8(const char *p, size_t char_cnt)
 {
 	uint64_t l;
 	unsigned int digit, base;
@@ -611,7 +574,7 @@
 }
 
 static uint64_t
-ar_atol10(const char *p, unsigned char_cnt)
+ar_atol10(const char *p, size_t char_cnt)
 {
 	uint64_t l;
 	unsigned int base, digit;
diff --git a/c/archive_read_support_format_cab.c b/c/archive_read_support_format_cab.c
--- a/c/archive_read_support_format_cab.c
+++ b/c/archive_read_support_format_cab.c
@@ -49,85 +49,186 @@
 #include "archive_endian.h"
 
 
+/*
+ * Huffman coding.
+ *
+ * Array representation of a Huffman tree for codes of up to 16 bit lengths.
+ * Lookups are performed through a direct, expanded lookup table.
+ *
+ * An expanded table has as many elements as needed to cover all possible
+ * indices formable with bit patterns of given lookup_bits length.
+ *
+ * If less codes exist, these span multiple entries for all possible
+ * combinations of following bits.
+ *
+ * Example of a Huffman tree with len_size = 3, lookup_bits = 2:
+ *
+ * Symbol | Code
+ * -------+-----
+ *      A | 0b0
+ *      B | 0b10
+ *      C | 0b11
+ *
+ * The bit sequences 0b00 and 0b01 are rightfully not covered by a code,
+ * since code 0b0 already maps to symbol A. The table will contain two
+ * entries for symbol A instead:
+ *
+ *  idx | tbl[idx]
+ * -----+---------
+ * 0b00 |        A
+ * 0b01 |        A
+ * 0b10 |        B
+ * 0b11 |        C
+ *
+ * By using lookup_bits bits as a lookup, it becomes apparent that 0b00 and
+ * 0b01 point to a symbol which actually has the code 0b0. A user of this
+ * data structure must check the code bit length of a retrieved symbol after
+ * the lookup to properly advance the bit stream:
+ *
+ * idx | bitlen[idx]
+ * ----+------------
+ *   A |           1
+ *   B |           2
+ *   C |           2
+ *
+ * Thus, a proper code sequence would be:
+ *
+ * symbol = tbl[read_bits(lookup_bits)]
+ * consume_bits(bitlen[symbol])
+ */
+struct huffman {
+	/*
+	 * Amount of symbols.
+	 *
+	 * This implementation keeps track of unused symbols as well,
+	 * thus symbols start with 0x0000 up to given symbol amount:
+	 * [0..symbol_count)
+	 *
+	 * Used to construct tbl.
+	 */
+	uint16_t	 symbol_count;
+	/*
+	 * Frequency of code bit lengths.
+	 *
+	 * Represents the amount of occurrences of given bit lengths.
+	 * Index 0 is used for "empty" codes (aka unused symbols),
+	 * otherwise index represents the bit length
+	 * (index 1 is bit length 1 and so on).
+	 *
+	 * Used to construct tbl.
+	 */
+	uint16_t	 freq[17];
+	/* Map of symbols to their code bit lengths. */
+	uint8_t		*bitlen;
+	/* Amount of bits to use for lookup (<= tbl_bits). */
+	uint8_t		 lookup_bits;
+	/*
+	 * Code bit length used for allocation (<= 16).
+	 *
+	 * Used to construct tbl.
+	 */
+	uint8_t		 tbl_bits;
+	/* Direct, expanded lookup table. */
+	uint16_t	*tbl;
+};
+
+/*
+ * Bit stream reader.
+ */
+struct lzx_br {
+#define CACHE_TYPE		uint64_t
+#define CACHE_BITS		(8 * sizeof(CACHE_TYPE))
+	/* Cache buffer. */
+	CACHE_TYPE	 cache_buffer;
+	/* Indicates how many bits avail in cache_buffer. */
+	size_t		 cache_avail;
+	uint8_t		 odd;
+	int		 have_odd;
+};
+
+struct lzx_pos_tbl {
+	uint32_t	 base;
+	uint8_t		 footer_bits;
+};
+
 struct lzx_dec {
 	/* Decoding status. */
 	int     		 state;
+#define ST_RD_TRANSLATION	0
+#define ST_RD_TRANSLATION_SIZE	1
+#define ST_RD_BLOCK_TYPE	2
+#define ST_RD_BLOCK_SIZE	3
+#define ST_RD_ALIGNMENT		4
+#define ST_RD_R0		5
+#define ST_RD_R1		6
+#define ST_RD_R2		7
+#define ST_COPY_UNCOMP1		8
+#define ST_COPY_UNCOMP2		9
+#define ST_RD_ALIGNED_OFFSET	10
+#define ST_RD_VERBATIM		11
+#define ST_RD_PRE_MAIN_TREE_256	12
+#define ST_MAIN_TREE_256	13
+#define ST_RD_PRE_MAIN_TREE_REM	14
+#define ST_MAIN_TREE_REM	15
+#define ST_RD_PRE_LENGTH_TREE	16
+#define ST_LENGTH_TREE		17
+#define ST_MAIN			18
+#define ST_LENGTH		19
+#define ST_OFFSET		20
+#define ST_REAL_POS		21
+#define ST_COPY			22
 
 	/*
-	 * Window to see last decoded data, from 32KBi to 2MBi.
+	 * Window to see last decoded data, from 32 KiB to 2 MiB.
 	 */
-	int			 w_size;
-	int			 w_mask;
+	size_t			 w_size;
+	size_t			 w_mask;
 	/* Window buffer, which is a loop buffer. */
-	unsigned char		*w_buff;
+	uint8_t			*w_buff;
 	/* The insert position to the window. */
-	int			 w_pos;
+	size_t			 w_pos;
 	/* The position where we can copy decoded code from the window. */
-	int     		 copy_pos;
+	size_t			 copy_pos;
 	/* The length how many bytes we can copy decoded code from
 	 * the window. */
-	int     		 copy_len;
+	size_t			 copy_len;
 	/* Translation reversal for x86 processor CALL byte sequence(E8).
 	 * This is used for LZX only. */
-	uint32_t		 translation_size;
-	char			 translation;
-	char			 block_type;
+	int32_t			 translation_size;
+	int			 translation;
+	uint8_t			 block_type;
 #define VERBATIM_BLOCK		1
 #define ALIGNED_OFFSET_BLOCK	2
 #define UNCOMPRESSED_BLOCK	3
 	size_t			 block_size;
 	size_t			 block_bytes_avail;
 	/* Repeated offset. */
-	int			 r0, r1, r2;
-	unsigned char		 rbytes[4];
-	int			 rbytes_avail;
-	int			 length_header;
-	int			 position_slot;
-	int			 offset_bits;
+	size_t			 r0, r1, r2;
+	uint8_t			 rbytes[4];
+	size_t			 rbytes_avail;
+	uint8_t			 length_header;
+	uint16_t		 position_slot;
+	uint8_t			 offset_bits;
 
-	struct lzx_pos_tbl {
-		int		 base;
-		int		 footer_bits;
-	}			*pos_tbl;
+	struct lzx_pos_tbl 	*pos_tbl;
 	/*
 	 * Bit stream reader.
 	 */
-	struct lzx_br {
-#define CACHE_TYPE		uint64_t
-#define CACHE_BITS		(8 * sizeof(CACHE_TYPE))
-	 	/* Cache buffer. */
-		CACHE_TYPE	 cache_buffer;
-		/* Indicates how many bits avail in cache_buffer. */
-		int		 cache_avail;
-		unsigned char	 odd;
-		char		 have_odd;
-	} br;
+	struct lzx_br		 br;
 
 	/*
 	 * Huffman coding.
 	 */
-	struct huffman {
-		int		 len_size;
-		int		 freq[17];
-		unsigned char	*bitlen;
-
-		/*
-		 * Use an index table. It's faster than searching a huffman
-		 * coding tree, which is a binary tree. But usage of a large
-		 * index table causes L1 cache read miss many times.
-		 */
-		int		 max_bits;
-		int		 tbl_bits;
-		int		 tree_used;
-		/* Direct access table. */
-		uint16_t	*tbl;
-	}			 at, lt, mt, pt;
+	struct huffman		 at;
+	struct huffman		 lt;
+	struct huffman		 mt;
+	struct huffman		 pt;
 
-	int			 loop;
+	uint16_t		 loop;
 	int			 error;
 };
 
-static const int slots[] = {
+static const size_t slots[] = {
 	30, 32, 34, 36, 38, 42, 50, 66, 98, 162, 290
 };
 #define SLOT_BASE	15
@@ -135,11 +236,11 @@
 
 struct lzx_stream {
 	const unsigned char	*next_in;
-	int64_t			 avail_in;
-	int64_t			 total_in;
+	size_t			 avail_in;
+	size_t			 total_in;
 	unsigned char		*next_out;
-	int64_t			 avail_out;
-	int64_t			 total_out;
+	size_t			 avail_out;
+	size_t			 total_out;
 	struct lzx_dec		*ds;
 };
 
@@ -179,6 +280,11 @@
 #define CFDATA_cbData		4
 #define CFDATA_cbUncomp		6
 
+/* Limits */
+#define MAX_UNCOMPRESS_SIZE	0x8000
+#define MAX_FILE_SIZE		(UINT16_MAX * MAX_UNCOMPRESS_SIZE)
+#define MAX_E8_TRANSLATION	(0x8000 * MAX_UNCOMPRESS_SIZE)
+
 static const char * const compression_name[] = {
 	"NONE",
 	"MSZIP",
@@ -240,8 +346,6 @@
 };
 
 struct cfheader {
-	/* Total bytes of all file size in a Cabinet. */
-	uint32_t		 total_bytes;
 	uint32_t		 files_offset;
 	uint16_t		 folder_count;
 	uint16_t		 file_count;
@@ -249,7 +353,6 @@
 #define PREV_CABINET	0x0001
 #define NEXT_CABINET	0x0002
 #define RESERVE_PRESENT	0x0004
-	uint16_t		 setid;
 	uint16_t		 cabinet;
 	/* Version number. */
 	unsigned char		 major;
@@ -268,8 +371,6 @@
 	int64_t			 entry_offset;
 	int64_t			 entry_bytes_remaining;
 	int64_t			 entry_unconsumed;
-	int64_t			 entry_compressed_bytes_read;
-	int64_t			 entry_uncompressed_bytes_read;
 	struct cffolder		*entry_cffolder;
 	struct cffile		*entry_cffile;
 	struct cfdata		*entry_cfdata;
@@ -314,7 +415,7 @@
 static int	archive_read_format_cab_cleanup(struct archive_read *);
 
 static int	cab_skip_sfx(struct archive_read *);
-static time_t	cab_dos_time(const unsigned char *);
+static time_t	cab_dos_time(const char *);
 static int	cab_read_data(struct archive_read *, const void **,
 		    size_t *, int64_t *);
 static int	cab_read_header(struct archive_read *);
@@ -335,15 +436,15 @@
 static int	lzx_read_blocks(struct lzx_stream *, int);
 static int	lzx_decode_blocks(struct lzx_stream *, int);
 static void	lzx_decode_free(struct lzx_stream *);
-static void	lzx_translation(struct lzx_stream *, void *, size_t, uint32_t);
+static void	lzx_translation(struct lzx_stream *, unsigned char *, size_t, int32_t);
 static void	lzx_cleanup_bitstream(struct lzx_stream *);
 static int	lzx_decode(struct lzx_stream *, int);
 static int	lzx_read_pre_tree(struct lzx_stream *);
-static int	lzx_read_bitlen(struct lzx_stream *, struct huffman *, int);
-static int	lzx_huffman_init(struct huffman *, size_t, int);
+static int	lzx_read_bitlen(struct lzx_stream *, struct huffman *, uint16_t);
+static int	lzx_huffman_init(struct huffman *, uint16_t, uint8_t);
 static void	lzx_huffman_free(struct huffman *);
 static int	lzx_make_huffman_table(struct huffman *);
-static inline int lzx_decode_huffman(struct huffman *, unsigned);
+static uint16_t	lzx_decode_huffman(struct huffman *, uint16_t);
 
 
 int
@@ -465,10 +566,9 @@
 archive_read_format_cab_options(struct archive_read *a,
     const char *key, const char *val)
 {
-	struct cab *cab;
+	struct cab *cab = a->format->data;
 	int ret = ARCHIVE_FAILED;
 
-	cab = (struct cab *)(a->format->data);
 	if (strcmp(key, "hdrcharset")  == 0) {
 		if (val == NULL || val[0] == 0)
 			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
@@ -540,19 +640,21 @@
 	return (ARCHIVE_FATAL);
 }
 
-static ssize_t
-cab_strnlen(const unsigned char *p, size_t maxlen)
+#ifdef HAVE_STRNLEN
+#define cab_strnlen(a,b) strnlen(a,b)
+#else
+static size_t
+cab_strnlen(const char *p, size_t maxlen)
 {
 	size_t i;
 
-	for (i = 0; i <= maxlen; i++) {
+	for (i = 0; i < maxlen; i++) {
 		if (p[i] == 0)
 			break;
 	}
-	if (i > maxlen)
-		return (-1);/* invalid */
-	return ((ssize_t)i);
+	return (i);
 }
+#endif
 
 /* Read up to max remaining bytes. */
 static const void *
@@ -622,11 +724,11 @@
 static int
 cab_read_header(struct archive_read *a)
 {
-	const unsigned char *p;
-	struct cab *cab;
+	struct cab *cab = a->format->data;
+	const char *p;
 	struct cfheader *hd;
-	size_t bytes, used;
-	ssize_t avail, len;
+	size_t bytes, len, maxlen, used;
+	ssize_t avail;
 	int64_t skip;
 	int err, i;
 	int cur_folder, prev_folder;
@@ -639,7 +741,6 @@
 	if ((p = __archive_read_ahead(a, 42, NULL)) == NULL)
 		return (truncated_error(a));
 
-	cab = (struct cab *)(a->format->data);
 	if (cab->found_header == 0 &&
 	    p[0] == 'M' && p[1] == 'Z') {
 		/* This is an executable?  Must be self-extracting... */
@@ -663,7 +764,6 @@
 		    "Couldn't find out CAB header");
 		return (ARCHIVE_FATAL);
 	}
-	hd->total_bytes = archive_le32dec(p + CFHEADER_cbCabinet);
 	hd->files_offset = archive_le32dec(p + CFHEADER_coffFiles);
 	hd->minor = p[CFHEADER_versionMinor];
 	hd->major = p[CFHEADER_versionMajor];
@@ -674,7 +774,6 @@
 	if (hd->file_count == 0)
 		goto invalid;
 	hd->flags = archive_le16dec(p + CFHEADER_flags);
-	hd->setid = archive_le16dec(p + CFHEADER_setID);
 	hd->cabinet = archive_le16dec(p + CFHEADER_iCabinet);
 	used = CFHEADER_iCabinet + 2;
 	if (hd->flags & RESERVE_PRESENT) {
@@ -693,7 +792,9 @@
 		if ((p = cab_read_ahead_remaining(a, used + 256,
 		    &avail)) == NULL || (size_t)avail <= used)
 			return (truncated_error(a));
-		if ((len = cab_strnlen(p + used, avail - used - 1)) <= 0) {
+		maxlen = avail - used;
+		len = cab_strnlen(p + used, maxlen);
+		if (len == 0 || len == maxlen) {
 			goto invalid;
 		}
 		used += len + 1;
@@ -701,7 +802,9 @@
 		if ((p = cab_read_ahead_remaining(a, used + 256,
 		    &avail)) == NULL || (size_t)avail <= used)
 			return (truncated_error(a));
-		if ((len = cab_strnlen(p + used, avail - used - 1)) < 0)
+		maxlen = avail - used;
+		len = cab_strnlen(p + used, maxlen);
+		if (len == maxlen)
 			goto invalid;
 		used += len + 1;
 	}
@@ -710,14 +813,18 @@
 		if ((p = cab_read_ahead_remaining(a, used + 256,
 		    &avail)) == NULL || (size_t)avail <= used)
 			return (truncated_error(a));
-		if ((len = cab_strnlen(p + used, avail - used - 1)) <= 0)
+		maxlen = avail - used;
+		len = cab_strnlen(p + used, maxlen);
+		if (len == 0 || len == maxlen)
 			goto invalid;
 		used += len + 1;
 		/* How many bytes are used for szDiskNext. */
 		if ((p = cab_read_ahead_remaining(a, used + 256,
 		    &avail)) == NULL || (size_t)avail <= used)
 			return (truncated_error(a));
-		if ((len = cab_strnlen(p + used, avail - used - 1)) < 0)
+		maxlen = avail - used;
+		len = cab_strnlen(p + used, maxlen);
+		if (len == maxlen)
 			goto invalid;
 		used += len + 1;
 	}
@@ -787,7 +894,8 @@
 		return (ARCHIVE_FATAL);
 	}
 	if (skip) {
-		__archive_read_consume(a, skip);
+		if (__archive_read_consume(a, skip) < 0)
+			return (truncated_error(a));
 		cab->cab_offset += skip;
 	}
 	/* Allocate memory for CFDATA */
@@ -812,7 +920,9 @@
 		cab->cab_offset += 16;
 		if ((p = cab_read_ahead_remaining(a, 256, &avail)) == NULL)
 			return (truncated_error(a));
-		if ((len = cab_strnlen(p, avail-1)) <= 0)
+		maxlen = avail;
+		len = cab_strnlen(p, maxlen);
+		if (len == 0 || len == maxlen)
 			goto invalid;
 
 		/* Copy a pathname.  */
@@ -824,10 +934,10 @@
 		/*
 		 * Sanity check if each data is acceptable.
 		 */
-		if (file->uncompressed_size > 0x7FFF8000)
+		if (file->uncompressed_size > MAX_FILE_SIZE)
 			goto invalid;/* Too large */
 		if ((int64_t)file->offset + (int64_t)file->uncompressed_size
-		    > ARCHIVE_LITERAL_LL(0x7FFF8000))
+		    > (int64_t)MAX_FILE_SIZE)
 			goto invalid;/* Too large */
 		switch (file->folder) {
 		case iFoldCONTINUED_TO_NEXT:
@@ -893,14 +1003,13 @@
 archive_read_format_cab_read_header(struct archive_read *a,
     struct archive_entry *entry)
 {
-	struct cab *cab;
+	struct cab *cab = a->format->data;
 	struct cfheader *hd;
 	struct cffolder *prev_folder;
 	struct cffile *file;
 	struct archive_string_conv *sconv;
 	int err = ARCHIVE_OK, r;
-	
-	cab = (struct cab *)(a->format->data);
+
 	if (cab->found_header == 0) {
 		err = cab_read_header(a); 
 		if (err < ARCHIVE_WARN)
@@ -918,8 +1027,6 @@
 
 	cab->end_of_entry = 0;
 	cab->end_of_entry_cleanup = 0;
-	cab->entry_compressed_bytes_read = 0;
-	cab->entry_uncompressed_bytes_read = 0;
 	cab->entry_unconsumed = 0;
 	cab->entry_cffile = file;
 
@@ -1017,7 +1124,7 @@
 archive_read_format_cab_read_data(struct archive_read *a,
     const void **buff, size_t *size, int64_t *offset)
 {
-	struct cab *cab = (struct cab *)(a->format->data);
+	struct cab *cab = a->format->data;
 	int r;
 
 	switch (cab->entry_cffile->folder) {
@@ -1117,7 +1224,7 @@
 static void
 cab_checksum_update(struct archive_read *a, size_t bytes)
 {
-	struct cab *cab = (struct cab *)(a->format->data);
+	struct cab *cab = a->format->data;
 	struct cfdata *cfdata = cab->entry_cfdata;
 	const unsigned char *p;
 	size_t sumbytes;
@@ -1157,7 +1264,7 @@
 static int
 cab_checksum_finish(struct archive_read *a)
 {
-	struct cab *cab = (struct cab *)(a->format->data);
+	struct cab *cab = a->format->data;
 	struct cfdata *cfdata = cab->entry_cfdata;
 	int l;
 
@@ -1202,7 +1309,7 @@
 static int
 cab_next_cfdata(struct archive_read *a)
 {
-	struct cab *cab = (struct cab *)(a->format->data);
+	struct cab *cab = a->format->data;
 	struct cfdata *cfdata = cab->entry_cfdata;
 
 	/* There are remaining bytes in current CFDATA, use it first. */
@@ -1280,9 +1387,9 @@
 		 * Sanity check if data size is acceptable.
 		 */
 		if (cfdata->compressed_size == 0 ||
-		    cfdata->compressed_size > (0x8000+6144))
+		    cfdata->compressed_size > (MAX_UNCOMPRESS_SIZE + 6144))
 			goto invalid;
-		if (cfdata->uncompressed_size > 0x8000)
+		if (cfdata->uncompressed_size > MAX_UNCOMPRESS_SIZE)
 			goto invalid;
 		if (cfdata->uncompressed_size == 0) {
 			switch (cab->entry_cffile->folder) {
@@ -1295,10 +1402,10 @@
 			}
 		}
 		/* If CFDATA is not last in a folder, an uncompressed
-		 * size must be 0x8000(32KBi) */
+		 * size must be 0x8000 (32 KiB) */
 		if ((cab->entry_cffolder->cfdata_index <
 		     cab->entry_cffolder->cfdata_count) &&
-		       cfdata->uncompressed_size != 0x8000)
+		       cfdata->uncompressed_size != MAX_UNCOMPRESS_SIZE)
 			goto invalid;
 
 		/* A compressed data size and an uncompressed data size must
@@ -1339,10 +1446,6 @@
 	}
 	return (ARCHIVE_OK);
 invalid:
-	cfdata->compressed_size = 0;
-	cfdata->compressed_bytes_remaining = 0;
-	cfdata->uncompressed_size = 0;
-	cfdata->uncompressed_bytes_remaining = 0;
 	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 	    "Invalid CFDATA");
 	return (ARCHIVE_FATAL);
@@ -1354,7 +1457,7 @@
 static const void *
 cab_read_ahead_cfdata(struct archive_read *a, ssize_t *avail)
 {
-	struct cab *cab = (struct cab *)(a->format->data);
+	struct cab *cab = a->format->data;
 	int err;
 
 	err = cab_next_cfdata(a);
@@ -1385,7 +1488,7 @@
 static const void *
 cab_read_ahead_cfdata_none(struct archive_read *a, ssize_t *avail)
 {
-	struct cab *cab = (struct cab *)(a->format->data);
+	struct cab *cab = a->format->data;
 	struct cfdata *cfdata;
 	const void *d;
 
@@ -1417,7 +1520,7 @@
 static const void *
 cab_read_ahead_cfdata_deflate(struct archive_read *a, ssize_t *avail)
 {
-	struct cab *cab = (struct cab *)(a->format->data);
+	struct cab *cab = a->format->data;
 	struct cfdata *cfdata;
 	const void *d;
 	int r, mszip;
@@ -1427,7 +1530,7 @@
 	cfdata = cab->entry_cfdata;
 	/* If the buffer hasn't been allocated, allocate it now. */
 	if (cab->uncompressed_buffer == NULL) {
-		cab->uncompressed_buffer_size = 0x8000;
+		cab->uncompressed_buffer_size = MAX_UNCOMPRESS_SIZE;
 		cab->uncompressed_buffer
 		    = malloc(cab->uncompressed_buffer_size);
 		if (cab->uncompressed_buffer == NULL) {
@@ -1485,14 +1588,6 @@
 		    cab->uncompressed_buffer + cab->stream.total_out;
 		cab->stream.avail_out =
 		    cfdata->uncompressed_size - cab->stream.total_out;
-		if ((size_t)cfdata->uncompressed_size >
-		    cab->uncompressed_buffer_size) {
-			archive_set_error(&a->archive,
-			    ARCHIVE_ERRNO_FILE_FORMAT,
-			    "Invalid CFDATA uncompressed size");
-			*avail = ARCHIVE_FATAL;
-			return (NULL);
-		}
 
 		d = __archive_read_ahead(a, 1, &bytes_avail);
 		if (bytes_avail <= 0) {
@@ -1653,7 +1748,7 @@
 static const void *
 cab_read_ahead_cfdata_lzx(struct archive_read *a, ssize_t *avail)
 {
-	struct cab *cab = (struct cab *)(a->format->data);
+	struct cab *cab = a->format->data;
 	struct cfdata *cfdata;
 	const void *d;
 	int r;
@@ -1662,7 +1757,7 @@
 	cfdata = cab->entry_cfdata;
 	/* If the buffer hasn't been allocated, allocate it now. */
 	if (cab->uncompressed_buffer == NULL) {
-		cab->uncompressed_buffer_size = 0x8000;
+		cab->uncompressed_buffer_size = MAX_UNCOMPRESS_SIZE;
 		cab->uncompressed_buffer
 		    = malloc(cab->uncompressed_buffer_size);
 		if (cab->uncompressed_buffer == NULL) {
@@ -1703,13 +1798,6 @@
 		    cab->uncompressed_buffer + cab->xstrm.total_out;
 		cab->xstrm.avail_out =
 		    cfdata->uncompressed_size - cab->xstrm.total_out;
-		
-		if ((size_t)cfdata->uncompressed_size > cab->uncompressed_buffer_size) {
-			archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
-				"Invalid CFDATA uncompressed size");
-			*avail = ARCHIVE_FATAL;
-			return (NULL);
-		}
 
 		d = __archive_read_ahead(a, 1, &bytes_avail);
 		if (d == NULL) {
@@ -1769,7 +1857,7 @@
 	 */
 	lzx_translation(&cab->xstrm, cab->uncompressed_buffer,
 	    cfdata->uncompressed_size,
-	    (cab->entry_cffolder->cfdata_index-1) * 0x8000);
+	    (cab->entry_cffolder->cfdata_index - 1) * MAX_UNCOMPRESS_SIZE);
 
 	d = cab->uncompressed_buffer + cfdata->read_offset;
 	*avail = uavail - cfdata->read_offset;
@@ -1790,7 +1878,7 @@
 static int64_t
 cab_consume_cfdata(struct archive_read *a, int64_t consumed_bytes)
 {
-	struct cab *cab = (struct cab *)(a->format->data);
+	struct cab *cab = a->format->data;
 	struct cfdata *cfdata;
 	int64_t cbytes, rbytes;
 	int err;
@@ -1883,7 +1971,7 @@
 static int64_t
 cab_minimum_consume_cfdata(struct archive_read *a, int64_t consumed_bytes)
 {
-	struct cab *cab = (struct cab *)(a->format->data);
+	struct cab *cab = a->format->data;
 	struct cfdata *cfdata;
 	int64_t cbytes, rbytes;
 	int err;
@@ -1940,7 +2028,7 @@
 cab_read_data(struct archive_read *a, const void **buff,
     size_t *size, int64_t *offset)
 {
-	struct cab *cab = (struct cab *)(a->format->data);
+	struct cab *cab = a->format->data;
 	ssize_t bytes_avail;
 
 	if (cab->entry_bytes_remaining == 0) {
@@ -1986,12 +2074,10 @@
 static int
 archive_read_format_cab_read_data_skip(struct archive_read *a)
 {
-	struct cab *cab;
+	struct cab *cab = a->format->data;
 	int64_t bytes_skipped;
 	int r;
 
-	cab = (struct cab *)(a->format->data);
-
 	if (cab->end_of_archive)
 		return (ARCHIVE_EOF);
 
@@ -2041,9 +2127,9 @@
 static int
 archive_read_format_cab_cleanup(struct archive_read *a)
 {
-	struct cab *cab = (struct cab *)(a->format->data);
+	struct cab *cab = a->format->data;
 	struct cfheader *hd = &cab->cfheader;
-	int i;
+	uint16_t i;
 
 	if (hd->folder_array != NULL) {
 		for (i = 0; i < hd->folder_count; i++)
@@ -2063,13 +2149,13 @@
 	archive_wstring_free(&cab->ws);
 	free(cab->uncompressed_buffer);
 	free(cab);
-	(a->format->data) = NULL;
+	a->format->data = NULL;
 	return (ARCHIVE_OK);
 }
 
 /* Convert an MSDOS-style date/time into Unix-style time. */
 static time_t
-cab_dos_time(const unsigned char *p)
+cab_dos_time(const char *p)
 {
 	int msTime, msDate;
 	struct tm ts;
@@ -2105,10 +2191,11 @@
 static int
 lzx_decode_init(struct lzx_stream *strm, int w_bits)
 {
-	struct lzx_dec *ds;
-	int slot, w_size, w_slot;
-	int base, footer;
 	int base_inc[18];
+	struct lzx_dec *ds;
+	uint32_t base;
+	uint16_t slot, w_size, w_slot;
+	uint8_t footer;
 
 	if (strm->ds == NULL) {
 		strm->ds = calloc(1, sizeof(*strm->ds));
@@ -2118,7 +2205,7 @@
 	ds = strm->ds;
 	ds->error = ARCHIVE_FAILED;
 
-	/* Allow bits from 15(32KBi) up to 21(2MBi) */
+	/* Allow bits from 15 (32 KiB) up to 21 (2 MiB) */
 	if (w_bits < SLOT_BASE || w_bits > SLOT_MAX)
 		return (ARCHIVE_FAILED);
 
@@ -2130,7 +2217,7 @@
 	w_size = ds->w_size;
 	w_slot = slots[w_bits - SLOT_BASE];
 	ds->w_size = 1U << w_bits;
-	ds->w_mask = ds->w_size -1;
+	ds->w_mask = ds->w_size - 1;
 	if (ds->w_buff == NULL || w_size != ds->w_size) {
 		free(ds->w_buff);
 		ds->w_buff = malloc(ds->w_size);
@@ -2152,18 +2239,20 @@
 		else
 			base += base_inc[footer];
 		if (footer < 17) {
-			footer = -2;
+			footer = 0;
 			for (n = base; n; n >>= 1)
 				footer++;
-			if (footer <= 0)
+			if (footer <= 2)
 				footer = 0;
+			else
+				footer -= 2;
 		}
 		ds->pos_tbl[slot].base = base;
 		ds->pos_tbl[slot].footer_bits = footer;
 	}
 
 	ds->w_pos = 0;
-	ds->state = 0;
+	ds->state = ST_RD_TRANSLATION;
 	ds->br.cache_buffer = 0;
 	ds->br.cache_avail = 0;
 	ds->r0 = ds->r1 = ds->r2 = 1;
@@ -2177,7 +2266,7 @@
 		return (ARCHIVE_FATAL);
 
 	/* Initialize Main tree. */
-	if (lzx_huffman_init(&(ds->mt), 256+(w_slot<<3), 16)
+	if (lzx_huffman_init(&(ds->mt), 256 + (w_slot << 3), 16)
 	    != ARCHIVE_OK)
 		return (ARCHIVE_FATAL);
 
@@ -2213,29 +2302,36 @@
  * E8 Call Translation reversal.
  */
 static void
-lzx_translation(struct lzx_stream *strm, void *p, size_t size, uint32_t offset)
+lzx_translation(struct lzx_stream *strm, unsigned char *buffer, size_t size,
+    int32_t offset)
 {
 	struct lzx_dec *ds = strm->ds;
-	unsigned char *b, *end;
+	unsigned char *p, *end;
 
-	if (!ds->translation || size <= 10)
+	if (!ds->translation || offset >= MAX_E8_TRANSLATION || size <= 10)
 		return;
-	b = p;
-	end = b + size - 10;
-	while (b < end && (b = memchr(b, 0xE8, end - b)) != NULL) {
-		size_t i = b - (unsigned char *)p;
-		int32_t cp, displacement, value;
 
-		cp = (int32_t)(offset + (uint32_t)i);
-		value = archive_le32dec(&b[1]);
-		if (value >= -cp && value < (int32_t)ds->translation_size) {
-			if (value >= 0)
-				displacement = value - cp;
+	p = buffer;
+	end = buffer + size - 10;
+
+	while (p < end && (p = memchr(p, 0xE8, end - p)) != NULL) {
+		int32_t address, position;
+
+		address = archive_le32dec(p + 1);
+		position = offset + (p - buffer);
+
+		if (address >= -position && address < ds->translation_size) {
+			uint32_t relative;
+
+			if (address >= 0)
+				relative = address - position;
 			else
-				displacement = value + ds->translation_size;
-			archive_le32enc(&b[1], (uint32_t)displacement);
+				relative = address + ds->translation_size;
+
+			archive_le32enc(p + 1, relative);
 		}
-		b += 5;
+
+		p += 5;
 	}
 }
 
@@ -2257,7 +2353,7 @@
  *  False : we met that strm->next_in is empty, we have to get following
  *          bytes. */
 #define lzx_br_read_ahead_0(strm, br, n)	\
-	(lzx_br_has((br), (n)) || lzx_br_fillup(strm, br))
+	(lzx_br_has((br), (n)) || lzx_br_fillup(strm, br) == ARCHIVE_OK)
 /*  True  : the cache buffer has some bits as much as we need.
  *  False : there are no enough bits in the cache buffer to be used,
  *          we have to get following bytes if we could. */
@@ -2286,8 +2382,8 @@
  * Shift away used bits in the cache data and fill it up with following bits.
  * Call this when cache buffer does not have enough bits you need.
  *
- * Returns 1 if the cache buffer is full.
- * Returns 0 if the cache buffer is not full; input buffer is empty.
+ * Returns ARCHIVE_OK if the cache buffer is full.
+ * Returns ARCHIVE_EOF if the cache buffer is not full; input buffer is empty.
  */
 static int
 lzx_br_fillup(struct lzx_stream *strm, struct lzx_br *br)
@@ -2295,7 +2391,7 @@
 /*
  * x86 processor family can read misaligned data without an access error.
  */
-	int n = CACHE_BITS - br->cache_avail;
+	ssize_t n = CACHE_BITS - br->cache_avail;
 
 	for (;;) {
 		switch (n >> 4) {
@@ -2313,7 +2409,7 @@
 				strm->next_in += 8;
 				strm->avail_in -= 8;
 				br->cache_avail += 8 * 8;
-				return (1);
+				return (ARCHIVE_OK);
 			}
 			break;
 		case 3:
@@ -2329,13 +2425,13 @@
 				strm->next_in += 6;
 				strm->avail_in -= 6;
 				br->cache_avail += 6 * 8;
-				return (1);
+				return (ARCHIVE_OK);
 			}
 			break;
 		case 0:
 			/* We have enough compressed data in
 			 * the cache buffer.*/
-			return (1);
+			return (ARCHIVE_EOF);
 		default:
 			break;
 		}
@@ -2347,7 +2443,7 @@
 				strm->avail_in--;
 				br->have_odd = 1;
 			}
-			return (0);
+			return (ARCHIVE_EOF);
 		}
 		br->cache_buffer =
 		   (br->cache_buffer << 16) |
@@ -2362,7 +2458,7 @@
 static void
 lzx_br_fixup(struct lzx_stream *strm, struct lzx_br *br)
 {
-	int n = CACHE_BITS - br->cache_avail;
+	ssize_t n = CACHE_BITS - br->cache_avail;
 
 	if (br->have_odd && n >= 16 && strm->avail_in > 0) {
 		br->cache_buffer =
@@ -2391,35 +2487,11 @@
  * 3. Returns ARCHIVE_FAILED if an error occurred; compressed data
  *    is broken or you do not set 'last' flag properly.
  */
-#define ST_RD_TRANSLATION	0
-#define ST_RD_TRANSLATION_SIZE	1
-#define ST_RD_BLOCK_TYPE	2
-#define ST_RD_BLOCK_SIZE	3
-#define ST_RD_ALIGNMENT		4
-#define ST_RD_R0		5
-#define ST_RD_R1		6
-#define ST_RD_R2		7
-#define ST_COPY_UNCOMP1		8
-#define ST_COPY_UNCOMP2		9
-#define ST_RD_ALIGNED_OFFSET	10
-#define ST_RD_VERBATIM		11
-#define ST_RD_PRE_MAIN_TREE_256	12
-#define ST_MAIN_TREE_256	13
-#define ST_RD_PRE_MAIN_TREE_REM	14
-#define ST_MAIN_TREE_REM	15
-#define ST_RD_PRE_LENGTH_TREE	16
-#define ST_LENGTH_TREE		17
-#define ST_MAIN			18
-#define ST_LENGTH		19
-#define ST_OFFSET		20
-#define ST_REAL_POS		21
-#define ST_COPY			22
-
 static int
 lzx_decode(struct lzx_stream *strm, int last)
 {
 	struct lzx_dec *ds = strm->ds;
-	int64_t avail_in;
+	size_t avail_in;
 	int r;
 
 	if (ds->error)
@@ -2431,7 +2503,8 @@
 		if (ds->state < ST_MAIN)
 			r = lzx_read_blocks(strm, last);
 		else {
-			int64_t bytes_written = strm->avail_out;
+			size_t bytes_written = strm->avail_out;
+
 			r = lzx_decode_blocks(strm, last);
 			bytes_written -= strm->avail_out;
 			strm->next_out += bytes_written;
@@ -2447,7 +2520,8 @@
 {
 	struct lzx_dec *ds = strm->ds;
 	struct lzx_br *br = &(ds->br);
-	int i, r;
+	int r;
+	uint16_t i;
 
 	for (;;) {
 		switch (ds->state) {
@@ -2463,16 +2537,21 @@
 			/* FALL THROUGH */
 		case ST_RD_TRANSLATION_SIZE:
 			if (ds->translation) {
+				uint32_t v;
+
 				if (!lzx_br_read_ahead(strm, br, 32)) {
 					ds->state = ST_RD_TRANSLATION_SIZE;
 					if (last)
 						goto failed;
 					return (ARCHIVE_OK);
 				}
-				ds->translation_size = lzx_br_bits(br, 16);
+				v = lzx_br_bits(br, 16);
 				lzx_br_consume(br, 16);
-				ds->translation_size <<= 16;
-				ds->translation_size |= lzx_br_bits(br, 16);
+				v <<= 16;
+				v |= lzx_br_bits(br, 16);
+				if (v > MAX_FILE_SIZE)
+					goto failed;
+				ds->translation_size = (int32_t)v;
 				lzx_br_consume(br, 16);
 			}
 			/* FALL THROUGH */
@@ -2579,17 +2658,17 @@
 				ds->rbytes_avail = 0;
 				if (ds->state == ST_RD_R0) {
 					ds->r0 = archive_le32dec(ds->rbytes);
-					if (ds->r0 < 0)
+					if (ds->r0 > (size_t)INT32_MAX)
 						goto failed;
 					ds->state = ST_RD_R1;
 				} else if (ds->state == ST_RD_R1) {
 					ds->r1 = archive_le32dec(ds->rbytes);
-					if (ds->r1 < 0)
+					if (ds->r1 > (size_t)INT32_MAX)
 						goto failed;
 					ds->state = ST_RD_R2;
 				} else if (ds->state == ST_RD_R2) {
 					ds->r2 = archive_le32dec(ds->rbytes);
-					if (ds->r2 < 0)
+					if (ds->r2 > (size_t)INT32_MAX)
 						goto failed;
 					/* We've gotten all repeated offsets. */
 					ds->state = ST_COPY_UNCOMP1;
@@ -2598,10 +2677,10 @@
 			/* FALL THROUGH */
 		case ST_COPY_UNCOMP1:
 			/*
-			 * Copy bytes form next_in to next_out directly.
+			 * Copy bytes from next_in to next_out directly.
 			 */
 			while (ds->block_bytes_avail) {
-				int l;
+				size_t l;
 
 				if (strm->avail_out <= 0)
 					/* Output buffer is empty. */
@@ -2612,13 +2691,13 @@
 						goto failed;
 					return (ARCHIVE_OK);
 				}
-				l = (int)ds->block_bytes_avail;
+				l = ds->block_bytes_avail;
 				if (l > ds->w_size - ds->w_pos)
 					l = ds->w_size - ds->w_pos;
 				if (l > strm->avail_out)
-					l = (int)strm->avail_out;
+					l = strm->avail_out;
 				if (l > strm->avail_in)
-					l = (int)strm->avail_in;
+					l = strm->avail_in;
 				memcpy(strm->next_out, strm->next_in, l);
 				memcpy(&(ds->w_buff[ds->w_pos]),
 				    strm->next_in, l);
@@ -2652,19 +2731,19 @@
 			/*
 			 * Read Aligned offset tree.
 			 */
-			if (!lzx_br_read_ahead(strm, br, 3 * ds->at.len_size)) {
+			if (!lzx_br_read_ahead(strm, br, 3 * ds->at.symbol_count)) {
 				ds->state = ST_RD_ALIGNED_OFFSET;
 				if (last)
 					goto failed;
 				return (ARCHIVE_OK);
 			}
 			memset(ds->at.freq, 0, sizeof(ds->at.freq));
-			for (i = 0; i < ds->at.len_size; i++) {
+			for (i = 0; i < ds->at.symbol_count; i++) {
 				ds->at.bitlen[i] = lzx_br_bits(br, 3);
 				ds->at.freq[ds->at.bitlen[i]]++;
 				lzx_br_consume(br, 3);
 			}
-			if (!lzx_make_huffman_table(&ds->at))
+			if (lzx_make_huffman_table(&ds->at) < 0)
 				goto failed;
 			/* FALL THROUGH */
 		case ST_RD_VERBATIM:
@@ -2674,13 +2753,13 @@
 			/*
 			 * Read Pre-tree for first 256 elements of main tree.
 			 */
-			if (!lzx_read_pre_tree(strm)) {
+			if (lzx_read_pre_tree(strm) < 0) {
 				ds->state = ST_RD_PRE_MAIN_TREE_256;
 				if (last)
 					goto failed;
 				return (ARCHIVE_OK);
 			}
-			if (!lzx_make_huffman_table(&ds->pt))
+			if (lzx_make_huffman_table(&ds->pt) < 0)
 				goto failed;
 			ds->loop = 0;
 			/* FALL THROUGH */
@@ -2689,27 +2768,26 @@
 			 * Get path lengths of first 256 elements of main tree.
 			 */
 			r = lzx_read_bitlen(strm, &ds->mt, 256);
-			if (r < 0)
-				goto failed;
-			else if (!r) {
+			if (r == ARCHIVE_EOF) {
 				ds->state = ST_MAIN_TREE_256;
 				if (last)
 					goto failed;
 				return (ARCHIVE_OK);
-			}
+			} else if (r < 0)
+				goto failed;
 			ds->loop = 0;
 			/* FALL THROUGH */
 		case ST_RD_PRE_MAIN_TREE_REM:
 			/*
 			 * Read Pre-tree for remaining elements of main tree.
 			 */
-			if (!lzx_read_pre_tree(strm)) {
+			if (lzx_read_pre_tree(strm) < 0) {
 				ds->state = ST_RD_PRE_MAIN_TREE_REM;
 				if (last)
 					goto failed;
 				return (ARCHIVE_OK);
 			}
-			if (!lzx_make_huffman_table(&ds->pt))
+			if (lzx_make_huffman_table(&ds->pt) < 0)
 				goto failed;
 			ds->loop = 256;
 			/* FALL THROUGH */
@@ -2717,30 +2795,29 @@
 			/*
 			 * Get path lengths of remaining elements of main tree.
 			 */
-			r = lzx_read_bitlen(strm, &ds->mt, -1);
-			if (r < 0)
-				goto failed;
-			else if (!r) {
+			r = lzx_read_bitlen(strm, &ds->mt, 0);
+			if (r == ARCHIVE_EOF) {
 				ds->state = ST_MAIN_TREE_REM;
 				if (last)
 					goto failed;
 				return (ARCHIVE_OK);
-			}
-			if (!lzx_make_huffman_table(&ds->mt))
+			} else if (r < 0)
 				goto failed;
+			if (lzx_make_huffman_table(&ds->mt) < 0)
+				goto failed;
 			ds->loop = 0;
 			/* FALL THROUGH */
 		case ST_RD_PRE_LENGTH_TREE:
 			/*
 			 * Read Pre-tree for remaining elements of main tree.
 			 */
-			if (!lzx_read_pre_tree(strm)) {
+			if (lzx_read_pre_tree(strm) < 0) {
 				ds->state = ST_RD_PRE_LENGTH_TREE;
 				if (last)
 					goto failed;
 				return (ARCHIVE_OK);
 			}
-			if (!lzx_make_huffman_table(&ds->pt))
+			if (lzx_make_huffman_table(&ds->pt) < 0)
 				goto failed;
 			ds->loop = 0;
 			/* FALL THROUGH */
@@ -2748,17 +2825,16 @@
 			/*
 			 * Get path lengths of remaining elements of main tree.
 			 */
-			r = lzx_read_bitlen(strm, &ds->lt, -1);
-			if (r < 0)
-				goto failed;
-			else if (!r) {
+			r = lzx_read_bitlen(strm, &ds->lt, 0);
+			if (r == ARCHIVE_EOF) {
 				ds->state = ST_LENGTH_TREE;
 				if (last)
 					goto failed;
 				return (ARCHIVE_OK);
-			}
-			if (!lzx_make_huffman_table(&ds->lt))
+			} else if (r < 0)
 				goto failed;
+			if (lzx_make_huffman_table(&ds->lt) < 0)
+				goto failed;
 			ds->state = ST_MAIN;
 			return (100);
 		}
@@ -2776,22 +2852,23 @@
 	const struct lzx_pos_tbl *pos_tbl = ds->pos_tbl;
 	unsigned char *noutp = strm->next_out;
 	unsigned char *endp = noutp + strm->avail_out;
-	unsigned char *w_buff = ds->w_buff;
-	unsigned char *at_bitlen = at->bitlen;
-	unsigned char *lt_bitlen = lt->bitlen;
-	unsigned char *mt_bitlen = mt->bitlen;
+	uint8_t *w_buff = ds->w_buff;
+	uint8_t *at_bitlen = at->bitlen;
+	uint8_t *lt_bitlen = lt->bitlen;
+	uint8_t *mt_bitlen = mt->bitlen;
 	size_t block_bytes_avail = ds->block_bytes_avail;
-	int at_max_bits = at->max_bits;
-	int lt_max_bits = lt->max_bits;
-	int mt_max_bits = mt->max_bits;
-	int c, copy_len = ds->copy_len, copy_pos = ds->copy_pos;
-	int w_pos = ds->w_pos, w_mask = ds->w_mask, w_size = ds->w_size;
-	int length_header = ds->length_header;
-	int offset_bits = ds->offset_bits;
-	int position_slot = ds->position_slot;
-	int r0 = ds->r0, r1 = ds->r1, r2 = ds->r2;
+	uint8_t at_lookup_bits = at->lookup_bits;
+	uint8_t lt_lookup_bits = lt->lookup_bits;
+	uint8_t mt_lookup_bits = mt->lookup_bits;
+	size_t copy_len = ds->copy_len, copy_pos = ds->copy_pos;
+	size_t w_pos = ds->w_pos, w_mask = ds->w_mask, w_size = ds->w_size;
+	uint8_t length_header = ds->length_header;
+	uint8_t offset_bits = ds->offset_bits;
+	uint16_t position_slot = ds->position_slot;
+	size_t r0 = ds->r0, r1 = ds->r1, r2 = ds->r2;
 	int state = ds->state;
-	char block_type = ds->block_type;
+	uint16_t c;
+	uint8_t block_type = ds->block_type;
 
 	for (;;) {
 		switch (state) {
@@ -2817,26 +2894,26 @@
 					goto next_data;
 
 				if (!lzx_br_read_ahead(strm, &bre,
-				    mt_max_bits)) {
+				    mt_lookup_bits)) {
 					if (!last)
 						goto next_data;
 					/* Remaining bits are less than
-					 * maximum bits(mt.max_bits) but maybe
-					 * it still remains as much as we need,
-					 * so we should try to use it with
-					 * dummy bits. */
+					 * maximum bits (mt.lookup_bits) but
+					 * maybe it still remains as much as we
+					 * need, so we should try to use it
+					 * with dummy bits. */
 					c = lzx_decode_huffman(mt,
 					      lzx_br_bits_forced(
-				 	        &bre, mt_max_bits));
-					lzx_br_consume(&bre, mt_bitlen[c]);
-					if (!lzx_br_has(&bre, 0))
+					        &bre, mt_lookup_bits));
+					if (!lzx_br_has(&bre, mt_bitlen[c]))
 						goto failed;/* Over read. */
+					lzx_br_consume(&bre, mt_bitlen[c]);
 				} else {
 					c = lzx_decode_huffman(mt,
-					      lzx_br_bits(&bre, mt_max_bits));
+					      lzx_br_bits(&bre, mt_lookup_bits));
 					lzx_br_consume(&bre, mt_bitlen[c]);
 				}
-				if ((unsigned int)c > UCHAR_MAX)
+				if (c > UCHAR_MAX)
 					break;
 				/*
 				 * 'c' is exactly literal code.
@@ -2862,26 +2939,26 @@
 			 */
 			if (length_header == 7) {
 				if (!lzx_br_read_ahead(strm, &bre,
-				    lt_max_bits)) {
+				    lt_lookup_bits)) {
 					if (!last) {
 						state = ST_LENGTH;
 						goto next_data;
 					}
 					c = lzx_decode_huffman(lt,
 					      lzx_br_bits_forced(
-					        &bre, lt_max_bits));
-					lzx_br_consume(&bre, lt_bitlen[c]);
-					if (!lzx_br_has(&bre, 0))
+					        &bre, lt_lookup_bits));
+					if (!lzx_br_has(&bre, lt_bitlen[c]))
 						goto failed;/* Over read. */
+					lzx_br_consume(&bre, lt_bitlen[c]);
 				} else {
 					c = lzx_decode_huffman(lt,
-					    lzx_br_bits(&bre, lt_max_bits));
+					    lzx_br_bits(&bre, lt_lookup_bits));
 					lzx_br_consume(&bre, lt_bitlen[c]);
 				}
 				copy_len = c + 7 + 2;
 			} else
 				copy_len = length_header + 2;
-			if ((size_t)copy_len > block_bytes_avail)
+			if (copy_len > block_bytes_avail)
 				goto failed;
 			/*
 			 * Get an offset.
@@ -2918,7 +2995,7 @@
 			 */
 			if (block_type == ALIGNED_OFFSET_BLOCK &&
 			    offset_bits >= 3) {
-				int offbits = offset_bits - 3;
+				unsigned offbits = offset_bits - 3;
 
 				if (!lzx_br_read_ahead(strm, &bre, offbits)) {
 					state = ST_OFFSET;
@@ -2930,7 +3007,7 @@
 
 				/* Get an aligned number. */
 				if (!lzx_br_read_ahead(strm, &bre,
-				    offbits + at_max_bits)) {
+				    offbits + at_lookup_bits)) {
 					if (!last) {
 						state = ST_OFFSET;
 						goto next_data;
@@ -2938,14 +3015,14 @@
 					lzx_br_consume(&bre, offbits);
 					c = lzx_decode_huffman(at,
 					      lzx_br_bits_forced(&bre,
-					        at_max_bits));
-					lzx_br_consume(&bre, at_bitlen[c]);
-					if (!lzx_br_has(&bre, 0))
+					        at_lookup_bits));
+					if (!lzx_br_has(&bre, at_bitlen[c]))
 						goto failed;/* Over read. */
+					lzx_br_consume(&bre, at_bitlen[c]);
 				} else {
 					lzx_br_consume(&bre, offbits);
 					c = lzx_decode_huffman(at,
-					      lzx_br_bits(&bre, at_max_bits));
+					      lzx_br_bits(&bre, at_lookup_bits));
 					lzx_br_consume(&bre, at_bitlen[c]);
 				}
 				/* Add an aligned number. */
@@ -2961,7 +3038,7 @@
 				copy_pos = lzx_br_bits(&bre, offset_bits);
 				lzx_br_consume(&bre, offset_bits);
 			}
-			copy_pos += pos_tbl[position_slot].base -2;
+			copy_pos += pos_tbl[position_slot].base - 2;
 
 			/* Update repeated offset LRU queue. */
 			r2 = r1;
@@ -2980,8 +3057,8 @@
 			 * into the output buffer.
 			 */
 			for (;;) {
-				const unsigned char *s;
-				int l;
+				const uint8_t *s;
+				size_t l;
 
 				l = copy_len;
 				if (copy_pos > w_pos) {
@@ -2992,15 +3069,15 @@
 						l = w_size - w_pos;
 				}
 				if (noutp + l >= endp)
-					l = (int)(endp - noutp);
+					l = endp - noutp;
 				s = w_buff + copy_pos;
 				if (l >= 8 && ((copy_pos + l < w_pos)
 				  || (w_pos + l < copy_pos))) {
 					memcpy(w_buff + w_pos, s, l);
 					memcpy(noutp, s, l);
 				} else {
-					unsigned char *d;
-					int li;
+					uint8_t *d;
+					size_t li;
 
 					d = w_buff + w_pos;
 					for (li = 0; li < l; li++)
@@ -3046,122 +3123,124 @@
 {
 	struct lzx_dec *ds = strm->ds;
 	struct lzx_br *br = &(ds->br);
-	int i;
+	uint16_t i;
 
 	if (ds->loop == 0)
 		memset(ds->pt.freq, 0, sizeof(ds->pt.freq));
-	for (i = ds->loop; i < ds->pt.len_size; i++) {
+	for (i = ds->loop; i < ds->pt.symbol_count; i++) {
 		if (!lzx_br_read_ahead(strm, br, 4)) {
 			ds->loop = i;
-			return (0);
+			return (ARCHIVE_EOF);
 		}
 		ds->pt.bitlen[i] = lzx_br_bits(br, 4);
 		ds->pt.freq[ds->pt.bitlen[i]]++;
 		lzx_br_consume(br, 4);
 	}
 	ds->loop = i;
-	return (1);
+	return (ARCHIVE_OK);
 }
 
 /*
  * Read a bunch of bit-lengths from pre-tree.
  */
 static int
-lzx_read_bitlen(struct lzx_stream *strm, struct huffman *d, int end)
+lzx_read_bitlen(struct lzx_stream *strm, struct huffman *d, uint16_t end)
 {
 	struct lzx_dec *ds = strm->ds;
 	struct lzx_br *br = &(ds->br);
-	int c, i, j, ret, same;
-	unsigned rbits;
+	int ret;
+	uint16_t c, i, j, rbits, same;
 
 	i = ds->loop;
 	if (i == 0)
 		memset(d->freq, 0, sizeof(d->freq));
-	ret = 0;
-	if (end < 0)
-		end = d->len_size;
+	ret = ARCHIVE_EOF;
+	if (end == 0)
+		end = d->symbol_count;
 	while (i < end) {
 		ds->loop = i;
-		if (!lzx_br_read_ahead(strm, br, ds->pt.max_bits))
+		if (!lzx_br_read_ahead(strm, br, (unsigned)ds->pt.lookup_bits))
 			goto getdata;
-		rbits = lzx_br_bits(br, ds->pt.max_bits);
+		rbits = lzx_br_bits(br, ds->pt.lookup_bits);
 		c = lzx_decode_huffman(&(ds->pt), rbits);
 		switch (c) {
 		case 17:/* several zero lengths, from 4 to 19. */
-			if (!lzx_br_read_ahead(strm, br, ds->pt.bitlen[c]+4))
+			if (!lzx_br_read_ahead(strm, br, ds->pt.bitlen[c] + 4U))
 				goto getdata;
 			lzx_br_consume(br, ds->pt.bitlen[c]);
 			same = lzx_br_bits(br, 4) + 4;
-			if (i + same > end)
-				return (-1);/* Invalid */
+			if (same > end - i)
+				return (ARCHIVE_FATAL);
 			lzx_br_consume(br, 4);
 			for (j = 0; j < same; j++)
 				d->bitlen[i++] = 0;
 			break;
 		case 18:/* many zero lengths, from 20 to 51. */
-			if (!lzx_br_read_ahead(strm, br, ds->pt.bitlen[c]+5))
+			if (!lzx_br_read_ahead(strm, br, ds->pt.bitlen[c] + 5U))
 				goto getdata;
 			lzx_br_consume(br, ds->pt.bitlen[c]);
 			same = lzx_br_bits(br, 5) + 20;
-			if (i + same > end)
-				return (-1);/* Invalid */
+			if (same > end - i)
+				return (ARCHIVE_FATAL);
 			lzx_br_consume(br, 5);
 			memset(d->bitlen + i, 0, same);
 			i += same;
 			break;
 		case 19:/* a few same lengths. */
 			if (!lzx_br_read_ahead(strm, br,
-			    ds->pt.bitlen[c]+1+ds->pt.max_bits))
+			    ds->pt.bitlen[c] + 1U + ds->pt.lookup_bits))
 				goto getdata;
 			lzx_br_consume(br, ds->pt.bitlen[c]);
 			same = lzx_br_bits(br, 1) + 4;
-			if (i + same > end)
-				return (-1);
+			if (same > end - i)
+				return (ARCHIVE_FATAL);
 			lzx_br_consume(br, 1);
-			rbits = lzx_br_bits(br, ds->pt.max_bits);
+			rbits = lzx_br_bits(br, ds->pt.lookup_bits);
 			c = lzx_decode_huffman(&(ds->pt), rbits);
 			lzx_br_consume(br, ds->pt.bitlen[c]);
-			c = (d->bitlen[i] - c + 17) % 17;
-			if (c < 0)
-				return (-1);/* Invalid */
+			if (c > d->bitlen[i] + 17)
+				return (ARCHIVE_FATAL);
+			c = (d->bitlen[i] + 17 - c) % 17;
 			for (j = 0; j < same; j++)
 				d->bitlen[i++] = c;
 			d->freq[c] += same;
 			break;
 		default:
 			lzx_br_consume(br, ds->pt.bitlen[c]);
-			c = (d->bitlen[i] - c + 17) % 17;
-			if (c < 0)
-				return (-1);/* Invalid */
+			if (c > d->bitlen[i] + 17)
+				return (ARCHIVE_FATAL);
+			c = (d->bitlen[i] + 17 - c) % 17;
 			d->freq[c]++;
 			d->bitlen[i++] = c;
 			break;
 		}
 	}
-	ret = 1;
+	ret = ARCHIVE_OK;
 getdata:
 	ds->loop = i;
 	return (ret);
 }
 
 static int
-lzx_huffman_init(struct huffman *hf, size_t len_size, int tbl_bits)
+lzx_huffman_init(struct huffman *hf, uint16_t symbol_count, uint8_t tbl_bits)
 {
+	size_t tbl_size = (size_t)1 << tbl_bits;
 
-	if (hf->bitlen == NULL || hf->len_size != (int)len_size) {
+	if (hf->bitlen == NULL || hf->symbol_count != symbol_count) {
 		free(hf->bitlen);
-		hf->bitlen = calloc(len_size,  sizeof(hf->bitlen[0]));
+		hf->bitlen = calloc(symbol_count, sizeof(hf->bitlen[0]));
 		if (hf->bitlen == NULL)
 			return (ARCHIVE_FATAL);
-		hf->len_size = (int)len_size;
+		hf->symbol_count = symbol_count;
 	} else
-		memset(hf->bitlen, 0, len_size *  sizeof(hf->bitlen[0]));
+		memset(hf->bitlen, 0, symbol_count * sizeof(hf->bitlen[0]));
 	if (hf->tbl == NULL) {
-		hf->tbl = malloc(((size_t)1 << tbl_bits) * sizeof(hf->tbl[0]));
+		hf->tbl = calloc(tbl_size, sizeof(hf->tbl[0]));
 		if (hf->tbl == NULL)
 			return (ARCHIVE_FATAL);
 		hf->tbl_bits = tbl_bits;
-	}
+	} else
+		memset(hf->tbl, 0, tbl_size * sizeof(hf->bitlen[0]));
 	return (ARCHIVE_OK);
 }
 
@@ -3173,19 +3252,47 @@
 }
 
 /*
- * Make a huffman coding table.
+ * Create a direct, expanded Huffman lookup table based on
+ * canonical representation.
  */
 static int
 lzx_make_huffman_table(struct huffman *hf)
 {
+	uint16_t bitptn[17], weight[17];
 	uint16_t *tbl;
-	const unsigned char *bitlen;
-	int bitptn[17], weight[17];
-	int i, maxbits = 0, ptn, tbl_size, w;
-	int len_avail;
+	const uint8_t *bitlen;
+	uint8_t maxbits = 0;
+	uint32_t ptn;
+	uint16_t i, symbol_count, w;
 
 	/*
 	 * Initialize bit patterns.
+	 *
+	 * Each bitptn element represents the smallest possible
+	 * code sequence allowed. The weight represents the amount
+	 * of lookup bit patterns covered by a code of this length.
+	 *
+	 * Example of a Huffman tree:
+	 *
+	 * idx | freq[idx]
+	 * ----+----------
+	 *   1 |         1
+	 *   2 |         2
+	 *
+	 * The result will be:
+	 *
+	 * idx | bitptn[idx] | weight[idx]
+	 * ----+-------------+------------
+	 *   1 |      0x0000 |       32768
+	 *   2 |      0x8000 |       16384
+	 *
+	 * This means that a code with bit length 1 will take 32768
+	 * possible combinations starting with 0b0. Since one such code exists
+	 * (freq[1] = 1), codes with bit length 2 must start with 0b1. Since
+	 * two codes with bit length 2 exist (freq[2] = 2), no more codes can
+	 * be added.
+	 *
+	 * In this example, maxbits will be 2 (longest code length is 2).
 	 */
 	ptn = 0;
 	for (i = 1, w = 1 << 15; i <= 16; i++, w >>= 1) {
@@ -3196,65 +3303,108 @@
 			maxbits = i;
 		}
 	}
-	if ((ptn & 0xffff) != 0 || maxbits > hf->tbl_bits)
-		return (0);/* Invalid */
-
-	hf->max_bits = maxbits;
+	/* Verify Kraft's inequality. */
+	if (ptn != 0 && ptn != 0x10000)
+		return (ARCHIVE_FATAL);
 
 	/*
-	 * Cut out extra bits which we won't house in the table.
-	 * This preparation reduces the same calculation in the for-loop
-	 * making the table.
+	 * Shrink codes to smallest size by removing extra bits after
+	 * the actual code sequences as good as possible.
+	 *
+	 * Continuing the example above:
+	 *
+	 * idx | bitptn[idx] | weight[idx]
+	 * ----+-------------+-------------
+	 *   1 |      0x0000 |       32768
+	 *   2 |      0x8000 |       16384
+	 *
+	 * As can be seen, a total of 65536 entries must be created even
+	 * though 4 would be sufficient (indices 0b00 to 0b11). Right shift
+	 * the pattern and divide weight as much as possible:
+	 *
+	 * idx | bitptn[idx] | weight[idx]
+	 * ----+-------------+-------------
+	 *   1 |      0x0000 |           2
+	 *   2 |      0x0002 |           1
+	 *
+	 * Thus, the direct, expanded lookup table only needs 4 entries.
 	 */
 	if (maxbits < 16) {
-		int ebits = 16 - maxbits;
+		uint8_t ebits = 16 - maxbits;
 		for (i = 1; i <= maxbits; i++) {
 			bitptn[i] >>= ebits;
 			weight[i] >>= ebits;
 		}
 	}
 
+	/* Grow table if necessary. */
+	if (maxbits > hf->tbl_bits) {
+		size_t tbl_size;
+
+		hf->tbl_bits = 16;
+		tbl_size = (size_t)1 << hf->tbl_bits;
+
+		free(hf->tbl);
+		hf->tbl = calloc(tbl_size, sizeof(hf->tbl[0]));
+		if (hf->tbl == NULL)
+			return (ARCHIVE_FATAL);
+	}
+	hf->lookup_bits = maxbits;
+
 	/*
-	 * Make the table.
+	 * Construct the direct, expanded lookup table.
+	 *
+	 * Store each symbol in table for every possible bit patterns starting
+	 * with their code.
+	 *
+	 * Following the example with len_avail being 4:
+	 *
+	 * idx | bitlen[idx]
+	 * ----+------------
+	 *   0 |           0
+	 *   1 |           1
+	 *   2 |           0
+	 *   3 |           2
+	 *   4 |           2
+	 *
+	 * The resulting table contains all used symbols (1, 3, 4) for up to
+	 * max_len bit patterns:
+	 *
+	 *       idx | tbl[idx]
+	 * ----------+---------
+	 *  0 (0b00) |        1
+	 *  1 (0b01) |        1
+	 *  2 (0b10) |        3
+	 *  3 (0b11) |        4
 	 */
-	tbl_size = 1 << hf->tbl_bits;
 	tbl = hf->tbl;
 	bitlen = hf->bitlen;
-	len_avail = hf->len_size;
-	hf->tree_used = 0;
-	/* Initialize table to invalid values */
-	for (i = 0; i < tbl_size; i++) {
-		tbl[i] = (uint16_t)hf->len_size;
-	}
-	for (i = 0; i < len_avail; i++) {
+	symbol_count = hf->symbol_count;
+	for (i = 0; i < symbol_count; i++) {
 		uint16_t *p;
-		int len, cnt;
+		uint16_t cnt;
+		uint8_t len;
 
 		if (bitlen[i] == 0)
 			continue;
 		/* Get a bit pattern */
 		len = bitlen[i];
-		if (len > tbl_size)
-			return (0);
+		if (len > maxbits)
+			return (ARCHIVE_FATAL);
 		ptn = bitptn[len];
 		cnt = weight[len];
 		/* Calculate next bit pattern */
-		if ((bitptn[len] = ptn + cnt) > tbl_size)
-			return (0);/* Invalid */
+		bitptn[len] = ptn + cnt;
 		/* Update the table */
-		p = &(tbl[ptn]);
-		while (--cnt >= 0)
-			p[cnt] = (uint16_t)i;
+		p = tbl + ptn;
+		while (cnt-- > 0)
+			*p++ = i;
 	}
-	return (1);
+	return (ARCHIVE_OK);
 }
 
-static inline int
-lzx_decode_huffman(struct huffman *hf, unsigned rbits)
+static uint16_t
+lzx_decode_huffman(struct huffman *hf, uint16_t rbits)
 {
-	int c;
-	c = hf->tbl[rbits];
-	if (c < hf->len_size)
-		return (c);
-	return (0);
+	return hf->tbl[rbits];
 }
diff --git a/c/archive_read_support_format_cpio.c b/c/archive_read_support_format_cpio.c
--- a/c/archive_read_support_format_cpio.c
+++ b/c/archive_read_support_format_cpio.c
@@ -261,14 +261,12 @@
 static int
 archive_read_format_cpio_bid(struct archive_read *a, int best_bid)
 {
+	struct cpio *cpio = a->format->data;
 	const unsigned char *p;
-	struct cpio *cpio;
 	int bid;
 
 	(void)best_bid; /* UNUSED */
 
-	cpio = (struct cpio *)(a->format->data);
-
 	if ((p = __archive_read_ahead(a, 6, NULL)) == NULL)
 		return (-1);
 
@@ -326,10 +324,9 @@
 archive_read_format_cpio_options(struct archive_read *a,
     const char *key, const char *val)
 {
-	struct cpio *cpio;
+	struct cpio *cpio = a->format->data;
 	int ret = ARCHIVE_FAILED;
 
-	cpio = (struct cpio *)(a->format->data);
 	if (strcmp(key, "compat-2x")  == 0) {
 		/* Handle filenames as libarchive 2.x */
 		cpio->init_default_conversion = (val != NULL)?1:0;
@@ -364,14 +361,14 @@
 archive_read_format_cpio_read_header(struct archive_read *a,
     struct archive_entry *entry)
 {
-	struct cpio *cpio;
+	struct cpio *cpio = a->format->data;
 	const void *h, *hl;
 	struct archive_string_conv *sconv;
 	size_t namelength;
 	size_t name_pad;
+	int is_trailer;
 	int r;
 
-	cpio = (struct cpio *)(a->format->data);
 	sconv = cpio->opt_sconv;
 	if (sconv == NULL) {
 		if (!cpio->init_default_conversion) {
@@ -411,6 +408,9 @@
 		    archive_string_conversion_charset_name(sconv));
 		r = ARCHIVE_WARN;
 	}
+	/* Save this before consuming the name buffer below. */
+	is_trailer = (namelength == 11 &&
+	    memcmp((const char *)h, "TRAILER!!!", 10) == 0);
 	cpio->entry_offset = 0;
 
 	__archive_read_consume(a, namelength);
@@ -451,8 +451,7 @@
 	 * header.  XXX */
 
 	/* Compare name to "TRAILER!!!" to test for end-of-archive. */
-	if (namelength == 11 && strncmp((const char *)h, "TRAILER!!!",
-	    10) == 0) {
+	if (is_trailer) {
 		/* TODO: Store file location of start of block. */
 		archive_clear_error(&a->archive);
 		return (ARCHIVE_EOF);
@@ -470,11 +469,9 @@
 archive_read_format_cpio_read_data(struct archive_read *a,
     const void **buff, size_t *size, int64_t *offset)
 {
+	struct cpio *cpio = a->format->data;
 	ssize_t bytes_read;
-	struct cpio *cpio;
 
-	cpio = (struct cpio *)(a->format->data);
-
 	if (cpio->entry_bytes_unconsumed) {
 		__archive_read_consume(a, cpio->entry_bytes_unconsumed);
 		cpio->entry_bytes_unconsumed = 0;
@@ -508,7 +505,7 @@
 static int
 archive_read_format_cpio_skip(struct archive_read *a)
 {
-	struct cpio *cpio = (struct cpio *)(a->format->data);
+	struct cpio *cpio = a->format->data;
 	int64_t to_skip = cpio->entry_bytes_remaining + cpio->entry_padding +
 		cpio->entry_bytes_unconsumed;
 
@@ -545,7 +542,8 @@
 {
 	const void *h;
 	const char *p, *q;
-	size_t skip, skipped = 0;
+	int64_t skip;
+	uintmax_t skipped = 0;
 	ssize_t bytes;
 
 	for (;;) {
@@ -577,9 +575,9 @@
 					if (skipped > 0) {
 						archive_set_error(&a->archive,
 						    0,
-						    "Skipped %d bytes before "
+						    "Skipped %ju bytes before "
 						    "finding valid header",
-						    (int)skipped);
+						    skipped);
 						return (ARCHIVE_WARN);
 					}
 					return (ARCHIVE_OK);
@@ -1000,9 +998,8 @@
 static int
 archive_read_format_cpio_cleanup(struct archive_read *a)
 {
-	struct cpio *cpio;
+	struct cpio *cpio = a->format->data;
 
-	cpio = (struct cpio *)(a->format->data);
         /* Free inode->name map */
         while (cpio->links_head != NULL) {
                 struct links_entry *lp = cpio->links_head->next;
@@ -1012,7 +1009,7 @@
                 cpio->links_head = lp;
         }
 	free(cpio);
-	(a->format->data) = NULL;
+	a->format->data = NULL;
 	return (ARCHIVE_OK);
 }
 
diff --git a/c/archive_read_support_format_iso9660.c b/c/archive_read_support_format_iso9660.c
--- a/c/archive_read_support_format_iso9660.c
+++ b/c/archive_read_support_format_iso9660.c
@@ -47,6 +47,7 @@
 #include "archive_endian.h"
 #include "archive_entry.h"
 #include "archive_entry_locale.h"
+#include "archive_integer.h"
 #include "archive_private.h"
 #include "archive_read_private.h"
 #include "archive_string.h"
@@ -314,8 +315,8 @@
 
 struct heap_queue {
 	struct file_info **files;
-	int		 allocated;
-	int		 used;
+	size_t		 allocated;
+	size_t		 used;
 };
 
 struct iso9660 {
@@ -337,8 +338,8 @@
 			uint64_t	 offset;/* Offset of CE on disk. */
 			struct file_info *file;
 		}		*reqs;
-		int		 cnt;
-		int		 allocated;
+		size_t		 cnt;
+		size_t		 allocated;
 	}	read_ce_req;
 
 	int64_t		previous_number;
@@ -379,8 +380,6 @@
 	size_t		 utf16be_path_len;
 	unsigned char *utf16be_previous_path;
 	size_t		 utf16be_previous_path_len;
-	/* Null buffer used in bidder to improve its performance. */
-	unsigned char	 null[2048];
 };
 
 static int	archive_read_format_iso9660_bid(struct archive_read *, int);
@@ -403,12 +402,12 @@
 static int	isodate17_valid(const unsigned char *);
 static time_t	isodate7(const unsigned char *);
 static int	isodate7_valid(const unsigned char *);
-static int	isBootRecord(struct iso9660 *, const unsigned char *);
+static int	isBootRecord(const unsigned char *);
 static int	isVolumePartition(struct iso9660 *, const unsigned char *);
-static int	isVDSetTerminator(struct iso9660 *, const unsigned char *);
+static int	isVDSetTerminator(const unsigned char *);
 static int	isJolietSVD(struct iso9660 *, const unsigned char *);
-static int	isSVD(struct iso9660 *, const unsigned char *);
-static int	isEVD(struct iso9660 *, const unsigned char *);
+static int	isSVD(const unsigned char *);
+static int	isEVD(const unsigned char *);
 static int	isPVD(struct iso9660 *, const unsigned char *);
 static int	isRootDirectoryRecord(const unsigned char *);
 static int	isValid723Integer(const unsigned char *);
@@ -503,7 +502,7 @@
 static int
 archive_read_format_iso9660_bid(struct archive_read *a, int best_bid)
 {
-	struct iso9660 *iso9660;
+	struct iso9660 *iso9660 = a->format->data;
 	ssize_t bytes_read;
 	const unsigned char *p;
 	int seenTerminator;
@@ -513,8 +512,6 @@
 	if (best_bid > 48)
 		return (-1);
 
-	iso9660 = (struct iso9660 *)(a->format->data);
-
 	/*
 	 * Skip the first 32k (reserved area) and get the first
 	 * 8 sectors of the volume descriptor table.  Of course,
@@ -547,15 +544,15 @@
 			if (isJolietSVD(iso9660, p))
 				continue;
 		}
-		if (isBootRecord(iso9660, p))
+		if (isBootRecord(p))
 			continue;
-		if (isEVD(iso9660, p))
+		if (isEVD(p))
 			continue;
-		if (isSVD(iso9660, p))
+		if (isSVD(p))
 			continue;
 		if (isVolumePartition(iso9660, p))
 			continue;
-		if (isVDSetTerminator(iso9660, p)) {
+		if (isVDSetTerminator(p)) {
 			seenTerminator = 1;
 			break;
 		}
@@ -576,9 +573,7 @@
 archive_read_format_iso9660_options(struct archive_read *a,
 		const char *key, const char *val)
 {
-	struct iso9660 *iso9660;
-
-	iso9660 = (struct iso9660 *)(a->format->data);
+	struct iso9660 *iso9660 = a->format->data;
 
 	if (strcmp(key, "joliet") == 0) {
 		if (val == NULL || strcmp(val, "off") == 0 ||
@@ -603,26 +598,23 @@
 }
 
 static int
-isNull(struct iso9660 *iso9660, const unsigned char *h, unsigned offset,
-unsigned bytes)
+isNull(const unsigned char *h, unsigned offset, unsigned bytes)
 {
-
-	while (bytes >= sizeof(iso9660->null)) {
-		if (!memcmp(iso9660->null, h + offset, sizeof(iso9660->null)))
-			return (0);
-		offset += sizeof(iso9660->null);
-		bytes -= sizeof(iso9660->null);
-	}
-	if (bytes)
-		return memcmp(iso9660->null, h + offset, bytes) == 0;
-	else
+	if (bytes == 0)
 		return (1);
+
+	/*
+	 * If the first byte is zero and every byte equals the following
+	 * byte, the entire range is zero.
+	 */
+	return (h[offset] == 0 &&
+	    (bytes == 1 ||
+	    memcmp(h + offset, h + offset + 1, bytes - 1) == 0));
 }
 
 static int
-isBootRecord(struct iso9660 *iso9660, const unsigned char *h)
+isBootRecord(const unsigned char *h)
 {
-	(void)iso9660; /* UNUSED */
 
 	/* Type of the Volume Descriptor Boot Record must be 0. */
 	if (h[0] != 0)
@@ -662,9 +654,8 @@
 }
 
 static int
-isVDSetTerminator(struct iso9660 *iso9660, const unsigned char *h)
+isVDSetTerminator(const unsigned char *h)
 {
-	(void)iso9660; /* UNUSED */
 
 	/* Type of the Volume Descriptor Set Terminator must be 255. */
 	if (h[0] != 255)
@@ -675,7 +666,7 @@
 		return (0);
 
 	/* Reserved field must be 0. */
-	if (!isNull(iso9660, h, 7, 2048-7))
+	if (!isNull(h, 7, 2048-7))
 		return (0);
 
 	return (1);
@@ -690,7 +681,7 @@
 
 	/* Check if current sector is a kind of Supplementary Volume
 	 * Descriptor. */
-	if (!isSVD(iso9660, h))
+	if (!isSVD(h))
 		return (0);
 
 	/* FIXME: do more validations according to joliet spec. */
@@ -731,25 +722,24 @@
 }
 
 static int
-isSVD(struct iso9660 *iso9660, const unsigned char *h)
+isSVD(const unsigned char *h)
 {
 	const unsigned char *p;
 	ssize_t logical_block_size;
 	int32_t volume_block;
 	int32_t location;
 
-	(void)iso9660; /* UNUSED */
 
 	/* Type 2 means it's a SVD. */
 	if (h[SVD_type_offset] != 2)
 		return (0);
 
 	/* Reserved field must be 0. */
-	if (!isNull(iso9660, h, SVD_reserved1_offset, SVD_reserved1_size))
+	if (!isNull(h, SVD_reserved1_offset, SVD_reserved1_size))
 		return (0);
-	if (!isNull(iso9660, h, SVD_reserved2_offset, SVD_reserved2_size))
+	if (!isNull(h, SVD_reserved2_offset, SVD_reserved2_size))
 		return (0);
-	if (!isNull(iso9660, h, SVD_reserved3_offset, SVD_reserved3_size))
+	if (!isNull(h, SVD_reserved3_offset, SVD_reserved3_size))
 		return (0);
 
 	/* File structure version must be 1 for ISO9660/ECMA119. */
@@ -791,14 +781,13 @@
 }
 
 static int
-isEVD(struct iso9660 *iso9660, const unsigned char *h)
+isEVD(const unsigned char *h)
 {
 	const unsigned char *p;
 	ssize_t logical_block_size;
 	int32_t volume_block;
 	int32_t location;
 
-	(void)iso9660; /* UNUSED */
 
 	/* Type of the Enhanced Volume Descriptor must be 2. */
 	if (h[PVD_type_offset] != 2)
@@ -813,11 +802,11 @@
 		return (0);
 
 	/* Reserved field must be 0. */
-	if (!isNull(iso9660, h, PVD_reserved2_offset, PVD_reserved2_size))
+	if (!isNull(h, PVD_reserved2_offset, PVD_reserved2_size))
 		return (0);
 
 	/* Reserved field must be 0. */
-	if (!isNull(iso9660, h, PVD_reserved3_offset, PVD_reserved3_size))
+	if (!isNull(h, PVD_reserved3_offset, PVD_reserved3_size))
 		return (0);
 
 	/* Logical block size must be > 0. */
@@ -853,11 +842,11 @@
 		return (0);
 
 	/* Reserved field must be 0. */
-	if (!isNull(iso9660, h, PVD_reserved4_offset, PVD_reserved4_size))
+	if (!isNull(h, PVD_reserved4_offset, PVD_reserved4_size))
 		return (0);
 
 	/* Reserved field must be 0. */
-	if (!isNull(iso9660, h, PVD_reserved5_offset, PVD_reserved5_size))
+	if (!isNull(h, PVD_reserved5_offset, PVD_reserved5_size))
 		return (0);
 
 	/* Read Root Directory Record in Volume Descriptor. */
@@ -891,7 +880,7 @@
 		return (0);
 
 	/* Reserved field must be 0. */
-	if (!isNull(iso9660, h, PVD_reserved2_offset, PVD_reserved2_size))
+	if (!isNull(h, PVD_reserved2_offset, PVD_reserved2_size))
 		return (0);
 
 	/* Volume space size must be encoded according to 7.3.3 */
@@ -903,7 +892,7 @@
 		return (0);
 
 	/* Reserved field must be 0. */
-	if (!isNull(iso9660, h, PVD_reserved3_offset, PVD_reserved3_size))
+	if (!isNull(h, PVD_reserved3_offset, PVD_reserved3_size))
 		return (0);
 
 	/* Volume set size must be encoded according to 7.2.3 */
@@ -961,7 +950,7 @@
 			return (0);
 
 	/* Reserved field must be 0. */
-	if (!isNull(iso9660, h, PVD_reserved5_offset, PVD_reserved5_size))
+	if (!isNull(h, PVD_reserved5_offset, PVD_reserved5_size))
 		return (0);
 
 	/* XXX TODO: Check other values for sanity; reject more
@@ -1034,12 +1023,11 @@
 static int
 read_children(struct archive_read *a, struct file_info *parent)
 {
-	struct iso9660 *iso9660;
+	struct iso9660 *iso9660 = a->format->data;
 	const unsigned char *b, *p;
 	struct file_info *multi;
 	size_t step, skip_size;
 
-	iso9660 = (struct iso9660 *)(a->format->data);
 	/* flush any remaining bytes from the last round to ensure
 	 * we're positioned */
 	if (iso9660->entry_bytes_unconsumed) {
@@ -1086,7 +1074,7 @@
 		p = b;
 		b += iso9660->logical_block_size;
 		step -= iso9660->logical_block_size;
-		for (; *p != 0 && p + DR_name_offset < b && p + *p <= b;
+		for (; p < b && b - p > DR_name_offset && *p != 0 && *p <= b - p;
 			p += *p) {
 			struct file_info *child;
 
@@ -1247,12 +1235,10 @@
 archive_read_format_iso9660_read_header(struct archive_read *a,
     struct archive_entry *entry)
 {
-	struct iso9660 *iso9660;
+	struct iso9660 *iso9660 = a->format->data;
 	struct file_info *file;
 	int r, rd_r = ARCHIVE_OK;
 
-	iso9660 = (struct iso9660 *)(a->format->data);
-
 	if (!a->archive.archive_format) {
 		a->archive.archive_format = ARCHIVE_FORMAT_ISO9660;
 		a->archive.archive_format_name = "ISO9660";
@@ -1499,7 +1485,7 @@
 zisofs_read_data(struct archive_read *a,
     const void **buff, size_t *size, int64_t *offset)
 {
-	struct iso9660 *iso9660;
+	struct iso9660 *iso9660 = a->format->data;
 	struct zisofs  *zisofs;
 	const unsigned char *p;
 	size_t avail;
@@ -1507,7 +1493,6 @@
 	size_t uncompressed_size;
 	int r;
 
-	iso9660 = (struct iso9660 *)(a->format->data);
 	zisofs = &iso9660->entry_zisofs;
 
 	p = __archive_read_ahead(a, 1, &bytes_read);
@@ -1738,11 +1723,9 @@
 archive_read_format_iso9660_read_data(struct archive_read *a,
     const void **buff, size_t *size, int64_t *offset)
 {
+	struct iso9660 *iso9660 = a->format->data;
 	ssize_t bytes_read;
-	struct iso9660 *iso9660;
 
-	iso9660 = (struct iso9660 *)(a->format->data);
-
 	if (iso9660->entry_bytes_unconsumed) {
 		__archive_read_consume(a, iso9660->entry_bytes_unconsumed);
 		iso9660->entry_bytes_unconsumed = 0;
@@ -1805,10 +1788,9 @@
 static int
 archive_read_format_iso9660_cleanup(struct archive_read *a)
 {
-	struct iso9660 *iso9660;
+	struct iso9660 *iso9660 = a->format->data;
 	int r = ARCHIVE_OK;
 
-	iso9660 = (struct iso9660 *)(a->format->data);
 	release_files(iso9660);
 	free(iso9660->read_ce_req.reqs);
 	archive_string_free(&iso9660->pathname);
@@ -1828,7 +1810,7 @@
 	free(iso9660->utf16be_path);
 	free(iso9660->utf16be_previous_path);
 	free(iso9660);
-	(a->format->data) = NULL;
+	a->format->data = NULL;
 	return (r);
 }
 
@@ -1840,7 +1822,7 @@
 parse_file_info(struct archive_read *a, struct file_info *parent,
     const unsigned char *isodirrec, size_t reclen)
 {
-	struct iso9660 *iso9660;
+	struct iso9660 *iso9660 = a->format->data;
 	struct file_info *file, *filep;
 	size_t name_len;
 	const unsigned char *rr_start, *rr_end;
@@ -1850,8 +1832,6 @@
 	int32_t location;
 	int flags;
 
-	iso9660 = (struct iso9660 *)(a->format->data);
-
 	if (reclen != 0)
 		dr_len = (size_t)isodirrec[DR_length_offset];
 	/*
@@ -2192,11 +2172,9 @@
 parse_rockridge(struct archive_read *a, struct file_info *file,
     const unsigned char *p, const unsigned char *end)
 {
-	struct iso9660 *iso9660;
+	struct iso9660 *iso9660 = a->format->data;
 	int entry_seen = 0;
 
-	iso9660 = (struct iso9660 *)(a->format->data);
-
 	while (p + 4 <= end  /* Enough space for another entry. */
 	    && p[0] >= 'A' && p[0] <= 'Z' /* Sanity-check 1st char of name. */
 	    && p[1] >= 'A' && p[1] <= 'Z' /* Sanity-check 2nd char of name. */
@@ -2366,13 +2344,11 @@
 register_CE(struct archive_read *a, int32_t location,
     struct file_info *file)
 {
-	struct iso9660 *iso9660;
+	struct iso9660 *iso9660 = a->format->data;
 	struct read_ce_queue *heap;
-	struct read_ce_req *p;
 	uint64_t offset, parent_offset;
-	int hole, parent;
+	size_t hole, parent;
 
-	iso9660 = (struct iso9660 *)(a->format->data);
 	offset = ((uint64_t)location) * (uint64_t)iso9660->logical_block_size;
 	if (((file->mode & AE_IFMT) == AE_IFREG &&
 	    offset >= file->offset) ||
@@ -2389,14 +2365,13 @@
 	/* Expand our CE list as necessary. */
 	heap = &(iso9660->read_ce_req);
 	if (heap->cnt >= heap->allocated) {
-		int new_size;
+		struct read_ce_req *p;
+		size_t new_size;
 
 		if (heap->allocated < 16)
 			new_size = 16;
-		else
-			new_size = heap->allocated * 2;
-		/* Overflow might keep us from growing the list. */
-		if (new_size <= heap->allocated) {
+		else if (archive_ckd_mul_size(&new_size, heap->allocated, 2)) {
+			/* Overflow keeps us from growing the list. */
 			archive_set_error(&a->archive, ENOMEM, "Out of memory");
 			return (ARCHIVE_FATAL);
 		}
@@ -2438,7 +2413,7 @@
 next_CE(struct read_ce_queue *heap)
 {
 	uint64_t a_offset, b_offset, c_offset;
-	int a, b, c;
+	size_t a, b, c;
 	struct read_ce_req tmp;
 
 	if (heap->cnt < 1)
@@ -2765,7 +2740,7 @@
     int data_length)
 {
 
-	if (data[0] == 0x70 && data[1] == 0x7a && data_length == 12) {
+	if (data_length == 12 && data[0] == 0x70 && data[1] == 0x7a) {
         /* paged zlib */
         file->pz = 1;
         file->pz_log2_bs = data[3];
@@ -3144,7 +3119,7 @@
     struct file_info *file, uint64_t key)
 {
 	uint64_t file_key, parent_key;
-	int hole, parent;
+	size_t hole, parent;
 
 	/* Reserve 16 bits for possible key collisions (needed for linked items) */
 	/* For ISO files with more than 65535 entries, reordering will still occur */
@@ -3154,12 +3129,12 @@
 	/* Expand our pending files list as necessary. */
 	if (heap->used >= heap->allocated) {
 		struct file_info **new_pending_files;
-		int new_size = heap->allocated * 2;
+		size_t new_size;
 
 		if (heap->allocated < 1024)
 			new_size = 1024;
-		/* Overflow might keep us from growing the list. */
-		if (new_size <= heap->allocated) {
+		else if (archive_ckd_mul_size(&new_size, heap->allocated, 2)) {
+			/* Overflow keeps us from growing the list. */
 			archive_set_error(&a->archive,
 			    ENOMEM, "Out of memory");
 			return (ARCHIVE_FATAL);
@@ -3186,7 +3161,7 @@
 	 */
 	hole = heap->used++;
 	while (hole > 0) {
-		parent = (hole - 1)/2;
+		parent = (hole - 1) / 2;
 		parent_key = heap->files[parent]->key;
 		if (file_key >= parent_key) {
 			heap->files[hole] = file;
@@ -3205,7 +3180,7 @@
 heap_get_entry(struct heap_queue *heap)
 {
 	uint64_t a_key, b_key, c_key;
-	int a, b, c;
+	size_t a, b, c;
 	struct file_info *r, *tmp;
 
 	if (heap->used < 1)
diff --git a/c/archive_read_support_format_lha.c b/c/archive_read_support_format_lha.c
--- a/c/archive_read_support_format_lha.c
+++ b/c/archive_read_support_format_lha.c
@@ -390,10 +390,9 @@
 archive_read_format_lha_options(struct archive_read *a,
     const char *key, const char *val)
 {
-	struct lha *lha;
+	struct lha *lha = a->format->data;
 	int ret = ARCHIVE_FAILED;
 
-	lha = (struct lha *)(a->format->data);
 	if (strcmp(key, "hdrcharset")  == 0) {
 		if (val == NULL || val[0] == 0)
 			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
@@ -472,9 +471,9 @@
 archive_read_format_lha_read_header(struct archive_read *a,
     struct archive_entry *entry)
 {
+	struct lha *lha = a->format->data;
 	struct archive_wstring linkname;
 	struct archive_wstring pathname;
-	struct lha *lha;
 	const unsigned char *p;
 	const char *signature;
 	int err;
@@ -485,7 +484,6 @@
 	if (a->archive.archive_format_name == NULL)
 		a->archive.archive_format_name = "lha";
 
-	lha = (struct lha *)(a->format->data);
 	lha->decompress_init = 0;
 	lha->end_of_entry = 0;
 	lha->end_of_entry_cleanup = 0;
@@ -900,7 +898,7 @@
 {
 	const unsigned char *p;
 	size_t extdsize;
-	int i, err, err2;
+	int err, err2;
 	int namelen, padding;
 	unsigned char headersum, sum_calculated;
 
@@ -925,10 +923,9 @@
 	if ((p = __archive_read_ahead(a, lha->header_size, NULL)) == NULL)
 		return (truncated_error(a));
 
-	for (i = 0; i < namelen; i++) {
-		if (p[i + H1_FILE_NAME_OFFSET] == 0xff)
-			goto invalid;/* Invalid filename. */
-	}
+	if (memchr(p + H1_FILE_NAME_OFFSET, 0xff,
+	    (size_t)namelen) != NULL)
+		goto invalid; /* Invalid filename. */
 	archive_strncpy(&lha->filename, p + H1_FILE_NAME_OFFSET, namelen);
 	lha->crc = archive_le16dec(p + H1_FILE_NAME_OFFSET + namelen);
 	lha->setflag |= CRC_IS_SET;
@@ -1097,7 +1094,7 @@
 	header_crc = lha_crc16(0, p, H3_FIXED_SIZE);
 	__archive_read_consume(a, H3_FIXED_SIZE);
 
-	/* Reject rediculously large header */
+	/* Reject ridiculously large header */
 	if (lha->header_size > 65536) {
 		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 		    "LHa header size too large");
@@ -1452,7 +1449,7 @@
 static int
 lha_end_of_entry(struct archive_read *a)
 {
-	struct lha *lha = (struct lha *)(a->format->data);
+	struct lha *lha = a->format->data;
 	int r = ARCHIVE_EOF;
 
 	if (!lha->end_of_entry_cleanup) {
@@ -1473,7 +1470,7 @@
 archive_read_format_lha_read_data(struct archive_read *a,
     const void **buff, size_t *size, int64_t *offset)
 {
-	struct lha *lha = (struct lha *)(a->format->data);
+	struct lha *lha = a->format->data;
 	int r;
 
 	if (lha->entry_unconsumed) {
@@ -1506,7 +1503,7 @@
 lha_read_data_none(struct archive_read *a, const void **buff,
     size_t *size, int64_t *offset)
 {
-	struct lha *lha = (struct lha *)(a->format->data);
+	struct lha *lha = a->format->data;
 	ssize_t bytes_avail;
 
 	if (lha->entry_bytes_remaining == 0) {
@@ -1553,7 +1550,7 @@
 lha_read_data_lzh(struct archive_read *a, const void **buff,
     size_t *size, int64_t *offset)
 {
-	struct lha *lha = (struct lha *)(a->format->data);
+	struct lha *lha = a->format->data;
 	ssize_t bytes_avail;
 	int r;
 
@@ -1643,11 +1640,9 @@
 static int
 archive_read_format_lha_read_data_skip(struct archive_read *a)
 {
-	struct lha *lha;
+	struct lha *lha = a->format->data;
 	int64_t bytes_skipped;
 
-	lha = (struct lha *)(a->format->data);
-
 	if (lha->entry_unconsumed) {
 		/* Consume as much as the decompressor actually used. */
 		__archive_read_consume(a, lha->entry_unconsumed);
@@ -1674,7 +1669,7 @@
 static int
 archive_read_format_lha_cleanup(struct archive_read *a)
 {
-	struct lha *lha = (struct lha *)(a->format->data);
+	struct lha *lha = a->format->data;
 
 	lzh_decode_free(&(lha->strm));
 	archive_string_free(&(lha->dirname));
@@ -1683,7 +1678,7 @@
 	archive_string_free(&(lha->gname));
 	archive_wstring_free(&(lha->ws));
 	free(lha);
-	(a->format->data) = NULL;
+	a->format->data = NULL;
 	return (ARCHIVE_OK);
 }
 
diff --git a/c/archive_read_support_format_mtree.c b/c/archive_read_support_format_mtree.c
--- a/c/archive_read_support_format_mtree.c
+++ b/c/archive_read_support_format_mtree.c
@@ -141,56 +141,6 @@
 static size_t	mtree_strnlen(const char *, size_t);
 #endif
 
-/*
- * There's no standard for TIME_T_MAX/TIME_T_MIN.  So we compute them
- * here.  TODO: Move this to configure time, but be careful
- * about cross-compile environments.
- */
-static int64_t
-get_time_t_max(void)
-{
-#if defined(TIME_T_MAX)
-	return TIME_T_MAX;
-#else
-	/* ISO C allows time_t to be a floating-point type,
-	   but POSIX requires an integer type.  The following
-	   should work on any system that follows the POSIX
-	   conventions. */
-	if (((time_t)0) < ((time_t)-1)) {
-		/* Time_t is unsigned */
-		return (~(time_t)0);
-	} else {
-		/* Time_t is signed. */
-		/* Assume it's the same as int64_t or int32_t */
-		if (sizeof(time_t) == sizeof(int64_t)) {
-			return (time_t)INT64_MAX;
-		} else {
-			return (time_t)INT32_MAX;
-		}
-	}
-#endif
-}
-
-static int64_t
-get_time_t_min(void)
-{
-#if defined(TIME_T_MIN)
-	return TIME_T_MIN;
-#else
-	if (((time_t)0) < ((time_t)-1)) {
-		/* Time_t is unsigned */
-		return (time_t)0;
-	} else {
-		/* Time_t is signed. */
-		if (sizeof(time_t) == sizeof(int64_t)) {
-			return (time_t)INT64_MIN;
-		} else {
-			return (time_t)INT32_MIN;
-		}
-	}
-#endif
-}
-
 #ifdef HAVE_STRNLEN
 #define mtree_strnlen(a,b) strnlen(a,b)
 #else
@@ -199,12 +149,10 @@
 {
 	size_t i;
 
-	for (i = 0; i <= maxlen; i++) {
+	for (i = 0; i < maxlen; i++) {
 		if (p[i] == 0)
 			break;
 	}
-	if (i > maxlen)
-		return (-1);/* invalid */
 	return (i);
 }
 #endif
@@ -213,9 +161,8 @@
 archive_read_format_mtree_options(struct archive_read *a,
     const char *key, const char *val)
 {
-	struct mtree *mtree;
+	struct mtree *mtree = a->format->data;
 
-	mtree = (struct mtree *)(a->format->data);
 	if (strcmp(key, "checkfs")  == 0) {
 		/* Allows to read information missing from the mtree from the file system */
 		if (val == NULL || val[0] == 0) {
@@ -297,11 +244,9 @@
 static int
 cleanup(struct archive_read *a)
 {
-	struct mtree *mtree;
+	struct mtree *mtree = a->format->data;
 	struct mtree_entry *p, *q;
 
-	mtree = (struct mtree *)(a->format->data);
-	
 	/* Close any dangling file descriptor before freeing */
     if (mtree->fd >= 0) {
         close(mtree->fd);
@@ -322,7 +267,7 @@
 
 	free(mtree->buff);
 	free(mtree);
-	(a->format->data) = NULL;
+	a->format->data = NULL;
 	return (ARCHIVE_OK);
 }
 
@@ -387,7 +332,7 @@
 	 */
 	while (*nl == 0 && len == *avail && !quit) {
 		ssize_t diff = *ravail - *avail;
-		size_t nbytes_req = (*ravail+1023) & ~1023U;
+		size_t nbytes_req = ((size_t)*ravail + 1023) & ~1023U;
 		ssize_t tested;
 
 		/*
@@ -876,7 +821,7 @@
 		line = next;
 		next = line + strcspn(line, " \t\r\n");
 		eq = strchr(line, '=');
-		if (eq > next)
+		if (eq == NULL || eq > next)
 			len = next - line;
 		else
 			len = eq - line;
@@ -1121,12 +1066,10 @@
 static int
 read_header(struct archive_read *a, struct archive_entry *entry)
 {
-	struct mtree *mtree;
+	struct mtree *mtree = a->format->data;
 	char *p;
 	int r, use_next;
 
-	mtree = (struct mtree *)(a->format->data);
-
 	if (mtree->fd >= 0) {
 		close(mtree->fd);
 		mtree->fd = -1;
@@ -1781,8 +1724,6 @@
 		}
 		if (strcmp(key, "time") == 0) {
 			int64_t m;
-			int64_t my_time_t_max = get_time_t_max();
-			int64_t my_time_t_min = get_time_t_min();
 			long ns = 0;
 
 			*parsed_kws |= MTREE_HAS_MTIME;
@@ -1802,10 +1743,10 @@
 				else
 					ns = (long)v;
 			}
-			if (m > my_time_t_max)
-				m = my_time_t_max;
-			else if (m < my_time_t_min)
-				m = my_time_t_min;
+			if (m > TIME_MAX)
+				m = TIME_MAX;
+			else if (m < TIME_MIN)
+				m = TIME_MIN;
 			archive_entry_set_mtime(entry, (time_t)m, ns);
 			return (ARCHIVE_OK);
 		}
@@ -1892,11 +1833,10 @@
 read_data(struct archive_read *a, const void **buff, size_t *size,
     int64_t *offset)
 {
+	struct mtree *mtree = a->format->data;
 	size_t bytes_to_read;
 	ssize_t bytes_read;
-	struct mtree *mtree;
 
-	mtree = (struct mtree *)(a->format->data);
 	if (mtree->fd < 0) {
 		*buff = NULL;
 		*offset = 0;
@@ -1937,9 +1877,8 @@
 static int
 skip(struct archive_read *a)
 {
-	struct mtree *mtree;
+	struct mtree *mtree = a->format->data;
 
-	mtree = (struct mtree *)(a->format->data);
 	if (mtree->fd >= 0) {
 		close(mtree->fd);
 		mtree->fd = -1;
diff --git a/c/archive_read_support_format_rar.c b/c/archive_read_support_format_rar.c
--- a/c/archive_read_support_format_rar.c
+++ b/c/archive_read_support_format_rar.c
@@ -42,6 +42,7 @@
 #include "archive_endian.h"
 #include "archive_entry.h"
 #include "archive_entry_locale.h"
+#include "archive_integer.h"
 #include "archive_ppmd7_private.h"
 #include "archive_private.h"
 #include "archive_read_private.h"
@@ -201,8 +202,8 @@
 struct huffman_code
 {
   struct huffman_tree_node *tree;
-  int numentries;
-  int numallocatedentries;
+  size_t numentries;
+  size_t numallocatedentries;
   int minlength;
   int maxlength;
   int tablesize;
@@ -227,8 +228,6 @@
 {
   uint8_t *staticdata;
   uint32_t staticdatalen;
-  uint8_t *globalbackup;
-  uint32_t globalbackuplen;
   uint64_t fingerprint;
   uint32_t usagecount;
   uint32_t oldfilterlength;
@@ -499,7 +498,7 @@
 static int
 rar_br_fillup(struct archive_read *a, struct rar_br *br)
 {
-  struct rar *rar = (struct rar *)(a->format->data);
+  struct rar *rar = a->format->data;
   int n = CACHE_BITS - br->cache_avail;
 
   for (;;) {
@@ -594,7 +593,7 @@
 static int
 rar_br_preparation(struct archive_read *a, struct rar_br *br)
 {
-  struct rar *rar = (struct rar *)(a->format->data);
+  struct rar *rar = a->format->data;
 
   if (rar->bytes_remaining > 0) {
     br->next_in = rar_read_ahead(a, 1, &(br->avail_in));
@@ -709,7 +708,7 @@
 ppmd_read(void *p)
 {
   struct archive_read *a = ((IByteIn*)p)->a;
-  struct rar *rar = (struct rar *)(a->format->data);
+  struct rar *rar = a->format->data;
   struct rar_br *br = &(rar->br);
   Byte b;
   if (!rar_br_read_ahead(a, br, 8))
@@ -777,7 +776,7 @@
 archive_read_format_rar_has_encrypted_entries(struct archive_read *_a)
 {
   if (_a && _a->format) {
-    struct rar * rar = (struct rar *)_a->format->data;
+    struct rar *rar = _a->format->data;
     if (rar) {
       return rar->has_encrypted_entries;
     }
@@ -877,10 +876,9 @@
 archive_read_format_rar_options(struct archive_read *a,
     const char *key, const char *val)
 {
-  struct rar *rar;
+  struct rar *rar = a->format->data;
   int ret = ARCHIVE_FAILED;
 
-  rar = (struct rar *)(a->format->data);
   if (strcmp(key, "hdrcharset")  == 0) {
     if (val == NULL || val[0] == 0)
       archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
@@ -907,9 +905,9 @@
 archive_read_format_rar_read_header(struct archive_read *a,
                                     struct archive_entry *entry)
 {
+  struct rar *rar = a->format->data;
   const void *h;
   const char *p;
-  struct rar *rar;
   int64_t skip;
   char head_type;
   int ret;
@@ -920,8 +918,6 @@
   if (a->archive.archive_format_name == NULL)
     a->archive.archive_format_name = "RAR";
 
-  rar = (struct rar *)(a->format->data);
-
   /*
    * It should be sufficient to call archive_read_next_header() for
    * a reader to determine if an entry is encrypted or not. If the
@@ -1100,7 +1096,7 @@
 archive_read_format_rar_read_data(struct archive_read *a, const void **buff,
                                   size_t *size, int64_t *offset)
 {
-  struct rar *rar = (struct rar *)(a->format->data);
+  struct rar *rar = a->format->data;
   int ret;
 
   if (rar->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) {
@@ -1151,12 +1147,10 @@
 static int
 archive_read_format_rar_read_data_skip(struct archive_read *a)
 {
-  struct rar *rar;
+  struct rar *rar = a->format->data;
   int64_t bytes_skipped;
   int ret;
 
-  rar = (struct rar *)(a->format->data);
-
   if (rar->bytes_unconsumed > 0) {
       /* Consume as much as the decompressor actually used. */
       __archive_read_consume(a, rar->bytes_unconsumed);
@@ -1189,9 +1183,9 @@
 archive_read_format_rar_seek_data(struct archive_read *a, int64_t offset,
     int whence)
 {
+  struct rar *rar = a->format->data;
   int64_t client_offset, ret;
   size_t i;
-  struct rar *rar = (struct rar *)(a->format->data);
 
   if (rar->compression_method == COMPRESS_METHOD_STORE)
   {
@@ -1294,6 +1288,12 @@
               "Error during seek of RAR file");
             return (ARCHIVE_FAILED);
           }
+          if (rar->cursor == 0)
+          {
+            archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+              "Invalid RAR seek cursor state");
+            return (ARCHIVE_FAILED);
+          }
           client_offset += rar->dbo[rar->cursor].start_offset -
             rar->dbo[rar->cursor-1].end_offset;
           continue;
@@ -1342,9 +1342,8 @@
 static int
 archive_read_format_rar_cleanup(struct archive_read *a)
 {
-  struct rar *rar;
+  struct rar *rar = a->format->data;
 
-  rar = (struct rar *)(a->format->data);
   free_codes(a);
   clear_filters(&rar->filters);
   free(rar->filename);
@@ -1354,7 +1353,7 @@
   free(rar->lzss.window);
   __archive_ppmd7_functions.Ppmd7_Free(&rar->ppmd7_context);
   free(rar);
-  (a->format->data) = NULL;
+  a->format->data = NULL;
   return (ARCHIVE_OK);
 }
 
@@ -1362,9 +1361,9 @@
 read_header(struct archive_read *a, struct archive_entry *entry,
             char head_type)
 {
+  struct rar *rar = a->format->data;
   const void *h;
   const char *p, *endp;
-  struct rar *rar;
   struct rar_header rar_header;
   struct rar_file_header file_header;
   int64_t header_size;
@@ -1380,8 +1379,6 @@
   char *newptr;
   size_t newsize;
 
-  rar = (struct rar *)(a->format->data);
-
   /* Setup a string conversion object for non-rar-unicode filenames. */
   sconv = rar->opt_sconv;
   if (sconv == NULL) {
@@ -1668,16 +1665,18 @@
           return (ARCHIVE_FATAL);
       }
       fn_sconv = rar->sconv_utf8;
-      while ((strp = strchr(filename, '\\')) != NULL)
-        *strp = '/';
+      strp = filename;
+      while ((strp = strchr(strp, '\\')) != NULL)
+        *strp++ = '/';
       p += filename_size;
     }
   }
   else
   {
     fn_sconv = sconv;
-    while ((strp = strchr(filename, '\\')) != NULL)
-      *strp = '/';
+    strp = filename;
+    while ((strp = strchr(strp, '\\')) != NULL)
+      *strp++ = '/';
     p += filename_size;
   }
 
@@ -1962,12 +1961,11 @@
 read_symlink_stored(struct archive_read *a, struct archive_entry *entry,
                     struct archive_string_conv *sconv)
 {
+  struct rar *rar = a->format->data;
   const void *h;
   const char *p;
-  struct rar *rar;
   int ret = (ARCHIVE_OK);
 
-  rar = (struct rar *)(a->format->data);
   if ((uintmax_t)rar->packed_size > SIZE_MAX)
   {
     archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
@@ -2004,10 +2002,9 @@
 read_data_stored(struct archive_read *a, const void **buff, size_t *size,
                  int64_t *offset)
 {
-  struct rar *rar;
+  struct rar *rar = a->format->data;
   ssize_t bytes_avail;
 
-  rar = (struct rar *)(a->format->data);
   if (rar->bytes_remaining == 0 &&
     !(rar->main_flags & MHD_VOLUME && rar->file_flags & FHD_SPLIT_AFTER))
   {
@@ -2052,13 +2049,11 @@
   if (looper++ > MAX_COMPRESS_DEPTH)
     return (ARCHIVE_FAILED);
 
-  struct rar *rar;
+  struct rar *rar = a->format->data;
   int64_t start, end;
   size_t bs;
   int ret = (ARCHIVE_OK), sym, code, lzss_offset, length, i;
 
-  rar = (struct rar *)(a->format->data);
-
   do {
     if (!rar->valid)
       return (ARCHIVE_FAILED);
@@ -2303,11 +2298,11 @@
 static int
 parse_codes(struct archive_read *a)
 {
+  struct rar *rar = a->format->data;
   int i, j, val, n, r;
   unsigned char bitlengths[MAX_SYMBOLS], zerocount, ppmd_flags;
   unsigned int maxorder;
   struct huffman_code precode;
-  struct rar *rar = (struct rar *)(a->format->data);
   struct rar_br *br = &(rar->br);
 
   free_codes(a);
@@ -2589,7 +2584,7 @@
 static void
 free_codes(struct archive_read *a)
 {
-  struct rar *rar = (struct rar *)(a->format->data);
+  struct rar *rar = a->format->data;
   free(rar->maincode.tree);
   free(rar->offsetcode.tree);
   free(rar->lowoffsetcode.tree);
@@ -2608,10 +2603,10 @@
 static int
 read_next_symbol(struct archive_read *a, struct huffman_code *code)
 {
+  struct rar *rar = a->format->data;
   unsigned char bit;
   unsigned int bits;
   int length, value, node;
-  struct rar *rar;
   struct rar_br *br;
 
   if (!code->table)
@@ -2620,7 +2615,6 @@
       return -1;
   }
 
-  rar = (struct rar *)(a->format->data);
   br = &(rar->br);
 
   /* Look ahead (peek) at bits */
@@ -2825,11 +2819,16 @@
 {
   void *new_tree;
   if (code->numallocatedentries == code->numentries) {
-    int new_num_entries = 256;
-    if (code->numentries > 0) {
-        new_num_entries = code->numentries * 2;
-    }
-    new_tree = realloc(code->tree, new_num_entries * sizeof(*code->tree));
+    size_t size, new_num_entries;
+
+    if (code->numentries == 0)
+        new_num_entries = 256;
+    else if (archive_ckd_mul_size(&new_num_entries, code->numentries, 2)
+      || new_num_entries > INT_MAX)
+        return -1;
+    if (archive_ckd_mul_size(&size, new_num_entries, sizeof(*code->tree)))
+        return -1;
+    new_tree = realloc(code->tree, size);
     if (new_tree == NULL)
         return (-1);
     code->tree = (struct huffman_tree_node *)new_tree;
@@ -2870,7 +2869,7 @@
                       "Huffman tree was not created");
     return (ARCHIVE_FAILED);
   }
-  if (node < 0 || node >= code->numentries)
+  if (node < 0 || (size_t)node >= code->numentries)
   {
     archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
                       "Invalid location to Huffman tree specified");
@@ -2958,9 +2957,9 @@
   static const unsigned char shortbits[] =
     { 2, 2, 3, 4, 5, 6, 6, 6 };
 
+  struct rar *rar = a->format->data;
   int symbol, offs, len, offsindex, lensymbol, i, offssymbol, lowoffsetsymbol;
   unsigned char newfile;
-  struct rar *rar = (struct rar *)(a->format->data);
   struct rar_br *br = &(rar->br);
 
   if (rar->filters.filterstart < *end)
@@ -3148,8 +3147,8 @@
 copy_from_lzss_window(struct archive_read *a, uint8_t *buffer,
                       int64_t startpos, int length)
 {
+  struct rar *rar = a->format->data;
   int windowoffs, firstpart;
-  struct rar *rar = (struct rar *)(a->format->data);
 
   windowoffs = lzss_offset_for_position(&rar->lzss, startpos);
   firstpart = lzss_size(&rar->lzss) - windowoffs;
@@ -3176,8 +3175,8 @@
 copy_from_lzss_window_to_unp(struct archive_read *a, const void **buffer,
                              int64_t startpos, size_t length)
 {
+  struct rar *rar = a->format->data;
   int windowoffs, firstpart;
-  struct rar *rar = (struct rar *)(a->format->data);
 
   if (length > rar->unp_buffer_size)
   {
@@ -3233,7 +3232,7 @@
 static const void *
 rar_read_ahead(struct archive_read *a, size_t min, ssize_t *avail)
 {
-  struct rar *rar = (struct rar *)(a->format->data);
+  struct rar *rar = a->format->data;
   const void *h;
   int ret;
 
@@ -3270,7 +3269,7 @@
 static int
 parse_filter(struct archive_read *a, const uint8_t *bytes, uint16_t length, uint8_t flags)
 {
-  struct rar *rar = (struct rar *)(a->format->data);
+  struct rar *rar = a->format->data;
   struct rar_filters *filters = &rar->filters;
 
   struct memory_bit_reader br = { 0 };
@@ -3438,7 +3437,7 @@
 static int
 run_filters(struct archive_read *a)
 {
-  struct rar *rar = (struct rar *)(a->format->data);
+  struct rar *rar = a->format->data;
   struct rar_filters *filters = &rar->filters;
   struct rar_filter *filter = filters->stack;
   struct rar_filter *f;
@@ -3606,7 +3605,6 @@
   {
     struct rar_program_code *next = prog->next;
     free(prog->staticdata);
-    free(prog->globalbackup);
     free(prog);
     prog = next;
   }
@@ -3659,7 +3657,7 @@
 static int
 read_filter(struct archive_read *a, int64_t *end)
 {
-  struct rar *rar = (struct rar *)(a->format->data);
+  struct rar *rar = a->format->data;
   uint8_t flags, val, *code;
   uint16_t length, i;
 
@@ -3855,7 +3853,7 @@
       state.delta[0] = state.lastdelta;
       predbyte = ((8 * state.lastbyte + state.weight[0] * state.delta[0] + state.weight[1] * state.delta[1] + state.weight[2] * state.delta[2]) >> 3) & 0xFF;
       byte = (predbyte - delta) & 0xFF;
-      prederror = delta << 3;
+      prederror = delta * 8;
       state.error[0] += abs(prederror);
       state.error[1] += abs(prederror - state.delta[0]); state.error[2] += abs(prederror + state.delta[0]);
       state.error[3] += abs(prederror - state.delta[1]); state.error[4] += abs(prederror + state.delta[1]);
@@ -3912,7 +3910,7 @@
 static int
 rar_decode_byte(struct archive_read *a, uint8_t *byte)
 {
-  struct rar *rar = (struct rar *)(a->format->data);
+  struct rar *rar = a->format->data;
   struct rar_br *br = &(rar->br);
   if (!rar_br_read_ahead(a, br, 8))
     return 0;
diff --git a/c/archive_read_support_format_rar5.c b/c/archive_read_support_format_rar5.c
--- a/c/archive_read_support_format_rar5.c
+++ b/c/archive_read_support_format_rar5.c
@@ -44,4443 +44,4457 @@
 
 #include "archive_entry.h"
 #include "archive_entry_locale.h"
-#include "archive_ppmd7_private.h"
-#include "archive_entry_private.h"
-#include "archive_time_private.h"
-
-#ifdef HAVE_BLAKE2_H
-#include <blake2.h>
-#else
-#include "archive_blake2.h"
-#endif
-
-/*#define CHECK_CRC_ON_SOLID_SKIP*/
-/*#define DONT_FAIL_ON_CRC_ERROR*/
-/*#define DEBUG*/
-
-#define rar5_min(a, b) (((a) > (b)) ? (b) : (a))
-#define rar5_max(a, b) (((a) > (b)) ? (a) : (b))
-#define rar5_countof(X) ((const ssize_t) (sizeof(X) / sizeof(*X)))
-
-#if defined DEBUG
-#define DEBUG_CODE if(1)
-#define LOG(...) do { printf("rar5: " __VA_ARGS__); puts(""); } while(0)
-#else
-#define DEBUG_CODE if(0)
-#endif
-
-/* Real RAR5 magic number is:
- *
- * 0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x01, 0x00
- * "Rar!→•☺·\x00"
- *
- * Retrieved with `rar5_signature()` by XOR'ing it with 0xA1, because I don't
- * want to put this magic sequence in each binary that uses libarchive, so
- * applications that scan through the file for this marker won't trigger on
- * this "false" one.
- *
- * The array itself is decrypted in `rar5_init` function. */
-
-static const unsigned char rar5_signature_xor[] = {
-	243, 192, 211, 128, 187, 166, 160, 161
-};
-static const size_t g_unpack_window_size = 0x20000;
-
-/* These could have been static const's, but they aren't, because of
- * Visual Studio. */
-#define MAX_NAME_IN_CHARS 2048
-#define MAX_NAME_IN_BYTES (4 * MAX_NAME_IN_CHARS)
-
-struct file_header {
-	ssize_t bytes_remaining;
-	ssize_t unpacked_size;
-	int64_t last_offset;         /* Used in sanity checks. */
-	int64_t last_size;           /* Used in sanity checks. */
-
-	uint8_t solid : 1;           /* Is this a solid stream? */
-	uint8_t service : 1;         /* Is this file a service data? */
-	uint8_t eof : 1;             /* Did we finish unpacking the file? */
-	uint8_t dir : 1;             /* Is this file entry a directory? */
-
-	/* Optional time fields. */
-	int64_t e_mtime;
-	int64_t e_ctime;
-	int64_t e_atime;
-	uint32_t e_mtime_ns;
-	uint32_t e_ctime_ns;
-	uint32_t e_atime_ns;
-
-	/* Optional hash fields. */
-	uint32_t stored_crc32;
-	uint32_t calculated_crc32;
-	uint8_t blake2sp[32];
-	blake2sp_state b2state;
-	char has_blake2;
-
-	/* Optional redir fields */
-	uint64_t redir_type;
-	uint64_t redir_flags;
-
-	ssize_t solid_window_size; /* Used in file format check. */
-};
-
-enum EXTRA {
-	EX_CRYPT = 0x01,
-	EX_HASH = 0x02,
-	EX_HTIME = 0x03,
-	EX_VERSION = 0x04,
-	EX_REDIR = 0x05,
-	EX_UOWNER = 0x06,
-	EX_SUBDATA = 0x07
-};
-
-#define REDIR_SYMLINK_IS_DIR	1
-
-enum REDIR_TYPE {
-	REDIR_TYPE_NONE = 0,
-	REDIR_TYPE_UNIXSYMLINK = 1,
-	REDIR_TYPE_WINSYMLINK = 2,
-	REDIR_TYPE_JUNCTION = 3,
-	REDIR_TYPE_HARDLINK = 4,
-	REDIR_TYPE_FILECOPY = 5,
-};
-
-#define	OWNER_USER_NAME		0x01
-#define	OWNER_GROUP_NAME	0x02
-#define	OWNER_USER_UID		0x04
-#define	OWNER_GROUP_GID		0x08
-#define	OWNER_MAXNAMELEN	256
-
-enum FILTER_TYPE {
-	FILTER_DELTA = 0,   /* Generic pattern. */
-	FILTER_E8    = 1,   /* Intel x86 code. */
-	FILTER_E8E9  = 2,   /* Intel x86 code. */
-	FILTER_ARM   = 3,   /* ARM code. */
-	FILTER_AUDIO = 4,   /* Audio filter, not used in RARv5. */
-	FILTER_RGB   = 5,   /* Color palette, not used in RARv5. */
-	FILTER_ITANIUM = 6, /* Intel's Itanium, not used in RARv5. */
-	FILTER_PPM   = 7,   /* Predictive pattern matching, not used in
-			       RARv5. */
-	FILTER_NONE  = 8,
-};
-
-struct filter_info {
-	int type;
-	int channels;
-	int pos_r;
-
-	int64_t block_start;
-	ssize_t block_length;
-	uint16_t width;
-};
-
-struct data_ready {
-	char used;
-	const uint8_t* buf;
-	size_t size;
-	int64_t offset;
-};
-
-struct cdeque {
-	uint16_t beg_pos;
-	uint16_t end_pos;
-	uint16_t cap_mask;
-	uint16_t size;
-	size_t* arr;
-};
-
-struct decode_table {
-	uint32_t size;
-	int32_t decode_len[16];
-	uint32_t decode_pos[16];
-	uint32_t quick_bits;
-	uint8_t quick_len[1 << 10];
-	uint16_t quick_num[1 << 10];
-	uint16_t decode_num[306];
-};
-
-struct comp_state {
-	/* Flag used to specify if unpacker needs to reinitialize the
-	   uncompression context. */
-	uint8_t initialized : 1;
-
-	/* Flag used when applying filters. */
-	uint8_t all_filters_applied : 1;
-
-	/* Flag used to skip file context reinitialization, used when unpacker
-	   is skipping through different multivolume archives. */
-	uint8_t switch_multivolume : 1;
-
-	/* Flag used to specify if unpacker has processed the whole data block
-	   or just a part of it. */
-	uint8_t block_parsing_finished : 1;
-
-	/* Flag used to indicate that a previous file using this buffer was
-	   encrypted, meaning no data in the buffer can be trusted */
-	uint8_t data_encrypted : 1;
-
-	signed int notused : 3;
-
-	int flags;                   /* Uncompression flags. */
-	int method;                  /* Uncompression algorithm method. */
-	int version;                 /* Uncompression algorithm version. */
-	ssize_t window_size;         /* Size of window_buf. */
-	uint8_t* window_buf;         /* Circular buffer used during
-	                                decompression. */
-	uint8_t* filtered_buf;       /* Buffer used when applying filters. */
-	const uint8_t* block_buf;    /* Buffer used when merging blocks. */
-	ssize_t window_mask;         /* Convenience field; window_size - 1. */
-	int64_t write_ptr;           /* This amount of data has been unpacked
-					in the window buffer. */
-	int64_t last_write_ptr;      /* This amount of data has been stored in
-	                                the output file. */
-	int64_t last_unstore_ptr;    /* Counter of bytes extracted during
-	                                unstoring. This is separate from
-	                                last_write_ptr because of how SERVICE
-	                                base blocks are handled during skipping
-	                                in solid multiarchive archives. */
-	int64_t solid_offset;        /* Additional offset inside the window
-	                                buffer, used in unpacking solid
-	                                archives. */
-	ssize_t cur_block_size;      /* Size of current data block. */
-	int last_len;                /* Flag used in lzss decompression. */
-
-	/* Decode tables used during lzss uncompression. */
-
-#define HUFF_BC 20
-	struct decode_table bd;      /* huffman bit lengths */
-#define HUFF_NC 306
-	struct decode_table ld;      /* literals */
-#define HUFF_DC 64
-	struct decode_table dd;      /* distances */
-#define HUFF_LDC 16
-	struct decode_table ldd;     /* lower bits of distances */
-#define HUFF_RC 44
-	struct decode_table rd;      /* repeating distances */
-#define HUFF_TABLE_SIZE (HUFF_NC + HUFF_DC + HUFF_RC + HUFF_LDC)
-
-	/* Circular deque for storing filters. */
-	struct cdeque filters;
-	int64_t last_block_start;    /* Used for sanity checking. */
-	ssize_t last_block_length;   /* Used for sanity checking. */
-
-	/* Distance cache used during lzss uncompression. */
-	int dist_cache[4];
-
-	/* Data buffer stack. */
-	struct data_ready dready[2];
-};
-
-/* Bit reader state. */
-struct bit_reader {
-	int8_t bit_addr;    /* Current bit pointer inside current byte. */
-	int in_addr;        /* Current byte pointer. */
-};
-
-/* RARv5 block header structure. Use bf_* functions to get values from
- * block_flags_u8 field. I.e. bf_byte_count, etc. */
-struct compressed_block_header {
-	/* block_flags_u8 contain fields encoded in little-endian bitfield:
-	 *
-	 * - table present flag (shr 7, and 1),
-	 * - last block flag    (shr 6, and 1),
-	 * - byte_count         (shr 3, and 7),
-	 * - bit_size           (shr 0, and 7).
-	 */
-	uint8_t block_flags_u8;
-	uint8_t block_cksum;
-};
-
-/* RARv5 main header structure. */
-struct main_header {
-	/* Does the archive contain solid streams? */
-	uint8_t solid : 1;
-
-	/* If this a multi-file archive? */
-	uint8_t volume : 1;
-	uint8_t endarc : 1;
-	uint8_t notused : 5;
-
-	unsigned int vol_no;
-};
-
-struct generic_header {
-	uint8_t split_after : 1;
-	uint8_t split_before : 1;
-	uint8_t padding : 6;
-	int size;
-	int last_header_id;
-};
-
-struct multivolume {
-	unsigned int expected_vol_no;
-	uint8_t* push_buf;
-};
-
-/* Main context structure. */
-struct rar5 {
-	int header_initialized;
-
-	/* Set to 1 if current file is positioned AFTER the magic value
-	 * of the archive file. This is used in header reading functions. */
-	int skipped_magic;
-
-	/* Set to not zero if we're in skip mode (either by calling
-	 * rar5_data_skip function or when skipping over solid streams).
-	 * Set to 0 when in * extraction mode. This is used during checksum
-	 * calculation functions. */
-	int skip_mode;
-
-	/* Set to not zero if we're in block merging mode (i.e. when switching
-	 * to another file in multivolume archive, last block from 1st archive
-	 * needs to be merged with 1st block from 2nd archive). This flag
-	 * guards against recursive use of the merging function, which doesn't
-	 * support recursive calls. */
-	int merge_mode;
-
-	/* An offset to QuickOpen list. This is not supported by this unpacker,
-	 * because we're focusing on streaming interface. QuickOpen is designed
-	 * to make things quicker for non-stream interfaces, so it's not our
-	 * use case. */
-	uint64_t qlist_offset;
-
-	/* An offset to additional Recovery data. This is not supported by this
-	 * unpacker. Recovery data are additional Reed-Solomon codes that could
-	 * be used to calculate bytes that are missing in archive or are
-	 * corrupted. */
-	uint64_t rr_offset;
-
-	/* Various context variables grouped to different structures. */
-	struct generic_header generic;
-	struct main_header main;
-	struct comp_state cstate;
-	struct file_header file;
-	struct bit_reader bits;
-	struct multivolume vol;
-
-	/* The header of currently processed RARv5 block. Used in main
-	 * decompression logic loop. */
-	struct compressed_block_header last_block_hdr;
-
-	/*
-	 * Custom field to denote that this archive contains encrypted entries
-	 */
-	int has_encrypted_entries;
-	int headers_are_encrypted;
-};
-
-/* Forward function declarations. */
-
-static void rar5_signature(char *buf);
-static int verify_global_checksums(struct archive_read* a);
-static int rar5_read_data_skip(struct archive_read *a);
-static int push_data_ready(struct archive_read* a, struct rar5* rar,
-	const uint8_t* buf, size_t size, int64_t offset);
-static void clear_data_ready_stack(struct rar5* rar);
-static void rar5_deinit(struct rar5* rar);
-
-/* CDE_xxx = Circular Double Ended (Queue) return values. */
-enum CDE_RETURN_VALUES {
-	CDE_OK, CDE_ALLOC, CDE_PARAM, CDE_OUT_OF_BOUNDS,
-};
-
-/* Clears the contents of this circular deque. */
-static void cdeque_clear(struct cdeque* d) {
-	d->size = 0;
-	d->beg_pos = 0;
-	d->end_pos = 0;
-}
-
-/* Creates a new circular deque object. Capacity must be power of 2: 8, 16, 32,
- * 64, 256, etc. When the user will add another item above current capacity,
- * the circular deque will overwrite the oldest entry. */
-static int cdeque_init(struct cdeque* d, int max_capacity_power_of_2) {
-	if(d == NULL || max_capacity_power_of_2 == 0)
-		return CDE_PARAM;
-
-	d->cap_mask = max_capacity_power_of_2 - 1;
-	d->arr = NULL;
-
-	if((max_capacity_power_of_2 & d->cap_mask) != 0)
-		return CDE_PARAM;
-
-	cdeque_clear(d);
-	d->arr = malloc(sizeof(void*) * max_capacity_power_of_2);
-
-	return d->arr ? CDE_OK : CDE_ALLOC;
-}
-
-/* Return the current size (not capacity) of circular deque `d`. */
-static size_t cdeque_size(struct cdeque* d) {
-	return d->size;
-}
-
-/* Returns the first element of current circular deque. Note that this function
- * doesn't perform any bounds checking. If you need bounds checking, use
- * `cdeque_front()` function instead. */
-static void cdeque_front_fast(struct cdeque* d, void** value) {
-	*value = (void*) d->arr[d->beg_pos];
-}
-
-/* Returns the first element of current circular deque. This function
- * performs bounds checking. */
-static int cdeque_front(struct cdeque* d, void** value) {
-	if(d->size > 0) {
-		cdeque_front_fast(d, value);
-		return CDE_OK;
-	} else
-		return CDE_OUT_OF_BOUNDS;
-}
-
-/* Pushes a new element into the end of this circular deque object. */
-static int cdeque_push_back(struct cdeque* d, void* item) {
-	if(d == NULL)
-		return CDE_PARAM;
-
-	if(d->size == d->cap_mask + 1)
-		return CDE_OUT_OF_BOUNDS;
-
-	d->arr[d->end_pos] = (size_t) item;
-	d->end_pos = (d->end_pos + 1) & d->cap_mask;
-	d->size++;
-
-	return CDE_OK;
-}
-
-/* Pops a front element of this circular deque object and returns its value.
- * This function doesn't perform any bounds checking. */
-static void cdeque_pop_front_fast(struct cdeque* d, void** value) {
-	*value = (void*) d->arr[d->beg_pos];
-	d->beg_pos = (d->beg_pos + 1) & d->cap_mask;
-	d->size--;
-}
-
-/* Pops a front element of this circular deque object and returns its value.
- * This function performs bounds checking. */
-static int cdeque_pop_front(struct cdeque* d, void** value) {
-	if(!d || !value)
-		return CDE_PARAM;
-
-	if(d->size == 0)
-		return CDE_OUT_OF_BOUNDS;
-
-	cdeque_pop_front_fast(d, value);
-	return CDE_OK;
-}
-
-/* Convenience function to cast filter_info** to void **. */
-static void** cdeque_filter_p(struct filter_info** f) {
-	return (void**) (size_t) f;
-}
-
-/* Convenience function to cast filter_info* to void *. */
-static void* cdeque_filter(struct filter_info* f) {
-	return (void**) (size_t) f;
-}
-
-/* Destroys this circular deque object. Deallocates the memory of the
- * collection buffer, but doesn't deallocate the memory of any pointer passed
- * to this deque as a value. */
-static void cdeque_free(struct cdeque* d) {
-	if(!d)
-		return;
-
-	if(!d->arr)
-		return;
-
-	free(d->arr);
-
-	d->arr = NULL;
-	d->beg_pos = -1;
-	d->end_pos = -1;
-	d->cap_mask = 0;
-}
-
-static inline
-uint8_t bf_bit_size(const struct compressed_block_header* hdr) {
-	return hdr->block_flags_u8 & 7;
-}
-
-static inline
-uint8_t bf_byte_count(const struct compressed_block_header* hdr) {
-	return (hdr->block_flags_u8 >> 3) & 7;
-}
-
-static inline
-uint8_t bf_is_table_present(const struct compressed_block_header* hdr) {
-	return (hdr->block_flags_u8 >> 7) & 1;
-}
-
-static inline
-uint8_t bf_is_last_block(const struct compressed_block_header* hdr) {
-	return (hdr->block_flags_u8 >> 6) & 1;
-}
-
-static inline struct rar5* get_context(struct archive_read* a) {
-	return (struct rar5*) a->format->data;
-}
-
-/* Convenience functions used by filter implementations. */
-static void circular_memcpy(uint8_t* dst, uint8_t* window, const ssize_t mask,
-    int64_t start, int64_t end)
-{
-	if((start & mask) > (end & mask)) {
-		ssize_t len1 = mask + 1 - (start & mask);
-		ssize_t len2 = end & mask;
-
-		memcpy(dst, &window[start & mask], len1);
-		memcpy(dst + len1, window, len2);
-	} else {
-		memcpy(dst, &window[start & mask], (size_t) (end - start));
-	}
-}
-
-static uint32_t read_filter_data(struct rar5* rar, uint32_t offset) {
-	uint8_t linear_buf[4];
-	circular_memcpy(linear_buf, rar->cstate.window_buf,
-	    rar->cstate.window_mask, offset, offset + 4);
-	return archive_le32dec(linear_buf);
-}
-
-static void write_filter_data(struct rar5* rar, uint32_t offset,
-    uint32_t value)
-{
-	archive_le32enc(&rar->cstate.filtered_buf[offset], value);
-}
-
-/* Allocates a new filter descriptor and adds it to the filter array. */
-static struct filter_info* add_new_filter(struct rar5* rar) {
-	struct filter_info* f = calloc(1, sizeof(*f));
-
-	if(!f) {
-		return NULL;
-	}
-
-	if (CDE_OK != cdeque_push_back(&rar->cstate.filters, cdeque_filter(f))) {
-		free(f);
-		return NULL;
-	}
-
-	return f;
-}
-
-static int run_delta_filter(struct rar5* rar, struct filter_info* flt) {
-	int i;
-	ssize_t dest_pos, src_pos = 0;
-
-	for(i = 0; i < flt->channels; i++) {
-		uint8_t prev_byte = 0;
-		for(dest_pos = i;
-				dest_pos < flt->block_length;
-				dest_pos += flt->channels)
-		{
-			uint8_t byte;
-
-			byte = rar->cstate.window_buf[
-			    (rar->cstate.solid_offset + flt->block_start +
-			    src_pos) & rar->cstate.window_mask];
-
-			prev_byte -= byte;
-			rar->cstate.filtered_buf[dest_pos] = prev_byte;
-			src_pos++;
-		}
-	}
-
-	return ARCHIVE_OK;
-}
-
-static int run_e8e9_filter(struct rar5* rar, struct filter_info* flt,
-		int extended)
-{
-	const uint32_t file_size = 0x1000000;
-	ssize_t i;
-
-	circular_memcpy(rar->cstate.filtered_buf,
-	    rar->cstate.window_buf, rar->cstate.window_mask,
-	    rar->cstate.solid_offset + flt->block_start,
-	    rar->cstate.solid_offset + flt->block_start + flt->block_length);
-
-	for(i = 0; i < flt->block_length - 4;) {
-		uint8_t b = rar->cstate.window_buf[
-		    (rar->cstate.solid_offset + flt->block_start +
-		    i++) & rar->cstate.window_mask];
-
-		/*
-		 * 0xE8 = x86's call <relative_addr_uint32> (function call)
-		 * 0xE9 = x86's jmp <relative_addr_uint32> (unconditional jump)
-		 */
-		if(b == 0xE8 || (extended && b == 0xE9)) {
-
-			uint32_t addr;
-			uint32_t offset = (i + flt->block_start) % file_size;
-
-			addr = read_filter_data(rar,
-			    (uint32_t)(rar->cstate.solid_offset +
-			    flt->block_start + i) & rar->cstate.window_mask);
-
-			if(addr & 0x80000000) {
-				if(((addr + offset) & 0x80000000) == 0) {
-					write_filter_data(rar, (uint32_t)i,
-					    addr + file_size);
-				}
-			} else {
-				if((addr - file_size) & 0x80000000) {
-					uint32_t naddr = addr - offset;
-					write_filter_data(rar, (uint32_t)i,
-					    naddr);
-				}
-			}
-
-			i += 4;
-		}
-	}
-
-	return ARCHIVE_OK;
-}
-
-static int run_arm_filter(struct rar5* rar, struct filter_info* flt) {
-	ssize_t i = 0;
-	uint32_t offset;
-
-	circular_memcpy(rar->cstate.filtered_buf,
-	    rar->cstate.window_buf, rar->cstate.window_mask,
-	    rar->cstate.solid_offset + flt->block_start,
-	    rar->cstate.solid_offset + flt->block_start + flt->block_length);
-
-	for(i = 0; i < flt->block_length - 3; i += 4) {
-		uint8_t* b = &rar->cstate.window_buf[
-		    (rar->cstate.solid_offset +
-		    flt->block_start + i + 3) & rar->cstate.window_mask];
-
-		if(*b == 0xEB) {
-			/* 0xEB = ARM's BL (branch + link) instruction. */
-			offset = read_filter_data(rar,
-			    (rar->cstate.solid_offset + flt->block_start + i) &
-			     (uint32_t)rar->cstate.window_mask) & 0x00ffffff;
-
-			offset -= (uint32_t) ((i + flt->block_start) / 4);
-			offset = (offset & 0x00ffffff) | 0xeb000000;
-			write_filter_data(rar, (uint32_t)i, offset);
-		}
-	}
-
-	return ARCHIVE_OK;
-}
-
-static int run_filter(struct archive_read* a, struct filter_info* flt) {
-	int ret;
-	struct rar5* rar = get_context(a);
-
-	clear_data_ready_stack(rar);
-	free(rar->cstate.filtered_buf);
-
-	rar->cstate.filtered_buf = malloc(flt->block_length);
-	if(!rar->cstate.filtered_buf) {
-		archive_set_error(&a->archive, ENOMEM,
-		    "Can't allocate memory for filter data");
-		return ARCHIVE_FATAL;
-	}
-
-	switch(flt->type) {
-		case FILTER_DELTA:
-			ret = run_delta_filter(rar, flt);
-			break;
-
-		case FILTER_E8:
-			/* fallthrough */
-		case FILTER_E8E9:
-			ret = run_e8e9_filter(rar, flt,
-			    flt->type == FILTER_E8E9);
-			break;
-
-		case FILTER_ARM:
-			ret = run_arm_filter(rar, flt);
-			break;
-
-		default:
-			archive_set_error(&a->archive,
-			    ARCHIVE_ERRNO_FILE_FORMAT,
-			    "Unsupported filter type: 0x%x",
-			    (unsigned int)flt->type);
-			return ARCHIVE_FAILED;
-	}
-
-	if(ret != ARCHIVE_OK) {
-		/* Filter has failed. */
-		return ret;
-	}
-
-	if(ARCHIVE_OK != push_data_ready(a, rar, rar->cstate.filtered_buf,
-	    flt->block_length, rar->cstate.last_write_ptr))
-	{
-		archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
-		    "Stack overflow when submitting unpacked data");
-
-		return ARCHIVE_FATAL;
-	}
-
-	rar->cstate.last_write_ptr += flt->block_length;
-	return ARCHIVE_OK;
-}
-
-/* The `push_data` function submits the selected data range to the user.
- * Next call of `use_data` will use the pointer, size and offset arguments
- * that are specified here. These arguments are pushed to the FIFO stack here,
- * and popped from the stack by the `use_data` function. */
-static void push_data(struct archive_read* a, struct rar5* rar,
-    const uint8_t* buf, int64_t idx_begin, int64_t idx_end)
-{
-	const ssize_t wmask = rar->cstate.window_mask;
-	const ssize_t solid_write_ptr = (rar->cstate.solid_offset +
-	    rar->cstate.last_write_ptr) & wmask;
-
-	idx_begin += rar->cstate.solid_offset;
-	idx_end += rar->cstate.solid_offset;
-
-	/* Check if our unpacked data is wrapped inside the window circular
-	 * buffer.  If it's not wrapped, it can be copied out by using
-	 * a single memcpy, but when it's wrapped, we need to copy the first
-	 * part with one memcpy, and the second part with another memcpy. */
-
-	if((idx_begin & wmask) > (idx_end & wmask)) {
-		/* The data is wrapped (begin offset sis bigger than end
-		 * offset). */
-		const ssize_t frag1_size = rar->cstate.window_size -
-		    (idx_begin & wmask);
-		const ssize_t frag2_size = idx_end & wmask;
-
-		/* Copy the first part of the buffer first. */
-		push_data_ready(a, rar, buf + solid_write_ptr, frag1_size,
-		    rar->cstate.last_write_ptr);
-
-		/* Copy the second part of the buffer. */
-		push_data_ready(a, rar, buf, frag2_size,
-		    rar->cstate.last_write_ptr + frag1_size);
-
-		rar->cstate.last_write_ptr += frag1_size + frag2_size;
-	} else {
-		/* Data is not wrapped, so we can just use one call to copy the
-		 * data. */
-		push_data_ready(a, rar,
-		    buf + solid_write_ptr, (idx_end - idx_begin) & wmask,
-		    rar->cstate.last_write_ptr);
-
-		rar->cstate.last_write_ptr += idx_end - idx_begin;
-	}
-}
-
-/* Convenience function that submits the data to the user. It uses the
- * unpack window buffer as a source location. */
-static void push_window_data(struct archive_read* a, struct rar5* rar,
-    int64_t idx_begin, int64_t idx_end)
-{
-	push_data(a, rar, rar->cstate.window_buf, idx_begin, idx_end);
-}
-
-static int apply_filters(struct archive_read* a) {
-	struct filter_info* flt;
-	struct rar5* rar = get_context(a);
-	int ret;
-
-	rar->cstate.all_filters_applied = 0;
-
-	/* Get the first filter that can be applied to our data. The data
-	 * needs to be fully unpacked before the filter can be run. */
-	if(CDE_OK == cdeque_front(&rar->cstate.filters,
-	    cdeque_filter_p(&flt))) {
-		/* Check if our unpacked data fully covers this filter's
-		 * range. */
-		if(rar->cstate.write_ptr > flt->block_start &&
-		    rar->cstate.write_ptr >= flt->block_start +
-		    flt->block_length) {
-			/* Check if we have some data pending to be written
-			 * right before the filter's start offset. */
-			if(rar->cstate.last_write_ptr == flt->block_start) {
-				/* Run the filter specified by descriptor
-				 * `flt`. */
-				ret = run_filter(a, flt);
-				if(ret != ARCHIVE_OK) {
-					/* Filter failure, return error. */
-					return ret;
-				}
-
-				/* Filter descriptor won't be needed anymore
-				 * after it's used, * so remove it from the
-				 * filter list and free its memory. */
-				(void) cdeque_pop_front(&rar->cstate.filters,
-				    cdeque_filter_p(&flt));
-
-				free(flt);
-			} else {
-				/* We can't run filters yet, dump the memory
-				 * right before the filter. */
-				push_window_data(a, rar,
-				    rar->cstate.last_write_ptr,
-				    flt->block_start);
-			}
-
-			/* Return 'filter applied or not needed' state to the
-			 * caller. */
-			return ARCHIVE_RETRY;
-		}
-	}
-
-	rar->cstate.all_filters_applied = 1;
-	return ARCHIVE_OK;
-}
-
-static void dist_cache_push(struct rar5* rar, int value) {
-	int* q = rar->cstate.dist_cache;
-
-	q[3] = q[2];
-	q[2] = q[1];
-	q[1] = q[0];
-	q[0] = value;
-}
-
-static int dist_cache_touch(struct rar5* rar, int idx) {
-	int* q = rar->cstate.dist_cache;
-	int i, dist = q[idx];
-
-	for(i = idx; i > 0; i--)
-		q[i] = q[i - 1];
-
-	q[0] = dist;
-	return dist;
-}
-
-static void free_filters(struct rar5* rar) {
-	struct cdeque* d = &rar->cstate.filters;
-
-	/* Free any remaining filters. All filters should be naturally
-	 * consumed by the unpacking function, so remaining filters after
-	 * unpacking normally mean that unpacking wasn't successful.
-	 * But still of course we shouldn't leak memory in such case. */
-
-	/* cdeque_size() is a fast operation, so we can use it as a loop
-	 * expression. */
-	while(cdeque_size(d) > 0) {
-		struct filter_info* f = NULL;
-
-		/* Pop_front will also decrease the collection's size. */
-		if (CDE_OK == cdeque_pop_front(d, cdeque_filter_p(&f)))
-			free(f);
-	}
-
-	cdeque_clear(d);
-
-	/* Also clear out the variables needed for sanity checking. */
-	rar->cstate.last_block_start = 0;
-	rar->cstate.last_block_length = 0;
-}
-
-static void reset_file_context(struct rar5* rar) {
-	memset(&rar->file, 0, sizeof(rar->file));
-	blake2sp_init(&rar->file.b2state, 32);
-
-	if(rar->main.solid) {
-		rar->cstate.solid_offset += rar->cstate.write_ptr;
-	} else {
-		rar->cstate.solid_offset = 0;
-	}
-
-	rar->cstate.write_ptr = 0;
-	rar->cstate.last_write_ptr = 0;
-	rar->cstate.last_unstore_ptr = 0;
-
-	rar->file.redir_type = REDIR_TYPE_NONE;
-	rar->file.redir_flags = 0;
-
-	free_filters(rar);
-}
-
-static inline int get_archive_read(struct archive* a,
-    struct archive_read** ar)
-{
-	*ar = (struct archive_read*) a;
-	archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
-	    "archive_read_support_format_rar5");
-
-	return ARCHIVE_OK;
-}
-
-static int read_ahead(struct archive_read* a, size_t how_many,
-    const uint8_t** ptr)
-{
-	if(!ptr)
-		return 0;
-
-	*ptr = __archive_read_ahead(a, how_many, NULL);
-	if(*ptr == NULL) {
-		return 0;
-	}
-
-	return 1;
-}
-
-static int consume(struct archive_read* a, int64_t how_many) {
-	int ret;
-
-	ret = how_many == __archive_read_consume(a, how_many)
-		? ARCHIVE_OK
-		: ARCHIVE_FATAL;
-
-	return ret;
-}
-
-/**
- * Read a RAR5 variable sized numeric value. This value will be stored in
- * `pvalue`. The `pvalue_len` argument points to a variable that will receive
- * the byte count that was consumed in order to decode the `pvalue` value, plus
- * one.
- *
- * pvalue_len is optional and can be NULL.
- *
- * NOTE: if `pvalue_len` is NOT NULL, the caller needs to manually consume
- * the number of bytes that `pvalue_len` value contains. If the `pvalue_len`
- * is NULL, this consuming operation is done automatically.
- *
- * Returns 1 if *pvalue was successfully read.
- * Returns 0 if there was an error. In this case, *pvalue contains an
- *           invalid value.
- */
-
-static int read_var(struct archive_read* a, uint64_t* pvalue,
-    uint64_t* pvalue_len)
-{
-	uint64_t result = 0;
-	size_t shift, i;
-	const uint8_t* p;
-	uint8_t b;
-
-	/* We will read maximum of 8 bytes. We don't have to handle the
-	 * situation to read the RAR5 variable-sized value stored at the end of
-	 * the file, because such situation will never happen. */
-	if(!read_ahead(a, 8, &p))
-		return 0;
-
-	for(shift = 0, i = 0; i < 8; i++, shift += 7) {
-		b = p[i];
-
-		/* Strip the MSB from the input byte and add the resulting
-		 * number to the `result`. */
-		result += (b & (uint64_t)0x7F) << shift;
-
-		/* MSB set to 1 means we need to continue decoding process.
-		 * MSB set to 0 means we're done.
-		 *
-		 * This conditional checks for the second case. */
-		if((b & 0x80) == 0) {
-			if(pvalue) {
-				*pvalue = result;
-			}
-
-			/* If the caller has passed the `pvalue_len` pointer,
-			 * store the number of consumed bytes in it and do NOT
-			 * consume those bytes, since the caller has all the
-			 * information it needs to perform */
-			if(pvalue_len) {
-				*pvalue_len = 1 + i;
-			} else {
-				/* If the caller did not provide the
-				 * `pvalue_len` pointer, it will not have the
-				 * possibility to advance the file pointer,
-				 * because it will not know how many bytes it
-				 * needs to consume. This is why we handle
-				 * such situation here automatically. */
-				if(ARCHIVE_OK != consume(a, 1 + i)) {
-					return 0;
-				}
-			}
-
-			/* End of decoding process, return success. */
-			return 1;
-		}
-	}
-
-	/* The decoded value takes the maximum number of 8 bytes.
-	 * It's a maximum number of bytes, so end decoding process here
-	 * even if the first bit of last byte is 1. */
-	if(pvalue) {
-		*pvalue = result;
-	}
-
-	if(pvalue_len) {
-		*pvalue_len = 9;
-	} else {
-		if(ARCHIVE_OK != consume(a, 9)) {
-			return 0;
-		}
-	}
-
-	return 1;
-}
-
-static int read_var_sized(struct archive_read* a, size_t* pvalue,
-    size_t* pvalue_len)
-{
-	uint64_t v;
-	uint64_t v_size = 0;
-
-	const int ret = pvalue_len ? read_var(a, &v, &v_size)
-				   : read_var(a, &v, NULL);
-
-	if(ret == 1 && pvalue) {
-		*pvalue = (size_t) v;
-	}
-
-	if(pvalue_len) {
-		/* Possible data truncation should be safe. */
-		*pvalue_len = (size_t) v_size;
-	}
-
-	return ret;
-}
-
-static int read_bits_32(struct archive_read* a, struct rar5* rar,
-	const uint8_t* p, uint32_t* value)
-{
-	if(rar->bits.in_addr >= rar->cstate.cur_block_size) {
-		archive_set_error(&a->archive,
-			ARCHIVE_ERRNO_PROGRAMMER,
-			"Premature end of stream during extraction of data (#1)");
-		return ARCHIVE_FATAL;
-	}
-
-	uint32_t bits = archive_be32dec(p + rar->bits.in_addr);
-	bits <<= rar->bits.bit_addr;
-	bits |= p[rar->bits.in_addr + 4] >> (8 - rar->bits.bit_addr);
-	*value = bits;
-	return ARCHIVE_OK;
-}
-
-static int read_bits_16(struct archive_read* a, struct rar5* rar,
-	const uint8_t* p, uint16_t* value)
-{
-	if(rar->bits.in_addr >= rar->cstate.cur_block_size) {
-		archive_set_error(&a->archive,
-			ARCHIVE_ERRNO_PROGRAMMER,
-			"Premature end of stream during extraction of data (#2)");
-		return ARCHIVE_FATAL;
-	}
-
-	uint32_t bits = archive_be24dec(p + (unsigned)rar->bits.in_addr);
-	bits >>= (8 - rar->bits.bit_addr);
-	*value = bits & 0xffff;
-	return ARCHIVE_OK;
-}
-
-static void skip_bits(struct rar5* rar, int bits) {
-	const int new_bits = rar->bits.bit_addr + bits;
-	rar->bits.in_addr += new_bits >> 3;
-	rar->bits.bit_addr = new_bits & 7;
-}
-
-/* n = up to 16 */
-static int read_consume_bits(struct archive_read* a, struct rar5* rar,
-	const uint8_t* p, int n, int* value)
-{
-	uint16_t v;
-	int ret, num;
-
-	if(n == 0 || n > 16) {
-		/* This is a programmer error and should never happen
-		 * in runtime. */
-		return ARCHIVE_FATAL;
-	}
-
-	ret = read_bits_16(a, rar, p, &v);
-	if(ret != ARCHIVE_OK)
-		return ret;
-
-	num = (int) v;
-	num >>= 16 - n;
-
-	skip_bits(rar, n);
-
-	if(value)
-		*value = num;
-
-	return ARCHIVE_OK;
-}
-
-static char read_u32(struct archive_read* a, uint32_t* pvalue) {
-	const uint8_t* p;
-	if(!read_ahead(a, 4, &p))
-		return 0;
-
-	*pvalue = archive_le32dec(p);
-	return ARCHIVE_OK == consume(a, 4);
-}
-
-static char read_u64(struct archive_read* a, uint64_t* pvalue) {
-	const uint8_t* p;
-	if(!read_ahead(a, 8, &p))
-		return 0;
-
-	*pvalue = archive_le64dec(p);
-	return ARCHIVE_OK == consume(a, 8);
-}
-
-static int bid_standard(struct archive_read* a) {
-	const uint8_t* p;
-	char signature[sizeof(rar5_signature_xor)];
-
-	rar5_signature(signature);
-
-	if(!read_ahead(a, sizeof(rar5_signature_xor), &p))
-		return -1;
-
-	if(!memcmp(signature, p, sizeof(rar5_signature_xor)))
-		return 30;
-
-	return -1;
-}
-
-static int bid_sfx(struct archive_read *a)
-{
-	const char *p;
-
-	if ((p = __archive_read_ahead(a, 7, NULL)) == NULL)
-		return -1;
-
-	if ((p[0] == 'M' && p[1] == 'Z') || memcmp(p, "\x7F\x45LF", 4) == 0) {
-		/* This is a PE file */
-		char signature[sizeof(rar5_signature_xor)];
-		ssize_t offset = 0x10000;
-		ssize_t window = 4096;
-		ssize_t bytes_avail;
-
-		rar5_signature(signature);
-
-		while (offset + window <= (1024 * 512)) {
-			const char *buff = __archive_read_ahead(a, offset + window, &bytes_avail);
-			if (buff == NULL) {
-				/* Remaining bytes are less than window. */
-				window >>= 1;
-				if (window < 0x40)
-					return 0;
-				continue;
-			}
-			p = buff + offset;
-			while (p + 8 < buff + bytes_avail) {
-				if (memcmp(p, signature, sizeof(signature)) == 0)
-					return 30;
-				p += 0x10;
-			}
-			offset = p - buff;
-		}
-	}
-
-	return 0;
-}
-
-static int rar5_bid(struct archive_read* a, int best_bid) {
-	int my_bid;
-
-	if(best_bid > 30)
-		return -1;
-
-	my_bid = bid_standard(a);
-	if(my_bid > -1) {
-		return my_bid;
-	}
-	my_bid = bid_sfx(a);
-	if (my_bid > -1) {
-		return my_bid;
-	}
-
-	return -1;
-}
-
-static int rar5_options(struct archive_read *a, const char *key,
-    const char *val) {
-	(void) a;
-	(void) key;
-	(void) val;
-
-	/* No options supported in this version. Return the ARCHIVE_WARN code
-	 * to signal the options supervisor that the unpacker didn't handle
-	 * setting this option. */
-
-	return ARCHIVE_WARN;
-}
-
-static void init_header(struct archive_read* a) {
-	a->archive.archive_format = ARCHIVE_FORMAT_RAR_V5;
-	a->archive.archive_format_name = "RAR5";
-}
-
-static void init_window_mask(struct rar5* rar) {
-	if (rar->cstate.window_size)
-		rar->cstate.window_mask = rar->cstate.window_size - 1;
-	else
-		rar->cstate.window_mask = 0;
-}
-
-enum HEADER_FLAGS {
-	HFL_EXTRA_DATA = 0x0001,
-	HFL_DATA = 0x0002,
-	HFL_SKIP_IF_UNKNOWN = 0x0004,
-	HFL_SPLIT_BEFORE = 0x0008,
-	HFL_SPLIT_AFTER = 0x0010,
-	HFL_CHILD = 0x0020,
-	HFL_INHERITED = 0x0040
-};
-
-static int process_main_locator_extra_block(struct archive_read* a,
-    struct rar5* rar)
-{
-	uint64_t locator_flags;
-
-	enum LOCATOR_FLAGS {
-		QLIST = 0x01, RECOVERY = 0x02,
-	};
-
-	if(!read_var(a, &locator_flags, NULL)) {
-		return ARCHIVE_EOF;
-	}
-
-	if(locator_flags & QLIST) {
-		if(!read_var(a, &rar->qlist_offset, NULL)) {
-			return ARCHIVE_EOF;
-		}
-
-		/* qlist is not used */
-	}
-
-	if(locator_flags & RECOVERY) {
-		if(!read_var(a, &rar->rr_offset, NULL)) {
-			return ARCHIVE_EOF;
-		}
-
-		/* rr is not used */
-	}
-
-	return ARCHIVE_OK;
-}
-
-static int parse_file_extra_hash(struct archive_read* a, struct rar5* rar,
-    int64_t* extra_data_size)
-{
-	size_t hash_type = 0;
-	size_t value_len;
-
-	enum HASH_TYPE {
-		BLAKE2sp = 0x00
-	};
-
-	if(!read_var_sized(a, &hash_type, &value_len))
-		return ARCHIVE_EOF;
-
-	*extra_data_size -= value_len;
-	if(ARCHIVE_OK != consume(a, value_len)) {
-		return ARCHIVE_EOF;
-	}
-
-	/* The file uses BLAKE2sp checksum algorithm instead of plain old
-	 * CRC32. */
-	if(hash_type == BLAKE2sp) {
-		const uint8_t* p;
-		const int hash_size = sizeof(rar->file.blake2sp);
-
-		if(!read_ahead(a, hash_size, &p))
-			return ARCHIVE_EOF;
-
-		rar->file.has_blake2 = 1;
-		memcpy(&rar->file.blake2sp, p, hash_size);
-
-		if(ARCHIVE_OK != consume(a, hash_size)) {
-			return ARCHIVE_EOF;
-		}
-
-		*extra_data_size -= hash_size;
-	} else {
-		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
-		    "Unsupported hash type (0x%jx)", (uintmax_t)hash_type);
-		return ARCHIVE_FATAL;
-	}
-
-	return ARCHIVE_OK;
-}
-
-static int parse_htime_item(struct archive_read* a, char unix_time,
-    int64_t* sec, uint32_t* nsec, int64_t* extra_data_size)
-{
-	if(unix_time) {
-		uint32_t time_val;
-		if(!read_u32(a, &time_val))
-			return ARCHIVE_EOF;
-
-		*extra_data_size -= 4;
-		*sec = (int64_t) time_val;
-	} else {
-		uint64_t windows_time;
-		if(!read_u64(a, &windows_time))
-			return ARCHIVE_EOF;
-
-		ntfs_to_unix(windows_time, sec, nsec);
-		*extra_data_size -= 8;
-	}
-
-	return ARCHIVE_OK;
-}
-
-static int parse_file_extra_version(struct archive_read* a,
-    struct archive_entry* e, int64_t* extra_data_size)
-{
-	size_t flags = 0;
-	size_t version = 0;
-	size_t value_len = 0;
-	struct archive_string version_string;
-	struct archive_string name_utf8_string;
-	const char* cur_filename;
-
-	/* Flags are ignored. */
-	if(!read_var_sized(a, &flags, &value_len))
-		return ARCHIVE_EOF;
-
-	*extra_data_size -= value_len;
-	if(ARCHIVE_OK != consume(a, value_len))
-		return ARCHIVE_EOF;
-
-	if(!read_var_sized(a, &version, &value_len))
-		return ARCHIVE_EOF;
-
-	*extra_data_size -= value_len;
-	if(ARCHIVE_OK != consume(a, value_len))
-		return ARCHIVE_EOF;
-
-	/* extra_data_size should be zero here. */
-
-	cur_filename = archive_entry_pathname_utf8(e);
-	if(cur_filename == NULL) {
-		archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
-		    "Version entry without file name");
-		return ARCHIVE_FATAL;
-	}
-
-	archive_string_init(&version_string);
-	archive_string_init(&name_utf8_string);
-
-	/* Prepare a ;123 suffix for the filename, where '123' is the version
-	 * value of this file. */
-	archive_string_sprintf(&version_string, ";%zu", version);
-
-	/* Build the new filename. */
-	archive_strcat(&name_utf8_string, cur_filename);
-	archive_strcat(&name_utf8_string, version_string.s);
-
-	/* Apply the new filename into this file's context. */
-	archive_entry_update_pathname_utf8(e, name_utf8_string.s);
-
-	/* Free buffers. */
-	archive_string_free(&version_string);
-	archive_string_free(&name_utf8_string);
-	return ARCHIVE_OK;
-}
-
-static int parse_file_extra_htime(struct archive_read* a,
-    struct archive_entry* e, struct rar5* rar, int64_t* extra_data_size)
-{
-	char unix_time, has_unix_ns, has_mtime, has_ctime, has_atime;
-	size_t flags = 0;
-	size_t value_len;
-
-	enum HTIME_FLAGS {
-		IS_UNIX       = 0x01,
-		HAS_MTIME     = 0x02,
-		HAS_CTIME     = 0x04,
-		HAS_ATIME     = 0x08,
-		HAS_UNIX_NS   = 0x10,
-	};
-
-	if(!read_var_sized(a, &flags, &value_len))
-		return ARCHIVE_EOF;
-
-	*extra_data_size -= value_len;
-	if(ARCHIVE_OK != consume(a, value_len)) {
-		return ARCHIVE_EOF;
-	}
-
-	unix_time = flags & IS_UNIX;
-	has_unix_ns = unix_time && (flags & HAS_UNIX_NS);
-	has_mtime = flags & HAS_MTIME;
-	has_atime = flags & HAS_ATIME;
-	has_ctime = flags & HAS_CTIME;
-	rar->file.e_atime_ns = rar->file.e_ctime_ns = rar->file.e_mtime_ns = 0;
-
-	if(has_mtime) {
-		parse_htime_item(a, unix_time, &rar->file.e_mtime,
-		    &rar->file.e_mtime_ns, extra_data_size);
-	}
-
-	if(has_ctime) {
-		parse_htime_item(a, unix_time, &rar->file.e_ctime,
-		    &rar->file.e_ctime_ns, extra_data_size);
-	}
-
-	if(has_atime) {
-		parse_htime_item(a, unix_time, &rar->file.e_atime,
-		    &rar->file.e_atime_ns, extra_data_size);
-	}
-
-	if(has_mtime && has_unix_ns) {
-		if(!read_u32(a, &rar->file.e_mtime_ns))
-			return ARCHIVE_EOF;
-
-		*extra_data_size -= 4;
-	}
-
-	if(has_ctime && has_unix_ns) {
-		if(!read_u32(a, &rar->file.e_ctime_ns))
-			return ARCHIVE_EOF;
-
-		*extra_data_size -= 4;
-	}
-
-	if(has_atime && has_unix_ns) {
-		if(!read_u32(a, &rar->file.e_atime_ns))
-			return ARCHIVE_EOF;
-
-		*extra_data_size -= 4;
-	}
-
-	/* The seconds and nanoseconds are either together, or separated in two
-	 * fields so we parse them, then set the archive_entry's times. */
-	if(has_mtime) {
-		archive_entry_set_mtime(e, rar->file.e_mtime, rar->file.e_mtime_ns);
-	}
-
-	if(has_ctime) {
-		archive_entry_set_ctime(e, rar->file.e_ctime, rar->file.e_ctime_ns);
-	}
-
-	if(has_atime) {
-		archive_entry_set_atime(e, rar->file.e_atime, rar->file.e_atime_ns);
-	}
-
-	return ARCHIVE_OK;
-}
-
-static int parse_file_extra_redir(struct archive_read* a,
-    struct archive_entry* e, struct rar5* rar, int64_t* extra_data_size)
-{
-	uint64_t value_size = 0;
-	size_t target_size = 0;
-	char target_utf8_buf[MAX_NAME_IN_BYTES];
-	const uint8_t* p;
-
-	if(!read_var(a, &rar->file.redir_type, &value_size))
-		return ARCHIVE_EOF;
-	if(ARCHIVE_OK != consume(a, (int64_t)value_size))
-		return ARCHIVE_EOF;
-	*extra_data_size -= value_size;
-
-	if(!read_var(a, &rar->file.redir_flags, &value_size))
-		return ARCHIVE_EOF;
-	if(ARCHIVE_OK != consume(a, (int64_t)value_size))
-		return ARCHIVE_EOF;
-	*extra_data_size -= value_size;
-
-	if(!read_var_sized(a, &target_size, NULL))
-		return ARCHIVE_EOF;
-	*extra_data_size -= target_size + 1;
-
-	if(target_size > (MAX_NAME_IN_CHARS - 1)) {
-		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
-		    "Link target is too long");
-		return ARCHIVE_FATAL;
-	}
-
-	if(target_size == 0) {
-		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
-		    "No link target specified");
-		return ARCHIVE_FATAL;
-	}
-
-	if(!read_ahead(a, target_size, &p))
-		return ARCHIVE_EOF;
-
-	memcpy(target_utf8_buf, p, target_size);
-	target_utf8_buf[target_size] = 0;
-
-	if(ARCHIVE_OK != consume(a, (int64_t)target_size))
-		return ARCHIVE_EOF;
-
-	switch(rar->file.redir_type) {
-		case REDIR_TYPE_UNIXSYMLINK:
-		case REDIR_TYPE_WINSYMLINK:
-			archive_entry_set_filetype(e, AE_IFLNK);
-			archive_entry_update_symlink_utf8(e, target_utf8_buf);
-			if (rar->file.redir_flags & REDIR_SYMLINK_IS_DIR) {
-				archive_entry_set_symlink_type(e,
-					AE_SYMLINK_TYPE_DIRECTORY);
-			} else {
-				archive_entry_set_symlink_type(e,
-				AE_SYMLINK_TYPE_FILE);
-			}
-			break;
-
-		case REDIR_TYPE_HARDLINK:
-			archive_entry_set_filetype(e, AE_IFREG);
-			archive_entry_update_hardlink_utf8(e, target_utf8_buf);
-			break;
-
-		default:
-			/* Unknown redir type, skip it. */
-			break;
-	}
-	return ARCHIVE_OK;
-}
-
-static int parse_file_extra_owner(struct archive_read* a,
-    struct archive_entry* e, int64_t* extra_data_size)
-{
-	uint64_t flags = 0;
-	uint64_t value_size = 0;
-	uint64_t id = 0;
-	size_t name_len = 0;
-	size_t name_size = 0;
-	char namebuf[OWNER_MAXNAMELEN];
-	const uint8_t* p;
-
-	if(!read_var(a, &flags, &value_size))
-		return ARCHIVE_EOF;
-	if(ARCHIVE_OK != consume(a, (int64_t)value_size))
-		return ARCHIVE_EOF;
-	*extra_data_size -= value_size;
-
-	if ((flags & OWNER_USER_NAME) != 0) {
-		if(!read_var_sized(a, &name_size, NULL))
-			return ARCHIVE_EOF;
-		*extra_data_size -= name_size + 1;
-
-		if(!read_ahead(a, name_size, &p))
-			return ARCHIVE_EOF;
-
-		if (name_size >= OWNER_MAXNAMELEN) {
-			name_len = OWNER_MAXNAMELEN - 1;
-		} else {
-			name_len = name_size;
-		}
-
-		memcpy(namebuf, p, name_len);
-		namebuf[name_len] = 0;
-		if(ARCHIVE_OK != consume(a, (int64_t)name_size))
-			return ARCHIVE_EOF;
-
-		archive_entry_set_uname(e, namebuf);
-	}
-	if ((flags & OWNER_GROUP_NAME) != 0) {
-		if(!read_var_sized(a, &name_size, NULL))
-			return ARCHIVE_EOF;
-		*extra_data_size -= name_size + 1;
-
-		if(!read_ahead(a, name_size, &p))
-			return ARCHIVE_EOF;
-
-		if (name_size >= OWNER_MAXNAMELEN) {
-			name_len = OWNER_MAXNAMELEN - 1;
-		} else {
-			name_len = name_size;
-		}
-
-		memcpy(namebuf, p, name_len);
-		namebuf[name_len] = 0;
-		if(ARCHIVE_OK != consume(a, (int64_t)name_size))
-			return ARCHIVE_EOF;
-
-		archive_entry_set_gname(e, namebuf);
-	}
-	if ((flags & OWNER_USER_UID) != 0) {
-		if(!read_var(a, &id, &value_size))
-			return ARCHIVE_EOF;
-		if(ARCHIVE_OK != consume(a, (int64_t)value_size))
-			return ARCHIVE_EOF;
-		*extra_data_size -= value_size;
-
-		archive_entry_set_uid(e, (la_int64_t)id);
-	}
-	if ((flags & OWNER_GROUP_GID) != 0) {
-		if(!read_var(a, &id, &value_size))
-			return ARCHIVE_EOF;
-		if(ARCHIVE_OK != consume(a, (int64_t)value_size))
-			return ARCHIVE_EOF;
-		*extra_data_size -= value_size;
-
-		archive_entry_set_gid(e, (la_int64_t)id);
-	}
-	return ARCHIVE_OK;
-}
-
-static int process_head_file_extra(struct archive_read* a,
-    struct archive_entry* e, struct rar5* rar, int64_t extra_data_size)
-{
-	uint64_t extra_field_size;
-	uint64_t extra_field_id = 0;
-	uint64_t var_size;
-
-	while(extra_data_size > 0) {
-		/* Make sure we won't fail if the file declares only unsupported
-		attributes. */
-		int ret = ARCHIVE_OK;
-
-		if(!read_var(a, &extra_field_size, &var_size))
-			return ARCHIVE_EOF;
-
-		extra_data_size -= var_size;
-		if(ARCHIVE_OK != consume(a, var_size)) {
-			return ARCHIVE_EOF;
-		}
-
-		if(!read_var(a, &extra_field_id, &var_size))
-			return ARCHIVE_EOF;
-
-		extra_field_size -= var_size;
-		extra_data_size -= var_size;
-		if(ARCHIVE_OK != consume(a, var_size)) {
-			return ARCHIVE_EOF;
-		}
-
-		switch(extra_field_id) {
-			case EX_HASH:
-				ret = parse_file_extra_hash(a, rar,
-				    &extra_data_size);
-				break;
-			case EX_HTIME:
-				ret = parse_file_extra_htime(a, e, rar,
-				    &extra_data_size);
-				break;
-			case EX_REDIR:
-				ret = parse_file_extra_redir(a, e, rar,
-				    &extra_data_size);
-				break;
-			case EX_UOWNER:
-				ret = parse_file_extra_owner(a, e,
-				    &extra_data_size);
-				break;
-			case EX_VERSION:
-				ret = parse_file_extra_version(a, e,
-				    &extra_data_size);
-				break;
-			case EX_CRYPT:
-				/* Mark the entry as encrypted */
-				archive_entry_set_is_data_encrypted(e, 1);
-				rar->has_encrypted_entries = 1;
-				rar->cstate.data_encrypted = 1;
-				/* fallthrough */
-			case EX_SUBDATA:
-				/* fallthrough */
-			default:
-				/* Skip unsupported entry. */
-				extra_data_size -= extra_field_size;
-				if (ARCHIVE_OK != consume(a, extra_field_size)) {
-					return ARCHIVE_EOF;
-				}
-
-				/* Don't fail on unsupported attribute -- we've handled it
-				   by skipping over it. */
-				ret = ARCHIVE_OK;
-		}
-
-		if (ret != ARCHIVE_OK) {
-			/* Forward any errors signalled by the attribute parsing
-			   functions. */
-			return ret;
-		}
-	}
-
-	if (extra_data_size != 0) {
-		/* We didn't skip everything, or we skipped too much; either way,
-		   there's an error in this parsing function. */
-
-		archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
-				"unsupported structure of file header extra data");
-		return ARCHIVE_FATAL;
-	}
-
-	return ARCHIVE_OK;
-}
-
-static int file_entry_sanity_checks(struct archive_read* a,
-	size_t block_flags, uint8_t is_dir, uint64_t unpacked_size,
-	size_t packed_size)
-{
-	if (is_dir) {
-		const int declares_data_size =
-			(int) (unpacked_size != 0 || packed_size != 0);
-
-		/* FILE entries for directories still declare HFL_DATA in block flags,
-		   even though attaching data to such blocks doesn't make much sense. */
-		if (declares_data_size) {
-			archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
-				"directory entries cannot have any data");
-			return ARCHIVE_FATAL;
-		}
-	} else {
-		const int declares_hfl_data = (int) ((block_flags & HFL_DATA) != 0);
-		if (!declares_hfl_data) {
-			archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
-					"no data found in file/service block");
-			return ARCHIVE_FATAL;
-		}
-	}
-
-	return ARCHIVE_OK;
-}
-
-static int process_head_file(struct archive_read* a, struct rar5* rar,
-    struct archive_entry* entry, size_t block_flags)
-{
-	int64_t extra_data_size = 0;
-	size_t data_size = 0;
-	size_t file_flags = 0;
-	size_t file_attr = 0;
-	size_t compression_info = 0;
-	size_t host_os = 0;
-	size_t name_size = 0;
-	uint64_t unpacked_size, window_size;
-	uint32_t mtime = 0, crc = 0;
-	int c_method = 0, c_version = 0;
-	char name_utf8_buf[MAX_NAME_IN_BYTES];
-	const uint8_t* p;
-	int sanity_ret;
-
-	enum FILE_FLAGS {
-		DIRECTORY = 0x0001, UTIME = 0x0002, CRC32 = 0x0004,
-		UNKNOWN_UNPACKED_SIZE = 0x0008,
-	};
-
-	enum FILE_ATTRS {
-		ATTR_READONLY = 0x1, ATTR_HIDDEN = 0x2, ATTR_SYSTEM = 0x4,
-		ATTR_DIRECTORY = 0x10,
-	};
-
-	enum COMP_INFO_FLAGS {
-		SOLID = 0x0040,
-	};
-
-	enum HOST_OS {
-		HOST_WINDOWS = 0,
-		HOST_UNIX = 1,
-	};
-
-	archive_entry_clear(entry);
-
-	/* Do not reset file context if we're switching archives. */
-	if(!rar->cstate.switch_multivolume) {
-		reset_file_context(rar);
-	}
-
-	if(block_flags & HFL_EXTRA_DATA) {
-		uint64_t edata_size = 0;
-		if(!read_var(a, &edata_size, NULL))
-			return ARCHIVE_EOF;
-
-		/* Intentional type cast from unsigned to signed. */
-		extra_data_size = (int64_t) edata_size;
-	}
-
-	if(block_flags & HFL_DATA) {
-		if(!read_var_sized(a, &data_size, NULL))
-			return ARCHIVE_EOF;
-
-		if(data_size > SSIZE_MAX) {
-			archive_set_error(&a->archive,
-			    ARCHIVE_ERRNO_FILE_FORMAT,
-			    "File data size is too large");
-			return ARCHIVE_FATAL;
-		}
-
-		rar->file.bytes_remaining = data_size;
-	} else {
-		rar->file.bytes_remaining = 0;
-	}
-
-	if(!read_var_sized(a, &file_flags, NULL))
-		return ARCHIVE_EOF;
-
-	if(!read_var(a, &unpacked_size, NULL))
-		return ARCHIVE_EOF;
-
-	if(file_flags & UNKNOWN_UNPACKED_SIZE) {
-		archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
-		    "Files with unknown unpacked size are not supported");
-		return ARCHIVE_FATAL;
-	}
-
-	rar->file.dir = (uint8_t) ((file_flags & DIRECTORY) > 0);
-
-	sanity_ret = file_entry_sanity_checks(a, block_flags, rar->file.dir,
-		unpacked_size, data_size);
-
-	if (sanity_ret != ARCHIVE_OK) {
-		return sanity_ret;
-	}
-
-	if(!read_var_sized(a, &file_attr, NULL))
-		return ARCHIVE_EOF;
-
-	if(file_flags & UTIME) {
-		if(!read_u32(a, &mtime))
-			return ARCHIVE_EOF;
-	}
-
-	if(file_flags & CRC32) {
-		if(!read_u32(a, &crc))
-			return ARCHIVE_EOF;
-	}
-
-	if(!read_var_sized(a, &compression_info, NULL))
-		return ARCHIVE_EOF;
-
-	c_method = (int) (compression_info >> 7) & 0x7;
-	c_version = (int) (compression_info & 0x3f);
-
-	/* RAR5 seems to limit the dictionary size to 64MB. */
-	window_size = (rar->file.dir > 0) ?
-		0 :
-		g_unpack_window_size << ((compression_info >> 10) & 15);
-	rar->cstate.method = c_method;
-	rar->cstate.version = c_version + 50;
-	rar->file.solid = (compression_info & SOLID) > 0;
-
-	/* Archives which declare solid files without initializing the window
-	 * buffer first are invalid, unless previous data was encrypted, in
-	 * which case we may never have had the chance */
-
-	if(rar->file.solid > 0 && rar->cstate.data_encrypted == 0 &&
-	    rar->cstate.window_buf == NULL) {
-		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
-				  "Declared solid file, but no window buffer "
-				  "initialized yet");
-		return ARCHIVE_FATAL;
-	}
-
-	/* Check if window_size is a sane value. Also, if the file is not
-	 * declared as a directory, disallow window_size == 0. */
-	if(window_size > (64 * 1024 * 1024) ||
-	    (rar->file.dir == 0 && window_size == 0))
-	{
-		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
-		    "Declared dictionary size is not supported");
-		return ARCHIVE_FATAL;
-	}
-
-	if(rar->file.solid > 0) {
-		/* Re-check if current window size is the same as previous
-		 * window size (for solid files only). */
-		if(rar->file.solid_window_size > 0 &&
-		    rar->file.solid_window_size != (ssize_t) window_size)
-		{
-			archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
-			    "Window size for this solid file doesn't match "
-			    "the window size used in previous solid file");
-			return ARCHIVE_FATAL;
-		}
-	}
-	else
-		rar->cstate.data_encrypted = 0; /* Reset for new buffer */
-
-	if(rar->cstate.window_size < (ssize_t) window_size &&
-	    rar->cstate.window_buf)
-	{
-		/* The `data_ready` stack contains pointers to the `window_buf` or
-		 * `filtered_buf` buffers.  Since we're about to reallocate the first
-		 * buffer, some of those pointers could become invalid. Therefore, we
-		 * need to dispose of all entries from the stack before attempting the
-		 * realloc. */
-		clear_data_ready_stack(rar);
-
-		/* If window_buf has been allocated before, reallocate it, so
-		 * that its size will match new window_size. */
-
-		uint8_t* new_window_buf =
-			realloc(rar->cstate.window_buf, (size_t) window_size);
-
-		if(!new_window_buf) {
-			archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
-				"Not enough memory when trying to realloc the window "
-				"buffer");
-			return ARCHIVE_FATAL;
-		}
-
-		rar->cstate.window_buf = new_window_buf;
-	}
-
-	/* Values up to 64M should fit into ssize_t on every
-	 * architecture. */
-	rar->cstate.window_size = (ssize_t) window_size;
-
-	if(rar->file.solid > 0 && rar->file.solid_window_size == 0) {
-		/* Solid files have to have the same window_size across
-		   whole archive. Remember the window_size parameter
-		   for first solid file found. */
-		rar->file.solid_window_size = rar->cstate.window_size;
-	}
-
-	init_window_mask(rar);
-
-	rar->file.service = 0;
-
-	if(!read_var_sized(a, &host_os, NULL))
-		return ARCHIVE_EOF;
-
-	if(host_os == HOST_WINDOWS) {
-		/* Host OS is Windows */
-
-		__LA_MODE_T mode;
-
-		if(file_attr & ATTR_DIRECTORY) {
-			if (file_attr & ATTR_READONLY) {
-				mode = 0555 | AE_IFDIR;
-			} else {
-				mode = 0755 | AE_IFDIR;
-			}
-		} else {
-			if (file_attr & ATTR_READONLY) {
-				mode = 0444 | AE_IFREG;
-			} else {
-				mode = 0644 | AE_IFREG;
-			}
-		}
-
-		archive_entry_set_mode(entry, mode);
-
-		if (file_attr & (ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM)) {
-			char buf[sizeof(",rdonly,hidden,system")];
-			char *fflags[3] = { "", "", "" };
-			char **flag = fflags;
-
-			if (file_attr & ATTR_READONLY)
-				*flag++ = ",rdonly";
-			if (file_attr & ATTR_HIDDEN)
-				*flag++ = ",hidden";
-			if (file_attr & ATTR_SYSTEM)
-				*flag++ = ",system";
-
-			snprintf(buf, sizeof(buf), "%s%s%s",
-			    fflags[0], fflags[1], fflags[2]);
-			archive_entry_copy_fflags_text(entry, buf + 1);
-		}
-	} else if(host_os == HOST_UNIX) {
-		/* Host OS is Unix */
-		archive_entry_set_mode(entry, (__LA_MODE_T) file_attr);
-	} else {
-		/* Unknown host OS */
-		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
-				"Unsupported Host OS: 0x%jx",
-				(uintmax_t)host_os);
-
-		return ARCHIVE_FATAL;
-	}
-
-	if(!read_var_sized(a, &name_size, NULL))
-		return ARCHIVE_EOF;
-
-	if(name_size > (MAX_NAME_IN_CHARS - 1)) {
-		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
-				"Filename is too long");
-
-		return ARCHIVE_FATAL;
-	}
-
-	if(name_size == 0) {
-		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
-				"No filename specified");
-
-		return ARCHIVE_FATAL;
-	}
-
-	if(!read_ahead(a, name_size, &p))
-		return ARCHIVE_EOF;
-
-	memcpy(name_utf8_buf, p, name_size);
-	name_utf8_buf[name_size] = 0;
-	if(ARCHIVE_OK != consume(a, name_size)) {
-		return ARCHIVE_EOF;
-	}
-
-	archive_entry_update_pathname_utf8(entry, name_utf8_buf);
-
-	if(extra_data_size > 0) {
-		int ret = process_head_file_extra(a, entry, rar,
-		    extra_data_size);
-
-		/*
-		 * TODO: rewrite or remove useless sanity check
-		 *       as extra_data_size is not passed as a pointer
-		 *
-		if(extra_data_size < 0) {
-			archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
-			    "File extra data size is not zero");
-			return ARCHIVE_FATAL;
-		}
-		 */
-
-		if(ret != ARCHIVE_OK)
-			return ret;
-	}
-
-	if((file_flags & UNKNOWN_UNPACKED_SIZE) == 0) {
-		rar->file.unpacked_size = (ssize_t) unpacked_size;
-		if(rar->file.redir_type == REDIR_TYPE_NONE)
-			archive_entry_set_size(entry, unpacked_size);
-	}
-
-	if(file_flags & UTIME) {
-		archive_entry_set_mtime(entry, (time_t) mtime, 0);
-	}
-
-	if(file_flags & CRC32) {
-		rar->file.stored_crc32 = crc;
-	}
-
-	if(!rar->cstate.switch_multivolume) {
-		/* Do not reinitialize unpacking state if we're switching
-		 * archives. */
-		rar->cstate.block_parsing_finished = 1;
-		rar->cstate.all_filters_applied = 1;
-		rar->cstate.initialized = 0;
-	}
-
-	if(rar->generic.split_before > 0) {
-		/* If now we're standing on a header that has a 'split before'
-		 * mark, it means we're standing on a 'continuation' file
-		 * header. Signal the caller that if it wants to move to
-		 * another file, it must call rar5_read_header() function
-		 * again. */
-
-		return ARCHIVE_RETRY;
-	} else {
-		return ARCHIVE_OK;
-	}
-}
-
-static int process_head_service(struct archive_read* a, struct rar5* rar,
-    struct archive_entry* entry, size_t block_flags)
-{
-	/* Process this SERVICE block the same way as FILE blocks. */
-	int ret = process_head_file(a, rar, entry, block_flags);
-	if(ret != ARCHIVE_OK)
-		return ret;
-
-	rar->file.service = 1;
-
-	/* But skip the data part automatically. It's no use for the user
-	 * anyway.  It contains only service data, not even needed to
-	 * properly unpack the file. */
-	ret = rar5_read_data_skip(a);
-	if(ret != ARCHIVE_OK)
-		return ret;
-
-	/* After skipping, try parsing another block automatically. */
-	return ARCHIVE_RETRY;
-}
-
-static int process_head_main(struct archive_read* a, struct rar5* rar,
-    struct archive_entry* entry, size_t block_flags)
-{
-	int ret;
-	uint64_t extra_data_size = 0;
-	size_t extra_field_size = 0;
-	size_t extra_field_id = 0;
-	size_t archive_flags = 0;
-
-	enum MAIN_FLAGS {
-		VOLUME = 0x0001,         /* multi-volume archive */
-		VOLUME_NUMBER = 0x0002,  /* volume number, first vol doesn't
-					  * have it */
-		SOLID = 0x0004,          /* solid archive */
-		PROTECT = 0x0008,        /* contains Recovery info */
-		LOCK = 0x0010,           /* readonly flag, not used */
-	};
-
-	enum MAIN_EXTRA {
-		// Just one attribute here.
-		LOCATOR = 0x01,
-	};
-
-	(void) entry;
-
-	if(block_flags & HFL_EXTRA_DATA) {
-		if(!read_var(a, &extra_data_size, NULL))
-			return ARCHIVE_EOF;
-	} else {
-		extra_data_size = 0;
-	}
-
-	if(!read_var_sized(a, &archive_flags, NULL)) {
-		return ARCHIVE_EOF;
-	}
-
-	rar->main.volume = (archive_flags & VOLUME) > 0;
-	rar->main.solid = (archive_flags & SOLID) > 0;
-
-	if(archive_flags & VOLUME_NUMBER) {
-		size_t v = 0;
-		if(!read_var_sized(a, &v, NULL)) {
-			return ARCHIVE_EOF;
-		}
-
-		if (v > UINT_MAX) {
-			archive_set_error(&a->archive,
-			    ARCHIVE_ERRNO_FILE_FORMAT,
-			    "Invalid volume number");
-			return ARCHIVE_FATAL;
-		}
-
-		rar->main.vol_no = (unsigned int) v;
-	} else {
-		rar->main.vol_no = 0;
-	}
-
-	if(rar->vol.expected_vol_no > 0 &&
-		rar->main.vol_no != rar->vol.expected_vol_no)
-	{
-		/* Returning EOF instead of FATAL because of strange
-		 * libarchive behavior. When opening multiple files via
-		 * archive_read_open_filenames(), after reading up the whole
-		 * last file, the __archive_read_ahead function wraps up to
-		 * the first archive instead of returning EOF. */
-		return ARCHIVE_EOF;
-	}
-
-	if(extra_data_size == 0) {
-		/* Early return. */
-		return ARCHIVE_OK;
-	}
-
-	if(!read_var_sized(a, &extra_field_size, NULL)) {
-		return ARCHIVE_EOF;
-	}
-
-	if(!read_var_sized(a, &extra_field_id, NULL)) {
-		return ARCHIVE_EOF;
-	}
-
-	if(extra_field_size == 0) {
-		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
-		    "Invalid extra field size");
-		return ARCHIVE_FATAL;
-	}
-
-	switch(extra_field_id) {
-		case LOCATOR:
-			ret = process_main_locator_extra_block(a, rar);
-			if(ret != ARCHIVE_OK) {
-				/* Error while parsing main locator extra
-				 * block. */
-				return ret;
-			}
-
-			break;
-		default:
-			archive_set_error(&a->archive,
-			    ARCHIVE_ERRNO_FILE_FORMAT,
-			    "Unsupported extra type (0x%jx)",
-			    (uintmax_t)extra_field_id);
-			return ARCHIVE_FATAL;
-	}
-
-	return ARCHIVE_OK;
-}
-
-static int skip_unprocessed_bytes(struct archive_read* a) {
-	struct rar5* rar = get_context(a);
-	int ret;
-
-	if(rar->file.bytes_remaining) {
-		/* Use different skipping method in block merging mode than in
-		 * normal mode. If merge mode is active, rar5_read_data_skip
-		 * can't be used, because it could allow recursive use of
-		 * merge_block() * function, and this function doesn't support
-		 * recursive use. */
-		if(rar->merge_mode) {
-			/* Discard whole merged block. This is valid in solid
-			 * mode as well, because the code will discard blocks
-			 * only if those blocks are safe to discard (i.e.
-			 * they're not FILE blocks).  */
-			ret = consume(a, rar->file.bytes_remaining);
-			if(ret != ARCHIVE_OK) {
-				return ret;
-			}
-			rar->file.bytes_remaining = 0;
-		} else {
-			/* If we're not in merge mode, use safe skipping code.
-			 * This will ensure we'll handle solid archives
-			 * properly. */
-			ret = rar5_read_data_skip(a);
-			if(ret != ARCHIVE_OK) {
-				return ret;
-			}
-		}
-	}
-
-	return ARCHIVE_OK;
-}
-
-static int scan_for_signature(struct archive_read* a);
-
-/* Base block processing function. A 'base block' is a RARv5 header block
- * that tells the reader what kind of data is stored inside the block.
- *
- * From the birds-eye view a RAR file looks file this:
- *
- * <magic><base_block_1><base_block_2>...<base_block_n>
- *
- * There are a few types of base blocks. Those types are specified inside
- * the 'switch' statement in this function. For example purposes, I'll write
- * how a standard RARv5 file could look like here:
- *
- * <magic><MAIN><FILE><FILE><FILE><SERVICE><ENDARC>
- *
- * The structure above could describe an archive file with 3 files in it,
- * one service "QuickOpen" block (that is ignored by this parser), and an
- * end of file base block marker.
- *
- * If the file is stored in multiple archive files ("multiarchive"), it might
- * look like this:
- *
- * .part01.rar: <magic><MAIN><FILE><ENDARC>
- * .part02.rar: <magic><MAIN><FILE><ENDARC>
- * .part03.rar: <magic><MAIN><FILE><ENDARC>
- *
- * This example could describe 3 RAR files that contain ONE archived file.
- * Or it could describe 3 RAR files that contain 3 different files. Or 3
- * RAR files than contain 2 files. It all depends what metadata is stored in
- * the headers of <FILE> blocks.
- *
- * Each <FILE> block contains info about its size, the name of the file it's
- * storing inside, and whether this FILE block is a continuation block of
- * previous archive ('split before'), and is this FILE block should be
- * continued in another archive ('split after'). By parsing the 'split before'
- * and 'split after' flags, we're able to tell if multiple <FILE> base blocks
- * are describing one file, or multiple files (with the same filename, for
- * example).
- *
- * One thing to note is that if we're parsing the first <FILE> block, and
- * we see 'split after' flag, then we need to jump over to another <FILE>
- * block to be able to decompress rest of the data. To do this, we need
- * to skip the <ENDARC> block, then switch to another file, then skip the
- * <magic> block, <MAIN> block, and then we're standing on the proper
- * <FILE> block.
- */
-
-/*
- * A header that carries no file data (HEAD_MAIN, or an unknown block
- * flagged HFL_SKIP_IF_UNKNOWN) may leave bytes in its body that the
- * sub-parser did not read. Skip them before returning ARCHIVE_RETRY,
- * otherwise rar5_read_header() re-parses the same block region O(N)
- * times instead of O(1), letting a crafted RAR5 file stall the reader
- * (GHSA-9h2c-464f-j3hj).
- *
- * Safe because read_ahead(a, hdr_size, &p) pre-loaded the whole block
- * into one contiguous buffer with no compaction until we return, so
- * body_start stays valid and (cur - body_start) is the exact number of
- * body bytes consumed so far.
- */
-static void
-rar5_skip_remaining_block(struct archive_read* a,
-    const uint8_t* body_start, size_t raw_hdr_size)
-{
-	const uint8_t* cur;
-
-	if(read_ahead(a, 1, &cur)) {
-		size_t body_used = (size_t)(cur - body_start);
-
-		if(body_used < raw_hdr_size)
-			(void)consume(a, raw_hdr_size - body_used);
-	}
-}
-
-static int process_base_block(struct archive_read* a,
-    struct archive_entry* entry)
-{
-	const size_t SMALLEST_RAR5_BLOCK_SIZE = 3;
-
-	struct rar5* rar = get_context(a);
-	uint32_t hdr_crc, computed_crc;
-	size_t raw_hdr_size = 0, hdr_size_len, hdr_size;
-	size_t header_id = 0;
-	size_t header_flags = 0;
-	const uint8_t* p;
-	const uint8_t* body_start;
-	int ret;
-
-	enum HEADER_TYPE {
-		HEAD_MARK    = 0x00, HEAD_MAIN  = 0x01, HEAD_FILE   = 0x02,
-		HEAD_SERVICE = 0x03, HEAD_CRYPT = 0x04, HEAD_ENDARC = 0x05,
-		HEAD_UNKNOWN = 0xff,
-	};
-
-	/* Skip any unprocessed data for this file. */
-	ret = skip_unprocessed_bytes(a);
-	if(ret != ARCHIVE_OK)
-		return ret;
-
-	/* Read the expected CRC32 checksum. */
-	if(!read_u32(a, &hdr_crc)) {
-		return ARCHIVE_EOF;
-	}
-
-	/* Read header size. */
-	if(!read_var_sized(a, &raw_hdr_size, &hdr_size_len)) {
-		return ARCHIVE_EOF;
-	}
-
-	hdr_size = raw_hdr_size + hdr_size_len;
-
-	/* Sanity check, maximum header size for RAR5 is 2MB. */
-	if(hdr_size > (2 * 1024 * 1024)) {
-		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
-		    "Base block header is too large");
-
-		return ARCHIVE_FATAL;
-	}
-
-	/* Additional sanity checks to weed out invalid files. */
-	if(raw_hdr_size == 0 || hdr_size_len == 0 ||
-		hdr_size < SMALLEST_RAR5_BLOCK_SIZE)
-	{
-		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
-		    "Too small block encountered (%zu bytes)",
-		    raw_hdr_size);
-
-		return ARCHIVE_FATAL;
-	}
-
-	/* Read the whole header data into memory, maximum memory use here is
-	 * 2MB. */
-	if(!read_ahead(a, hdr_size, &p)) {
-		return ARCHIVE_EOF;
-	}
-
-	/* Verify the CRC32 of the header data. */
-	computed_crc = (uint32_t) crc32(0, p, (int) hdr_size);
-	if(computed_crc != hdr_crc) {
-#ifndef DONT_FAIL_ON_CRC_ERROR
-		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
-		    "Header CRC error");
-
-		return ARCHIVE_FATAL;
-#endif
-	}
-
-	/* Remember the first byte of the block body so we can later skip
-	 * any bytes the sub-parser leaves unconsumed. */
-	body_start = p + hdr_size_len;
-
-	/* If the checksum is OK, we proceed with parsing. */
-	if(ARCHIVE_OK != consume(a, hdr_size_len)) {
-		return ARCHIVE_EOF;
-	}
-
-	if(!read_var_sized(a, &header_id, NULL))
-		return ARCHIVE_EOF;
-
-	if(!read_var_sized(a, &header_flags, NULL))
-		return ARCHIVE_EOF;
-
-	rar->generic.split_after = (header_flags & HFL_SPLIT_AFTER) > 0;
-	rar->generic.split_before = (header_flags & HFL_SPLIT_BEFORE) > 0;
-	rar->generic.size = (int)hdr_size;
-	rar->generic.last_header_id = (int)header_id;
-	rar->main.endarc = 0;
-
-	/* Those are possible header ids in RARv5. */
-	switch(header_id) {
-		case HEAD_MAIN:
-			ret = process_head_main(a, rar, entry, header_flags);
-
-			/* Main header doesn't have any files in it, so it's
-			 * pointless to return to the caller. Retry to next
-			 * header, which should be HEAD_FILE/HEAD_SERVICE. */
-			if(ret == ARCHIVE_OK) {
-				rar5_skip_remaining_block(a, body_start,
-				    raw_hdr_size);
-				return ARCHIVE_RETRY;
-			}
-
-			return ret;
-		case HEAD_SERVICE:
-			ret = process_head_service(a, rar, entry, header_flags);
-			return ret;
-		case HEAD_FILE:
-			ret = process_head_file(a, rar, entry, header_flags);
-			return ret;
-		case HEAD_CRYPT:
-			archive_entry_set_is_metadata_encrypted(entry, 1);
-			archive_entry_set_is_data_encrypted(entry, 1);
-			rar->has_encrypted_entries = 1;
-			rar->headers_are_encrypted = 1;
-			archive_set_error(&a->archive,
-			    ARCHIVE_ERRNO_FILE_FORMAT,
-			    "Encryption is not supported");
-			return ARCHIVE_FATAL;
-		case HEAD_ENDARC:
-			rar->main.endarc = 1;
-
-			/* After encountering an end of file marker, we need
-			 * to take into consideration if this archive is
-			 * continued in another file (i.e. is it part01.rar:
-			 * is there a part02.rar?) */
-			if(rar->main.volume) {
-				/* In case there is part02.rar, position the
-				 * read pointer in a proper place, so we can
-				 * resume parsing. */
-				ret = scan_for_signature(a);
-				if(ret == ARCHIVE_FATAL) {
-					return ARCHIVE_EOF;
-				} else {
-					if(rar->vol.expected_vol_no ==
-					    UINT_MAX) {
-						archive_set_error(&a->archive,
-						    ARCHIVE_ERRNO_FILE_FORMAT,
-						    "Header error");
-							return ARCHIVE_FATAL;
-					}
-
-					rar->vol.expected_vol_no =
-					    rar->main.vol_no + 1;
-					return ARCHIVE_OK;
-				}
-			} else {
-				return ARCHIVE_EOF;
-			}
-		case HEAD_MARK:
-			return ARCHIVE_EOF;
-		default:
-			if((header_flags & HFL_SKIP_IF_UNKNOWN) == 0) {
-				archive_set_error(&a->archive,
-				    ARCHIVE_ERRNO_FILE_FORMAT,
-				    "Header type error");
-				return ARCHIVE_FATAL;
-			} else {
-				/* If the block is marked as 'skip if unknown',
-				 * do as the flag says: skip the block
-				 * instead on failing on it. */
-				rar5_skip_remaining_block(a, body_start,
-				    raw_hdr_size);
-				return ARCHIVE_RETRY;
-			}
-	}
-
-#if !defined WIN32
-	// Not reached.
-	archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
-	    "Internal unpacker error");
-	return ARCHIVE_FATAL;
-#endif
-}
-
-static int skip_base_block(struct archive_read* a) {
-	int ret;
-	struct rar5* rar = get_context(a);
-
-	/* Create a new local archive_entry structure that will be operated on
-	 * by header reader; operations on this archive_entry will be discarded.
-	 */
-	struct archive_entry* entry = archive_entry_new();
-	ret = process_base_block(a, entry);
-
-	/* Discard operations on this archive_entry structure. */
-	archive_entry_free(entry);
-	if(ret == ARCHIVE_FATAL)
-		return ret;
-
-	if(rar->generic.last_header_id == 2 && rar->generic.split_before > 0)
-		return ARCHIVE_OK;
-
-	if(ret == ARCHIVE_OK)
-		return ARCHIVE_RETRY;
-	else
-		return ret;
-}
-
-static int try_skip_sfx(struct archive_read *a)
-{
-	const char *p;
-
-	if ((p = __archive_read_ahead(a, 7, NULL)) == NULL)
-		return ARCHIVE_EOF;
-
-	if ((p[0] == 'M' && p[1] == 'Z') || memcmp(p, "\x7F\x45LF", 4) == 0)
-	{
-		char signature[sizeof(rar5_signature_xor)];
-		const void *h;
-		const char *q;
-		size_t skip, total = 0;
-		ssize_t bytes, window = 4096;
-
-		rar5_signature(signature);
-
-		while (total + window <= (1024 * 512)) {
-			h = __archive_read_ahead(a, window, &bytes);
-			if (h == NULL) {
-				/* Remaining bytes are less than window. */
-				window >>= 1;
-				if (window < 0x40)
-					goto fatal;
-				continue;
-			}
-			if (bytes < 0x40)
-				goto fatal;
-			p = h;
-			q = p + bytes;
-
-			/*
-			 * Scan ahead until we find something that looks
-			 * like the RAR header.
-			 */
-			while (p + 8 < q) {
-				if (memcmp(p, signature, sizeof(signature)) == 0) {
-					skip = p - (const char *)h;
-					__archive_read_consume(a, skip);
-					return (ARCHIVE_OK);
-				}
-				p += 0x10;
-			}
-			skip = p - (const char *)h;
-			__archive_read_consume(a, skip);
-			total += skip;
-		}
-	}
-
-	return ARCHIVE_OK;
-fatal:
-	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
-			"Couldn't find out RAR header");
-	return (ARCHIVE_FATAL);
-}
-
-static int rar5_read_header(struct archive_read *a,
-    struct archive_entry *entry)
-{
-	struct rar5* rar = get_context(a);
-	int ret;
-
-	/*
-	 * It should be sufficient to call archive_read_next_header() for
-	 * a reader to determine if an entry is encrypted or not.
-	 */
-	if (rar->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) {
-		rar->has_encrypted_entries = 0;
-	}
-
-	if(rar->header_initialized == 0) {
-		init_header(a);
-		if ((ret = try_skip_sfx(a)) < ARCHIVE_WARN)
-			return ret;
-		rar->header_initialized = 1;
-	}
-
-	if(rar->skipped_magic == 0) {
-		if(ARCHIVE_OK != consume(a, sizeof(rar5_signature_xor))) {
-			return ARCHIVE_EOF;
-		}
-
-		rar->skipped_magic = 1;
-	}
-
-	do {
-		ret = process_base_block(a, entry);
-	} while(ret == ARCHIVE_RETRY ||
-			(rar->main.endarc > 0 && ret == ARCHIVE_OK));
-
-	return ret;
-}
-
-static int init_unpack(struct rar5* rar) {
-	rar->file.calculated_crc32 = 0;
-	init_window_mask(rar);
-
-	free(rar->cstate.window_buf);
-	free(rar->cstate.filtered_buf);
-
-	rar->cstate.window_buf = NULL;
-	rar->cstate.filtered_buf = NULL;
-
-	if(rar->cstate.window_size > 0) {
-		rar->cstate.window_buf = calloc(1, rar->cstate.window_size);
-		if(rar->cstate.window_buf == NULL)
-			return ARCHIVE_FATAL;
-		rar->cstate.filtered_buf = calloc(1, rar->cstate.window_size);
-		if(rar->cstate.filtered_buf == NULL)
-			return ARCHIVE_FATAL;
-	}
-
-	clear_data_ready_stack(rar);
-
-	rar->cstate.write_ptr = 0;
-	rar->cstate.last_write_ptr = 0;
-
-	memset(&rar->cstate.bd, 0, sizeof(rar->cstate.bd));
-	memset(&rar->cstate.ld, 0, sizeof(rar->cstate.ld));
-	memset(&rar->cstate.dd, 0, sizeof(rar->cstate.dd));
-	memset(&rar->cstate.ldd, 0, sizeof(rar->cstate.ldd));
-	memset(&rar->cstate.rd, 0, sizeof(rar->cstate.rd));
-	return ARCHIVE_OK;
-}
-
-static void update_crc(struct rar5* rar, const uint8_t* p, size_t to_read) {
-    int verify_crc;
-
-	if(rar->skip_mode) {
-#if defined CHECK_CRC_ON_SOLID_SKIP
-		verify_crc = 1;
-#else
-		verify_crc = 0;
-#endif
-	} else
-		verify_crc = 1;
-
-	if(verify_crc) {
-		/* Don't update CRC32 if the file doesn't have the
-		 * `stored_crc32` info filled in. */
-		if(rar->file.stored_crc32 > 0) {
-			rar->file.calculated_crc32 =
-				crc32(rar->file.calculated_crc32, p, (unsigned int)to_read);
-		}
-
-		/* Check if the file uses an optional BLAKE2sp checksum
-		 * algorithm. */
-		if(rar->file.has_blake2 > 0) {
-			/* Return value of the `update` function is always 0,
-			 * so we can explicitly ignore it here. */
-			(void) blake2sp_update(&rar->file.b2state, p, to_read);
-		}
-	}
-}
-
-static int create_decode_tables(uint8_t* bit_length,
-    struct decode_table* table, int size)
-{
-	int code, upper_limit = 0, i, lc[16];
-	uint32_t decode_pos_clone[rar5_countof(table->decode_pos)];
-	ssize_t cur_len, quick_data_size;
-
-	memset(&lc, 0, sizeof(lc));
-	memset(table->decode_num, 0, sizeof(table->decode_num));
-	table->size = size;
-	table->quick_bits = size == HUFF_NC ? 10 : 7;
-
-	for(i = 0; i < size; i++) {
-		lc[bit_length[i] & 15]++;
-	}
-
-	lc[0] = 0;
-	table->decode_pos[0] = 0;
-	table->decode_len[0] = 0;
-
-	for(i = 1; i < 16; i++) {
-		upper_limit += lc[i];
-
-		table->decode_len[i] = upper_limit << (16 - i);
-		table->decode_pos[i] = table->decode_pos[i - 1] + lc[i - 1];
-
-		upper_limit <<= 1;
-	}
-
-	/* Verify the code-length distribution is not over-subscribed.
-	 * After the loop above, upper_limit == sum(lc[i] * 2^(16-i)).
-	 * For a valid prefix-free code this must be <= 2^16 = 65536.
-	 * An over-subscribed table (> 65536) cannot produce a valid
-	 * decode table and must be rejected. */
-	if(upper_limit > 65536) {
-		return ARCHIVE_FAILED;
-	}
-
-	memcpy(decode_pos_clone, table->decode_pos, sizeof(decode_pos_clone));
-
-	for(i = 0; i < size; i++) {
-		uint8_t clen = bit_length[i] & 15;
-		if(clen > 0) {
-			int last_pos = decode_pos_clone[clen];
-			table->decode_num[last_pos] = i;
-			decode_pos_clone[clen]++;
-		}
-	}
-
-	quick_data_size = (int64_t)1 << table->quick_bits;
-	cur_len = 1;
-	for(code = 0; code < quick_data_size; code++) {
-		int bit_field = code << (16 - table->quick_bits);
-		int dist, pos;
-
-		while(cur_len < rar5_countof(table->decode_len) &&
-				bit_field >= table->decode_len[cur_len]) {
-			cur_len++;
-		}
-
-		table->quick_len[code] = (uint8_t) cur_len;
-
-		dist = bit_field - table->decode_len[cur_len - 1];
-		dist >>= (16 - cur_len);
-
-		pos = table->decode_pos[cur_len & 15] + dist;
-		if(cur_len < rar5_countof(table->decode_pos) && pos < size) {
-			table->quick_num[code] = table->decode_num[pos];
-		} else {
-			table->quick_num[code] = 0;
-		}
-	}
-
-	return ARCHIVE_OK;
-}
-
-static int decode_number(struct archive_read* a, struct decode_table* table,
-    const uint8_t* p, uint16_t* num)
-{
-	int i, bits, dist, ret;
-	uint16_t bitfield;
-	uint32_t pos;
-	struct rar5* rar = get_context(a);
-
-	if(ARCHIVE_OK != (ret = read_bits_16(a, rar, p, &bitfield))) {
-		return ret;
-	}
-
-	bitfield &= 0xfffe;
-
-	if(bitfield < table->decode_len[table->quick_bits]) {
-		int code = bitfield >> (16 - table->quick_bits);
-		skip_bits(rar, table->quick_len[code]);
-		*num = table->quick_num[code];
-		return ARCHIVE_OK;
-	}
-
-	bits = 15;
-
-	for(i = table->quick_bits + 1; i < 15; i++) {
-		if(bitfield < table->decode_len[i]) {
-			bits = i;
-			break;
-		}
-	}
-
-	skip_bits(rar, bits);
-
-	dist = bitfield - table->decode_len[bits - 1];
-	dist >>= (16 - bits);
-	pos = table->decode_pos[bits] + dist;
-
-	if(pos >= table->size)
-		pos = 0;
-
-	*num = table->decode_num[pos];
-	return ARCHIVE_OK;
-}
-
-/* Reads and parses Huffman tables from the beginning of the block. */
-static int parse_tables(struct archive_read* a, struct rar5* rar,
-    const uint8_t* p)
-{
-	int ret, value, i, w, idx = 0;
-	uint8_t bit_length[HUFF_BC],
-		table[HUFF_TABLE_SIZE],
-		nibble_mask = 0xF0,
-		nibble_shift = 4;
-
-	enum { ESCAPE = 15 };
-
-	/* The data for table generation is compressed using a simple RLE-like
-	 * algorithm when storing zeroes, so we need to unpack it first. */
-	for(w = 0, i = 0; w < HUFF_BC;) {
-		if(i >= rar->cstate.cur_block_size) {
-			/* Truncated data, can't continue. */
-			archive_set_error(&a->archive,
-			    ARCHIVE_ERRNO_FILE_FORMAT,
-			    "Truncated data in huffman tables");
-			return ARCHIVE_FAILED;
-		}
-
-		value = (p[i] & nibble_mask) >> nibble_shift;
-
-		if(nibble_mask == 0x0F)
-			++i;
-
-		nibble_mask ^= 0xFF;
-		nibble_shift ^= 4;
-
-		/* Values smaller than 15 is data, so we write it directly.
-		 * Value 15 is a flag telling us that we need to unpack more
-		 * bytes. */
-		if(value == ESCAPE) {
-			value = (p[i] & nibble_mask) >> nibble_shift;
-			if(nibble_mask == 0x0F)
-				++i;
-			nibble_mask ^= 0xFF;
-			nibble_shift ^= 4;
-
-			if(value == 0) {
-				/* We sometimes need to write the actual value
-				 * of 15, so this case handles that. */
-				bit_length[w++] = ESCAPE;
-			} else {
-				int k;
-
-				/* Fill zeroes. */
-				for(k = 0; (k < value + 2) && (w < HUFF_BC);
-				    k++) {
-					bit_length[w++] = 0;
-				}
-			}
-		} else {
-			bit_length[w++] = value;
-		}
-	}
-
-	rar->bits.in_addr = i;
-	rar->bits.bit_addr = nibble_shift ^ 4;
-
-	ret = create_decode_tables(bit_length, &rar->cstate.bd, HUFF_BC);
-	if(ret != ARCHIVE_OK) {
-		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
-		    "Decoding huffman tables failed");
-		return ARCHIVE_FAILED;
-	}
-
-	for(i = 0; i < HUFF_TABLE_SIZE;) {
-		uint16_t num;
-
-		ret = decode_number(a, &rar->cstate.bd, p, &num);
-		if(ret != ARCHIVE_OK) {
-			archive_set_error(&a->archive,
-			    ARCHIVE_ERRNO_FILE_FORMAT,
-			    "Decoding huffman tables failed");
-			return ARCHIVE_FAILED;
-		}
-
-		if(num < 16) {
-			/* 0..15: store directly */
-			table[i] = (uint8_t) num;
-			i++;
-		} else if(num < 18) {
-			/* 16..17: repeat previous code */
-			uint16_t n;
-
-			if(ARCHIVE_OK != (ret = read_bits_16(a, rar, p, &n)))
-				return ret;
-
-			if(num == 16) {
-				n >>= 13;
-				n += 3;
-				skip_bits(rar, 3);
-			} else {
-				n >>= 9;
-				n += 11;
-				skip_bits(rar, 7);
-			}
-
-			if(i > 0) {
-				while(n-- > 0 && i < HUFF_TABLE_SIZE) {
-					table[i] = table[i - 1];
-					i++;
-				}
-			} else {
-				archive_set_error(&a->archive,
-				    ARCHIVE_ERRNO_FILE_FORMAT,
-				    "Unexpected error when decoding "
-				    "huffman tables");
-				return ARCHIVE_FAILED;
-			}
-		} else {
-			/* other codes: fill with zeroes `n` times */
-			uint16_t n;
-
-			if(ARCHIVE_OK != (ret = read_bits_16(a, rar, p, &n)))
-				return ret;
-
-			if(num == 18) {
-				n >>= 13;
-				n += 3;
-				skip_bits(rar, 3);
-			} else {
-				n >>= 9;
-				n += 11;
-				skip_bits(rar, 7);
-			}
-
-			while(n-- > 0 && i < HUFF_TABLE_SIZE)
-				table[i++] = 0;
-		}
-	}
-
-	ret = create_decode_tables(&table[idx], &rar->cstate.ld, HUFF_NC);
-	if(ret != ARCHIVE_OK) {
-		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
-		     "Failed to create literal table");
-		return ARCHIVE_FAILED;
-	}
-
-	idx += HUFF_NC;
-
-	ret = create_decode_tables(&table[idx], &rar->cstate.dd, HUFF_DC);
-	if(ret != ARCHIVE_OK) {
-		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
-		    "Failed to create distance table");
-		return ARCHIVE_FAILED;
-	}
-
-	idx += HUFF_DC;
-
-	ret = create_decode_tables(&table[idx], &rar->cstate.ldd, HUFF_LDC);
-	if(ret != ARCHIVE_OK) {
-		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
-		    "Failed to create lower bits of distances table");
-		return ARCHIVE_FAILED;
-	}
-
-	idx += HUFF_LDC;
-
-	ret = create_decode_tables(&table[idx], &rar->cstate.rd, HUFF_RC);
-	if(ret != ARCHIVE_OK) {
-		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
-		    "Failed to create repeating distances table");
-		return ARCHIVE_FAILED;
-	}
-
-	return ARCHIVE_OK;
-}
-
-/* Parses the block header, verifies its CRC byte, and saves the header
- * fields inside the `hdr` pointer. */
-static int parse_block_header(struct archive_read* a, const uint8_t* p,
-    ssize_t* block_size, struct compressed_block_header* hdr)
-{
-	uint8_t calculated_cksum;
-	memcpy(hdr, p, sizeof(struct compressed_block_header));
-
-	if(bf_byte_count(hdr) > 2) {
-		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
-		    "Unsupported block header size (was %d, max is 2)",
-		    bf_byte_count(hdr));
-		return ARCHIVE_FAILED;
-	}
-
-	/* This should probably use bit reader interface in order to be more
-	 * future-proof. */
-	*block_size = 0;
-	switch(bf_byte_count(hdr)) {
-		/* 1-byte block size */
-		case 0:
-			*block_size = *(const uint8_t*) &p[2];
-			break;
-
-		/* 2-byte block size */
-		case 1:
-			*block_size = archive_le16dec(&p[2]);
-			break;
-
-		/* 3-byte block size */
-		case 2:
-			*block_size = archive_le32dec(&p[2]);
-			*block_size &= 0x00FFFFFF;
-			break;
-
-		/* Other block sizes are not supported. This case is not
-		 * reached, because we have an 'if' guard before the switch
-		 * that makes sure of it. */
-		default:
-			return ARCHIVE_FATAL;
-	}
-
-	/* Verify the block header checksum. 0x5A is a magic value and is
-	 * always * constant. */
-	calculated_cksum = 0x5A
-	    ^ (uint8_t) hdr->block_flags_u8
-	    ^ (uint8_t) *block_size
-	    ^ (uint8_t) (*block_size >> 8)
-	    ^ (uint8_t) (*block_size >> 16);
-
-	if(calculated_cksum != hdr->block_cksum) {
-#ifndef DONT_FAIL_ON_CRC_ERROR
-		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
-		    "Block checksum error: got 0x%x, expected 0x%x",
-		    hdr->block_cksum, calculated_cksum);
-
-		return ARCHIVE_FAILED;
-#endif
-	}
-
-	return ARCHIVE_OK;
-}
-
-/* Convenience function used during filter processing. */
-static int parse_filter_data(struct archive_read* a, struct rar5* rar,
-	const uint8_t* p, uint32_t* filter_data)
-{
-	int i, bytes, ret;
-	uint32_t data = 0;
-
-	if(ARCHIVE_OK != (ret = read_consume_bits(a, rar, p, 2, &bytes)))
-		return ret;
-
-	bytes++;
-
-	for(i = 0; i < bytes; i++) {
-		uint16_t byte;
-
-		if(ARCHIVE_OK != (ret = read_bits_16(a, rar, p, &byte))) {
-			return ret;
-		}
-
-		/* Cast to uint32_t will ensure the shift operation will not
-		 * produce undefined result. */
-		data += ((uint32_t) byte >> 8) << (i * 8);
-		skip_bits(rar, 8);
-	}
-
-	*filter_data = data;
-	return ARCHIVE_OK;
-}
-
-/* Function is used during sanity checking. */
-static int is_valid_filter_block_start(struct rar5* rar,
-    uint32_t start)
-{
-	const int64_t block_start = (ssize_t) start + rar->cstate.write_ptr;
-	const int64_t last_bs = rar->cstate.last_block_start;
-	const ssize_t last_bl = rar->cstate.last_block_length;
-
-	if(last_bs == 0 || last_bl == 0) {
-		/* We didn't have any filters yet, so accept this offset. */
-		return 1;
-	}
-
-	if(block_start >= last_bs + last_bl) {
-		/* Current offset is bigger than last block's end offset, so
-		 * accept current offset. */
-		return 1;
-	}
-
-	/* Any other case is not a normal situation and we should fail. */
-	return 0;
-}
-
-/* The function will create a new filter, read its parameters from the input
- * stream and add it to the filter collection. */
-static int parse_filter(struct archive_read* ar, const uint8_t* p) {
-	uint32_t block_start, block_length;
-	uint16_t filter_type;
-	struct filter_info* filt = NULL;
-	struct rar5* rar = get_context(ar);
-	int ret;
-
-	/* Read the parameters from the input stream. */
-	if(ARCHIVE_OK != (ret = parse_filter_data(ar, rar, p, &block_start)))
-		return ret;
-
-	if(ARCHIVE_OK != (ret = parse_filter_data(ar, rar, p, &block_length)))
-		return ret;
-
-	if(ARCHIVE_OK != (ret = read_bits_16(ar, rar, p, &filter_type)))
-		return ret;
-
-	filter_type >>= 13;
-	skip_bits(rar, 3);
-
-	/* Perform some sanity checks on this filter parameters. */
-
-	if(block_length < 4 ||
-	    block_length > 0x400000 ||
-	    !is_valid_filter_block_start(rar, block_start) ||
-	    (rar->cstate.window_size > 0 &&
-	     (ssize_t)block_length > rar->cstate.window_size >> 1))
-	{
-		archive_set_error(&ar->archive, ARCHIVE_ERRNO_FILE_FORMAT,
-		    "Invalid filter encountered");
-		return ARCHIVE_FAILED;
-	}
-
-	/* Allocate a new filter. */
-	filt = add_new_filter(rar);
-	if(filt == NULL) {
-		archive_set_error(&ar->archive, ENOMEM,
-		    "Can't allocate memory for a filter descriptor");
-		return ARCHIVE_FATAL;
-	}
-
-	filt->type = filter_type;
-	filt->block_start = rar->cstate.write_ptr + block_start;
-	filt->block_length = block_length;
-
-	rar->cstate.last_block_start = filt->block_start;
-	rar->cstate.last_block_length = filt->block_length;
-
-	/* Read some more data in case this is a DELTA filter. Other filter
-	 * types don't require any additional data over what was already
-	 * read. */
-	if(filter_type == FILTER_DELTA) {
-		int channels;
-
-		if(ARCHIVE_OK != (ret = read_consume_bits(ar, rar, p, 5, &channels)))
-			return ret;
-
-		filt->channels = channels + 1;
-	}
-
-	return ARCHIVE_OK;
-}
-
-static int decode_code_length(struct archive_read* a, struct rar5* rar,
-	const uint8_t* p, uint16_t code)
-{
-	int lbits, length = 2;
-
-	if(code < 8) {
-		lbits = 0;
-		length += code;
-	} else {
-		lbits = code / 4 - 1;
-		length += (4 | (code & 3)) << lbits;
-	}
-
-	if(lbits > 0) {
-		int add;
-
-		if(ARCHIVE_OK != read_consume_bits(a, rar, p, lbits, &add))
-			return -1;
-
-		length += add;
-	}
-
-	return length;
-}
-
-static int copy_string(struct archive_read* a, int len, int dist) {
-	struct rar5* rar = get_context(a);
-	const ssize_t cmask = rar->cstate.window_mask;
-	const uint64_t write_ptr = rar->cstate.write_ptr +
-	    rar->cstate.solid_offset;
-	int i;
-
-	if (rar->cstate.window_buf == NULL)
-		return ARCHIVE_FATAL;
-
-	/* The unpacker spends most of the time in this function. It would be
-	 * a good idea to introduce some optimizations here.
-	 *
-	 * Just remember that this loop treats buffers that overlap differently
-	 * than buffers that do not overlap. This is why a simple memcpy(3)
-	 * call will not be enough. */
-
-	for(i = 0; i < len; i++) {
-		const ssize_t write_idx = (write_ptr + i) & cmask;
-		const ssize_t read_idx = (write_ptr + i - dist) & cmask;
-		rar->cstate.window_buf[write_idx] =
-		    rar->cstate.window_buf[read_idx];
-	}
-
-	rar->cstate.write_ptr += len;
-	return ARCHIVE_OK;
-}
-
-static int do_uncompress_block(struct archive_read* a, const uint8_t* p) {
-	struct rar5* rar = get_context(a);
-	uint16_t num;
-	int ret;
-
-	const uint64_t cmask = rar->cstate.window_mask;
-	const struct compressed_block_header* hdr = &rar->last_block_hdr;
-	const uint8_t bit_size = 1 + bf_bit_size(hdr);
-
-	while(1) {
-		if(rar->cstate.write_ptr - rar->cstate.last_write_ptr >
-		    (rar->cstate.window_size >> 1)) {
-			/* Don't allow growing data by more than half of the
-			 * window size at a time. In such case, break the loop;
-			 *  next call to this function will continue processing
-			 *  from this moment. */
-			break;
-		}
-
-		if(rar->bits.in_addr > rar->cstate.cur_block_size - 1 ||
-		    (rar->bits.in_addr == rar->cstate.cur_block_size - 1 &&
-		    rar->bits.bit_addr >= bit_size))
-		{
-			/* If the program counter is here, it means the
-			 * function has finished processing the block. */
-			rar->cstate.block_parsing_finished = 1;
-			break;
-		}
-
-		/* Decode the next literal. */
-		if(ARCHIVE_OK != decode_number(a, &rar->cstate.ld, p, &num)) {
-			return ARCHIVE_EOF;
-		}
-
-		/* Num holds a decompression literal, or 'command code'.
-		 *
-		 * - Values lower than 256 are just bytes. Those codes
-		 *   can be stored in the output buffer directly.
-		 *
-		 * - Code 256 defines a new filter, which is later used to
-		 *   transform the data block accordingly to the filter type.
-		 *   The data block needs to be fully uncompressed first.
-		 *
-		 * - Code bigger than 257 and smaller than 262 define
-		 *   a repetition pattern that should be copied from
-		 *   an already uncompressed chunk of data.
-		 */
-
-		if(num < 256) {
-			/* Directly store the byte. */
-			int64_t write_idx = rar->cstate.solid_offset +
-			    rar->cstate.write_ptr++;
-
-			rar->cstate.window_buf[write_idx & cmask] =
-			    (uint8_t) num;
-			continue;
-		} else if(num >= 262) {
-			uint16_t dist_slot;
-			int len = decode_code_length(a, rar, p, num - 262),
-				dbits,
-				dist = 1;
-
-			if(len == -1) {
-				archive_set_error(&a->archive,
-				    ARCHIVE_ERRNO_PROGRAMMER,
-				    "Failed to decode the code length");
-
-				return rar->main.solid ? ARCHIVE_FATAL : ARCHIVE_FAILED;
-			}
-
-			if(ARCHIVE_OK != decode_number(a, &rar->cstate.dd, p,
-			    &dist_slot))
-			{
-				archive_set_error(&a->archive,
-				    ARCHIVE_ERRNO_PROGRAMMER,
-				    "Failed to decode the distance slot");
-
-				return rar->main.solid ? ARCHIVE_FATAL : ARCHIVE_FAILED;
-			}
-
-			if(dist_slot < 4) {
-				dbits = 0;
-				dist += dist_slot;
-			} else {
-				dbits = dist_slot / 2 - 1;
-
-				/* Cast to uint32_t will make sure the shift
-				 * left operation won't produce undefined
-				 * result. Then, the uint32_t type will
-				 * be implicitly casted to int. */
-				dist += (uint32_t) (2 |
-				    (dist_slot & 1)) << dbits;
-			}
-
-			if(dbits > 0) {
-				if(dbits >= 4) {
-					uint32_t add = 0;
-					uint16_t low_dist;
-
-					if(dbits > 4) {
-						if(ARCHIVE_OK != (ret = read_bits_32(
-						    a, rar, p, &add))) {
-							/* Return EOF if we
-							 * can't read more
-							 * data. */
-							return ret;
-						}
-
-						skip_bits(rar, dbits - 4);
-						add = (add >> (
-						    36 - dbits)) << 4;
-						dist += add;
-					}
-
-					if(ARCHIVE_OK != decode_number(a,
-					    &rar->cstate.ldd, p, &low_dist))
-					{
-						archive_set_error(&a->archive,
-						    ARCHIVE_ERRNO_PROGRAMMER,
-						    "Failed to decode the "
-						    "distance slot");
-
-						return rar->main.solid ? ARCHIVE_FATAL : ARCHIVE_FAILED;
-					}
-
-					if(dist >= INT_MAX - low_dist - 1) {
-						/* This only happens in
-						 * invalid archives. */
-						archive_set_error(&a->archive,
-						    ARCHIVE_ERRNO_FILE_FORMAT,
-						    "Distance pointer "
-						    "overflow");
-						return rar->main.solid ? ARCHIVE_FATAL : ARCHIVE_FAILED;
-					}
-
-					dist += low_dist;
-				} else {
-					/* dbits is one of [0,1,2,3] */
-					int add;
-
-					if(ARCHIVE_OK != (ret = read_consume_bits(a, rar,
-					     p, dbits, &add))) {
-						/* Return EOF if we can't read
-						 * more data. */
-						return ret;
-					}
-
-					dist += add;
-				}
-			}
-
-			if(dist > 0x100) {
-				len++;
-
-				if(dist > 0x2000) {
-					len++;
-
-					if(dist > 0x40000) {
-						len++;
-					}
-				}
-			}
-
-			dist_cache_push(rar, dist);
-			rar->cstate.last_len = len;
-
-			if(ARCHIVE_OK != copy_string(a, len, dist))
-				return rar->main.solid ? ARCHIVE_FATAL : ARCHIVE_FAILED;
-
-			continue;
-		} else if(num == 256) {
-			/* Create a filter. */
-			ret = parse_filter(a, p);
-			if(ret != ARCHIVE_OK)
-				return ret;
-
-			continue;
-		} else if(num == 257) {
-			if(rar->cstate.last_len != 0) {
-				if(ARCHIVE_OK != copy_string(a,
-				    rar->cstate.last_len,
-				    rar->cstate.dist_cache[0]))
-				{
-					return rar->main.solid ? ARCHIVE_FATAL : ARCHIVE_FAILED;
-				}
-			}
-
-			continue;
-		} else {
-			/* num < 262 */
-			const int idx = num - 258;
-			const int dist = dist_cache_touch(rar, idx);
-
-			uint16_t len_slot;
-			int len;
-
-			if(ARCHIVE_OK != decode_number(a, &rar->cstate.rd, p,
-			    &len_slot)) {
-				return rar->main.solid ? ARCHIVE_FATAL : ARCHIVE_FAILED;
-			}
-
-			len = decode_code_length(a, rar, p, len_slot);
-			if (len == -1) {
-				return rar->main.solid ? ARCHIVE_FATAL : ARCHIVE_FAILED;
-			}
-
-			rar->cstate.last_len = len;
-
-			if(ARCHIVE_OK != copy_string(a, len, dist))
-				return rar->main.solid ? ARCHIVE_FATAL : ARCHIVE_FAILED;
-
-			continue;
-		}
-	}
-
-	return ARCHIVE_OK;
-}
-
-/* Binary search for the RARv5 signature. */
-static int scan_for_signature(struct archive_read* a) {
-	const uint8_t* p;
-	const int chunk_size = 512;
-	ssize_t i;
-	char signature[sizeof(rar5_signature_xor)];
-
-	/* If we're here, it means we're on an 'unknown territory' data.
-	 * There's no indication what kind of data we're reading here.
-	 * It could be some text comment, any kind of binary data,
-	 * digital sign, dragons, etc.
-	 *
-	 * We want to find a valid RARv5 magic header inside this unknown
-	 * data. */
-
-	/* Is it possible in libarchive to just skip everything until the
-	 * end of the file? If so, it would be a better approach than the
-	 * current implementation of this function. */
-
-	rar5_signature(signature);
-
-	while(1) {
-		if(!read_ahead(a, chunk_size, &p))
-			return ARCHIVE_EOF;
-
-		for(i = 0; i < chunk_size - (int)sizeof(rar5_signature_xor);
-		    i++) {
-			if(memcmp(&p[i], signature,
-			    sizeof(rar5_signature_xor)) == 0) {
-				/* Consume the number of bytes we've used to
-				 * search for the signature, as well as the
-				 * number of bytes used by the signature
-				 * itself. After this we should be standing
-				 * on a valid base block header. */
-				(void) consume(a,
-				    i + sizeof(rar5_signature_xor));
-				return ARCHIVE_OK;
-			}
-		}
-
-		consume(a, chunk_size);
-	}
-
-	return ARCHIVE_FATAL;
-}
-
-/* This function will switch the multivolume archive file to another file,
- * i.e. from part03 to part 04. */
-static int advance_multivolume(struct archive_read* a) {
-	int lret;
-	struct rar5* rar = get_context(a);
-
-	/* A small state machine that will skip unnecessary data, needed to
-	 * switch from one multivolume to another. Such skipping is needed if
-	 * we want to be an stream-oriented (instead of file-oriented)
-	 * unpacker.
-	 *
-	 * The state machine starts with `rar->main.endarc` == 0. It also
-	 * assumes that current stream pointer points to some base block
-	 * header.
-	 *
-	 * The `endarc` field is being set when the base block parsing
-	 * function encounters the 'end of archive' marker.
-	 */
-
-	while(1) {
-		if(rar->main.endarc == 1) {
-			int looping = 1;
-
-			rar->main.endarc = 0;
-
-			while(looping) {
-				lret = skip_base_block(a);
-				switch(lret) {
-					case ARCHIVE_RETRY:
-						/* Continue looping. */
-						break;
-					case ARCHIVE_OK:
-						/* Break loop. */
-						looping = 0;
-						break;
-					default:
-						/* Forward any errors to the
-						 * caller. */
-						return lret;
-				}
-			}
-
-			break;
-		} else {
-			/* Skip current base block. In order to properly skip
-			 * it, we really need to simply parse it and discard
-			 * the results. */
-
-			lret = skip_base_block(a);
-			if(lret == ARCHIVE_FATAL || lret == ARCHIVE_FAILED)
-				return lret;
-
-			/* The `skip_base_block` function tells us if we
-			 * should continue with skipping, or we should stop
-			 * skipping. We're trying to skip everything up to
-			 * a base FILE block. */
-
-			if(lret != ARCHIVE_RETRY) {
-				/* If there was an error during skipping, or we
-				 * have just skipped a FILE base block... */
-
-				if(rar->main.endarc == 0) {
-					return lret;
-				} else {
-					continue;
-				}
-			}
-		}
-	}
-
-	return ARCHIVE_OK;
-}
-
-/* Merges the partial block from the first multivolume archive file, and
- * partial block from the second multivolume archive file. The result is
- * a chunk of memory containing the whole block, and the stream pointer
- * is advanced to the next block in the second multivolume archive file. */
-static int merge_block(struct archive_read* a, ssize_t block_size,
-    const uint8_t** p)
-{
-	struct rar5* rar = get_context(a);
-	ssize_t cur_block_size, partial_offset = 0;
-	const uint8_t* lp;
-	int ret;
-
-	if(rar->merge_mode) {
-		archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
-		    "Recursive merge is not allowed");
-
-		return ARCHIVE_FATAL;
-	}
-
-	/* Set a flag that we're in the switching mode. */
-	rar->cstate.switch_multivolume = 1;
-
-	/* Reallocate the memory which will hold the whole block. */
-	if(rar->vol.push_buf)
-		free((void*) rar->vol.push_buf);
-
-	/* Increasing the allocation block by 8 is due to bit reading functions,
-	 * which are using additional 2 or 4 bytes. Allocating the block size
-	 * by exact value would make bit reader perform reads from invalid
-	 * memory block when reading the last byte from the buffer. */
-	rar->vol.push_buf = malloc(block_size + 8);
-	if(!rar->vol.push_buf) {
-		archive_set_error(&a->archive, ENOMEM,
-		    "Can't allocate memory for a merge block buffer");
-		rar->cstate.switch_multivolume = 0;
-		return ARCHIVE_FATAL;
-	}
-
-	/* Valgrind complains if the extension block for bit reader is not
-	 * initialized, so initialize it. */
-	memset(&rar->vol.push_buf[block_size], 0, 8);
-
-	/* A single block can span across multiple multivolume archive files,
-	 * so we use a loop here. This loop will consume enough multivolume
-	 * archive files until the whole block is read. */
-
-	while(1) {
-		/* Get the size of current block chunk in this multivolume
-		 * archive file and read it. */
-		cur_block_size = rar5_min(rar->file.bytes_remaining,
-		    block_size - partial_offset);
-
-		if(cur_block_size < 1) {
-			/* bytes_remaining is less than 1 at the wrong point in
-			 * the merge loop, indicating corrupt volume
-			 * accounting. */
-			archive_set_error(&a->archive,
-			    ARCHIVE_ERRNO_FILE_FORMAT,
-			    "Encountered invalid block size during block merge");
-			rar->cstate.switch_multivolume = 0;
-			return ARCHIVE_FATAL;
-		}
-
-		if(!read_ahead(a, cur_block_size, &lp)) {
-			rar->cstate.switch_multivolume = 0;
-			return ARCHIVE_EOF;
-		}
-
-		/* Sanity check; there should never be a situation where this
-		 * function reads more data than the block's size. */
-		if(partial_offset + cur_block_size > block_size) {
-			archive_set_error(&a->archive,
-			    ARCHIVE_ERRNO_PROGRAMMER,
-			    "Consumed too much data when merging blocks");
-			rar->cstate.switch_multivolume = 0;
-			return ARCHIVE_FATAL;
-		}
-
-		/* Merge previous block chunk with current block chunk,
-		 * or create first block chunk if this is our first
-		 * iteration. */
-		memcpy(&rar->vol.push_buf[partial_offset], lp, cur_block_size);
-
-		/* Advance the stream read pointer by this block chunk size. */
-		if(ARCHIVE_OK != consume(a, cur_block_size)) {
-			/* Data was copied but stream pointer didn't advance;
-			 * stream position is unrecoverable. */
-			rar->cstate.switch_multivolume = 0;
-			return ARCHIVE_FATAL;
-		}
-
-		/* Update the pointers. `partial_offset` contains information
-		 * about the sum of merged block chunks. */
-		partial_offset += cur_block_size;
-		rar->file.bytes_remaining -= cur_block_size;
-
-		/* If `partial_offset` is the same as `block_size`, this means
-		 * we've merged all block chunks and we have a valid full
-		 * block. */
-		if(partial_offset == block_size) {
-			break;
-		}
-
-		/* If we don't have any bytes to read, this means we should
-		 * switch to another multivolume archive file. */
-		if(rar->file.bytes_remaining == 0) {
-			rar->merge_mode++;
-			ret = advance_multivolume(a);
-			rar->merge_mode--;
-			if(ret != ARCHIVE_OK) {
-				rar->cstate.switch_multivolume = 0;
-				return ret;
-			}
-		}
-	}
-
-	*p = rar->vol.push_buf;
-
-	/* If we're here, we can resume unpacking by processing the block
-	 * pointed to by the `*p` memory pointer. */
-
-	return ARCHIVE_OK;
-}
-
-static int process_block(struct archive_read* a) {
-	const uint8_t* p;
-	struct rar5* rar = get_context(a);
-	int ret;
-
-	/* If we don't have any data to be processed, this most probably means
-	 * we need to switch to the next volume. */
-	if(rar->main.volume && rar->file.bytes_remaining == 0) {
-		ret = advance_multivolume(a);
-		if(ret != ARCHIVE_OK)
-			return ret;
-	}
-
-	if(rar->cstate.block_parsing_finished) {
-		ssize_t block_size;
-		ssize_t to_skip;
-		ssize_t cur_block_size;
-
-		/* The header size won't be bigger than 6 bytes. */
-		if(!read_ahead(a, 6, &p)) {
-			/* Failed to prefetch data block header. */
-			return ARCHIVE_EOF;
-		}
-
-		/*
-		 * Read block_size by parsing block header. Validate the header
-		 * by calculating CRC byte stored inside the header. Size of
-		 * the header is not constant (block size can be stored either
-		 * in 1 or 2 bytes), that's why block size is left out from the
-		 * `compressed_block_header` structure and returned by
-		 * `parse_block_header` as the second argument. */
-
-		ret = parse_block_header(a, p, &block_size,
-		    &rar->last_block_hdr);
-		if(ret != ARCHIVE_OK) {
-			return ret;
-		}
-
-		/* Skip block header. Next data is huffman tables,
-		 * if present. */
-		to_skip = sizeof(struct compressed_block_header) +
-			bf_byte_count(&rar->last_block_hdr) + 1;
-
-		/* If the block header's to_skip value exceeds the declared
-		 * remaining data, the archive is malformed. */
-		if(to_skip > rar->file.bytes_remaining) {
-			archive_set_error(&a->archive,
-			    ARCHIVE_ERRNO_FILE_FORMAT,
-			    "Block header size exceeds remaining file data");
-			return ARCHIVE_FATAL;
-		}
-
-		if(ARCHIVE_OK != consume(a, to_skip))
-			return ARCHIVE_EOF;
-
-		rar->file.bytes_remaining -= to_skip;
-
-		/* The block size gives information about the whole block size,
-		 * but the block could be stored in split form when using
-		 * multi-volume archives. In this case, the block size will be
-		 * bigger than the actual data stored in this file. Remaining
-		 * part of the data will be in another file. */
-
-		cur_block_size =
-			rar5_min(rar->file.bytes_remaining, block_size);
-
-		if(block_size > rar->file.bytes_remaining) {
-			/* If current blocks' size is bigger than our data
-			 * size, this means we have a multivolume archive.
-			 * In this case, skip all base headers until the end
-			 * of the file, proceed to next "partXXX.rar" volume,
-			 * find its signature, skip all headers up to the first
-			 * FILE base header, and continue from there.
-			 *
-			 * Note that `merge_block` will update the `rar`
-			 * context structure quite extensively. */
-
-			ret = merge_block(a, block_size, &p);
-			if(ret != ARCHIVE_OK) {
-				return ret;
-			}
-
-			cur_block_size = block_size;
-
-			/* Current stream pointer should be now directly
-			 * *after* the block that spanned through multiple
-			 * archive files. `p` pointer should have the data of
-			 * the *whole* block (merged from partial blocks
-			 * stored in multiple archives files). */
-		} else {
-			rar->cstate.switch_multivolume = 0;
-
-			/* Read the whole block size into memory. This can take
-			 * up to  8 megabytes of memory in theoretical cases.
-			 * Might be worth to optimize this and use a standard
-			 * chunk of 4kb's. */
-			if(!read_ahead(a, 4 + cur_block_size, &p)) {
-				/* Failed to prefetch block data. */
-				return ARCHIVE_EOF;
-			}
-		}
-
-		rar->cstate.block_buf = p;
-		rar->cstate.cur_block_size = cur_block_size;
-		rar->cstate.block_parsing_finished = 0;
-
-		rar->bits.in_addr = 0;
-		rar->bits.bit_addr = 0;
-
-		if(bf_is_table_present(&rar->last_block_hdr)) {
-			/* Load Huffman tables. */
-			ret = parse_tables(a, rar, p);
-			if(ret != ARCHIVE_OK) {
-				/* Error during decompression of Huffman
-				 * tables. */
-				return ret;
-			}
-		}
-	} else {
-		/* Block parsing not finished, reuse previous memory buffer. */
-		p = rar->cstate.block_buf;
-	}
-
-	/* Uncompress the block, or a part of it, depending on how many bytes
-	 * will be generated by uncompressing the block.
-	 *
-	 * In case too many bytes will be generated, calling this function
-	 * again will resume the uncompression operation. */
-	ret = do_uncompress_block(a, p);
-	if(ret != ARCHIVE_OK) {
-		return ret;
-	}
-
-	if(rar->cstate.block_parsing_finished &&
-	    rar->cstate.switch_multivolume == 0 &&
-	    rar->cstate.cur_block_size > 0)
-	{
-		/* If we're processing a normal block, consume the whole
-		 * block. We can do this because we've already read the whole
-		 * block to memory. */
-		if(ARCHIVE_OK != consume(a, rar->cstate.cur_block_size))
-			return ARCHIVE_FATAL;
-
-		rar->file.bytes_remaining -= rar->cstate.cur_block_size;
-	} else if(rar->cstate.switch_multivolume) {
-		/* Don't consume the block if we're doing multivolume
-		 * processing. The volume switching function will consume
-		 * the proper count of bytes instead. */
-		rar->cstate.switch_multivolume = 0;
-	}
-
-	return ARCHIVE_OK;
-}
-
-/* Pops the `buf`, `size` and `offset` from the "data ready" stack.
- *
- * Returns ARCHIVE_OK when those arguments can be used, ARCHIVE_RETRY
- * when there is no data on the stack. */
-static int use_data(struct rar5* rar, const void** buf, size_t* size,
-    int64_t* offset)
-{
-	int i;
-
-	for(i = 0; i < rar5_countof(rar->cstate.dready); i++) {
-		struct data_ready *d = &rar->cstate.dready[i];
-
-		if(d->used) {
-			if(buf)    *buf = d->buf;
-			if(size)   *size = d->size;
-			if(offset) *offset = d->offset;
-
-			d->used = 0;
-			return ARCHIVE_OK;
-		}
-	}
-
-	return ARCHIVE_RETRY;
-}
-
-static void clear_data_ready_stack(struct rar5* rar) {
-	memset(&rar->cstate.dready, 0, sizeof(rar->cstate.dready));
-}
-
-/* Pushes the `buf`, `size` and `offset` arguments to the rar->cstate.dready
- * FIFO stack. Those values will be popped from this stack by the `use_data`
- * function. */
-static int push_data_ready(struct archive_read* a, struct rar5* rar,
-    const uint8_t* buf, size_t size, int64_t offset)
-{
-	int i;
-
-	/* Don't push if we're in skip mode. This is needed because solid
-	 * streams need full processing even if we're skipping data. After
-	 * fully processing the stream, we need to discard the generated bytes,
-	 * because we're interested only in the side effect: building up the
-	 * internal window circular buffer. This window buffer will be used
-	 * later during unpacking of requested data. */
-	if(rar->skip_mode)
-		return ARCHIVE_OK;
-
-	/* Sanity check. */
-	if(offset != rar->file.last_offset + rar->file.last_size) {
-		archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
-		    "Sanity check error: output stream is not continuous");
-		return ARCHIVE_FATAL;
-	}
-
-	for(i = 0; i < rar5_countof(rar->cstate.dready); i++) {
-		struct data_ready* d = &rar->cstate.dready[i];
-		if(!d->used) {
-			d->used = 1;
-			d->buf = buf;
-			d->size = size;
-			d->offset = offset;
-
-			/* These fields are used only in sanity checking. */
-			rar->file.last_offset = offset;
-			rar->file.last_size = size;
-
-			/* Calculate the checksum of this new block before
-			 * submitting data to libarchive's engine. */
-			update_crc(rar, d->buf, d->size);
-
-			return ARCHIVE_OK;
-		}
-	}
-
-	/* Program counter will reach this code if the `rar->cstate.data_ready`
-	 * stack will be filled up so that no new entries will be allowed. The
-	 * code shouldn't allow such situation to occur. So we treat this case
-	 * as an internal error. */
-
-	archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
-	    "Premature end of data_ready stack");
-	return ARCHIVE_FATAL;
-}
-
-/* This function uncompresses the data that is stored in the <FILE> base
- * block.
- *
- * The FILE base block looks like this:
- *
- * <header><huffman tables><block_1><block_2>...<block_n>
- *
- * The <header> is a block header, that is parsed in parse_block_header().
- * It's a "compressed_block_header" structure, containing metadata needed
- * to know when we should stop looking for more <block_n> blocks.
- *
- * <huffman tables> contain data needed to set up the huffman tables, needed
- * for the actual decompression.
- *
- * Each <block_n> consists of series of literals:
- *
- * <literal><literal><literal>...<literal>
- *
- * Those literals generate the uncompression data. They operate on a circular
- * buffer, sometimes writing raw data into it, sometimes referencing
- * some previous data inside this buffer, and sometimes declaring a filter
- * that will need to be executed on the data stored in the circular buffer.
- * It all depends on the literal that is used.
- *
- * Sometimes blocks produce output data, sometimes they don't. For example, for
- * some huge files that use lots of filters, sometimes a block is filled with
- * only filter declaration literals. Such blocks won't produce any data in the
- * circular buffer.
- *
- * Sometimes blocks will produce 4 bytes of data, and sometimes 1 megabyte,
- * because a literal can reference previously decompressed data. For example,
- * there can be a literal that says: 'append a byte 0xFE here', and after
- * it another literal can say 'append 1 megabyte of data from circular buffer
- * offset 0x12345'. This is how RAR format handles compressing repeated
- * patterns.
- *
- * The RAR compressor creates those literals and the actual efficiency of
- * compression depends on what those literals are. The literals can also
- * be seen as a kind of a non-turing-complete virtual machine that simply
- * tells the decompressor what it should do.
- * */
-
-static int do_uncompress_file(struct archive_read* a) {
-	struct rar5* rar = get_context(a);
-	int ret;
-	int64_t max_end_pos;
-
-	if(!rar->cstate.initialized) {
-		/* Don't perform full context reinitialization if we're
-		 * processing a solid archive. */
-		if(!rar->main.solid || !rar->cstate.window_buf) {
-			if((ret = init_unpack(rar)) != ARCHIVE_OK)
-				return ret;
-		}
-
-		rar->cstate.initialized = 1;
-	}
-
-	/* Don't allow extraction if window_size is invalid. */
-	if(rar->cstate.window_size == 0) {
-		archive_set_error(&a->archive,
-			ARCHIVE_ERRNO_FILE_FORMAT,
-			"Invalid window size declaration in this file");
-
-		/* This should never happen in valid files. */
-		return ARCHIVE_FAILED;
-	}
-
-	if(rar->cstate.all_filters_applied == 1) {
-		/* We use while(1) here, but standard case allows for just 1
-		 * iteration. The loop will iterate if process_block() didn't
-		 * generate any data at all. This can happen if the block
-		 * contains only filter definitions (this is common in big
-		 * files). */
-		while(1) {
-			ret = process_block(a);
-			if(ret != ARCHIVE_OK)
-				return ret;
-
-			if(rar->cstate.last_write_ptr ==
-			    rar->cstate.write_ptr) {
-				/* The block didn't generate any new data,
-				 * so just process a new block if this one
-				 * wasn't the last block in the file. */
-				if (bf_is_last_block(&rar->last_block_hdr)) {
-					return ARCHIVE_EOF;
-				}
-
-				continue;
-			}
-
-			/* The block has generated some new data, so break
-			 * the loop. */
-			break;
-		}
-	}
-
-	/* Try to run filters. If filters won't be applied, it means that
-	 * insufficient data was generated. */
-	ret = apply_filters(a);
-	if(ret == ARCHIVE_RETRY) {
-		return ARCHIVE_OK;
-	} else if(ret != ARCHIVE_OK) {
-		return ret;
-	}
-
-	if(cdeque_size(&rar->cstate.filters) > 0) {
-		/* Check if we can write something before hitting first
-		 * filter. */
-		struct filter_info* flt;
-
-		/* Get the block_start offset from the first filter. */
-		if(CDE_OK != cdeque_front(&rar->cstate.filters,
-		    cdeque_filter_p(&flt)))
-		{
-			archive_set_error(&a->archive,
-			    ARCHIVE_ERRNO_PROGRAMMER,
-			    "Can't read first filter");
-			return ARCHIVE_FATAL;
-		}
-
-		max_end_pos = rar5_min(flt->block_start,
-		    rar->cstate.write_ptr);
-	} else {
-		/* There are no filters defined, or all filters were applied.
-		 * This means we can just store the data without any
-		 * postprocessing. */
-		max_end_pos = rar->cstate.write_ptr;
-	}
-
-	if(max_end_pos == rar->cstate.last_write_ptr) {
-		/* We can't write anything yet. The block uncompression
-		 * function did not generate enough data, and no filter can be
-		 * applied. At the same time we don't have any data that can be
-		 *  stored without filter postprocessing. This means we need to
-		 *  wait for more data to be generated, so we can apply the
-		 * filters.
-		 *
-		 * Signal the caller that we need more data to be able to do
-		 * anything.
-		 */
-		return ARCHIVE_RETRY;
-	} else {
-		/* We can write the data before hitting the first filter.
-		 * So let's do it. The push_window_data() function will
-		 * effectively return the selected data block to the user
-		 * application. */
-		push_window_data(a, rar, rar->cstate.last_write_ptr,
-		    max_end_pos);
-		rar->cstate.last_write_ptr = max_end_pos;
-	}
-
-	return ARCHIVE_OK;
-}
-
-static int uncompress_file(struct archive_read* a) {
-	int ret;
-
-	while(1) {
-		/* Sometimes the uncompression function will return a
-		 * 'retry' signal. If this will happen, we have to retry
-		 * the function. */
-		ret = do_uncompress_file(a);
-		if(ret != ARCHIVE_RETRY)
-			return ret;
-	}
-}
-
-
-static int do_unstore_file(struct archive_read* a,
-    struct rar5* rar, const void** buf, size_t* size, int64_t* offset)
-{
-	size_t to_read;
-	const uint8_t* p;
-
-	if(rar->file.bytes_remaining == 0 && rar->main.volume > 0 &&
-	    rar->generic.split_after > 0)
-	{
-		int ret;
-
-		rar->cstate.switch_multivolume = 1;
-		ret = advance_multivolume(a);
-		rar->cstate.switch_multivolume = 0;
-
-		if(ret != ARCHIVE_OK) {
-			/* Failed to advance to next multivolume archive
-			 * file. */
-			return ret;
-		}
-	}
-
-	to_read = rar5_min(rar->file.bytes_remaining, 64 * 1024);
-	if(to_read == 0) {
-		return ARCHIVE_EOF;
-	}
-
-	if(!read_ahead(a, to_read, &p)) {
-		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
-		    "I/O error when unstoring file");
-		return ARCHIVE_FATAL;
-	}
-
-	if(ARCHIVE_OK != consume(a, to_read)) {
-		return ARCHIVE_EOF;
-	}
-
-	if(buf)    *buf = p;
-	if(size)   *size = to_read;
-	if(offset) *offset = rar->cstate.last_unstore_ptr;
-
-	rar->file.bytes_remaining -= to_read;
-	rar->cstate.last_unstore_ptr += to_read;
-
-	update_crc(rar, p, to_read);
-	return ARCHIVE_OK;
-}
-
-static int do_unpack(struct archive_read* a, struct rar5* rar,
-    const void** buf, size_t* size, int64_t* offset)
-{
-	enum COMPRESSION_METHOD {
-		STORE = 0, FASTEST = 1, FAST = 2, NORMAL = 3, GOOD = 4,
-		BEST = 5
-	};
-
-	if(rar->file.service > 0) {
-		return do_unstore_file(a, rar, buf, size, offset);
-	} else {
-		switch(rar->cstate.method) {
-			case STORE:
-				return do_unstore_file(a, rar, buf, size,
-				    offset);
-			case FASTEST:
-				/* fallthrough */
-			case FAST:
-				/* fallthrough */
-			case NORMAL:
-				/* fallthrough */
-			case GOOD:
-				/* fallthrough */
-			case BEST:
-				/* No data is returned here. But because a sparse-file aware
-				 * caller (like archive_read_data_into_fd) may treat zero-size
-				 * as a sparse file block, we need to update the offset
-				 * accordingly. At this point the decoder doesn't have any
-				 * pending uncompressed data blocks, so the current position in
-				 * the output file should be last_write_ptr. */
-				if (offset) *offset = rar->cstate.last_write_ptr;
-				return uncompress_file(a);
-			default:
-				archive_set_error(&a->archive,
-				    ARCHIVE_ERRNO_FILE_FORMAT,
-				    "Compression method not supported: 0x%x",
-				    (unsigned int)rar->cstate.method);
-
-				return ARCHIVE_FATAL;
-		}
-	}
-
-#if !defined WIN32
-	/* Not reached. */
-	return ARCHIVE_OK;
-#endif
-}
-
-static int verify_checksums(struct archive_read* a) {
-	int verify_crc;
-	struct rar5* rar = get_context(a);
-
-	/* Check checksums only when actually unpacking the data. There's no
-	 * need to calculate checksum when we're skipping data in solid archives
-	 * (skipping in solid archives is the same thing as unpacking compressed
-	 * data and discarding the result). */
-
-	if(!rar->skip_mode) {
-		/* Always check checksums if we're not in skip mode */
-		verify_crc = 1;
-	} else {
-		/* We can override the logic above with a compile-time option
-		 * NO_CRC_ON_SOLID_SKIP. This option is used during debugging,
-		 * and it will check checksums of unpacked data even when
-		 * we're skipping it. */
-
-#if defined CHECK_CRC_ON_SOLID_SKIP
-		/* Debug case */
-		verify_crc = 1;
-#else
-		/* Normal case */
-		verify_crc = 0;
-#endif
-	}
-
-	if(verify_crc) {
-		/* During unpacking, on each unpacked block we're calling the
-		 * update_crc() function. Since we are here, the unpacking
-		 * process is already over and we can check if calculated
-		 * checksum (CRC32 or BLAKE2sp) is the same as what is stored
-		 * in the archive. */
-		if(rar->file.stored_crc32 > 0) {
-			/* Check CRC32 only when the file contains a CRC32
-			 * value for this file. */
-
-			if(rar->file.calculated_crc32 !=
-			    rar->file.stored_crc32) {
-				/* Checksums do not match; the unpacked file
-				 * is corrupted. */
-
-				DEBUG_CODE {
-					printf("Checksum error: CRC32 "
-					    "(was: %08" PRIx32 ", expected: %08" PRIx32 ")\n",
-					    rar->file.calculated_crc32,
-					    rar->file.stored_crc32);
-				}
-
-#ifndef DONT_FAIL_ON_CRC_ERROR
-				archive_set_error(&a->archive,
-				    ARCHIVE_ERRNO_FILE_FORMAT,
-				    "Checksum error: CRC32");
-				return ARCHIVE_FAILED;
-#endif
-			} else {
-				DEBUG_CODE {
-					printf("Checksum OK: CRC32 "
-					    "(%08" PRIx32 "/%08" PRIx32 ")\n",
-					    rar->file.stored_crc32,
-					    rar->file.calculated_crc32);
-				}
-			}
-		}
-
-		if(rar->file.has_blake2 > 0) {
-			/* BLAKE2sp is an optional checksum algorithm that is
-			 * added to RARv5 archives when using the `-htb` switch
-			 *  during creation of archive.
-			 *
-			 * We now finalize the hash calculation by calling the
-			 * `final` function. This will generate the final hash
-			 * value we can use to compare it with the BLAKE2sp
-			 * checksum that is stored in the archive.
-			 *
-			 * The return value of this `final` function is not
-			 * very helpful, as it guards only against improper use.
- 			 * This is why we're explicitly ignoring it. */
-
-			uint8_t b2_buf[32];
-			(void) blake2sp_final(&rar->file.b2state, b2_buf, 32);
-
-			if(memcmp(&rar->file.blake2sp, b2_buf, 32) != 0) {
-#ifndef DONT_FAIL_ON_CRC_ERROR
-				archive_set_error(&a->archive,
-				    ARCHIVE_ERRNO_FILE_FORMAT,
-				    "Checksum error: BLAKE2");
-
-				return ARCHIVE_FAILED;
-#endif
-			}
-		}
-	}
-
-	/* Finalization for this file has been successfully completed. */
-	return ARCHIVE_OK;
-}
-
-static int verify_global_checksums(struct archive_read* a) {
-	return verify_checksums(a);
-}
-
-/*
- * Decryption function for the magic signature pattern. Check the comment near
- * the `rar5_signature_xor` symbol to read the rationale behind this.
- */
-static void rar5_signature(char *buf) {
-		size_t i;
-
-		for(i = 0; i < sizeof(rar5_signature_xor); i++) {
-			buf[i] = rar5_signature_xor[i] ^ 0xA1;
-		}
-}
-
-static int rar5_read_data(struct archive_read *a, const void **buff,
-    size_t *size, int64_t *offset) {
-	int ret;
-	struct rar5* rar = get_context(a);
-
-	if (size)
-		*size = 0;
-
-	if (rar->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) {
-		rar->has_encrypted_entries = 0;
-	}
-
-	if (rar->headers_are_encrypted || rar->cstate.data_encrypted) {
-		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
-		    "Reading encrypted data is not currently supported");
-		return ARCHIVE_FAILED;
-	}
-
-	if(rar->file.dir > 0) {
-		/* Don't process any data if this file entry was declared
-		 * as a directory. This is needed, because entries marked as
-		 * directory doesn't have any dictionary buffer allocated, so
-		 * it's impossible to perform any decompression. */
-		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
-		    "Can't decompress an entry marked as a directory");
-		return ARCHIVE_FATAL;
-	}
-
-	if(!rar->skip_mode && (rar->cstate.last_write_ptr > rar->file.unpacked_size)) {
-		archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
-		    "Unpacker has written too many bytes");
-		return ARCHIVE_FATAL;
-	}
-
-	ret = use_data(rar, buff, size, offset);
-	if(ret == ARCHIVE_OK) {
-		return ret;
-	}
-
-	if(rar->file.eof == 1) {
-		return ARCHIVE_EOF;
-	}
-
-	ret = do_unpack(a, rar, buff, size, offset);
-	if(ret != ARCHIVE_OK) {
-		return ret;
-	}
-
-	if(rar->file.bytes_remaining == 0 &&
-			rar->cstate.last_write_ptr == rar->file.unpacked_size)
-	{
-		/* If all bytes of current file were processed, run
-		 * finalization.
-		 *
-		 * Finalization will check checksum against proper values. If
-		 * some of the checksums will not match, we'll return an error
-		 * value in the last `archive_read_data` call to signal an error
-		 * to the user. */
-
-		rar->file.eof = 1;
-		return verify_global_checksums(a);
-	}
-
-	return ARCHIVE_OK;
-}
-
-static int rar5_read_data_skip(struct archive_read *a) {
-	struct rar5* rar = get_context(a);
-
-	if(rar->main.solid && (rar->cstate.data_encrypted == 0)) {
-		/* In solid archives, instead of skipping the data, we need to
-		 * extract it, and dispose the result. The side effect of this
-		 * operation will be setting up the initial window buffer state
-		 * needed to be able to extract the selected file. Note that
-		 * this is only possible when data withing this solid block is
-		 * not encrypted, in which case we'll skip and fail if the user
-		 * tries to read data. */
-
-		int ret;
-
-		/* Make sure to process all blocks in the compressed stream. */
-		while(rar->file.bytes_remaining > 0) {
-			/* Setting the "skip mode" will allow us to skip
-			 * checksum checks during data skipping. Checking the
-			 * checksum of skipped data isn't really necessary and
-			 * it's only slowing things down.
-			 *
-			 * This is incremented instead of setting to 1 because
-			 * this data skipping function can be called
-			 * recursively. */
-			rar->skip_mode++;
-
-			/* We're disposing 1 block of data, so we use triple
-			 * NULLs in arguments. */
-			ret = rar5_read_data(a, NULL, NULL, NULL);
-
-			/* Turn off "skip mode". */
-			rar->skip_mode--;
-
-			if(ret < 0 || ret == ARCHIVE_EOF) {
-				/* Propagate any potential error conditions
-				 * to the caller. */
-				return ret;
-			}
-		}
-	} else {
-		/* In standard archives, we can just jump over the compressed
-		 * stream. Each file in non-solid archives starts from an empty
-		 * window buffer. */
-
-		if(ARCHIVE_OK != consume(a, rar->file.bytes_remaining)) {
-			return ARCHIVE_FATAL;
-		}
-
-		rar->file.bytes_remaining = 0;
-	}
-
-	return ARCHIVE_OK;
-}
-
-static int64_t rar5_seek_data(struct archive_read *a, int64_t offset,
-    int whence)
-{
-	(void) a;
-	(void) offset;
-	(void) whence;
-
-	/* We're a streaming unpacker, and we don't support seeking. */
-
-	return ARCHIVE_FATAL;
-}
-
-static int rar5_cleanup(struct archive_read *a) {
-	struct rar5* rar = get_context(a);
-
-	free(rar->cstate.window_buf);
-	free(rar->cstate.filtered_buf);
-	clear_data_ready_stack(rar);
-
-	free(rar->vol.push_buf);
-
-	free_filters(rar);
-	rar5_deinit(rar);
-
-	free(rar);
-	a->format->data = NULL;
-
-	return ARCHIVE_OK;
-}
-
-static int rar5_capabilities(struct archive_read * a) {
-	(void) a;
-	return (ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA
-			| ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA);
-}
-
-static int rar5_has_encrypted_entries(struct archive_read *_a) {
-	if (_a && _a->format) {
-		struct rar5 *rar = (struct rar5 *)_a->format->data;
-		if (rar) {
-			return rar->has_encrypted_entries;
-		}
-	}
-
-	return ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW;
-}
-
-/* Must match deallocations in rar5_deinit */
-static int rar5_init(struct rar5* rar) {
-	memset(rar, 0, sizeof(struct rar5));
-
-	if(CDE_OK != cdeque_init(&rar->cstate.filters, 8192))
-		return ARCHIVE_FATAL;
-
-	/*
-	 * Until enough data has been read, we cannot tell about
-	 * any encrypted entries yet.
-	 */
-	rar->has_encrypted_entries = ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW;
-
-	return ARCHIVE_OK;
-}
-
-/* Must match allocations in rar5_init */
-static void rar5_deinit(struct rar5* rar) {
-	cdeque_free(&rar->cstate.filters);
-}
-
-int archive_read_support_format_rar5(struct archive *_a) {
-	struct archive_read* ar;
-	int ret;
-	struct rar5* rar;
-
-	if(ARCHIVE_OK != (ret = get_archive_read(_a, &ar)))
-		return ret;
-
-	rar = malloc(sizeof(*rar));
-	if(rar == NULL) {
-		archive_set_error(&ar->archive, ENOMEM,
-		    "Can't allocate rar5 data");
-		return ARCHIVE_FATAL;
-	}
-
-	if(ARCHIVE_OK != rar5_init(rar)) {
-		archive_set_error(&ar->archive, ENOMEM,
-		    "Can't allocate rar5 filter buffer");
-		free(rar);
-		return ARCHIVE_FATAL;
-	}
-
-	ret = __archive_read_register_format(ar,
-	    rar,
-	    "rar5",
-	    rar5_bid,
-	    rar5_options,
-	    rar5_read_header,
-	    rar5_read_data,
-	    rar5_read_data_skip,
-	    rar5_seek_data,
-	    rar5_cleanup,
-	    rar5_capabilities,
-	    rar5_has_encrypted_entries);
-
-	if(ret != ARCHIVE_OK) {
-		rar5_deinit(rar);
-		free(rar);
+#include "archive_integer.h"
+#include "archive_ppmd7_private.h"
+#include "archive_entry_private.h"
+#include "archive_time_private.h"
+
+#ifdef HAVE_BLAKE2_H
+#include <blake2.h>
+#else
+#include "archive_blake2.h"
+#endif
+
+/*#define CHECK_CRC_ON_SOLID_SKIP*/
+/*#define DONT_FAIL_ON_CRC_ERROR*/
+/*#define DEBUG*/
+
+#define rar5_min(a, b) (((a) > (b)) ? (b) : (a))
+#define rar5_max(a, b) (((a) > (b)) ? (a) : (b))
+#define rar5_countof(X) ((const ssize_t) (sizeof(X) / sizeof(*X)))
+
+#if defined DEBUG
+#define DEBUG_CODE if(1)
+#define LOG(...) do { printf("rar5: " __VA_ARGS__); puts(""); } while(0)
+#else
+#define DEBUG_CODE if(0)
+#endif
+
+/* Real RAR5 magic number is:
+ *
+ * 0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x01, 0x00
+ * "Rar!→•☺·\x00"
+ *
+ * Retrieved with `rar5_signature()` by XOR'ing it with 0xA1, because I don't
+ * want to put this magic sequence in each binary that uses libarchive, so
+ * applications that scan through the file for this marker won't trigger on
+ * this "false" one.
+ *
+ * The array itself is decrypted in `rar5_init` function. */
+
+static const unsigned char rar5_signature_xor[] = {
+	243, 192, 211, 128, 187, 166, 160, 161
+};
+static const size_t g_unpack_window_size = 0x20000;
+
+/* These could have been static const's, but they aren't, because of
+ * Visual Studio. */
+#define MAX_NAME_IN_CHARS 2048
+#define MAX_NAME_IN_BYTES (4 * MAX_NAME_IN_CHARS)
+
+struct file_header {
+	ssize_t bytes_remaining;
+	ssize_t unpacked_size;
+	int64_t last_offset;         /* Used in sanity checks. */
+	int64_t last_size;           /* Used in sanity checks. */
+
+	uint8_t solid : 1;           /* Is this a solid stream? */
+	uint8_t service : 1;         /* Is this file a service data? */
+	uint8_t eof : 1;             /* Did we finish unpacking the file? */
+	uint8_t dir : 1;             /* Is this file entry a directory? */
+
+	/* Optional time fields. */
+	int64_t e_mtime;
+	int64_t e_ctime;
+	int64_t e_atime;
+	uint32_t e_mtime_ns;
+	uint32_t e_ctime_ns;
+	uint32_t e_atime_ns;
+
+	/* Optional hash fields. */
+	uint32_t stored_crc32;
+	uint32_t calculated_crc32;
+	uint8_t blake2sp[32];
+	blake2sp_state b2state;
+	char has_blake2;
+
+	/* Optional redir fields */
+	uint64_t redir_type;
+	uint64_t redir_flags;
+
+	ssize_t solid_window_size; /* Used in file format check. */
+};
+
+enum EXTRA {
+	EX_CRYPT = 0x01,
+	EX_HASH = 0x02,
+	EX_HTIME = 0x03,
+	EX_VERSION = 0x04,
+	EX_REDIR = 0x05,
+	EX_UOWNER = 0x06,
+	EX_SUBDATA = 0x07
+};
+
+#define REDIR_SYMLINK_IS_DIR	1
+
+enum REDIR_TYPE {
+	REDIR_TYPE_NONE = 0,
+	REDIR_TYPE_UNIXSYMLINK = 1,
+	REDIR_TYPE_WINSYMLINK = 2,
+	REDIR_TYPE_JUNCTION = 3,
+	REDIR_TYPE_HARDLINK = 4,
+	REDIR_TYPE_FILECOPY = 5,
+};
+
+#define	OWNER_USER_NAME		0x01
+#define	OWNER_GROUP_NAME	0x02
+#define	OWNER_USER_UID		0x04
+#define	OWNER_GROUP_GID		0x08
+#define	OWNER_MAXNAMELEN	256
+
+enum FILTER_TYPE {
+	FILTER_DELTA = 0,   /* Generic pattern. */
+	FILTER_E8    = 1,   /* Intel x86 code. */
+	FILTER_E8E9  = 2,   /* Intel x86 code. */
+	FILTER_ARM   = 3,   /* ARM code. */
+	FILTER_AUDIO = 4,   /* Audio filter, not used in RARv5. */
+	FILTER_RGB   = 5,   /* Color palette, not used in RARv5. */
+	FILTER_ITANIUM = 6, /* Intel's Itanium, not used in RARv5. */
+	FILTER_PPM   = 7,   /* Predictive pattern matching, not used in
+			       RARv5. */
+	FILTER_NONE  = 8,
+};
+
+struct filter_info {
+	int type;
+	int channels;
+
+	int64_t block_start;
+	ssize_t block_length;
+};
+
+struct data_ready {
+	char used;
+	const uint8_t* buf;
+	size_t size;
+	int64_t offset;
+};
+
+struct cdeque {
+	uint16_t beg_pos;
+	uint16_t end_pos;
+	uint16_t cap_mask;
+	uint16_t size;
+	size_t* arr;
+};
+
+struct decode_table {
+	uint32_t size;
+	int32_t decode_len[16];
+	uint32_t decode_pos[16];
+	uint32_t quick_bits;
+	uint8_t quick_len[1 << 10];
+	uint16_t quick_num[1 << 10];
+	uint16_t decode_num[306];
+};
+
+struct comp_state {
+	/* Flag used to specify if unpacker needs to reinitialize the
+	   uncompression context. */
+	uint8_t initialized : 1;
+
+	/* Flag used when applying filters. */
+	uint8_t all_filters_applied : 1;
+
+	/* Flag used to skip file context reinitialization, used when unpacker
+	   is skipping through different multivolume archives. */
+	uint8_t switch_multivolume : 1;
+
+	/* Flag used to specify if unpacker has processed the whole data block
+	   or just a part of it. */
+	uint8_t block_parsing_finished : 1;
+
+	/* Flag used to indicate that a previous file using this buffer was
+	   encrypted, meaning no data in the buffer can be trusted */
+	uint8_t data_encrypted : 1;
+
+	signed int notused : 3;
+
+	int flags;                   /* Uncompression flags. */
+	int method;                  /* Uncompression algorithm method. */
+	int version;                 /* Uncompression algorithm version. */
+	ssize_t window_size;         /* Size of window_buf. */
+	uint8_t* window_buf;         /* Circular buffer used during
+	                                decompression. */
+	uint8_t* filtered_buf;       /* Buffer used when applying filters. */
+	const uint8_t* block_buf;    /* Buffer used when merging blocks. */
+	ssize_t window_mask;         /* Convenience field; window_size - 1. */
+	int64_t write_ptr;           /* This amount of data has been unpacked
+					in the window buffer. */
+	int64_t last_write_ptr;      /* This amount of data has been stored in
+	                                the output file. */
+	int64_t last_unstore_ptr;    /* Counter of bytes extracted during
+	                                unstoring. This is separate from
+	                                last_write_ptr because of how SERVICE
+	                                base blocks are handled during skipping
+	                                in solid multiarchive archives. */
+	int64_t solid_offset;        /* Additional offset inside the window
+	                                buffer, used in unpacking solid
+	                                archives. */
+	ssize_t cur_block_size;      /* Size of current data block. */
+	int last_len;                /* Flag used in lzss decompression. */
+
+	/* Decode tables used during lzss uncompression. */
+
+#define HUFF_BC 20
+	struct decode_table bd;      /* huffman bit lengths */
+#define HUFF_NC 306
+	struct decode_table ld;      /* literals */
+#define HUFF_DC 64
+	struct decode_table dd;      /* distances */
+#define HUFF_LDC 16
+	struct decode_table ldd;     /* lower bits of distances */
+#define HUFF_RC 44
+	struct decode_table rd;      /* repeating distances */
+#define HUFF_TABLE_SIZE (HUFF_NC + HUFF_DC + HUFF_RC + HUFF_LDC)
+
+	/* Circular deque for storing filters. */
+	struct cdeque filters;
+	int64_t last_block_start;    /* Used for sanity checking. */
+	ssize_t last_block_length;   /* Used for sanity checking. */
+
+	/* Distance cache used during lzss uncompression. */
+	int dist_cache[4];
+
+	/* Data buffer stack. */
+	struct data_ready dready[2];
+};
+
+/* Bit reader state. */
+struct bit_reader {
+	int8_t bit_addr;    /* Current bit pointer inside current byte. */
+	int in_addr;        /* Current byte pointer. */
+};
+
+/* RARv5 block header structure. Use bf_* functions to get values from
+ * block_flags_u8 field. I.e. bf_byte_count, etc. */
+struct compressed_block_header {
+	/* block_flags_u8 contain fields encoded in little-endian bitfield:
+	 *
+	 * - table present flag (shr 7, and 1),
+	 * - last block flag    (shr 6, and 1),
+	 * - byte_count         (shr 3, and 7),
+	 * - bit_size           (shr 0, and 7).
+	 */
+	uint8_t block_flags_u8;
+	uint8_t block_cksum;
+};
+
+/* RARv5 main header structure. */
+struct main_header {
+	/* Does the archive contain solid streams? */
+	uint8_t solid : 1;
+
+	/* If this a multi-file archive? */
+	uint8_t volume : 1;
+	uint8_t endarc : 1;
+	uint8_t notused : 5;
+
+	unsigned int vol_no;
+};
+
+struct generic_header {
+	uint8_t split_after : 1;
+	uint8_t split_before : 1;
+	uint8_t padding : 6;
+	int size;
+	int last_header_id;
+};
+
+struct multivolume {
+	unsigned int expected_vol_no;
+	uint8_t* push_buf;
+};
+
+/* Main context structure. */
+struct rar5 {
+	int header_initialized;
+
+	/* Set to 1 if current file is positioned AFTER the magic value
+	 * of the archive file. This is used in header reading functions. */
+	int skipped_magic;
+
+	/* Set to not zero if we're in skip mode (either by calling
+	 * rar5_data_skip function or when skipping over solid streams).
+	 * Set to 0 when in * extraction mode. This is used during checksum
+	 * calculation functions. */
+	int skip_mode;
+
+	/* Set to not zero if we're in block merging mode (i.e. when switching
+	 * to another file in multivolume archive, last block from 1st archive
+	 * needs to be merged with 1st block from 2nd archive). This flag
+	 * guards against recursive use of the merging function, which doesn't
+	 * support recursive calls. */
+	int merge_mode;
+
+	/* An offset to QuickOpen list. This is not supported by this unpacker,
+	 * because we're focusing on streaming interface. QuickOpen is designed
+	 * to make things quicker for non-stream interfaces, so it's not our
+	 * use case. */
+	uint64_t qlist_offset;
+
+	/* An offset to additional Recovery data. This is not supported by this
+	 * unpacker. Recovery data are additional Reed-Solomon codes that could
+	 * be used to calculate bytes that are missing in archive or are
+	 * corrupted. */
+	uint64_t rr_offset;
+
+	/* Various context variables grouped to different structures. */
+	struct generic_header generic;
+	struct main_header main;
+	struct comp_state cstate;
+	struct file_header file;
+	struct bit_reader bits;
+	struct multivolume vol;
+
+	/* The header of currently processed RARv5 block. Used in main
+	 * decompression logic loop. */
+	struct compressed_block_header last_block_hdr;
+
+	/*
+	 * Custom field to denote that this archive contains encrypted entries
+	 */
+	int has_encrypted_entries;
+	int headers_are_encrypted;
+};
+
+/* Forward function declarations. */
+
+static void rar5_signature(char *buf);
+static int verify_global_checksums(struct archive_read* a);
+static int rar5_read_data_skip(struct archive_read *a);
+static int push_data_ready(struct archive_read* a, struct rar5 *rar5,
+	const uint8_t* buf, size_t size, int64_t offset);
+static void clear_data_ready_stack(struct rar5 *rar5);
+static void rar5_deinit(struct rar5 *rar5);
+
+/* CDE_xxx = Circular Double Ended (Queue) return values. */
+enum CDE_RETURN_VALUES {
+	CDE_OK, CDE_ALLOC, CDE_PARAM, CDE_OUT_OF_BOUNDS,
+};
+
+/* Clears the contents of this circular deque. */
+static void cdeque_clear(struct cdeque* d) {
+	d->size = 0;
+	d->beg_pos = 0;
+	d->end_pos = 0;
+}
+
+/* Creates a new circular deque object. Capacity must be power of 2: 8, 16, 32,
+ * 64, 256, etc. When the user will add another item above current capacity,
+ * the circular deque will overwrite the oldest entry. */
+static int cdeque_init(struct cdeque* d, int max_capacity_power_of_2) {
+	if(d == NULL || max_capacity_power_of_2 == 0)
+		return CDE_PARAM;
+
+	d->cap_mask = max_capacity_power_of_2 - 1;
+	d->arr = NULL;
+
+	if((max_capacity_power_of_2 & d->cap_mask) != 0)
+		return CDE_PARAM;
+
+	cdeque_clear(d);
+	d->arr = malloc(sizeof(void*) * max_capacity_power_of_2);
+
+	return d->arr ? CDE_OK : CDE_ALLOC;
+}
+
+/* Return the current size (not capacity) of circular deque `d`. */
+static size_t cdeque_size(struct cdeque* d) {
+	return d->size;
+}
+
+/* Returns the first element of current circular deque. Note that this function
+ * doesn't perform any bounds checking. If you need bounds checking, use
+ * `cdeque_front()` function instead. */
+static void cdeque_front_fast(struct cdeque* d, void** value) {
+	*value = (void*) d->arr[d->beg_pos];
+}
+
+/* Returns the first element of current circular deque. This function
+ * performs bounds checking. */
+static int cdeque_front(struct cdeque* d, void** value) {
+	if(d->size > 0) {
+		cdeque_front_fast(d, value);
+		return CDE_OK;
+	} else
+		return CDE_OUT_OF_BOUNDS;
+}
+
+/* Pushes a new element into the end of this circular deque object. */
+static int cdeque_push_back(struct cdeque* d, void* item) {
+	if(d == NULL)
+		return CDE_PARAM;
+
+	if(d->size == d->cap_mask + 1)
+		return CDE_OUT_OF_BOUNDS;
+
+	d->arr[d->end_pos] = (size_t) item;
+	d->end_pos = (d->end_pos + 1) & d->cap_mask;
+	d->size++;
+
+	return CDE_OK;
+}
+
+/* Pops a front element of this circular deque object and returns its value.
+ * This function doesn't perform any bounds checking. */
+static void cdeque_pop_front_fast(struct cdeque* d, void** value) {
+	*value = (void*) d->arr[d->beg_pos];
+	d->beg_pos = (d->beg_pos + 1) & d->cap_mask;
+	d->size--;
+}
+
+/* Pops a front element of this circular deque object and returns its value.
+ * This function performs bounds checking. */
+static int cdeque_pop_front(struct cdeque* d, void** value) {
+	if(!d || !value)
+		return CDE_PARAM;
+
+	if(d->size == 0)
+		return CDE_OUT_OF_BOUNDS;
+
+	cdeque_pop_front_fast(d, value);
+	return CDE_OK;
+}
+
+/* Convenience function to cast filter_info** to void **. */
+static void** cdeque_filter_p(struct filter_info** f) {
+	return (void**) (size_t) f;
+}
+
+/* Convenience function to cast filter_info* to void *. */
+static void* cdeque_filter(struct filter_info* f) {
+	return (void**) (size_t) f;
+}
+
+/* Destroys this circular deque object. Deallocates the memory of the
+ * collection buffer, but doesn't deallocate the memory of any pointer passed
+ * to this deque as a value. */
+static void cdeque_free(struct cdeque* d) {
+	if(!d)
+		return;
+
+	if(!d->arr)
+		return;
+
+	free(d->arr);
+
+	d->arr = NULL;
+	d->beg_pos = -1;
+	d->end_pos = -1;
+	d->cap_mask = 0;
+}
+
+static inline
+uint8_t bf_bit_size(const struct compressed_block_header* hdr) {
+	return hdr->block_flags_u8 & 7;
+}
+
+static inline
+uint8_t bf_byte_count(const struct compressed_block_header* hdr) {
+	return (hdr->block_flags_u8 >> 3) & 7;
+}
+
+static inline
+uint8_t bf_is_table_present(const struct compressed_block_header* hdr) {
+	return (hdr->block_flags_u8 >> 7) & 1;
+}
+
+static inline
+uint8_t bf_is_last_block(const struct compressed_block_header* hdr) {
+	return (hdr->block_flags_u8 >> 6) & 1;
+}
+
+/* Convenience functions used by filter implementations. */
+static void circular_memcpy(uint8_t* dst, uint8_t* window, const ssize_t mask,
+    int64_t start, int64_t end)
+{
+	if((start & mask) > (end & mask)) {
+		ssize_t len1 = mask + 1 - (start & mask);
+		ssize_t len2 = end & mask;
+
+		memcpy(dst, &window[start & mask], len1);
+		memcpy(dst + len1, window, len2);
+	} else {
+		memcpy(dst, &window[start & mask], (size_t) (end - start));
+	}
+}
+
+static uint32_t read_filter_data(struct rar5 *rar5, uint32_t offset) {
+	uint8_t linear_buf[4];
+	circular_memcpy(linear_buf, rar5->cstate.window_buf,
+	    rar5->cstate.window_mask, offset, offset + 4);
+	return archive_le32dec(linear_buf);
+}
+
+static void write_filter_data(struct rar5 *rar5, uint32_t offset,
+    uint32_t value)
+{
+	archive_le32enc(&rar5->cstate.filtered_buf[offset], value);
+}
+
+/* Allocates a new filter descriptor and adds it to the filter array. */
+static struct filter_info* add_new_filter(struct rar5 *rar5) {
+	struct filter_info* f = calloc(1, sizeof(*f));
+
+	if(!f) {
+		return NULL;
+	}
+
+	if (CDE_OK != cdeque_push_back(&rar5->cstate.filters, cdeque_filter(f))) {
+		free(f);
+		return NULL;
+	}
+
+	return f;
+}
+
+static int run_delta_filter(struct rar5 *rar5, struct filter_info* flt) {
+	int i;
+	ssize_t dest_pos, src_pos = 0;
+
+	for(i = 0; i < flt->channels; i++) {
+		uint8_t prev_byte = 0;
+		for(dest_pos = i;
+				dest_pos < flt->block_length;
+				dest_pos += flt->channels)
+		{
+			uint8_t byte;
+
+			byte = rar5->cstate.window_buf[
+			    (rar5->cstate.solid_offset + flt->block_start +
+			    src_pos) & rar5->cstate.window_mask];
+
+			prev_byte -= byte;
+			rar5->cstate.filtered_buf[dest_pos] = prev_byte;
+			src_pos++;
+		}
+	}
+
+	return ARCHIVE_OK;
+}
+
+static int run_e8e9_filter(struct rar5 *rar5, struct filter_info* flt,
+		int extended)
+{
+	const uint32_t file_size = 0x1000000;
+	ssize_t i;
+
+	circular_memcpy(rar5->cstate.filtered_buf,
+	    rar5->cstate.window_buf, rar5->cstate.window_mask,
+	    rar5->cstate.solid_offset + flt->block_start,
+	    rar5->cstate.solid_offset + flt->block_start + flt->block_length);
+
+	for(i = 0; i < flt->block_length - 4;) {
+		uint8_t b = rar5->cstate.window_buf[
+		    (rar5->cstate.solid_offset + flt->block_start +
+		    i++) & rar5->cstate.window_mask];
+
+		/*
+		 * 0xE8 = x86's call <relative_addr_uint32> (function call)
+		 * 0xE9 = x86's jmp <relative_addr_uint32> (unconditional jump)
+		 */
+		if(b == 0xE8 || (extended && b == 0xE9)) {
+
+			uint32_t addr;
+			uint32_t offset = (i + flt->block_start) % file_size;
+
+			addr = read_filter_data(rar5,
+			    (uint32_t)(rar5->cstate.solid_offset +
+			    flt->block_start + i) & rar5->cstate.window_mask);
+
+			if(addr & 0x80000000) {
+				if(((addr + offset) & 0x80000000) == 0) {
+					write_filter_data(rar5, (uint32_t)i,
+					    addr + file_size);
+				}
+			} else {
+				if((addr - file_size) & 0x80000000) {
+					uint32_t naddr = addr - offset;
+					write_filter_data(rar5, (uint32_t)i,
+					    naddr);
+				}
+			}
+
+			i += 4;
+		}
+	}
+
+	return ARCHIVE_OK;
+}
+
+static int run_arm_filter(struct rar5 *rar5, struct filter_info* flt) {
+	ssize_t i = 0;
+	uint32_t offset;
+
+	circular_memcpy(rar5->cstate.filtered_buf,
+	    rar5->cstate.window_buf, rar5->cstate.window_mask,
+	    rar5->cstate.solid_offset + flt->block_start,
+	    rar5->cstate.solid_offset + flt->block_start + flt->block_length);
+
+	for(i = 0; i < flt->block_length - 3; i += 4) {
+		uint8_t* b = &rar5->cstate.window_buf[
+		    (rar5->cstate.solid_offset +
+		    flt->block_start + i + 3) & rar5->cstate.window_mask];
+
+		if(*b == 0xEB) {
+			/* 0xEB = ARM's BL (branch + link) instruction. */
+			offset = read_filter_data(rar5,
+			    (rar5->cstate.solid_offset + flt->block_start + i) &
+			     (uint32_t)rar5->cstate.window_mask) & 0x00ffffff;
+
+			offset -= (uint32_t) ((i + flt->block_start) / 4);
+			offset = (offset & 0x00ffffff) | 0xeb000000;
+			write_filter_data(rar5, (uint32_t)i, offset);
+		}
+	}
+
+	return ARCHIVE_OK;
+}
+
+static int run_filter(struct archive_read* a, struct filter_info* flt) {
+	struct rar5 *rar5 = a->format->data;
+	int ret;
+
+	clear_data_ready_stack(rar5);
+	free(rar5->cstate.filtered_buf);
+
+	rar5->cstate.filtered_buf = malloc(flt->block_length);
+	if(!rar5->cstate.filtered_buf) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate memory for filter data");
+		return ARCHIVE_FATAL;
+	}
+
+	switch(flt->type) {
+		case FILTER_DELTA:
+			ret = run_delta_filter(rar5, flt);
+			break;
+
+		case FILTER_E8:
+			/* fallthrough */
+		case FILTER_E8E9:
+			ret = run_e8e9_filter(rar5, flt,
+			    flt->type == FILTER_E8E9);
+			break;
+
+		case FILTER_ARM:
+			ret = run_arm_filter(rar5, flt);
+			break;
+
+		default:
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Unsupported filter type: 0x%x",
+			    (unsigned int)flt->type);
+			return ARCHIVE_FAILED;
+	}
+
+	if(ret != ARCHIVE_OK) {
+		/* Filter has failed. */
+		return ret;
+	}
+
+	if(ARCHIVE_OK != push_data_ready(a, rar5, rar5->cstate.filtered_buf,
+	    flt->block_length, rar5->cstate.last_write_ptr))
+	{
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+		    "Stack overflow when submitting unpacked data");
+
+		return ARCHIVE_FATAL;
+	}
+
+	rar5->cstate.last_write_ptr += flt->block_length;
+	return ARCHIVE_OK;
+}
+
+/* The `push_data` function submits the selected data range to the user.
+ * Next call of `use_data` will use the pointer, size and offset arguments
+ * that are specified here. These arguments are pushed to the FIFO stack here,
+ * and popped from the stack by the `use_data` function. */
+static void push_data(struct archive_read* a, struct rar5 *rar5,
+    const uint8_t* buf, int64_t idx_begin, int64_t idx_end)
+{
+	const ssize_t wmask = rar5->cstate.window_mask;
+	const ssize_t solid_write_ptr = (rar5->cstate.solid_offset +
+	    rar5->cstate.last_write_ptr) & wmask;
+
+	idx_begin += rar5->cstate.solid_offset;
+	idx_end += rar5->cstate.solid_offset;
+
+	/* Check if our unpacked data is wrapped inside the window circular
+	 * buffer.  If it's not wrapped, it can be copied out by using
+	 * a single memcpy, but when it's wrapped, we need to copy the first
+	 * part with one memcpy, and the second part with another memcpy. */
+
+	if((idx_begin & wmask) > (idx_end & wmask)) {
+		/* The data is wrapped (begin offset sis bigger than end
+		 * offset). */
+		const ssize_t frag1_size = rar5->cstate.window_size -
+		    (idx_begin & wmask);
+		const ssize_t frag2_size = idx_end & wmask;
+
+		/* Copy the first part of the buffer first. */
+		push_data_ready(a, rar5, buf + solid_write_ptr, frag1_size,
+		    rar5->cstate.last_write_ptr);
+
+		/* Copy the second part of the buffer. */
+		push_data_ready(a, rar5, buf, frag2_size,
+		    rar5->cstate.last_write_ptr + frag1_size);
+
+		rar5->cstate.last_write_ptr += frag1_size + frag2_size;
+	} else {
+		/* Data is not wrapped, so we can just use one call to copy the
+		 * data. */
+		push_data_ready(a, rar5,
+		    buf + solid_write_ptr, (idx_end - idx_begin) & wmask,
+		    rar5->cstate.last_write_ptr);
+
+		rar5->cstate.last_write_ptr += idx_end - idx_begin;
+	}
+}
+
+/* Convenience function that submits the data to the user. It uses the
+ * unpack window buffer as a source location. */
+static void push_window_data(struct archive_read* a, struct rar5 *rar5,
+    int64_t idx_begin, int64_t idx_end)
+{
+	push_data(a, rar5, rar5->cstate.window_buf, idx_begin, idx_end);
+}
+
+static int apply_filters(struct archive_read* a) {
+	struct rar5 *rar5 = a->format->data;
+	struct filter_info* flt;
+	int ret;
+
+	rar5->cstate.all_filters_applied = 0;
+
+	/* Get the first filter that can be applied to our data. The data
+	 * needs to be fully unpacked before the filter can be run. */
+	if(CDE_OK == cdeque_front(&rar5->cstate.filters,
+	    cdeque_filter_p(&flt))) {
+		/* Check if our unpacked data fully covers this filter's
+		 * range. */
+		if(rar5->cstate.write_ptr > flt->block_start &&
+		    rar5->cstate.write_ptr >= flt->block_start +
+		    flt->block_length) {
+			/* Check if we have some data pending to be written
+			 * right before the filter's start offset. */
+			if(rar5->cstate.last_write_ptr == flt->block_start) {
+				/* Run the filter specified by descriptor
+				 * `flt`. */
+				ret = run_filter(a, flt);
+				if(ret != ARCHIVE_OK) {
+					/* Filter failure, return error. */
+					return ret;
+				}
+
+				/* Filter descriptor won't be needed anymore
+				 * after it's used, * so remove it from the
+				 * filter list and free its memory. */
+				(void) cdeque_pop_front(&rar5->cstate.filters,
+				    cdeque_filter_p(&flt));
+
+				free(flt);
+			} else {
+				/* We can't run filters yet, dump the memory
+				 * right before the filter. */
+				push_window_data(a, rar5,
+				    rar5->cstate.last_write_ptr,
+				    flt->block_start);
+			}
+
+			/* Return 'filter applied or not needed' state to the
+			 * caller. */
+			return ARCHIVE_RETRY;
+		}
+	}
+
+	rar5->cstate.all_filters_applied = 1;
+	return ARCHIVE_OK;
+}
+
+static void dist_cache_push(struct rar5 *rar5, int value) {
+	int* q = rar5->cstate.dist_cache;
+
+	q[3] = q[2];
+	q[2] = q[1];
+	q[1] = q[0];
+	q[0] = value;
+}
+
+static int dist_cache_touch(struct rar5 *rar5, int idx) {
+	int* q = rar5->cstate.dist_cache;
+	int i, dist = q[idx];
+
+	for(i = idx; i > 0; i--)
+		q[i] = q[i - 1];
+
+	q[0] = dist;
+	return dist;
+}
+
+static void free_filters(struct rar5 *rar5) {
+	struct cdeque* d = &rar5->cstate.filters;
+
+	/* Free any remaining filters. All filters should be naturally
+	 * consumed by the unpacking function, so remaining filters after
+	 * unpacking normally mean that unpacking wasn't successful.
+	 * But still of course we shouldn't leak memory in such case. */
+
+	/* cdeque_size() is a fast operation, so we can use it as a loop
+	 * expression. */
+	while(cdeque_size(d) > 0) {
+		struct filter_info* f = NULL;
+
+		/* Pop_front will also decrease the collection's size. */
+		if (CDE_OK == cdeque_pop_front(d, cdeque_filter_p(&f)))
+			free(f);
+	}
+
+	cdeque_clear(d);
+
+	/* Also clear out the variables needed for sanity checking. */
+	rar5->cstate.last_block_start = 0;
+	rar5->cstate.last_block_length = 0;
+}
+
+static void reset_file_context(struct rar5 *rar5) {
+	memset(&rar5->file, 0, sizeof(rar5->file));
+	blake2sp_init(&rar5->file.b2state, 32);
+
+	if(rar5->main.solid) {
+		rar5->cstate.solid_offset += rar5->cstate.write_ptr;
+	} else {
+		rar5->cstate.solid_offset = 0;
+	}
+
+	rar5->cstate.write_ptr = 0;
+	rar5->cstate.last_write_ptr = 0;
+	rar5->cstate.last_unstore_ptr = 0;
+
+	rar5->file.redir_type = REDIR_TYPE_NONE;
+	rar5->file.redir_flags = 0;
+
+	free_filters(rar5);
+}
+
+static inline int get_archive_read(struct archive* a,
+    struct archive_read** ar)
+{
+	*ar = (struct archive_read*) a;
+	archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
+	    "archive_read_support_format_rar5");
+
+	return ARCHIVE_OK;
+}
+
+static int read_ahead(struct archive_read* a, size_t how_many,
+    const uint8_t** ptr)
+{
+	if(!ptr)
+		return 0;
+
+	*ptr = __archive_read_ahead(a, how_many, NULL);
+	if(*ptr == NULL) {
+		return 0;
+	}
+
+	return 1;
+}
+
+static int consume(struct archive_read* a, int64_t how_many) {
+	int ret;
+
+	ret = how_many == __archive_read_consume(a, how_many)
+		? ARCHIVE_OK
+		: ARCHIVE_FATAL;
+
+	return ret;
+}
+
+/**
+ * Read a RAR5 variable sized numeric value. This value will be stored in
+ * `pvalue`. The `pvalue_len` argument points to a variable that will receive
+ * the byte count that was consumed in order to decode the `pvalue` value, plus
+ * one.
+ *
+ * pvalue_len is optional and can be NULL.
+ *
+ * NOTE: if `pvalue_len` is NOT NULL, the caller needs to manually consume
+ * the number of bytes that `pvalue_len` value contains. If the `pvalue_len`
+ * is NULL, this consuming operation is done automatically.
+ *
+ * Returns 1 if *pvalue was successfully read.
+ * Returns 0 if there was an error. In this case, *pvalue contains an
+ *           invalid value.
+ */
+
+static int read_var(struct archive_read* a, uint64_t* pvalue,
+    uint64_t* pvalue_len)
+{
+	uint64_t multiplier;
+	uint64_t result = 0;
+	size_t i;
+	const uint8_t* p;
+
+	/* We will read maximum of 10 bytes. We don't have to handle the
+	 * situation to read the RAR5 variable-sized value stored at the end of
+	 * the file, because such situation will never happen. */
+	if(!read_ahead(a, 10, &p))
+		return 0;
+
+	for(multiplier = 1, i = 0; i < 10; i++, multiplier *= 128) {
+		uint64_t val;
+		uint8_t b;
+
+		b = p[i];
+
+		/* Strip the MSB from the input byte and add the resulting
+		 * number to the `result`. */
+		if(archive_ckd_mul_u64(&val, b & 0x7F, multiplier) ||
+		   archive_ckd_add_u64(&result, result, val)) {
+			/* Integer overflow occurred. */
+			return 0;
+		}
+
+		/* MSB set to 1 means we need to continue decoding process.
+		 * MSB set to 0 means we're done.
+		 *
+		 * This conditional checks for the second case. */
+		if((b & 0x80) == 0) {
+			if(pvalue) {
+				*pvalue = result;
+			}
+
+			/* If the caller has passed the `pvalue_len` pointer,
+			 * store the number of consumed bytes in it and do NOT
+			 * consume those bytes, since the caller has all the
+			 * information it needs to perform */
+			if(pvalue_len) {
+				*pvalue_len = 1 + i;
+			} else {
+				/* If the caller did not provide the
+				 * `pvalue_len` pointer, it will not have the
+				 * possibility to advance the file pointer,
+				 * because it will not know how many bytes it
+				 * needs to consume. This is why we handle
+				 * such situation here automatically. */
+				if(ARCHIVE_OK != consume(a, 1 + i)) {
+					return 0;
+				}
+			}
+
+			/* End of decoding process, return success. */
+			return 1;
+		}
+	}
+
+	/* All continuation bits were set. This is an error. */
+	return 0;
+}
+
+static int read_var_sized(struct archive_read* a, size_t* pvalue,
+    size_t* pvalue_len)
+{
+	uint64_t v;
+	uint64_t v_size = 0;
+
+	const int ret = pvalue_len ? read_var(a, &v, &v_size)
+				   : read_var(a, &v, NULL);
+
+	if(ret == 1 && pvalue) {
+		*pvalue = (size_t) v;
+	}
+
+	if(pvalue_len) {
+		/* Possible data truncation should be safe. */
+		*pvalue_len = (size_t) v_size;
+	}
+
+	return ret;
+}
+
+static int read_bits_32(struct archive_read* a, struct rar5 *rar5,
+	const uint8_t* p, uint32_t* value)
+{
+	if(rar5->bits.in_addr >= rar5->cstate.cur_block_size) {
+		archive_set_error(&a->archive,
+			ARCHIVE_ERRNO_PROGRAMMER,
+			"Premature end of stream during extraction of data (#1)");
+		return ARCHIVE_FATAL;
+	}
+
+	uint32_t bits = archive_be32dec(p + rar5->bits.in_addr);
+	bits <<= rar5->bits.bit_addr;
+	bits |= p[rar5->bits.in_addr + 4] >> (8 - rar5->bits.bit_addr);
+	*value = bits;
+	return ARCHIVE_OK;
+}
+
+static int read_bits_16(struct archive_read* a, struct rar5 *rar5,
+	const uint8_t* p, uint16_t* value)
+{
+	if(rar5->bits.in_addr >= rar5->cstate.cur_block_size) {
+		archive_set_error(&a->archive,
+			ARCHIVE_ERRNO_PROGRAMMER,
+			"Premature end of stream during extraction of data (#2)");
+		return ARCHIVE_FATAL;
+	}
+
+	uint32_t bits = archive_be24dec(p + (unsigned)rar5->bits.in_addr);
+	bits >>= (8 - rar5->bits.bit_addr);
+	*value = bits & 0xffff;
+	return ARCHIVE_OK;
+}
+
+static void skip_bits(struct rar5 *rar5, int bits) {
+	const int new_bits = rar5->bits.bit_addr + bits;
+	rar5->bits.in_addr += new_bits >> 3;
+	rar5->bits.bit_addr = new_bits & 7;
+}
+
+/* n = up to 16 */
+static int read_consume_bits(struct archive_read* a, struct rar5 *rar5,
+	const uint8_t* p, int n, int* value)
+{
+	uint16_t v;
+	int ret, num;
+
+	if(n == 0 || n > 16) {
+		/* This is a programmer error and should never happen
+		 * in runtime. */
+		return ARCHIVE_FATAL;
+	}
+
+	ret = read_bits_16(a, rar5, p, &v);
+	if(ret != ARCHIVE_OK)
+		return ret;
+
+	num = (int) v;
+	num >>= 16 - n;
+
+	skip_bits(rar5, n);
+
+	if(value)
+		*value = num;
+
+	return ARCHIVE_OK;
+}
+
+static char read_u32(struct archive_read* a, uint32_t* pvalue) {
+	const uint8_t* p;
+	if(!read_ahead(a, 4, &p))
+		return 0;
+
+	*pvalue = archive_le32dec(p);
+	return ARCHIVE_OK == consume(a, 4);
+}
+
+static char read_u64(struct archive_read* a, uint64_t* pvalue) {
+	const uint8_t* p;
+	if(!read_ahead(a, 8, &p))
+		return 0;
+
+	*pvalue = archive_le64dec(p);
+	return ARCHIVE_OK == consume(a, 8);
+}
+
+static int bid_standard(struct archive_read* a) {
+	const uint8_t* p;
+	char signature[sizeof(rar5_signature_xor)];
+
+	rar5_signature(signature);
+
+	if(!read_ahead(a, sizeof(rar5_signature_xor), &p))
+		return -1;
+
+	if(!memcmp(signature, p, sizeof(rar5_signature_xor)))
+		return 30;
+
+	return -1;
+}
+
+static int bid_sfx(struct archive_read *a)
+{
+	const char *p;
+
+	if ((p = __archive_read_ahead(a, 7, NULL)) == NULL)
+		return -1;
+
+	if ((p[0] == 'M' && p[1] == 'Z') || memcmp(p, "\x7F\x45LF", 4) == 0) {
+		/* This is a PE file */
+		char signature[sizeof(rar5_signature_xor)];
+		ssize_t offset = 0x10000;
+		ssize_t window = 4096;
+		ssize_t bytes_avail;
+
+		rar5_signature(signature);
+
+		while (offset + window <= (1024 * 512)) {
+			const char *buff = __archive_read_ahead(a, offset + window, &bytes_avail);
+			if (buff == NULL) {
+				/* Remaining bytes are less than window. */
+				window >>= 1;
+				if (window < 0x40)
+					return 0;
+				continue;
+			}
+			p = buff + offset;
+			while (p + 8 < buff + bytes_avail) {
+				if (memcmp(p, signature, sizeof(signature)) == 0)
+					return 30;
+				p += 0x10;
+			}
+			offset = p - buff;
+		}
+	}
+
+	return 0;
+}
+
+static int rar5_bid(struct archive_read* a, int best_bid) {
+	int my_bid;
+
+	if(best_bid > 30)
+		return -1;
+
+	my_bid = bid_standard(a);
+	if(my_bid > -1) {
+		return my_bid;
+	}
+	my_bid = bid_sfx(a);
+	if (my_bid > -1) {
+		return my_bid;
+	}
+
+	return -1;
+}
+
+static int rar5_options(struct archive_read *a, const char *key,
+    const char *val) {
+	(void) a;
+	(void) key;
+	(void) val;
+
+	/* No options supported in this version. Return the ARCHIVE_WARN code
+	 * to signal the options supervisor that the unpacker didn't handle
+	 * setting this option. */
+
+	return ARCHIVE_WARN;
+}
+
+static void init_header(struct archive_read* a) {
+	a->archive.archive_format = ARCHIVE_FORMAT_RAR_V5;
+	a->archive.archive_format_name = "RAR5";
+}
+
+static void init_window_mask(struct rar5 *rar5) {
+	if (rar5->cstate.window_size)
+		rar5->cstate.window_mask = rar5->cstate.window_size - 1;
+	else
+		rar5->cstate.window_mask = 0;
+}
+
+enum HEADER_FLAGS {
+	HFL_EXTRA_DATA = 0x0001,
+	HFL_DATA = 0x0002,
+	HFL_SKIP_IF_UNKNOWN = 0x0004,
+	HFL_SPLIT_BEFORE = 0x0008,
+	HFL_SPLIT_AFTER = 0x0010,
+	HFL_CHILD = 0x0020,
+	HFL_INHERITED = 0x0040
+};
+
+static int process_main_locator_extra_block(struct archive_read* a,
+    struct rar5 *rar5)
+{
+	uint64_t locator_flags;
+
+	enum LOCATOR_FLAGS {
+		QLIST = 0x01, RECOVERY = 0x02,
+	};
+
+	if(!read_var(a, &locator_flags, NULL)) {
+		return ARCHIVE_EOF;
+	}
+
+	if(locator_flags & QLIST) {
+		if(!read_var(a, &rar5->qlist_offset, NULL)) {
+			return ARCHIVE_EOF;
+		}
+
+		/* qlist is not used */
+	}
+
+	if(locator_flags & RECOVERY) {
+		if(!read_var(a, &rar5->rr_offset, NULL)) {
+			return ARCHIVE_EOF;
+		}
+
+		/* rr is not used */
+	}
+
+	return ARCHIVE_OK;
+}
+
+static int parse_file_extra_hash(struct archive_read* a, struct rar5 *rar5,
+    int64_t* extra_data_size)
+{
+	size_t hash_type = 0;
+	size_t value_len;
+
+	enum HASH_TYPE {
+		BLAKE2sp = 0x00
+	};
+
+	if(!read_var_sized(a, &hash_type, &value_len))
+		return ARCHIVE_EOF;
+
+	*extra_data_size -= value_len;
+	if(ARCHIVE_OK != consume(a, value_len)) {
+		return ARCHIVE_EOF;
+	}
+
+	/* The file uses BLAKE2sp checksum algorithm instead of plain old
+	 * CRC32. */
+	if(hash_type == BLAKE2sp) {
+		const uint8_t* p;
+		const int hash_size = sizeof(rar5->file.blake2sp);
+
+		if(!read_ahead(a, hash_size, &p))
+			return ARCHIVE_EOF;
+
+		rar5->file.has_blake2 = 1;
+		memcpy(&rar5->file.blake2sp, p, hash_size);
+
+		if(ARCHIVE_OK != consume(a, hash_size)) {
+			return ARCHIVE_EOF;
+		}
+
+		*extra_data_size -= hash_size;
+	} else {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Unsupported hash type (0x%jx)", (uintmax_t)hash_type);
+		return ARCHIVE_FATAL;
+	}
+
+	return ARCHIVE_OK;
+}
+
+static int parse_htime_item(struct archive_read* a, char unix_time,
+    int64_t* sec, uint32_t* nsec, int64_t* extra_data_size)
+{
+	if(unix_time) {
+		uint32_t time_val;
+		if(!read_u32(a, &time_val))
+			return ARCHIVE_EOF;
+
+		*extra_data_size -= 4;
+		*sec = (int64_t) time_val;
+	} else {
+		uint64_t windows_time;
+		if(!read_u64(a, &windows_time))
+			return ARCHIVE_EOF;
+
+		ntfs_to_unix(windows_time, sec, nsec);
+		*extra_data_size -= 8;
+	}
+
+	return ARCHIVE_OK;
+}
+
+static int parse_file_extra_version(struct archive_read* a,
+    struct archive_entry* e, int64_t* extra_data_size)
+{
+	size_t flags = 0;
+	size_t version = 0;
+	size_t value_len = 0;
+	struct archive_string version_string;
+	struct archive_string name_utf8_string;
+	const char* cur_filename;
+
+	/* Flags are ignored. */
+	if(!read_var_sized(a, &flags, &value_len))
+		return ARCHIVE_EOF;
+
+	*extra_data_size -= value_len;
+	if(ARCHIVE_OK != consume(a, value_len))
+		return ARCHIVE_EOF;
+
+	if(!read_var_sized(a, &version, &value_len))
+		return ARCHIVE_EOF;
+
+	*extra_data_size -= value_len;
+	if(ARCHIVE_OK != consume(a, value_len))
+		return ARCHIVE_EOF;
+
+	/* extra_data_size should be zero here. */
+
+	cur_filename = archive_entry_pathname_utf8(e);
+	if(cur_filename == NULL) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+		    "Version entry without file name");
+		return ARCHIVE_FATAL;
+	}
+
+	archive_string_init(&version_string);
+	archive_string_init(&name_utf8_string);
+
+	/* Prepare a ;123 suffix for the filename, where '123' is the version
+	 * value of this file. */
+	archive_string_sprintf(&version_string, ";%zu", version);
+
+	/* Build the new filename. */
+	archive_strcat(&name_utf8_string, cur_filename);
+	archive_strcat(&name_utf8_string, version_string.s);
+
+	/* Apply the new filename into this file's context. */
+	archive_entry_update_pathname_utf8(e, name_utf8_string.s);
+
+	/* Free buffers. */
+	archive_string_free(&version_string);
+	archive_string_free(&name_utf8_string);
+	return ARCHIVE_OK;
+}
+
+static int parse_file_extra_htime(struct archive_read* a,
+    struct archive_entry* e, struct rar5 *rar5, int64_t* extra_data_size)
+{
+	char unix_time, has_unix_ns, has_mtime, has_ctime, has_atime;
+	size_t flags = 0;
+	size_t value_len;
+
+	enum HTIME_FLAGS {
+		IS_UNIX       = 0x01,
+		HAS_MTIME     = 0x02,
+		HAS_CTIME     = 0x04,
+		HAS_ATIME     = 0x08,
+		HAS_UNIX_NS   = 0x10,
+	};
+
+	if(!read_var_sized(a, &flags, &value_len))
+		return ARCHIVE_EOF;
+
+	*extra_data_size -= value_len;
+	if(ARCHIVE_OK != consume(a, value_len)) {
+		return ARCHIVE_EOF;
+	}
+
+	unix_time = flags & IS_UNIX;
+	has_unix_ns = unix_time && (flags & HAS_UNIX_NS);
+	has_mtime = flags & HAS_MTIME;
+	has_atime = flags & HAS_ATIME;
+	has_ctime = flags & HAS_CTIME;
+	rar5->file.e_atime_ns = rar5->file.e_ctime_ns = rar5->file.e_mtime_ns = 0;
+
+	if(has_mtime) {
+		parse_htime_item(a, unix_time, &rar5->file.e_mtime,
+		    &rar5->file.e_mtime_ns, extra_data_size);
+	}
+
+	if(has_ctime) {
+		parse_htime_item(a, unix_time, &rar5->file.e_ctime,
+		    &rar5->file.e_ctime_ns, extra_data_size);
+	}
+
+	if(has_atime) {
+		parse_htime_item(a, unix_time, &rar5->file.e_atime,
+		    &rar5->file.e_atime_ns, extra_data_size);
+	}
+
+	if(has_mtime && has_unix_ns) {
+		if(!read_u32(a, &rar5->file.e_mtime_ns))
+			return ARCHIVE_EOF;
+
+		*extra_data_size -= 4;
+	}
+
+	if(has_ctime && has_unix_ns) {
+		if(!read_u32(a, &rar5->file.e_ctime_ns))
+			return ARCHIVE_EOF;
+
+		*extra_data_size -= 4;
+	}
+
+	if(has_atime && has_unix_ns) {
+		if(!read_u32(a, &rar5->file.e_atime_ns))
+			return ARCHIVE_EOF;
+
+		*extra_data_size -= 4;
+	}
+
+	/* The seconds and nanoseconds are either together, or separated in two
+	 * fields so we parse them, then set the archive_entry's times. */
+	if(has_mtime) {
+		archive_entry_set_mtime(e, rar5->file.e_mtime, rar5->file.e_mtime_ns);
+	}
+
+	if(has_ctime) {
+		archive_entry_set_ctime(e, rar5->file.e_ctime, rar5->file.e_ctime_ns);
+	}
+
+	if(has_atime) {
+		archive_entry_set_atime(e, rar5->file.e_atime, rar5->file.e_atime_ns);
+	}
+
+	return ARCHIVE_OK;
+}
+
+static int parse_file_extra_redir(struct archive_read* a,
+    struct archive_entry* e, struct rar5 *rar5, int64_t* extra_data_size)
+{
+	uint64_t value_size = 0;
+	size_t target_size = 0;
+	char target_utf8_buf[MAX_NAME_IN_BYTES];
+	const uint8_t* p;
+
+	if(!read_var(a, &rar5->file.redir_type, &value_size))
+		return ARCHIVE_EOF;
+	if(ARCHIVE_OK != consume(a, (int64_t)value_size))
+		return ARCHIVE_EOF;
+	*extra_data_size -= value_size;
+
+	if(!read_var(a, &rar5->file.redir_flags, &value_size))
+		return ARCHIVE_EOF;
+	if(ARCHIVE_OK != consume(a, (int64_t)value_size))
+		return ARCHIVE_EOF;
+	*extra_data_size -= value_size;
+
+	if(!read_var_sized(a, &target_size, NULL))
+		return ARCHIVE_EOF;
+	*extra_data_size -= target_size + 1;
+
+	if(target_size > (MAX_NAME_IN_CHARS - 1)) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Link target is too long");
+		return ARCHIVE_FATAL;
+	}
+
+	if(target_size == 0) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "No link target specified");
+		return ARCHIVE_FATAL;
+	}
+
+	if(!read_ahead(a, target_size, &p))
+		return ARCHIVE_EOF;
+
+	memcpy(target_utf8_buf, p, target_size);
+	target_utf8_buf[target_size] = 0;
+
+	if(ARCHIVE_OK != consume(a, (int64_t)target_size))
+		return ARCHIVE_EOF;
+
+	switch(rar5->file.redir_type) {
+		case REDIR_TYPE_UNIXSYMLINK:
+		case REDIR_TYPE_WINSYMLINK:
+			archive_entry_set_filetype(e, AE_IFLNK);
+			archive_entry_update_symlink_utf8(e, target_utf8_buf);
+			if (rar5->file.redir_flags & REDIR_SYMLINK_IS_DIR) {
+				archive_entry_set_symlink_type(e,
+					AE_SYMLINK_TYPE_DIRECTORY);
+			} else {
+				archive_entry_set_symlink_type(e,
+				AE_SYMLINK_TYPE_FILE);
+			}
+			break;
+
+		case REDIR_TYPE_HARDLINK:
+			archive_entry_set_filetype(e, AE_IFREG);
+			archive_entry_update_hardlink_utf8(e, target_utf8_buf);
+			break;
+
+		default:
+			/* Unknown redir type, skip it. */
+			break;
+	}
+	return ARCHIVE_OK;
+}
+
+static int parse_file_extra_owner(struct archive_read* a,
+    struct archive_entry* e, int64_t* extra_data_size)
+{
+	uint64_t flags = 0;
+	uint64_t value_size = 0;
+	uint64_t id = 0;
+	size_t name_len = 0;
+	size_t name_size = 0;
+	char namebuf[OWNER_MAXNAMELEN];
+	const uint8_t* p;
+
+	if(!read_var(a, &flags, &value_size))
+		return ARCHIVE_EOF;
+	if(ARCHIVE_OK != consume(a, (int64_t)value_size))
+		return ARCHIVE_EOF;
+	*extra_data_size -= value_size;
+
+	if ((flags & OWNER_USER_NAME) != 0) {
+		if(!read_var_sized(a, &name_size, NULL))
+			return ARCHIVE_EOF;
+		*extra_data_size -= name_size + 1;
+
+		if(!read_ahead(a, name_size, &p))
+			return ARCHIVE_EOF;
+
+		if (name_size >= OWNER_MAXNAMELEN) {
+			name_len = OWNER_MAXNAMELEN - 1;
+		} else {
+			name_len = name_size;
+		}
+
+		memcpy(namebuf, p, name_len);
+		namebuf[name_len] = 0;
+		if(ARCHIVE_OK != consume(a, (int64_t)name_size))
+			return ARCHIVE_EOF;
+
+		archive_entry_set_uname(e, namebuf);
+	}
+	if ((flags & OWNER_GROUP_NAME) != 0) {
+		if(!read_var_sized(a, &name_size, NULL))
+			return ARCHIVE_EOF;
+		*extra_data_size -= name_size + 1;
+
+		if(!read_ahead(a, name_size, &p))
+			return ARCHIVE_EOF;
+
+		if (name_size >= OWNER_MAXNAMELEN) {
+			name_len = OWNER_MAXNAMELEN - 1;
+		} else {
+			name_len = name_size;
+		}
+
+		memcpy(namebuf, p, name_len);
+		namebuf[name_len] = 0;
+		if(ARCHIVE_OK != consume(a, (int64_t)name_size))
+			return ARCHIVE_EOF;
+
+		archive_entry_set_gname(e, namebuf);
+	}
+	if ((flags & OWNER_USER_UID) != 0) {
+		if(!read_var(a, &id, &value_size))
+			return ARCHIVE_EOF;
+		if(ARCHIVE_OK != consume(a, (int64_t)value_size))
+			return ARCHIVE_EOF;
+		*extra_data_size -= value_size;
+
+		archive_entry_set_uid(e, (la_int64_t)id);
+	}
+	if ((flags & OWNER_GROUP_GID) != 0) {
+		if(!read_var(a, &id, &value_size))
+			return ARCHIVE_EOF;
+		if(ARCHIVE_OK != consume(a, (int64_t)value_size))
+			return ARCHIVE_EOF;
+		*extra_data_size -= value_size;
+
+		archive_entry_set_gid(e, (la_int64_t)id);
+	}
+	return ARCHIVE_OK;
+}
+
+static int process_head_file_extra(struct archive_read* a,
+    struct archive_entry* e, struct rar5 *rar5, int64_t extra_data_size)
+{
+	uint64_t extra_field_size;
+	uint64_t extra_field_id = 0;
+	uint64_t var_size;
+
+	while(extra_data_size > 0) {
+		/* Make sure we won't fail if the file declares only unsupported
+		attributes. */
+		int ret = ARCHIVE_OK;
+
+		if(!read_var(a, &extra_field_size, &var_size))
+			return ARCHIVE_EOF;
+
+		extra_data_size -= var_size;
+		if(ARCHIVE_OK != consume(a, var_size)) {
+			return ARCHIVE_EOF;
+		}
+
+		if(!read_var(a, &extra_field_id, &var_size))
+			return ARCHIVE_EOF;
+
+		extra_field_size -= var_size;
+		extra_data_size -= var_size;
+		if(ARCHIVE_OK != consume(a, var_size)) {
+			return ARCHIVE_EOF;
+		}
+
+		switch(extra_field_id) {
+			case EX_HASH:
+				ret = parse_file_extra_hash(a, rar5,
+				    &extra_data_size);
+				break;
+			case EX_HTIME:
+				ret = parse_file_extra_htime(a, e, rar5,
+				    &extra_data_size);
+				break;
+			case EX_REDIR:
+				ret = parse_file_extra_redir(a, e, rar5,
+				    &extra_data_size);
+				break;
+			case EX_UOWNER:
+				ret = parse_file_extra_owner(a, e,
+				    &extra_data_size);
+				break;
+			case EX_VERSION:
+				ret = parse_file_extra_version(a, e,
+				    &extra_data_size);
+				break;
+			case EX_CRYPT:
+				/* Mark the entry as encrypted */
+				archive_entry_set_is_data_encrypted(e, 1);
+				rar5->has_encrypted_entries = 1;
+				rar5->cstate.data_encrypted = 1;
+				/* fallthrough */
+			case EX_SUBDATA:
+				/* fallthrough */
+			default:
+				/* Skip unsupported entry. */
+				extra_data_size -= extra_field_size;
+				if (ARCHIVE_OK != consume(a, extra_field_size)) {
+					return ARCHIVE_EOF;
+				}
+
+				/* Don't fail on unsupported attribute -- we've handled it
+				   by skipping over it. */
+				ret = ARCHIVE_OK;
+		}
+
+		if (ret != ARCHIVE_OK) {
+			/* Forward any errors signalled by the attribute parsing
+			   functions. */
+			return ret;
+		}
+	}
+
+	if (extra_data_size != 0) {
+		/* We didn't skip everything, or we skipped too much; either way,
+		   there's an error in this parsing function. */
+
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+				"unsupported structure of file header extra data");
+		return ARCHIVE_FATAL;
+	}
+
+	return ARCHIVE_OK;
+}
+
+static int file_entry_sanity_checks(struct archive_read* a,
+	size_t block_flags, uint8_t is_dir, uint64_t unpacked_size,
+	size_t packed_size)
+{
+	if (is_dir) {
+		const int declares_data_size =
+			(int) (unpacked_size != 0 || packed_size != 0);
+
+		/* FILE entries for directories still declare HFL_DATA in block flags,
+		   even though attaching data to such blocks doesn't make much sense. */
+		if (declares_data_size) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+				"directory entries cannot have any data");
+			return ARCHIVE_FATAL;
+		}
+	} else {
+		const int declares_hfl_data = (int) ((block_flags & HFL_DATA) != 0);
+		if (!declares_hfl_data) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+					"no data found in file/service block");
+			return ARCHIVE_FATAL;
+		}
+	}
+
+	return ARCHIVE_OK;
+}
+
+static int process_head_file(struct archive_read* a, struct rar5 *rar5,
+    struct archive_entry* entry, size_t block_flags)
+{
+	int64_t extra_data_size = 0;
+	size_t data_size = 0;
+	size_t file_flags = 0;
+	size_t file_attr = 0;
+	size_t compression_info = 0;
+	size_t host_os = 0;
+	size_t name_size = 0;
+	uint64_t unpacked_size, window_size;
+	uint32_t mtime = 0, crc = 0;
+	int c_method = 0, c_version = 0;
+	char name_utf8_buf[MAX_NAME_IN_BYTES];
+	const uint8_t* p;
+	int sanity_ret;
+
+	enum FILE_FLAGS {
+		DIRECTORY = 0x0001, UTIME = 0x0002, CRC32 = 0x0004,
+		UNKNOWN_UNPACKED_SIZE = 0x0008,
+	};
+
+	enum FILE_ATTRS {
+		ATTR_READONLY = 0x1, ATTR_HIDDEN = 0x2, ATTR_SYSTEM = 0x4,
+		ATTR_DIRECTORY = 0x10,
+	};
+
+	enum COMP_INFO_FLAGS {
+		SOLID = 0x0040,
+	};
+
+	enum HOST_OS {
+		HOST_WINDOWS = 0,
+		HOST_UNIX = 1,
+	};
+
+	archive_entry_clear(entry);
+
+	/* Do not reset file context if we're switching archives. */
+	if(!rar5->cstate.switch_multivolume) {
+		reset_file_context(rar5);
+	}
+
+	if(block_flags & HFL_EXTRA_DATA) {
+		uint64_t edata_size = 0;
+		if(!read_var(a, &edata_size, NULL))
+			return ARCHIVE_EOF;
+
+		/* Intentional type cast from unsigned to signed. */
+		extra_data_size = (int64_t) edata_size;
+	}
+
+	if(block_flags & HFL_DATA) {
+		if(!read_var_sized(a, &data_size, NULL))
+			return ARCHIVE_EOF;
+
+		if(data_size > SSIZE_MAX) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "File data size is too large");
+			return ARCHIVE_FATAL;
+		}
+
+		rar5->file.bytes_remaining = data_size;
+	} else {
+		rar5->file.bytes_remaining = 0;
+	}
+
+	if(!read_var_sized(a, &file_flags, NULL))
+		return ARCHIVE_EOF;
+
+	if(!read_var(a, &unpacked_size, NULL))
+		return ARCHIVE_EOF;
+
+	if(file_flags & UNKNOWN_UNPACKED_SIZE) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+		    "Files with unknown unpacked size are not supported");
+		return ARCHIVE_FATAL;
+	}
+
+	rar5->file.dir = (uint8_t) ((file_flags & DIRECTORY) > 0);
+
+	sanity_ret = file_entry_sanity_checks(a, block_flags, rar5->file.dir,
+		unpacked_size, data_size);
+
+	if (sanity_ret != ARCHIVE_OK) {
+		return sanity_ret;
+	}
+
+	if(!read_var_sized(a, &file_attr, NULL))
+		return ARCHIVE_EOF;
+
+	if(file_flags & UTIME) {
+		if(!read_u32(a, &mtime))
+			return ARCHIVE_EOF;
+	}
+
+	if(file_flags & CRC32) {
+		if(!read_u32(a, &crc))
+			return ARCHIVE_EOF;
+	}
+
+	if(!read_var_sized(a, &compression_info, NULL))
+		return ARCHIVE_EOF;
+
+	c_method = (int) (compression_info >> 7) & 0x7;
+	c_version = (int) (compression_info & 0x3f);
+
+	/* RAR5 seems to limit the dictionary size to 64MB. */
+	window_size = (rar5->file.dir > 0) ?
+		0 :
+		g_unpack_window_size << ((compression_info >> 10) & 15);
+	rar5->cstate.method = c_method;
+	rar5->cstate.version = c_version + 50;
+	rar5->file.solid = (compression_info & SOLID) > 0;
+
+	/* Archives which declare solid files without initializing the window
+	 * buffer first are invalid, unless previous data was encrypted, in
+	 * which case we may never have had the chance */
+
+	if(rar5->file.solid > 0 && rar5->cstate.data_encrypted == 0 &&
+	    rar5->cstate.window_buf == NULL) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+				  "Declared solid file, but no window buffer "
+				  "initialized yet");
+		return ARCHIVE_FATAL;
+	}
+
+	/* Check if window_size is a sane value. Also, if the file is not
+	 * declared as a directory, disallow window_size == 0. */
+	if(window_size > (64 * 1024 * 1024) ||
+	    (rar5->file.dir == 0 && window_size == 0))
+	{
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Declared dictionary size is not supported");
+		return ARCHIVE_FATAL;
+	}
+
+	if(rar5->file.solid > 0) {
+		/* Re-check if current window size is the same as previous
+		 * window size (for solid files only). */
+		if(rar5->file.solid_window_size > 0 &&
+		    rar5->file.solid_window_size != (ssize_t) window_size)
+		{
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Window size for this solid file doesn't match "
+			    "the window size used in previous solid file");
+			return ARCHIVE_FATAL;
+		}
+	}
+	else
+		rar5->cstate.data_encrypted = 0; /* Reset for new buffer */
+
+	if(rar5->cstate.window_size < (ssize_t) window_size &&
+	    rar5->cstate.window_buf)
+	{
+		/* The `data_ready` stack contains pointers to the `window_buf` or
+		 * `filtered_buf` buffers.  Since we're about to reallocate the first
+		 * buffer, some of those pointers could become invalid. Therefore, we
+		 * need to dispose of all entries from the stack before attempting the
+		 * realloc. */
+		clear_data_ready_stack(rar5);
+
+		/* If window_buf has been allocated before, reallocate it, so
+		 * that its size will match new window_size. */
+
+		uint8_t* new_window_buf =
+			realloc(rar5->cstate.window_buf, (size_t) window_size);
+
+		if(!new_window_buf) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+				"Not enough memory when trying to realloc the window "
+				"buffer");
+			return ARCHIVE_FATAL;
+		}
+
+		rar5->cstate.window_buf = new_window_buf;
+	}
+
+	/* Values up to 64M should fit into ssize_t on every
+	 * architecture. */
+	rar5->cstate.window_size = (ssize_t) window_size;
+
+	if(rar5->file.solid > 0 && rar5->file.solid_window_size == 0) {
+		/* Solid files have to have the same window_size across
+		   whole archive. Remember the window_size parameter
+		   for first solid file found. */
+		rar5->file.solid_window_size = rar5->cstate.window_size;
+	}
+
+	init_window_mask(rar5);
+
+	rar5->file.service = 0;
+
+	if(!read_var_sized(a, &host_os, NULL))
+		return ARCHIVE_EOF;
+
+	if(host_os == HOST_WINDOWS) {
+		/* Host OS is Windows */
+
+		__LA_MODE_T mode;
+
+		if(file_attr & ATTR_DIRECTORY) {
+			if (file_attr & ATTR_READONLY) {
+				mode = 0555 | AE_IFDIR;
+			} else {
+				mode = 0755 | AE_IFDIR;
+			}
+		} else {
+			if (file_attr & ATTR_READONLY) {
+				mode = 0444 | AE_IFREG;
+			} else {
+				mode = 0644 | AE_IFREG;
+			}
+		}
+
+		archive_entry_set_mode(entry, mode);
+
+		if (file_attr & (ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM)) {
+			char buf[sizeof(",rdonly,hidden,system")];
+			const char *fflags[3] = { "", "", "" };
+			const char **flag = fflags;
+
+			if (file_attr & ATTR_READONLY)
+				*flag++ = ",rdonly";
+			if (file_attr & ATTR_HIDDEN)
+				*flag++ = ",hidden";
+			if (file_attr & ATTR_SYSTEM)
+				*flag++ = ",system";
+
+			snprintf(buf, sizeof(buf), "%s%s%s",
+			    fflags[0], fflags[1], fflags[2]);
+			archive_entry_copy_fflags_text(entry, buf + 1);
+		}
+	} else if(host_os == HOST_UNIX) {
+		/* Host OS is Unix */
+		archive_entry_set_mode(entry, (__LA_MODE_T) file_attr);
+	} else {
+		/* Unknown host OS */
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+				"Unsupported Host OS: 0x%jx",
+				(uintmax_t)host_os);
+
+		return ARCHIVE_FATAL;
+	}
+
+	if(!read_var_sized(a, &name_size, NULL))
+		return ARCHIVE_EOF;
+
+	if(name_size > (MAX_NAME_IN_CHARS - 1)) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+				"Filename is too long");
+
+		return ARCHIVE_FATAL;
+	}
+
+	if(name_size == 0) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+				"No filename specified");
+
+		return ARCHIVE_FATAL;
+	}
+
+	if(!read_ahead(a, name_size, &p))
+		return ARCHIVE_EOF;
+
+	memcpy(name_utf8_buf, p, name_size);
+	name_utf8_buf[name_size] = 0;
+	if(ARCHIVE_OK != consume(a, name_size)) {
+		return ARCHIVE_EOF;
+	}
+
+	archive_entry_update_pathname_utf8(entry, name_utf8_buf);
+
+	if(extra_data_size > 0) {
+		int ret = process_head_file_extra(a, entry, rar5,
+		    extra_data_size);
+
+		/*
+		 * TODO: rewrite or remove useless sanity check
+		 *       as extra_data_size is not passed as a pointer
+		 *
+		if(extra_data_size < 0) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+			    "File extra data size is not zero");
+			return ARCHIVE_FATAL;
+		}
+		 */
+
+		if(ret != ARCHIVE_OK)
+			return ret;
+	}
+
+	if((file_flags & UNKNOWN_UNPACKED_SIZE) == 0) {
+		rar5->file.unpacked_size = (ssize_t) unpacked_size;
+		if(rar5->file.redir_type == REDIR_TYPE_NONE)
+			archive_entry_set_size(entry, unpacked_size);
+	}
+
+	if(file_flags & UTIME) {
+		archive_entry_set_mtime(entry, (time_t) mtime, 0);
+	}
+
+	if(file_flags & CRC32) {
+		rar5->file.stored_crc32 = crc;
+	}
+
+	if(!rar5->cstate.switch_multivolume) {
+		/* Do not reinitialize unpacking state if we're switching
+		 * archives. */
+		rar5->cstate.block_parsing_finished = 1;
+		rar5->cstate.all_filters_applied = 1;
+		rar5->cstate.initialized = 0;
+	}
+
+	if(rar5->generic.split_before > 0) {
+		/* If now we're standing on a header that has a 'split before'
+		 * mark, it means we're standing on a 'continuation' file
+		 * header. Signal the caller that if it wants to move to
+		 * another file, it must call rar5_read_header() function
+		 * again. */
+
+		return ARCHIVE_RETRY;
+	} else {
+		return ARCHIVE_OK;
+	}
+}
+
+static int process_head_service(struct archive_read* a, struct rar5 *rar5,
+    struct archive_entry* entry, size_t block_flags)
+{
+	/* Process this SERVICE block the same way as FILE blocks. */
+	int ret = process_head_file(a, rar5, entry, block_flags);
+	if(ret != ARCHIVE_OK)
+		return ret;
+
+	rar5->file.service = 1;
+
+	/* But skip the data part automatically. It's no use for the user
+	 * anyway.  It contains only service data, not even needed to
+	 * properly unpack the file. */
+	ret = rar5_read_data_skip(a);
+	if(ret != ARCHIVE_OK)
+		return ret;
+
+	/* After skipping, try parsing another block automatically. */
+	return ARCHIVE_RETRY;
+}
+
+static int process_head_main(struct archive_read* a, struct rar5 *rar5,
+    struct archive_entry* entry, size_t block_flags)
+{
+	int ret;
+	uint64_t extra_data_size = 0;
+	size_t extra_field_size = 0;
+	size_t extra_field_id = 0;
+	size_t archive_flags = 0;
+
+	enum MAIN_FLAGS {
+		VOLUME = 0x0001,         /* multi-volume archive */
+		VOLUME_NUMBER = 0x0002,  /* volume number, first vol doesn't
+					  * have it */
+		SOLID = 0x0004,          /* solid archive */
+		PROTECT = 0x0008,        /* contains Recovery info */
+		LOCK = 0x0010,           /* readonly flag, not used */
+	};
+
+	enum MAIN_EXTRA {
+		// Just one attribute here.
+		LOCATOR = 0x01,
+	};
+
+	(void) entry;
+
+	if(block_flags & HFL_EXTRA_DATA) {
+		if(!read_var(a, &extra_data_size, NULL))
+			return ARCHIVE_EOF;
+	} else {
+		extra_data_size = 0;
+	}
+
+	if(!read_var_sized(a, &archive_flags, NULL)) {
+		return ARCHIVE_EOF;
+	}
+
+	rar5->main.volume = (archive_flags & VOLUME) > 0;
+	rar5->main.solid = (archive_flags & SOLID) > 0;
+
+	if(archive_flags & VOLUME_NUMBER) {
+		size_t v = 0;
+		if(!read_var_sized(a, &v, NULL)) {
+			return ARCHIVE_EOF;
+		}
+
+		if (v > UINT_MAX) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Invalid volume number");
+			return ARCHIVE_FATAL;
+		}
+
+		rar5->main.vol_no = (unsigned int) v;
+	} else {
+		rar5->main.vol_no = 0;
+	}
+
+	if(rar5->vol.expected_vol_no > 0 &&
+		rar5->main.vol_no != rar5->vol.expected_vol_no)
+	{
+		/* Returning EOF instead of FATAL because of strange
+		 * libarchive behavior. When opening multiple files via
+		 * archive_read_open_filenames(), after reading up the whole
+		 * last file, the __archive_read_ahead function wraps up to
+		 * the first archive instead of returning EOF. */
+		return ARCHIVE_EOF;
+	}
+
+	if(extra_data_size == 0) {
+		/* Early return. */
+		return ARCHIVE_OK;
+	}
+
+	if(!read_var_sized(a, &extra_field_size, NULL)) {
+		return ARCHIVE_EOF;
+	}
+
+	if(!read_var_sized(a, &extra_field_id, NULL)) {
+		return ARCHIVE_EOF;
+	}
+
+	if(extra_field_size == 0) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Invalid extra field size");
+		return ARCHIVE_FATAL;
+	}
+
+	switch(extra_field_id) {
+		case LOCATOR:
+			ret = process_main_locator_extra_block(a, rar5);
+			if(ret != ARCHIVE_OK) {
+				/* Error while parsing main locator extra
+				 * block. */
+				return ret;
+			}
+
+			break;
+		default:
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Unsupported extra type (0x%jx)",
+			    (uintmax_t)extra_field_id);
+			return ARCHIVE_FATAL;
+	}
+
+	return ARCHIVE_OK;
+}
+
+static int skip_unprocessed_bytes(struct archive_read* a) {
+	struct rar5 *rar5 = a->format->data;
+	int ret;
+
+	if(rar5->file.bytes_remaining) {
+		/* Use different skipping method in block merging mode than in
+		 * normal mode. If merge mode is active, rar5_read_data_skip
+		 * can't be used, because it could allow recursive use of
+		 * merge_block() * function, and this function doesn't support
+		 * recursive use. */
+		if(rar5->merge_mode) {
+			/* Discard whole merged block. This is valid in solid
+			 * mode as well, because the code will discard blocks
+			 * only if those blocks are safe to discard (i.e.
+			 * they're not FILE blocks).  */
+			ret = consume(a, rar5->file.bytes_remaining);
+			if(ret != ARCHIVE_OK) {
+				return ret;
+			}
+			rar5->file.bytes_remaining = 0;
+		} else {
+			/* If we're not in merge mode, use safe skipping code.
+			 * This will ensure we'll handle solid archives
+			 * properly. */
+			ret = rar5_read_data_skip(a);
+			if(ret != ARCHIVE_OK) {
+				return ret;
+			}
+		}
+	}
+
+	return ARCHIVE_OK;
+}
+
+static int scan_for_signature(struct archive_read* a);
+
+/* Base block processing function. A 'base block' is a RARv5 header block
+ * that tells the reader what kind of data is stored inside the block.
+ *
+ * From the birds-eye view a RAR file looks file this:
+ *
+ * <magic><base_block_1><base_block_2>...<base_block_n>
+ *
+ * There are a few types of base blocks. Those types are specified inside
+ * the 'switch' statement in this function. For example purposes, I'll write
+ * how a standard RARv5 file could look like here:
+ *
+ * <magic><MAIN><FILE><FILE><FILE><SERVICE><ENDARC>
+ *
+ * The structure above could describe an archive file with 3 files in it,
+ * one service "QuickOpen" block (that is ignored by this parser), and an
+ * end of file base block marker.
+ *
+ * If the file is stored in multiple archive files ("multiarchive"), it might
+ * look like this:
+ *
+ * .part01.rar: <magic><MAIN><FILE><ENDARC>
+ * .part02.rar: <magic><MAIN><FILE><ENDARC>
+ * .part03.rar: <magic><MAIN><FILE><ENDARC>
+ *
+ * This example could describe 3 RAR files that contain ONE archived file.
+ * Or it could describe 3 RAR files that contain 3 different files. Or 3
+ * RAR files than contain 2 files. It all depends what metadata is stored in
+ * the headers of <FILE> blocks.
+ *
+ * Each <FILE> block contains info about its size, the name of the file it's
+ * storing inside, and whether this FILE block is a continuation block of
+ * previous archive ('split before'), and is this FILE block should be
+ * continued in another archive ('split after'). By parsing the 'split before'
+ * and 'split after' flags, we're able to tell if multiple <FILE> base blocks
+ * are describing one file, or multiple files (with the same filename, for
+ * example).
+ *
+ * One thing to note is that if we're parsing the first <FILE> block, and
+ * we see 'split after' flag, then we need to jump over to another <FILE>
+ * block to be able to decompress rest of the data. To do this, we need
+ * to skip the <ENDARC> block, then switch to another file, then skip the
+ * <magic> block, <MAIN> block, and then we're standing on the proper
+ * <FILE> block.
+ */
+
+/*
+ * A header that carries no file data (HEAD_MAIN, or an unknown block
+ * flagged HFL_SKIP_IF_UNKNOWN) may leave bytes in its body that the
+ * sub-parser did not read. Skip them before returning ARCHIVE_RETRY,
+ * otherwise rar5_read_header() re-parses the same block region O(N)
+ * times instead of O(1), letting a crafted RAR5 file stall the reader
+ * (GHSA-9h2c-464f-j3hj).
+ *
+ * Safe because read_ahead(a, hdr_size, &p) pre-loaded the whole block
+ * into one contiguous buffer with no compaction until we return, so
+ * body_start stays valid and (cur - body_start) is the exact number of
+ * body bytes consumed so far.
+ */
+static void
+rar5_skip_remaining_block(struct archive_read* a,
+    const uint8_t* body_start, size_t raw_hdr_size)
+{
+	const uint8_t* cur;
+
+	if(read_ahead(a, 1, &cur)) {
+		size_t body_used = (size_t)(cur - body_start);
+
+		if(body_used < raw_hdr_size)
+			(void)consume(a, raw_hdr_size - body_used);
+	}
+}
+
+static int process_base_block(struct archive_read* a,
+    struct archive_entry* entry)
+{
+	const size_t SMALLEST_RAR5_BLOCK_SIZE = 3;
+
+	struct rar5 *rar5 = a->format->data;
+	uint32_t hdr_crc, computed_crc;
+	size_t raw_hdr_size = 0, hdr_size_len, hdr_size;
+	size_t header_id = 0;
+	size_t header_flags = 0;
+	const uint8_t* p;
+	const uint8_t* body_start;
+	int ret;
+
+	enum HEADER_TYPE {
+		HEAD_MARK    = 0x00, HEAD_MAIN  = 0x01, HEAD_FILE   = 0x02,
+		HEAD_SERVICE = 0x03, HEAD_CRYPT = 0x04, HEAD_ENDARC = 0x05,
+		HEAD_UNKNOWN = 0xff,
+	};
+
+	/* Skip any unprocessed data for this file. */
+	ret = skip_unprocessed_bytes(a);
+	if(ret != ARCHIVE_OK)
+		return ret;
+
+	/* Read the expected CRC32 checksum. */
+	if(!read_u32(a, &hdr_crc)) {
+		return ARCHIVE_EOF;
+	}
+
+	/* Read header size. */
+	if(!read_var_sized(a, &raw_hdr_size, &hdr_size_len)) {
+		return ARCHIVE_EOF;
+	}
+
+	hdr_size = raw_hdr_size + hdr_size_len;
+
+	/* Sanity check, maximum header size for RAR5 is 2MB. */
+	if(hdr_size > (2 * 1024 * 1024)) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Base block header is too large");
+
+		return ARCHIVE_FATAL;
+	}
+
+	/* Additional sanity checks to weed out invalid files. */
+	if(raw_hdr_size == 0 || hdr_size_len == 0 ||
+		hdr_size < SMALLEST_RAR5_BLOCK_SIZE)
+	{
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Too small block encountered (%zu bytes)",
+		    raw_hdr_size);
+
+		return ARCHIVE_FATAL;
+	}
+
+	/* Read the whole header data into memory, maximum memory use here is
+	 * 2MB. */
+	if(!read_ahead(a, hdr_size, &p)) {
+		return ARCHIVE_EOF;
+	}
+
+	/* Verify the CRC32 of the header data. */
+	computed_crc = (uint32_t) crc32(0, p, (int) hdr_size);
+	if(computed_crc != hdr_crc) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Header CRC error");
+
+		return ARCHIVE_FATAL;
+#endif
+	}
+
+	/* Remember the first byte of the block body so we can later skip
+	 * any bytes the sub-parser leaves unconsumed. */
+	body_start = p + hdr_size_len;
+
+	/* If the checksum is OK, we proceed with parsing. */
+	if(ARCHIVE_OK != consume(a, hdr_size_len)) {
+		return ARCHIVE_EOF;
+	}
+
+	if(!read_var_sized(a, &header_id, NULL))
+		return ARCHIVE_EOF;
+
+	if(!read_var_sized(a, &header_flags, NULL))
+		return ARCHIVE_EOF;
+
+	rar5->generic.split_after = (header_flags & HFL_SPLIT_AFTER) > 0;
+	rar5->generic.split_before = (header_flags & HFL_SPLIT_BEFORE) > 0;
+	rar5->generic.size = (int)hdr_size;
+	rar5->generic.last_header_id = (int)header_id;
+	rar5->main.endarc = 0;
+
+	/* Those are possible header ids in RARv5. */
+	switch(header_id) {
+		case HEAD_MAIN:
+			ret = process_head_main(a, rar5, entry, header_flags);
+
+			/* Main header doesn't have any files in it, so it's
+			 * pointless to return to the caller. Retry to next
+			 * header, which should be HEAD_FILE/HEAD_SERVICE. */
+			if(ret == ARCHIVE_OK) {
+				rar5_skip_remaining_block(a, body_start,
+				    raw_hdr_size);
+				return ARCHIVE_RETRY;
+			}
+
+			return ret;
+		case HEAD_SERVICE:
+			ret = process_head_service(a, rar5, entry, header_flags);
+			return ret;
+		case HEAD_FILE:
+			ret = process_head_file(a, rar5, entry, header_flags);
+			return ret;
+		case HEAD_CRYPT:
+			archive_entry_set_is_metadata_encrypted(entry, 1);
+			archive_entry_set_is_data_encrypted(entry, 1);
+			rar5->has_encrypted_entries = 1;
+			rar5->headers_are_encrypted = 1;
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Encryption is not supported");
+			return ARCHIVE_FATAL;
+		case HEAD_ENDARC:
+			rar5->main.endarc = 1;
+
+			/* After encountering an end of file marker, we need
+			 * to take into consideration if this archive is
+			 * continued in another file (i.e. is it part01.rar:
+			 * is there a part02.rar?) */
+			if(rar5->main.volume) {
+				/* In case there is part02.rar, position the
+				 * read pointer in a proper place, so we can
+				 * resume parsing. */
+				ret = scan_for_signature(a);
+				if(ret == ARCHIVE_FATAL) {
+					return ARCHIVE_EOF;
+				} else {
+					if(rar5->vol.expected_vol_no ==
+					    UINT_MAX) {
+						archive_set_error(&a->archive,
+						    ARCHIVE_ERRNO_FILE_FORMAT,
+						    "Header error");
+							return ARCHIVE_FATAL;
+					}
+
+					rar5->vol.expected_vol_no =
+					    rar5->main.vol_no + 1;
+					return ARCHIVE_OK;
+				}
+			} else {
+				return ARCHIVE_EOF;
+			}
+		case HEAD_MARK:
+			return ARCHIVE_EOF;
+		default:
+			if((header_flags & HFL_SKIP_IF_UNKNOWN) == 0) {
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_FILE_FORMAT,
+				    "Header type error");
+				return ARCHIVE_FATAL;
+			} else {
+				/* If the block is marked as 'skip if unknown',
+				 * do as the flag says: skip the block
+				 * instead on failing on it. */
+				rar5_skip_remaining_block(a, body_start,
+				    raw_hdr_size);
+				return ARCHIVE_RETRY;
+			}
+	}
+
+#if !defined WIN32
+	// Not reached.
+	archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+	    "Internal unpacker error");
+	return ARCHIVE_FATAL;
+#endif
+}
+
+static int skip_base_block(struct archive_read* a) {
+	struct rar5 *rar5 = a->format->data;
+	int ret;
+
+	/* Create a new local archive_entry structure that will be operated on
+	 * by header reader; operations on this archive_entry will be discarded.
+	 */
+	struct archive_entry* entry = archive_entry_new();
+	if (entry == NULL)
+		return ARCHIVE_FATAL;
+
+	ret = process_base_block(a, entry);
+
+	/* Discard operations on this archive_entry structure. */
+	archive_entry_free(entry);
+	if(ret == ARCHIVE_FATAL)
+		return ret;
+
+	if(rar5->generic.last_header_id == 2 && rar5->generic.split_before > 0)
+		return ARCHIVE_OK;
+
+	if(ret == ARCHIVE_OK)
+		return ARCHIVE_RETRY;
+	else
+		return ret;
+}
+
+static int try_skip_sfx(struct archive_read *a)
+{
+	const char *p;
+
+	if ((p = __archive_read_ahead(a, 7, NULL)) == NULL)
+		return ARCHIVE_EOF;
+
+	if ((p[0] == 'M' && p[1] == 'Z') || memcmp(p, "\x7F\x45LF", 4) == 0)
+	{
+		char signature[sizeof(rar5_signature_xor)];
+		const void *h;
+		const char *q;
+		size_t skip, total = 0;
+		ssize_t bytes, window = 4096;
+
+		rar5_signature(signature);
+
+		while (total + window <= (1024 * 512)) {
+			h = __archive_read_ahead(a, window, &bytes);
+			if (h == NULL) {
+				/* Remaining bytes are less than window. */
+				window >>= 1;
+				if (window < 0x40)
+					goto fatal;
+				continue;
+			}
+			if (bytes < 0x40)
+				goto fatal;
+			p = h;
+			q = p + bytes;
+
+			/*
+			 * Scan ahead until we find something that looks
+			 * like the RAR header.
+			 */
+			while (p + 8 < q) {
+				if (memcmp(p, signature, sizeof(signature)) == 0) {
+					skip = p - (const char *)h;
+					__archive_read_consume(a, skip);
+					return (ARCHIVE_OK);
+				}
+				p += 0x10;
+			}
+			skip = p - (const char *)h;
+			__archive_read_consume(a, skip);
+			total += skip;
+		}
+	}
+
+	return ARCHIVE_OK;
+fatal:
+	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+			"Couldn't find out RAR header");
+	return (ARCHIVE_FATAL);
+}
+
+static int rar5_read_header(struct archive_read *a,
+    struct archive_entry *entry)
+{
+	struct rar5 *rar5 = a->format->data;
+	int ret;
+
+	/*
+	 * It should be sufficient to call archive_read_next_header() for
+	 * a reader to determine if an entry is encrypted or not.
+	 */
+	if (rar5->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) {
+		rar5->has_encrypted_entries = 0;
+	}
+
+	if(rar5->header_initialized == 0) {
+		init_header(a);
+		if ((ret = try_skip_sfx(a)) < ARCHIVE_WARN)
+			return ret;
+		rar5->header_initialized = 1;
+	}
+
+	if(rar5->skipped_magic == 0) {
+		if(ARCHIVE_OK != consume(a, sizeof(rar5_signature_xor))) {
+			return ARCHIVE_EOF;
+		}
+
+		rar5->skipped_magic = 1;
+	}
+
+	do {
+		ret = process_base_block(a, entry);
+	} while(ret == ARCHIVE_RETRY ||
+			(rar5->main.endarc > 0 && ret == ARCHIVE_OK));
+
+	return ret;
+}
+
+static int init_unpack(struct rar5 *rar5) {
+	rar5->file.calculated_crc32 = 0;
+	init_window_mask(rar5);
+
+	free(rar5->cstate.window_buf);
+	free(rar5->cstate.filtered_buf);
+
+	rar5->cstate.window_buf = NULL;
+	rar5->cstate.filtered_buf = NULL;
+
+	if(rar5->cstate.window_size > 0) {
+		rar5->cstate.window_buf = calloc(1, rar5->cstate.window_size);
+		if(rar5->cstate.window_buf == NULL)
+			return ARCHIVE_FATAL;
+		rar5->cstate.filtered_buf = calloc(1,
+		    rar5->cstate.window_size);
+		if(rar5->cstate.filtered_buf == NULL)
+			return ARCHIVE_FATAL;
+	}
+
+	clear_data_ready_stack(rar5);
+
+	rar5->cstate.write_ptr = 0;
+	rar5->cstate.last_write_ptr = 0;
+
+	memset(&rar5->cstate.bd, 0, sizeof(rar5->cstate.bd));
+	memset(&rar5->cstate.ld, 0, sizeof(rar5->cstate.ld));
+	memset(&rar5->cstate.dd, 0, sizeof(rar5->cstate.dd));
+	memset(&rar5->cstate.ldd, 0, sizeof(rar5->cstate.ldd));
+	memset(&rar5->cstate.rd, 0, sizeof(rar5->cstate.rd));
+	return ARCHIVE_OK;
+}
+
+static void update_crc(struct rar5 *rar5, const uint8_t* p, size_t to_read) {
+    int verify_crc;
+
+	if(rar5->skip_mode) {
+#if defined CHECK_CRC_ON_SOLID_SKIP
+		verify_crc = 1;
+#else
+		verify_crc = 0;
+#endif
+	} else
+		verify_crc = 1;
+
+	if(verify_crc) {
+		/* Don't update CRC32 if the file doesn't have the
+		 * `stored_crc32` info filled in. */
+		if(rar5->file.stored_crc32 > 0) {
+			rar5->file.calculated_crc32 =
+				crc32(rar5->file.calculated_crc32, p,
+				    (unsigned int)to_read);
+		}
+
+		/* Check if the file uses an optional BLAKE2sp checksum
+		 * algorithm. */
+		if(rar5->file.has_blake2 > 0) {
+			/* Return value of the `update` function is always 0,
+			 * so we can explicitly ignore it here. */
+			(void) blake2sp_update(&rar5->file.b2state, p,
+			    to_read);
+		}
+	}
+}
+
+static int create_decode_tables(uint8_t* bit_length,
+    struct decode_table* table, int size)
+{
+	int code, upper_limit = 0, i, lc[16];
+	uint32_t decode_pos_clone[rar5_countof(table->decode_pos)];
+	ssize_t cur_len, quick_data_size;
+
+	memset(&lc, 0, sizeof(lc));
+	memset(table->decode_num, 0, sizeof(table->decode_num));
+	table->size = size;
+	table->quick_bits = size == HUFF_NC ? 10 : 7;
+
+	for(i = 0; i < size; i++) {
+		lc[bit_length[i] & 15]++;
+	}
+
+	lc[0] = 0;
+	table->decode_pos[0] = 0;
+	table->decode_len[0] = 0;
+
+	for(i = 1; i < 16; i++) {
+		upper_limit += lc[i];
+
+		table->decode_len[i] = upper_limit << (16 - i);
+		table->decode_pos[i] = table->decode_pos[i - 1] + lc[i - 1];
+
+		upper_limit <<= 1;
+	}
+
+	/* Verify the code-length distribution is not over-subscribed.
+	 * After the loop above, upper_limit == sum(lc[i] * 2^(16-i)).
+	 * For a valid prefix-free code this must be <= 2^16 = 65536.
+	 * An over-subscribed table (> 65536) cannot produce a valid
+	 * decode table and must be rejected. */
+	if(upper_limit > 65536) {
+		return ARCHIVE_FAILED;
+	}
+
+	memcpy(decode_pos_clone, table->decode_pos, sizeof(decode_pos_clone));
+
+	for(i = 0; i < size; i++) {
+		uint8_t clen = bit_length[i] & 15;
+		if(clen > 0) {
+			int last_pos = decode_pos_clone[clen];
+			table->decode_num[last_pos] = i;
+			decode_pos_clone[clen]++;
+		}
+	}
+
+	quick_data_size = (int64_t)1 << table->quick_bits;
+	cur_len = 1;
+	for(code = 0; code < quick_data_size; code++) {
+		int bit_field = code << (16 - table->quick_bits);
+		int dist, pos;
+
+		while(cur_len < rar5_countof(table->decode_len) &&
+				bit_field >= table->decode_len[cur_len]) {
+			cur_len++;
+		}
+
+		table->quick_len[code] = (uint8_t) cur_len;
+
+		dist = bit_field - table->decode_len[cur_len - 1];
+		dist >>= (16 - cur_len);
+
+		pos = table->decode_pos[cur_len & 15] + dist;
+		if(cur_len < rar5_countof(table->decode_pos) && pos < size) {
+			table->quick_num[code] = table->decode_num[pos];
+		} else {
+			table->quick_num[code] = 0;
+		}
+	}
+
+	return ARCHIVE_OK;
+}
+
+static int decode_number(struct archive_read* a, struct decode_table* table,
+    const uint8_t* p, uint16_t* num)
+{
+	struct rar5 *rar5 = a->format->data;
+	int i, bits, dist, ret;
+	uint16_t bitfield;
+	uint32_t pos;
+
+	if(ARCHIVE_OK != (ret = read_bits_16(a, rar5, p, &bitfield))) {
+		return ret;
+	}
+
+	bitfield &= 0xfffe;
+
+	if(bitfield < table->decode_len[table->quick_bits]) {
+		int code = bitfield >> (16 - table->quick_bits);
+		skip_bits(rar5, table->quick_len[code]);
+		*num = table->quick_num[code];
+		return ARCHIVE_OK;
+	}
+
+	bits = 15;
+
+	for(i = table->quick_bits + 1; i < 15; i++) {
+		if(bitfield < table->decode_len[i]) {
+			bits = i;
+			break;
+		}
+	}
+
+	skip_bits(rar5, bits);
+
+	dist = bitfield - table->decode_len[bits - 1];
+	dist >>= (16 - bits);
+	pos = table->decode_pos[bits] + dist;
+
+	if(pos >= table->size)
+		pos = 0;
+
+	*num = table->decode_num[pos];
+	return ARCHIVE_OK;
+}
+
+/* Reads and parses Huffman tables from the beginning of the block. */
+static int parse_tables(struct archive_read* a, struct rar5 *rar5,
+    const uint8_t* p)
+{
+	int ret, value, i, w, idx = 0;
+	uint8_t bit_length[HUFF_BC],
+		table[HUFF_TABLE_SIZE],
+		nibble_mask = 0xF0,
+		nibble_shift = 4;
+
+	enum { ESCAPE = 15 };
+
+	/* The data for table generation is compressed using a simple RLE-like
+	 * algorithm when storing zeroes, so we need to unpack it first. */
+	for(w = 0, i = 0; w < HUFF_BC;) {
+		if(i >= rar5->cstate.cur_block_size) {
+			/* Truncated data, can't continue. */
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Truncated data in huffman tables");
+			return ARCHIVE_FAILED;
+		}
+
+		value = (p[i] & nibble_mask) >> nibble_shift;
+
+		if(nibble_mask == 0x0F)
+			++i;
+
+		nibble_mask ^= 0xFF;
+		nibble_shift ^= 4;
+
+		/* Values smaller than 15 is data, so we write it directly.
+		 * Value 15 is a flag telling us that we need to unpack more
+		 * bytes. */
+		if(value == ESCAPE) {
+			value = (p[i] & nibble_mask) >> nibble_shift;
+			if(nibble_mask == 0x0F)
+				++i;
+			nibble_mask ^= 0xFF;
+			nibble_shift ^= 4;
+
+			if(value == 0) {
+				/* We sometimes need to write the actual value
+				 * of 15, so this case handles that. */
+				bit_length[w++] = ESCAPE;
+			} else {
+				int k;
+
+				/* Fill zeroes. */
+				for(k = 0; (k < value + 2) && (w < HUFF_BC);
+				    k++) {
+					bit_length[w++] = 0;
+				}
+			}
+		} else {
+			bit_length[w++] = value;
+		}
+	}
+
+	rar5->bits.in_addr = i;
+	rar5->bits.bit_addr = nibble_shift ^ 4;
+
+	ret = create_decode_tables(bit_length, &rar5->cstate.bd, HUFF_BC);
+	if(ret != ARCHIVE_OK) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Decoding huffman tables failed");
+		return ARCHIVE_FAILED;
+	}
+
+	for(i = 0; i < HUFF_TABLE_SIZE;) {
+		uint16_t num;
+
+		ret = decode_number(a, &rar5->cstate.bd, p, &num);
+		if(ret != ARCHIVE_OK) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Decoding huffman tables failed");
+			return ARCHIVE_FAILED;
+		}
+
+		if(num < 16) {
+			/* 0..15: store directly */
+			table[i] = (uint8_t) num;
+			i++;
+		} else if(num < 18) {
+			/* 16..17: repeat previous code */
+			uint16_t n;
+
+			if(ARCHIVE_OK != (ret = read_bits_16(a, rar5, p, &n)))
+				return ret;
+
+			if(num == 16) {
+				n >>= 13;
+				n += 3;
+				skip_bits(rar5, 3);
+			} else {
+				n >>= 9;
+				n += 11;
+				skip_bits(rar5, 7);
+			}
+
+			if(i > 0) {
+				while(n-- > 0 && i < HUFF_TABLE_SIZE) {
+					table[i] = table[i - 1];
+					i++;
+				}
+			} else {
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_FILE_FORMAT,
+				    "Unexpected error when decoding "
+				    "huffman tables");
+				return ARCHIVE_FAILED;
+			}
+		} else {
+			/* other codes: fill with zeroes `n` times */
+			uint16_t n;
+
+			if(ARCHIVE_OK != (ret = read_bits_16(a, rar5, p, &n)))
+				return ret;
+
+			if(num == 18) {
+				n >>= 13;
+				n += 3;
+				skip_bits(rar5, 3);
+			} else {
+				n >>= 9;
+				n += 11;
+				skip_bits(rar5, 7);
+			}
+
+			while(n-- > 0 && i < HUFF_TABLE_SIZE)
+				table[i++] = 0;
+		}
+	}
+
+	ret = create_decode_tables(&table[idx], &rar5->cstate.ld, HUFF_NC);
+	if(ret != ARCHIVE_OK) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		     "Failed to create literal table");
+		return ARCHIVE_FAILED;
+	}
+
+	idx += HUFF_NC;
+
+	ret = create_decode_tables(&table[idx], &rar5->cstate.dd, HUFF_DC);
+	if(ret != ARCHIVE_OK) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Failed to create distance table");
+		return ARCHIVE_FAILED;
+	}
+
+	idx += HUFF_DC;
+
+	ret = create_decode_tables(&table[idx], &rar5->cstate.ldd, HUFF_LDC);
+	if(ret != ARCHIVE_OK) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Failed to create lower bits of distances table");
+		return ARCHIVE_FAILED;
+	}
+
+	idx += HUFF_LDC;
+
+	ret = create_decode_tables(&table[idx], &rar5->cstate.rd, HUFF_RC);
+	if(ret != ARCHIVE_OK) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Failed to create repeating distances table");
+		return ARCHIVE_FAILED;
+	}
+
+	return ARCHIVE_OK;
+}
+
+/* Parses the block header, verifies its CRC byte, and saves the header
+ * fields inside the `hdr` pointer. */
+static int parse_block_header(struct archive_read* a, const uint8_t* p,
+    ssize_t* block_size, struct compressed_block_header* hdr)
+{
+	uint8_t calculated_cksum;
+	memcpy(hdr, p, sizeof(struct compressed_block_header));
+
+	if(bf_byte_count(hdr) > 2) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Unsupported block header size (was %d, max is 2)",
+		    bf_byte_count(hdr));
+		return ARCHIVE_FAILED;
+	}
+
+	/* This should probably use bit reader interface in order to be more
+	 * future-proof. */
+	*block_size = 0;
+	switch(bf_byte_count(hdr)) {
+		/* 1-byte block size */
+		case 0:
+			*block_size = *(const uint8_t*) &p[2];
+			break;
+
+		/* 2-byte block size */
+		case 1:
+			*block_size = archive_le16dec(&p[2]);
+			break;
+
+		/* 3-byte block size */
+		case 2:
+			*block_size = archive_le32dec(&p[2]);
+			*block_size &= 0x00FFFFFF;
+			break;
+
+		/* Other block sizes are not supported. This case is not
+		 * reached, because we have an 'if' guard before the switch
+		 * that makes sure of it. */
+		default:
+			return ARCHIVE_FATAL;
+	}
+
+	/* Verify the block header checksum. 0x5A is a magic value and is
+	 * always * constant. */
+	calculated_cksum = 0x5A
+	    ^ (uint8_t) hdr->block_flags_u8
+	    ^ (uint8_t) *block_size
+	    ^ (uint8_t) (*block_size >> 8)
+	    ^ (uint8_t) (*block_size >> 16);
+
+	if(calculated_cksum != hdr->block_cksum) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Block checksum error: got 0x%x, expected 0x%x",
+		    hdr->block_cksum, calculated_cksum);
+
+		return ARCHIVE_FAILED;
+#endif
+	}
+
+	return ARCHIVE_OK;
+}
+
+/* Convenience function used during filter processing. */
+static int parse_filter_data(struct archive_read* a, struct rar5 *rar5,
+	const uint8_t* p, uint32_t* filter_data)
+{
+	int i, bytes, ret;
+	uint32_t data = 0;
+
+	if(ARCHIVE_OK != (ret = read_consume_bits(a, rar5, p, 2, &bytes)))
+		return ret;
+
+	bytes++;
+
+	for(i = 0; i < bytes; i++) {
+		uint16_t byte;
+
+		if(ARCHIVE_OK != (ret = read_bits_16(a, rar5, p, &byte))) {
+			return ret;
+		}
+
+		/* Cast to uint32_t will ensure the shift operation will not
+		 * produce undefined result. */
+		data += ((uint32_t) byte >> 8) << (i * 8);
+		skip_bits(rar5, 8);
+	}
+
+	*filter_data = data;
+	return ARCHIVE_OK;
+}
+
+/* Function is used during sanity checking. */
+static int is_valid_filter_block_start(struct rar5 *rar5,
+    uint32_t start)
+{
+	const int64_t block_start = (ssize_t) start + rar5->cstate.write_ptr;
+	const int64_t last_bs = rar5->cstate.last_block_start;
+	const ssize_t last_bl = rar5->cstate.last_block_length;
+
+	if(last_bs == 0 || last_bl == 0) {
+		/* We didn't have any filters yet, so accept this offset. */
+		return 1;
+	}
+
+	if(block_start >= last_bs + last_bl) {
+		/* Current offset is bigger than last block's end offset, so
+		 * accept current offset. */
+		return 1;
+	}
+
+	/* Any other case is not a normal situation and we should fail. */
+	return 0;
+}
+
+/* The function will create a new filter, read its parameters from the input
+ * stream and add it to the filter collection. */
+static int parse_filter(struct archive_read* ar, const uint8_t* p) {
+	struct rar5 *rar5 = ar->format->data;
+	uint32_t block_start, block_length;
+	uint16_t filter_type;
+	struct filter_info* filt = NULL;
+	int ret;
+
+	/* Read the parameters from the input stream. */
+	if(ARCHIVE_OK != (ret = parse_filter_data(ar, rar5, p, &block_start)))
+		return ret;
+
+	if(ARCHIVE_OK != (ret = parse_filter_data(ar, rar5, p, &block_length)))
+		return ret;
+
+	if(ARCHIVE_OK != (ret = read_bits_16(ar, rar5, p, &filter_type)))
+		return ret;
+
+	filter_type >>= 13;
+	skip_bits(rar5, 3);
+
+	/* Perform some sanity checks on this filter parameters. */
+
+	if(block_length < 4 ||
+	    block_length > 0x400000 ||
+	    !is_valid_filter_block_start(rar5, block_start) ||
+	    (rar5->cstate.window_size > 0 &&
+	     (ssize_t)block_length > rar5->cstate.window_size >> 1))
+	{
+		archive_set_error(&ar->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Invalid filter encountered");
+		return ARCHIVE_FAILED;
+	}
+
+	/* Allocate a new filter. */
+	filt = add_new_filter(rar5);
+	if(filt == NULL) {
+		archive_set_error(&ar->archive, ENOMEM,
+		    "Can't allocate memory for a filter descriptor");
+		return ARCHIVE_FATAL;
+	}
+
+	filt->type = filter_type;
+	filt->block_start = rar5->cstate.write_ptr + block_start;
+	filt->block_length = block_length;
+
+	rar5->cstate.last_block_start = filt->block_start;
+	rar5->cstate.last_block_length = filt->block_length;
+
+	/* Read some more data in case this is a DELTA filter. Other filter
+	 * types don't require any additional data over what was already
+	 * read. */
+	if(filter_type == FILTER_DELTA) {
+		int channels;
+
+		if(ARCHIVE_OK != (ret = read_consume_bits(ar, rar5, p, 5, &channels)))
+			return ret;
+
+		filt->channels = channels + 1;
+	}
+
+	return ARCHIVE_OK;
+}
+
+static int decode_code_length(struct archive_read* a, struct rar5 *rar5,
+	const uint8_t* p, uint16_t code)
+{
+	int lbits, length = 2;
+
+	if(code < 8) {
+		lbits = 0;
+		length += code;
+	} else {
+		lbits = code / 4 - 1;
+		length += (4 | (code & 3)) << lbits;
+	}
+
+	if(lbits > 0) {
+		int add;
+
+		if(ARCHIVE_OK != read_consume_bits(a, rar5, p, lbits, &add))
+			return -1;
+
+		length += add;
+	}
+
+	return length;
+}
+
+static int copy_string(struct archive_read* a, int len, int dist) {
+	struct rar5 *rar5 = a->format->data;
+	const ssize_t cmask = rar5->cstate.window_mask;
+	const uint64_t write_ptr = rar5->cstate.write_ptr +
+	    rar5->cstate.solid_offset;
+	int i;
+
+	if (rar5->cstate.window_buf == NULL)
+		return ARCHIVE_FATAL;
+
+	if (rar5->cstate.write_ptr > rar5->file.unpacked_size ||
+	    len > rar5->file.unpacked_size - rar5->cstate.write_ptr) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Uncompressed data exceeds declared size");
+		return rar5->main.solid ? ARCHIVE_FATAL : ARCHIVE_FAILED;
+	}
+
+	/* The unpacker spends most of the time in this function. It would be
+	 * a good idea to introduce some optimizations here.
+	 *
+	 * Just remember that this loop treats buffers that overlap differently
+	 * than buffers that do not overlap. This is why a simple memcpy(3)
+	 * call will not be enough. */
+
+	for(i = 0; i < len; i++) {
+		const ssize_t write_idx = (write_ptr + i) & cmask;
+		const ssize_t read_idx = (write_ptr + i - dist) & cmask;
+		rar5->cstate.window_buf[write_idx] =
+		    rar5->cstate.window_buf[read_idx];
+	}
+
+	rar5->cstate.write_ptr += len;
+	return ARCHIVE_OK;
+}
+
+static int do_uncompress_block(struct archive_read* a, const uint8_t* p) {
+	struct rar5 *rar5 = a->format->data;
+	uint16_t num;
+	int ret;
+
+	const uint64_t cmask = rar5->cstate.window_mask;
+	const struct compressed_block_header* hdr = &rar5->last_block_hdr;
+	const uint8_t bit_size = 1 + bf_bit_size(hdr);
+
+	while(1) {
+		if(rar5->cstate.write_ptr - rar5->cstate.last_write_ptr >
+		    (rar5->cstate.window_size >> 1)) {
+			/* Don't allow growing data by more than half of the
+			 * window size at a time. In such case, break the loop;
+			 *  next call to this function will continue processing
+			 *  from this moment. */
+			break;
+		}
+
+		if(rar5->bits.in_addr > rar5->cstate.cur_block_size - 1 ||
+		    (rar5->bits.in_addr == rar5->cstate.cur_block_size - 1 &&
+		    rar5->bits.bit_addr >= bit_size))
+		{
+			/* If the program counter is here, it means the
+			 * function has finished processing the block. */
+			rar5->cstate.block_parsing_finished = 1;
+			break;
+		}
+
+		/* Decode the next literal. */
+		if(ARCHIVE_OK != decode_number(a, &rar5->cstate.ld, p, &num)) {
+			return ARCHIVE_EOF;
+		}
+
+		/* Num holds a decompression literal, or 'command code'.
+		 *
+		 * - Values lower than 256 are just bytes. Those codes
+		 *   can be stored in the output buffer directly.
+		 *
+		 * - Code 256 defines a new filter, which is later used to
+		 *   transform the data block accordingly to the filter type.
+		 *   The data block needs to be fully uncompressed first.
+		 *
+		 * - Code bigger than 257 and smaller than 262 define
+		 *   a repetition pattern that should be copied from
+		 *   an already uncompressed chunk of data.
+		 */
+
+		if(num < 256) {
+			/* Directly store the byte. */
+			int64_t write_idx;
+
+			/* A literal write emits one byte; copy_string() checks len. */
+			if(rar5->cstate.write_ptr >= rar5->file.unpacked_size) {
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_FILE_FORMAT,
+				    "Uncompressed data exceeds declared size");
+				return rar5->main.solid ? ARCHIVE_FATAL : ARCHIVE_FAILED;
+			}
+
+			write_idx = rar5->cstate.solid_offset +
+			    rar5->cstate.write_ptr++;
+
+			rar5->cstate.window_buf[write_idx & cmask] =
+			    (uint8_t) num;
+			continue;
+		} else if(num >= 262) {
+			uint16_t dist_slot;
+			int len = decode_code_length(a, rar5, p, num - 262),
+				dbits,
+				dist = 1;
+
+			if(len == -1) {
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_PROGRAMMER,
+				    "Failed to decode the code length");
+
+				return rar5->main.solid ? ARCHIVE_FATAL : ARCHIVE_FAILED;
+			}
+
+			if(ARCHIVE_OK != decode_number(a, &rar5->cstate.dd, p,
+			    &dist_slot))
+			{
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_PROGRAMMER,
+				    "Failed to decode the distance slot");
+
+				return rar5->main.solid ? ARCHIVE_FATAL : ARCHIVE_FAILED;
+			}
+
+			if(dist_slot < 4) {
+				dbits = 0;
+				dist += dist_slot;
+			} else {
+				dbits = dist_slot / 2 - 1;
+
+				/* Cast to uint32_t will make sure the shift
+				 * left operation won't produce undefined
+				 * result. Then, the uint32_t type will
+				 * be implicitly casted to int. */
+				dist += (uint32_t) (2 |
+				    (dist_slot & 1)) << dbits;
+			}
+
+			if(dbits > 0) {
+				if(dbits >= 4) {
+					uint32_t add = 0;
+					uint16_t low_dist;
+
+					if(dbits > 4) {
+						if(ARCHIVE_OK != (ret = read_bits_32(
+						    a, rar5, p, &add))) {
+							/* Return EOF if we
+							 * can't read more
+							 * data. */
+							return ret;
+						}
+
+						skip_bits(rar5, dbits - 4);
+						add = (add >> (
+						    36 - dbits)) << 4;
+						dist += add;
+					}
+
+					if(ARCHIVE_OK != decode_number(a,
+					    &rar5->cstate.ldd, p, &low_dist))
+					{
+						archive_set_error(&a->archive,
+						    ARCHIVE_ERRNO_PROGRAMMER,
+						    "Failed to decode the "
+						    "distance slot");
+
+						return rar5->main.solid ? ARCHIVE_FATAL : ARCHIVE_FAILED;
+					}
+
+					if(dist >= INT_MAX - low_dist - 1) {
+						/* This only happens in
+						 * invalid archives. */
+						archive_set_error(&a->archive,
+						    ARCHIVE_ERRNO_FILE_FORMAT,
+						    "Distance pointer "
+						    "overflow");
+						return rar5->main.solid ? ARCHIVE_FATAL : ARCHIVE_FAILED;
+					}
+
+					dist += low_dist;
+				} else {
+					/* dbits is one of [0,1,2,3] */
+					int add;
+
+					if(ARCHIVE_OK != (ret = read_consume_bits(a, rar5,
+					     p, dbits, &add))) {
+						/* Return EOF if we can't read
+						 * more data. */
+						return ret;
+					}
+
+					dist += add;
+				}
+			}
+
+			if(dist > 0x100) {
+				len++;
+
+				if(dist > 0x2000) {
+					len++;
+
+					if(dist > 0x40000) {
+						len++;
+					}
+				}
+			}
+
+			dist_cache_push(rar5, dist);
+			rar5->cstate.last_len = len;
+
+			ret = copy_string(a, len, dist);
+			if(ret != ARCHIVE_OK)
+				return ret;
+
+			continue;
+		} else if(num == 256) {
+			/* Create a filter. */
+			ret = parse_filter(a, p);
+			if(ret != ARCHIVE_OK)
+				return ret;
+
+			continue;
+		} else if(num == 257) {
+			if(rar5->cstate.last_len != 0) {
+				ret = copy_string(a,
+				    rar5->cstate.last_len,
+				    rar5->cstate.dist_cache[0]);
+				if(ret != ARCHIVE_OK)
+					return ret;
+			}
+
+			continue;
+		} else {
+			/* num < 262 */
+			const int idx = num - 258;
+			const int dist = dist_cache_touch(rar5, idx);
+
+			uint16_t len_slot;
+			int len;
+
+			if(ARCHIVE_OK != decode_number(a, &rar5->cstate.rd, p,
+			    &len_slot)) {
+				return rar5->main.solid ? ARCHIVE_FATAL : ARCHIVE_FAILED;
+			}
+
+			len = decode_code_length(a, rar5, p, len_slot);
+			if (len == -1) {
+				return rar5->main.solid ? ARCHIVE_FATAL : ARCHIVE_FAILED;
+			}
+
+			rar5->cstate.last_len = len;
+
+			ret = copy_string(a, len, dist);
+			if(ret != ARCHIVE_OK)
+				return ret;
+
+			continue;
+		}
+	}
+
+	return ARCHIVE_OK;
+}
+
+/* Binary search for the RARv5 signature. */
+static int scan_for_signature(struct archive_read* a) {
+	const uint8_t* p;
+	const int chunk_size = 512;
+	ssize_t i;
+	char signature[sizeof(rar5_signature_xor)];
+
+	/* If we're here, it means we're on an 'unknown territory' data.
+	 * There's no indication what kind of data we're reading here.
+	 * It could be some text comment, any kind of binary data,
+	 * digital sign, dragons, etc.
+	 *
+	 * We want to find a valid RARv5 magic header inside this unknown
+	 * data. */
+
+	/* Is it possible in libarchive to just skip everything until the
+	 * end of the file? If so, it would be a better approach than the
+	 * current implementation of this function. */
+
+	rar5_signature(signature);
+
+	while(1) {
+		if(!read_ahead(a, chunk_size, &p))
+			return ARCHIVE_EOF;
+
+		for(i = 0; i < chunk_size - (int)sizeof(rar5_signature_xor);
+		    i++) {
+			if(memcmp(&p[i], signature,
+			    sizeof(rar5_signature_xor)) == 0) {
+				/* Consume the number of bytes we've used to
+				 * search for the signature, as well as the
+				 * number of bytes used by the signature
+				 * itself. After this we should be standing
+				 * on a valid base block header. */
+				(void) consume(a,
+				    i + sizeof(rar5_signature_xor));
+				return ARCHIVE_OK;
+			}
+		}
+
+		consume(a, chunk_size);
+	}
+
+	return ARCHIVE_FATAL;
+}
+
+/* This function will switch the multivolume archive file to another file,
+ * i.e. from part03 to part 04. */
+static int advance_multivolume(struct archive_read* a) {
+	struct rar5 *rar5 = a->format->data;
+	int lret;
+
+	/* A small state machine that will skip unnecessary data, needed to
+	 * switch from one multivolume to another. Such skipping is needed if
+	 * we want to be an stream-oriented (instead of file-oriented)
+	 * unpacker.
+	 *
+	 * The state machine starts with `rar5->main.endarc` == 0. It also
+	 * assumes that current stream pointer points to some base block
+	 * header.
+	 *
+	 * The `endarc` field is being set when the base block parsing
+	 * function encounters the 'end of archive' marker.
+	 */
+
+	while(1) {
+		if(rar5->main.endarc == 1) {
+			int looping = 1;
+
+			rar5->main.endarc = 0;
+
+			while(looping) {
+				lret = skip_base_block(a);
+				switch(lret) {
+					case ARCHIVE_RETRY:
+						/* Continue looping. */
+						break;
+					case ARCHIVE_OK:
+						/* Break loop. */
+						looping = 0;
+						break;
+					default:
+						/* Forward any errors to the
+						 * caller. */
+						return lret;
+				}
+			}
+
+			break;
+		} else {
+			/* Skip current base block. In order to properly skip
+			 * it, we really need to simply parse it and discard
+			 * the results. */
+
+			lret = skip_base_block(a);
+			if(lret == ARCHIVE_FATAL || lret == ARCHIVE_FAILED)
+				return lret;
+
+			/* The `skip_base_block` function tells us if we
+			 * should continue with skipping, or we should stop
+			 * skipping. We're trying to skip everything up to
+			 * a base FILE block. */
+
+			if(lret != ARCHIVE_RETRY) {
+				/* If there was an error during skipping, or we
+				 * have just skipped a FILE base block... */
+
+				if(rar5->main.endarc == 0) {
+					return lret;
+				} else {
+					continue;
+				}
+			}
+		}
+	}
+
+	return ARCHIVE_OK;
+}
+
+/* Merges the partial block from the first multivolume archive file, and
+ * partial block from the second multivolume archive file. The result is
+ * a chunk of memory containing the whole block, and the stream pointer
+ * is advanced to the next block in the second multivolume archive file. */
+static int merge_block(struct archive_read* a, ssize_t block_size,
+    const uint8_t** p)
+{
+	struct rar5 *rar5 = a->format->data;
+	ssize_t cur_block_size, partial_offset = 0;
+	const uint8_t* lp;
+	int ret;
+
+	if(rar5->merge_mode) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+		    "Recursive merge is not allowed");
+
+		return ARCHIVE_FATAL;
+	}
+
+	/* Set a flag that we're in the switching mode. */
+	rar5->cstate.switch_multivolume = 1;
+
+	/* Reallocate the memory which will hold the whole block. */
+	if(rar5->vol.push_buf)
+		free((void*) rar5->vol.push_buf);
+
+	/* Increasing the allocation block by 8 is due to bit reading functions,
+	 * which are using additional 2 or 4 bytes. Allocating the block size
+	 * by exact value would make bit reader perform reads from invalid
+	 * memory block when reading the last byte from the buffer. */
+	rar5->vol.push_buf = malloc(block_size + 8);
+	if(!rar5->vol.push_buf) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate memory for a merge block buffer");
+		rar5->cstate.switch_multivolume = 0;
+		return ARCHIVE_FATAL;
+	}
+
+	/* Valgrind complains if the extension block for bit reader is not
+	 * initialized, so initialize it. */
+	memset(&rar5->vol.push_buf[block_size], 0, 8);
+
+	/* A single block can span across multiple multivolume archive files,
+	 * so we use a loop here. This loop will consume enough multivolume
+	 * archive files until the whole block is read. */
+
+	while(1) {
+		/* Get the size of current block chunk in this multivolume
+		 * archive file and read it. */
+		cur_block_size = rar5_min(rar5->file.bytes_remaining,
+		    block_size - partial_offset);
+
+		if(cur_block_size < 1) {
+			/* bytes_remaining is less than 1 at the wrong point in
+			 * the merge loop, indicating corrupt volume
+			 * accounting. */
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Encountered invalid block size during block merge");
+			rar5->cstate.switch_multivolume = 0;
+			return ARCHIVE_FATAL;
+		}
+
+		if(!read_ahead(a, cur_block_size, &lp)) {
+			rar5->cstate.switch_multivolume = 0;
+			return ARCHIVE_EOF;
+		}
+
+		/* Sanity check; there should never be a situation where this
+		 * function reads more data than the block's size. */
+		if(partial_offset + cur_block_size > block_size) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_PROGRAMMER,
+			    "Consumed too much data when merging blocks");
+			rar5->cstate.switch_multivolume = 0;
+			return ARCHIVE_FATAL;
+		}
+
+		/* Merge previous block chunk with current block chunk,
+		 * or create first block chunk if this is our first
+		 * iteration. */
+		memcpy(&rar5->vol.push_buf[partial_offset], lp, cur_block_size);
+
+		/* Advance the stream read pointer by this block chunk size. */
+		if(ARCHIVE_OK != consume(a, cur_block_size)) {
+			/* Data was copied but stream pointer didn't advance;
+			 * stream position is unrecoverable. */
+			rar5->cstate.switch_multivolume = 0;
+			return ARCHIVE_FATAL;
+		}
+
+		/* Update the pointers. `partial_offset` contains information
+		 * about the sum of merged block chunks. */
+		partial_offset += cur_block_size;
+		rar5->file.bytes_remaining -= cur_block_size;
+
+		/* If `partial_offset` is the same as `block_size`, this means
+		 * we've merged all block chunks and we have a valid full
+		 * block. */
+		if(partial_offset == block_size) {
+			break;
+		}
+
+		/* If we don't have any bytes to read, this means we should
+		 * switch to another multivolume archive file. */
+		if(rar5->file.bytes_remaining == 0) {
+			rar5->merge_mode++;
+			ret = advance_multivolume(a);
+			rar5->merge_mode--;
+			if(ret != ARCHIVE_OK) {
+				rar5->cstate.switch_multivolume = 0;
+				return ret;
+			}
+		}
+	}
+
+	*p = rar5->vol.push_buf;
+
+	/* If we're here, we can resume unpacking by processing the block
+	 * pointed to by the `*p` memory pointer. */
+
+	return ARCHIVE_OK;
+}
+
+static int process_block(struct archive_read* a) {
+	struct rar5 *rar5 = a->format->data;
+	const uint8_t* p;
+	int ret;
+
+	/* If we don't have any data to be processed, this most probably means
+	 * we need to switch to the next volume. */
+	if(rar5->main.volume && rar5->file.bytes_remaining == 0) {
+		ret = advance_multivolume(a);
+		if(ret != ARCHIVE_OK)
+			return ret;
+	}
+
+	if(rar5->cstate.block_parsing_finished) {
+		ssize_t block_size;
+		ssize_t to_skip;
+		ssize_t cur_block_size;
+
+		/* The header size won't be bigger than 6 bytes. */
+		if(!read_ahead(a, 6, &p)) {
+			/* Failed to prefetch data block header. */
+			return ARCHIVE_EOF;
+		}
+
+		/*
+		 * Read block_size by parsing block header. Validate the header
+		 * by calculating CRC byte stored inside the header. Size of
+		 * the header is not constant (block size can be stored either
+		 * in 1 or 2 bytes), that's why block size is left out from the
+		 * `compressed_block_header` structure and returned by
+		 * `parse_block_header` as the second argument. */
+
+		ret = parse_block_header(a, p, &block_size,
+		    &rar5->last_block_hdr);
+		if(ret != ARCHIVE_OK) {
+			return ret;
+		}
+
+		/* Skip block header. Next data is huffman tables,
+		 * if present. */
+		to_skip = sizeof(struct compressed_block_header) +
+			bf_byte_count(&rar5->last_block_hdr) + 1;
+
+		/* If the block header's to_skip value exceeds the declared
+		 * remaining data, the archive is malformed. */
+		if(to_skip > rar5->file.bytes_remaining) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Block header size exceeds remaining file data");
+			return ARCHIVE_FATAL;
+		}
+
+		if(ARCHIVE_OK != consume(a, to_skip))
+			return ARCHIVE_EOF;
+
+		rar5->file.bytes_remaining -= to_skip;
+
+		/* The block size gives information about the whole block size,
+		 * but the block could be stored in split form when using
+		 * multi-volume archives. In this case, the block size will be
+		 * bigger than the actual data stored in this file. Remaining
+		 * part of the data will be in another file. */
+
+		cur_block_size =
+			rar5_min(rar5->file.bytes_remaining, block_size);
+
+		if(block_size > rar5->file.bytes_remaining) {
+			/* If current blocks' size is bigger than our data
+			 * size, this means we have a multivolume archive.
+			 * In this case, skip all base headers until the end
+			 * of the file, proceed to next "partXXX.rar" volume,
+			 * find its signature, skip all headers up to the first
+			 * FILE base header, and continue from there.
+			 *
+			 * Note that `merge_block` will update the `rar`
+			 * context structure quite extensively. */
+
+			ret = merge_block(a, block_size, &p);
+			if(ret != ARCHIVE_OK) {
+				return ret;
+			}
+
+			cur_block_size = block_size;
+
+			/* Current stream pointer should be now directly
+			 * *after* the block that spanned through multiple
+			 * archive files. `p` pointer should have the data of
+			 * the *whole* block (merged from partial blocks
+			 * stored in multiple archives files). */
+		} else {
+			rar5->cstate.switch_multivolume = 0;
+
+			/* Read the whole block size into memory. This can take
+			 * up to  8 megabytes of memory in theoretical cases.
+			 * Might be worth to optimize this and use a standard
+			 * chunk of 4kb's. */
+			if(!read_ahead(a, 4 + cur_block_size, &p)) {
+				/* Failed to prefetch block data. */
+				return ARCHIVE_EOF;
+			}
+		}
+
+		rar5->cstate.block_buf = p;
+		rar5->cstate.cur_block_size = cur_block_size;
+		rar5->cstate.block_parsing_finished = 0;
+
+		rar5->bits.in_addr = 0;
+		rar5->bits.bit_addr = 0;
+
+		if(bf_is_table_present(&rar5->last_block_hdr)) {
+			/* Load Huffman tables. */
+			ret = parse_tables(a, rar5, p);
+			if(ret != ARCHIVE_OK) {
+				/* Error during decompression of Huffman
+				 * tables. */
+				return ret;
+			}
+		}
+	} else {
+		/* Block parsing not finished, reuse previous memory buffer. */
+		p = rar5->cstate.block_buf;
+	}
+
+	/* Uncompress the block, or a part of it, depending on how many bytes
+	 * will be generated by uncompressing the block.
+	 *
+	 * In case too many bytes will be generated, calling this function
+	 * again will resume the uncompression operation. */
+	ret = do_uncompress_block(a, p);
+	if(ret != ARCHIVE_OK) {
+		return ret;
+	}
+
+	if(rar5->cstate.block_parsing_finished &&
+	    rar5->cstate.switch_multivolume == 0 &&
+	    rar5->cstate.cur_block_size > 0)
+	{
+		/* If we're processing a normal block, consume the whole
+		 * block. We can do this because we've already read the whole
+		 * block to memory. */
+		if(ARCHIVE_OK != consume(a, rar5->cstate.cur_block_size))
+			return ARCHIVE_FATAL;
+
+		rar5->file.bytes_remaining -= rar5->cstate.cur_block_size;
+	} else if(rar5->cstate.switch_multivolume) {
+		/* Don't consume the block if we're doing multivolume
+		 * processing. The volume switching function will consume
+		 * the proper count of bytes instead. */
+		rar5->cstate.switch_multivolume = 0;
+	}
+
+	return ARCHIVE_OK;
+}
+
+/* Pops the `buf`, `size` and `offset` from the "data ready" stack.
+ *
+ * Returns ARCHIVE_OK when those arguments can be used, ARCHIVE_RETRY
+ * when there is no data on the stack. */
+static int use_data(struct rar5 *rar5, const void** buf, size_t* size,
+    int64_t* offset)
+{
+	int i;
+
+	for(i = 0; i < rar5_countof(rar5->cstate.dready); i++) {
+		struct data_ready *d = &rar5->cstate.dready[i];
+
+		if(d->used) {
+			if(buf)    *buf = d->buf;
+			if(size)   *size = d->size;
+			if(offset) *offset = d->offset;
+
+			d->used = 0;
+			return ARCHIVE_OK;
+		}
+	}
+
+	return ARCHIVE_RETRY;
+}
+
+static void clear_data_ready_stack(struct rar5 *rar5) {
+	memset(&rar5->cstate.dready, 0, sizeof(rar5->cstate.dready));
+}
+
+/* Pushes the `buf`, `size` and `offset` arguments to the rar5->cstate.dready
+ * FIFO stack. Those values will be popped from this stack by the `use_data`
+ * function. */
+static int push_data_ready(struct archive_read* a, struct rar5 *rar5,
+    const uint8_t* buf, size_t size, int64_t offset)
+{
+	int i;
+
+	/* Don't push if we're in skip mode. This is needed because solid
+	 * streams need full processing even if we're skipping data. After
+	 * fully processing the stream, we need to discard the generated bytes,
+	 * because we're interested only in the side effect: building up the
+	 * internal window circular buffer. This window buffer will be used
+	 * later during unpacking of requested data. */
+	if(rar5->skip_mode)
+		return ARCHIVE_OK;
+
+	/* Sanity check. */
+	if(offset != rar5->file.last_offset + rar5->file.last_size) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+		    "Sanity check error: output stream is not continuous");
+		return ARCHIVE_FATAL;
+	}
+
+	for(i = 0; i < rar5_countof(rar5->cstate.dready); i++) {
+		struct data_ready* d = &rar5->cstate.dready[i];
+		if(!d->used) {
+			d->used = 1;
+			d->buf = buf;
+			d->size = size;
+			d->offset = offset;
+
+			/* These fields are used only in sanity checking. */
+			rar5->file.last_offset = offset;
+			rar5->file.last_size = size;
+
+			/* Calculate the checksum of this new block before
+			 * submitting data to libarchive's engine. */
+			update_crc(rar5, d->buf, d->size);
+
+			return ARCHIVE_OK;
+		}
+	}
+
+	/* Program counter will reach this code if the
+	 * `rar5->cstate.data_ready` stack will be filled up so that no new
+	 * entries will be allowed. The code shouldn't allow such situation to
+	 * occur. So we treat this case as an internal error. */
+
+	archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+	    "Premature end of data_ready stack");
+	return ARCHIVE_FATAL;
+}
+
+/* This function uncompresses the data that is stored in the <FILE> base
+ * block.
+ *
+ * The FILE base block looks like this:
+ *
+ * <header><huffman tables><block_1><block_2>...<block_n>
+ *
+ * The <header> is a block header, that is parsed in parse_block_header().
+ * It's a "compressed_block_header" structure, containing metadata needed
+ * to know when we should stop looking for more <block_n> blocks.
+ *
+ * <huffman tables> contain data needed to set up the huffman tables, needed
+ * for the actual decompression.
+ *
+ * Each <block_n> consists of series of literals:
+ *
+ * <literal><literal><literal>...<literal>
+ *
+ * Those literals generate the uncompression data. They operate on a circular
+ * buffer, sometimes writing raw data into it, sometimes referencing
+ * some previous data inside this buffer, and sometimes declaring a filter
+ * that will need to be executed on the data stored in the circular buffer.
+ * It all depends on the literal that is used.
+ *
+ * Sometimes blocks produce output data, sometimes they don't. For example, for
+ * some huge files that use lots of filters, sometimes a block is filled with
+ * only filter declaration literals. Such blocks won't produce any data in the
+ * circular buffer.
+ *
+ * Sometimes blocks will produce 4 bytes of data, and sometimes 1 megabyte,
+ * because a literal can reference previously decompressed data. For example,
+ * there can be a literal that says: 'append a byte 0xFE here', and after
+ * it another literal can say 'append 1 megabyte of data from circular buffer
+ * offset 0x12345'. This is how RAR format handles compressing repeated
+ * patterns.
+ *
+ * The RAR compressor creates those literals and the actual efficiency of
+ * compression depends on what those literals are. The literals can also
+ * be seen as a kind of a non-turing-complete virtual machine that simply
+ * tells the decompressor what it should do.
+ * */
+
+static int do_uncompress_file(struct archive_read* a) {
+	struct rar5 *rar5 = a->format->data;
+	int ret;
+	int64_t max_end_pos;
+
+	if(!rar5->cstate.initialized) {
+		/* Don't perform full context reinitialization if we're
+		 * processing a solid archive. */
+		if(!rar5->main.solid || !rar5->cstate.window_buf) {
+			if((ret = init_unpack(rar5)) != ARCHIVE_OK)
+				return ret;
+		}
+
+		rar5->cstate.initialized = 1;
+	}
+
+	/* Don't allow extraction if window_size is invalid. */
+	if(rar5->cstate.window_size == 0) {
+		archive_set_error(&a->archive,
+			ARCHIVE_ERRNO_FILE_FORMAT,
+			"Invalid window size declaration in this file");
+
+		/* This should never happen in valid files. */
+		return ARCHIVE_FAILED;
+	}
+
+	if(rar5->cstate.all_filters_applied == 1) {
+		/* We use while(1) here, but standard case allows for just 1
+		 * iteration. The loop will iterate if process_block() didn't
+		 * generate any data at all. This can happen if the block
+		 * contains only filter definitions (this is common in big
+		 * files). */
+		while(1) {
+			ret = process_block(a);
+			if(ret != ARCHIVE_OK)
+				return ret;
+
+			if(rar5->cstate.last_write_ptr ==
+			    rar5->cstate.write_ptr) {
+				/* The block didn't generate any new data,
+				 * so just process a new block if this one
+				 * wasn't the last block in the file. */
+				if (bf_is_last_block(&rar5->last_block_hdr)) {
+					return ARCHIVE_EOF;
+				}
+
+				continue;
+			}
+
+			/* The block has generated some new data, so break
+			 * the loop. */
+			break;
+		}
+	}
+
+	/* Try to run filters. If filters won't be applied, it means that
+	 * insufficient data was generated. */
+	ret = apply_filters(a);
+	if(ret == ARCHIVE_RETRY) {
+		return ARCHIVE_OK;
+	} else if(ret != ARCHIVE_OK) {
+		return ret;
+	}
+
+	if(cdeque_size(&rar5->cstate.filters) > 0) {
+		/* Check if we can write something before hitting first
+		 * filter. */
+		struct filter_info* flt;
+
+		/* Get the block_start offset from the first filter. */
+		if(CDE_OK != cdeque_front(&rar5->cstate.filters,
+		    cdeque_filter_p(&flt)))
+		{
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_PROGRAMMER,
+			    "Can't read first filter");
+			return ARCHIVE_FATAL;
+		}
+
+		max_end_pos = rar5_min(flt->block_start,
+		    rar5->cstate.write_ptr);
+	} else {
+		/* There are no filters defined, or all filters were applied.
+		 * This means we can just store the data without any
+		 * postprocessing. */
+		max_end_pos = rar5->cstate.write_ptr;
+	}
+
+	if(max_end_pos == rar5->cstate.last_write_ptr) {
+		/* We can't write anything yet. The block uncompression
+		 * function did not generate enough data, and no filter can be
+		 * applied. At the same time we don't have any data that can be
+		 *  stored without filter postprocessing. This means we need to
+		 *  wait for more data to be generated, so we can apply the
+		 * filters.
+		 *
+		 * Signal the caller that we need more data to be able to do
+		 * anything.
+		 */
+		return ARCHIVE_RETRY;
+	} else {
+		/* We can write the data before hitting the first filter.
+		 * So let's do it. The push_window_data() function will
+		 * effectively return the selected data block to the user
+		 * application. */
+		push_window_data(a, rar5, rar5->cstate.last_write_ptr,
+		    max_end_pos);
+		rar5->cstate.last_write_ptr = max_end_pos;
+	}
+
+	return ARCHIVE_OK;
+}
+
+static int uncompress_file(struct archive_read* a) {
+	int ret;
+
+	while(1) {
+		/* Sometimes the uncompression function will return a
+		 * 'retry' signal. If this will happen, we have to retry
+		 * the function. */
+		ret = do_uncompress_file(a);
+		if(ret != ARCHIVE_RETRY)
+			return ret;
+	}
+}
+
+
+static int do_unstore_file(struct archive_read* a,
+    struct rar5 *rar5, const void** buf, size_t* size, int64_t* offset)
+{
+	size_t to_read;
+	const uint8_t* p;
+
+	if(rar5->file.bytes_remaining == 0 && rar5->main.volume > 0 &&
+	    rar5->generic.split_after > 0)
+	{
+		int ret;
+
+		rar5->cstate.switch_multivolume = 1;
+		ret = advance_multivolume(a);
+		rar5->cstate.switch_multivolume = 0;
+
+		if(ret != ARCHIVE_OK) {
+			/* Failed to advance to next multivolume archive
+			 * file. */
+			return ret;
+		}
+	}
+
+	to_read = rar5_min(rar5->file.bytes_remaining, 64 * 1024);
+	if(to_read == 0) {
+		return ARCHIVE_EOF;
+	}
+
+	if(!read_ahead(a, to_read, &p)) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "I/O error when unstoring file");
+		return ARCHIVE_FATAL;
+	}
+
+	if(ARCHIVE_OK != consume(a, to_read)) {
+		return ARCHIVE_EOF;
+	}
+
+	if(buf)    *buf = p;
+	if(size)   *size = to_read;
+	if(offset) *offset = rar5->cstate.last_unstore_ptr;
+
+	rar5->file.bytes_remaining -= to_read;
+	rar5->cstate.last_unstore_ptr += to_read;
+
+	update_crc(rar5, p, to_read);
+	return ARCHIVE_OK;
+}
+
+static int do_unpack(struct archive_read* a, struct rar5 *rar5,
+    const void** buf, size_t* size, int64_t* offset)
+{
+	enum COMPRESSION_METHOD {
+		STORE = 0, FASTEST = 1, FAST = 2, NORMAL = 3, GOOD = 4,
+		BEST = 5
+	};
+
+	if(rar5->file.service > 0) {
+		return do_unstore_file(a, rar5, buf, size, offset);
+	} else {
+		switch(rar5->cstate.method) {
+			case STORE:
+				return do_unstore_file(a, rar5, buf, size,
+				    offset);
+			case FASTEST:
+				/* fallthrough */
+			case FAST:
+				/* fallthrough */
+			case NORMAL:
+				/* fallthrough */
+			case GOOD:
+				/* fallthrough */
+			case BEST:
+				/* No data is returned here. But because a sparse-file aware
+				 * caller (like archive_read_data_into_fd) may treat zero-size
+				 * as a sparse file block, we need to update the offset
+				 * accordingly. At this point the decoder doesn't have any
+				 * pending uncompressed data blocks, so the current position in
+				 * the output file should be last_write_ptr. */
+				if (offset) *offset = rar5->cstate.last_write_ptr;
+				return uncompress_file(a);
+			default:
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_FILE_FORMAT,
+				    "Compression method not supported: 0x%x",
+				    (unsigned int)rar5->cstate.method);
+
+				return ARCHIVE_FATAL;
+		}
+	}
+
+#if !defined WIN32
+	/* Not reached. */
+	return ARCHIVE_OK;
+#endif
+}
+
+static int verify_checksums(struct archive_read* a) {
+	struct rar5 *rar5 = a->format->data;
+	int verify_crc;
+
+	/* Check checksums only when actually unpacking the data. There's no
+	 * need to calculate checksum when we're skipping data in solid archives
+	 * (skipping in solid archives is the same thing as unpacking compressed
+	 * data and discarding the result). */
+
+	if(!rar5->skip_mode) {
+		/* Always check checksums if we're not in skip mode */
+		verify_crc = 1;
+	} else {
+		/* We can override the logic above with a compile-time option
+		 * NO_CRC_ON_SOLID_SKIP. This option is used during debugging,
+		 * and it will check checksums of unpacked data even when
+		 * we're skipping it. */
+
+#if defined CHECK_CRC_ON_SOLID_SKIP
+		/* Debug case */
+		verify_crc = 1;
+#else
+		/* Normal case */
+		verify_crc = 0;
+#endif
+	}
+
+	if(verify_crc) {
+		/* During unpacking, on each unpacked block we're calling the
+		 * update_crc() function. Since we are here, the unpacking
+		 * process is already over and we can check if calculated
+		 * checksum (CRC32 or BLAKE2sp) is the same as what is stored
+		 * in the archive. */
+		if(rar5->file.stored_crc32 > 0) {
+			/* Check CRC32 only when the file contains a CRC32
+			 * value for this file. */
+
+			if(rar5->file.calculated_crc32 !=
+			    rar5->file.stored_crc32) {
+				/* Checksums do not match; the unpacked file
+				 * is corrupted. */
+
+				DEBUG_CODE {
+					printf("Checksum error: CRC32 "
+					    "(was: %08" PRIx32 ", expected: %08" PRIx32 ")\n",
+					    rar5->file.calculated_crc32,
+					    rar5->file.stored_crc32);
+				}
+
+#ifndef DONT_FAIL_ON_CRC_ERROR
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_FILE_FORMAT,
+				    "Checksum error: CRC32");
+				return ARCHIVE_FAILED;
+#endif
+			} else {
+				DEBUG_CODE {
+					printf("Checksum OK: CRC32 "
+					    "(%08" PRIx32 "/%08" PRIx32 ")\n",
+					    rar5->file.stored_crc32,
+					    rar5->file.calculated_crc32);
+				}
+			}
+		}
+
+		if(rar5->file.has_blake2 > 0) {
+			/* BLAKE2sp is an optional checksum algorithm that is
+			 * added to RARv5 archives when using the `-htb` switch
+			 *  during creation of archive.
+			 *
+			 * We now finalize the hash calculation by calling the
+			 * `final` function. This will generate the final hash
+			 * value we can use to compare it with the BLAKE2sp
+			 * checksum that is stored in the archive.
+			 *
+			 * The return value of this `final` function is not
+			 * very helpful, as it guards only against improper use.
+ 			 * This is why we're explicitly ignoring it. */
+
+			uint8_t b2_buf[32];
+			(void) blake2sp_final(&rar5->file.b2state, b2_buf, 32);
+
+			if(memcmp(&rar5->file.blake2sp, b2_buf, 32) != 0) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_FILE_FORMAT,
+				    "Checksum error: BLAKE2");
+
+				return ARCHIVE_FAILED;
+#endif
+			}
+		}
+	}
+
+	/* Finalization for this file has been successfully completed. */
+	return ARCHIVE_OK;
+}
+
+static int verify_global_checksums(struct archive_read* a) {
+	return verify_checksums(a);
+}
+
+/*
+ * Decryption function for the magic signature pattern. Check the comment near
+ * the `rar5_signature_xor` symbol to read the rationale behind this.
+ */
+static void rar5_signature(char *buf) {
+		size_t i;
+
+		for(i = 0; i < sizeof(rar5_signature_xor); i++) {
+			buf[i] = rar5_signature_xor[i] ^ 0xA1;
+		}
+}
+
+static int rar5_read_data(struct archive_read *a, const void **buff,
+    size_t *size, int64_t *offset) {
+	struct rar5 *rar5 = a->format->data;
+	int ret;
+
+	if (size)
+		*size = 0;
+
+	if (rar5->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) {
+		rar5->has_encrypted_entries = 0;
+	}
+
+	if (rar5->headers_are_encrypted || rar5->cstate.data_encrypted) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Reading encrypted data is not currently supported");
+		return ARCHIVE_FAILED;
+	}
+
+	if(rar5->file.dir > 0) {
+		/* Don't process any data if this file entry was declared
+		 * as a directory. This is needed, because entries marked as
+		 * directory doesn't have any dictionary buffer allocated, so
+		 * it's impossible to perform any decompression. */
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Can't decompress an entry marked as a directory");
+		return ARCHIVE_FATAL;
+	}
+
+	if(!rar5->skip_mode && (rar5->cstate.last_write_ptr > rar5->file.unpacked_size)) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+		    "Unpacker has written too many bytes");
+		return ARCHIVE_FATAL;
+	}
+
+	ret = use_data(rar5, buff, size, offset);
+	if(ret == ARCHIVE_OK) {
+		return ret;
+	}
+
+	if(rar5->file.eof == 1) {
+		return ARCHIVE_EOF;
+	}
+
+	ret = do_unpack(a, rar5, buff, size, offset);
+	if(ret != ARCHIVE_OK) {
+		return ret;
+	}
+
+	if(rar5->file.bytes_remaining == 0 &&
+			rar5->cstate.last_write_ptr == rar5->file.unpacked_size)
+	{
+		/* If all bytes of current file were processed, run
+		 * finalization.
+		 *
+		 * Finalization will check checksum against proper values. If
+		 * some of the checksums will not match, we'll return an error
+		 * value in the last `archive_read_data` call to signal an error
+		 * to the user. */
+
+		rar5->file.eof = 1;
+		return verify_global_checksums(a);
+	}
+
+	return ARCHIVE_OK;
+}
+
+static int rar5_read_data_skip(struct archive_read *a) {
+	struct rar5 *rar5 = a->format->data;
+
+	if(rar5->main.solid && (rar5->cstate.data_encrypted == 0)) {
+		/* In solid archives, instead of skipping the data, we need to
+		 * extract it, and dispose the result. The side effect of this
+		 * operation will be setting up the initial window buffer state
+		 * needed to be able to extract the selected file. Note that
+		 * this is only possible when data within this solid block is
+		 * not encrypted, in which case we'll skip and fail if the user
+		 * tries to read data. */
+
+		int ret;
+
+		/* Make sure to process all blocks in the compressed stream. */
+		while(rar5->file.bytes_remaining > 0) {
+			/* Setting the "skip mode" will allow us to skip
+			 * checksum checks during data skipping. Checking the
+			 * checksum of skipped data isn't really necessary and
+			 * it's only slowing things down.
+			 *
+			 * This is incremented instead of setting to 1 because
+			 * this data skipping function can be called
+			 * recursively. */
+			rar5->skip_mode++;
+
+			/* We're disposing 1 block of data, so we use triple
+			 * NULLs in arguments. */
+			ret = rar5_read_data(a, NULL, NULL, NULL);
+
+			/* Turn off "skip mode". */
+			rar5->skip_mode--;
+
+			if(ret < 0 || ret == ARCHIVE_EOF) {
+				/* Propagate any potential error conditions
+				 * to the caller. */
+				return ret;
+			}
+		}
+	} else {
+		/* In standard archives, we can just jump over the compressed
+		 * stream. Each file in non-solid archives starts from an empty
+		 * window buffer. */
+
+		if(ARCHIVE_OK != consume(a, rar5->file.bytes_remaining)) {
+			return ARCHIVE_FATAL;
+		}
+
+		rar5->file.bytes_remaining = 0;
+	}
+
+	return ARCHIVE_OK;
+}
+
+static int64_t rar5_seek_data(struct archive_read *a, int64_t offset,
+    int whence)
+{
+	(void) offset;
+	(void) whence;
+
+	/* We're a streaming unpacker, and we don't support seeking.
+	 * That's a capability gap, not a fatal error. */
+	archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+	    "Seeking of RAR5 files is unsupported");
+
+	return (ARCHIVE_FAILED);
+}
+
+static int rar5_cleanup(struct archive_read *a) {
+	struct rar5 *rar5 = a->format->data;
+
+	free(rar5->cstate.window_buf);
+	free(rar5->cstate.filtered_buf);
+	clear_data_ready_stack(rar5);
+
+	free(rar5->vol.push_buf);
+
+	free_filters(rar5);
+	rar5_deinit(rar5);
+
+	free(rar5);
+	a->format->data = NULL;
+
+	return ARCHIVE_OK;
+}
+
+static int rar5_capabilities(struct archive_read * a) {
+	(void) a;
+	return (ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA
+			| ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA);
+}
+
+static int rar5_has_encrypted_entries(struct archive_read *_a) {
+	if (_a && _a->format) {
+		struct rar5 *rar5 = _a->format->data;
+		if (rar5) {
+			return rar5->has_encrypted_entries;
+		}
+	}
+
+	return ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW;
+}
+
+/* Must match deallocations in rar5_deinit */
+static int rar5_init(struct rar5 *rar5) {
+	memset(rar5, 0, sizeof(struct rar5));
+
+	if(CDE_OK != cdeque_init(&rar5->cstate.filters, 8192))
+		return ARCHIVE_FATAL;
+
+	/*
+	 * Until enough data has been read, we cannot tell about
+	 * any encrypted entries yet.
+	 */
+	rar5->has_encrypted_entries = ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW;
+
+	return ARCHIVE_OK;
+}
+
+/* Must match allocations in rar5_init */
+static void rar5_deinit(struct rar5 *rar5) {
+	cdeque_free(&rar5->cstate.filters);
+}
+
+int archive_read_support_format_rar5(struct archive *_a) {
+	struct archive_read* ar;
+	int ret;
+	struct rar5 *rar5;
+
+	if(ARCHIVE_OK != (ret = get_archive_read(_a, &ar)))
+		return ret;
+
+	rar5 = malloc(sizeof(*rar5));
+	if(rar5 == NULL) {
+		archive_set_error(&ar->archive, ENOMEM,
+		    "Can't allocate rar5 data");
+		return ARCHIVE_FATAL;
+	}
+
+	if(ARCHIVE_OK != rar5_init(rar5)) {
+		archive_set_error(&ar->archive, ENOMEM,
+		    "Can't allocate rar5 filter buffer");
+		free(rar5);
+		return ARCHIVE_FATAL;
+	}
+
+	ret = __archive_read_register_format(ar,
+	    rar5,
+	    "rar5",
+	    rar5_bid,
+	    rar5_options,
+	    rar5_read_header,
+	    rar5_read_data,
+	    rar5_read_data_skip,
+	    rar5_seek_data,
+	    rar5_cleanup,
+	    rar5_capabilities,
+	    rar5_has_encrypted_entries);
+
+	if(ret != ARCHIVE_OK) {
+		rar5_deinit(rar5);
+		free(rar5);
 	}
 
 	return ARCHIVE_OK;
diff --git a/c/archive_read_support_format_raw.c b/c/archive_read_support_format_raw.c
--- a/c/archive_read_support_format_raw.c
+++ b/c/archive_read_support_format_raw.c
@@ -34,10 +34,11 @@
 
 #include "archive.h"
 #include "archive_entry.h"
+#include "archive_integer.h"
 #include "archive_private.h"
 #include "archive_read_private.h"
 
-struct raw_info {
+struct raw {
 	int64_t offset; /* Current position in the file. */
 	int64_t unconsumed;
 	int     end_of_file;
@@ -54,22 +55,22 @@
 int
 archive_read_support_format_raw(struct archive *_a)
 {
-	struct raw_info *info;
 	struct archive_read *a = (struct archive_read *)_a;
+	struct raw *raw;
 	int r;
 
 	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
 	    ARCHIVE_STATE_NEW, "archive_read_support_format_raw");
 
-	info = calloc(1, sizeof(*info));
-	if (info == NULL) {
+	raw = calloc(1, sizeof(*raw));
+	if (raw == NULL) {
 		archive_set_error(&a->archive, ENOMEM,
-		    "Can't allocate raw_info data");
+		    "Can't allocate raw data");
 		return (ARCHIVE_FATAL);
 	}
 
 	r = __archive_read_register_format(a,
-	    info,
+	    raw,
 	    "raw",
 	    archive_read_format_raw_bid,
 	    NULL,
@@ -81,7 +82,7 @@
 	    NULL,
 	    NULL);
 	if (r != ARCHIVE_OK)
-		free(info);
+		free(raw);
 	return (r);
 }
 
@@ -107,10 +108,9 @@
 archive_read_format_raw_read_header(struct archive_read *a,
     struct archive_entry *entry)
 {
-	struct raw_info *info;
+	struct raw *raw = a->format->data;
 
-	info = (struct raw_info *)(a->format->data);
-	if (info->end_of_file)
+	if (raw->end_of_file)
 		return (ARCHIVE_EOF);
 
 	a->archive.archive_format = ARCHIVE_FORMAT_RAW;
@@ -128,39 +128,43 @@
 archive_read_format_raw_read_data(struct archive_read *a,
     const void **buff, size_t *size, int64_t *offset)
 {
-	struct raw_info *info;
+	struct raw *raw = a->format->data;
 	ssize_t avail;
 
-	info = (struct raw_info *)(a->format->data);
-
 	/* Consume the bytes we read last time. */
-	if (info->unconsumed) {
-		__archive_read_consume(a, info->unconsumed);
-		info->unconsumed = 0;
+	if (raw->unconsumed) {
+		__archive_read_consume(a, raw->unconsumed);
+		raw->unconsumed = 0;
 	}
 
-	if (info->end_of_file)
+	if (raw->end_of_file)
 		return (ARCHIVE_EOF);
 
 	/* Get whatever bytes are immediately available. */
 	*buff = __archive_read_ahead(a, 1, &avail);
 	if (avail > 0) {
 		/* Return the bytes we just read */
+		*offset = raw->offset;
+		if (archive_ckd_add_i64(&raw->offset, raw->offset, avail)) {
+			avail = INT64_MAX - *offset;
+			if (avail == 0) {
+				*size = 0;
+				return (ARCHIVE_FATAL);
+			}
+		}
 		*size = avail;
-		*offset = info->offset;
-		info->offset += *size;
-		info->unconsumed = avail;
+		raw->unconsumed = avail;
 		return (ARCHIVE_OK);
 	} else if (0 == avail) {
 		/* Record and return end-of-file. */
-		info->end_of_file = 1;
+		raw->end_of_file = 1;
 		*size = 0;
-		*offset = info->offset;
+		*offset = raw->offset;
 		return (ARCHIVE_EOF);
 	} else {
 		/* Record and return an error. */
 		*size = 0;
-		*offset = info->offset;
+		*offset = raw->offset;
 		return ((int)avail);
 	}
 }
@@ -168,24 +172,23 @@
 static int
 archive_read_format_raw_read_data_skip(struct archive_read *a)
 {
-	struct raw_info *info = (struct raw_info *)(a->format->data);
+	struct raw *raw = a->format->data;
 
 	/* Consume the bytes we read last time. */
-	if (info->unconsumed) {
-		__archive_read_consume(a, info->unconsumed);
-		info->unconsumed = 0;
+	if (raw->unconsumed) {
+		__archive_read_consume(a, raw->unconsumed);
+		raw->unconsumed = 0;
 	}
-	info->end_of_file = 1;
+	raw->end_of_file = 1;
 	return (ARCHIVE_OK);
 }
 
 static int
 archive_read_format_raw_cleanup(struct archive_read *a)
 {
-	struct raw_info *info;
+	struct raw *raw = a->format->data;
 
-	info = (struct raw_info *)(a->format->data);
-	free(info);
+	free(raw);
 	a->format->data = NULL;
 	return (ARCHIVE_OK);
 }
diff --git a/c/archive_read_support_format_tar.c b/c/archive_read_support_format_tar.c
--- a/c/archive_read_support_format_tar.c
+++ b/c/archive_read_support_format_tar.c
@@ -251,36 +251,6 @@
 static const size_t acl_limit = 131072; /* Longest textual ACL: 128kiB */
 static const int64_t entry_limit = 0xfffffffffffffffLL; /* 2^60 bytes = 1 ExbiByte */
 
-/*
- * There's no standard for TIME_T_MAX.  So we compute it
- * here.  TODO: Move this to configure time, but be careful
- * about cross-compile environments.
- */
-static time_t
-get_time_t_max(void)
-{
-#if defined(TIME_T_MAX)
-        return TIME_T_MAX;
-#else
-        /* ISO C allows time_t to be a floating-point type,
-           but POSIX requires an integer type.  The following
-           should work on any system that follows the POSIX
-           conventions. */
-        if (((time_t)0) < ((time_t)-1)) {
-                /* Time_t is unsigned */
-                return (~(time_t)0);
-        } else {
-                /* Time_t is signed. */
-                /* Assume it's the same as int64_t or int32_t */
-                if (sizeof(time_t) == sizeof(int64_t)) {
-                        return (time_t)INT64_MAX;
-                } else {
-                        return (time_t)INT32_MAX;
-                }
-        }
-#endif
-}
-
 int
 archive_read_support_format_gnutar(struct archive *a)
 {
@@ -330,9 +300,8 @@
 static int
 archive_read_format_tar_cleanup(struct archive_read *a)
 {
-	struct tar *tar;
+	struct tar *tar = a->format->data;
 
-	tar = (struct tar *)(a->format->data);
 	gnu_clear_sparse_list(tar);
 	archive_string_free(&tar->entry_pathname);
 	archive_string_free(&tar->entry_pathname_override);
@@ -342,7 +311,7 @@
 	archive_string_free(&tar->line);
 	archive_string_free(&tar->localname);
 	free(tar);
-	(a->format->data) = NULL;
+	a->format->data = NULL;
 	return (ARCHIVE_OK);
 }
 
@@ -469,10 +438,9 @@
 archive_read_format_tar_options(struct archive_read *a,
     const char *key, const char *val)
 {
-	struct tar *tar;
+	struct tar *tar = a->format->data;
 	int ret = ARCHIVE_FAILED;
 
-	tar = (struct tar *)(a->format->data);
 	if (strcmp(key, "compat-2x")  == 0) {
 		/* Handle UTF-8 filenames as libarchive 2.x */
 		tar->compat_2x = (val != NULL && val[0] != 0);
@@ -555,15 +523,13 @@
 	 * probably not worthwhile just to support the relatively
 	 * obscure tar->cpio conversion case.
 	 */
-	struct tar *tar;
+	struct tar *tar = a->format->data;
 	const char *p;
 	const wchar_t *wp;
 	int r;
 	size_t l;
 	int64_t unconsumed = 0;
 
-	tar = (struct tar *)(a->format->data);
-
 	/* Assign default device/inode values. */
 	archive_entry_set_dev(entry, 1 + tar->default_dev); /* Don't use zero. */
 	archive_entry_set_ino(entry, ++tar->default_inode); /* Don't use zero. */
@@ -639,12 +605,10 @@
 archive_read_format_tar_read_data(struct archive_read *a,
     const void **buff, size_t *size, int64_t *offset)
 {
+	struct tar *tar = a->format->data;
 	ssize_t bytes_read;
-	struct tar *tar;
 	struct sparse_block *p;
 
-	tar = (struct tar *)(a->format->data);
-
 	for (;;) {
 		/* Remove exhausted entries from sparse list. */
 		while (tar->sparse_list != NULL &&
@@ -703,11 +667,9 @@
 static int
 archive_read_format_tar_skip(struct archive_read *a)
 {
+	struct tar *tar = a->format->data;
 	int64_t request;
-	struct tar* tar;
 
-	tar = (struct tar *)(a->format->data);
-
 	request = tar->entry_bytes_remaining + tar->entry_padding +
 	    tar->entry_bytes_unconsumed;
 
@@ -1104,21 +1066,19 @@
 header_Solaris_ACL(struct archive_read *a, struct tar *tar,
     struct archive_entry *entry, const void *h, int64_t *unconsumed)
 {
-	const struct archive_entry_header_ustar *header;
 	struct archive_string	 acl_text;
 	size_t size;
 	int err, acl_type;
 	uint64_t type;
 	char *acl, *p;
 
-	header = (const struct archive_entry_header_ustar *)h;
-	size = (size_t)tar_atol(header->size, sizeof(header->size));
 	archive_string_init(&acl_text);
 	err = read_body_to_string(a, tar, &acl_text, h, unconsumed);
 	if (err != ARCHIVE_OK) {
 		archive_string_free(&acl_text);
 		return (err);
 	}
+	size = archive_strlen(&acl_text);
 
 	/* TODO: Examine the first characters to see if this
 	 * is an AIX ACL descriptor.  We'll likely never support
@@ -1377,6 +1337,7 @@
 	const struct archive_entry_header_ustar	*header;
 	const char *existing_linkpath;
 	const wchar_t *existing_wcs_linkpath;
+	mode_t header_mode;
 	int     err = ARCHIVE_OK;
 
 	header = (const struct archive_entry_header_ustar *)h;
@@ -1384,12 +1345,10 @@
 	/* Parse out the numeric fields (all are octal) */
 
 	/* Split mode handling: Set filetype always, perm only if not already set */
-	archive_entry_set_filetype(entry,
-	    (mode_t)tar_atol(header->mode, sizeof(header->mode)));
-	if (!archive_entry_perm_is_set(entry)) {
-		archive_entry_set_perm(entry,
-			(mode_t)tar_atol(header->mode, sizeof(header->mode)));
-	}
+	header_mode = (mode_t)tar_atol(header->mode, sizeof(header->mode));
+	archive_entry_set_filetype(entry, header_mode);
+	if (!archive_entry_perm_is_set(entry))
+		archive_entry_set_perm(entry, header_mode);
 
 	/* Set uid, gid, mtime if not already set */
 	if (!archive_entry_uid_is_set(entry)) {
@@ -1402,7 +1361,7 @@
 		int64_t t64 = tar_atol(header->mtime, sizeof(header->mtime));
 		time_t t = (time_t)t64;
 		if ((int64_t)t != t64) { /* time_t overflowed */
-			t = get_time_t_max();
+			t = TIME_MAX;
 		}
 		archive_entry_set_mtime(entry, t, 0);
 	}
@@ -1679,33 +1638,33 @@
 	const char *p, *name;
 	const wchar_t *wp, *wname;
 
-	wname = wp = archive_entry_pathname_w(entry);
-	if (wp != NULL) {
+	name = p = archive_entry_pathname(entry);
+	if (p != NULL) {
 		/* Find the last path element. */
-		for (; *wp != L'\0'; ++wp) {
-			if (wp[0] == '/' && wp[1] != L'\0')
-				wname = wp + 1;
+		for (; *p != '\0'; ++p) {
+			if (p[0] == '/' && p[1] != '\0')
+				name = p + 1;
 		}
 		/*
 		 * If last path element starts with "._", then
 		 * this is a Mac extension.
 		 */
-		if (wname[0] == L'.' && wname[1] == L'_' && wname[2] != L'\0')
+		if (name[0] == '.' && name[1] == '_' && name[2] != '\0')
 			return 1;
 	} else {
 		/* Find the last path element. */
-		name = p = archive_entry_pathname(entry);
-		if (p == NULL)
-			return (ARCHIVE_FAILED);
-		for (; *p != '\0'; ++p) {
-			if (p[0] == '/' && p[1] != '\0')
-				name = p + 1;
+		wname = wp = archive_entry_pathname_w(entry);
+		if (wp == NULL)
+			return 0;
+		for (; *wp != L'\0'; ++wp) {
+			if (wp[0] == L'/' && wp[1] != L'\0')
+				wname = wp + 1;
 		}
 		/*
 		 * If last path element starts with "._", then
 		 * this is a Mac extension.
 		 */
-		if (name[0] == '.' && name[1] == '_' && name[2] != '\0')
+		if (wname[0] == L'.' && wname[1] == L'_' && wname[2] != L'\0')
 			return 1;
 	}
 	/* Not a mac extension */
@@ -1787,7 +1746,6 @@
 	const struct archive_entry_header_ustar *header;
 	int64_t size, to_consume;
 
-	(void)a; /* UNUSED */
 	(void)tar; /* UNUSED */
 	(void)entry; /* UNUSED */
 
@@ -1799,6 +1757,11 @@
 		    (long long)size);
 		return (ARCHIVE_FATAL);
 	}
+	if (size == 0) {
+		archive_set_error(&a->archive, EINVAL,
+		    "Invalid empty pax global extended header");
+		return (ARCHIVE_FATAL);
+	}
 	to_consume = ((size + 511) & ~511);
 	*unconsumed += to_consume;
 	return (ARCHIVE_OK);
@@ -1945,6 +1908,11 @@
 			    (long long)ext_size);
 	  return (ARCHIVE_FATAL);
 	}
+	if (ext_size == 0) {
+		archive_set_error(&a->archive, EINVAL,
+		    "Invalid empty pax extended header");
+		return (ARCHIVE_FATAL);
+	}
 
 	ext_padding = 0x1ff & (-ext_size);
 	if (ext_size > ext_size_limit) {
@@ -3056,6 +3024,7 @@
 header_gnutar(struct archive_read *a, struct tar *tar,
     struct archive_entry *entry, const void *h, int64_t *unconsumed)
 {
+	struct archive_entry_header_gnutar header_copy;
 	const struct archive_entry_header_gnutar *header;
 	int64_t t;
 	int err = ARCHIVE_OK;
@@ -3066,8 +3035,14 @@
 	 * filename is stored as in old-style archives.
 	 */
 
+	/*
+	 * Sparse extension reads can reuse the callback buffer containing h,
+	 * so preserve the main header until common fields are parsed.
+	 */
+	memcpy(&header_copy, h, sizeof(header_copy));
+	header = &header_copy;
+
 	/* Copy filename over (to ensure null termination). */
-	header = (const struct archive_entry_header_gnutar *)h;
 	const char *existing_pathname = archive_entry_pathname(entry);
 	const wchar_t *existing_wcs_pathname = archive_entry_pathname_w(entry);
 	if ((existing_pathname == NULL || existing_pathname[0] == '\0')
@@ -3146,7 +3121,7 @@
 	}
 
 	/* Grab fields common to all tar variants. */
-	err = header_common(a, tar, entry, h);
+	err = header_common(a, tar, entry, header);
 	if (err == ARCHIVE_FATAL)
 		return (err);
 
@@ -3469,13 +3444,10 @@
 				return (ARCHIVE_FATAL);
 			tar->sparse_last->hole = hole;
 		}
-		if (length == 0 || *e == '\n') {
-			if (length == 0 && *e == '\n') {
-				return (ARCHIVE_OK);
-			} else {
-				return (ARCHIVE_WARN);
-			}
-		}
+		if (length == 0)
+			return (ARCHIVE_OK);
+		if (*e == '\n')
+			return (ARCHIVE_WARN);
 		p = e + 1;
 		length--;
 		hole = hole == 0;
@@ -3538,17 +3510,17 @@
 	}
 
 	l = 0;
-	if (char_cnt != 0) {
+	while (char_cnt != 0) {
 		digit = *p - '0';
-		while (digit >= 0 && digit < base && char_cnt != 0) {
-			if (archive_ckd_mul_i64(&l, l, base) ||
-			    archive_ckd_add_i64(&l, l, sign * digit)) {
-				 /* Truncate on overflow. */
-				return sign < 0 ? INT64_MIN : INT64_MAX;
-			}
-			digit = *++p - '0';
-			char_cnt--;
+		if (digit < 0 || digit >= base)
+			break;
+		if (archive_ckd_mul_i64(&l, l, base) ||
+		    archive_ckd_add_i64(&l, l, sign * digit)) {
+			 /* Truncate on overflow. */
+			return sign < 0 ? INT64_MIN : INT64_MAX;
 		}
+		p++;
+		char_cnt--;
 	}
 	return l;
 }
diff --git a/c/archive_read_support_format_warc.c b/c/archive_read_support_format_warc.c
--- a/c/archive_read_support_format_warc.c
+++ b/c/archive_read_support_format_warc.c
@@ -25,28 +25,35 @@
 
 #include "archive_platform.h"
 
-/**
- * WARC is standardised by ISO TC46/SC4/WG12 and currently available as
- * ISO 28500:2009.
- * For the purposes of this file we used the final draft from:
+/*
+ * An overview of WARC format:
+ *
+ * WARC files are laid out as a sequence of records.  Each record has
+ * a text header followed by a content block whose size is given by
+ * Content-Length.  This reader supports WARC/0.12 through WARC/1.0
+ * and was written using the final draft that became ISO 28500:2009:
  * http://bibnum.bnf.fr/warc/WARC_ISO_28500_version1_latestdraft.pdf
  *
- * Todo:
- * [ ] real-world warcs can contain resources at endpoints ending in /
- *     e.g. http://bibnum.bnf.fr/warc/
- *     if you're lucky their response contains a Content-Location: header
- *     pointing to a unix-compliant filename, in the example above it's
- *     Content-Location: http://bibnum.bnf.fr/warc/index.html
- *     however, that's not mandated and github for example doesn't follow
- *     this convention.
- *     We need a set of archive options to control what to do with
- *     entries like these, at the moment care is taken to skip them.
+ * This reader exposes resource and response records as regular files
+ * when they have a usable WARC-Target-URI.  WARC-Date is exposed as
+ * ctime, and a Last-Modified record header is exposed as mtime when
+ * present.
  *
- **/
+ * TODO: Real-world WARCs can contain resources at endpoints ending in
+ * a slash, for example http://bibnum.bnf.fr/warc/.  Some responses
+ * include a Content-Location header that points to a Unix-compatible
+ * filename such as http://bibnum.bnf.fr/warc/index.html, but WARC does
+ * not require that convention and some sites do not follow it.  Until
+ * archive options exist to control these entries, this reader skips
+ * them instead of creating directory endpoints as files.
+ */
 
 #ifdef HAVE_SYS_STAT_H
 #include <sys/stat.h>
 #endif
+#ifdef HAVE_CTYPE_H
+#include <ctype.h>
+#endif
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
 #endif
@@ -56,40 +63,35 @@
 #ifdef HAVE_STRING_H
 #include <string.h>
 #endif
-#ifdef HAVE_LIMITS_H
-#include <limits.h>
-#endif
-#ifdef HAVE_CTYPE_H
-#include <ctype.h>
-#endif
 #ifdef HAVE_TIME_H
 #include <time.h>
 #endif
 
 #include "archive.h"
 #include "archive_entry.h"
+#include "archive_integer.h"
 #include "archive_private.h"
 #include "archive_read_private.h"
 
 typedef enum {
 	WT_NONE,
-	/* warcinfo */
+	/* WARC info */
 	WT_INFO,
-	/* metadata */
+	/* Metadata */
 	WT_META,
-	/* resource */
+	/* Resource */
 	WT_RSRC,
-	/* request, unsupported */
+	/* Request, unsupported */
 	WT_REQ,
-	/* response, unsupported */
+	/* Response */
 	WT_RSP,
-	/* revisit, unsupported */
+	/* Revisit, unsupported */
 	WT_RVIS,
-	/* conversion, unsupported */
+	/* Conversion, unsupported */
 	WT_CONV,
-	/* continuation, unsupported at the moment */
+	/* Continuation, currently unsupported */
 	WT_CONT,
-	/* invalid type */
+	/* Invalid type */
 	LAST_WT
 } warc_type_t;
 
@@ -103,82 +105,92 @@
 	char *str;
 } warc_strbuf_t;
 
-struct warc_s {
-	/* content length ahead */
-	size_t cntlen;
-	/* and how much we've processed so far */
-	size_t cntoff;
-	/* and how much we need to consume between calls */
-	size_t unconsumed;
+struct warc {
+	/* Content length of the current record */
+	int64_t cntlen;
+	/* Bytes processed from the current record */
+	int64_t cntoff;
+	/* Bytes to consume before the next read */
+	int64_t unconsumed;
 
-	/* string pool */
+	/* String pool */
 	warc_strbuf_t pool;
-	/* previous version */
+	/* Previous version */
 	unsigned int pver;
-	/* stringified format name */
+	/* Stringified format name */
 	struct archive_string sver;
 };
 
-static int _warc_bid(struct archive_read *a, int);
-static int _warc_cleanup(struct archive_read *a);
-static int _warc_read(struct archive_read*, const void**, size_t*, int64_t*);
-static int _warc_skip(struct archive_read *a);
-static int _warc_rdhdr(struct archive_read *a, struct archive_entry *e);
+static int	archive_read_format_warc_bid(struct archive_read *, int);
+static int	archive_read_format_warc_cleanup(struct archive_read *);
+static int	archive_read_format_warc_read_data(struct archive_read *,
+		    const void **, size_t *, int64_t *);
+static int	archive_read_format_warc_skip(struct archive_read *);
+static int	archive_read_format_warc_read_header(struct archive_read *,
+		    struct archive_entry *);
 
-/* private routines */
-static unsigned int _warc_rdver(const char *buf, size_t bsz);
-static unsigned int _warc_rdtyp(const char *buf, size_t bsz);
-static warc_string_t _warc_rduri(const char *buf, size_t bsz);
-static ssize_t _warc_rdlen(const char *buf, size_t bsz);
-static time_t _warc_rdrtm(const char *buf, size_t bsz);
-static time_t _warc_rdmtm(const char *buf, size_t bsz);
-static const char *_warc_find_eoh(const char *buf, size_t bsz);
-static const char *_warc_find_eol(const char *buf, size_t bsz);
+/* Private routines */
+static unsigned int	warc_read_version(const char *, size_t);
+static unsigned int	warc_read_type(const char *, size_t);
+static warc_string_t	warc_read_uri(const char *, size_t);
+static int64_t		warc_read_length(const char *, size_t);
+static time_t		warc_read_date(const char *, size_t);
+static time_t		warc_read_last_modified(const char *, size_t);
+static const char	*warc_find_eoh(const char *, size_t);
+static const char	*warc_find_eol(const char *, size_t);
 
 int
 archive_read_support_format_warc(struct archive *_a)
 {
 	struct archive_read *a = (struct archive_read *)_a;
-	struct warc_s *w;
+	struct warc *warc;
 	int r;
 
 	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
 	    ARCHIVE_STATE_NEW, "archive_read_support_format_warc");
 
-	if ((w = calloc(1, sizeof(*w))) == NULL) {
+	if ((warc = calloc(1, sizeof(*warc))) == NULL) {
 		archive_set_error(&a->archive, ENOMEM,
 		    "Can't allocate warc data");
 		return (ARCHIVE_FATAL);
 	}
 
-	r = __archive_read_register_format(
-		a, w, "warc",
-		_warc_bid, NULL, _warc_rdhdr, _warc_read,
-		_warc_skip, NULL, _warc_cleanup, NULL, NULL);
+	r = __archive_read_register_format(a,
+	    warc,
+	    "warc",
+	    archive_read_format_warc_bid,
+	    NULL,
+	    archive_read_format_warc_read_header,
+	    archive_read_format_warc_read_data,
+	    archive_read_format_warc_skip,
+	    NULL,
+	    archive_read_format_warc_cleanup,
+	    NULL,
+	    NULL);
 
 	if (r != ARCHIVE_OK) {
-		free(w);
+		free(warc);
 		return (r);
 	}
 	return (ARCHIVE_OK);
 }
 
 static int
-_warc_cleanup(struct archive_read *a)
+archive_read_format_warc_cleanup(struct archive_read *a)
 {
-	struct warc_s *w = a->format->data;
+	struct warc *warc = a->format->data;
 
-	if (w->pool.len > 0U) {
-		free(w->pool.str);
+	if (warc->pool.len > 0U) {
+		free(warc->pool.str);
 	}
-	archive_string_free(&w->sver);
-	free(w);
+	archive_string_free(&warc->sver);
+	free(warc);
 	a->format->data = NULL;
 	return (ARCHIVE_OK);
 }
 
 static int
-_warc_bid(struct archive_read *a, int best_bid)
+archive_read_format_warc_bid(struct archive_read *a, int best_bid)
 {
 	const char *hdr;
 	ssize_t nrd;
@@ -186,55 +198,52 @@
 
 	(void)best_bid; /* UNUSED */
 
-	/* check first line of file, it should be a record already */
-	if ((hdr = __archive_read_ahead(a, 12U, &nrd)) == NULL) {
-		/* no idea what to do */
-		return -1;
-	} else if (nrd < 12) {
-		/* nah, not for us, our magic cookie is at least 12 bytes */
+	/* Check the first line, which should already be a record header. */
+	if ((hdr = __archive_read_ahead(a, 12, &nrd)) == NULL) {
+		/* Not enough data to identify this format. */
 		return -1;
 	}
 
-	/* otherwise snarf the record's version number */
-	ver = _warc_rdver(hdr, nrd);
+	/* Parse the record version number. */
+	ver = warc_read_version(hdr, nrd);
 	if (ver < 1200U || ver > 10000U) {
-		/* we only support WARC 0.12 to 1.0 */
+		/* Only WARC 0.12 through WARC 1.0 are supported. */
 		return -1;
 	}
 
-	/* otherwise be confident */
+	/* WARC magic and version checks passed. */
 	return (64);
 }
 
 static int
-_warc_rdhdr(struct archive_read *a, struct archive_entry *entry)
+archive_read_format_warc_read_header(struct archive_read *a,
+    struct archive_entry *entry)
 {
 #define HDR_PROBE_LEN		(12U)
-	struct warc_s *w = a->format->data;
+	struct warc *warc = a->format->data;
 	unsigned int ver;
 	const char *buf;
 	ssize_t nrd;
 	const char *eoh;
 	char *tmp;
-	/* for the file name, saves some strndup()'ing */
+	/* Reuse the header buffer while parsing the file name. */
 	warc_string_t fnam;
-	/* warc record type, not that we really use it a lot */
+	/* WARC record type */
 	warc_type_t ftyp;
-	/* content-length+error monad */
-	ssize_t cntlen;
-	/* record time is the WARC-Date time we reinterpret it as ctime */
+	/* Content length, or a negative error indicator */
+	int64_t cntlen;
+	/* WARC-Date is exposed as the entry ctime. */
 	time_t rtime;
-	/* mtime is the Last-Modified time which will be the entry's mtime */
+	/* A Last-Modified record header is exposed as the entry mtime. */
 	time_t mtime;
 
 start_over:
-	/* just use read_ahead() they keep track of unconsumed
-	 * bits and bobs for us; no need to put an extra shift in
-	 * and reproduce that functionality here */
+	/* Use read_ahead(); it already tracks unconsumed bytes, so this
+	 * reader does not need a separate shift buffer. */
 	buf = __archive_read_ahead(a, HDR_PROBE_LEN, &nrd);
 
 	if (nrd < 0) {
-		/* no good */
+		/* I/O or stream error. */
 		archive_set_error(
 			&a->archive, ARCHIVE_ERRNO_MISC,
 			"Bad record header");
@@ -244,19 +253,17 @@
 		 * must be EOF therefore */
 		return (ARCHIVE_EOF);
 	}
- 	/* looks good so far, try and find the end of the header now */
-	eoh = _warc_find_eoh(buf, nrd);
+	/* Locate the end of the record header. */
+	eoh = warc_find_eoh(buf, nrd);
 	if (eoh == NULL) {
-		/* still no good, the header end might be beyond the
-		 * probe we've requested, but then again who'd cram
-		 * so much stuff into the header *and* be 28500-compliant */
+		/* The header terminator was not found in the probed data. */
 		archive_set_error(
 			&a->archive, ARCHIVE_ERRNO_MISC,
 			"Bad record header");
 		return (ARCHIVE_FATAL);
 	}
-	ver = _warc_rdver(buf, eoh - buf);
-	/* we currently support WARC 0.12 to 1.0 */
+	ver = warc_read_version(buf, eoh - buf);
+	/* Only WARC 0.12 through WARC 1.0 are supported. */
 	if (ver == 0U) {
 		archive_set_error(
 			&a->archive, ARCHIVE_ERRNO_MISC,
@@ -269,78 +276,73 @@
 			ver / 10000, (ver % 10000) / 100);
 		return (ARCHIVE_FATAL);
 	}
-	cntlen = _warc_rdlen(buf, eoh - buf);
+	cntlen = warc_read_length(buf, eoh - buf);
 	if (cntlen < 0) {
-		/* nightmare!  the specs say content-length is mandatory
-		 * so I don't feel overly bad stopping the reader here */
+		/* This reader requires Content-Length before processing a record. */
 		archive_set_error(
 			&a->archive, EINVAL,
 			"Bad content length");
 		return (ARCHIVE_FATAL);
 	}
-	rtime = _warc_rdrtm(buf, eoh - buf);
+	rtime = warc_read_date(buf, eoh - buf);
 	if (rtime == (time_t)-1) {
-		/* record time is mandatory as per WARC/1.0,
-		 * so just barf here, fast and loud */
+		/* This reader requires WARC-Date before processing a record. */
 		archive_set_error(
 			&a->archive, EINVAL,
 			"Bad record time");
 		return (ARCHIVE_FATAL);
 	}
 
-	/* let the world know we're a WARC archive */
+	/* Report this archive as WARC. */
 	a->archive.archive_format = ARCHIVE_FORMAT_WARC;
-	if (ver != w->pver) {
-		/* stringify this entry's version */
-		archive_string_sprintf(&w->sver,
+	if (ver != warc->pver) {
+		/* Format this entry's WARC version. */
+		archive_string_sprintf(&warc->sver,
 			"WARC/%u.%u", ver / 10000, (ver % 10000) / 100);
-		/* remember the version */
-		w->pver = ver;
+		/* Remember the version for later entries. */
+		warc->pver = ver;
 	}
-	/* start off with the type */
-	ftyp = _warc_rdtyp(buf, eoh - buf);
-	/* and let future calls know about the content */
-	w->cntlen = cntlen;
-	w->cntoff = 0U;
-	mtime = 0;/* Avoid compiling error on some platform. */
+	/* Parse the record type. */
+	ftyp = warc_read_type(buf, eoh - buf);
+	/* Save content state for subsequent read calls. */
+	warc->cntlen = cntlen;
+	warc->cntoff = 0;
+	mtime = 0;/* Avoid compiler warnings on some platforms. */
 
 	switch (ftyp) {
 	case WT_RSRC:
 	case WT_RSP:
-		/* only try and read the filename in the cases that are
-		 * guaranteed to have one */
-		fnam = _warc_rduri(buf, eoh - buf);
-		/* check the last character in the URI to avoid creating
-		 * directory endpoints as files, see Todo above */
+		/* Read the filename only for record types that are expected to
+		 * have a target URI. */
+		fnam = warc_read_uri(buf, eoh - buf);
+		/* Avoid creating directory endpoints as files. */
 		if (fnam.len == 0 || fnam.str[fnam.len - 1] == '/') {
-			/* break here for now */
+			/* Skip this record. */
 			fnam.len = 0U;
 			fnam.str = NULL;
 			break;
 		}
-		/* bang to our string pool, so we save a
-		 * malloc()+free() roundtrip */
-		if (fnam.len + 1U > w->pool.len) {
-			w->pool.len = ((fnam.len + 64U) / 64U) * 64U;
-			tmp = realloc(w->pool.str, w->pool.len);
+		/* Copy the name into the reusable string pool to avoid a malloc/free
+		 * roundtrip for each entry. */
+		if (fnam.len + 1U > warc->pool.len) {
+			warc->pool.len = ((fnam.len + 64U) / 64U) * 64U;
+			tmp = realloc(warc->pool.str, warc->pool.len);
 			if (tmp == NULL) {
 				archive_set_error(
 					&a->archive, ENOMEM,
 					"Out of memory");
 				return (ARCHIVE_FATAL);
 			}
-			w->pool.str = tmp;
+			warc->pool.str = tmp;
 		}
-		memcpy(w->pool.str, fnam.str, fnam.len);
-		w->pool.str[fnam.len] = '\0';
-		/* let no one else know about the pool, it's a secret, shhh */
-		fnam.str = w->pool.str;
+		memcpy(warc->pool.str, fnam.str, fnam.len);
+		warc->pool.str[fnam.len] = '\0';
+		/* Hide the pool implementation behind the parsed string. */
+		fnam.str = warc->pool.str;
 
-		/* snarf mtime or deduce from rtime
-		 * this is a custom header added by our writer, it's quite
-		 * hard to believe anyone else would go through with it
-		 * (apart from being part of some http responses of course) */
-		if ((mtime = _warc_rdmtm(buf, eoh - buf)) == (time_t)-1) {
+		/* Use a Last-Modified record header when present; otherwise fall back
+		 * to WARC-Date. */
+		if ((mtime = warc_read_last_modified(buf, eoh - buf)) == (time_t)-1) {
 			mtime = rtime;
 		}
 		break;
@@ -358,19 +360,19 @@
 		break;
 	}
 
-	/* now eat some of those delicious buffer bits */
+	/* Consume the record header. */
 	__archive_read_consume(a, eoh - buf);
 
 	switch (ftyp) {
 	case WT_RSRC:
 	case WT_RSP:
 		if (fnam.len > 0U) {
-			/* populate entry object */
+			/* Populate the entry object. */
 			archive_entry_set_filetype(entry, AE_IFREG);
 			archive_entry_copy_pathname(entry, fnam.str);
 			archive_entry_set_size(entry, cntlen);
 			archive_entry_set_perm(entry, 0644);
-			/* rtime is the new ctime, mtime stays mtime */
+			/* WARC-Date becomes ctime; mtime comes from Last-Modified or WARC-Date. */
 			archive_entry_set_ctime(entry, rtime, 0L);
 			archive_entry_set_mtime(entry, mtime, 0L);
 			break;
@@ -385,8 +387,8 @@
 	case WT_CONT:
 	case LAST_WT:
 	default:
-		/* consume the content and start over */
-		if (_warc_skip(a) < 0)
+		/* Skip this record body and look for the next one. */
+		if (archive_read_format_warc_skip(a) < 0)
 			return (ARCHIVE_FATAL);
 		goto start_over;
 	}
@@ -394,62 +396,69 @@
 }
 
 static int
-_warc_read(struct archive_read *a, const void **buf, size_t *bsz, int64_t *off)
+archive_read_format_warc_read_data(struct archive_read *a, const void **buf,
+    size_t *bsz, int64_t *off)
 {
-	struct warc_s *w = a->format->data;
+	struct warc *warc = a->format->data;
 	const char *rab;
 	ssize_t nrd;
 
-	if (w->cntoff >= w->cntlen) {
-	eof:
-		/* it's our lucky day, no work, we can leave early */
+	if (warc->unconsumed) {
+		__archive_read_consume(a, warc->unconsumed);
+		warc->unconsumed = 0;
+	}
+
+	if (warc->cntoff >= warc->cntlen) {
+		/* No data is available to return for this entry. */
 		*buf = NULL;
 		*bsz = 0U;
-		*off = w->cntoff;
-		w->unconsumed = 0U;
+		*off = warc->cntoff;
 		return (ARCHIVE_EOF);
 	}
 
-	if (w->unconsumed) {
-		__archive_read_consume(a, w->unconsumed);
-		w->unconsumed = 0U;
-	}
-
 	rab = __archive_read_ahead(a, 1U, &nrd);
 	if (nrd < 0) {
 		*bsz = 0U;
-		/* big catastrophe */
+		/* Propagate the read error. */
 		return (int)nrd;
 	} else if (nrd == 0) {
-		goto eof;
-	} else if ((size_t)nrd > w->cntlen - w->cntoff) {
-		/* clamp to content-length */
-		nrd = w->cntlen - w->cntoff;
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Truncated WARC file data");
+		return (ARCHIVE_FATAL);
+	} else if ((int64_t)nrd > warc->cntlen - warc->cntoff) {
+		/* Clamp reads to Content-Length. */
+		nrd = warc->cntlen - warc->cntoff;
 	}
-	*off = w->cntoff;
+	*off = warc->cntoff;
 	*bsz = nrd;
 	*buf = rab;
 
-	w->cntoff += nrd;
-	w->unconsumed = (size_t)nrd;
+	warc->cntoff += nrd;
+	warc->unconsumed = nrd;
 	return (ARCHIVE_OK);
 }
 
 static int
-_warc_skip(struct archive_read *a)
+archive_read_format_warc_skip(struct archive_read *a)
 {
-	struct warc_s *w = a->format->data;
+	struct warc *warc = a->format->data;
 
-	if (__archive_read_consume(a, w->cntlen) < 0 ||
+	if (warc->cntoff > warc->cntlen)
+		return (ARCHIVE_FATAL);
+	if (warc->unconsumed) {
+		__archive_read_consume(a, warc->unconsumed);
+		warc->unconsumed = 0;
+	}
+	if (__archive_read_consume(a, warc->cntlen - warc->cntoff) < 0 ||
 	    __archive_read_consume(a, 4U/*\r\n\r\n separator*/) < 0)
 		return (ARCHIVE_FATAL);
-	w->cntlen = 0U;
-	w->cntoff = 0U;
+	warc->cntlen = 0;
+	warc->cntoff = 0;
 	return (ARCHIVE_OK);
 }
 
 
-/* private routines */
+/* Private routines */
 static void*
 deconst(const void *c)
 {
@@ -469,44 +478,41 @@
 	unsigned int nsum;
 	unsigned int eqp;
 
-	/* trivial checks first
-         * a 0-sized needle is defined to be found anywhere in haystack
-         * then run strchr() to find a candidate in HAYSTACK (i.e. a portion
-         * that happens to begin with *NEEDLE) */
+	/* Handle trivial cases first.  A zero-sized needle is defined to be
+	 * found anywhere in the haystack; otherwise find the first candidate
+	 * that begins with *NEEDLE. */
 	if (needlesize == 0UL) {
 		return deconst(hay);
 	} else if ((hay = memchr(hay, *needle, haysize)) == NULL) {
-		/* trivial */
+		/* No candidate match remains. */
 		return NULL;
 	}
 
-	/* First characters of haystack and needle are the same now. Both are
-	 * guaranteed to be at least one character long.  Now computes the sum
-	 * of characters values of needle together with the sum of the first
-	 * needle_len characters of haystack. */
+	/* The first characters of haystack and needle already match, and both
+	 * strings are at least one character long.  Compute the rolling XOR
+	 * values for the needle and the first NEEDLESIZE characters of haystack. */
 	for (hp = hay + 1U, np = needle + 1U, hsum = *hay, nsum = *hay, eqp = 1U;
 	     hp < eoh && np < eon;
 	     hsum ^= *hp, nsum ^= *np, eqp &= *hp == *np, hp++, np++);
 
 	/* HP now references the (NEEDLESIZE + 1)-th character. */
 	if (np < eon) {
-		/* haystack is smaller than needle, :O */
+		/* The haystack is smaller than the needle. */
 		return NULL;
 	} else if (eqp) {
-		/* found a match */
+		/* Found a match. */
 		return deconst(hay);
 	}
 
-	/* now loop through the rest of haystack,
-	 * updating the sum iteratively */
+	/* Loop through the rest of the haystack and update the rolling XOR
+	 * iteratively. */
 	for (cand = hay; hp < eoh; hp++) {
 		hsum ^= *cand++;
 		hsum ^= *hp;
 
-		/* Since the sum of the characters is already known to be
-		 * equal at that point, it is enough to check just NEEDLESIZE - 1
-		 * characters for equality,
-		 * also CAND is by design < HP, so no need for range checks */
+		/* When the rolling XOR values match, it is enough to check
+		 * NEEDLESIZE - 1 characters for equality.  CAND is always before
+		 * HP by design, so no range check is needed. */
 		if (hsum == nsum && memcmp(cand, needle, needlesize - 1U) == 0) {
 			return deconst(cand);
 		}
@@ -519,7 +525,7 @@
 {
 	int res = 0;
 	const char *sp;
-	/* we keep track of the number of digits via rulim */
+	/* Track the number of digits with rulim. */
 	int rulim;
 
 	for (sp = str, rulim = ulim > 10 ? ulim : 10;
@@ -546,11 +552,11 @@
         /* Use platform timegm() if available. */
         return (timegm(t));
 #else
-        /* Else use direct calculation using POSIX assumptions. */
-        /* First, fix up tm_yday based on the year/month/day. */
+        /* Otherwise, calculate directly using POSIX assumptions. */
+        /* First, fix up tm_yday based on the year, month, and day. */
         if (mktime(t) == (time_t)-1)
                 return ((time_t)-1);
-        /* Then we can compute timegm() from first principles. */
+        /* Then compute timegm() from first principles. */
         return (t->tm_sec
             + t->tm_min * 60
             + t->tm_hour * 3600
@@ -565,48 +571,48 @@
 static time_t
 xstrpisotime(const char *s, char **endptr)
 {
-/** like strptime() but strictly for ISO 8601 Zulu strings */
+/* Like strptime(), but only for ISO 8601 Zulu strings. */
 	struct tm tm;
 	time_t res = (time_t)-1;
 
-	/* make sure tm is clean */
+	/* Clear the tm structure. */
 	memset(&tm, 0, sizeof(tm));
 
-	/* as a courtesy to our callers, and since this is a non-standard
-	 * routine, we skip leading whitespace */
+	/* This is a non-standard routine, so skip leading whitespace for
+	 * caller convenience. */
 	while (*s == ' ' || *s == '\t')
 		++s;
 
-	/* read year */
+	/* Read the year. */
 	if ((tm.tm_year = strtoi_lim(s, &s, 1583, 4095)) < 0 || *s++ != '-') {
 		goto out;
 	}
-	/* read month */
+	/* Read the month. */
 	if ((tm.tm_mon = strtoi_lim(s, &s, 1, 12)) < 0 || *s++ != '-') {
 		goto out;
 	}
-	/* read day-of-month */
+	/* Read the day of the month. */
 	if ((tm.tm_mday = strtoi_lim(s, &s, 1, 31)) < 0 || *s++ != 'T') {
 		goto out;
 	}
-	/* read hour */
+	/* Read the hour. */
 	if ((tm.tm_hour = strtoi_lim(s, &s, 0, 23)) < 0 || *s++ != ':') {
 		goto out;
 	}
-	/* read minute */
+	/* Read the minute. */
 	if ((tm.tm_min = strtoi_lim(s, &s, 0, 59)) < 0 || *s++ != ':') {
 		goto out;
 	}
-	/* read second */
+	/* Read the second. */
 	if ((tm.tm_sec = strtoi_lim(s, &s, 0, 60)) < 0 || *s++ != 'Z') {
 		goto out;
 	}
 
-	/* massage TM to fulfill some of POSIX' constraints */
+	/* Adjust tm fields to satisfy POSIX constraints. */
 	tm.tm_year -= 1900;
 	tm.tm_mon--;
 
-	/* now convert our custom tm struct to a unix stamp using UTC */
+	/* Convert the tm structure to a Unix timestamp in UTC. */
 	res = time_from_tm(&tm);
 
 out:
@@ -616,8 +622,14 @@
 	return res;
 }
 
+static int
+warc_isdigit(const char c)
+{
+	return c >= '0' && c <= '9';
+}
+
 static unsigned int
-_warc_rdver(const char *buf, size_t bsz)
+warc_read_version(const char *buf, size_t bsz)
 {
 	static const char magic[] = "WARC/";
 	const char *c;
@@ -625,35 +637,34 @@
 	unsigned int end = 0U;
 
 	if (bsz < 12 || memcmp(buf, magic, sizeof(magic) - 1U) != 0) {
-		/* buffer too small or invalid magic */
+		/* Buffer too small or invalid magic. */
 		return ver;
 	}
-	/* looks good so far, read the version number for a laugh */
+	/* Parse the version number. */
 	buf += sizeof(magic) - 1U;
 
-	if (isdigit((unsigned char)buf[0U]) && (buf[1U] == '.') &&
-	    isdigit((unsigned char)buf[2U])) {
-		/* we support a maximum of 2 digits in the minor version */
-		if (isdigit((unsigned char)buf[3U]))
+	if (warc_isdigit(buf[0]) && buf[1] == '.' && warc_isdigit(buf[2])) {
+		/* Support at most two digits in the minor version. */
+		if (warc_isdigit(buf[3]))
 			end = 1U;
-		/* set up major version */
+		/* Set up the major version. */
 		ver = (buf[0U] - '0') * 10000U;
-		/* set up minor version */
+		/* Set up the minor version. */
 		if (end == 1U) {
 			ver += (buf[2U] - '0') * 1000U;
 			ver += (buf[3U] - '0') * 100U;
 		} else
 			ver += (buf[2U] - '0') * 100U;
 		/*
-		 * WARC below version 0.12 has a space-separated header
-		 * WARC 0.12 and above terminates the version with a CRLF
+		 * WARC versions before 0.12 use a space-separated header.
+		 * WARC 0.12 and later terminate the version with CRLF.
 		 */
 		c = buf + 3U + end;
 		if (ver >= 1200U) {
 			if (memcmp(c, "\r\n", 2U) != 0)
 				ver = 0U;
 		} else {
-			/* ver < 1200U */
+			/* Version is below WARC 0.12. */
 			if (*c != ' ' && *c != '\t')
 				ver = 0U;
 		}
@@ -662,22 +673,22 @@
 }
 
 static unsigned int
-_warc_rdtyp(const char *buf, size_t bsz)
+warc_read_type(const char *buf, size_t bsz)
 {
 	static const char _key[] = "\r\nWARC-Type:";
 	const char *val, *eol;
 
 	if ((val = xmemmem(buf, bsz, _key, sizeof(_key) - 1U)) == NULL) {
-		/* no bother */
+		/* Header field is absent. */
 		return WT_NONE;
 	}
 	val += sizeof(_key) - 1U;
-	if ((eol = _warc_find_eol(val, buf + bsz - val)) == NULL) {
-		/* no end of line */
+	if ((eol = warc_find_eol(val, buf + bsz - val)) == NULL) {
+		/* Header field has no end of line. */
 		return WT_NONE;
 	}
 
-	/* overread whitespace */
+	/* Skip leading whitespace. */
 	while (val < eol && (*val == ' ' || *val == '\t'))
 		++val;
 
@@ -691,55 +702,55 @@
 }
 
 static warc_string_t
-_warc_rduri(const char *buf, size_t bsz)
+warc_read_uri(const char *buf, size_t bsz)
 {
 	static const char _key[] = "\r\nWARC-Target-URI:";
 	const char *val, *uri, *eol, *p;
 	warc_string_t res = {0U, NULL};
 
 	if ((val = xmemmem(buf, bsz, _key, sizeof(_key) - 1U)) == NULL) {
-		/* no bother */
+		/* Header field is absent. */
 		return res;
 	}
-	/* overread whitespace */
+	/* Skip leading whitespace. */
 	val += sizeof(_key) - 1U;
-	if ((eol = _warc_find_eol(val, buf + bsz - val)) == NULL) {
-		/* no end of line */
+	if ((eol = warc_find_eol(val, buf + bsz - val)) == NULL) {
+		/* Header field has no end of line. */
 		return res;
 	}
 
 	while (val < eol && (*val == ' ' || *val == '\t'))
 		++val;
 
-	/* overread URL designators */
+	/* Locate the :// separator. */
 	if ((uri = xmemmem(val, eol - val, "://", 3U)) == NULL) {
-		/* not touching that! */
+		/* Ignore values without a :// separator. */
 		return res;
 	}
 
-	/* spaces inside uri are not allowed, CRLF should follow */
+	/* Spaces inside a URI are not allowed; CRLF should follow. */
 	for (p = val; p < eol; p++) {
 		if (isspace((unsigned char)*p))
 			return res;
 	}
 
-	/* there must be at least space for ftp */
+	/* Require enough room for the shortest supported scheme. */
 	if (uri < (val + 3U))
 		return res;
 
-	/* move uri to point to after :// */
+	/* Move uri past the :// separator. */
 	uri += 3U;
 
-	/* now then, inspect the URI */
+	/* Inspect the scheme prefix. */
 	if (memcmp(val, "file", 4U) == 0) {
-		/* perfect, nothing left to do here */
+		/* Keep file:// paths as-is. */
 
 	} else if (memcmp(val, "http", 4U) == 0 ||
 		   memcmp(val, "ftp", 3U) == 0) {
-		/* overread domain, and the first / */
+		/* Skip the domain and the first slash. */
 		while (uri < eol && *uri++ != '/');
 	} else {
-		/* not sure what to do? best to bugger off */
+		/* Unsupported URI scheme. */
 		return res;
 	}
 	res.str = uri;
@@ -747,42 +758,49 @@
 	return res;
 }
 
-static ssize_t
-_warc_rdlen(const char *buf, size_t bsz)
+static int64_t
+warc_read_length(const char *buf, size_t bsz)
 {
 	static const char _key[] = "\r\nContent-Length:";
-	const char *val, *eol;
-	char *on = NULL;
-	long int len;
+	const char *val, *eol, *p;
+	int64_t len;
 
 	if ((val = xmemmem(buf, bsz, _key, sizeof(_key) - 1U)) == NULL) {
-		/* no bother */
+		/* Header field is absent. */
 		return -1;
 	}
 	val += sizeof(_key) - 1U;
-	if ((eol = _warc_find_eol(val, buf + bsz - val)) == NULL) {
-		/* no end of line */
+
+	if ((eol = warc_find_eol(val, buf + bsz - val)) == NULL) {
+		/* Malformed field with no end of line. */
 		return -1;
 	}
 
-	/* skip leading whitespace */
+	/* Skip leading whitespace. */
 	while (val < eol && (*val == ' ' || *val == '\t'))
 		val++;
-	/* there must be at least one digit */
-	if (!isdigit((unsigned char)*val))
-		return -1;
-	errno = 0;
-	len = strtol(val, &on, 10);
-	if (errno != 0 || on != eol) {
-		/* line must end here */
+
+	/* Require at least one digit. */
+	if (val >= eol || *val < '0' || *val > '9')
 		return -1;
+
+	len = 0;
+	for (p = val; p < eol; p++) {
+		int64_t digit;
+
+		if (*p < '0' || *p > '9')
+			return -1;
+		digit = *p - '0';
+		if (archive_ckd_mul_i64(&len, len, 10) ||
+		    archive_ckd_add_i64(&len, len, digit))
+			return -1;
 	}
 
-	return (size_t)len;
+	return len;
 }
 
 static time_t
-_warc_rdrtm(const char *buf, size_t bsz)
+warc_read_date(const char *buf, size_t bsz)
 {
 	static const char _key[] = "\r\nWARC-Date:";
 	const char *val, *eol;
@@ -790,26 +808,26 @@
 	time_t res;
 
 	if ((val = xmemmem(buf, bsz, _key, sizeof(_key) - 1U)) == NULL) {
-		/* no bother */
+		/* Header field is absent. */
 		return (time_t)-1;
 	}
 	val += sizeof(_key) - 1U;
-	if ((eol = _warc_find_eol(val, buf + bsz - val)) == NULL ) {
-		/* no end of line */
+	if ((eol = warc_find_eol(val, buf + bsz - val)) == NULL ) {
+		/* Header field has no end of line. */
 		return -1;
 	}
 
-	/* xstrpisotime() kindly overreads whitespace for us, so use that */
+	/* xstrpisotime() skips leading whitespace. */
 	res = xstrpisotime(val, &on);
 	if (on != eol) {
-		/* line must end here */
+		/* The field must end here. */
 		return -1;
 	}
 	return res;
 }
 
 static time_t
-_warc_rdmtm(const char *buf, size_t bsz)
+warc_read_last_modified(const char *buf, size_t bsz)
 {
 	static const char _key[] = "\r\nLast-Modified:";
 	const char *val, *eol;
@@ -817,26 +835,26 @@
 	time_t res;
 
 	if ((val = xmemmem(buf, bsz, _key, sizeof(_key) - 1U)) == NULL) {
-		/* no bother */
+		/* Header field is absent. */
 		return (time_t)-1;
 	}
 	val += sizeof(_key) - 1U;
-	if ((eol = _warc_find_eol(val, buf + bsz - val)) == NULL ) {
-		/* no end of line */
+	if ((eol = warc_find_eol(val, buf + bsz - val)) == NULL ) {
+		/* Header field has no end of line. */
 		return -1;
 	}
 
-	/* xstrpisotime() kindly overreads whitespace for us, so use that */
+	/* xstrpisotime() skips leading whitespace. */
 	res = xstrpisotime(val, &on);
 	if (on != eol) {
-		/* line must end here */
+		/* The field must end here. */
 		return -1;
 	}
 	return res;
 }
 
-static const char*
-_warc_find_eoh(const char *buf, size_t bsz)
+static const char *
+warc_find_eoh(const char *buf, size_t bsz)
 {
 	static const char _marker[] = "\r\n\r\n";
 	const char *hit = xmemmem(buf, bsz, _marker, sizeof(_marker) - 1U);
@@ -847,12 +865,11 @@
 	return hit;
 }
 
-static const char*
-_warc_find_eol(const char *buf, size_t bsz)
+static const char *
+warc_find_eol(const char *buf, size_t bsz)
 {
 	static const char _marker[] = "\r\n";
 	const char *hit = xmemmem(buf, bsz, _marker, sizeof(_marker) - 1U);
 
 	return hit;
 }
-/* archive_read_support_format_warc.c ends here */
diff --git a/c/archive_read_support_format_xar.c b/c/archive_read_support_format_xar.c
--- a/c/archive_read_support_format_xar.c
+++ b/c/archive_read_support_format_xar.c
@@ -108,10 +108,14 @@
 #define CKSUM_NONE	0
 #define CKSUM_SHA1	1
 #define CKSUM_MD5	2
+#define CKSUM_SHA256	3
+#define CKSUM_SHA512	4
 
 #define MD5_SIZE	16
 #define SHA1_SIZE	20
-#define MAX_SUM_SIZE	20
+#define SHA256_SIZE	32
+#define SHA512_SIZE	64
+#define MAX_SUM_SIZE	64
 
 enum enctype {
 	NONE,
@@ -135,6 +139,12 @@
 #ifdef ARCHIVE_HAS_SHA1
 	archive_sha1_ctx	 sha1ctx;
 #endif
+#ifdef ARCHIVE_HAS_SHA256
+	archive_sha256_ctx	 sha256ctx;
+#endif
+#ifdef ARCHIVE_HAS_SHA512
+	archive_sha512_ctx	 sha512ctx;
+#endif
 };
 
 struct xattr {
@@ -214,8 +224,8 @@
 
 struct heap_queue {
 	struct xar_file		**files;
-	int			 allocated;
-	int			 used;
+	size_t			 allocated;
+	size_t			 used;
 };
 
 enum xmlstatus {
@@ -396,7 +406,7 @@
 static struct xar_file *heap_get_entry(struct heap_queue *);
 static int	add_link(struct archive_read *,
     struct xar *, struct xar_file *);
-static void	checksum_init(struct archive_read *, int, int);
+static int	checksum_init(struct archive_read *, int, int);
 static void	checksum_update(struct archive_read *, const void *,
 		    size_t, const void *, size_t);
 static int	checksum_final(struct archive_read *, const void *,
@@ -524,6 +534,8 @@
 	case CKSUM_NONE:
 	case CKSUM_SHA1:
 	case CKSUM_MD5:
+	case CKSUM_SHA256:
+	case CKSUM_SHA512:
 		bid += 32;
 		break;
 	default:
@@ -536,7 +548,7 @@
 static int
 read_toc(struct archive_read *a)
 {
-	struct xar *xar;
+	struct xar *xar = a->format->data;
 	struct xar_file *file;
 	const unsigned char *b;
 	uint64_t toc_compressed_size;
@@ -545,8 +557,6 @@
 	ssize_t bytes;
 	int r;
 
-	xar = (struct xar *)(a->format->data);
-
 	/*
 	 * Read xar header.
 	 */
@@ -674,12 +684,11 @@
 static int
 xar_read_header(struct archive_read *a, struct archive_entry *entry)
 {
-	struct xar *xar;
+	struct xar *xar = a->format->data;
 	struct xar_file *file;
 	struct xattr *xattr;
 	int r;
 
-	xar = (struct xar *)(a->format->data);
 	r = ARCHIVE_OK;
 
 	if (xar->offset == 0) {
@@ -729,6 +738,7 @@
 		if (errno == ENOMEM) {
 			archive_set_error(&a->archive, ENOMEM,
 			    "Can't allocate memory for Gname");
+			file_free(file);
 			return (ARCHIVE_FATAL);
 		}
 		archive_set_error(&a->archive,
@@ -744,6 +754,7 @@
 		if (errno == ENOMEM) {
 			archive_set_error(&a->archive, ENOMEM,
 			    "Can't allocate memory for Uname");
+			file_free(file);
 			return (ARCHIVE_FATAL);
 		}
 		archive_set_error(&a->archive,
@@ -758,6 +769,7 @@
 		if (errno == ENOMEM) {
 			archive_set_error(&a->archive, ENOMEM,
 			    "Can't allocate memory for Pathname");
+			file_free(file);
 			return (ARCHIVE_FATAL);
 		}
 		archive_set_error(&a->archive,
@@ -774,6 +786,7 @@
 		if (errno == ENOMEM) {
 			archive_set_error(&a->archive, ENOMEM,
 			    "Can't allocate memory for Linkname");
+			file_free(file);
 			return (ARCHIVE_FATAL);
 		}
 		archive_set_error(&a->archive,
@@ -874,12 +887,10 @@
 xar_read_data(struct archive_read *a,
     const void **buff, size_t *size, int64_t *offset)
 {
-	struct xar *xar;
+	struct xar *xar = a->format->data;
 	size_t used = 0;
 	int r;
 
-	xar = (struct xar *)(a->format->data);
-
 	if (xar->entry_unconsumed) {
 		__archive_read_consume(a, xar->entry_unconsumed);
 		xar->entry_unconsumed = 0;
@@ -937,10 +948,9 @@
 static int
 xar_read_data_skip(struct archive_read *a)
 {
-	struct xar *xar;
+	struct xar *xar = a->format->data;
 	int64_t bytes_skipped;
 
-	xar = (struct xar *)(a->format->data);
 	if (xar->end_of_file)
 		return (ARCHIVE_EOF);
 	bytes_skipped = __archive_read_consume(a, xar->entry_remaining +
@@ -955,12 +965,11 @@
 static int
 xar_cleanup(struct archive_read *a)
 {
-	struct xar *xar;
+	struct xar *xar = a->format->data;
 	struct hdlink *hdlink;
-	int i;
+	size_t i;
 	int r;
 
-	xar = (struct xar *)(a->format->data);
 	checksum_cleanup(a);
 	r = decompression_cleanup(a);
 	hdlink = xar->hdlink_list;
@@ -990,9 +999,8 @@
 static int
 move_reading_point(struct archive_read *a, uint64_t offset)
 {
-	struct xar *xar;
+	struct xar *xar = a->format->data;
 
-	xar = (struct xar *)(a->format->data);
 	if (xar->offset - xar->h_base != offset) {
 		/* Seek forward to the start of file contents. */
 		int64_t step;
@@ -1027,8 +1035,7 @@
 	if ((r = decompression_init(a, encoding)) != ARCHIVE_OK)
 		return (r);
 	/* Init checksum library. */
-	checksum_init(a, a_sum_alg, e_sum_alg);
-	return (ARCHIVE_OK);
+	return (checksum_init(a, a_sum_alg, e_sum_alg));
 }
 
 static int
@@ -1077,16 +1084,14 @@
 	uint64_t l;
 
 	l = 0;
-	if (char_cnt > 0) {
-		int digit;
+	while (char_cnt-- > 0) {
+		int digit = *p++ - '0';
 
-		digit = *p - '0';
-		while (digit >= 0 && digit < base && char_cnt-- > 0) {
-			if (archive_ckd_mul_u64(&l, l, base) ||
-			    archive_ckd_add_u64(&l, l, digit))
-				return (ARCHIVE_FATAL);
-			digit = *++p - '0';
-		}
+		if (digit < 0 || digit >= base)
+			break;
+		if (archive_ckd_mul_u64(&l, l, base) ||
+		    archive_ckd_add_u64(&l, l, digit))
+			return (ARCHIVE_FATAL);
 	}
 
 	*val = l;
@@ -1166,31 +1171,31 @@
 	p += 4;
 	if (*p++ != '-')
 		return (t);
-	if (atou64(p, 4, 10, &data) != ARCHIVE_OK || data < 1 || data > 12)
+	if (atou64(p, 2, 10, &data) != ARCHIVE_OK || data < 1 || data > 12)
 		return (t);
 	tm.tm_mon = (int)data -1;
 	p += 2;
 	if (*p++ != '-')
 		return (t);
-	if (atou64(p, 4, 10, &data) != ARCHIVE_OK || data < 1 || data > 31)
+	if (atou64(p, 2, 10, &data) != ARCHIVE_OK || data < 1 || data > 31)
 		return (t);
 	tm.tm_mday = (int)data;
 	p += 2;
 	if (*p++ != 'T')
 		return (t);
-	if (atou64(p, 4, 10, &data) != ARCHIVE_OK || data > 23)
+	if (atou64(p, 2, 10, &data) != ARCHIVE_OK || data > 23)
 		return (t);
 	tm.tm_hour = (int)data;
 	p += 2;
 	if (*p++ != ':')
 		return (t);
-	if (atou64(p, 4, 10, &data) != ARCHIVE_OK || data > 59)
+	if (atou64(p, 2, 10, &data) != ARCHIVE_OK || data > 59)
 		return (t);
 	tm.tm_min = (int)data;
 	p += 2;
 	if (*p++ != ':')
 		return (t);
-	if (atou64(p, 4, 10, &data) != ARCHIVE_OK || data > 60)
+	if (atou64(p, 2, 10, &data) != ARCHIVE_OK || data > 60)
 		return (t);
 	tm.tm_sec = (int)data;
 #if 0
@@ -1209,19 +1214,17 @@
     struct heap_queue *heap, struct xar_file *file)
 {
 	uint64_t file_id, parent_id;
-	int hole, parent;
+	size_t hole, parent;
 
 	/* Expand our pending files list as necessary. */
 	if (heap->used >= heap->allocated) {
 		struct xar_file **new_pending_files;
-		int new_size;
+		size_t new_size;
 
 		if (heap->allocated < 1024)
 			new_size = 1024;
-		else
-			new_size = heap->allocated * 2;
-		/* Overflow might keep us from growing the list. */
-		if (new_size <= heap->allocated) {
+		else if (archive_ckd_mul_size(&new_size, heap->allocated, 2)) {
+			/* Overflow keeps us from growing the list. */
 			archive_set_error(&a->archive,
 			    ENOMEM, "Out of memory");
 			return (ARCHIVE_FATAL);
@@ -1249,7 +1252,7 @@
 	 */
 	hole = heap->used++;
 	while (hole > 0) {
-		parent = (hole - 1)/2;
+		parent = (hole - 1) / 2;
 		parent_id = heap->files[parent]->id;
 		if (file_id >= parent_id) {
 			heap->files[hole] = file;
@@ -1268,7 +1271,7 @@
 heap_get_entry(struct heap_queue *heap)
 {
 	uint64_t a_id, b_id, c_id;
-	int a, b, c;
+	size_t a, b, c;
 	struct xar_file *r, *tmp;
 
 	if (heap->used < 1)
@@ -1338,7 +1341,7 @@
 	return (ARCHIVE_OK);
 }
 
-static void
+static int
 _checksum_init(struct chksumwork *sumwrk, int sum_alg)
 {
 	sumwrk->alg = sum_alg;
@@ -1351,7 +1354,20 @@
 	case CKSUM_MD5:
 		archive_md5_init(&(sumwrk->md5ctx));
 		break;
+#ifdef ARCHIVE_HAS_SHA256
+	case CKSUM_SHA256:
+		archive_sha256_init(&(sumwrk->sha256ctx));
+		break;
+#endif
+#ifdef ARCHIVE_HAS_SHA512
+	case CKSUM_SHA512:
+		archive_sha512_init(&(sumwrk->sha512ctx));
+		break;
+#endif
+	default:
+		return (ARCHIVE_FATAL);
 	}
+	return (ARCHIVE_OK);
 }
 
 static void
@@ -1367,6 +1383,16 @@
 	case CKSUM_MD5:
 		archive_md5_update(&(sumwrk->md5ctx), buff, size);
 		break;
+#ifdef ARCHIVE_HAS_SHA256
+	case CKSUM_SHA256:
+		archive_sha256_update(&(sumwrk->sha256ctx), buff, size);
+		break;
+#endif
+#ifdef ARCHIVE_HAS_SHA512
+	case CKSUM_SHA512:
+		archive_sha512_update(&(sumwrk->sha512ctx), buff, size);
+		break;
+#endif
 	}
 }
 
@@ -1391,27 +1417,48 @@
 		    memcmp(val, sum, MD5_SIZE) != 0)
 			r = ARCHIVE_FAILED;
 		break;
+#ifdef ARCHIVE_HAS_SHA256
+	case CKSUM_SHA256:
+		archive_sha256_final(&(sumwrk->sha256ctx), sum);
+		if (len != SHA256_SIZE ||
+		    memcmp(val, sum, SHA256_SIZE) != 0)
+			r = ARCHIVE_FAILED;
+		break;
+#endif
+#ifdef ARCHIVE_HAS_SHA512
+	case CKSUM_SHA512:
+		archive_sha512_final(&(sumwrk->sha512ctx), sum);
+		if (len != SHA512_SIZE ||
+		    memcmp(val, sum, SHA512_SIZE) != 0)
+			r = ARCHIVE_FAILED;
+		break;
+#endif
+	default:
+		r = ARCHIVE_FAILED;
 	}
 	return (r);
 }
 
-static void
+static int
 checksum_init(struct archive_read *a, int a_sum_alg, int e_sum_alg)
 {
-	struct xar *xar;
+	struct xar *xar = a->format->data;
 
-	xar = (struct xar *)(a->format->data);
-	_checksum_init(&(xar->a_sumwrk), a_sum_alg);
-	_checksum_init(&(xar->e_sumwrk), e_sum_alg);
+	if (_checksum_init(&(xar->a_sumwrk), a_sum_alg) != ARCHIVE_OK ||
+	    _checksum_init(&(xar->e_sumwrk), e_sum_alg) != ARCHIVE_OK) {
+		archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
+		    "Unsupported checksum");
+		return (ARCHIVE_FATAL);
+	}
+	return (ARCHIVE_OK);
 }
 
 static void
 checksum_update(struct archive_read *a, const void *abuff, size_t asize,
     const void *ebuff, size_t esize)
 {
-	struct xar *xar;
+	struct xar *xar = a->format->data;
 
-	xar = (struct xar *)(a->format->data);
 	_checksum_update(&(xar->a_sumwrk), abuff, asize);
 	_checksum_update(&(xar->e_sumwrk), ebuff, esize);
 }
@@ -1420,27 +1467,24 @@
 checksum_final(struct archive_read *a, const void *a_sum_val,
     size_t a_sum_len, const void *e_sum_val, size_t e_sum_len)
 {
-	struct xar *xar;
-	int r;
+	struct xar *xar = a->format->data;
 
-	xar = (struct xar *)(a->format->data);
-	r = _checksum_final(&(xar->a_sumwrk), a_sum_val, a_sum_len);
-	if (r == ARCHIVE_OK)
-		r = _checksum_final(&(xar->e_sumwrk), e_sum_val, e_sum_len);
-	if (r != ARCHIVE_OK)
+	if (_checksum_final(&(xar->a_sumwrk), a_sum_val, a_sum_len) != ARCHIVE_OK ||
+	    _checksum_final(&(xar->e_sumwrk), e_sum_val, e_sum_len) != ARCHIVE_OK) {
 		archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
-		    "Sumcheck error");
-	return (r);
+		    "Checksum error");
+		return (ARCHIVE_FATAL);
+	}
+	return (ARCHIVE_OK);
 }
 
 static int
 decompression_init(struct archive_read *a, enum enctype encoding)
 {
-	struct xar *xar;
+	struct xar *xar = a->format->data;
 	const char *detail;
 	int r;
 
-	xar = (struct xar *)(a->format->data);
 	xar->rd_encoding = encoding;
 	switch (encoding) {
 	case NONE:
@@ -1576,12 +1620,11 @@
 decompress(struct archive_read *a, const void **buff, size_t *outbytes,
     const void *b, size_t *used)
 {
-	struct xar *xar;
+	struct xar *xar = a->format->data;
 	void *outbuff;
 	size_t avail_in, avail_out;
 	int r;
 
-	xar = (struct xar *)(a->format->data);
 	avail_in = *used;
 	outbuff = (void *)(uintptr_t)*buff;
 	if (outbuff == NULL) {
@@ -1704,10 +1747,9 @@
 static int
 decompression_cleanup(struct archive_read *a)
 {
-	struct xar *xar;
+	struct xar *xar = a->format->data;
 	int r;
 
-	xar = (struct xar *)(a->format->data);
 	r = ARCHIVE_OK;
 	if (xar->stream_valid) {
 		if (inflateEnd(&(xar->stream)) != Z_OK) {
@@ -1736,9 +1778,7 @@
 
 static void
 checksum_cleanup(struct archive_read *a) {
-	struct xar *xar;
-
-	xar = (struct xar *)(a->format->data);
+	struct xar *xar = a->format->data;
 
 	_checksum_final(&(xar->a_sumwrk), NULL, 0);
 	_checksum_final(&(xar->e_sumwrk), NULL, 0);
@@ -1791,8 +1831,11 @@
 	}
 	xar->file = file;
 	file->nlink = 1;
-	if (heap_add_entry(a, &(xar->file_queue), file) != ARCHIVE_OK)
+	if (heap_add_entry(a, &(xar->file_queue), file) != ARCHIVE_OK) {
+		xar->file = file->parent;
+		file_free(file);
 		return (ARCHIVE_FATAL);
+	}
 	return (ARCHIVE_OK);
 }
 
@@ -1897,9 +1940,16 @@
 			const char *v = attr->value;
 			if ((v[0] == 'S' || v[0] == 's') &&
 			    (v[1] == 'H' || v[1] == 'h') &&
-			    (v[2] == 'A' || v[2] == 'a') &&
-			    v[3] == '1' && v[4] == '\0')
-				alg = CKSUM_SHA1;
+			    (v[2] == 'A' || v[2] == 'a')) {
+				if (v[3] == '1' && v[4] == '\0')
+					alg = CKSUM_SHA1;
+				else if (v[3] == '2' && v[4] == '5' &&
+				    v[5] == '6' && v[6] == '\0')
+					alg = CKSUM_SHA256;
+				else if (v[3] == '5' && v[4] == '1' &&
+				    v[5] == '2' && v[6] == '\0')
+					alg = CKSUM_SHA512;
+			}
 			if ((v[0] == 'M' || v[0] == 'm') &&
 			    (v[1] == 'D' || v[1] == 'd') &&
 			    v[2] == '5' && v[3] == '\0')
@@ -1957,11 +2007,9 @@
 static int
 xml_start(struct archive_read *a, const char *name, struct xmlattr_list *list)
 {
-	struct xar *xar;
+	struct xar *xar = a->format->data;
 	struct xmlattr *attr;
 
-	xar = (struct xar *)(a->format->data);
-
 #if DEBUG
 	fprintf(stderr, "xml_sta:[%s]\n", name);
 	for (attr = list->first; attr != NULL; attr = attr->next)
@@ -2253,11 +2301,8 @@
 static void
 xml_end(void *userData, const char *name)
 {
-	struct archive_read *a;
-	struct xar *xar;
-
-	a = (struct archive_read *)userData;
-	xar = (struct xar *)(a->format->data);
+	struct archive_read *a = (struct archive_read *)userData;
+	struct xar *xar = a->format->data;
 
 #if DEBUG
 	fprintf(stderr, "xml_end:[%s]\n", name);
@@ -2631,7 +2676,7 @@
 	len = 0;
 	out = buff;
 	b = (const unsigned char *)s;
-	while (l > 0) {
+	while (l > 1) {
 		int n = 0;
 
 		if (base64[b[0]] < 0 || base64[b[1]] < 0)
@@ -2679,14 +2724,11 @@
 static int
 xml_data(void *userData, const char *s, size_t len)
 {
+	struct archive_read *a = (struct archive_read *)userData;
+	struct xar *xar = a->format->data;
 	uint64_t val;
-	struct archive_read *a;
-	struct xar *xar;
 	int r;
 
-	a = (struct archive_read *)userData;
-	xar = (struct xar *)(a->format->data);
-
 #if DEBUG
 	{
 		char buff[1024];
@@ -3166,16 +3208,13 @@
 static int
 xml2_read_cb(void *context, char *buffer, int len)
 {
-	struct archive_read *a;
-	struct xar *xar;
+	struct archive_read *a = (struct archive_read *)context;
+	struct xar *xar = a->format->data;
 	const void *d;
 	size_t outbytes;
 	size_t used = 0;
 	int r;
 
-	a = (struct archive_read *)context;
-	xar = (struct xar *)(a->format->data);
-
 	if (xar->toc_remaining <= 0)
 		return (0);
 	d = buffer;
@@ -3204,10 +3243,9 @@
 xml2_error_hdr(void *arg, const char *msg, xmlParserSeverities severity,
     xmlTextReaderLocatorPtr locator)
 {
-	struct archive_read *a;
+	struct archive_read *a = (struct archive_read *)arg;
 
 	(void)locator; /* UNUSED */
-	a = (struct archive_read *)arg;
 	switch (severity) {
 	case XML_PARSER_SEVERITY_VALIDITY_WARNING:
 	case XML_PARSER_SEVERITY_WARNING:
@@ -3357,15 +3395,13 @@
 static int
 expat_read_toc(struct archive_read *a)
 {
-	struct xar *xar;
+	struct xar *xar = a->format->data;
 	XML_Parser parser;
 	struct expat_userData ud;
 
 	ud.state = ARCHIVE_OK;
 	ud.archive = a;
 
-	xar = (struct xar *)(a->format->data);
-
 	/* Initialize XML Parser library. */
 	parser = XML_ParserCreate(NULL);
 	if (parser == NULL) {
@@ -3449,15 +3485,12 @@
 asaRead(ISequentialStream *this, void *pv, ULONG cb, ULONG *pcbRead)
 {
 	struct ArchiveStreamAdapter *asa = (struct ArchiveStreamAdapter *)this;
-	struct archive_read *a;
-	struct xar *xar;
+	struct archive_read *a = asa->a;
+	struct xar *xar = a->format->data;
 	const void *d = pv;
 	size_t outbytes = cb;
 	size_t used = 0;
 	int r;
-
-	a = asa->a;
-	xar = (struct xar *)(a->format->data);
 
 	*pcbRead = 0;
 
diff --git a/c/archive_read_support_format_zip.c b/c/archive_read_support_format_zip.c
--- a/c/archive_read_support_format_zip.c
+++ b/c/archive_read_support_format_zip.c
@@ -152,8 +152,6 @@
 	struct archive_string	format_name;
 	int64_t			central_directory_offset;
 	int64_t			central_directory_offset_adjusted;
-	size_t			central_directory_entries_total;
-	size_t			central_directory_entries_on_this_disk;
 	int			has_encrypted_entries;
 
 	/* List of entries (seekable Zip only) */
@@ -261,6 +259,15 @@
 /* Many systems define min or MIN, but not all. */
 #define	zipmin(a,b) ((a) < (b) ? (a) : (b))
 
+/* True if this entry has declared a specific uncompressed size. */
+static int
+zip_entry_size_is_set(const struct zip_entry *zip_entry)
+{
+	return (0 == (zip_entry->zip_flags & ZIP_LENGTH_AT_END)
+	    || (zip_entry->uncompressed_size > 0
+		&& zip_entry->uncompressed_size != 0xffffffff));
+}
+
 #ifdef HAVE_ZLIB_H
 static int
 zip_read_data_deflate(struct archive_read *a, const void **buff,
@@ -284,7 +291,7 @@
 ppmd_read(void* p) {
 	/* Get the handle to current decompression context. */
 	struct archive_read *a = ((IByteIn*)p)->a;
-	struct zip *zip = (struct zip*) a->format->data;
+	struct zip *zip = a->format->data;
 
 	/* Fetch next byte. */
 	const uint8_t* data = __archive_read_ahead(a, 1, NULL);
@@ -430,7 +437,7 @@
 zipx_read_header_and_decrypt(struct archive_read *a, const void **buf, size_t in_len,
     size_t *out_len, size_t *consumed)
 {
-	struct zip *zip = (struct zip *)(a->format->data);
+	struct zip *zip = a->format->data;
 	const void *raw;
 	ssize_t bytes_avail;
 	size_t to_decrypt;
@@ -482,6 +489,13 @@
 {
 	*sp = compressed_buff;
 
+	/* Safety check to prevent potential OOB reads if something went wrong
+	 * previously. We should not have a negative bytes_avail count here.
+	 * If we do, set them to zero so that reading the ZIP will fail later,
+	 * safely as corrupted instead of crashing. */
+	if (bytes_avail < 0)
+		bytes_avail = 0;
+
 	if (zip->tctx_valid || zip->cctx_valid) {
 		if (zip->decrypted_bytes_remaining < (size_t)bytes_avail) {
 			size_t buff_remaining =
@@ -642,8 +656,8 @@
 process_extra(struct archive_read *a, struct archive_entry *entry,
      const char *p, size_t extra_length, struct zip_entry* zip_entry)
 {
+	struct zip *zip = a->format->data;
 	unsigned offset = 0;
-	struct zip *zip = (struct zip *)(a->format->data);
 
 	if (extra_length == 0) {
 		return ARCHIVE_OK;
@@ -742,6 +756,22 @@
 			 * on which file starts, but we don't handle
 			 * multi-volume Zip files. */
 			break;
+		case 0x000d:
+			/* PKWARE Unix Extra Field fixed metadata. */
+			if (datasize >= 12) {
+				zip_entry->atime = archive_le32dec(p + offset);
+				zip_entry->mtime =
+				    archive_le32dec(p + offset + 4);
+				zip_entry->uid =
+				    archive_le16dec(p + offset + 8);
+				zip_entry->gid =
+				    archive_le16dec(p + offset + 10);
+				/*
+				 * APPNOTE.TXT also defines additional data after
+				 * this fixed metadata, depending on file type.
+				 */
+			}
+			break;
 #ifdef DEBUG
 		case 0x0017:
 		{
@@ -1430,10 +1460,7 @@
 			    "Read error skipping symlink target name");
 			return ARCHIVE_FATAL;
 		}
-	} else if (0 == (zip_entry->zip_flags & ZIP_LENGTH_AT_END)
-	   || (zip_entry->uncompressed_size > 0
-	       && zip_entry->uncompressed_size != 0xffffffff)) {
-		/* Set the size only if it's meaningful. */
+	} else if (zip_entry_size_is_set(zip_entry)) {
 		archive_entry_set_size(entry, zip_entry->uncompressed_size);
 	}
 	zip->entry_bytes_remaining = zip_entry->compressed_size;
@@ -1456,7 +1483,7 @@
 static int
 check_authentication_code(struct archive_read *a, const void *_p)
 {
-	struct zip *zip = (struct zip *)(a->format->data);
+	struct zip *zip = a->format->data;
 
 	/* Check authentication code. */
 	if (zip->hctx_valid) {
@@ -1683,6 +1710,26 @@
 		return;
 	}
 
+	/* None of the exact patterns matched. If entry size was unknown
+	 * (ZIP_LENGTH_AT_END flag), before treating this as
+	 * corruption, check whether the next ZIP record follows the data
+	 * immediately: a length-at-end entry whose compression format has
+	 * its own end-of-stream marker (e.g. PPMd) may be written with no
+	 * data descriptor at all.  In that case the byte counts we measured
+	 * during decompression are authoritative, so trust them and leave
+	 * the stream untouched. */
+	if (zip->entry->zip_flags & ZIP_LENGTH_AT_END)
+	{
+		const uint32_t sig = archive_le32dec(p);
+		if (sig == 0x04034b50U     /* Local file header */
+		    || sig == 0x02014b50U  /* Central directory record */
+		    || sig == 0x06054b50U) /* End of central directory */ {
+			zip->entry->compressed_size = compressed_actual;
+			zip->entry->uncompressed_size = uncompressed_actual;
+			return;
+		}
+	}
+
 	/* If none of the above patterns gives us a full exact match,
 	 * then there's something definitely amiss.  The fallback code
 	 * below will parse out some plausible values for error
@@ -1760,7 +1807,7 @@
 zip_read_data_none(struct archive_read *a, const void **_buff,
     size_t *size, int64_t *offset)
 {
-	struct zip *zip;
+	struct zip *zip = a->format->data;
 	const char *buff;
 	ssize_t bytes_avail;
 	ssize_t trailing_extra;
@@ -1768,7 +1815,6 @@
 
 	(void)offset; /* UNUSED */
 
-	zip = (struct zip *)(a->format->data);
 	trailing_extra = zip->hctx_valid ? AUTH_CODE_SIZE : 0;
 
 	if (zip->entry->zip_flags & ZIP_LENGTH_AT_END) {
@@ -1986,7 +2032,13 @@
 	 */
 
 	/* Read magic1,magic2,lzma_params from the ZIPX stream. */
-	if(zip->entry_bytes_remaining < 9) {
+	/* When the compressed size is unknown (e.g. ZIP_LENGTH_AT_END read
+	 * from a non-seekable source), entry_bytes_remaining is 0 or negative
+	 * here.  We can still attempt to read the 9-byte header; if the data
+	 * is truly truncated, the __archive_read_ahead calls below will catch
+	 * it. */
+	if(zip->entry_bytes_remaining > 0
+		&& zip->entry_bytes_remaining < 9) {
 		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 		    "Truncated lzma data");
 		return (ARCHIVE_FATAL);
@@ -2060,8 +2112,10 @@
 
 	/* We've already consumed some bytes, so take this into account. */
 	__archive_read_consume(a, 9);
-	zip->entry_bytes_remaining -= 9;
 	zip->entry_compressed_bytes_read += 9;
+	if (zip->entry_bytes_remaining > 0) {
+		zip->entry_bytes_remaining -= 9;
+	}
 
 	zip->decompress_init = 1;
 	return (ARCHIVE_OK);
@@ -2071,12 +2125,12 @@
 zip_read_data_zipx_xz(struct archive_read *a, const void **buff,
 	size_t *size, int64_t *offset)
 {
-	struct zip* zip = (struct zip *)(a->format->data);
+	struct zip *zip = a->format->data;
 	int ret;
 	lzma_ret lz_ret;
 	const void* compressed_buf;
 	const void* sp;
-	ssize_t bytes_avail, in_bytes, to_consume = 0;
+	ssize_t bytes_avail, to_consume = 0;
 
 	(void) offset; /* UNUSED */
 
@@ -2088,19 +2142,21 @@
 	}
 
 	compressed_buf = sp = __archive_read_ahead(a, 1, &bytes_avail);
+	if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END)
+		&& bytes_avail > zip->entry_bytes_remaining) {
+		bytes_avail = (ssize_t)zip->entry_bytes_remaining;
+	}
 	if (bytes_avail < 0) {
 		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 		    "Truncated xz file body");
 		return (ARCHIVE_FATAL);
 	}
 
-	in_bytes = (ssize_t)zipmin(zip->entry_bytes_remaining, bytes_avail);
-
-	zip_read_decrypt(zip, compressed_buf, in_bytes,
-		&compressed_buf, &in_bytes, &sp);
+	zip_read_decrypt(zip, compressed_buf, bytes_avail,
+		&compressed_buf, &bytes_avail, &sp);
 
 	zip->zipx_lzma_stream.next_in = compressed_buf;
-	zip->zipx_lzma_stream.avail_in = in_bytes;
+	zip->zipx_lzma_stream.avail_in = bytes_avail;
 	zip->zipx_lzma_stream.total_in = 0;
 	zip->zipx_lzma_stream.next_out = zip->uncompressed_buffer;
 	zip->zipx_lzma_stream.avail_out = zip->uncompressed_buffer_size;
@@ -2127,8 +2183,10 @@
 			lzma_end(&zip->zipx_lzma_stream);
 			zip->zipx_lzma_valid = 0;
 
-			if((int64_t) zip->zipx_lzma_stream.total_in !=
-			    zip->entry_bytes_remaining)
+			/* This assertion is only possible if the size of the compressed data
+			 * stream is known -> !ZIP_LENGTH_AT_END */
+			if((int64_t) zip->zipx_lzma_stream.total_in != zip->entry_bytes_remaining
+				&& !(zip->entry->zip_flags & ZIP_LENGTH_AT_END))
 			{
 				archive_set_error(&a->archive,
 				    ARCHIVE_ERRNO_MISC,
@@ -2167,12 +2225,12 @@
 zip_read_data_zipx_lzma_alone(struct archive_read *a, const void **buff,
     size_t *size, int64_t *offset)
 {
-	struct zip* zip = (struct zip *)(a->format->data);
+	struct zip *zip = a->format->data;
 	int ret;
 	lzma_ret lz_ret;
 	const void* compressed_buf;
 	const void* sp;
-	ssize_t bytes_avail, in_bytes, to_consume;
+	ssize_t bytes_avail, to_consume;
 
 	(void) offset; /* UNUSED */
 
@@ -2192,29 +2250,38 @@
 	 * data.
 	 */
 	compressed_buf = __archive_read_ahead(a, 1, &bytes_avail);
+	if (zip->entry_bytes_remaining > 0
+		&& bytes_avail > zip->entry_bytes_remaining) {
+		bytes_avail = (ssize_t)zip->entry_bytes_remaining;
+	}
 	if (bytes_avail < 0) {
 		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 		    "Truncated lzma file body");
 		return (ARCHIVE_FATAL);
 	}
 
-	/* Set decompressor parameters. */
-	in_bytes = (ssize_t)zipmin(zip->entry_bytes_remaining, bytes_avail);
-
-	zip_read_decrypt(zip, compressed_buf, in_bytes,
-	    &compressed_buf, &in_bytes, &sp);
+	zip_read_decrypt(zip, compressed_buf, bytes_avail,
+	    &compressed_buf, &bytes_avail, &sp);
 
 	zip->zipx_lzma_stream.next_in = compressed_buf;
-	zip->zipx_lzma_stream.avail_in = in_bytes;
+	zip->zipx_lzma_stream.avail_in = bytes_avail;
 	zip->zipx_lzma_stream.total_in = 0;
 	zip->zipx_lzma_stream.next_out = zip->uncompressed_buffer;
-	zip->zipx_lzma_stream.avail_out =
-		/* These lzma_alone streams lack end of stream marker, so let's
-		 * make sure the unpacker won't try to unpack more than it's
-		 * supposed to. */
-		(size_t)zipmin((int64_t) zip->uncompressed_buffer_size,
-		    zip->entry->uncompressed_size -
-		    zip->entry_uncompressed_bytes_read);
+	/* These lzma_alone streams lack an end of stream marker in some
+	 * cases, so when the uncompressed size is known we cap avail_out to
+	 * make sure the unpacker won't try to unpack more than it's supposed
+	 * to.  When the compressed size is unknown (entry_bytes_remaining <= 0,
+	 * e.g. ZIP_LENGTH_AT_END from a non-seekable source) we must use the
+	 * full buffer and rely on the LZMA stream end marker to detect the end
+	 * of the entry. */
+	if (zip->entry_bytes_remaining <= 0) {
+		zip->zipx_lzma_stream.avail_out = zip->uncompressed_buffer_size;
+	} else {
+		zip->zipx_lzma_stream.avail_out =
+			(size_t)zipmin((int64_t) zip->uncompressed_buffer_size,
+			    zip->entry->uncompressed_size -
+			    zip->entry_uncompressed_bytes_read);
+	}
 	zip->zipx_lzma_stream.total_out = 0;
 
 	/* Perform the decompression. */
@@ -2228,8 +2295,11 @@
 		/* This case is optional in lzma alone format. It can happen,
 		 * but most of the files don't have it. (GitHub #1257) */
 		case LZMA_STREAM_END:
+			/* This assertion is only possible if the size of the
+			 * compressed data stream is known. */
 			if((int64_t) zip->zipx_lzma_stream.total_in !=
-			    zip->entry_bytes_remaining)
+			    zip->entry_bytes_remaining
+			    && zip->entry_bytes_remaining > 0)
 			{
 				archive_set_error(&a->archive,
 				    ARCHIVE_ERRNO_MISC,
@@ -2245,7 +2315,13 @@
 
 		case LZMA_BUF_ERROR:
 			if (zip->zipx_lzma_stream.avail_out == 0) {
-				zip->end_of_entry = 1;
+				/* The output buffer was filled exactly.  When
+				 * the uncompressed size is known this means we
+				 * have decompressed all expected bytes.  When
+				 * the size is unknown a full buffer just means
+				 * we need another iteration. */
+				if (zip->entry_bytes_remaining > 0)
+					zip->end_of_entry = 1;
 				break;
 			}
 			/* FALL THROUGH */
@@ -2259,14 +2335,16 @@
 
 	/* Update pointers. */
 	__archive_read_consume(a, to_consume);
-	zip->entry_bytes_remaining -= to_consume;
 	zip->entry_compressed_bytes_read += to_consume;
 	zip->entry_uncompressed_bytes_read += zip->zipx_lzma_stream.total_out;
 
 	zip_read_decrypt_update(zip, to_consume, sp);
 
-	if(zip->entry_bytes_remaining == 0) {
-		zip->end_of_entry = 1;
+	if(zip->entry_bytes_remaining > 0) {
+		zip->entry_bytes_remaining -= to_consume;
+		if(zip->entry_bytes_remaining == 0) {
+			zip->end_of_entry = 1;
+		}
 	}
 
 	if(zip->end_of_entry && zip->entry_bytes_remaining > 0) {
@@ -2417,7 +2495,7 @@
 zip_read_data_zipx_ppmd(struct archive_read *a, const void **buff,
     size_t *size, int64_t *offset)
 {
-	struct zip* zip = (struct zip *)(a->format->data);
+	struct zip *zip = a->format->data;
 	int ret;
 	size_t consumed_bytes = 0;
 
@@ -2536,8 +2614,8 @@
 zip_read_data_zipx_bzip2(struct archive_read *a, const void **buff,
     size_t *size, int64_t *offset)
 {
-	struct zip *zip = (struct zip *)(a->format->data);
-	ssize_t bytes_avail = 0, in_bytes, to_consume;
+	struct zip *zip = a->format->data;
+	ssize_t bytes_avail = 0, to_consume;
 	const void *compressed_buff;
 	const void *sp;
 	int r;
@@ -2554,30 +2632,26 @@
 
 	/* Fetch more compressed bytes. */
 	compressed_buff = __archive_read_ahead(a, 1, &bytes_avail);
-	if(bytes_avail < 0) {
-		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
-		    "Truncated bzip2 file body");
-		return (ARCHIVE_FATAL);
+	if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END)
+		&& bytes_avail > zip->entry_bytes_remaining) {
+		bytes_avail = (ssize_t)zip->entry_bytes_remaining;
 	}
-
-	in_bytes = (ssize_t)zipmin(zip->entry_bytes_remaining, bytes_avail);
-	if(in_bytes < 1) {
+	if(bytes_avail < 1) {
 		/* libbz2 doesn't complain when caller feeds avail_in == 0.
 		 * It will actually return success in this case, which is
 		 * undesirable. This is why we need to make this check
 		 * manually. */
-
 		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 		    "Truncated bzip2 file body");
 		return (ARCHIVE_FATAL);
 	}
 
-	zip_read_decrypt(zip, compressed_buff, in_bytes,
-	    &compressed_buff, &in_bytes, &sp);
+	zip_read_decrypt(zip, compressed_buff, bytes_avail,
+	    &compressed_buff, &bytes_avail, &sp);
 
 	/* Setup buffer boundaries. */
 	zip->bzstream.next_in = (char*)(uintptr_t) compressed_buff;
-	zip->bzstream.avail_in = (uint32_t)in_bytes;
+	zip->bzstream.avail_in = (uint32_t)bytes_avail;
 	zip->bzstream.total_in_hi32 = 0;
 	zip->bzstream.total_in_lo32 = 0;
 	zip->bzstream.next_out = (char*) zip->uncompressed_buffer;
@@ -2691,8 +2765,8 @@
 zip_read_data_zipx_zstd(struct archive_read *a, const void **buff,
     size_t *size, int64_t *offset)
 {
-	struct zip *zip = (struct zip *)(a->format->data);
-	ssize_t bytes_avail = 0, in_bytes, to_consume;
+	struct zip *zip = a->format->data;
+	ssize_t bytes_avail = 0, to_consume;
 	const void *compressed_buff;
 	const void *sp;
 	int r;
@@ -2712,14 +2786,11 @@
 
 	/* Fetch more compressed bytes */
 	compressed_buff = sp = __archive_read_ahead(a, 1, &bytes_avail);
-	if(bytes_avail < 0) {
-		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
-		    "Truncated zstd file body");
-		return (ARCHIVE_FATAL);
+	if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END)
+		&& bytes_avail > zip->entry_bytes_remaining) {
+		bytes_avail = (ssize_t)zip->entry_bytes_remaining;
 	}
-
-	in_bytes = (ssize_t)zipmin(zip->entry_bytes_remaining, bytes_avail);
-	if(in_bytes < 1) {
+	if(bytes_avail < 1) {
 		/* zstd doesn't complain when caller feeds avail_in == 0.
 		 * It will actually return success in this case, which is
 		 * undesirable. This is why we need to make this check
@@ -2729,12 +2800,12 @@
 		return (ARCHIVE_FATAL);
 	}
 
-	zip_read_decrypt(zip, compressed_buff, in_bytes,
-	    &compressed_buff, &in_bytes, &sp);
+	zip_read_decrypt(zip, compressed_buff, bytes_avail,
+	    &compressed_buff, &bytes_avail, &sp);
 
 	/* Setup buffer boundaries */
 	in.src = compressed_buff;
-	in.size = in_bytes;
+	in.size = bytes_avail;
 	in.pos = 0;
 	out = (ZSTD_outBuffer) { zip->uncompressed_buffer, zip->uncompressed_buffer_size, 0 };
 
@@ -2746,6 +2817,10 @@
 			ZSTD_getErrorName(ret));
 		return (ARCHIVE_FATAL);
 	}
+	/* End of stream handling for zips with ZIP_LENGTH_AT_END flag */
+	if (ret == 0 && (zip->entry->zip_flags & ZIP_LENGTH_AT_END)) {
+		zip->end_of_entry = 1;
+	}
 
 	/* Check end of the stream. */
 	if (ret == 0) {
@@ -2812,7 +2887,7 @@
 zip_read_data_deflate(struct archive_read *a, const void **buff,
     size_t *size, int64_t *offset)
 {
-	struct zip *zip;
+	struct zip *zip = a->format->data;
 	ssize_t bytes_avail, to_consume = 0;
 	const void *compressed_buff;
 	const void *sp;
@@ -2820,8 +2895,6 @@
 
 	(void)offset; /* UNUSED */
 
-	zip = (struct zip *)(a->format->data);
-
 	/* If the buffer hasn't been allocated, allocate it now. */
 	if (zip->uncompressed_buffer == NULL) {
 		zip->uncompressed_buffer_size = 256 * 1024;
@@ -2914,7 +2987,7 @@
 static int
 read_decryption_header(struct archive_read *a)
 {
-	struct zip *zip = (struct zip *)(a->format->data);
+	struct zip *zip = a->format->data;
 	const char *p;
 	unsigned int remaining_size;
 	unsigned int ts;
@@ -3108,7 +3181,7 @@
 static int
 zip_alloc_decryption_buffer(struct archive_read *a)
 {
-	struct zip *zip = (struct zip *)(a->format->data);
+	struct zip *zip = a->format->data;
 	size_t bs = 256 * 1024;
 
 	if (zip->decrypted_buffer == NULL) {
@@ -3127,7 +3200,7 @@
 static int
 init_traditional_PKWARE_decryption(struct archive_read *a)
 {
-	struct zip *zip = (struct zip *)(a->format->data);
+	struct zip *zip = a->format->data;
 	const void *p;
 	int retry;
 	int r;
@@ -3199,7 +3272,7 @@
 static int
 init_WinZip_AES_decryption(struct archive_read *a)
 {
-	struct zip *zip = (struct zip *)(a->format->data);
+	struct zip *zip = a->format->data;
 	const void *p;
 	const uint8_t *pv;
 	size_t key_len, salt_len;
@@ -3293,8 +3366,8 @@
 archive_read_format_zip_read_data(struct archive_read *a,
     const void **buff, size_t *size, int64_t *offset)
 {
+	struct zip *zip = a->format->data;
 	int r;
-	struct zip *zip = (struct zip *)(a->format->data);
 
 	if (zip->has_encrypted_entries ==
 			ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) {
@@ -3372,6 +3445,25 @@
 	}
 	if (r != ARCHIVE_OK)
 		return (r);
+
+	/*
+	 * FAIL if there are more uncompressed bytes than were
+	 * initially advertised.  The end-of-entry check below also
+	 * compares these values, but only once decoding reaches its
+	 * own natural end. Fail the entry so a caller relying on
+	 * archive_entry_size() as a hard boundary is never misled;
+	 * later entries in the archive can still be read normally.
+	 */
+	if (*size > 0 && zip_entry_size_is_set(zip->entry) &&
+	    zip->entry_uncompressed_bytes_read > zip->entry->uncompressed_size) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "ZIP uncompressed data is larger than the declared "
+		    "entry size (read at least %jd, expected %jd)",
+		    (intmax_t)zip->entry_uncompressed_bytes_read,
+		    (intmax_t)zip->entry->uncompressed_size);
+		return (ARCHIVE_FAILED);
+	}
+
 	if (*size > 0) {
 		zip->computed_crc32 = zip->crc32func(zip->computed_crc32, *buff,
 						     (unsigned)*size);
@@ -3402,10 +3494,8 @@
 			    (intmax_t)zip->entry->compressed_size);
 			return (ARCHIVE_FAILED);
 		}
-		/* Size field only stores the lower 32 bits of the actual
-		 * size. */
-		if ((zip->entry->uncompressed_size & UINT32_MAX)
-		    != (zip->entry_uncompressed_bytes_read & UINT32_MAX)) {
+		if (zip->entry->uncompressed_size !=
+		    zip->entry_uncompressed_bytes_read) {
 			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 			    "ZIP uncompressed data is wrong size "
 			    "(read %jd, expected %jd)",
@@ -3421,11 +3511,9 @@
 static int
 archive_read_format_zip_cleanup(struct archive_read *a)
 {
-	struct zip *zip;
+	struct zip *zip = a->format->data;
 	struct zip_entry *zip_entry, *next_zip_entry;
 
-	zip = (struct zip *)(a->format->data);
-
 #ifdef HAVE_ZLIB_H
 	if (zip->stream_valid)
 		inflateEnd(&zip->stream);
@@ -3473,7 +3561,7 @@
 	free(zip->v_data);
 	archive_string_free(&zip->format_name);
 	free(zip);
-	(a->format->data) = NULL;
+	a->format->data = NULL;
 	return (ARCHIVE_OK);
 }
 
@@ -3481,7 +3569,7 @@
 archive_read_format_zip_has_encrypted_entries(struct archive_read *_a)
 {
 	if (_a && _a->format) {
-		struct zip * zip = (struct zip *)_a->format->data;
+		struct zip *zip = _a->format->data;
 		if (zip) {
 			return zip->has_encrypted_entries;
 		}
@@ -3493,10 +3581,9 @@
 archive_read_format_zip_options(struct archive_read *a,
     const char *key, const char *val)
 {
-	struct zip *zip;
+	struct zip *zip = a->format->data;
 	int ret = ARCHIVE_FAILED;
 
-	zip = (struct zip *)(a->format->data);
 	if (strcmp(key, "compat-2x")  == 0) {
 		/* Handle filenames as libarchive 2.x */
 		zip->init_default_conversion = (val != NULL) ? 1 : 0;
@@ -3603,14 +3690,12 @@
 archive_read_format_zip_streamable_read_header(struct archive_read *a,
     struct archive_entry *entry)
 {
-	struct zip *zip;
+	struct zip *zip = a->format->data;
 
 	a->archive.archive_format = ARCHIVE_FORMAT_ZIP;
 	if (a->archive.archive_format_name == NULL)
 		a->archive.archive_format_name = "ZIP";
 
-	zip = (struct zip *)(a->format->data);
-
 	/*
 	 * It should be sufficient to call archive_read_next_header() for
 	 * a reader to determine if an entry is encrypted or not. If the
@@ -3696,10 +3781,9 @@
 static int
 archive_read_format_zip_read_data_skip_streamable(struct archive_read *a)
 {
-	struct zip *zip;
+	struct zip *zip = a->format->data;
 	int64_t bytes_skipped;
 
-	zip = (struct zip *)(a->format->data);
 	bytes_skipped = __archive_read_consume(a, zip->unconsumed);
 	zip->unconsumed = 0;
 	if (bytes_skipped < 0)
@@ -3941,7 +4025,7 @@
 static int
 archive_read_format_zip_seekable_bid(struct archive_read *a, int best_bid)
 {
-	struct zip *zip = (struct zip *)a->format->data;
+	struct zip *zip = a->format->data;
 	int64_t file_size, current_offset;
 	const char *p;
 	int i, tail;
@@ -4147,7 +4231,6 @@
 	__archive_rb_tree_init(&zip->tree, &rb_ops);
 	__archive_rb_tree_init(&zip->tree_rsrc, &rb_rsrc_ops);
 
-	zip->central_directory_entries_total = 0;
 	while (1) {
 		struct zip_entry *zip_entry;
 		size_t filename_length, extra_length, comment_length;
@@ -4176,7 +4259,6 @@
 		zip_entry->next = zip->zip_entries;
 		zip_entry->flags |= LA_FROM_CENTRAL_DIRECTORY;
 		zip->zip_entries = zip_entry;
-		zip->central_directory_entries_total++;
 
 		/* version = p[4]; */
 		zip_entry->system = p[5];
@@ -4256,12 +4338,14 @@
 			r = rsrc_basename(name, filename_length);
 			if (filename_length >= 9 &&
 			    strncmp("__MACOSX/", name, 9) == 0) {
+				const char *name_end;
+
+				name_end = name + filename_length;
 				/* If this file is not a resource fork nor
 				 * a directory. We should treat it as a non
 				 * resource fork file to expose it. */
 				if (name[filename_length-1] != '/' &&
-				    (r - name < 3 || r[0] != '.' ||
-				     r[1] != '_')) {
+				    (name_end - r < 2 || r[0] != '.' || r[1] != '_')) {
 					__archive_rb_tree_insert_node(
 					    &zip->tree, &zip_entry->node);
 					/* Expose its parent directories. */
@@ -4339,7 +4423,7 @@
 zip_read_mac_metadata(struct archive_read *a, struct archive_entry *entry,
     struct zip_entry *rsrc)
 {
-	struct zip *zip = (struct zip *)a->format->data;
+	struct zip *zip = a->format->data;
 	unsigned char *metadata, *mp;
 	int64_t offset = archive_filter_bytes(&a->archive, 0);
 	size_t remaining_bytes, metadata_bytes;
@@ -4490,7 +4574,7 @@
 archive_read_format_zip_seekable_read_header(struct archive_read *a,
 	struct archive_entry *entry)
 {
-	struct zip *zip = (struct zip *)a->format->data;
+	struct zip *zip = a->format->data;
 	struct zip_entry *rsrc;
 	int64_t offset;
 	int r, ret = ARCHIVE_OK;
@@ -4570,8 +4654,7 @@
 static int
 archive_read_format_zip_read_data_skip_seekable(struct archive_read *a)
 {
-	struct zip *zip;
-	zip = (struct zip *)(a->format->data);
+	struct zip *zip = a->format->data;
 
 	zip->unconsumed = 0;
 	return (ARCHIVE_OK);
diff --git a/c/archive_string.c b/c/archive_string.c
--- a/c/archive_string.c
+++ b/c/archive_string.c
@@ -450,6 +450,8 @@
 	return locale_charset();
 #elif HAVE_NL_LANGINFO
 	return nl_langinfo(CODESET);
+#elif defined(__BIONIC__)
+	return "UTF-8";
 #else
 	return "";
 #endif
@@ -2325,7 +2327,7 @@
 
 	remaining = length;
 	itp = (const uint8_t *)_p;
-	while (*itp && remaining > 0) {
+	while (remaining > 0 && *itp) {
 		if (*itp > 127) {
 			// Non-ASCII: Substitute with suitable replacement
 			if (sc->flag & SCONV_TO_UTF8) {
@@ -2340,6 +2342,7 @@
 			archive_strappend_char(as, *itp);
 		}
 		++itp;
+		--remaining;
 	}
 	return (return_value);
 }
diff --git a/c/archive_write.c b/c/archive_write.c
--- a/c/archive_write.c
+++ b/c/archive_write.c
@@ -69,7 +69,7 @@
 static int	_archive_write_finish_entry(struct archive *);
 static ssize_t	_archive_write_data(struct archive *, const void *, size_t);
 
-struct archive_none {
+struct client {
 	size_t buffer_size;
 	size_t avail;
 	char *buffer;
@@ -135,7 +135,7 @@
 
 	if (bytes_per_block < 0) {
 		// Do nothing if the bytes_per_block is negative
-		return 0;
+		return ARCHIVE_OK;
 	}
 	a->bytes_per_block = bytes_per_block;
 	return (ARCHIVE_OK);
@@ -240,8 +240,10 @@
 		/* If unset, a fatal error has already occurred, so this filter
 		 * didn't open. We cannot write anything. */
 		return(ARCHIVE_FATAL);
+	if (length > (uint64_t)(INT64_MAX - f->bytes_written))
+		return(ARCHIVE_FATAL);
 	r = (f->write)(f, buff, length);
-	f->bytes_written += length;
+	f->bytes_written += (int64_t)length;
 	return (r);
 }
 
@@ -338,7 +340,7 @@
 }
 
 int
-__archive_write_nulls(struct archive_write *a, size_t length)
+__archive_write_nulls(struct archive_write *a, uint64_t length)
 {
 	if (length == 0)
 		return (ARCHIVE_OK);
@@ -357,7 +359,7 @@
 archive_write_client_open(struct archive_write_filter *f)
 {
 	struct archive_write *a = (struct archive_write *)f->archive;
-	struct archive_none *state;
+	struct client *client;
 	void *buffer;
 	size_t buffer_size;
 
@@ -366,21 +368,21 @@
 	    archive_write_get_bytes_in_last_block(f->archive);
 	buffer_size = f->bytes_per_block;
 
-	state = calloc(1, sizeof(*state));
+	client = calloc(1, sizeof(*client));
 	buffer = malloc(buffer_size);
-	if (state == NULL || buffer == NULL) {
-		free(state);
+	if (client == NULL || buffer == NULL) {
+		free(client);
 		free(buffer);
 		archive_set_error(f->archive, ENOMEM,
 		    "Can't allocate data for output buffering");
 		return (ARCHIVE_FATAL);
 	}
 
-	state->buffer_size = buffer_size;
-	state->buffer = buffer;
-	state->next = state->buffer;
-	state->avail = state->buffer_size;
-	f->data = state;
+	client->buffer_size = buffer_size;
+	client->buffer = buffer;
+	client->next = client->buffer;
+	client->avail = client->buffer_size;
+	f->data = client;
 
 	if (a->client_opener == NULL)
 		return (ARCHIVE_OK);
@@ -392,7 +394,7 @@
     const void *_buff, size_t length)
 {
 	struct archive_write *a = (struct archive_write *)f->archive;
-        struct archive_none *state = (struct archive_none *)f->data;
+	struct client *client = f->data;
 	const char *buff = (const char *)_buff;
 	ssize_t remaining, to_copy;
 	ssize_t bytes_written;
@@ -405,7 +407,7 @@
 	 * particular, this supports "no write delay" operation for
 	 * special applications.  Just set the block size to zero.
 	 */
-	if (state->buffer_size == 0) {
+	if (client->buffer_size == 0) {
 		while (remaining > 0) {
 			bytes_written = (a->client_writer)(&a->archive,
 			    a->client_data, buff, remaining);
@@ -418,20 +420,20 @@
 	}
 
 	/* If the copy buffer isn't empty, try to fill it. */
-	if (state->avail < state->buffer_size) {
+	if (client->avail < client->buffer_size) {
 		/* If buffer is not empty... */
 		/* ... copy data into buffer ... */
-		to_copy = ((size_t)remaining > state->avail) ?
-			state->avail : (size_t)remaining;
-		memcpy(state->next, buff, to_copy);
-		state->next += to_copy;
-		state->avail -= to_copy;
+		to_copy = ((size_t)remaining > client->avail) ?
+			client->avail : (size_t)remaining;
+		memcpy(client->next, buff, to_copy);
+		client->next += to_copy;
+		client->avail -= to_copy;
 		buff += to_copy;
 		remaining -= to_copy;
 		/* ... if it's full, write it out. */
-		if (state->avail == 0) {
-			char *p = state->buffer;
-			size_t to_write = state->buffer_size;
+		if (client->avail == 0) {
+			char *p = client->buffer;
+			size_t to_write = client->buffer_size;
 			while (to_write > 0) {
 				bytes_written = (a->client_writer)(&a->archive,
 				    a->client_data, p, to_write);
@@ -445,15 +447,15 @@
 				p += bytes_written;
 				to_write -= bytes_written;
 			}
-			state->next = state->buffer;
-			state->avail = state->buffer_size;
+			client->next = client->buffer;
+			client->avail = client->buffer_size;
 		}
 	}
 
-	while ((size_t)remaining >= state->buffer_size) {
+	while ((size_t)remaining >= client->buffer_size) {
 		/* Write out full blocks directly to client. */
 		bytes_written = (a->client_writer)(&a->archive,
-		    a->client_data, buff, state->buffer_size);
+		    a->client_data, buff, client->buffer_size);
 		if (bytes_written <= 0)
 			return (ARCHIVE_FATAL);
 		buff += bytes_written;
@@ -462,9 +464,9 @@
 
 	if (remaining > 0) {
 		/* Copy last bit into copy buffer. */
-		memcpy(state->next, buff, remaining);
-		state->next += remaining;
-		state->avail -= remaining;
+		memcpy(client->next, buff, remaining);
+		client->next += remaining;
+		client->avail -= remaining;
 	}
 	return (ARCHIVE_OK);
 }
@@ -473,7 +475,7 @@
 archive_write_client_free(struct archive_write_filter *f)
 {
 	struct archive_write *a = (struct archive_write *)f->archive;
-	struct archive_none *state = (struct archive_none *)f->data;
+	struct client *client = f->data;
 
 	if (a->client_freer)
 		(*a->client_freer)(&a->archive, a->client_data);
@@ -487,9 +489,9 @@
 	}
 
 	/* Free state. */
-	if (state != NULL) {
-		free(state->buffer);
-		free(state);
+	if (client != NULL) {
+		free(client->buffer);
+		free(client);
 		f->data = NULL;
 	}
 
@@ -500,7 +502,7 @@
 archive_write_client_close(struct archive_write_filter *f)
 {
 	struct archive_write *a = (struct archive_write *)f->archive;
-	struct archive_none *state = (struct archive_none *)f->data;
+	struct client *client = f->data;
 	ssize_t block_length;
 	ssize_t target_block_length;
 	ssize_t bytes_written;
@@ -509,8 +511,8 @@
 	int ret = ARCHIVE_OK;
 
 	/* If there's pending data, pad and write the last block */
-	if (state->next != state->buffer) {
-		block_length = state->buffer_size - state->avail;
+	if (client->next != client->buffer) {
+		block_length = client->buffer_size - client->avail;
 
 		/* Tricky calculation to determine size of last block */
 		if (a->bytes_in_last_block <= 0)
@@ -524,11 +526,11 @@
 		if (target_block_length > a->bytes_per_block)
 			target_block_length = a->bytes_per_block;
 		if (block_length < target_block_length) {
-			memset(state->next, 0,
+			memset(client->next, 0,
 			    target_block_length - block_length);
 			block_length = target_block_length;
 		}
-		p = state->buffer;
+		p = client->buffer;
 		to_write = block_length;
 		while (to_write > 0) {
 			bytes_written = (a->client_writer)(&a->archive,
@@ -683,6 +685,29 @@
 	a->filter_last = NULL;
 }
 
+int
+__archive_write_unregister_format(struct archive_write *a)
+{
+	int r = ARCHIVE_OK;
+
+	if (a->format_free != NULL)
+		r = (a->format_free)(a);
+
+	a->format_data = NULL;
+	a->format_name = NULL;
+	a->format_init = NULL;
+	a->format_options = NULL;
+	a->format_finish_entry = NULL;
+	a->format_write_header = NULL;
+	a->format_write_data = NULL;
+	a->format_close = NULL;
+	a->format_free = NULL;
+	a->archive.archive_format = 0;
+	a->archive.archive_format_name = NULL;
+
+	return (r);
+}
+
 /*
  * Destroy the archive structure.
  *
@@ -705,11 +730,9 @@
 		r = archive_write_close(&a->archive);
 
 	/* Release format resources. */
-	if (a->format_free != NULL) {
-		r1 = (a->format_free)(a);
-		if (r1 < r)
-			r = r1;
-	}
+	r1 = __archive_write_unregister_format(a);
+	if (r1 < r)
+		r = r1;
 
 	__archive_write_filters_free(_a);
 
diff --git a/c/archive_write_add_filter_b64encode.c b/c/archive_write_add_filter_b64encode.c
--- a/c/archive_write_add_filter_b64encode.c
+++ b/c/archive_write_add_filter_b64encode.c
@@ -46,7 +46,7 @@
 
 #define LBYTES	57
 
-struct private_b64encode {
+struct b64encode {
 	int			mode;
 	struct archive_string	name;
 	struct archive_string	encoded_buff;
@@ -64,6 +64,7 @@
 static int archive_filter_b64encode_free(struct archive_write_filter *);
 static void la_b64_encode(struct archive_string *, const unsigned char *, size_t);
 static int64_t atol8(const char *, size_t);
+static void free_data(struct b64encode *);
 
 static const char base64[] = {
 	'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
@@ -80,34 +81,38 @@
  * Add a compress filter to this write handle.
  */
 int
-archive_write_add_filter_b64encode(struct archive *_a)
+archive_write_add_filter_b64encode(struct archive *a)
 {
-	struct archive_write *a = (struct archive_write *)_a;
-	struct archive_write_filter *f = __archive_write_allocate_filter(_a);
-	struct private_b64encode *state;
+	struct archive_write_filter *f;
+	struct b64encode *b64encode;
 
-	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+	archive_check_magic(a, ARCHIVE_WRITE_MAGIC,
 	    ARCHIVE_STATE_NEW, "archive_write_add_filter_b64encode");
 
-	state = calloc(1, sizeof(*state));
-	if (state == NULL) {
-		archive_set_error(f->archive, ENOMEM,
-		    "Can't allocate data for b64encode filter");
-		return (ARCHIVE_FATAL);
-	}
-	archive_strcpy(&state->name, "-");
-	state->mode = 0644;
+	b64encode = calloc(1, sizeof(*b64encode));
+	if (b64encode == NULL)
+		goto memerr;
+	archive_strcpy(&b64encode->name, "-");
+	b64encode->mode = 0644;
 
-	f->data = state;
+	f = __archive_write_allocate_filter(a);
+	if (f == NULL)
+		goto memerr;
 	f->name = "b64encode";
 	f->code = ARCHIVE_FILTER_UU;
-	f->open = archive_filter_b64encode_open;
+	f->data = b64encode;
 	f->options = archive_filter_b64encode_options;
+	f->open = archive_filter_b64encode_open;
 	f->write = archive_filter_b64encode_write;
 	f->close = archive_filter_b64encode_close;
 	f->free = archive_filter_b64encode_free;
 
 	return (ARCHIVE_OK);
+memerr:
+	free_data(b64encode);
+	archive_set_error(a, ENOMEM,
+	    "Can't allocate data for b64encode filter");
+	return (ARCHIVE_FATAL);
 }
 
 /*
@@ -117,7 +122,7 @@
 archive_filter_b64encode_options(struct archive_write_filter *f, const char *key,
     const char *value)
 {
-	struct private_b64encode *state = (struct private_b64encode *)f->data;
+	struct b64encode *b64encode = f->data;
 
 	if (strcmp(key, "mode") == 0) {
 		int64_t val;
@@ -133,7 +138,7 @@
 			    "invalid mode option");
 			return (ARCHIVE_FAILED);
 		}
-		state->mode = (int)val & 0777;
+		b64encode->mode = (int)val & 0777;
 		return (ARCHIVE_OK);
 	} else if (strcmp(key, "name") == 0) {
 		if (value == NULL) {
@@ -141,7 +146,7 @@
 			    "name option requires a string");
 			return (ARCHIVE_FAILED);
 		}
-		archive_strcpy(&state->name, value);
+		archive_strcpy(&b64encode->name, value);
 		return (ARCHIVE_OK);
 	}
 
@@ -157,7 +162,7 @@
 static int
 archive_filter_b64encode_open(struct archive_write_filter *f)
 {
-	struct private_b64encode *state = (struct private_b64encode *)f->data;
+	struct b64encode *b64encode = f->data;
 	size_t bs = 65536, bpb;
 
 	if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {
@@ -170,18 +175,17 @@
 			bs -= bs % bpb;
 	}
 
-	state->bs = bs;
-	if (archive_string_ensure(&state->encoded_buff, bs + 512) == NULL) {
+	b64encode->bs = bs;
+	if (archive_string_ensure(&b64encode->encoded_buff, bs + 512) == NULL) {
 		archive_set_error(f->archive, ENOMEM,
 		    "Can't allocate data for b64encode buffer");
 		return (ARCHIVE_FATAL);
 	}
 
-	archive_string_sprintf(&state->encoded_buff, "begin-base64 %o %s\n",
-	    (unsigned int)state->mode, state->name.s);
+	archive_string_sprintf(&b64encode->encoded_buff, "begin-base64 %o %s\n",
+	    (unsigned int)b64encode->mode, b64encode->name.s);
 
-	f->data = state;
-	return (0);
+	return (ARCHIVE_OK);
 }
 
 static void
@@ -225,39 +229,39 @@
 archive_filter_b64encode_write(struct archive_write_filter *f, const void *buff,
     size_t length)
 {
-	struct private_b64encode *state = (struct private_b64encode *)f->data;
+	struct b64encode *b64encode = f->data;
 	const unsigned char *p = buff;
 	int ret = ARCHIVE_OK;
 
 	if (length == 0)
 		return (ret);
 
-	if (state->hold_len) {
-		while (state->hold_len < LBYTES && length > 0) {
-			state->hold[state->hold_len++] = *p++;
+	if (b64encode->hold_len) {
+		while (b64encode->hold_len < LBYTES && length > 0) {
+			b64encode->hold[b64encode->hold_len++] = *p++;
 			length--;
 		}
-		if (state->hold_len < LBYTES)
+		if (b64encode->hold_len < LBYTES)
 			return (ret);
-		la_b64_encode(&state->encoded_buff, state->hold, LBYTES);
-		state->hold_len = 0;
+		la_b64_encode(&b64encode->encoded_buff, b64encode->hold, LBYTES);
+		b64encode->hold_len = 0;
 	}
 
 	for (; length >= LBYTES; length -= LBYTES, p += LBYTES)
-		la_b64_encode(&state->encoded_buff, p, LBYTES);
+		la_b64_encode(&b64encode->encoded_buff, p, LBYTES);
 
 	/* Save remaining bytes. */
 	if (length > 0) {
-		memcpy(state->hold, p, length);
-		state->hold_len = length;
+		memcpy(b64encode->hold, p, length);
+		b64encode->hold_len = length;
 	}
-	while (archive_strlen(&state->encoded_buff) >= state->bs) {
+	while (archive_strlen(&b64encode->encoded_buff) >= b64encode->bs) {
 		ret = __archive_write_filter(f->next_filter,
-		    state->encoded_buff.s, state->bs);
-		memmove(state->encoded_buff.s,
-		    state->encoded_buff.s + state->bs,
-		    state->encoded_buff.length - state->bs);
-		state->encoded_buff.length -= state->bs;
+		    b64encode->encoded_buff.s, b64encode->bs);
+		memmove(b64encode->encoded_buff.s,
+		    b64encode->encoded_buff.s + b64encode->bs,
+		    b64encode->encoded_buff.length - b64encode->bs);
+		b64encode->encoded_buff.length -= b64encode->bs;
 	}
 
 	return (ret);
@@ -270,26 +274,24 @@
 static int
 archive_filter_b64encode_close(struct archive_write_filter *f)
 {
-	struct private_b64encode *state = (struct private_b64encode *)f->data;
+	struct b64encode *b64encode = f->data;
 
 	/* Flush remaining bytes. */
-	if (state->hold_len != 0)
-		la_b64_encode(&state->encoded_buff, state->hold, state->hold_len);
-	archive_string_sprintf(&state->encoded_buff, "====\n");
+	if (b64encode->hold_len != 0)
+		la_b64_encode(&b64encode->encoded_buff, b64encode->hold,
+		    b64encode->hold_len);
+	archive_string_sprintf(&b64encode->encoded_buff, "====\n");
 	/* Write the last block */
 	archive_write_set_bytes_in_last_block(f->archive, 1);
 	return __archive_write_filter(f->next_filter,
-	    state->encoded_buff.s, archive_strlen(&state->encoded_buff));
+	    b64encode->encoded_buff.s, archive_strlen(&b64encode->encoded_buff));
 }
 
 static int
 archive_filter_b64encode_free(struct archive_write_filter *f)
 {
-	struct private_b64encode *state = (struct private_b64encode *)f->data;
-
-	archive_string_free(&state->name);
-	archive_string_free(&state->encoded_buff);
-	free(state);
+	free_data(f->data);
+	f->data = NULL;
 	return (ARCHIVE_OK);
 }
 
@@ -316,3 +318,12 @@
 	return (l);
 }
 
+static void
+free_data(struct b64encode *b64encode)
+{
+	if (b64encode != NULL) {
+		archive_string_free(&b64encode->name);
+		archive_string_free(&b64encode->encoded_buff);
+		free(b64encode);
+	}
+}
diff --git a/c/archive_write_add_filter_bzip2.c b/c/archive_write_add_filter_bzip2.c
--- a/c/archive_write_add_filter_bzip2.c
+++ b/c/archive_write_add_filter_bzip2.c
@@ -53,7 +53,7 @@
 }
 #endif
 
-struct private_data {
+struct bzip2 {
 	int		 compression_level;
 #if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
 	bz_stream	 stream;
@@ -71,50 +71,66 @@
 		    const char *, const char *);
 static int archive_compressor_bzip2_write(struct archive_write_filter *,
 		    const void *, size_t);
+static void free_data(struct bzip2 *);
 
 /*
  * Add a bzip2 compression filter to this write handle.
  */
 int
-archive_write_add_filter_bzip2(struct archive *_a)
+archive_write_add_filter_bzip2(struct archive *a)
 {
-	struct archive_write *a = (struct archive_write *)_a;
-	struct archive_write_filter *f = __archive_write_allocate_filter(_a);
-	struct private_data *data;
+	struct archive_write_filter *f;
+	struct bzip2 *bzip2;
+	int r;
 
-	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+	archive_check_magic(a, ARCHIVE_WRITE_MAGIC,
 	    ARCHIVE_STATE_NEW, "archive_write_add_filter_bzip2");
 
-	data = calloc(1, sizeof(*data));
-	if (data == NULL) {
-		archive_set_error(&a->archive, ENOMEM, "Out of memory");
-		return (ARCHIVE_FATAL);
-	}
-	data->compression_level = 9; /* default */
-
-	f->data = data;
-	f->options = &archive_compressor_bzip2_options;
-	f->close = &archive_compressor_bzip2_close;
-	f->free = &archive_compressor_bzip2_free;
-	f->open = &archive_compressor_bzip2_open;
-	f->code = ARCHIVE_FILTER_BZIP2;
-	f->name = "bzip2";
+	bzip2 = calloc(1, sizeof(*bzip2));
+	if (bzip2 == NULL)
+		goto memerr;
 #if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
-	return (ARCHIVE_OK);
+	bzip2->compression_level = 9;
+
+	r = ARCHIVE_OK;
 #else
-	data->pdata = __archive_write_program_allocate("bzip2");
-	if (data->pdata == NULL) {
-		free(data);
-		archive_set_error(&a->archive, ENOMEM, "Out of memory");
-		return (ARCHIVE_FATAL);
-	}
-	data->compression_level = 0;
-	archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+	bzip2->pdata = __archive_write_program_allocate("bzip2");
+	if (bzip2->pdata == NULL)
+		goto memerr;
+	bzip2->compression_level = 0;
+
+	archive_set_error(a, ARCHIVE_ERRNO_MISC,
 	    "Using external bzip2 program");
-	return (ARCHIVE_WARN);
+	r = ARCHIVE_WARN;
 #endif
+
+	f = __archive_write_allocate_filter(a);
+	if (f == NULL)
+		goto memerr;
+	f->name = "bzip2";
+	f->code = ARCHIVE_FILTER_BZIP2;
+	f->data = bzip2;
+	f->options = archive_compressor_bzip2_options;
+	f->open = archive_compressor_bzip2_open;
+	f->write = archive_compressor_bzip2_write;
+	f->close = archive_compressor_bzip2_close;
+	f->free = archive_compressor_bzip2_free;
+
+	return (r);
+memerr:
+	free_data(bzip2);
+	archive_set_error(a, ENOMEM, "Out of memory");
+	return (ARCHIVE_FATAL);
 }
 
+static int
+archive_compressor_bzip2_free(struct archive_write_filter *f)
+{
+	free_data(f->data);
+	f->data = NULL;
+	return (ARCHIVE_OK);
+}
+
 /*
  * Set write options.
  */
@@ -122,7 +138,7 @@
 archive_compressor_bzip2_options(struct archive_write_filter *f,
     const char *key, const char *value)
 {
-	struct private_data *data = (struct private_data *)f->data;
+	struct bzip2 *bzip2 = f->data;
 
 	if (strcmp(key, "compression-level") == 0) {
 		if (value == NULL || !(value[0] >= '0' && value[0] <= '9') ||
@@ -131,12 +147,12 @@
 			    "compression-level invalid");
 			return (ARCHIVE_FAILED);
 		}
-		data->compression_level = value[0] - '0';
+		bzip2->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 (data->compression_level < 1)
-			data->compression_level = 1;
+		if (bzip2->compression_level < 1)
+			bzip2->compression_level = 1;
 		return (ARCHIVE_OK);
 	}
 
@@ -156,7 +172,7 @@
 #define	SET_NEXT_IN(st,src)					\
 	(st)->stream.next_in = (char *)(uintptr_t)(const void *)(src)
 static int drive_compressor(struct archive_write_filter *,
-		    struct private_data *, int finishing);
+		    struct bzip2 *, int finishing);
 
 /*
  * Setup callback.
@@ -164,10 +180,10 @@
 static int
 archive_compressor_bzip2_open(struct archive_write_filter *f)
 {
-	struct private_data *data = (struct private_data *)f->data;
+	struct bzip2 *bzip2 = f->data;
 	int ret;
 
-	if (data->compressed == NULL) {
+	if (bzip2->compressed == NULL) {
 		size_t bs = 65536, bpb;
 		if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {
 			/* Buffer size should be a multiple number of the bytes
@@ -178,25 +194,23 @@
 			else if (bpb != 0)
 				bs -= bs % bpb;
 		}
-		data->compressed_buffer_size = bs;
-		data->compressed = malloc(data->compressed_buffer_size);
-		if (data->compressed == NULL) {
+		bzip2->compressed_buffer_size = bs;
+		bzip2->compressed = malloc(bzip2->compressed_buffer_size);
+		if (bzip2->compressed == NULL) {
 			archive_set_error(f->archive, ENOMEM,
 			    "Can't allocate data for compression buffer");
 			return (ARCHIVE_FATAL);
 		}
 	}
 
-	memset(&data->stream, 0, sizeof(data->stream));
-	data->stream.next_out = data->compressed;
-	data->stream.avail_out = (uint32_t)data->compressed_buffer_size;
-	f->write = archive_compressor_bzip2_write;
+	memset(&bzip2->stream, 0, sizeof(bzip2->stream));
+	bzip2->stream.next_out = bzip2->compressed;
+	bzip2->stream.avail_out = (uint32_t)bzip2->compressed_buffer_size;
 
 	/* Initialize compression library */
-	ret = BZ2_bzCompressInit(&(data->stream),
-	    data->compression_level, 0, 30);
+	ret = BZ2_bzCompressInit(&(bzip2->stream),
+	    bzip2->compression_level, 0, 30);
 	if (ret == BZ_OK) {
-		f->data = data;
 		return (ARCHIVE_OK);
 	}
 
@@ -236,12 +250,12 @@
 archive_compressor_bzip2_write(struct archive_write_filter *f,
     const void *buff, size_t length)
 {
-	struct private_data *data = (struct private_data *)f->data;
+	struct bzip2 *bzip2 = f->data;
 
 	/* Compress input data to output buffer */
-	SET_NEXT_IN(data, buff);
-	data->stream.avail_in = (uint32_t)length;
-	if (drive_compressor(f, data, 0))
+	SET_NEXT_IN(bzip2, buff);
+	bzip2->stream.avail_in = (uint32_t)length;
+	if (drive_compressor(f, bzip2, 0))
 		return (ARCHIVE_FATAL);
 	return (ARCHIVE_OK);
 }
@@ -253,19 +267,19 @@
 static int
 archive_compressor_bzip2_close(struct archive_write_filter *f)
 {
-	struct private_data *data = (struct private_data *)f->data;
+	struct bzip2 *bzip2 = f->data;
 	int ret;
 
 	/* Finish compression cycle. */
-	ret = drive_compressor(f, data, 1);
+	ret = drive_compressor(f, bzip2, 1);
 	if (ret == ARCHIVE_OK) {
 		/* Write the last block */
 		ret = __archive_write_filter(f->next_filter,
-		    data->compressed,
-		    data->compressed_buffer_size - data->stream.avail_out);
+		    bzip2->compressed,
+		    bzip2->compressed_buffer_size - bzip2->stream.avail_out);
 	}
 
-	switch (BZ2_bzCompressEnd(&(data->stream))) {
+	switch (BZ2_bzCompressEnd(&(bzip2->stream))) {
 	case BZ_OK:
 		break;
 	default:
@@ -276,20 +290,6 @@
 	return ret;
 }
 
-static int
-archive_compressor_bzip2_free(struct archive_write_filter *f)
-{
-	struct private_data *data = (struct private_data *)f->data;
-
-	/* May already have been called, but not necessarily. */
-	(void)BZ2_bzCompressEnd(&(data->stream));
-
-	free(data->compressed);
-	free(data);
-	f->data = NULL;
-	return (ARCHIVE_OK);
-}
-
 /*
  * Utility function to push input data through compressor, writing
  * full output blocks as necessary.
@@ -299,35 +299,35 @@
  */
 static int
 drive_compressor(struct archive_write_filter *f,
-    struct private_data *data, int finishing)
+    struct bzip2 *bzip2, int finishing)
 {
 	int ret;
 
 	for (;;) {
-		if (data->stream.avail_out == 0) {
+		if (bzip2->stream.avail_out == 0) {
 			ret = __archive_write_filter(f->next_filter,
-			    data->compressed,
-			    data->compressed_buffer_size);
+			    bzip2->compressed,
+			    bzip2->compressed_buffer_size);
 			if (ret != ARCHIVE_OK) {
 				/* TODO: Handle this write failure */
 				return (ARCHIVE_FATAL);
 			}
-			data->stream.next_out = data->compressed;
-			data->stream.avail_out = (uint32_t)data->compressed_buffer_size;
+			bzip2->stream.next_out = bzip2->compressed;
+			bzip2->stream.avail_out = (uint32_t)bzip2->compressed_buffer_size;
 		}
 
 		/* If there's nothing to do, we're done. */
-		if (!finishing && data->stream.avail_in == 0)
+		if (!finishing && bzip2->stream.avail_in == 0)
 			return (ARCHIVE_OK);
 
-		ret = BZ2_bzCompress(&(data->stream),
+		ret = BZ2_bzCompress(&(bzip2->stream),
 		    finishing ? BZ_FINISH : BZ_RUN);
 
 		switch (ret) {
 		case BZ_RUN_OK:
 			/* In non-finishing case, did compressor
 			 * consume everything? */
-			if (!finishing && data->stream.avail_in == 0)
+			if (!finishing && bzip2->stream.avail_in == 0)
 				return (ARCHIVE_OK);
 			break;
 		case BZ_FINISH_OK:  /* Finishing: There's more work to do */
@@ -347,12 +347,24 @@
 	}
 }
 
+static void
+free_data(struct bzip2 *bzip2)
+{
+	if (bzip2 != NULL) {
+		/* May already have been called, but not necessarily. */
+		(void)BZ2_bzCompressEnd(&(bzip2->stream));
+
+		free(bzip2->compressed);
+		free(bzip2);
+	}
+}
+
 #else /* HAVE_BZLIB_H && BZ_CONFIG_ERROR */
 
 static int
 archive_compressor_bzip2_open(struct archive_write_filter *f)
 {
-	struct private_data *data = (struct private_data *)f->data;
+	struct bzip2 *bzip2 = f->data;
 	struct archive_string as;
 	int r;
 
@@ -360,13 +372,12 @@
 	archive_strcpy(&as, "bzip2");
 
 	/* Specify compression level. */
-	if (data->compression_level > 0) {
+	if (bzip2->compression_level > 0) {
 		archive_strcat(&as, " -");
-		archive_strappend_char(&as, '0' + data->compression_level);
+		archive_strappend_char(&as, '0' + bzip2->compression_level);
 	}
-	f->write = archive_compressor_bzip2_write;
 
-	r = __archive_write_program_open(f, data->pdata, as.s);
+	r = __archive_write_program_open(f, bzip2->pdata, as.s);
 	archive_string_free(&as);
 	return (r);
 }
@@ -375,27 +386,26 @@
 archive_compressor_bzip2_write(struct archive_write_filter *f, const void *buff,
     size_t length)
 {
-	struct private_data *data = (struct private_data *)f->data;
+	struct bzip2 *bzip2 = f->data;
 
-	return __archive_write_program_write(f, data->pdata, buff, length);
+	return __archive_write_program_write(f, bzip2->pdata, buff, length);
 }
 
 static int
 archive_compressor_bzip2_close(struct archive_write_filter *f)
 {
-	struct private_data *data = (struct private_data *)f->data;
+	struct bzip2 *bzip2 = f->data;
 
-	return __archive_write_program_close(f, data->pdata);
+	return __archive_write_program_close(f, bzip2->pdata);
 }
 
-static int
-archive_compressor_bzip2_free(struct archive_write_filter *f)
+static void
+free_data(struct bzip2 *bzip2)
 {
-	struct private_data *data = (struct private_data *)f->data;
-
-	__archive_write_program_free(data->pdata);
-	free(data);
-	return (ARCHIVE_OK);
+	if (bzip2 != NULL) {
+		__archive_write_program_free(bzip2->pdata);
+		free(bzip2);
+	}
 }
 
 #endif /* HAVE_BZLIB_H && BZ_CONFIG_ERROR */
diff --git a/c/archive_write_add_filter_compress.c b/c/archive_write_add_filter_compress.c
--- a/c/archive_write_add_filter_compress.c
+++ b/c/archive_write_add_filter_compress.c
@@ -85,7 +85,7 @@
 #define	FIRST	257		/* First free entry. */
 #define	CLEAR	256		/* Table clear output code. */
 
-struct private_data {
+struct compress {
 	int64_t in_count, out_count, checkpoint;
 
 	int code_len;			/* Number of bits/code. */
@@ -111,6 +111,7 @@
 		    const void *, size_t);
 static int archive_compressor_compress_close(struct archive_write_filter *);
 static int archive_compressor_compress_free(struct archive_write_filter *);
+static void free_data(struct compress *);
 
 #if ARCHIVE_VERSION_NUMBER < 4000000
 int
@@ -125,17 +126,35 @@
  * Add a compress filter to this write handle.
  */
 int
-archive_write_add_filter_compress(struct archive *_a)
+archive_write_add_filter_compress(struct archive *a)
 {
-	struct archive_write *a = (struct archive_write *)_a;
-	struct archive_write_filter *f = __archive_write_allocate_filter(_a);
+	struct archive_write_filter *f;
+	struct compress *compress;
 
-	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+	archive_check_magic(a, ARCHIVE_WRITE_MAGIC,
 	    ARCHIVE_STATE_NEW, "archive_write_add_filter_compress");
-	f->open = &archive_compressor_compress_open;
-	f->code = ARCHIVE_FILTER_COMPRESS;
+
+	compress = calloc(1, sizeof(*compress));
+	if (compress == NULL)
+		goto memerr;
+
+	f = __archive_write_allocate_filter(a);
+	if (f == NULL)
+		goto memerr;
 	f->name = "compress";
+	f->code = ARCHIVE_FILTER_COMPRESS;
+	f->data = compress;
+	f->open = archive_compressor_compress_open;
+	f->write = archive_compressor_compress_write;
+	f->close = archive_compressor_compress_close;
+	f->free = archive_compressor_compress_free;
+
 	return (ARCHIVE_OK);
+memerr:
+	free_data(compress);
+	archive_set_error(a, ENOMEM,
+	    "Can't allocate data for compression");
+	return (ARCHIVE_FATAL);
 }
 
 /*
@@ -144,19 +163,9 @@
 static int
 archive_compressor_compress_open(struct archive_write_filter *f)
 {
-	struct private_data *state;
+	struct compress *compress = f->data;
 	size_t bs = 65536, bpb;
 
-	f->code = ARCHIVE_FILTER_COMPRESS;
-	f->name = "compress";
-
-	state = calloc(1, sizeof(*state));
-	if (state == NULL) {
-		archive_set_error(f->archive, ENOMEM,
-		    "Can't allocate data for compression");
-		return (ARCHIVE_FATAL);
-	}
-
 	if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {
 		/* Buffer size should be a multiple number of the bytes
 		 * per block for performance. */
@@ -166,41 +175,38 @@
 		else if (bpb != 0)
 			bs -= bs % bpb;
 	}
-	state->compressed_buffer_size = bs;
-	state->compressed = malloc(state->compressed_buffer_size);
+	compress->compressed_buffer_size = bs;
+	compress->compressed = malloc(compress->compressed_buffer_size);
 
-	if (state->compressed == NULL) {
+	if (compress->compressed == NULL) {
 		archive_set_error(f->archive, ENOMEM,
 		    "Can't allocate data for compression buffer");
-		free(state);
 		return (ARCHIVE_FATAL);
 	}
 
-	f->write = archive_compressor_compress_write;
-	f->close = archive_compressor_compress_close;
-	f->free = archive_compressor_compress_free;
-
-	state->max_maxcode = 0x10000;	/* Should NEVER generate this code. */
-	state->in_count = 0;		/* Length of input. */
-	state->bit_buf = 0;
-	state->bit_offset = 0;
-	state->out_count = 3;		/* Includes 3-byte header mojo. */
-	state->compress_ratio = 0;
-	state->checkpoint = CHECK_GAP;
-	state->code_len = 9;
-	state->cur_maxcode = MAXCODE(state->code_len);
-	state->first_free = FIRST;
+	/* Should NEVER generate this code. */
+	compress->max_maxcode = 0x10000;
+	/* Length of input. */
+	compress->in_count = 0;
+	compress->bit_buf = 0;
+	compress->bit_offset = 0;
+	/* Includes 3-byte header mojo. */
+	compress->out_count = 3;
+	compress->compress_ratio = 0;
+	compress->checkpoint = CHECK_GAP;
+	compress->code_len = 9;
+	compress->cur_maxcode = MAXCODE(compress->code_len);
+	compress->first_free = FIRST;
 
-	memset(state->hashtab, 0xff, sizeof(state->hashtab));
+	memset(compress->hashtab, 0xff, sizeof(compress->hashtab));
 
 	/* Prime output buffer with a gzip header. */
-	state->compressed[0] = 0x1f; /* Compress */
-	state->compressed[1] = 0x9d;
-	state->compressed[2] = 0x90; /* Block mode, 16bit max */
-	state->compressed_offset = 3;
+	compress->compressed[0] = 0x1f; /* Compress */
+	compress->compressed[1] = 0x9d;
+	compress->compressed[2] = 0x90; /* Block mode, 16bit max */
+	compress->compressed_offset = 3;
 
-	f->data = state;
-	return (0);
+	return (ARCHIVE_OK);
 }
 
 /*-
@@ -224,17 +230,17 @@
 static int
 output_byte(struct archive_write_filter *f, unsigned char c)
 {
-	struct private_data *state = f->data;
+	struct compress *compress = f->data;
 
-	state->compressed[state->compressed_offset++] = c;
-	++state->out_count;
+	compress->compressed[compress->compressed_offset++] = c;
+	++compress->out_count;
 
-	if (state->compressed_buffer_size == state->compressed_offset) {
+	if (compress->compressed_buffer_size == compress->compressed_offset) {
 		int ret = __archive_write_filter(f->next_filter,
-		    state->compressed, state->compressed_buffer_size);
+		    compress->compressed, compress->compressed_buffer_size);
 		if (ret != ARCHIVE_OK)
 			return ARCHIVE_FATAL;
-		state->compressed_offset = 0;
+		compress->compressed_offset = 0;
 	}
 
 	return ARCHIVE_OK;
@@ -243,7 +249,7 @@
 static int
 output_code(struct archive_write_filter *f, int ocode)
 {
-	struct private_data *state = f->data;
+	struct compress *compress = f->data;
 	int bits, ret, clear_flg, bit_offset;
 
 	clear_flg = ocode == CLEAR;
@@ -252,11 +258,11 @@
 	 * Since ocode is always >= 8 bits, only need to mask the first
 	 * hunk on the left.
 	 */
-	bit_offset = state->bit_offset % 8;
-	state->bit_buf |= (ocode << bit_offset) & 0xff;
-	output_byte(f, state->bit_buf);
+	bit_offset = compress->bit_offset % 8;
+	compress->bit_buf |= (ocode << bit_offset) & 0xff;
+	output_byte(f, compress->bit_buf);
 
-	bits = state->code_len - (8 - bit_offset);
+	bits = compress->code_len - (8 - bit_offset);
 	ocode >>= 8 - bit_offset;
 	/* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */
 	if (bits >= 8) {
@@ -265,41 +271,41 @@
 		bits -= 8;
 	}
 	/* Last bits. */
-	state->bit_offset += state->code_len;
-	state->bit_buf = ocode & rmask[bits];
-	if (state->bit_offset == state->code_len * 8)
-		state->bit_offset = 0;
+	compress->bit_offset += compress->code_len;
+	compress->bit_buf = ocode & rmask[bits];
+	if (compress->bit_offset == compress->code_len * 8)
+		compress->bit_offset = 0;
 
 	/*
 	 * If the next entry is going to be too big for the ocode size,
 	 * then increase it, if possible.
 	 */
-	if (clear_flg || state->first_free > state->cur_maxcode) {
+	if (clear_flg || compress->first_free > compress->cur_maxcode) {
 	       /*
 		* Write the whole buffer, because the input side won't
 		* discover the size increase until after it has read it.
 		*/
-		if (state->bit_offset > 0) {
-			while (state->bit_offset < state->code_len * 8) {
-				ret = output_byte(f, state->bit_buf);
+		if (compress->bit_offset > 0) {
+			while (compress->bit_offset < compress->code_len * 8) {
+				ret = output_byte(f, compress->bit_buf);
 				if (ret != ARCHIVE_OK)
 					return ret;
-				state->bit_offset += 8;
-				state->bit_buf = 0;
+				compress->bit_offset += 8;
+				compress->bit_buf = 0;
 			}
 		}
-		state->bit_buf = 0;
-		state->bit_offset = 0;
+		compress->bit_buf = 0;
+		compress->bit_offset = 0;
 
 		if (clear_flg) {
-			state->code_len = 9;
-			state->cur_maxcode = MAXCODE(state->code_len);
+			compress->code_len = 9;
+			compress->cur_maxcode = MAXCODE(compress->code_len);
 		} else {
-			state->code_len++;
-			if (state->code_len == 16)
-				state->cur_maxcode = state->max_maxcode;
+			compress->code_len++;
+			if (compress->code_len == 16)
+				compress->cur_maxcode = compress->max_maxcode;
 			else
-				state->cur_maxcode = MAXCODE(state->code_len);
+				compress->cur_maxcode = MAXCODE(compress->code_len);
 		}
 	}
 
@@ -309,13 +315,13 @@
 static int
 output_flush(struct archive_write_filter *f)
 {
-	struct private_data *state = f->data;
+	struct compress *compress = f->data;
 	int ret;
 
 	/* At EOF, write the rest of the buffer. */
-	if (state->bit_offset % 8) {
-		state->code_len = (state->bit_offset % 8 + 7) / 8;
-		ret = output_byte(f, state->bit_buf);
+	if (compress->bit_offset % 8) {
+		compress->code_len = (compress->bit_offset % 8 + 7) / 8;
+		ret = output_byte(f, compress->bit_buf);
 		if (ret != ARCHIVE_OK)
 			return ret;
 	}
@@ -330,7 +336,7 @@
 archive_compressor_compress_write(struct archive_write_filter *f,
     const void *buff, size_t length)
 {
-	struct private_data *state = (struct private_data *)f->data;
+	struct compress *compress = f->data;
 	int i;
 	int ratio;
 	int c, disp, ret;
@@ -341,23 +347,24 @@
 
 	bp = buff;
 
-	if (state->in_count == 0) {
-		state->cur_code = *bp++;
-		++state->in_count;
+	if (compress->in_count == 0) {
+		compress->cur_code = *bp++;
+		++compress->in_count;
 		--length;
 	}
 
 	while (length--) {
 		c = *bp++;
-		state->in_count++;
-		state->cur_fcode = (c << 16) | state->cur_code;
-		i = ((c << HSHIFT) ^ state->cur_code);	/* Xor hashing. */
+		compress->in_count++;
+		compress->cur_fcode = (c << 16) | compress->cur_code;
+		/* Xor hashing. */
+		i = ((c << HSHIFT) ^ compress->cur_code);
 
-		if (state->hashtab[i] == state->cur_fcode) {
-			state->cur_code = state->codetab[i];
+		if (compress->hashtab[i] == compress->cur_fcode) {
+			compress->cur_code = compress->codetab[i];
 			continue;
 		}
-		if (state->hashtab[i] < 0)	/* Empty slot. */
+		if (compress->hashtab[i] < 0)	/* Empty slot. */
 			goto nomatch;
 		/* Secondary hash (after G. Knott). */
 		if (i == 0)
@@ -368,40 +375,40 @@
 		if ((i -= disp) < 0)
 			i += HSIZE;
 
-		if (state->hashtab[i] == state->cur_fcode) {
-			state->cur_code = state->codetab[i];
+		if (compress->hashtab[i] == compress->cur_fcode) {
+			compress->cur_code = compress->codetab[i];
 			continue;
 		}
-		if (state->hashtab[i] >= 0)
+		if (compress->hashtab[i] >= 0)
 			goto probe;
  nomatch:
-		ret = output_code(f, state->cur_code);
+		ret = output_code(f, compress->cur_code);
 		if (ret != ARCHIVE_OK)
 			return ret;
-		state->cur_code = c;
-		if (state->first_free < state->max_maxcode) {
-			state->codetab[i] = state->first_free++;	/* code -> hashtable */
-			state->hashtab[i] = state->cur_fcode;
+		compress->cur_code = c;
+		if (compress->first_free < compress->max_maxcode) {
+			compress->codetab[i] = compress->first_free++;	/* code -> hashtable */
+			compress->hashtab[i] = compress->cur_fcode;
 			continue;
 		}
-		if (state->in_count < state->checkpoint)
+		if (compress->in_count < compress->checkpoint)
 			continue;
 
-		state->checkpoint = state->in_count + CHECK_GAP;
+		compress->checkpoint = compress->in_count + CHECK_GAP;
 
-		if (state->in_count <= 0x007fffff && state->out_count != 0)
-			ratio = (int)(state->in_count * 256 / state->out_count);
-		else if ((ratio = (int)(state->out_count / 256)) == 0)
+		if (compress->in_count <= 0x007fffff && compress->out_count != 0)
+			ratio = (int)(compress->in_count * 256 / compress->out_count);
+		else if ((ratio = (int)(compress->out_count / 256)) == 0)
 			ratio = 0x7fffffff;
 		else
-			ratio = (int)(state->in_count / ratio);
+			ratio = (int)(compress->in_count / ratio);
 
-		if (ratio > state->compress_ratio)
-			state->compress_ratio = ratio;
+		if (ratio > compress->compress_ratio)
+			compress->compress_ratio = ratio;
 		else {
-			state->compress_ratio = 0;
-			memset(state->hashtab, 0xff, sizeof(state->hashtab));
-			state->first_free = FIRST;
+			compress->compress_ratio = 0;
+			memset(compress->hashtab, 0xff, sizeof(compress->hashtab));
+			compress->first_free = FIRST;
 			ret = output_code(f, CLEAR);
 			if (ret != ARCHIVE_OK)
 				return ret;
@@ -418,10 +425,10 @@
 static int
 archive_compressor_compress_close(struct archive_write_filter *f)
 {
-	struct private_data *state = (struct private_data *)f->data;
+	struct compress *compress = f->data;
 	int ret;
 
-	ret = output_code(f, state->cur_code);
+	ret = output_code(f, compress->cur_code);
 	if (ret != ARCHIVE_OK)
 		return ret;
 	ret = output_flush(f);
@@ -430,16 +437,23 @@
 
 	/* Write the last block */
 	ret = __archive_write_filter(f->next_filter,
-	    state->compressed, state->compressed_offset);
+	    compress->compressed, compress->compressed_offset);
 	return (ret);
 }
 
 static int
 archive_compressor_compress_free(struct archive_write_filter *f)
 {
-	struct private_data *state = (struct private_data *)f->data;
-
-	free(state->compressed);
-	free(state);
+	free_data(f->data);
+	f->data = NULL;
 	return (ARCHIVE_OK);
+}
+
+static void
+free_data(struct compress *compress)
+{
+	if (compress != NULL) {
+		free(compress->compressed);
+		free(compress);
+	}
 }
diff --git a/c/archive_write_add_filter_grzip.c b/c/archive_write_add_filter_grzip.c
--- a/c/archive_write_add_filter_grzip.c
+++ b/c/archive_write_add_filter_grzip.c
@@ -35,7 +35,7 @@
 #include "archive.h"
 #include "archive_write_private.h"
 
-struct write_grzip {
+struct grzip {
 	struct archive_write_program_data *pdata;
 };
 
@@ -46,42 +46,45 @@
 		    const void *, size_t);
 static int archive_write_grzip_close(struct archive_write_filter *);
 static int archive_write_grzip_free(struct archive_write_filter *);
+static void free_data(struct grzip *);
 
 int
-archive_write_add_filter_grzip(struct archive *_a)
+archive_write_add_filter_grzip(struct archive *a)
 {
-	struct archive_write_filter *f = __archive_write_allocate_filter(_a);
-	struct write_grzip *data;
+	struct archive_write_filter *f;
+	struct grzip *grzip;
 
-	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+	archive_check_magic(a, ARCHIVE_WRITE_MAGIC,
 	    ARCHIVE_STATE_NEW, "archive_write_add_filter_grzip");
 
-	data = calloc(1, sizeof(*data));
-	if (data == NULL) {
-		archive_set_error(_a, ENOMEM, "Can't allocate memory");
-		return (ARCHIVE_FATAL);
-	}
-	data->pdata = __archive_write_program_allocate("grzip");
-	if (data->pdata == NULL) {
-		free(data);
-		archive_set_error(_a, ENOMEM, "Can't allocate memory");
-		return (ARCHIVE_FATAL);
-	}
+	grzip = calloc(1, sizeof(*grzip));
+	if (grzip == NULL)
+		goto memerr;
+	grzip->pdata = __archive_write_program_allocate("grzip");
+	if (grzip->pdata == NULL)
+		goto memerr;
 
+	f = __archive_write_allocate_filter(a);
+	if (f == NULL)
+		goto memerr;
 	f->name = "grzip";
 	f->code = ARCHIVE_FILTER_GRZIP;
-	f->data = data;
-	f->open = archive_write_grzip_open;
+	f->data = grzip;
 	f->options = archive_write_grzip_options;
+	f->open = archive_write_grzip_open;
 	f->write = archive_write_grzip_write;
 	f->close = archive_write_grzip_close;
 	f->free = archive_write_grzip_free;
 
 	/* Note: This filter always uses an external program, so we
 	 * return "warn" to inform of the fact. */
-	archive_set_error(_a, ARCHIVE_ERRNO_MISC,
+	archive_set_error(a, ARCHIVE_ERRNO_MISC,
 	    "Using external grzip program for grzip compression");
 	return (ARCHIVE_WARN);
+memerr:
+	free_data(grzip);
+	archive_set_error(a, ENOMEM, "Can't allocate memory");
+	return (ARCHIVE_FATAL);
 }
 
 static int
@@ -100,34 +103,41 @@
 static int
 archive_write_grzip_open(struct archive_write_filter *f)
 {
-	struct write_grzip *data = (struct write_grzip *)f->data;
+	struct grzip *grzip = f->data;
 
-	return __archive_write_program_open(f, data->pdata, "grzip");
+	return __archive_write_program_open(f, grzip->pdata, "grzip");
 }
 
 static int
 archive_write_grzip_write(struct archive_write_filter *f,
     const void *buff, size_t length)
 {
-	struct write_grzip *data = (struct write_grzip *)f->data;
+	struct grzip *grzip = f->data;
 
-	return __archive_write_program_write(f, data->pdata, buff, length);
+	return __archive_write_program_write(f, grzip->pdata, buff, length);
 }
 
 static int
 archive_write_grzip_close(struct archive_write_filter *f)
 {
-	struct write_grzip *data = (struct write_grzip *)f->data;
+	struct grzip *grzip = f->data;
 
-	return __archive_write_program_close(f, data->pdata);
+	return __archive_write_program_close(f, grzip->pdata);
 }
 
 static int
 archive_write_grzip_free(struct archive_write_filter *f)
 {
-	struct write_grzip *data = (struct write_grzip *)f->data;
-
-	__archive_write_program_free(data->pdata);
-	free(data);
+	free_data(f->data);
+	f->data = NULL;
 	return (ARCHIVE_OK);
+}
+
+static void
+free_data(struct grzip *grzip)
+{
+	if (grzip != NULL) {
+		__archive_write_program_free(grzip->pdata);
+		free(grzip);
+	}
 }
diff --git a/c/archive_write_add_filter_gzip.c b/c/archive_write_add_filter_gzip.c
--- a/c/archive_write_add_filter_gzip.c
+++ b/c/archive_write_add_filter_gzip.c
@@ -56,7 +56,7 @@
 
 /* Don't compile this if we don't have zlib. */
 
-struct private_data {
+struct gzip {
 	int		 compression_level;
 	int		 timestamp;
 	char	*original_filename;
@@ -87,65 +87,66 @@
 static int archive_compressor_gzip_free(struct archive_write_filter *);
 #ifdef HAVE_ZLIB_H
 static int drive_compressor(struct archive_write_filter *,
-		    struct private_data *, int finishing);
+		    struct gzip *, int finishing);
 #endif
+static void free_data(struct gzip *);
 
 
 /*
  * Add a gzip compression filter to this write handle.
  */
 int
-archive_write_add_filter_gzip(struct archive *_a)
+archive_write_add_filter_gzip(struct archive *a)
 {
-	struct archive_write *a = (struct archive_write *)_a;
-	struct archive_write_filter *f = __archive_write_allocate_filter(_a);
-	struct private_data *data;
-	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
-	    ARCHIVE_STATE_NEW, "archive_write_add_filter_gzip");
+	struct archive_write_filter *f;
+	struct gzip *gzip;
+	int r;
 
-	data = calloc(1, sizeof(*data));
-	if (data == NULL) {
-		archive_set_error(&a->archive, ENOMEM, "Out of memory");
-		return (ARCHIVE_FATAL);
-	}
-	f->data = data;
-	f->open = &archive_compressor_gzip_open;
-	f->options = &archive_compressor_gzip_options;
-	f->close = &archive_compressor_gzip_close;
-	f->free = &archive_compressor_gzip_free;
-	f->code = ARCHIVE_FILTER_GZIP;
-	f->name = "gzip";
+	archive_check_magic(a, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_write_add_filter_gzip");
 
-	data->original_filename = NULL;
+	gzip = calloc(1, sizeof(*gzip));
+	if (gzip == NULL)
+		goto memerr;
+	gzip->original_filename = NULL;
 #ifdef HAVE_ZLIB_H
-	data->compression_level = Z_DEFAULT_COMPRESSION;
-	return (ARCHIVE_OK);
+	gzip->compression_level = Z_DEFAULT_COMPRESSION;
+
+	r = ARCHIVE_OK;
 #else
-	data->pdata = __archive_write_program_allocate("gzip");
-	if (data->pdata == NULL) {
-		free(data);
-		archive_set_error(&a->archive, ENOMEM, "Out of memory");
-		return (ARCHIVE_FATAL);
-	}
-	data->compression_level = 0;
-	archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+	gzip->pdata = __archive_write_program_allocate("gzip");
+	if (gzip->pdata == NULL)
+		goto memerr;
+	gzip->compression_level = 0;
+
+	archive_set_error(a, ARCHIVE_ERRNO_MISC,
 	    "Using external gzip program");
-	return (ARCHIVE_WARN);
+	r = ARCHIVE_WARN;
 #endif
+
+	f = __archive_write_allocate_filter(a);
+	if (f == NULL)
+		goto memerr;
+	f->name = "gzip";
+	f->code = ARCHIVE_FILTER_GZIP;
+	f->data = gzip;
+	f->options = archive_compressor_gzip_options;
+	f->open = archive_compressor_gzip_open;
+	f->write = archive_compressor_gzip_write;
+	f->close = archive_compressor_gzip_close;
+	f->free = archive_compressor_gzip_free;
+
+	return (r);
+memerr:
+	free_data(gzip);
+	archive_set_error(a, ENOMEM, "Out of memory");
+	return (ARCHIVE_FATAL);
 }
 
 static int
 archive_compressor_gzip_free(struct archive_write_filter *f)
 {
-	struct private_data *data = (struct private_data *)f->data;
-
-#ifdef HAVE_ZLIB_H
-	free(data->compressed);
-#else
-	__archive_write_program_free(data->pdata);
-#endif
-	free((void*)data->original_filename);
-	free(data);
+	free_data(f->data);
 	f->data = NULL;
 	return (ARCHIVE_OK);
 }
@@ -157,7 +158,7 @@
 archive_compressor_gzip_options(struct archive_write_filter *f, const char *key,
     const char *value)
 {
-	struct private_data *data = (struct private_data *)f->data;
+	struct gzip *gzip = f->data;
 
 	if (strcmp(key, "compression-level") == 0) {
 		if (value == NULL || !(value[0] >= '0' && value[0] <= '9') ||
@@ -166,19 +167,19 @@
 			    "compression-level invalid");
 			return (ARCHIVE_FAILED);
 		}
-		data->compression_level = value[0] - '0';
+		gzip->compression_level = value[0] - '0';
 		return (ARCHIVE_OK);
 	}
 	if (strcmp(key, "timestamp") == 0) {
-		data->timestamp = (value == NULL)?-1:1;
+		gzip->timestamp = (value == NULL)?-1:1;
 		return (ARCHIVE_OK);
 	}
 	if (strcmp(key, "original-filename") == 0) {
-		free((void*)data->original_filename);
-		data->original_filename = NULL;
+		free((void*)gzip->original_filename);
+		gzip->original_filename = NULL;
 		if (value) {
-			data->original_filename = strdup(value);
-			if (data->original_filename == NULL)
+			gzip->original_filename = strdup(value);
+			if (gzip->original_filename == NULL)
 				return (ARCHIVE_FAILED);
 		}
 		return (ARCHIVE_OK);
@@ -197,11 +198,11 @@
 static int
 archive_compressor_gzip_open(struct archive_write_filter *f)
 {
-	struct private_data *data = (struct private_data *)f->data;
+	struct gzip *gzip = f->data;
 	int ret = ARCHIVE_OK;
 	int init_success;
 
-	if (data->compressed == NULL) {
+	if (gzip->compressed == NULL) {
 		size_t bs = 65536, bpb;
 		if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {
 			/* Buffer size should be a multiple number of
@@ -212,60 +213,60 @@
 			else if (bpb != 0)
 				bs -= bs % bpb;
 		}
-		data->compressed_buffer_size = bs;
-		data->compressed = malloc(data->compressed_buffer_size);
-		if (data->compressed == NULL) {
+		gzip->compressed_buffer_size = bs;
+		gzip->compressed = malloc(gzip->compressed_buffer_size);
+		if (gzip->compressed == NULL) {
 			archive_set_error(f->archive, ENOMEM,
 			    "Can't allocate data for compression buffer");
 			return (ARCHIVE_FATAL);
 		}
 	}
 
-	data->crc = crc32(0L, NULL, 0);
-	data->stream.next_out = data->compressed;
-	data->stream.avail_out = (uInt)data->compressed_buffer_size;
+	gzip->crc = crc32(0L, NULL, 0);
+	gzip->stream.next_out = gzip->compressed;
+	gzip->stream.avail_out = (uInt)gzip->compressed_buffer_size;
 
 	/* Prime output buffer with a gzip header. */
-	data->compressed[0] = 0x1f; /* GZip signature bytes */
-	data->compressed[1] = 0x8b;
-	data->compressed[2] = 0x08; /* "Deflate" compression */
-	data->compressed[3] = 0x00; /* Flags */
-	if (data->timestamp >= 0) {
+	gzip->compressed[0] = 0x1f; /* GZip signature bytes */
+	gzip->compressed[1] = 0x8b;
+	gzip->compressed[2] = 0x08; /* "Deflate" compression */
+	gzip->compressed[3] = 0x00; /* Flags */
+	if (gzip->timestamp >= 0) {
 		uint32_t t = (uint32_t)time(NULL);
-		archive_le32enc(data->compressed + 4, t); /* Timestamp */
+		archive_le32enc(gzip->compressed + 4, t); /* Timestamp */
 	} else {
-		memset(&data->compressed[4], 0, 4);
+		memset(&gzip->compressed[4], 0, 4);
 	}
-	if (data->compression_level == 9) {
-		data->compressed[8] = 2;
-	} else if(data->compression_level == 1) {
-		data->compressed[8] = 4;
+	if (gzip->compression_level == 9) {
+		gzip->compressed[8] = 2;
+	} else if(gzip->compression_level == 1) {
+		gzip->compressed[8] = 4;
 	} else {
-		data->compressed[8] = 0;
+		gzip->compressed[8] = 0;
 	}
-	data->compressed[9] = 3; /* OS=Unix */
-	data->stream.next_out += 10;
-	data->stream.avail_out -= 10;
+	gzip->compressed[9] = 3; /* OS=Unix */
+	gzip->stream.next_out += 10;
+	gzip->stream.avail_out -= 10;
 
-	if (data->original_filename != NULL) {
+	if (gzip->original_filename != NULL) {
 		/* Limit "original filename" to 32k or the
 		 * remaining space in the buffer, whichever is smaller.
 		 */
-		size_t ofn_length = strlen(data->original_filename);
+		size_t ofn_length = strlen(gzip->original_filename);
 		size_t ofn_max_length = 32768;
-		size_t ofn_space_available = data->compressed
-			+ data->compressed_buffer_size
-			- data->stream.next_out
+		size_t ofn_space_available = gzip->compressed
+			+ gzip->compressed_buffer_size
+			- gzip->stream.next_out
 			- 1;
 		if (ofn_max_length > ofn_space_available) {
 			ofn_max_length = ofn_space_available;
 		}
 		if (ofn_length < ofn_max_length) {
-			data->compressed[3] |= 0x8;
-			strcpy((char*)data->compressed + 10,
-			       data->original_filename);
-			data->stream.next_out += ofn_length + 1;
-			data->stream.avail_out -= ofn_length + 1;
+			gzip->compressed[3] |= 0x8;
+			strcpy((char*)gzip->compressed + 10,
+			       gzip->original_filename);
+			gzip->stream.next_out += ofn_length + 1;
+			gzip->stream.avail_out -= ofn_length + 1;
 		} else {
 			archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
 					  "Gzip 'Original Filename' ignored because it is too long");
@@ -273,18 +274,15 @@
 		}
 	}
 
-	f->write = archive_compressor_gzip_write;
-
 	/* Initialize compression library. */
-	init_success = deflateInit2(&(data->stream),
-	    data->compression_level,
+	init_success = deflateInit2(&(gzip->stream),
+	    gzip->compression_level,
 	    Z_DEFLATED,
 	    -15 /* < 0 to suppress zlib header */,
 	    8,
 	    Z_DEFAULT_STRATEGY);
 
 	if (init_success == Z_OK) {
-		f->data = data;
 		return (ret);
 	}
 
@@ -320,17 +318,17 @@
 archive_compressor_gzip_write(struct archive_write_filter *f, const void *buff,
     size_t length)
 {
-	struct private_data *data = (struct private_data *)f->data;
+	struct gzip *gzip = f->data;
 	int ret;
 
 	/* Update statistics */
-	data->crc = crc32(data->crc, (const Bytef *)buff, (uInt)length);
-	data->total_in += length;
+	gzip->crc = crc32(gzip->crc, (const Bytef *)buff, (uInt)length);
+	gzip->total_in += length;
 
 	/* Compress input data to output buffer */
-	SET_NEXT_IN(data, buff);
-	data->stream.avail_in = (uInt)length;
-	if ((ret = drive_compressor(f, data, 0)) != ARCHIVE_OK)
+	SET_NEXT_IN(gzip, buff);
+	gzip->stream.avail_in = (uInt)length;
+	if ((ret = drive_compressor(f, gzip, 0)) != ARCHIVE_OK)
 		return (ret);
 
 	return (ARCHIVE_OK);
@@ -342,26 +340,26 @@
 static int
 archive_compressor_gzip_close(struct archive_write_filter *f)
 {
+	struct gzip *gzip = f->data;
 	unsigned char trailer[8];
-	struct private_data *data = (struct private_data *)f->data;
 	int ret;
 
 	/* Finish compression cycle */
-	ret = drive_compressor(f, data, 1);
+	ret = drive_compressor(f, gzip, 1);
 	if (ret == ARCHIVE_OK) {
 		/* Write the last compressed data. */
 		ret = __archive_write_filter(f->next_filter,
-		    data->compressed,
-		    data->compressed_buffer_size - data->stream.avail_out);
+		    gzip->compressed,
+		    gzip->compressed_buffer_size - gzip->stream.avail_out);
 	}
 	if (ret == ARCHIVE_OK) {
 		/* Build and write out 8-byte trailer. */
-		archive_le32enc(trailer, data->crc);
-		archive_le32enc(trailer + 4, data->total_in);
+		archive_le32enc(trailer, gzip->crc);
+		archive_le32enc(trailer + 4, gzip->total_in);
 		ret = __archive_write_filter(f->next_filter, trailer, 8);
 	}
 
-	switch (deflateEnd(&(data->stream))) {
+	switch (deflateEnd(&(gzip->stream))) {
 	case Z_OK:
 		break;
 	default:
@@ -381,34 +379,34 @@
  */
 static int
 drive_compressor(struct archive_write_filter *f,
-    struct private_data *data, int finishing)
+    struct gzip *gzip, int finishing)
 {
 	int ret;
 
 	for (;;) {
-		if (data->stream.avail_out == 0) {
+		if (gzip->stream.avail_out == 0) {
 			ret = __archive_write_filter(f->next_filter,
-			    data->compressed,
-			    data->compressed_buffer_size);
+			    gzip->compressed,
+			    gzip->compressed_buffer_size);
 			if (ret != ARCHIVE_OK)
 				return (ARCHIVE_FATAL);
-			data->stream.next_out = data->compressed;
-			data->stream.avail_out =
-			    (uInt)data->compressed_buffer_size;
+			gzip->stream.next_out = gzip->compressed;
+			gzip->stream.avail_out =
+			    (uInt)gzip->compressed_buffer_size;
 		}
 
 		/* If there's nothing to do, we're done. */
-		if (!finishing && data->stream.avail_in == 0)
+		if (!finishing && gzip->stream.avail_in == 0)
 			return (ARCHIVE_OK);
 
-		ret = deflate(&(data->stream),
+		ret = deflate(&(gzip->stream),
 		    finishing ? Z_FINISH : Z_NO_FLUSH );
 
 		switch (ret) {
 		case Z_OK:
 			/* In non-finishing case, check if compressor
 			 * consumed everything */
-			if (!finishing && data->stream.avail_in == 0)
+			if (!finishing && gzip->stream.avail_in == 0)
 				return (ARCHIVE_OK);
 			/* In finishing case, this return always means
 			 * there's more work */
@@ -427,12 +425,22 @@
 	}
 }
 
+static void
+free_data(struct gzip *gzip)
+{
+	if (gzip != NULL) {
+		free(gzip->compressed);
+		free(gzip->original_filename);
+		free(gzip);
+	}
+}
+
 #else /* HAVE_ZLIB_H */
 
 static int
 archive_compressor_gzip_open(struct archive_write_filter *f)
 {
-	struct private_data *data = (struct private_data *)f->data;
+	struct gzip *gzip = f->data;
 	struct archive_string as;
 	int r;
 
@@ -440,19 +448,18 @@
 	archive_strcpy(&as, "gzip");
 
 	/* Specify compression level. */
-	if (data->compression_level > 0) {
+	if (gzip->compression_level > 0) {
 		archive_strcat(&as, " -");
-		archive_strappend_char(&as, '0' + data->compression_level);
+		archive_strappend_char(&as, '0' + gzip->compression_level);
 	}
-	if (data->timestamp < 0)
+	if (gzip->timestamp < 0)
 		/* Do not save timestamp. */
 		archive_strcat(&as, " -n");
-	else if (data->timestamp > 0)
+	else if (gzip->timestamp > 0)
 		/* Save timestamp. */
 		archive_strcat(&as, " -N");
 
-	f->write = archive_compressor_gzip_write;
-	r = __archive_write_program_open(f, data->pdata, as.s);
+	r = __archive_write_program_open(f, gzip->pdata, as.s);
 	archive_string_free(&as);
 	return (r);
 }
@@ -461,17 +468,26 @@
 archive_compressor_gzip_write(struct archive_write_filter *f, const void *buff,
     size_t length)
 {
-	struct private_data *data = (struct private_data *)f->data;
+	struct gzip *gzip = f->data;
 
-	return __archive_write_program_write(f, data->pdata, buff, length);
+	return __archive_write_program_write(f, gzip->pdata, buff, length);
 }
 
 static int
 archive_compressor_gzip_close(struct archive_write_filter *f)
 {
-	struct private_data *data = (struct private_data *)f->data;
+	struct gzip *gzip = f->data;
 
-	return __archive_write_program_close(f, data->pdata);
+	return __archive_write_program_close(f, gzip->pdata);
 }
 
+static void
+free_data(struct gzip *gzip)
+{
+	if (gzip != NULL) {
+		__archive_write_program_free(gzip->pdata);
+		free(gzip->original_filename);
+		free(gzip);
+	}
+}
 #endif /* HAVE_ZLIB_H */
diff --git a/c/archive_write_add_filter_lrzip.c b/c/archive_write_add_filter_lrzip.c
--- a/c/archive_write_add_filter_lrzip.c
+++ b/c/archive_write_add_filter_lrzip.c
@@ -39,7 +39,7 @@
 #include "archive_string.h"
 #include "archive_write_private.h"
 
-struct write_lrzip {
+struct lrzip {
 	struct archive_write_program_data *pdata;
 	int	compression_level;
 	enum { lzma = 0, bzip2, gzip, lzo, none, zpaq } compression;
@@ -52,49 +52,52 @@
 		    const void *, size_t);
 static int archive_write_lrzip_close(struct archive_write_filter *);
 static int archive_write_lrzip_free(struct archive_write_filter *);
+static void free_data(struct lrzip *);
 
 int
-archive_write_add_filter_lrzip(struct archive *_a)
+archive_write_add_filter_lrzip(struct archive *a)
 {
-	struct archive_write_filter *f = __archive_write_allocate_filter(_a);
-	struct write_lrzip *data;
+	struct archive_write_filter *f;
+	struct lrzip *lrzip;
 
-	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+	archive_check_magic(a, ARCHIVE_WRITE_MAGIC,
 	    ARCHIVE_STATE_NEW, "archive_write_add_filter_lrzip");
 
-	data = calloc(1, sizeof(*data));
-	if (data == NULL) {
-		archive_set_error(_a, ENOMEM, "Can't allocate memory");
-		return (ARCHIVE_FATAL);
-	}
-	data->pdata = __archive_write_program_allocate("lrzip");
-	if (data->pdata == NULL) {
-		free(data);
-		archive_set_error(_a, ENOMEM, "Can't allocate memory");
-		return (ARCHIVE_FATAL);
-	}
+	lrzip = calloc(1, sizeof(*lrzip));
+	if (lrzip == NULL)
+		goto memerr;
+	lrzip->pdata = __archive_write_program_allocate("lrzip");
+	if (lrzip->pdata == NULL)
+		goto memerr;
 
+	f = __archive_write_allocate_filter(a);
+	if (f == NULL)
+		goto memerr;
 	f->name = "lrzip";
 	f->code = ARCHIVE_FILTER_LRZIP;
-	f->data = data;
-	f->open = archive_write_lrzip_open;
+	f->data = lrzip;
 	f->options = archive_write_lrzip_options;
+	f->open = archive_write_lrzip_open;
 	f->write = archive_write_lrzip_write;
 	f->close = archive_write_lrzip_close;
 	f->free = archive_write_lrzip_free;
 
 	/* Note: This filter always uses an external program, so we
 	 * return "warn" to inform of the fact. */
-	archive_set_error(_a, ARCHIVE_ERRNO_MISC,
+	archive_set_error(a, ARCHIVE_ERRNO_MISC,
 	    "Using external lrzip program for lrzip compression");
 	return (ARCHIVE_WARN);
+memerr:
+	free_data(lrzip);
+	archive_set_error(a, ENOMEM, "Can't allocate memory");
+	return (ARCHIVE_FATAL);
 }
 
 static int
 archive_write_lrzip_options(struct archive_write_filter *f, const char *key,
     const char *value)
 {
-	struct write_lrzip *data = (struct write_lrzip *)f->data;
+	struct lrzip *lrzip = f->data;
 
 	if (strcmp(key, "compression") == 0) {
 		if (value == NULL) {
@@ -103,15 +106,15 @@
 			return (ARCHIVE_FAILED);
 		}
 		else if (strcmp(value, "bzip2") == 0)
-			data->compression = bzip2;
+			lrzip->compression = bzip2;
 		else if (strcmp(value, "gzip") == 0)
-			data->compression = gzip;
+			lrzip->compression = gzip;
 		else if (strcmp(value, "lzo") == 0)
-			data->compression = lzo;
+			lrzip->compression = lzo;
 		else if (strcmp(value, "none") == 0)
-			data->compression = none;
+			lrzip->compression = none;
 		else if (strcmp(value, "zpaq") == 0)
-			data->compression = zpaq;
+			lrzip->compression = zpaq;
 		else {
 			archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
 			    "compression invalid");
@@ -125,7 +128,7 @@
 			    "compression-level invalid");
 			return (ARCHIVE_FAILED);
 		}
-		data->compression_level = value[0] - '0';
+		lrzip->compression_level = value[0] - '0';
 		return (ARCHIVE_OK);
 	}
 	/* Note: The "warn" return is just to inform the options
@@ -137,7 +140,7 @@
 static int
 archive_write_lrzip_open(struct archive_write_filter *f)
 {
-	struct write_lrzip *data = (struct write_lrzip *)f->data;
+	struct lrzip *lrzip = f->data;
 	struct archive_string as;
 	int r;
 
@@ -145,7 +148,7 @@
 	archive_strcpy(&as, "lrzip -q");
 
 	/* Specify compression type. */
-	switch (data->compression) {
+	switch (lrzip->compression) {
 	case lzma:/* default compression */
 		break;
 	case bzip2:
@@ -166,12 +169,12 @@
 	}
 
 	/* Specify compression level. */
-	if (data->compression_level > 0) {
+	if (lrzip->compression_level > 0) {
 		archive_strcat(&as, " -L ");
-		archive_strappend_char(&as, '0' + data->compression_level);
+		archive_strappend_char(&as, '0' + lrzip->compression_level);
 	}
 
-	r = __archive_write_program_open(f, data->pdata, as.s);
+	r = __archive_write_program_open(f, lrzip->pdata, as.s);
 	archive_string_free(&as);
 	return (r);
 }
@@ -180,25 +183,32 @@
 archive_write_lrzip_write(struct archive_write_filter *f,
     const void *buff, size_t length)
 {
-	struct write_lrzip *data = (struct write_lrzip *)f->data;
+	struct lrzip *lrzip = f->data;
 
-	return __archive_write_program_write(f, data->pdata, buff, length);
+	return __archive_write_program_write(f, lrzip->pdata, buff, length);
 }
 
 static int
 archive_write_lrzip_close(struct archive_write_filter *f)
 {
-	struct write_lrzip *data = (struct write_lrzip *)f->data;
+	struct lrzip *lrzip = f->data;
 
-	return __archive_write_program_close(f, data->pdata);
+	return __archive_write_program_close(f, lrzip->pdata);
 }
 
 static int
 archive_write_lrzip_free(struct archive_write_filter *f)
 {
-	struct write_lrzip *data = (struct write_lrzip *)f->data;
-
-	__archive_write_program_free(data->pdata);
-	free(data);
+	free_data(f->data);
+	f->data = NULL;
 	return (ARCHIVE_OK);
+}
+
+static void
+free_data(struct lrzip *lrzip)
+{
+	if (lrzip != NULL) {
+		__archive_write_program_free(lrzip->pdata);
+		free(lrzip);
+	}
 }
diff --git a/c/archive_write_add_filter_lz4.c b/c/archive_write_add_filter_lz4.c
--- a/c/archive_write_add_filter_lz4.c
+++ b/c/archive_write_add_filter_lz4.c
@@ -50,7 +50,7 @@
 
 #define LZ4_MAGICNUMBER	0x184d2204
 
-struct private_data {
+struct lz4 {
 	int		 compression_level;
 	unsigned	 header_written:1;
 	unsigned	 version_number:1;
@@ -85,68 +85,83 @@
 		    const char *, const char *);
 static int archive_filter_lz4_write(struct archive_write_filter *,
 		    const void *, size_t);
+static void free_data(struct lz4 *);
 
 /*
  * Add an lz4 compression filter to this write handle.
  */
 int
-archive_write_add_filter_lz4(struct archive *_a)
+archive_write_add_filter_lz4(struct archive *a)
 {
-	struct archive_write *a = (struct archive_write *)_a;
-	struct archive_write_filter *f = __archive_write_allocate_filter(_a);
-	struct private_data *data;
+	struct archive_write_filter *f;
+	struct lz4 *lz4;
+	int r;
 
-	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+	archive_check_magic(a, ARCHIVE_WRITE_MAGIC,
 	    ARCHIVE_STATE_NEW, "archive_write_add_filter_lz4");
 
-	data = calloc(1, sizeof(*data));
-	if (data == NULL) {
-		archive_set_error(&a->archive, ENOMEM, "Out of memory");
-		return (ARCHIVE_FATAL);
-	}
-
+	lz4 = calloc(1, sizeof(*lz4));
+	if (lz4 == NULL)
+		goto memerr;
 	/*
 	 * Setup default settings.
 	 */
-	data->compression_level = 1;
-	data->version_number = 0x01;
-	data->block_independence = 1;
-	data->block_checksum = 0;
-	data->stream_size = 0;
-	data->stream_checksum = 1;
-	data->preset_dictionary = 0;
-	data->block_maximum_size = 7;
-
-	/*
-	 * Setup a filter setting.
-	 */
-	f->data = data;
-	f->options = &archive_filter_lz4_options;
-	f->close = &archive_filter_lz4_close;
-	f->free = &archive_filter_lz4_free;
-	f->open = &archive_filter_lz4_open;
-	f->code = ARCHIVE_FILTER_LZ4;
-	f->name = "lz4";
+	lz4->version_number = 0x01;
+	lz4->block_independence = 1;
+	lz4->block_checksum = 0;
+	lz4->stream_size = 0;
+	lz4->stream_checksum = 1;
+	lz4->preset_dictionary = 0;
+	lz4->block_maximum_size = 7;
 #if defined(HAVE_LIBLZ4) && LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 2
-	return (ARCHIVE_OK);
+	lz4->compression_level = 1;
+
+	r = ARCHIVE_OK;
 #else
 	/*
 	 * We don't have lz4 library, and execute external lz4 program
 	 * instead.
 	 */
-	data->pdata = __archive_write_program_allocate("lz4");
-	if (data->pdata == NULL) {
-		free(data);
-		archive_set_error(&a->archive, ENOMEM, "Out of memory");
-		return (ARCHIVE_FATAL);
-	}
-	data->compression_level = 0;
-	archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+	lz4->pdata = __archive_write_program_allocate("lz4");
+	if (lz4->pdata == NULL)
+		goto memerr;
+	lz4->compression_level = 0;
+
+	archive_set_error(a, ARCHIVE_ERRNO_MISC,
 	    "Using external lz4 program");
-	return (ARCHIVE_WARN);
+	r = ARCHIVE_WARN;
 #endif
+
+	/*
+	 * Setup a filter setting.
+	 */
+	f = __archive_write_allocate_filter(a);
+	if (f == NULL)
+		goto memerr;
+	f->name = "lz4";
+	f->code = ARCHIVE_FILTER_LZ4;
+	f->data = lz4;
+	f->options = archive_filter_lz4_options;
+	f->open = archive_filter_lz4_open;
+	f->write = archive_filter_lz4_write;
+	f->close = archive_filter_lz4_close;
+	f->free = archive_filter_lz4_free;
+
+	return (r);
+memerr:
+	free_data(lz4);
+	archive_set_error(a, ENOMEM, "Out of memory");
+	return (ARCHIVE_FATAL);
 }
 
+static int
+archive_filter_lz4_free(struct archive_write_filter *f)
+{
+	free_data(f->data);
+	f->data = NULL;
+	return (ARCHIVE_OK);
+}
+
 /*
  * Set write options.
  */
@@ -154,7 +169,7 @@
 archive_filter_lz4_options(struct archive_write_filter *f,
     const char *key, const char *value)
 {
-	struct private_data *data = (struct private_data *)f->data;
+	struct lz4 *lz4 = f->data;
 
 	if (strcmp(key, "compression-level") == 0) {
 		int val;
@@ -173,15 +188,15 @@
 			return (ARCHIVE_FATAL);
 		}
 #endif
-		data->compression_level = val;
+		lz4->compression_level = val;
 		return (ARCHIVE_OK);
 	}
 	if (strcmp(key, "stream-checksum") == 0) {
-		data->stream_checksum = value != NULL;
+		lz4->stream_checksum = value != NULL;
 		return (ARCHIVE_OK);
 	}
 	if (strcmp(key, "block-checksum") == 0) {
-		data->block_checksum = value != NULL;
+		lz4->block_checksum = value != NULL;
 		return (ARCHIVE_OK);
 	}
 	if (strcmp(key, "block-size") == 0) {
@@ -191,11 +206,11 @@
 			    "block-size invalid");
 			return (ARCHIVE_FAILED);
 		}
-		data->block_maximum_size = value[0] - '0';
+		lz4->block_maximum_size = value[0] - '0';
 		return (ARCHIVE_OK);
 	}
 	if (strcmp(key, "block-dependence") == 0) {
-		data->block_independence = value == NULL;
+		lz4->block_independence = value == NULL;
 		return (ARCHIVE_OK);
 	}
 
@@ -225,21 +240,21 @@
 static int
 archive_filter_lz4_open(struct archive_write_filter *f)
 {
-	struct private_data *data = (struct private_data *)f->data;
+	struct lz4 *lz4 = f->data;
 	size_t required_size;
 	static const size_t bkmap[] = { 64 * 1024, 256 * 1024, 1 * 1024 * 1024,
 			   4 * 1024 * 1024 };
 	size_t pre_block_size;
 
-	if (data->block_maximum_size < 4)
-		data->block_size = bkmap[0];
+	if (lz4->block_maximum_size < 4)
+		lz4->block_size = bkmap[0];
 	else
-		data->block_size = bkmap[data->block_maximum_size - 4];
+		lz4->block_size = bkmap[lz4->block_maximum_size - 4];
 
-	required_size = 4 + 15 + 4 + data->block_size + 4 + 4;
-	if (data->out_buffer_size < required_size) {
+	required_size = 4 + 15 + 4 + lz4->block_size + 4 + 4;
+	if (lz4->out_buffer_size < required_size) {
 		size_t bs = required_size, bpb;
-		free(data->out_buffer);
+		free(lz4->out_buffer);
 		if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {
 			/* Buffer size should be a multiple number of
 			 * the bytes per block for performance. */
@@ -251,34 +266,32 @@
 				bs -= bs % bpb;
 			}
 		}
-		data->out_block_size = bs;
+		lz4->out_block_size = bs;
 		bs += required_size;
-		data->out_buffer = malloc(bs);
-		data->out = data->out_buffer;
-		data->out_buffer_size = bs;
+		lz4->out_buffer = malloc(bs);
+		lz4->out = lz4->out_buffer;
+		lz4->out_buffer_size = bs;
 	}
 
-	pre_block_size = (data->block_independence)? 0: 64 * 1024;
-	if (data->in_buffer_size < data->block_size + pre_block_size) {
-		free(data->in_buffer_allocated);
-		data->in_buffer_size = data->block_size;
-		data->in_buffer_allocated =
-		    malloc(data->in_buffer_size + pre_block_size);
-		data->in_buffer = data->in_buffer_allocated + pre_block_size;
-		if (!data->block_independence && data->compression_level >= 3)
-		    data->in_buffer = data->in_buffer_allocated;
-		data->in = data->in_buffer;
-		data->in_buffer_size = data->block_size;
+	pre_block_size = (lz4->block_independence)? 0: 64 * 1024;
+	if (lz4->in_buffer_size < lz4->block_size + pre_block_size) {
+		free(lz4->in_buffer_allocated);
+		lz4->in_buffer_size = lz4->block_size;
+		lz4->in_buffer_allocated =
+		    malloc(lz4->in_buffer_size + pre_block_size);
+		lz4->in_buffer = lz4->in_buffer_allocated + pre_block_size;
+		if (!lz4->block_independence && lz4->compression_level >= 3)
+		    lz4->in_buffer = lz4->in_buffer_allocated;
+		lz4->in = lz4->in_buffer;
+		lz4->in_buffer_size = lz4->block_size;
 	}
 
-	if (data->out_buffer == NULL || data->in_buffer_allocated == NULL) {
+	if (lz4->out_buffer == NULL || lz4->in_buffer_allocated == NULL) {
 		archive_set_error(f->archive, ENOMEM,
 		    "Can't allocate data for compression buffer");
 		return (ARCHIVE_FATAL);
 	}
 
-	f->write = archive_filter_lz4_write;
-
 	return (ARCHIVE_OK);
 }
 
@@ -291,18 +304,18 @@
 archive_filter_lz4_write(struct archive_write_filter *f,
     const void *buff, size_t length)
 {
-	struct private_data *data = (struct private_data *)f->data;
+	struct lz4 *lz4 = f->data;
 	int ret = ARCHIVE_OK;
 	const char *p;
 	size_t remaining;
 	ssize_t size;
 
 	/* If we haven't written a stream descriptor, we have to do it first. */
-	if (!data->header_written) {
+	if (!lz4->header_written) {
 		ret = lz4_write_stream_descriptor(f);
 		if (ret != ARCHIVE_OK)
 			return (ret);
-		data->header_written = 1;
+		lz4->header_written = 1;
 	}
 
 	p = (const char *)buff;
@@ -313,14 +326,14 @@
 		size = lz4_write_one_block(f, p, remaining);
 		if (size < ARCHIVE_OK)
 			return (ARCHIVE_FATAL);
-		l = data->out - data->out_buffer;
-		if (l >= data->out_block_size) {
+		l = lz4->out - lz4->out_buffer;
+		if (l >= lz4->out_block_size) {
 			ret = __archive_write_filter(f->next_filter,
-			    data->out_buffer, data->out_block_size);
-			l -= data->out_block_size;
-			memcpy(data->out_buffer,
-			    data->out_buffer + data->out_block_size, l);
-			data->out = data->out_buffer + l;
+			    lz4->out_buffer, lz4->out_block_size);
+			l -= lz4->out_block_size;
+			memcpy(lz4->out_buffer,
+			    lz4->out_buffer + lz4->out_block_size, l);
+			lz4->out = lz4->out_buffer + l;
 			if (ret < ARCHIVE_WARN)
 				break;
 		}
@@ -337,7 +350,7 @@
 static int
 archive_filter_lz4_close(struct archive_write_filter *f)
 {
-	struct private_data *data = (struct private_data *)f->data;
+	struct lz4 *lz4 = f->data;
 	int ret;
 
 	/* Finish compression cycle. */
@@ -348,77 +361,48 @@
 		 */
 
 		/* Write End Of Stream. */
-		memset(data->out, 0, 4); data->out += 4;
+		memset(lz4->out, 0, 4); lz4->out += 4;
 		/* Write Stream checksum if needed. */
-		if (data->stream_checksum) {
+		if (lz4->stream_checksum) {
 			unsigned int checksum;
 			checksum = __archive_xxhash.XXH32_digest(
-					data->xxh32_state);
-			data->xxh32_state = NULL;
-			archive_le32enc(data->out, checksum);
-			data->out += 4;
+					lz4->xxh32_state);
+			lz4->xxh32_state = NULL;
+			archive_le32enc(lz4->out, checksum);
+			lz4->out += 4;
 		}
 		ret = __archive_write_filter(f->next_filter,
-			    data->out_buffer, data->out - data->out_buffer);
+			    lz4->out_buffer, lz4->out - lz4->out_buffer);
 	}
 	return ret;
 }
 
 static int
-archive_filter_lz4_free(struct archive_write_filter *f)
-{
-	struct private_data *data = (struct private_data *)f->data;
-
-	if (data->lz4_stream != NULL) {
-#ifdef HAVE_LZ4HC_H
-		if (data->compression_level >= 3)
-#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
-			LZ4_freeStreamHC(data->lz4_stream);
-#else
-			LZ4_freeHC(data->lz4_stream);
-#endif
-		else
-#endif
-#if LZ4_VERSION_MINOR >= 3
-			LZ4_freeStream(data->lz4_stream);
-#else
-			LZ4_free(data->lz4_stream);
-#endif
-	}
-	free(data->out_buffer);
-	free(data->in_buffer_allocated);
-	free(data->xxh32_state);
-	free(data);
-	f->data = NULL;
-	return (ARCHIVE_OK);
-}
-
-static int
 lz4_write_stream_descriptor(struct archive_write_filter *f)
 {
-	struct private_data *data = (struct private_data *)f->data;
+	struct lz4 *lz4 = f->data;
 	uint8_t *sd;
 
-	sd = (uint8_t *)data->out;
+	sd = (uint8_t *)lz4->out;
 	/* Write Magic Number. */
 	archive_le32enc(&sd[0], LZ4_MAGICNUMBER);
 	/* FLG */
-	sd[4] = (data->version_number << 6)
-	      | (data->block_independence << 5)
-	      | (data->block_checksum << 4)
-	      | (data->stream_size << 3)
-	      | (data->stream_checksum << 2)
-	      | (data->preset_dictionary << 0);
+	sd[4] = (lz4->version_number << 6)
+	      | (lz4->block_independence << 5)
+	      | (lz4->block_checksum << 4)
+	      | (lz4->stream_size << 3)
+	      | (lz4->stream_checksum << 2)
+	      | (lz4->preset_dictionary << 0);
 	/* BD */
-	sd[5] = (data->block_maximum_size << 4);
+	sd[5] = (lz4->block_maximum_size << 4);
 	sd[6] = (__archive_xxhash.XXH32(&sd[4], 2, 0) >> 8) & 0xff;
-	data->out += 7;
-	if (data->stream_checksum) {
-		data->xxh32_state = __archive_xxhash.XXH32_init(0);
-		if (data->xxh32_state == NULL)
+	lz4->out += 7;
+	if (lz4->stream_checksum) {
+		lz4->xxh32_state = __archive_xxhash.XXH32_init(0);
+		if (lz4->xxh32_state == NULL)
 			return (ARCHIVE_FATAL);
 	} else
-		data->xxh32_state = NULL;
+		lz4->xxh32_state = NULL;
 	return (ARCHIVE_OK);
 }
 
@@ -426,36 +410,36 @@
 lz4_write_one_block(struct archive_write_filter *f, const char *p,
     size_t length)
 {
-	struct private_data *data = (struct private_data *)f->data;
+	struct lz4 *lz4 = f->data;
 	ssize_t r;
 
 	if (p == NULL) {
 		/* Compress remaining uncompressed data. */
-		if (data->in_buffer == data->in)
+		if (lz4->in_buffer == lz4->in)
 			return 0;
 		else {
-			size_t l = data->in - data->in_buffer;
-			r = drive_compressor(f, data->in_buffer, l);
+			size_t l = lz4->in - lz4->in_buffer;
+			r = drive_compressor(f, lz4->in_buffer, l);
 			if (r == ARCHIVE_OK)
 				r = (ssize_t)l;
 		}
-	} else if ((data->block_independence || data->compression_level < 3) &&
-	    data->in_buffer == data->in && length >= data->block_size) {
-		r = drive_compressor(f, p, data->block_size);
+	} else if ((lz4->block_independence || lz4->compression_level < 3) &&
+	    lz4->in_buffer == lz4->in && length >= lz4->block_size) {
+		r = drive_compressor(f, p, lz4->block_size);
 		if (r == ARCHIVE_OK)
-			r = (ssize_t)data->block_size;
+			r = (ssize_t)lz4->block_size;
 	} else {
-		size_t remaining_size = data->in_buffer_size -
-			(data->in - data->in_buffer);
+		size_t remaining_size = lz4->in_buffer_size -
+			(lz4->in - lz4->in_buffer);
 		size_t l = (remaining_size > length)? length: remaining_size;
-		memcpy(data->in, p, l);
-		data->in += l;
+		memcpy(lz4->in, p, l);
+		lz4->in += l;
 		if (l == remaining_size) {
-			r = drive_compressor(f, data->in_buffer,
-			    data->block_size);
+			r = drive_compressor(f, lz4->in_buffer,
+			    lz4->block_size);
 			if (r == ARCHIVE_OK)
 				r = (ssize_t)l;
-			data->in = data->in_buffer;
+			lz4->in = lz4->in_buffer;
 		} else
 			r = (ssize_t)l;
 	}
@@ -474,12 +458,12 @@
 static int
 drive_compressor(struct archive_write_filter *f, const char *p, size_t length)
 {
-	struct private_data *data = (struct private_data *)f->data;
+	struct lz4 *lz4 = f->data;
 
-	if (data->stream_checksum)
-		__archive_xxhash.XXH32_update(data->xxh32_state,
+	if (lz4->stream_checksum)
+		__archive_xxhash.XXH32_update(lz4->xxh32_state,
 			p, (int)length);
-	if (data->block_independence)
+	if (lz4->block_independence)
 		return drive_compressor_independence(f, p, length);
 	else
 		return drive_compressor_dependence(f, p, length);
@@ -489,48 +473,48 @@
 drive_compressor_independence(struct archive_write_filter *f, const char *p,
     size_t length)
 {
-	struct private_data *data = (struct private_data *)f->data;
+	struct lz4 *lz4 = f->data;
 	unsigned int outsize;
 
 #ifdef HAVE_LZ4HC_H
-	if (data->compression_level >= 3)
+	if (lz4->compression_level >= 3)
 #if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
-		outsize = LZ4_compress_HC(p, data->out + 4,
-		     (int)length, (int)data->block_size,
-		    data->compression_level);
+		outsize = LZ4_compress_HC(p, lz4->out + 4,
+		     (int)length, (int)lz4->block_size,
+		    lz4->compression_level);
 #else
-		outsize = LZ4_compressHC2_limitedOutput(p, data->out + 4,
-		    (int)length, (int)data->block_size,
-		    data->compression_level);
+		outsize = LZ4_compressHC2_limitedOutput(p, lz4->out + 4,
+		    (int)length, (int)lz4->block_size,
+		    lz4->compression_level);
 #endif
 	else
 #endif
 #if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
-		outsize = LZ4_compress_default(p, data->out + 4,
-		    (int)length, (int)data->block_size);
+		outsize = LZ4_compress_default(p, lz4->out + 4,
+		    (int)length, (int)lz4->block_size);
 #else
-		outsize = LZ4_compress_limitedOutput(p, data->out + 4,
-		    (int)length, (int)data->block_size);
+		outsize = LZ4_compress_limitedOutput(p, lz4->out + 4,
+		    (int)length, (int)lz4->block_size);
 #endif
 
 	if (outsize) {
 		/* The buffer is compressed. */
-		archive_le32enc(data->out, outsize);
-		data->out += 4;
+		archive_le32enc(lz4->out, outsize);
+		lz4->out += 4;
 	} else {
 		/* The buffer is not compressed. The compressed size was
 		 * bigger than its uncompressed size. */
-		archive_le32enc(data->out, (uint32_t)(length | 0x80000000));
-		data->out += 4;
-		memcpy(data->out, p, length);
+		archive_le32enc(lz4->out, (uint32_t)(length | 0x80000000));
+		lz4->out += 4;
+		memcpy(lz4->out, p, length);
 		outsize = (uint32_t)length;
 	}
-	data->out += outsize;
-	if (data->block_checksum) {
+	lz4->out += outsize;
+	if (lz4->block_checksum) {
 		unsigned int checksum =
-		    __archive_xxhash.XXH32(data->out - outsize, outsize, 0);
-		archive_le32enc(data->out, checksum);
-		data->out += 4;
+		    __archive_xxhash.XXH32(lz4->out - outsize, outsize, 0);
+		archive_le32enc(lz4->out, checksum);
+		lz4->out += 4;
 	}
 	return (ARCHIVE_OK);
 }
@@ -539,21 +523,21 @@
 drive_compressor_dependence(struct archive_write_filter *f, const char *p,
     size_t length)
 {
-	struct private_data *data = (struct private_data *)f->data;
+	struct lz4 *lz4 = f->data;
 	int outsize;
 
 #define DICT_SIZE	(64 * 1024)
 #ifdef HAVE_LZ4HC_H
-	if (data->compression_level >= 3) {
-		if (data->lz4_stream == NULL) {
+	if (lz4->compression_level >= 3) {
+		if (lz4->lz4_stream == NULL) {
 #if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
-			data->lz4_stream = LZ4_createStreamHC();
-			LZ4_resetStreamHC(data->lz4_stream, data->compression_level);
+			lz4->lz4_stream = LZ4_createStreamHC();
+			LZ4_resetStreamHC(lz4->lz4_stream, lz4->compression_level);
 #else
-			data->lz4_stream =
-			    LZ4_createHC(data->in_buffer_allocated);
+			lz4->lz4_stream =
+			    LZ4_createHC(lz4->in_buffer_allocated);
 #endif
-			if (data->lz4_stream == NULL) {
+			if (lz4->lz4_stream == NULL) {
 				archive_set_error(f->archive, ENOMEM,
 				    "Can't allocate data for compression"
 				    " buffer");
@@ -561,23 +545,23 @@
 			}
 		}
 		else
-			LZ4_loadDictHC(data->lz4_stream, data->in_buffer_allocated, DICT_SIZE);
+			LZ4_loadDictHC(lz4->lz4_stream, lz4->in_buffer_allocated, DICT_SIZE);
 
 #if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
 		outsize = LZ4_compress_HC_continue(
-		    data->lz4_stream, p, data->out + 4, (int)length,
-		    (int)data->block_size);
+		    lz4->lz4_stream, p, lz4->out + 4, (int)length,
+		    (int)lz4->block_size);
 #else
 		outsize = LZ4_compressHC2_limitedOutput_continue(
-		    data->lz4_stream, p, data->out + 4, (int)length,
-		    (int)data->block_size, data->compression_level);
+		    lz4->lz4_stream, p, lz4->out + 4, (int)length,
+		    (int)lz4->block_size, lz4->compression_level);
 #endif
 	} else
 #endif
 	{
-		if (data->lz4_stream == NULL) {
-			data->lz4_stream = LZ4_createStream();
-			if (data->lz4_stream == NULL) {
+		if (lz4->lz4_stream == NULL) {
+			lz4->lz4_stream = LZ4_createStream();
+			if (lz4->lz4_stream == NULL) {
 				archive_set_error(f->archive, ENOMEM,
 				    "Can't allocate data for compression"
 				    " buffer");
@@ -585,64 +569,91 @@
 			}
 		}
 		else
-			LZ4_loadDict(data->lz4_stream, data->in_buffer_allocated, DICT_SIZE);
+			LZ4_loadDict(lz4->lz4_stream, lz4->in_buffer_allocated, DICT_SIZE);
 
 #if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
 		outsize = LZ4_compress_fast_continue(
-		    data->lz4_stream, p, data->out + 4, (int)length,
-		    (int)data->block_size, 1);
+		    lz4->lz4_stream, p, lz4->out + 4, (int)length,
+		    (int)lz4->block_size, 1);
 #else
 		outsize = LZ4_compress_limitedOutput_continue(
-		    data->lz4_stream, p, data->out + 4, (int)length,
-		    (int)data->block_size);
+		    lz4->lz4_stream, p, lz4->out + 4, (int)length,
+		    (int)lz4->block_size);
 #endif
 	}
 
 	if (outsize) {
 		/* The buffer is compressed. */
-		archive_le32enc(data->out, outsize);
-		data->out += 4;
+		archive_le32enc(lz4->out, outsize);
+		lz4->out += 4;
 	} else {
 		/* The buffer is not compressed. The compressed size was
 		 * bigger than its uncompressed size. */
-		archive_le32enc(data->out, (uint32_t)(length | 0x80000000));
-		data->out += 4;
-		memcpy(data->out, p, length);
+		archive_le32enc(lz4->out, (uint32_t)(length | 0x80000000));
+		lz4->out += 4;
+		memcpy(lz4->out, p, length);
 		outsize = (uint32_t)length;
 	}
-	data->out += outsize;
-	if (data->block_checksum) {
+	lz4->out += outsize;
+	if (lz4->block_checksum) {
 		unsigned int checksum =
-		    __archive_xxhash.XXH32(data->out - outsize, outsize, 0);
-		archive_le32enc(data->out, checksum);
-		data->out += 4;
+		    __archive_xxhash.XXH32(lz4->out - outsize, outsize, 0);
+		archive_le32enc(lz4->out, checksum);
+		lz4->out += 4;
 	}
 
-	if (length == data->block_size) {
+	if (length == lz4->block_size) {
 #ifdef HAVE_LZ4HC_H
-		if (data->compression_level >= 3) {
+		if (lz4->compression_level >= 3) {
 #if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
-			LZ4_saveDictHC(data->lz4_stream, data->in_buffer_allocated, DICT_SIZE);
+			LZ4_saveDictHC(lz4->lz4_stream, lz4->in_buffer_allocated, DICT_SIZE);
 #else
-			LZ4_slideInputBufferHC(data->lz4_stream);
+			LZ4_slideInputBufferHC(lz4->lz4_stream);
 #endif
-			data->in_buffer = data->in_buffer_allocated + DICT_SIZE;
+			lz4->in_buffer = lz4->in_buffer_allocated + DICT_SIZE;
 		}
 		else
 #endif
-			LZ4_saveDict(data->lz4_stream,
-			    data->in_buffer_allocated, DICT_SIZE);
+			LZ4_saveDict(lz4->lz4_stream,
+			    lz4->in_buffer_allocated, DICT_SIZE);
 #undef DICT_SIZE
 	}
 	return (ARCHIVE_OK);
 }
 
+static void
+free_data(struct lz4 *lz4)
+{
+	if (lz4 != NULL) {
+		if (lz4->lz4_stream != NULL) {
+#ifdef HAVE_LZ4HC_H
+			if (lz4->compression_level >= 3)
+#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
+				LZ4_freeStreamHC(lz4->lz4_stream);
+#else
+				LZ4_freeHC(lz4->lz4_stream);
+#endif
+			else
+#endif
+#if LZ4_VERSION_MINOR >= 3
+				LZ4_freeStream(lz4->lz4_stream);
+#else
+				LZ4_free(lz4->lz4_stream);
+#endif
+		}
+		free(lz4->out_buffer);
+		free(lz4->in_buffer_allocated);
+		free(lz4->xxh32_state);
+		free(lz4);
+	}
+}
+
 #else /* HAVE_LIBLZ4 */
 
 static int
 archive_filter_lz4_open(struct archive_write_filter *f)
 {
-	struct private_data *data = (struct private_data *)f->data;
+	struct lz4 *lz4 = f->data;
 	struct archive_string as;
 	int r;
 
@@ -650,24 +661,22 @@
 	archive_strcpy(&as, "lz4 -z -q -q");
 
 	/* Specify a compression level. */
-	if (data->compression_level > 0) {
+	if (lz4->compression_level > 0) {
 		archive_strcat(&as, " -");
-		archive_strappend_char(&as, '0' + data->compression_level);
+		archive_strappend_char(&as, '0' + lz4->compression_level);
 	}
 	/* Specify a block size. */
 	archive_strcat(&as, " -B");
-	archive_strappend_char(&as, '0' + data->block_maximum_size);
+	archive_strappend_char(&as, '0' + lz4->block_maximum_size);
 
-	if (data->block_checksum)
+	if (lz4->block_checksum)
 		archive_strcat(&as, " -BX");
-	if (data->stream_checksum == 0)
+	if (lz4->stream_checksum == 0)
 		archive_strcat(&as, " --no-frame-crc");
-	if (data->block_independence == 0)
+	if (lz4->block_independence == 0)
 		archive_strcat(&as, " -BD");
 
-	f->write = archive_filter_lz4_write;
-
-	r = __archive_write_program_open(f, data->pdata, as.s);
+	r = __archive_write_program_open(f, lz4->pdata, as.s);
 	archive_string_free(&as);
 	return (r);
 }
@@ -676,27 +685,26 @@
 archive_filter_lz4_write(struct archive_write_filter *f, const void *buff,
     size_t length)
 {
-	struct private_data *data = (struct private_data *)f->data;
+	struct lz4 *lz4 = f->data;
 
-	return __archive_write_program_write(f, data->pdata, buff, length);
+	return __archive_write_program_write(f, lz4->pdata, buff, length);
 }
 
 static int
 archive_filter_lz4_close(struct archive_write_filter *f)
 {
-	struct private_data *data = (struct private_data *)f->data;
+	struct lz4 *lz4 = f->data;
 
-	return __archive_write_program_close(f, data->pdata);
+	return __archive_write_program_close(f, lz4->pdata);
 }
 
-static int
-archive_filter_lz4_free(struct archive_write_filter *f)
+static void
+free_data(struct lz4 *lz4)
 {
-	struct private_data *data = (struct private_data *)f->data;
-
-	__archive_write_program_free(data->pdata);
-	free(data);
-	return (ARCHIVE_OK);
+	if (lz4 != NULL) {
+		__archive_write_program_free(lz4->pdata);
+		free(lz4);
+	}
 }
 
 #endif /* HAVE_LIBLZ4 */
diff --git a/c/archive_write_add_filter_lzop.c b/c/archive_write_add_filter_lzop.c
--- a/c/archive_write_add_filter_lzop.c
+++ b/c/archive_write_add_filter_lzop.c
@@ -55,7 +55,7 @@
 	METHOD_LZO1X_1_15 = 2,
 	METHOD_LZO1X_999 = 3
 };
-struct write_lzop {
+struct lzop {
 	int compression_level;
 #if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H)
 	unsigned char	*uncompressed;
@@ -80,6 +80,7 @@
 		    const void *, size_t);
 static int archive_write_lzop_close(struct archive_write_filter *);
 static int archive_write_lzop_free(struct archive_write_filter *);
+static void free_data(struct lzop *);
 
 #if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H)
 /* Maximum block size. */
@@ -133,73 +134,72 @@
 #endif
 
 int
-archive_write_add_filter_lzop(struct archive *_a)
+archive_write_add_filter_lzop(struct archive *a)
 {
-	struct archive_write_filter *f = __archive_write_allocate_filter(_a);
-	struct write_lzop *data;
+	struct archive_write_filter *f;
+	struct lzop *lzop;
+	int r;
 
-	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+	archive_check_magic(a, ARCHIVE_WRITE_MAGIC,
 	    ARCHIVE_STATE_NEW, "archive_write_add_filter_lzop");
 
-	data = calloc(1, sizeof(*data));
-	if (data == NULL) {
-		archive_set_error(_a, ENOMEM, "Can't allocate memory");
-		return (ARCHIVE_FATAL);
-	}
-
-	f->name = "lzop";
-	f->code = ARCHIVE_FILTER_LZOP;
-	f->data = data;
-	f->open = archive_write_lzop_open;
-	f->options = archive_write_lzop_options;
-	f->write = archive_write_lzop_write;
-	f->close = archive_write_lzop_close;
-	f->free = archive_write_lzop_free;
+	lzop = calloc(1, sizeof(*lzop));
+	if (lzop == NULL)
+		goto memerr;
 #if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H)
 	if (lzo_init() != LZO_E_OK) {
-		free(data);
-		archive_set_error(_a, ARCHIVE_ERRNO_MISC,
+		free_data(lzop);
+		archive_set_error(a, ARCHIVE_ERRNO_MISC,
 		    "lzo_init(type check) failed");
 		return (ARCHIVE_FATAL);
 	}
 	if (lzo_version() < 0x940) {
-		free(data);
-		archive_set_error(_a, ARCHIVE_ERRNO_MISC,
+		free_data(lzop);
+		archive_set_error(a, ARCHIVE_ERRNO_MISC,
 		    "liblzo library is too old(%s < 0.940)",
 		    lzo_version_string());
 		return (ARCHIVE_FATAL);
 	}
-	data->compression_level = 5;
-	return (ARCHIVE_OK);
+	lzop->compression_level = 5;
+
+	r = ARCHIVE_OK;
 #else
-	data->pdata = __archive_write_program_allocate("lzop");
-	if (data->pdata == NULL) {
-		free(data);
-		archive_set_error(_a, ENOMEM, "Can't allocate memory");
-		return (ARCHIVE_FATAL);
-	}
-	data->compression_level = 0;
+	lzop->pdata = __archive_write_program_allocate("lzop");
+	if (lzop->pdata == NULL)
+		goto memerr;
+	lzop->compression_level = 0;
+
 	/* Note: We return "warn" to inform of using an external lzop
 	 * program. */
-	archive_set_error(_a, ARCHIVE_ERRNO_MISC,
+	archive_set_error(a, ARCHIVE_ERRNO_MISC,
 	    "Using external lzop program for lzop compression");
-	return (ARCHIVE_WARN);
+	r = ARCHIVE_WARN;
 #endif
+
+	f = __archive_write_allocate_filter(a);
+	if (f == NULL)
+		goto memerr;
+	f->name = "lzop";
+	f->code = ARCHIVE_FILTER_LZOP;
+	f->data = lzop;
+	f->options = archive_write_lzop_options;
+	f->open = archive_write_lzop_open;
+	f->write = archive_write_lzop_write;
+	f->close = archive_write_lzop_close;
+	f->free = archive_write_lzop_free;
+
+	return (r);
+memerr:
+	free_data(lzop);
+	archive_set_error(a, ENOMEM, "Can't allocate memory");
+	return (ARCHIVE_FATAL);
 }
 
 static int
 archive_write_lzop_free(struct archive_write_filter *f)
 {
-	struct write_lzop *data = (struct write_lzop *)f->data;
-
-#if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H)
-	free(data->uncompressed);
-	free(data->compressed);
-	free(data->work_buffer);
-#else
-	__archive_write_program_free(data->pdata);
-#endif
-	free(data);
+	free_data(f->data);
+	f->data = NULL;
 	return (ARCHIVE_OK);
 }
 
@@ -207,7 +207,7 @@
 archive_write_lzop_options(struct archive_write_filter *f, const char *key,
     const char *value)
 {
-	struct write_lzop *data = (struct write_lzop *)f->data;
+	struct lzop *lzop = f->data;
 
 	if (strcmp(key, "compression-level") == 0) {
 		if (value == NULL || !(value[0] >= '1' && value[0] <= '9') ||
@@ -216,7 +216,7 @@
 			    "compression-level invalid");
 			return (ARCHIVE_FAILED);
 		}
-		data->compression_level = value[0] - '0';
+		lzop->compression_level = value[0] - '0';
 		return (ARCHIVE_OK);
 	}
 	/* Note: The "warn" return is just to inform the options
@@ -229,58 +229,58 @@
 static int
 archive_write_lzop_open(struct archive_write_filter *f)
 {
-	struct write_lzop *data = (struct write_lzop *)f->data;
+	struct lzop *lzop = f->data;
 
-	switch (data->compression_level) {
+	switch (lzop->compression_level) {
 	case 1:
-		data->method = METHOD_LZO1X_1_15; data->level = 1; break;
+		lzop->method = METHOD_LZO1X_1_15; lzop->level = 1; break;
 	default:
 	case 2: case 3: case 4: case 5: case 6:
-		data->method = METHOD_LZO1X_1; data->level = 5; break;
+		lzop->method = METHOD_LZO1X_1; lzop->level = 5; break;
 	case 7:
-		data->method = METHOD_LZO1X_999; data->level = 7; break;
+		lzop->method = METHOD_LZO1X_999; lzop->level = 7; break;
 	case 8:
-		data->method = METHOD_LZO1X_999; data->level = 8; break;
+		lzop->method = METHOD_LZO1X_999; lzop->level = 8; break;
 	case 9:
-		data->method = METHOD_LZO1X_999; data->level = 9; break;
+		lzop->method = METHOD_LZO1X_999; lzop->level = 9; break;
 	}
-	switch (data->method) {
+	switch (lzop->method) {
 	case METHOD_LZO1X_1:
-		data->work_buffer_size = LZO1X_1_MEM_COMPRESS; break;
+		lzop->work_buffer_size = LZO1X_1_MEM_COMPRESS; break;
 	case METHOD_LZO1X_1_15:
-		data->work_buffer_size = LZO1X_1_15_MEM_COMPRESS; break;
+		lzop->work_buffer_size = LZO1X_1_15_MEM_COMPRESS; break;
 	case METHOD_LZO1X_999:
-		data->work_buffer_size = LZO1X_999_MEM_COMPRESS; break;
+		lzop->work_buffer_size = LZO1X_999_MEM_COMPRESS; break;
 	}
-	if (data->work_buffer == NULL) {
-		data->work_buffer = (lzo_voidp)malloc(data->work_buffer_size);
-		if (data->work_buffer == NULL) {
+	if (lzop->work_buffer == NULL) {
+		lzop->work_buffer = (lzo_voidp)malloc(lzop->work_buffer_size);
+		if (lzop->work_buffer == NULL) {
 			archive_set_error(f->archive, ENOMEM,
 			    "Can't allocate data for compression buffer");
 			return (ARCHIVE_FATAL);
 		}
 	}
-	if (data->compressed == NULL) {
-		data->compressed_buffer_size = sizeof(header) +
+	if (lzop->compressed == NULL) {
+		lzop->compressed_buffer_size = sizeof(header) +
 		    BLOCK_SIZE + (BLOCK_SIZE >> 4) + 64 + 3;
-		data->compressed = (unsigned char *)
-		    malloc(data->compressed_buffer_size);
-		if (data->compressed == NULL) {
+		lzop->compressed = (unsigned char *)
+		    malloc(lzop->compressed_buffer_size);
+		if (lzop->compressed == NULL) {
 			archive_set_error(f->archive, ENOMEM,
 			    "Can't allocate data for compression buffer");
 			return (ARCHIVE_FATAL);
 		}
 	}
-	if (data->uncompressed == NULL) {
-		data->uncompressed_buffer_size = BLOCK_SIZE;
-		data->uncompressed = (unsigned char *)
-		    malloc(data->uncompressed_buffer_size);
-		if (data->uncompressed == NULL) {
+	if (lzop->uncompressed == NULL) {
+		lzop->uncompressed_buffer_size = BLOCK_SIZE;
+		lzop->uncompressed = (unsigned char *)
+		    malloc(lzop->uncompressed_buffer_size);
+		if (lzop->uncompressed == NULL) {
 			archive_set_error(f->archive, ENOMEM,
 			    "Can't allocate data for compression buffer");
 			return (ARCHIVE_FATAL);
 		}
-		data->uncompressed_avail_bytes = BLOCK_SIZE;
+		lzop->uncompressed_avail_bytes = BLOCK_SIZE;
 	}
 	return (ARCHIVE_OK);
 }
@@ -288,68 +288,68 @@
 static int
 make_header(struct archive_write_filter *f)
 {
-	struct write_lzop *data = (struct write_lzop *)f->data;
+	struct lzop *lzop = f->data;
 	int64_t t;
 	uint32_t checksum;
 
-	memcpy(data->compressed, header, sizeof(header));
+	memcpy(lzop->compressed, header, sizeof(header));
 	/* Overwrite library version. */
-	data->compressed[HEADER_LIBVERSION] = (unsigned char )
+	lzop->compressed[HEADER_LIBVERSION] = (unsigned char )
 	    (lzo_version() >> 8) & 0xff;
-	data->compressed[HEADER_LIBVERSION + 1] = (unsigned char )
+	lzop->compressed[HEADER_LIBVERSION + 1] = (unsigned char )
 	    lzo_version() & 0xff;
 	/* Overwrite method and level. */
-	data->compressed[HEADER_METHOD] = (unsigned char)data->method;
-	data->compressed[HEADER_LEVEL] = data->level;
+	lzop->compressed[HEADER_METHOD] = (unsigned char)lzop->method;
+	lzop->compressed[HEADER_LEVEL] = lzop->level;
 	/* Overwrite mtime with current time. */
 	t = (int64_t)time(NULL);
-	archive_be32enc(&data->compressed[HEADER_MTIME_LOW],
+	archive_be32enc(&lzop->compressed[HEADER_MTIME_LOW],
 	    (uint32_t)(t & 0xffffffff));
-	archive_be32enc(&data->compressed[HEADER_MTIME_HIGH],
+	archive_be32enc(&lzop->compressed[HEADER_MTIME_HIGH],
 	    (uint32_t)((t >> 32) & 0xffffffff));
 	/* Overwrite header checksum with calculated value. */
-	checksum = lzo_adler32(1, data->compressed + HEADER_VERSION,
+	checksum = lzo_adler32(1, lzop->compressed + HEADER_VERSION,
 			(lzo_uint)(HEADER_H_CHECKSUM - HEADER_VERSION));
-	archive_be32enc(&data->compressed[HEADER_H_CHECKSUM], checksum);
+	archive_be32enc(&lzop->compressed[HEADER_H_CHECKSUM], checksum);
 	return (sizeof(header));
 }
 
 static int
 drive_compressor(struct archive_write_filter *f)
 {
-	struct write_lzop *data = (struct write_lzop *)f->data;
+	struct lzop *lzop = f->data;
 	unsigned char *p;
 	const int block_info_bytes = 12;
 	int header_bytes, r;
 	lzo_uint usize, csize;
 	uint32_t checksum;
 
-	if (!data->header_written) {
+	if (!lzop->header_written) {
 		header_bytes = make_header(f);
-		data->header_written = 1;
+		lzop->header_written = 1;
 	} else
 		header_bytes = 0;
-	p = data->compressed;
+	p = lzop->compressed;
 
 	usize = (lzo_uint)
-	    (data->uncompressed_buffer_size - data->uncompressed_avail_bytes);
+	    (lzop->uncompressed_buffer_size - lzop->uncompressed_avail_bytes);
 	csize = 0;
-	switch (data->method) {
+	switch (lzop->method) {
 	default:
 	case METHOD_LZO1X_1:
-		r = lzo1x_1_compress(data->uncompressed, usize,
+		r = lzo1x_1_compress(lzop->uncompressed, usize,
 			p + header_bytes + block_info_bytes, &csize,
-			data->work_buffer);
+			lzop->work_buffer);
 		break;
 	case METHOD_LZO1X_1_15:
-		r = lzo1x_1_15_compress(data->uncompressed, usize,
+		r = lzo1x_1_15_compress(lzop->uncompressed, usize,
 			p + header_bytes + block_info_bytes, &csize,
-			data->work_buffer);
+			lzop->work_buffer);
 		break;
 	case METHOD_LZO1X_999:
-		r = lzo1x_999_compress_level(data->uncompressed, usize,
+		r = lzo1x_999_compress_level(lzop->uncompressed, usize,
 			p + header_bytes + block_info_bytes, &csize,
-			data->work_buffer, NULL, 0, 0, data->level);
+			lzop->work_buffer, NULL, 0, 0, lzop->level);
 		break;
 	}
 	if (r != LZO_E_OK) {
@@ -361,13 +361,13 @@
 	/* Store uncompressed size. */
 	archive_be32enc(p + header_bytes, (uint32_t)usize);
 	/* Store the checksum of the uncompressed data. */
-	checksum = lzo_adler32(1, data->uncompressed, usize);
+	checksum = lzo_adler32(1, lzop->uncompressed, usize);
 	archive_be32enc(p + header_bytes + 8, checksum);
 
 	if (csize < usize) {
 		/* Store compressed size. */
 		archive_be32enc(p + header_bytes + 4, (uint32_t)csize);
-		r = __archive_write_filter(f->next_filter, data->compressed,
+		r = __archive_write_filter(f->next_filter, lzop->compressed,
 			header_bytes + block_info_bytes + csize);
 	} else {
 		/*
@@ -375,11 +375,11 @@
 		 */
 		/* Store uncompressed size as compressed size. */
 		archive_be32enc(p + header_bytes + 4, (uint32_t)usize);
-		r = __archive_write_filter(f->next_filter, data->compressed,
+		r = __archive_write_filter(f->next_filter, lzop->compressed,
 			header_bytes + block_info_bytes);
 		if (r != ARCHIVE_OK)
 			return (ARCHIVE_FATAL);
-		r = __archive_write_filter(f->next_filter, data->uncompressed,
+		r = __archive_write_filter(f->next_filter, lzop->uncompressed,
 			usize);
 	}
 
@@ -392,30 +392,30 @@
 archive_write_lzop_write(struct archive_write_filter *f,
     const void *buff, size_t length)
 {
-	struct write_lzop *data = (struct write_lzop *)f->data;
+	struct lzop *lzop = f->data;
 	const char *p = buff;
 	int r;
 
 	do {
-		if (data->uncompressed_avail_bytes > length) {
-			memcpy(data->uncompressed
-				+ data->uncompressed_buffer_size
-				- data->uncompressed_avail_bytes,
+		if (lzop->uncompressed_avail_bytes > length) {
+			memcpy(lzop->uncompressed
+				+ lzop->uncompressed_buffer_size
+				- lzop->uncompressed_avail_bytes,
 			    p, length);
-			data->uncompressed_avail_bytes -= length;
+			lzop->uncompressed_avail_bytes -= length;
 			return (ARCHIVE_OK);
 		}
 
-		memcpy(data->uncompressed + data->uncompressed_buffer_size
-			- data->uncompressed_avail_bytes,
-		    p, data->uncompressed_avail_bytes);
-		length -= data->uncompressed_avail_bytes;
-		p += data->uncompressed_avail_bytes;
-		data->uncompressed_avail_bytes = 0;
+		memcpy(lzop->uncompressed + lzop->uncompressed_buffer_size
+			- lzop->uncompressed_avail_bytes,
+		    p, lzop->uncompressed_avail_bytes);
+		length -= lzop->uncompressed_avail_bytes;
+		p += lzop->uncompressed_avail_bytes;
+		lzop->uncompressed_avail_bytes = 0;
 
 		r = drive_compressor(f);
 		if (r != ARCHIVE_OK) return (r);
-		data->uncompressed_avail_bytes = BLOCK_SIZE;
+		lzop->uncompressed_avail_bytes = BLOCK_SIZE;
 	} while (length);
 
 	return (ARCHIVE_OK);
@@ -424,11 +424,11 @@
 static int
 archive_write_lzop_close(struct archive_write_filter *f)
 {
-	struct write_lzop *data = (struct write_lzop *)f->data;
+	struct lzop *lzop = f->data;
 	const uint32_t endmark = 0;
 	int r;
 
-	if (data->uncompressed_avail_bytes < BLOCK_SIZE) {
+	if (lzop->uncompressed_avail_bytes < BLOCK_SIZE) {
 		/* Compress and output remaining data. */
 		r = drive_compressor(f);
 		if (r != ARCHIVE_OK)
@@ -439,24 +439,35 @@
 	return __archive_write_filter(f->next_filter, &endmark, sizeof(endmark));
 }
 
+static void
+free_data(struct lzop *lzop)
+{
+	if (lzop != NULL) {
+		free(lzop->uncompressed);
+		free(lzop->compressed);
+		free(lzop->work_buffer);
+		free(lzop);
+	}
+}
+
 #else
 static int
 archive_write_lzop_open(struct archive_write_filter *f)
 {
-	struct write_lzop *data = (struct write_lzop *)f->data;
+	struct lzop *lzop = f->data;
 	struct archive_string as;
 	int r;
 
 	archive_string_init(&as);
 	archive_strcpy(&as, "lzop");
 	/* Specify compression level. */
-	if (data->compression_level > 0) {
+	if (lzop->compression_level > 0) {
 		archive_strappend_char(&as, ' ');
 		archive_strappend_char(&as, '-');
-		archive_strappend_char(&as, '0' + data->compression_level);
+		archive_strappend_char(&as, '0' + lzop->compression_level);
 	}
 
-	r = __archive_write_program_open(f, data->pdata, as.s);
+	r = __archive_write_program_open(f, lzop->pdata, as.s);
 	archive_string_free(&as);
 	return (r);
 }
@@ -465,16 +476,25 @@
 archive_write_lzop_write(struct archive_write_filter *f,
     const void *buff, size_t length)
 {
-	struct write_lzop *data = (struct write_lzop *)f->data;
+	struct lzop *lzop = f->data;
 
-	return __archive_write_program_write(f, data->pdata, buff, length);
+	return __archive_write_program_write(f, lzop->pdata, buff, length);
 }
 
 static int
 archive_write_lzop_close(struct archive_write_filter *f)
 {
-	struct write_lzop *data = (struct write_lzop *)f->data;
+	struct lzop *lzop = f->data;
 
-	return __archive_write_program_close(f, data->pdata);
+	return __archive_write_program_close(f, lzop->pdata);
+}
+
+static void
+free_data(struct lzop *lzop)
+{
+	if (lzop != NULL) {
+		__archive_write_program_free(lzop->pdata);
+		free(lzop);
+	}
 }
 #endif
diff --git a/c/archive_write_add_filter_program.c b/c/archive_write_add_filter_program.c
--- a/c/archive_write_add_filter_program.c
+++ b/c/archive_write_add_filter_program.c
@@ -70,7 +70,7 @@
 	char		*program_name;
 };
 
-struct private_data {
+struct program {
 	struct archive_write_program_data *pdata;
 	struct archive_string description;
 	char		*cmd;
@@ -81,51 +81,53 @@
 		    const void *, size_t);
 static int archive_compressor_program_close(struct archive_write_filter *);
 static int archive_compressor_program_free(struct archive_write_filter *);
+static void free_data(struct program *);
 
 /*
  * Add a filter to this write handle that passes all data through an
  * external program.
  */
 int
-archive_write_add_filter_program(struct archive *_a, const char *cmd)
+archive_write_add_filter_program(struct archive *a, const char *cmd)
 {
-	struct archive_write_filter *f = __archive_write_allocate_filter(_a);
-	struct private_data *data;
+	struct archive_write_filter *f;
+	struct program *program;
 	static const char prefix[] = "Program: ";
 
-	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+	archive_check_magic(a, ARCHIVE_WRITE_MAGIC,
 	    ARCHIVE_STATE_NEW, "archive_write_add_filter_program");
 
-	f->data = calloc(1, sizeof(*data));
-	if (f->data == NULL)
+	program = calloc(1, sizeof(*program));
+	if (program == NULL)
 		goto memerr;
-	data = (struct private_data *)f->data;
-
-	data->cmd = strdup(cmd);
-	if (data->cmd == NULL)
+	program->cmd = strdup(cmd);
+	if (program->cmd == NULL)
 		goto memerr;
-
-	data->pdata = __archive_write_program_allocate(cmd);
-	if (data->pdata == NULL)
+	program->pdata = __archive_write_program_allocate(cmd);
+	if (program->pdata == NULL)
 		goto memerr;
-
 	/* Make up a description string. */
-	if (archive_string_ensure(&data->description,
+	if (archive_string_ensure(&program->description,
 	    strlen(prefix) + strlen(cmd) + 1) == NULL)
 		goto memerr;
-	archive_strcpy(&data->description, prefix);
-	archive_strcat(&data->description, cmd);
+	archive_strcpy(&program->description, prefix);
+	archive_strcat(&program->description, cmd);
 
-	f->name = data->description.s;
+	f = __archive_write_allocate_filter(a);
+	if (f == NULL)
+		goto memerr;
+	f->name = program->description.s;
 	f->code = ARCHIVE_FILTER_PROGRAM;
+	f->data = program;
 	f->open = archive_compressor_program_open;
 	f->write = archive_compressor_program_write;
 	f->close = archive_compressor_program_close;
 	f->free = archive_compressor_program_free;
+
 	return (ARCHIVE_OK);
 memerr:
-	archive_compressor_program_free(f);
-	archive_set_error(_a, ENOMEM,
+	free_data(program);
+	archive_set_error(a, ENOMEM,
 	    "Can't allocate memory for filter program");
 	return (ARCHIVE_FATAL);
 }
@@ -133,40 +135,33 @@
 static int
 archive_compressor_program_open(struct archive_write_filter *f)
 {
-	struct private_data *data = (struct private_data *)f->data;
+	struct program *program = f->data;
 
-	return __archive_write_program_open(f, data->pdata, data->cmd);
+	return __archive_write_program_open(f, program->pdata, program->cmd);
 }
 
 static int
 archive_compressor_program_write(struct archive_write_filter *f,
     const void *buff, size_t length)
 {
-	struct private_data *data = (struct private_data *)f->data;
+	struct program *program = f->data;
 
-	return __archive_write_program_write(f, data->pdata, buff, length);
+	return __archive_write_program_write(f, program->pdata, buff, length);
 }
 
 static int
 archive_compressor_program_close(struct archive_write_filter *f)
 {
-	struct private_data *data = (struct private_data *)f->data;
+	struct program *program = f->data;
 
-	return __archive_write_program_close(f, data->pdata);
+	return __archive_write_program_close(f, program->pdata);
 }
 
 static int
 archive_compressor_program_free(struct archive_write_filter *f)
 {
-	struct private_data *data = (struct private_data *)f->data;
-
-	if (data) {
-		free(data->cmd);
-		archive_string_free(&data->description);
-		__archive_write_program_free(data->pdata);
-		free(data);
-		f->data = NULL;
-	}
+	free_data(f->data);
+	f->data = NULL;
 	return (ARCHIVE_OK);
 }
 
@@ -391,3 +386,13 @@
 	return ret;
 }
 
+static void
+free_data(struct program *program)
+{
+	if (program) {
+		free(program->cmd);
+		archive_string_free(&program->description);
+		__archive_write_program_free(program->pdata);
+		free(program);
+	}
+}
diff --git a/c/archive_write_add_filter_uuencode.c b/c/archive_write_add_filter_uuencode.c
--- a/c/archive_write_add_filter_uuencode.c
+++ b/c/archive_write_add_filter_uuencode.c
@@ -46,7 +46,7 @@
 
 #define LBYTES 45
 
-struct private_uuencode {
+struct uuencode {
 	int			mode;
 	struct archive_string	name;
 	struct archive_string	encoded_buff;
@@ -64,39 +64,44 @@
 static int archive_filter_uuencode_free(struct archive_write_filter *);
 static void uu_encode(struct archive_string *, const unsigned char *, size_t);
 static int64_t atol8(const char *, size_t);
+static void free_data(struct uuencode *);
 
 /*
  * Add a compress filter to this write handle.
  */
 int
-archive_write_add_filter_uuencode(struct archive *_a)
+archive_write_add_filter_uuencode(struct archive *a)
 {
-	struct archive_write *a = (struct archive_write *)_a;
-	struct archive_write_filter *f = __archive_write_allocate_filter(_a);
-	struct private_uuencode *state;
+	struct archive_write_filter *f;
+	struct uuencode *uuencode;
 
-	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
-	    ARCHIVE_STATE_NEW, "archive_write_add_filter_uu");
+	archive_check_magic(a, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_write_add_filter_uuencode");
 
-	state = calloc(1, sizeof(*state));
-	if (state == NULL) {
-		archive_set_error(f->archive, ENOMEM,
-		    "Can't allocate data for uuencode filter");
-		return (ARCHIVE_FATAL);
-	}
-	archive_strcpy(&state->name, "-");
-	state->mode = 0644;
+	uuencode = calloc(1, sizeof(*uuencode));
+	if (uuencode == NULL)
+		goto memerr;
+	archive_strcpy(&uuencode->name, "-");
+	uuencode->mode = 0644;
 
-	f->data = state;
+	f = __archive_write_allocate_filter(a);
+	if (f == NULL)
+		goto memerr;
 	f->name = "uuencode";
 	f->code = ARCHIVE_FILTER_UU;
-	f->open = archive_filter_uuencode_open;
+	f->data = uuencode;
 	f->options = archive_filter_uuencode_options;
+	f->open = archive_filter_uuencode_open;
 	f->write = archive_filter_uuencode_write;
 	f->close = archive_filter_uuencode_close;
 	f->free = archive_filter_uuencode_free;
 
 	return (ARCHIVE_OK);
+memerr:
+	free_data(uuencode);
+	archive_set_error(a, ENOMEM,
+	    "Can't allocate data for uuencode filter");
+	return (ARCHIVE_FATAL);
 }
 
 /*
@@ -106,7 +111,7 @@
 archive_filter_uuencode_options(struct archive_write_filter *f, const char *key,
     const char *value)
 {
-	struct private_uuencode *state = (struct private_uuencode *)f->data;
+	struct uuencode *uuencode = f->data;
 
 	if (strcmp(key, "mode") == 0) {
 		int64_t val;
@@ -122,7 +127,7 @@
 			    "invalid mode option");
 			return (ARCHIVE_FAILED);
 		}
-		state->mode = (int)val & 0777;
+		uuencode->mode = (int)val & 0777;
 		return (ARCHIVE_OK);
 	} else if (strcmp(key, "name") == 0) {
 		if (value == NULL) {
@@ -130,7 +135,7 @@
 			    "name option requires a string");
 			return (ARCHIVE_FAILED);
 		}
-		archive_strcpy(&state->name, value);
+		archive_strcpy(&uuencode->name, value);
 		return (ARCHIVE_OK);
 	}
 
@@ -146,7 +151,7 @@
 static int
 archive_filter_uuencode_open(struct archive_write_filter *f)
 {
-	struct private_uuencode *state = (struct private_uuencode *)f->data;
+	struct uuencode *uuencode = f->data;
 	size_t bs = 65536, bpb;
 
 	if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {
@@ -159,18 +164,17 @@
 			bs -= bs % bpb;
 	}
 
-	state->bs = bs;
-	if (archive_string_ensure(&state->encoded_buff, bs + 512) == NULL) {
+	uuencode->bs = bs;
+	if (archive_string_ensure(&uuencode->encoded_buff, bs + 512) == NULL) {
 		archive_set_error(f->archive, ENOMEM,
 		    "Can't allocate data for uuencode buffer");
 		return (ARCHIVE_FATAL);
 	}
 
-	archive_string_sprintf(&state->encoded_buff, "begin %o %s\n",
-	    (unsigned int)state->mode, state->name.s);
+	archive_string_sprintf(&uuencode->encoded_buff, "begin %o %s\n",
+	    (unsigned int)uuencode->mode, uuencode->name.s);
 
-	f->data = state;
-	return (0);
+	return (ARCHIVE_OK);
 }
 
 static void
@@ -216,39 +220,39 @@
 archive_filter_uuencode_write(struct archive_write_filter *f, const void *buff,
     size_t length)
 {
-	struct private_uuencode *state = (struct private_uuencode *)f->data;
+	struct uuencode *uuencode = f->data;
 	const unsigned char *p = buff;
 	int ret = ARCHIVE_OK;
 
 	if (length == 0)
 		return (ret);
 
-	if (state->hold_len) {
-		while (state->hold_len < LBYTES && length > 0) {
-			state->hold[state->hold_len++] = *p++;
+	if (uuencode->hold_len) {
+		while (uuencode->hold_len < LBYTES && length > 0) {
+			uuencode->hold[uuencode->hold_len++] = *p++;
 			length--;
 		}
-		if (state->hold_len < LBYTES)
+		if (uuencode->hold_len < LBYTES)
 			return (ret);
-		uu_encode(&state->encoded_buff, state->hold, LBYTES);
-		state->hold_len = 0;
+		uu_encode(&uuencode->encoded_buff, uuencode->hold, LBYTES);
+		uuencode->hold_len = 0;
 	}
 
 	for (; length >= LBYTES; length -= LBYTES, p += LBYTES)
-		uu_encode(&state->encoded_buff, p, LBYTES);
+		uu_encode(&uuencode->encoded_buff, p, LBYTES);
 
 	/* Save remaining bytes. */
 	if (length > 0) {
-		memcpy(state->hold, p, length);
-		state->hold_len = length;
+		memcpy(uuencode->hold, p, length);
+		uuencode->hold_len = length;
 	}
-	while (archive_strlen(&state->encoded_buff) >= state->bs) {
+	while (archive_strlen(&uuencode->encoded_buff) >= uuencode->bs) {
 		ret = __archive_write_filter(f->next_filter,
-		    state->encoded_buff.s, state->bs);
-		memmove(state->encoded_buff.s,
-		    state->encoded_buff.s + state->bs,
-		    state->encoded_buff.length - state->bs);
-		state->encoded_buff.length -= state->bs;
+		    uuencode->encoded_buff.s, uuencode->bs);
+		memmove(uuencode->encoded_buff.s,
+		    uuencode->encoded_buff.s + uuencode->bs,
+		    uuencode->encoded_buff.length - uuencode->bs);
+		uuencode->encoded_buff.length -= uuencode->bs;
 	}
 
 	return (ret);
@@ -261,26 +265,24 @@
 static int
 archive_filter_uuencode_close(struct archive_write_filter *f)
 {
-	struct private_uuencode *state = (struct private_uuencode *)f->data;
+	struct uuencode *uuencode = f->data;
 
 	/* Flush remaining bytes. */
-	if (state->hold_len != 0)
-		uu_encode(&state->encoded_buff, state->hold, state->hold_len);
-	archive_string_sprintf(&state->encoded_buff, "`\nend\n");
+	if (uuencode->hold_len != 0)
+		uu_encode(&uuencode->encoded_buff, uuencode->hold,
+		    uuencode->hold_len);
+	archive_string_sprintf(&uuencode->encoded_buff, "`\nend\n");
 	/* Write the last block */
 	archive_write_set_bytes_in_last_block(f->archive, 1);
 	return __archive_write_filter(f->next_filter,
-	    state->encoded_buff.s, archive_strlen(&state->encoded_buff));
+	    uuencode->encoded_buff.s, archive_strlen(&uuencode->encoded_buff));
 }
 
 static int
 archive_filter_uuencode_free(struct archive_write_filter *f)
 {
-	struct private_uuencode *state = (struct private_uuencode *)f->data;
-
-	archive_string_free(&state->name);
-	archive_string_free(&state->encoded_buff);
-	free(state);
+	free_data(f->data);
+	f->data = NULL;
 	return (ARCHIVE_OK);
 }
 
@@ -307,3 +309,12 @@
 	return (l);
 }
 
+static void
+free_data(struct uuencode *uuencode)
+{
+	if (uuencode != NULL) {
+		archive_string_free(&uuencode->name);
+		archive_string_free(&uuencode->encoded_buff);
+		free(uuencode);
+	}
+}
diff --git a/c/archive_write_add_filter_xz.c b/c/archive_write_add_filter_xz.c
--- a/c/archive_write_add_filter_xz.c
+++ b/c/archive_write_add_filter_xz.c
@@ -99,7 +99,7 @@
 #else
 /* Don't compile this if we don't have liblzma. */
 
-struct private_data {
+struct xz {
 	int		 compression_level;
 	uint32_t	 threads;
 	lzma_stream	 stream;
@@ -121,7 +121,8 @@
 static int	archive_compressor_xz_close(struct archive_write_filter *);
 static int	archive_compressor_xz_free(struct archive_write_filter *);
 static int	drive_compressor(struct archive_write_filter *,
-		    struct private_data *, int finishing);
+		    struct xz *, int finishing);
+static void	free_data(struct xz *);
 
 struct option_value {
 	uint32_t dict_size;
@@ -142,85 +143,72 @@
 };
 
 static int
-common_setup(struct archive_write_filter *f)
+common_setup(struct archive *a, const char *name, int code)
 {
-	struct private_data *data;
-	struct archive_write *a = (struct archive_write *)f->archive;
-	data = calloc(1, sizeof(*data));
-	if (data == NULL) {
-		archive_set_error(&a->archive, ENOMEM, "Out of memory");
-		return (ARCHIVE_FATAL);
-	}
-	f->data = data;
-	data->compression_level = LZMA_PRESET_DEFAULT;
-	data->threads = 1;
-	f->open = &archive_compressor_xz_open;
+	struct xz *xz;
+	struct archive_write_filter *f;
+
+	xz = calloc(1, sizeof(*xz));
+	if (xz == NULL)
+		goto memerr;
+	xz->compression_level = LZMA_PRESET_DEFAULT;
+	xz->threads = 1;
+
+	f = __archive_write_allocate_filter(a);
+	if (f == NULL)
+		goto memerr;
+	f->name = name;
+	f->code = code;
+	f->data = xz;
+	f->options = archive_compressor_xz_options;
+	f->open = archive_compressor_xz_open;
+	f->write = archive_compressor_xz_write;
 	f->close = archive_compressor_xz_close;
 	f->free = archive_compressor_xz_free;
-	f->options = &archive_compressor_xz_options;
+
 	return (ARCHIVE_OK);
+memerr:
+	free_data(xz);
+	archive_set_error(a, ENOMEM, "Out of memory");
+	return (ARCHIVE_FATAL);
 }
 
 /*
  * Add an xz compression filter to this write handle.
  */
 int
-archive_write_add_filter_xz(struct archive *_a)
+archive_write_add_filter_xz(struct archive *a)
 {
-	struct archive_write_filter *f;
-	int r;
-
-	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+	archive_check_magic(a, ARCHIVE_WRITE_MAGIC,
 	    ARCHIVE_STATE_NEW, "archive_write_add_filter_xz");
-	f = __archive_write_allocate_filter(_a);
-	r = common_setup(f);
-	if (r == ARCHIVE_OK) {
-		f->code = ARCHIVE_FILTER_XZ;
-		f->name = "xz";
-	}
-	return (r);
+
+	return common_setup(a, "xz", ARCHIVE_FILTER_XZ);
 }
 
 /* LZMA is handled identically, we just need a different compression
  * code set.  (The liblzma setup looks at the code to determine
  * the one place that XZ and LZMA require different handling.) */
 int
-archive_write_add_filter_lzma(struct archive *_a)
+archive_write_add_filter_lzma(struct archive *a)
 {
-	struct archive_write_filter *f;
-	int r;
-
-	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+	archive_check_magic(a, ARCHIVE_WRITE_MAGIC,
 	    ARCHIVE_STATE_NEW, "archive_write_add_filter_lzma");
-	f = __archive_write_allocate_filter(_a);
-	r = common_setup(f);
-	if (r == ARCHIVE_OK) {
-		f->code = ARCHIVE_FILTER_LZMA;
-		f->name = "lzma";
-	}
-	return (r);
+
+	return common_setup(a, "lzma", ARCHIVE_FILTER_LZMA);
 }
 
 int
-archive_write_add_filter_lzip(struct archive *_a)
+archive_write_add_filter_lzip(struct archive *a)
 {
-	struct archive_write_filter *f;
-	int r;
-
-	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+	archive_check_magic(a, ARCHIVE_WRITE_MAGIC,
 	    ARCHIVE_STATE_NEW, "archive_write_add_filter_lzip");
-	f = __archive_write_allocate_filter(_a);
-	r = common_setup(f);
-	if (r == ARCHIVE_OK) {
-		f->code = ARCHIVE_FILTER_LZIP;
-		f->name = "lzip";
-	}
-	return (r);
+
+	return common_setup(a, "lzip", ARCHIVE_FILTER_LZIP);
 }
 
 static int
 archive_compressor_xz_init_stream(struct archive_write_filter *f,
-    struct private_data *data)
+    struct xz *xz)
 {
 	static const lzma_stream lzma_stream_init_data = LZMA_STREAM_INIT;
 	int ret;
@@ -228,27 +216,27 @@
 	lzma_mt mt_options;
 #endif
 
-	data->stream = lzma_stream_init_data;
-	data->stream.next_out = data->compressed;
-	data->stream.avail_out = data->compressed_buffer_size;
+	xz->stream = lzma_stream_init_data;
+	xz->stream.next_out = xz->compressed;
+	xz->stream.avail_out = xz->compressed_buffer_size;
 	if (f->code == ARCHIVE_FILTER_XZ) {
 #ifdef HAVE_LZMA_STREAM_ENCODER_MT
-		if (data->threads != 1) {
+		if (xz->threads != 1) {
 			memset(&mt_options, 0, sizeof(mt_options));
-			mt_options.threads = data->threads;
+			mt_options.threads = xz->threads;
 			mt_options.timeout = 300;
-			mt_options.filters = data->lzmafilters;
+			mt_options.filters = xz->lzmafilters;
 			mt_options.check = LZMA_CHECK_CRC64;
-			ret = lzma_stream_encoder_mt(&(data->stream),
+			ret = lzma_stream_encoder_mt(&(xz->stream),
 			    &mt_options);
 		} else
 #endif
-			ret = lzma_stream_encoder(&(data->stream),
-			    data->lzmafilters, LZMA_CHECK_CRC64);
+			ret = lzma_stream_encoder(&(xz->stream),
+			    xz->lzmafilters, LZMA_CHECK_CRC64);
 	} else if (f->code == ARCHIVE_FILTER_LZMA) {
-		ret = lzma_alone_encoder(&(data->stream), &data->lzma_opt);
+		ret = lzma_alone_encoder(&(xz->stream), &xz->lzma_opt);
 	} else {	/* ARCHIVE_FILTER_LZIP */
-		int dict_size = data->lzma_opt.dict_size;
+		int dict_size = xz->lzma_opt.dict_size;
 		int ds, log2dic, wedges;
 
 		/* Calculate a coded dictionary size */
@@ -270,18 +258,18 @@
 			wedges = 0;
 		ds = ((wedges << 5) & 0xe0) | (log2dic & 0x1f);
 
-		data->crc32 = 0;
+		xz->crc32 = 0;
 		/* Make a header */
-		data->compressed[0] = 0x4C;
-		data->compressed[1] = 0x5A;
-		data->compressed[2] = 0x49;
-		data->compressed[3] = 0x50;
-		data->compressed[4] = 1;/* Version */
-		data->compressed[5] = (unsigned char)ds;
-		data->stream.next_out += 6;
-		data->stream.avail_out -= 6;
+		xz->compressed[0] = 0x4C;
+		xz->compressed[1] = 0x5A;
+		xz->compressed[2] = 0x49;
+		xz->compressed[3] = 0x50;
+		xz->compressed[4] = 1;/* Version */
+		xz->compressed[5] = (unsigned char)ds;
+		xz->stream.next_out += 6;
+		xz->stream.avail_out -= 6;
 
-		ret = lzma_raw_encoder(&(data->stream), data->lzmafilters);
+		ret = lzma_raw_encoder(&(xz->stream), xz->lzmafilters);
 	}
 	if (ret == LZMA_OK)
 		return (ARCHIVE_OK);
@@ -307,10 +295,10 @@
 static int
 archive_compressor_xz_open(struct archive_write_filter *f)
 {
-	struct private_data *data = f->data;
+	struct xz *xz = f->data;
 	int ret;
 
-	if (data->compressed == NULL) {
+	if (xz->compressed == NULL) {
 		size_t bs = 65536, bpb;
 		if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {
 			/* Buffer size should be a multiple number of the bytes
@@ -321,49 +309,46 @@
 			else if (bpb != 0)
 				bs -= bs % bpb;
 		}
-		data->compressed_buffer_size = bs;
-		data->compressed = malloc(data->compressed_buffer_size);
-		if (data->compressed == NULL) {
+		xz->compressed_buffer_size = bs;
+		xz->compressed = malloc(xz->compressed_buffer_size);
+		if (xz->compressed == NULL) {
 			archive_set_error(f->archive, ENOMEM,
 			    "Can't allocate data for compression buffer");
 			return (ARCHIVE_FATAL);
 		}
 	}
 
-	f->write = archive_compressor_xz_write;
-
 	/* Initialize compression library. */
 	if (f->code == ARCHIVE_FILTER_LZIP) {
 		const struct option_value *val =
-		    &option_values[data->compression_level];
+		    &option_values[xz->compression_level];
 
-		data->lzma_opt.dict_size = val->dict_size;
-		data->lzma_opt.preset_dict = NULL;
-		data->lzma_opt.preset_dict_size = 0;
-		data->lzma_opt.lc = LZMA_LC_DEFAULT;
-		data->lzma_opt.lp = LZMA_LP_DEFAULT;
-		data->lzma_opt.pb = LZMA_PB_DEFAULT;
-		data->lzma_opt.mode =
-		    data->compression_level<= 2? LZMA_MODE_FAST:LZMA_MODE_NORMAL;
-		data->lzma_opt.nice_len = val->nice_len;
-		data->lzma_opt.mf = val->mf;
-		data->lzma_opt.depth = 0;
-		data->lzmafilters[0].id = LZMA_FILTER_LZMA1;
-		data->lzmafilters[0].options = &data->lzma_opt;
-		data->lzmafilters[1].id = LZMA_VLI_UNKNOWN;/* Terminate */
+		xz->lzma_opt.dict_size = val->dict_size;
+		xz->lzma_opt.preset_dict = NULL;
+		xz->lzma_opt.preset_dict_size = 0;
+		xz->lzma_opt.lc = LZMA_LC_DEFAULT;
+		xz->lzma_opt.lp = LZMA_LP_DEFAULT;
+		xz->lzma_opt.pb = LZMA_PB_DEFAULT;
+		xz->lzma_opt.mode =
+		    xz->compression_level<= 2? LZMA_MODE_FAST:LZMA_MODE_NORMAL;
+		xz->lzma_opt.nice_len = val->nice_len;
+		xz->lzma_opt.mf = val->mf;
+		xz->lzma_opt.depth = 0;
+		xz->lzmafilters[0].id = LZMA_FILTER_LZMA1;
+		xz->lzmafilters[0].options = &xz->lzma_opt;
+		xz->lzmafilters[1].id = LZMA_VLI_UNKNOWN;/* Terminate */
 	} else {
-		if (lzma_lzma_preset(&data->lzma_opt, data->compression_level)) {
+		if (lzma_lzma_preset(&xz->lzma_opt, xz->compression_level)) {
 			archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
 			    "Internal error initializing compression library");
 		}
-		data->lzmafilters[0].id = LZMA_FILTER_LZMA2;
-		data->lzmafilters[0].options = &data->lzma_opt;
-		data->lzmafilters[1].id = LZMA_VLI_UNKNOWN;/* Terminate */
+		xz->lzmafilters[0].id = LZMA_FILTER_LZMA2;
+		xz->lzmafilters[0].options = &xz->lzma_opt;
+		xz->lzmafilters[1].id = LZMA_VLI_UNKNOWN;/* Terminate */
 	}
-	ret = archive_compressor_xz_init_stream(f, data);
+	ret = archive_compressor_xz_init_stream(f, xz);
 	if (ret == LZMA_OK) {
-		f->data = data;
-		return (0);
+		return (ARCHIVE_OK);
 	}
 	return (ARCHIVE_FATAL);
 }
@@ -375,7 +360,7 @@
 archive_compressor_xz_options(struct archive_write_filter *f,
     const char *key, const char *value)
 {
-	struct private_data *data = (struct private_data *)f->data;
+	struct xz *xz = f->data;
 
 	if (strcmp(key, "compression-level") == 0) {
 		if (value == NULL || !(value[0] >= '0' && value[0] <= '9') ||
@@ -384,9 +369,9 @@
 			    "compression-level invalid");
 			return (ARCHIVE_FAILED);
 		}
-		data->compression_level = value[0] - '0';
-		if (data->compression_level > 9)
-			data->compression_level = 9;
+		xz->compression_level = value[0] - '0';
+		if (xz->compression_level > 9)
+			xz->compression_level = 9;
 		return (ARCHIVE_OK);
 	} else if (strcmp(key, "threads") == 0) {
 		char *endptr;
@@ -400,17 +385,17 @@
 		errno = 0;
 		val = strtoul(value, &endptr, 10);
 		if (errno != 0 || *endptr != '\0' || val > (unsigned)INT_MAX) {
-			data->threads = 1;
+			xz->threads = 1;
 			archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
 			    "threads invalid");
 			return (ARCHIVE_FAILED);
 		}
-		data->threads = (int)val;
-		if (data->threads == 0) {
+		xz->threads = (int)val;
+		if (xz->threads == 0) {
 #ifdef HAVE_LZMA_STREAM_ENCODER_MT
-			data->threads = lzma_cputhreads();
+			xz->threads = lzma_cputhreads();
 #else
-			data->threads = 1;
+			xz->threads = 1;
 #endif
 		}
 		return (ARCHIVE_OK);
@@ -429,18 +414,18 @@
 archive_compressor_xz_write(struct archive_write_filter *f,
     const void *buff, size_t length)
 {
-	struct private_data *data = (struct private_data *)f->data;
+	struct xz *xz = f->data;
 	int ret;
 
 	/* Update statistics */
-	data->total_in += length;
+	xz->total_in += length;
 	if (f->code == ARCHIVE_FILTER_LZIP)
-		data->crc32 = lzma_crc32(buff, length, data->crc32);
+		xz->crc32 = lzma_crc32(buff, length, xz->crc32);
 
 	/* Compress input data to output buffer */
-	data->stream.next_in = buff;
-	data->stream.avail_in = length;
-	if ((ret = drive_compressor(f, data, 0)) != ARCHIVE_OK)
+	xz->stream.next_in = buff;
+	xz->stream.avail_in = length;
+	if ((ret = drive_compressor(f, xz, 0)) != ARCHIVE_OK)
 		return (ret);
 
 	return (ARCHIVE_OK);
@@ -453,34 +438,32 @@
 static int
 archive_compressor_xz_close(struct archive_write_filter *f)
 {
-	struct private_data *data = (struct private_data *)f->data;
+	struct xz *xz = f->data;
 	int ret;
 
-	ret = drive_compressor(f, data, 1);
+	ret = drive_compressor(f, xz, 1);
 	if (ret == ARCHIVE_OK) {
-		data->total_out +=
-		    data->compressed_buffer_size - data->stream.avail_out;
+		xz->total_out +=
+		    xz->compressed_buffer_size - xz->stream.avail_out;
 		ret = __archive_write_filter(f->next_filter,
-		    data->compressed,
-		    data->compressed_buffer_size - data->stream.avail_out);
+		    xz->compressed,
+		    xz->compressed_buffer_size - xz->stream.avail_out);
 		if (f->code == ARCHIVE_FILTER_LZIP && ret == ARCHIVE_OK) {
-			archive_le32enc(data->compressed, data->crc32);
-			archive_le64enc(data->compressed+4, data->total_in);
-			archive_le64enc(data->compressed+12, data->total_out + 20);
+			archive_le32enc(xz->compressed, xz->crc32);
+			archive_le64enc(xz->compressed+4, xz->total_in);
+			archive_le64enc(xz->compressed+12, xz->total_out + 20);
 			ret = __archive_write_filter(f->next_filter,
-			    data->compressed, 20);
+			    xz->compressed, 20);
 		}
 	}
-	lzma_end(&(data->stream));
+	lzma_end(&(xz->stream));
 	return ret;
 }
 
 static int
 archive_compressor_xz_free(struct archive_write_filter *f)
 {
-	struct private_data *data = (struct private_data *)f->data;
-	free(data->compressed);
-	free(data);
+	free_data(f->data);
 	f->data = NULL;
 	return (ARCHIVE_OK);
 }
@@ -494,34 +477,34 @@
  */
 static int
 drive_compressor(struct archive_write_filter *f,
-    struct private_data *data, int finishing)
+    struct xz *xz, int finishing)
 {
 	int ret;
 
 	for (;;) {
-		if (data->stream.avail_out == 0) {
-			data->total_out += data->compressed_buffer_size;
+		if (xz->stream.avail_out == 0) {
+			xz->total_out += xz->compressed_buffer_size;
 			ret = __archive_write_filter(f->next_filter,
-			    data->compressed,
-			    data->compressed_buffer_size);
+			    xz->compressed,
+			    xz->compressed_buffer_size);
 			if (ret != ARCHIVE_OK)
 				return (ARCHIVE_FATAL);
-			data->stream.next_out = data->compressed;
-			data->stream.avail_out = data->compressed_buffer_size;
+			xz->stream.next_out = xz->compressed;
+			xz->stream.avail_out = xz->compressed_buffer_size;
 		}
 
 		/* If there's nothing to do, we're done. */
-		if (!finishing && data->stream.avail_in == 0)
+		if (!finishing && xz->stream.avail_in == 0)
 			return (ARCHIVE_OK);
 
-		ret = lzma_code(&(data->stream),
+		ret = lzma_code(&(xz->stream),
 		    finishing ? LZMA_FINISH : LZMA_RUN );
 
 		switch (ret) {
 		case LZMA_OK:
 			/* In non-finishing case, check if compressor
 			 * consumed everything */
-			if (!finishing && data->stream.avail_in == 0)
+			if (!finishing && xz->stream.avail_in == 0)
 				return (ARCHIVE_OK);
 			/* In finishing case, this return always means
 			 * there's more work */
@@ -537,7 +520,7 @@
 			archive_set_error(f->archive, ENOMEM,
 			    "lzma compression error: "
 			    "%ju MiB would have been needed",
-			    (uintmax_t)((lzma_memusage(&(data->stream))
+			    (uintmax_t)((lzma_memusage(&(xz->stream))
 				    + 1024 * 1024 -1)
 				/ (1024 * 1024)));
 			return (ARCHIVE_FATAL);
@@ -549,6 +532,15 @@
 			    ret);
 			return (ARCHIVE_FATAL);
 		}
+	}
+}
+
+static void
+free_data(struct xz *xz)
+{
+	if (xz != NULL) {
+		free(xz->compressed);
+		free(xz);
 	}
 }
 
diff --git a/c/archive_write_add_filter_zstd.c b/c/archive_write_add_filter_zstd.c
--- a/c/archive_write_add_filter_zstd.c
+++ b/c/archive_write_add_filter_zstd.c
@@ -55,7 +55,7 @@
 
 /* Don't compile this if we don't have zstd.h */
 
-struct private_data {
+struct zstd {
 	int		 compression_level;
 	int		 threads;
 	int		 long_distance;
@@ -103,79 +103,77 @@
 static int archive_compressor_zstd_free(struct archive_write_filter *);
 #if HAVE_ZSTD_H && HAVE_ZSTD_compressStream
 static int drive_compressor(struct archive_write_filter *,
-		    struct private_data *, int, const void *, size_t);
+		    struct zstd *, int, const void *, size_t);
 #endif
+static void free_data(struct zstd *);
 
 
 /*
  * Add a zstd compression filter to this write handle.
  */
 int
-archive_write_add_filter_zstd(struct archive *_a)
+archive_write_add_filter_zstd(struct archive *a)
 {
-	struct archive_write *a = (struct archive_write *)_a;
-	struct archive_write_filter *f = __archive_write_allocate_filter(_a);
-	struct private_data *data;
-	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+	struct archive_write_filter *f;
+	struct zstd *zstd;
+	int r;
+
+	archive_check_magic(a, ARCHIVE_WRITE_MAGIC,
 	    ARCHIVE_STATE_NEW, "archive_write_add_filter_zstd");
 
-	data = calloc(1, sizeof(*data));
-	if (data == NULL) {
-		archive_set_error(&a->archive, ENOMEM, "Out of memory");
-		return (ARCHIVE_FATAL);
-	}
-	f->data = data;
-	f->open = &archive_compressor_zstd_open;
-	f->options = &archive_compressor_zstd_options;
-	f->flush = &archive_compressor_zstd_flush;
-	f->close = &archive_compressor_zstd_close;
-	f->free = &archive_compressor_zstd_free;
-	f->code = ARCHIVE_FILTER_ZSTD;
-	f->name = "zstd";
-	data->compression_level = CLEVEL_DEFAULT;
-	data->threads = 0;
-	data->long_distance = 0;
+	zstd = calloc(1, sizeof(*zstd));
+	if (zstd == NULL)
+		goto memerr;
+	zstd->compression_level = CLEVEL_DEFAULT;
+	zstd->threads = 0;
+	zstd->long_distance = 0;
 #if HAVE_ZSTD_H && HAVE_ZSTD_compressStream
-	data->frame_per_file = 0;
-	data->min_frame_in = 0;
-	data->max_frame_in = SIZE_MAX;
-	data->min_frame_out = 0;
-	data->max_frame_out = SIZE_MAX;
-	data->cur_frame_in = 0;
-	data->cur_frame_out = 0;
-	data->cstream = ZSTD_createCStream();
-	if (data->cstream == NULL) {
-		free(data);
-		archive_set_error(&a->archive, ENOMEM,
-		    "Failed to allocate zstd compressor object");
-		return (ARCHIVE_FATAL);
-	}
+	zstd->frame_per_file = 0;
+	zstd->min_frame_in = 0;
+	zstd->max_frame_in = SIZE_MAX;
+	zstd->min_frame_out = 0;
+	zstd->max_frame_out = SIZE_MAX;
+	zstd->cur_frame_in = 0;
+	zstd->cur_frame_out = 0;
+	zstd->cstream = ZSTD_createCStream();
+	if (zstd->cstream == NULL)
+		goto memerr;
 
-	return (ARCHIVE_OK);
+	r = ARCHIVE_OK;
 #else
-	data->pdata = __archive_write_program_allocate("zstd");
-	if (data->pdata == NULL) {
-		free(data);
-		archive_set_error(&a->archive, ENOMEM, "Out of memory");
-		return (ARCHIVE_FATAL);
-	}
-	archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+	zstd->pdata = __archive_write_program_allocate("zstd");
+	if (zstd->pdata == NULL)
+		goto memerr;
+
+	archive_set_error(a, ARCHIVE_ERRNO_MISC,
 	    "Using external zstd program");
-	return (ARCHIVE_WARN);
+	r = ARCHIVE_WARN;
 #endif
+
+	f = __archive_write_allocate_filter(a);
+	if (f == NULL)
+		goto memerr;
+	f->name = "zstd";
+	f->code = ARCHIVE_FILTER_ZSTD;
+	f->data = zstd;
+	f->options = archive_compressor_zstd_options;
+	f->open = archive_compressor_zstd_open;
+	f->write = archive_compressor_zstd_write;
+	f->flush = archive_compressor_zstd_flush;
+	f->close = archive_compressor_zstd_close;
+	f->free = archive_compressor_zstd_free;
+
+	return (r);
+memerr:
+	free_data(zstd);
+	archive_set_error(a, ENOMEM, "Out of memory");
+	return (ARCHIVE_FATAL);
 }
 
 static int
 archive_compressor_zstd_free(struct archive_write_filter *f)
 {
-	struct private_data *data = (struct private_data *)f->data;
-#if HAVE_ZSTD_H && HAVE_ZSTD_compressStream
-	ZSTD_freeCStream(data->cstream);
-	free(data->out.dst);
-#else
-	__archive_write_program_free(data->pdata);
-#endif
-	free(data);
+	free_data(f->data);
 	f->data = NULL;
 	return (ARCHIVE_OK);
 }
@@ -239,7 +237,7 @@
 archive_compressor_zstd_options(struct archive_write_filter *f, const char *key,
     const char *value)
 {
-	struct private_data *data = (struct private_data *)f->data;
+	struct zstd *zstd = f->data;
 
 	if (strcmp(key, "compression-level") == 0) {
 		intmax_t level;
@@ -268,7 +266,7 @@
 			    "compression-level out of range");
 			return (ARCHIVE_FAILED);
 		}
-		data->compression_level = (int)level;
+		zstd->compression_level = (int)level;
 		return (ARCHIVE_OK);
 	} else if (strcmp(key, "threads") == 0) {
 		intmax_t threads;
@@ -295,14 +293,14 @@
 			    "threads out of rnage");
 			return (ARCHIVE_FAILED);
 		}
-		data->threads = (int)threads;
+		zstd->threads = (int)threads;
 		return (ARCHIVE_OK);
 #if HAVE_ZSTD_H && HAVE_ZSTD_compressStream
 	} else if (strcmp(key, "frame-per-file") == 0) {
-		data->frame_per_file = 1;
+		zstd->frame_per_file = 1;
 		return (ARCHIVE_OK);
 	} else if (strcmp(key, "min-frame-in") == 0) {
-		if (string_to_size(value, &data->min_frame_in) != ARCHIVE_OK) {
+		if (string_to_size(value, &zstd->min_frame_in) != ARCHIVE_OK) {
 			archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
 			    "min-frame-in invalid");
 			return (ARCHIVE_FAILED);
@@ -310,7 +308,7 @@
 		return (ARCHIVE_OK);
 	} else if (strcmp(key, "min-frame-out") == 0 ||
 	    strcmp(key, "min-frame-size") == 0) {
-		if (string_to_size(value, &data->min_frame_out) != ARCHIVE_OK) {
+		if (string_to_size(value, &zstd->min_frame_out) != ARCHIVE_OK) {
 			archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
 			    "min-frame-out invalid");
 			return (ARCHIVE_FAILED);
@@ -318,16 +316,16 @@
 		return (ARCHIVE_OK);
 	} else if (strcmp(key, "max-frame-in") == 0 ||
 	    strcmp(key, "max-frame-size") == 0) {
-		if (string_to_size(value, &data->max_frame_in) != ARCHIVE_OK ||
-		    data->max_frame_in < 1024) {
+		if (string_to_size(value, &zstd->max_frame_in) != ARCHIVE_OK ||
+		    zstd->max_frame_in < 1024) {
 			archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
 			    "max-frame-size invalid");
 			return (ARCHIVE_FAILED);
 		}
 		return (ARCHIVE_OK);
 	} else if (strcmp(key, "max-frame-out") == 0) {
-		if (string_to_size(value, &data->max_frame_out) != ARCHIVE_OK ||
-		    data->max_frame_out < 1024) {
+		if (string_to_size(value, &zstd->max_frame_out) != ARCHIVE_OK ||
+		    zstd->max_frame_out < 1024) {
 			archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
 			    "max-frame-out invalid");
 			return (ARCHIVE_FAILED);
@@ -363,7 +361,7 @@
 		if (((int)long_distance) < 10 || (int)long_distance > max_distance)
 		    return (ARCHIVE_FAILED);
 #endif
-		data->long_distance = (int)long_distance;
+		zstd->long_distance = (int)long_distance;
 		return (ARCHIVE_OK);
 	}
 
@@ -380,9 +378,9 @@
 static int
 archive_compressor_zstd_open(struct archive_write_filter *f)
 {
-	struct private_data *data = (struct private_data *)f->data;
+	struct zstd *zstd = f->data;
 
-	if (data->out.dst == NULL) {
+	if (zstd->out.dst == NULL) {
 		size_t bs = ZSTD_CStreamOutSize(), bpb;
 		if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {
 			/* Buffer size should be a multiple number of
@@ -393,31 +391,29 @@
 			else if (bpb != 0)
 				bs -= bs % bpb;
 		}
-		data->out.size = bs;
-		data->out.pos = 0;
-		data->out.dst = malloc(data->out.size);
-		if (data->out.dst == NULL) {
+		zstd->out.size = bs;
+		zstd->out.pos = 0;
+		zstd->out.dst = malloc(zstd->out.size);
+		if (zstd->out.dst == NULL) {
 			archive_set_error(f->archive, ENOMEM,
 			    "Can't allocate data for compression buffer");
 			return (ARCHIVE_FATAL);
 		}
 	}
 
-	f->write = archive_compressor_zstd_write;
-
-	if (ZSTD_isError(ZSTD_initCStream(data->cstream,
-	    data->compression_level))) {
+	if (ZSTD_isError(ZSTD_initCStream(zstd->cstream,
+	    zstd->compression_level))) {
 		archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
 		    "Internal error initializing zstd compressor object");
 		return (ARCHIVE_FATAL);
 	}
 
-	ZSTD_CCtx_setParameter(data->cstream, ZSTD_c_nbWorkers, data->threads);
+	ZSTD_CCtx_setParameter(zstd->cstream, ZSTD_c_nbWorkers, zstd->threads);
 
-	ZSTD_CCtx_setParameter(data->cstream, ZSTD_c_checksumFlag, 1);
+	ZSTD_CCtx_setParameter(zstd->cstream, ZSTD_c_checksumFlag, 1);
 
 #if ZSTD_VERSION_NUMBER >= MINVER_LONG
-	ZSTD_CCtx_setParameter(data->cstream, ZSTD_c_windowLog, data->long_distance);
+	ZSTD_CCtx_setParameter(zstd->cstream, ZSTD_c_windowLog, zstd->long_distance);
 #endif
 
 	return (ARCHIVE_OK);
@@ -430,9 +426,9 @@
 archive_compressor_zstd_write(struct archive_write_filter *f, const void *buff,
     size_t length)
 {
-	struct private_data *data = (struct private_data *)f->data;
+	struct zstd *zstd = f->data;
 
-	return (drive_compressor(f, data, 0, buff, length));
+	return (drive_compressor(f, zstd, 0, buff, length));
 }
 
 /*
@@ -441,15 +437,15 @@
 static int
 archive_compressor_zstd_flush(struct archive_write_filter *f)
 {
-	struct private_data *data = (struct private_data *)f->data;
+	struct zstd *zstd = f->data;
 
-	if (data->frame_per_file && data->state == running) {
-		if (data->cur_frame_in > data->min_frame_in &&
-		    data->cur_frame_out > data->min_frame_out) {
-			data->state = finishing;
+	if (zstd->frame_per_file && zstd->state == running) {
+		if (zstd->cur_frame_in > zstd->min_frame_in &&
+		    zstd->cur_frame_out > zstd->min_frame_out) {
+			zstd->state = finishing;
 		}
 	}
-	return (drive_compressor(f, data, 1, NULL, 0));
+	return (drive_compressor(f, zstd, 1, NULL, 0));
 }
 
 /*
@@ -458,11 +454,11 @@
 static int
 archive_compressor_zstd_close(struct archive_write_filter *f)
 {
-	struct private_data *data = (struct private_data *)f->data;
+	struct zstd *zstd = f->data;
 
-	if (data->state == running)
-		data->state = finishing;
-	return (drive_compressor(f, data, 1, NULL, 0));
+	if (zstd->state == running)
+		zstd->state = finishing;
+	return (drive_compressor(f, zstd, 1, NULL, 0));
 }
 
 /*
@@ -471,7 +467,7 @@
  */
 static int
 drive_compressor(struct archive_write_filter *f,
-    struct private_data *data, int flush, const void *src, size_t length)
+    struct zstd *zstd, int flush, const void *src, size_t length)
 {
 	ZSTD_inBuffer in = { .src = src, .size = length, .pos = 0 };
 	size_t ipos, opos, zstdret = 0;
@@ -479,46 +475,46 @@
 
 	for (;;) {
 		ipos = in.pos;
-		opos = data->out.pos;
-		switch (data->state) {
+		opos = zstd->out.pos;
+		switch (zstd->state) {
 		case running:
 			if (in.pos == in.size)
 				return (ARCHIVE_OK);
-			zstdret = ZSTD_compressStream(data->cstream,
-			    &data->out, &in);
+			zstdret = ZSTD_compressStream(zstd->cstream,
+			    &zstd->out, &in);
 			if (ZSTD_isError(zstdret))
 				goto zstd_fatal;
 			break;
 		case finishing:
-			zstdret = ZSTD_endStream(data->cstream, &data->out);
+			zstdret = ZSTD_endStream(zstd->cstream, &zstd->out);
 			if (ZSTD_isError(zstdret))
 				goto zstd_fatal;
 			if (zstdret == 0)
-				data->state = resetting;
+				zstd->state = resetting;
 			break;
 		case resetting:
-			ZSTD_CCtx_reset(data->cstream, ZSTD_reset_session_only);
-			data->cur_frame++;
-			data->cur_frame_in = 0;
-			data->cur_frame_out = 0;
-			data->state = running;
+			ZSTD_CCtx_reset(zstd->cstream, ZSTD_reset_session_only);
+			zstd->cur_frame++;
+			zstd->cur_frame_in = 0;
+			zstd->cur_frame_out = 0;
+			zstd->state = running;
 			break;
 		}
-		data->cur_frame_in += in.pos - ipos;
-		data->cur_frame_out += data->out.pos - opos;
-		if (data->state == running) {
-			if (data->cur_frame_in >= data->max_frame_in ||
-			    data->cur_frame_out >= data->max_frame_out) {
-				data->state = finishing;
+		zstd->cur_frame_in += in.pos - ipos;
+		zstd->cur_frame_out += zstd->out.pos - opos;
+		if (zstd->state == running) {
+			if (zstd->cur_frame_in >= zstd->max_frame_in ||
+			    zstd->cur_frame_out >= zstd->max_frame_out) {
+				zstd->state = finishing;
 			}
 		}
-		if (data->out.pos == data->out.size ||
-		    (flush && data->out.pos > 0)) {
+		if (zstd->out.pos == zstd->out.size ||
+		    (flush && zstd->out.pos > 0)) {
 			ret = __archive_write_filter(f->next_filter,
-			    data->out.dst, data->out.pos);
+			    zstd->out.dst, zstd->out.pos);
 			if (ret != ARCHIVE_OK)
 				goto fatal;
-			data->out.pos = 0;
+			zstd->out.pos = 0;
 		}
 	}
 zstd_fatal:
@@ -529,12 +525,22 @@
 	return (ARCHIVE_FATAL);
 }
 
+static void
+free_data(struct zstd *zstd)
+{
+	if (zstd != NULL) {
+		ZSTD_freeCStream(zstd->cstream);
+		free(zstd->out.dst);
+		free(zstd);
+	}
+}
+
 #else /* HAVE_ZSTD_H && HAVE_ZSTD_compressStream */
 
 static int
 archive_compressor_zstd_open(struct archive_write_filter *f)
 {
-	struct private_data *data = (struct private_data *)f->data;
+	struct zstd *zstd = f->data;
 	struct archive_string as;
 	int r;
 
@@ -542,26 +548,25 @@
 	/* --no-check matches library default */
 	archive_strcpy(&as, "zstd --no-check");
 
-	if (data->compression_level < CLEVEL_STD_MIN) {
-		archive_string_sprintf(&as, " --fast=%d", -data->compression_level);
+	if (zstd->compression_level < CLEVEL_STD_MIN) {
+		archive_string_sprintf(&as, " --fast=%d", -zstd->compression_level);
 	} else {
-		archive_string_sprintf(&as, " -%d", data->compression_level);
+		archive_string_sprintf(&as, " -%d", zstd->compression_level);
 	}
 
-	if (data->compression_level > CLEVEL_STD_MAX) {
+	if (zstd->compression_level > CLEVEL_STD_MAX) {
 		archive_strcat(&as, " --ultra");
 	}
 
-	if (data->threads != 0) {
-		archive_string_sprintf(&as, " --threads=%d", data->threads);
+	if (zstd->threads != 0) {
+		archive_string_sprintf(&as, " --threads=%d", zstd->threads);
 	}
 
-	if (data->long_distance != 0) {
-		archive_string_sprintf(&as, " --long=%d", data->long_distance);
+	if (zstd->long_distance != 0) {
+		archive_string_sprintf(&as, " --long=%d", zstd->long_distance);
 	}
 
-	f->write = archive_compressor_zstd_write;
-	r = __archive_write_program_open(f, data->pdata, as.s);
+	r = __archive_write_program_open(f, zstd->pdata, as.s);
 	archive_string_free(&as);
 	return (r);
 }
@@ -570,9 +575,9 @@
 archive_compressor_zstd_write(struct archive_write_filter *f, const void *buff,
     size_t length)
 {
-	struct private_data *data = (struct private_data *)f->data;
+	struct zstd *zstd = f->data;
 
-	return __archive_write_program_write(f, data->pdata, buff, length);
+	return __archive_write_program_write(f, zstd->pdata, buff, length);
 }
 
 static int
@@ -586,9 +591,18 @@
 static int
 archive_compressor_zstd_close(struct archive_write_filter *f)
 {
-	struct private_data *data = (struct private_data *)f->data;
+	struct zstd *zstd = f->data;
 
-	return __archive_write_program_close(f, data->pdata);
+	return __archive_write_program_close(f, zstd->pdata);
+}
+
+static void
+free_data(struct zstd *zstd)
+{
+	if (zstd != NULL) {
+		__archive_write_program_free(zstd->pdata);
+		free(zstd);
+	}
 }
 
 #endif /* HAVE_ZSTD_H && HAVE_ZSTD_compressStream */
diff --git a/c/archive_write_disk_posix.c b/c/archive_write_disk_posix.c
--- a/c/archive_write_disk_posix.c
+++ b/c/archive_write_disk_posix.c
@@ -300,7 +300,7 @@
 	uint32_t		 compressed_rsrc_position;
 	uint32_t		 compressed_rsrc_position_v;
 	/* Buffer for uncompressed data. */
-	char			*uncompressed_buffer;
+	unsigned char		*uncompressed_buffer;
 	size_t			 block_remaining_bytes;
 	size_t			 file_remaining_bytes;
 #ifdef HAVE_ZLIB_H
@@ -1254,7 +1254,7 @@
 hfs_decompress(struct archive_write_disk *a)
 {
 	uint32_t *block_info;
-	unsigned int block_count;
+	uint32_t block_count;
 	uint32_t data_pos, data_size;
 	ssize_t r;
 	ssize_t bytes_written, bytes_to_write;
@@ -1277,10 +1277,10 @@
 			bytes_to_write = data_size -1;
 			b = a->compressed_buffer + 1;
 		} else {
-			uLong dest_len = MAX_DECMPFS_BLOCK_SIZE;
+			size_t dest_len = MAX_DECMPFS_BLOCK_SIZE;
 			int zr;
 
-			zr = uncompress((Bytef *)a->uncompressed_buffer,
+			zr = uncompress(a->uncompressed_buffer,
 			    &dest_len, a->compressed_buffer, data_size);
 			if (zr != Z_OK) {
 				archive_set_error(&a->archive,
@@ -1289,7 +1289,7 @@
 				return (ARCHIVE_WARN);
 			}
 			bytes_to_write = dest_len;
-			b = (unsigned char *)a->uncompressed_buffer;
+			b = a->uncompressed_buffer;
 		}
 		do {
 			bytes_written = write(a->fd, b, bytes_to_write);
@@ -1472,8 +1472,7 @@
 
 	if (a->decmpfs_block_count == (unsigned)-1) {
 		void *new_block;
-		size_t new_size;
-		unsigned int block_count;
+		size_t block_count, new_size;
 
 		if (a->decmpfs_header_p == NULL) {
 			new_block = malloc(MAX_DECMPFS_XATTR_SIZE
@@ -1493,13 +1492,20 @@
 		archive_le64enc(&a->decmpfs_header_p[DECMPFS_UNCOMPRESSED_SIZE],
 		    a->filesize);
 
-		/* Calculate a block count of the file. */
-		block_count =
-		    (a->filesize + MAX_DECMPFS_BLOCK_SIZE -1) /
-			MAX_DECMPFS_BLOCK_SIZE;
 		/*
+		 * Calculate block count for the file.
+		 */
+		block_count = (a->filesize + MAX_DECMPFS_BLOCK_SIZE - 1) /
+		    MAX_DECMPFS_BLOCK_SIZE;
+		if (block_count > (SIZE_MAX - RSRC_H_SIZE - 4 - RSRC_F_SIZE) /
+		    (sizeof(uint32_t) * 2) || block_count > UINT32_MAX) {
+			archive_set_error(&a->archive, EFBIG, "File too big");
+			return (ARCHIVE_FATAL);
+		}
+
+		/*
 		 * Allocate buffer for resource fork.
-		 * Set up related pointers;
+		 * Set up related pointers.
 		 */
 		new_size =
 		    RSRC_H_SIZE + /* header */
@@ -1540,7 +1546,7 @@
 		a->decmpfs_block_info =
 		    (uint32_t *)(a->resource_fork + RSRC_H_SIZE);
 		/* Set the block count to the resource fork. */
-		archive_le32enc(a->decmpfs_block_info++, block_count);
+		archive_le32enc(a->decmpfs_block_info++, (uint32_t)block_count);
 		/* Get the position where we are going to set compressed
 		 * data. */
 		a->compressed_rsrc_position =
@@ -2369,7 +2375,7 @@
 #endif
 			if (r != 0)
 				r = errno;
-			else if ((st.st_mode & AE_IFMT) == AE_IFREG) {
+			else if (S_ISREG(st.st_mode)) {
 				a->fd = open(a->name, O_WRONLY | O_TRUNC |
 				    O_BINARY | O_CLOEXEC | O_NOFOLLOW);
 				__archive_ensure_cloexec_flag(a->fd);
@@ -4344,7 +4350,7 @@
 		 * with at least a writing mode(O_RDWR or O_WRONLY). it
 		 * makes the data fork uncompressed.
 		 */
-		dffd = open(datafork, 0);
+		dffd = open(datafork, O_SYMLINK);
 		if (dffd == -1) {
 			archive_set_error(&a->archive, errno,
 			    "Failed to open the data fork for metadata");
@@ -4446,14 +4452,16 @@
 	 */
 	archive_strncpy(&datafork, pathname, p - pathname);
 	archive_strcat(&datafork, p + 2);
-	if (
 #ifdef HAVE_LSTAT
-		lstat(datafork.s, &st) == -1 ||
+	if (lstat(datafork.s, &st) == -1)
+		goto skip_appledouble;
 #else
-		la_stat(datafork.s, &st) == -1 ||
+	if (la_stat(datafork.s, &st) == -1)
+		goto skip_appledouble;
 #endif
-	    (((st.st_mode & AE_IFMT) != AE_IFREG) &&
-		((st.st_mode & AE_IFMT) != AE_IFDIR)))
+	if (!S_ISREG(st.st_mode) &&
+	    !S_ISDIR(st.st_mode) &&
+	    !S_ISLNK(st.st_mode))
 		goto skip_appledouble;
 
 	/*
diff --git a/c/archive_write_disk_set_standard_lookup.c b/c/archive_write_disk_set_standard_lookup.c
--- a/c/archive_write_disk_set_standard_lookup.c
+++ b/c/archive_write_disk_set_standard_lookup.c
@@ -45,6 +45,7 @@
 #endif
 
 #include "archive.h"
+#include "archive_integer.h"
 #include "archive_private.h"
 #include "archive_read_private.h"
 #include "archive_write_disk_private.h"
@@ -134,7 +135,8 @@
 				break;
 			if (r != ERANGE)
 				break;
-			bufsize *= 2;
+			if (archive_ckd_mul_size(&bufsize, bufsize, 2))
+				break;
 			free(allocated);
 			allocated = malloc(bufsize);
 			if (allocated == NULL)
@@ -203,7 +205,8 @@
 				break;
 			if (r != ERANGE)
 				break;
-			bufsize *= 2;
+			if (archive_ckd_mul_size(&bufsize, bufsize, 2))
+				break;
 			free(allocated);
 			allocated = malloc(bufsize);
 			if (allocated == NULL)
diff --git a/c/archive_write_disk_windows.c b/c/archive_write_disk_windows.c
--- a/c/archive_write_disk_windows.c
+++ b/c/archive_write_disk_windows.c
@@ -194,6 +194,8 @@
 
 static int	disk_unlink(const wchar_t *);
 static int	disk_rmdir(const wchar_t *);
+static int	check_symlinks_by_path(struct archive_write_disk *,
+		    wchar_t *, size_t *, int);
 static int	check_symlinks(struct archive_write_disk *);
 static int	create_filesystem_object(struct archive_write_disk *);
 static struct fixup_entry *current_fixup(struct archive_write_disk *,
@@ -244,8 +246,8 @@
     ((((int64_t)(bhfi)->nFileSizeHigh) << 32) | (bhfi)->nFileSizeLow)
 
 static int
-file_information(struct archive_write_disk *a, wchar_t *path,
-    BY_HANDLE_FILE_INFORMATION *st, mode_t *mode, int sim_lstat)
+file_information(wchar_t *path, BY_HANDLE_FILE_INFORMATION *st,
+    mode_t *mode, int sim_lstat)
 {
 	HANDLE h;
 	int r;
@@ -282,10 +284,10 @@
 	ZeroMemory(&createExParams, sizeof(createExParams));
 	createExParams.dwSize = sizeof(createExParams);
 	createExParams.dwFileFlags = flag;
-	h = CreateFile2(a->name, 0, 0,
+	h = CreateFile2(path, 0, 0,
 		OPEN_EXISTING, &createExParams);
 #else
-	h = CreateFileW(a->name, 0, 0, NULL,
+	h = CreateFileW(path, 0, 0, NULL,
 	    OPEN_EXISTING, flag, NULL);
 #endif
 	if (h == INVALID_HANDLE_VALUE &&
@@ -629,6 +631,7 @@
 	DWORD attrs = 0;
 	DWORD flags = 0;
 	DWORD newflags = 0;
+	DWORD lasterr = 0;
 
 	len = wcslen(target);
 	if (len == 0) {
@@ -693,10 +696,14 @@
 	ret = CreateSymbolicLinkW(linkname, ttarget, newflags);
 	/*
 	 * Prior to Windows 10 calling CreateSymbolicLinkW() will fail
-	 * if SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE is set
+	 * if SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE is set; however,
+	 * trying this fallback if we fail due to a bad path will replace
+	 * ENOENT with EPERM and confuse later error recovery efforts.
 	 */
 	if (!ret) {
-		ret = CreateSymbolicLinkW(linkname, ttarget, flags);
+		lasterr = GetLastError();
+		if (lasterr != ERROR_PATH_NOT_FOUND)
+			ret = CreateSymbolicLinkW(linkname, ttarget, flags);
 	}
 	free(ttarget);
 #endif
@@ -741,7 +748,7 @@
 	 * XXX At this point, symlinks should not be hit, otherwise
 	 * XXX a race occurred.  Do we want to check explicitly for that?
 	 */
-	if (file_information(a, a->name, &a->st, NULL, 1) == 0) {
+	if (file_information(a->name, &a->st, NULL, 1) == 0) {
 		a->pst = &a->st;
 		return (ARCHIVE_OK);
 	}
@@ -1516,7 +1523,7 @@
 		 * such symlinks. We always need both source and target
 		 * information.
 		 */
-		r = file_information(a, a->name, &lst, &lst_mode, 1);
+		r = file_information(a->name, &lst, &lst_mode, 1);
 		if (r != 0) {
 			archive_set_error(&a->archive, errno,
 			    "Can't stat existing object");
@@ -1525,7 +1532,7 @@
 			if (lst.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
 				dirlnk = 1;
 			/* In case of a symlink we need target information */
-			r = file_information(a, a->name, &a->st, &st_mode, 0);
+			r = file_information(a->name, &a->st, &st_mode, 0);
 			if (r != 0) {
 				a->st = lst;
 				st_mode = lst_mode;
@@ -1653,6 +1660,7 @@
 	mode_t final_mode, mode;
 	int r;
 	DWORD attrs = 0;
+	DWORD file_flags = 0;
 # if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
 		CREATEFILE2_EXTENDED_PARAMETERS createExParams;
 #endif
@@ -1675,6 +1683,22 @@
 			free(linksanitized);
 			return (r);
 		}
+
+		if (a->flags & ARCHIVE_EXTRACT_SECURE_SYMLINKS) {
+			r = check_symlinks_by_path(a, linksanitized, NULL, 1);
+			if (r != ARCHIVE_OK) {
+				/*
+				 * Prevent extracting a hardlink to a symlink.
+				 * libarchive supports the POSIX feature of
+				 * hardlinks with data payloads, which would
+				 * provide a way to write through an otherwise
+				 * disallowed symlink.
+				 */
+				free(linksanitized);
+				return (r);
+			}
+		}
+
 		linkfull = __la_win_permissive_name_w(linksanitized);
 		free(linksanitized);
 		namefull = __la_win_permissive_name_w(a->name);
@@ -1752,6 +1776,13 @@
 			else
 				disk_unlink(a->name);
 		}
+
+		/*
+		 * Since the symlink-safe path cache is just an optimization, and we
+		 * are about to create a new symlink, invalidating the cache prevents
+		 * incidentally writing through the entry we are about to create.
+		 */
+		a->path_safe.s[0] = 0;
 #if HAVE_SYMLINK
 		return symlink(linkname, a->name) ? errno : 0;
 #else
@@ -1790,16 +1821,31 @@
 	case AE_IFREG:
 		a->tmpname = NULL;
 		fullname = a->name;
+		if ((a->flags & ARCHIVE_EXTRACT_SECURE_SYMLINKS) == 0) {
+			/*
+			 * Creating a regular file is expected to fail with EEXIST if *any
+			 * object* exists at the target location.
+			 * SECURE_SYMLINKS deletes any symbolic link that would have been
+			 * written through. When it's off, though, we risk writing through
+			 * extant but broken symlinks unless we allow CreateFile to fail
+			 * on the link rather than creating its target.
+			 *
+			 * This is effectively O_EXCL.
+			 */
+			file_flags |= FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS;
+		}
+
 		/* O_WRONLY | O_CREAT | O_EXCL */
 # if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
 		ZeroMemory(&createExParams, sizeof(createExParams));
 		createExParams.dwSize = sizeof(createExParams);
 		createExParams.dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
+		createExParams.dwFileFlags = file_flags;
 		a->fh = CreateFile2(fullname, GENERIC_WRITE, 0,
 		    CREATE_NEW, &createExParams);
 #else
 		a->fh = CreateFileW(fullname, GENERIC_WRITE, 0, NULL,
-		    CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
+		    CREATE_NEW, FILE_ATTRIBUTE_NORMAL | file_flags, NULL);
 #endif
 		if (a->fh == INVALID_HANDLE_VALUE &&
 		    GetLastError() == ERROR_INVALID_NAME &&
@@ -1810,7 +1856,7 @@
 			    CREATE_NEW, &createExParams);
 #else
 			a->fh = CreateFileW(fullname, GENERIC_WRITE, 0, NULL,
-			    CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
+			    CREATE_NEW, FILE_ATTRIBUTE_NORMAL | file_flags, NULL);
 #endif
 		}
 		if (a->fh == INVALID_HANDLE_VALUE) {
@@ -2075,9 +2121,10 @@
  * recent paths.
  */
 static int
-check_symlinks(struct archive_write_disk *a)
+check_symlinks_by_path(struct archive_write_disk *a,
+    wchar_t *path, size_t *safe_len, int checking_linkname)
 {
-	wchar_t *pn, *p;
+	wchar_t *pn, *p, *su;
 	wchar_t c;
 	int r;
 	BY_HANDLE_FILE_INFORMATION st;
@@ -2088,13 +2135,17 @@
 	 * destination would be altered by a symlink.
 	 */
 	/* Whatever we checked last time doesn't need to be re-checked. */
-	pn = a->name;
+	pn = path;
 	p = a->path_safe.s;
 	while ((*pn != '\0') && (*p == *pn))
 		++p, ++pn;
 	/* Skip leading backslashes */
 	while (*pn == '\\')
 		++pn;
+	su = pn;
+	/* The end-of-safe region, however, should not cover a partial leaf name */
+	while (su > path && *su != '\\')
+		--su;
 	c = pn[0];
 	/* Keep going until we've checked the entire name. */
 	while (pn[0] != '\0' && (pn[0] != '\\' || pn[1] != '\0')) {
@@ -2104,13 +2155,20 @@
 		c = pn[0];
 		pn[0] = '\0';
 		/* Check that we haven't hit a symlink. */
-		r = file_information(a, a->name, &st, &st_mode, 1);
+		r = file_information(path, &st, &st_mode, 1);
 		if (r != 0) {
 			/* We've hit a dir that doesn't exist; stop now. */
 			if (errno == ENOENT)
 				break;
 		} else if (S_ISLNK(st_mode)) {
 			if (c == '\0') {
+				if (checking_linkname) {
+					archive_set_error(&a->archive, ELOOP,
+					    "Cannot write hardlink to symlink %ls",
+					    path);
+					pn[0] = c;
+					return (ARCHIVE_FAILED);
+				}
 				/*
 				 * Last element is a file or directory symlink.
 				 * Remove it so we can overwrite it with the
@@ -2122,14 +2180,14 @@
 				}
 				if (st.dwFileAttributes &
 				    FILE_ATTRIBUTE_DIRECTORY) {
-					r = disk_rmdir(a->name);
+					r = disk_rmdir(path);
 				} else {
-					r = disk_unlink(a->name);
+					r = disk_unlink(path);
 				}
 				if (r) {
 					archive_set_error(&a->archive, errno,
 					    "Could not remove symlink %ls",
-					    a->name);
+					    path);
 					pn[0] = c;
 					return (ARCHIVE_FAILED);
 				}
@@ -2143,11 +2201,16 @@
 				if (!S_ISLNK(a->mode)) {
 					archive_set_error(&a->archive, -1,
 					    "Removing symlink %ls",
-					    a->name);
+					    path);
+					/*
+					 * We can safely return the entire path as a safe range,
+					 * having deleted the symlink at the leaf with no intent
+					 * to create another symlink.
+					 */
+					su = pn;
 				}
 				/* Symlink gone.  No more problem! */
-				pn[0] = c;
-				return (0);
+				break;
 			} else if (a->flags & ARCHIVE_EXTRACT_UNLINK) {
 				/* User asked us to remove problems. */
 				if (a->flags &
@@ -2156,14 +2219,14 @@
 				}
 				if (st.dwFileAttributes &
 				    FILE_ATTRIBUTE_DIRECTORY) {
-					r = disk_rmdir(a->name);
+					r = disk_rmdir(path);
 				} else {
-					r = disk_unlink(a->name);
+					r = disk_unlink(path);
 				}
 				if (r != 0) {
 					archive_set_error(&a->archive, EIO,
 					    "Cannot remove intervening "
-					    "symlink %ls", a->name);
+					    "symlink %ls", path);
 					pn[0] = c;
 					return (ARCHIVE_FAILED);
 				}
@@ -2171,23 +2234,48 @@
 			} else {
 				archive_set_error(&a->archive, ELOOP,
 				    "Cannot extract through symlink %ls",
-				    a->name);
+				    path);
 				pn[0] = c;
 				return (ARCHIVE_FAILED);
 			}
 		}
+		su = pn;
 		if (!c)
 			break;
 		pn[0] = c;
 		pn++;
 	}
 	pn[0] = c;
-	/* We've checked and/or cleaned the whole path, so remember it. */
-	archive_wstrcpy(&a->path_safe, a->name);
+	if (safe_len)
+		*safe_len = su - path;
 	return (ARCHIVE_OK);
 }
 
 static int
+check_symlinks(struct archive_write_disk *a)
+{
+	int r;
+	size_t safe_len;
+	r = check_symlinks_by_path(a, a->name, &safe_len, 0);
+
+	if (r == ARCHIVE_OK) {
+		/*
+		 * We've checked and/or cleaned the whole path, so remember the
+		 * portion check_symlinks_by_path returned as "safe" (typically
+		 * the parent of the entry we are about to create, unless that
+		 * entry was just unlinked.)
+		 *
+		 * We only cache path safety info for entry paths (which we
+		 * operate on in a->name). We do not cache safety info for
+		 * any other paths.
+		 */
+		archive_wstrncpy(&a->path_safe, a->name, safe_len);
+	}
+
+	return (r);
+}
+
+static int
 guidword(wchar_t *p, int n)
 {
 	int i;
@@ -2456,7 +2544,7 @@
 	 * here loses the ability to extract through symlinks.  Also note
 	 * that this should not use the a->st cache.
 	 */
-	if (file_information(a, path, &st, &st_mode, 0) == 0) {
+	if (file_information(path, &st, &st_mode, 0) == 0) {
 		if (S_ISDIR(st_mode))
 			return (ARCHIVE_OK);
 		if ((a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE)) {
@@ -2523,7 +2611,7 @@
 	 * don't add it to the fixup list here, as it's already been
 	 * added.
 	 */
-	if (file_information(a, path, &st, &st_mode, 0) == 0 &&
+	if (file_information(path, &st, &st_mode, 0) == 0 &&
 	    S_ISDIR(st_mode))
 		return (ARCHIVE_OK);
 
diff --git a/c/archive_write_private.h b/c/archive_write_private.h
--- a/c/archive_write_private.h
+++ b/c/archive_write_private.h
@@ -69,7 +69,7 @@
 struct archive_write_filter *__archive_write_allocate_filter(struct archive *);
 
 int __archive_write_output(struct archive_write *, const void *, size_t);
-int __archive_write_nulls(struct archive_write *, size_t);
+int __archive_write_nulls(struct archive_write *, uint64_t);
 int __archive_write_filter(struct archive_write_filter *, const void *, size_t);
 
 struct archive_write {
@@ -131,6 +131,8 @@
 	archive_passphrase_callback *passphrase_callback;
 	void		*passphrase_client_data;
 };
+
+int	__archive_write_unregister_format(struct archive_write *);
 
 /*
  * Utility function to format a USTAR header into a buffer.  If
diff --git a/c/archive_write_set_format_7zip.c b/c/archive_write_set_format_7zip.c
--- a/c/archive_write_set_format_7zip.c
+++ b/c/archive_write_set_format_7zip.c
@@ -215,8 +215,6 @@
 	size_t			 total_number_dir_entry;
 	size_t			 total_bytes_entry_name;
 	size_t			 total_number_time_defined[3];
-	uint64_t		 total_bytes_compressed;
-	uint64_t		 total_bytes_uncompressed;
 	uint64_t		 entry_bytes_remaining;
 	uint32_t		 entry_crc32;
 	uint32_t		 precode_crc32;
@@ -358,8 +356,7 @@
 	    ARCHIVE_STATE_NEW, "archive_write_set_format_7zip");
 
 	/* If another format was already registered, unregister it. */
-	if (a->format_free != NULL)
-		(a->format_free)(a);
+	(void)__archive_write_unregister_format(a);
 
 	zip = calloc(1, sizeof(*zip));
 	if (zip == NULL) {
@@ -415,9 +412,7 @@
 static int
 _7z_options(struct archive_write *a, const char *key, const char *value)
 {
-	struct _7zip *zip;
-
-	zip = (struct _7zip *)a->format_data;
+	struct _7zip *zip = a->format_data;
 
 	if (strcmp(key, "compression") == 0) {
 		const char *name = NULL;
@@ -559,11 +554,10 @@
 static int
 _7z_write_header(struct archive_write *a, struct archive_entry *entry)
 {
-	struct _7zip *zip;
+	struct _7zip *zip = a->format_data;
 	struct file *file;
 	int r;
 
-	zip = (struct _7zip *)a->format_data;
 	zip->cur_file = NULL;
 	zip->entry_bytes_remaining = 0;
 
@@ -667,12 +661,10 @@
 static int
 write_to_temp(struct archive_write *a, const void *buff, size_t s)
 {
-	struct _7zip *zip;
+	struct _7zip *zip = a->format_data;
 	const unsigned char *p;
 	ssize_t ws;
 
-	zip = (struct _7zip *)a->format_data;
-
 	/*
 	 * Open a temporary file.
 	 */
@@ -705,7 +697,7 @@
 compress_out(struct archive_write *a, const void *buff, size_t s,
     enum la_zaction run)
 {
-	struct _7zip *zip = (struct _7zip *)a->format_data;
+	struct _7zip *zip = a->format_data;
 	int r;
 
 	if (run == ARCHIVE_Z_FINISH && zip->stream.total_in == 0 && s == 0)
@@ -751,11 +743,9 @@
 static ssize_t
 _7z_write_data(struct archive_write *a, const void *buff, size_t s)
 {
-	struct _7zip *zip;
+	struct _7zip *zip = a->format_data;
 	ssize_t bytes;
 
-	zip = (struct _7zip *)a->format_data;
-
 	if (s > zip->entry_bytes_remaining)
 		s = (size_t)zip->entry_bytes_remaining;
 	if (s == 0 || zip->cur_file == NULL)
@@ -771,11 +761,10 @@
 static int
 _7z_finish_entry(struct archive_write *a)
 {
-	struct _7zip *zip;
+	struct _7zip *zip = a->format_data;
 	size_t s;
 	ssize_t r;
 
-	zip = (struct _7zip *)a->format_data;
 	if (zip->cur_file == NULL)
 		return (ARCHIVE_OK);
 
@@ -787,8 +776,6 @@
 		if (r < 0)
 			return ((int)r);
 	}
-	zip->total_bytes_compressed += zip->stream.total_in;
-	zip->total_bytes_uncompressed += zip->stream.total_out;
 	zip->cur_file->crc32 = zip->entry_crc32;
 	zip->cur_file = NULL;
 
@@ -798,11 +785,10 @@
 static int
 flush_wbuff(struct archive_write *a)
 {
-	struct _7zip *zip;
+	struct _7zip *zip = a->format_data;
 	int r;
 	size_t s;
 
-	zip = (struct _7zip *)a->format_data;
 	s = sizeof(zip->wbuff) - zip->wbuff_remaining;
 	r = __archive_write_output(a, zip->wbuff, s);
 	if (r != ARCHIVE_OK)
@@ -814,10 +800,9 @@
 static int
 copy_out(struct archive_write *a, uint64_t offset, uint64_t length)
 {
-	struct _7zip *zip;
+	struct _7zip *zip = a->format_data;
 	int r;
 
-	zip = (struct _7zip *)a->format_data;
 	if (zip->temp_offset > 0 &&
 	    lseek(zip->temp_fd, offset, SEEK_SET) < 0) {
 		archive_set_error(&(a->archive), errno, "lseek failed");
@@ -860,15 +845,13 @@
 static int
 _7z_close(struct archive_write *a)
 {
-	struct _7zip *zip;
+	struct _7zip *zip = a->format_data;
 	unsigned char *wb;
 	uint64_t header_offset, header_size, header_unpacksize;
 	uint64_t length;
 	uint32_t header_crc32;
 	int r;
 
-	zip = (struct _7zip *)a->format_data;
-
 	if (zip->total_number_entry > 0) {
 		struct archive_rb_node *n;
 		uint64_t data_offset, data_size, data_unpacksize;
@@ -1033,7 +1016,7 @@
 static int
 make_substreamsInfo(struct archive_write *a, struct coder *coders)
 {
-	struct _7zip *zip = (struct _7zip *)a->format_data;
+	struct _7zip *zip = a->format_data;
 	struct file *file;
 	int r;
 
@@ -1109,7 +1092,7 @@
     uint64_t unpack_size, int num_coder, struct coder *coders, int substrm,
     uint32_t header_crc)
 {
-	struct _7zip *zip = (struct _7zip *)a->format_data;
+	struct _7zip *zip = a->format_data;
 	uint8_t codec_buff[8];
 	int numFolders, fi;
 	int codec_size;
@@ -1301,8 +1284,8 @@
 static int
 make_time(struct archive_write *a, uint8_t type, unsigned flg, int ti)
 {
+	struct _7zip *zip = a->format_data;
 	uint8_t filetime[8];
-	struct _7zip *zip = (struct _7zip *)a->format_data;
 	struct file *file;
 	int r;
 	uint8_t b, mask;
@@ -1390,7 +1373,7 @@
 make_header(struct archive_write *a, uint64_t offset, uint64_t pack_size,
     uint64_t unpack_size, int codernum, struct coder *coders)
 {
-	struct _7zip *zip = (struct _7zip *)a->format_data;
+	struct _7zip *zip = a->format_data;
 	struct file *file;
 	int r;
 	uint8_t b, mask;
@@ -1598,7 +1581,7 @@
 static int
 _7z_free(struct archive_write *a)
 {
-	struct _7zip *zip = (struct _7zip *)a->format_data;
+	struct _7zip *zip = a->format_data;
 
 	/* Close the temporary file. */
 	if (zip->temp_fd >= 0)
@@ -1636,13 +1619,12 @@
 file_new(struct archive_write *a, struct archive_entry *entry,
     struct file **newfile)
 {
-	struct _7zip *zip;
+	struct _7zip *zip = a->format_data;
 	struct file *file;
 	const char *u16;
 	size_t u16len;
 	int ret = ARCHIVE_OK;
 
-	zip = (struct _7zip *)a->format_data;
 	*newfile = NULL;
 
 	file = calloc(1, sizeof(*file));
@@ -2260,7 +2242,7 @@
 ppmd_write(void *p, Byte b)
 {
 	struct archive_write *a = ((IByteOut *)p)->a;
-	struct _7zip *zip = (struct _7zip *)(a->format_data);
+	struct _7zip *zip = a->format_data;
 	struct la_zstream *lastrm = &(zip->stream);
 	struct ppmd_stream *strm;
 
@@ -2517,10 +2499,9 @@
 _7z_compression_init_encoder(struct archive_write *a, unsigned compression,
     int compression_level)
 {
-	struct _7zip *zip;
+	struct _7zip *zip = a->format_data;
 	int r;
 
-	zip = (struct _7zip *)a->format_data;
 	switch (compression) {
 	case _7Z_DEFLATE:
 		r = compression_init_encoder_deflate(
diff --git a/c/archive_write_set_format_ar.c b/c/archive_write_set_format_ar.c
--- a/c/archive_write_set_format_ar.c
+++ b/c/archive_write_set_format_ar.c
@@ -40,10 +40,11 @@
 #include "archive.h"
 #include "archive_entry.h"
 #include "archive_private.h"
+#include "archive_string.h"
 #include "archive_write_private.h"
 #include "archive_write_set_format_private.h"
 
-struct ar_w {
+struct ar {
 	uint64_t	 entry_bytes_remaining;
 	uint64_t	 entry_padding;
 	int		 is_strtab;
@@ -78,7 +79,6 @@
 static int		 archive_write_ar_free(struct archive_write *);
 static int		 archive_write_ar_close(struct archive_write *);
 static int		 archive_write_ar_finish_entry(struct archive_write *);
-static const char	*ar_basename(const char *path);
 static int		 format_octal(int64_t v, char *p, int s);
 static int		 format_decimal(int64_t v, char *p, int s);
 
@@ -120,11 +120,10 @@
 static int
 archive_write_set_format_ar(struct archive_write *a)
 {
-	struct ar_w *ar;
+	struct ar *ar;
 
 	/* If someone else was already registered, unregister them. */
-	if (a->format_free != NULL)
-		(a->format_free)(a);
+	(void)__archive_write_unregister_format(a);
 
 	ar = calloc(1, sizeof(*ar));
 	if (ar == NULL) {
@@ -147,16 +146,18 @@
 {
 	int ret, append_fn;
 	char buff[60];
-	char *ss, *se;
-	struct ar_w *ar;
+	char *ss;
+	struct ar *ar = a->format_data;
+	struct archive_string se;
 	const char *pathname;
 	const char *filename;
+	size_t filename_length;
 	int64_t size;
 
 	append_fn = 0;
-	ar = (struct ar_w *)a->format_data;
 	ar->is_strtab = 0;
 	filename = NULL;
+	filename_length = 0;
 	size = archive_entry_size(entry);
 
 
@@ -215,12 +216,17 @@
 	 * Otherwise, entry is a normal archive member.
 	 * Strip leading paths from filenames, if any.
 	 */
-	if ((filename = ar_basename(pathname)) == NULL) {
+	filename = strrchr(pathname, '/');
+	if (filename == NULL)
+		filename = pathname;
+	else if (filename[1] == '\0') {
 		/* Reject filenames with trailing "/" */
 		archive_set_error(&a->archive, EINVAL,
 		    "Invalid filename");
 		return (ARCHIVE_WARN);
-	}
+	} else
+		filename++;
+	filename_length = strlen(filename);
 
 	if (a->archive.archive_format == ARCHIVE_FORMAT_AR_GNU) {
 		/*
@@ -229,10 +235,10 @@
 		 * So, the longest filename here (without extension) is
 		 * actually 15 bytes.
 		 */
-		if (strlen(filename) <= 15) {
+		if (filename_length <= 15) {
 			memcpy(&buff[AR_name_offset],
-			    filename, strlen(filename));
-			buff[AR_name_offset + strlen(filename)] = '/';
+			    filename, filename_length);
+			buff[AR_name_offset + filename_length] = '/';
 		} else {
 			/*
 			 * For filename longer than 15 bytes, GNU variant
@@ -246,18 +252,10 @@
 				return (ARCHIVE_WARN);
 			}
 
-			se = malloc(strlen(filename) + 3);
-			if (se == NULL) {
-				archive_set_error(&a->archive, ENOMEM,
-				    "Can't allocate filename buffer");
-				return (ARCHIVE_FATAL);
-			}
-
-			memcpy(se, filename, strlen(filename));
-			strcpy(se + strlen(filename), "/\n");
-
-			ss = strstr(ar->strtab, se);
-			free(se);
+			archive_string_init(&se);
+			archive_string_sprintf(&se, "%s/\n", filename);
+			ss = strstr(ar->strtab, se.s);
+			archive_string_free(&se);
 
 			if (ss == NULL) {
 				archive_set_error(&a->archive, EINVAL,
@@ -289,13 +287,13 @@
 		 * The name is then written immediately following the
 		 * archive header.
 		 */
-		if (strlen(filename) <= 16 && strchr(filename, ' ') == NULL) {
-			memcpy(&buff[AR_name_offset], filename, strlen(filename));
-			buff[AR_name_offset + strlen(filename)] = ' ';
+		if (filename_length <= 16 && strchr(filename, ' ') == NULL) {
+			memcpy(&buff[AR_name_offset], filename, filename_length);
+			buff[AR_name_offset + filename_length] = ' ';
 		}
 		else {
 			memcpy(buff + AR_name_offset, "#1/", 3);
-			if (format_decimal(strlen(filename),
+			if (format_decimal(filename_length,
 			    buff + AR_name_offset + 3,
 			    AR_name_size - 3)) {
 				archive_set_error(&a->archive, ERANGE,
@@ -303,7 +301,7 @@
 				return (ARCHIVE_WARN);
 			}
 			append_fn = 1;
-			size += strlen(filename);
+			size += filename_length;
 		}
 	}
 
@@ -353,10 +351,10 @@
 	ar->entry_padding = ar->entry_bytes_remaining % 2;
 
 	if (append_fn > 0) {
-		ret = __archive_write_output(a, filename, strlen(filename));
+		ret = __archive_write_output(a, filename, filename_length);
 		if (ret != ARCHIVE_OK)
 			return (ret);
-		ar->entry_bytes_remaining -= strlen(filename);
+		ar->entry_bytes_remaining -= filename_length;
 	}
 
 	return (ARCHIVE_OK);
@@ -365,10 +363,9 @@
 static ssize_t
 archive_write_ar_data(struct archive_write *a, const void *buff, size_t s)
 {
-	struct ar_w *ar;
+	struct ar *ar = a->format_data;
 	int ret;
 
-	ar = (struct ar_w *)a->format_data;
 	if (s > ar->entry_bytes_remaining)
 		s = (size_t)ar->entry_bytes_remaining;
 
@@ -401,9 +398,7 @@
 static int
 archive_write_ar_free(struct archive_write *a)
 {
-	struct ar_w *ar;
-
-	ar = (struct ar_w *)a->format_data;
+	struct ar *ar = a->format_data;
 
 	if (ar == NULL)
 		return (ARCHIVE_OK);
@@ -421,14 +416,13 @@
 static int
 archive_write_ar_close(struct archive_write *a)
 {
-	struct ar_w *ar;
+	struct ar *ar = a->format_data;
 	int ret;
 
 	/*
 	 * If we haven't written anything yet, we need to write
 	 * the ar global header now to make it a valid ar archive.
 	 */
-	ar = (struct ar_w *)a->format_data;
 	if (!ar->wrote_global_header) {
 		ar->wrote_global_header = 1;
 		ret = __archive_write_output(a, "!<arch>\n", 8);
@@ -441,11 +435,9 @@
 static int
 archive_write_ar_finish_entry(struct archive_write *a)
 {
-	struct ar_w *ar;
+	struct ar *ar = a->format_data;
 	int ret;
 
-	ar = (struct ar_w *)a->format_data;
-
 	if (ar->entry_bytes_remaining != 0) {
 		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 		    "Entry remaining bytes larger than 0");
@@ -545,25 +537,4 @@
 		*p++ = '9';
 
 	return (-1);
-}
-
-static const char *
-ar_basename(const char *path)
-{
-	const char *endp, *startp;
-
-	endp = path + strlen(path) - 1;
-	/*
-	 * For filename with trailing slash(es), we return
-	 * NULL indicating an error.
-	 */
-	if (*endp == '/')
-		return (NULL);
-
-	/* Find the start of the base */
-	startp = endp;
-	while (startp > path && *(startp - 1) != '/')
-		startp--;
-	
-	return (startp);
 }
diff --git a/c/archive_write_set_format_cpio_binary.c b/c/archive_write_set_format_cpio_binary.c
--- a/c/archive_write_set_format_cpio_binary.c
+++ b/c/archive_write_set_format_cpio_binary.c
@@ -40,6 +40,7 @@
 #include "archive.h"
 #include "archive_entry.h"
 #include "archive_entry_locale.h"
+#include "archive_integer.h"
 #include "archive_private.h"
 #include "archive_write_private.h"
 #include "archive_write_set_format_private.h"
@@ -182,8 +183,7 @@
 	    ARCHIVE_STATE_NEW, "archive_write_set_format_cpio_binary");
 
 	/* If someone else was already registered, unregister them. */
-	if (a->format_free != NULL)
-		(a->format_free)(a);
+	(void)__archive_write_unregister_format(a);
 
 	cpio = calloc(1, sizeof(*cpio));
 	if (cpio == NULL) {
@@ -235,7 +235,7 @@
 archive_write_binary_options(struct archive_write *a, const char *key,
     const char *val)
 {
-	struct cpio *cpio = (struct cpio *)a->format_data;
+	struct cpio *cpio = a->format_data;
 	int ret = ARCHIVE_FAILED;
 
 	if (strcmp(key, "hdrcharset")  == 0) {
@@ -307,10 +307,16 @@
 
 	/* Ensure space for the new mapping. */
 	if (cpio->ino_list_size <= cpio->ino_list_next) {
-		size_t newsize = cpio->ino_list_size < 512
-		    ? 512 : cpio->ino_list_size * 2;
-		void *newlist = realloc(cpio->ino_list,
-		    sizeof(cpio->ino_list[0]) * newsize);
+		size_t newsize, size;
+		if (cpio->ino_list_size < 512)
+			newsize = 512;
+		else if (archive_ckd_mul_size(&newsize,
+		    cpio->ino_list_size, 2))
+			return (-1);
+		if (archive_ckd_mul_size(&size,
+		    newsize, sizeof(cpio->ino_list[0])))
+			return (-1);
+		void *newlist = realloc(cpio->ino_list, size);
 		if (newlist == NULL)
 			return (-1);
 
@@ -329,10 +335,9 @@
 static struct archive_string_conv *
 get_sconv(struct archive_write *a)
 {
-	struct cpio *cpio;
+	struct cpio *cpio = a->format_data;
 	struct archive_string_conv *sconv;
 
-	cpio = (struct cpio *)a->format_data;
 	sconv = cpio->opt_sconv;
 	if (sconv == NULL) {
 		if (!cpio->init_default_conversion) {
@@ -378,7 +383,7 @@
 static int
 write_header(struct archive_write *a, struct archive_entry *entry)
 {
-	struct cpio *cpio;
+	struct cpio *cpio = a->format_data;
 	const char *p, *path;
 	int ret, ret_final;
 	int64_t	ino;
@@ -387,7 +392,6 @@
 	struct archive_entry *entry_main;
 	size_t len, pathlength;
 
-	cpio = (struct cpio *)a->format_data;
 	ret_final = ARCHIVE_OK;
 	sconv = get_sconv(a);
 
@@ -561,10 +565,9 @@
 static ssize_t
 archive_write_binary_data(struct archive_write *a, const void *buff, size_t s)
 {
-	struct cpio *cpio;
+	struct cpio *cpio = a->format_data;
 	int ret;
 
-	cpio = (struct cpio *)a->format_data;
 	if (s > cpio->entry_bytes_remaining)
 		s = (size_t)cpio->entry_bytes_remaining;
 
@@ -598,9 +601,8 @@
 static int
 archive_write_binary_free(struct archive_write *a)
 {
-	struct cpio *cpio;
+	struct cpio *cpio = a->format_data;
 
-	cpio = (struct cpio *)a->format_data;
 	free(cpio->ino_list);
 	free(cpio);
 	a->format_data = NULL;
@@ -610,9 +612,7 @@
 static int
 archive_write_binary_finish_entry(struct archive_write *a)
 {
-	struct cpio *cpio;
+	struct cpio *cpio = a->format_data;
 
-	cpio = (struct cpio *)a->format_data;
-	return (__archive_write_nulls(a,
-		(size_t)cpio->entry_bytes_remaining));
+	return (__archive_write_nulls(a, cpio->entry_bytes_remaining));
 }
diff --git a/c/archive_write_set_format_cpio_newc.c b/c/archive_write_set_format_cpio_newc.c
--- a/c/archive_write_set_format_cpio_newc.c
+++ b/c/archive_write_set_format_cpio_newc.c
@@ -113,8 +113,7 @@
 	    ARCHIVE_STATE_NEW, "archive_write_set_format_cpio_newc");
 
 	/* If someone else was already registered, unregister them. */
-	if (a->format_free != NULL)
-		(a->format_free)(a);
+	(void)__archive_write_unregister_format(a);
 
 	cpio = calloc(1, sizeof(*cpio));
 	if (cpio == NULL) {
@@ -138,7 +137,7 @@
 archive_write_newc_options(struct archive_write *a, const char *key,
     const char *val)
 {
-	struct cpio *cpio = (struct cpio *)a->format_data;
+	struct cpio *cpio = a->format_data;
 	int ret = ARCHIVE_FAILED;
 
 	if (strcmp(key, "hdrcharset")  == 0) {
@@ -166,10 +165,9 @@
 static struct archive_string_conv *
 get_sconv(struct archive_write *a)
 {
-	struct cpio *cpio;
+	struct cpio *cpio = a->format_data;
 	struct archive_string_conv *sconv;
 
-	cpio = (struct cpio *)a->format_data;
 	sconv = cpio->opt_sconv;
 	if (sconv == NULL) {
 		if (!cpio->init_default_conversion) {
@@ -216,8 +214,8 @@
 static int
 write_header(struct archive_write *a, struct archive_entry *entry)
 {
+	struct cpio *cpio = a->format_data;
 	int64_t ino;
-	struct cpio *cpio;
 	const char *p, *path;
 	int ret, ret_final;
 	char h[c_header_size];
@@ -226,7 +224,6 @@
 	size_t len, pathlength;
 	int pad;
 
-	cpio = (struct cpio *)a->format_data;
 	ret_final = ARCHIVE_OK;
 	sconv = get_sconv(a);
 
@@ -378,10 +375,9 @@
 static ssize_t
 archive_write_newc_data(struct archive_write *a, const void *buff, size_t s)
 {
-	struct cpio *cpio;
+	struct cpio *cpio = a->format_data;
 	int ret;
 
-	cpio = (struct cpio *)a->format_data;
 	if (s > cpio->entry_bytes_remaining)
 		s = (size_t)cpio->entry_bytes_remaining;
 
@@ -430,6 +426,8 @@
 	struct archive_entry *trailer;
 
 	trailer = archive_entry_new();
+	if (trailer == NULL)
+		return ARCHIVE_FATAL;
 	archive_entry_set_nlink(trailer, 1);
 	archive_entry_set_size(trailer, 0);
 	archive_entry_set_pathname(trailer, "TRAILER!!!");
@@ -442,9 +440,8 @@
 static int
 archive_write_newc_free(struct archive_write *a)
 {
-	struct cpio *cpio;
+	struct cpio *cpio = a->format_data;
 
-	cpio = (struct cpio *)a->format_data;
 	free(cpio);
 	a->format_data = NULL;
 	return (ARCHIVE_OK);
@@ -453,9 +450,8 @@
 static int
 archive_write_newc_finish_entry(struct archive_write *a)
 {
-	struct cpio *cpio;
+	struct cpio *cpio = a->format_data;
 
-	cpio = (struct cpio *)a->format_data;
 	return (__archive_write_nulls(a,
-		(size_t)cpio->entry_bytes_remaining + cpio->padding));
+	    cpio->entry_bytes_remaining + cpio->padding));
 }
diff --git a/c/archive_write_set_format_cpio_odc.c b/c/archive_write_set_format_cpio_odc.c
--- a/c/archive_write_set_format_cpio_odc.c
+++ b/c/archive_write_set_format_cpio_odc.c
@@ -40,6 +40,7 @@
 #include "archive.h"
 #include "archive_entry.h"
 #include "archive_entry_locale.h"
+#include "archive_integer.h"
 #include "archive_private.h"
 #include "archive_write_private.h"
 #include "archive_write_set_format_private.h"
@@ -107,8 +108,7 @@
 	    ARCHIVE_STATE_NEW, "archive_write_set_format_cpio_odc");
 
 	/* If someone else was already registered, unregister them. */
-	if (a->format_free != NULL)
-		(a->format_free)(a);
+	(void)__archive_write_unregister_format(a);
 
 	cpio = calloc(1, sizeof(*cpio));
 	if (cpio == NULL) {
@@ -132,7 +132,7 @@
 archive_write_odc_options(struct archive_write *a, const char *key,
     const char *val)
 {
-	struct cpio *cpio = (struct cpio *)a->format_data;
+	struct cpio *cpio = a->format_data;
 	int ret = ARCHIVE_FAILED;
 
 	if (strcmp(key, "hdrcharset")  == 0) {
@@ -204,10 +204,16 @@
 
 	/* Ensure space for the new mapping. */
 	if (cpio->ino_list_size <= cpio->ino_list_next) {
-		size_t newsize = cpio->ino_list_size < 512
-		    ? 512 : cpio->ino_list_size * 2;
-		void *newlist = realloc(cpio->ino_list,
-		    sizeof(cpio->ino_list[0]) * newsize);
+		size_t newsize, size;
+		if (cpio->ino_list_size < 512)
+			newsize = 512;
+		else if (archive_ckd_mul_size(&newsize,
+		    cpio->ino_list_size, 2))
+			return (-1);
+		if (archive_ckd_mul_size(&size,
+		    newsize, sizeof(cpio->ino_list[0])))
+			return (-1);
+		void *newlist = realloc(cpio->ino_list, size);
 		if (newlist == NULL)
 			return (-1);
 
@@ -226,10 +232,9 @@
 static struct archive_string_conv *
 get_sconv(struct archive_write *a)
 {
-	struct cpio *cpio;
+	struct cpio *cpio = a->format_data;
 	struct archive_string_conv *sconv;
 
-	cpio = (struct cpio *)a->format_data;
 	sconv = cpio->opt_sconv;
 	if (sconv == NULL) {
 		if (!cpio->init_default_conversion) {
@@ -275,7 +280,7 @@
 static int
 write_header(struct archive_write *a, struct archive_entry *entry)
 {
-	struct cpio *cpio;
+	struct cpio *cpio = a->format_data;
 	const char *p, *path;
 	int ret, ret_final;
 	int64_t	ino;
@@ -284,7 +289,6 @@
 	struct archive_entry *entry_main;
 	size_t len, pathlength;
 
-	cpio = (struct cpio *)a->format_data;
 	ret_final = ARCHIVE_OK;
 	sconv = get_sconv(a);
 
@@ -420,10 +424,9 @@
 static ssize_t
 archive_write_odc_data(struct archive_write *a, const void *buff, size_t s)
 {
-	struct cpio *cpio;
+	struct cpio *cpio = a->format_data;
 	int ret;
 
-	cpio = (struct cpio *)a->format_data;
 	if (s > cpio->entry_bytes_remaining)
 		s = (size_t)cpio->entry_bytes_remaining;
 
@@ -487,9 +490,8 @@
 static int
 archive_write_odc_free(struct archive_write *a)
 {
-	struct cpio *cpio;
+	struct cpio *cpio = a->format_data;
 
-	cpio = (struct cpio *)a->format_data;
 	free(cpio->ino_list);
 	free(cpio);
 	a->format_data = NULL;
@@ -499,9 +501,8 @@
 static int
 archive_write_odc_finish_entry(struct archive_write *a)
 {
-	struct cpio *cpio;
+	struct cpio *cpio = a->format_data;
 
-	cpio = (struct cpio *)a->format_data;
-	return (__archive_write_nulls(a,
-		(size_t)cpio->entry_bytes_remaining));
+	return (__archive_write_nulls(a, 
+	    cpio->entry_bytes_remaining));
 }
diff --git a/c/archive_write_set_format_gnutar.c b/c/archive_write_set_format_gnutar.c
--- a/c/archive_write_set_format_gnutar.c
+++ b/c/archive_write_set_format_gnutar.c
@@ -174,6 +174,11 @@
 	struct archive_write *a = (struct archive_write *)_a;
 	struct gnutar *gnutar;
 
+	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_write_set_format_gnutar");
+
+	(void)__archive_write_unregister_format(a);
+
 	gnutar = calloc(1, sizeof(*gnutar));
 	if (gnutar == NULL) {
 		archive_set_error(&a->archive, ENOMEM,
@@ -197,7 +202,7 @@
 archive_write_gnutar_options(struct archive_write *a, const char *key,
     const char *val)
 {
-	struct gnutar *gnutar = (struct gnutar *)a->format_data;
+	struct gnutar *gnutar = a->format_data;
 	int ret = ARCHIVE_FAILED;
 
 	if (strcmp(key, "hdrcharset")  == 0) {
@@ -231,9 +236,8 @@
 static int
 archive_write_gnutar_free(struct archive_write *a)
 {
-	struct gnutar *gnutar;
+	struct gnutar *gnutar = a->format_data;
 
-	gnutar = (struct gnutar *)a->format_data;
 	free(gnutar);
 	a->format_data = NULL;
 	return (ARCHIVE_OK);
@@ -242,12 +246,11 @@
 static int
 archive_write_gnutar_finish_entry(struct archive_write *a)
 {
-	struct gnutar *gnutar;
+	struct gnutar *gnutar = a->format_data;
 	int ret;
 
-	gnutar = (struct gnutar *)a->format_data;
-	ret = __archive_write_nulls(a, (size_t)
-	    (gnutar->entry_bytes_remaining + gnutar->entry_padding));
+	ret = __archive_write_nulls(a,
+	    gnutar->entry_bytes_remaining + gnutar->entry_padding);
 	gnutar->entry_bytes_remaining = gnutar->entry_padding = 0;
 	return (ret);
 }
@@ -255,10 +258,9 @@
 static ssize_t
 archive_write_gnutar_data(struct archive_write *a, const void *buff, size_t s)
 {
-	struct gnutar *gnutar;
+	struct gnutar *gnutar = a->format_data;
 	int ret;
 
-	gnutar = (struct gnutar *)a->format_data;
 	if (s > gnutar->entry_bytes_remaining)
 		s = (size_t)gnutar->entry_bytes_remaining;
 	ret = __archive_write_output(a, buff, s);
@@ -272,15 +274,13 @@
 archive_write_gnutar_header(struct archive_write *a,
      struct archive_entry *entry)
 {
+	struct gnutar *gnutar = a->format_data;
 	char buff[512];
 	int r, ret, ret2 = ARCHIVE_OK;
 	char tartype;
-	struct gnutar *gnutar;
 	struct archive_string_conv *sconv;
 	struct archive_entry *entry_main;
 
-	gnutar = (struct gnutar *)a->format_data;
-
 	/* Setup default string conversion. */
 	if (gnutar->opt_sconv == NULL) {
 		if (!gnutar->init_default_conversion) {
@@ -494,6 +494,13 @@
 		size_t length = gnutar->linkname_length + 1;
 		struct archive_entry *temp = archive_entry_new2(&a->archive);
 
+		if (temp == NULL) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory for Linkname");
+			ret = ARCHIVE_FATAL;
+			goto exit_write_header;
+		}
+
 		/* Uname/gname here don't really matter since no one reads them;
 		 * these are the values that GNU tar happens to use on FreeBSD. */
 		archive_entry_set_uname(temp, "root");
@@ -524,6 +531,13 @@
 		size_t length = gnutar->pathname_length + 1;
 		struct archive_entry *temp = archive_entry_new2(&a->archive);
 
+		if (temp == NULL) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory for Linkname");
+			ret = ARCHIVE_FATAL;
+			goto exit_write_header;
+		}
+
 		/* Uname/gname here don't really matter since no one reads them;
 		 * these are the values that GNU tar happens to use on FreeBSD. */
 		archive_entry_set_uname(temp, "root");
@@ -589,14 +603,12 @@
 archive_format_gnutar_header(struct archive_write *a, char h[512],
     struct archive_entry *entry, char tartype)
 {
+	struct gnutar *gnutar = a->format_data;
 	unsigned int checksum;
 	int i, ret;
 	size_t copy_length;
 	const char *p;
-	struct gnutar *gnutar;
 
-	gnutar = (struct gnutar *)a->format_data;
-
 	ret = 0;
 
 	/*
@@ -652,7 +664,7 @@
 		copy_length = gnutar->gname_length;
 	}
 	if (copy_length > 0) {
-		if (strlen(p) > GNUTAR_gname_size)
+		if (copy_length > GNUTAR_gname_size)
 			copy_length = GNUTAR_gname_size;
 		memcpy(h + GNUTAR_gname_offset, p, copy_length);
 	}
diff --git a/c/archive_write_set_format_iso9660.c b/c/archive_write_set_format_iso9660.c
--- a/c/archive_write_set_format_iso9660.c
+++ b/c/archive_write_set_format_iso9660.c
@@ -1059,8 +1059,7 @@
 	    ARCHIVE_STATE_NEW, "archive_write_set_format_iso9660");
 
 	/* If another format was already registered, unregister it. */
-	if (a->format_free != NULL)
-		(a->format_free)(a);
+	(void)__archive_write_unregister_format(a);
 
 	iso9660 = calloc(1, sizeof(*iso9660));
 	if (iso9660 == NULL) {
@@ -1427,8 +1426,8 @@
 		break;
 	case 'i':
 		if (strcmp(key, "iso-level") == 0) {
-			if (value != NULL && value[1] == '\0' &&
-			    (value[0] >= '1' && value[0] <= '4')) {
+			if (value != NULL && value[0] >= '1' &&
+			    value[0] <= '4' && value[1] == '\0') {
 				iso9660->opt.iso_level = value[0]-'0';
 				return (ARCHIVE_OK);
 			}
@@ -1529,13 +1528,11 @@
 static int
 iso9660_write_header(struct archive_write *a, struct archive_entry *entry)
 {
-	struct iso9660 *iso9660;
+	struct iso9660 *iso9660 = a->format_data;
 	struct isofile *file;
 	struct isoent *isoent;
 	int r, ret = ARCHIVE_OK;
 
-	iso9660 = a->format_data;
-
 	iso9660->cur_file = NULL;
 	iso9660->bytes_remaining = 0;
 	iso9660->need_multi_extent = 0;
@@ -1677,7 +1674,7 @@
 	 * order to reduce a extra memory copy.
 	 */
 	if (wb_remaining(a) == wb_buffmax() && s > (1024 * 16)) {
-		struct iso9660 *iso9660 = (struct iso9660 *)a->format_data;
+		struct iso9660 *iso9660 = a->format_data;
 		xs = s % LOGICAL_BLOCK_SIZE;
 		iso9660->wbuff_offset += s - xs;
 		if (write_to_temp(a, buff, s - xs) != ARCHIVE_OK)
@@ -1859,11 +1856,9 @@
 static int
 iso9660_close(struct archive_write *a)
 {
-	struct iso9660 *iso9660;
+	struct iso9660 *iso9660 = a->format_data;
 	int ret, blocks;
 
-	iso9660 = a->format_data;
-
 	/*
 	 * Write remaining data out to the temporary file.
 	 */
@@ -2121,11 +2116,9 @@
 static int
 iso9660_free(struct archive_write *a)
 {
-	struct iso9660 *iso9660;
+	struct iso9660 *iso9660 = a->format_data;
 	int i, ret;
 
-	iso9660 = a->format_data;
-
 	/* Close the temporary file. */
 	if (iso9660->temp_fd >= 0)
 		close(iso9660->temp_fd);
@@ -2921,10 +2914,10 @@
 			extra_tell_used_size(&ctl, length);
 			if (extra_space(&ctl) < 6) {
 				bp = extra_next_record(&ctl, 6);
-				nmmax = extra_space(&ctl);
-				if (nmmax > 0xff)
-					nmmax = 0xff;
 			}
+			nmmax = extra_space(&ctl);
+			if (nmmax > 0xff)
+				nmmax = 0xff;
 			if (bp != NULL) {
 				bp[1] = 'N';
 				bp[2] = 'M';
@@ -3619,7 +3612,7 @@
 static inline unsigned char *
 wb_buffptr(struct archive_write *a)
 {
-	struct iso9660 *iso9660 = (struct iso9660 *)a->format_data;
+	struct iso9660 *iso9660 = a->format_data;
 
 	return (&(iso9660->wbuff[sizeof(iso9660->wbuff)
 		- iso9660->wbuff_remaining]));
@@ -3628,7 +3621,7 @@
 static int
 wb_write_out(struct archive_write *a)
 {
-	struct iso9660 *iso9660 = (struct iso9660 *)a->format_data;
+	struct iso9660 *iso9660 = a->format_data;
 	size_t wsize, nw;
 	int r;
 
@@ -3653,7 +3646,7 @@
 static int
 wb_consume(struct archive_write *a, size_t size)
 {
-	struct iso9660 *iso9660 = (struct iso9660 *)a->format_data;
+	struct iso9660 *iso9660 = a->format_data;
 
 	if (size > iso9660->wbuff_remaining ||
 	    iso9660->wbuff_remaining == 0) {
@@ -3674,7 +3667,7 @@
 static int
 wb_set_offset(struct archive_write *a, int64_t off)
 {
-	struct iso9660 *iso9660 = (struct iso9660 *)a->format_data;
+	struct iso9660 *iso9660 = a->format_data;
 	int64_t used, ext_bytes;
 
 	if (iso9660->wbuff_type != WB_TO_TEMP) {
@@ -3841,7 +3834,7 @@
 static int
 write_VD(struct archive_write *a, struct vdd *vdd)
 {
-	struct iso9660 *iso9660;
+	struct iso9660 *iso9660 = a->format_data;
 	unsigned char *bp;
 	uint16_t volume_set_size = 1;
 	char identifier[256];
@@ -3850,7 +3843,6 @@
 	unsigned char vd_ver, fst_ver;
 	int r;
 
-	iso9660 = a->format_data;
 	switch (vdd->vdd_type) {
 	case VDD_JOLIET:
 		vdt = VDT_SUPPLEMENTARY;
@@ -3988,10 +3980,9 @@
 static int
 write_VD_boot_record(struct archive_write *a)
 {
-	struct iso9660 *iso9660;
+	struct iso9660 *iso9660 = a->format_data;
 	unsigned char *bp;
 
-	iso9660 = a->format_data;
 	bp = wb_buffptr(a) -1;
 	/* Volume Descriptor Type */
 	set_VD_bp(bp, VDT_BOOT_RECORD, 1);
@@ -4059,7 +4050,7 @@
 static int
 write_information_block(struct archive_write *a)
 {
-	struct iso9660 *iso9660;
+	struct iso9660 *iso9660 = a->format_data;
 	char buf[128];
 	const char *v;
 	int opt, r;
@@ -4067,7 +4058,6 @@
 	size_t info_size = LOGICAL_BLOCK_SIZE *
 			       NON_ISO_FILE_SYSTEM_INFORMATION_BLOCK;
 
-	iso9660 = (struct iso9660 *)a->format_data;
 	if (info_size > wb_remaining(a)) {
 		r = wb_write_out(a);
 		if (r != ARCHIVE_OK)
@@ -4717,14 +4707,12 @@
 static int
 isofile_gen_utility_names(struct archive_write *a, struct isofile *file)
 {
-	struct iso9660 *iso9660;
+	struct iso9660 *iso9660 = a->format_data;
 	const char *pathname;
 	char *p, *dirname, *slash;
 	size_t len;
 	int ret = ARCHIVE_OK;
 
-	iso9660 = a->format_data;
-
 	archive_string_empty(&(file->parentdir));
 	archive_string_empty(&(file->basename));
 	archive_string_empty(&(file->basename_utf16));
@@ -5563,6 +5551,7 @@
 static int
 isoent_tree(struct archive_write *a, struct isoent **isoentpp)
 {
+	struct iso9660 *iso9660 = a->format_data;
 #if defined(_WIN32) && !defined(__CYGWIN__)
 	char name[_MAX_FNAME];/* Included null terminator size. */
 #elif defined(NAME_MAX) && NAME_MAX >= 255
@@ -5570,7 +5559,6 @@
 #else
 	char name[256];
 #endif
-	struct iso9660 *iso9660 = a->format_data;
 	struct isoent *dent, *isoent, *np;
 	struct isofile *f1, *f2;
 	const char *fn, *p;
@@ -5998,7 +5986,7 @@
 isoent_gen_iso9660_identifier(struct archive_write *a, struct isoent *isoent,
     struct idr *idr)
 {
-	struct iso9660 *iso9660;
+	struct iso9660 *iso9660 = a->format_data;
 	struct isoent *np;
 	char *p;
 	int l, r;
@@ -6014,9 +6002,8 @@
 	const int null_size = 1;
 
 	if (isoent->children.cnt == 0)
-		return (0);
+		return (ARCHIVE_OK);
 
-	iso9660 = a->format_data;
 	char_map = idr->char_map;
 	if (iso9660->opt.iso_level <= 3) {
 		allow_ldots = 0;
@@ -6258,7 +6245,7 @@
 isoent_gen_joliet_identifier(struct archive_write *a, struct isoent *isoent,
     struct idr *idr)
 {
-	struct iso9660 *iso9660;
+	struct iso9660 *iso9660 = a->format_data;
 	struct isoent *np;
 	unsigned char *p;
 	size_t l;
@@ -6271,9 +6258,8 @@
 	const int null_size = 2;
 
 	if (isoent->children.cnt == 0)
-		return (0);
+		return (ARCHIVE_OK);
 
-	iso9660 = a->format_data;
 	if (iso9660->opt.joliet == OPT_JOLIET_LONGNAME)
 		ffmax = 206;
 	else
@@ -7522,8 +7508,9 @@
 		(uint32_t)archive_entry_size(file->entry);
 
 	/* Calculate a size of Block Pointers of zisofs. */
-	_ceil = (file->zisofs.uncompressed_size + ZF_BLOCK_SIZE -1)
-		>> file->zisofs.log2_bs;
+	_ceil = (size_t)(((uint64_t)file->zisofs.uncompressed_size +
+		    (ZF_BLOCK_SIZE - 1))
+		>> file->zisofs.log2_bs);
 	iso9660->zisofs.block_pointers_cnt = (int)_ceil + 1;
 	iso9660->zisofs.block_pointers_idx = 0;
 
diff --git a/c/archive_write_set_format_mtree.c b/c/archive_write_set_format_mtree.c
--- a/c/archive_write_set_format_mtree.c
+++ b/c/archive_write_set_format_mtree.c
@@ -117,7 +117,7 @@
 	int64_t ino;
 };
 
-struct mtree_writer {
+struct mtree {
 	struct mtree_entry *mtree_entry;
 	struct mtree_entry *root;
 	struct mtree_entry *cur_dirent;
@@ -220,10 +220,10 @@
 	struct attr_counter *, struct mtree_entry *);
 static struct attr_counter * attr_counter_new(struct mtree_entry *,
 	struct attr_counter *);
-static int attr_counter_set_collect(struct mtree_writer *,
+static int attr_counter_set_collect(struct mtree *,
 	struct mtree_entry *);
-static void attr_counter_set_free(struct mtree_writer *);
-static int get_global_set_keys(struct mtree_writer *, struct mtree_entry *);
+static void attr_counter_set_free(struct mtree *);
+static int get_global_set_keys(struct mtree *, struct mtree_entry *);
 static int mtree_entry_add_child_tail(struct mtree_entry *,
 	struct mtree_entry *);
 static int mtree_entry_create_virtual_dir(struct archive_write *, const char *,
@@ -236,14 +236,14 @@
 static void mtree_entry_free(struct mtree_entry *);
 static int mtree_entry_new(struct archive_write *, struct archive_entry *,
 	struct mtree_entry **);
-static void mtree_entry_register_free(struct mtree_writer *);
-static void mtree_entry_register_init(struct mtree_writer *);
+static void mtree_entry_register_free(struct mtree *);
+static void mtree_entry_register_init(struct mtree *);
 static int mtree_entry_setup_filenames(struct archive_write *,
 	struct mtree_entry *, struct archive_entry *);
 static int mtree_entry_tree_add(struct archive_write *, struct mtree_entry **);
-static void sum_init(struct mtree_writer *);
-static void sum_update(struct mtree_writer *, const void *, size_t);
-static void sum_final(struct mtree_writer *, struct reg_info *);
+static void sum_init(struct mtree *);
+static void sum_update(struct mtree *, const void *, size_t);
+static void sum_final(struct mtree *, struct reg_info *);
 static void sum_write(struct archive_string *, struct reg_info *);
 static int write_mtree_entry(struct archive_write *, struct mtree_entry *);
 static int write_dot_dot_entry(struct archive_write *, struct mtree_entry *);
@@ -358,7 +358,7 @@
  * Indent a line as the mtree utility does so it is readable for people.
  */
 static void
-mtree_indent(struct mtree_writer *mtree)
+mtree_indent(struct mtree *mtree)
 {
 	int i, fn, nd, pd;
 	const char *r, *s, *x;
@@ -434,7 +434,7 @@
  * collected by the attr_counter_set_collect() function.
  */
 static void
-write_global(struct mtree_writer *mtree)
+write_global(struct mtree *mtree)
 {
 	struct archive_string setstr;
 	struct archive_string unsetstr;
@@ -636,7 +636,7 @@
  * Tabulate uid, gid, mode and fflags of a entry in order to be used for /set.
  */
 static int
-attr_counter_set_collect(struct mtree_writer *mtree, struct mtree_entry *me)
+attr_counter_set_collect(struct mtree *mtree, struct mtree_entry *me)
 {
 	struct attr_counter *ac, *last;
 	struct attr_counter_set *acs = &mtree->acs;
@@ -713,7 +713,7 @@
 }
 
 static void
-attr_counter_set_free(struct mtree_writer *mtree)
+attr_counter_set_free(struct mtree *mtree)
 {
 	struct attr_counter_set *acs = &mtree->acs;
 
@@ -724,7 +724,7 @@
 }
 
 static int
-get_global_set_keys(struct mtree_writer *mtree, struct mtree_entry *me)
+get_global_set_keys(struct mtree *mtree, struct mtree_entry *me)
 {
 	int keys;
 
@@ -913,7 +913,7 @@
 archive_write_mtree_header(struct archive_write *a,
     struct archive_entry *entry)
 {
-	struct mtree_writer *mtree= a->format_data;
+	struct mtree *mtree = a->format_data;
 	struct mtree_entry *mtree_entry;
 	int r, r2;
 
@@ -956,7 +956,7 @@
 static int
 write_mtree_entry(struct archive_write *a, struct mtree_entry *me)
 {
-	struct mtree_writer *mtree = a->format_data;
+	struct mtree *mtree = a->format_data;
 	struct archive_string *str;
 	int keys, ret;
 
@@ -1107,7 +1107,7 @@
 static int
 write_dot_dot_entry(struct archive_write *a, struct mtree_entry *n)
 {
-	struct mtree_writer *mtree = a->format_data;
+	struct mtree *mtree = a->format_data;
 	int ret;
 
 	if (n->parentdir.s) {
@@ -1142,7 +1142,7 @@
 static int
 write_mtree_entry_tree(struct archive_write *a)
 {
-	struct mtree_writer *mtree = a->format_data;
+	struct mtree *mtree = a->format_data;
 	struct mtree_entry *np = mtree->root;
 	struct archive_rb_node *n;
 	int ret;
@@ -1245,7 +1245,7 @@
 static int
 archive_write_mtree_finish_entry(struct archive_write *a)
 {
-	struct mtree_writer *mtree = a->format_data;
+	struct mtree *mtree = a->format_data;
 	struct mtree_entry *me;
 
 	if ((me = mtree->mtree_entry) == NULL)
@@ -1261,7 +1261,7 @@
 static int
 archive_write_mtree_close(struct archive_write *a)
 {
-	struct mtree_writer *mtree= a->format_data;
+	struct mtree *mtree = a->format_data;
 	int ret;
 
 	if (mtree->root != NULL) {
@@ -1278,7 +1278,7 @@
 static ssize_t
 archive_write_mtree_data(struct archive_write *a, const void *buff, size_t n)
 {
-	struct mtree_writer *mtree= a->format_data;
+	struct mtree *mtree = a->format_data;
 
 	if (n > mtree->entry_bytes_remaining)
 		n = (size_t)mtree->entry_bytes_remaining;
@@ -1297,7 +1297,7 @@
 static int
 archive_write_mtree_free(struct archive_write *a)
 {
-	struct mtree_writer *mtree= a->format_data;
+	struct mtree *mtree = a->format_data;
 
 	if (mtree == NULL)
 		return (ARCHIVE_OK);
@@ -1317,7 +1317,7 @@
 archive_write_mtree_options(struct archive_write *a, const char *key,
     const char *value)
 {
-	struct mtree_writer *mtree= a->format_data;
+	struct mtree *mtree = a->format_data;
 	int keybit = 0;
 
 	switch (key[0]) {
@@ -1429,12 +1429,11 @@
 archive_write_set_format_mtree_default(struct archive *_a, const char *fn)
 {
 	struct archive_write *a = (struct archive_write *)_a;
-	struct mtree_writer *mtree;
+	struct mtree *mtree;
 
 	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, fn);
 
-	if (a->format_free != NULL)
-		(a->format_free)(a);
+	(void)__archive_write_unregister_format(a);
 
 	if ((mtree = calloc(1, sizeof(*mtree))) == NULL) {
 		archive_set_error(&a->archive, ENOMEM,
@@ -1481,9 +1480,7 @@
 		"archive_write_set_format_mtree_classic");
 	if (r == ARCHIVE_OK) {
 		struct archive_write *a = (struct archive_write *)_a;
-		struct mtree_writer *mtree;
-
-		mtree = (struct mtree_writer *)a->format_data;
+		struct mtree *mtree = a->format_data;
 
 		/* Set to output a mtree archive in classic format. */
 		mtree->classic = 1;
@@ -1495,7 +1492,7 @@
 }
 
 static void
-sum_init(struct mtree_writer *mtree)
+sum_init(struct mtree *mtree)
 {
 
 	mtree->compute_sum = 0;
@@ -1556,7 +1553,7 @@
 }
 
 static void
-sum_update(struct mtree_writer *mtree, const void *buff, size_t n)
+sum_update(struct mtree *mtree, const void *buff, size_t n)
 {
 	if (mtree->compute_sum & F_CKSUM) {
 		/*
@@ -1614,7 +1611,7 @@
 }
 
 static void
-sum_final(struct mtree_writer *mtree, struct reg_info *reg)
+sum_final(struct mtree *mtree, struct reg_info *reg)
 {
 	struct ae_digest digest;
 
@@ -1995,7 +1992,7 @@
 }
 
 static void
-mtree_entry_register_add(struct mtree_writer *mtree, struct mtree_entry *file)
+mtree_entry_register_add(struct mtree *mtree, struct mtree_entry *file)
 {
         file->next = NULL;
         *mtree->file_list.last = file;
@@ -2003,14 +2000,14 @@
 }
 
 static void
-mtree_entry_register_init(struct mtree_writer *mtree)
+mtree_entry_register_init(struct mtree *mtree)
 {
 	mtree->file_list.first = NULL;
 	mtree->file_list.last = &(mtree->file_list.first);
 }
 
 static void
-mtree_entry_register_free(struct mtree_writer *mtree)
+mtree_entry_register_free(struct mtree *mtree)
 {
 	struct mtree_entry *file, *file_next;
 
@@ -2073,6 +2070,7 @@
 static int
 mtree_entry_tree_add(struct archive_write *a, struct mtree_entry **filep)
 {
+	struct mtree *mtree = a->format_data;
 #if defined(_WIN32) && !defined(__CYGWIN__)
 	char name[_MAX_FNAME];/* Included null terminator size. */
 #elif defined(NAME_MAX) && NAME_MAX >= 255
@@ -2080,7 +2078,6 @@
 #else
 	char name[256];
 #endif
-	struct mtree_writer *mtree = (struct mtree_writer *)a->format_data;
 	struct mtree_entry *dent, *file, *np;
 	const char *fn, *p;
 	int l, r;
diff --git a/c/archive_write_set_format_pax.c b/c/archive_write_set_format_pax.c
--- a/c/archive_write_set_format_pax.c
+++ b/c/archive_write_set_format_pax.c
@@ -45,6 +45,8 @@
 #include "archive_write_private.h"
 #include "archive_write_set_format_private.h"
 
+#define PAX_MAX_ALIGN (1U << 20)
+
 struct sparse_block {
 	struct sparse_block	*next;
 	int		is_hole;
@@ -64,6 +66,10 @@
 	struct archive_string_conv *sconv_utf8;
 	int			 opt_binary;
 
+	/* If non-zero, align regular file data to this many bytes in the
+	 * uncompressed stream (power of two, multiple of 512). */
+	size_t			 align;
+
 	unsigned flags;
 #define WRITE_SCHILY_XATTR       (1 << 0)
 #define WRITE_LIBARCHIVE_XATTR   (1 << 1)
@@ -79,6 +85,8 @@
 static void		 add_pax_attr_time(struct archive_string *,
 			     const char *key, int64_t sec,
 			     unsigned long nanos);
+static size_t		 add_pax_padding_prefix(struct archive_string *,
+			     size_t cur_len, size_t target_len);
 static int		 add_pax_acl(struct archive_write *,
 			    struct archive_entry *, struct pax *, int);
 static ssize_t		 archive_write_pax_data(struct archive_write *,
@@ -90,6 +98,7 @@
 			     struct archive_entry *);
 static int		 archive_write_pax_options(struct archive_write *,
 			     const char *, const char *);
+static int		 write_pax_padding(struct archive_write *, size_t);
 static char		*base64_encode(const char *src, size_t len);
 static char		*build_gnu_sparse_name(char *dest, const char *src);
 static char		*build_pax_attribute_name(char *dest, const char *src);
@@ -136,8 +145,7 @@
 	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
 	    ARCHIVE_STATE_NEW, "archive_write_set_format_pax");
 
-	if (a->format_free != NULL)
-		(a->format_free)(a);
+	(void)__archive_write_unregister_format(a);
 
 	pax = calloc(1, sizeof(*pax));
 	if (pax == NULL) {
@@ -164,7 +172,7 @@
 archive_write_pax_options(struct archive_write *a, const char *key,
     const char *val)
 {
-	struct pax *pax = (struct pax *)a->format_data;
+	struct pax *pax = a->format_data;
 	int ret = ARCHIVE_FAILED;
 
 	if (strcmp(key, "hdrcharset")  == 0) {
@@ -223,6 +231,25 @@
 			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 			    "pax: invalid xattr header name");
 		return (ret);
+	} else if (strcmp(key, "align") == 0) {
+		unsigned long v;
+		char *end;
+
+		if (val == NULL || val[0] == 0) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "pax: align option needs a value");
+			return (ARCHIVE_FAILED);
+		}
+		v = strtoul(val, &end, 10);
+		if (*end != '\0' || v == 0 || (v % 512) != 0 ||
+		    (v & (v - 1)) != 0 || v > PAX_MAX_ALIGN) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "pax: align must be a power of two, a multiple "
+			    "of 512, and no larger than 1 MiB");
+			return (ARCHIVE_FAILED);
+		}
+		pax->align = (size_t)v;
+		return (ARCHIVE_OK);
 	}
 
 	/* Note: The "warn" return is just to inform the options
@@ -361,6 +388,81 @@
 	archive_strappend_char(as, '\n');
 }
 
+/*
+ * Append the length/key prefix of an ignorable "LIBARCHIVE.pad" record whose
+ * full length rounds the pax body up to 'target_len'.  Return the number of
+ * value/newline bytes that must be emitted after the in-memory header.
+ */
+static size_t
+add_pax_padding_prefix(struct archive_string *as, size_t cur_len,
+    size_t target_len)
+{
+	static const char key[] = "LIBARCHIVE.pad";
+	char tmp[1 + 3 * sizeof(int64_t)];
+	char *length;
+	size_t prefix_len, record_len;
+
+	if (target_len <= cur_len)
+		return (0);
+	record_len = target_len - cur_len;
+	tmp[sizeof(tmp) - 1] = 0;
+	length = format_int(tmp + sizeof(tmp) - 1, (int64_t)record_len);
+	prefix_len = strlen(length) + 1 + sizeof(key) - 1 + 1;
+	if (record_len <= prefix_len)
+		return (0); /* not enough room; leave unaligned */
+
+	archive_strcat(as, length);
+	archive_strappend_char(as, ' ');
+	archive_strcat(as, key);
+	archive_strappend_char(as, '=');
+	return (record_len - prefix_len);
+}
+
+/* Emit a deferred pax padding value followed by its record newline. */
+static int
+write_pax_padding(struct archive_write *a, size_t length)
+{
+	char padding[1024];
+
+	if (length == 0)
+		return (ARCHIVE_OK);
+	memset(padding, 'X', sizeof(padding));
+	while (length > 1) {
+		size_t to_write = length - 1;
+		int r;
+
+		if (to_write > sizeof(padding))
+			to_write = sizeof(padding);
+		r = __archive_write_output(a, padding, to_write);
+		if (r != ARCHIVE_OK)
+			return (r);
+		length -= to_write;
+	}
+	return (__archive_write_output(a, "\n", 1));
+}
+
+/*
+ * For the pax "align" option: given entry offset 'off' (a multiple of 512) and
+ * current extended-header body length 'l0', return the body length needed so
+ * the file data (after the x header) lands on 'align', or 'l0' if already
+ * aligned.
+ */
+static size_t
+pax_align_body_len(uint64_t off, size_t l0, size_t align)
+{
+	uint64_t amask = (uint64_t)align - 1;
+	/* Data offset with no extra padding (no x header when l0 == 0). */
+	uint64_t natural_off = (l0 == 0) ? off + 512
+	    : off + 1024 + (((uint64_t)l0 + 511) & ~(uint64_t)511);
+
+	if ((natural_off & amask) == 0)
+		return (l0);
+
+	/* Grow the body (past l0 plus pad-record room) to land data on align. */
+	uint64_t base = off + 1024;
+	return ((size_t)(((base + l0 + 64 + amask) & ~amask) - base));
+}
+
 static void
 archive_write_pax_header_xattr(struct pax *pax, const char *encoded_name,
     const void *value, size_t value_len)
@@ -578,6 +680,7 @@
 archive_write_pax_header(struct archive_write *a,
     struct archive_entry *entry_original)
 {
+	struct pax *pax = a->format_data;
 	struct archive_entry *entry_main;
 	const char *p;
 	const char *suffix;
@@ -585,7 +688,6 @@
 	int acl_types;
 	int sparse_count;
 	uint64_t sparse_total, real_size;
-	struct pax *pax;
 	const char *hardlink;
 	const char *path = NULL, *linkpath = NULL;
 	const char *uname = NULL, *gname = NULL;
@@ -593,6 +695,7 @@
 	size_t mac_metadata_size;
 	struct archive_string_conv *sconv;
 	size_t hardlink_length, path_length, linkpath_length;
+	size_t pax_header_padding = 0;
 	size_t uname_length, gname_length;
 
 	char paxbuff[512];
@@ -604,7 +707,6 @@
 
 	ret = ARCHIVE_OK;
 	need_extension = 0;
-	pax = (struct pax *)a->format_data;
 
 	const time_t ustar_max_mtime = get_ustar_max_mtime();
 
@@ -1338,14 +1440,6 @@
 		archive_entry_set_size(entry_main, 0);
 
 	/*
-	 * Pax-restricted does not store data for hardlinks, in order
-	 * to improve compatibility with ustar.
-	 */
-	if (a->archive.archive_format != ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE &&
-	    hardlink != NULL)
-		archive_entry_set_size(entry_main, 0);
-
-	/*
 	 * XXX Full pax interchange format does permit a hardlink
 	 * entry to have data associated with it.  I'm not supporting
 	 * that here because the client expects me to tell them whether
@@ -1411,6 +1505,21 @@
 		return (ARCHIVE_FATAL);
 	}
 
+	/* Pad the extended header so contiguous file data starts on a pax->align
+	 * boundary (for reflinking); short and sparse files are left alone. */
+	if (pax->align > 0
+	    && archive_entry_filetype(entry_main) == AE_IFREG
+	    && hardlink == NULL
+	    && sparse_count == 0
+	    && real_size >= (uint64_t)pax->align) {
+		size_t l0 = archive_strlen(&(pax->pax_header));
+		size_t needed = pax_align_body_len(
+		    (uint64_t)a->filter_first->bytes_written, l0, pax->align);
+		if (needed != l0)
+			pax_header_padding = add_pax_padding_prefix(
+			    &(pax->pax_header), l0, needed);
+	}
+
 	/* If we built any extended attributes, write that entry first. */
 	if (archive_strlen(&(pax->pax_header)) > 0) {
 		struct archive_entry *pax_attr_entry;
@@ -1419,11 +1528,20 @@
 		__LA_MODE_T mode;
 
 		pax_attr_entry = archive_entry_new2(&a->archive);
+		if (pax_attr_entry == NULL) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Out of memory");
+			archive_entry_free(entry_main);
+			archive_string_free(&entry_name);
+			return (ARCHIVE_FATAL);
+		}
+
 		p = entry_name.s;
 		archive_entry_set_pathname(pax_attr_entry,
 		    build_pax_attribute_name(pax_entry_name, p));
 		archive_entry_set_size(pax_attr_entry,
-		    archive_strlen(&(pax->pax_header)));
+		    archive_strlen(&(pax->pax_header)) +
+		    pax_header_padding);
 		/* Copy uid/gid (but clip to ustar limits). */
 		uid = archive_entry_uid(entry_main);
 		if (uid >= 1 << 18)
@@ -1489,7 +1607,8 @@
 			return (ARCHIVE_FATAL);
 		}
 
-		pax->entry_bytes_remaining = archive_strlen(&(pax->pax_header));
+		pax->entry_bytes_remaining = archive_strlen(&(pax->pax_header)) +
+		    pax_header_padding;
 		pax->entry_padding =
 		    0x1ff & (-(int64_t)pax->entry_bytes_remaining);
 
@@ -1501,8 +1620,14 @@
 			archive_string_free(&entry_name);
 			return (ARCHIVE_FATAL);
 		}
+		r = write_pax_padding(a, pax_header_padding);
+		if (r != ARCHIVE_OK) {
+			archive_entry_free(entry_main);
+			archive_string_free(&entry_name);
+			return (ARCHIVE_FATAL);
+		}
 		/* Pad out the end of the entry. */
-		r = __archive_write_nulls(a, (size_t)pax->entry_padding);
+		r = __archive_write_nulls(a, pax->entry_padding);
 		if (r != ARCHIVE_OK) {
 			/* If a write fails, we're pretty much toast. */
 			archive_entry_free(entry_main);
@@ -1574,18 +1699,21 @@
 	char *p;
 	int need_slash = 0; /* Was there a trailing slash? */
 	size_t suffix_length = 98; /* 99 - 1 for trailing slash */
-	size_t insert_length;
+	size_t insert_length, insert_name_length;
 
 	/* Length of additional dir element to be added. */
-	if (insert == NULL)
+	if (insert == NULL) {
+		insert_name_length = 0;
 		insert_length = 0;
-	else
+	} else {
+		insert_name_length = strlen(insert);
 		/* +2 here allows for '/' before and after the insert. */
-		insert_length = strlen(insert) + 2;
+		insert_length = insert_name_length + 2;
+	}
 
 	/* Step 0: Quick bailout in a common case. */
 	if (src_length < 100 && insert == NULL) {
-		strncpy(dest, src, src_length);
+		memcpy(dest, src, src_length);
 		dest[src_length] = '\0';
 		return (dest);
 	}
@@ -1607,6 +1735,26 @@
 		}
 		break;
 	}
+
+	/*
+	 * Pathological case: after trimming trailing '/' characters and
+	 * '/.' path elements, there is no filename component left.  This
+	 * happens for pathnames made entirely of '/' characters.  Do not
+	 * attempt to compute filename_end - 1 in that case, which would
+	 * move the filename pointer in front of the input buffer and read
+	 * one byte out of bounds.  Emit a root-like ustar name instead.
+	 */
+	if (filename_end == src) {
+		p = dest;
+		if (insert != NULL) {
+			strcpy(p, insert);
+			p += strlen(insert);
+		}
+		*p++ = '/';
+		*p = '\0';
+		return (dest);
+	}
+
 	if (need_slash)
 		suffix_length--;
 	/* Find start of filename. */
@@ -1647,24 +1795,22 @@
 		suffix_end++;
 
 	/* Step 4: Build the new name. */
-	/* The OpenBSD strlcpy function is safer, but less portable. */
-	/* Rather than maintain two versions, just use the strncpy version. */
 	p = dest;
 	if (prefix_end > prefix) {
-		strncpy(p, prefix, prefix_end - prefix);
+		memcpy(p, prefix, (size_t)(prefix_end - prefix));
 		p += prefix_end - prefix;
 	}
 	if (suffix_end > suffix) {
-		strncpy(p, suffix, suffix_end - suffix);
+		memcpy(p, suffix, (size_t)(suffix_end - suffix));
 		p += suffix_end - suffix;
 	}
 	if (insert != NULL) {
 		/* Note: assume insert does not have leading or trailing '/' */
-		strcpy(p, insert);
-		p += strlen(insert);
+		memcpy(p, insert, insert_name_length);
+		p += insert_name_length;
 		*p++ = '/';
 	}
-	strncpy(p, filename, filename_end - filename);
+	memcpy(p, filename, (size_t)(filename_end - filename));
 	p += filename_end - filename;
 	if (need_slash)
 		*p++ = '/';
@@ -1794,7 +1940,12 @@
 	}
 
 	/* General case: build a ustar-compatible name adding
-	 * "/GNUSparseFile/". */
+	 * "/GNUSparseFile.0/". */
+
+	if (p == src) {
+		strcpy(dest, "/GNUSparseFile.0/rootdir");
+		return (dest);
+	}
 	build_ustar_entry_name(dest, src, p - src, "GNUSparseFile.0");
 
 	return (dest);
@@ -1810,9 +1961,8 @@
 static int
 archive_write_pax_free(struct archive_write *a)
 {
-	struct pax *pax;
+	struct pax *pax = a->format_data;
 
-	pax = (struct pax *)a->format_data;
 	if (pax == NULL)
 		return (ARCHIVE_OK);
 
@@ -1828,11 +1978,10 @@
 static int
 archive_write_pax_finish_entry(struct archive_write *a)
 {
-	struct pax *pax;
+	struct pax *pax = a->format_data;
 	uint64_t remaining;
 	int ret;
 
-	pax = (struct pax *)a->format_data;
 	remaining = pax->entry_bytes_remaining;
 	if (remaining == 0) {
 		while (pax->sparse_list) {
@@ -1844,7 +1993,7 @@
 			pax->sparse_list = sb;
 		}
 	}
-	ret = __archive_write_nulls(a, (size_t)(remaining + pax->entry_padding));
+	ret = __archive_write_nulls(a, remaining + pax->entry_padding);
 	pax->entry_bytes_remaining = pax->entry_padding = 0;
 	return (ret);
 }
@@ -1852,13 +2001,11 @@
 static ssize_t
 archive_write_pax_data(struct archive_write *a, const void *buff, size_t s)
 {
-	struct pax *pax;
+	struct pax *pax = a->format_data;
 	size_t ws;
 	size_t total;
 	int ret;
 
-	pax = (struct pax *)a->format_data;
-
 	/*
 	 * According to GNU PAX format 1.0, write a sparse map
 	 * before the body.
@@ -1980,20 +2127,27 @@
 	      'e','f','g','h','i','j','k','l','m','n','o','p','q','r','s',
 	      't','u','v','w','x','y','z','0','1','2','3','4','5','6','7',
 	      '8','9','+','/' };
-	int v;
+	uint32_t v;
 	char *d, *out;
+	size_t out_len;
 
 	/* 3 bytes becomes 4 chars, but round up and allow for trailing NUL */
-	out = malloc((len * 4 + 2) / 3 + 1);
+	if (archive_ckd_mul_size(&out_len, len, 4) ||
+	    archive_ckd_add_size(&out_len, out_len, 2))
+		return (NULL);
+	out_len = out_len / 3;
+	if (archive_ckd_add_size(&out_len, out_len, 1))
+		return (NULL);
+	out = malloc(out_len);
 	if (out == NULL)
 		return (NULL);
 	d = out;
 
 	/* Convert each group of 3 bytes into 4 characters. */
 	while (len >= 3) {
-		v = (((int)s[0] << 16) & 0xff0000)
-		    | (((int)s[1] << 8) & 0xff00)
-		    | (((int)s[2]) & 0x00ff);
+		v = ((uint32_t)(unsigned char)s[0] << 16)
+		    | ((uint32_t)(unsigned char)s[1] << 8)
+		    | (uint32_t)(unsigned char)s[2];
 		s += 3;
 		len -= 3;
 		*d++ = digits[(v >> 18) & 0x3f];
@@ -2005,13 +2159,13 @@
 	switch (len) {
 	case 0: break;
 	case 1:
-		v = (((int)s[0] << 16) & 0xff0000);
+		v = ((uint32_t)(unsigned char)s[0] << 16);
 		*d++ = digits[(v >> 18) & 0x3f];
 		*d++ = digits[(v >> 12) & 0x3f];
 		break;
 	case 2:
-		v = (((int)s[0] << 16) & 0xff0000)
-		    | (((int)s[1] << 8) & 0xff00);
+		v = ((uint32_t)(unsigned char)s[0] << 16)
+		    | ((uint32_t)(unsigned char)s[1] << 8);
 		*d++ = digits[(v >> 18) & 0x3f];
 		*d++ = digits[(v >> 12) & 0x3f];
 		*d++ = digits[(v >> 6) & 0x3f];
diff --git a/c/archive_write_set_format_raw.c b/c/archive_write_set_format_raw.c
--- a/c/archive_write_set_format_raw.c
+++ b/c/archive_write_set_format_raw.c
@@ -55,8 +55,7 @@
 	    ARCHIVE_STATE_NEW, "archive_write_set_format_raw");
 
 	/* If someone else was already registered, unregister them. */
-	if (a->format_free != NULL)
-		(a->format_free)(a);
+	(void)__archive_write_unregister_format(a);
 
 	raw = calloc(1, sizeof(*raw));
 	if (raw == NULL) {
@@ -82,7 +81,7 @@
 static int
 archive_write_raw_header(struct archive_write *a, struct archive_entry *entry)
 {
-	struct raw *raw = (struct raw *)a->format_data;
+	struct raw *raw = a->format_data;
 
 	if (archive_entry_filetype(entry) != AE_IFREG) {
 		archive_set_error(&a->archive, ERANGE,
@@ -116,9 +115,8 @@
 static int
 archive_write_raw_free(struct archive_write *a)
 {
-	struct raw *raw;
+	struct raw *raw = a->format_data;
 
-	raw = (struct raw *)a->format_data;
 	free(raw);
 	a->format_data = NULL;
 	return (ARCHIVE_OK);
diff --git a/c/archive_write_set_format_shar.c b/c/archive_write_set_format_shar.c
--- a/c/archive_write_set_format_shar.c
+++ b/c/archive_write_set_format_shar.c
@@ -98,11 +98,8 @@
 	}
 }
 
-/*
- * Set output format to 'shar' format.
- */
-int
-archive_write_set_format_shar(struct archive *_a)
+static int
+shar_set_format(struct archive *_a, int dump)
 {
 	struct archive_write *a = (struct archive_write *)_a;
 	struct shar *shar;
@@ -111,8 +108,7 @@
 	    ARCHIVE_STATE_NEW, "archive_write_set_format_shar");
 
 	/* If someone else was already registered, unregister them. */
-	if (a->format_free != NULL)
-		(a->format_free)(a);
+	(void)__archive_write_unregister_format(a);
 
 	shar = calloc(1, sizeof(*shar));
 	if (shar == NULL) {
@@ -121,50 +117,54 @@
 	}
 	archive_string_init(&shar->work);
 	archive_string_init(&shar->quoted_name);
+	if (dump) {
+		shar->dump = 1;
+		a->format_write_data = archive_write_shar_data_uuencode;
+		a->archive.archive_format = ARCHIVE_FORMAT_SHAR_DUMP;
+		a->archive.archive_format_name = "shar dump";
+	} else {
+		a->format_write_data = archive_write_shar_data_sed;
+		a->archive.archive_format = ARCHIVE_FORMAT_SHAR_BASE;
+		a->archive.archive_format_name = "shar";
+	}
 	a->format_data = shar;
 	a->format_name = "shar";
 	a->format_write_header = archive_write_shar_header;
 	a->format_close = archive_write_shar_close;
 	a->format_free = archive_write_shar_free;
-	a->format_write_data = archive_write_shar_data_sed;
 	a->format_finish_entry = archive_write_shar_finish_entry;
-	a->archive.archive_format = ARCHIVE_FORMAT_SHAR_BASE;
-	a->archive.archive_format_name = "shar";
 	return (ARCHIVE_OK);
 }
 
 /*
+ * Set output format to 'shar' format.
+ */
+int
+archive_write_set_format_shar(struct archive *a)
+{
+	return shar_set_format(a, 0);
+}
+
+/*
  * An alternate 'shar' that uses uudecode instead of 'sed' to encode
  * file contents and can therefore be used to archive binary files.
  * In addition, this variant also attempts to restore ownership, file modes,
  * and other extended file information.
  */
 int
-archive_write_set_format_shar_dump(struct archive *_a)
+archive_write_set_format_shar_dump(struct archive *a)
 {
-	struct archive_write *a = (struct archive_write *)_a;
-	struct shar *shar;
-
-	int ret = archive_write_set_format_shar(&a->archive);
-	if (ret != ARCHIVE_OK)
-		return ret;
-	shar = (struct shar *)a->format_data;
-	shar->dump = 1;
-	a->format_write_data = archive_write_shar_data_uuencode;
-	a->archive.archive_format = ARCHIVE_FORMAT_SHAR_DUMP;
-	a->archive.archive_format_name = "shar dump";
-	return (ARCHIVE_OK);
+	return shar_set_format(a, 1);
 }
 
 static int
 archive_write_shar_header(struct archive_write *a, struct archive_entry *entry)
 {
+	struct shar *shar = a->format_data;
 	const char *linkname;
 	const char *name;
 	char *p, *pp;
-	struct shar *shar;
 
-	shar = (struct shar *)a->format_data;
 	if (!shar->wrote_header) {
 		archive_strcat(&shar->work, "#!/bin/sh\n");
 		archive_strcat(&shar->work, "# This is a shell archive\n");
@@ -343,14 +343,13 @@
 static ssize_t
 archive_write_shar_data_sed(struct archive_write *a, const void *buff, size_t n)
 {
+	struct shar *shar = a->format_data;
 	static const size_t ensured = 65533;
-	struct shar *shar;
 	const char *src;
 	char *buf, *buf_end;
 	int ret;
 	size_t written = n;
 
-	shar = (struct shar *)a->format_data;
 	if (!shar->has_data || n == 0)
 		return (0);
 
@@ -475,12 +474,11 @@
 archive_write_shar_data_uuencode(struct archive_write *a, const void *buff,
     size_t length)
 {
-	struct shar *shar;
+	struct shar *shar = a->format_data;
 	const char *src;
 	size_t n;
 	int ret;
 
-	shar = (struct shar *)a->format_data;
 	if (!shar->has_data)
 		return (ARCHIVE_OK);
 	src = (const char *)buff;
@@ -524,13 +522,12 @@
 static int
 archive_write_shar_finish_entry(struct archive_write *a)
 {
+	struct shar *shar = a->format_data;
 	const char *g, *p, *u;
-	struct shar *shar;
 	int ret;
 
-	shar = (struct shar *)a->format_data;
 	if (shar->entry == NULL)
-		return (0);
+		return (ARCHIVE_OK);
 
 	if (shar->dump) {
 		/* Finish uuencoded data. */
@@ -602,7 +599,7 @@
 static int
 archive_write_shar_close(struct archive_write *a)
 {
-	struct shar *shar;
+	struct shar *shar = a->format_data;
 	int ret;
 
 	/*
@@ -610,8 +607,6 @@
 	 * fix them all up at end-of-archive.
 	 */
 
-	shar = (struct shar *)a->format_data;
-
 	/*
 	 * Only write the end-of-archive markers if the archive was
 	 * actually started.  This avoids problems if someone sets
@@ -640,9 +635,8 @@
 static int
 archive_write_shar_free(struct archive_write *a)
 {
-	struct shar *shar;
+	struct shar *shar = a->format_data;
 
-	shar = (struct shar *)a->format_data;
 	if (shar == NULL)
 		return (ARCHIVE_OK);
 
diff --git a/c/archive_write_set_format_ustar.c b/c/archive_write_set_format_ustar.c
--- a/c/archive_write_set_format_ustar.c
+++ b/c/archive_write_set_format_ustar.c
@@ -172,8 +172,7 @@
 	    ARCHIVE_STATE_NEW, "archive_write_set_format_ustar");
 
 	/* If someone else was already registered, unregister them. */
-	if (a->format_free != NULL)
-		(a->format_free)(a);
+	(void)__archive_write_unregister_format(a);
 
 	/* Basic internal sanity test. */
 	if (sizeof(template_header) != 512) {
@@ -206,7 +205,7 @@
 archive_write_ustar_options(struct archive_write *a, const char *key,
     const char *val)
 {
-	struct ustar *ustar = (struct ustar *)a->format_data;
+	struct ustar *ustar = a->format_data;
 	int ret = ARCHIVE_FAILED;
 
 	if (strcmp(key, "hdrcharset")  == 0) {
@@ -234,14 +233,12 @@
 static int
 archive_write_ustar_header(struct archive_write *a, struct archive_entry *entry)
 {
+	struct ustar *ustar = a->format_data;
 	char buff[512];
 	int ret, ret2;
-	struct ustar *ustar;
 	struct archive_entry *entry_main;
 	struct archive_string_conv *sconv;
 
-	ustar = (struct ustar *)a->format_data;
-
 	/* Setup default string conversion. */
 	if (ustar->opt_sconv == NULL) {
 		if (!ustar->init_default_conversion) {
@@ -736,9 +733,8 @@
 static int
 archive_write_ustar_free(struct archive_write *a)
 {
-	struct ustar *ustar;
+	struct ustar *ustar = a->format_data;
 
-	ustar = (struct ustar *)a->format_data;
 	free(ustar);
 	a->format_data = NULL;
 	return (ARCHIVE_OK);
@@ -747,12 +743,11 @@
 static int
 archive_write_ustar_finish_entry(struct archive_write *a)
 {
-	struct ustar *ustar;
+	struct ustar *ustar = a->format_data;
 	int ret;
 
-	ustar = (struct ustar *)a->format_data;
 	ret = __archive_write_nulls(a,
-	    (size_t)(ustar->entry_bytes_remaining + ustar->entry_padding));
+	    ustar->entry_bytes_remaining + ustar->entry_padding);
 	ustar->entry_bytes_remaining = ustar->entry_padding = 0;
 	return (ret);
 }
@@ -760,10 +755,9 @@
 static ssize_t
 archive_write_ustar_data(struct archive_write *a, const void *buff, size_t s)
 {
-	struct ustar *ustar;
+	struct ustar *ustar = a->format_data;
 	int ret;
 
-	ustar = (struct ustar *)a->format_data;
 	if (s > ustar->entry_bytes_remaining)
 		s = (size_t)ustar->entry_bytes_remaining;
 	ret = __archive_write_output(a, buff, s);
diff --git a/c/archive_write_set_format_v7tar.c b/c/archive_write_set_format_v7tar.c
--- a/c/archive_write_set_format_v7tar.c
+++ b/c/archive_write_set_format_v7tar.c
@@ -149,8 +149,7 @@
 	    ARCHIVE_STATE_NEW, "archive_write_set_format_v7tar");
 
 	/* If someone else was already registered, unregister them. */
-	if (a->format_free != NULL)
-		(a->format_free)(a);
+	(void)__archive_write_unregister_format(a);
 
 	/* Basic internal sanity test. */
 	if (sizeof(template_header) != 512) {
@@ -183,7 +182,7 @@
 archive_write_v7tar_options(struct archive_write *a, const char *key,
     const char *val)
 {
-	struct v7tar *v7tar = (struct v7tar *)a->format_data;
+	struct v7tar *v7tar = a->format_data;
 	int ret = ARCHIVE_FAILED;
 
 	if (strcmp(key, "hdrcharset")  == 0) {
@@ -211,14 +210,12 @@
 static int
 archive_write_v7tar_header(struct archive_write *a, struct archive_entry *entry)
 {
+	struct v7tar *v7tar = a->format_data;
 	char buff[512];
 	int ret, ret2;
-	struct v7tar *v7tar;
 	struct archive_entry *entry_main;
 	struct archive_string_conv *sconv;
 
-	v7tar = (struct v7tar *)a->format_data;
-
 	/* Setup default string conversion. */
 	if (v7tar->opt_sconv == NULL) {
 		if (!v7tar->init_default_conversion) {
@@ -616,9 +613,8 @@
 static int
 archive_write_v7tar_free(struct archive_write *a)
 {
-	struct v7tar *v7tar;
+	struct v7tar *v7tar = a->format_data;
 
-	v7tar = (struct v7tar *)a->format_data;
 	free(v7tar);
 	a->format_data = NULL;
 	return (ARCHIVE_OK);
@@ -627,12 +623,11 @@
 static int
 archive_write_v7tar_finish_entry(struct archive_write *a)
 {
-	struct v7tar *v7tar;
+	struct v7tar *v7tar = a->format_data;
 	int ret;
 
-	v7tar = (struct v7tar *)a->format_data;
 	ret = __archive_write_nulls(a,
-	    (size_t)(v7tar->entry_bytes_remaining + v7tar->entry_padding));
+	    v7tar->entry_bytes_remaining + v7tar->entry_padding);
 	v7tar->entry_bytes_remaining = v7tar->entry_padding = 0;
 	return (ret);
 }
@@ -640,10 +635,9 @@
 static ssize_t
 archive_write_v7tar_data(struct archive_write *a, const void *buff, size_t s)
 {
-	struct v7tar *v7tar;
+	struct v7tar *v7tar = a->format_data;
 	int ret;
 
-	v7tar = (struct v7tar *)a->format_data;
 	if (s > v7tar->entry_bytes_remaining)
 		s = (size_t)v7tar->entry_bytes_remaining;
 	ret = __archive_write_output(a, buff, s);
diff --git a/c/archive_write_set_format_warc.c b/c/archive_write_set_format_warc.c
--- a/c/archive_write_set_format_warc.c
+++ b/c/archive_write_set_format_warc.c
@@ -49,68 +49,81 @@
 #include "archive_write_private.h"
 #include "archive_write_set_format_private.h"
 
-struct warc_s {
+/*
+ * Overview of the WARC writer:
+ *
+ * This writer emits WARC/1.0 resource records for regular files.  It
+ * writes a warcinfo record once by default, unless the omit-warcinfo
+ * option is used.  Each resource record gets a WARC-Target-URI from
+ * the entry pathname, a generated WARC-Record-ID, and a mandatory
+ * Content-Length header.
+ */
+
+struct warc {
 	unsigned int omit_warcinfo:1;
 
 	time_t now;
-	mode_t typ;
-	unsigned int rng;
-	/* populated size */
-	uint64_t populz;
+	mode_t filetype;
+	/* Remaining bytes to write for the current entry */
+	uint64_t entry_bytes_remaining;
 };
 
-static const char warcinfo[] =
+#define WARC_HEADER_MAX_SIZE 512
+
+static const char warcinfo_payload[] =
     "software: libarchive/" ARCHIVE_VERSION_ONLY_STRING "\r\n"
     "format: WARC file version 1.0\r\n";
 
-typedef enum {
-	WT_NONE,
-	/* warcinfo */
-	WT_INFO,
-	/* metadata */
-	WT_META,
-	/* resource */
-	WT_RSRC,
-	/* request, unsupported */
-	WT_REQ,
-	/* response, unsupported */
-	WT_RSP,
-	/* revisit, unsupported */
-	WT_RVIS,
-	/* conversion, unsupported */
-	WT_CONV,
-	/* continuation, unsupported at the moment */
-	WT_CONT,
-	/* invalid type */
-	LAST_WT
-} warc_type_t;
+enum warc_type {
+	WARC_TYPE_NONE,
+	/* WARC info */
+	WARC_TYPE_INFO,
+	/* Metadata */
+	WARC_TYPE_METADATA,
+	/* Resource */
+	WARC_TYPE_RESOURCE,
+	/* Request, unsupported */
+	WARC_TYPE_REQUEST,
+	/* Response, unsupported by this writer */
+	WARC_TYPE_RESPONSE,
+	/* Revisit, unsupported */
+	WARC_TYPE_REVISIT,
+	/* Conversion, unsupported */
+	WARC_TYPE_CONVERSION,
+	/* Continuation, currently unsupported */
+	WARC_TYPE_CONTINUATION,
+	WARC_TYPE_LAST
+};
 
-typedef struct {
-	warc_type_t type;
-	const char *tgturi;
-	const char *recid;
-	time_t rtime;
-	time_t mtime;
-	const char *cnttyp;
-	uint64_t cntlen;
-} warc_essential_hdr_t;
+struct warc_header {
+	enum warc_type type;
+	const char *target_uri;
+	const char *record_id;
+	time_t record_time;
+	time_t modification_time;
+	const char *content_type;
+	uint64_t content_length;
+};
 
-typedef struct {
-	unsigned int u[4U];
-} warc_uuid_t;
+struct warc_uuid {
+	unsigned int value[4U];
+};
 
-static int _warc_options(struct archive_write*, const char *key, const char *v);
-static int _warc_header(struct archive_write *a, struct archive_entry *entry);
-static ssize_t _warc_data(struct archive_write *a, const void *buf, size_t sz);
-static int _warc_finish_entry(struct archive_write *a);
-static int _warc_close(struct archive_write *a);
-static int _warc_free(struct archive_write *a);
+static int	archive_write_warc_options(struct archive_write *,
+		    const char *, const char *);
+static int	archive_write_warc_header(struct archive_write *,
+		    struct archive_entry *);
+static ssize_t	archive_write_warc_data(struct archive_write *, const void *,
+		    size_t);
+static int	archive_write_warc_finish_entry(struct archive_write *);
+static int	archive_write_warc_close(struct archive_write *);
+static int	archive_write_warc_free(struct archive_write *);
 
-/* private routines */
-static ssize_t _popul_ehdr(struct archive_string *t, size_t z, warc_essential_hdr_t);
-static int _gen_uuid(warc_uuid_t *tgt);
+static void	warc_format_time(struct archive_string *, const char *, time_t);
+static ssize_t	warc_populate_header(struct archive_string *, size_t,
+		    struct warc_header);
+static void	warc_generate_uuid(struct warc_uuid *);
 
-
 /*
  * Set output format to ISO 28500 (aka WARC) format.
  */
@@ -118,103 +131,110 @@
 archive_write_set_format_warc(struct archive *_a)
 {
 	struct archive_write *a = (struct archive_write *)_a;
-	struct warc_s *w;
+	struct warc *warc;
 
 	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
 	    ARCHIVE_STATE_NEW, "archive_write_set_format_warc");
 
 	/* If another format was already registered, unregister it. */
-	if (a->format_free != NULL) {
-		(a->format_free)(a);
-	}
+	(void)__archive_write_unregister_format(a);
 
-	w = malloc(sizeof(*w));
-	if (w == NULL) {
+	warc = malloc(sizeof(*warc));
+	if (warc == NULL) {
 		archive_set_error(&a->archive, ENOMEM,
 		    "Can't allocate warc data");
 		return (ARCHIVE_FATAL);
 	}
-	/* by default we're emitting a file wide header */
-	w->omit_warcinfo = 0U;
-	/* obtain current time for date fields */
-	w->now = time(NULL);
-	/* reset file type info */
-	w->typ = 0;
-	/* also initialise our rng */
-	w->rng = (unsigned int)w->now;
+	/* Emit a warcinfo record by default. */
+	warc->omit_warcinfo = 0U;
+	/* Use the current time for WARC-Date values. */
+	warc->now = time(NULL);
+	/* Reset file type information. */
+	warc->filetype = 0;
 
-	a->format_data = w;
+	a->format_data = warc;
 	a->format_name = "WARC/1.0";
-	a->format_options = _warc_options;
-	a->format_write_header = _warc_header;
-	a->format_write_data = _warc_data;
-	a->format_close = _warc_close;
-	a->format_free = _warc_free;
-	a->format_finish_entry = _warc_finish_entry;
+	a->format_options = archive_write_warc_options;
+	a->format_write_header = archive_write_warc_header;
+	a->format_write_data = archive_write_warc_data;
+	a->format_close = archive_write_warc_close;
+	a->format_free = archive_write_warc_free;
+	a->format_finish_entry = archive_write_warc_finish_entry;
 	a->archive.archive_format = ARCHIVE_FORMAT_WARC;
 	a->archive.archive_format_name = "WARC/1.0";
 	return (ARCHIVE_OK);
 }
 
-
-/* archive methods */
 static int
-_warc_options(struct archive_write *a, const char *key, const char *val)
+archive_write_warc_options(struct archive_write *a, const char *key,
+    const char *val)
 {
-	struct warc_s *w = a->format_data;
+	struct warc *warc = a->format_data;
 
 	if (strcmp(key, "omit-warcinfo") == 0) {
 		if (val == NULL || strcmp(val, "true") == 0) {
-			/* great */
-			w->omit_warcinfo = 1U;
+			/* Option accepted. */
+			warc->omit_warcinfo = 1U;
 			return (ARCHIVE_OK);
 		}
 	}
 
-	/* Note: The "warn" return is just to inform the options
-	 * supervisor that we didn't handle it.  It will generate
-	 * a suitable error if no one used this option. */
+	/* ARCHIVE_WARN tells the options supervisor that this option was not
+	 * handled here.  It will report an error if no module uses it. */
 	return (ARCHIVE_WARN);
 }
 
 static int
-_warc_header(struct archive_write *a, struct archive_entry *entry)
+archive_write_warc_header(struct archive_write *a, struct archive_entry *entry)
 {
-	struct warc_s *w = a->format_data;
-	struct archive_string hdr;
-#define MAX_HDR_SIZE 512
+	struct warc *warc = a->format_data;
+	struct archive_string header;
 
-	/* check whether warcinfo record needs outputting */
-	if (!w->omit_warcinfo) {
-		ssize_t r;
-		warc_essential_hdr_t wi = {
-			WT_INFO,
-			/*uri*/NULL,
-			/*urn*/NULL,
-			/*rtm*/0,
-			/*mtm*/0,
-			/*cty*/"application/warc-fields",
-			/*len*/sizeof(warcinfo) - 1U,
+	/* Emit the warcinfo record if needed. */
+	if (!warc->omit_warcinfo) {
+		ssize_t header_size;
+		int ret;
+		struct warc_header warcinfo_header = {
+			WARC_TYPE_INFO,
+			/* URI */NULL,
+			/* Record ID */NULL,
+			/* Record time */0,
+			/* Modified time */0,
+			/* Content type */"application/warc-fields",
+			/* Content length */sizeof(warcinfo_payload) - 1U,
 		};
-		wi.rtime = w->now;
-		wi.mtime = w->now;
+		warcinfo_header.record_time = warc->now;
+		warcinfo_header.modification_time = warc->now;
 
-		archive_string_init(&hdr);
-		r = _popul_ehdr(&hdr, MAX_HDR_SIZE, wi);
-		if (r >= 0) {
-			/* jackpot! */
-			/* now also use HDR buffer for the actual warcinfo */
-			archive_strncat(&hdr, warcinfo, sizeof(warcinfo) -1);
+		archive_string_init(&header);
+		header_size = warc_populate_header(&header,
+		    WARC_HEADER_MAX_SIZE, warcinfo_header);
+		if (header_size < 0) {
+			archive_string_free(&header);
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Cannot archive warcinfo record");
+			return (ARCHIVE_FAILED);
+		}
 
-			/* append end-of-record indicator */
-			archive_strncat(&hdr, "\r\n\r\n", 4);
+		/* Reuse the header buffer for the warcinfo payload. */
+		archive_strncat(&header, warcinfo_payload,
+		    sizeof(warcinfo_payload) - 1);
 
-			/* write to output stream */
-			__archive_write_output(a, hdr.s, archive_strlen(&hdr));
+		/* Append the end-of-record indicator. */
+		archive_strncat(&header, "\r\n\r\n", 4);
+
+		/* Write the warcinfo record to the output stream. */
+		ret = __archive_write_output(a, header.s,
+		    archive_strlen(&header));
+		if (ret != ARCHIVE_OK) {
+			archive_string_free(&header);
+			return (ret);
 		}
-		/* indicate we're done with file header writing */
-		w->omit_warcinfo = 1U;
-		archive_string_free(&hdr);
+
+		/* Mark the file header as written. */
+		warc->omit_warcinfo = 1U;
+		archive_string_free(&header);
 	}
 
 	if (archive_entry_pathname(entry) == NULL) {
@@ -223,222 +243,249 @@
 		return (ARCHIVE_WARN);
 	}
 
-	w->typ = archive_entry_filetype(entry);
-	w->populz = 0U;
-	if (w->typ == AE_IFREG) {
-		warc_essential_hdr_t rh = {
-			WT_RSRC,
-			/*uri*/NULL,
-			/*urn*/NULL,
-			/*rtm*/0,
-			/*mtm*/0,
-			/*cty*/NULL,
-			/*len*/0,
+	warc->filetype = archive_entry_filetype(entry);
+	warc->entry_bytes_remaining = 0U;
+	if (warc->filetype == AE_IFREG) {
+		struct warc_header resource_header = {
+			WARC_TYPE_RESOURCE,
+			/* URI */NULL,
+			/* Record ID */NULL,
+			/* Record time */0,
+			/* Modified time */0,
+			/* Content type */NULL,
+			/* Content length */0,
 		};
-		ssize_t r;
-		rh.tgturi = archive_entry_pathname(entry);
-		rh.rtime = w->now;
-		rh.mtime = archive_entry_mtime(entry);
-		rh.cntlen = (size_t)archive_entry_size(entry);
+		ssize_t header_size;
+		int ret;
+		int64_t size;
 
-		archive_string_init(&hdr);
-		r = _popul_ehdr(&hdr, MAX_HDR_SIZE, rh);
-		if (r < 0) {
-			/* don't bother */
-			archive_string_free(&hdr);
-			archive_set_error(
-				&a->archive,
-				ARCHIVE_ERRNO_FILE_FORMAT,
-				"cannot archive file");
-			return (ARCHIVE_WARN);
+		resource_header.target_uri = archive_entry_pathname(entry);
+		resource_header.record_time = warc->now;
+		resource_header.modification_time = archive_entry_mtime(entry);
+		if (!archive_entry_size_is_set(entry)) {
+			archive_set_error(&a->archive, -1,
+			    "Size required");
+			return (ARCHIVE_FAILED);
 		}
-		/* otherwise append to output stream */
-		__archive_write_output(a, hdr.s, r);
-		/* and let subsequent calls to _data() know about the size */
-		w->populz = rh.cntlen;
-		archive_string_free(&hdr);
+		size = archive_entry_size(entry);
+		if (size < 0) {
+			archive_set_error(&a->archive, -1,
+			    "Size required");
+			return (ARCHIVE_FAILED);
+		}
+		resource_header.content_length = (uint64_t)size;
+
+		archive_string_init(&header);
+		header_size = warc_populate_header(&header,
+		    WARC_HEADER_MAX_SIZE, resource_header);
+		if (header_size < 0) {
+			/* Header generation failed. */
+			archive_string_free(&header);
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "WARC resource header is too large");
+			return (ARCHIVE_FATAL);
+		}
+		/* Append the header to the output stream. */
+		ret = __archive_write_output(a, header.s, header_size);
+		if (ret != ARCHIVE_OK) {
+			archive_string_free(&header);
+			return (ret);
+		}
+		/* Save the remaining size for subsequent data callbacks. */
+		warc->entry_bytes_remaining = resource_header.content_length;
+		archive_string_free(&header);
 		return (ARCHIVE_OK);
 	}
-	/* just resort to erroring as per Tim's advice */
+	/* Report unsupported file types through the common helper. */
 	__archive_write_entry_filetype_unsupported(
 	    &a->archive, entry, "WARC");
 	return (ARCHIVE_FAILED);
 }
 
 static ssize_t
-_warc_data(struct archive_write *a, const void *buf, size_t len)
+archive_write_warc_data(struct archive_write *a, const void *buf, size_t len)
 {
-	struct warc_s *w = a->format_data;
+	struct warc *warc = a->format_data;
 
-	if (w->typ == AE_IFREG) {
-		int rc;
+	if (warc->filetype == AE_IFREG) {
+		int ret;
 
-		/* never write more bytes than announced */
-		if (len > w->populz) {
-			len = (size_t)w->populz;
+		/* Never write more bytes than announced. */
+		if ((uint64_t)len > warc->entry_bytes_remaining) {
+			len = (size_t)warc->entry_bytes_remaining;
 		}
 
-		/* now then, out we put the whole shebang */
-		rc = __archive_write_output(a, buf, len);
-		if (rc != ARCHIVE_OK) {
-			return rc;
+		/* Write the entry data. */
+		ret = __archive_write_output(a, buf, len);
+		if (ret != ARCHIVE_OK) {
+			return (ret);
 		}
+		warc->entry_bytes_remaining -= len;
 	}
-	return len;
+	return (len);
 }
 
 static int
-_warc_finish_entry(struct archive_write *a)
+archive_write_warc_finish_entry(struct archive_write *a)
 {
-	static const char _eor[] = "\r\n\r\n";
-	struct warc_s *w = a->format_data;
+	static const char end_of_record[] = "\r\n\r\n";
+	struct warc *warc = a->format_data;
 
-	if (w->typ == AE_IFREG) {
-		int rc = __archive_write_output(a, _eor, sizeof(_eor) - 1U);
+	if (warc->filetype == AE_IFREG) {
+		int ret;
 
-		if (rc != ARCHIVE_OK) {
-			return rc;
+		if (warc->entry_bytes_remaining != 0U) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "WARC entry is shorter than Content-Length");
+			return (ARCHIVE_FATAL);
 		}
+
+		ret = __archive_write_output(a, end_of_record,
+		    sizeof(end_of_record) - 1U);
+		if (ret != ARCHIVE_OK) {
+			return (ret);
+		}
 	}
-	/* reset type info */
-	w->typ = 0;
+	/* Reset file type information. */
+	warc->filetype = 0;
 	return (ARCHIVE_OK);
 }
 
 static int
-_warc_close(struct archive_write *a)
+archive_write_warc_close(struct archive_write *a)
 {
 	(void)a; /* UNUSED */
 	return (ARCHIVE_OK);
 }
 
 static int
-_warc_free(struct archive_write *a)
+archive_write_warc_free(struct archive_write *a)
 {
-	struct warc_s *w = a->format_data;
+	struct warc *warc = a->format_data;
 
-	free(w);
+	free(warc);
 	a->format_data = NULL;
 	return (ARCHIVE_OK);
 }
 
-
-/* private routines */
+/* Like strftime(3), but for time_t objects. */
 static void
-xstrftime(struct archive_string *as, const char *fmt, time_t t)
+warc_format_time(struct archive_string *str, const char *format, time_t t)
 {
-/** like strftime(3) but for time_t objects */
-	struct tm *rt;
+	struct tm *tm;
 #if defined(HAVE_GMTIME_R) || defined(HAVE_GMTIME_S)
-	struct tm timeHere;
+	struct tm tm_storage;
 #endif
-	char strtime[100];
+	char time_string[100];
 	size_t len;
 
 #if defined(HAVE_GMTIME_S)
-	rt = gmtime_s(&timeHere, &t) ? NULL : &timeHere;
+	tm = gmtime_s(&tm_storage, &t) ? NULL : &tm_storage;
 #elif defined(HAVE_GMTIME_R)
-	rt = gmtime_r(&t, &timeHere);
+	tm = gmtime_r(&t, &tm_storage);
 #else
-	rt = gmtime(&t);
+	tm = gmtime(&t);
 #endif
-	if (!rt)
+	if (tm == NULL)
 		return;
-	/* leave the hard yacker to our role model strftime() */
-	len = strftime(strtime, sizeof(strtime)-1, fmt, rt);
-	archive_strncat(as, strtime, len);
+	/* Let strftime() handle the actual formatting. */
+	len = strftime(time_string, sizeof(time_string) - 1, format, tm);
+	archive_strncat(str, time_string, len);
 }
 
 static ssize_t
-_popul_ehdr(struct archive_string *tgt, size_t tsz, warc_essential_hdr_t hdr)
+warc_populate_header(struct archive_string *header_string, size_t max_size,
+    struct warc_header header)
 {
-	static const char _ver[] = "WARC/1.0\r\n";
-	static const char * const _typ[LAST_WT] = {
+	static const char version[] = "WARC/1.0\r\n";
+	static const char * const record_types[WARC_TYPE_LAST] = {
 		NULL, "warcinfo", "metadata", "resource", NULL
 	};
-	char std_uuid[48U];
+	char generated_record_id[48U];
 
-	if (hdr.type == WT_NONE || hdr.type > WT_RSRC) {
-		/* brilliant, how exactly did we get here? */
-		return -1;
+	if (header.type == WARC_TYPE_NONE || header.type > WARC_TYPE_RESOURCE) {
+		/* Invalid record type for this writer. */
+		return (-1);
 	}
 
-	archive_strcpy(tgt, _ver);
+	archive_strcpy(header_string, version);
 
-	archive_string_sprintf(tgt, "WARC-Type: %s\r\n", _typ[hdr.type]);
+	archive_string_sprintf(header_string, "WARC-Type: %s\r\n",
+	    record_types[header.type]);
 
-	if (hdr.tgturi != NULL) {
-		/* check if there's a xyz:// */
-		static const char _uri[] = "";
-		static const char _fil[] = "file://";
-		const char *u;
-		const char *chk = strchr(hdr.tgturi, ':');
+	if (header.target_uri != NULL) {
+		const char *uri_prefix;
+		const char *scheme = strchr(header.target_uri, ':');
 
-		if (chk != NULL && chk[1U] == '/' && chk[2U] == '/') {
-			/* yep, it's definitely a URI */
-			u = _uri;
+		/* Check whether the value already contains ://. */
+		if (scheme != NULL && scheme[1U] == '/' && scheme[2U] == '/') {
+			/* Already has a scheme-style :// prefix. */
+			uri_prefix = "";
 		} else {
-			/* hm, best to prepend file:// then */
-			u = _fil;
+			/* Prepend file:// for local paths. */
+			uri_prefix = "file://";
 		}
-		archive_string_sprintf(tgt,
-			"WARC-Target-URI: %s%s\r\n", u, hdr.tgturi);
+		archive_string_sprintf(header_string,
+		    "WARC-Target-URI: %s%s\r\n", uri_prefix,
+		    header.target_uri);
 	}
 
-	/* record time is usually when the http is sent off,
-	 * just treat the archive writing as such for a moment */
-	xstrftime(tgt, "WARC-Date: %Y-%m-%dT%H:%M:%SZ\r\n", hdr.rtime);
+	/* Write WARC-Date from header.record_time. */
+	warc_format_time(header_string,
+	    "WARC-Date: %Y-%m-%dT%H:%M:%SZ\r\n", header.record_time);
 
-	/* while we're at it, record the mtime */
-	xstrftime(tgt, "Last-Modified: %Y-%m-%dT%H:%M:%SZ\r\n", hdr.mtime);
+	/* Also write Last-Modified from header.modification_time. */
+	warc_format_time(header_string,
+	    "Last-Modified: %Y-%m-%dT%H:%M:%SZ\r\n",
+	    header.modification_time);
 
-	if (hdr.recid == NULL) {
-		/* generate one, grrrr */
-		warc_uuid_t u;
+	if (header.record_id == NULL) {
+		/* Generate a record ID when one was not provided. */
+		struct warc_uuid uuid;
 
-		_gen_uuid(&u);
-		/* Unfortunately, archive_string_sprintf does not
-		 * handle the minimum number following '%'.
-		 * So we have to use snprintf function here instead
-		 * of archive_string_snprintf function. */
-#if defined(_WIN32) && !defined(__CYGWIN__) && !( defined(_MSC_VER) && _MSC_VER >= 1900)
+		warc_generate_uuid(&uuid);
+		/* archive_string_sprintf() does not support minimum field widths, so
+		 * use snprintf() for UUID formatting. */
+#if defined(_WIN32) && !defined(__CYGWIN__) && \
+    !(defined(_MSC_VER) && _MSC_VER >= 1900)
 #define snprintf _snprintf
 #endif
-		snprintf(
-			std_uuid, sizeof(std_uuid),
-			"<urn:uuid:%08x-%04x-%04x-%04x-%04x%08x>",
-			u.u[0U],
-			u.u[1U] >> 16U, u.u[1U] & 0xffffU,
-			u.u[2U] >> 16U, u.u[2U] & 0xffffU,
-			u.u[3U]);
-		hdr.recid = std_uuid;
+		snprintf(generated_record_id, sizeof(generated_record_id),
+		    "<urn:uuid:%08x-%04x-%04x-%04x-%04x%08x>",
+		    uuid.value[0U],
+		    uuid.value[1U] >> 16U, uuid.value[1U] & 0xffffU,
+		    uuid.value[2U] >> 16U, uuid.value[2U] & 0xffffU,
+		    uuid.value[3U]);
+		header.record_id = generated_record_id;
 	}
 
-	/* record-id is mandatory, fingers crossed we won't fail */
-	archive_string_sprintf(tgt, "WARC-Record-ID: %s\r\n", hdr.recid);
+	/* WARC-Record-ID is mandatory. */
+	archive_string_sprintf(header_string, "WARC-Record-ID: %s\r\n",
+	    header.record_id);
 
-	if (hdr.cnttyp != NULL) {
-		archive_string_sprintf(tgt, "Content-Type: %s\r\n", hdr.cnttyp);
+	if (header.content_type != NULL) {
+		archive_string_sprintf(header_string, "Content-Type: %s\r\n",
+		    header.content_type);
 	}
 
-	/* next one is mandatory */
-	archive_string_sprintf(tgt, "Content-Length: %ju\r\n", (uintmax_t)hdr.cntlen);
-	/**/
-	archive_strncat(tgt, "\r\n", 2);
+	/* Content-Length is mandatory. */
+	archive_string_sprintf(header_string, "Content-Length: %ju\r\n",
+	    (uintmax_t)header.content_length);
+	/* End of header. */
+	archive_strncat(header_string, "\r\n", 2);
 
-	return (archive_strlen(tgt) >= tsz)? -1: (ssize_t)archive_strlen(tgt);
+	return (archive_strlen(header_string) >= max_size) ?
+	    -1 : (ssize_t)archive_strlen(header_string);
 }
 
-static int
-_gen_uuid(warc_uuid_t *tgt)
+static void
+warc_generate_uuid(struct warc_uuid *uuid)
 {
-	archive_random(tgt->u, sizeof(tgt->u));
-	/* obey uuid version 4 rules */
-	tgt->u[1U] &= 0xffff0fffU;
-	tgt->u[1U] |= 0x4000U;
-	tgt->u[2U] &= 0x3fffffffU;
-	tgt->u[2U] |= 0x80000000U;
-	return 0;
+	archive_random(uuid->value, sizeof(uuid->value));
+	/* Apply UUID version 4 rules. */
+	uuid->value[1U] &= 0xffff0fffU;
+	uuid->value[1U] |= 0x4000U;
+	uuid->value[2U] &= 0x3fffffffU;
+	uuid->value[2U] |= 0x80000000U;
 }
-
-/* archive_write_set_format_warc.c ends here */
diff --git a/c/archive_write_set_format_xar.c b/c/archive_write_set_format_xar.c
--- a/c/archive_write_set_format_xar.c
+++ b/c/archive_write_set_format_xar.c
@@ -111,7 +111,7 @@
 static int xml_writer_write_attribute(struct xml_writer *ctx, const char *key,
     const char *value);
 static int xml_writer_write_attributef(struct xml_writer *ctx, const char *key,
-    const char *format, ...);
+    const char *format, ...) __LA_PRINTF(3, 4);
 static int xml_writer_write_string(struct xml_writer *ctx, const char *string);
 static int xml_writer_write_base64(struct xml_writer* ctx,
     const char *data, size_t start, size_t len);
@@ -129,14 +129,20 @@
 enum sumalg {
 	CKSUM_NONE = 0,
 	CKSUM_SHA1 = 1,
-	CKSUM_MD5 = 2
+	CKSUM_MD5 = 2,
+	CKSUM_SHA256 = 3,
+	CKSUM_SHA512 = 4
 };
 
 #define MD5_SIZE	16
 #define SHA1_SIZE	20
-#define MAX_SUM_SIZE	20
+#define SHA256_SIZE	32
+#define SHA512_SIZE	64
+#define MAX_SUM_SIZE	64
 #define MD5_NAME	"md5"
 #define SHA1_NAME	"sha1"
+#define SHA256_NAME	"sha256"
+#define SHA512_NAME	"sha512"
 
 enum enctype {
 	NONE,
@@ -154,6 +160,12 @@
 #ifdef ARCHIVE_HAS_SHA1
 	archive_sha1_ctx	 sha1ctx;
 #endif
+#ifdef ARCHIVE_HAS_SHA256
+	archive_sha256_ctx	 sha256ctx;
+#endif
+#ifdef ARCHIVE_HAS_SHA512
+	archive_sha512_ctx	 sha512ctx;
+#endif
 };
 
 enum la_zaction {
@@ -366,8 +378,7 @@
 	    ARCHIVE_STATE_NEW, "archive_write_set_format_xar");
 
 	/* If another format was already registered, unregister it. */
-	if (a->format_free != NULL)
-		(a->format_free)(a);
+	(void)__archive_write_unregister_format(a);
 
 	xar = calloc(1, sizeof(*xar));
 	if (xar == NULL) {
@@ -380,22 +391,19 @@
 	file_init_hardlinks(xar);
 	archive_string_init(&(xar->tstr));
 	archive_string_init(&(xar->vstr));
+	archive_string_init(&(xar->cur_dirstr));
 
 	/*
 	 * Create the root directory.
 	 */
 	xar->root = file_create_virtual_dir(a, xar, "");
-	if (xar->root == NULL) {
-		free(xar);
-		archive_set_error(&a->archive, ENOMEM,
-		    "Can't allocate xar data");
-		return (ARCHIVE_FATAL);
-	}
+	if (xar->root == NULL)
+		goto memerr;
 	xar->root->parent = xar->root;
 	file_register(xar, xar->root);
 	xar->cur_dirent = xar->root;
-	archive_string_init(&(xar->cur_dirstr));
-	archive_string_ensure(&(xar->cur_dirstr), 1);
+	if (archive_string_ensure(&(xar->cur_dirstr), 1) == NULL)
+		goto memerr;
 	xar->cur_dirstr.s[0] = 0;
 
 	/*
@@ -422,14 +430,23 @@
 	a->archive.archive_format_name = "xar";
 
 	return (ARCHIVE_OK);
+
+memerr:
+	archive_string_free(&(xar->cur_dirstr));
+	archive_string_free(&(xar->tstr));
+	archive_string_free(&(xar->vstr));
+	file_free_hardlinks(xar);
+	file_free_register(xar);
+	free(xar);
+	archive_set_error(&a->archive, ENOMEM,
+	    "Can't allocate xar data");
+	return (ARCHIVE_FATAL);
 }
 
 static int
 xar_options(struct archive_write *a, const char *key, const char *value)
 {
-	struct xar *xar;
-
-	xar = (struct xar *)a->format_data;
+	struct xar *xar = a->format_data;
 
 	if (strcmp(key, "checksum") == 0) {
 		if (value == NULL)
@@ -440,6 +457,14 @@
 			xar->opt_sumalg = CKSUM_SHA1;
 		else if (strcmp(value, "md5") == 0)
 			xar->opt_sumalg = CKSUM_MD5;
+#ifdef ARCHIVE_HAS_SHA256
+		else if (strcmp(value, "sha256") == 0)
+			xar->opt_sumalg = CKSUM_SHA256;
+#endif
+#ifdef ARCHIVE_HAS_SHA512
+		else if (strcmp(value, "sha512") == 0)
+			xar->opt_sumalg = CKSUM_SHA512;
+#endif
 		else {
 			archive_set_error(&(a->archive),
 			    ARCHIVE_ERRNO_MISC,
@@ -515,6 +540,14 @@
 			xar->opt_toc_sumalg = CKSUM_SHA1;
 		else if (strcmp(value, "md5") == 0)
 			xar->opt_toc_sumalg = CKSUM_MD5;
+#ifdef ARCHIVE_HAS_SHA256
+		else if (strcmp(value, "sha256") == 0)
+			xar->opt_toc_sumalg = CKSUM_SHA256;
+#endif
+#ifdef ARCHIVE_HAS_SHA512
+		else if (strcmp(value, "sha512") == 0)
+			xar->opt_toc_sumalg = CKSUM_SHA512;
+#endif
 		else {
 			archive_set_error(&(a->archive),
 			    ARCHIVE_ERRNO_MISC,
@@ -559,12 +592,11 @@
 static int
 xar_write_header(struct archive_write *a, struct archive_entry *entry)
 {
-	struct xar *xar;
+	struct xar *xar = a->format_data;
 	struct file *file;
 	struct archive_entry *file_entry;
 	int r, r2;
 
-	xar = (struct xar *)a->format_data;
 	xar->cur_file = NULL;
 	xar->bytes_remaining = 0;
 
@@ -582,8 +614,10 @@
 		return (ARCHIVE_FATAL);
 	}
 	r2 = file_gen_utility_names(a, file);
-	if (r2 < ARCHIVE_WARN)
+	if (r2 < ARCHIVE_WARN) {
+		file_free(file);
 		return (r2);
+	}
 
 	/*
 	 * Ignore a path which looks like the top of directory name
@@ -672,20 +706,25 @@
 	checksum_init(&(xar->e_sumwrk), xar->opt_sumalg);
 	r = xar_compression_init_encoder(a);
 
-	if (r != ARCHIVE_OK)
+	if (r != ARCHIVE_OK) {
+		struct chksumval sumval;
+
+		checksum_final(&(xar->a_sumwrk), &sumval);
+		checksum_final(&(xar->e_sumwrk), &sumval);
+		xar->a_sumwrk.alg = CKSUM_NONE;
+		xar->e_sumwrk.alg = CKSUM_NONE;
 		return (r);
-	else
+	} else
 		return (r2);
 }
 
 static int
 write_to_temp(struct archive_write *a, const void *buff, size_t s)
 {
-	struct xar *xar;
+	struct xar *xar = a->format_data;
 	const unsigned char *p;
 	ssize_t ws;
 
-	xar = (struct xar *)a->format_data;
 	p = (const unsigned char *)buff;
 	while (s) {
 		ws = write(xar->temp_fd, p, s);
@@ -704,14 +743,12 @@
 static ssize_t
 xar_write_data(struct archive_write *a, const void *buff, size_t s)
 {
-	struct xar *xar;
+	struct xar *xar = a->format_data;
 	enum la_zaction run;
 	size_t size = 0;
 	size_t rsize;
 	int r;
 
-	xar = (struct xar *)a->format_data;
-
 	if (s > xar->bytes_remaining)
 		s = (size_t)xar->bytes_remaining;
 	if (s == 0 || xar->cur_file == NULL)
@@ -805,12 +842,11 @@
 static int
 xar_finish_entry(struct archive_write *a)
 {
-	struct xar *xar;
+	struct xar *xar = a->format_data;
 	struct file *file;
 	size_t s;
 	ssize_t w;
 
-	xar = (struct xar *)a->format_data;
 	if (xar->cur_file == NULL)
 		return (ARCHIVE_OK);
 
@@ -907,14 +943,13 @@
 	return (ARCHIVE_OK);
 }
 
-static int
+static int __LA_PRINTF(4, 5)
 xmlwrite_fstring(struct archive_write *a, struct xml_writer *writer,
 	const char *key, const char *fmt, ...)
 {
-	struct xar *xar;
+	struct xar *xar = a->format_data;
 	va_list ap;
 
-	xar = (struct xar *)a->format_data;
 	va_start(ap, fmt);
 	archive_string_empty(&xar->vstr);
 	archive_string_vsprintf(&xar->vstr, fmt, ap);
@@ -1202,7 +1237,7 @@
 make_file_entry(struct archive_write *a, struct xml_writer *writer,
     struct file *file)
 {
-	struct xar *xar;
+	struct xar *xar = a->format_data;
 	const char *filetype, *filelink, *fflags;
 	struct archive_string linkto;
 	struct heap_data *heap;
@@ -1210,7 +1245,6 @@
 	size_t len;
 	int r, r2;
 
-	xar = (struct xar *)a->format_data;
 	r2 = ARCHIVE_OK;
 
 	/*
@@ -1333,11 +1367,11 @@
 			return (ARCHIVE_FATAL);
 		}
 		r = xmlwrite_fstring(a, writer, "major",
-		    "%d", archive_entry_rdevmajor(file->entry));
+		    "%ld", (long)archive_entry_rdevmajor(file->entry));
 		if (r < 0)
 			return (ARCHIVE_FATAL);
 		r = xmlwrite_fstring(a, writer, "minor",
-		    "%d", archive_entry_rdevminor(file->entry));
+		    "%ld", (long)archive_entry_rdevminor(file->entry));
 		if (r < 0)
 			return (ARCHIVE_FATAL);
 		r = xml_writer_end_element(writer);
@@ -1361,7 +1395,7 @@
 		return (ARCHIVE_FATAL);
 	if (archive_entry_dev(file->entry) != 0) {
 		r = xmlwrite_fstring(a, writer, "deviceno",
-		    "%d", archive_entry_dev(file->entry));
+		    "%ld", (long)archive_entry_dev(file->entry));
 		if (r < 0)
 			return (ARCHIVE_FATAL);
 	}
@@ -1572,7 +1606,7 @@
 static int
 make_toc(struct archive_write *a)
 {
-	struct xar *xar;
+	struct xar *xar = a->format_data;
 	struct file *np;
 	struct xml_writer *writer;
 	const char* content;
@@ -1580,8 +1614,6 @@
 	int algsize;
 	int r, ret;
 
-	xar = (struct xar *)a->format_data;
-
 	ret = ARCHIVE_FATAL;
 
 	/*
@@ -1822,11 +1854,10 @@
 static int
 flush_wbuff(struct archive_write *a)
 {
-	struct xar *xar;
+	struct xar *xar = a->format_data;
 	int r;
 	size_t s;
 
-	xar = (struct xar *)a->format_data;
 	s = sizeof(xar->wbuff) - xar->wbuff_remaining;
 	r = __archive_write_output(a, xar->wbuff, s);
 	if (r != ARCHIVE_OK)
@@ -1838,10 +1869,9 @@
 static int
 copy_out(struct archive_write *a, uint64_t offset, uint64_t length)
 {
-	struct xar *xar;
+	struct xar *xar = a->format_data;
 	int r;
 
-	xar = (struct xar *)a->format_data;
 	if (lseek(xar->temp_fd, offset, SEEK_SET) < 0) {
 		archive_set_error(&(a->archive), errno, "lseek failed");
 		return (ARCHIVE_FATAL);
@@ -1883,13 +1913,11 @@
 static int
 xar_close(struct archive_write *a)
 {
-	struct xar *xar;
+	struct xar *xar = a->format_data;
 	unsigned char *wb;
 	uint64_t length;
 	int r;
 
-	xar = (struct xar *)a->format_data;
-
 	/* Empty! */
 	if (xar->root->children.first == NULL)
 		return (ARCHIVE_OK);
@@ -1949,9 +1977,7 @@
 static int
 xar_free(struct archive_write *a)
 {
-	struct xar *xar;
-
-	xar = (struct xar *)a->format_data;
+	struct xar *xar = a->format_data;
 
 	/* Close the temporary file. */
 	if (xar->temp_fd >= 0)
@@ -2109,13 +2135,12 @@
 static int
 file_gen_utility_names(struct archive_write *a, struct file *file)
 {
-	struct xar *xar;
+	struct xar *xar = a->format_data;
 	const char *pp;
 	char *p, *dirname, *slash;
 	size_t len;
 	int r = ARCHIVE_OK;
 
-	xar = (struct xar *)a->format_data;
 	archive_string_empty(&(file->parentdir));
 	archive_string_empty(&(file->basename));
 	archive_string_empty(&(file->symlink));
@@ -2303,6 +2328,7 @@
 static int
 file_tree(struct archive_write *a, struct file **filepp)
 {
+	struct xar *xar = a->format_data;
 #if defined(_WIN32) && !defined(__CYGWIN__)
 	char name[_MAX_FNAME];/* Included null terminator size. */
 #elif defined(NAME_MAX) && NAME_MAX >= 255
@@ -2310,7 +2336,6 @@
 #else
 	char name[256];
 #endif
-	struct xar *xar = (struct xar *)a->format_data;
 	struct file *dent, *file, *np;
 	struct archive_entry *ent;
 	const char *fn, *p;
@@ -2414,8 +2439,12 @@
 				return (ARCHIVE_FATAL);
 			}
 			archive_string_free(&as);
-			if (file_gen_utility_names(a, vp) <= ARCHIVE_FAILED)
+			if (file_gen_utility_names(a, vp) <= ARCHIVE_FAILED) {
+				file_free(vp);
+				file_free(file);
+				*filepp = NULL;
 				return (ARCHIVE_FATAL);
+			}
 			file_add_child_tail(dent, vp);
 			file_register(xar, vp);
 			np = vp;
@@ -2526,7 +2555,7 @@
 static int
 file_register_hardlink(struct archive_write *a, struct file *file)
 {
-	struct xar *xar = (struct xar *)a->format_data;
+	struct xar *xar = a->format_data;
 	struct hardlink *hl;
 	const char *pathname;
 
@@ -2648,6 +2677,16 @@
 	case CKSUM_MD5:
 		archive_md5_init(&(sumwrk->md5ctx));
 		break;
+#ifdef ARCHIVE_HAS_SHA256
+	case CKSUM_SHA256:
+		archive_sha256_init(&(sumwrk->sha256ctx));
+		break;
+#endif
+#ifdef ARCHIVE_HAS_SHA512
+	case CKSUM_SHA512:
+		archive_sha512_init(&(sumwrk->sha512ctx));
+		break;
+#endif
 	}
 }
 
@@ -2664,6 +2703,16 @@
 	case CKSUM_MD5:
 		archive_md5_update(&(sumwrk->md5ctx), buff, size);
 		break;
+#ifdef ARCHIVE_HAS_SHA256
+	case CKSUM_SHA256:
+		archive_sha256_update(&(sumwrk->sha256ctx), buff, size);
+		break;
+#endif
+#ifdef ARCHIVE_HAS_SHA512
+	case CKSUM_SHA512:
+		archive_sha512_update(&(sumwrk->sha512ctx), buff, size);
+		break;
+#endif
 	}
 }
 
@@ -2683,6 +2732,18 @@
 		archive_md5_final(&(sumwrk->md5ctx), sumval->val);
 		sumval->len = MD5_SIZE;
 		break;
+#ifdef ARCHIVE_HAS_SHA256
+	case CKSUM_SHA256:
+		archive_sha256_final(&(sumwrk->sha256ctx), sumval->val);
+		sumval->len = SHA256_SIZE;
+		break;
+#endif
+#ifdef ARCHIVE_HAS_SHA512
+	case CKSUM_SHA512:
+		archive_sha512_final(&(sumwrk->sha512ctx), sumval->val);
+		sumval->len = SHA512_SIZE;
+		break;
+#endif
 	}
 	sumval->alg = sumwrk->alg;
 }
@@ -3132,10 +3193,9 @@
 static int
 xar_compression_init_encoder(struct archive_write *a)
 {
-	struct xar *xar;
+	struct xar *xar = a->format_data;
 	int r;
 
-	xar = (struct xar *)a->format_data;
 	switch (xar->opt_compression) {
 	case GZIP:
 		r = compression_init_encoder_gzip(
@@ -3192,14 +3252,13 @@
 static int
 save_xattrs(struct archive_write *a, struct file *file)
 {
-	struct xar *xar;
+	struct xar *xar = a->format_data;
 	const char *name;
 	const void *value;
 	struct heap_data *heap;
 	size_t size;
 	int count, r;
 
-	xar = (struct xar *)a->format_data;
 	count = archive_entry_xattr_reset(file->entry);
 	if (count == 0)
 		return (ARCHIVE_OK);
@@ -3211,6 +3270,12 @@
 
 		heap = calloc(1, sizeof(*heap));
 		if (heap == NULL) {
+			struct chksumval sumval;
+
+			checksum_final(&(xar->a_sumwrk), &sumval);
+			checksum_final(&(xar->e_sumwrk), &sumval);
+			xar->a_sumwrk.alg = CKSUM_NONE;
+			xar->e_sumwrk.alg = CKSUM_NONE;
 			archive_set_error(&a->archive, ENOMEM,
 			    "Can't allocate memory for xattr");
 			return (ARCHIVE_FATAL);
@@ -3248,6 +3313,10 @@
 		 */
 		r = xar_compression_init_encoder(a);
 		if (r != ARCHIVE_OK) {
+			struct chksumval sumval;
+
+			checksum_final(&(xar->a_sumwrk), &sumval);
+			xar->a_sumwrk.alg = CKSUM_NONE;
 			free(heap);
 			return (ARCHIVE_FATAL);
 		}
@@ -3302,6 +3371,10 @@
 		return (SHA1_SIZE);
 	case CKSUM_MD5:
 		return (MD5_SIZE);
+	case CKSUM_SHA256:
+		return (SHA256_SIZE);
+	case CKSUM_SHA512:
+		return (SHA512_SIZE);
 	}
 }
 
@@ -3316,6 +3389,10 @@
 		return (SHA1_NAME);
 	case CKSUM_MD5:
 		return (MD5_NAME);
+	case CKSUM_SHA256:
+		return (SHA256_NAME);
+	case CKSUM_SHA512:
+		return (SHA512_NAME);
 	}
 }
 
diff --git a/c/archive_write_set_format_zip.c b/c/archive_write_set_format_zip.c
--- a/c/archive_write_set_format_zip.c
+++ b/c/archive_write_set_format_zip.c
@@ -68,6 +68,7 @@
 #include "archive_entry.h"
 #include "archive_entry_locale.h"
 #include "archive_hmac_private.h"
+#include "archive_integer.h"
 #include "archive_private.h"
 #include "archive_random_private.h"
 #include "archive_time_private.h"
@@ -154,7 +155,6 @@
 	enum compression entry_compression;
 	enum encryption  entry_encryption;
 	int entry_flags;
-	int experiments;
 	struct trad_enc_ctx tctx;
 	char tctx_valid;
 	unsigned char trad_chkdat;
@@ -235,7 +235,6 @@
 	      struct archive_entry *);
 static int archive_write_zip_options(struct archive_write *,
 	      const char *, const char *);
-static size_t path_length(struct archive_entry *);
 static int write_path(struct archive_entry *, struct archive_write *);
 static void copy_path(struct archive_entry *, unsigned char *);
 static struct archive_string_conv *get_sconv(struct archive_write *, struct zip *);
@@ -730,8 +729,7 @@
 	    ARCHIVE_STATE_NEW, "archive_write_set_format_zip");
 
 	/* If another format was already registered, unregister it. */
-	if (a->format_free != NULL)
-		(a->format_free)(a);
+	(void)__archive_write_unregister_format(a);
 
 	zip = calloc(1, sizeof(*zip));
 	if (zip == NULL) {
@@ -791,9 +789,9 @@
 static int
 archive_write_zip_header(struct archive_write *a, struct archive_entry *entry)
 {
+	struct zip *zip = a->format_data;
 	unsigned char local_header[32];
 	unsigned char local_extra[144];
-	struct zip *zip = a->format_data;
 	unsigned char *e;
 	unsigned char *cd_extra;
 	size_t filename_length;
@@ -959,8 +957,6 @@
 #endif
 		}
 	}
-	filename_length = path_length(zip->entry);
-
 	/* Reject empty or overlong pathnames */
 	path = archive_entry_pathname(zip->entry);
 	if (path == NULL || path[0] == '\0') {
@@ -968,6 +964,10 @@
 		    "ZIP format requires a non-empty pathname");
 		return (ARCHIVE_FAILED);
 	}
+	filename_length = strlen(path);
+	/* Include the trailing slash added to directories. */
+	if (type == AE_IFDIR && path[filename_length - 1] != '/')
+		filename_length++;
 	if (filename_length > 0xffff) {
 		archive_set_error(&a->archive, ENAMETOOLONG,
 		    "Pathname too long for ZIP format");
@@ -1066,8 +1066,13 @@
 			default:
 				break;
 			}
-			if (zip->entry_compression == COMPRESSION_STORE)
-				zip->entry_compressed_size += additional_size;
+			if (zip->entry_compression == COMPRESSION_STORE &&
+			    archive_ckd_add_i64(&zip->entry_compressed_size,
+				zip->entry_compressed_size, additional_size)) {
+				archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+				    "File size too large for encrypted ZIP entry");
+				return (ARCHIVE_FAILED);
+			}
 		}
 
 		/*
@@ -1081,7 +1086,7 @@
 		 *    (compression might make file larger)
 		 */
 		if ((zip->flags & ZIP_FLAG_FORCE_ZIP64)
-		    || (zip->entry_uncompressed_size + additional_size > ZIP_4GB_MAX)
+		    || (zip->entry_uncompressed_size > ZIP_4GB_MAX - additional_size)
 		    || (zip->entry_uncompressed_size > ZIP_4GB_MAX_UNCOMPRESSED
 			&& zip->entry_compression != COMPRESSION_STORE)) {
 			MIN_VERSION_NEEDED(45);
@@ -1515,8 +1520,8 @@
 static ssize_t
 archive_write_zip_data(struct archive_write *a, const void *buff, size_t s)
 {
-	int ret;
 	struct zip *zip = a->format_data;
+	int ret;
 
 	if ((int64_t)s > zip->entry_uncompressed_limit)
 		s = (size_t)zip->entry_uncompressed_limit;
@@ -2184,9 +2189,9 @@
 static int
 archive_write_zip_close(struct archive_write *a)
 {
+	struct zip *zip = a->format_data;
 	uint8_t buff[64];
 	int64_t offset_start, offset_end;
-	struct zip *zip = a->format_data;
 	struct cd_segment *segment;
 	int ret;
 
@@ -2256,10 +2261,9 @@
 static int
 archive_write_zip_free(struct archive_write *a)
 {
-	struct zip *zip;
+	struct zip *zip = a->format_data;
 	struct cd_segment *segment;
 
-	zip = a->format_data;
 	while (zip->central_directory != NULL) {
 		segment = zip->central_directory;
 		zip->central_directory = segment->next;
@@ -2277,24 +2281,6 @@
 	free(zip);
 	a->format_data = NULL;
 	return (ARCHIVE_OK);
-}
-
-static size_t
-path_length(struct archive_entry *entry)
-{
-	mode_t type;
-	const char *path;
-	size_t len;
-
-	type = archive_entry_filetype(entry);
-	path = archive_entry_pathname(entry);
-
-	if (path == NULL)
-		return (0);
-	len = strlen(path);
-	if (type == AE_IFDIR && (path[0] == '\0' || path[len - 1] != '/'))
-		++len; /* Space for the trailing / */
-	return len;
 }
 
 static int
diff --git a/libarchive-clib.cabal b/libarchive-clib.cabal
--- a/libarchive-clib.cabal
+++ b/libarchive-clib.cabal
@@ -2,7 +2,7 @@
 name:               libarchive-clib
 -- this is actually corresponding to upstream's 3.8.6,
 -- but we had to fix configure.ac and PVP has no patch versions
-version:            3.8.8
+version:            3.8.9
 license:            BSD-2-Clause
 license-file:       LICENSE
 copyright:          Copyright: (c) 2018-2020 Vanessa McHale
