aboutsummaryrefslogtreecommitdiff
path: root/archival
diff options
context:
space:
mode:
authorGlenn L McGrath <bug1@ihug.co.nz>2002-01-02 13:52:26 +0000
committerGlenn L McGrath <bug1@ihug.co.nz>2002-01-02 13:52:26 +0000
commit87ac7028e01cdc4f504ea558a6ae3d086ed1bf2b (patch)
tree4f406c877f88a013c4e1dc650ac312640a95cb00 /archival
parent438803311b67c6337ea97476e97336e027ef9a3a (diff)
downloadbusybox-87ac7028e01cdc4f504ea558a6ae3d086ed1bf2b.tar.gz
unzip applet by Laurence Anderson
----------------------------------------------------------------------
Diffstat (limited to 'archival')
-rw-r--r--archival/Makefile1
-rw-r--r--archival/config.in1
-rw-r--r--archival/libunarchive/Makefile8
-rw-r--r--archival/libunarchive/decompress_unzip.c48
-rw-r--r--archival/libunarchive/get_header_zip.c110
-rw-r--r--archival/libunarchive/unarchive.c3
-rw-r--r--archival/libunarchive/unzip.c48
-rw-r--r--archival/unzip.c94
8 files changed, 260 insertions, 53 deletions
diff --git a/archival/Makefile b/archival/Makefile
index 8787589ff..35ba02f3b 100644
--- a/archival/Makefile
+++ b/archival/Makefile
@@ -34,6 +34,7 @@ obj-$(CONFIG_GUNZIP) += gunzip.o
obj-$(CONFIG_GZIP) += gzip.o
obj-$(CONFIG_RPM2CPIO) += rpm2cpio.o
obj-$(CONFIG_TAR) += tar.o
+obj-$(CONFIG_UNZIP) += unzip.o
# Hand off to toplevel Rules.mak
diff --git a/archival/config.in b/archival/config.in
index 76a192e13..7b5644f01 100644
--- a/archival/config.in
+++ b/archival/config.in
@@ -20,4 +20,5 @@ if [ "$CONFIG_TAR" = "y" ] ; then
bool ' Enable -X and --exclude options (exclude files)' CONFIG_FEATURE_TAR_EXCLUDE
bool ' Enable -z option (currently only for extracting)' CONFIG_FEATURE_TAR_GZIP
fi
+bool 'unzip' CONFIG_UNZIP
endmenu
diff --git a/archival/libunarchive/Makefile b/archival/libunarchive/Makefile
index 0c7219dcd..a8409a432 100644
--- a/archival/libunarchive/Makefile
+++ b/archival/libunarchive/Makefile
@@ -20,7 +20,7 @@
TOPDIR :=../..
L_TARGET := libunarchive.a
-obj-y := unarchive.o seek_sub_file.o
+obj-y := unarchive.o seek_sub_file.o
obj-n :=
obj- :=
@@ -41,13 +41,17 @@ ifeq ($(CONFIG_CPIO),y)
endif
ifeq ($(CONFIG_RPM2CPIO),y)
- obj-y += get_header_cpio.o
+ obj-y += get_header_cpio.o
endif
ifeq ($(CONFIG_TAR),y)
obj-y += get_header_tar.o
endif
+ifeq ($(CONFIG_UNZIP),y)
+ obj-y += get_header_zip.o
+endif
+
# Hand off to toplevel Rules.mak
include $(TOPDIR)/Rules.mak
diff --git a/archival/libunarchive/decompress_unzip.c b/archival/libunarchive/decompress_unzip.c
index 6c28d181d..8075fd717 100644
--- a/archival/libunarchive/decompress_unzip.c
+++ b/archival/libunarchive/decompress_unzip.c
@@ -80,7 +80,7 @@ static const int ERROR = 1;
/*
* window size--must be a power of two, and
- * at least 32K for zip's deflate method
+ * at least 32K for zip's deflate method
*/
static const int WSIZE = 0x8000;
@@ -846,7 +846,7 @@ static int inflate_block(int *e)
*
* GLOBAL VARIABLES: outcnt, bk, bb, hufts, inptr
*/
-static int inflate(void)
+extern int inflate(FILE *in, FILE *out)
{
int e; /* last block flag */
int r; /* result code */
@@ -857,6 +857,13 @@ static int inflate(void)
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;
+
/* Create the crc table */
make_crc_table();
@@ -881,13 +888,15 @@ static int inflate(void)
/* flush out window */
flush_window();
+ free(window);
+ free(crc_table);
/* return success */
return 0;
}
/* ===========================================================================
- * Unzip in to out. This routine works on both gzip and pkzip files.
+ * 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.
@@ -901,9 +910,6 @@ extern int unzip(FILE *l_in_file, FILE *l_out_file)
typedef void (*sig_type) (int);
unsigned short i;
- in_file = l_in_file;
- out_file = l_out_file;
-
if (signal(SIGINT, SIG_IGN) != SIG_IGN) {
(void) signal(SIGINT, (sig_type) abort_gzip);
}
@@ -918,53 +924,48 @@ extern int unzip(FILE *l_in_file, FILE *l_out_file)
}
#endif
- /* Allocate all global buffers (for DYN_ALLOC option) */
- window = xmalloc((size_t)(((2L*WSIZE)+1L)*sizeof(unsigned char)));
- outcnt = 0;
- bytes_out = 0L;
-
/* Magic header for gzip files, 1F 8B = \037\213 */
- if ((fgetc(in_file) != 0x1F) || (fgetc(in_file) != 0x8b)) {
+ if ((fgetc(l_in_file) != 0x1F) || (fgetc(l_in_file) != 0x8b)) {
error_msg("Invalid gzip magic");
return EXIT_FAILURE;
}
/* Check the compression method */
- if (fgetc(in_file) != 8) {
+ if (fgetc(l_in_file) != 8) {
error_msg("Unknown compression method");
return(-1);
}
- flags = (unsigned char) fgetc(in_file);
+ flags = (unsigned char) fgetc(l_in_file);
/* Ignore time stamp(4), extra flags(1), OS type(1) */
for (i = 0; i < 6; i++) {
- fgetc(in_file);
+ fgetc(l_in_file);
}
if (flags & 0x04) {
/* bit 2 set: extra field present */
- const unsigned short extra = fgetc(in_file) + (fgetc(in_file) << 8);
+ const unsigned short extra = fgetc(l_in_file) + (fgetc(l_in_file) << 8);
for (i = 0; i < extra; i++) {
- fgetc(in_file);
+ fgetc(l_in_file);
}
}
/* Discard original name if any */
if (flags & 0x08) {
/* bit 3 set: original file name present */
- while (fgetc(in_file) != 0); /* null */
+ while (fgetc(l_in_file) != 0); /* null */
}
/* Discard file comment if any */
if (flags & 0x10) {
/* bit 4 set: file comment present */
- while (fgetc(in_file) != 0); /* null */
+ while (fgetc(l_in_file) != 0); /* null */
}
/* Decompress */
- if (inflate() != 0) {
+ if (inflate(l_in_file, l_out_file) != 0) {
error_msg("invalid compressed data--format violated");
}
@@ -972,7 +973,7 @@ extern int unzip(FILE *l_in_file, FILE *l_out_file)
* crc32 (see algorithm.doc)
* uncompressed input size modulo 2^32
*/
- fread(buf, 1, 8, in_file);
+ 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)) {
@@ -983,14 +984,11 @@ extern int unzip(FILE *l_in_file, FILE *l_out_file)
error_msg("invalid compressed data--length error");
}
- free(window);
- free(crc_table);
-
return 0;
}
/*
- * This needs access to global variables wondow and crc_table, so its not in its own file.
+ * This needs access to global variables window and crc_table, so its not in its own file.
*/
extern void gz_close(int gunzip_pid)
{
diff --git a/archival/libunarchive/get_header_zip.c b/archival/libunarchive/get_header_zip.c
new file mode 100644
index 000000000..84f2a54f2
--- /dev/null
+++ b/archival/libunarchive/get_header_zip.c
@@ -0,0 +1,110 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * get_header_zip for busybox
+ *
+ * Copyright (C) 2001 by Laurence Anderson
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "unarchive.h"
+#include "libbb.h"
+
+#define ZIP_FILEHEADER_MAGIC 0x04034b50
+#define ZIP_CDS_MAGIC 0x02014b50
+#define ZIP_CDS_END_MAGIC 0x06054b50
+#define ZIP_DD_MAGIC 0x8074b50
+
+file_header_t *get_header_zip(FILE *zip_stream)
+{
+ struct {
+ short version __attribute__ ((packed));
+ short flags __attribute__ ((packed));
+ short method __attribute__ ((packed));
+ short modtime __attribute__ ((packed));
+ short moddate __attribute__ ((packed));
+ int crc32 __attribute__ ((packed));
+ int cmpsize __attribute__ ((packed));
+ int ucmpsize __attribute__ ((packed));
+ short filename_len __attribute__ ((packed));
+ short extra_len __attribute__ ((packed));
+ } zip_header;
+ file_header_t *zip_entry = NULL;
+ int magic;
+ static int dd_ahead = 0; // If this is true, the we didn't know how long the last extraced file was
+
+ fread (&magic, 4, 1, zip_stream);
+ archive_offset += 4;
+
+ if (feof(zip_stream)) return(NULL);
+checkmagic:
+ switch (magic) {
+ case ZIP_FILEHEADER_MAGIC:
+ zip_entry = xcalloc(1, sizeof(file_header_t));
+ fread (&zip_header, sizeof(zip_header), 1, zip_stream);
+ archive_offset += sizeof(zip_header);
+ if (!(zip_header.method == 8 || zip_header.method == 0)) { printf("Unsupported compression method %d\n", zip_header.method); return(NULL); }
+ zip_entry->name = calloc(zip_header.filename_len + 1, sizeof(char));
+ fread (zip_entry->name, sizeof(char), zip_header.filename_len, zip_stream);
+ archive_offset += zip_header.filename_len;
+ seek_sub_file(zip_stream, zip_header.extra_len);
+ zip_entry->size = zip_header.cmpsize;
+ if (zip_header.method == 8) zip_entry->extract_func = &inflate;
+ zip_entry->mode = S_IFREG | 0777;
+ // Time/Date?
+ if (*(zip_entry->name + strlen(zip_entry->name) - 1) == '/') { // Files that end in a / are directories
+ zip_entry->mode ^= S_IFREG;
+ zip_entry->mode |= S_IFDIR;
+ *(zip_entry->name + strlen(zip_entry->name) - 1) = '\0'; // Remove trailing / so unarchive doesn't get confused
+ }
+ //printf("cmpsize: %d, ucmpsize: %d, method: %d\n", zip_header.cmpsize, zip_header.ucmpsize, zip_header.method);
+ if (zip_header.flags & 0x8) { // crc32, and sizes are in the data description _after_ the file
+ if (zip_header.cmpsize == 0) dd_ahead = 1; // As we don't know how long this file it is difficult to skip! but it is compressed, so normally its ok
+ if (zip_header.ucmpsize != 0) dd_ahead = 2; // Humm... we would otherwise skip this twice - not good!
+ }
+ break;
+ case ZIP_CDS_MAGIC: /* FALLTHRU */
+ case ZIP_CDS_END_MAGIC:
+ return(NULL);
+ break;
+ case ZIP_DD_MAGIC: {
+ int cmpsize;
+ seek_sub_file(zip_stream, 4); // Skip crc32
+ fread(&cmpsize, 4, 1, zip_stream);
+ archive_offset += 4;
+ if (dd_ahead == 1) archive_offset += cmpsize;
+ seek_sub_file(zip_stream, 4); // Skip uncompressed size
+ dd_ahead = 0;
+ return (get_header_zip(zip_stream));
+ break; }
+ default:
+ if (!dd_ahead) error_msg("Invalid magic (%#x): Trying to skip junk", magic);
+ dd_ahead = 0;
+ while (!feof(zip_stream)) {
+ int tmpmagic;
+ tmpmagic = fgetc(zip_stream);
+ archive_offset++;
+ magic = ((magic >> 8) & 0x00ffffff) | ((tmpmagic << 24) & 0xff000000);
+ if (magic == ZIP_FILEHEADER_MAGIC || magic == ZIP_CDS_MAGIC || magic == ZIP_CDS_END_MAGIC) goto checkmagic;
+ }
+ error_msg("End of archive reached: Bad archive");
+ return(NULL);
+ }
+ //if (archive_offset != ftell(zip_stream)) printf("Archive offset out of sync (%d,%d)\n", (int) archive_offset, (int) ftell(zip_stream));
+ return(zip_entry);
+}
diff --git a/archival/libunarchive/unarchive.c b/archival/libunarchive/unarchive.c
index ff9b5876f..41be963ef 100644
--- a/archival/libunarchive/unarchive.c
+++ b/archival/libunarchive/unarchive.c
@@ -120,7 +120,8 @@ char *extract_archive(FILE *src_stream, FILE *out_stream, const file_header_t *f
return NULL;
}
archive_offset += file_entry->size;
- copy_file_chunk(src_stream, dst_stream, file_entry->size);
+ if (file_entry->extract_func) file_entry->extract_func(src_stream, dst_stream);
+ else copy_file_chunk(src_stream, dst_stream, file_entry->size);
fclose(dst_stream);
}
break;
diff --git a/archival/libunarchive/unzip.c b/archival/libunarchive/unzip.c
index 6c28d181d..8075fd717 100644
--- a/archival/libunarchive/unzip.c
+++ b/archival/libunarchive/unzip.c
@@ -80,7 +80,7 @@ static const int ERROR = 1;
/*
* window size--must be a power of two, and
- * at least 32K for zip's deflate method
+ * at least 32K for zip's deflate method
*/
static const int WSIZE = 0x8000;
@@ -846,7 +846,7 @@ static int inflate_block(int *e)
*
* GLOBAL VARIABLES: outcnt, bk, bb, hufts, inptr
*/
-static int inflate(void)
+extern int inflate(FILE *in, FILE *out)
{
int e; /* last block flag */
int r; /* result code */
@@ -857,6 +857,13 @@ static int inflate(void)
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;
+
/* Create the crc table */
make_crc_table();
@@ -881,13 +888,15 @@ static int inflate(void)
/* flush out window */
flush_window();
+ free(window);
+ free(crc_table);
/* return success */
return 0;
}
/* ===========================================================================
- * Unzip in to out. This routine works on both gzip and pkzip files.
+ * 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.
@@ -901,9 +910,6 @@ extern int unzip(FILE *l_in_file, FILE *l_out_file)
typedef void (*sig_type) (int);
unsigned short i;
- in_file = l_in_file;
- out_file = l_out_file;
-
if (signal(SIGINT, SIG_IGN) != SIG_IGN) {
(void) signal(SIGINT, (sig_type) abort_gzip);
}
@@ -918,53 +924,48 @@ extern int unzip(FILE *l_in_file, FILE *l_out_file)
}
#endif
- /* Allocate all global buffers (for DYN_ALLOC option) */
- window = xmalloc((size_t)(((2L*WSIZE)+1L)*sizeof(unsigned char)));
- outcnt = 0;
- bytes_out = 0L;
-
/* Magic header for gzip files, 1F 8B = \037\213 */
- if ((fgetc(in_file) != 0x1F) || (fgetc(in_file) != 0x8b)) {
+ if ((fgetc(l_in_file) != 0x1F) || (fgetc(l_in_file) != 0x8b)) {
error_msg("Invalid gzip magic");
return EXIT_FAILURE;
}
/* Check the compression method */
- if (fgetc(in_file) != 8) {
+ if (fgetc(l_in_file) != 8) {
error_msg("Unknown compression method");
return(-1);
}
- flags = (unsigned char) fgetc(in_file);
+ flags = (unsigned char) fgetc(l_in_file);
/* Ignore time stamp(4), extra flags(1), OS type(1) */
for (i = 0; i < 6; i++) {
- fgetc(in_file);
+ fgetc(l_in_file);
}
if (flags & 0x04) {
/* bit 2 set: extra field present */
- const unsigned short extra = fgetc(in_file) + (fgetc(in_file) << 8);
+ const unsigned short extra = fgetc(l_in_file) + (fgetc(l_in_file) << 8);
for (i = 0; i < extra; i++) {
- fgetc(in_file);
+ fgetc(l_in_file);
}
}
/* Discard original name if any */
if (flags & 0x08) {
/* bit 3 set: original file name present */
- while (fgetc(in_file) != 0); /* null */
+ while (fgetc(l_in_file) != 0); /* null */
}
/* Discard file comment if any */
if (flags & 0x10) {
/* bit 4 set: file comment present */
- while (fgetc(in_file) != 0); /* null */
+ while (fgetc(l_in_file) != 0); /* null */
}
/* Decompress */
- if (inflate() != 0) {
+ if (inflate(l_in_file, l_out_file) != 0) {
error_msg("invalid compressed data--format violated");
}
@@ -972,7 +973,7 @@ extern int unzip(FILE *l_in_file, FILE *l_out_file)
* crc32 (see algorithm.doc)
* uncompressed input size modulo 2^32
*/
- fread(buf, 1, 8, in_file);
+ 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)) {
@@ -983,14 +984,11 @@ extern int unzip(FILE *l_in_file, FILE *l_out_file)
error_msg("invalid compressed data--length error");
}
- free(window);
- free(crc_table);
-
return 0;
}
/*
- * This needs access to global variables wondow and crc_table, so its not in its own file.
+ * This needs access to global variables window and crc_table, so its not in its own file.
*/
extern void gz_close(int gunzip_pid)
{
diff --git a/archival/unzip.c b/archival/unzip.c
new file mode 100644
index 000000000..ae0d7c1dc
--- /dev/null
+++ b/archival/unzip.c
@@ -0,0 +1,94 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini unzip implementation for busybox
+ *
+ * Copyright (C) 2001 by Laurence Anderson
+ *
+ * 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 <fcntl.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "unarchive.h"
+#include "busybox.h"
+
+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;
+
+ while ((opt = getopt(argc, argv, "lnopqxd:")) != -1) {
+ switch (opt) {
+ case 'l':
+ extract_function |= extract_verbose_list;
+ extract_function ^= extract_all_to_fs;
+ break;
+ case 'n':
+ break;
+ case 'o':
+ extract_function |= extract_unconditional;
+ break;
+ case 'p':
+ extract_function |= extract_to_stdout;
+ extract_function ^= extract_all_to_fs;
+ /* FALLTHROUGH */
+ case 'q':
+ msgout = xfopen("/dev/null", "w");
+ break;
+ case 'd':
+ outdir = xstrdup(optarg);
+ strcat(outdir, "/");
+ break;
+ case 'x':
+ exclude = 1;
+ break;
+ }
+ }
+
+ if (optind == argc) {
+ show_usage();
+ }
+
+ if (*argv[optind] == '-') src_stream = stdin;
+ else src_stream = xfopen(argv[optind++], "r");
+
+ 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;
+ optind++;
+ }
+
+ unarchive(src_stream, msgout, &get_header_zip, extract_function, outdir, extract_names, exclude_names);
+ return EXIT_SUCCESS;
+}