#include <sys/stat.h>
#include <sys/uio.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <limits.h>
#include <zlib.h>
#include "compress.h"
#define ASCII_FLAG 0x01
#define HEAD_CRC 0x02
#define EXTRA_FIELD 0x04
#define ORIG_NAME 0x08
#define COMMENT 0x10
#define RESERVED 0xE0
#define DEF_MEM_LEVEL 8
#define OS_CODE 0x03
typedef
struct gz_stream {
int z_fd;
int z_eof;
z_stream z_stream;
u_char z_buf[Z_BUFSIZE];
char z_mode;
u_int32_t z_time;
u_int32_t z_crc;
u_int32_t z_hlen;
u_int64_t z_total_in;
u_int64_t z_total_out;
} gz_stream;
static const u_char gz_magic[2] = {0x1f, 0x8b};
static u_int32_t get_int32(gz_stream *);
static int get_header(gz_stream *, char *, int);
static int get_byte(gz_stream *);
void *
gz_ropen(int fd, char *name, int gotmagic)
{
gz_stream *s;
if (fd < 0)
return NULL;
if ((s = calloc(1, sizeof(gz_stream))) == NULL)
return NULL;
s->z_stream.zalloc = (alloc_func)0;
s->z_stream.zfree = (free_func)0;
s->z_stream.opaque = (voidpf)0;
s->z_stream.next_in = Z_NULL;
s->z_stream.next_out = Z_NULL;
s->z_stream.avail_in = s->z_stream.avail_out = 0;
s->z_fd = 0;
s->z_eof = 0;
s->z_time = 0;
s->z_hlen = 0;
s->z_total_in = 0;
s->z_total_out = 0;
s->z_crc = crc32(0L, Z_NULL, 0);
s->z_mode = 'r';
if (inflateInit2(&(s->z_stream), -MAX_WBITS) != Z_OK) {
free (s);
return NULL;
}
s->z_stream.next_in = s->z_buf;
s->z_stream.avail_out = Z_BUFSIZE;
errno = 0;
s->z_fd = fd;
if (get_header(s, name, gotmagic) != 0) {
gz_close(s, NULL, NULL, NULL);
s = NULL;
}
return s;
}
static int
get_byte(gz_stream *s)
{
if (s->z_eof)
return EOF;
if (s->z_stream.avail_in == 0) {
errno = 0;
s->z_stream.avail_in = read(s->z_fd, s->z_buf, Z_BUFSIZE);
if ((int)s->z_stream.avail_in <= 0) {
s->z_eof = 1;
return EOF;
}
s->z_stream.next_in = s->z_buf;
}
s->z_stream.avail_in--;
return *s->z_stream.next_in++;
}
static u_int32_t
get_int32(gz_stream *s)
{
u_int32_t x;
x = ((u_int32_t)(get_byte(s) & 0xff));
x |= ((u_int32_t)(get_byte(s) & 0xff))<<8;
x |= ((u_int32_t)(get_byte(s) & 0xff))<<16;
x |= ((u_int32_t)(get_byte(s) & 0xff))<<24;
return x;
}
static int
get_header(gz_stream *s, char *name, int gotmagic)
{
int method;
int flags;
char *ep;
uInt len;
int c;
if (!gotmagic) {
for (len = 0; len < 2; len++) {
c = get_byte(s);
if (c != gz_magic[len]) {
errno = EFTYPE;
return -1;
}
}
}
method = get_byte(s);
flags = get_byte(s);
if (method != Z_DEFLATED || (flags & RESERVED) != 0) {
errno = EFTYPE;
return -1;
}
s->z_time = get_int32(s);
(void)get_byte(s);
(void)get_byte(s);
s->z_hlen += 10;
if ((flags & EXTRA_FIELD) != 0) {
len = (uInt)get_byte(s);
len += ((uInt)get_byte(s))<<8;
s->z_hlen += 2;
while (len-- != 0 && get_byte(s) != EOF)
s->z_hlen++;
}
if ((flags & ORIG_NAME) != 0) {
if ((ep = name) != NULL)
ep += PATH_MAX - 1;
while ((c = get_byte(s)) != EOF) {
s->z_hlen++;
if (c == '\0')
break;
if (name < ep)
*name++ = c;
}
if (name != NULL)
*name = '\0';
}
if ((flags & COMMENT) != 0) {
while ((c = get_byte(s)) != EOF) {
s->z_hlen++;
if (c == '\0')
break;
}
}
if ((flags & HEAD_CRC) != 0) {
(void)get_byte(s);
(void)get_byte(s);
s->z_hlen += 2;
}
if (s->z_eof) {
errno = EFTYPE;
return -1;
}
return 0;
}
int
gz_read(void *cookie, char *buf, int len)
{
gz_stream *s = (gz_stream*)cookie;
uLong old_total_in;
u_char *start = buf;
int error = Z_OK;
old_total_in = s->z_stream.total_in;
s->z_stream.next_out = buf;
s->z_stream.avail_out = len;
while (error == Z_OK && !s->z_eof && s->z_stream.avail_out != 0) {
if (s->z_stream.avail_in == 0) {
errno = 0;
s->z_stream.avail_in = read(s->z_fd, s->z_buf,
Z_BUFSIZE);
if ((int)s->z_stream.avail_in <= 0)
s->z_eof = 1;
s->z_stream.next_in = s->z_buf;
}
error = inflate(&(s->z_stream), Z_NO_FLUSH);
if (error == Z_DATA_ERROR) {
errno = EINVAL;
goto bad;
}
if (error == Z_BUF_ERROR) {
errno = EIO;
goto bad;
}
if (error == Z_STREAM_END) {
s->z_crc = crc32(s->z_crc, start,
(uInt)(s->z_stream.next_out - start));
start = s->z_stream.next_out;
if (get_int32(s) != s->z_crc) {
errno = EINVAL;
goto bad;
}
if (get_int32(s) != (u_int32_t)s->z_stream.total_out) {
errno = EIO;
return -1;
}
s->z_hlen += 2 * sizeof(int32_t);
if (get_header(s, NULL, 0) != 0) {
s->z_eof = 1;
break;
}
s->z_total_in += (uLong)(s->z_stream.total_in -
old_total_in);
inflateReset(&(s->z_stream));
s->z_crc = crc32(0L, Z_NULL, 0);
old_total_in = 0;
error = Z_OK;
}
}
s->z_crc = crc32(s->z_crc, start,
(uInt)(s->z_stream.next_out - start));
len -= s->z_stream.avail_out;
s->z_total_in += (uLong)(s->z_stream.total_in - old_total_in);
s->z_total_out += len;
return (len);
bad:
s->z_total_in += (uLong)(s->z_stream.total_in - old_total_in);
s->z_total_out += (len - s->z_stream.avail_out);
return (-1);
}
#ifndef SMALL
static int
put_int32(gz_stream *s, u_int32_t x)
{
u_int32_t y = htole32(x);
if (write(s->z_fd, &y, sizeof(y)) != sizeof(y))
return Z_ERRNO;
return 0;
}
static int
put_header(gz_stream *s, char *name, u_int32_t mtime, int bits)
{
struct iovec iov[2];
u_char buf[10];
buf[0] = gz_magic[0];
buf[1] = gz_magic[1];
buf[2] = Z_DEFLATED;
buf[3] = name ? ORIG_NAME : 0;
buf[4] = mtime & 0xff;
buf[5] = (mtime >> 8) & 0xff;
buf[6] = (mtime >> 16) & 0xff;
buf[7] = (mtime >> 24) & 0xff;
buf[8] = bits == 1 ? 4 : bits == 9 ? 2 : 0;
buf[9] = OS_CODE;
iov[0].iov_base = buf;
iov[0].iov_len = sizeof(buf);
s->z_hlen = sizeof(buf);
if (name != NULL) {
iov[1].iov_base = name;
iov[1].iov_len = strlen(name) + 1;
s->z_hlen += iov[1].iov_len;
}
if (writev(s->z_fd, iov, name ? 2 : 1) == -1)
return (-1);
return (0);
}
void *
gz_wopen(int fd, char *name, int bits, u_int32_t mtime)
{
gz_stream *s;
if (fd < 0)
return NULL;
if (bits < 0 || bits > Z_BEST_COMPRESSION) {
errno = EINVAL;
return NULL;
}
if ((s = calloc(1, sizeof(gz_stream))) == NULL)
return NULL;
s->z_stream.zalloc = (alloc_func)0;
s->z_stream.zfree = (free_func)0;
s->z_stream.opaque = (voidpf)0;
s->z_stream.next_in = Z_NULL;
s->z_stream.next_out = Z_NULL;
s->z_stream.avail_in = s->z_stream.avail_out = 0;
s->z_fd = 0;
s->z_eof = 0;
s->z_time = 0;
s->z_hlen = 0;
s->z_total_in = 0;
s->z_total_out = 0;
s->z_crc = crc32(0L, Z_NULL, 0);
s->z_mode = 'w';
if (deflateInit2(&(s->z_stream), bits, Z_DEFLATED,
-MAX_WBITS, DEF_MEM_LEVEL, 0) != Z_OK) {
free (s);
return NULL;
}
s->z_stream.next_out = s->z_buf;
s->z_stream.avail_out = Z_BUFSIZE;
errno = 0;
s->z_fd = fd;
if (put_header(s, name, mtime, bits) != 0) {
gz_close(s, NULL, NULL, NULL);
s = NULL;
}
return s;
}
int
gz_write(void *cookie, const char *buf, int len)
{
gz_stream *s = (gz_stream*)cookie;
s->z_stream.next_in = (char *)buf;
s->z_stream.avail_in = len;
while (s->z_stream.avail_in != 0) {
if (s->z_stream.avail_out == 0) {
s->z_total_out += Z_BUFSIZE;
if (write(s->z_fd, s->z_buf, Z_BUFSIZE) != Z_BUFSIZE)
break;
s->z_stream.next_out = s->z_buf;
s->z_stream.avail_out = Z_BUFSIZE;
}
if (deflate(&(s->z_stream), Z_NO_FLUSH) != Z_OK)
break;
}
s->z_crc = crc32(s->z_crc, buf, len);
len -= s->z_stream.avail_in;
s->z_total_in += len;
return len;
}
int
gz_flush(void *cookie, int flush)
{
gz_stream *s = (gz_stream*)cookie;
size_t len;
int done = 0;
int err;
if (s == NULL || s->z_mode != 'w') {
errno = EBADF;
return Z_ERRNO;
}
s->z_stream.avail_in = 0;
for (;;) {
len = Z_BUFSIZE - s->z_stream.avail_out;
if (len != 0) {
s->z_total_out += len;
if (write(s->z_fd, s->z_buf, len) != len)
return Z_ERRNO;
s->z_stream.next_out = s->z_buf;
s->z_stream.avail_out = Z_BUFSIZE;
}
if (done)
break;
if ((err = deflate(&(s->z_stream), flush)) != Z_OK &&
err != Z_STREAM_END)
return err;
done = (s->z_stream.avail_out != 0 || err == Z_STREAM_END);
}
return 0;
}
#endif
int
gz_close(void *cookie, struct z_info *info, const char *name, struct stat *sb)
{
gz_stream *s = (gz_stream*)cookie;
int err = 0;
if (s == NULL)
return -1;
#ifndef SMALL
if (s->z_mode == 'w' && (err = gz_flush (s, Z_FINISH)) == Z_OK) {
if ((err = put_int32 (s, s->z_crc)) == Z_OK) {
s->z_hlen += sizeof(int32_t);
if ((err = put_int32 (s, s->z_stream.total_in)) == Z_OK)
s->z_hlen += sizeof(int32_t);
}
}
#endif
if (!err && s->z_stream.state != NULL) {
if (s->z_mode == 'w')
#ifndef SMALL
err = deflateEnd(&s->z_stream);
#else
err = -1;
#endif
else if (s->z_mode == 'r')
err = inflateEnd(&s->z_stream);
}
if (info != NULL) {
info->mtime = s->z_time;
info->crc = s->z_crc;
info->hlen = s->z_hlen;
info->total_in = s->z_total_in;
info->total_out = s->z_total_out;
}
setfile(name, s->z_fd, sb);
if (!err)
err = close(s->z_fd);
else
(void)close(s->z_fd);
free(s);
return err;
}