New common unarchive code.
diff --git a/archival/ar.c b/archival/ar.c
index ac8c41f..b5fa795 100644
--- a/archival/ar.c
+++ b/archival/ar.c
@@ -21,41 +21,100 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  *
+ * There is no signle standard to adhere to so ar may not portable
+ * between different systems
+ * http://www.unix-systems.org/single_unix_specification_v2/xcu/ar.html
  */
+#include <sys/types.h>
 #include <fcntl.h>
+#include <fnmatch.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <time.h>
+#include <utime.h>
+#include <unistd.h>
 #include <getopt.h>
 #include "unarchive.h"
 #include "busybox.h"
 
+static void header_verbose_list_ar(const file_header_t *file_header)
+{
+	const char *mode = mode_string(file_header->mode);
+	char *mtime;
+
+	mtime = ctime(&file_header->mtime);
+	mtime[16] = ' ';
+	memmove(&mtime[17], &mtime[20], 4);
+	mtime[21] = '\0';
+	printf("%s %d/%d%7d %s %s\n", &mode[1], file_header->uid, file_header->gid, (int) file_header->size, &mtime[4], file_header->name);
+}
+
+#if defined CONFIG_TAR | defined CONFIG_DPKG_DEB | defined CONFIG_CPIO
+/* This is simpler than data_extract_all */
+static void data_extract_regular_file(archive_handle_t *archive_handle)
+{
+	file_header_t *file_header;
+	int dst_fd;
+
+	file_header = archive_handle->file_header;
+	dst_fd = xopen(file_header->name, O_WRONLY | O_CREAT);
+	copy_file_chunk_fd(archive_handle->src_fd, dst_fd, file_header->size);
+	close(dst_fd);
+
+	chmod(file_header->name, file_header->mode);
+	chown(file_header->name, file_header->uid, file_header->gid);
+
+	if (archive_handle->flags & ARCHIVE_PRESERVE_DATE) {
+		struct utimbuf t;
+		t.actime = t.modtime = file_header->mtime;
+		utime(file_header->name, &t);
+	}
+
+	return;
+}
+#endif
+
 extern int ar_main(int argc, char **argv)
 {
-	FILE *src_stream = NULL;
-	char **extract_names = NULL;
-	char ar_magic[8];
-	int extract_function =  extract_unconditional;
+	archive_handle_t *archive_handle;
 	int opt;
-	int num_of_entries = 0;
-	extern off_t archive_offset;
 
-	while ((opt = getopt(argc, argv, "ovtpx")) != -1) {
+#if defined CONFIG_TAR | defined CONFIG_DPKG_DEB | defined CONFIG_CPIO
+	archive_handle = init_handle();
+#else
+	char magic[8];
+
+	archive_handle = xcalloc(1, sizeof(archive_handle_t));
+	archive_handle->filter = filter_accept_all;
+	archive_handle->action_data = data_skip;
+	archive_handle->action_header = header_skip;
+	archive_handle->file_header =xmalloc(sizeof(file_header_t));
+#endif
+
+	while ((opt = getopt(argc, argv, "covtpxX")) != -1) {
 		switch (opt) {
-		case 'o':
-			extract_function |= extract_preserve_date;
+		case 'p':	/* print */
+			archive_handle->action_data = data_extract_to_stdout;
 			break;
-		case 'v':
-			extract_function |= extract_verbose_list;
+		case 't':	/* list contents */
+			archive_handle->action_header = header_list;
 			break;
-		case 't':
-			extract_function |= extract_list;
+		case 'X':
+			archive_handle->action_header = header_verbose_list_ar;
+		case 'x':	/* extract */
+#if defined CONFIG_TAR | defined CONFIG_DPKG_DEB | defined CONFIG_CPIO
+			archive_handle->action_data = data_extract_all;
+#else
+			archive_handle->action_data = data_extract_regular_file;
+#endif
 			break;
-		case 'p':
-			extract_function |= extract_to_stdout;
+		/* Modifiers */
+		case 'o':	/* preserve original dates */
+			archive_handle->flags |= ARCHIVE_PRESERVE_DATE;
 			break;
-		case 'x':
-			extract_function |= extract_all_to_fs;
+		case 'v':	/* verbose */
+			archive_handle->action_header = header_verbose_list_ar;
 			break;
 		default:
 			show_usage();
@@ -67,24 +126,26 @@
 		show_usage();
 	}
 
-	src_stream = xfopen(argv[optind++], "r");
+	archive_handle->src_fd = xopen(argv[optind++], O_RDONLY);
 
-	/* check ar magic */
-	fread(ar_magic, 1, 8, src_stream);
-	archive_offset = 8;
-	if (strncmp(ar_magic,"!<arch>",7) != 0) {
-		error_msg_and_die("invalid magic");
-	}
-
-	/* Create a list of files to extract */
+	/* TODO: This is the same as in tar, seperate function ? */
 	while (optind < argc) {
-		extract_names = xrealloc(extract_names, sizeof(char *) * (num_of_entries + 2));
-		extract_names[num_of_entries] = xstrdup(argv[optind]);
-		num_of_entries++;
-		extract_names[num_of_entries] = NULL;
+		archive_handle->filter = filter_accept_list;
+		archive_handle->accept = add_to_list(archive_handle->accept, argv[optind]);
 		optind++;
 	}
 
-	unarchive(src_stream, stdout, &get_header_ar, extract_function, "./", extract_names, NULL);
+#if defined CONFIG_DPKG_DEB
+	unpack_ar_archive(archive_handle);
+#else
+	xread_all(archive_handle->src_fd, magic, 7);
+	if (strncmp(magic, "!<arch>", 7) != 0) {
+		error_msg_and_die("Invalid ar magic");
+	}
+	archive_handle->offset += 7;
+
+	while (get_header_ar(archive_handle) == EXIT_SUCCESS);
+#endif
+
 	return EXIT_SUCCESS;
 }
diff --git a/archival/config.in b/archival/config.in
index 3de6715..dd97909 100644
--- a/archival/config.in
+++ b/archival/config.in
@@ -7,10 +7,16 @@
 comment 'Archival Utilities'
 
 bool 'ar'	    CONFIG_AR
+if [ "$CONFIG_AR" = "y" ] ; then
+    bool '  Enable support for long filenames (not need for debs)'	CONFIG_FEATURE_AR_LONG_FILENAMES
+fi
 bool 'bunzip2'	    CONFIG_BUNZIP2
 bool 'cpio'	    CONFIG_CPIO
 bool 'dpkg'	    CONFIG_DPKG
 bool 'dpkg_deb'	    CONFIG_DPKG_DEB
+if [ "$CONFIG_DPKG_DEB" = "y" ] ; then
+    bool '  -x support only'	CONFIG_FEATURE_DPKG_DEB_EXTRACT_ONLY
+fi
 bool 'gunzip'	    CONFIG_GUNZIP
 bool 'gzip'	    CONFIG_GZIP
 bool 'rpm2cpio'     CONFIG_RPM2CPIO
diff --git a/archival/cpio.c b/archival/cpio.c
index baaa72e..7615175 100644
--- a/archival/cpio.c
+++ b/archival/cpio.c
@@ -31,66 +31,210 @@
 #include "unarchive.h"
 #include "busybox.h"
 
+typedef struct hardlinks_s {
+	file_header_t *entry;
+	int inode;
+	struct hardlinks_s *next;
+} hardlinks_t;
+
 extern int cpio_main(int argc, char **argv)
 {
-	FILE *src_stream = stdin;
-	char **extract_names = NULL;
-	int extract_function = 0;
-	int num_of_entries = 0;
-	int opt = 0;
-	mode_t oldmask = 0;
+	archive_handle_t *archive_handle;
+	int opt;
+
+	/* Initialise */
+	archive_handle = init_handle();
+	archive_handle->src_fd = fileno(stdin);
+	archive_handle->action_header = header_list;
 
 	while ((opt = getopt(argc, argv, "idmuvtF:")) != -1) {
 		switch (opt) {
-		case 'i': // extract
-			extract_function |= extract_all_to_fs;
+		case 'i': /* extract */
+			archive_handle->action_data = data_extract_all;
 			break;
-		case 'd': // create _leading_ directories
-			extract_function |= extract_create_leading_dirs;
-			oldmask = umask(077); /* Make make_directory act like GNU cpio */
+		case 'd': /* create _leading_ directories */
+			archive_handle->flags |= ARCHIVE_CREATE_LEADING_DIRS;
 			break;
-		case 'm': // preserve modification time
-			extract_function |= extract_preserve_date;
+		case 'm': /* preserve modification time */
+			archive_handle->flags |= ARCHIVE_PRESERVE_DATE;
 			break;
-		case 'v': // verbosly list files
-			extract_function |= extract_verbose_list;
+		case 'v': /* verbosly list files */
+			archive_handle->action_header = header_verbose_list;
 			break;
-		case 'u': // unconditional
-			extract_function |= extract_unconditional;
+		case 'u': /* unconditional */
+			archive_handle->flags |= ARCHIVE_EXTRACT_UNCONDITIONAL;
 			break;
-		case 't': // list files
-			extract_function |= extract_list;
+		case 't': /* list files */
+			archive_handle->action_header = header_list;
 			break;
 		case 'F':
-			src_stream = xfopen(optarg, "r");
+			archive_handle->src_fd = xopen(optarg, O_RDONLY);
 			break;
 		default:
 			show_usage();
 		}
 	}
 
-	if ((extract_function & extract_all_to_fs) && (extract_function & extract_list)) {
-		extract_function ^= extract_all_to_fs; /* If specify t, don't extract*/
-	}
-
-	if ((extract_function & extract_all_to_fs) && (extract_function & extract_verbose_list)) {
-		/* The meaning of v changes on extract */
-		extract_function ^= extract_verbose_list;
-		extract_function |= extract_list;
-	}
-
 	while (optind < argc) {
-		extract_names = xrealloc(extract_names, sizeof(char *) * (num_of_entries + 2));
-		extract_names[num_of_entries] = xstrdup(argv[optind]);
-		num_of_entries++;
-		extract_names[num_of_entries] = NULL;
+		archive_handle->filter = filter_accept_list;
+		archive_handle->accept = add_to_list(archive_handle->accept, argv[optind]);
 		optind++;
 	}
 
-	unarchive(src_stream, stdout, &get_header_cpio, extract_function, "./", extract_names, NULL);
-	if (oldmask) {
-		umask(oldmask); /* Restore umask if we changed it */
+	while (1) {
+	 	static hardlinks_t *saved_hardlinks = NULL;
+ 		static unsigned short pending_hardlinks = 0;
+		file_header_t *file_header = archive_handle->file_header;
+		char cpio_header[110];
+		int namesize;
+ 		char dummy[16];
+ 		int major, minor, nlink, inode;
+		char extract_flag;
+
+		if (pending_hardlinks) { /* Deal with any pending hardlinks */
+			hardlinks_t *tmp;
+			hardlinks_t *oldtmp;
+
+			tmp = saved_hardlinks;
+			oldtmp = NULL;
+
+			while (tmp) {
+				error_msg_and_die("need to fix this\n");
+				if (tmp->entry->link_name) { /* Found a hardlink ready to be extracted */
+					file_header = tmp->entry;
+					if (oldtmp) {
+						oldtmp->next = tmp->next; /* Remove item from linked list */
+					} else {
+						saved_hardlinks = tmp->next;
+					}
+					free(tmp);
+					continue;
+				}
+				oldtmp = tmp;
+				tmp = tmp->next;
+			}
+			pending_hardlinks = 0; /* No more pending hardlinks, read next file entry */
 	}
-	return EXIT_SUCCESS;
+
+		/* There can be padding before archive header */
+		archive_handle->offset += data_align(archive_handle->src_fd, archive_handle->offset, 4);
+
+		if (xread_all_eof(archive_handle->src_fd, cpio_header, 110) == 0) {
+			return(EXIT_FAILURE);
+		}
+		archive_handle->offset += 110;
+
+		if (strncmp(&cpio_header[0], "07070", 5) != 0) {
+			printf("cpio header is %x-%x-%x-%x-%x\n",
+				cpio_header[0],
+				cpio_header[1],
+				cpio_header[2],
+				cpio_header[3],
+				cpio_header[4]);
+			error_msg_and_die("Unsupported cpio format");
+		}
+		
+		if ((cpio_header[5] != '1') && (cpio_header[5] != '2')) {
+			error_msg_and_die("Unsupported cpio format, use newc or crc");
+	}
+
+		sscanf(cpio_header, "%6c%8x%8x%8x%8x%8x%8lx%8lx%16c%8x%8x%8x%8c",
+			dummy, &inode, (unsigned int*)&file_header->mode, 
+			(unsigned int*)&file_header->uid, (unsigned int*)&file_header->gid,
+			&nlink, &file_header->mtime, &file_header->size,
+			dummy, &major, &minor, &namesize, dummy);
+
+		file_header->name = (char *) xmalloc(namesize + 1);
+		xread(archive_handle->src_fd, file_header->name, namesize); /* Read in filename */
+		file_header->name[namesize] = '\0';
+		archive_handle->offset += namesize;
+
+		/* Update offset amount and skip padding before file contents */
+		archive_handle->offset += data_align(archive_handle->src_fd, archive_handle->offset, 4);
+
+		if (strcmp(file_header->name, "TRAILER!!!") == 0) {
+			printf("%d blocks\n", (int) (archive_handle->offset % 512 ? (archive_handle->offset / 512) + 1 : archive_handle->offset / 512)); /* Always round up */
+			if (saved_hardlinks) { /* Bummer - we still have unresolved hardlinks */
+				hardlinks_t *tmp = saved_hardlinks;
+				hardlinks_t *oldtmp = NULL;
+				while (tmp) {
+					error_msg("%s not created: cannot resolve hardlink", tmp->entry->name);
+					oldtmp = tmp;
+					tmp = tmp->next;
+					free (oldtmp->entry->name);
+					free (oldtmp->entry);
+					free (oldtmp);
+				}
+				saved_hardlinks = NULL;
+				pending_hardlinks = 0;
+	}
+			return(EXIT_FAILURE);
+		}
+
+		if (S_ISLNK(file_header->mode)) {
+			file_header->link_name = (char *) xmalloc(file_header->size + 1);
+			xread(archive_handle->src_fd, file_header->link_name, file_header->size);
+			file_header->link_name[file_header->size] = '\0';
+			archive_handle->offset += file_header->size;
+			file_header->size = 0; /* Stop possible seeks in future */
+		}
+		if (nlink > 1 && !S_ISDIR(file_header->mode)) {
+			if (file_header->size == 0) { /* Put file on a linked list for later */
+				hardlinks_t *new = xmalloc(sizeof(hardlinks_t));
+				new->next = saved_hardlinks;
+				new->inode = inode;
+				new->entry = file_header;
+				saved_hardlinks = new;
+				continue;
+			} else { /* Found the file with data in */
+				hardlinks_t *tmp = saved_hardlinks;
+				pending_hardlinks = 1;
+				while (tmp) {
+					if (tmp->inode == inode) {
+						tmp->entry->link_name = xstrdup(file_header->name);
+						nlink--;
+					}
+					tmp = tmp->next;
+				}
+				if (nlink > 1) {
+					error_msg("error resolving hardlink: did you create the archive with GNU cpio 2.0-2.2?");
+				}
+			}
+		}
+		file_header->device = (major << 8) | minor;
+
+		extract_flag = FALSE;
+		if (archive_handle->filter(archive_handle->accept, archive_handle->reject, file_header->name) == EXIT_SUCCESS) {
+			struct stat statbuf;
+
+			extract_flag = TRUE;
+
+			/* Check if the file already exists */
+			if (lstat (file_header->name, &statbuf) == 0) {
+				if ((archive_handle->flags & ARCHIVE_EXTRACT_UNCONDITIONAL) || (statbuf.st_mtime < file_header->mtime)) {
+					/* Remove file if flag set or its older than the file to be extracted */
+					if (unlink(file_header->name) == -1) {
+						perror_msg_and_die("Couldnt remove old file");
+					}
+				} else {
+					if (! archive_handle->flags & ARCHIVE_EXTRACT_QUIET) {
+						error_msg("%s not created: newer or same age file exists", file_header->name);
+					}
+					extract_flag = FALSE;
+				}
+			}
+			archive_handle->action_header(file_header);
+		}
+
+		archive_handle->action_header(file_header);
+		if (extract_flag) {
+			archive_handle->action_data(archive_handle);
+		} else {
+			data_skip(archive_handle);
+		}
+		archive_handle->offset += file_header->size;
+	}
+
+	return(EXIT_SUCCESS);
 }
 
diff --git a/archival/dpkg_deb.c b/archival/dpkg_deb.c
index 29692f7..06a810c 100644
--- a/archival/dpkg_deb.c
+++ b/archival/dpkg_deb.c
@@ -13,119 +13,100 @@
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  */
-
+#include <fcntl.h>
 #include <stdlib.h>
 #include <string.h>
+#include <unistd.h>
 #include <getopt.h>
 #include "unarchive.h"
 #include "busybox.h"
 
 extern int dpkg_deb_main(int argc, char **argv)
 {
-	char *prefix = NULL;
-	char *filename = NULL;
-	char *output_buffer = NULL;
+	archive_handle_t *ar_archive;
+	archive_handle_t *tar_gz_archive;
 	int opt = 0;
-	int arg_type = 0;
-	int deb_extract_funct = extract_create_leading_dirs | extract_unconditional;	
+#ifndef CONFIG_FEATURE_DPKG_DEB_EXTRACT_ONLY
+	const llist_t *control_tar_gz_llist = add_to_list(NULL, "control.tar.gz");
+#endif
+#ifndef CONFIG_AR
+	char magic[7];
+#endif
 	
-	const int arg_type_prefix = 1;
-	const int arg_type_field = 2;
-	const int arg_type_filename = 4;
-//	const int arg_type_un_ar_gz = 8;
+	/* a .deb file is an ar archive that contain three files,
+	 * data.tar.gz, control.tar.gz and debian
+	 */
+	
+	/* Setup the tar archive handle */
+	tar_gz_archive = init_handle();
 
-	while ((opt = getopt(argc, argv, "ceftXxI")) != -1) {
+	/* Setup an ar archive handle that refers to the gzip sub archive */	
+	ar_archive = init_handle();
+	ar_archive->action_data_subarchive = get_header_tar_gz;
+	ar_archive->sub_archive = tar_gz_archive;
+	ar_archive->filter = filter_accept_list;
+	ar_archive->accept = add_to_list(NULL, "data.tar.gz");
+
+#ifndef CONFIG_FEATURE_DPKG_DEB_EXTRACT_ONLY
+	while ((opt = getopt(argc, argv, "cefXx")) != -1) {
+#else
+	while ((opt = getopt(argc, argv, "x")) != -1) {
+#endif
 		switch (opt) {
+#ifndef CONFIG_FEATURE_DPKG_DEB_EXTRACT_ONLY
 			case 'c':
-				deb_extract_funct |= extract_data_tar_gz;
-				deb_extract_funct |= extract_verbose_list;
+				tar_gz_archive->action_header = header_verbose_list;
 				break;
 			case 'e':
-				arg_type = arg_type_prefix;
-				deb_extract_funct |= extract_control_tar_gz;
-				deb_extract_funct |= extract_all_to_fs;
+				ar_archive->accept = control_tar_gz_llist;
+				tar_gz_archive->action_data = data_extract_all;
 				break;
 			case 'f':
-				arg_type = arg_type_field;
-				deb_extract_funct |= extract_control_tar_gz;
-				deb_extract_funct |= extract_one_to_buffer;
-				filename = xstrdup("./control");
-				break;
-			case 't': /* --fsys-tarfile, i just made up this short name */
-				/* Integrate the functionality needed with some code from ar.c */
-				error_msg_and_die("Option disabled");
-//				arg_type = arg_type_un_ar_gz;
+				/* Print the entire control file
+				 * it should accept a second argument which specifies a 
+				 * specific field to print */
+				ar_archive->accept = control_tar_gz_llist;
+				tar_gz_archive->accept = add_to_list(NULL, "./control");;
+				tar_gz_archive->filter = filter_accept_list;
+				tar_gz_archive->action_data = data_extract_to_stdout;
 				break;
 			case 'X':
-				arg_type = arg_type_prefix;
-				deb_extract_funct |= extract_data_tar_gz;
-				deb_extract_funct |= extract_all_to_fs;
-				deb_extract_funct |= extract_list;
+				tar_gz_archive->action_header = header_list;
+#endif
 			case 'x':
-				arg_type = arg_type_prefix;
-				deb_extract_funct |= extract_data_tar_gz;
-				deb_extract_funct |= extract_all_to_fs;
-				break;
-			case 'I':
-				arg_type = arg_type_filename;
-				deb_extract_funct |= extract_control_tar_gz;
-				deb_extract_funct |= extract_one_to_buffer;
+				tar_gz_archive->action_data = data_extract_all;
 				break;
 			default:
 				show_usage();
 		}
 	}
 
-	if (optind == argc)  {
+	if (optind + 2 < argc)  {
 		show_usage();
 	}
 
+	tar_gz_archive->src_fd = ar_archive->src_fd = xopen(argv[optind++], O_RDONLY);
+
 	/* Workout where to extract the files */
-	if (arg_type == arg_type_prefix) {
-		/* argument is a dir name */
-		if ((optind + 1) == argc ) {
-			prefix = xstrdup("./DEBIAN/");
-		} else {
-			prefix = (char *) xmalloc(strlen(argv[optind + 1]) + 2);
-			strcpy(prefix, argv[optind + 1]);
-			/* Make sure the directory has a trailing '/' */
-			if (last_char_is(prefix, '/') == NULL) {
-				strcat(prefix, "/");
-			}
-		}
-		mkdir(prefix, 0777);
-	}
+	/* 2nd argument is a dir name */
+	mkdir(argv[optind], 0777);
+	chdir(argv[optind]);
 
-	if (arg_type == arg_type_filename) {
-		if ((optind + 1) != argc) {
-			filename = xstrdup(argv[optind + 1]);
-		} else {
-			error_msg_and_die("-I currently requires a filename to be specified");
-		}
+#ifdef CONFIG_AR
+	unpack_ar_archive(ar_archive);
+#else
+	xread_all(ar_archive->src_fd, magic, 7);
+	if (strncmp(magic, "!<arch>", 7) != 0) {
+		error_msg_and_die("Invalid ar magic");
 	}
+	ar_archive->offset += 7;
 
-	output_buffer = deb_extract(argv[optind], stdout, deb_extract_funct, prefix, filename);
+	while (get_header_ar(ar_archive) == EXIT_SUCCESS);
+#endif
 
-	if ((arg_type == arg_type_filename) && (output_buffer != NULL)) {
-		puts(output_buffer);
-	}
-	else if (arg_type == arg_type_field) {
-		char *field = NULL;
-		char *name;
-		char *value;
-		int field_start = 0;
-
-		while (1) {
-			field_start += read_package_field(&output_buffer[field_start], &name, &value);
-			if (name == NULL) {
-				break;
-			}
-			if (strcmp(name, argv[optind + 1]) == 0) {
-				puts(value);
-			}
-			free(field);
-		}
-	}
+	/* Cleanup */
+	close (ar_archive->src_fd);
 
 	return(EXIT_SUCCESS);
 }
+
diff --git a/archival/gunzip.c b/archival/gunzip.c
index ac2fb17..4489204 100644
--- a/archival/gunzip.c
+++ b/archival/gunzip.c
@@ -64,7 +64,12 @@
 #include <string.h>
 #include <unistd.h>
 #include <getopt.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
 #include "busybox.h"
+#include "unarchive.h"
 
 const char gunzip_to_stdout = 1;
 const char gunzip_force = 2;
@@ -100,23 +105,20 @@
 	}
 
 	do {
-		FILE *in_file, *out_file;
 		struct stat stat_buf;
 		const char *old_path = argv[optind];
 		const char *delete_path = NULL;
 		char *new_path = NULL;
+		int src_fd;
+		int dst_fd;
 
 		optind++;
 
 		if (old_path == NULL || strcmp(old_path, "-") == 0) {
-			in_file = stdin;
+			src_fd = fileno(stdin);
 			flags |= gunzip_to_stdout;
 		} else {
-			in_file = wfopen(old_path, "r");
-			if (in_file == NULL) {
-				status = EXIT_FAILURE;
-				break;
-			}
+			src_fd = xopen(old_path, O_RDONLY);
 
 			/* Get the time stamp on the input file. */
 			if (stat(old_path, &stat_buf) < 0) {
@@ -125,16 +127,16 @@
 		}
 
 		/* Check that the input is sane.  */
-		if (isatty(fileno(in_file)) && ((flags & gunzip_force) == 0)) {
+		if (isatty(src_fd) && ((flags & gunzip_force) == 0)) {
 			error_msg_and_die
 				("compressed data not read from terminal.  Use -f to force it.");
 		}
 
 		/* Set output filename and number */
 		if (flags & gunzip_test) {
-			out_file = xfopen("/dev/null", "w");	/* why does test use filenum 2 ? */
+			dst_fd = xopen("/dev/null", O_WRONLY);	/* why does test use filenum 2 ? */
 		} else if (flags & gunzip_to_stdout) {
-			out_file = stdout;
+			dst_fd = fileno(stdout);
 		} else {
 			char *extension;
 
@@ -151,7 +153,7 @@
 			}
 
 			/* Open output file */
-			out_file = xfopen(new_path, "w");
+			dst_fd = xopen(new_path, O_WRONLY | O_CREAT);
 
 			/* Set permissions on the file */
 			chmod(new_path, stat_buf.st_mode);
@@ -161,16 +163,22 @@
 		}
 
 		/* do the decompression, and cleanup */
-		if ((unzip(in_file, out_file) != 0) && (new_path)) {
+		check_header_gzip(src_fd);
+		if (inflate(src_fd, dst_fd) != 0) {
+			error_msg("Error inflating");
+		}
+		check_trailer_gzip(src_fd);
+
+		if ((status != EXIT_SUCCESS) && (new_path)) {
 			/* Unzip failed, remove new path instead of old path */
 			delete_path = new_path;
 		}
 
-		if (out_file != stdout) {
-			fclose(out_file);
+		if (dst_fd != fileno(stdout)) {
+			close(dst_fd);
 		}
-		if (in_file != stdin) {
-			fclose(in_file);
+		if (src_fd != fileno(stdin)) {
+			close(src_fd);
 		}
 
 		/* delete_path will be NULL if in test mode or from stdin */
diff --git a/archival/libunarchive/Makefile.in b/archival/libunarchive/Makefile.in
index cd68be7..e2ac546 100644
--- a/archival/libunarchive/Makefile.in
+++ b/archival/libunarchive/Makefile.in
@@ -22,14 +22,41 @@
 LIBUNARCHIVE_DIR:=$(TOPDIR)archival/libunarchive/
 endif
 
-LIBUNARCHIVE-y:=unarchive.o seek_sub_file.o
-LIBUNARCHIVE-$(CONFIG_DPKG) += deb_extract.o get_header_ar.o get_header_tar.o
-LIBUNARCHIVE-$(CONFIG_DPKG_DEB) += deb_extract.o get_header_ar.o get_header_tar.o
-LIBUNARCHIVE-$(CONFIG_AR) += get_header_ar.o
-LIBUNARCHIVE-$(CONFIG_CPIO) += get_header_cpio.o
-LIBUNARCHIVE-$(CONFIG_RPM2CPIO) += get_header_cpio.o
-LIBUNARCHIVE-$(CONFIG_TAR) += get_header_tar.o
-LIBUNARCHIVE-$(CONFIG_UNZIP) += get_header_zip.o
+LIBUNARCHIVE-y:= \
+\
+	data_skip.o \
+	data_extract_all.o \
+	data_extract_to_stdout.o \
+\
+	filter_accept_all.o \
+	filter_accept_list.o \
+	filter_accept_reject_list.o \
+\
+	get_header_ar.o \
+	get_header_tar.o \
+	get_header_tar_gz.o \
+\
+	header_skip.o \
+	header_list.o \
+	header_verbose_list.o \
+\
+	add_to_list.o \
+	check_header_gzip.o \
+	check_trailer_gzip.o \
+	copy_file_chunk_fd.o \
+	data_align.o \
+	init_handle.o \
+	seek_sub_file.o \
+	unpack_ar_archive.o \
+
+LIBUNARCHIVE-$(CONFIG_DPKG) += 
+LIBUNARCHIVE-$(CONFIG_DPKG_DEB) += 
+LIBUNARCHIVE-$(CONFIG_AR) += 
+LIBUNARCHIVE-$(CONFIG_CPIO) += 
+LIBUNARCHIVE-$(CONFIG_GUNZIP) += 
+LIBUNARCHIVE-$(CONFIG_RPM2CPIO) += 
+LIBUNARCHIVE-$(CONFIG_TAR) += 
+LIBUNARCHIVE-$(CONFIG_UNZIP) += 
 
 libraries-y+=$(LIBUNARCHIVE_DIR)$(LIBUNARCHIVE_AR)
 
diff --git a/archival/libunarchive/add_to_list.c b/archival/libunarchive/add_to_list.c
new file mode 100644
index 0000000..052bca3
--- /dev/null
+++ b/archival/libunarchive/add_to_list.c
@@ -0,0 +1,15 @@
+#include <stdlib.h>
+#include <string.h>
+#include "unarchive.h"
+#include "libbb.h"
+
+extern const llist_t *add_to_list(const llist_t *old_head, const char *new_item)
+{
+	llist_t *new_head;
+
+	new_head = xmalloc(sizeof(llist_t));
+	new_head->data = new_item;
+	new_head->link = old_head;
+
+	return(new_head);
+}
diff --git a/archival/libunarchive/check_header_gzip.c b/archival/libunarchive/check_header_gzip.c
new file mode 100644
index 0000000..508d309
--- /dev/null
+++ b/archival/libunarchive/check_header_gzip.c
@@ -0,0 +1,75 @@
+#include <stdlib.h>
+#include <unistd.h>
+#include "libbb.h"
+
+extern void check_header_gzip(int src_fd)
+{
+	union {
+		unsigned char raw[10];
+		struct {
+			unsigned char magic[2];
+			unsigned char method;
+			unsigned char flags;
+			unsigned int mtime;
+			unsigned char xtra_flags;
+			unsigned char os_flags;
+		} formated;
+	} header;
+
+	xread_all(src_fd, header.raw, 10);
+
+   /* Magic header for gzip files, 1F 8B = \037\213 */
+	if ((header.formated.magic[0] != 0x1F)
+		|| (header.formated.magic[1] != 0x8b)) {
+		error_msg_and_die("Invalid gzip magic");
+	}
+
+   /* Check the compression method */
+	if (header.formated.method != 8) {
+		error_msg_and_die("Unknown compression method %d",
+						  header.formated.method);
+	}
+
+	if (header.formated.flags & 0x04) {
+		/* bit 2 set: extra field present */
+		unsigned char extra_short;
+
+		extra_short = xread_char(src_fd);
+		extra_short += xread_char(src_fd) << 8;
+		while (extra_short > 0) {
+		   /* Ignore extra field */
+			xread_char(src_fd);
+			extra_short--;
+		}
+	}
+
+   /* Discard original name if any */
+	if (header.formated.flags & 0x08) {
+	   /* bit 3 set: original file name present */
+		char tmp;
+
+		do {
+			read(src_fd, &tmp, 1);
+		} while (tmp != 0);
+	}
+
+   /* Discard file comment if any */
+	if (header.formated.flags & 0x10) {
+	   /* bit 4 set: file comment present */
+		char tmp;
+
+		do {
+			read(src_fd, &tmp, 1);
+		} while (tmp != 0);
+	}
+
+   /* Read the header checksum */
+	if (header.formated.flags & 0x02) {
+		char tmp;
+
+		read(src_fd, &tmp, 1);
+		read(src_fd, &tmp, 1);
+	}
+
+	return;
+}
diff --git a/archival/libunarchive/check_trailer_gzip.c b/archival/libunarchive/check_trailer_gzip.c
new file mode 100644
index 0000000..215e829
--- /dev/null
+++ b/archival/libunarchive/check_trailer_gzip.c
@@ -0,0 +1,62 @@
+/* vi: set sw=4 ts=4: */
+/*
+ *  busybox gunzip trailing header handler
+ *  Copyright Glenn McGrath 2002
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include "config.h"
+#include "busybox.h"
+#include "unarchive.h"
+
+extern unsigned int gunzip_crc;
+extern unsigned int gunzip_bytes_out;
+extern unsigned char *gunzip_in_buffer;
+extern unsigned char gunzip_in_buffer_count;
+
+extern void check_trailer_gzip(int src_fd)
+{
+
+	unsigned int stored_crc = 0;
+	unsigned char count;
+
+	/* top up the input buffer with the rest of the trailer */
+	xread_all(src_fd, &gunzip_in_buffer[gunzip_in_buffer_count], 8 - gunzip_in_buffer_count);
+	for (count = 0; count != 4; count++) {
+		stored_crc |= (gunzip_in_buffer[count] << (count * 8));
+	}
+
+	/* Validate decompression - crc */
+	if (stored_crc != (gunzip_crc ^ 0xffffffffL)) {
+		error_msg("invalid compressed data--crc error");
+	}
+
+	/* Validate decompression - size */
+	if (gunzip_bytes_out != 
+		(gunzip_in_buffer[4] | (gunzip_in_buffer[5] << 8) |
+		(gunzip_in_buffer[6] << 16) | (gunzip_in_buffer[7] << 24))) {
+		error_msg("invalid compressed data--length error");
+	}
+
+}
diff --git a/archival/libunarchive/copy_file_chunk_fd.c b/archival/libunarchive/copy_file_chunk_fd.c
new file mode 100644
index 0000000..fb513e6
--- /dev/null
+++ b/archival/libunarchive/copy_file_chunk_fd.c
@@ -0,0 +1,33 @@
+#include <unistd.h>
+#include <sys/types.h>
+#include "libbb.h"
+
+/* Copy CHUNKSIZE bytes (or untill EOF if chunksize == -1)
+ * from SRC_FILE to DST_FILE. */
+extern int copy_file_chunk_fd(int src_fd, int dst_fd, off_t chunksize)
+{
+	size_t nread, size;
+	char buffer[BUFSIZ];
+
+	while (chunksize != 0) {
+		if (chunksize > BUFSIZ) {
+			size = BUFSIZ;
+		} else {
+			size = chunksize;
+		}
+		nread = xread(src_fd, buffer, size);
+		if (nread == 0) {
+			return 1;
+		}
+
+		if (write (dst_fd, buffer, nread) != nread) {
+			error_msg_and_die ("Short write");
+		}
+
+		if (chunksize != -1) {
+			chunksize -= nread;
+		}
+	}
+
+	return 0;
+}
diff --git a/archival/libunarchive/data_align.c b/archival/libunarchive/data_align.c
new file mode 100644
index 0000000..d6243bc
--- /dev/null
+++ b/archival/libunarchive/data_align.c
@@ -0,0 +1,13 @@
+#include <errno.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include "unarchive.h"
+#include "libbb.h"
+
+extern const unsigned short data_align(const int src_fd, const unsigned int offset, const unsigned short align_to)
+{
+	const unsigned short skip_amount = (align_to - (offset % align_to)) % align_to;
+	seek_sub_file(src_fd, skip_amount);
+
+	return(skip_amount);
+}
diff --git a/archival/libunarchive/data_extract_all.c b/archival/libunarchive/data_extract_all.c
new file mode 100644
index 0000000..20d99aa
--- /dev/null
+++ b/archival/libunarchive/data_extract_all.c
@@ -0,0 +1,76 @@
+#include <sys/types.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <utime.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include "libbb.h"
+#include "unarchive.h"
+
+extern void data_extract_all(archive_handle_t *archive_handle)
+{
+	file_header_t *file_header = archive_handle->file_header;
+	int dst_fd;
+	int res;
+
+	if (archive_handle->flags & ARCHIVE_CREATE_LEADING_DIRS) {
+		char *dir = dirname(strdup(file_header->name));
+		make_directory (dir, 0777, FILEUTILS_RECUR);
+		free(dir);
+	}
+
+	/* Create the file */
+	switch(file_header->mode & S_IFMT) {
+		case S_IFREG: {
+#ifdef CONFIG_CPIO
+			if (file_header->link_name) {
+				/* hard link */
+				res = link(file_header->link_name, file_header->name);
+				if ((res == -1) && !(archive_handle->flags & ARCHIVE_EXTRACT_QUIET)) {
+					perror_msg("Couldnt create hard link");
+				}
+			} else
+#endif
+			{
+				/* Regular file */
+				dst_fd = xopen(file_header->name, O_WRONLY | O_CREAT);
+				copy_file_chunk_fd(archive_handle->src_fd, dst_fd, file_header->size);
+				close(dst_fd);
+			}
+			break;
+		}
+		case S_IFDIR:
+			res = mkdir(file_header->name, file_header->mode);
+			if ((res == -1) && !(archive_handle->flags & ARCHIVE_EXTRACT_QUIET)) {
+				perror_msg("extract_archive: %s", file_header->name);
+			}
+			break;
+		case S_IFLNK:
+			/* Symlink */
+			res = symlink(file_header->link_name, file_header->name);
+			if ((res == -1) && !(archive_handle->flags & ARCHIVE_EXTRACT_QUIET)) {
+				perror_msg("Cannot create symlink from %s to '%s'", file_header->name, file_header->link_name);
+			}
+			break;
+		case S_IFSOCK:
+		case S_IFBLK:
+		case S_IFCHR:
+		case S_IFIFO:
+			res = mknod(file_header->name, file_header->mode, file_header->device);
+			if ((res == -1) && !(archive_handle->flags & ARCHIVE_EXTRACT_QUIET)) {
+				perror_msg("Cannot create node %s", file_header->name);
+			}
+			break;
+	}
+
+	chmod(file_header->name, file_header->mode);
+	chown(file_header->name, file_header->uid, file_header->gid);
+
+	if (archive_handle->flags & ARCHIVE_PRESERVE_DATE) {
+		struct utimbuf t;
+		t.actime = t.modtime = file_header->mtime;
+		utime(file_header->name, &t);
+	}
+}
diff --git a/archival/libunarchive/data_extract_to_stdout.c b/archival/libunarchive/data_extract_to_stdout.c
new file mode 100644
index 0000000..00687b3
--- /dev/null
+++ b/archival/libunarchive/data_extract_to_stdout.c
@@ -0,0 +1,8 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include "unarchive.h"
+
+extern void data_extract_to_stdout(archive_handle_t *archive_handle)
+{
+	copy_file_chunk_fd(archive_handle->src_fd, fileno(stdout), archive_handle->file_header->size);
+}
diff --git a/archival/libunarchive/data_skip.c b/archival/libunarchive/data_skip.c
new file mode 100644
index 0000000..4e63d43
--- /dev/null
+++ b/archival/libunarchive/data_skip.c
@@ -0,0 +1,27 @@
+/*
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <sys/types.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include "unarchive.h"
+#include "libbb.h"
+
+extern void data_skip(archive_handle_t *archive_handle)
+{
+	seek_sub_file(archive_handle->src_fd, archive_handle->file_header->size);
+}
diff --git a/archival/libunarchive/decompress_unzip.c b/archival/libunarchive/decompress_unzip.c
index d840670..20bf88f 100644
--- a/archival/libunarchive/decompress_unzip.c
+++ b/archival/libunarchive/decompress_unzip.c
@@ -65,40 +65,11 @@
 #include <signal.h>
 #include <stdlib.h>
 #include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
 #include "config.h"
-#include "libbb.h"
-
-#ifdef CONFIG_FEATURE_UNCOMPRESS
-int uncompress(FILE * in, FILE * out);
-#endif
-
-static FILE *in_file, *out_file;
-
-/* these are freed by gz_close */
-static unsigned char *window;
-static unsigned long *crc_table;
-
-static unsigned long crc;	/* shift register contents */
-
-/* Return codes from gzip */
-#define ERROR 1
-
-/*
- * window size--must be a power of two, and
- *  at least 32K for zip's deflate method
- */
-#define WSIZE 0x8000
-
-/* If BMAX needs to be larger than 16, then h and x[] should be ulg. */
-#define BMAX 16	/* maximum bit length of any code (16 for explode) */
-#define N_MAX 288	/* maximum number of codes in any set */
-
-static long bytes_out;	/* number of output bytes */
-static unsigned long outcnt;	/* bytes in output buffer */
-
-static unsigned hufts;	/* track memory usage */
-static unsigned long bb;	/* bit buffer */
-static unsigned bk;		/* bits in bit buffer */
+#include "busybox.h"
+#include "unarchive.h"
 
 typedef struct huft_s {
 	unsigned char e;	/* number of extra bits or operation */
@@ -109,70 +80,111 @@
 	} v;
 } huft_t;
 
+static int gunzip_src_fd;
+static int gunzip_dst_fd;
+unsigned int gunzip_bytes_out;	/* number of output bytes */
+static unsigned int gunzip_outbuf_count;	/* bytes in output buffer */
+
+/* This is used to sanify any unused bits from the bitbuffer 
+ * so they arent skipped when reading trailers (trailing headers) */
+unsigned char gunzip_in_buffer_count;
+unsigned char *gunzip_in_buffer;
+
+/* gunzip_window size--must be a power of two, and
+ *  at least 32K for zip's deflate method */
+static const int gunzip_wsize = 0x8000;
+
+static unsigned char *gunzip_window;
+static unsigned int *gunzip_crc_table;
+unsigned int gunzip_crc;
+
+/* If BMAX needs to be larger than 16, then h and x[] should be ulg. */
+#define BMAX 16	/* maximum bit length of any code (16 for explode) */
+#define N_MAX 288	/* maximum number of codes in any set */
+
+static unsigned int gunzip_hufts;	/* track memory usage */
+static unsigned int gunzip_bb;	/* bit buffer */
+static unsigned char gunzip_bk;	/* bits in bit buffer */
+
 static const unsigned short mask_bits[] = {
-	0x0000,
-	0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff,
+	0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff,
 	0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff
 };
 
-/* static int error_number = 0; */
-/* ========================================================================
- * Signal and error handler.
- */
+/* Copy lengths for literal codes 257..285 */
+static const unsigned short cplens[] = {
+	3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59,
+		67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0
+};
+
+/* note: see note #13 above about the 258 in this list. */
+/* Extra bits for literal codes 257..285 */
+static const unsigned char cplext[] = {
+	0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5,
+		5, 5, 5, 0, 99, 99
+};						/* 99==invalid */
+
+/* Copy offsets for distance codes 0..29 */
+static const unsigned short cpdist[] = {
+	1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513,
+		769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577
+};
+
+/* Extra bits for distance codes */
+static const unsigned char cpdext[] = {
+	0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10,
+		11, 11, 12, 12, 13, 13
+};
+
+/* Tables for deflate from PKZIP's appnote.txt. */
+/* Order of the bit length code lengths */
+static const unsigned char border[] = {
+	16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
+};
+
+static unsigned int fill_bitbuffer(unsigned int bitbuffer, unsigned int *current, const unsigned int required)
+{
+	while (*current < required) {
+		bitbuffer |= ((unsigned int) xread_char(gunzip_src_fd)) << *current;
+		*current += 8;
+	}
+
+	return(bitbuffer);
+}
 
 static void abort_gzip(void)
 {
 	error_msg("gzip aborted\n");
-	exit(ERROR);
+	exit(-1);
 }
 
-static void make_crc_table(void)
+static void make_gunzip_crc_table(void)
 {
-	const unsigned long poly = 0xedb88320;	/* polynomial exclusive-or pattern */
+	const unsigned int poly = 0xedb88320;	/* polynomial exclusive-or pattern */
 	unsigned short i;	/* counter for all possible eight bit values */
 
 	/* initial shift register value */
-	crc = 0xffffffffL;
-	crc_table = (unsigned long *) malloc(256 * sizeof(unsigned long));
+	gunzip_crc = 0xffffffffL;
+	gunzip_crc_table = (unsigned int *) malloc(256 * sizeof(unsigned int));
 
 	/* Compute and print table of CRC's, five per line */
 	for (i = 0; i < 256; i++) {
-		unsigned long table_entry;	/* crc shift register */
-		char k;			/* byte being shifted into crc apparatus */
+		unsigned int table_entry;	/* crc shift register */
+		unsigned char k;	/* byte being shifted into crc apparatus */
 
 		table_entry = i;
 		/* The idea to initialize the register with the byte instead of
 		   * zero was stolen from Haruhiko Okumura's ar002
 		 */
 		for (k = 8; k; k--) {
-			table_entry =
-				table_entry & 1 ? (table_entry >> 1) ^ poly : table_entry >>
-				1;
-		}
-		crc_table[i] = table_entry;
+			if (table_entry & 1) {
+				table_entry = (table_entry >> 1) ^ poly;
+			} else {
+				table_entry >>= 1;
 	}
-}
-
-/* ===========================================================================
- * Write the output window window[0..outcnt-1] and update crc and bytes_out.
- * (Used for the decompressed data only.)
- */
-static void flush_window(void)
-{
-	int n;
-
-	if (outcnt == 0)
-		return;
-
-	for (n = 0; n < outcnt; n++) {
-		crc = crc_table[((int) crc ^ (window[n])) & 0xff] ^ (crc >> 8);
 	}
-
-	if (fwrite(window, 1, outcnt, out_file) != outcnt) {
-		error_msg_and_die("Couldnt write");
+		gunzip_crc_table[i] = table_entry;
 	}
-	bytes_out += (unsigned long) outcnt;
-	outcnt = 0;
 }
 
 /*
@@ -183,7 +195,8 @@
  */
 static int huft_free(huft_t * t)
 {
-	huft_t *p, *q;
+	huft_t *p;
+	huft_t *q;
 
 	/* Go through linked list, freeing from the malloced (t[-1]) address. */
 	p = t;
@@ -195,8 +208,6 @@
 	return 0;
 }
 
-typedef unsigned char extra_bits_t;
-
 /* Given a list of code lengths and a maximum table size, make a set of
  * tables to decode that set of codes.  Return zero on success, one if
  * the given code set is incomplete (the tables are still built in this
@@ -213,7 +224,7 @@
  */
 static int huft_build(unsigned int *b, const unsigned int n,
 					  const unsigned int s, const unsigned short *d,
-					  const extra_bits_t * e, huft_t ** t, int *m)
+					  const unsigned char *e, huft_t ** t, int *m)
 {
 	unsigned a;			/* counter for codes of length k */
 	unsigned c[BMAX + 1];	/* bit length count table */
@@ -251,26 +262,35 @@
 
 	/* Find minimum and maximum length, bound *m by those */
 	l = *m;
-	for (j = 1; j <= BMAX; j++)
-		if (c[j])
+	for (j = 1; j <= BMAX; j++) {
+		if (c[j]) {
 			break;
+		}
+	}
 	k = j;				/* minimum code length */
-	if ((unsigned) l < j)
+	if ((unsigned) l < j) {
 		l = j;
-	for (i = BMAX; i; i--)
-		if (c[i])
+	}
+	for (i = BMAX; i; i--) {
+		if (c[i]) {
 			break;
+		}
+	}
 	g = i;				/* maximum code length */
-	if ((unsigned) l > i)
+	if ((unsigned) l > i) {
 		l = i;
+	}
 	*m = l;
 
 	/* Adjust last length count to fill out codes, if needed */
-	for (y = 1 << j; j < i; j++, y <<= 1)
-		if ((y -= c[j]) < 0)
+	for (y = 1 << j; j < i; j++, y <<= 1) {
+		if ((y -= c[j]) < 0) {
 			return 2;	/* bad input: more codes than bits */
-	if ((y -= c[i]) < 0)
+		}
+	}
+	if ((y -= c[i]) < 0) {
 		return 2;
+	}
 	c[i] += y;
 
 	/* Generate starting offsets into the value table for each length */
@@ -285,8 +305,9 @@
 	p = b;
 	i = 0;
 	do {
-		if ((j = *p++) != 0)
+		if ((j = *p++) != 0) {
 			v[x[j]++] = i;
+		}
 	} while (++i < n);
 
 	/* Generate the Huffman codes and for each, make the table entries */
@@ -314,8 +335,9 @@
 					f -= a + 1;	/* deduct codes from patterns left */
 					xp = c + k;
 					while (++j < z) {	/* try smaller tables up to z bits */
-						if ((f <<= 1) <= *++xp)
+						if ((f <<= 1) <= *++xp) {
 							break;	/* enough codes to use up j bits */
+						}
 						f -= *xp;	/* else deduct codes from patterns */
 					}
 				}
@@ -324,7 +346,7 @@
 				/* allocate and link in new table */
 				q = (huft_t *) xmalloc((z + 1) * sizeof(huft_t));
 
-				hufts += z + 1;	/* track memory usage */
+				gunzip_hufts += z + 1;	/* track memory usage */
 				*t = q + 1;	/* link to list for huft_free() */
 				*(t = &(q->v.t)) = NULL;
 				u[h] = ++q;	/* table starts after link */
@@ -342,9 +364,9 @@
 
 			/* set up table entry in r */
 			r.b = (unsigned char) (k - w);
-			if (p >= v + n)
+			if (p >= v + n) {
 				r.e = 99;	/* out of values--invalid code */
-			else if (*p < s) {
+			} else if (*p < s) {
 				r.e = (unsigned char) (*p < 256 ? 16 : 15);	/* 256 is end-of-block code */
 				r.v.n = (unsigned short) (*p);	/* simple code is just the value */
 				p++;	/* one compiler does not like *p++ */
@@ -355,12 +377,14 @@
 
 			/* fill code-like entries with r */
 			f = 1 << (k - w);
-			for (j = i >> w; j < z; j += f)
+			for (j = i >> w; j < z; j += f) {
 				q[j] = r;
+			}
 
 			/* backwards increment the k-bit code i */
-			for (j = 1 << (k - 1); i & j; j >>= 1)
+			for (j = 1 << (k - 1); i & j; j >>= 1) {
 				i ^= j;
+			}
 			i ^= j;
 
 			/* backup over finished tables */
@@ -374,6 +398,25 @@
 	return y != 0 && g != 1;
 }
 
+/* ===========================================================================
+ * Write the output gunzip_window gunzip_window[0..gunzip_outbuf_count-1] and update crc and gunzip_bytes_out.
+ * (Used for the decompressed data only.)
+ */
+static void flush_gunzip_window(void)
+{
+	int n;
+
+	for (n = 0; n < gunzip_outbuf_count; n++) {
+		gunzip_crc = gunzip_crc_table[((int) gunzip_crc ^ (gunzip_window[n])) & 0xff] ^ (gunzip_crc >> 8);
+	}
+
+	if (write(gunzip_dst_fd, gunzip_window, gunzip_outbuf_count) != gunzip_outbuf_count) {
+		error_msg_and_die("Couldnt write");
+	}
+	gunzip_bytes_out += gunzip_outbuf_count;
+	gunzip_outbuf_count = 0;
+}
+
 /*
  * inflate (decompress) the codes in a deflated (compressed) block.
  * Return an error code or zero if it all goes ok.
@@ -381,32 +424,26 @@
  * tl, td: literal/length and distance decoder tables
  * bl, bd: number of bits decoded by tl[] and td[]
  */
-static int inflate_codes(huft_t * tl, huft_t * td, int bl, int bd)
+static int inflate_codes(huft_t * tl, huft_t * td, const unsigned int bl, const unsigned int bd)
 {
-	register unsigned long e;	/* table entry flag/number of extra bits */
-	unsigned long n, d;	/* length and index for copy */
-	unsigned long w;	/* current window position */
+	unsigned int e;	/* table entry flag/number of extra bits */
+	unsigned int n, d;	/* length and index for copy */
+	unsigned int w;	/* current gunzip_window position */
 	huft_t *t;			/* pointer to table entry */
-	unsigned ml, md;	/* masks for bl and bd bits */
-	register unsigned long b;	/* bit buffer */
-	register unsigned k;	/* number of bits in bit buffer */
-	register int input_char;
+	unsigned int ml, md;	/* masks for bl and bd bits */
+	unsigned int b;	/* bit buffer */
+	unsigned int k;			/* number of bits in bit buffer */
 
 	/* make local copies of globals */
-	b = bb;				/* initialize bit buffer */
-	k = bk;
-	w = outcnt;			/* initialize window position */
+	b = gunzip_bb;				/* initialize bit buffer */
+	k = gunzip_bk;
+	w = gunzip_outbuf_count;			/* initialize gunzip_window position */
 
 	/* inflate the coded data */
 	ml = mask_bits[bl];	/* precompute masks for speed */
 	md = mask_bits[bd];
-	for (;;) {			/* do until end of block */
-		while (k < (unsigned) bl) {
- 			input_char = fgetc(in_file);
- 			if (input_char == EOF) return 1;
- 			b |= ((unsigned long)input_char) << k;
-			k += 8;
-		}
+	while (1) {			/* do until end of block */
+		b = fill_bitbuffer(b, &k, bl);
 		if ((e = (t = tl + ((unsigned) b & ml))->e) > 16)
 			do {
 				if (e == 99) {
@@ -415,20 +452,16 @@
 				b >>= t->b;
 				k -= t->b;
 				e -= 16;
-				while (k < e) {
-					input_char = fgetc(in_file);
-					if (input_char == EOF) return 1;
-					b |= ((unsigned long)input_char) << k;
-					k += 8;
-				}
+				b = fill_bitbuffer(b, &k, e);
 			} while ((e =
 					  (t = t->v.t + ((unsigned) b & mask_bits[e]))->e) > 16);
 		b >>= t->b;
 		k -= t->b;
 		if (e == 16) {	/* then it's a literal */
-			window[w++] = (unsigned char) t->v.n;
-			if (w == WSIZE) {
-				outcnt = (w), flush_window();
+			gunzip_window[w++] = (unsigned char) t->v.n;
+			if (w == gunzip_wsize) {
+				gunzip_outbuf_count = (w);
+				flush_gunzip_window();
 				w = 0;
 			}
 		} else {		/* it's an EOB or a length */
@@ -439,24 +472,13 @@
 			}
 
 			/* get length of block to copy */
-			while (k < e) {
-				input_char = fgetc(in_file);
-				if (input_char == EOF) return 1;
-				b |= ((unsigned long)input_char) << k;
-				k += 8;
-			}
+			b = fill_bitbuffer(b, &k, e);
 			n = t->v.n + ((unsigned) b & mask_bits[e]);
 			b >>= e;
 			k -= e;
 
 			/* decode distance of block to copy */
-			while (k < (unsigned) bd) {
-				input_char = fgetc(in_file);
-				if (input_char == EOF) return 1;
-				b |= ((unsigned long)input_char) << k;
-				k += 8;
-			}
-
+			b = fill_bitbuffer(b, &k, bd);
 			if ((e = (t = td + ((unsigned) b & md))->e) > 16)
 				do {
 					if (e == 99)
@@ -464,23 +486,13 @@
 					b >>= t->b;
 					k -= t->b;
 					e -= 16;
-					while (k < e) {
-						input_char = fgetc(in_file);
-						if (input_char == EOF) return 1;
-						b |= ((unsigned long)input_char) << k;
-						k += 8;
-					}
+					b = fill_bitbuffer(b, &k, e);
 				} while ((e =
 						  (t =
 						   t->v.t + ((unsigned) b & mask_bits[e]))->e) > 16);
 			b >>= t->b;
 			k -= t->b;
-			while (k < e) {
-				input_char = fgetc(in_file);
-				if (input_char == EOF) return 1;
-				b |= ((unsigned long)input_char) << k;
-				k += 8;
-			}
+			b = fill_bitbuffer(b, &k, e);
 			d = w - t->v.n - ((unsigned) b & mask_bits[e]);
 			b >>= e;
 			k -= e;
@@ -489,60 +501,38 @@
 			do {
 				n -= (e =
 					  (e =
-					   WSIZE - ((d &= WSIZE - 1) > w ? d : w)) > n ? n : e);
-#if !defined(NOMEMCPY) && !defined(DEBUG)
+					   gunzip_wsize - ((d &= gunzip_wsize - 1) > w ? d : w)) > n ? n : e);
+			   /* copy to new buffer to prevent possible overwrite */
 				if (w - d >= e) {	/* (this test assumes unsigned comparison) */
-					memcpy(window + w, window + d, e);
+					memcpy(gunzip_window + w, gunzip_window + d, e);
 					w += e;
 					d += e;
-				} else	/* do it slow to avoid memcpy() overlap */
-#endif							/* !NOMEMCPY */
+				} else {
+				   /* do it slow to avoid memcpy() overlap */
+				   /* !NOMEMCPY */
 					do {
-						window[w++] = window[d++];
+						gunzip_window[w++] = gunzip_window[d++];
 					} while (--e);
-				if (w == WSIZE) {
-					outcnt = (w), flush_window();
+				}
+				if (w == gunzip_wsize) {
+					gunzip_outbuf_count = (w);
+					flush_gunzip_window();
 					w = 0;
 				}
+
 			} while (n);
 		}
 	}
 
 	/* restore the globals from the locals */
-	outcnt = w;			/* restore global window pointer */
-	bb = b;				/* restore global bit buffer */
-	bk = k;
+	gunzip_outbuf_count = w;			/* restore global gunzip_window pointer */
+	gunzip_bb = b;				/* restore global bit buffer */
+	gunzip_bk = k;
 
 	/* done */
 	return 0;
 }
 
-static const unsigned short cplens[] = {	/* Copy lengths for literal codes 257..285 */
-	3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
-	35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0
-};
-
-/* note: see note #13 above about the 258 in this list. */
-static const extra_bits_t cplext[] = {	/* Extra bits for literal codes 257..285 */
-	0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
-	3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 99, 99
-};						/* 99==invalid */
-static const unsigned short cpdist[] = {	/* Copy offsets for distance codes 0..29 */
-	1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
-	257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
-	8193, 12289, 16385, 24577
-};
-static const extra_bits_t cpdext[] = {	/* Extra bits for distance codes */
-	0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
-	7, 7, 8, 8, 9, 9, 10, 10, 11, 11,
-	12, 12, 13, 13
-};
-
-/* Tables for deflate from PKZIP's appnote.txt. */
-static const extra_bits_t border[] = {	/* Order of the bit length code lengths */
-	16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
-};
-
 /*
  * decompress an inflated block
  * e: last block flag
@@ -552,53 +542,43 @@
 static int inflate_block(int *e)
 {
 	unsigned t;			/* block type */
-	register unsigned long b;	/* bit buffer */
-	register unsigned k;	/* number of bits in bit buffer */
-	int input_char;
+	register unsigned int b;	/* bit buffer */
+	unsigned int k;	/* number of bits in bit buffer */
 
 	/* make local bit buffer */
-	b = bb;
-	k = bk;
+
+	b = gunzip_bb;
+	k = gunzip_bk;
 
 	/* read in last block bit */
-	while (k < 1) {
-		input_char = fgetc(in_file);
-		if (input_char == EOF) return 1;
-		b |= ((unsigned long)input_char) << k;
-		k += 8;
-	}
+	b = fill_bitbuffer(b, &k, 1);
 	*e = (int) b & 1;
 	b >>= 1;
 	k -= 1;
 
 	/* read in block type */
-	while (k < 2) {
-		input_char = fgetc(in_file);
-		if (input_char == EOF) return 1;
-		b |= ((unsigned long)input_char) << k;
-		k += 8;
-	}
+	b = fill_bitbuffer(b, &k, 2);
 	t = (unsigned) b & 3;
 	b >>= 2;
 	k -= 2;
 
 	/* restore the global bit buffer */
-	bb = b;
-	bk = k;
+	gunzip_bb = b;
+	gunzip_bk = k;
 
 	/* inflate that block type */
 	switch (t) {
 	case 0:			/* Inflate stored */
 	{
-		unsigned long n;	/* number of bytes in block */
-		unsigned long w;	/* current window position */
-		register unsigned long b_stored;	/* bit buffer */
-		register unsigned long k_stored;	/* number of bits in bit buffer */
+		unsigned int n;	/* number of bytes in block */
+		unsigned int w;	/* current gunzip_window position */
+		unsigned int b_stored;	/* bit buffer */
+		unsigned int k_stored;	/* number of bits in bit buffer */
 
 		/* make local copies of globals */
-		b_stored = bb;	/* initialize bit buffer */
-		k_stored = bk;
-		w = outcnt;		/* initialize window position */
+		b_stored = gunzip_bb;	/* initialize bit buffer */
+		k_stored = gunzip_bk;
+		w = gunzip_outbuf_count;		/* initialize gunzip_window position */
 
 		/* go to byte boundary */
 		n = k_stored & 7;
@@ -606,21 +586,12 @@
 		k_stored -= n;
 
 		/* get the length and its complement */
-		while (k_stored < 16) {
-			input_char = fgetc(in_file);
-			if (input_char == EOF) return 1;
-			b_stored |= ((unsigned long)input_char) << k_stored;
-			k_stored += 8;
-		}
+		b_stored = fill_bitbuffer(b_stored, &k_stored, 16);
 		n = ((unsigned) b_stored & 0xffff);
 		b_stored >>= 16;
 		k_stored -= 16;
-		while (k_stored < 16) {
-			input_char = fgetc(in_file);
-			if (input_char == EOF) return 1;
-			b_stored |= ((unsigned long)input_char) << k_stored;
-			k_stored += 8;
-		}
+
+		b_stored = fill_bitbuffer(b_stored, &k_stored, 16);
 		if (n != (unsigned) ((~b_stored) & 0xffff)) {
 			return 1;	/* error in compressed data */
 		}
@@ -629,15 +600,11 @@
 
 		/* read and output the compressed data */
 		while (n--) {
-			while (k_stored < 8) {
-				input_char = fgetc(in_file);
-				if (input_char == EOF) return 1;
-				b_stored |= ((unsigned long)input_char) << k_stored;
-				k_stored += 8;
-			}
-			window[w++] = (unsigned char) b_stored;
-			if (w == (unsigned long) WSIZE) {
-				outcnt = (w), flush_window();
+			b_stored = fill_bitbuffer(b_stored, &k_stored, 8);
+			gunzip_window[w++] = (unsigned char) b_stored;
+			if (w == (unsigned int) gunzip_wsize) {
+				gunzip_outbuf_count = (w);
+				flush_gunzip_window();
 				w = 0;
 			}
 			b_stored >>= 8;
@@ -645,9 +612,9 @@
 		}
 
 		/* restore the globals from the locals */
-		outcnt = w;		/* restore global window pointer */
-		bb = b_stored;	/* restore global bit buffer */
-		bk = k_stored;
+		gunzip_outbuf_count = w;		/* restore global gunzip_window pointer */
+		gunzip_bb = b_stored;	/* restore global bit buffer */
+		gunzip_bk = k_stored;
 		return 0;
 	}
 	case 1:			/* Inflate fixed 
@@ -659,8 +626,8 @@
 		int i;			/* temporary variable */
 		huft_t *tl;		/* literal/length code table */
 		huft_t *td;		/* distance code table */
-		int bl;			/* lookup bits for tl */
-		int bd;			/* lookup bits for td */
+		unsigned int bl;			/* lookup bits for tl */
+		unsigned int bd;			/* lookup bits for td */
 		unsigned int l[288];	/* length list for huft_build */
 
 		/* set up literal table */
@@ -692,8 +659,9 @@
 		}
 
 		/* decompress until an end-of-block code */
-		if (inflate_codes(tl, td, bl, bd))
+		if (inflate_codes(tl, td, bl, bd)) {
 			return 1;
+		}
 
 		/* free the decoding tables, return */
 		huft_free(tl);
@@ -705,53 +673,41 @@
 		const int dbits = 6;	/* bits in base distance lookup table */
 		const int lbits = 9;	/* bits in base literal/length lookup table */
 
-		int i;			/* temporary variables */
-		unsigned j;
-		unsigned l;		/* last length */
-		unsigned m;		/* mask for bit lengths table */
-		unsigned n;		/* number of lengths to get */
 		huft_t *tl;		/* literal/length code table */
 		huft_t *td;		/* distance code table */
-		int bl;			/* lookup bits for tl */
-		int bd;			/* lookup bits for td */
-		unsigned nb;	/* number of bit length codes */
-		unsigned nl;	/* number of literal/length codes */
-		unsigned nd;	/* number of distance codes */
+		unsigned int i;			/* temporary variables */
+		unsigned int j;
+		unsigned int l;		/* last length */
+		unsigned int m;		/* mask for bit lengths table */
+		unsigned int n;		/* number of lengths to get */
+		unsigned int bl;			/* lookup bits for tl */
+		unsigned int bd;			/* lookup bits for td */
+		unsigned int nb;	/* number of bit length codes */
+		unsigned int nl;	/* number of literal/length codes */
+		unsigned int nd;	/* number of distance codes */
 
-		unsigned ll[286 + 30];	/* literal/length and distance code lengths */
-		register unsigned long b_dynamic;	/* bit buffer */
-		register unsigned k_dynamic;	/* number of bits in bit buffer */
+		unsigned int ll[286 + 30];	/* literal/length and distance code lengths */
+		unsigned int b_dynamic;	/* bit buffer */
+		unsigned int k_dynamic;	/* number of bits in bit buffer */
 
 		/* make local bit buffer */
-		b_dynamic = bb;
-		k_dynamic = bk;
+		b_dynamic = gunzip_bb;
+		k_dynamic = gunzip_bk;
 
 		/* read in table lengths */
-		while (k_dynamic < 5) {
-			input_char = fgetc(in_file);
-			if (input_char == EOF) return 1;
-			b_dynamic |= ((unsigned long)input_char) << k_dynamic;
-			k_dynamic += 8;
-		}
-		nl = 257 + ((unsigned) b_dynamic & 0x1f);	/* number of literal/length codes */
+		b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 5);
+		nl = 257 + ((unsigned int) b_dynamic & 0x1f);	/* number of literal/length codes */
+
 		b_dynamic >>= 5;
 		k_dynamic -= 5;
-		while (k_dynamic < 5) {
-			input_char = fgetc(in_file);
-			if (input_char == EOF) return 1;
-			b_dynamic |= ((unsigned long)input_char) << k_dynamic;
-			k_dynamic += 8;
-		}
-		nd = 1 + ((unsigned) b_dynamic & 0x1f);	/* number of distance codes */
+		b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 5);
+		nd = 1 + ((unsigned int) b_dynamic & 0x1f);	/* number of distance codes */
+
 		b_dynamic >>= 5;
 		k_dynamic -= 5;
-		while (k_dynamic < 4) {
-			input_char = fgetc(in_file);
-			if (input_char == EOF) return 1;
-			b_dynamic |= ((unsigned long)input_char) << k_dynamic;
-			k_dynamic += 8;
-		}
-		nb = 4 + ((unsigned) b_dynamic & 0xf);	/* number of bit length codes */
+		b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 4);
+		nb = 4 + ((unsigned int) b_dynamic & 0xf);	/* number of bit length codes */
+
 		b_dynamic >>= 4;
 		k_dynamic -= 4;
 		if (nl > 286 || nd > 30) {
@@ -760,13 +716,8 @@
 
 		/* read in bit-length-code lengths */
 		for (j = 0; j < nb; j++) {
-			while (k_dynamic < 3) {
-				input_char = fgetc(in_file);
-				if (input_char == EOF) return 1;
-				b_dynamic |= ((unsigned long)input_char) << k_dynamic;
-				k_dynamic += 8;
-			}
-			ll[border[j]] = (unsigned) b_dynamic & 7;
+			b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 3);
+			ll[border[j]] = (unsigned int) b_dynamic & 7;
 			b_dynamic >>= 3;
 			k_dynamic -= 3;
 		}
@@ -776,7 +727,8 @@
 
 		/* build decoding table for trees--single level, 7 bit lookup */
 		bl = 7;
-		if ((i = huft_build(ll, 19, 19, NULL, NULL, &tl, &bl)) != 0) {
+		i = huft_build(ll, 19, 19, NULL, NULL, &tl, &bl);
+		if (i != 0) {
 			if (i == 1) {
 				huft_free(tl);
 			}
@@ -787,46 +739,31 @@
 		n = nl + nd;
 		m = mask_bits[bl];
 		i = l = 0;
-		while ((unsigned) i < n) {
-			while (k_dynamic < (unsigned) bl) {
-				input_char = fgetc(in_file);
-				if (input_char == EOF) return 1;
-				b_dynamic |= ((unsigned long)input_char) << k_dynamic;
-				k_dynamic += 8;
-			}
-			j = (td = tl + ((unsigned) b_dynamic & m))->b;
+		while ((unsigned int) i < n) {
+			b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, (unsigned int)bl);
+			j = (td = tl + ((unsigned int) b_dynamic & m))->b;
 			b_dynamic >>= j;
 			k_dynamic -= j;
 			j = td->v.n;
 			if (j < 16) {	/* length of code in bits (0..15) */
 				ll[i++] = l = j;	/* save last length in l */
 			} else if (j == 16) {	/* repeat last length 3 to 6 times */
-				while (k_dynamic < 2) {
-					input_char = fgetc(in_file);
-					if (input_char == EOF) return 1;
-					b_dynamic |= ((unsigned long)input_char) << k_dynamic;
-					k_dynamic += 8;
-				}
-				j = 3 + ((unsigned) b_dynamic & 3);
+				b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 2);
+				j = 3 + ((unsigned int) b_dynamic & 3);
 				b_dynamic >>= 2;
 				k_dynamic -= 2;
-				if ((unsigned) i + j > n) {
+				if ((unsigned int) i + j > n) {
 					return 1;
 				}
 				while (j--) {
 					ll[i++] = l;
 				}
 			} else if (j == 17) {	/* 3 to 10 zero length codes */
-				while (k_dynamic < 3) {
-					input_char = fgetc(in_file);
-					if (input_char == EOF) return 1;
-					b_dynamic |= ((unsigned long)input_char) << k_dynamic;
-					k_dynamic += 8;
-				}
-				j = 3 + ((unsigned) b_dynamic & 7);
+				b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 3);
+				j = 3 + ((unsigned int) b_dynamic & 7);
 				b_dynamic >>= 3;
 				k_dynamic -= 3;
-				if ((unsigned) i + j > n) {
+				if ((unsigned int) i + j > n) {
 					return 1;
 				}
 				while (j--) {
@@ -834,16 +771,11 @@
 				}
 				l = 0;
 			} else {	/* j == 18: 11 to 138 zero length codes */
-				while (k_dynamic < 7) {
-					input_char = fgetc(in_file);
-					if (input_char == EOF) return 1;
-					b_dynamic |= ((unsigned long)input_char) << k_dynamic;
-					k_dynamic += 8;
-				}
-				j = 11 + ((unsigned) b_dynamic & 0x7f);
+				b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 7);
+				j = 11 + ((unsigned int) b_dynamic & 0x7f);
 				b_dynamic >>= 7;
 				k_dynamic -= 7;
-				if ((unsigned) i + j > n) {
+				if ((unsigned int) i + j > n) {
 					return 1;
 				}
 				while (j--) {
@@ -857,22 +789,24 @@
 		huft_free(tl);
 
 		/* restore the global bit buffer */
-		bb = b_dynamic;
-		bk = k_dynamic;
+		gunzip_bb = b_dynamic;
+		gunzip_bk = k_dynamic;
 
 		/* build the decoding tables for literal/length and distance codes */
 		bl = lbits;
+
 		if ((i = huft_build(ll, nl, 257, cplens, cplext, &tl, &bl)) != 0) {
 			if (i == 1) {
-				error_msg("Incomplete literal tree");
+				error_msg_and_die("Incomplete literal tree");
 				huft_free(tl);
 			}
 			return i;	/* incomplete code set */
 		}
+
 		bd = dbits;
 		if ((i = huft_build(ll + nl, nd, 0, cpdist, cpdext, &td, &bd)) != 0) {
 			if (i == 1) {
-				error_msg("incomplete distance tree");
+				error_msg_and_die("incomplete distance tree");
 				huft_free(td);
 			}
 			huft_free(tl);
@@ -880,8 +814,9 @@
 		}
 
 		/* decompress until an end-of-block code */
-		if (inflate_codes(tl, td, bl, bd))
+		if (inflate_codes(tl, td, bl, bd)) {
 			return 1;
+		}
 
 		/* free the decoding tables, return */
 		huft_free(tl);
@@ -890,6 +825,7 @@
 	}
 	default:
 		/* bad block type */
+		error_msg("bad block type %d\n", t);
 		return 2;
 	}
 }
@@ -897,72 +833,24 @@
 /*
  * decompress an inflated entry
  *
- * GLOBAL VARIABLES: outcnt, bk, bb, hufts, inptr
+ * GLOBAL VARIABLES: gunzip_outbuf_count, bk, gunzip_bb, hufts, inptr
  */
-extern int inflate(FILE * in, FILE * out)
+extern int inflate(int in, int out)
 {
+	typedef void (*sig_type) (int);
 	int e;				/* last block flag */
 	int r;				/* result code */
 	unsigned h = 0;		/* maximum struct huft's malloc'ed */
 
-	/* initialize window, bit buffer */
-	outcnt = 0;
-	bk = 0;
-	bb = 0;
-
-	in_file = in;
-	out_file = out;
-
 	/* Allocate all global buffers (for DYN_ALLOC option) */
-	window = xmalloc((size_t) (((2L * WSIZE) + 1L) * sizeof(unsigned char)));
-	bytes_out = 0L;
+	gunzip_window = xmalloc(0x8000);
+	gunzip_outbuf_count = 0;
+	gunzip_bytes_out = 0;
 
-	/* Create the crc table */
-	make_crc_table();
+	gunzip_src_fd = in;
+	gunzip_dst_fd = out;
 
-	/* decompress until the last block */
-	do {
-		hufts = 0;
-		if ((r = inflate_block(&e)) != 0) {
-			return r;
-		}
-		if (hufts > h) {
-			h = hufts;
-		}
-	} while (!e);
-
-	/* Undo too much lookahead. The next read will be byte aligned so we
-	 * can discard unused bits in the last meaningful byte.
-	 */
-	while (bk >= 8) {
-		bk -= 8;
-		ungetc((bb << bk), in_file);
-	}
-
-	/* flush out window */
-	flush_window();
-	free(window);
-	free(crc_table);
-
-	/* return success */
-	return 0;
-}
-
-/* ===========================================================================
- * Unzip in to out.  This routine works on gzip files only.
- *
- * IN assertions: the buffer inbuf contains already the beginning of
- *   the compressed data, from offsets inptr to insize-1 included.
- *   The magic header has already been checked. The output buffer is cleared.
- * in, out: input and output file descriptors
- */
-extern int unzip(FILE * l_in_file, FILE * l_out_file)
-{
-	unsigned char buf[8];	/* extended local header */
-	unsigned char flags;	/* compression flags */
-	typedef void (*sig_type) (int);
-	unsigned short i;
-	unsigned char magic[2];
+	gunzip_in_buffer = malloc(8);
 
 	if (signal(SIGINT, SIG_IGN) != SIG_IGN) {
 		(void) signal(SIGINT, (sig_type) abort_gzip);
@@ -973,97 +861,44 @@
 	}
 #endif
 
-	magic[0] = fgetc(l_in_file);
-	magic[1] = fgetc(l_in_file);
+	/* initialize gunzip_window, bit buffer */
+	gunzip_bk = 0;
+	gunzip_bb = 0;
 
-#ifdef CONFIG_FEATURE_UNCOMPRESS
-	/* Magic header for compress files, 1F 9d = \037\235 */
-	if ((magic[0] == 0x1F) && (magic[1] == 0x9d)) {
-		return uncompress(l_in_file, l_out_file);
+	/* Create the crc table */
+	make_gunzip_crc_table();
+
+	/* decompress until the last block */
+	do {
+		gunzip_hufts = 0;
+		r = inflate_block(&e);
+		if (r != 0) {
+			error_msg_and_die("inflate error %d", r);
+			return r;
 	}
-#endif
+		if (gunzip_hufts > h) {
+			h = gunzip_hufts;
+	}
+	} while (!e);
 
-	/* Magic header for gzip files, 1F 8B = \037\213 */
-	if ((magic[0] != 0x1F) || (magic[1] != 0x8b)) {
-		error_msg("Invalid gzip magic");
-		return EXIT_FAILURE;
+	/* write any buffered uncompressed data */
+	flush_gunzip_window();
+	free(gunzip_window);
+
+	/* Cleanup */
+	free(gunzip_crc_table);
+
+	/* Store unused bytes in a global buffer so calling applets can access it */
+	gunzip_in_buffer_count = 0;
+	if (gunzip_bk >= 8) {
+		/* Undo too much lookahead. The next read will be byte aligned
+		 * so we can discard unused bits in the last meaningful byte. */
+		gunzip_in_buffer[gunzip_in_buffer_count] = gunzip_bb & 0xff;
+		gunzip_in_buffer_count++;
+		gunzip_bb >>= 8;
+		gunzip_bk -= 8;
 	}
 
-	/* Check the compression method */
-	if (fgetc(l_in_file) != 8) /* also catches EOF */ {
-		error_msg("Unknown compression method");
-		return (-1);
-	}
-
-	flags = (unsigned char) fgetc(l_in_file);
-
-	/* Ignore time stamp(4), extra flags(1), OS type(1) */
-	for (i = 0; i < 6; i++) {
-		fgetc(l_in_file);
-	}
-
-	if (flags & 0x04) {
-		/* bit 2 set: extra field present */
-		const unsigned short extra =
-			fgetc(l_in_file) + (fgetc(l_in_file) << 8);
-		if (feof(in_file)) return 1;
-		for (i = 0; i < extra; i++) {
-			fgetc(l_in_file);
-		}
-	}
-
-	/* Discard original name if any */
-	if (flags & 0x08) {
-		/* bit 3 set: original file name present */
-		while (fgetc(l_in_file) != 0 && !feof(l_in_file));	/* null */
-	}
-
-	/* Discard file comment if any */
-	if (flags & 0x10) {
-		/* bit 4 set: file comment present */
-		while (fgetc(l_in_file) != 0 && !feof(l_in_file));	/* null */
-	}
-
-	/* Decompress */
-	if (inflate(l_in_file, l_out_file) != 0) {
-		error_msg("invalid compressed data--format violated");
-	}
-
-	/* Get the crc and original length
-	 * crc32  (see algorithm.doc)
-	 * uncompressed input size modulo 2^32
-	 */
-	fread(buf, 1, 8, l_in_file);
-
-	/* Validate decompression - crc */
-	if ((unsigned int) ((buf[0] | (buf[1] << 8)) |
-						((buf[2] | (buf[3] << 8)) << 16)) !=
-		(crc ^ 0xffffffffL)) {
-		error_msg("invalid compressed data--crc error");
-	}
-	/* Validate decompression - size */
-	if (((buf[4] | (buf[5] << 8)) | ((buf[6] | (buf[7] << 8)) << 16)) !=
-		(unsigned long) bytes_out) {
-		error_msg("invalid compressed data--length error");
-	}
-
+	/* return success */
 	return 0;
 }
-
-/*
- * This needs access to global variables window and crc_table, so its not in its own file.
- */
-extern void gz_close(int gunzip_pid)
-{
-	if (kill(gunzip_pid, SIGTERM) == -1) {
-		error_msg_and_die
-			("***  Couldnt kill old gunzip process *** aborting");
-	}
-
-	if (waitpid(gunzip_pid, NULL, 0) == -1) {
-		printf("Couldnt wait ?");
-	}
-
-	free(window);
-	free(crc_table);
-}
diff --git a/archival/libunarchive/filter_accept_all.c b/archival/libunarchive/filter_accept_all.c
new file mode 100644
index 0000000..0471cce
--- /dev/null
+++ b/archival/libunarchive/filter_accept_all.c
@@ -0,0 +1,10 @@
+#include <fnmatch.h>
+#include <stdlib.h>
+#include "unarchive.h"
+/*
+ * Accept names that are in the accept list
+ */
+extern char filter_accept_all(const llist_t *accept_list, const llist_t *reject_list, const char *key)
+{
+	return(EXIT_SUCCESS);
+}
diff --git a/archival/libunarchive/filter_accept_list.c b/archival/libunarchive/filter_accept_list.c
new file mode 100644
index 0000000..06b1dd3
--- /dev/null
+++ b/archival/libunarchive/filter_accept_list.c
@@ -0,0 +1,16 @@
+#include <fnmatch.h>
+#include <stdlib.h>
+#include "unarchive.h"
+/*
+ * Accept names that are in the accept list
+ */
+extern char filter_accept_list(const llist_t *accept_list, const llist_t *reject_list, const char *key)
+{
+	while (accept_list) {
+		if (fnmatch(accept_list->data, key, 0) == 0) {
+			return(EXIT_SUCCESS);
+		}
+		accept_list = accept_list->link;
+	}
+	return(EXIT_FAILURE);
+}
diff --git a/archival/libunarchive/filter_accept_reject_list.c b/archival/libunarchive/filter_accept_reject_list.c
new file mode 100644
index 0000000..c893dfc
--- /dev/null
+++ b/archival/libunarchive/filter_accept_reject_list.c
@@ -0,0 +1,34 @@
+#include <fnmatch.h>
+#include <stdlib.h>
+#include "unarchive.h"
+
+static char check_list(const llist_t *list, const char *filename)
+{
+	if (list) {
+		while (list) {
+			if (fnmatch(list->data, filename, 0) == 0) {
+				return(EXIT_SUCCESS);
+			}
+			list = list->link;
+		}
+	}
+	return(EXIT_FAILURE);
+}
+
+/*
+ * Accept names that are in the accept list
+ */
+extern char filter_accept_reject_list(const llist_t *accept_list, const llist_t *reject_list, const char *key)
+{
+	/* Fail if an accept list was specified and the key wasnt in there */
+	if ((accept_list) && (check_list(accept_list, key) == EXIT_FAILURE)) {
+		return(EXIT_FAILURE);
+	}
+
+	/* If the key is in a reject list fail */
+	if (check_list(reject_list, key) == EXIT_FAILURE) {
+		return(EXIT_FAILURE);
+	}
+
+	return(EXIT_SUCCESS);
+}
diff --git a/archival/libunarchive/get_header_ar.c b/archival/libunarchive/get_header_ar.c
index f172fa7..b7f2cfb 100644
--- a/archival/libunarchive/get_header_ar.c
+++ b/archival/libunarchive/get_header_ar.c
@@ -17,12 +17,13 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <unistd.h>
 #include "unarchive.h"
 #include "libbb.h"
 
-file_header_t *get_header_ar(FILE *src_stream)
+extern char get_header_ar(archive_handle_t *archive_handle)
 {
-	file_header_t *typed;
+	file_header_t *typed = archive_handle->file_header;
 	union {
 		char raw[60];
 	 	struct {
@@ -35,72 +36,87 @@
  			char magic[2];
  		} formated;
 	} ar;
+#ifdef CONFIG_FEATURE_AR_LONG_FILENAMES
 	static char *ar_long_names;
+	static unsigned int ar_long_name_size;
+#endif
 
-	if (fread(ar.raw, 1, 60, src_stream) != 60) {
-		return(NULL);
+	/* dont use xread as we want to handle the error ourself */
+	if (read(archive_handle->src_fd, ar.raw, 60) != 60) {
+		/* End Of File */
+		return(EXIT_FAILURE);
+		}
+
+	/* Some ar entries have a trailing '\n' after the previous data entry */
+	if (ar.raw[0] == '\n') {
+		/* fix up the header, we started reading 1 byte too early */
+		memmove(ar.raw, &ar.raw[1], 59);
+		ar.raw[59] = xread_char(archive_handle->src_fd);
+		archive_handle->offset++;
 	}
-	archive_offset += 60;
+	archive_handle->offset += 60;
+		
 	/* align the headers based on the header magic */
 	if ((ar.formated.magic[0] != '`') || (ar.formated.magic[1] != '\n')) {
-		/* some version of ar, have an extra '\n' after each data entry,
-		 * this puts the next header out by 1 */
-		if (ar.formated.magic[1] != '`') {
-			error_msg("Invalid magic");
-			return(NULL);
-		}
-		/* read the next char out of what would be the data section,
-		 * if its a '\n' then it is a valid header offset by 1*/
-		archive_offset++;
-		if (fgetc(src_stream) != '\n') {
-			error_msg("Invalid magic");
-			return(NULL);
-		}
-		/* fix up the header, we started reading 1 byte too early */
-		/* raw_header[60] wont be '\n' as it should, but it doesnt matter */
-		memmove(ar.raw, &ar.raw[1], 59);
+		error_msg_and_die("Invalid ar header");
 	}
-		
-	typed = (file_header_t *) xcalloc(1, sizeof(file_header_t));
 
-	typed->size = (size_t) atoi(ar.formated.size);
+	typed->mode = strtol(ar.formated.mode, NULL, 8);
+	typed->mtime = atoi(ar.formated.date);
+	typed->uid = atoi(ar.formated.uid);
+	typed->gid = atoi(ar.formated.gid);
+	typed->size = atoi(ar.formated.size);
+
 	/* long filenames have '/' as the first character */
 	if (ar.formated.name[0] == '/') {
+#ifdef CONFIG_FEATURE_AR_LONG_FILENAMES
 		if (ar.formated.name[1] == '/') {
 			/* If the second char is a '/' then this entries data section
 			 * stores long filename for multiple entries, they are stored
 			 * in static variable long_names for use in future entries */
-			ar_long_names = (char *) xrealloc(ar_long_names, typed->size);
-			fread(ar_long_names, 1, typed->size, src_stream);
-			archive_offset += typed->size;
+			ar_long_name_size = typed->size;
+			ar_long_names = xmalloc(ar_long_name_size);
+			xread_all(archive_handle->src_fd, ar_long_names, ar_long_name_size);
+			archive_handle->offset += ar_long_name_size;
 			/* This ar entries data section only contained filenames for other records
 			 * they are stored in the static ar_long_names for future reference */
-			return (get_header_ar(src_stream)); /* Return next header */
+			return (get_header_ar(archive_handle)); /* Return next header */
 		} else if (ar.formated.name[1] == ' ') {
 			/* This is the index of symbols in the file for compilers */
-			seek_sub_file(src_stream, typed->size);
-			return (get_header_ar(src_stream)); /* Return next header */
+			data_skip(archive_handle);
+			return (get_header_ar(archive_handle)); /* Return next header */
 		} else {
 			/* The number after the '/' indicates the offset in the ar data section
 			(saved in variable long_name) that conatains the real filename */
-			if (!ar_long_names) {
-				error_msg("Cannot resolve long file name");
-				return (NULL);
+			const unsigned int long_offset = atoi(&ar.formated.name[1]);
+			if (long_offset >= ar_long_name_size) {
+				error_msg_and_die("Cant resolve long filename");
 			}
-			typed->name = xstrdup(ar_long_names + atoi(&ar.formated.name[1]));
+			typed->name = xstrdup(ar_long_names + long_offset);
 		}
+#else
+		error_msg_and_die("long filenames not supported");
+#endif
 	} else {
 		/* short filenames */
                typed->name = xstrndup(ar.formated.name, 16);
 	}
-	typed->name[strcspn(typed->name, " /")]='\0';
 
-	/* convert the rest of the now valid char header to its typed struct */	
-	parse_mode(ar.formated.mode, &typed->mode);
-	typed->mtime = atoi(ar.formated.date);
-	typed->uid = atoi(ar.formated.uid);
-	typed->gid = atoi(ar.formated.gid);
+	typed->name[strcspn(typed->name, " /")] = '\0';
 
-	return(typed);
+	if (archive_handle->filter(archive_handle->accept, archive_handle->reject, typed->name) == EXIT_SUCCESS) {
+		archive_handle->action_header(typed);
+		if (archive_handle->sub_archive) {
+			while (archive_handle->action_data_subarchive(archive_handle->sub_archive) == EXIT_SUCCESS);
+		} else {
+			archive_handle->action_data(archive_handle);
+		}
+	} else {
+		data_skip(archive_handle);			
+	}
+
+	archive_handle->offset += typed->size + 1;
+
+	return(EXIT_SUCCESS);
 }
 
diff --git a/archival/libunarchive/get_header_tar.c b/archival/libunarchive/get_header_tar.c
index e6747b7..2c8fc0a 100644
--- a/archival/libunarchive/get_header_tar.c
+++ b/archival/libunarchive/get_header_tar.c
@@ -20,8 +20,9 @@
 #include "unarchive.h"
 #include "libbb.h"
 
-file_header_t *get_header_tar(FILE * tar_stream)
+extern char get_header_tar(archive_handle_t *archive_handle)
 {
+	file_header_t *file_header = archive_handle->file_header;
 	union {
 		unsigned char raw[512];
 		struct {
@@ -44,20 +45,22 @@
 			char padding[12];	/* 500-512 */
 		} formated;
 	} tar;
-	file_header_t *tar_entry = NULL;
 	long sum = 0;
 	long i;
 
-	if (archive_offset % 512 != 0) {
-		seek_sub_file(tar_stream, 512 - (archive_offset % 512));
-	}
+	/* Align header */
+	archive_handle->offset += data_align(archive_handle->src_fd, archive_handle->offset, 512);
 
-	if (fread(tar.raw, 1, 512, tar_stream) != 512) {
-		/* Unfortunatly its common for tar files to have all sorts of
-		 * trailing garbage, fail silently */
-		return (NULL);
+	if (xread_all_eof(archive_handle->src_fd, tar.raw, 512) == 0) {
+		/* End of file */
+		return(EXIT_FAILURE);
 	}
-	archive_offset += 512;
+	archive_handle->offset += 512;
+
+	/* If there is no filename its an empty header */
+	if (tar.formated.name[0] == 0) {
+		return(EXIT_SUCCESS);
+	}
 
 	/* Check header has valid magic, "ustar" is for the proper tar
 	 * 0's are for the old tar format
@@ -66,100 +69,102 @@
 #ifdef CONFIG_FEATURE_TAR_OLD_FORMAT
 		if (strncmp(tar.formated.magic, "\0\0\0\0\0", 5) != 0)
 #endif
-			return (NULL);
-	}
-
-	/* If there is no filename its an empty header, skip it */
-	if (tar.formated.name[0] == 0) {
-		return (NULL);
+			error_msg_and_die("Invalid tar magic");
 	}
 
 	/* Do checksum on headers */
-	for (i = 0; i < 148; i++) {
+	for (i =  0; i < 148 ; i++) {
 		sum += tar.raw[i];
 	}
 	sum += ' ' * 8;
-	for (i = 156; i < 512; i++) {
+	for (i =  156; i < 512 ; i++) {
 		sum += tar.raw[i];
 	}
 	if (sum != strtol(tar.formated.chksum, NULL, 8)) {
 		error_msg("Invalid tar header checksum");
-		return (NULL);
+		return(EXIT_FAILURE);
 	}
 
 	/* convert to type'ed variables */
-	tar_entry = xcalloc(1, sizeof(file_header_t));
 	if (tar.formated.prefix[0] == 0) {
-		tar_entry->name = xstrdup(tar.formated.name);
+		file_header->name = strdup(tar.formated.name);
 	} else {
-		tar_entry->name =
-			concat_path_file(tar.formated.prefix, tar.formated.name);
+		file_header->name = concat_path_file(tar.formated.prefix, tar.formated.name);
 	}
-
-	tar_entry->mode = strtol(tar.formated.mode, NULL, 8);
-	tar_entry->uid = strtol(tar.formated.uid, NULL, 8);
-	tar_entry->gid = strtol(tar.formated.gid, NULL, 8);
-	tar_entry->size = strtol(tar.formated.size, NULL, 8);
-	tar_entry->mtime = strtol(tar.formated.mtime, NULL, 8);
-	tar_entry->link_name =
-		strlen(tar.formated.linkname) ? xstrdup(tar.formated.linkname) : NULL;
-	tar_entry->device =
-		(dev_t) ((strtol(tar.formated.devmajor, NULL, 8) << 8) +
+	file_header->mode = strtol(tar.formated.mode, NULL, 8);
+	file_header->uid = strtol(tar.formated.uid, NULL, 8);
+	file_header->gid = strtol(tar.formated.gid, NULL, 8);
+	file_header->size = strtol(tar.formated.size, NULL, 8);
+	file_header->mtime = strtol(tar.formated.mtime, NULL, 8);
+	file_header->link_name = (tar.formated.linkname[0] != '\0') ? 
+	    xstrdup(tar.formated.linkname) : NULL;
+	file_header->device = (dev_t) ((strtol(tar.formated.devmajor, NULL, 8) << 8) +
 				 strtol(tar.formated.devminor, NULL, 8));
 
 #if defined CONFIG_FEATURE_TAR_OLD_FORMAT || defined CONFIG_FEATURE_GNUTAR_LONG_FILENAME
+	/* Fix mode, used by the old format */
 	switch (tar.formated.typeflag) {
 # ifdef CONFIG_FEATURE_TAR_OLD_FORMAT
 	case 0:
-		tar_entry->mode |= S_IFREG;
+		file_header->mode |= S_IFREG;
 		break;
 	case 1:
-		error_msg("internal hard link not handled\n");
+		error_msg("Internal hard link not supported");
 		break;
 	case 2:
-		tar_entry->mode |= S_IFLNK;
+		file_header->mode |= S_IFLNK;
 		break;
 	case 3:
-		tar_entry->mode |= S_IFCHR;
+		file_header->mode |= S_IFCHR;
 		break;
 	case 4:
-		tar_entry->mode |= S_IFBLK;
+		file_header->mode |= S_IFBLK;
 		break;
 	case 5:
-		tar_entry->mode |= S_IFDIR;
+		file_header->mode |= S_IFDIR;
 		break;
 	case 6:
-		tar_entry->mode |= S_IFIFO;
+		file_header->mode |= S_IFIFO;
 		break;
 # endif
 # ifdef CONFIG_FEATURE_GNUTAR_LONG_FILENAME
 	case 'L': {
 			char *longname;
 
-			longname = xmalloc(tar_entry->size + 1);
-			fread(longname, 1, tar_entry->size, tar_stream);
-			archive_offset += tar_entry->size;
-			longname[tar_entry->size] = '\0';
+			longname = xmalloc(file_header->size + 1);
+			xread_all(archive_handle->src_fd, longname, file_header->size);
+			longname[file_header->size] = '\0';
+			archive_handle->offset += file_header->size;
 
-			tar_entry = get_header_tar(tar_stream);
-			tar_entry->name = longname;
+			get_header_tar(archive_handle);
+			file_header->name = longname;
 			break;
 		}
 	case 'K': {
-			char *longname;
+			char *linkname;
 
-			longname = xmalloc(tar_entry->size + 1);
-			fread(longname, 1, tar_entry->size, tar_stream);
-			archive_offset += tar_entry->size;
-			longname[tar_entry->size] = '\0';
+			linkname = xmalloc(file_header->size + 1);
+			xread_all(archive_handle->src_fd, linkname, file_header->size);
+			linkname[file_header->size] = '\0';
+			archive_handle->offset += file_header->size;
 
-			tar_entry = get_header_tar(tar_stream);
-			tar_entry->link_name = longname;
+			get_header_tar(archive_handle);
+			file_header->name = linkname;
 			break;
 		}
 # endif
 	}
 #endif
 
-	return (tar_entry);
+	if (archive_handle->filter(archive_handle->accept, archive_handle->reject, archive_handle->file_header->name) == EXIT_SUCCESS) {
+		archive_handle->action_header(archive_handle->file_header);
+		archive_handle->flags |= ARCHIVE_EXTRACT_QUIET;
+		archive_handle->action_data(archive_handle);
+	} else {
+		data_skip(archive_handle);			
+	}
+	archive_handle->offset += file_header->size;
+
+	return(EXIT_SUCCESS);
 }
+
diff --git a/archival/libunarchive/get_header_tar_gz.c b/archival/libunarchive/get_header_tar_gz.c
new file mode 100644
index 0000000..c06beac
--- /dev/null
+++ b/archival/libunarchive/get_header_tar_gz.c
@@ -0,0 +1,73 @@
+/*
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <sys/types.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "libbb.h"
+#include "unarchive.h"
+
+extern char get_header_tar_gz(archive_handle_t *archive_handle)
+{
+	int fd_pipe[2];
+	int pid;
+
+	check_header_gzip(archive_handle->src_fd);
+
+	if (pipe(fd_pipe) != 0) {
+		error_msg_and_die("Can't create pipe\n");
+	}
+
+	pid = fork();
+	if (pid == -1) {
+		error_msg_and_die("Fork failed\n");
+	}
+
+	if (pid == 0) {
+		/* child process */
+	    close(fd_pipe[0]); /* We don't wan't to read from the pipe */
+	    inflate(archive_handle->src_fd, fd_pipe[1]);
+		check_trailer_gzip(archive_handle->src_fd);
+	    close(fd_pipe[1]); /* Send EOF */
+	    exit(0);
+	    /* notreached */
+	}
+	/* parent process */
+	close(fd_pipe[1]); /* Don't want to write down the pipe */
+	close(archive_handle->src_fd);
+
+	archive_handle->src_fd = fd_pipe[0];
+
+	while (get_header_tar(archive_handle) == EXIT_SUCCESS);
+
+	if (kill(pid, SIGTERM) == -1) {
+		error_msg_and_die("Couldnt kill gunzip process");
+	}
+	
+	/* I dont think this is needed */
+#if 0
+	if (waitpid(pid, NULL, 0) == -1) {
+		error_msg("Couldnt wait ?");
+	}
+#endif
+
+	/* Can only do one file at a time */
+	return(EXIT_FAILURE);
+}
+
diff --git a/archival/libunarchive/header_list.c b/archival/libunarchive/header_list.c
new file mode 100644
index 0000000..5849a76
--- /dev/null
+++ b/archival/libunarchive/header_list.c
@@ -0,0 +1,7 @@
+#include <stdio.h>
+#include "unarchive.h"
+
+extern void header_list(const file_header_t *file_header)
+{
+	puts(file_header->name);
+}
diff --git a/archival/libunarchive/header_skip.c b/archival/libunarchive/header_skip.c
new file mode 100644
index 0000000..4430178
--- /dev/null
+++ b/archival/libunarchive/header_skip.c
@@ -0,0 +1,7 @@
+#include <stdio.h>
+#include "unarchive.h"
+
+extern void header_skip(const file_header_t *file_header)
+{
+	return;
+}
diff --git a/archival/libunarchive/header_verbose_list.c b/archival/libunarchive/header_verbose_list.c
new file mode 100644
index 0000000..ff7b3bc
--- /dev/null
+++ b/archival/libunarchive/header_verbose_list.c
@@ -0,0 +1,29 @@
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include "libbb.h"
+#include "unarchive.h"
+
+extern void header_verbose_list(const file_header_t *file_header)
+{
+	struct tm *mtime = localtime(&file_header->mtime);
+
+	printf("%s %d/%d%10u %4u-%02u-%02u %02u:%02u:%02u %s",
+		mode_string(file_header->mode),
+		file_header->uid,
+		file_header->gid,
+		(unsigned int) file_header->size,
+		1900 + mtime->tm_year,
+		1 + mtime->tm_mon,
+		mtime->tm_mday,
+		mtime->tm_hour,
+		mtime->tm_min,
+		mtime->tm_sec,
+		file_header->name);
+
+	if (file_header->link_name) {
+		printf(" -> %s", file_header->link_name);
+	}
+	/* putchar isnt used anywhere else i dont think */
+	puts("");
+}
diff --git a/archival/libunarchive/init_handle.c b/archival/libunarchive/init_handle.c
new file mode 100644
index 0000000..12d9e71
--- /dev/null
+++ b/archival/libunarchive/init_handle.c
@@ -0,0 +1,18 @@
+#include <string.h>
+#include "libbb.h"
+#include "unarchive.h"
+
+archive_handle_t *init_handle(void)
+{
+	archive_handle_t *archive_handle;
+
+	/* Initialise default values */
+	archive_handle = xmalloc(sizeof(archive_handle_t));
+	memset(archive_handle, 0, sizeof(archive_handle_t));
+	archive_handle->file_header = xmalloc(sizeof(file_header_t));
+	archive_handle->action_header = header_skip;
+	archive_handle->action_data = data_skip;
+	archive_handle->filter = filter_accept_all;
+
+	return(archive_handle);
+}
diff --git a/archival/libunarchive/seek_sub_file.c b/archival/libunarchive/seek_sub_file.c
index 7523a52..733bb36 100644
--- a/archival/libunarchive/seek_sub_file.c
+++ b/archival/libunarchive/seek_sub_file.c
@@ -14,23 +14,19 @@
  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  */
 
-#include <stdio.h>
+#include <sys/types.h>
 #include <errno.h>
-#include <stdlib.h>
 #include <unistd.h>
+#include <stdlib.h>
+#include "unarchive.h"
+#include "libbb.h"
 
-off_t archive_offset;
-
-void seek_sub_file(FILE *src_stream, const int count)
+extern void seek_sub_file(const int src_fd, const unsigned int amount)
 {
-	/* Try to fseek as faster */
-	archive_offset += count;
-	if (fseek(src_stream, count, SEEK_CUR) != 0 && errno == ESPIPE) {
-		int i;
-		for (i = 0; i < count; i++) {
-			fgetc(src_stream);
+	if ((lseek(src_fd, amount, SEEK_CUR) == -1) && (errno == ESPIPE)) {
+		unsigned int i;
+		for (i = 0; i < amount; i++) {
+			xread_char(src_fd);
 		}
 	}
-	return;
 }
-
diff --git a/archival/libunarchive/unpack_ar_archive.c b/archival/libunarchive/unpack_ar_archive.c
new file mode 100644
index 0000000..923b8a0
--- /dev/null
+++ b/archival/libunarchive/unpack_ar_archive.c
@@ -0,0 +1,34 @@
+/*
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include "unarchive.h"
+#include "busybox.h"
+
+extern void unpack_ar_archive(archive_handle_t *ar_archive)
+{
+	char magic[7];
+
+	xread_all(ar_archive->src_fd, magic, 7);
+	if (strncmp(magic, "!<arch>", 7) != 0) {
+		error_msg_and_die("Invalid ar magic");
+	}
+	ar_archive->offset += 7;
+
+	while (get_header_ar(ar_archive) == EXIT_SUCCESS);
+}
diff --git a/archival/libunarchive/unzip.c b/archival/libunarchive/unzip.c
index d840670..20bf88f 100644
--- a/archival/libunarchive/unzip.c
+++ b/archival/libunarchive/unzip.c
@@ -65,40 +65,11 @@
 #include <signal.h>
 #include <stdlib.h>
 #include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
 #include "config.h"
-#include "libbb.h"
-
-#ifdef CONFIG_FEATURE_UNCOMPRESS
-int uncompress(FILE * in, FILE * out);
-#endif
-
-static FILE *in_file, *out_file;
-
-/* these are freed by gz_close */
-static unsigned char *window;
-static unsigned long *crc_table;
-
-static unsigned long crc;	/* shift register contents */
-
-/* Return codes from gzip */
-#define ERROR 1
-
-/*
- * window size--must be a power of two, and
- *  at least 32K for zip's deflate method
- */
-#define WSIZE 0x8000
-
-/* If BMAX needs to be larger than 16, then h and x[] should be ulg. */
-#define BMAX 16	/* maximum bit length of any code (16 for explode) */
-#define N_MAX 288	/* maximum number of codes in any set */
-
-static long bytes_out;	/* number of output bytes */
-static unsigned long outcnt;	/* bytes in output buffer */
-
-static unsigned hufts;	/* track memory usage */
-static unsigned long bb;	/* bit buffer */
-static unsigned bk;		/* bits in bit buffer */
+#include "busybox.h"
+#include "unarchive.h"
 
 typedef struct huft_s {
 	unsigned char e;	/* number of extra bits or operation */
@@ -109,70 +80,111 @@
 	} v;
 } huft_t;
 
+static int gunzip_src_fd;
+static int gunzip_dst_fd;
+unsigned int gunzip_bytes_out;	/* number of output bytes */
+static unsigned int gunzip_outbuf_count;	/* bytes in output buffer */
+
+/* This is used to sanify any unused bits from the bitbuffer 
+ * so they arent skipped when reading trailers (trailing headers) */
+unsigned char gunzip_in_buffer_count;
+unsigned char *gunzip_in_buffer;
+
+/* gunzip_window size--must be a power of two, and
+ *  at least 32K for zip's deflate method */
+static const int gunzip_wsize = 0x8000;
+
+static unsigned char *gunzip_window;
+static unsigned int *gunzip_crc_table;
+unsigned int gunzip_crc;
+
+/* If BMAX needs to be larger than 16, then h and x[] should be ulg. */
+#define BMAX 16	/* maximum bit length of any code (16 for explode) */
+#define N_MAX 288	/* maximum number of codes in any set */
+
+static unsigned int gunzip_hufts;	/* track memory usage */
+static unsigned int gunzip_bb;	/* bit buffer */
+static unsigned char gunzip_bk;	/* bits in bit buffer */
+
 static const unsigned short mask_bits[] = {
-	0x0000,
-	0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff,
+	0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff,
 	0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff
 };
 
-/* static int error_number = 0; */
-/* ========================================================================
- * Signal and error handler.
- */
+/* Copy lengths for literal codes 257..285 */
+static const unsigned short cplens[] = {
+	3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59,
+		67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0
+};
+
+/* note: see note #13 above about the 258 in this list. */
+/* Extra bits for literal codes 257..285 */
+static const unsigned char cplext[] = {
+	0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5,
+		5, 5, 5, 0, 99, 99
+};						/* 99==invalid */
+
+/* Copy offsets for distance codes 0..29 */
+static const unsigned short cpdist[] = {
+	1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513,
+		769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577
+};
+
+/* Extra bits for distance codes */
+static const unsigned char cpdext[] = {
+	0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10,
+		11, 11, 12, 12, 13, 13
+};
+
+/* Tables for deflate from PKZIP's appnote.txt. */
+/* Order of the bit length code lengths */
+static const unsigned char border[] = {
+	16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
+};
+
+static unsigned int fill_bitbuffer(unsigned int bitbuffer, unsigned int *current, const unsigned int required)
+{
+	while (*current < required) {
+		bitbuffer |= ((unsigned int) xread_char(gunzip_src_fd)) << *current;
+		*current += 8;
+	}
+
+	return(bitbuffer);
+}
 
 static void abort_gzip(void)
 {
 	error_msg("gzip aborted\n");
-	exit(ERROR);
+	exit(-1);
 }
 
-static void make_crc_table(void)
+static void make_gunzip_crc_table(void)
 {
-	const unsigned long poly = 0xedb88320;	/* polynomial exclusive-or pattern */
+	const unsigned int poly = 0xedb88320;	/* polynomial exclusive-or pattern */
 	unsigned short i;	/* counter for all possible eight bit values */
 
 	/* initial shift register value */
-	crc = 0xffffffffL;
-	crc_table = (unsigned long *) malloc(256 * sizeof(unsigned long));
+	gunzip_crc = 0xffffffffL;
+	gunzip_crc_table = (unsigned int *) malloc(256 * sizeof(unsigned int));
 
 	/* Compute and print table of CRC's, five per line */
 	for (i = 0; i < 256; i++) {
-		unsigned long table_entry;	/* crc shift register */
-		char k;			/* byte being shifted into crc apparatus */
+		unsigned int table_entry;	/* crc shift register */
+		unsigned char k;	/* byte being shifted into crc apparatus */
 
 		table_entry = i;
 		/* The idea to initialize the register with the byte instead of
 		   * zero was stolen from Haruhiko Okumura's ar002
 		 */
 		for (k = 8; k; k--) {
-			table_entry =
-				table_entry & 1 ? (table_entry >> 1) ^ poly : table_entry >>
-				1;
-		}
-		crc_table[i] = table_entry;
+			if (table_entry & 1) {
+				table_entry = (table_entry >> 1) ^ poly;
+			} else {
+				table_entry >>= 1;
 	}
-}
-
-/* ===========================================================================
- * Write the output window window[0..outcnt-1] and update crc and bytes_out.
- * (Used for the decompressed data only.)
- */
-static void flush_window(void)
-{
-	int n;
-
-	if (outcnt == 0)
-		return;
-
-	for (n = 0; n < outcnt; n++) {
-		crc = crc_table[((int) crc ^ (window[n])) & 0xff] ^ (crc >> 8);
 	}
-
-	if (fwrite(window, 1, outcnt, out_file) != outcnt) {
-		error_msg_and_die("Couldnt write");
+		gunzip_crc_table[i] = table_entry;
 	}
-	bytes_out += (unsigned long) outcnt;
-	outcnt = 0;
 }
 
 /*
@@ -183,7 +195,8 @@
  */
 static int huft_free(huft_t * t)
 {
-	huft_t *p, *q;
+	huft_t *p;
+	huft_t *q;
 
 	/* Go through linked list, freeing from the malloced (t[-1]) address. */
 	p = t;
@@ -195,8 +208,6 @@
 	return 0;
 }
 
-typedef unsigned char extra_bits_t;
-
 /* Given a list of code lengths and a maximum table size, make a set of
  * tables to decode that set of codes.  Return zero on success, one if
  * the given code set is incomplete (the tables are still built in this
@@ -213,7 +224,7 @@
  */
 static int huft_build(unsigned int *b, const unsigned int n,
 					  const unsigned int s, const unsigned short *d,
-					  const extra_bits_t * e, huft_t ** t, int *m)
+					  const unsigned char *e, huft_t ** t, int *m)
 {
 	unsigned a;			/* counter for codes of length k */
 	unsigned c[BMAX + 1];	/* bit length count table */
@@ -251,26 +262,35 @@
 
 	/* Find minimum and maximum length, bound *m by those */
 	l = *m;
-	for (j = 1; j <= BMAX; j++)
-		if (c[j])
+	for (j = 1; j <= BMAX; j++) {
+		if (c[j]) {
 			break;
+		}
+	}
 	k = j;				/* minimum code length */
-	if ((unsigned) l < j)
+	if ((unsigned) l < j) {
 		l = j;
-	for (i = BMAX; i; i--)
-		if (c[i])
+	}
+	for (i = BMAX; i; i--) {
+		if (c[i]) {
 			break;
+		}
+	}
 	g = i;				/* maximum code length */
-	if ((unsigned) l > i)
+	if ((unsigned) l > i) {
 		l = i;
+	}
 	*m = l;
 
 	/* Adjust last length count to fill out codes, if needed */
-	for (y = 1 << j; j < i; j++, y <<= 1)
-		if ((y -= c[j]) < 0)
+	for (y = 1 << j; j < i; j++, y <<= 1) {
+		if ((y -= c[j]) < 0) {
 			return 2;	/* bad input: more codes than bits */
-	if ((y -= c[i]) < 0)
+		}
+	}
+	if ((y -= c[i]) < 0) {
 		return 2;
+	}
 	c[i] += y;
 
 	/* Generate starting offsets into the value table for each length */
@@ -285,8 +305,9 @@
 	p = b;
 	i = 0;
 	do {
-		if ((j = *p++) != 0)
+		if ((j = *p++) != 0) {
 			v[x[j]++] = i;
+		}
 	} while (++i < n);
 
 	/* Generate the Huffman codes and for each, make the table entries */
@@ -314,8 +335,9 @@
 					f -= a + 1;	/* deduct codes from patterns left */
 					xp = c + k;
 					while (++j < z) {	/* try smaller tables up to z bits */
-						if ((f <<= 1) <= *++xp)
+						if ((f <<= 1) <= *++xp) {
 							break;	/* enough codes to use up j bits */
+						}
 						f -= *xp;	/* else deduct codes from patterns */
 					}
 				}
@@ -324,7 +346,7 @@
 				/* allocate and link in new table */
 				q = (huft_t *) xmalloc((z + 1) * sizeof(huft_t));
 
-				hufts += z + 1;	/* track memory usage */
+				gunzip_hufts += z + 1;	/* track memory usage */
 				*t = q + 1;	/* link to list for huft_free() */
 				*(t = &(q->v.t)) = NULL;
 				u[h] = ++q;	/* table starts after link */
@@ -342,9 +364,9 @@
 
 			/* set up table entry in r */
 			r.b = (unsigned char) (k - w);
-			if (p >= v + n)
+			if (p >= v + n) {
 				r.e = 99;	/* out of values--invalid code */
-			else if (*p < s) {
+			} else if (*p < s) {
 				r.e = (unsigned char) (*p < 256 ? 16 : 15);	/* 256 is end-of-block code */
 				r.v.n = (unsigned short) (*p);	/* simple code is just the value */
 				p++;	/* one compiler does not like *p++ */
@@ -355,12 +377,14 @@
 
 			/* fill code-like entries with r */
 			f = 1 << (k - w);
-			for (j = i >> w; j < z; j += f)
+			for (j = i >> w; j < z; j += f) {
 				q[j] = r;
+			}
 
 			/* backwards increment the k-bit code i */
-			for (j = 1 << (k - 1); i & j; j >>= 1)
+			for (j = 1 << (k - 1); i & j; j >>= 1) {
 				i ^= j;
+			}
 			i ^= j;
 
 			/* backup over finished tables */
@@ -374,6 +398,25 @@
 	return y != 0 && g != 1;
 }
 
+/* ===========================================================================
+ * Write the output gunzip_window gunzip_window[0..gunzip_outbuf_count-1] and update crc and gunzip_bytes_out.
+ * (Used for the decompressed data only.)
+ */
+static void flush_gunzip_window(void)
+{
+	int n;
+
+	for (n = 0; n < gunzip_outbuf_count; n++) {
+		gunzip_crc = gunzip_crc_table[((int) gunzip_crc ^ (gunzip_window[n])) & 0xff] ^ (gunzip_crc >> 8);
+	}
+
+	if (write(gunzip_dst_fd, gunzip_window, gunzip_outbuf_count) != gunzip_outbuf_count) {
+		error_msg_and_die("Couldnt write");
+	}
+	gunzip_bytes_out += gunzip_outbuf_count;
+	gunzip_outbuf_count = 0;
+}
+
 /*
  * inflate (decompress) the codes in a deflated (compressed) block.
  * Return an error code or zero if it all goes ok.
@@ -381,32 +424,26 @@
  * tl, td: literal/length and distance decoder tables
  * bl, bd: number of bits decoded by tl[] and td[]
  */
-static int inflate_codes(huft_t * tl, huft_t * td, int bl, int bd)
+static int inflate_codes(huft_t * tl, huft_t * td, const unsigned int bl, const unsigned int bd)
 {
-	register unsigned long e;	/* table entry flag/number of extra bits */
-	unsigned long n, d;	/* length and index for copy */
-	unsigned long w;	/* current window position */
+	unsigned int e;	/* table entry flag/number of extra bits */
+	unsigned int n, d;	/* length and index for copy */
+	unsigned int w;	/* current gunzip_window position */
 	huft_t *t;			/* pointer to table entry */
-	unsigned ml, md;	/* masks for bl and bd bits */
-	register unsigned long b;	/* bit buffer */
-	register unsigned k;	/* number of bits in bit buffer */
-	register int input_char;
+	unsigned int ml, md;	/* masks for bl and bd bits */
+	unsigned int b;	/* bit buffer */
+	unsigned int k;			/* number of bits in bit buffer */
 
 	/* make local copies of globals */
-	b = bb;				/* initialize bit buffer */
-	k = bk;
-	w = outcnt;			/* initialize window position */
+	b = gunzip_bb;				/* initialize bit buffer */
+	k = gunzip_bk;
+	w = gunzip_outbuf_count;			/* initialize gunzip_window position */
 
 	/* inflate the coded data */
 	ml = mask_bits[bl];	/* precompute masks for speed */
 	md = mask_bits[bd];
-	for (;;) {			/* do until end of block */
-		while (k < (unsigned) bl) {
- 			input_char = fgetc(in_file);
- 			if (input_char == EOF) return 1;
- 			b |= ((unsigned long)input_char) << k;
-			k += 8;
-		}
+	while (1) {			/* do until end of block */
+		b = fill_bitbuffer(b, &k, bl);
 		if ((e = (t = tl + ((unsigned) b & ml))->e) > 16)
 			do {
 				if (e == 99) {
@@ -415,20 +452,16 @@
 				b >>= t->b;
 				k -= t->b;
 				e -= 16;
-				while (k < e) {
-					input_char = fgetc(in_file);
-					if (input_char == EOF) return 1;
-					b |= ((unsigned long)input_char) << k;
-					k += 8;
-				}
+				b = fill_bitbuffer(b, &k, e);
 			} while ((e =
 					  (t = t->v.t + ((unsigned) b & mask_bits[e]))->e) > 16);
 		b >>= t->b;
 		k -= t->b;
 		if (e == 16) {	/* then it's a literal */
-			window[w++] = (unsigned char) t->v.n;
-			if (w == WSIZE) {
-				outcnt = (w), flush_window();
+			gunzip_window[w++] = (unsigned char) t->v.n;
+			if (w == gunzip_wsize) {
+				gunzip_outbuf_count = (w);
+				flush_gunzip_window();
 				w = 0;
 			}
 		} else {		/* it's an EOB or a length */
@@ -439,24 +472,13 @@
 			}
 
 			/* get length of block to copy */
-			while (k < e) {
-				input_char = fgetc(in_file);
-				if (input_char == EOF) return 1;
-				b |= ((unsigned long)input_char) << k;
-				k += 8;
-			}
+			b = fill_bitbuffer(b, &k, e);
 			n = t->v.n + ((unsigned) b & mask_bits[e]);
 			b >>= e;
 			k -= e;
 
 			/* decode distance of block to copy */
-			while (k < (unsigned) bd) {
-				input_char = fgetc(in_file);
-				if (input_char == EOF) return 1;
-				b |= ((unsigned long)input_char) << k;
-				k += 8;
-			}
-
+			b = fill_bitbuffer(b, &k, bd);
 			if ((e = (t = td + ((unsigned) b & md))->e) > 16)
 				do {
 					if (e == 99)
@@ -464,23 +486,13 @@
 					b >>= t->b;
 					k -= t->b;
 					e -= 16;
-					while (k < e) {
-						input_char = fgetc(in_file);
-						if (input_char == EOF) return 1;
-						b |= ((unsigned long)input_char) << k;
-						k += 8;
-					}
+					b = fill_bitbuffer(b, &k, e);
 				} while ((e =
 						  (t =
 						   t->v.t + ((unsigned) b & mask_bits[e]))->e) > 16);
 			b >>= t->b;
 			k -= t->b;
-			while (k < e) {
-				input_char = fgetc(in_file);
-				if (input_char == EOF) return 1;
-				b |= ((unsigned long)input_char) << k;
-				k += 8;
-			}
+			b = fill_bitbuffer(b, &k, e);
 			d = w - t->v.n - ((unsigned) b & mask_bits[e]);
 			b >>= e;
 			k -= e;
@@ -489,60 +501,38 @@
 			do {
 				n -= (e =
 					  (e =
-					   WSIZE - ((d &= WSIZE - 1) > w ? d : w)) > n ? n : e);
-#if !defined(NOMEMCPY) && !defined(DEBUG)
+					   gunzip_wsize - ((d &= gunzip_wsize - 1) > w ? d : w)) > n ? n : e);
+			   /* copy to new buffer to prevent possible overwrite */
 				if (w - d >= e) {	/* (this test assumes unsigned comparison) */
-					memcpy(window + w, window + d, e);
+					memcpy(gunzip_window + w, gunzip_window + d, e);
 					w += e;
 					d += e;
-				} else	/* do it slow to avoid memcpy() overlap */
-#endif							/* !NOMEMCPY */
+				} else {
+				   /* do it slow to avoid memcpy() overlap */
+				   /* !NOMEMCPY */
 					do {
-						window[w++] = window[d++];
+						gunzip_window[w++] = gunzip_window[d++];
 					} while (--e);
-				if (w == WSIZE) {
-					outcnt = (w), flush_window();
+				}
+				if (w == gunzip_wsize) {
+					gunzip_outbuf_count = (w);
+					flush_gunzip_window();
 					w = 0;
 				}
+
 			} while (n);
 		}
 	}
 
 	/* restore the globals from the locals */
-	outcnt = w;			/* restore global window pointer */
-	bb = b;				/* restore global bit buffer */
-	bk = k;
+	gunzip_outbuf_count = w;			/* restore global gunzip_window pointer */
+	gunzip_bb = b;				/* restore global bit buffer */
+	gunzip_bk = k;
 
 	/* done */
 	return 0;
 }
 
-static const unsigned short cplens[] = {	/* Copy lengths for literal codes 257..285 */
-	3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
-	35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0
-};
-
-/* note: see note #13 above about the 258 in this list. */
-static const extra_bits_t cplext[] = {	/* Extra bits for literal codes 257..285 */
-	0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
-	3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 99, 99
-};						/* 99==invalid */
-static const unsigned short cpdist[] = {	/* Copy offsets for distance codes 0..29 */
-	1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
-	257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
-	8193, 12289, 16385, 24577
-};
-static const extra_bits_t cpdext[] = {	/* Extra bits for distance codes */
-	0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
-	7, 7, 8, 8, 9, 9, 10, 10, 11, 11,
-	12, 12, 13, 13
-};
-
-/* Tables for deflate from PKZIP's appnote.txt. */
-static const extra_bits_t border[] = {	/* Order of the bit length code lengths */
-	16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
-};
-
 /*
  * decompress an inflated block
  * e: last block flag
@@ -552,53 +542,43 @@
 static int inflate_block(int *e)
 {
 	unsigned t;			/* block type */
-	register unsigned long b;	/* bit buffer */
-	register unsigned k;	/* number of bits in bit buffer */
-	int input_char;
+	register unsigned int b;	/* bit buffer */
+	unsigned int k;	/* number of bits in bit buffer */
 
 	/* make local bit buffer */
-	b = bb;
-	k = bk;
+
+	b = gunzip_bb;
+	k = gunzip_bk;
 
 	/* read in last block bit */
-	while (k < 1) {
-		input_char = fgetc(in_file);
-		if (input_char == EOF) return 1;
-		b |= ((unsigned long)input_char) << k;
-		k += 8;
-	}
+	b = fill_bitbuffer(b, &k, 1);
 	*e = (int) b & 1;
 	b >>= 1;
 	k -= 1;
 
 	/* read in block type */
-	while (k < 2) {
-		input_char = fgetc(in_file);
-		if (input_char == EOF) return 1;
-		b |= ((unsigned long)input_char) << k;
-		k += 8;
-	}
+	b = fill_bitbuffer(b, &k, 2);
 	t = (unsigned) b & 3;
 	b >>= 2;
 	k -= 2;
 
 	/* restore the global bit buffer */
-	bb = b;
-	bk = k;
+	gunzip_bb = b;
+	gunzip_bk = k;
 
 	/* inflate that block type */
 	switch (t) {
 	case 0:			/* Inflate stored */
 	{
-		unsigned long n;	/* number of bytes in block */
-		unsigned long w;	/* current window position */
-		register unsigned long b_stored;	/* bit buffer */
-		register unsigned long k_stored;	/* number of bits in bit buffer */
+		unsigned int n;	/* number of bytes in block */
+		unsigned int w;	/* current gunzip_window position */
+		unsigned int b_stored;	/* bit buffer */
+		unsigned int k_stored;	/* number of bits in bit buffer */
 
 		/* make local copies of globals */
-		b_stored = bb;	/* initialize bit buffer */
-		k_stored = bk;
-		w = outcnt;		/* initialize window position */
+		b_stored = gunzip_bb;	/* initialize bit buffer */
+		k_stored = gunzip_bk;
+		w = gunzip_outbuf_count;		/* initialize gunzip_window position */
 
 		/* go to byte boundary */
 		n = k_stored & 7;
@@ -606,21 +586,12 @@
 		k_stored -= n;
 
 		/* get the length and its complement */
-		while (k_stored < 16) {
-			input_char = fgetc(in_file);
-			if (input_char == EOF) return 1;
-			b_stored |= ((unsigned long)input_char) << k_stored;
-			k_stored += 8;
-		}
+		b_stored = fill_bitbuffer(b_stored, &k_stored, 16);
 		n = ((unsigned) b_stored & 0xffff);
 		b_stored >>= 16;
 		k_stored -= 16;
-		while (k_stored < 16) {
-			input_char = fgetc(in_file);
-			if (input_char == EOF) return 1;
-			b_stored |= ((unsigned long)input_char) << k_stored;
-			k_stored += 8;
-		}
+
+		b_stored = fill_bitbuffer(b_stored, &k_stored, 16);
 		if (n != (unsigned) ((~b_stored) & 0xffff)) {
 			return 1;	/* error in compressed data */
 		}
@@ -629,15 +600,11 @@
 
 		/* read and output the compressed data */
 		while (n--) {
-			while (k_stored < 8) {
-				input_char = fgetc(in_file);
-				if (input_char == EOF) return 1;
-				b_stored |= ((unsigned long)input_char) << k_stored;
-				k_stored += 8;
-			}
-			window[w++] = (unsigned char) b_stored;
-			if (w == (unsigned long) WSIZE) {
-				outcnt = (w), flush_window();
+			b_stored = fill_bitbuffer(b_stored, &k_stored, 8);
+			gunzip_window[w++] = (unsigned char) b_stored;
+			if (w == (unsigned int) gunzip_wsize) {
+				gunzip_outbuf_count = (w);
+				flush_gunzip_window();
 				w = 0;
 			}
 			b_stored >>= 8;
@@ -645,9 +612,9 @@
 		}
 
 		/* restore the globals from the locals */
-		outcnt = w;		/* restore global window pointer */
-		bb = b_stored;	/* restore global bit buffer */
-		bk = k_stored;
+		gunzip_outbuf_count = w;		/* restore global gunzip_window pointer */
+		gunzip_bb = b_stored;	/* restore global bit buffer */
+		gunzip_bk = k_stored;
 		return 0;
 	}
 	case 1:			/* Inflate fixed 
@@ -659,8 +626,8 @@
 		int i;			/* temporary variable */
 		huft_t *tl;		/* literal/length code table */
 		huft_t *td;		/* distance code table */
-		int bl;			/* lookup bits for tl */
-		int bd;			/* lookup bits for td */
+		unsigned int bl;			/* lookup bits for tl */
+		unsigned int bd;			/* lookup bits for td */
 		unsigned int l[288];	/* length list for huft_build */
 
 		/* set up literal table */
@@ -692,8 +659,9 @@
 		}
 
 		/* decompress until an end-of-block code */
-		if (inflate_codes(tl, td, bl, bd))
+		if (inflate_codes(tl, td, bl, bd)) {
 			return 1;
+		}
 
 		/* free the decoding tables, return */
 		huft_free(tl);
@@ -705,53 +673,41 @@
 		const int dbits = 6;	/* bits in base distance lookup table */
 		const int lbits = 9;	/* bits in base literal/length lookup table */
 
-		int i;			/* temporary variables */
-		unsigned j;
-		unsigned l;		/* last length */
-		unsigned m;		/* mask for bit lengths table */
-		unsigned n;		/* number of lengths to get */
 		huft_t *tl;		/* literal/length code table */
 		huft_t *td;		/* distance code table */
-		int bl;			/* lookup bits for tl */
-		int bd;			/* lookup bits for td */
-		unsigned nb;	/* number of bit length codes */
-		unsigned nl;	/* number of literal/length codes */
-		unsigned nd;	/* number of distance codes */
+		unsigned int i;			/* temporary variables */
+		unsigned int j;
+		unsigned int l;		/* last length */
+		unsigned int m;		/* mask for bit lengths table */
+		unsigned int n;		/* number of lengths to get */
+		unsigned int bl;			/* lookup bits for tl */
+		unsigned int bd;			/* lookup bits for td */
+		unsigned int nb;	/* number of bit length codes */
+		unsigned int nl;	/* number of literal/length codes */
+		unsigned int nd;	/* number of distance codes */
 
-		unsigned ll[286 + 30];	/* literal/length and distance code lengths */
-		register unsigned long b_dynamic;	/* bit buffer */
-		register unsigned k_dynamic;	/* number of bits in bit buffer */
+		unsigned int ll[286 + 30];	/* literal/length and distance code lengths */
+		unsigned int b_dynamic;	/* bit buffer */
+		unsigned int k_dynamic;	/* number of bits in bit buffer */
 
 		/* make local bit buffer */
-		b_dynamic = bb;
-		k_dynamic = bk;
+		b_dynamic = gunzip_bb;
+		k_dynamic = gunzip_bk;
 
 		/* read in table lengths */
-		while (k_dynamic < 5) {
-			input_char = fgetc(in_file);
-			if (input_char == EOF) return 1;
-			b_dynamic |= ((unsigned long)input_char) << k_dynamic;
-			k_dynamic += 8;
-		}
-		nl = 257 + ((unsigned) b_dynamic & 0x1f);	/* number of literal/length codes */
+		b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 5);
+		nl = 257 + ((unsigned int) b_dynamic & 0x1f);	/* number of literal/length codes */
+
 		b_dynamic >>= 5;
 		k_dynamic -= 5;
-		while (k_dynamic < 5) {
-			input_char = fgetc(in_file);
-			if (input_char == EOF) return 1;
-			b_dynamic |= ((unsigned long)input_char) << k_dynamic;
-			k_dynamic += 8;
-		}
-		nd = 1 + ((unsigned) b_dynamic & 0x1f);	/* number of distance codes */
+		b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 5);
+		nd = 1 + ((unsigned int) b_dynamic & 0x1f);	/* number of distance codes */
+
 		b_dynamic >>= 5;
 		k_dynamic -= 5;
-		while (k_dynamic < 4) {
-			input_char = fgetc(in_file);
-			if (input_char == EOF) return 1;
-			b_dynamic |= ((unsigned long)input_char) << k_dynamic;
-			k_dynamic += 8;
-		}
-		nb = 4 + ((unsigned) b_dynamic & 0xf);	/* number of bit length codes */
+		b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 4);
+		nb = 4 + ((unsigned int) b_dynamic & 0xf);	/* number of bit length codes */
+
 		b_dynamic >>= 4;
 		k_dynamic -= 4;
 		if (nl > 286 || nd > 30) {
@@ -760,13 +716,8 @@
 
 		/* read in bit-length-code lengths */
 		for (j = 0; j < nb; j++) {
-			while (k_dynamic < 3) {
-				input_char = fgetc(in_file);
-				if (input_char == EOF) return 1;
-				b_dynamic |= ((unsigned long)input_char) << k_dynamic;
-				k_dynamic += 8;
-			}
-			ll[border[j]] = (unsigned) b_dynamic & 7;
+			b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 3);
+			ll[border[j]] = (unsigned int) b_dynamic & 7;
 			b_dynamic >>= 3;
 			k_dynamic -= 3;
 		}
@@ -776,7 +727,8 @@
 
 		/* build decoding table for trees--single level, 7 bit lookup */
 		bl = 7;
-		if ((i = huft_build(ll, 19, 19, NULL, NULL, &tl, &bl)) != 0) {
+		i = huft_build(ll, 19, 19, NULL, NULL, &tl, &bl);
+		if (i != 0) {
 			if (i == 1) {
 				huft_free(tl);
 			}
@@ -787,46 +739,31 @@
 		n = nl + nd;
 		m = mask_bits[bl];
 		i = l = 0;
-		while ((unsigned) i < n) {
-			while (k_dynamic < (unsigned) bl) {
-				input_char = fgetc(in_file);
-				if (input_char == EOF) return 1;
-				b_dynamic |= ((unsigned long)input_char) << k_dynamic;
-				k_dynamic += 8;
-			}
-			j = (td = tl + ((unsigned) b_dynamic & m))->b;
+		while ((unsigned int) i < n) {
+			b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, (unsigned int)bl);
+			j = (td = tl + ((unsigned int) b_dynamic & m))->b;
 			b_dynamic >>= j;
 			k_dynamic -= j;
 			j = td->v.n;
 			if (j < 16) {	/* length of code in bits (0..15) */
 				ll[i++] = l = j;	/* save last length in l */
 			} else if (j == 16) {	/* repeat last length 3 to 6 times */
-				while (k_dynamic < 2) {
-					input_char = fgetc(in_file);
-					if (input_char == EOF) return 1;
-					b_dynamic |= ((unsigned long)input_char) << k_dynamic;
-					k_dynamic += 8;
-				}
-				j = 3 + ((unsigned) b_dynamic & 3);
+				b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 2);
+				j = 3 + ((unsigned int) b_dynamic & 3);
 				b_dynamic >>= 2;
 				k_dynamic -= 2;
-				if ((unsigned) i + j > n) {
+				if ((unsigned int) i + j > n) {
 					return 1;
 				}
 				while (j--) {
 					ll[i++] = l;
 				}
 			} else if (j == 17) {	/* 3 to 10 zero length codes */
-				while (k_dynamic < 3) {
-					input_char = fgetc(in_file);
-					if (input_char == EOF) return 1;
-					b_dynamic |= ((unsigned long)input_char) << k_dynamic;
-					k_dynamic += 8;
-				}
-				j = 3 + ((unsigned) b_dynamic & 7);
+				b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 3);
+				j = 3 + ((unsigned int) b_dynamic & 7);
 				b_dynamic >>= 3;
 				k_dynamic -= 3;
-				if ((unsigned) i + j > n) {
+				if ((unsigned int) i + j > n) {
 					return 1;
 				}
 				while (j--) {
@@ -834,16 +771,11 @@
 				}
 				l = 0;
 			} else {	/* j == 18: 11 to 138 zero length codes */
-				while (k_dynamic < 7) {
-					input_char = fgetc(in_file);
-					if (input_char == EOF) return 1;
-					b_dynamic |= ((unsigned long)input_char) << k_dynamic;
-					k_dynamic += 8;
-				}
-				j = 11 + ((unsigned) b_dynamic & 0x7f);
+				b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 7);
+				j = 11 + ((unsigned int) b_dynamic & 0x7f);
 				b_dynamic >>= 7;
 				k_dynamic -= 7;
-				if ((unsigned) i + j > n) {
+				if ((unsigned int) i + j > n) {
 					return 1;
 				}
 				while (j--) {
@@ -857,22 +789,24 @@
 		huft_free(tl);
 
 		/* restore the global bit buffer */
-		bb = b_dynamic;
-		bk = k_dynamic;
+		gunzip_bb = b_dynamic;
+		gunzip_bk = k_dynamic;
 
 		/* build the decoding tables for literal/length and distance codes */
 		bl = lbits;
+
 		if ((i = huft_build(ll, nl, 257, cplens, cplext, &tl, &bl)) != 0) {
 			if (i == 1) {
-				error_msg("Incomplete literal tree");
+				error_msg_and_die("Incomplete literal tree");
 				huft_free(tl);
 			}
 			return i;	/* incomplete code set */
 		}
+
 		bd = dbits;
 		if ((i = huft_build(ll + nl, nd, 0, cpdist, cpdext, &td, &bd)) != 0) {
 			if (i == 1) {
-				error_msg("incomplete distance tree");
+				error_msg_and_die("incomplete distance tree");
 				huft_free(td);
 			}
 			huft_free(tl);
@@ -880,8 +814,9 @@
 		}
 
 		/* decompress until an end-of-block code */
-		if (inflate_codes(tl, td, bl, bd))
+		if (inflate_codes(tl, td, bl, bd)) {
 			return 1;
+		}
 
 		/* free the decoding tables, return */
 		huft_free(tl);
@@ -890,6 +825,7 @@
 	}
 	default:
 		/* bad block type */
+		error_msg("bad block type %d\n", t);
 		return 2;
 	}
 }
@@ -897,72 +833,24 @@
 /*
  * decompress an inflated entry
  *
- * GLOBAL VARIABLES: outcnt, bk, bb, hufts, inptr
+ * GLOBAL VARIABLES: gunzip_outbuf_count, bk, gunzip_bb, hufts, inptr
  */
-extern int inflate(FILE * in, FILE * out)
+extern int inflate(int in, int out)
 {
+	typedef void (*sig_type) (int);
 	int e;				/* last block flag */
 	int r;				/* result code */
 	unsigned h = 0;		/* maximum struct huft's malloc'ed */
 
-	/* initialize window, bit buffer */
-	outcnt = 0;
-	bk = 0;
-	bb = 0;
-
-	in_file = in;
-	out_file = out;
-
 	/* Allocate all global buffers (for DYN_ALLOC option) */
-	window = xmalloc((size_t) (((2L * WSIZE) + 1L) * sizeof(unsigned char)));
-	bytes_out = 0L;
+	gunzip_window = xmalloc(0x8000);
+	gunzip_outbuf_count = 0;
+	gunzip_bytes_out = 0;
 
-	/* Create the crc table */
-	make_crc_table();
+	gunzip_src_fd = in;
+	gunzip_dst_fd = out;
 
-	/* decompress until the last block */
-	do {
-		hufts = 0;
-		if ((r = inflate_block(&e)) != 0) {
-			return r;
-		}
-		if (hufts > h) {
-			h = hufts;
-		}
-	} while (!e);
-
-	/* Undo too much lookahead. The next read will be byte aligned so we
-	 * can discard unused bits in the last meaningful byte.
-	 */
-	while (bk >= 8) {
-		bk -= 8;
-		ungetc((bb << bk), in_file);
-	}
-
-	/* flush out window */
-	flush_window();
-	free(window);
-	free(crc_table);
-
-	/* return success */
-	return 0;
-}
-
-/* ===========================================================================
- * Unzip in to out.  This routine works on gzip files only.
- *
- * IN assertions: the buffer inbuf contains already the beginning of
- *   the compressed data, from offsets inptr to insize-1 included.
- *   The magic header has already been checked. The output buffer is cleared.
- * in, out: input and output file descriptors
- */
-extern int unzip(FILE * l_in_file, FILE * l_out_file)
-{
-	unsigned char buf[8];	/* extended local header */
-	unsigned char flags;	/* compression flags */
-	typedef void (*sig_type) (int);
-	unsigned short i;
-	unsigned char magic[2];
+	gunzip_in_buffer = malloc(8);
 
 	if (signal(SIGINT, SIG_IGN) != SIG_IGN) {
 		(void) signal(SIGINT, (sig_type) abort_gzip);
@@ -973,97 +861,44 @@
 	}
 #endif
 
-	magic[0] = fgetc(l_in_file);
-	magic[1] = fgetc(l_in_file);
+	/* initialize gunzip_window, bit buffer */
+	gunzip_bk = 0;
+	gunzip_bb = 0;
 
-#ifdef CONFIG_FEATURE_UNCOMPRESS
-	/* Magic header for compress files, 1F 9d = \037\235 */
-	if ((magic[0] == 0x1F) && (magic[1] == 0x9d)) {
-		return uncompress(l_in_file, l_out_file);
+	/* Create the crc table */
+	make_gunzip_crc_table();
+
+	/* decompress until the last block */
+	do {
+		gunzip_hufts = 0;
+		r = inflate_block(&e);
+		if (r != 0) {
+			error_msg_and_die("inflate error %d", r);
+			return r;
 	}
-#endif
+		if (gunzip_hufts > h) {
+			h = gunzip_hufts;
+	}
+	} while (!e);
 
-	/* Magic header for gzip files, 1F 8B = \037\213 */
-	if ((magic[0] != 0x1F) || (magic[1] != 0x8b)) {
-		error_msg("Invalid gzip magic");
-		return EXIT_FAILURE;
+	/* write any buffered uncompressed data */
+	flush_gunzip_window();
+	free(gunzip_window);
+
+	/* Cleanup */
+	free(gunzip_crc_table);
+
+	/* Store unused bytes in a global buffer so calling applets can access it */
+	gunzip_in_buffer_count = 0;
+	if (gunzip_bk >= 8) {
+		/* Undo too much lookahead. The next read will be byte aligned
+		 * so we can discard unused bits in the last meaningful byte. */
+		gunzip_in_buffer[gunzip_in_buffer_count] = gunzip_bb & 0xff;
+		gunzip_in_buffer_count++;
+		gunzip_bb >>= 8;
+		gunzip_bk -= 8;
 	}
 
-	/* Check the compression method */
-	if (fgetc(l_in_file) != 8) /* also catches EOF */ {
-		error_msg("Unknown compression method");
-		return (-1);
-	}
-
-	flags = (unsigned char) fgetc(l_in_file);
-
-	/* Ignore time stamp(4), extra flags(1), OS type(1) */
-	for (i = 0; i < 6; i++) {
-		fgetc(l_in_file);
-	}
-
-	if (flags & 0x04) {
-		/* bit 2 set: extra field present */
-		const unsigned short extra =
-			fgetc(l_in_file) + (fgetc(l_in_file) << 8);
-		if (feof(in_file)) return 1;
-		for (i = 0; i < extra; i++) {
-			fgetc(l_in_file);
-		}
-	}
-
-	/* Discard original name if any */
-	if (flags & 0x08) {
-		/* bit 3 set: original file name present */
-		while (fgetc(l_in_file) != 0 && !feof(l_in_file));	/* null */
-	}
-
-	/* Discard file comment if any */
-	if (flags & 0x10) {
-		/* bit 4 set: file comment present */
-		while (fgetc(l_in_file) != 0 && !feof(l_in_file));	/* null */
-	}
-
-	/* Decompress */
-	if (inflate(l_in_file, l_out_file) != 0) {
-		error_msg("invalid compressed data--format violated");
-	}
-
-	/* Get the crc and original length
-	 * crc32  (see algorithm.doc)
-	 * uncompressed input size modulo 2^32
-	 */
-	fread(buf, 1, 8, l_in_file);
-
-	/* Validate decompression - crc */
-	if ((unsigned int) ((buf[0] | (buf[1] << 8)) |
-						((buf[2] | (buf[3] << 8)) << 16)) !=
-		(crc ^ 0xffffffffL)) {
-		error_msg("invalid compressed data--crc error");
-	}
-	/* Validate decompression - size */
-	if (((buf[4] | (buf[5] << 8)) | ((buf[6] | (buf[7] << 8)) << 16)) !=
-		(unsigned long) bytes_out) {
-		error_msg("invalid compressed data--length error");
-	}
-
+	/* return success */
 	return 0;
 }
-
-/*
- * This needs access to global variables window and crc_table, so its not in its own file.
- */
-extern void gz_close(int gunzip_pid)
-{
-	if (kill(gunzip_pid, SIGTERM) == -1) {
-		error_msg_and_die
-			("***  Couldnt kill old gunzip process *** aborting");
-	}
-
-	if (waitpid(gunzip_pid, NULL, 0) == -1) {
-		printf("Couldnt wait ?");
-	}
-
-	free(window);
-	free(crc_table);
-}
diff --git a/archival/rpm2cpio.c b/archival/rpm2cpio.c
index 22051da..e1e9398 100644
--- a/archival/rpm2cpio.c
+++ b/archival/rpm2cpio.c
@@ -18,10 +18,13 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
-
-#include "busybox.h"
+#include <sys/types.h>
 #include <netinet/in.h> /* For ntohl & htonl function */
+#include <fcntl.h>
+#include <unistd.h>
 #include <string.h>
+#include "busybox.h"
+#include "unarchive.h"
 
 #define RPM_MAGIC "\355\253\356\333"
 #define RPM_HEADER_MAGIC "\216\255\350"
@@ -45,46 +48,54 @@
 	u_int32_t size; /* Size of store (4 bytes) */
 };
 
-void skip_header(FILE *rpmfile)
+void skip_header(int rpm_fd)
 {
 	struct rpm_header header;
 
-	fread(&header, sizeof(struct rpm_header), 1, rpmfile);
-	if (strncmp((char *) &header.magic, RPM_HEADER_MAGIC, 3) != 0) error_msg_and_die("Invalid RPM header magic"); /* Invalid magic */
-	if (header.version != 1) error_msg_and_die("Unsupported RPM header version"); /* This program only supports v1 headers */
+	xread_all(rpm_fd, &header, sizeof(struct rpm_header));
+	if (strncmp((char *) &header.magic, RPM_HEADER_MAGIC, 3) != 0) {
+		error_msg_and_die("Invalid RPM header magic"); /* Invalid magic */
+	}
+	if (header.version != 1) {
+		error_msg_and_die("Unsupported RPM header version"); /* This program only supports v1 headers */
+	}
 	header.entries = ntohl(header.entries);
 	header.size = ntohl(header.size);
-	fseek (rpmfile, 16 * header.entries, SEEK_CUR); /* Seek past index entries */
-	fseek (rpmfile, header.size, SEEK_CUR); /* Seek past store */
+	lseek (rpm_fd, 16 * header.entries, SEEK_CUR); /* Seek past index entries */
+	lseek (rpm_fd, header.size, SEEK_CUR); /* Seek past store */
 }
 
 /* No getopt required */
 extern int rpm2cpio_main(int argc, char **argv)
 {
 	struct rpm_lead lead;
-	int gunzip_pid;
-	FILE *rpmfile, *cpiofile;
+	int rpm_fd;
 
 	if (argc == 1) {
-		rpmfile = stdin;
+		rpm_fd = fileno(stdin);
 	} else {
-		rpmfile = xfopen(argv[1], "r");
-		/* set the buffer size */
-		setvbuf(rpmfile, NULL, _IOFBF, 0x8000);
+		rpm_fd = xopen(argv[1], O_RDONLY);
 	}
 
-	fread (&lead, sizeof(struct rpm_lead), 1, rpmfile);
-	if (strncmp((char *) &lead.magic, RPM_MAGIC, 4) != 0) error_msg_and_die("Invalid RPM magic"); /* Just check the magic, the rest is irrelevant */
+	xread_all(rpm_fd, &lead, sizeof(struct rpm_lead));
+	if (strncmp((char *) &lead.magic, RPM_MAGIC, 4) != 0) {
+		error_msg_and_die("Invalid RPM magic"); /* Just check the magic, the rest is irrelevant */
+	}
+
 	/* Skip the signature header */
-	skip_header(rpmfile);
-	fseek(rpmfile, (8 - (ftell(rpmfile) % 8)) % 8, SEEK_CUR); /* Pad to 8 byte boundary */
+	skip_header(rpm_fd);
+	data_align(rpm_fd, lseek(rpm_fd, 0, SEEK_CUR), 8);
+
 	/* Skip the main header */
-	skip_header(rpmfile);
+	skip_header(rpm_fd);
 
-	cpiofile = gz_open(rpmfile, &gunzip_pid);
+	check_header_gzip(rpm_fd);
+	if (inflate(rpm_fd, fileno(stdout)) != 0) {
+		error_msg("Error inflating");
+	}
+	check_trailer_gzip(rpm_fd);
 
-	copyfd(fileno(cpiofile), fileno(stdout));
-	gz_close(gunzip_pid);
-	fclose(rpmfile);
+	close(rpm_fd);
+
 	return 0;
 }
diff --git a/archival/tar.c b/archival/tar.c
index 2ab0229..6ef698e 100644
--- a/archival/tar.c
+++ b/archival/tar.c
@@ -115,7 +115,7 @@
 							   tarball lives, so we can avoid trying 
 							   to include the tarball into itself */
 	int verboseFlag;	/* Whether to print extra stuff or not */
-	char **excludeList;	/* List of files to not include */
+	const llist_t *excludeList;	/* List of files to not include */
 	HardLinkInfo *hlInfoHead;	/* Hard Link Tracking Information */
 	HardLinkInfo *hlInfo;	/* Hard Link Info for the current file */
 };
@@ -325,16 +325,15 @@
 }
 
 # if defined CONFIG_FEATURE_TAR_EXCLUDE
-static inline int exclude_file(char **excluded_files, const char *file)
+static inline int exclude_file(const llist_t *excluded_files, const char *file)
 {
-	int i;
-
-	if (excluded_files == NULL)
+	if (excluded_files == NULL) {
 		return 0;
+	}
 
-	for (i = 0; excluded_files[i] != NULL; i++) {
-		if (excluded_files[i][0] == '/') {
-			if (fnmatch(excluded_files[i], file,
+	while (excluded_files) {
+		if (excluded_files->data[0] == '/') {
+			if (fnmatch(excluded_files->data, file,
 						FNM_PATHNAME | FNM_LEADING_DIR) == 0)
 				return 1;
 		} else {
@@ -342,11 +341,12 @@
 
 			for (p = file; p[0] != '\0'; p++) {
 				if ((p == file || p[-1] == '/') && p[0] != '/' &&
-					fnmatch(excluded_files[i], p,
+					fnmatch(excluded_files->data, p,
 							FNM_PATHNAME | FNM_LEADING_DIR) == 0)
 					return 1;
 			}
 		}
+		excluded_files = excluded_files->link;
 	}
 
 	return 0;
@@ -455,8 +455,8 @@
 	return (TRUE);
 }
 
-static inline int writeTarFile(const char *tarName, int verboseFlag,
-							   char **argv, char **excludeList, int gzip)
+static inline int writeTarFile(const char *tarName, const int verboseFlag,
+							   const llist_t *include, const llist_t *exclude, const int gzip)
 {
 #ifdef CONFIG_FEATURE_TAR_GZIP
 	int gzipDataPipe[2] = { -1, -1 };
@@ -471,8 +471,9 @@
 	tbInfo.hlInfoHead = NULL;
 
 	/* Make sure there is at least one file to tar up.  */
-	if (*argv == NULL)
+	if (include == NULL) {
 		error_msg_and_die("Cowardly refusing to create an empty archive");
+	}
 
 	/* Open the tar file for writing.  */
 	if (tarName == NULL) {
@@ -544,15 +545,16 @@
 	}
 #endif
 
-	tbInfo.excludeList = excludeList;
+	tbInfo.excludeList = exclude;
 
 	/* Read the directory/files and iterate over them one at a time */
-	while (*argv != NULL) {
-		if (!recursive_action(*argv++, TRUE, FALSE, FALSE,
+	while (include) {
+		if (!recursive_action(include->data, TRUE, FALSE, FALSE,
 							  writeFileToTarball, writeFileToTarball,
 							  (void *) &tbInfo)) {
 			errorFlag = TRUE;
 		}
+		include = include->link;
 	}
 	/* Write two empty blocks to the end of the archive */
 	for (size = 0; size < (2 * TAR_BLOCK_SIZE); size++) {
@@ -582,64 +584,48 @@
 }
 #endif							/* tar_create */
 
-void append_file_to_list(const char *new_name, char ***list, int *list_count)
+#ifdef CONFIG_FEATURE_TAR_EXCLUDE
+static const llist_t *append_file_list_to_list(const char *filename, const llist_t *list)
 {
-	*list = realloc(*list, sizeof(char *) * (*list_count + 2));
-	(*list)[*list_count] = xstrdup(new_name);
-	(*list_count)++;
-	(*list)[*list_count] = NULL;
-}
-
-void append_file_list_to_list(char *filename, char ***name_list,
-							  int *num_of_entries)
-{
-	FILE *src_stream;
-	char *line;
-
-	src_stream = xfopen(filename, "r");
-	while ((line = get_line_from_file(src_stream)) != NULL) {
+	FILE *src_stream = xfopen(filename, "r");
+	while(1) {
+		char *line = get_line_from_file(src_stream);
+		if (line == NULL) {
+			break;
+		}
 		chomp(line);
-		append_file_to_list(line, name_list, num_of_entries);
+		list = add_to_list(list, line);
 		free(line);
 	}
 	fclose(src_stream);
+
+	return (list);
 }
+#endif
 
 int tar_main(int argc, char **argv)
 {
-	enum untar_funct_e {
-		/* This is optional */
-		untar_unzip = 1,
-		/* Require one and only one of these */
-		untar_list = 2,
-		untar_create = 4,
-		untar_extract = 8
-	};
-
-	FILE *src_stream = NULL;
-	FILE *uncompressed_stream = NULL;
-	char **include_list = NULL;
-	char **exclude_list = NULL;
-	char *src_filename = NULL;
-	char *dst_prefix = NULL;
-	int opt;
-	unsigned short untar_funct = 0;
-	unsigned short untar_funct_required = 0;
-	unsigned short extract_function = 0;
-	int include_list_count = 0;
-
-#ifdef CONFIG_FEATURE_TAR_EXCLUDE
-	int exclude_list_count = 0;
-#endif
 #ifdef CONFIG_FEATURE_TAR_GZIP
-	int gunzip_pid;
-	int gz_fd = 0;
+	char (*get_header_ptr)(archive_handle_t *) = get_header_tar;
+#endif
+	archive_handle_t *tar_handle;
+	int opt;
+	char *base_dir = NULL;
+
+#ifdef CONFIG_FEATURE_TAR_CREATE
+	char *src_filename = NULL;
+	unsigned char tar_create = FALSE;
 #endif
 
 	if (argc < 2) {
 		show_usage();
 	}
 
+	/* Initialise default values */
+	tar_handle = init_handle();
+	tar_handle->src_fd = fileno(stdin);
+	tar_handle->flags = ARCHIVE_CREATE_LEADING_DIRS;
+
 	/* Prepend '-' to the first argument if required */
 	if (argv[1][0] != '-') {
 		char *tmp = xmalloc(strlen(argv[1]) + 2);
@@ -648,61 +634,69 @@
 		strcpy(tmp + 1, argv[1]);
 		argv[1] = tmp;
 	}
-
 	while ((opt = getopt(argc, argv, "ctxT:X:C:f:Opvz")) != -1) {
 		switch (opt) {
-
 			/* One and only one of these is required */
+#ifdef CONFIG_FEATURE_TAR_CREATE
 		case 'c':
-			untar_funct_required |= untar_create;
+			tar_create = TRUE;
 			break;
+#endif
 		case 't':
-			untar_funct_required |= untar_list;
-			extract_function |= extract_list | extract_unconditional;
+			if ((tar_handle->action_header == header_list) || 
+				(tar_handle->action_header == header_verbose_list)) {
+				tar_handle->action_header = header_verbose_list;
+			} else {
+				tar_handle->action_header = header_list;
+			}
 			break;
 		case 'x':
-			untar_funct_required |= untar_extract;
-			extract_function |=
-				(extract_all_to_fs | extract_unconditional |
-				 extract_create_leading_dirs);
+			tar_handle->action_data = data_extract_all;
 			break;
 
 			/* These are optional */
 			/* Exclude or Include files listed in <filename> */
 #ifdef CONFIG_FEATURE_TAR_EXCLUDE
 		case 'X':
-			append_file_list_to_list(optarg, &exclude_list,
-									 &exclude_list_count);
+			tar_handle->reject =
+				append_file_list_to_list(optarg, tar_handle->reject);
 			break;
 #endif
 		case 'T':
 			/* by default a list is an include list */
-			append_file_list_to_list(optarg, &include_list,
-									 &include_list_count);
 			break;
-
 		case 'C':		/* Change to dir <optarg> */
-			/* Make sure dst_prefix ends in a '/' */
-			dst_prefix = concat_path_file(optarg, "/");
+			base_dir = optarg;
 			break;
 		case 'f':		/* archive filename */
-			if (strcmp(optarg, "-") == 0) {
-				src_filename = NULL;
-			} else {
-				src_filename = xstrdup(optarg);
-			}
+#ifdef CONFIG_FEATURE_TAR_CREATE
+			src_filename = optarg;
+#endif
+			tar_handle->src_fd = xopen(optarg, O_RDONLY);
 			break;
-		case 'O':
-			extract_function |= extract_to_stdout;
+		case 'O':		/* To stdout */
+			tar_handle->action_data = data_extract_to_stdout;
 			break;
 		case 'p':
+			tar_handle->flags |= ARCHIVE_PRESERVE_DATE;
 			break;
 		case 'v':
-			extract_function |= extract_verbose_list;
+			if ((tar_handle->action_header == header_list) || 
+				(tar_handle->action_header == header_verbose_list)) {
+				tar_handle->action_header = header_verbose_list;
+			} else {
+				tar_handle->action_header = header_list;
+			}
 			break;
 #ifdef CONFIG_FEATURE_TAR_GZIP
 		case 'z':
-			untar_funct |= untar_unzip;
+			get_header_ptr = get_header_tar_gz;
+			break;
+#endif
+#ifdef CONFIG_FEATURE_TAR_BZIP2
+			/* Not enabled yet */
+		case 'j':
+			archive_handle->archive_action = bunzip2;
 			break;
 #endif
 		default:
@@ -710,77 +704,54 @@
 		}
 	}
 
-	/* Make sure the valid arguments were passed */
-	if (untar_funct_required == 0) {
-		error_msg_and_die("You must specify one of the `-ctx' options");
-	}
-	if ((untar_funct_required != untar_create) &&
-		(untar_funct_required != untar_extract) &&
-		(untar_funct_required != untar_list)) {
-		error_msg_and_die("You may not specify more than one `ctx' option.");
-	}
-	untar_funct |= untar_funct_required;
-
 	/* Setup an array of filenames to work with */
+	/* TODO: This is the same as in ar, seperate function ? */
 	while (optind < argc) {
-		append_file_to_list(argv[optind], &include_list, &include_list_count);
+		char absolute_path[PATH_MAX];
+
+		realpath(argv[optind], absolute_path);
+		tar_handle->accept = add_to_list(tar_handle->accept, absolute_path);
 		optind++;
-	}
-	if (extract_function & (extract_list | extract_all_to_fs)) {
-		if (dst_prefix == NULL) {
-			dst_prefix = xstrdup("./");
-		}
-
-		/* Setup the source of the tar data */
-		if (src_filename != NULL) {
-			src_stream = xfopen(src_filename, "r");
-		} else {
-			src_stream = stdin;
-		}
-#ifdef CONFIG_FEATURE_TAR_GZIP
-		/* Get a binary tree of all the tar file headers */
-		if (untar_funct & untar_unzip) {
-			uncompressed_stream = gz_open(src_stream, &gunzip_pid);
+#ifdef CONFIG_FEATURE_TAR_EXCLUDE
+		if (tar_handle->reject) {
+			tar_handle->filter = filter_accept_reject_list;
 		} else
-#endif							/* CONFIG_FEATURE_TAR_GZIP */
-			uncompressed_stream = src_stream;
+#endif
+			tar_handle->filter = filter_accept_list;
+		}
 
-		/* extract or list archive */
-		unarchive(uncompressed_stream, stdout, &get_header_tar,
-				  extract_function, dst_prefix, include_list, exclude_list);
-		fclose(uncompressed_stream);
-	}
+	if ((base_dir) && (chdir(base_dir))) {
+		perror_msg_and_die("Couldnt chdir");
+		}
+
 #ifdef CONFIG_FEATURE_TAR_CREATE
 	/* create an archive */
-	else if (untar_funct & untar_create) {
+	if (tar_create == TRUE) {
 		int verboseFlag = FALSE;
 		int gzipFlag = FALSE;
 
-#ifdef CONFIG_FEATURE_TAR_GZIP
-		if (untar_funct & untar_unzip)
+# ifdef CONFIG_FEATURE_TAR_GZIP
+		if (get_header_ptr == get_header_tar_gz) {
 			gzipFlag = TRUE;
-
-#endif							/* CONFIG_FEATURE_TAR_GZIP */
-		if (extract_function & extract_verbose_list)
+		}
+# endif
+		if (tar_handle->action_header == header_verbose_list) {
 			verboseFlag = TRUE;
-
-		writeTarFile(src_filename, verboseFlag, include_list, exclude_list,
-					 gzipFlag);
 	}
-#endif							/* CONFIG_FEATURE_TAR_CREATE */
-
-	/* Cleanups */
-#ifdef CONFIG_FEATURE_TAR_GZIP
-	if (!(untar_funct & untar_create) && (untar_funct & untar_unzip)) {
-		fclose(src_stream);
-		close(gz_fd);
-		gz_close(gunzip_pid);
-	}
-#endif							/* CONFIG_FEATURE_TAR_GZIP */
-#ifdef CONFIG_FEATURE_CLEAN_UP
-	if (src_filename) {
-		free(src_filename);
-	}
+		writeTarFile(src_filename, verboseFlag, tar_handle->accept,
+			tar_handle->reject, gzipFlag);
+	} else 
 #endif
-	return (EXIT_SUCCESS);
+#ifdef CONFIG_FEATURE_TAR_GZIP
+		if (get_header_ptr == get_header_tar_gz) {
+			get_header_tar_gz(tar_handle);
+		} else
+#endif
+			while (get_header_tar(tar_handle) == EXIT_SUCCESS);
+
+#ifdef CONFIG_FEATURE_CLEAN_UP
+	close(tar_handle->src_fd);
+#endif
+
+	return(EXIT_SUCCESS);
 }
diff --git a/archival/unzip.c b/archival/unzip.c
index ae0d7c1..5a22d24 100644
--- a/archival/unzip.c
+++ b/archival/unzip.c
@@ -20,6 +20,10 @@
  *
  */
 
+/* For reference to format see http://www.pkware.com/support/appnote.html */
+
+/* TODO Endian issues, exclude, should we accept input from stdin ? */
+
 #include <fcntl.h>
 #include <getopt.h>
 #include <stdlib.h>
@@ -28,67 +32,213 @@
 #include "unarchive.h"
 #include "busybox.h"
 
+#define ZIP_FILEHEADER_MAGIC	0x04034b50
+#define ZIP_CDS_MAGIC			0x02014b50
+#define ZIP_CDS_END_MAGIC		0x06054b50
+#define ZIP_DD_MAGIC			0x08074b50
+
+extern unsigned int gunzip_crc;
+extern unsigned int gunzip_bytes_out;
+
+static void header_list_unzip(const file_header_t *file_header)
+{
+	printf("  inflating: %s\n", file_header->name);
+}
+
+static void header_verbose_list_unzip(const file_header_t *file_header)
+{
+	unsigned int dostime = (unsigned int) file_header->mtime;
+
+	/* can printf arguments cut of the decade component ? */
+	unsigned short year = 1980 + ((dostime & 0xfe000000) >> 25);
+	while (year >= 100) {
+		year -= 100;
+	}
+
+	printf("%9u  %02u-%02u-%02u %02u:%02u   %s\n",
+		(unsigned int) file_header->size,
+		(dostime & 0x01e00000) >> 21,
+		(dostime & 0x001f0000) >> 16,
+		year,
+		(dostime & 0x0000f800) >> 11,
+		(dostime & 0x000007e0) >> 5,
+		file_header->name);
+}
+
 extern int unzip_main(int argc, char **argv)
 {
-	FILE *src_stream;
-	int extract_function = extract_all_to_fs | extract_create_leading_dirs;
-	char **extract_names = NULL;
-	char **exclude_names = NULL;
-	int opt = 0;
-	int num_of_entries = 0;
-	int exclude = 0;
-	char *outdir = "./";
-	FILE *msgout = stdout;
+	union {
+		unsigned char raw[26];
+		struct {
+			unsigned short version;	/* 0-1 */
+			unsigned short flags;	/* 2-3 */
+			unsigned short method;	/* 4-5 */
+			unsigned short modtime;	/* 6-7 */
+			unsigned short moddate;	/* 8-9 */
+			unsigned int crc32 __attribute__ ((packed));		/* 10-13 */
+			unsigned int cmpsize __attribute__ ((packed));;	/* 14-17 */
+			unsigned int ucmpsize __attribute__ ((packed));;	/* 18-21 */
+			unsigned short filename_len;		/* 22-23 */
+			unsigned short extra_len;		/* 24-25 */
+		} formated __attribute__ ((packed));
+	} zip_header;
 
-	while ((opt = getopt(argc, argv, "lnopqxd:")) != -1) {
+	archive_handle_t *archive_handle;
+	unsigned int total_size = 0;
+	unsigned int total_entries = 0;
+	char *base_dir = NULL;
+	int opt = 0;
+
+	/* Initialise */
+	archive_handle = init_handle();
+	archive_handle->action_data = NULL;
+	archive_handle->action_header = header_list_unzip;
+
+	while ((opt = getopt(argc, argv, "lnopqd:")) != -1) {
 		switch (opt) {
-			case 'l':
-				extract_function |= extract_verbose_list;
-				extract_function ^= extract_all_to_fs;
+			case 'l':	/* list */
+				archive_handle->action_header = header_verbose_list_unzip;
+				archive_handle->action_data = data_skip;
 				break;
-			case 'n':
+			case 'n':	/* never overwright existing files */
 				break;
 			case 'o':
-				extract_function |= extract_unconditional;
+				archive_handle->flags = ARCHIVE_EXTRACT_UNCONDITIONAL;
 				break;
-			case 'p':
-				extract_function |= extract_to_stdout;
-				extract_function ^= extract_all_to_fs;
-				/* FALLTHROUGH */
-			case 'q':
-				msgout = xfopen("/dev/null", "w");
+			case 'p':	/* extract files to stdout */
+				archive_handle->action_data = data_extract_to_stdout;
 				break;
-			case 'd':
-				outdir = xstrdup(optarg);
-				strcat(outdir, "/");
+			case 'q':	/* Extract files quietly */
+				archive_handle->action_header = header_skip;
 				break;
-			case 'x':
-				exclude = 1;
+			case 'd':	/* Extract files to specified base directory*/
+				base_dir = optarg;
 				break;
+#if 0
+			case 'x':	/* Exclude the specified files */
+				archive_handle->filter = filter_accept_reject_list;
+				break;
+#endif
+			default:
+				show_usage();
 		}
 	}
 
-	if (optind == argc) {
+	if (argc == optind) {
 		show_usage();
 	}
 
-	if (*argv[optind] == '-') src_stream = stdin;
-	else src_stream = xfopen(argv[optind++], "r");
+	printf("Archive:  %s\n", argv[optind]);
+	if (archive_handle->action_header == header_verbose_list_unzip) {
+		printf("  Length     Date   Time    Name\n");
+		printf(" --------    ----   ----    ----\n");
+	}
+
+	if (*argv[optind] == '-') {
+		archive_handle->src_fd = fileno(stdin);
+		} else {
+		archive_handle->src_fd = xopen(argv[optind++], O_RDONLY);
+		}
+
+	if ((base_dir) && (chdir(base_dir))) {
+		perror_msg_and_die("Couldnt chdir");
+		}
 
 	while (optind < argc) {
-		if (exclude) {
-			exclude_names = xrealloc(exclude_names, sizeof(char *) * (num_of_entries + 2));
-			exclude_names[num_of_entries] = xstrdup(argv[optind]);
-		} else {
-			extract_names = xrealloc(extract_names, sizeof(char *) * (num_of_entries + 2));
-			extract_names[num_of_entries] = xstrdup(argv[optind]);
-		}
-		num_of_entries++;
-		if (exclude) exclude_names[num_of_entries] = NULL;
-		else extract_names[num_of_entries] = NULL;
+		archive_handle->filter = filter_accept_list;
+		archive_handle->accept = add_to_list(archive_handle->accept, argv[optind]);
 		optind++;
 	}
 
-	unarchive(src_stream, msgout, &get_header_zip, extract_function, outdir, extract_names, exclude_names);
-	return EXIT_SUCCESS;
+	while (1) {
+		unsigned int magic;
+		int dst_fd;
+
+		/* TODO Endian issues */
+		xread_all(archive_handle->src_fd, &magic, 4);
+		archive_handle->offset += 4;
+
+		if (magic == ZIP_CDS_MAGIC) {
+			break;
+		}
+		else if (magic != ZIP_FILEHEADER_MAGIC) {
+			error_msg_and_die("Invlaide zip magic");
+		}
+
+		/* Read the file header */
+		xread_all(archive_handle->src_fd, zip_header.raw, 26);
+		archive_handle->offset += 26;
+		archive_handle->file_header->mode = S_IFREG | 0777;
+
+		if (zip_header.formated.method != 8) {
+			error_msg_and_die("Unsupported compression method %d\n", zip_header.formated.method);
+		}
+
+		/* Read filename */
+		archive_handle->file_header->name = xmalloc(zip_header.formated.filename_len + 1);
+		xread_all(archive_handle->src_fd, archive_handle->file_header->name, zip_header.formated.filename_len);
+		archive_handle->offset += zip_header.formated.filename_len;
+		archive_handle->file_header->name[zip_header.formated.filename_len] = '\0';
+
+		/* Skip extra header bits */
+		archive_handle->file_header->size = zip_header.formated.extra_len;
+		data_skip(archive_handle);
+		archive_handle->offset += zip_header.formated.extra_len;
+
+		/* Handle directories */
+		archive_handle->file_header->mode = S_IFREG | 0777;
+		if (last_char_is(archive_handle->file_header->name, '/')) {
+			archive_handle->file_header->mode ^= S_IFREG;
+			archive_handle->file_header->mode |= S_IFDIR;
+		}
+
+		/* Data section */
+		archive_handle->file_header->size = zip_header.formated.cmpsize;
+		if (archive_handle->action_data) {
+			archive_handle->action_data(archive_handle);
+		} else {
+			dst_fd = xopen(archive_handle->file_header->name, O_WRONLY | O_CREAT);
+			inflate(archive_handle->src_fd, dst_fd);
+			close(dst_fd);
+			chmod(archive_handle->file_header->name, archive_handle->file_header->mode);
+
+			/* Validate decompression - crc */
+			if (zip_header.formated.crc32 != (gunzip_crc ^ 0xffffffffL)) {
+				error_msg("Invalid compressed data--crc error");
+			}
+
+			/* Validate decompression - size */
+			if (gunzip_bytes_out != zip_header.formated.ucmpsize) {
+				error_msg("Invalid compressed data--length error");
+			}
+		}
+
+		/* local file descriptor section */
+		archive_handle->offset += zip_header.formated.cmpsize;
+		/* This ISNT unix time */
+		archive_handle->file_header->mtime = zip_header.formated.modtime | (zip_header.formated.moddate << 16);
+		archive_handle->file_header->size = zip_header.formated.ucmpsize;
+		total_size += archive_handle->file_header->size;
+		total_entries++;
+
+		archive_handle->action_header(archive_handle->file_header);
+
+		/* Data descriptor section */
+		if (zip_header.formated.flags & 4) {
+			/* skip over duplicate crc, compressed size and uncompressed size */
+			unsigned short i;
+			for (i = 0; i != 12; i++) {
+				xread_char(archive_handle->src_fd);
+			}
+			archive_handle->offset += 12;
+		}
+	}
+	/* Central directory section */
+
+	if (archive_handle->action_header == header_verbose_list_unzip) {
+		printf(" --------                   -------\n");
+		printf("%9d                   %d files\n", total_size, total_entries);
+	}
+
+	return(EXIT_SUCCESS);
 }
diff --git a/include/libbb.h b/include/libbb.h
index 6ab942b..bd0d1e9 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -48,10 +48,6 @@
 # include "sha1.h"
 #endif
 
-/* Compatability with ANSI C */
-#ifndef inline
-# define inline
-#endif
 
 #if (__GNU_LIBRARY__ < 5) && (!defined __dietlibc__)
 /* libc5 doesn't define socklen_t */
@@ -74,6 +70,9 @@
 #define BUF_SIZE        8192
 #define EXPAND_ALLOC    1024
 
+static inline int is_decimal(int ch) { return ((ch >= '0') && (ch <= '9')); }
+static inline int is_octal(int ch)   { return ((ch >= '0') && (ch <= '7')); }
+
 /* Macros for min/max.  */
 #ifndef MIN
 #define	MIN(a,b) (((a)<(b))?(a):(b))
@@ -83,8 +82,6 @@
 #define	MAX(a,b) (((a)>(b))?(a):(b))
 #endif
 
-
-
 extern void show_usage(void) __attribute__ ((noreturn));
 extern void error_msg(const char *s, ...) __attribute__ ((format (printf, 1, 2)));
 extern void error_msg_and_die(const char *s, ...) __attribute__ ((noreturn, format (printf, 1, 2)));
@@ -228,10 +225,7 @@
 int read_package_field(const char *package_buffer, char **field_name, char **field_value);
 char *fgets_str(FILE *file, const char *terminating_string);
 
-extern int inflate(FILE *in, FILE *out);
-extern int unzip(FILE *l_in_file, FILE *l_out_file);
-extern void gz_close(int gunzip_pid);
-extern FILE *gz_open(FILE *compressed_file, int *pid);
+extern int inflate(int in, int out);
 
 extern struct hostent *xgethostbyname(const char *name);
 extern struct hostent *xgethostbyname2(const char *name, int af);
@@ -335,4 +329,11 @@
 extern struct spwd *pwd_to_spwd(const struct passwd *pw);
 extern int obscure(const char *old, const char *newval, const struct passwd *pwdp);
 
+//extern int xopen(const char *pathname, int flags, mode_t mode);
+extern int xopen(const char *pathname, int flags);
+extern ssize_t xread(int fd, void *buf, size_t count);
+extern ssize_t xread_all_eof(int fd, void *buf, size_t count);
+extern void xread_all(int fd, void *buf, size_t count);
+extern unsigned char xread_char(int fd);
+
 #endif /* __LIBCONFIG_H__ */
diff --git a/include/unarchive.h b/include/unarchive.h
index ffddc89..e564e95 100644
--- a/include/unarchive.h
+++ b/include/unarchive.h
@@ -1,21 +1,19 @@
-#include <stdio.h>		/* for FILE */
-#include <unistd.h>		/* for off_t */
+#ifndef	__UNARCHIVE_H__
+#define	__UNARCHIVE_H__
 
-enum extract_functions_e {
-	extract_verbose_list = 1,
-	extract_list = 2,
-	extract_one_to_buffer = 4,
-	extract_to_stdout = 8,
-	extract_all_to_fs = 16,
-	extract_preserve_date = 32,
-	extract_data_tar_gz = 64,
-	extract_control_tar_gz = 128,
-	extract_unzip_only = 256,
-	extract_unconditional = 512,
-	extract_create_leading_dirs = 1024,
-	extract_quiet = 2048,
-	extract_exclude_list = 4096
-};
+#define ARCHIVE_PRESERVE_DATE	1
+#define ARCHIVE_CREATE_LEADING_DIRS		2
+#define ARCHIVE_EXTRACT_UNCONDITIONAL	4
+#define ARCHIVE_EXTRACT_QUIET	8
+
+#include <sys/types.h>
+
+typedef struct gunzip_s {
+	unsigned short buffer_count;
+	unsigned char *buffer;
+	unsigned int crc;
+	unsigned int count;
+} gunzip_t;
 
 typedef struct file_headers_s {
 	char *name;
@@ -26,23 +24,70 @@
 	mode_t mode;
 	time_t mtime;
 	dev_t device;
-	int (*extract_func) (FILE *, FILE *);
 } file_header_t;
 
-file_header_t *get_header_ar(FILE * in_file);
-file_header_t *get_header_cpio(FILE * src_stream);
-file_header_t *get_header_tar(FILE * tar_stream);
-file_header_t *get_header_zip(FILE * zip_stream);
+typedef struct llist_s {
+	const char *data;
+	const struct llist_s *link;
+} llist_t;
 
-void seek_sub_file(FILE * src_stream, const int count);
+typedef struct archive_handle_s {
+	/* define if the header and data compenent should processed */
+	char (*filter)(const llist_t *, const llist_t *, const char *);
+	const llist_t *accept;
+	const llist_t *reject;
 
-extern off_t archive_offset;
+	/* Contains the processed header entry */
+	file_header_t *file_header;
 
-char *unarchive(FILE * src_stream, FILE * out_stream,
-				file_header_t * (*get_headers) (FILE *),
-				const int extract_function, const char *prefix,
-				char **include_name, char **exclude_name);
+	/* process the header component, e.g. tar -t */
+	void (*action_header)(const file_header_t *);
 
-char *deb_extract(const char *package_filename, FILE * out_stream,
-				  const int extract_function, const char *prefix,
-				  const char *filename);
+	/* process the data componenet, e.g. extract to filesystem */
+	void (*action_data)(struct archive_handle_s *);
+	char (*action_data_subarchive)(struct archive_handle_s *);
+
+	/* Contains the handle to a sub archive */
+	struct archive_handle_s *sub_archive;
+
+	/* The raw stream as read from disk or stdin */
+	int src_fd;
+
+	/* Count the number of bytes processed */
+	off_t offset;
+
+	/* Misc. stuff */
+	unsigned char flags;
+
+} archive_handle_t;
+
+extern archive_handle_t *init_handle(void);
+
+extern char filter_accept_all(const llist_t *accept_list, const llist_t *reject_list, const char *key);
+extern char filter_accept_list(const llist_t *accept_list, const llist_t *reject_list, const char *key);
+extern char filter_accept_reject_list(const llist_t *accept_list, const llist_t *reject_list, const char *key);
+
+extern void unpack_ar_archive(archive_handle_t *ar_archive);
+
+extern void data_gunzip(archive_handle_t *archive_handle);
+extern void data_skip(archive_handle_t *archive_handle);
+extern void data_extract_all(archive_handle_t *archive_handle);
+extern void data_extract_to_stdout(archive_handle_t *archive_handle);
+
+extern void header_skip(const file_header_t *file_header);
+extern void header_list(const file_header_t *file_header);
+extern void header_verbose_list(const file_header_t *file_header);
+
+extern void check_header_gzip(int src_fd);
+extern void check_trailer_gzip(int src_fd);
+
+extern char get_header_ar(archive_handle_t *archive_handle);
+extern char get_header_tar(archive_handle_t *archive_handle);
+extern char get_header_tar_gz(archive_handle_t *archive_handle);
+
+//extern void seek_sub_file(int src_fd, unsigned int amount);
+extern const unsigned short data_align(const int src_fd, const unsigned int offset, const unsigned short align_to);
+extern const llist_t *add_to_list(const llist_t *old_head, const char *new_item);
+extern int copy_file_chunk_fd(int src_fd, int dst_fd, unsigned long long chunksize);
+
+#endif
diff --git a/libbb/Makefile.in b/libbb/Makefile.in
index e7ca9aa..449d23c 100644
--- a/libbb/Makefile.in
+++ b/libbb/Makefile.in
@@ -28,20 +28,22 @@
 	copy_file_chunk.c dump.c libc5.c device_open.c error_msg.c \
 	error_msg_and_die.c fgets_str.c find_mount_point.c find_pid_by_name.c \
 	find_root_device.c full_read.c full_write.c get_console.c \
-	get_last_path_component.c get_line_from_file.c gz_open.c human_readable.c \
-	isdirectory.c kernel_version.c loop.c mode_string.c module_syscalls.c mtab.c \
-	mtab_file.c my_getgrnam.c my_getgrgid.c my_getpwnam.c my_getpwnamegid.c \
-	my_getpwuid.c parse_mode.c parse_number.c perror_msg.c perror_msg_and_die.c \
-	print_file.c process_escape_sequence.c read_package_field.c recursive_action.c \
-	safe_read.c safe_strncpy.c syscalls.c syslog_msg_with_name.c time_string.c \
-	trim.c unzip.c uncompress.c vdprintf.c verror_msg.c vperror_msg.c wfopen.c \
-	xgetcwd.c xreadlink.c xregcomp.c interface.c remove_file.c last_char_is.c \
-	copyfd.c vherror_msg.c herror_msg.c herror_msg_and_die.c xgethostbyname.c \
-	dirname.c make_directory.c create_icmp_socket.c u_signal_names.c arith.c \
-	simplify_path.c inet_common.c inode_hash.c obscure.c pwd2spwd.c xfuncs.c \
-	correct_password.c change_identity.c setup_environment.c run_shell.c \
-	pw_encrypt.c restricted_shell.c xgethostbyname2.c create_icmp6_socket.c \
-       xconnect.c bb_asprintf.c
+	get_last_path_component.c get_line_from_file.c  \
+	human_readable.c isdirectory.c kernel_version.c loop.c \
+	mode_string.c module_syscalls.c mtab.c mtab_file.c my_getgrnam.c \
+	my_getgrgid.c my_getpwnam.c my_getpwnamegid.c my_getpwuid.c \
+	parse_mode.c parse_number.c perror_msg.c perror_msg_and_die.c \
+	print_file.c process_escape_sequence.c read_package_field.c \
+	recursive_action.c safe_read.c safe_strncpy.c syscalls.c \
+	syslog_msg_with_name.c time_string.c trim.c unzip.c uncompress.c \
+	vdprintf.c verror_msg.c vperror_msg.c wfopen.c xgetcwd.c xreadlink.c \
+	xregcomp.c interface.c remove_file.c last_char_is.c copyfd.c \
+	vherror_msg.c herror_msg.c herror_msg_and_die.c xgethostbyname.c \
+	dirname.c make_directory.c create_icmp_socket.c u_signal_names.c \
+	arith.c simplify_path.c inet_common.c inode_hash.c obscure.c \
+	pwd2spwd.c xfuncs.c correct_password.c change_identity.c \
+	setup_environment.c run_shell.c pw_encrypt.c restricted_shell.c \
+	xgethostbyname2.c create_icmp6_socket.c xconnect.c bb_asprintf.c
 
 LIBBB_OBJS=$(patsubst %.c,$(LIBBB_DIR)%.o, $(LIBBB_SRC))
 
diff --git a/libbb/unzip.c b/libbb/unzip.c
index d840670..20bf88f 100644
--- a/libbb/unzip.c
+++ b/libbb/unzip.c
@@ -65,40 +65,11 @@
 #include <signal.h>
 #include <stdlib.h>
 #include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
 #include "config.h"
-#include "libbb.h"
-
-#ifdef CONFIG_FEATURE_UNCOMPRESS
-int uncompress(FILE * in, FILE * out);
-#endif
-
-static FILE *in_file, *out_file;
-
-/* these are freed by gz_close */
-static unsigned char *window;
-static unsigned long *crc_table;
-
-static unsigned long crc;	/* shift register contents */
-
-/* Return codes from gzip */
-#define ERROR 1
-
-/*
- * window size--must be a power of two, and
- *  at least 32K for zip's deflate method
- */
-#define WSIZE 0x8000
-
-/* If BMAX needs to be larger than 16, then h and x[] should be ulg. */
-#define BMAX 16	/* maximum bit length of any code (16 for explode) */
-#define N_MAX 288	/* maximum number of codes in any set */
-
-static long bytes_out;	/* number of output bytes */
-static unsigned long outcnt;	/* bytes in output buffer */
-
-static unsigned hufts;	/* track memory usage */
-static unsigned long bb;	/* bit buffer */
-static unsigned bk;		/* bits in bit buffer */
+#include "busybox.h"
+#include "unarchive.h"
 
 typedef struct huft_s {
 	unsigned char e;	/* number of extra bits or operation */
@@ -109,70 +80,111 @@
 	} v;
 } huft_t;
 
+static int gunzip_src_fd;
+static int gunzip_dst_fd;
+unsigned int gunzip_bytes_out;	/* number of output bytes */
+static unsigned int gunzip_outbuf_count;	/* bytes in output buffer */
+
+/* This is used to sanify any unused bits from the bitbuffer 
+ * so they arent skipped when reading trailers (trailing headers) */
+unsigned char gunzip_in_buffer_count;
+unsigned char *gunzip_in_buffer;
+
+/* gunzip_window size--must be a power of two, and
+ *  at least 32K for zip's deflate method */
+static const int gunzip_wsize = 0x8000;
+
+static unsigned char *gunzip_window;
+static unsigned int *gunzip_crc_table;
+unsigned int gunzip_crc;
+
+/* If BMAX needs to be larger than 16, then h and x[] should be ulg. */
+#define BMAX 16	/* maximum bit length of any code (16 for explode) */
+#define N_MAX 288	/* maximum number of codes in any set */
+
+static unsigned int gunzip_hufts;	/* track memory usage */
+static unsigned int gunzip_bb;	/* bit buffer */
+static unsigned char gunzip_bk;	/* bits in bit buffer */
+
 static const unsigned short mask_bits[] = {
-	0x0000,
-	0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff,
+	0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff,
 	0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff
 };
 
-/* static int error_number = 0; */
-/* ========================================================================
- * Signal and error handler.
- */
+/* Copy lengths for literal codes 257..285 */
+static const unsigned short cplens[] = {
+	3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59,
+		67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0
+};
+
+/* note: see note #13 above about the 258 in this list. */
+/* Extra bits for literal codes 257..285 */
+static const unsigned char cplext[] = {
+	0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5,
+		5, 5, 5, 0, 99, 99
+};						/* 99==invalid */
+
+/* Copy offsets for distance codes 0..29 */
+static const unsigned short cpdist[] = {
+	1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513,
+		769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577
+};
+
+/* Extra bits for distance codes */
+static const unsigned char cpdext[] = {
+	0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10,
+		11, 11, 12, 12, 13, 13
+};
+
+/* Tables for deflate from PKZIP's appnote.txt. */
+/* Order of the bit length code lengths */
+static const unsigned char border[] = {
+	16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
+};
+
+static unsigned int fill_bitbuffer(unsigned int bitbuffer, unsigned int *current, const unsigned int required)
+{
+	while (*current < required) {
+		bitbuffer |= ((unsigned int) xread_char(gunzip_src_fd)) << *current;
+		*current += 8;
+	}
+
+	return(bitbuffer);
+}
 
 static void abort_gzip(void)
 {
 	error_msg("gzip aborted\n");
-	exit(ERROR);
+	exit(-1);
 }
 
-static void make_crc_table(void)
+static void make_gunzip_crc_table(void)
 {
-	const unsigned long poly = 0xedb88320;	/* polynomial exclusive-or pattern */
+	const unsigned int poly = 0xedb88320;	/* polynomial exclusive-or pattern */
 	unsigned short i;	/* counter for all possible eight bit values */
 
 	/* initial shift register value */
-	crc = 0xffffffffL;
-	crc_table = (unsigned long *) malloc(256 * sizeof(unsigned long));
+	gunzip_crc = 0xffffffffL;
+	gunzip_crc_table = (unsigned int *) malloc(256 * sizeof(unsigned int));
 
 	/* Compute and print table of CRC's, five per line */
 	for (i = 0; i < 256; i++) {
-		unsigned long table_entry;	/* crc shift register */
-		char k;			/* byte being shifted into crc apparatus */
+		unsigned int table_entry;	/* crc shift register */
+		unsigned char k;	/* byte being shifted into crc apparatus */
 
 		table_entry = i;
 		/* The idea to initialize the register with the byte instead of
 		   * zero was stolen from Haruhiko Okumura's ar002
 		 */
 		for (k = 8; k; k--) {
-			table_entry =
-				table_entry & 1 ? (table_entry >> 1) ^ poly : table_entry >>
-				1;
-		}
-		crc_table[i] = table_entry;
+			if (table_entry & 1) {
+				table_entry = (table_entry >> 1) ^ poly;
+			} else {
+				table_entry >>= 1;
 	}
-}
-
-/* ===========================================================================
- * Write the output window window[0..outcnt-1] and update crc and bytes_out.
- * (Used for the decompressed data only.)
- */
-static void flush_window(void)
-{
-	int n;
-
-	if (outcnt == 0)
-		return;
-
-	for (n = 0; n < outcnt; n++) {
-		crc = crc_table[((int) crc ^ (window[n])) & 0xff] ^ (crc >> 8);
 	}
-
-	if (fwrite(window, 1, outcnt, out_file) != outcnt) {
-		error_msg_and_die("Couldnt write");
+		gunzip_crc_table[i] = table_entry;
 	}
-	bytes_out += (unsigned long) outcnt;
-	outcnt = 0;
 }
 
 /*
@@ -183,7 +195,8 @@
  */
 static int huft_free(huft_t * t)
 {
-	huft_t *p, *q;
+	huft_t *p;
+	huft_t *q;
 
 	/* Go through linked list, freeing from the malloced (t[-1]) address. */
 	p = t;
@@ -195,8 +208,6 @@
 	return 0;
 }
 
-typedef unsigned char extra_bits_t;
-
 /* Given a list of code lengths and a maximum table size, make a set of
  * tables to decode that set of codes.  Return zero on success, one if
  * the given code set is incomplete (the tables are still built in this
@@ -213,7 +224,7 @@
  */
 static int huft_build(unsigned int *b, const unsigned int n,
 					  const unsigned int s, const unsigned short *d,
-					  const extra_bits_t * e, huft_t ** t, int *m)
+					  const unsigned char *e, huft_t ** t, int *m)
 {
 	unsigned a;			/* counter for codes of length k */
 	unsigned c[BMAX + 1];	/* bit length count table */
@@ -251,26 +262,35 @@
 
 	/* Find minimum and maximum length, bound *m by those */
 	l = *m;
-	for (j = 1; j <= BMAX; j++)
-		if (c[j])
+	for (j = 1; j <= BMAX; j++) {
+		if (c[j]) {
 			break;
+		}
+	}
 	k = j;				/* minimum code length */
-	if ((unsigned) l < j)
+	if ((unsigned) l < j) {
 		l = j;
-	for (i = BMAX; i; i--)
-		if (c[i])
+	}
+	for (i = BMAX; i; i--) {
+		if (c[i]) {
 			break;
+		}
+	}
 	g = i;				/* maximum code length */
-	if ((unsigned) l > i)
+	if ((unsigned) l > i) {
 		l = i;
+	}
 	*m = l;
 
 	/* Adjust last length count to fill out codes, if needed */
-	for (y = 1 << j; j < i; j++, y <<= 1)
-		if ((y -= c[j]) < 0)
+	for (y = 1 << j; j < i; j++, y <<= 1) {
+		if ((y -= c[j]) < 0) {
 			return 2;	/* bad input: more codes than bits */
-	if ((y -= c[i]) < 0)
+		}
+	}
+	if ((y -= c[i]) < 0) {
 		return 2;
+	}
 	c[i] += y;
 
 	/* Generate starting offsets into the value table for each length */
@@ -285,8 +305,9 @@
 	p = b;
 	i = 0;
 	do {
-		if ((j = *p++) != 0)
+		if ((j = *p++) != 0) {
 			v[x[j]++] = i;
+		}
 	} while (++i < n);
 
 	/* Generate the Huffman codes and for each, make the table entries */
@@ -314,8 +335,9 @@
 					f -= a + 1;	/* deduct codes from patterns left */
 					xp = c + k;
 					while (++j < z) {	/* try smaller tables up to z bits */
-						if ((f <<= 1) <= *++xp)
+						if ((f <<= 1) <= *++xp) {
 							break;	/* enough codes to use up j bits */
+						}
 						f -= *xp;	/* else deduct codes from patterns */
 					}
 				}
@@ -324,7 +346,7 @@
 				/* allocate and link in new table */
 				q = (huft_t *) xmalloc((z + 1) * sizeof(huft_t));
 
-				hufts += z + 1;	/* track memory usage */
+				gunzip_hufts += z + 1;	/* track memory usage */
 				*t = q + 1;	/* link to list for huft_free() */
 				*(t = &(q->v.t)) = NULL;
 				u[h] = ++q;	/* table starts after link */
@@ -342,9 +364,9 @@
 
 			/* set up table entry in r */
 			r.b = (unsigned char) (k - w);
-			if (p >= v + n)
+			if (p >= v + n) {
 				r.e = 99;	/* out of values--invalid code */
-			else if (*p < s) {
+			} else if (*p < s) {
 				r.e = (unsigned char) (*p < 256 ? 16 : 15);	/* 256 is end-of-block code */
 				r.v.n = (unsigned short) (*p);	/* simple code is just the value */
 				p++;	/* one compiler does not like *p++ */
@@ -355,12 +377,14 @@
 
 			/* fill code-like entries with r */
 			f = 1 << (k - w);
-			for (j = i >> w; j < z; j += f)
+			for (j = i >> w; j < z; j += f) {
 				q[j] = r;
+			}
 
 			/* backwards increment the k-bit code i */
-			for (j = 1 << (k - 1); i & j; j >>= 1)
+			for (j = 1 << (k - 1); i & j; j >>= 1) {
 				i ^= j;
+			}
 			i ^= j;
 
 			/* backup over finished tables */
@@ -374,6 +398,25 @@
 	return y != 0 && g != 1;
 }
 
+/* ===========================================================================
+ * Write the output gunzip_window gunzip_window[0..gunzip_outbuf_count-1] and update crc and gunzip_bytes_out.
+ * (Used for the decompressed data only.)
+ */
+static void flush_gunzip_window(void)
+{
+	int n;
+
+	for (n = 0; n < gunzip_outbuf_count; n++) {
+		gunzip_crc = gunzip_crc_table[((int) gunzip_crc ^ (gunzip_window[n])) & 0xff] ^ (gunzip_crc >> 8);
+	}
+
+	if (write(gunzip_dst_fd, gunzip_window, gunzip_outbuf_count) != gunzip_outbuf_count) {
+		error_msg_and_die("Couldnt write");
+	}
+	gunzip_bytes_out += gunzip_outbuf_count;
+	gunzip_outbuf_count = 0;
+}
+
 /*
  * inflate (decompress) the codes in a deflated (compressed) block.
  * Return an error code or zero if it all goes ok.
@@ -381,32 +424,26 @@
  * tl, td: literal/length and distance decoder tables
  * bl, bd: number of bits decoded by tl[] and td[]
  */
-static int inflate_codes(huft_t * tl, huft_t * td, int bl, int bd)
+static int inflate_codes(huft_t * tl, huft_t * td, const unsigned int bl, const unsigned int bd)
 {
-	register unsigned long e;	/* table entry flag/number of extra bits */
-	unsigned long n, d;	/* length and index for copy */
-	unsigned long w;	/* current window position */
+	unsigned int e;	/* table entry flag/number of extra bits */
+	unsigned int n, d;	/* length and index for copy */
+	unsigned int w;	/* current gunzip_window position */
 	huft_t *t;			/* pointer to table entry */
-	unsigned ml, md;	/* masks for bl and bd bits */
-	register unsigned long b;	/* bit buffer */
-	register unsigned k;	/* number of bits in bit buffer */
-	register int input_char;
+	unsigned int ml, md;	/* masks for bl and bd bits */
+	unsigned int b;	/* bit buffer */
+	unsigned int k;			/* number of bits in bit buffer */
 
 	/* make local copies of globals */
-	b = bb;				/* initialize bit buffer */
-	k = bk;
-	w = outcnt;			/* initialize window position */
+	b = gunzip_bb;				/* initialize bit buffer */
+	k = gunzip_bk;
+	w = gunzip_outbuf_count;			/* initialize gunzip_window position */
 
 	/* inflate the coded data */
 	ml = mask_bits[bl];	/* precompute masks for speed */
 	md = mask_bits[bd];
-	for (;;) {			/* do until end of block */
-		while (k < (unsigned) bl) {
- 			input_char = fgetc(in_file);
- 			if (input_char == EOF) return 1;
- 			b |= ((unsigned long)input_char) << k;
-			k += 8;
-		}
+	while (1) {			/* do until end of block */
+		b = fill_bitbuffer(b, &k, bl);
 		if ((e = (t = tl + ((unsigned) b & ml))->e) > 16)
 			do {
 				if (e == 99) {
@@ -415,20 +452,16 @@
 				b >>= t->b;
 				k -= t->b;
 				e -= 16;
-				while (k < e) {
-					input_char = fgetc(in_file);
-					if (input_char == EOF) return 1;
-					b |= ((unsigned long)input_char) << k;
-					k += 8;
-				}
+				b = fill_bitbuffer(b, &k, e);
 			} while ((e =
 					  (t = t->v.t + ((unsigned) b & mask_bits[e]))->e) > 16);
 		b >>= t->b;
 		k -= t->b;
 		if (e == 16) {	/* then it's a literal */
-			window[w++] = (unsigned char) t->v.n;
-			if (w == WSIZE) {
-				outcnt = (w), flush_window();
+			gunzip_window[w++] = (unsigned char) t->v.n;
+			if (w == gunzip_wsize) {
+				gunzip_outbuf_count = (w);
+				flush_gunzip_window();
 				w = 0;
 			}
 		} else {		/* it's an EOB or a length */
@@ -439,24 +472,13 @@
 			}
 
 			/* get length of block to copy */
-			while (k < e) {
-				input_char = fgetc(in_file);
-				if (input_char == EOF) return 1;
-				b |= ((unsigned long)input_char) << k;
-				k += 8;
-			}
+			b = fill_bitbuffer(b, &k, e);
 			n = t->v.n + ((unsigned) b & mask_bits[e]);
 			b >>= e;
 			k -= e;
 
 			/* decode distance of block to copy */
-			while (k < (unsigned) bd) {
-				input_char = fgetc(in_file);
-				if (input_char == EOF) return 1;
-				b |= ((unsigned long)input_char) << k;
-				k += 8;
-			}
-
+			b = fill_bitbuffer(b, &k, bd);
 			if ((e = (t = td + ((unsigned) b & md))->e) > 16)
 				do {
 					if (e == 99)
@@ -464,23 +486,13 @@
 					b >>= t->b;
 					k -= t->b;
 					e -= 16;
-					while (k < e) {
-						input_char = fgetc(in_file);
-						if (input_char == EOF) return 1;
-						b |= ((unsigned long)input_char) << k;
-						k += 8;
-					}
+					b = fill_bitbuffer(b, &k, e);
 				} while ((e =
 						  (t =
 						   t->v.t + ((unsigned) b & mask_bits[e]))->e) > 16);
 			b >>= t->b;
 			k -= t->b;
-			while (k < e) {
-				input_char = fgetc(in_file);
-				if (input_char == EOF) return 1;
-				b |= ((unsigned long)input_char) << k;
-				k += 8;
-			}
+			b = fill_bitbuffer(b, &k, e);
 			d = w - t->v.n - ((unsigned) b & mask_bits[e]);
 			b >>= e;
 			k -= e;
@@ -489,60 +501,38 @@
 			do {
 				n -= (e =
 					  (e =
-					   WSIZE - ((d &= WSIZE - 1) > w ? d : w)) > n ? n : e);
-#if !defined(NOMEMCPY) && !defined(DEBUG)
+					   gunzip_wsize - ((d &= gunzip_wsize - 1) > w ? d : w)) > n ? n : e);
+			   /* copy to new buffer to prevent possible overwrite */
 				if (w - d >= e) {	/* (this test assumes unsigned comparison) */
-					memcpy(window + w, window + d, e);
+					memcpy(gunzip_window + w, gunzip_window + d, e);
 					w += e;
 					d += e;
-				} else	/* do it slow to avoid memcpy() overlap */
-#endif							/* !NOMEMCPY */
+				} else {
+				   /* do it slow to avoid memcpy() overlap */
+				   /* !NOMEMCPY */
 					do {
-						window[w++] = window[d++];
+						gunzip_window[w++] = gunzip_window[d++];
 					} while (--e);
-				if (w == WSIZE) {
-					outcnt = (w), flush_window();
+				}
+				if (w == gunzip_wsize) {
+					gunzip_outbuf_count = (w);
+					flush_gunzip_window();
 					w = 0;
 				}
+
 			} while (n);
 		}
 	}
 
 	/* restore the globals from the locals */
-	outcnt = w;			/* restore global window pointer */
-	bb = b;				/* restore global bit buffer */
-	bk = k;
+	gunzip_outbuf_count = w;			/* restore global gunzip_window pointer */
+	gunzip_bb = b;				/* restore global bit buffer */
+	gunzip_bk = k;
 
 	/* done */
 	return 0;
 }
 
-static const unsigned short cplens[] = {	/* Copy lengths for literal codes 257..285 */
-	3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
-	35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0
-};
-
-/* note: see note #13 above about the 258 in this list. */
-static const extra_bits_t cplext[] = {	/* Extra bits for literal codes 257..285 */
-	0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
-	3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 99, 99
-};						/* 99==invalid */
-static const unsigned short cpdist[] = {	/* Copy offsets for distance codes 0..29 */
-	1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
-	257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
-	8193, 12289, 16385, 24577
-};
-static const extra_bits_t cpdext[] = {	/* Extra bits for distance codes */
-	0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
-	7, 7, 8, 8, 9, 9, 10, 10, 11, 11,
-	12, 12, 13, 13
-};
-
-/* Tables for deflate from PKZIP's appnote.txt. */
-static const extra_bits_t border[] = {	/* Order of the bit length code lengths */
-	16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
-};
-
 /*
  * decompress an inflated block
  * e: last block flag
@@ -552,53 +542,43 @@
 static int inflate_block(int *e)
 {
 	unsigned t;			/* block type */
-	register unsigned long b;	/* bit buffer */
-	register unsigned k;	/* number of bits in bit buffer */
-	int input_char;
+	register unsigned int b;	/* bit buffer */
+	unsigned int k;	/* number of bits in bit buffer */
 
 	/* make local bit buffer */
-	b = bb;
-	k = bk;
+
+	b = gunzip_bb;
+	k = gunzip_bk;
 
 	/* read in last block bit */
-	while (k < 1) {
-		input_char = fgetc(in_file);
-		if (input_char == EOF) return 1;
-		b |= ((unsigned long)input_char) << k;
-		k += 8;
-	}
+	b = fill_bitbuffer(b, &k, 1);
 	*e = (int) b & 1;
 	b >>= 1;
 	k -= 1;
 
 	/* read in block type */
-	while (k < 2) {
-		input_char = fgetc(in_file);
-		if (input_char == EOF) return 1;
-		b |= ((unsigned long)input_char) << k;
-		k += 8;
-	}
+	b = fill_bitbuffer(b, &k, 2);
 	t = (unsigned) b & 3;
 	b >>= 2;
 	k -= 2;
 
 	/* restore the global bit buffer */
-	bb = b;
-	bk = k;
+	gunzip_bb = b;
+	gunzip_bk = k;
 
 	/* inflate that block type */
 	switch (t) {
 	case 0:			/* Inflate stored */
 	{
-		unsigned long n;	/* number of bytes in block */
-		unsigned long w;	/* current window position */
-		register unsigned long b_stored;	/* bit buffer */
-		register unsigned long k_stored;	/* number of bits in bit buffer */
+		unsigned int n;	/* number of bytes in block */
+		unsigned int w;	/* current gunzip_window position */
+		unsigned int b_stored;	/* bit buffer */
+		unsigned int k_stored;	/* number of bits in bit buffer */
 
 		/* make local copies of globals */
-		b_stored = bb;	/* initialize bit buffer */
-		k_stored = bk;
-		w = outcnt;		/* initialize window position */
+		b_stored = gunzip_bb;	/* initialize bit buffer */
+		k_stored = gunzip_bk;
+		w = gunzip_outbuf_count;		/* initialize gunzip_window position */
 
 		/* go to byte boundary */
 		n = k_stored & 7;
@@ -606,21 +586,12 @@
 		k_stored -= n;
 
 		/* get the length and its complement */
-		while (k_stored < 16) {
-			input_char = fgetc(in_file);
-			if (input_char == EOF) return 1;
-			b_stored |= ((unsigned long)input_char) << k_stored;
-			k_stored += 8;
-		}
+		b_stored = fill_bitbuffer(b_stored, &k_stored, 16);
 		n = ((unsigned) b_stored & 0xffff);
 		b_stored >>= 16;
 		k_stored -= 16;
-		while (k_stored < 16) {
-			input_char = fgetc(in_file);
-			if (input_char == EOF) return 1;
-			b_stored |= ((unsigned long)input_char) << k_stored;
-			k_stored += 8;
-		}
+
+		b_stored = fill_bitbuffer(b_stored, &k_stored, 16);
 		if (n != (unsigned) ((~b_stored) & 0xffff)) {
 			return 1;	/* error in compressed data */
 		}
@@ -629,15 +600,11 @@
 
 		/* read and output the compressed data */
 		while (n--) {
-			while (k_stored < 8) {
-				input_char = fgetc(in_file);
-				if (input_char == EOF) return 1;
-				b_stored |= ((unsigned long)input_char) << k_stored;
-				k_stored += 8;
-			}
-			window[w++] = (unsigned char) b_stored;
-			if (w == (unsigned long) WSIZE) {
-				outcnt = (w), flush_window();
+			b_stored = fill_bitbuffer(b_stored, &k_stored, 8);
+			gunzip_window[w++] = (unsigned char) b_stored;
+			if (w == (unsigned int) gunzip_wsize) {
+				gunzip_outbuf_count = (w);
+				flush_gunzip_window();
 				w = 0;
 			}
 			b_stored >>= 8;
@@ -645,9 +612,9 @@
 		}
 
 		/* restore the globals from the locals */
-		outcnt = w;		/* restore global window pointer */
-		bb = b_stored;	/* restore global bit buffer */
-		bk = k_stored;
+		gunzip_outbuf_count = w;		/* restore global gunzip_window pointer */
+		gunzip_bb = b_stored;	/* restore global bit buffer */
+		gunzip_bk = k_stored;
 		return 0;
 	}
 	case 1:			/* Inflate fixed 
@@ -659,8 +626,8 @@
 		int i;			/* temporary variable */
 		huft_t *tl;		/* literal/length code table */
 		huft_t *td;		/* distance code table */
-		int bl;			/* lookup bits for tl */
-		int bd;			/* lookup bits for td */
+		unsigned int bl;			/* lookup bits for tl */
+		unsigned int bd;			/* lookup bits for td */
 		unsigned int l[288];	/* length list for huft_build */
 
 		/* set up literal table */
@@ -692,8 +659,9 @@
 		}
 
 		/* decompress until an end-of-block code */
-		if (inflate_codes(tl, td, bl, bd))
+		if (inflate_codes(tl, td, bl, bd)) {
 			return 1;
+		}
 
 		/* free the decoding tables, return */
 		huft_free(tl);
@@ -705,53 +673,41 @@
 		const int dbits = 6;	/* bits in base distance lookup table */
 		const int lbits = 9;	/* bits in base literal/length lookup table */
 
-		int i;			/* temporary variables */
-		unsigned j;
-		unsigned l;		/* last length */
-		unsigned m;		/* mask for bit lengths table */
-		unsigned n;		/* number of lengths to get */
 		huft_t *tl;		/* literal/length code table */
 		huft_t *td;		/* distance code table */
-		int bl;			/* lookup bits for tl */
-		int bd;			/* lookup bits for td */
-		unsigned nb;	/* number of bit length codes */
-		unsigned nl;	/* number of literal/length codes */
-		unsigned nd;	/* number of distance codes */
+		unsigned int i;			/* temporary variables */
+		unsigned int j;
+		unsigned int l;		/* last length */
+		unsigned int m;		/* mask for bit lengths table */
+		unsigned int n;		/* number of lengths to get */
+		unsigned int bl;			/* lookup bits for tl */
+		unsigned int bd;			/* lookup bits for td */
+		unsigned int nb;	/* number of bit length codes */
+		unsigned int nl;	/* number of literal/length codes */
+		unsigned int nd;	/* number of distance codes */
 
-		unsigned ll[286 + 30];	/* literal/length and distance code lengths */
-		register unsigned long b_dynamic;	/* bit buffer */
-		register unsigned k_dynamic;	/* number of bits in bit buffer */
+		unsigned int ll[286 + 30];	/* literal/length and distance code lengths */
+		unsigned int b_dynamic;	/* bit buffer */
+		unsigned int k_dynamic;	/* number of bits in bit buffer */
 
 		/* make local bit buffer */
-		b_dynamic = bb;
-		k_dynamic = bk;
+		b_dynamic = gunzip_bb;
+		k_dynamic = gunzip_bk;
 
 		/* read in table lengths */
-		while (k_dynamic < 5) {
-			input_char = fgetc(in_file);
-			if (input_char == EOF) return 1;
-			b_dynamic |= ((unsigned long)input_char) << k_dynamic;
-			k_dynamic += 8;
-		}
-		nl = 257 + ((unsigned) b_dynamic & 0x1f);	/* number of literal/length codes */
+		b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 5);
+		nl = 257 + ((unsigned int) b_dynamic & 0x1f);	/* number of literal/length codes */
+
 		b_dynamic >>= 5;
 		k_dynamic -= 5;
-		while (k_dynamic < 5) {
-			input_char = fgetc(in_file);
-			if (input_char == EOF) return 1;
-			b_dynamic |= ((unsigned long)input_char) << k_dynamic;
-			k_dynamic += 8;
-		}
-		nd = 1 + ((unsigned) b_dynamic & 0x1f);	/* number of distance codes */
+		b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 5);
+		nd = 1 + ((unsigned int) b_dynamic & 0x1f);	/* number of distance codes */
+
 		b_dynamic >>= 5;
 		k_dynamic -= 5;
-		while (k_dynamic < 4) {
-			input_char = fgetc(in_file);
-			if (input_char == EOF) return 1;
-			b_dynamic |= ((unsigned long)input_char) << k_dynamic;
-			k_dynamic += 8;
-		}
-		nb = 4 + ((unsigned) b_dynamic & 0xf);	/* number of bit length codes */
+		b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 4);
+		nb = 4 + ((unsigned int) b_dynamic & 0xf);	/* number of bit length codes */
+
 		b_dynamic >>= 4;
 		k_dynamic -= 4;
 		if (nl > 286 || nd > 30) {
@@ -760,13 +716,8 @@
 
 		/* read in bit-length-code lengths */
 		for (j = 0; j < nb; j++) {
-			while (k_dynamic < 3) {
-				input_char = fgetc(in_file);
-				if (input_char == EOF) return 1;
-				b_dynamic |= ((unsigned long)input_char) << k_dynamic;
-				k_dynamic += 8;
-			}
-			ll[border[j]] = (unsigned) b_dynamic & 7;
+			b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 3);
+			ll[border[j]] = (unsigned int) b_dynamic & 7;
 			b_dynamic >>= 3;
 			k_dynamic -= 3;
 		}
@@ -776,7 +727,8 @@
 
 		/* build decoding table for trees--single level, 7 bit lookup */
 		bl = 7;
-		if ((i = huft_build(ll, 19, 19, NULL, NULL, &tl, &bl)) != 0) {
+		i = huft_build(ll, 19, 19, NULL, NULL, &tl, &bl);
+		if (i != 0) {
 			if (i == 1) {
 				huft_free(tl);
 			}
@@ -787,46 +739,31 @@
 		n = nl + nd;
 		m = mask_bits[bl];
 		i = l = 0;
-		while ((unsigned) i < n) {
-			while (k_dynamic < (unsigned) bl) {
-				input_char = fgetc(in_file);
-				if (input_char == EOF) return 1;
-				b_dynamic |= ((unsigned long)input_char) << k_dynamic;
-				k_dynamic += 8;
-			}
-			j = (td = tl + ((unsigned) b_dynamic & m))->b;
+		while ((unsigned int) i < n) {
+			b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, (unsigned int)bl);
+			j = (td = tl + ((unsigned int) b_dynamic & m))->b;
 			b_dynamic >>= j;
 			k_dynamic -= j;
 			j = td->v.n;
 			if (j < 16) {	/* length of code in bits (0..15) */
 				ll[i++] = l = j;	/* save last length in l */
 			} else if (j == 16) {	/* repeat last length 3 to 6 times */
-				while (k_dynamic < 2) {
-					input_char = fgetc(in_file);
-					if (input_char == EOF) return 1;
-					b_dynamic |= ((unsigned long)input_char) << k_dynamic;
-					k_dynamic += 8;
-				}
-				j = 3 + ((unsigned) b_dynamic & 3);
+				b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 2);
+				j = 3 + ((unsigned int) b_dynamic & 3);
 				b_dynamic >>= 2;
 				k_dynamic -= 2;
-				if ((unsigned) i + j > n) {
+				if ((unsigned int) i + j > n) {
 					return 1;
 				}
 				while (j--) {
 					ll[i++] = l;
 				}
 			} else if (j == 17) {	/* 3 to 10 zero length codes */
-				while (k_dynamic < 3) {
-					input_char = fgetc(in_file);
-					if (input_char == EOF) return 1;
-					b_dynamic |= ((unsigned long)input_char) << k_dynamic;
-					k_dynamic += 8;
-				}
-				j = 3 + ((unsigned) b_dynamic & 7);
+				b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 3);
+				j = 3 + ((unsigned int) b_dynamic & 7);
 				b_dynamic >>= 3;
 				k_dynamic -= 3;
-				if ((unsigned) i + j > n) {
+				if ((unsigned int) i + j > n) {
 					return 1;
 				}
 				while (j--) {
@@ -834,16 +771,11 @@
 				}
 				l = 0;
 			} else {	/* j == 18: 11 to 138 zero length codes */
-				while (k_dynamic < 7) {
-					input_char = fgetc(in_file);
-					if (input_char == EOF) return 1;
-					b_dynamic |= ((unsigned long)input_char) << k_dynamic;
-					k_dynamic += 8;
-				}
-				j = 11 + ((unsigned) b_dynamic & 0x7f);
+				b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 7);
+				j = 11 + ((unsigned int) b_dynamic & 0x7f);
 				b_dynamic >>= 7;
 				k_dynamic -= 7;
-				if ((unsigned) i + j > n) {
+				if ((unsigned int) i + j > n) {
 					return 1;
 				}
 				while (j--) {
@@ -857,22 +789,24 @@
 		huft_free(tl);
 
 		/* restore the global bit buffer */
-		bb = b_dynamic;
-		bk = k_dynamic;
+		gunzip_bb = b_dynamic;
+		gunzip_bk = k_dynamic;
 
 		/* build the decoding tables for literal/length and distance codes */
 		bl = lbits;
+
 		if ((i = huft_build(ll, nl, 257, cplens, cplext, &tl, &bl)) != 0) {
 			if (i == 1) {
-				error_msg("Incomplete literal tree");
+				error_msg_and_die("Incomplete literal tree");
 				huft_free(tl);
 			}
 			return i;	/* incomplete code set */
 		}
+
 		bd = dbits;
 		if ((i = huft_build(ll + nl, nd, 0, cpdist, cpdext, &td, &bd)) != 0) {
 			if (i == 1) {
-				error_msg("incomplete distance tree");
+				error_msg_and_die("incomplete distance tree");
 				huft_free(td);
 			}
 			huft_free(tl);
@@ -880,8 +814,9 @@
 		}
 
 		/* decompress until an end-of-block code */
-		if (inflate_codes(tl, td, bl, bd))
+		if (inflate_codes(tl, td, bl, bd)) {
 			return 1;
+		}
 
 		/* free the decoding tables, return */
 		huft_free(tl);
@@ -890,6 +825,7 @@
 	}
 	default:
 		/* bad block type */
+		error_msg("bad block type %d\n", t);
 		return 2;
 	}
 }
@@ -897,72 +833,24 @@
 /*
  * decompress an inflated entry
  *
- * GLOBAL VARIABLES: outcnt, bk, bb, hufts, inptr
+ * GLOBAL VARIABLES: gunzip_outbuf_count, bk, gunzip_bb, hufts, inptr
  */
-extern int inflate(FILE * in, FILE * out)
+extern int inflate(int in, int out)
 {
+	typedef void (*sig_type) (int);
 	int e;				/* last block flag */
 	int r;				/* result code */
 	unsigned h = 0;		/* maximum struct huft's malloc'ed */
 
-	/* initialize window, bit buffer */
-	outcnt = 0;
-	bk = 0;
-	bb = 0;
-
-	in_file = in;
-	out_file = out;
-
 	/* Allocate all global buffers (for DYN_ALLOC option) */
-	window = xmalloc((size_t) (((2L * WSIZE) + 1L) * sizeof(unsigned char)));
-	bytes_out = 0L;
+	gunzip_window = xmalloc(0x8000);
+	gunzip_outbuf_count = 0;
+	gunzip_bytes_out = 0;
 
-	/* Create the crc table */
-	make_crc_table();
+	gunzip_src_fd = in;
+	gunzip_dst_fd = out;
 
-	/* decompress until the last block */
-	do {
-		hufts = 0;
-		if ((r = inflate_block(&e)) != 0) {
-			return r;
-		}
-		if (hufts > h) {
-			h = hufts;
-		}
-	} while (!e);
-
-	/* Undo too much lookahead. The next read will be byte aligned so we
-	 * can discard unused bits in the last meaningful byte.
-	 */
-	while (bk >= 8) {
-		bk -= 8;
-		ungetc((bb << bk), in_file);
-	}
-
-	/* flush out window */
-	flush_window();
-	free(window);
-	free(crc_table);
-
-	/* return success */
-	return 0;
-}
-
-/* ===========================================================================
- * Unzip in to out.  This routine works on gzip files only.
- *
- * IN assertions: the buffer inbuf contains already the beginning of
- *   the compressed data, from offsets inptr to insize-1 included.
- *   The magic header has already been checked. The output buffer is cleared.
- * in, out: input and output file descriptors
- */
-extern int unzip(FILE * l_in_file, FILE * l_out_file)
-{
-	unsigned char buf[8];	/* extended local header */
-	unsigned char flags;	/* compression flags */
-	typedef void (*sig_type) (int);
-	unsigned short i;
-	unsigned char magic[2];
+	gunzip_in_buffer = malloc(8);
 
 	if (signal(SIGINT, SIG_IGN) != SIG_IGN) {
 		(void) signal(SIGINT, (sig_type) abort_gzip);
@@ -973,97 +861,44 @@
 	}
 #endif
 
-	magic[0] = fgetc(l_in_file);
-	magic[1] = fgetc(l_in_file);
+	/* initialize gunzip_window, bit buffer */
+	gunzip_bk = 0;
+	gunzip_bb = 0;
 
-#ifdef CONFIG_FEATURE_UNCOMPRESS
-	/* Magic header for compress files, 1F 9d = \037\235 */
-	if ((magic[0] == 0x1F) && (magic[1] == 0x9d)) {
-		return uncompress(l_in_file, l_out_file);
+	/* Create the crc table */
+	make_gunzip_crc_table();
+
+	/* decompress until the last block */
+	do {
+		gunzip_hufts = 0;
+		r = inflate_block(&e);
+		if (r != 0) {
+			error_msg_and_die("inflate error %d", r);
+			return r;
 	}
-#endif
+		if (gunzip_hufts > h) {
+			h = gunzip_hufts;
+	}
+	} while (!e);
 
-	/* Magic header for gzip files, 1F 8B = \037\213 */
-	if ((magic[0] != 0x1F) || (magic[1] != 0x8b)) {
-		error_msg("Invalid gzip magic");
-		return EXIT_FAILURE;
+	/* write any buffered uncompressed data */
+	flush_gunzip_window();
+	free(gunzip_window);
+
+	/* Cleanup */
+	free(gunzip_crc_table);
+
+	/* Store unused bytes in a global buffer so calling applets can access it */
+	gunzip_in_buffer_count = 0;
+	if (gunzip_bk >= 8) {
+		/* Undo too much lookahead. The next read will be byte aligned
+		 * so we can discard unused bits in the last meaningful byte. */
+		gunzip_in_buffer[gunzip_in_buffer_count] = gunzip_bb & 0xff;
+		gunzip_in_buffer_count++;
+		gunzip_bb >>= 8;
+		gunzip_bk -= 8;
 	}
 
-	/* Check the compression method */
-	if (fgetc(l_in_file) != 8) /* also catches EOF */ {
-		error_msg("Unknown compression method");
-		return (-1);
-	}
-
-	flags = (unsigned char) fgetc(l_in_file);
-
-	/* Ignore time stamp(4), extra flags(1), OS type(1) */
-	for (i = 0; i < 6; i++) {
-		fgetc(l_in_file);
-	}
-
-	if (flags & 0x04) {
-		/* bit 2 set: extra field present */
-		const unsigned short extra =
-			fgetc(l_in_file) + (fgetc(l_in_file) << 8);
-		if (feof(in_file)) return 1;
-		for (i = 0; i < extra; i++) {
-			fgetc(l_in_file);
-		}
-	}
-
-	/* Discard original name if any */
-	if (flags & 0x08) {
-		/* bit 3 set: original file name present */
-		while (fgetc(l_in_file) != 0 && !feof(l_in_file));	/* null */
-	}
-
-	/* Discard file comment if any */
-	if (flags & 0x10) {
-		/* bit 4 set: file comment present */
-		while (fgetc(l_in_file) != 0 && !feof(l_in_file));	/* null */
-	}
-
-	/* Decompress */
-	if (inflate(l_in_file, l_out_file) != 0) {
-		error_msg("invalid compressed data--format violated");
-	}
-
-	/* Get the crc and original length
-	 * crc32  (see algorithm.doc)
-	 * uncompressed input size modulo 2^32
-	 */
-	fread(buf, 1, 8, l_in_file);
-
-	/* Validate decompression - crc */
-	if ((unsigned int) ((buf[0] | (buf[1] << 8)) |
-						((buf[2] | (buf[3] << 8)) << 16)) !=
-		(crc ^ 0xffffffffL)) {
-		error_msg("invalid compressed data--crc error");
-	}
-	/* Validate decompression - size */
-	if (((buf[4] | (buf[5] << 8)) | ((buf[6] | (buf[7] << 8)) << 16)) !=
-		(unsigned long) bytes_out) {
-		error_msg("invalid compressed data--length error");
-	}
-
+	/* return success */
 	return 0;
 }
-
-/*
- * This needs access to global variables window and crc_table, so its not in its own file.
- */
-extern void gz_close(int gunzip_pid)
-{
-	if (kill(gunzip_pid, SIGTERM) == -1) {
-		error_msg_and_die
-			("***  Couldnt kill old gunzip process *** aborting");
-	}
-
-	if (waitpid(gunzip_pid, NULL, 0) == -1) {
-		printf("Couldnt wait ?");
-	}
-
-	free(window);
-	free(crc_table);
-}
diff --git a/libbb/xfuncs.c b/libbb/xfuncs.c
index 869c04a..2249e26 100644
--- a/libbb/xfuncs.c
+++ b/libbb/xfuncs.c
@@ -19,10 +19,13 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
 
+#include <sys/types.h>
+#include <sys/stat.h>
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
 #include <unistd.h>
+#include <fcntl.h>
 #include "libbb.h"
 
 
@@ -85,6 +88,59 @@
 	return fp;
 }
 
+extern int xopen(const char *pathname, int flags)
+{
+	int ret;
+	
+	ret = open(pathname, flags);
+	if (ret == -1) {
+		perror_msg_and_die("%s", pathname);
+	}
+	return ret;
+}
+
+extern ssize_t xread(int fd, void *buf, size_t count)
+{
+	ssize_t size;
+
+	size = read(fd, buf, count);
+	if (size == -1) {
+		perror_msg_and_die("Read error");
+	}
+	return(size);
+}
+
+extern void xread_all(int fd, void *buf, size_t count)
+{
+	ssize_t size;
+
+	size = xread(fd, buf, count);
+	if (size != count) {
+		error_msg_and_die("Short read");
+	}
+	return;
+}
+
+extern ssize_t xread_all_eof(int fd, void *buf, size_t count)
+{
+	ssize_t size;
+
+	size = xread(fd, buf, count);
+	if ((size != 0) && (size != count)) {
+		error_msg_and_die("Short read");
+	}
+	return(size);
+}
+
+extern unsigned char xread_char(int fd)
+{
+	char tmp;
+	
+	xread_all(fd, &tmp, 1);
+
+	return(tmp);	
+}
+
 /* Stupid gcc always includes its own builtin strlen()... */
 #undef strlen
 size_t xstrlen(const char *string)