[BusyBox] [PATCH] unzip applet

Ed Clark eclarknz at hotmail.com
Sun Oct 17 19:37:28 MDT 2004


hi all,

i ran into number of problems with the unzip applet and PKZIP files whilst 
running the DSL (Damn Small Linux) distribution. For example, unzip was 
unable to handle directories stored with compression method 0, it did not 
correctly support accept/reject lists, and was unable to unzip data to 
stdout. I didn't see evidence of anyone raising such problems in the mailing 
list, so is it possible that no one out there is using unzip ?!

Here is a patch that fixes all problems i'm aware of. Please note that this 
patch affects archival/libunarchive/decompress_unzip.c which is at the core 
a number of baseline unarchival applets (including gunzip and unzip). I have 
tested the new code as far as is practically possible, but would appreciate 
further testing and feedback from the community - thanks.

Ed



Index: archival/unzip.c
===================================================================
RCS file: /var/cvs/busybox/archival/unzip.c,v
retrieving revision 1.8
diff -u -r1.8 unzip.c
--- archival/unzip.c	6 Jun 2004 10:22:43 -0000	1.8
+++ archival/unzip.c	18 Oct 2004 01:32:25 -0000
@@ -2,7 +2,10 @@
/*
  * Mini unzip implementation for busybox
  *
- * Copyright (C) 2001 by Laurence Anderson
+ * Copyright (C) 2004 by Ed Clark
+ *
+ * Loosely based on original busybox unzip applet by Laurence Anderson.
+ * All options and features should work in this version.
  *
  * 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
@@ -21,17 +24,25 @@
  */

/* For reference see
- * http://www.pkware.com/products/enterprise/white_papers/appnote.txt
+ * http://www.pkware.com/company/standards/appnote/
  * http://www.info-zip.org/pub/infozip/doc/appnote-iz-latest.zip
  */

-/* TODO Endian issues, exclude, should we accept input from stdin ? */
+/* TODO
+ * Endian issues
+ * Zip64 + other methods
+ * Improve handling of zip format, ie.
+ * - deferred CRC, comp. & uncomp. lengths (zip header flags bit 3)
+ * - unix file permissions, etc.
+ * - central directory
+ */

#include <fcntl.h>
#include <getopt.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <errno.h>
#include "unarchive.h"
#include "busybox.h"

@@ -43,205 +54,353 @@
extern unsigned int gunzip_crc;
extern unsigned int gunzip_bytes_out;

-static void header_list_unzip(const file_header_t *file_header)
+typedef 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_t;
+
+static void unzip_skip(int fd, off_t skip)
{
-	printf("  inflating: %s\n", file_header->name);
+	if (lseek(fd, skip, SEEK_CUR) == (off_t)-1) {
+		if ((errno != ESPIPE) || (bb_copyfd_size(fd, -1, skip) != skip)) {
+			bb_error_msg_and_die("Seek failure");
+		}
+	}
}

-static void header_verbose_list_unzip(const file_header_t *file_header)
+static void unzip_read(int fd, void *buf, size_t count)
{
-	unsigned int dostime = (unsigned int) file_header->mtime;
+	if (bb_xread(fd, buf, count) != count) {
+		bb_error_msg_and_die("Read failure");
+	}
+}

-	/* 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);
+static void unzip_create_leading_dirs(char *fn)
+{
+	/* Create all leading directories */
+	char *name = bb_xstrdup(fn);
+	if (bb_make_directory(dirname(name), 0777, FILEUTILS_RECUR)) {
+		bb_error_msg_and_die("Failed to create directory");
+	}
+	free(name);
}

