applying fixes from: 0000142: unzip enhancements
No binary (build failed)
—
(log yok)
| @@ -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 @@ static const unsigned short mask_bits[] = { | |
| /* 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. */ | |
| @@ -187,8 +192,8 @@ static void make_gunzip_crc_table(void) | |
| table_entry = (table_entry >> 1) ^ poly; | |
| } else { | |
| table_entry >>= 1; | |
| - | } |
| - | } |
| + | } |
| + | } |
| gunzip_crc_table[i] = table_entry; | |
| } | |
| } | |
| @@ -228,70 +233,59 @@ static int huft_free(huft_t * t) | |
| * 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) { | |
| @@ -303,7 +297,7 @@ static int huft_build(unsigned int *b, const unsigned int n, | |
| 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++); | |
| } | |
| @@ -317,13 +311,13 @@ static int huft_build(unsigned int *b, const unsigned int n, | |
| } 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++) { | |
| @@ -331,52 +325,52 @@ static int huft_build(unsigned int *b, const unsigned int n, | |
| 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]; | |
| } | |
| @@ -394,11 +388,14 @@ static int huft_build(unsigned int *b, const unsigned int n, | |
| /* 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; | |
| } | |
| @@ -904,6 +901,11 @@ extern void inflate_init(unsigned int bufsize) | |
| bytebuffer_size = 0; | |
| } | |
| + | extern void inflate_cleanup(void) |
| + | { |
| + | free(bytebuffer); |
| + | } |
| + | |
| extern int inflate_unzip(int in, int out) | |
| { | |
| ssize_t nwrote; | |
| @@ -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,227 +24,413 @@ | |
| */ | |
| /* 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" | |
| - | #define ZIP_FILEHEADER_MAGIC 0x04034b50 |
| - | #define ZIP_CDS_MAGIC 0x02014b50 |
| - | #define ZIP_CDS_END_MAGIC 0x06054b50 |
| - | #define ZIP_DD_MAGIC 0x08074b50 |
| + | #if (BYTE_ORDER == BIG_ENDIAN) |
| + | static inline unsigned short |
| + | __swap16(unsigned short x) { |
| + | return (((uint16_t)(x) & 0xFF) << 8) | (((uint16_t)(x) & 0xFF00) >> 8); |
| + | } |
| + | |
| + | static inline uint32_t |
| + | __swap32(uint32_t x) { |
| + | return (((x & 0xFF) << 24) | |
| + | ((x & 0xFF00) << 8) | |
| + | ((x & 0xFF0000) >> 8) | |
| + | ((x & 0xFF000000) >> 24)); |
| + | } |
| + | #else |
| + | #define __swap16(x) (x) |
| + | #define __swap32(x) (x) |
| + | #endif |
| + | |
| + | #define ZIP_FILEHEADER_MAGIC __swap32(0x04034b50) |
| + | #define ZIP_CDS_MAGIC __swap32(0x02014b50) |
| + | #define ZIP_CDS_END_MAGIC __swap32(0x06054b50) |
| + | #define ZIP_DD_MAGIC __swap32(0x08074b50) |
| 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; |
| + | 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); |
| + | } |
| - | 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_extract(zip_header_t *zip_header, int src_fd, int dst_fd) |
| + | { |
| + | 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"); |
| + | } |
| + | } |
| } | |
| extern int unzip_main(int argc, char **argv) | |
| { | |
| - | 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; |
| - | |
| - | archive_handle_t *archive_handle; |
| + | 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; |
| - | break; |
| - | case 'p': /* extract files to stdout */ |
| - | archive_handle->action_data = data_extract_to_stdout; |
| + | |
| + | case 'n': /* Never overwrite existing files */ |
| + | overwrite = o_never; |
| break; | |
| - | case 'q': /* Extract files quietly */ |
| - | archive_handle->action_header = header_skip; |
| + | |
| + | case 'o': /* Always overwrite existing files */ |
| + | overwrite = o_always; |
| break; | |
| - | case 'd': /* Extract files to specified base directory*/ |
| - | base_dir = optarg; |
| + | |
| + | 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; | |
| - | #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"); |
| - | } |
| + | /* 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; |
| - | if (*argv[optind] == '-') { |
| - | archive_handle->src_fd = STDIN_FILENO; |
| - | archive_handle->seek = seek_by_char; |
| } 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("Invlaide zip magic"); |
| + | } else if (magic != ZIP_FILEHEADER_MAGIC) { |
| + | bb_error_msg_and_die("Invalid zip magic %08X", 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 (BYTE_ORDER == BIG_ENDIAN) |
| + | zip_header.formated.version = __swap16(zip_header.formated.version); |
| + | zip_header.formated.flags = __swap16(zip_header.formated.flags); |
| + | zip_header.formated.method = __swap16(zip_header.formated.method); |
| + | zip_header.formated.modtime = __swap16(zip_header.formated.modtime); |
| + | zip_header.formated.moddate = __swap16(zip_header.formated.moddate); |
| + | zip_header.formated.crc32 = __swap32(zip_header.formated.crc32); |
| + | zip_header.formated.cmpsize = __swap32(zip_header.formated.cmpsize); |
| + | zip_header.formated.ucmpsize = __swap32(zip_header.formated.ucmpsize); |
| + | zip_header.formated.filename_len = __swap16(zip_header.formated.filename_len); |
| + | zip_header.formated.extra_len = __swap16(zip_header.formated.extra_len); |
| + | #endif |
| + | 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; |
| + | 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; |
| } | |
| - | /* 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"); |
| + | /* 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); |
| + | } |
| + | } |
| } | |
| + | } |
| - | /* Validate decompression - size */ |
| - | if (gunzip_bytes_out != zip_header.formated.ucmpsize) { |
| - | bb_error_msg("Invalid compressed data--length error"); |
| + | 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; |
| - | /* 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++; |
| + | case 'N': |
| + | overwrite = o_never; |
| + | case 'n': |
| + | /* Skip entry data */ |
| + | unzip_skip(src_fd, zip_header.formated.cmpsize); |
| + | break; |
| - | archive_handle->action_header(archive_handle->file_header); |
| + | 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: |
| + | */ |
| @@ -98,6 +98,7 @@ extern const llist_t *find_list_entry(const llist_t *list, const char *filename) | |
| 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); | |