Improve rebuilding using asciidoc by using files in .deps/
[elinks.git] / src / encoding / gzip.c
blobcd513838b7edee84511c65b630d9ca77f80c124e
1 /* Gzip encoding (ENCODING_GZIP) backend */
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
7 #include <stdio.h>
8 #include <string.h>
9 #ifdef HAVE_UNISTD_H
10 #include <unistd.h>
11 #endif
12 #ifdef HAVE_ZLIB_H
13 #include <zlib.h>
14 #endif
16 #include "elinks.h"
18 #include "encoding/encoding.h"
19 #include "encoding/gzip.h"
20 #include "osdep/osdep.h"
21 #include "util/memory.h"
24 static int
25 gzip_open(struct stream_encoded *stream, int fd)
27 stream->data = (void *) gzdopen(fd, "rb");
28 if (!stream->data) return -1;
30 return 0;
33 static int
34 gzip_read(struct stream_encoded *stream, unsigned char *data, int len)
36 return gzread((gzFile *) stream->data, data, len);
39 static unsigned char *
40 gzip_decode(struct stream_encoded *stream, unsigned char *data, int len,
41 int *new_len)
43 *new_len = len;
44 return data;
48 /* The following code for decoding gzip in memory is a mix of code from zlib's
49 * gzio.c file copyrighted 1995-2002 by Jean-loup Gailly and the costumized
50 * header extraction in the linux kernels lib/inflate.c file not copyrighted
51 * 1992 by Mark Adler. */
53 static int gzip_header_magic[2] = { 0x1f, 0x8b };
55 enum gzip_header_flag {
56 GZIP_ASCII_TEXT = 0x01, /* File probably ascii text (unused) */
57 GZIP_HEADER_CRC = 0x02, /* Header CRC present */
58 GZIP_EXTRA_FIELD = 0x04, /* Extra field present */
59 GZIP_ORIG_NAME = 0x08, /* Original file name present */
60 GZIP_COMMENT = 0x10, /* File comment present */
61 GZIP_RESERVED = 0xE0, /* bits 5..7: reserved */
64 /* Read a byte from a gz_stream; update next_in and avail_in. Return EOF for
65 * end of file. */
66 static int
67 get_gzip_byte(z_stream *stream)
69 if (stream->avail_in == 0)
70 return EOF;
72 stream->avail_in--;
74 return *(stream->next_in)++;
77 #define skip_gzip_bytes(stream, bytes) \
78 do { int i = bytes; while (i-- > 0) get_gzip_byte(stream); } while (0)
80 #define skip_gzip_string(stream) \
81 do { int i; while ((i = get_gzip_byte(stream)) != 0 && i != EOF) ; } while (0)
83 /* Check the gzip header of a gz_stream opened for reading. Set the stream mode
84 * to transparent if the gzip magic header is not present; set s->err to
85 * Z_DATA_ERROR if the magic header is present but the rest of the header is
86 * incorrect. */
87 static int
88 skip_gzip_header(z_stream *stream)
90 unsigned int len;
91 int method; /* method byte */
92 int flags; /* flags byte */
94 /* Check the gzip magic header */
95 for (len = 0; len < 2; len++) {
96 int byte = get_gzip_byte(stream);
98 if (byte != gzip_header_magic[len]) {
99 if (len != 0) {
100 stream->avail_in++;
101 stream->next_in--;
104 if (byte != EOF) {
105 stream->avail_in++;
106 stream->next_in--;
109 return stream->avail_in != 0 ? Z_OK : Z_STREAM_END;
113 method = get_gzip_byte(stream);
114 flags = get_gzip_byte(stream);
116 if (method != Z_DEFLATED || (flags & GZIP_RESERVED) != 0)
117 return Z_DATA_ERROR;
119 /* Discard time, xflags and OS code: */
120 skip_gzip_bytes(stream, 6);
122 if (flags & GZIP_EXTRA_FIELD) {
123 /* Skip the extra field */
124 len = (unsigned int) get_gzip_byte(stream);
125 len += ((unsigned int) get_gzip_byte(stream)) << 8;
127 /* If EOF is encountered @len is garbage, but the loop below
128 * will quit anyway. */
129 while (len-- > 0 && get_gzip_byte(stream) != EOF) ;
132 /* Skip the original file name */
133 if (flags & GZIP_ORIG_NAME)
134 skip_gzip_string(stream);
136 /* Skip the .gz file comment */
137 if (flags & GZIP_COMMENT)
138 skip_gzip_string(stream);
140 /* Skip the header CRC */
141 if (flags & GZIP_HEADER_CRC)
142 skip_gzip_bytes(stream, 2);
144 return Z_OK;
148 /* Freaking dammit. This is impossible for me to get working. */
149 static unsigned char *
150 gzip_decode_buffer(unsigned char *data, int len, int *new_len)
152 unsigned char *buffer = NULL;
153 int error = Z_OK;
154 int tries, wbits;
156 /* This WBITS loop thing was something I got from
157 * http://lists.infradead.org/pipermail/linux-mtd/2002-March/004429.html
158 * but it doesn't fix it. :/ --jonas */
159 /* -MAX_WBITS impiles -> suppress zlib header and adler32. try first
160 * with -MAX_WBITS, if that fails, try MAX_WBITS to be backwards
161 * compatible */
162 wbits = -MAX_WBITS;
164 for (tries = 0; tries < 2; tries++) {
165 z_stream stream;
167 memset(&stream, 0, sizeof(z_stream));
169 /* FIXME: Use inflateInit2() to configure low memory
170 * usage for CONFIG_SMALL configurations. --jonas */
171 error = inflateInit2(&stream, wbits);
172 if (error != Z_OK) break;
174 stream.next_in = (char *)data;
175 stream.avail_in = len;
177 error = skip_gzip_header(&stream);
178 if (error != Z_OK) {
179 stream.next_in = (char *)data;
180 stream.avail_in = len;
183 do {
184 unsigned char *new_buffer;
185 size_t size = stream.total_out + MAX_STR_LEN;
187 assert(stream.total_out >= 0);
188 assert(stream.next_in);
190 new_buffer = mem_realloc(buffer, size);
191 if (!new_buffer) {
192 error = Z_MEM_ERROR;
193 break;
196 buffer = new_buffer;
197 stream.next_out = buffer + stream.total_out;
198 stream.avail_out = MAX_STR_LEN;
200 error = inflate(&stream, Z_NO_FLUSH);
201 if (error == Z_STREAM_END) {
202 /* Here gzio.c has some detection of
203 * concatenated .gz files and will do a gzip
204 * header skip and an inflateReset() call
205 * before continuing. It partly uses CRC to
206 * detect that. */
207 *new_len = stream.total_out;
208 error = Z_OK;
209 break;
212 } while (error == Z_OK && stream.avail_in > 0);
214 inflateEnd(&stream);
216 if (error != Z_DATA_ERROR)
217 break;
219 /* Try again with next wbits */
220 wbits = -wbits;
223 if (error != Z_OK) {
224 if (buffer) mem_free(buffer);
225 *new_len = 0;
226 return NULL;
229 return buffer;
233 static void
234 gzip_close(struct stream_encoded *stream)
236 gzclose((gzFile *) stream->data);
239 static unsigned char *gzip_extensions[] = { ".gz", ".tgz", NULL };
241 struct decoding_backend gzip_decoding_backend = {
242 "gzip",
243 gzip_extensions,
244 gzip_open,
245 gzip_read,
246 gzip_decode,
247 gzip_decode_buffer,
248 gzip_close,