-extern int unzip_main(int argc, char **argv)
+static void unzip_extract(zip_header_t *zip_header, int src_fd, int dst_fd)
{
-	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;
+	if (zip_header->formated.method == 0) {
+		/* Method 0 - stored (not compressed) */
+		int size = zip_header->formated.ucmpsize;
+		if (size && (bb_copyfd_size(src_fd, dst_fd, size) != size)) {
+			bb_error_msg_and_die("Cannot complete extraction");
+		}
+
+	} else {
+		/* Method 8 - inflate */
+		inflate_init(zip_header->formated.cmpsize);
+		inflate_unzip(src_fd, dst_fd);
+		inflate_cleanup();
+		/* Validate decompression - crc */
+		if (zip_header->formated.crc32 != (gunzip_crc ^ 0xffffffffL)) {
+			bb_error_msg("Invalid compressed data--crc error");
+		}
+		/* Validate decompression - size */
+		if (zip_header->formated.ucmpsize != gunzip_bytes_out) {
+			bb_error_msg("Invalid compressed data--length error");
+		}
+	}
+}

-	archive_handle_t *archive_handle;
+extern int unzip_main(int argc, char **argv)
+{
+	zip_header_t zip_header;
+	enum {v_silent, v_normal, v_list} verbosity = v_normal;
+	enum {o_prompt, o_never, o_always} overwrite = o_prompt;
	unsigned int total_size = 0;
	unsigned int total_entries = 0;
+	int src_fd = -1, dst_fd = -1;
+	char *src_fn = NULL, *dst_fn = NULL;
+	llist_t *accept = NULL;
+	llist_t *reject = NULL;
	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':	/* list */
-				archive_handle->action_header = header_verbose_list_unzip;
-				archive_handle->action_data = data_skip;
-				break;
-			case 'n':	/* never overwright existing files */
+	int i, opt, opt_range = 0, list_header_done = 0;
+	char key_buf[512];
+	struct stat stat_buf;
+
+	while((opt = getopt(argc, argv, "-d:lnopqx")) != -1) {
+		switch(opt_range) {
+		case 0: /* Options */
+			switch(opt) {
+			case 'l': /* List */
+				verbosity = v_list;
				break;
-			case 'o':
-				archive_handle->flags = ARCHIVE_EXTRACT_UNCONDITIONAL;
+
+			case 'n': /* Never overwrite existing files */
+				overwrite = o_never;
				break;
-			case 'p':	/* extract files to stdout */
-				archive_handle->action_data = data_extract_to_stdout;
+
+			case 'o': /* Always overwrite existing files */
+				overwrite = o_always;
				break;
-			case 'q':	/* Extract files quietly */
-				archive_handle->action_header = header_skip;
+
+			case 'p': /* Extract files to stdout and fall through to set verbosity 
*/
+				dst_fd = STDOUT_FILENO;
+
+			case 'q': /* Be quiet */
+				verbosity = (verbosity == v_normal) ? v_silent : verbosity;
				break;
-			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;
+
+			case 1 : /* The zip file */
+				src_fn = bb_xstrndup(optarg, strlen(optarg)+4);
+				opt_range++;
				break;
-#endif
+
			default:
				bb_show_usage();
+
+			}
+			break;
+
+		case 1: /* Include files */
+			if (opt == 1) {
+				accept = llist_add_to(accept, optarg);
+
+			} else if (opt == 'd') {
+				base_dir = optarg;
+				opt_range += 2;
+
+			} else if (opt == 'x') {
+				opt_range++;
+
+			} else {
+				bb_show_usage();
+			}
+			break;
+
+		case 2 : /* Exclude files */
+			if (opt == 1) {
+				reject = llist_add_to(reject, optarg);
+
+			} else if (opt == 'd') { /* Extract to base directory */
+				base_dir = optarg;
+				opt_range++;
+
+			} else {
+				bb_show_usage();
+			}
+			break;
+
+		default:
+			bb_show_usage();
		}
	}
-
-	if (argc == optind) {
+
+	if (src_fn == NULL) {
		bb_show_usage();
	}

-	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 = STDIN_FILENO;
-		archive_handle->seek = seek_by_char;
+	/* Open input file */
+	if (strcmp("-", src_fn) == 0) {
+		src_fd = STDIN_FILENO;
+		/* Cannot use prompt mode since zip data is arriving on STDIN */
+		overwrite = (overwrite == o_prompt) ? o_never : overwrite;
+
	} else {
-		archive_handle->src_fd = bb_xopen(argv[optind++], O_RDONLY);
+		char *extn[] = {"", ".zip", ".ZIP"};
+		int orig_src_fn_len = strlen(src_fn);
+		for(i = 0; (i < 3) && (src_fd == -1); i++) {
+			strcpy(src_fn + orig_src_fn_len, extn[i]);
+			src_fd = open(src_fn, O_RDONLY);
+		}
+		if (src_fd == -1) {
+			src_fn[orig_src_fn_len] = 0;
+			bb_error_msg_and_die("Cannot open %s, %s.zip, %s.ZIP", src_fn, src_fn, 
src_fn);
+		}
	}

-	if ((base_dir) && (chdir(base_dir))) {
-		bb_perror_msg_and_die("Couldnt chdir");
+	/* Change dir if necessary */
+	if (base_dir && chdir(base_dir)) {
+		bb_perror_msg_and_die("Cannot chdir");
	}

-	while (optind < argc) {
-		archive_handle->filter = filter_accept_list;
-		archive_handle->accept = llist_add_to(archive_handle->accept, 
argv[optind]);
-		optind++;
-	}
+	if (verbosity != v_silent)
+		printf("Archive:  %s\n", src_fn);

	while (1) {
		unsigned int magic;
-		int dst_fd;
-
-		/* TODO Endian issues */
-		archive_xread_all(archive_handle, &magic, 4);
-		archive_handle->offset += 4;

+		/* Check magic number */
+		unzip_read(src_fd, &magic, 4);
		if (magic == ZIP_CDS_MAGIC) {
			break;
+		} else if (magic != ZIP_FILEHEADER_MAGIC) {
+			bb_error_msg_and_die("Invalid zip magic %08X", magic);
		}
-		else if (magic != ZIP_FILEHEADER_MAGIC) {
-			bb_error_msg_and_die("Invlaide zip magic");
-		}
-
+
		/* Read the file header */
-		archive_xread_all(archive_handle, zip_header.raw, 26);
-		archive_handle->offset += 26;
-		archive_handle->file_header->mode = S_IFREG | 0777;
-
-		if (zip_header.formated.method != 8) {
-			bb_error_msg_and_die("Unsupported compression method %d\n", 
zip_header.formated.method);
+		unzip_read(src_fd, zip_header.raw, 26);
+		if ((zip_header.formated.method != 0) && (zip_header.formated.method != 
8)) {
+			bb_error_msg_and_die("Unsupported compression method %d", 
zip_header.formated.method);
		}

		/* Read filename */
-		archive_handle->file_header->name = 
xmalloc(zip_header.formated.filename_len + 1);
-		archive_xread_all(archive_handle, 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 = bb_xopen(archive_handle->file_header->name, O_WRONLY | 
O_CREAT);
-			inflate_init(zip_header.formated.cmpsize);
-			inflate_unzip(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)) {
-				bb_error_msg("Invalid compressed data--crc error");
-			}
-
-			/* Validate decompression - size */
-			if (gunzip_bytes_out != zip_header.formated.ucmpsize) {
-				bb_error_msg("Invalid compressed data--length error");
-			}
+		free(dst_fn);
+		dst_fn = xmalloc(zip_header.formated.filename_len + 1);
+		unzip_read(src_fd, dst_fn, zip_header.formated.filename_len);
+		dst_fn[zip_header.formated.filename_len] = 0;
+
+		/* Skip extra header bytes */
+		unzip_skip(src_fd, zip_header.formated.extra_len);
+
+		if ((verbosity == v_list) && !list_header_done){
+			printf("  Length     Date   Time    Name\n");
+			printf(" --------    ----   ----    ----\n");
+			list_header_done = 1;
		}

-		/* 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++;
+		/* Filter zip entries */
+		if (find_list_entry(reject, dst_fn) ||
+			(accept && !find_list_entry(accept, dst_fn))) { /* Skip entry */
+			i = 'n';
+
+		} else { /* Extract entry */
+			total_size += zip_header.formated.ucmpsize;
+
+			if (verbosity == v_list) { /* List entry */
+				unsigned int dostime = zip_header.formated.modtime | 
(zip_header.formated.moddate << 16);
+				printf("%9u  %02u-%02u-%02u %02u:%02u   %s\n",
+					   zip_header.formated.ucmpsize,
+					   (dostime & 0x01e00000) >> 21,
+					   (dostime & 0x001f0000) >> 16,
+					   (((dostime & 0xfe000000) >> 25) + 1980) % 100,
+					   (dostime & 0x0000f800) >> 11,
+					   (dostime & 0x000007e0) >> 5,
+					   dst_fn);
+				total_entries++;
+				i = 'n';
+
+			} else if (dst_fd == STDOUT_FILENO) { /* Extracting to STDOUT */
+				i = -1;
+
+			} else if (last_char_is(dst_fn, '/')) { /* Extract directory */
+				if (stat(dst_fn, &stat_buf) == -1) {
+					if (errno != ENOENT) {
+						bb_perror_msg_and_die("Cannot stat '%s'",dst_fn);
+					}
+					if (verbosity == v_normal) {
+						printf("   creating: %s\n", dst_fn);
+					}
+					unzip_create_leading_dirs(dst_fn);
+					if (bb_make_directory(dst_fn, 0777, 0)) {
+						bb_error_msg_and_die("Failed to create directory");
+					}
+				} else {
+					if (!S_ISDIR(stat_buf.st_mode)) {
+						bb_error_msg_and_die("'%s' exists but is not directory", dst_fn);
+					}
+				}
+				i = 'n';
+
+			} else {  /* Extract file */
+			_check_file:
+				if (stat(dst_fn, &stat_buf) == -1) { /* File does not exist */
+					if (errno != ENOENT) {
+						bb_perror_msg_and_die("Cannot stat '%s'",dst_fn);
+					}
+					i = 'y';
+
+				} else { /* File already exists */
+					if (overwrite == o_never) {
+						i = 'n';
+
+					} else if (S_ISREG(stat_buf.st_mode)) { /* File is regular file */
+						if (overwrite == o_always) {
+							i = 'y';
+						} else {
+							printf("replace %s? [y]es, [n]o, [A]ll, [N]one, [r]ename: ", 
dst_fn);
+							if (!fgets(key_buf, 512, stdin)) {
+								bb_perror_msg_and_die("Cannot read input");
+							}
+							i = key_buf[0];
+						}
+
+					} else { /* File is not regular file */
+						bb_error_msg_and_die("'%s' exists but is not regular file",dst_fn);
+					}
+				}
+			}
+		}

-		archive_handle->action_header(archive_handle->file_header);
+		switch (i) {
+		case 'A':
+			overwrite = o_always;
+		case 'y': /* Open file and fall into unzip */
+			unzip_create_leading_dirs(dst_fn);
+			dst_fd = bb_xopen(dst_fn, O_WRONLY | O_CREAT);
+		case -1: /* Unzip */
+			if (verbosity == v_normal) {
+				printf("  inflating: %s\n", dst_fn);
+			}
+			unzip_extract(&zip_header, src_fd, dst_fd);
+			if (dst_fd != STDOUT_FILENO) {
+				/* closing STDOUT is potentially bad for future business */
+				close(dst_fd);
+			}
+			break;

+		case 'N':
+			overwrite = o_never;
+		case 'n':
+			/* Skip entry data */
+			unzip_skip(src_fd, zip_header.formated.cmpsize);
+			break;
+
+		case 'r':
+			/* Prompt for new name */
+			printf("new name: ");
+			if (!fgets(key_buf, 512, stdin)) {
+				bb_perror_msg_and_die("Cannot read input");
+			}
+			free(dst_fn);
+			dst_fn = bb_xstrdup(key_buf);
+			chomp(dst_fn);
+			goto _check_file;
+
+		default:
+			printf("error:  invalid response [%c]\n",(char)i);
+			goto _check_file;
+		}
+
		/* Data descriptor section */
		if (zip_header.formated.flags & 4) {
			/* skip over duplicate crc, compressed size and uncompressed size */
-			unsigned char data_description[12];
-			archive_xread_all(archive_handle, data_description, 12);
-			archive_handle->offset += 12;
+			unzip_skip(src_fd, 12);
		}
	}
-	/* Central directory section */

-	if (archive_handle->action_header == header_verbose_list_unzip) {
+	if (verbosity == v_list) {
		printf(" --------                   -------\n");
		printf("%9d                   %d files\n", total_size, total_entries);
	}
-
+
	return(EXIT_SUCCESS);
}
+
+/* END CODE */
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
Index: archival/libunarchive/decompress_unzip.c
===================================================================
RCS file: /var/cvs/busybox/archival/libunarchive/decompress_unzip.c,v
retrieving revision 1.35
diff -u -r1.35 decompress_unzip.c
--- archival/libunarchive/decompress_unzip.c	25 Apr 2004 05:11:13 -0000	1.35
+++ archival/libunarchive/decompress_unzip.c	18 Oct 2004 01:32:29 -0000
@@ -16,6 +16,11 @@
  *
  * read_gz interface + associated hacking by Laurence Anderson
  *
+ * Fixed huft_build() so decoding end-of-block code does not grab more bits
+ * than necessary (this is required by unzip applet), added 
inflate_cleanup()
+ * to free leaked bytebuffer memory (used in unzip.c), and some minor style
+ * guide cleanups by Ed Clark
+ *
  * 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
@@ -116,26 +121,26 @@
/* 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
+	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
+	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
+	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
+	11, 11, 12, 12, 13, 13
};

/* Tables for deflate from PKZIP's appnote.txt. */
@@ -184,8 +189,8 @@
				table_entry = (table_entry >> 1) ^ poly;
			} else {
				table_entry >>= 1;
-	}
-	}
+			}
+		}
		gunzip_crc_table[i] = table_entry;
	}
}
@@ -225,70 +230,59 @@
  * t:	result: starting table
  * m:	maximum lookup bits, returns actual
  */
