123a0898aSmrg/* lib/font/fontfile/gunzip.c
223a0898aSmrg   written by Mark Eichin <eichin@kitten.gen.ma.us> September 1996.
323a0898aSmrg   intended for inclusion in X11 public releases. */
423a0898aSmrg
523a0898aSmrg#ifdef HAVE_CONFIG_H
623a0898aSmrg#include <config.h>
723a0898aSmrg#endif
823a0898aSmrg#include <X11/fonts/fontmisc.h>
923a0898aSmrg#include <X11/fonts/bufio.h>
1023a0898aSmrg#include <zlib.h>
1123a0898aSmrg
1223a0898aSmrgtypedef struct _xzip_buf {
1323a0898aSmrg  z_stream z;
1423a0898aSmrg  int zstat;
1523a0898aSmrg  BufChar b[BUFFILESIZE];
1623a0898aSmrg  BufChar b_in[BUFFILESIZE];
1723a0898aSmrg  BufFilePtr f;
1823a0898aSmrg} xzip_buf;
1923a0898aSmrg
2023a0898aSmrgstatic int BufZipFileClose ( BufFilePtr f, int flag );
2123a0898aSmrgstatic int BufZipFileFill ( BufFilePtr f );
2223a0898aSmrgstatic int BufZipFileSkip ( BufFilePtr f, int c );
2323a0898aSmrgstatic int BufCheckZipHeader ( BufFilePtr f );
2423a0898aSmrg
2523a0898aSmrgBufFilePtr
2623a0898aSmrgBufFilePushZIP (BufFilePtr f)
2723a0898aSmrg{
2823a0898aSmrg  xzip_buf *x;
2923a0898aSmrg
307f7f5e4eSmrg  x = malloc (sizeof (xzip_buf));
3123a0898aSmrg  if (!x) return 0;
3223a0898aSmrg  /* these are just for raw calloc/free */
3323a0898aSmrg  x->z.zalloc = Z_NULL;
3423a0898aSmrg  x->z.zfree = Z_NULL;
3523a0898aSmrg  x->z.opaque = Z_NULL;
3623a0898aSmrg  x->f = f;
3723a0898aSmrg
3823a0898aSmrg  /* force inflateInit to allocate it's own history buffer */
3923a0898aSmrg  x->z.next_in = Z_NULL;
4023a0898aSmrg  x->z.next_out = Z_NULL;
4123a0898aSmrg  x->z.avail_in = x->z.avail_out = 0;
4223a0898aSmrg
4323a0898aSmrg  /* using negative windowBits sets "nowrap" mode, which turns off
4423a0898aSmrg     zlib header checking [undocumented, for gzip compatibility only?] */
4523a0898aSmrg  x->zstat = inflateInit2(&(x->z), -MAX_WBITS);
4623a0898aSmrg  if (x->zstat != Z_OK) {
477f7f5e4eSmrg    free(x);
4823a0898aSmrg    return 0;
4923a0898aSmrg  }
5023a0898aSmrg
5123a0898aSmrg  /* now that the history buffer is allocated, we provide the data buffer */
5223a0898aSmrg  x->z.next_out = x->b;
5323a0898aSmrg  x->z.avail_out = BUFFILESIZE;
5423a0898aSmrg  x->z.next_out = x->b_in;
5523a0898aSmrg  x->z.avail_in = 0;
5623a0898aSmrg
5723a0898aSmrg  if (BufCheckZipHeader(x->f)) {
587f7f5e4eSmrg    free(x);
5923a0898aSmrg    return 0;
6023a0898aSmrg  }
6123a0898aSmrg
6223a0898aSmrg  return BufFileCreate((char *)x,
6323a0898aSmrg		       BufZipFileFill,
6423a0898aSmrg		       0,
6523a0898aSmrg		       BufZipFileSkip,
6623a0898aSmrg		       BufZipFileClose);
6723a0898aSmrg}
6823a0898aSmrg
6941c30155Smrgstatic int
7023a0898aSmrgBufZipFileClose(BufFilePtr f, int flag)
7123a0898aSmrg{
7223a0898aSmrg  xzip_buf *x = (xzip_buf *)f->private;
7323a0898aSmrg  inflateEnd (&(x->z));
7423a0898aSmrg  BufFileClose (x->f, flag);
757f7f5e4eSmrg  free (x);
7623a0898aSmrg  return 1;
7723a0898aSmrg}
7823a0898aSmrg
7941c30155Smrg/* here's the real work.
8023a0898aSmrg   -- we need to put stuff in f.buffer, update f.left and f.bufp,
8123a0898aSmrg      then return the first byte (or BUFFILEEOF).
8241c30155Smrg   -- to do this, we need to get stuff into avail_in, and next_in,
8323a0898aSmrg      and call inflate appropriately.
8423a0898aSmrg   -- we may also need to add CRC maintenance - if inflate tells us
8523a0898aSmrg      Z_STREAM_END, we then have 4bytes CRC and 4bytes length...
8623a0898aSmrg   gzio.c:gzread shows most of the mechanism.
8723a0898aSmrg   */
8841c30155Smrgstatic int
8923a0898aSmrgBufZipFileFill (BufFilePtr f)
9023a0898aSmrg{
9123a0898aSmrg  xzip_buf *x = (xzip_buf *)f->private;
9223a0898aSmrg
9323a0898aSmrg  /* we only get called when left == 0... */
9423a0898aSmrg  /* but just in case, deal */
9523a0898aSmrg  if (f->left >= 0) {
9623a0898aSmrg    f->left--;
9723a0898aSmrg    return *(f->bufp++);
9823a0898aSmrg  }
9923a0898aSmrg  /* did we run out last time? */
10023a0898aSmrg  switch (x->zstat) {
10123a0898aSmrg  case Z_OK:
10223a0898aSmrg    break;
10323a0898aSmrg  case Z_STREAM_END:
10423a0898aSmrg  case Z_DATA_ERROR:
10523a0898aSmrg  case Z_ERRNO:
10623a0898aSmrg      f->left = 0;
10723a0898aSmrg      return BUFFILEEOF;
10823a0898aSmrg  default:
10923a0898aSmrg    return BUFFILEEOF;
11023a0898aSmrg  }
11123a0898aSmrg  /* now we work to consume what we can */
11223a0898aSmrg  /* let zlib know what we can handle */
11323a0898aSmrg  x->z.next_out = x->b;
11423a0898aSmrg  x->z.avail_out = BUFFILESIZE;
11523a0898aSmrg
11623a0898aSmrg  /* and try to consume all of it */
11723a0898aSmrg  while (x->z.avail_out > 0) {
11823a0898aSmrg    /* if we don't have anything to work from... */
11923a0898aSmrg    if (x->z.avail_in == 0) {
12023a0898aSmrg      /* ... fill the z buf from underlying file */
12123a0898aSmrg      int i, c;
12223a0898aSmrg      for (i = 0; i < sizeof(x->b_in); i++) {
12323a0898aSmrg	c = BufFileGet(x->f);
12423a0898aSmrg	if (c == BUFFILEEOF) break;
12523a0898aSmrg	x->b_in[i] = c;
12623a0898aSmrg      }
12723a0898aSmrg      x->z.avail_in += i;
12823a0898aSmrg      x->z.next_in = x->b_in;
12923a0898aSmrg    }
13023a0898aSmrg    /* so now we have some output space and some input data */
13123a0898aSmrg    x->zstat = inflate(&(x->z), Z_NO_FLUSH);
13223a0898aSmrg    /* the inflation output happens in the f buffer directly... */
13323a0898aSmrg    if (x->zstat == Z_STREAM_END) {
13423a0898aSmrg      /* deal with EOF, crc */
13523a0898aSmrg      break;
13623a0898aSmrg    }
13723a0898aSmrg    if (x->zstat != Z_OK) {
13823a0898aSmrg      break;
13923a0898aSmrg    }
14023a0898aSmrg  }
14123a0898aSmrg  f->bufp = x->b;
14241c30155Smrg  f->left = BUFFILESIZE - x->z.avail_out;
14323a0898aSmrg
14423a0898aSmrg  if (f->left >= 0) {
14523a0898aSmrg    f->left--;
14623a0898aSmrg    return *(f->bufp++);
14723a0898aSmrg  } else {
14823a0898aSmrg    return BUFFILEEOF;
14923a0898aSmrg  }
15023a0898aSmrg}
15123a0898aSmrg
15223a0898aSmrg/* there should be a BufCommonSkip... */
15341c30155Smrgstatic int
15423a0898aSmrgBufZipFileSkip (BufFilePtr f, int c)
15523a0898aSmrg{
15623a0898aSmrg  /* BufFileRawSkip returns the count unchanged.
15723a0898aSmrg     BufCompressedSkip returns 0.
15823a0898aSmrg     That means it probably never gets called... */
15923a0898aSmrg  int retval = c;
16023a0898aSmrg  while(c--) {
16123a0898aSmrg    int get = BufFileGet(f);
16223a0898aSmrg    if (get == BUFFILEEOF) return get;
16323a0898aSmrg  }
16423a0898aSmrg  return retval;
16523a0898aSmrg}
16623a0898aSmrg
16723a0898aSmrg/* now we need to duplicate check_header */
16823a0898aSmrg/* contents:
16923a0898aSmrg     0x1f, 0x8b	-- magic number
17023a0898aSmrg     1 byte	-- method (Z_DEFLATED)
17123a0898aSmrg     1 byte	-- flags (mask with RESERVED -> fail)
17223a0898aSmrg     4 byte	-- time (discard)
17323a0898aSmrg     1 byte	-- xflags (discard)
17423a0898aSmrg     1 byte	-- "os" code (discard)
17523a0898aSmrg     [if flags & EXTRA_FIELD:
17623a0898aSmrg         2 bytes -- LSBfirst length n
17723a0898aSmrg	 n bytes -- extra data (discard)]
17823a0898aSmrg     [if flags & ORIG_NAME:
17923a0898aSmrg	 n bytes -- null terminated name (discard)]
18023a0898aSmrg     [if flags & COMMENT:
18123a0898aSmrg	 n bytes -- null terminated comment (discard)]
18223a0898aSmrg     [if flags & HEAD_CRC:
18323a0898aSmrg         2 bytes -- crc of headers? (discard)]
18423a0898aSmrg */
18523a0898aSmrg
18623a0898aSmrg/* gzip flag byte -- from gzio.c */
18723a0898aSmrg#define ASCII_FLAG   0x01 /* bit 0 set: file probably ascii text */
18823a0898aSmrg#define HEAD_CRC     0x02 /* bit 1 set: header CRC present */
18923a0898aSmrg#define EXTRA_FIELD  0x04 /* bit 2 set: extra field present */
19023a0898aSmrg#define ORIG_NAME    0x08 /* bit 3 set: original file name present */
19123a0898aSmrg#define COMMENT      0x10 /* bit 4 set: file comment present */
19223a0898aSmrg#define RESERVED     0xE0 /* bits 5..7: reserved */
19323a0898aSmrg
19423a0898aSmrg#define GET(f) do {c = BufFileGet(f); if (c == BUFFILEEOF) return c;} while(0)
19541c30155Smrgstatic int
19623a0898aSmrgBufCheckZipHeader(BufFilePtr f)
19723a0898aSmrg{
19823a0898aSmrg  int c, flags;
19923a0898aSmrg  GET(f); if (c != 0x1f) return 1; /* magic 1 */
20023a0898aSmrg  GET(f); if (c != 0x8b) return 2; /* magic 2 */
20123a0898aSmrg  GET(f); if (c != Z_DEFLATED) return 3; /* method */
20223a0898aSmrg  GET(f); if (c & RESERVED) return 4; /* reserved flags */
20323a0898aSmrg  flags = c;
20423a0898aSmrg  GET(f); GET(f); GET(f); GET(f); /* time */
20523a0898aSmrg  GET(f);			/* xflags */
20623a0898aSmrg  GET(f);			/* os code */
20723a0898aSmrg  if (flags & EXTRA_FIELD) {
20823a0898aSmrg    int len;
20923a0898aSmrg    GET(f); len = c;
21023a0898aSmrg    GET(f); len += (c<<8);
21123a0898aSmrg    while (len-- >= 0) {
21223a0898aSmrg      GET(f);
21323a0898aSmrg    }
21423a0898aSmrg  }
21523a0898aSmrg  if (flags & ORIG_NAME) {
21623a0898aSmrg    do { GET(f); } while (c != 0);
21723a0898aSmrg  }
21823a0898aSmrg  if (flags & COMMENT) {
21923a0898aSmrg    do { GET(f); } while (c != 0);
22023a0898aSmrg  }
22123a0898aSmrg  if (flags & HEAD_CRC) {
22223a0898aSmrg    GET(f); GET(f);		/* header crc */
22323a0898aSmrg  }
22423a0898aSmrg  return 0;
22523a0898aSmrg}
226