#include <assert.h>
#include "cvs.h"
#include "buffer.h"
#if defined (SERVER_SUPPORT) || defined (CLIENT_SUPPORT)
#include "zlib.h"
#if !defined (EIO)
#define EIO EBADPOS
#endif
struct compress_buffer
{
struct buffer *buf;
z_stream zstr;
};
static void compress_error PROTO((int, int, z_stream *, const char *));
static int compress_buffer_input PROTO((void *, char *, int, int, int *));
static int compress_buffer_output PROTO((void *, const char *, int, int *));
static int compress_buffer_flush PROTO((void *));
static int compress_buffer_block PROTO((void *, int));
static int compress_buffer_shutdown_input PROTO((void *));
static int compress_buffer_shutdown_output PROTO((void *));
static void
compress_error (status, zstatus, zstr, msg)
int status;
int zstatus;
z_stream *zstr;
const char *msg;
{
int hold_errno;
const char *zmsg;
char buf[100];
hold_errno = errno;
zmsg = zstr->msg;
if (zmsg == NULL)
{
sprintf (buf, "error %d", zstatus);
zmsg = buf;
}
error (status,
zstatus == Z_ERRNO ? hold_errno : 0,
"%s: %s", msg, zmsg);
}
struct buffer *
compress_buffer_initialize (buf, input, level, memory)
struct buffer *buf;
int input;
int level;
void (*memory) PROTO((struct buffer *));
{
struct compress_buffer *n;
int zstatus;
n = (struct compress_buffer *) xmalloc (sizeof *n);
memset (n, 0, sizeof *n);
n->buf = buf;
if (input)
zstatus = inflateInit (&n->zstr);
else
zstatus = deflateInit (&n->zstr, level);
if (zstatus != Z_OK)
compress_error (1, zstatus, &n->zstr, "compression initialization");
assert (! input || buf->data == NULL || buf->data->next == NULL);
return buf_initialize (input ? compress_buffer_input : NULL,
input ? NULL : compress_buffer_output,
input ? NULL : compress_buffer_flush,
compress_buffer_block,
(input
? compress_buffer_shutdown_input
: compress_buffer_shutdown_output),
memory,
n);
}
static int
compress_buffer_input (closure, data, need, size, got)
void *closure;
char *data;
int need;
int size;
int *got;
{
struct compress_buffer *cb = (struct compress_buffer *) closure;
struct buffer_data *bd;
if (cb->buf->input == NULL)
abort ();
bd = cb->buf->data;
if (bd == NULL)
{
bd = ((struct buffer_data *) malloc (sizeof (struct buffer_data)));
if (bd == NULL)
return -2;
bd->text = (char *) malloc (BUFFER_DATA_SIZE);
if (bd->text == NULL)
{
free (bd);
return -2;
}
bd->bufp = bd->text;
bd->size = 0;
cb->buf->data = bd;
}
cb->zstr.avail_out = size;
cb->zstr.next_out = (Bytef *) data;
while (1)
{
int zstatus, sofar, status, nread;
cb->zstr.avail_in = bd->size;
cb->zstr.next_in = (Bytef *) bd->bufp;
do
{
zstatus = inflate (&cb->zstr, Z_NO_FLUSH);
if (zstatus == Z_STREAM_END)
break;
if (zstatus != Z_OK && zstatus != Z_BUF_ERROR)
{
compress_error (0, zstatus, &cb->zstr, "inflate");
return EIO;
}
} while (cb->zstr.avail_in > 0
&& cb->zstr.avail_out > 0);
bd->size = cb->zstr.avail_in;
bd->bufp = (char *) cb->zstr.next_in;
if (zstatus == Z_STREAM_END)
return -1;
sofar = size - cb->zstr.avail_out;
if (sofar > 0 && sofar >= need)
break;
assert (bd->size == 0);
status = (*cb->buf->input) (cb->buf->closure, bd->text,
need > 0 ? 1 : 0,
BUFFER_DATA_SIZE, &nread);
if (status != 0)
return status;
if (nread == 0)
{
assert (need == 0);
break;
}
bd->bufp = bd->text;
bd->size = nread;
}
*got = size - cb->zstr.avail_out;
return 0;
}
static int
compress_buffer_output (closure, data, have, wrote)
void *closure;
const char *data;
int have;
int *wrote;
{
struct compress_buffer *cb = (struct compress_buffer *) closure;
cb->zstr.avail_in = have;
cb->zstr.next_in = (unsigned char *) data;
while (cb->zstr.avail_in > 0)
{
char buffer[BUFFER_DATA_SIZE];
int zstatus;
cb->zstr.avail_out = BUFFER_DATA_SIZE;
cb->zstr.next_out = (unsigned char *) buffer;
zstatus = deflate (&cb->zstr, Z_NO_FLUSH);
if (zstatus != Z_OK)
{
compress_error (0, zstatus, &cb->zstr, "deflate");
return EIO;
}
if (cb->zstr.avail_out != BUFFER_DATA_SIZE)
buf_output (cb->buf, buffer,
BUFFER_DATA_SIZE - cb->zstr.avail_out);
}
*wrote = have;
return buf_send_output (cb->buf);
}
static int
compress_buffer_flush (closure)
void *closure;
{
struct compress_buffer *cb = (struct compress_buffer *) closure;
cb->zstr.avail_in = 0;
cb->zstr.next_in = NULL;
while (1)
{
char buffer[BUFFER_DATA_SIZE];
int zstatus;
cb->zstr.avail_out = BUFFER_DATA_SIZE;
cb->zstr.next_out = (unsigned char *) buffer;
zstatus = deflate (&cb->zstr, Z_SYNC_FLUSH);
if (zstatus == Z_BUF_ERROR)
break;
if (zstatus != Z_OK)
{
compress_error (0, zstatus, &cb->zstr, "deflate flush");
return EIO;
}
if (cb->zstr.avail_out != BUFFER_DATA_SIZE)
buf_output (cb->buf, buffer,
BUFFER_DATA_SIZE - cb->zstr.avail_out);
if (cb->zstr.avail_out > 0)
break;
}
return buf_flush (cb->buf, 0);
}
static int
compress_buffer_block (closure, block)
void *closure;
int block;
{
struct compress_buffer *cb = (struct compress_buffer *) closure;
if (block)
return set_block (cb->buf);
else
return set_nonblock (cb->buf);
}
static int
compress_buffer_shutdown_input (closure)
void *closure;
{
struct compress_buffer *cb = (struct compress_buffer *) closure;
int zstatus;
while (1)
{
int status, nread;
char buf[100];
status = compress_buffer_input (cb, buf, 0, sizeof buf, &nread);
if (status == -1)
break;
if (status != 0)
return status;
}
zstatus = inflateEnd (&cb->zstr);
if (zstatus != Z_OK)
{
compress_error (0, zstatus, &cb->zstr, "inflateEnd");
return EIO;
}
return buf_shutdown (cb->buf);
}
static int
compress_buffer_shutdown_output (closure)
void *closure;
{
struct compress_buffer *cb = (struct compress_buffer *) closure;
int zstatus, status;
do
{
char buffer[BUFFER_DATA_SIZE];
cb->zstr.avail_out = BUFFER_DATA_SIZE;
cb->zstr.next_out = (unsigned char *) buffer;
zstatus = deflate (&cb->zstr, Z_FINISH);
if (zstatus != Z_OK && zstatus != Z_STREAM_END)
{
compress_error (0, zstatus, &cb->zstr, "deflate finish");
return EIO;
}
if (cb->zstr.avail_out != BUFFER_DATA_SIZE)
buf_output (cb->buf, buffer,
BUFFER_DATA_SIZE - cb->zstr.avail_out);
} while (zstatus != Z_STREAM_END);
zstatus = deflateEnd (&cb->zstr);
if (zstatus != Z_OK)
{
compress_error (0, zstatus, &cb->zstr, "deflateEnd");
return EIO;
}
status = buf_flush (cb->buf, 1);
if (status != 0)
return status;
return buf_shutdown (cb->buf);
}
void
gunzip_and_write (fd, fullname, buf, size)
int fd;
char *fullname;
unsigned char *buf;
size_t size;
{
size_t pos;
z_stream zstr;
int zstatus;
unsigned char outbuf[32768];
unsigned long crc;
if (buf[0] != 31 || buf[1] != 139)
error (1, 0, "gzipped data does not start with gzip identification");
if (buf[2] != 8)
error (1, 0, "only the deflate compression method is supported");
pos = 10;
if (buf[3] & 4)
pos += buf[pos] + (buf[pos + 1] << 8) + 2;
if (buf[3] & 8)
pos += strlen (buf + pos) + 1;
if (buf[3] & 16)
pos += strlen (buf + pos) + 1;
if (buf[3] & 2)
pos += 2;
memset (&zstr, 0, sizeof zstr);
zstatus = inflateInit2 (&zstr, -15);
if (zstatus != Z_OK)
compress_error (1, zstatus, &zstr, fullname);
zstr.avail_in = size - pos;
zstr.next_in = buf + pos;
crc = crc32 (0, NULL, 0);
do
{
zstr.avail_out = sizeof (outbuf);
zstr.next_out = outbuf;
zstatus = inflate (&zstr, Z_NO_FLUSH);
if (zstatus != Z_STREAM_END && zstatus != Z_OK)
compress_error (1, zstatus, &zstr, fullname);
if (write (fd, outbuf, sizeof (outbuf) - zstr.avail_out) < 0)
error (1, errno, "writing decompressed file %s", fullname);
crc = crc32 (crc, outbuf, sizeof (outbuf) - zstr.avail_out);
} while (zstatus != Z_STREAM_END);
zstatus = inflateEnd (&zstr);
if (zstatus != Z_OK)
compress_error (0, zstatus, &zstr, fullname);
if (crc != (buf[zstr.total_in + 10]
+ (buf[zstr.total_in + 11] << 8)
+ (buf[zstr.total_in + 12] << 16)
+ (buf[zstr.total_in + 13] << 24)))
error (1, 0, "CRC error uncompressing %s", fullname);
if (zstr.total_out != (buf[zstr.total_in + 14]
+ (buf[zstr.total_in + 15] << 8)
+ (buf[zstr.total_in + 16] << 16)
+ (buf[zstr.total_in + 17] << 24)))
error (1, 0, "invalid length uncompressing %s", fullname);
}
void
read_and_gzip (fd, fullname, buf, size, len, level)
int fd;
char *fullname;
unsigned char **buf;
size_t *size;
size_t *len;
int level;
{
z_stream zstr;
int zstatus;
unsigned char inbuf[8192];
int nread;
unsigned long crc;
if (*size < 1024)
{
*size = 1024;
*buf = (unsigned char *) xrealloc (*buf, *size);
}
(*buf)[0] = 31;
(*buf)[1] = 139;
(*buf)[2] = 8;
(*buf)[3] = 0;
(*buf)[4] = (*buf)[5] = (*buf)[6] = (*buf)[7] = 0;
(*buf)[8] = 0;
(*buf)[9] = 255;
memset (&zstr, 0, sizeof zstr);
zstatus = deflateInit2 (&zstr, level, Z_DEFLATED, -15, 8,
Z_DEFAULT_STRATEGY);
crc = crc32 (0, NULL, 0);
if (zstatus != Z_OK)
compress_error (1, zstatus, &zstr, fullname);
zstr.avail_out = *size;
zstr.next_out = *buf + 10;
while (1)
{
int finish = 0;
nread = read (fd, inbuf, sizeof inbuf);
if (nread < 0)
error (1, errno, "cannot read %s", fullname);
else if (nread == 0)
finish = 1;
crc = crc32 (crc, inbuf, nread);
zstr.next_in = inbuf;
zstr.avail_in = nread;
do
{
size_t offset;
if (zstr.avail_out < 4096)
{
offset = zstr.next_out - *buf;
*size *= 2;
*buf = xrealloc (*buf, *size);
zstr.next_out = *buf + offset;
zstr.avail_out = *size - offset;
}
zstatus = deflate (&zstr, finish ? Z_FINISH : 0);
if (zstatus == Z_STREAM_END)
goto done;
else if (zstatus != Z_OK)
compress_error (0, zstatus, &zstr, fullname);
} while (zstr.avail_out == 0);
}
done:
*(*buf + zstr.total_out + 10) = crc & 0xff;
*(*buf + zstr.total_out + 11) = (crc >> 8) & 0xff;
*(*buf + zstr.total_out + 12) = (crc >> 16) & 0xff;
*(*buf + zstr.total_out + 13) = (crc >> 24) & 0xff;
*(*buf + zstr.total_out + 14) = zstr.total_in & 0xff;
*(*buf + zstr.total_out + 15) = (zstr.total_in >> 8) & 0xff;
*(*buf + zstr.total_out + 16) = (zstr.total_in >> 16) & 0xff;
*(*buf + zstr.total_out + 17) = (zstr.total_in >> 24) & 0xff;
*len = zstr.total_out + 18;
zstatus = deflateEnd (&zstr);
if (zstatus != Z_OK)
compress_error (0, zstatus, &zstr, fullname);
}
#endif