-static int huft_build(unsigned int *b, const unsigned int n,
-					  const unsigned int s, const unsigned short *d,
-					  const unsigned char *e, huft_t ** t, int *m)
+int huft_build(unsigned int *b, const unsigned int n,
+			   const unsigned int s, const unsigned short *d,
+			   const unsigned char *e, huft_t ** t, int *m)
{
-	unsigned a;			/* counter for codes of length k */
+	unsigned a;				/* counter for codes of length k */
	unsigned c[BMAX + 1];	/* bit length count table */
-	unsigned f;			/* i repeats in table every f entries */
-	int g;				/* maximum code length */
-	int h;				/* table level */
+	unsigned eob_len;		/* length of end-of-block code (value 256) */
+	unsigned f;				/* i repeats in table every f entries */
+	int g;					/* maximum code length */
+	int h;					/* table level */
	register unsigned i;	/* counter, current code */
	register unsigned j;	/* counter */
-	register int k;		/* number of bits in current code */
-	int l;				/* bits per table (returned in m) */
+	register int k;			/* number of bits in current code */
	register unsigned *p;	/* pointer into c[], b[], or v[] */
-	register huft_t *q;	/* points to current table */
-	huft_t r;			/* table entry for structure assignment */
-	huft_t *u[BMAX];	/* table stack */
-	unsigned v[N_MAX];	/* values in order of bit length */
-	register int w;		/* bits before this table == (l * h) */
+	register huft_t *q;		/* points to current table */
+	huft_t r;				/* table entry for structure assignment */
+	huft_t *u[BMAX];		/* table stack */
+	unsigned v[N_MAX];		/* values in order of bit length */
+	int ws[BMAX+1];			/* bits decoded stack */
+	register int w;			/* bits decoded */
	unsigned x[BMAX + 1];	/* bit offsets, then code stack */
-	unsigned *xp;		/* pointer into x */
-	int y;				/* number of dummy codes added */
-	unsigned z;			/* number of entries in current table */
+	unsigned *xp;			/* pointer into x */
+	int y;					/* number of dummy codes added */
+	unsigned z;				/* number of entries in current table */
+
+	/* Length of EOB code, if any */
+	eob_len = n > 256 ? b[256] : BMAX;

	/* Generate counts for each bit length */
-	memset((void *) (c), 0, sizeof(c));
+	memset((void *)c, 0, sizeof(c));
	p = b;
	i = n;
	do {
-		c[*p]++;		/* assume all entries <= BMAX */
-		p++;			/* Can't combine with above line (Solaris bug) */
+		c[*p]++; /* assume all entries <= BMAX */
+		p++; /* Can't combine with above line (Solaris bug) */
	} while (--i);
-	if (c[0] == n) {	/* null input--all zero length codes */
+	if (c[0] == n) { /* null input--all zero length codes */
		*t = (huft_t *) NULL;
		*m = 0;
		return 0;
	}

	/* Find minimum and maximum length, bound *m by those */
-	l = *m;
-	for (j = 1; j <= BMAX; j++) {
-		if (c[j]) {
-			break;
-		}
-	}
-	k = j;				/* minimum code length */
-	if ((unsigned) l < j) {
-		l = j;
-	}
-	for (i = BMAX; i; i--) {
-		if (c[i]) {
-			break;
-		}
-	}
-	g = i;				/* maximum code length */
-	if ((unsigned) l > i) {
-		l = i;
-	}
-	*m = l;
+	for (j = 1; (c[j] == 0) && (j <= BMAX); j++);
+	k = j; /* minimum code length */
+	for (i = BMAX; (c[i] == 0) && i; i--);
+	g = i; /* maximum code length */
+	*m = (*m < j) ? j : ((*m > i) ? i : *m);

	/* Adjust last length count to fill out codes, if needed */
	for (y = 1 << j; j < i; j++, y <<= 1) {
		if ((y -= c[j]) < 0) {
-			return 2;	/* bad input: more codes than bits */
+			return 2; /* bad input: more codes than bits */
		}
	}
	if ((y -= c[i]) < 0) {
@@ -300,7 +294,7 @@
	x[1] = j = 0;
	p = c + 1;
	xp = x + 2;
-	while (--i) {		/* note that i == g from above */
+	while (--i) { /* note that i == g from above */
		*xp++ = (j += *p++);
	}

@@ -314,13 +308,13 @@
	} while (++i < n);

	/* Generate the Huffman codes and for each, make the table entries */
-	x[0] = i = 0;		/* first Huffman code is zero */
-	p = v;				/* grab values in bit order */
-	h = -1;				/* no tables yet--level -1 */
-	w = -l;				/* bits decoded == (l * h) */
+	x[0] = i = 0;			/* first Huffman code is zero */
+	p = v;					/* grab values in bit order */
+	h = -1;					/* no tables yet--level -1 */
+	w = ws[0] = 0;			/* bits decoded */
	u[0] = (huft_t *) NULL;	/* just to keep compilers happy */
	q = (huft_t *) NULL;	/* ditto */
-	z = 0;				/* ditto */
+	z = 0;					/* ditto */

	/* go through the bit lengths (k already is bits in shortest code) */
	for (; k <= g; k++) {
@@ -328,52 +322,52 @@
		while (a--) {
			/* here i is the Huffman code of length k bits for value *p */
			/* make tables up to required level */
-			while (k > w + l) {
-				h++;
-				w += l;	/* previous table always l bits */
-
-				/* compute minimum size table less than or equal to l bits */
-				z = (z = g - w) > (unsigned) l ? l : z;	/* upper limit on table size */
-				if ((f = 1 << (j = k - w)) > a + 1) {	/* try a k-w bit table *//* too 
few codes for k-w bit table */
-					f -= a + 1;	/* deduct codes from patterns left */
+			while (k > ws[h + 1]) {
+				w = ws[++h];
+
+				/* compute minimum size table less than or equal to *m bits */
+				z = (z = g - w) > *m ? *m : z; /* upper limit on table size */
+				if ((f = 1 << (j = k - w)) > a + 1) { /* try a k-w bit table */
+					/* too few codes for k-w bit table */
+					f -= a + 1; /* deduct codes from patterns left */
					xp = c + k;
-					while (++j < z) {	/* try smaller tables up to z bits */
+					while (++j < z) { /* try smaller tables up to z bits */
						if ((f <<= 1) <= *++xp) {
-							break;	/* enough codes to use up j bits */
+							break; /* enough codes to use up j bits */
						}
-						f -= *xp;	/* else deduct codes from patterns */
+						f -= *xp; /* else deduct codes from patterns */
					}
				}
+				j = (w + j > eob_len && w < eob_len) ? eob_len - w : j;	/* make EOB 
code end at table */
				z = 1 << j;	/* table entries for j-bit table */
+				ws[h+1] = w + j;	/* set bits decoded in stack */

				/* allocate and link in new table */
				q = (huft_t *) xmalloc((z + 1) * sizeof(huft_t));
-
				*t = q + 1;	/* link to list for huft_free() */
				*(t = &(q->v.t)) = NULL;
				u[h] = ++q;	/* table starts after link */

				/* connect to last table, if there is one */
				if (h) {
-					x[h] = i;	/* save pattern for backing up */
-					r.b = (unsigned char) l;	/* bits to dump before this table */
-					r.e = (unsigned char) (16 + j);	/* bits in this table */
-					r.v.t = q;	/* pointer to this table */
-					j = i >> (w - l);	/* (get around Turbo C bug) */
-					u[h - 1][j] = r;	/* connect to last table */
+					x[h] = i; /* save pattern for backing up */
+					r.b = (unsigned char) (w - ws[h - 1]); /* bits to dump before this 
table */
+					r.e = (unsigned char) (16 + j); /* bits in this table */
+					r.v.t = q; /* pointer to this table */
+					j = (i & ((1 << w) - 1)) >> ws[h - 1];
+					u[h - 1][j] = r; /* connect to last table */
				}
			}
-
+
			/* set up table entry in r */
			r.b = (unsigned char) (k - w);
			if (p >= v + n) {
-				r.e = 99;	/* out of values--invalid code */
+				r.e = 99; /* out of values--invalid code */
			} 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++ */
+				r.e = (unsigned char) (*p < 256 ? 16 : 15);	/* 256 is EOB code */
+				r.v.n = (unsigned short) (*p++); /* simple code is just the value */
			} else {
-				r.e = (unsigned char) e[*p - s];	/* non-simple--look up in lists */
+				r.e = (unsigned char) e[*p - s]; /* non-simple--look up in lists */
				r.v.n = d[*p++ - s];
			}

@@ -391,11 +385,14 @@

			/* backup over finished tables */
			while ((i & ((1 << w) - 1)) != x[h]) {
-				h--;	/* don't need to update q */
-				w -= l;
+				w = ws[--h];
			}
		}
	}
+
+	/* return actual size of base table */
+	*m = ws[1];
+
	/* Return true (1) if we were given an incomplete table */
	return y != 0 && g != 1;
}
@@ -901,6 +898,11 @@
	bytebuffer_size = 0;
}

+extern void inflate_cleanup(void)
+{
+	free(bytebuffer);
+}
+
extern int inflate_unzip(int in, int out)
{
	ssize_t nwrote;
Index: include/unarchive.h
===================================================================
RCS file: /var/cvs/busybox/include/unarchive.h,v
retrieving revision 1.23
diff -u -r1.23 unarchive.h
--- include/unarchive.h	15 Mar 2004 08:28:38 -0000	1.23
+++ include/unarchive.h	18 Oct 2004 01:32:30 -0000
@@ -98,6 +98,7 @@

extern int uncompressStream(int src_fd, int dst_fd);
extern void inflate_init(unsigned int bufsize);
+extern void inflate_cleanup(void);
extern int inflate_unzip(int in, int out);
extern int inflate_gunzip(int in, int out);

_________________________________________________________________
Express yourself instantly with MSN Messenger! Download today it's FREE! 
http://messenger.msn.com/



More information about the busybox mailing list