diff options
author | Glenn L McGrath <bug1@ihug.co.nz> | 2002-01-02 13:52:26 +0000 |
---|---|---|
committer | Glenn L McGrath <bug1@ihug.co.nz> | 2002-01-02 13:52:26 +0000 |
commit | 87ac7028e01cdc4f504ea558a6ae3d086ed1bf2b (patch) | |
tree | 4f406c877f88a013c4e1dc650ac312640a95cb00 | |
parent | 438803311b67c6337ea97476e97336e027ef9a3a (diff) | |
download | busybox-w32-87ac7028e01cdc4f504ea558a6ae3d086ed1bf2b.tar.gz busybox-w32-87ac7028e01cdc4f504ea558a6ae3d086ed1bf2b.tar.bz2 busybox-w32-87ac7028e01cdc4f504ea558a6ae3d086ed1bf2b.zip |
unzip applet by Laurence Anderson
----------------------------------------------------------------------
-rw-r--r-- | archival/Makefile | 1 | ||||
-rw-r--r-- | archival/config.in | 1 | ||||
-rw-r--r-- | archival/libunarchive/Makefile | 8 | ||||
-rw-r--r-- | archival/libunarchive/decompress_unzip.c | 48 | ||||
-rw-r--r-- | archival/libunarchive/get_header_zip.c | 110 | ||||
-rw-r--r-- | archival/libunarchive/unarchive.c | 3 | ||||
-rw-r--r-- | archival/libunarchive/unzip.c | 48 | ||||
-rw-r--r-- | archival/unzip.c | 94 | ||||
-rw-r--r-- | include/applets.h | 3 | ||||
-rw-r--r-- | include/libbb.h | 1 | ||||
-rw-r--r-- | include/unarchive.h | 2 | ||||
-rw-r--r-- | include/usage.h | 13 | ||||
-rw-r--r-- | libbb/unzip.c | 48 |
13 files changed, 302 insertions, 78 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 | |||
34 | obj-$(CONFIG_GZIP) += gzip.o | 34 | obj-$(CONFIG_GZIP) += gzip.o |
35 | obj-$(CONFIG_RPM2CPIO) += rpm2cpio.o | 35 | obj-$(CONFIG_RPM2CPIO) += rpm2cpio.o |
36 | obj-$(CONFIG_TAR) += tar.o | 36 | obj-$(CONFIG_TAR) += tar.o |
37 | obj-$(CONFIG_UNZIP) += unzip.o | ||
37 | 38 | ||
38 | 39 | ||
39 | # Hand off to toplevel Rules.mak | 40 | # 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 | |||
20 | bool ' Enable -X and --exclude options (exclude files)' CONFIG_FEATURE_TAR_EXCLUDE | 20 | bool ' Enable -X and --exclude options (exclude files)' CONFIG_FEATURE_TAR_EXCLUDE |
21 | bool ' Enable -z option (currently only for extracting)' CONFIG_FEATURE_TAR_GZIP | 21 | bool ' Enable -z option (currently only for extracting)' CONFIG_FEATURE_TAR_GZIP |
22 | fi | 22 | fi |
23 | bool 'unzip' CONFIG_UNZIP | ||
23 | endmenu | 24 | 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 @@ | |||
20 | TOPDIR :=../.. | 20 | TOPDIR :=../.. |
21 | L_TARGET := libunarchive.a | 21 | L_TARGET := libunarchive.a |
22 | 22 | ||
23 | obj-y := unarchive.o seek_sub_file.o | 23 | obj-y := unarchive.o seek_sub_file.o |
24 | obj-n := | 24 | obj-n := |
25 | obj- := | 25 | obj- := |
26 | 26 | ||
@@ -41,13 +41,17 @@ ifeq ($(CONFIG_CPIO),y) | |||
41 | endif | 41 | endif |
42 | 42 | ||
43 | ifeq ($(CONFIG_RPM2CPIO),y) | 43 | ifeq ($(CONFIG_RPM2CPIO),y) |
44 | obj-y += get_header_cpio.o | 44 | obj-y += get_header_cpio.o |
45 | endif | 45 | endif |
46 | 46 | ||
47 | ifeq ($(CONFIG_TAR),y) | 47 | ifeq ($(CONFIG_TAR),y) |
48 | obj-y += get_header_tar.o | 48 | obj-y += get_header_tar.o |
49 | endif | 49 | endif |
50 | 50 | ||
51 | ifeq ($(CONFIG_UNZIP),y) | ||
52 | obj-y += get_header_zip.o | ||
53 | endif | ||
54 | |||
51 | 55 | ||
52 | # Hand off to toplevel Rules.mak | 56 | # Hand off to toplevel Rules.mak |
53 | include $(TOPDIR)/Rules.mak | 57 | 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; | |||
80 | 80 | ||
81 | /* | 81 | /* |
82 | * window size--must be a power of two, and | 82 | * window size--must be a power of two, and |
83 | * at least 32K for zip's deflate method | 83 | * at least 32K for zip's deflate method |
84 | */ | 84 | */ |
85 | static const int WSIZE = 0x8000; | 85 | static const int WSIZE = 0x8000; |
86 | 86 | ||
@@ -846,7 +846,7 @@ static int inflate_block(int *e) | |||
846 | * | 846 | * |
847 | * GLOBAL VARIABLES: outcnt, bk, bb, hufts, inptr | 847 | * GLOBAL VARIABLES: outcnt, bk, bb, hufts, inptr |
848 | */ | 848 | */ |
849 | static int inflate(void) | 849 | extern int inflate(FILE *in, FILE *out) |
850 | { | 850 | { |
851 | int e; /* last block flag */ | 851 | int e; /* last block flag */ |
852 | int r; /* result code */ | 852 | int r; /* result code */ |
@@ -857,6 +857,13 @@ static int inflate(void) | |||
857 | bk = 0; | 857 | bk = 0; |
858 | bb = 0; | 858 | bb = 0; |
859 | 859 | ||
860 | in_file = in; | ||
861 | out_file = out; | ||
862 | |||
863 | /* Allocate all global buffers (for DYN_ALLOC option) */ | ||
864 | window = xmalloc((size_t)(((2L*WSIZE)+1L)*sizeof(unsigned char))); | ||
865 | bytes_out = 0L; | ||
866 | |||
860 | /* Create the crc table */ | 867 | /* Create the crc table */ |
861 | make_crc_table(); | 868 | make_crc_table(); |
862 | 869 | ||
@@ -881,13 +888,15 @@ static int inflate(void) | |||
881 | 888 | ||
882 | /* flush out window */ | 889 | /* flush out window */ |
883 | flush_window(); | 890 | flush_window(); |
891 | free(window); | ||
892 | free(crc_table); | ||
884 | 893 | ||
885 | /* return success */ | 894 | /* return success */ |
886 | return 0; | 895 | return 0; |
887 | } | 896 | } |
888 | 897 | ||
889 | /* =========================================================================== | 898 | /* =========================================================================== |
890 | * Unzip in to out. This routine works on both gzip and pkzip files. | 899 | * Unzip in to out. This routine works on gzip files only. |
891 | * | 900 | * |
892 | * IN assertions: the buffer inbuf contains already the beginning of | 901 | * IN assertions: the buffer inbuf contains already the beginning of |
893 | * the compressed data, from offsets inptr to insize-1 included. | 902 | * 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) | |||
901 | typedef void (*sig_type) (int); | 910 | typedef void (*sig_type) (int); |
902 | unsigned short i; | 911 | unsigned short i; |
903 | 912 | ||
904 | in_file = l_in_file; | ||
905 | out_file = l_out_file; | ||
906 | |||
907 | if (signal(SIGINT, SIG_IGN) != SIG_IGN) { | 913 | if (signal(SIGINT, SIG_IGN) != SIG_IGN) { |
908 | (void) signal(SIGINT, (sig_type) abort_gzip); | 914 | (void) signal(SIGINT, (sig_type) abort_gzip); |
909 | } | 915 | } |
@@ -918,53 +924,48 @@ extern int unzip(FILE *l_in_file, FILE *l_out_file) | |||
918 | } | 924 | } |
919 | #endif | 925 | #endif |
920 | 926 | ||
921 | /* Allocate all global buffers (for DYN_ALLOC option) */ | ||
922 | window = xmalloc((size_t)(((2L*WSIZE)+1L)*sizeof(unsigned char))); | ||
923 | outcnt = 0; | ||
924 | bytes_out = 0L; | ||
925 | |||
926 | /* Magic header for gzip files, 1F 8B = \037\213 */ | 927 | /* Magic header for gzip files, 1F 8B = \037\213 */ |
927 | if ((fgetc(in_file) != 0x1F) || (fgetc(in_file) != 0x8b)) { | 928 | if ((fgetc(l_in_file) != 0x1F) || (fgetc(l_in_file) != 0x8b)) { |
928 | error_msg("Invalid gzip magic"); | 929 | error_msg("Invalid gzip magic"); |
929 | return EXIT_FAILURE; | 930 | return EXIT_FAILURE; |
930 | } | 931 | } |
931 | 932 | ||
932 | /* Check the compression method */ | 933 | /* Check the compression method */ |
933 | if (fgetc(in_file) != 8) { | 934 | if (fgetc(l_in_file) != 8) { |
934 | error_msg("Unknown compression method"); | 935 | error_msg("Unknown compression method"); |
935 | return(-1); | 936 | return(-1); |
936 | } | 937 | } |
937 | 938 | ||
938 | flags = (unsigned char) fgetc(in_file); | 939 | flags = (unsigned char) fgetc(l_in_file); |
939 | 940 | ||
940 | /* Ignore time stamp(4), extra flags(1), OS type(1) */ | 941 | /* Ignore time stamp(4), extra flags(1), OS type(1) */ |
941 | for (i = 0; i < 6; i++) { | 942 | for (i = 0; i < 6; i++) { |
942 | fgetc(in_file); | 943 | fgetc(l_in_file); |
943 | } | 944 | } |
944 | 945 | ||
945 | if (flags & 0x04) { | 946 | if (flags & 0x04) { |
946 | /* bit 2 set: extra field present */ | 947 | /* bit 2 set: extra field present */ |
947 | const unsigned short extra = fgetc(in_file) + (fgetc(in_file) << 8); | 948 | const unsigned short extra = fgetc(l_in_file) + (fgetc(l_in_file) << 8); |
948 | 949 | ||
949 | for (i = 0; i < extra; i++) { | 950 | for (i = 0; i < extra; i++) { |
950 | fgetc(in_file); | 951 | fgetc(l_in_file); |
951 | } | 952 | } |
952 | } | 953 | } |
953 | 954 | ||
954 | /* Discard original name if any */ | 955 | /* Discard original name if any */ |
955 | if (flags & 0x08) { | 956 | if (flags & 0x08) { |
956 | /* bit 3 set: original file name present */ | 957 | /* bit 3 set: original file name present */ |
957 | while (fgetc(in_file) != 0); /* null */ | 958 | while (fgetc(l_in_file) != 0); /* null */ |
958 | } | 959 | } |
959 | 960 | ||
960 | /* Discard file comment if any */ | 961 | /* Discard file comment if any */ |
961 | if (flags & 0x10) { | 962 | if (flags & 0x10) { |
962 | /* bit 4 set: file comment present */ | 963 | /* bit 4 set: file comment present */ |
963 | while (fgetc(in_file) != 0); /* null */ | 964 | while (fgetc(l_in_file) != 0); /* null */ |
964 | } | 965 | } |
965 | 966 | ||
966 | /* Decompress */ | 967 | /* Decompress */ |
967 | if (inflate() != 0) { | 968 | if (inflate(l_in_file, l_out_file) != 0) { |
968 | error_msg("invalid compressed data--format violated"); | 969 | error_msg("invalid compressed data--format violated"); |
969 | } | 970 | } |
970 | 971 | ||
@@ -972,7 +973,7 @@ extern int unzip(FILE *l_in_file, FILE *l_out_file) | |||
972 | * crc32 (see algorithm.doc) | 973 | * crc32 (see algorithm.doc) |
973 | * uncompressed input size modulo 2^32 | 974 | * uncompressed input size modulo 2^32 |
974 | */ | 975 | */ |
975 | fread(buf, 1, 8, in_file); | 976 | fread(buf, 1, 8, l_in_file); |
976 | 977 | ||
977 | /* Validate decompression - crc */ | 978 | /* Validate decompression - crc */ |
978 | if ((unsigned int)((buf[0] | (buf[1] << 8)) |((buf[2] | (buf[3] << 8)) << 16)) != (crc ^ 0xffffffffL)) { | 979 | 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) | |||
983 | error_msg("invalid compressed data--length error"); | 984 | error_msg("invalid compressed data--length error"); |
984 | } | 985 | } |
985 | 986 | ||
986 | free(window); | ||
987 | free(crc_table); | ||
988 | |||
989 | return 0; | 987 | return 0; |
990 | } | 988 | } |
991 | 989 | ||
992 | /* | 990 | /* |
993 | * This needs access to global variables wondow and crc_table, so its not in its own file. | 991 | * This needs access to global variables window and crc_table, so its not in its own file. |
994 | */ | 992 | */ |
995 | extern void gz_close(int gunzip_pid) | 993 | extern void gz_close(int gunzip_pid) |
996 | { | 994 | { |
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 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * get_header_zip for busybox | ||
4 | * | ||
5 | * Copyright (C) 2001 by Laurence Anderson | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
15 | * General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
20 | */ | ||
21 | |||
22 | #include <stdio.h> | ||
23 | #include <stdlib.h> | ||
24 | #include <string.h> | ||
25 | #include "unarchive.h" | ||
26 | #include "libbb.h" | ||
27 | |||
28 | #define ZIP_FILEHEADER_MAGIC 0x04034b50 | ||
29 | #define ZIP_CDS_MAGIC 0x02014b50 | ||
30 | #define ZIP_CDS_END_MAGIC 0x06054b50 | ||
31 | #define ZIP_DD_MAGIC 0x8074b50 | ||
32 | |||
33 | file_header_t *get_header_zip(FILE *zip_stream) | ||
34 | { | ||
35 | struct { | ||
36 | short version __attribute__ ((packed)); | ||
37 | short flags __attribute__ ((packed)); | ||
38 | short method __attribute__ ((packed)); | ||
39 | short modtime __attribute__ ((packed)); | ||
40 | short moddate __attribute__ ((packed)); | ||
41 | int crc32 __attribute__ ((packed)); | ||
42 | int cmpsize __attribute__ ((packed)); | ||
43 | int ucmpsize __attribute__ ((packed)); | ||
44 | short filename_len __attribute__ ((packed)); | ||
45 | short extra_len __attribute__ ((packed)); | ||
46 | } zip_header; | ||
47 | file_header_t *zip_entry = NULL; | ||
48 | int magic; | ||
49 | static int dd_ahead = 0; // If this is true, the we didn't know how long the last extraced file was | ||
50 | |||
51 | fread (&magic, 4, 1, zip_stream); | ||
52 | archive_offset += 4; | ||
53 | |||
54 | if (feof(zip_stream)) return(NULL); | ||
55 | checkmagic: | ||
56 | switch (magic) { | ||
57 | case ZIP_FILEHEADER_MAGIC: | ||
58 | zip_entry = xcalloc(1, sizeof(file_header_t)); | ||
59 | fread (&zip_header, sizeof(zip_header), 1, zip_stream); | ||
60 | archive_offset += sizeof(zip_header); | ||
61 | if (!(zip_header.method == 8 || zip_header.method == 0)) { printf("Unsupported compression method %d\n", zip_header.method); return(NULL); } | ||
62 | zip_entry->name = calloc(zip_header.filename_len + 1, sizeof(char)); | ||
63 | fread (zip_entry->name, sizeof(char), zip_header.filename_len, zip_stream); | ||
64 | archive_offset += zip_header.filename_len; | ||
65 | seek_sub_file(zip_stream, zip_header.extra_len); | ||
66 | zip_entry->size = zip_header.cmpsize; | ||
67 | if (zip_header.method == 8) zip_entry->extract_func = &inflate; | ||
68 | zip_entry->mode = S_IFREG | 0777; | ||
69 | // Time/Date? | ||
70 | if (*(zip_entry->name + strlen(zip_entry->name) - 1) == '/') { // Files that end in a / are directories | ||
71 | zip_entry->mode ^= S_IFREG; | ||
72 | zip_entry->mode |= S_IFDIR; | ||
73 | *(zip_entry->name + strlen(zip_entry->name) - 1) = '\0'; // Remove trailing / so unarchive doesn't get confused | ||
74 | } | ||
75 | //printf("cmpsize: %d, ucmpsize: %d, method: %d\n", zip_header.cmpsize, zip_header.ucmpsize, zip_header.method); | ||
76 | if (zip_header.flags & 0x8) { // crc32, and sizes are in the data description _after_ the file | ||
77 | 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 | ||
78 | if (zip_header.ucmpsize != 0) dd_ahead = 2; // Humm... we would otherwise skip this twice - not good! | ||
79 | } | ||
80 | break; | ||
81 | case ZIP_CDS_MAGIC: /* FALLTHRU */ | ||
82 | case ZIP_CDS_END_MAGIC: | ||
83 | return(NULL); | ||
84 | break; | ||
85 | case ZIP_DD_MAGIC: { | ||
86 | int cmpsize; | ||
87 | seek_sub_file(zip_stream, 4); // Skip crc32 | ||
88 | fread(&cmpsize, 4, 1, zip_stream); | ||
89 | archive_offset += 4; | ||
90 | if (dd_ahead == 1) archive_offset += cmpsize; | ||
91 | seek_sub_file(zip_stream, 4); // Skip uncompressed size | ||
92 | dd_ahead = 0; | ||
93 | return (get_header_zip(zip_stream)); | ||
94 | break; } | ||
95 | default: | ||
96 | if (!dd_ahead) error_msg("Invalid magic (%#x): Trying to skip junk", magic); | ||
97 | dd_ahead = 0; | ||
98 | while (!feof(zip_stream)) { | ||
99 | int tmpmagic; | ||
100 | tmpmagic = fgetc(zip_stream); | ||
101 | archive_offset++; | ||
102 | magic = ((magic >> 8) & 0x00ffffff) | ((tmpmagic << 24) & 0xff000000); | ||
103 | if (magic == ZIP_FILEHEADER_MAGIC || magic == ZIP_CDS_MAGIC || magic == ZIP_CDS_END_MAGIC) goto checkmagic; | ||
104 | } | ||
105 | error_msg("End of archive reached: Bad archive"); | ||
106 | return(NULL); | ||
107 | } | ||
108 | //if (archive_offset != ftell(zip_stream)) printf("Archive offset out of sync (%d,%d)\n", (int) archive_offset, (int) ftell(zip_stream)); | ||
109 | return(zip_entry); | ||
110 | } | ||
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 | |||
120 | return NULL; | 120 | return NULL; |
121 | } | 121 | } |
122 | archive_offset += file_entry->size; | 122 | archive_offset += file_entry->size; |
123 | copy_file_chunk(src_stream, dst_stream, file_entry->size); | 123 | if (file_entry->extract_func) file_entry->extract_func(src_stream, dst_stream); |
124 | else copy_file_chunk(src_stream, dst_stream, file_entry->size); | ||
124 | fclose(dst_stream); | 125 | fclose(dst_stream); |
125 | } | 126 | } |
126 | break; | 127 | 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; | |||
80 | 80 | ||
81 | /* | 81 | /* |
82 | * window size--must be a power of two, and | 82 | * window size--must be a power of two, and |
83 | * at least 32K for zip's deflate method | 83 | * at least 32K for zip's deflate method |
84 | */ | 84 | */ |
85 | static const int WSIZE = 0x8000; | 85 | static const int WSIZE = 0x8000; |
86 | 86 | ||
@@ -846,7 +846,7 @@ static int inflate_block(int *e) | |||
846 | * | 846 | * |
847 | * GLOBAL VARIABLES: outcnt, bk, bb, hufts, inptr | 847 | * GLOBAL VARIABLES: outcnt, bk, bb, hufts, inptr |
848 | */ | 848 | */ |
849 | static int inflate(void) | 849 | extern int inflate(FILE *in, FILE *out) |
850 | { | 850 | { |
851 | int e; /* last block flag */ | 851 | int e; /* last block flag */ |
852 | int r; /* result code */ | 852 | int r; /* result code */ |
@@ -857,6 +857,13 @@ static int inflate(void) | |||
857 | bk = 0; | 857 | bk = 0; |
858 | bb = 0; | 858 | bb = 0; |
859 | 859 | ||
860 | in_file = in; | ||
861 | out_file = out; | ||
862 | |||
863 | /* Allocate all global buffers (for DYN_ALLOC option) */ | ||
864 | window = xmalloc((size_t)(((2L*WSIZE)+1L)*sizeof(unsigned char))); | ||
865 | bytes_out = 0L; | ||
866 | |||
860 | /* Create the crc table */ | 867 | /* Create the crc table */ |
861 | make_crc_table(); | 868 | make_crc_table(); |
862 | 869 | ||
@@ -881,13 +888,15 @@ static int inflate(void) | |||
881 | 888 | ||
882 | /* flush out window */ | 889 | /* flush out window */ |
883 | flush_window(); | 890 | flush_window(); |
891 | free(window); | ||
892 | free(crc_table); | ||
884 | 893 | ||
885 | /* return success */ | 894 | /* return success */ |
886 | return 0; | 895 | return 0; |
887 | } | 896 | } |
888 | 897 | ||
889 | /* =========================================================================== | 898 | /* =========================================================================== |
890 | * Unzip in to out. This routine works on both gzip and pkzip files. | 899 | * Unzip in to out. This routine works on gzip files only. |
891 | * | 900 | * |
892 | * IN assertions: the buffer inbuf contains already the beginning of | 901 | * IN assertions: the buffer inbuf contains already the beginning of |
893 | * the compressed data, from offsets inptr to insize-1 included. | 902 | * 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) | |||
901 | typedef void (*sig_type) (int); | 910 | typedef void (*sig_type) (int); |
902 | unsigned short i; | 911 | unsigned short i; |
903 | 912 | ||
904 | in_file = l_in_file; | ||
905 | out_file = l_out_file; | ||
906 | |||
907 | if (signal(SIGINT, SIG_IGN) != SIG_IGN) { | 913 | if (signal(SIGINT, SIG_IGN) != SIG_IGN) { |
908 | (void) signal(SIGINT, (sig_type) abort_gzip); | 914 | (void) signal(SIGINT, (sig_type) abort_gzip); |
909 | } | 915 | } |
@@ -918,53 +924,48 @@ extern int unzip(FILE *l_in_file, FILE *l_out_file) | |||
918 | } | 924 | } |
919 | #endif | 925 | #endif |
920 | 926 | ||
921 | /* Allocate all global buffers (for DYN_ALLOC option) */ | ||
922 | window = xmalloc((size_t)(((2L*WSIZE)+1L)*sizeof(unsigned char))); | ||
923 | outcnt = 0; | ||
924 | bytes_out = 0L; | ||
925 | |||
926 | /* Magic header for gzip files, 1F 8B = \037\213 */ | 927 | /* Magic header for gzip files, 1F 8B = \037\213 */ |
927 | if ((fgetc(in_file) != 0x1F) || (fgetc(in_file) != 0x8b)) { | 928 | if ((fgetc(l_in_file) != 0x1F) || (fgetc(l_in_file) != 0x8b)) { |
928 | error_msg("Invalid gzip magic"); | 929 | error_msg("Invalid gzip magic"); |
929 | return EXIT_FAILURE; | 930 | return EXIT_FAILURE; |
930 | } | 931 | } |
931 | 932 | ||
932 | /* Check the compression method */ | 933 | /* Check the compression method */ |
933 | if (fgetc(in_file) != 8) { | 934 | if (fgetc(l_in_file) != 8) { |
934 | error_msg("Unknown compression method"); | 935 | error_msg("Unknown compression method"); |
935 | return(-1); | 936 | return(-1); |
936 | } | 937 | } |
937 | 938 | ||
938 | flags = (unsigned char) fgetc(in_file); | 939 | flags = (unsigned char) fgetc(l_in_file); |
939 | 940 | ||
940 | /* Ignore time stamp(4), extra flags(1), OS type(1) */ | 941 | /* Ignore time stamp(4), extra flags(1), OS type(1) */ |
941 | for (i = 0; i < 6; i++) { | 942 | for (i = 0; i < 6; i++) { |
942 | fgetc(in_file); | 943 | fgetc(l_in_file); |
943 | } | 944 | } |
944 | 945 | ||
945 | if (flags & 0x04) { | 946 | if (flags & 0x04) { |
946 | /* bit 2 set: extra field present */ | 947 | /* bit 2 set: extra field present */ |
947 | const unsigned short extra = fgetc(in_file) + (fgetc(in_file) << 8); | 948 | const unsigned short extra = fgetc(l_in_file) + (fgetc(l_in_file) << 8); |
948 | 949 | ||
949 | for (i = 0; i < extra; i++) { | 950 | for (i = 0; i < extra; i++) { |
950 | fgetc(in_file); | 951 | fgetc(l_in_file); |
951 | } | 952 | } |
952 | } | 953 | } |
953 | 954 | ||
954 | /* Discard original name if any */ | 955 | /* Discard original name if any */ |
955 | if (flags & 0x08) { | 956 | if (flags & 0x08) { |
956 | /* bit 3 set: original file name present */ | 957 | /* bit 3 set: original file name present */ |
957 | while (fgetc(in_file) != 0); /* null */ | 958 | while (fgetc(l_in_file) != 0); /* null */ |
958 | } | 959 | } |
959 | 960 | ||
960 | /* Discard file comment if any */ | 961 | /* Discard file comment if any */ |
961 | if (flags & 0x10) { | 962 | if (flags & 0x10) { |
962 | /* bit 4 set: file comment present */ | 963 | /* bit 4 set: file comment present */ |
963 | while (fgetc(in_file) != 0); /* null */ | 964 | while (fgetc(l_in_file) != 0); /* null */ |
964 | } | 965 | } |
965 | 966 | ||
966 | /* Decompress */ | 967 | /* Decompress */ |
967 | if (inflate() != 0) { | 968 | if (inflate(l_in_file, l_out_file) != 0) { |
968 | error_msg("invalid compressed data--format violated"); | 969 | error_msg("invalid compressed data--format violated"); |
969 | } | 970 | } |
970 | 971 | ||
@@ -972,7 +973,7 @@ extern int unzip(FILE *l_in_file, FILE *l_out_file) | |||
972 | * crc32 (see algorithm.doc) | 973 | * crc32 (see algorithm.doc) |
973 | * uncompressed input size modulo 2^32 | 974 | * uncompressed input size modulo 2^32 |
974 | */ | 975 | */ |
975 | fread(buf, 1, 8, in_file); | 976 | fread(buf, 1, 8, l_in_file); |
976 | 977 | ||
977 | /* Validate decompression - crc */ | 978 | /* Validate decompression - crc */ |
978 | if ((unsigned int)((buf[0] | (buf[1] << 8)) |((buf[2] | (buf[3] << 8)) << 16)) != (crc ^ 0xffffffffL)) { | 979 | 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) | |||
983 | error_msg("invalid compressed data--length error"); | 984 | error_msg("invalid compressed data--length error"); |
984 | } | 985 | } |
985 | 986 | ||
986 | free(window); | ||
987 | free(crc_table); | ||
988 | |||
989 | return 0; | 987 | return 0; |
990 | } | 988 | } |
991 | 989 | ||
992 | /* | 990 | /* |
993 | * This needs access to global variables wondow and crc_table, so its not in its own file. | 991 | * This needs access to global variables window and crc_table, so its not in its own file. |
994 | */ | 992 | */ |
995 | extern void gz_close(int gunzip_pid) | 993 | extern void gz_close(int gunzip_pid) |
996 | { | 994 | { |
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 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Mini unzip implementation for busybox | ||
4 | * | ||
5 | * Copyright (C) 2001 by Laurence Anderson | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
15 | * General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
20 | * | ||
21 | */ | ||
22 | |||
23 | #include <fcntl.h> | ||
24 | #include <getopt.h> | ||
25 | #include <stdlib.h> | ||
26 | #include <string.h> | ||
27 | #include <unistd.h> | ||
28 | #include "unarchive.h" | ||
29 | #include "busybox.h" | ||
30 | |||
31 | extern int unzip_main(int argc, char **argv) | ||
32 | { | ||
33 | FILE *src_stream; | ||
34 | int extract_function = extract_all_to_fs | extract_create_leading_dirs; | ||
35 | char **extract_names = NULL; | ||
36 | char **exclude_names = NULL; | ||
37 | int opt = 0; | ||
38 | int num_of_entries = 0; | ||
39 | int exclude = 0; | ||
40 | char *outdir = "./"; | ||
41 | FILE *msgout = stdout; | ||
42 | |||
43 | while ((opt = getopt(argc, argv, "lnopqxd:")) != -1) { | ||
44 | switch (opt) { | ||
45 | case 'l': | ||
46 | extract_function |= extract_verbose_list; | ||
47 | extract_function ^= extract_all_to_fs; | ||
48 | break; | ||
49 | case 'n': | ||
50 | break; | ||
51 | case 'o': | ||
52 | extract_function |= extract_unconditional; | ||
53 | break; | ||
54 | case 'p': | ||
55 | extract_function |= extract_to_stdout; | ||
56 | extract_function ^= extract_all_to_fs; | ||
57 | /* FALLTHROUGH */ | ||
58 | case 'q': | ||
59 | msgout = xfopen("/dev/null", "w"); | ||
60 | break; | ||
61 | case 'd': | ||
62 | outdir = xstrdup(optarg); | ||
63 | strcat(outdir, "/"); | ||
64 | break; | ||
65 | case 'x': | ||
66 | exclude = 1; | ||
67 | break; | ||
68 | } | ||
69 | } | ||
70 | |||
71 | if (optind == argc) { | ||
72 | show_usage(); | ||
73 | } | ||
74 | |||
75 | if (*argv[optind] == '-') src_stream = stdin; | ||
76 | else src_stream = xfopen(argv[optind++], "r"); | ||
77 | |||
78 | while (optind < argc) { | ||
79 | if (exclude) { | ||
80 | exclude_names = xrealloc(exclude_names, sizeof(char *) * (num_of_entries + 2)); | ||
81 | exclude_names[num_of_entries] = xstrdup(argv[optind]); | ||
82 | } else { | ||
83 | extract_names = xrealloc(extract_names, sizeof(char *) * (num_of_entries + 2)); | ||
84 | extract_names[num_of_entries] = xstrdup(argv[optind]); | ||
85 | } | ||
86 | num_of_entries++; | ||
87 | if (exclude) exclude_names[num_of_entries] = NULL; | ||
88 | else extract_names[num_of_entries] = NULL; | ||
89 | optind++; | ||
90 | } | ||
91 | |||
92 | unarchive(src_stream, msgout, &get_header_zip, extract_function, outdir, extract_names, exclude_names); | ||
93 | return EXIT_SUCCESS; | ||
94 | } | ||
diff --git a/include/applets.h b/include/applets.h index ba0a9aaa9..0d310bdc3 100644 --- a/include/applets.h +++ b/include/applets.h | |||
@@ -470,6 +470,9 @@ | |||
470 | #ifdef CONFIG_UNIX2DOS | 470 | #ifdef CONFIG_UNIX2DOS |
471 | APPLET(unix2dos, dos2unix_main, _BB_DIR_USR_BIN) | 471 | APPLET(unix2dos, dos2unix_main, _BB_DIR_USR_BIN) |
472 | #endif | 472 | #endif |
473 | #ifdef CONFIG_UNZIP | ||
474 | APPLET(unzip, unzip_main, _BB_DIR_USR_BIN) | ||
475 | #endif | ||
473 | #ifdef CONFIG_UPDATE | 476 | #ifdef CONFIG_UPDATE |
474 | APPLET(update, update_main, _BB_DIR_SBIN) | 477 | APPLET(update, update_main, _BB_DIR_SBIN) |
475 | #endif | 478 | #endif |
diff --git a/include/libbb.h b/include/libbb.h index fccdf5fdf..8dadfd958 100644 --- a/include/libbb.h +++ b/include/libbb.h | |||
@@ -215,6 +215,7 @@ extern long arith (const char *startbuf, int *errcode); | |||
215 | int read_package_field(const char *package_buffer, char **field_name, char **field_value); | 215 | int read_package_field(const char *package_buffer, char **field_name, char **field_value); |
216 | char *fgets_str(FILE *file, const char *terminating_string); | 216 | char *fgets_str(FILE *file, const char *terminating_string); |
217 | 217 | ||
218 | extern int inflate(FILE *in, FILE *out); | ||
218 | extern int unzip(FILE *l_in_file, FILE *l_out_file); | 219 | extern int unzip(FILE *l_in_file, FILE *l_out_file); |
219 | extern void gz_close(int gunzip_pid); | 220 | extern void gz_close(int gunzip_pid); |
220 | extern FILE *gz_open(FILE *compressed_file, int *pid); | 221 | extern FILE *gz_open(FILE *compressed_file, int *pid); |
diff --git a/include/unarchive.h b/include/unarchive.h index be49f3d01..eada1c337 100644 --- a/include/unarchive.h +++ b/include/unarchive.h | |||
@@ -26,11 +26,13 @@ typedef struct file_headers_s { | |||
26 | mode_t mode; | 26 | mode_t mode; |
27 | time_t mtime; | 27 | time_t mtime; |
28 | dev_t device; | 28 | dev_t device; |
29 | int (*extract_func)(FILE *, FILE *); | ||
29 | } file_header_t; | 30 | } file_header_t; |
30 | 31 | ||
31 | file_header_t *get_header_ar(FILE *in_file); | 32 | file_header_t *get_header_ar(FILE *in_file); |
32 | file_header_t *get_header_cpio(FILE *src_stream); | 33 | file_header_t *get_header_cpio(FILE *src_stream); |
33 | file_header_t *get_header_tar(FILE *tar_stream); | 34 | file_header_t *get_header_tar(FILE *tar_stream); |
35 | file_header_t *get_header_zip(FILE *zip_stream); | ||
34 | 36 | ||
35 | void seek_sub_file(FILE *src_stream, const int count); | 37 | void seek_sub_file(FILE *src_stream, const int count); |
36 | 38 | ||
diff --git a/include/usage.h b/include/usage.h index 20e2448fd..cd3af9c55 100644 --- a/include/usage.h +++ b/include/usage.h | |||
@@ -1851,6 +1851,19 @@ | |||
1851 | "\t-u\toutput will be in UNIX format\n" \ | 1851 | "\t-u\toutput will be in UNIX format\n" \ |
1852 | "\t-d\toutput will be in DOS format" | 1852 | "\t-d\toutput will be in DOS format" |
1853 | 1853 | ||
1854 | #define unzip_trivial_usage \ | ||
1855 | "[-opts[modifiers]] file[.zip] [list] [-x xlist] [-d exdir]" | ||
1856 | #define unzip_full_usage \ | ||
1857 | "Extracts files from ZIP archives\n" \ | ||
1858 | "Options:\n" \ | ||
1859 | "\t-l\tlist archive contents (short form)\n" \ | ||
1860 | "\t-n\tnever overwrite existing files (default)\n" \ | ||
1861 | "\t-o\toverwrite files without prompting\n" \ | ||
1862 | "\t-p\tsend output to stdout\n" \ | ||
1863 | "\t-q\tbe quiet\n" \ | ||
1864 | "\t-x\texclude these files\n" \ | ||
1865 | "\t-d\textract files into this directory" | ||
1866 | |||
1854 | #define update_trivial_usage \ | 1867 | #define update_trivial_usage \ |
1855 | "[options]" | 1868 | "[options]" |
1856 | #define update_full_usage \ | 1869 | #define update_full_usage \ |
diff --git a/libbb/unzip.c b/libbb/unzip.c index 6c28d181d..8075fd717 100644 --- a/libbb/unzip.c +++ b/libbb/unzip.c | |||
@@ -80,7 +80,7 @@ static const int ERROR = 1; | |||
80 | 80 | ||
81 | /* | 81 | /* |
82 | * window size--must be a power of two, and | 82 | * window size--must be a power of two, and |
83 | * at least 32K for zip's deflate method | 83 | * at least 32K for zip's deflate method |
84 | */ | 84 | */ |
85 | static const int WSIZE = 0x8000; | 85 | static const int WSIZE = 0x8000; |
86 | 86 | ||
@@ -846,7 +846,7 @@ static int inflate_block(int *e) | |||
846 | * | 846 | * |
847 | * GLOBAL VARIABLES: outcnt, bk, bb, hufts, inptr | 847 | * GLOBAL VARIABLES: outcnt, bk, bb, hufts, inptr |
848 | */ | 848 | */ |
849 | static int inflate(void) | 849 | extern int inflate(FILE *in, FILE *out) |
850 | { | 850 | { |
851 | int e; /* last block flag */ | 851 | int e; /* last block flag */ |
852 | int r; /* result code */ | 852 | int r; /* result code */ |
@@ -857,6 +857,13 @@ static int inflate(void) | |||
857 | bk = 0; | 857 | bk = 0; |
858 | bb = 0; | 858 | bb = 0; |
859 | 859 | ||
860 | in_file = in; | ||
861 | out_file = out; | ||
862 | |||
863 | /* Allocate all global buffers (for DYN_ALLOC option) */ | ||
864 | window = xmalloc((size_t)(((2L*WSIZE)+1L)*sizeof(unsigned char))); | ||
865 | bytes_out = 0L; | ||
866 | |||
860 | /* Create the crc table */ | 867 | /* Create the crc table */ |
861 | make_crc_table(); | 868 | make_crc_table(); |
862 | 869 | ||
@@ -881,13 +888,15 @@ static int inflate(void) | |||
881 | 888 | ||
882 | /* flush out window */ | 889 | /* flush out window */ |
883 | flush_window(); | 890 | flush_window(); |
891 | free(window); | ||
892 | free(crc_table); | ||
884 | 893 | ||
885 | /* return success */ | 894 | /* return success */ |
886 | return 0; | 895 | return 0; |
887 | } | 896 | } |
888 | 897 | ||
889 | /* =========================================================================== | 898 | /* =========================================================================== |
890 | * Unzip in to out. This routine works on both gzip and pkzip files. | 899 | * Unzip in to out. This routine works on gzip files only. |
891 | * | 900 | * |
892 | * IN assertions: the buffer inbuf contains already the beginning of | 901 | * IN assertions: the buffer inbuf contains already the beginning of |
893 | * the compressed data, from offsets inptr to insize-1 included. | 902 | * 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) | |||
901 | typedef void (*sig_type) (int); | 910 | typedef void (*sig_type) (int); |
902 | unsigned short i; | 911 | unsigned short i; |
903 | 912 | ||
904 | in_file = l_in_file; | ||
905 | out_file = l_out_file; | ||
906 | |||
907 | if (signal(SIGINT, SIG_IGN) != SIG_IGN) { | 913 | if (signal(SIGINT, SIG_IGN) != SIG_IGN) { |
908 | (void) signal(SIGINT, (sig_type) abort_gzip); | 914 | (void) signal(SIGINT, (sig_type) abort_gzip); |
909 | } | 915 | } |
@@ -918,53 +924,48 @@ extern int unzip(FILE *l_in_file, FILE *l_out_file) | |||
918 | } | 924 | } |
919 | #endif | 925 | #endif |
920 | 926 | ||
921 | /* Allocate all global buffers (for DYN_ALLOC option) */ | ||
922 | window = xmalloc((size_t)(((2L*WSIZE)+1L)*sizeof(unsigned char))); | ||
923 | outcnt = 0; | ||
924 | bytes_out = 0L; | ||
925 | |||
926 | /* Magic header for gzip files, 1F 8B = \037\213 */ | 927 | /* Magic header for gzip files, 1F 8B = \037\213 */ |
927 | if ((fgetc(in_file) != 0x1F) || (fgetc(in_file) != 0x8b)) { | 928 | if ((fgetc(l_in_file) != 0x1F) || (fgetc(l_in_file) != 0x8b)) { |
928 | error_msg("Invalid gzip magic"); | 929 | error_msg("Invalid gzip magic"); |
929 | return EXIT_FAILURE; | 930 | return EXIT_FAILURE; |
930 | } | 931 | } |
931 | 932 | ||
932 | /* Check the compression method */ | 933 | /* Check the compression method */ |
933 | if (fgetc(in_file) != 8) { | 934 | if (fgetc(l_in_file) != 8) { |
934 | error_msg("Unknown compression method"); | 935 | error_msg("Unknown compression method"); |
935 | return(-1); | 936 | return(-1); |
936 | } | 937 | } |
937 | 938 | ||
938 | flags = (unsigned char) fgetc(in_file); | 939 | flags = (unsigned char) fgetc(l_in_file); |
939 | 940 | ||
940 | /* Ignore time stamp(4), extra flags(1), OS type(1) */ | 941 | /* Ignore time stamp(4), extra flags(1), OS type(1) */ |
941 | for (i = 0; i < 6; i++) { | 942 | for (i = 0; i < 6; i++) { |
942 | fgetc(in_file); | 943 | fgetc(l_in_file); |
943 | } | 944 | } |
944 | 945 | ||
945 | if (flags & 0x04) { | 946 | if (flags & 0x04) { |
946 | /* bit 2 set: extra field present */ | 947 | /* bit 2 set: extra field present */ |
947 | const unsigned short extra = fgetc(in_file) + (fgetc(in_file) << 8); | 948 | const unsigned short extra = fgetc(l_in_file) + (fgetc(l_in_file) << 8); |
948 | 949 | ||
949 | for (i = 0; i < extra; i++) { | 950 | for (i = 0; i < extra; i++) { |
950 | fgetc(in_file); | 951 | fgetc(l_in_file); |
951 | } | 952 | } |
952 | } | 953 | } |
953 | 954 | ||
954 | /* Discard original name if any */ | 955 | /* Discard original name if any */ |
955 | if (flags & 0x08) { | 956 | if (flags & 0x08) { |
956 | /* bit 3 set: original file name present */ | 957 | /* bit 3 set: original file name present */ |
957 | while (fgetc(in_file) != 0); /* null */ | 958 | while (fgetc(l_in_file) != 0); /* null */ |
958 | } | 959 | } |
959 | 960 | ||
960 | /* Discard file comment if any */ | 961 | /* Discard file comment if any */ |
961 | if (flags & 0x10) { | 962 | if (flags & 0x10) { |
962 | /* bit 4 set: file comment present */ | 963 | /* bit 4 set: file comment present */ |
963 | while (fgetc(in_file) != 0); /* null */ | 964 | while (fgetc(l_in_file) != 0); /* null */ |
964 | } | 965 | } |
965 | 966 | ||
966 | /* Decompress */ | 967 | /* Decompress */ |
967 | if (inflate() != 0) { | 968 | if (inflate(l_in_file, l_out_file) != 0) { |
968 | error_msg("invalid compressed data--format violated"); | 969 | error_msg("invalid compressed data--format violated"); |
969 | } | 970 | } |
970 | 971 | ||
@@ -972,7 +973,7 @@ extern int unzip(FILE *l_in_file, FILE *l_out_file) | |||
972 | * crc32 (see algorithm.doc) | 973 | * crc32 (see algorithm.doc) |
973 | * uncompressed input size modulo 2^32 | 974 | * uncompressed input size modulo 2^32 |
974 | */ | 975 | */ |
975 | fread(buf, 1, 8, in_file); | 976 | fread(buf, 1, 8, l_in_file); |
976 | 977 | ||
977 | /* Validate decompression - crc */ | 978 | /* Validate decompression - crc */ |
978 | if ((unsigned int)((buf[0] | (buf[1] << 8)) |((buf[2] | (buf[3] << 8)) << 16)) != (crc ^ 0xffffffffL)) { | 979 | 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) | |||
983 | error_msg("invalid compressed data--length error"); | 984 | error_msg("invalid compressed data--length error"); |
984 | } | 985 | } |
985 | 986 | ||
986 | free(window); | ||
987 | free(crc_table); | ||
988 | |||
989 | return 0; | 987 | return 0; |
990 | } | 988 | } |
991 | 989 | ||
992 | /* | 990 | /* |
993 | * This needs access to global variables wondow and crc_table, so its not in its own file. | 991 | * This needs access to global variables window and crc_table, so its not in its own file. |
994 | */ | 992 | */ |
995 | extern void gz_close(int gunzip_pid) | 993 | extern void gz_close(int gunzip_pid) |
996 | { | 994 | { |