/* $Xorg: gunzip.c,v 1.3 2000/08/17 19:46:37 cpqbld Exp $ */
/* lib/font/fontfile/gunzip.c
written by Mark Eichin <eichin@kitten.gen.ma.us> September 1996.
intended for inclusion in X11 public releases. */
/* $XFree86: xc/lib/font/fontfile/gunzip.c,v 1.4 2000/09/19 12:46:08 eich Exp $ */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <X11/fonts/fontmisc.h>
#include <X11/fonts/bufio.h>
#include <zlib.h>
typedef struct _xzip_buf {
z_stream z;
int zstat;
BufChar b[BUFFILESIZE];
BufChar b_in[BUFFILESIZE];
BufFilePtr f;
} xzip_buf;
static int BufZipFileClose ( BufFilePtr f, int flag );
static int BufZipFileFill ( BufFilePtr f );
static int BufZipFileSkip ( BufFilePtr f, int c );
static int BufCheckZipHeader ( BufFilePtr f );
BufFilePtr
BufFilePushZIP (BufFilePtr f)
{
xzip_buf *x;
x = (xzip_buf *) xalloc (sizeof (xzip_buf));
if (!x) return 0;
/* these are just for raw calloc/free */
x->z.zalloc = Z_NULL;
x->z.zfree = Z_NULL;
x->z.opaque = Z_NULL;
x->f = f;
/* force inflateInit to allocate it's own history buffer */
x->z.next_in = Z_NULL;
x->z.next_out = Z_NULL;
x->z.avail_in = x->z.avail_out = 0;
/* using negative windowBits sets "nowrap" mode, which turns off
zlib header checking [undocumented, for gzip compatibility only?] */
x->zstat = inflateInit2(&(x->z), -MAX_WBITS);
if (x->zstat != Z_OK) {
xfree(x);
return 0;
}
/* now that the history buffer is allocated, we provide the data buffer */
x->z.next_out = x->b;
x->z.avail_out = BUFFILESIZE;
x->z.next_out = x->b_in;
x->z.avail_in = 0;
if (BufCheckZipHeader(x->f)) {
xfree(x);
return 0;
}
return BufFileCreate((char *)x,
BufZipFileFill,
0,
BufZipFileSkip,
BufZipFileClose);
}
static int
BufZipFileClose(BufFilePtr f, int flag)
{
xzip_buf *x = (xzip_buf *)f->private;
inflateEnd (&(x->z));
BufFileClose (x->f, flag);
xfree (x);
return 1;
}
/* here's the real work.
-- we need to put stuff in f.buffer, update f.left and f.bufp,
then return the first byte (or BUFFILEEOF).
-- to do this, we need to get stuff into avail_in, and next_in,
and call inflate appropriately.
-- we may also need to add CRC maintenance - if inflate tells us
Z_STREAM_END, we then have 4bytes CRC and 4bytes length...
gzio.c:gzread shows most of the mechanism.
*/
static int
BufZipFileFill (BufFilePtr f)
{
xzip_buf *x = (xzip_buf *)f->private;
/* we only get called when left == 0... */
/* but just in case, deal */
if (f->left >= 0) {
f->left--;
return *(f->bufp++);
}
/* did we run out last time? */
switch (x->zstat) {
case Z_OK:
break;
case Z_STREAM_END:
case Z_DATA_ERROR:
case Z_ERRNO:
f->left = 0;
return BUFFILEEOF;
default:
return BUFFILEEOF;
}
/* now we work to consume what we can */
/* let zlib know what we can handle */
x->z.next_out = x->b;
x->z.avail_out = BUFFILESIZE;
/* and try to consume all of it */
while (x->z.avail_out > 0) {
/* if we don't have anything to work from... */
if (x->z.avail_in == 0) {
/* ... fill the z buf from underlying file */
int i, c;
for (i = 0; i < sizeof(x->b_in); i++) {
c = BufFileGet(x->f);
if (c == BUFFILEEOF) break;
x->b_in[i] = c;
}
x->z.avail_in += i;
x->z.next_in = x->b_in;
}
/* so now we have some output space and some input data */
x->zstat = inflate(&(x->z), Z_NO_FLUSH);
/* the inflation output happens in the f buffer directly... */
if (x->zstat == Z_STREAM_END) {
/* deal with EOF, crc */
break;
}
if (x->zstat != Z_OK) {
break;
}
}
f->bufp = x->b;
f->left = BUFFILESIZE - x->z.avail_out;
if (f->left >= 0) {
f->left--;
return *(f->bufp++);
} else {
return BUFFILEEOF;
}
}
/* there should be a BufCommonSkip... */
static int
BufZipFileSkip (BufFilePtr f, int c)
{
/* BufFileRawSkip returns the count unchanged.
BufCompressedSkip returns 0.
That means it probably never gets called... */
int retval = c;
while(c--) {
int get = BufFileGet(f);
if (get == BUFFILEEOF) return get;
}
return retval;
}
/* now we need to duplicate check_header */
/* contents:
0x1f, 0x8b -- magic number
1 byte -- method (Z_DEFLATED)
1 byte -- flags (mask with RESERVED -> fail)
4 byte -- time (discard)
1 byte -- xflags (discard)
1 byte -- "os" code (discard)
[if flags & EXTRA_FIELD:
2 bytes -- LSBfirst length n
n bytes -- extra data (discard)]
[if flags & ORIG_NAME:
n bytes -- null terminated name (discard)]
[if flags & COMMENT:
n bytes -- null terminated comment (discard)]
[if flags & HEAD_CRC:
2 bytes -- crc of headers? (discard)]
*/
/* gzip flag byte -- from gzio.c */
#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */
#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */
#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
#define ORIG_NAME 0x08 /* bit 3 set: original file name present */
#define COMMENT 0x10 /* bit 4 set: file comment present */
#define RESERVED 0xE0 /* bits 5..7: reserved */
#define GET(f) do {c = BufFileGet(f); if (c == BUFFILEEOF) return c;} while(0)
static int
BufCheckZipHeader(BufFilePtr f)
{
int c, flags;
GET(f); if (c != 0x1f) return 1; /* magic 1 */
GET(f); if (c != 0x8b) return 2; /* magic 2 */
GET(f); if (c != Z_DEFLATED) return 3; /* method */
GET(f); if (c & RESERVED) return 4; /* reserved flags */
flags = c;
GET(f); GET(f); GET(f); GET(f); /* time */
GET(f); /* xflags */
GET(f); /* os code */
if (flags & EXTRA_FIELD) {
int len;
GET(f); len = c;
GET(f); len += (c<<8);
while (len-- >= 0) {
GET(f);
}
}
if (flags & ORIG_NAME) {
do { GET(f); } while (c != 0);
}
if (flags & COMMENT) {
do { GET(f); } while (c != 0);
}
if (flags & HEAD_CRC) {
GET(f); GET(f); /* header crc */
}
return 0;
}
|